javassist-3.12.1.ga/0000755000175000017500000000000011637463441014142 5ustar twernertwernerjavassist-3.12.1.ga/.classpath0000644000175000017500000000060611015721760016116 0ustar twernertwerner javassist-3.12.1.ga/tutorial/0000755000175000017500000000000011637463204016002 5ustar twernertwernerjavassist-3.12.1.ga/tutorial/tutorial.html0000644000175000017500000012136611116674241020541 0ustar twernertwerner Javassist Tutorial Getting Started with Javassist

Shigeru Chiba

Next page


1. Reading and writing bytecode

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.

The class Javassist.CtClass is an absatract representation of a class file. A CtClass (compile-time class) object is a handle for dealing with a class file. The following program is a very simple example:

This program first obtains a ClassPool object, which controls bytecode modification with Javassist. The ClassPool object is a container of CtClass object representing a class file. It reads a class file on demand for constructing a CtClass object and records the constructed object for responding later accesses. To modify the definition of a class, the users must first obtain from a ClassPool object a reference to a CtClass object representing that class. get() in ClassPool is used for this purpose. In the case of the program shown above, the CtClass object representing a class test.Rectangle is obtained from the ClassPool object and it is assigned to a variable cc. The ClassPool object returned by getDfault() searches the default system search path.

From the implementation viewpoint, ClassPool is a hash table of CtClass objects, which uses the class names as keys. get() in ClassPool searches this hash table to find a CtClass object associated with the specified key. If such a CtClass object is not found, get() reads a class file to construct a new CtClass object, which is recorded in the hash table and then returned as the resulting value of get().

The CtClass object obtained from a ClassPool object can be modified (details of how to modify a CtClass will be presented later). In the example above, it is modified so that the superclass of test.Rectangle is changed into a class test.Point. This change is reflected on the original class file when writeFile() in CtClass() is finally called.

writeFile() translates the CtClass object into a class file and writes it on a local disk. Javassist also provides a method for directly obtaining the modified bytecode. To obtain the bytecode, call toBytecode():

You can directly load the CtClass as well:

toClass() requests the context class loader for the current thread to load the class file represented by the CtClass. It returns a java.lang.Class object representing the loaded class. For more details, please see this section below.

Defining a new class

To define a new class from scratch, makeClass() must be called on a ClassPool.

This program defines a class Point including no members. Member methods of Point can be created with factory methods declared in CtNewMethod and appended to Point with addMethod() in CtClass.

makeClass() cannot create a new interface; makeInterface() in ClassPool can do. Member methods in an interface can be created with abstractMethod() in CtNewMethod. Note that an interface method is an abstract method.

Frozen classes

If a CtClass object is converted into a class file by writeFile(), toClass(), or toBytecode(), Javassist freezes that CtClass object. Further modifications of that CtClass object are not permitted. This is for warning the developers when they attempt to modify a class file that has been already loaded since the JVM does not allow reloading a class.

A frozen CtClass can be defrost so that modifications of the class definition will be permitted. For example,

After defrost() is called, the CtClass object can be modified again.

If ClassPool.doPruning is set to true, then Javassist prunes the data structure contained in a CtClass object when Javassist freezes that object. To reduce memory consumption, pruning discards unnecessary attributes (attribute_info structures) in that object. For example, Code_attribute structures (method bodies) are discarded. Thus, after a CtClass object is pruned, the bytecode of a method is not accessible except method names, signatures, and annotations. The pruned CtClass object cannot be defrost again. The default value of ClassPool.doPruning is false.

To disallow pruning a particular CtClass, stopPruning() must be called on that object in advance:

The CtClass object cc is not pruned. Thus it can be defrost after writeFile() is called.

Class search path

The default ClassPool returned by a static method ClassPool.getDefault() searches the same path that the underlying JVM (Java virtual machine) has. If a program is running on a web application server such as JBoss and Tomcat, the ClassPool object may not be able to find user classes since such a web application server uses multiple class loaders as well as the system class loader. In that case, an additional class path must be registered to the ClassPool. Suppose that pool refers to a ClassPool object:

This statement registers the class path that was used for loading the class of the object that this refers to. You can use any Class object as an argument instead of this.getClass(). The class path used for loading the class represented by that Class object is registered.

You can register a directory name as the class search path. For example, the following code adds a directory /usr/local/javalib to the search path:

The search path that the users can add is not only a directory but also a URL:

This program adds "http://www.javassist.org:80/java/" to the class search path. This URL is used only for searching classes belonging to a package org.javassist. For example, to load a class org.javassist.test.Main, its class file will be obtained from:

Furthermore, you can directly give a byte array to a ClassPool object and construct a CtClass object from that array. To do this, use ByteArrayClassPath. For example,

The obtained CtClass object represents a class defined by the class file specified by b. The ClassPool reads a class file from the given ByteArrayClassPath if get() is called and the class name given to get() is equal to one specified by name.

If you do not know the fully-qualified name of the class, then you can use makeClass() in ClassPool:

makeClass() returns the CtClass object constructed from the given input stream. You can use makeClass() for eagerly feeding class files to the ClassPool object. This might improve performance if the search path includes a large jar file. Since a ClassPool object reads a class file on demand, it might repeatedly search the whole jar file for every class file. makeClass() can be used for optimizing this search. The CtClass constructed by makeClass() is kept in the ClassPool object and the class file is never read again.

The users can extend the class search path. They can define a new class implementing ClassPath interface and give an instance of that class to insertClassPath() in ClassPool. This allows a non-standard resource to be included in the search path.


2. ClassPool

A ClassPool object is a container of CtClass objects. Once a CtClass object is created, it is recorded in a ClassPool for ever. This is because a compiler may need to access the CtClass object later when it compiles source code that refers to the class represented by that CtClass.

For example, suppose that a new method getter() is added to a CtClass object representing Point class. Later, the program attempts to compile source code including a method call to getter() in Point and use the compiled code as the body of a method, which will be added to another class Line. If the CtClass object representing Point is lost, the compiler cannot compile the method call to getter(). Note that the original class definition does not include getter(). Therefore, to correctly compile such a method call, the ClassPool must contain all the instances of CtClass all the time of program execution.

Avoid out of memory

This specification of ClassPool may cause huge memory consumption if the number of CtClass objects becomes amazingly large (this rarely happens since Javassist tries to reduce memory consumption in various ways). To avoid this problem, you can explicitly remove an unnecessary CtClass object from the ClassPool. If you call detach() on a CtClass object, then that CtClass object is removed from the ClassPool. For example,

You must not call any method on that CtClass object after detach() is called. However, you can call get() on ClassPool to make a new instance of CtClass representing the same class. If you call get(), the ClassPool reads a class file again and newly creates a CtClass object, which is returned by get().

Another idea is to occasionally replace a ClassPool with a new one and discard the old one. If an old ClassPool is garbage collected, the CtClass objects included in that ClassPool are also garbage collected. To create a new instance of ClassPool, execute the following code snippet:

This creates a ClassPool object that behaves as the default ClassPool returned by ClassPool.getDefault() does. Note that ClassPool.getDefault() is a singleton factory method provided for convenience. It creates a ClassPool object in the same way shown above although it keeps a single instance of ClassPool and reuses it. A ClassPool object returned by getDefault() does not have a special role. getDefault() is a convenience method.

Note that new ClassPool(true) is a convenient constructor, which constructs a ClassPool object and appends the system search path to it. Calling that constructor is equivalent to the following code:

Cascaded ClassPools

If a program is running on a web application server, creating multiple instances of ClassPool might be necessary; an instance of ClassPool should be created for each class loader (i.e. container). The program should create a ClassPool object by not calling getDefault() but a constructor of ClassPool.

Multiple ClassPool objects can be cascaded like java.lang.ClassLoader. For example,

If child.get() is called, the child ClassPool first delegates to the parent ClassPool. If the parent ClassPool fails to find a class file, then the child ClassPool attempts to find a class file under the ./classes directory.

If child.childFirstLookup is true, the child ClassPool attempts to find a class file before delegating to the parent ClassPool. For example,

Changing a class name for defining a new class

A new class can be defined as a copy of an existing class. The program below does that:

This program first obtains the CtClass object for class Point. Then it calls setName() to give a new name Pair to that CtClass object. After this call, all occurrences of the class name in the class definition represented by that CtClass object are changed from Point to Pair. The other part of the class definition does not change.

Note that setName() in CtClass changes a record in the ClassPool object. From the implementation viewpoint, a ClassPool object is a hash table of CtClass objects. setName() changes the key associated to the CtClass object in the hash table. The key is changed from the original class name to the new class name.

Therefore, if get("Point") is later called on the ClassPool object again, then it never returns the CtClass object that the variable cc refers to. The ClassPool object reads a class file Point.class again and it constructs a new CtClass object for class Point. This is because the CtClass object associated with the name Point does not exist any more. See the followings:

cc1 and cc2 refer to the same instance of CtClass that cc does whereas cc3 does not. Note that, after cc.setName("Pair") is executed, the CtClass object that cc and cc1 refer to represents the Pair class.

The ClassPool object is used to maintain one-to-one mapping between classes and CtClass objects. Javassist never allows two distinct CtClass objects to represent the same class unless two independent ClassPool are created. This is a significant feature for consistent program transformation.

To create another copy of the default instance of ClassPool, which is returned by ClassPool.getDefault(), execute the following code snippet (this code was already shown above):

If you have two ClassPool objects, then you can obtain, from each ClassPool, a distinct CtClass object representing the same class file. You can differently modify these CtClass objects to generate different versions of the class.

Renaming a frozen class for defining a new class

Once a CtClass object is converted into a class file by writeFile() or toBytecode(), Javassist rejects further modifications of that CtClass object. Hence, after the CtClass object representing Point class is converted into a class file, you cannot define Pair class as a copy of Point since executing setName() on Point is rejected. The following code snippet is wrong:

To avoid this restriction, you should call getAndRename() in ClassPool. For example,

If getAndRename() is called, the ClassPool first reads Point.class for creating a new CtClass object representing Point class. However, it renames that CtClass object from Point to Pair before it records that CtClass object in a hash table. Thus getAndRename() can be executed after writeFile() or toBytecode() is called on the the CtClass object representing Point class.


3. Class loader

If what classes must be modified is known in advance, the easiest way for modifying the classes is as follows:

If whether a class is modified or not is determined at load time, the users must make Javassist collaborate with a class loader. 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.


3.1 The toClass method in CtClass

The CtClass provides a convenience method toClass(), which requests the context class loader for the current thread to load the class represented by the CtClass object. To call this method, the caller must have appropriate permission; otherwise, a SecurityException may be thrown.

The following program shows how to use toClass():

Test.main() inserts a call to println() in the method body of say() in Hello. Then it constructs an instance of the modified Hello class and calls say() on that instance.

Note that the program above depends on the fact that the Hello class is never loaded before toClass() is invoked. If not, the JVM would load the original Hello class before toClass() requests to load the modified Hello class. Hence loading the modified Hello class would be failed (LinkageError is thrown). For example, if main() in Test is something like this:

then the original Hello class is loaded at the first line of main and the call to toClass() throws an exception since the class loader cannot load two different versions of the Hello class at the same time.

If the program is running on some application server such as JBoss and Tomcat, the context class loader used by toClass() might be inappropriate. In this case, you would see an unexpected ClassCastException. To avoid this exception, you must explicitly give an appropriate class loader to toClass(). For example, if bean is your session bean object, then the following code:

would work. You should give toClass() the class loader that has loaded your program (in the above example, the class of the bean object).

toClass() is provided for convenience. If you need more complex functionality, you should write your own class loader.


3.2 Class loading in Java

In Java, multiple class loaders can coexist and each class loader creates its own name space. Different class loaders can load different class files with the same class name. The loaded two classes are regarded as different ones. This feature enables us to run multiple application programs on a single JVM even if these programs include different classes with the same name.

If the same class file is loaded by two distinct class loaders, the JVM makes two distinct classes with the same name and definition. The two classes are regarded as different ones. Since the two classes are not identical, an instance of one class is not assignable to a variable of the other class. The cast operation between the two classes fails and throws a ClassCastException.

For example, the following code snippet throws an exception:

The Box class is loaded by two class loaders. Suppose that a class loader CL loads a class including this code snippet. Since this code snippet refers to MyClassLoader, Class, Object, and Box, CL also loads these classes (unless it delegates to another class loader). Hence the type of the variable b is the Box class loaded by CL. On the other hand, myLoader also loads the Box class. The object obj is an instance of the Box class loaded by myLoader. Therefore, the last statement always throws a ClassCastException since the class of obj is a different verison of the Box class from one used as the type of the variable b.

Multiple class loaders form a tree structure. Each class loader except the bootstrap loader has a parent class loader, which has normally loaded the class of that child class loader. Since the request to load a class can be delegated along this hierarchy of class loaders, a class may be loaded by a class loader that you do not request the class loading. Therefore, the class loader that has been requested to load a class C may be different from the loader that actually loads the class C. For distinction, we call the former loader the initiator of C and we call the latter loader the real loader of C.

Furthermore, if a class loader CL requested to load a class C (the initiator of C) delegates to the parent class loader PL, then the class loader CL is never requested to load any classes referred to in the definition of the class C. CL is not the initiator of those classes. Instead, the parent class loader PL becomes their initiators and it is requested to load them. The classes that the definition of a class C referes to are loaded by the real loader of C.

To understand this behavior, let's consider the following example.

Suppose that a class Window is loaded by a class loader L. Both the initiator and the real loader of Window are L. Since the definition of Window refers to Box, the JVM will request L to load Box. Here, suppose that L delegates this task to the parent class loader PL. The initiator of Box is L but the real loader is PL. In this case, the initiator of Point is not L but PL since it is the same as the real loader of Box. Thus L is never requested to load Point.

Next, let's consider a slightly modified example.

Now, the definition of Window also refers to Point. In this case, the class loader L must also delegate to PL if it is requested to load Point. You must avoid having two class loaders doubly load the same class. One of the two loaders must delegate to the other.

If L does not delegate to PL when Point is loaded, widthIs() would throw a ClassCastException. Since the real loader of Box is PL, Point referred to in Box is also loaded by PL. Therefore, the resulting value of getSize() is an instance of Point loaded by PL whereas the type of the variable p in widthIs() is Point loaded by L. The JVM regards them as distinct types and thus it throws an exception because of type mismatch.

This behavior is somewhat inconvenient but necessary. If the following statement:

did not throw an exception, then the programmer of Window could break the encapsulation of Point objects. For example, the field x is private in Point loaded by PL. However, the Window class could directly access the value of x if L loads Point with the following definition:

For more details of class loaders in Java, the following paper would be helpful:


3.3 Using javassist.Loader

Javassist provides a class loader javassist.Loader. This class loader uses a javassist.ClassPool object for reading a class file.

For example, javassist.Loader can be used for loading a particular class modified with Javassist.

This program modifies a class test.Rectangle. The superclass of test.Rectangle is set to a test.Point class. Then this program loads the modified class, and creates a new instance of the test.Rectangle class.

If the users want to modify a class on demand when it is loaded, the users can add an event listener to a javassist.Loader. The added event listener is notified when the class loader loads a class. The event-listener class must implement the following interface:

The method start() is called when this event listener is added to a javassist.Loader object by addTranslator() in javassist.Loader. The method onLoad() is called before javassist.Loader loads a class. onLoad() can modify the definition of the loaded class.

For example, the following event listener changes all classes to public classes just before they are loaded.

Note that onLoad() does not have to call toBytecode() or writeFile() since javassist.Loader calls these methods to obtain a class file.

To run an application class MyApp with a MyTranslator object, write a main class as following:

To run this program, do:

The class MyApp and the other application classes are translated by MyTranslator.

Note that application classes like MyApp cannot access the loader classes such as Main2, MyTranslator, and ClassPool because they are loaded by different loaders. The application classes are loaded by javassist.Loader whereas the loader classes such as Main2 are by the default Java class loader.

javassist.Loader searches for classes in a different order from java.lang.ClassLoader. ClassLoader first delegates the loading operations to the parent class loader and then attempts to load the classes only if the parent class loader cannot find them. On the other hand, javassist.Loader attempts to load the classes before delegating to the parent class loader. It delegates only if:

This search order allows loading modified classes by Javassist. However, it delegates to the parent class loader if it fails to find modified classes for some reason. Once a class is loaded by the parent class loader, the other classes referred to in that class will be also loaded by the parent class loader and thus they are never modified. Recall that all the classes referred to in a class C are loaded by the real loader of C. If your program fails to load a modified class, you should make sure whether all the classes using that class have been loaded by javassist.Loader.


3.4 Writing a class loader

A simple class loader using Javassist is as follows:

The class MyApp is an application program. To execute this program, first put the class file under the ./class directory, which must not be included in the class search path. Otherwise, MyApp.class would be loaded by the default system class loader, which is the parent loader of SampleLoader. The directory name ./class is specified by insertClassPath() in the constructor. You can choose a different name instead of ./class if you want. Then do as follows:

The class loader loads the class MyApp (./class/MyApp.class) and calls MyApp.main() with the command line parameters.

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 MyApp class in a name space separated from the name space that the class SampleLoader belongs to because the two classes are loaded by different class loaders. Hence, the MyApp class cannot directly access the class SampleLoader.


3.5 Modifying a system class

The system classes like java.lang.String cannot be loaded by a class loader other than the system class loader. Therefore, SampleLoader or javassist.Loader shown above cannot modify the system classes at loading time.

If your application needs to do that, the system classes must be statically modified. For example, the following program adds a new field hiddenValue to java.lang.String:

This program produces a file "./java/lang/String.class".

To run your program MyApp with this modified String class, do as follows:

Suppose that the definition of MyApp is as follows:

If the modified String class is correctly loaded, MyApp prints hiddenValue.

Note: Applications that use this technique for the purpose of overriding a system class in rt.jar should not be deployed as doing so would contravene the Java 2 Runtime Environment binary code license.


3.6 Reloading a class at runtime

If the JVM is launched with the JPDA (Java Platform Debugger Architecture) enabled, a class is dynamically reloadable. After the JVM loads a class, the old version of the class definition can be unloaded and a new one can be reloaded again. That is, the definition of that class can be dynamically modified during runtime. However, the new class definition must be somewhat compatible to the old one. The JVM does not allow schema changes between the two versions. They have the same set of methods and fields.

Javassist provides a convenient class for reloading a class at runtime. For more information, see the API documentation of javassist.tools.HotSwapper.


Next page


Java(TM) is a trademark of Sun Microsystems, Inc.
Copyright (C) 2000-2007 by Shigeru Chiba, All rights reserved. javassist-3.12.1.ga/tutorial/tutorial3.html0000644000175000017500000002422211371311506020610 0ustar twernertwerner Javassist Tutorial
Getting Started with Javassist
Previous page

5. Bytecode level API

6. Generics

7. J2ME


5. Bytecode level API

Javassist also provides lower-level API for directly editing a class file. To use this level of API, you need detailed knowledge of the Java bytecode and the class file format while this level of API allows you any kind of modification of class files.

If you want to just produce a simple class file, javassist.bytecode.ClassFileWriter might provide the best API for you. It is much faster than javassist.bytecode.ClassFile although its API is minimum.

5.1 Obtaining a ClassFile object

A javassist.bytecode.ClassFile object represents a class file. To obtian this object, getClassFile() in CtClass should be called.

Otherwise, you can construct a javassist.bytecode.ClassFile directly from a class file. For example,

This code snippet creats a ClassFile object from Point.class.

A ClassFile object can be written back to a class file. write() in ClassFile writes the contents of the class file to a given DataOutputStream.


5.2 Adding and removing a member

ClassFile provides addField() and addMethod() for adding a field or a method (note that a constructor is regarded as a method at the bytecode level). It also provides addAttribute() for adding an attribute to the class file.

Note that FieldInfo, MethodInfo, and AttributeInfo objects include a link to a ConstPool (constant pool table) object. The ConstPool object must be common to the ClassFile object and a FieldInfo (or MethodInfo etc.) object that is added to that ClassFile object. In other words, a FieldInfo (or MethodInfo etc.) object must not be shared among different ClassFile objects.

To remove a field or a method from a ClassFile object, you must first obtain a java.util.List object containing all the fields of the class. getFields() and getMethods() return the lists. A field or a method can be removed by calling remove() on the List object. An attribute can be removed in a similar way. Call getAttributes() in FieldInfo or MethodInfo to obtain the list of attributes, and remove one from the list.


5.3 Traversing a method body

To examine every bytecode instruction in a method body, CodeIterator is useful. To otbain this object, do as follows:

A CodeIterator object allows you to visit every bytecode instruction one by one from the beginning to the end. The following methods are part of the methods declared in CodeIterator:

The following code snippet displays all the instructions included in a method body:


5.4 Producing a bytecode sequence

A Bytecode object represents a sequence of bytecode instructions. It is a growable array of bytecode. Here is a sample code snippet:

This produces the code attribute representing the following sequence:

You can also obtain a byte array containing this sequence by calling get() in Bytecode. The obtained array can be inserted in another code attribute.

While Bytecode provides a number of methods for adding a specific instruction to the sequence, it provides addOpcode() for adding an 8bit opcode and addIndex() for adding an index. The 8bit value of each opcode is defined in the Opcode interface.

addOpcode() and other methods for adding a specific instruction are automatically maintain the maximum stack depth unless the control flow does not include a branch. This value can be obtained by calling getMaxStack() on the Bytecode object. It is also reflected on the CodeAttribute object constructed from the Bytecode object. To recompute the maximum stack depth of a method body, call computeMaxStack() in CodeAttribute.


5.5 Annotations (Meta tags)

Annotations are stored in a class file as runtime invisible (or visible) annotations attribute. These attributes can be obtained from ClassFile, MethodInfo, or FieldInfo objects. Call getAttribute(AnnotationsAttribute.invisibleTag) on those objects. For more details, see the javadoc manual of javassist.bytecode.AnnotationsAttribute class and the javassist.bytecode.annotation package.

Javassist also let you access annotations by the higher-level API. If you want to access annotations through CtClass, call getAnnotations() in CtClass or CtBehavior.


6. Generics

The lower-level API of Javassist fully supports generics introduced by Java 5. On the other hand, the higher-level API such as CtClass does not directly support generics. However, this is not a serious problem for bytecode transformation.

The generics of Java is implemented by the erasure technique. After compilation, all type parameters are dropped off. For example, suppose that your source code declares a parameterized type Vector<String>:

The compiled bytecode is equivalent to the following code:

So when you write a bytecode transformer, you can just drop off all type parameters. For example, if you have a class:

and want to add an interface Getter<T> to the class Wrapper<T>:

Then the interface you really have to add is Getter (the type parameters <T> drops off) and the method you also have to add to the Wrapper class is this simple one:

Note that no type parameters are necessary.


7. J2ME

If you modify a class file for the J2ME execution environment, you must perform preverification. Preverifying is basically producing stack maps, which is similar to stack map tables introduced into J2SE at JDK 1.6. Javassist maintains the stack maps for J2ME only if javassist.bytecode.MethodInfo.doPreverify is true.

You can also manually produce a stack map for a modified method. For a given method represented by a CtMethod object m, you can produce a stack map by calling the following methods:

Here, cpool is a ClassPool object, which is available by calling getClassPool() on a CtClass object. A ClassPool object is responsible for finding class files from given class pathes. To obtain all the CtMethod objects, call the getDeclaredMethods method on a CtClass object.


Previous page


Java(TM) is a trademark of Sun Microsystems, Inc.
Copyright (C) 2000-2007 by Shigeru Chiba, All rights reserved. javassist-3.12.1.ga/tutorial/brown.css0000644000175000017500000000032007651544225017642 0ustar twernertwernerh1,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; } javassist-3.12.1.ga/tutorial/tutorial2.html0000644000175000017500000013665011021204176020614 0ustar twernertwerner Javassist Tutorial
Getting Started with Javassist
Previous page
Next page

4. Introspection and customization


4. Introspection and customization

CtClass provides methods for introspection. The introspective ability of Javassist is compatible with that of the Java reflection API. CtClass provides getName(), getSuperclass(), getMethods(), and so on. CtClass 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.

Methods are represented by CtMethod objects. CtMethod provides several methods for modifying the definition of the method. Note that if a method is inherited from a super class, then the same CtMethod object that represents the inherited method represents the method declared in that super class. A CtMethod object corresponds to every method declaration.

For example, if class Point declares method move() and a subclass ColorPoint of Point does not override move(), the two move() methods declared in Point and inherited in ColorPoint are represented by the identical CtMethod object. If the method definition represented by this CtMethod object is modified, the modification is reflected on both the methods. If you want to modify only the move() method in ColorPoint, you first have to add to ColorPoint a copy of the CtMethod object representing move() in Point. A copy of the the CtMethod object can be obtained by CtNewMethod.copy().



Javassist also provides low-level API for directly editing a raw class file. For example, getClassFile() in CtClass returns a ClassFile object representing a raw class file. getMethodInfo() in CtMethod returns a MethodInfo object representing a method_info 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 javassist.bytecode package.

The class files modified by Javassist requires the javassist.runtime package for runtime support only if some special identifiers starting with $ are used. Those special identifiers are described below. The class files modified without those special identifiers do not need the javassist.runtime package or any other Javassist packages at runtime. For more details, see the API documentation of the javassist.runtime package.


4.1 Inserting source text at the beginning/end of a method body

CtMethod and CtConstructor provide methods insertBefore(), insertAfter(), and addCatch(). They are used for inserting a code fragment into the body of an existing method. The users can specify those code fragments with source text 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 inlined into a method body.

Inserting a code fragment at the position specified by a line number is also possible (if the line number table is contained in the class file). insertAt() in CtMethod and CtConstructor takes source text and a line number in the source file of the original class definition. It compiles the source text and inserts the compiled code at the line number.

The methods insertBefore(), insertAfter(), addCatch(), and insertAt() receive a String object representing a statement or a block. A statement is a single control structure like if and while or an expression ending with a semi colon (;). A block is a set of statements surrounded with braces {}. Hence each of the following lines is an example of valid statement or block:

The statement and the block can refer to fields and methods. They can also refer to the parameters to the method that they are inserted into if that method was compiled with the -g option (to include a local variable attribute in the class file). Otherwise, they must access the method parameters through the special variables $0, $1, $2, ... described below. Accessing local variables declared in the method is not allowed although declaring a new local variable in the block is allowed. However, insertAt() allows the statement and the block to access local variables if these variables are available at the specified line number and the target method was compiled with the -g option.

The String object passed to the methods insertBefore(), insertAfter(), addCatch(), and insertAt() are compiled by the compiler included in Javassist. Since the compiler supports language extensions, several identifiers starting with $ have special meaning:

$0, $1, $2, ...

The parameters passed to the target method are accessible with $1, $2, ... instead of the original parameter names. $1 represents the first parameter, $2 represents the second parameter, and so on. The types of those variables are identical to the parameter types. $0 is equivalent to this. If the method is static, $0 is not available.

These variables are used as following. Suppose that a class Point:

To print the values of dx and dy whenever the method move() is called, execute this program:

Note that the source text passed to insertBefore() is surrounded with braces {}. insertBefore() accepts only a single statement or a block surrounded with braces.

The definition of the class Point after the modification is like this:

$1 and $2 are replaced with dx and dy, respectively.

$1, $2, $3 ... 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.

$args

The variable $args represents an array of all the parameters. The type of that variable is an array of class Object. If a parameter type is a primitive type such as int, then the parameter value is converted into a wrapper object such as java.lang.Integer to store in $args. Thus, $args[0] is equivalent to $1 unless the type of the first parameter is a primitive type. Note that $args[0] is not equivalent to $0; $0 represents this.

If an array of Object is assigned to $args, 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.

$$

The variable $$ is abbreviation of a list of all the parameters separated by commas. For example, if the number of the parameters to method move() is three, then

is equivalent to this:

If move() does not take any parameters, then move($$) is equivalent to move().

$$ can be used with another method. If you write an expression:

then this expression is equivalent to:

Note that $$ enables generic notation of method call with respect to the number of parameters. It is typically used with $proceed shown later.

$cflow

$cflow means "control flow". This read-only variable returns the depth of the recursive calls to a specific method.

Suppose that the method shown below is represented by a CtMethod object cm:

To use $cflow, first declare that $cflow is used for monitoring calls to the method fact():

The parameter to useCflow() is the identifier of the declared $cflow variable. Any valid Java name can be used as the identifier. Since the identifier can also include . (dot), for example, "my.Test.fact" is a valid identifier.

Then, $cflow(fact) represents the depth of the recursive calls to the method specified by cm. The value of $cflow(fact) is 0 (zero) when the method is first called whereas it is 1 when the method is recursively called within the method. For example,

translates the method fact() so that it shows the parameter. Since the value of $cflow(fact) is checked, the method fact() does not show the parameter if it is recursively called within fact().

The value of $cflow is the number of stack frames associated with the specified method cm under the current topmost stack frame for the current thread. $cflow is also accessible within a method different from the specified method cm.

$r

$r 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:

If the result type is a primitive type, then ($r) follows special semantics. First, if the operand type of the cast expression is a primitive type, ($r) works as a normal cast operator to the result type. On the other hand, if the operand type is a wrapper type, ($r) converts from the wrapper type to the result type. For example, if the result type is int, then ($r) converts from java.lang.Integer to int.

If the result type is void, then ($r) does not convert a type; it does nothing. However, if the operand is a call to a void method, then ($r) results in null. For example, if the result type is void and foo() is a void method, then

is a valid statement.

The cast operator ($r) is also useful in a return statement. Even if the result type is void, the following return statement is valid:

Here, result is some local variable. Since ($r) is specified, the resulting value is discarded. This return statement is regarded as the equivalent of the return statement without a resulting value:

$w

$w represents a wrapper type. It must be used as the cast type in a cast expression. ($w) converts from a primitive type to the corresponding wrapper type. The following code is an example:

The selected wrapper type depends on the type of the expression following ($w). If the type of the expression is double, then the wrapper type is java.lang.Double.

If the type of the expression following ($w) is not a primitive type, then ($w) does nothing.

$_

insertAfter() in CtMethod and CtConstructor inserts the compiled code at the end of the method. In the statement given to insertAfter(), not only the variables shown above such as $0, $1, ... but also $_ is available.

The variable $_ 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 void, then the type of $_ is Object and the value of $_ is null.

Although the compiled code inserted by insertAfter() 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 asFinally to insertAfter() must be true.

If an exception is thrown, the compiled code inserted by insertAfter() is executed as a finally clause. The value of $_ is 0 or null 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 $_ is never thrown to the caller; it is rather discarded.

$sig

The value of $sig is an array of java.lang.Class objects that represent the formal parameter types in declaration order.

$type

The value of $type is an java.lang.Class object representing the formal type of the result value. This variable refers to Void.class if this is a constructor.

$class

The value of $class is an java.lang.Class object representing the class in which the edited method is declared. This represents the type of $0.

addCatch()

addCatch() 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 special variable $e.

For example, this program:

translates the method body represented by m into something like this:

Note that the inserted code fragment must end with a throw or return statement.


4.2 Altering a method body

CtMethod and CtConstructor provide setBody() for substituting a whole method body. They compile the given source text into Java bytecode and substitutes it for the original method body. If the given source text is null, the substituted body includes only a return statement, which returns zero or null unless the result type is void.

In the source text given to setBody(), the identifiers starting with $ have special meaning

Note that $_ is not available.

Substituting source text for an existing expression

Javassist allows modifying only an expression included in a method body. javassist.expr.ExprEditor is a class for replacing an expression in a method body. The users can define a subclass of ExprEditor to specify how an expression is modified.

To run an ExprEditor object, the users must call instrument() in CtMethod or CtClass. For example,

searches the method body represented by cm and replaces all calls to move() in class Point with a block:

so that the first parameter to move() is always 0. Note that the substituted code is not an expression but a statement or a block.

The method instrument() searches a method body. If it finds an expression such as a method call, field access, and object creation, then it calls edit() on the given ExprEditor object. The parameter to edit() is an object representing the found expression. The edit() method can inspect and replace the expression through that object.

Calling replace() on the parameter to edit() substitutes the given statement or block for the expression. If the given block is an empty block, that is, if replace("{}") 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 replace():

whichever the expression is either a method call, field access, object creation, or others. The second statement could be:

if the expression is read access, or

if the expression is write access.

Local variables available in the target expression is also available in the source text passed to replace() if the method searched by instrument() was compiled with the -g option (the class file includes a local variable attribute).

javassist.expr.MethodCall

A MethodCall object represents a method call. The method replace() in MethodCall 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 $ have special meaning as in the source text passed to insertBefore().

Here the method call means the one represented by the MethodCall object.

The other identifiers such as $w, $args and $$ are also available.

Unless the result type of the method call is void, a value must be assigned to $_ in the source text and the type of $_ is the result type. If the result type is void, the type of $_ is Object and the value assigned to $_ is ignored.

$proceed is not a String value but special syntax. It must be followed by an argument list surrounded by parentheses ( ).

javassist.expr.ConstructorCall

A ConstructorCall object represents a constructor call such as this() and super included in a constructor body. The method replace() in ConstructorCall substitutes a statement or a block for the constructor call. It receives source text representing the substituted statement or block, in which the identifiers starting with $ have special meaning as in the source text passed to insertBefore().

Here the constructor call means the one represented by the ConstructorCall object.

The other identifiers such as $w, $args and $$ are also available.

Since any constructor must call either a constructor of the super class or another constructor of the same class, the substituted statement must include a constructor call, normally a call to $proceed().

$proceed is not a String value but special syntax. It must be followed by an argument list surrounded by parentheses ( ).

javassist.expr.FieldAccess

A FieldAccess object represents field access. The method edit() in ExprEditor receives this object if field access is found. The method replace() in FieldAccess receives source text representing the substitued statement or block for the field access.

In the source text, the identifiers starting with $ have special meaning:

The other identifiers such as $w, $args and $$ are also available.

If the expression is read access, a value must be assigned to $_ in the source text. The type of $_ is the type of the field.

javassist.expr.NewExpr

A NewExpr object represents object creation with the new operator (not including array creation). The method edit() in ExprEditor receives this object if object creation is found. The method replace() in NewExpr receives source text representing the substitued statement or block for the object creation.

In the source text, the identifiers starting with $ have special meaning:

The other identifiers such as $w, $args and $$ are also available.

javassist.expr.NewArray

A NewArray object represents array creation with the new operator. The method edit() in ExprEditor receives this object if array creation is found. The method replace() in NewArray receives source text representing the substitued statement or block for the array creation.

In the source text, the identifiers starting with $ have special meaning:

The other identifiers such as $w, $args and $$ are also available.

For example, if the array creation is the following expression,

then the value of $1 and $2 are 3 and 4, respectively. $3 is not available.

If the array creation is the following expression,

then the value of $1 is 3 but $2 is not available.

javassist.expr.Instanceof

A Instanceof object represents an instanceof expression. The method edit() in ExprEditor receives this object if an instanceof expression is found. The method replace() in Instanceof receives source text representing the substitued statement or block for the expression.

In the source text, the identifiers starting with $ have special meaning:

The other identifiers such as $w, $args and $$ are also available.

javassist.expr.Cast

A Cast object represents an expression for explicit type casting. The method edit() in ExprEditor receives this object if explicit type casting is found. The method replace() in Cast receives source text representing the substitued statement or block for the expression.

In the source text, the identifiers starting with $ have special meaning:

The other identifiers such as $w, $args and $$ are also available.

javassist.expr.Handler

A Handler object represents a catch clause of try-catch statement. The method edit() in ExprEditor receives this object if a catch is found. The method insertBefore() in Handler compiles the received source text and inserts it at the beginning of the catch clause.

In the source text, the identifiers starting with $ have meaning:

If a new exception object is assigned to $1, it is passed to the original catch clause as the caught exception.


4.3 Adding a new method or field

Adding a method

Javassist allows the users to create a new method and constructor from scratch. CtNewMethod and CtNewConstructor provide several factory methods, which are static methods for creating CtMethod or CtConstructor objects. Especially, make() creates a CtMethod or CtConstructor object from the given source text.

For example, this program:

adds a public method xmove() to class Point. In this example, x is a int field in the class Point.

The source text passed to make() can include the identifiers starting with $ except $_ as in setBody(). It can also include $proceed if the target object and the target method name are also given to make(). For example,

this program creates a method ymove() defined below:

Note that $proceed has been replaced with this.move.

Javassist provides another way to add a new method. You can first create an abstract method and later give it a method body:

Since Javassist makes a class abstract if an abstract method is added to the class, you have to explicitly change the class back to a non-abstract one after calling setBody().

Mutual recursive methods

Javassist cannot compile a method if it calls another method that has not been added to a class. (Javassist can compile a method that calls itself recursively.) To add mutual recursive methods to a class, you need a trick shown below. Suppose that you want to add methods m() and n() to a class represented by cc:

You must first make two abstract methods and add them to the class. Then you can give the method bodies to these methods even if the method bodies include method calls to each other. Finally you must change the class to a not-abstract class since addMethod() automatically changes a class into an abstract one if an abstract method is added.

Adding a field

Javassist also allows the users to create a new field.

This program adds a field named z to class Point.

If the initial value of the added field must be specified, the program shown above must be modified into:

Now, the method addField() 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 (;).

Furthermore, the above code can be rewritten into the following simple code:

Removing a member

To remove a field or a method, call removeField() or removeMethod() in CtClass. A CtConstructor can be removed by removeConstructor() in CtClass.


4.4 Annotations

CtClass, CtMethod, CtField and CtConstructor provides a convenient method getAnnotations() for reading annotations. It returns an annotation-type object.

For example, suppose the following annotation:

This annotation is used as the following:

Then, the value of the annotation can be obtained by getAnnotations(). It returns an array containing annotation-type objects.

This code snippet should print:

Since the annoation of Point is only @Author, the length of the array all is one and all[0] is an Author object. The member values of the annotation can be obtained by calling name() and year() on the Author object.

To use getAnnotations(), annotation types such as Author must be included in the current class path. They must be also accessible from a ClassPool object. If the class file of an annotation type is not found, Javassist cannot obtain the default values of the members of that annotation type.


4.5 Runtime support classes

In most cases, a class modified by Javassist does not require Javassist to run. However, some kinds of bytecode generated by the Javassist compiler need runtime support classes, which are in the javassist.runtime package (for details, please read the API reference of that package). Note that the javassist.runtime package is the only package that classes modified by Javassist may need for running. The other Javassist classes are never used at runtime of the modified classes.


4.6 Import

All the class names in source code must be fully qualified (they must include package names). However, the java.lang package is an exception; for example, the Javassist compiler can resolve Object as well as java.lang.Object.

To tell the compiler to search other packages when resolving a class name, call importPackage() in ClassPool. For example,

The seconde line instructs the compiler to import the java.awt package. Thus, the third line will not throw an exception. The compiler can recognize Point as java.awt.Point.

Note that importPackage() does not affect the get() method in ClassPool. Only the compiler considers the imported packages. The parameter to get() must be always a fully qualified name.


4.7 Limitations

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:

  • The new syntax introduced by J2SE 5.0 (including enums and generics) has not been supported. Annotations are supported only by the low level API of Javassist. See the javassist.bytecode.annotation package.

  • Array initializers, a comma-separated list of expressions enclosed by braces { and }, are not available unless the array dimension is one.

  • Inner classes or anonymous classes are not supported.

  • Labeled continue and break statements are not supported.

  • 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.

    For example,

    If the compiled expression is x.foo(new C()), where x is an instance of X, the compiler may produce a call to foo(A) although the compiler can correctly compile foo((B)new C()).

  • The users are recommended to use # as the separator between a class name and a static method or field name. For example, in regular Java,

    calls a method getName() on the object indicated by the static field intType in javassist.CtClass. In Javassist, the users can write the expression shown above but they are recommended to write:

    so that the compiler can quickly parse the expression.


    Previous page    Next page


    Java(TM) is a trademark of Sun Microsystems, Inc.
    Copyright (C) 2000-2007 by Shigeru Chiba, All rights reserved. javassist-3.12.1.ga/License.html0000644000175000017500000006231211361244304016403 0ustar twernertwerner Javassist License
    MOZILLA PUBLIC LICENSE
    Version 1.1


    1. Definitions.

    2. Source Code License.


    3. Distribution Obligations.

    4. Inability to Comply Due to Statute or Regulation. 5. Application of this License. 6. Versions of the License. 7. DISCLAIMER OF WARRANTY. 8. TERMINATION. 9. LIMITATION OF LIABILITY. 10. U.S. GOVERNMENT END USERS. 11. MISCELLANEOUS. 12. RESPONSIBILITY FOR CLAIMS. 13. MULTIPLE-LICENSED CODE.


    EXHIBIT A -Mozilla Public License.

    javassist-3.12.1.ga/sample/0000755000175000017500000000000011637463176015430 5ustar twernertwernerjavassist-3.12.1.ga/sample/evolve/0000755000175000017500000000000011637463162016723 5ustar twernertwernerjavassist-3.12.1.ga/sample/evolve/start.html0000644000175000017500000000123310256751717020747 0ustar twernertwerner

    Instructions

    1. Compile sample/evolve/*.java.

    2. change the current directory to sample/evolve
    and compile there sample/evolve/WebPage.java
    (i.e. compile sample/evolve/sample/evolve/WebPage.java).

    The two versions of WebPage.class are used
    for changing the contents of WebPage.class during the demo.

    3. Run the server on the local host (where your web browser is running):

    4. Click below:

    javassist-3.12.1.ga/sample/evolve/sample/0000755000175000017500000000000011637463156020207 5ustar twernertwernerjavassist-3.12.1.ga/sample/evolve/sample/evolve/0000755000175000017500000000000011637463157021510 5ustar twernertwernerjavassist-3.12.1.ga/sample/evolve/sample/evolve/WebPage.java0000644000175000017500000000106010256751717023660 0ustar twernertwernerpackage sample.evolve; import java.io.*; import java.util.*; /** * Updatable class. DemoServer instantiates this class and calls * show() on the created object. */ public class WebPage { public void show(OutputStreamWriter out) throws IOException { out.write("

    Current Time:

    "); Calendar c = new GregorianCalendar(); out.write("

    "); out.write(c.getTime().toString()); out.write("


    "); out.write("

    Return to the home page."); } } javassist-3.12.1.ga/sample/evolve/DemoServer.java0000644000175000017500000000561710361120146021633 0ustar twernertwernerpackage sample.evolve; import javassist.tools.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 sample/evolve/WebPage.class * or sample/evolve/sample/evolve/WebPage.class. */ 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 "); } 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; } } } javassist-3.12.1.ga/sample/evolve/demo.html0000644000175000017500000000336210256751717020543 0ustar twernertwerner

    Class Evolution

    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.

    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.

    Run WebPage.show()

    The web server creates a new WebPage object and calls show() 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.

    Change WebPage.class

    The web server overwrites class file WebPage.class on the local disk. Then it signals that WebPage.class must be reloaded into the JVM. If you run WebPage.show() again, you will see a different page on your browser.

    Source files

    Web server: DemoServer.java

    WebPage: WebPage.java and another WebPage.java

    Class loader: DemoLoader.java, Evolution.java, and VersionManager.java. javassist-3.12.1.ga/sample/evolve/WebPage.java0000644000175000017500000000065510256751717021110 0ustar twernertwernerpackage sample.evolve; import java.io.*; import java.util.*; /** * Updatable class. DemoServer instantiates this class and calls * show() on the created object. */ public class WebPage { public void show(OutputStreamWriter out) throws IOException { Calendar c = new GregorianCalendar(); out.write(c.getTime().toString()); out.write("

    Return to the home page."); } } javassist-3.12.1.ga/sample/evolve/update.html0000644000175000017500000000015307651243732021072 0ustar twernertwerner

    WebPage.class has been changed.

    Return to the home page. javassist-3.12.1.ga/sample/evolve/VersionManager.java0000644000175000017500000000543410361120146022475 0ustar twernertwernerpackage sample.evolve; import java.util.Hashtable; import java.lang.reflect.*; /** * 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 " 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"); } } javassist-3.12.1.ga/sample/evolve/Evolution.java0000644000175000017500000001527610361120146021546 0ustar twernertwernerpackage sample.evolve; import javassist.*; /** * 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 onLoad(ClassPool _pool, String classname) throws NotFoundException, CannotCompileException { onLoadUpdatable(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 onLoadUpdatable(String classname) throws NotFoundException, CannotCompileException { // if the class is a concrete class, // classname is $$. 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 + 2)); } 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); } } javassist-3.12.1.ga/sample/evolve/DemoLoader.java0000644000175000017500000000233610241707042021571 0ustar twernertwernerpackage sample.evolve; import javassist.*; /** * 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 * * Then DemoLoader launches sample.evolve.DemoServer with . * 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 DemoLoader 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(); Loader cl = new Loader(); cl.addTranslator(cp, translator); translator.makeUpdatable("sample.evolve.WebPage"); cl.run("sample.evolve.DemoServer", args); } } javassist-3.12.1.ga/sample/evolve/CannotUpdateException.java0000644000175000017500000000045507651243732024036 0ustar twernertwernerpackage 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()); } } javassist-3.12.1.ga/sample/evolve/Sample.java0000644000175000017500000000037007651243732021007 0ustar twernertwernerpackage 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); } } javassist-3.12.1.ga/sample/evolve/CannotCreateException.java0000644000175000017500000000046507651243732024020 0ustar twernertwernerpackage 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()); } } javassist-3.12.1.ga/sample/reflect/0000755000175000017500000000000011637463166017053 5ustar twernertwernerjavassist-3.12.1.ga/sample/reflect/Main.java0000644000175000017500000000211110361120146020552 0ustar twernertwernerpackage sample.reflect; import javassist.tools.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.tools.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.tools.reflect.ClassMetaobject"); cl.run("sample.reflect.Person", args); } } javassist-3.12.1.ga/sample/reflect/VerboseMetaobj.java0000644000175000017500000000161010361120146022600 0ustar twernertwernerpackage sample.reflect; import javassist.tools.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); } } javassist-3.12.1.ga/sample/reflect/Person.java0000644000175000017500000000237510361120146021150 0ustar twernertwerner/* * A base-level class controlled by VerboseMetaobj. */ package sample.reflect; import javassist.tools.reflect.Metalevel; import javassist.tools.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; 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)); } } javassist-3.12.1.ga/sample/vector/0000755000175000017500000000000011637463165016730 5ustar twernertwernerjavassist-3.12.1.ga/sample/vector/Test.j0000644000175000017500000000202610330134637020006 0ustar twernertwerner/* A sample program using sample.vector.VectorAssistant and the sample.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 sample.preproc.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)); } } javassist-3.12.1.ga/sample/vector/Sample2.java0000644000175000017500000000043707651243732021077 0ustar twernertwernerpackage 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); } } javassist-3.12.1.ga/sample/vector/VectorAssistant.java0000644000175000017500000000752110330134637022720 0ustar twernertwernerpackage sample.vector; import java.io.IOException; import javassist.*; import sample.preproc.Assistant; /** * This is a Javassist program which produce a new class representing * vectors of a given type. For example, * *
      import java.util.Vector by sample.vector.VectorAssistant(int)
    * *

    requests the Javassist preprocessor to substitute the following * lines for the original import declaration: * *

       * import java.util.Vector;
       * import sample.vector.intVector;
       * 
    * *

    The Javassist preprocessor calls VectorAssistant.assist() * and produces class intVector equivalent to: * *

       * 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();
       *   }
       * }
       * 
    * *

    VectorAssistant.assist() uses * sample.vector.Sample and sample.vector.Sample2 * as a template to produce the methods add() and * at(). */ public class VectorAssistant implements Assistant { public final String packageName = "sample.vector."; /** * Calls makeSubclass() and produces a new vector class. * This method is called by a sample.preproc.Compiler. * * @see sample.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)); vec.writeFile(); 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); vec.writeFile(); return vec; } private String makeClassName(CtClass type) { return packageName + type.getSimpleName() + "Vector"; } } javassist-3.12.1.ga/sample/vector/Sample.java0000644000175000017500000000033207651243732021007 0ustar twernertwernerpackage 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 { } javassist-3.12.1.ga/sample/rmi/0000755000175000017500000000000011637463174016215 5ustar twernertwernerjavassist-3.12.1.ga/sample/rmi/start.html0000644000175000017500000000056510361120146020224 0ustar twernertwerner

    Instructions

    1. Run the server on the local host (where your web browser is running):

      % java sample.rmi.Counter 5001

    2. Click below:

    Start!

    If you don't want to use a web browser, do as follows:

      % java javassist.tools.web.Viewer localhost 5001 sample.rmi.CountApplet
    javassist-3.12.1.ga/sample/rmi/Counter.java0000644000175000017500000000131710361120146020457 0ustar twernertwernerpackage sample.rmi; import javassist.tools.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 "); } } javassist-3.12.1.ga/sample/rmi/CountApplet.java0000644000175000017500000000376610361120146021310 0ustar twernertwernerpackage sample.rmi; import java.applet.*; import java.awt.*; import java.awt.event.*; import javassist.tools.rmi.ObjectImporter; import javassist.tools.rmi.ObjectNotFoundException; import javassist.tools.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); } } javassist-3.12.1.ga/sample/rmi/AlertDialog.java0000644000175000017500000000113607651243732021245 0ustar twernertwernerpackage 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); } } javassist-3.12.1.ga/sample/rmi/inside.gif0000644000175000017500000001327607651243732020165 0ustar twernertwernerGIF89a ÷ÿ3fÿ€€ÿ™€€€ÀÀÀÌÿÌýýýÿÿÿ!ù, @ÿH° Áƒ*\Ȱ¡Ã‡#JœH±¢Å‹3jÜȱ£Ç CŠI²¤É“¨\ɲ¥Ë—0cÊœI³¦Í›8sêÜɳ'L”@ƒ J¤Ï£H“*]Ê´©Ó–E£JJ5jMXWf «W•Y„mù¬×°cÍŠízVíÓ·pã­J·®Ý»åêÝË·¯_›x ø¯áÈ#%̸±c Š#Kž<ã˘3kÞ̹³çÏ C‹MºôÇ)»4ͺµë×°]+¶»¶íÛ¸AØÍ{7꺪[æN¼xëÞ½ùÒ 3VÌÏD—Þœ:ôêѳW7Î4rÞ¿éÿgIÚëõËÚ5?ßžÞzgóçÝ·ŸÞ]ô÷ÝÊ/ÖßÏøýáU5ÞJýh kÿågÑ 6¨Û}R5 JVháe îµÜ…ø_„SM8@hô™v|:˜¡^¦è¢q‚hÕ„/Öã} Vdàvµñ¸>^£ŒD‰h㑹­(W‹I]‰¬ùâD e$’XƦd\L2èd–ZBXåPW‚yh¦©æšlÚ¸%\]xâYš`çxæ©§›bŽT™Ã±)hš :衈Šè¢ˆÖöæ[qÖf§”Vj©¥ Tšé¥œvê馞†*ªv:Ú§Ÿ(ú£¬¶êê«°yjÚ£OEZZ£æªë®¼öšk³žŠªI>Íiì±Æ®¦ž±¡Åêì³ÐºÊ­NÙz«¯Øf«­¯ÀûݰÑX_´ä–«f’8j¨_}wnë–Š•à’¤ª™ø>ø]Žåë/z ðÀl0ÁÆÖ{ѽM L翦ËÿâGÿUlñÅg¬ñÆwl1V‡,òÀ^)Œˆ0yÌqÀ òË0Ç|1¿&×ü y5òÎ$cÅÊ|±l0W@“ÍHßuÏ;Ÿ%’Ñp 0ÔT–ôÕÃêÌôÈ%ôÄÅU-Öd—½Ö[‹\ÖL¥A)ç—ñF­ØÕšm7E8”7yïóßõ­·@>Î÷á„uÈ[†âtÚù8vóQp|íYçöfsÓÍÔÝ ‡~òS`×¹çJ‰®ºB>óí÷A¯žøìˆ×._2ç®{î¨O¼úïf÷.üðo<ÖÄ'¯ü\Ç7oóòÐGoµóÔƒ+ýõØCUýöÜwï‹ý÷à‡/þøä—ÏPöè_oþúâÍ4ÖÚDÔVúôsÉþý3Ö¯ïø÷Oæþ¤›ÿ˜ªjL ½È@”Y«t`J à ˜¯Ù¬K4(rÍæ CAûK|tŽ”°´Á.u|ÏäÖó0€­rô)a@/ŠËqO’\ä|T¢‡µpY;t!|~($‰-ÿ)…3ÌW ÃÂ$"é„Iy §ôA&ÞÐ3™‹¡åä³ÅëLîrî)ƒÄ)fi‰àk¢©¸/u-hfBã÷ÔGÑ~oÜÑ{œƒž=ú‘)’£÷èXÇ"¶Ñw:ªÜ ‰¡*¦ñŠŒ¬Ñ™—Ç -r‚ì!sc®N2Š;“ô‰sƒ§KÍ ^ÚÂ(9GHÂæYŠZS, ËZ M¶”å f «ãÜNe| ®°*L“R J¦¦–y©b[ÝòrÖw/"zÆ“ØÌ&«JÊžŒò3ÃD¥8ÇÉ«hzð[A–:Õé˜jÆF›ð,lºÉ“o’&œäÌç»Ì9-VÒ•û Ч´ DÏØ“;yÒg§òÆ!lN˜É$÷6IT'­è~š¶íMuÍÔ¦Qþ²xÙJWÊR•„£=ëh˺F$±µ”7#uØMwºSš)Uh“éC?ú¿æÔñkà_~ʽ¥ µ`4•ŠT¥&†©X LŸÚ–¤”44VÍIVeÔ:ĵîu†;«VÍJ¸Ø¹ŽvËV¹*ÃeÅ-‹]¬!·E¾ró¤aÀXAt"…-ei_Õrû’ÁIp¬»QKòµÄzæ¢ésl€ Õ¶jõ£%;ká&K¬¾ðô´,e¬K4ËZˆ¨öµ†i­lÏÛÚºÿq¶¸-ˆmw‹ÇÜú¼ nÝ~KÜâ÷¸ÈM®r—ËÜæ:×dÂ. Ÿ‹UéZW”Ô­®LÒÂÝí&õºÌ®v½ ¹–×-h!ÚüÌ»ö¦—+ðŸzçkø‚×/âeªM¸›Ô÷¡÷»íM¯VÜÂþ˜½÷Åo~˜àÓdÁ v°„ aNøÂ,©°…1Œa Ãöpÿ@|a˜ÄΨe#A€®˜A¬ä‹ÔâÍøB1Näh.I¢ºVT¢Û#$xÜŸú¸B˜=Z09“×y1ƒ|Ý!‘ÅèOMºˆ?" ‡Ä'…Q„[~2{2hÈÅÞ¶_$ó^Ç F.fùÇ&Užè•9ˆ¹ Y:qË3“õêfIv¸:Ö “Žüb W/¤76ÝŸ›ÿ¢âD߯ÐÔC´£çµèÏ-yÒõ´ó$iSòˆ2îô~4Ý<:âµ9¨î«ªQÝg/›‡Ð|útoÑ,êþúx¦n²÷ºç]‡‘ÏT–50C]ëU¢³|j´fy”]èJ/¥ÑÅîç±ÉÇéhKÛ̈¤µµ=MóU{ÛÞqöt nEO{|ß.wh’|hk‹˜$žÿHg;žÛ†6VwqØ==rY…¬¢Dð@Æ9ÈsÖ÷kø]w7ôÆ·6^ºÞ†?øÒÿ^qÄ7qŠsNÜQÄx“,»ñßuÜ6mÕ,>‡‹4‡Œ,ùêNš”Ûü\”6Jýí¥c)´R %èÁðÿÖÀŠ—¸TS.iI(E-ý–IÇ%Ò5O“‘ØÃi—®¼òshÊëÑCtÑGC®˜]éfçåÙ×ÎöZž½éi—zÜcuíäœy"ÄÁg®œ‰Ìg*™›úû3L¾‹ŠŸÒì 5Ç^ó›;ž\Õy­D~Ow¥S—Ç<á3uùÌo¾˜i7;à=_κƒÇÛ®ˆ¤ºN{r"þ32gÌ:w_XƒT“Ùi½ðezüÜ]"¶Ñ{í—Ï-€ïÞ7¨Ï7q†ÏzOcÔýÕ_äå“U”A)”å…?™HNù^rÕeÑU_ÁˆF™aHY%‹`öõ_` •C–]Ñ^îE_Y¹†[!û5–e •&–q9—[ñ–M™–j¹–áÑ–wÙ”þµ6~9”: —Y_<‰—+¡—2‚˜a¥˜ ˜Vå˜l ™ ÿ$™B™•i™¿™ ¤™›É™á噚¡)š©Ašdší„šû£š§ÉšõãšO›ô#›„A›±i›‚›µ©›…Á›èc‚Xcã‘¶‘chœ­AœÁ¡œ±œùçœË¹a(¦ÈgÓùa¨€²ç¯}ÈÖÚi*ñM°¦B“–{¢“k¹è P²ž+§ŒØ…uGŸv…E"lÝVžÕ¹gì‘%úé!öéM”×G_†WmÆgªDñ‘ p¾9x9kæBóV¡ÚgêZO Ê,ÚjRÖE…äž¡cj^$p(ª9bÖdQVgköˆB'y€–œW¡ºökyú©Bª=š9`ƒ©Ñ©©'¨3W¤-ôC»êd¼ê«’šjajo÷7lj«Ä©­ä©ñ¹cLÚž¦ŠQ¨*žªŠ<|ªœ´:¥Èj¨ÿIm×jœÙžÛÊ­Š}Å9®ä¥X«èš¬Ý*ÌŠ¬á íº©ïŠoçZ¯qz¨–v§òŠ« õ­'­b5­ø2B§¬ÿ¯¶:¯¡G°zX©¸˜Cv¶£R­WS¦ç°!'ðFg¥ Lf±eV®š¯újRüúlûp#Ëk/»¡"[o °ï)°ȱ Ñ <6¬3ë³ûf³+гiº²ã–©UBK¤ «©:ËŠqK›¥Dk¦«dþZ@ ‰SK¦U»±WÛnÿ- ‚ͦ°õˆ²úú´ Á![{©]»§M{ Žg!j«Ò¶þ‚±I£±¤Q~T7jaÛoHë±0ø¶Œúµ×ä·õgnźsƒû66Zªf+gqë®zŠûI¸u˜µÝ±N •J H·†»ªˆû*SGKs§ºR׺o§º« »n»ÛTuFržkxb,íç™VºÖZ¹¢á,S7KKw¼Pw«›ºš[|0„«€Ûg)gÁ õ}a2¹ýˆ¶«-¹d¼·ôºÈu©Ë¼µy;y¹«}ÓÛL™Ö;*ïg»÷zŒÍY™›¿ãù¼Ç'®¤Ä+†÷y…x,z£gÀ¬)œ‡Àÿ¡‚½r¼{­ù;ÁèKŽÆú¸¦Á~Ü) ÇL„çÁ‚§À¡çÁåäÀ¸Á{›zý9Áún·{uÇz-ÚòwÜw›Â8üÁ¼-&œ®äé­²ŸÁ™[ÁêŠÚ*Lñ»ÄœÒÚ¡·HC¼‡,‰KÄ÷¿ÐÛ±±Ñ¾LÌN¼¯õK=S<Æq‘e‘{V,|ΛÅ;»Å]üÆ”òÅîj²t1Ƽ7¿¦i|só”âÁÅpì~FlÇ»GµÂÛ·{ŒMÙ›¾9Ê®­!ȃÜur<ǧw²÷k‹-`çÇ÷ È£!É“œO•lÉ? ¯ÜË›ü· êɺ¾,Ê£ÌÃ¥ ÆTtŒÊ™Ì ‹Ìu›ë7ˤÜÈ·Œ¯¹œ"s‹dœ›—Iœu² ÌM\Ë·ª½YÌõÚË¡Í ÇóÛÉa¼¬©Œ®Ö|²L컎¸ˆŽÍݼ°ß<®ÿál6€´¼'Ç3'Ãl¿ª‘²¶|Ä ÉÚiÏ:ÛKÍMb¥³šÌ-7¶'ÐMèŽ)œÈ 8SõΡ¯­‚ƒÚÎZû‚­ÐÈÐΙÑMÑQŒ¸$âQb Ò%8ƒñøÒ§EÒ<㌠51íQ0}„lL9ÝÓ1½e‡4i47=4>]„;=G½Ô7¨UЍ6B½˜P…8ÍÔ5˜ÔaÕZ3S#ÓPmÒxaSTÝ2[MƒXmb4ÝV@=Ók=Öaõ…ù‘‰Èo"V%×s]’®È4w};r8‡ ”uýÐEØPÕ’’Ù×n-Öy†C“®ÉØ%íØÙj‘­Ø¾é:mÝØ–í{ˆ=5’½Ù°ÖhÙ¡M2>IÚ QØM¸‡¤ƒi÷ÈÚAÙýQÔxc³MÛxcÛaí¶Û¼m‰ÝØGÜû8ÜYhÚ3…•0¡uÄ¥?©Ü ñÙIXÜ•-Ð &ÈÒÊÿC’Ø5¢…ަXVßU-¾ýÛ²‰böª>ô«/$¬”*eìÊú#’ •ŠkÅVë¸ßüŠÖ Û†ùÔ±£šà&*ªU*ªHª‹Æ'“úmÞf¨ŠÜèý];éýØ1±ÞΟ·¨e¶¨¡>Ÿl¶ŸšCdÒ-6¾ƒ4ÅŽlŠ1.Z¤8à´u®]0ÿ'ßa–¤À*ÄŽºN#€Ž™ yáC½L˜Œø›Ô?L·3´âGùäPÎL^äJeåW¾YnÁŒÅ×p;çØXŽiTN5b6n;(ÛÐÚä½’/.Ô¦àæíYàg}Z.I×Õê˜8à{¾}WŽZî‘.>è‡c8oeV€SÞVމÞS¼ÈåEbé—ÞR‹®éfœÑê‡-ê£Nê‡nêЃꩮêßÍêäê«ëo.ëËCëµnëɃ륥ë»Îë äë’=ìx,ìÃNì!aìÇŽìÓ¨ìÅÈìïèìžíÑ.í,Níaí¨ƒíÙ®íÓÍíàîâ>îä^îæ~îè.;javassist-3.12.1.ga/sample/rmi/webdemo.html0000644000175000017500000001534710361120146020515 0ustar twernertwerner

    Remote Method Invocation

    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 javassist.tools.rmi 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.

    1. Sample applet

    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.

    However, the program of the applet does not need to directly handle a socket. The ObjectImporter provided by Javassist deals with all the awkward programming. Look at the lines shown with red:

    Figure 1: Applet

    import javassist.tools.rmi.ObjectImporter;
    
    public class CountApplet extends Applet implements ActionListener {
      private Font font;
      private ObjectImporter importer;
      private Counter counter;
      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);
        importer = new ObjectImporter(this);
        dialog = new AlertDialog();
        message = "???";
      }
    
      public void start() {
        String counterName = getParameter("name");
        counter = (Counter)importer.getObject(counterName);
        message = Integer.toString(counter.get());
      }
    
      /* The method called when the button is pressed.
      */
      public void actionPerformed(ActionEvent e) {
        message = Integer.toString(counter.increase());
        repaint();
      }
    
      public void paint(Graphics g) {
        g.setFont(font);
        g.drawRect(50, 50, 100, 100);
        g.setColor(Color.blue);
        g.drawString(message, 60, 120);
      }
    }
    

    A Counter object running on a remote host maintains the counter number. To access this object, the applet first calls getObject() on an ObjectImporter 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, counter.get() and counter.increase() call methods on the remote object.

    The definition of the Counter class is also straightforward:

    Figure 2: Remote object

    public class Counter {
      private int count = 0;
    
      public int get() {
        return count;
      }
    
      public int increase() {
        count += 1;
        return count;
      }
    }
    

    Note that the javassist.tools.rmi package does not require the Counter class to be an interface unlike the Java RMI, with which Counter must be an interface and it must be implemented by another class.

    To make the Counter object available from the applet, it must be registered with the web server. A AppletServer object is a simple webserver that can distribute .html files and .class files (Java applets).

    Figure 3: Server-side program

    public class MyWebServer {
      public static void main(String[] args) throws IOException, CannotCompileException
      {
          AppletServer web = new AppletServer(args[0]);
          web.exportObject("counter", new Counter());
          web.run();
      }
    }
    

    The exportObject() method registers a remote object with the AppletServer object. In the example above, a Counter object is registered. The applet can access the object with the name "counter". The web server starts the service if the run() method is called.


    2. Features

    The remote method invocation mechanism provided by Javassist has the following features:
    • Regular Java syntax:
      The applet can call a method on a remote object with regular Java syntax.

    • No special naming convention:
      The applet can use the same class name as the server-side program. The reference object to a remote Foo object is also represented by the class Foo. Unlike other similar systems, it is not represented by a different class such as ProxyFoo or an interface implemented by Foo.

    • No extra compiler:
      All the programs, both the applet and the server-side program, are compiled by the regular Java compiler. No external compiler is needed.

    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 javassist.tools.rmi package does not require the programmer to follow that programming convention. It is suitable for writing simple distributed programs like applets.


    3. Inside of the system

    A key idea of the implementation is that the applet and the server-side program must use different versions of the class Counter. The Counter object in the applet must work as a proxy object, which transfers the method invocations to the Counter object in the server-side program.

    With other systems like the Java RMI, the class of this proxy object is produced by a special compiler such as rmic. It must be manually maintained by the programmer.

    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 Counter class, which is the class of an exported object, then the web server transfers the version of Counter that Javassist generates as a proxy class.


    javassist-3.12.1.ga/sample/hotswap/0000755000175000017500000000000011637463170017107 5ustar twernertwernerjavassist-3.12.1.ga/sample/hotswap/logging/0000755000175000017500000000000011637463170020535 5ustar twernertwernerjavassist-3.12.1.ga/sample/hotswap/logging/HelloWorld.java0000644000175000017500000000023410256041264023442 0ustar twernertwernerpublic class HelloWorld { public void print() { System.out.println("** HelloWorld.print()"); System.out.println("hello world"); } } javassist-3.12.1.ga/sample/hotswap/HelloWorld.java0000644000175000017500000000014710256041264022017 0ustar twernertwernerpublic class HelloWorld { public void print() { System.out.println("hello world"); } } javassist-3.12.1.ga/sample/hotswap/Test.java0000644000175000017500000000145210361120146020655 0ustar twernertwernerimport java.io.*; import javassist.util.HotSwapper; public class Test { public static void main(String[] args) throws Exception { HotSwapper hs = new HotSwapper(8000); new HelloWorld().print(); File newfile = new File("logging/HelloWorld.class"); byte[] bytes = new byte[(int)newfile.length()]; new FileInputStream(newfile).read(bytes); System.out.println("** reload a logging version"); hs.reload("HelloWorld", bytes); new HelloWorld().print(); newfile = new File("HelloWorld.class"); bytes = new byte[(int)newfile.length()]; new FileInputStream(newfile).read(bytes); System.out.println("** reload the original version"); hs.reload("HelloWorld", bytes); new HelloWorld().print(); } } javassist-3.12.1.ga/sample/duplicate/0000755000175000017500000000000011637463176017402 5ustar twernertwernerjavassist-3.12.1.ga/sample/duplicate/Viewer.java0000644000175000017500000000331107651243732021477 0ustar twernertwernerpackage 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); } } javassist-3.12.1.ga/sample/duplicate/Main.java0000644000175000017500000000335610361120146021114 0ustar twernertwernerpackage sample.duplicate; /* Runtime metaobject (JDK 1.2 or later only). With the javassist.tools.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.tools.reflect.Loader so that a metaobject would be attached. */ public class Main { public static void main(String[] args) throws Throwable { javassist.tools.reflect.Loader cl = new javassist.tools.reflect.Loader(); cl.makeReflective("sample.duplicate.Ball", "sample.duplicate.DuplicatedObject", "javassist.tools.reflect.ClassMetaobject"); cl.run("sample.duplicate.Viewer", args); } } javassist-3.12.1.ga/sample/duplicate/Ball.java0000644000175000017500000000151407651243732021113 0ustar twernertwernerpackage 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; } } javassist-3.12.1.ga/sample/duplicate/DuplicatedObject.java0000644000175000017500000000174110361120146023431 0ustar twernertwernerpackage sample.duplicate; import javassist.tools.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; } } javassist-3.12.1.ga/sample/preproc/0000755000175000017500000000000011637463200017066 5ustar twernertwernerjavassist-3.12.1.ga/sample/preproc/Assistant.java0000644000175000017500000000377410332624777021726 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package sample.preproc; import javassist.CtClass; import javassist.CannotCompileException; import javassist.ClassPool; /** * This is an interface for objects invoked by the * Javassist preprocessor when the preprocessor encounters an annotated * import declaration. * * @see sample.preproc.Compiler */ public interface Assistant { /** * Is called when the Javassist preprocessor encounters an * import declaration annotated with the "by" keyword. * *

    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: * *

           * public CtClass[] assist(ClassPool cp, String importname, String[] args) {
           *     return new CtClass[] { cp.get(importname) };
           * }
           * 
    * * @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; } javassist-3.12.1.ga/sample/preproc/package.html0000644000175000017500000000055610332624777021366 0ustar twernertwerner A sample preprocessor.

    The preprocessor for running Javassist at compile time. The produced class files are saved on a local disk.

    This package is provided as a sample implementation of the preprocessor using Javassist. All the programs in this package uses only the regular Javassist API; they never call any hidden methods. javassist-3.12.1.ga/sample/preproc/Compiler.java0000644000175000017500000002406310332624777021521 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2005 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package sample.preproc; import java.io.IOException; import java.io.BufferedReader; import java.io.FileReader; import java.io.BufferedWriter; import java.io.FileWriter; import java.util.Vector; import javassist.CannotCompileException; import javassist.CtClass; import javassist.ClassPool; /** * This is a preprocessor for Java source programs using annotated * import declarations. * *

       * import class-name by assistant-name [(arg1, arg2, ...)]
       * 
    * *

    To process this annotation, run this class as follows: * *

       * java sample.preproc.Compiler sample.j
       * 
    * *

    This command produces sample.java, which only includes * regular import declarations. Also, the Javassist program * specified by assistant-name is executed so that it produces * class files under the ./tmpjvst directory. The class * specified by assistant-name must implement * sample.preproc.Assistant. * * @see sample.preproc.Assistant */ public class Compiler { protected BufferedReader input; protected BufferedWriter output; protected ClassPool classPool; /** * Constructs a Compiler 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: * *

           * import class name by assistant (args1, args2, ...);
           * 
    * * @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; } } } javassist-3.12.1.ga/sample/Test.java0000644000175000017500000000224610037256643017206 0ustar twernertwernerpackage 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(); 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); cc.writeFile(); // update the class file System.out.println("g() was added."); } } } javassist-3.12.1.ga/pom.xml0000644000175000017500000001376111404426151015454 0ustar twernertwerner 4.0.0 javassist javassist jar Javassist (JAVA programming ASSISTant) makes Java bytecode manipulation simple. It is a class library for editing bytecodes in Java. 3.12.1.GA Javassist http://www.javassist.org/ JIRA https://jira.jboss.org/jira/browse/JASSIST/ MPL 1.1 http://www.mozilla.org/MPL/MPL-1.1.html LGPL 2.1 http://www.gnu.org/licenses/lgpl-2.1.html scm:svn:http://anonsvn.jboss.org/repos/javassist/tags/rel_3_12_1_ga scm:svn:https://svn.jboss.org/repos/javassist/tags/rel_3_12_1_ga http://fisheye.jboss.org/browse/javassist/tags/rel_3_12_1_ga chiba Shigeru Chiba chiba@acm.org Tokyo Institute Of Technology http://www.csg.is.titech.ac.jp/ project lead 8 adinn Andrew Dinn adinn@redhat.com JBoss http://www.jboss.org/ contributing developer 0 jboss-releases-repository JBoss Releases Repository https://repository.jboss.org/nexus/service/local/staging/deploy/maven2/ jboss-snapshots-repository JBoss Snapshots Repository https://repository.jboss.org/nexus/content/repositories/snapshots/ src/main/ src/test/ org.apache.maven.plugins maven-compiler-plugin 1.4 1.4 org.apache.maven.plugins maven-jar-plugin ${project.build.sourceDirectory}/META-INF/MANIFEST.MF maven-source-plugin 2.0.3 attach-sources jar true junit junit 3.8.1 test javassist-3.12.1.ga/src/0000755000175000017500000000000011637463221014725 5ustar twernertwernerjavassist-3.12.1.ga/src/test/0000755000175000017500000000000011637463212015704 5ustar twernertwernerjavassist-3.12.1.ga/src/test/test/0000755000175000017500000000000011637463212016663 5ustar twernertwernerjavassist-3.12.1.ga/src/test/test/javassist/0000755000175000017500000000000011637463217020677 5ustar twernertwernerjavassist-3.12.1.ga/src/test/test/javassist/bytecode/0000755000175000017500000000000011637463217022475 5ustar twernertwernerjavassist-3.12.1.ga/src/test/test/javassist/bytecode/analysis/0000755000175000017500000000000011637463221024313 5ustar twernertwernerjavassist-3.12.1.ga/src/test/test/javassist/bytecode/analysis/ErrorFinder.java0000644000175000017500000000377511017303315027400 0ustar twernertwernerpackage test.javassist.bytecode.analysis; import java.io.BufferedReader; import java.io.FileReader; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.bytecode.analysis.Analyzer; /** * Simple testing tool that verifies class files can be analyzed. * * @author Jason T. Greene */ public class ErrorFinder { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); String className = args[0]; if (!className.equals("-file")) { analyzeClass(pool, className); return; } FileReader reader = new FileReader(args[1]); BufferedReader lineReader = new BufferedReader(reader); String line = lineReader.readLine(); while (line != null) { analyzeClass(pool, line); line = lineReader.readLine(); } } private static void analyzeClass(ClassPool pool, String className) { try { CtClass clazz = pool.get(className); CtMethod[] methods = clazz.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) analyzeMethod(clazz, methods[i]); } catch (Throwable e) { System.out.println("FAIL: CLASS: " + className + " " + e.getClass() + ":" + e.getMessage()); } } private static void analyzeMethod(CtClass clazz, CtMethod method) { String methodName = clazz.getName() + "." + method.getName() + method.getSignature(); System.out.println("START: " + methodName); Analyzer analyzer = new Analyzer(); long time = System.currentTimeMillis(); try { analyzer.analyze(clazz, method.getMethodInfo2()); System.out.println("SUCCESS: " + methodName + " - " + (System.currentTimeMillis() - time)); } catch (Exception e) { System.out.println("FAIL: " + methodName + " - " + (e.getMessage() == null ? e.getClass().getName() : e.getMessage())); } } } javassist-3.12.1.ga/src/test/test/javassist/bytecode/analysis/AnalyzerTest.java0000644000175000017500000003377011015721760027610 0ustar twernertwernerpackage test.javassist.bytecode.analysis; import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.bytecode.AccessFlag; import javassist.bytecode.BadBytecode; import javassist.bytecode.Bytecode; import javassist.bytecode.CodeIterator; import javassist.bytecode.MethodInfo; import javassist.bytecode.Opcode; import javassist.bytecode.analysis.Analyzer; import javassist.bytecode.analysis.Frame; import javassist.bytecode.analysis.Type; import junit.framework.TestCase; /** * Tests Analyzer * * @author Jason T. Greene */ public class AnalyzerTest extends TestCase { public void testCommonSupperArray() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(getClass().getName() + "$Dummy"); CtMethod method = clazz.getDeclaredMethod("commonSuperArray"); verifyArrayLoad(clazz, method, "java.lang.Number"); } public void testCommonInterfaceArray() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(getClass().getName() + "$Dummy"); CtMethod method = clazz.getDeclaredMethod("commonInterfaceArray"); verifyArrayLoad(clazz, method, "java.io.Serializable"); } public void testSharedInterfaceAndSuperClass() throws Exception { CtMethod method = ClassPool.getDefault().getMethod( getClass().getName() + "$Dummy", "sharedInterfaceAndSuperClass"); verifyReturn(method, "java.io.Serializable"); method = ClassPool.getDefault().getMethod( getClass().getName() + "$Dummy", "sharedOffsetInterfaceAndSuperClass"); verifyReturn(method, "java.io.Serializable"); method = ClassPool.getDefault().getMethod( getClass().getName() + "$Dummy", "sharedSuperWithSharedInterface"); verifyReturn(method, getClass().getName() + "$Dummy$A"); } public void testArrayDifferentDims() throws Exception { CtMethod method = ClassPool.getDefault().getMethod( getClass().getName() + "$Dummy", "arrayDifferentDimensions1"); verifyReturn(method, "java.lang.Cloneable[]"); method = ClassPool.getDefault().getMethod( getClass().getName() + "$Dummy", "arrayDifferentDimensions2"); verifyReturn(method, "java.lang.Object[][]"); } public void testReusedLocalMerge() throws Exception { CtMethod method = ClassPool.getDefault().getMethod( getClass().getName() + "$Dummy", "reusedLocalMerge"); MethodInfo info = method.getMethodInfo2(); Analyzer analyzer = new Analyzer(); Frame[] frames = analyzer.analyze(method.getDeclaringClass(), info); assertNotNull(frames); int pos = findOpcode(info, Opcode.RETURN); Frame frame = frames[pos]; assertEquals("java.lang.Object", frame.getLocal(2).getCtClass().getName()); } private static int findOpcode(MethodInfo info, int opcode) throws BadBytecode { CodeIterator iter = info.getCodeAttribute().iterator(); // find return int pos = 0; while (iter.hasNext()) { pos = iter.next(); if (iter.byteAt(pos) == opcode) break; } return pos; } private static void verifyReturn(CtMethod method, String expected) throws BadBytecode { MethodInfo info = method.getMethodInfo2(); CodeIterator iter = info.getCodeAttribute().iterator(); // find areturn int pos = 0; while (iter.hasNext()) { pos = iter.next(); if (iter.byteAt(pos) == Opcode.ARETURN) break; } Analyzer analyzer = new Analyzer(); Frame[] frames = analyzer.analyze(method.getDeclaringClass(), info); assertNotNull(frames); Frame frame = frames[pos]; assertEquals(expected, frame.peek().getCtClass().getName()); } private static void verifyArrayLoad(CtClass clazz, CtMethod method, String component) throws BadBytecode { MethodInfo info = method.getMethodInfo2(); CodeIterator iter = info.getCodeAttribute().iterator(); // find aaload int pos = 0; while (iter.hasNext()) { pos = iter.next(); if (iter.byteAt(pos) == Opcode.AALOAD) break; } Analyzer analyzer = new Analyzer(); Frame[] frames = analyzer.analyze(clazz, info); assertNotNull(frames); Frame frame = frames[pos]; assertNotNull(frame); Type type = frame.getStack(frame.getTopIndex() - 1); assertEquals(component + "[]", type.getCtClass().getName()); pos = iter.next(); frame = frames[pos]; assertNotNull(frame); type = frame.getStack(frame.getTopIndex()); assertEquals(component, type.getCtClass().getName()); } private static void addJump(Bytecode code, int opcode, int pos) { int current = code.currentPc(); code.addOpcode(opcode); code.addIndex(pos - current); } public void testDeadCode() throws Exception { CtMethod method = generateDeadCode(ClassPool.getDefault()); Analyzer analyzer = new Analyzer(); Frame[] frames = analyzer.analyze(method.getDeclaringClass(), method.getMethodInfo2()); assertNotNull(frames); assertNull(frames[4]); assertNotNull(frames[5]); verifyReturn(method, "java.lang.String"); } public void testInvalidCode() throws Exception { CtMethod method = generateInvalidCode(ClassPool.getDefault()); Analyzer analyzer = new Analyzer(); try { analyzer.analyze(method.getDeclaringClass(), method.getMethodInfo2()); } catch (BadBytecode e) { return; } fail("Invalid code should have triggered a BadBytecode exception"); } public void testCodeFalloff() throws Exception { CtMethod method = generateCodeFalloff(ClassPool.getDefault()); Analyzer analyzer = new Analyzer(); try { analyzer.analyze(method.getDeclaringClass(), method.getMethodInfo2()); } catch (BadBytecode e) { return; } fail("Code falloff should have triggered a BadBytecode exception"); } public void testJsrMerge() throws Exception { CtMethod method = generateJsrMerge(ClassPool.getDefault()); Analyzer analyzer = new Analyzer(); analyzer.analyze(method.getDeclaringClass(), method.getMethodInfo2()); verifyReturn(method, "java.lang.String"); } public void testJsrMerge2() throws Exception { CtMethod method = generateJsrMerge2(ClassPool.getDefault()); Analyzer analyzer = new Analyzer(); analyzer.analyze(method.getDeclaringClass(), method.getMethodInfo2()); verifyReturn(method, "java.lang.String"); } private CtMethod generateDeadCode(ClassPool pool) throws Exception { CtClass clazz = pool.makeClass(getClass().getName() + "$Generated0"); CtClass stringClass = pool.get("java.lang.String"); CtMethod method = new CtMethod(stringClass, "foo", new CtClass[0], clazz); MethodInfo info = method.getMethodInfo2(); info.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); Bytecode code = new Bytecode(info.getConstPool(), 1, 2); /* 0 */ code.addIconst(1); /* 1 */ addJump(code, Opcode.GOTO, 5); /* 4 */ code.addIconst(0); // DEAD /* 5 */ code.addIconst(1); /* 6 */ code.addInvokestatic(stringClass, "valueOf", stringClass, new CtClass[]{CtClass.intType}); /* 9 */ code.addOpcode(Opcode.ARETURN); info.setCodeAttribute(code.toCodeAttribute()); clazz.addMethod(method); return method; } private CtMethod generateInvalidCode(ClassPool pool) throws Exception { CtClass clazz = pool.makeClass(getClass().getName() + "$Generated4"); CtClass intClass = pool.get("java.lang.Integer"); CtClass stringClass = pool.get("java.lang.String"); CtMethod method = new CtMethod(stringClass, "foo", new CtClass[0], clazz); MethodInfo info = method.getMethodInfo2(); info.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); Bytecode code = new Bytecode(info.getConstPool(), 1, 2); /* 0 */ code.addIconst(1); /* 1 */ code.addInvokestatic(intClass, "valueOf", intClass, new CtClass[]{CtClass.intType}); /* 4 */ code.addOpcode(Opcode.ARETURN); info.setCodeAttribute(code.toCodeAttribute()); clazz.addMethod(method); return method; } private CtMethod generateCodeFalloff(ClassPool pool) throws Exception { CtClass clazz = pool.makeClass(getClass().getName() + "$Generated3"); CtClass stringClass = pool.get("java.lang.String"); CtMethod method = new CtMethod(stringClass, "foo", new CtClass[0], clazz); MethodInfo info = method.getMethodInfo2(); info.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); Bytecode code = new Bytecode(info.getConstPool(), 1, 2); /* 0 */ code.addIconst(1); /* 1 */ code.addInvokestatic(stringClass, "valueOf", stringClass, new CtClass[]{CtClass.intType}); info.setCodeAttribute(code.toCodeAttribute()); clazz.addMethod(method); return method; } private CtMethod generateJsrMerge(ClassPool pool) throws Exception { CtClass clazz = pool.makeClass(getClass().getName() + "$Generated1"); CtClass stringClass = pool.get("java.lang.String"); CtMethod method = new CtMethod(stringClass, "foo", new CtClass[0], clazz); MethodInfo info = method.getMethodInfo2(); info.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); Bytecode code = new Bytecode(info.getConstPool(), 1, 2); /* 0 */ code.addIconst(5); /* 1 */ code.addIstore(0); /* 2 */ addJump(code, Opcode.JSR, 7); /* 5 */ code.addAload(0); /* 6 */ code.addOpcode(Opcode.ARETURN); /* 7 */ code.addAstore(1); /* 8 */ code.addIconst(3); /* 9 */ code.addInvokestatic(stringClass, "valueOf", stringClass, new CtClass[]{CtClass.intType}); /* 12 */ code.addAstore(0); /* 12 */ code.addRet(1); info.setCodeAttribute(code.toCodeAttribute()); clazz.addMethod(method); //System.out.println(clazz.toClass().getMethod("foo", new Class[0]).invoke(null, new Object[0])); return method; } private CtMethod generateJsrMerge2(ClassPool pool) throws Exception { CtClass clazz = pool.makeClass(getClass().getName() + "$Generated2"); CtClass stringClass = pool.get("java.lang.String"); CtMethod method = new CtMethod(stringClass, "foo", new CtClass[0], clazz); MethodInfo info = method.getMethodInfo2(); info.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); Bytecode code = new Bytecode(info.getConstPool(), 1, 2); /* 0 */ addJump(code, Opcode.JSR, 5); /* 3 */ code.addAload(0); /* 4 */ code.addOpcode(Opcode.ARETURN); /* 5 */ code.addAstore(1); /* 6 */ code.addIconst(4); /* 7 */ code.addInvokestatic(stringClass, "valueOf", stringClass, new CtClass[]{CtClass.intType}); /* 10 */ code.addAstore(0); /* 11 */ code.addRet(1); info.setCodeAttribute(code.toCodeAttribute()); clazz.addMethod(method); return method; } public static class Dummy { public Serializable commonSuperArray(int x) { Number[] n; if (x > 5) { n = new Long[10]; } else { n = new Double[5]; } return n[x]; } public Serializable commonInterfaceArray(int x) { Serializable[] n; if (x > 5) { n = new Long[10]; } else if (x > 3) { n = new Double[5]; } else { n = new String[3]; } return n[x]; } public static class A {}; public static class B1 extends A implements Serializable {}; public static class B2 extends A implements Serializable {}; public static class A2 implements Serializable, Cloneable {}; public static class A3 implements Serializable, Cloneable {}; public static class B3 extends A {}; public static class C31 extends B3 implements Serializable {}; public void dummy(Serializable s) {} public Object sharedInterfaceAndSuperClass(int x) { Serializable s; if (x > 5) { s = new B1(); } else { s = new B2(); } dummy(s); return s; } public A sharedSuperWithSharedInterface(int x) { A a; if (x > 5) { a = new B1(); } else if (x > 3) { a = new B2(); } else { a = new C31(); } return a; } public void reusedLocalMerge() { ArrayList list = new ArrayList(); try { Iterator i = list.iterator(); i.hasNext(); } catch (Exception e) { } } public Object sharedOffsetInterfaceAndSuperClass(int x) { Serializable s; if (x > 5) { s = new B1(); } else { s = new C31(); } dummy(s); return s; } public Object arrayDifferentDimensions1(int x) { Object[] n; if ( x > 5) { n = new Number[1][1]; } else { n = new Cloneable[1]; } return n; } public Object arrayDifferentDimensions2(int x) { Object[] n; if ( x> 5) { n = new String[1][1]; } else { n = new Number[1][1][1][1]; } return n; } } } javassist-3.12.1.ga/src/test/test/javassist/bytecode/analysis/ScannerTest.java0000644000175000017500000002007111024246704027402 0ustar twernertwernerpackage test.javassist.bytecode.analysis; import java.io.IOException; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.NotFoundException; import javassist.bytecode.AccessFlag; import javassist.bytecode.Bytecode; import javassist.bytecode.MethodInfo; import javassist.bytecode.Opcode; import javassist.bytecode.analysis.Subroutine; import javassist.bytecode.analysis.SubroutineScanner; import junit.framework.TestCase; /** * Tests Subroutine Scanner * * @author Jason T. Greene */ public class ScannerTest extends TestCase { public void testNestedFinally() throws Exception { ClassPool pool = ClassPool.getDefault(); generate(pool); CtClass clazz = pool.get("test.ScannerTest$GeneratedTest"); CtMethod method = clazz.getDeclaredMethod("doit"); SubroutineScanner scanner = new SubroutineScanner(); Subroutine[] subs = scanner.scan(method.getMethodInfo2()); verifySubroutine(subs, 31, 31, new int[]{125, 25}); verifySubroutine(subs, 32, 31, new int[]{125, 25}); verifySubroutine(subs, 33, 31, new int[]{125, 25}); verifySubroutine(subs, 60, 31, new int[]{125, 25}); verifySubroutine(subs, 61, 31, new int[]{125, 25}); verifySubroutine(subs, 63, 31, new int[]{125, 25}); verifySubroutine(subs, 66, 31, new int[]{125, 25}); verifySubroutine(subs, 69, 31, new int[]{125, 25}); verifySubroutine(subs, 71, 31, new int[]{125, 25}); verifySubroutine(subs, 74, 31, new int[]{125, 25}); verifySubroutine(subs, 76, 31, new int[]{125, 25}); verifySubroutine(subs, 77, 77, new int[]{111, 71}); verifySubroutine(subs, 79, 77, new int[]{111, 71}); verifySubroutine(subs, 80, 77, new int[]{111, 71}); verifySubroutine(subs, 82, 77, new int[]{111, 71}); verifySubroutine(subs, 85, 77, new int[]{111, 71}); verifySubroutine(subs, 88, 77, new int[]{111, 71}); verifySubroutine(subs, 90, 77, new int[]{111, 71}); verifySubroutine(subs, 93, 77, new int[]{111, 71}); verifySubroutine(subs, 95, 77, new int[]{111, 71}); verifySubroutine(subs, 96, 96, new int[]{106, 90}); verifySubroutine(subs, 98, 96, new int[]{106, 90}); verifySubroutine(subs, 99, 96, new int[]{106, 90}); verifySubroutine(subs, 101, 96, new int[]{106, 90}); verifySubroutine(subs, 104, 96, new int[]{106, 90}); verifySubroutine(subs, 106, 77, new int[]{111, 71}); verifySubroutine(subs, 109, 77, new int[]{111, 71}); verifySubroutine(subs, 111, 31, new int[]{125, 25}); verifySubroutine(subs, 114, 31, new int[]{125, 25}); verifySubroutine(subs, 117, 31, new int[]{125, 25}); verifySubroutine(subs, 118, 31, new int[]{125, 25}); verifySubroutine(subs, 120, 31, new int[]{125, 25}); verifySubroutine(subs, 123, 31, new int[]{125, 25}); } private static void verifySubroutine(Subroutine[] subs, int pos, int start, int[] callers) { Subroutine sub = subs[pos]; assertNotNull(sub); assertEquals(sub.start(), start); for (int i = 0; i < callers.length; i++) assertTrue(sub.callers().contains(new Integer(callers[i]))); } private static void generate(ClassPool pool) throws CannotCompileException, IOException, NotFoundException { // Generated from eclipse JDK4 compiler: // public void doit(int x) { // println("null"); // try { // println("try"); // } catch (RuntimeException e) { // e.printStackTrace(); // } finally { // switch (x) { // default: // case 15: // try { // println("inner-try"); // } finally { // try { // println("inner-inner-try"); // } finally { // println("inner-finally"); // } // } // break; // case 1789: // println("switch -17"); // } // } //} CtClass clazz = pool.makeClass("test.ScannerTest$GeneratedTest"); CtMethod method = new CtMethod(CtClass.voidType, "doit", new CtClass[] {CtClass.intType}, clazz); MethodInfo info = method.getMethodInfo2(); info.setAccessFlags(AccessFlag.PUBLIC); CtClass stringClass = pool.get("java.lang.String"); Bytecode code = new Bytecode(info.getConstPool(), 2, 9); /* 0 */ code.addAload(0); /* 1 */ code.addLdc("start"); /* 3 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass}); /* 6 */ code.addAload(0); /* 7 */ code.addLdc("try"); /* 9 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass}); /* 12 */ addJump(code, Opcode.GOTO, 125); /* 14 */ code.addAstore(2); /* 16 */ code.addAload(2); /* 17 */ code.addInvokevirtual("java.lang.Exception", "printStackTrace", "()V"); /* 20 */ addJump(code, Opcode.GOTO, 125); /* 23 */ code.addAstore(4); /* 25 */ addJump(code, Opcode.JSR, 31); /* 28 */ code.addAload(4); /* 30 */ code.addOpcode(Opcode.ATHROW); /* 31 */ code.addAstore(3); /* 32 */ code.addIload(1); int spos = code.currentPc(); /* 33 */ code.addOpcode(Opcode.LOOKUPSWITCH); code.addIndex(0); // 2 bytes pad - gets us to 36 code.add32bit(60 - spos); // default code.add32bit(2); // 2 pairs code.add32bit(15); code.add32bit(60 - spos); code.add32bit(1789); code.add32bit(117 - spos); /* 60 */ code.addAload(0); /* 61 */ code.addLdc("inner-try"); /* 63 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass}); /* 66 */ addJump(code, Opcode.GOTO, 111); /* 69 */ code.addAstore(6); /* 71 */ addJump(code, Opcode.JSR, 77); /* 74 */ code.addAload(6); /* 76 */ code.add(Opcode.ATHROW); /* 77 */ code.addAstore(5); /* 79 */ code.addAload(0); /* 80 */ code.addLdc("inner-inner-try"); /* 82 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass}); /* 85 */ addJump(code, Opcode.GOTO, 106); /* 88 */ code.addAstore(8); /* 90 */ addJump(code, Opcode.JSR, 96); /* 93 */ code.addAload(8); /* 95 */ code.add(Opcode.ATHROW); /* 96 */ code.addAstore(7); /* 98 */ code.addAload(0); /* 99 */ code.addLdc("inner-finally"); /* 101 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass}); /* 104 */ code.addRet(7); /* 106 */ addJump(code, Opcode.JSR, 96); /* 109 */ code.addRet(5); /* 111 */ addJump(code, Opcode.JSR, 77); /* 114 */ addJump(code, Opcode.GOTO, 123); /* 117 */ code.addAload(0); /* 118 */ code.addLdc("switch - 1789"); /* 120 */ code.addInvokevirtual(clazz, "println", CtClass.voidType, new CtClass[] {stringClass}); /* 123 */ code.addRet(3); /* 125 */ addJump(code, Opcode.JSR, 31); /* 128 */ code.addOpcode(Opcode.RETURN); code.addExceptionHandler(6, 12, 15, "java.lang.RuntimeException"); code.addExceptionHandler(6, 20, 23, 0); code.addExceptionHandler(125, 128, 23, 0); code.addExceptionHandler(60, 69, 69, 0); code.addExceptionHandler(111, 114, 69, 0); code.addExceptionHandler(79, 88, 88, 0); code.addExceptionHandler(106, 109, 88, 0); info.setCodeAttribute(code.toCodeAttribute()); clazz.addMethod(method); clazz.writeFile("/tmp"); } private static void addJump(Bytecode code, int opcode, int pos) { int current = code.currentPc(); code.addOpcode(opcode); code.addIndex(pos - current); } } javassist-3.12.1.ga/src/test/test/javassist/proxy/0000755000175000017500000000000011637463217022060 5ustar twernertwernerjavassist-3.12.1.ga/src/test/test/javassist/proxy/ProxySimpleTest.java0000644000175000017500000000451411360377004026051 0ustar twernertwernerpackage test.javassist.proxy; import junit.framework.TestCase; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Method; import javassist.util.proxy.ProxyFactory; public class ProxySimpleTest extends TestCase { public void testReadWrite() throws Exception { final String fileName = "read-write.bin"; ProxyFactory.ClassLoaderProvider cp = ProxyFactory.classLoaderProvider; ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() { public ClassLoader get(ProxyFactory pf) { return new javassist.Loader(); } }; ProxyFactory pf = new ProxyFactory(); pf.setSuperclass(ReadWriteData.class); Object data = pf.createClass().newInstance(); // Object data = new ReadWriteData(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName)); oos.writeObject(data); oos.close(); ProxyFactory.classLoaderProvider = cp; ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName)); Object data2 = ois.readObject(); ois.close(); int i = ((ReadWriteData)data2).foo(); assertEquals(4, i); } public static class ReadWriteData implements Serializable { public int foo() { return 4; } } public void testWriteReplace() throws Exception { ProxyFactory pf = new ProxyFactory(); pf.setSuperclass(WriteReplace.class); Object data = pf.createClass().newInstance(); assertEquals(data, ((WriteReplace)data).writeReplace()); ProxyFactory pf2 = new ProxyFactory(); pf2.setSuperclass(WriteReplace2.class); Object data2 = pf2.createClass().newInstance(); Method meth = data2.getClass().getDeclaredMethod("writeReplace", new Class[0]); assertEquals("javassist.util.proxy.SerializedProxy", meth.invoke(data2, new Object[0]).getClass().getName()); } public static class WriteReplace implements Serializable { public Object writeReplace() { return this; } } public static class WriteReplace2 implements Serializable { public Object writeReplace(int i) { return new Integer(i); } } } javassist-3.12.1.ga/src/test/test/javassist/proxy/ProxySerializationTest.java0000644000175000017500000001204711363611753027442 0ustar twernertwernerpackage test.javassist.proxy; import javassist.util.proxy.*; import junit.framework.TestCase; import java.io.*; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * Test to ensure that serialization and deserialization of javassist proxies via * {@link javassist.util.proxy.ProxyObjectOutputStream} and @link javassist.util.proxy.ProxyObjectInputStream} * reuses classes located in the proxy cache. This tests the fixes provided for JASSIST-42 and JASSIST-97. */ public class ProxySerializationTest extends TestCase { public void testSerialization() { ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(TestClass.class); factory.setInterfaces(new Class[] {TestInterface.class}); factory.setUseWriteReplace(true); Class proxyClass = factory.createClass(new TestFilter()); MethodHandler handler = new TestHandler(); // first try serialization using writeReplace try { String name = "proxytest_1"; Constructor constructor = proxyClass.getConstructor(new Class[] {String.class}); TestClass proxy = (TestClass)constructor.newInstance(new Object[] {name}); ((ProxyObject)proxy).setHandler(handler); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeObject(proxy); out.close(); byte[] bytes = bos.toByteArray(); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream in = new ObjectInputStream(bis); TestClass newProxy = (TestClass)in.readObject(); // inherited fields should not have been deserialized assertTrue("new name should be null", newProxy.getName() == null); // since we are reading into the same JVM the new proxy should have the same class as the old proxy assertTrue("classes should be equal", newProxy.getClass() == proxy.getClass()); } catch (Exception e) { e.printStackTrace(); fail(); } // second try serialization using proxy object output/input streams factory.setUseWriteReplace(false); proxyClass = factory.createClass(new TestFilter()); try { String name = "proxytest_2"; Constructor constructor = proxyClass.getConstructor(new Class[] {String.class}); TestClass proxy = (TestClass)constructor.newInstance(new Object[] {name}); ((ProxyObject)proxy).setHandler(handler); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ProxyObjectOutputStream out = new ProxyObjectOutputStream(bos); out.writeObject(proxy); out.close(); byte[] bytes = bos.toByteArray(); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ProxyObjectInputStream in = new ProxyObjectInputStream(bis); TestClass newProxy = (TestClass)in.readObject(); // inherited fields should have been deserialized assertTrue("names should be equal", proxy.getName().equals(newProxy.getName())); // since we are reading into the same JVM the new proxy should have the same class as the old proxy assertTrue("classes should still be equal", newProxy.getClass() == proxy.getClass()); } catch (Exception e) { e.printStackTrace(); fail(); } } public static class TestFilter implements MethodFilter, Serializable { public boolean isHandled(Method m) { if (m.getName().equals("getName")) { return true; } return false; } public boolean equals(Object o) { if (o instanceof TestFilter) { // all test filters are equal return true; } return false; } public int hashCode() { return TestFilter.class.hashCode(); } } public static class TestHandler implements MethodHandler, Serializable { public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { return proceed.invoke(self, args); } public boolean equals(Object o) { if (o instanceof TestHandler) { // all test handlers are equal return true; } return false; } public int hashCode() { return TestHandler.class.hashCode(); } } public static class TestClass implements Serializable { public String name; public TestClass() { } public TestClass(String name) { this.name = name; } public String getName() { return name; } } public static interface TestInterface { public String getName(); } } javassist-3.12.1.ga/src/test/test/javassist/proxy/ProxyCacheGCTest.java0000644000175000017500000001153311363611753026041 0ustar twernertwernerpackage test.javassist.proxy; import javassist.*; import javassist.util.proxy.MethodFilter; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyFactory; import javassist.util.proxy.ProxyObject; import junit.framework.TestCase; /** * test which checks that proxy classes are not retained after their classloader is released. * this is a before and after test which validates JASSIST-104 */ public class ProxyCacheGCTest extends TestCase { /** * creates a large number of proxies in separate classloaders then lets go of the classloaders and * forces a GC. If we run out of heap then we know there is a problem. */ public final static int REPETITION_COUNT = 10000; private ClassPool basePool; private CtClass baseHandler; private CtClass baseFilter; protected void setUp() { basePool = ClassPool.getDefault(); try { baseHandler = basePool.get("javassist.util.proxy.MethodHandler"); baseFilter = basePool.get("javassist.util.proxy.MethodFilter"); } catch (NotFoundException e) { e.printStackTrace(); fail("could not find class " + e); } } public void testCacheGC() { ClassLoader oldCL = Thread.currentThread().getContextClassLoader(); try { ProxyFactory.useCache = false; for (int i = 0; i < REPETITION_COUNT; i++) { ClassLoader newCL = new TestLoader(); try { Thread.currentThread().setContextClassLoader(newCL); createProxy(i); } finally { Thread.currentThread().setContextClassLoader(oldCL); } } } finally { ProxyFactory.useCache = true; } } /** * called when a specific classloader is in place on the thread to create a method handler, method filter * and proxy in the current loader and then */ public void createProxy(int counter) { try { ClassPool classPool = new ClassPool(basePool); // create a target class in the current class loader String targetName = "test.javassist.MyTarget_" + counter; String targetConstructorName = "MyTarget_" + counter; CtClass ctTargetClass = classPool.makeClass(targetName); CtMethod targetMethod = CtNewMethod.make("public Object test() { return this; }", ctTargetClass); ctTargetClass.addMethod(targetMethod); CtConstructor targetConstructor = CtNewConstructor.make("public " + targetConstructorName + "() { }", ctTargetClass); ctTargetClass.addConstructor(targetConstructor); // create a handler in the current classloader String handlerName = "test.javassist.MyHandler_" + counter; CtClass ctHandlerClass = classPool.makeClass(handlerName); ctHandlerClass.addInterface(baseHandler); CtMethod handlerInvoke = CtNewMethod.make("public Object invoke(Object self, java.lang.reflect.Method thisMethod, java.lang.reflect.Method proceed, Object[] args) throws Throwable { return proceed.invoke(self, args); }", ctHandlerClass); ctHandlerClass.addMethod(handlerInvoke); // create a filter in the current classloader String filterName = "test.javassist.MyFilter" + counter; CtClass ctFilterClass = classPool.makeClass(filterName); ctFilterClass.addInterface(baseFilter); CtMethod filterIsHandled = CtNewMethod.make("public boolean isHandled(java.lang.reflect.Method m) { return true; }", ctFilterClass); ctFilterClass.addMethod(filterIsHandled); // now create a proxyfactory and use it to create a proxy ProxyFactory factory = new ProxyFactory(); Class javaTargetClass = classPool.toClass(ctTargetClass); Class javaHandlerClass = classPool.toClass(ctHandlerClass); Class javaFilterClass = classPool.toClass(ctFilterClass); MethodHandler handler= (MethodHandler)javaHandlerClass.newInstance(); MethodFilter filter = (MethodFilter)javaFilterClass.newInstance(); // ok, now create a factory and a proxy class and proxy from that factory factory.setFilter(filter); factory.setSuperclass(javaTargetClass); // factory.setSuperclass(Object.class); Class proxyClass = factory.createClass(); Object target = proxyClass.newInstance(); ((ProxyObject)target).setHandler(handler); } catch (Exception e) { e.printStackTrace(); fail("cannot create proxy " + e); } } /** * a classloader which inherits from the system class loader and within which a proxy handler, * filter and proxy will be located. */ public static class TestLoader extends ClassLoader { } } javassist-3.12.1.ga/src/test/test/javassist/proxy/JASSIST113RegressionTest.java0000644000175000017500000000077311361031277027167 0ustar twernertwernerpackage test.javassist.proxy; import javassist.util.proxy.ProxyFactory; import junit.framework.TestCase; /** * Test for regression error detailed in JASSIST-113 */ public class JASSIST113RegressionTest extends TestCase { interface Bear { void hibernate(); } public void testProxyFactoryWithNonPublicInterface() { ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setInterfaces(new Class[]{Bear.class}); proxyFactory.createClass(); } } javassist-3.12.1.ga/src/test/test/javassist/proxy/ProxyFactoryCompatibilityTest.java0000644000175000017500000000776511363611753031001 0ustar twernertwernerpackage test.javassist.proxy; import javassist.*; import javassist.util.proxy.MethodFilter; import javassist.util.proxy.MethodHandler; import javassist.util.proxy.ProxyFactory; import javassist.util.proxy.ProxyObject; import junit.framework.TestCase; import java.lang.reflect.Method; /** * test which checks that it is still possible to use the old style proxy factory api * to create proxy classes which set their own handler. it checks that caching is * automatically disabled if this legacy api is used. it also exercises the new style * api, ensuring that caching works correctly with this model. */ public class ProxyFactoryCompatibilityTest extends TestCase { private ClassPool basePool; MethodFilter filter; MethodHandler handler; protected void setUp() { basePool = ClassPool.getDefault(); filter = new MethodFilter() { public boolean isHandled(Method m) { return !m.getName().equals("finalize"); } }; handler = new MethodHandler() { public Object invoke(Object self, Method m, Method proceed, Object[] args) throws Throwable { System.out.println("calling: " + m.getName()); return proceed.invoke(self, args); // execute the original method. } }; } public void testFactoryCompatibility() throws Exception { System.out.println("ProxyFactory.useCache = " + ProxyFactory.useCache); // create a factory which, by default, uses caching ProxyFactory factory = new ProxyFactory(); factory.setSuperclass(TestClass.class); factory.setInterfaces(new Class[] { TestInterface.class}); factory.setFilter(filter); // create the same class twice and check that it is reused Class proxyClass1 = factory.createClass(); System.out.println("created first class " + proxyClass1.getName()); TestClass proxy1 = (TestClass)proxyClass1.newInstance(); ((ProxyObject) proxy1).setHandler(handler); proxy1.testMethod(); assertTrue(proxy1.isTestCalled()); Class proxyClass2 = factory.createClass(); System.out.println("created second class " + proxyClass2.getName()); TestClass proxy2 = (TestClass)proxyClass2.newInstance(); ((ProxyObject) proxy2).setHandler(handler); proxy2.testMethod(); assertTrue(proxy2.isTestCalled()); assertTrue(proxyClass1 == proxyClass2); // create a factory which, by default, uses caching then set the handler so it creates // classes which do not get cached. ProxyFactory factory2 = new ProxyFactory(); factory.setSuperclass(TestClass.class); factory.setInterfaces(new Class[] { TestInterface.class}); factory.setFilter(filter); factory.setHandler(handler); // create the same class twice and check that it is reused Class proxyClass3 = factory.createClass(); System.out.println("created third class " + proxyClass3.getName()); TestClass proxy3 = (TestClass)proxyClass3.newInstance(); proxy3.testMethod(); assertTrue(proxy3.isTestCalled()); Class proxyClass4 = factory.createClass(); System.out.println("created fourth class " + proxyClass4.getName()); TestClass proxy4 = (TestClass)proxyClass4.newInstance(); proxy4.testMethod(); assertTrue(proxy4.isTestCalled()); assertTrue(proxyClass3 != proxyClass4); } /** * test class used as the super for the proxy */ public static class TestClass { private boolean testCalled = false; public void testMethod() { // record the call testCalled = true; } public boolean isTestCalled() { return testCalled; } } /** * test interface used as an interface implemented by the proxy */ public static interface TestInterface { public void testMethod(); } }javassist-3.12.1.ga/src/test/test/javassist/convert/0000755000175000017500000000000011637463214022354 5ustar twernertwernerjavassist-3.12.1.ga/src/test/test/javassist/convert/ArrayAccessReplaceTest.java0000644000175000017500000003117611024246704027554 0ustar twernertwernerpackage test.javassist.convert; import java.net.URL; import java.net.URLClassLoader; import java.util.HashMap; import java.util.Map; import javassist.ClassPool; import javassist.CodeConverter; import javassist.CtClass; import junit.framework.TestCase; public class ArrayAccessReplaceTest extends TestCase { private static SimpleInterface simple; public void setUp() throws Exception { ClassPool pool = new ClassPool(true); CtClass echoClass = pool.get(ArrayAccessReplaceTest.class.getName() + "$Echo"); CtClass simpleClass = pool.get(ArrayAccessReplaceTest.class.getName() + "$Simple"); CodeConverter converter = new CodeConverter(); converter.replaceArrayAccess(echoClass, new CodeConverter.DefaultArrayAccessReplacementMethodNames()); simpleClass.instrument(converter); //simpleClass.writeFile("/tmp"); simple = (SimpleInterface) simpleClass.toClass(new URLClassLoader(new URL[0], getClass().getClassLoader()), Class.class.getProtectionDomain()).newInstance(); } public void testComplex() throws Exception { ClassPool pool = new ClassPool(true); CtClass clazz = pool.get(ArrayAccessReplaceTest.class.getName() + "$Complex"); CodeConverter converter = new CodeConverter(); converter.replaceArrayAccess(clazz, new CodeConverter.DefaultArrayAccessReplacementMethodNames()); clazz.instrument(converter); ComplexInterface instance = (ComplexInterface) clazz.toClass(new URLClassLoader(new URL[0], getClass().getClassLoader()), Class.class.getProtectionDomain()).newInstance(); assertEquals(new Integer(5), instance.complexRead(4)); } public void testBoolean() throws Exception { for (int i = 0; i < 100; i++) { boolean value = i % 5 == 0; simple.setBoolean(i, value); } for (int i = 0; i < 100; i++) { boolean value = i % 5 == 0; assertEquals(value, simple.getBoolean(i)); } } public void testByte() throws Exception { for (byte i = 0; i < 100; i++) { simple.setByte(i, i); } for (byte i = 0; i < 100; i++) { assertEquals(i, simple.getByte(i)); } } public void testShort() throws Exception { for (short i = 0; i < 100; i++) { simple.setShort(i, i); } for (short i = 0; i < 100; i++) { assertEquals(i, simple.getShort(i)); } } public void testChar() throws Exception { for (char i = 0; i < 100; i++) { simple.setChar(i, i); } for (char i = 0; i < 100; i++) { assertEquals(i, simple.getChar(i)); } } public void testInt() throws Exception { for (int i = 0; i < 100; i++) { simple.setInt(i, i); } for (int i = 0; i < 100; i++) { assertEquals(i, simple.getInt(i)); } } public void testLong() throws Exception { for (int i = 0; i < 100; i++) { simple.setLong(i, i); } for (int i = 0; i < 100; i++) { assertEquals(i, simple.getLong(i)); } } public void testFloat() throws Exception { for (int i = 0; i < 100; i++) { simple.setFloat(i, i); } for (int i = 0; i < 100; i++) { assertEquals((float)i, simple.getFloat(i), 0); } } public void testDouble() throws Exception { for (int i = 0; i < 100; i++) { simple.setDouble(i, i); } for (int i = 0; i < 100; i++) { assertEquals((double)i, simple.getDouble(i), 0); } } public void testObject() throws Exception { for (int i = 0; i < 100; i++) { simple.setObject(i, new Integer(i)); } for (int i = 0; i < 100; i++) { assertEquals(new Integer(i), simple.getObject(i)); } } public void testFoo() throws Exception { for (int i = 0; i < 100; i++) { simple.setFoo(i, new Foo(i)); } for (int i = 0; i < 100; i++) { assertEquals(new Foo(i), simple.getFoo(i)); } } public void testMulti() throws Exception { for (int i = 2; i < 100; i++) { simple.setMultiFoo(0, 1, i, new Foo(i)); } for (int i = 2; i < 100; i++) { assertEquals(new Foo(i), simple.getMultiFoo(0, 1, i)); } } public static class Echo { public static Map byteMap = new HashMap(); public static Map charMap = new HashMap(); public static Map doubleMap = new HashMap(); public static Map floatMap = new HashMap(); public static Map intMap = new HashMap(); public static Map longMap = new HashMap(); public static Map objectMap = new HashMap(); public static Map shortMap = new HashMap(); public static Object arrayReadObject(Object array, int index) { return objectMap.get(new Integer(index)); } public static void arrayWriteObject(Object array, int index, Object element) { objectMap.put(new Integer(index), element); } public static byte arrayReadByteOrBoolean(Object array, int index) { return ((Byte)byteMap.get(new Integer(index))).byteValue(); } public static void arrayWriteByteOrBoolean(Object array, int index, byte element) { byteMap.put(new Integer(index), new Byte(element)); } public static char arrayReadChar(Object array, int index) { return ((Character)charMap.get(new Integer(index))).charValue(); } public static void arrayWriteChar(Object array, int index, char element) { charMap.put(new Integer(index), new Character(element)); } public static double arrayReadDouble(Object array, int index) { return ((Double)doubleMap.get(new Integer(index))).doubleValue(); } public static void arrayWriteDouble(Object array, int index, double element) { doubleMap.put(new Integer(index), new Double(element)); } public static float arrayReadFloat(Object array, int index) { return ((Float)floatMap.get(new Integer(index))).floatValue(); } public static void arrayWriteFloat(Object array, int index, float element) { floatMap.put(new Integer(index), new Float(element)); } public static int arrayReadInt(Object array, int index) { return ((Integer)intMap.get(new Integer(index))).intValue(); } public static void arrayWriteInt(Object array, int index, int element) { intMap.put(new Integer(index), new Integer(element)); } public static long arrayReadLong(Object array, int index) { return ((Long)longMap.get(new Integer(index))).longValue(); } public static void arrayWriteLong(Object array, int index, long element) { longMap.put(new Integer(index), new Long(element)); } public static short arrayReadShort(Object array, int index) { return ((Short)shortMap.get(new Integer(index))).shortValue(); } public static void arrayWriteShort(Object array, int index, short element) { shortMap.put(new Integer(index), new Short(element)); } } public static class Foo { public int bar; public Foo(int bar) { this.bar = bar; } public int hashCode() { return bar; } public boolean equals(Object o) { if (! (o instanceof Foo)) return false; return ((Foo)o).bar == bar; } } public static interface SimpleInterface { public void setBoolean(int pos, boolean value); public boolean getBoolean(int pos); public void setByte(int pos, byte value); public byte getByte(int pos); public void setShort(int pos, short value); public short getShort(int pos); public void setChar(int pos, char value); public char getChar(int pos); public void setInt(int pos, int value); public int getInt(int pos); public void setLong(int pos, long value); public long getLong(int pos); public void setFloat(int pos, float value); public float getFloat(int pos); public void setDouble(int pos, double value); public double getDouble(int pos); public void setObject(int pos, Object value); public Object getObject(int pos); public void setFoo(int pos, Foo value); public Foo getFoo(int pos); public void setMultiFoo(int one, int two, int three, Foo foo); public Foo getMultiFoo(int one, int two, int three); } public static class Simple implements SimpleInterface { private boolean[] booleans; private byte[] bytes; private short[] shorts; private char[] chars; private int[] ints; private long[] longs; private float[] floats; private double[] doubles; private Object[] objects; private Foo[] foos; private Foo[][][] multi; public Simple() { multi[0] = new Foo[0][0]; multi[0][1] = new Foo[0]; } public boolean getBoolean(int pos) { return booleans[pos]; } public byte getByte(int pos) { return bytes[pos]; } public char getChar(int pos) { return chars[pos]; } public double getDouble(int pos) { return doubles[pos]; } public float getFloat(int pos) { return floats[pos]; } public Foo getFoo(int pos) { return foos[pos]; } public int getInt(int pos) { return ints[pos]; } public long getLong(int pos) { return longs[pos]; } public Object getObject(int pos) { return objects[pos]; } public short getShort(int pos) { return shorts[pos]; } public Foo getMultiFoo(int one, int two, int three) { return multi[one][two][three]; } public void setBoolean(int pos, boolean value) { booleans[pos] = value; } public void setByte(int pos, byte value) { bytes[pos] = value; } public void setChar(int pos, char value) { chars[pos] = value; } public void setDouble(int pos, double value) { doubles[pos] = value; } public void setFloat(int pos, float value) { floats[pos] = value; } public void setFoo(int pos, Foo value) { foos[pos] = value; } public void setInt(int pos, int value) { ints[pos] = value; } public void setLong(int pos, long value) { longs[pos] = value; } public void setObject(int pos, Object value) { objects[pos] = value; } public void setShort(int pos, short value) { shorts[pos] = value; } public void setMultiFoo(int one, int two, int three, Foo foo) { multi[one][two][three] = foo; } } public static interface ComplexInterface { public Number complexRead(int x); } public static class Complex implements ComplexInterface { private Integer[] nums; private Long[] longNums; private static Integer justRead; public static Object arrayReadObject(Object array, int offset) { return new Integer(justRead.intValue() + offset); } public static void arrayWriteObject(Object array, int offset, Object element) { justRead = (Integer) element; } public Object getInteger(int i) { return (Object) new Integer(i); } public Number complexRead(int x) { Number[] ns = null; Number n1, n2, n3, n4; try { ((Object[])ns)[1] = getInteger(x); // We have to throw an error since we can't intercept // a guaranteed null array read yet (likely never will be able to) throw new Error("hi"); } catch (Error error) { ns = nums; } catch (Exception exception) { ns = longNums; } finally { n1 = ns[1]; n2 = ns[2]; n3 = ns[3]; n4 = ns[4]; n2.intValue(); n3.intValue(); n4.intValue(); } return n1; } } }javassist-3.12.1.ga/src/main/0000755000175000017500000000000011637463223015653 5ustar twernertwernerjavassist-3.12.1.ga/src/main/META-INF/0000755000175000017500000000000011637463223017013 5ustar twernertwernerjavassist-3.12.1.ga/src/main/META-INF/MANIFEST.MF0000644000175000017500000000040211361244304020430 0ustar twernertwernerManifest-Version: 1.1 Specification-Title: Javassist Created-By: Shigeru Chiba, Tokyo Institute of Technology Specification-Vendor: Shigeru Chiba, Tokyo Institute of Technology Specification-Version: 3.12.0.GA Main-Class: javassist.CtClass Name: javassist/ javassist-3.12.1.ga/src/main/javassist/0000755000175000017500000000000011637463437017671 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/Modifier.java0000644000175000017500000001407410630701321022253 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; 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 javassist.bytecode.AccessFlag. * *

    All the methods/constants in this class are compatible with * ones in java.lang.reflect.Modifier. * * @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; public static final int ANNOTATION = AccessFlag.ANNOTATION; public static final int ENUM = AccessFlag.ENUM; /** * Returns true if the modifiers include the public * modifier. */ public static boolean isPublic(int mod) { return (mod & PUBLIC) != 0; } /** * Returns true if the modifiers include the private * modifier. */ public static boolean isPrivate(int mod) { return (mod & PRIVATE) != 0; } /** * Returns true if the modifiers include the protected * modifier. */ public static boolean isProtected(int mod) { return (mod & PROTECTED) != 0; } /** * Returns true if the modifiers do not include either * public, protected, or private. */ public static boolean isPackage(int mod) { return (mod & (PUBLIC | PRIVATE | PROTECTED)) == 0; } /** * Returns true if the modifiers include the static * modifier. */ public static boolean isStatic(int mod) { return (mod & STATIC) != 0; } /** * Returns true if the modifiers include the final * modifier. */ public static boolean isFinal(int mod) { return (mod & FINAL) != 0; } /** * Returns true if the modifiers include the synchronized * modifier. */ public static boolean isSynchronized(int mod) { return (mod & SYNCHRONIZED) != 0; } /** * Returns true if the modifiers include the volatile * modifier. */ public static boolean isVolatile(int mod) { return (mod & VOLATILE) != 0; } /** * Returns true if the modifiers include the transient * modifier. */ public static boolean isTransient(int mod) { return (mod & TRANSIENT) != 0; } /** * Returns true if the modifiers include the native * modifier. */ public static boolean isNative(int mod) { return (mod & NATIVE) != 0; } /** * Returns true if the modifiers include the interface * modifier. */ public static boolean isInterface(int mod) { return (mod & INTERFACE) != 0; } /** * Returns true if the modifiers include the annotation * modifier. * * @since 3.2 */ public static boolean isAnnotation(int mod) { return (mod & ANNOTATION) != 0; } /** * Returns true if the modifiers include the enum * modifier. * * @since 3.2 */ public static boolean isEnum(int mod) { return (mod & ENUM) != 0; } /** * Returns true if the modifiers include the abstract * modifier. */ public static boolean isAbstract(int mod) { return (mod & ABSTRACT) != 0; } /** * Returns true if the modifiers include the strictfp * 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 mod. */ public static int clear(int mod, int clearBit) { return mod & ~clearBit; } /** * Return a string describing the access modifier flags in * the specified modifier. * * @param mod modifier flags. */ public static String toString(int mod) { return java.lang.reflect.Modifier.toString(mod); } } javassist-3.12.1.ga/src/main/javassist/URLClassPath.java0000644000175000017500000001324410630701321022760 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; 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). * *

    This search path is used only if a requested * class name starts with the name specified by packageName. * If packageName is "org.javassist." and a requested class is * "org.javassist.test.Main", then the given URL is used for loading that class. * The URLClassPath obtains a class file from: * *

      http://www.javassist.org:80/java/classes/org/javassist/test/Main.class
           * 
    * *

    Here, we assume that host is "www.javassist.org", * port is 80, and directory is "/java/classes/". * *

    If packageName is null, 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). * It must start with "/". * @param packageName package name. It must end with "." (dot). */ 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. * * @return null if the class file could not be found. */ public InputStream openClassfile(String classname) { try { URLConnection con = openClassfile0(classname); if (con != null) return con.getInputStream(); } catch (IOException e) {} return null; // not found } private URLConnection openClassfile0(String classname) throws IOException { if (packageName == null || classname.startsWith(packageName)) { String jarname = directory + classname.replace('.', '/') + ".class"; return fetchClass0(hostname, port, jarname); } else return null; // not found } /** * Returns the URL. * * @return null if the class file could not be obtained. */ public URL find(String classname) { try { URLConnection con = openClassfile0(classname); InputStream is = con.getInputStream(); if (is != null) { is.close(); return con.getURL(); } } catch (IOException e) {} return null; } /** * Closes this class path. */ public void close() {} /** * 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). * It must start with "/". * @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(); try { 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) throw new IOException("the stream was closed: " + classname); len += n; } while (len < size); } } finally { 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; } } javassist-3.12.1.ga/src/main/javassist/bytecode/0000755000175000017500000000000011637463416021464 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/bytecode/LineNumberAttribute.java0000644000175000017500000001211010630701321026224 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.IOException; import java.util.Map; /** * LineNumberTable_attribute. */ public class LineNumberAttribute extends AttributeInfo { /** * The name of this attribute "LineNumberTable". */ 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 line_number_table_length. * This represents the number of entries in the table. */ public int tableLength() { return ByteArray.readU16bit(info, 0); } /** * Returns line_number_table[i].start_pc. * 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 line_number_table[i].line_number. * 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; } /** * Used as a return type of toNearPc(). */ static public class Pc { /** * The index into the code array. */ public int index; /** * The line number. */ public int line; } /** * Returns the index into the code array at which the code for * the specified line (or the nearest line after the specified one) * begins. * * @param line the line number. * @return a pair of the index and the line number of the * bytecode at that index. */ public Pc toNearPc(int line) { int n = tableLength(); int nearPc = 0; int distance = 0; if (n > 0) { distance = lineNumber(0) - line; nearPc = startPc(0); } for (int i = 1; i < n; ++i) { int d = lineNumber(i) - line; if ((d < 0 && d > distance) || (d >= 0 && (d < distance || distance < 0))) { distance = d; nearPc = startPc(i); } } Pc res = new Pc(); res.index = nearPc; res.line = line + distance; return res; } /** * 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; } /** * Adjusts start_pc if bytecode is inserted in a method body. */ void shiftPc(int where, int gapLength, boolean exclusive) { int n = tableLength(); for (int i = 0; i < n; ++i) { int pos = i * 4 + 2; int pc = ByteArray.readU16bit(info, pos); if (pc > where || (exclusive && pc == where)) ByteArray.write16bit(pc + gapLength, info, pos); } } } javassist-3.12.1.ga/src/main/javassist/bytecode/StackMapTable.java0000644000175000017500000007644211275555573025023 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.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 javassist.CannotCompileException; /** * stack_map attribute. * *

    This is an entry in the attributes table of a Code attribute. * It was introduced by J2SE 6 for the process of verification by * typechecking. * * @see StackMap * @since 3.4 */ public class StackMapTable extends AttributeInfo { /** * The name of this attribute "StackMapTable". */ public static final String tag = "StackMapTable"; /** * Constructs a stack_map attribute. */ StackMapTable(ConstPool cp, byte[] newInfo) { super(cp, tag, newInfo); } StackMapTable(ConstPool cp, int name_id, DataInputStream in) throws IOException { super(cp, name_id, in); } /** * Makes a copy. * * @exception RuntimeCopyException if a BadBytecode * exception is thrown while copying, * it is converted into * RuntimeCopyException. * */ public AttributeInfo copy(ConstPool newCp, Map classnames) throws RuntimeCopyException { try { return new StackMapTable(newCp, new Copier(this.constPool, info, newCp).doit()); } catch (BadBytecode e) { throw new RuntimeCopyException("bad bytecode. fatal?"); } } /** * An exception that may be thrown by copy() * in StackMapTable. */ public static class RuntimeCopyException extends RuntimeException { /** * Constructs an exception. */ public RuntimeCopyException(String s) { super(s); } } void write(DataOutputStream out) throws IOException { super.write(out); } /** * Top_variable_info.tag. */ public static final int TOP = 0; /** * Integer_variable_info.tag. */ public static final int INTEGER = 1; /** * Float_variable_info.tag. */ public static final int FLOAT = 2; /** * Double_variable_info.tag. */ public static final int DOUBLE = 3; /** * Long_variable_info.tag. */ public static final int LONG = 4; /** * Null_variable_info.tag. */ public static final int NULL = 5; /** * UninitializedThis_variable_info.tag. */ public static final int THIS = 6; /** * Object_variable_info.tag. */ public static final int OBJECT = 7; /** * Uninitialized_variable_info.tag. */ public static final int UNINIT = 8; /** * A code walker for a StackMapTable attribute. */ public static class Walker { byte[] info; int numOfEntries; /** * Constructs a walker. * * @param smt the StackMapTable that this walker * walks around. */ public Walker(StackMapTable smt) { this(smt.get()); } /** * Constructs a walker. * * @param data the info field of the * attribute_info structure. * It can be obtained by get() * in the AttributeInfo class. */ public Walker(byte[] data) { info = data; numOfEntries = ByteArray.readU16bit(data, 0); } /** * Returns the number of the entries. */ public final int size() { return numOfEntries; } /** * Visits each entry of the stack map frames. */ public void parse() throws BadBytecode { int n = numOfEntries; int pos = 2; for (int i = 0; i < n; i++) pos = stackMapFrames(pos, i); } /** * Invoked when the next entry of the stack map frames is visited. * * @param pos the position of the frame in the info * field of attribute_info structure. * @param nth the frame is the N-th * (0, 1st, 2nd, 3rd, 4th, ...) entry. * @return the position of the next frame. */ int stackMapFrames(int pos, int nth) throws BadBytecode { int type = info[pos] & 0xff; if (type < 64) { sameFrame(pos, type); pos++; } else if (type < 128) pos = sameLocals(pos, type); else if (type < 247) throw new BadBytecode("bad frame_type in StackMapTable"); else if (type == 247) // SAME_LOCALS_1_STACK_ITEM_EXTENDED pos = sameLocals(pos, type); else if (type < 251) { int offset = ByteArray.readU16bit(info, pos + 1); chopFrame(pos, offset, 251 - type); pos += 3; } else if (type == 251) { // SAME_FRAME_EXTENDED int offset = ByteArray.readU16bit(info, pos + 1); sameFrame(pos, offset); pos += 3; } else if (type < 255) pos = appendFrame(pos, type); else // FULL_FRAME pos = fullFrame(pos); return pos; } /** * Invoked if the visited frame is a same_frame or * a same_frame_extended. * * @param pos the position of this frame in the info * field of attribute_info structure. * @param offsetDelta */ public void sameFrame(int pos, int offsetDelta) throws BadBytecode {} private int sameLocals(int pos, int type) throws BadBytecode { int top = pos; int offset; if (type < 128) offset = type - 64; else { // type == 247 offset = ByteArray.readU16bit(info, pos + 1); pos += 2; } int tag = info[pos + 1] & 0xff; int data = 0; if (tag == OBJECT || tag == UNINIT) { data = ByteArray.readU16bit(info, pos + 2); pos += 2; } sameLocals(top, offset, tag, data); return pos + 2; } /** * Invoked if the visited frame is a same_locals_1_stack_item_frame * or a same_locals_1_stack_item_frame_extended. * * @param pos the position. * @param offsetDelta * @param stackTag stack[0].tag. * @param stackData stack[0].cpool_index * if the tag is OBJECT, * or stack[0].offset * if the tag is UNINIT. */ public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) throws BadBytecode {} /** * Invoked if the visited frame is a chop_frame. * * @param pos the position. * @param offsetDelta * @param k the k last locals are absent. */ public void chopFrame(int pos, int offsetDelta, int k) throws BadBytecode {} private int appendFrame(int pos, int type) throws BadBytecode { int k = type - 251; int offset = ByteArray.readU16bit(info, pos + 1); int[] tags = new int[k]; int[] data = new int[k]; int p = pos + 3; for (int i = 0; i < k; i++) { int tag = info[p] & 0xff; tags[i] = tag; if (tag == OBJECT || tag == UNINIT) { data[i] = ByteArray.readU16bit(info, p + 1); p += 3; } else { data[i] = 0; p++; } } appendFrame(pos, offset, tags, data); return p; } /** * Invoked if the visited frame is a append_frame. * * @param pos the position. * @param offsetDelta * @param tags locals[i].tag. * @param data locals[i].cpool_index * or locals[i].offset. */ public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) throws BadBytecode {} private int fullFrame(int pos) throws BadBytecode { int offset = ByteArray.readU16bit(info, pos + 1); int numOfLocals = ByteArray.readU16bit(info, pos + 3); int[] localsTags = new int[numOfLocals]; int[] localsData = new int[numOfLocals]; int p = verifyTypeInfo(pos + 5, numOfLocals, localsTags, localsData); int numOfItems = ByteArray.readU16bit(info, p); int[] itemsTags = new int[numOfItems]; int[] itemsData = new int[numOfItems]; p = verifyTypeInfo(p + 2, numOfItems, itemsTags, itemsData); fullFrame(pos, offset, localsTags, localsData, itemsTags, itemsData); return p; } /** * Invoked if the visited frame is full_frame. * * @param pos the position. * @param offsetDelta * @param localTags locals[i].tag * @param localData locals[i].cpool_index * or locals[i].offset * @param stackTags stack[i].tag * @param stackData stack[i].cpool_index * or stack[i].offset */ public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData) throws BadBytecode {} private int verifyTypeInfo(int pos, int n, int[] tags, int[] data) { for (int i = 0; i < n; i++) { int tag = info[pos++] & 0xff; tags[i] = tag; if (tag == OBJECT || tag == UNINIT) { data[i] = ByteArray.readU16bit(info, pos); pos += 2; } } return pos; } } static class SimpleCopy extends Walker { private Writer writer; public SimpleCopy(byte[] data) { super(data); writer = new Writer(data.length); } public byte[] doit() throws BadBytecode { parse(); return writer.toByteArray(); } public void sameFrame(int pos, int offsetDelta) { writer.sameFrame(offsetDelta); } public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { writer.sameLocals(offsetDelta, stackTag, copyData(stackTag, stackData)); } public void chopFrame(int pos, int offsetDelta, int k) { writer.chopFrame(offsetDelta, k); } public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) { writer.appendFrame(offsetDelta, tags, copyData(tags, data)); } public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData) { writer.fullFrame(offsetDelta, localTags, copyData(localTags, localData), stackTags, copyData(stackTags, stackData)); } protected int copyData(int tag, int data) { return data; } protected int[] copyData(int[] tags, int[] data) { return data; } } static class Copier extends SimpleCopy { private ConstPool srcPool, destPool; public Copier(ConstPool src, byte[] data, ConstPool dest) { super(data); srcPool = src; destPool = dest; } protected int copyData(int tag, int data) { if (tag == OBJECT) return srcPool.copy(data, destPool, null); else return data; } protected int[] copyData(int[] tags, int[] data) { int[] newData = new int[data.length]; for (int i = 0; i < data.length; i++) if (tags[i] == OBJECT) newData[i] = srcPool.copy(data[i], destPool, null); else newData[i] = data[i]; return newData; } } /** * Updates this stack map table when a new local variable is inserted * for a new parameter. * * @param index the index of the added local variable. * @param tag the type tag of that local variable. * @param classInfo the index of the CONSTANT_Class_info structure * in a constant pool table. This should be zero unless the tag * is ITEM_Object. * * @see javassist.CtBehavior#addParameter(javassist.CtClass) * @see #typeTagOf(char) * @see ConstPool */ public void insertLocal(int index, int tag, int classInfo) throws BadBytecode { byte[] data = new InsertLocal(this.get(), index, tag, classInfo).doit(); this.set(data); } /** * Returns the tag of the type specified by the * descriptor. This method returns INTEGER * unless the descriptor is either D (double), F (float), * J (long), L (class type), or [ (array). * * @param descriptor the type descriptor. * @see Descriptor */ public static int typeTagOf(char descriptor) { switch (descriptor) { case 'D' : return DOUBLE; case 'F' : return FLOAT; case 'J' : return LONG; case 'L' : case '[' : return OBJECT; // case 'V' : default : return INTEGER; } } /* This implementation assumes that a local variable initially * holding a parameter value is never changed to be a different * type. * */ static class InsertLocal extends SimpleCopy { private int varIndex; private int varTag, varData; public InsertLocal(byte[] data, int varIndex, int varTag, int varData) { super(data); this.varIndex = varIndex; this.varTag = varTag; this.varData = varData; } public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData) { int len = localTags.length; if (len < varIndex) { super.fullFrame(pos, offsetDelta, localTags, localData, stackTags, stackData); return; } int typeSize = (varTag == LONG || varTag == DOUBLE) ? 2 : 1; int[] localTags2 = new int[len + typeSize]; int[] localData2 = new int[len + typeSize]; int index = varIndex; int j = 0; for (int i = 0; i < len; i++) { if (j == index) j += typeSize; localTags2[j] = localTags[i]; localData2[j++] = localData[i]; } localTags2[index] = varTag; localData2[index] = varData; if (typeSize > 1) { localTags2[index + 1] = TOP; localData2[index + 1] = 0; } super.fullFrame(pos, offsetDelta, localTags2, localData2, stackTags, stackData); } } /** * A writer of stack map tables. */ public static class Writer { ByteArrayOutputStream output; int numOfEntries; /** * Constructs a writer. * @param size the initial buffer size. */ public Writer(int size) { output = new ByteArrayOutputStream(size); numOfEntries = 0; output.write(0); // u2 number_of_entries output.write(0); } /** * Returns the stack map table written out. */ public byte[] toByteArray() { byte[] b = output.toByteArray(); ByteArray.write16bit(numOfEntries, b, 0); return b; } /** * Constructs and a return a stack map table containing * the written stack map entries. * * @param cp the constant pool used to write * the stack map entries. */ public StackMapTable toStackMapTable(ConstPool cp) { return new StackMapTable(cp, toByteArray()); } /** * Writes a same_frame or a same_frame_extended. */ public void sameFrame(int offsetDelta) { numOfEntries++; if (offsetDelta < 64) output.write(offsetDelta); else { output.write(251); // SAME_FRAME_EXTENDED write16(offsetDelta); } } /** * Writes a same_locals_1_stack_item * or a same_locals_1_stack_item_extended. * * @param tag stack[0].tag. * @param data stack[0].cpool_index * if the tag is OBJECT, * or stack[0].offset * if the tag is UNINIT. * Otherwise, this parameter is not used. */ public void sameLocals(int offsetDelta, int tag, int data) { numOfEntries++; if (offsetDelta < 64) output.write(offsetDelta + 64); else { output.write(247); // SAME_LOCALS_1_STACK_ITEM_EXTENDED write16(offsetDelta); } writeTypeInfo(tag, data); } /** * Writes a chop_frame. * * @param k the number of absent locals. 1, 2, or 3. */ public void chopFrame(int offsetDelta, int k) { numOfEntries++; output.write(251 - k); write16(offsetDelta); } /** * Writes a append_frame. The number of the appended * locals is specified by the length of tags. * * @param tags locals[].tag. * The length of this array must be * either 1, 2, or 3. * @param data locals[].cpool_index * if the tag is OBJECT, * or locals[].offset * if the tag is UNINIT. * Otherwise, this parameter is not used. */ public void appendFrame(int offsetDelta, int[] tags, int[] data) { numOfEntries++; int k = tags.length; // k is 1, 2, or 3 output.write(k + 251); write16(offsetDelta); for (int i = 0; i < k; i++) writeTypeInfo(tags[i], data[i]); } /** * Writes a full_frame. * number_of_locals and number_of_stack_items * are specified by the the length of localTags and * stackTags. * * @param localTags locals[].tag. * @param localData locals[].cpool_index * if the tag is OBJECT, * or locals[].offset * if the tag is UNINIT. * Otherwise, this parameter is not used. * @param stackTags stack[].tag. * @param stackData stack[].cpool_index * if the tag is OBJECT, * or stack[].offset * if the tag is UNINIT. * Otherwise, this parameter is not used. */ public void fullFrame(int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData) { numOfEntries++; output.write(255); // FULL_FRAME write16(offsetDelta); int n = localTags.length; write16(n); for (int i = 0; i < n; i++) writeTypeInfo(localTags[i], localData[i]); n = stackTags.length; write16(n); for (int i = 0; i < n; i++) writeTypeInfo(stackTags[i], stackData[i]); } private void writeTypeInfo(int tag, int data) { output.write(tag); if (tag == OBJECT || tag == UNINIT) write16(data); } private void write16(int value) { output.write((value >>> 8) & 0xff); output.write(value & 0xff); } } /** * Prints the stack table map. */ public void println(PrintWriter w) { Printer.print(this, w); } /** * Prints the stack table map. * * @param ps a print stream such as System.out. */ public void println(java.io.PrintStream ps) { Printer.print(this, new java.io.PrintWriter(ps, true)); } static class Printer extends Walker { private PrintWriter writer; private int offset; /** * Prints the stack table map. */ public static void print(StackMapTable smt, PrintWriter writer) { try { new Printer(smt.get(), writer).parse(); } catch (BadBytecode e) { writer.println(e.getMessage()); } } Printer(byte[] data, PrintWriter pw) { super(data); writer = pw; offset = -1; } public void sameFrame(int pos, int offsetDelta) { offset += offsetDelta + 1; writer.println(offset + " same frame: " + offsetDelta); } public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { offset += offsetDelta + 1; writer.println(offset + " same locals: " + offsetDelta); printTypeInfo(stackTag, stackData); } public void chopFrame(int pos, int offsetDelta, int k) { offset += offsetDelta + 1; writer.println(offset + " chop frame: " + offsetDelta + ", " + k + " last locals"); } public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) { offset += offsetDelta + 1; writer.println(offset + " append frame: " + offsetDelta); for (int i = 0; i < tags.length; i++) printTypeInfo(tags[i], data[i]); } public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData) { offset += offsetDelta + 1; writer.println(offset + " full frame: " + offsetDelta); writer.println("[locals]"); for (int i = 0; i < localTags.length; i++) printTypeInfo(localTags[i], localData[i]); writer.println("[stack]"); for (int i = 0; i < stackTags.length; i++) printTypeInfo(stackTags[i], stackData[i]); } private void printTypeInfo(int tag, int data) { String msg = null; switch (tag) { case TOP : msg = "top"; break; case INTEGER : msg = "integer"; break; case FLOAT : msg = "float"; break; case DOUBLE : msg = "double"; break; case LONG : msg = "long"; break; case NULL : msg = "null"; break; case THIS : msg = "this"; break; case OBJECT : msg = "object (cpool_index " + data + ")"; break; case UNINIT : msg = "uninitialized (offset " + data + ")"; break; } writer.print(" "); writer.println(msg); } } void shiftPc(int where, int gapSize, boolean exclusive) throws BadBytecode { new Shifter(this, where, gapSize, exclusive).doit(); } static class Shifter extends Walker { private StackMapTable stackMap; private int where, gap; private int position; private byte[] updatedInfo; private boolean exclusive; public Shifter(StackMapTable smt, int where, int gap, boolean exclusive) { super(smt); stackMap = smt; this.where = where; this.gap = gap; this.position = 0; this.updatedInfo = null; this.exclusive = exclusive; } public void doit() throws BadBytecode { parse(); if (updatedInfo != null) stackMap.set(updatedInfo); } public void sameFrame(int pos, int offsetDelta) { update(pos, offsetDelta, 0, 251); } public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { update(pos, offsetDelta, 64, 247); } private void update(int pos, int offsetDelta, int base, int entry) { int oldPos = position; position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); boolean match; if (exclusive) match = oldPos < where && where <= position; else match = oldPos <= where && where < position; if (match) { int newDelta = offsetDelta + gap; position += gap; if (newDelta < 64) info[pos] = (byte)(newDelta + base); else if (offsetDelta < 64) { byte[] newinfo = insertGap(info, pos, 2); newinfo[pos] = (byte)entry; ByteArray.write16bit(newDelta, newinfo, pos + 1); updatedInfo = newinfo; } else ByteArray.write16bit(newDelta, info, pos + 1); } } private static byte[] insertGap(byte[] info, int where, int gap) { int len = info.length; byte[] newinfo = new byte[len + gap]; for (int i = 0; i < len; i++) newinfo[i + (i < where ? 0 : gap)] = info[i]; return newinfo; } public void chopFrame(int pos, int offsetDelta, int k) { update(pos, offsetDelta); } public void appendFrame(int pos, int offsetDelta, int[] tags, int[] data) { update(pos, offsetDelta); } public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData) { update(pos, offsetDelta); } private void update(int pos, int offsetDelta) { int oldPos = position; position = oldPos + offsetDelta + (oldPos == 0 ? 0 : 1); boolean match; if (exclusive) match = oldPos < where && where <= position; else match = oldPos <= where && where < position; if (match) { int newDelta = offsetDelta + gap; ByteArray.write16bit(newDelta, info, pos + 1); position += gap; } } } /** * Undocumented method. Do not use; internal-use only. * *

    This method is for javassist.convert.TransformNew. * It is called to update the stack map table when * the NEW opcode (and the following DUP) is removed. * * @param where the position of the removed NEW opcode. */ public void removeNew(int where) throws CannotCompileException { try { byte[] data = new NewRemover(this.get(), where).doit(); this.set(data); } catch (BadBytecode e) { throw new CannotCompileException("bad stack map table", e); } } static class NewRemover extends SimpleCopy { int posOfNew; public NewRemover(byte[] data, int pos) { super(data); posOfNew = pos; } public void sameLocals(int pos, int offsetDelta, int stackTag, int stackData) { if (stackTag == UNINIT && stackData == posOfNew) super.sameFrame(pos, offsetDelta); else super.sameLocals(pos, offsetDelta, stackTag, stackData); } public void fullFrame(int pos, int offsetDelta, int[] localTags, int[] localData, int[] stackTags, int[] stackData) { int n = stackTags.length - 1; for (int i = 0; i < n; i++) if (stackTags[i] == UNINIT && stackData[i] == posOfNew && stackTags[i + 1] == UNINIT && stackData[i + 1] == posOfNew) { n++; int[] stackTags2 = new int[n - 2]; int[] stackData2 = new int[n - 2]; int k = 0; for (int j = 0; j < n; j++) if (j == i) j++; else { stackTags2[k] = stackTags[j]; stackData2[k++] = stackData[j]; } stackTags = stackTags2; stackData = stackData2; break; } super.fullFrame(pos, offsetDelta, localTags, localData, stackTags, stackData); } } } javassist-3.12.1.ga/src/main/javassist/bytecode/CodeAttribute.java0000644000175000017500000004414211366006772025067 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; /** * Code_attribute. * *

    To browse the code field of * a Code_attribute structure, * use CodeIterator. * * @see CodeIterator */ public class CodeAttribute extends AttributeInfo implements Opcode { /** * The name of this attribute "Code". */ public static final String tag = "Code"; // code[] is stored in AttributeInfo.info. private int maxStack; private int maxLocals; private ExceptionTable exceptions; private ArrayList attributes; /** * Constructs a Code_attribute. * * @param cp constant pool table * @param stack max_stack * @param locals max_locals * @param code code[] * @param etable exception_table[] */ 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 ArrayList(); } /** * Constructs a copy of Code_attribute. * 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); attributes = new ArrayList(); 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)); } info = src.copyCode(cp, classnames, exceptions, this); } 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 ArrayList(); 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 Map 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 BadBytecode * exception is thrown, it is * converted into * RuntimeCopyException. * * @return CodeAttribute 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 copy() * in CodeAttribute. */ public static class RuntimeCopyException extends RuntimeException { /** * Constructs an exception. */ public RuntimeCopyException(String s) { super(s); } } /** * Returns the length of this attribute_info * structure. * The returned value is attribute_length + 6. */ 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()"); } void renameClass(String oldname, String newname) { AttributeInfo.renameClass(attributes, oldname, newname); } void renameClass(Map classnames) { AttributeInfo.renameClass(attributes, classnames); } /** * Returns the name of the class declaring the method including * this code attribute. */ public String getDeclaringClass() { ConstPool cp = getConstPool(); return cp.getClassName(); } /** * Returns max_stack. */ public int getMaxStack() { return maxStack; } /** * Sets max_stack. */ public void setMaxStack(int value) { maxStack = value; } /** * Computes the maximum stack size and sets max_stack * to the computed size. * * @throws BadBytecode if this method fails in computing. * @return the newly computed value of max_stack */ public int computeMaxStack() throws BadBytecode { maxStack = new CodeAnalyzer(this).computeMaxStack(); return maxStack; } /** * Returns max_locals. */ public int getMaxLocals() { return maxLocals; } /** * Sets max_locals. */ public void setMaxLocals(int value) { maxLocals = value; } /** * Returns code_length. */ public int getCodeLength() { return info.length; } /** * Returns code[]. */ public byte[] getCode() { return info; } /** * Sets 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 exception_table[]. */ public ExceptionTable getExceptionTable() { return exceptions; } /** * Returns attributes[]. * It returns a list of AttributeInfo. * A new element can be added to the returned list * and an existing element can be removed from the list. * * @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 AttributeInfo object or null. */ public AttributeInfo getAttribute(String name) { return AttributeInfo.lookup(attributes, name); } /** * Adds a stack map table. If another copy of stack map table * is already contained, the old one is removed. * * @param smt the stack map table added to this code attribute. * If it is null, a new stack map is not added. * Only the old stack map is removed. */ public void setAttribute(StackMapTable smt) { AttributeInfo.remove(attributes, StackMapTable.tag); if (smt != null) attributes.add(smt); } /** * Adds a stack map table for J2ME (CLDC). If another copy of stack map table * is already contained, the old one is removed. * * @param sm the stack map table added to this code attribute. * If it is null, a new stack map is not added. * Only the old stack map is removed. * @since 3.12 */ public void setAttribute(StackMap sm) { AttributeInfo.remove(attributes, StackMap.tag); if (sm != null) attributes.add(sm); } /** * Copies code. */ private byte[] copyCode(ConstPool destCp, Map classnames, ExceptionTable etable, CodeAttribute destCa) throws BadBytecode { int len = getCodeLength(); byte[] newCode = new byte[len]; destCa.info = newCode; LdcEntry ldc = copyCode(this.info, 0, len, this.getConstPool(), newCode, destCp, classnames); return LdcEntry.doit(newCode, ldc, etable, destCa); } 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 { newcode[i] = NOP; newcode[i + 1] = NOP; 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; } static class LdcEntry { LdcEntry next; int where; int index; static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable, CodeAttribute ca) throws BadBytecode { if (ldc != null) code = CodeIterator.changeLdcToLdcW(code, etable, ca, ldc); /* The original code was the following: while (ldc != null) { int where = ldc.where; code = CodeIterator.insertGapCore0(code, where, 1, false, etable, ca); code[where] = (byte)Opcode.LDC_W; ByteArray.write16bit(ldc.index, code, where + 1); ldc = ldc.next; } But this code does not support a large method > 32KB. */ return code; } } /** * Changes the index numbers of the local variables * to append a new parameter. * This method does not update LocalVariableAttribute, * StackMapTable, or StackMap. * These attributes must be explicitly updated. * * @param where the index of the new parameter. * @param size the type size of the new parameter (1 or 2). * * @see LocalVariableAttribute#shiftIndex(int, int) * @see StackMapTable#insertLocal(int, int, int) * @see StackMap#insertLocal(int, int, int) */ public void insertLocalVar(int where, int size) throws BadBytecode { CodeIterator ci = iterator(); while (ci.hasNext()) shiftIndex(ci, where, size); setMaxLocals(getMaxLocals() + size); } /** * @param lessThan If the index of the local variable is * less than this value, it does not change. * Otherwise, the index is increased. * @param delta the indexes of the local variables are * increased by this value. */ private static void shiftIndex(CodeIterator ci, int lessThan, int delta) throws BadBytecode { int index = ci.next(); int opcode = ci.byteAt(index); if (opcode < ILOAD) return; else if (opcode < IASTORE) { if (opcode < ILOAD_0) { // iload, lload, fload, dload, aload shiftIndex8(ci, index, opcode, lessThan, delta); } else if (opcode < IALOAD) { // iload_0, ..., aload_3 shiftIndex0(ci, index, opcode, lessThan, delta, ILOAD_0, ILOAD); } else if (opcode < ISTORE) return; else if (opcode < ISTORE_0) { // istore, lstore, ... shiftIndex8(ci, index, opcode, lessThan, delta); } else { // istore_0, ..., astore_3 shiftIndex0(ci, index, opcode, lessThan, delta, ISTORE_0, ISTORE); } } else if (opcode == IINC) { int var = ci.byteAt(index + 1); if (var < lessThan) return; var += delta; if (var < 0x100) ci.writeByte(var, index + 1); else { int plus = (byte)ci.byteAt(index + 2); int pos = ci.insertExGap(3); ci.writeByte(WIDE, pos - 3); ci.writeByte(IINC, pos - 2); ci.write16bit(var, pos - 1); ci.write16bit(plus, pos + 1); } } else if (opcode == RET) shiftIndex8(ci, index, opcode, lessThan, delta); else if (opcode == WIDE) { int var = ci.u16bitAt(index + 2); if (var < lessThan) return; var += delta; ci.write16bit(var, index + 2); } } private static void shiftIndex8(CodeIterator ci, int index, int opcode, int lessThan, int delta) throws BadBytecode { int var = ci.byteAt(index + 1); if (var < lessThan) return; var += delta; if (var < 0x100) ci.writeByte(var, index + 1); else { int pos = ci.insertExGap(2); ci.writeByte(WIDE, pos - 2); ci.writeByte(opcode, pos - 1); ci.write16bit(var, pos); } } private static void shiftIndex0(CodeIterator ci, int index, int opcode, int lessThan, int delta, int opcode_i_0, int opcode_i) throws BadBytecode { int var = (opcode - opcode_i_0) % 4; if (var < lessThan) return; var += delta; if (var < 4) ci.writeByte(opcode + delta, index); else { opcode = (opcode - opcode_i_0) / 4 + opcode_i; if (var < 0x100) { int pos = ci.insertExGap(1); ci.writeByte(opcode, pos - 1); ci.writeByte(var, pos); } else { int pos = ci.insertExGap(3); ci.writeByte(WIDE, pos - 1); ci.writeByte(opcode, pos); ci.write16bit(var, pos + 1); } } } } javassist-3.12.1.ga/src/main/javassist/bytecode/ByteStream.java0000644000175000017500000001266211371307074024405 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.OutputStream; import java.io.IOException; final class ByteStream extends OutputStream { private byte[] buf; private int count; public ByteStream() { this(32); } public ByteStream(int size) { buf = new byte[size]; count = 0; } public int getPos() { return count; } public int size() { return count; } public void writeBlank(int len) { enlarge(len); count += len; } public void write(byte[] data) { write(data, 0, data.length); } public void write(byte[] data, int off, int len) { enlarge(len); System.arraycopy(data, off, buf, count, len); count += len; } public void write(int b) { enlarge(1); int oldCount = count; buf[oldCount] = (byte)b; count = oldCount + 1; } public void writeShort(int s) { enlarge(2); int oldCount = count; buf[oldCount] = (byte)(s >>> 8); buf[oldCount + 1] = (byte)s; count = oldCount + 2; } public void writeInt(int i) { enlarge(4); int oldCount = count; buf[oldCount] = (byte)(i >>> 24); buf[oldCount + 1] = (byte)(i >>> 16); buf[oldCount + 2] = (byte)(i >>> 8); buf[oldCount + 3] = (byte)i; count = oldCount + 4; } public void writeLong(long i) { enlarge(8); int oldCount = count; buf[oldCount] = (byte)(i >>> 56); buf[oldCount + 1] = (byte)(i >>> 48); buf[oldCount + 2] = (byte)(i >>> 40); buf[oldCount + 3] = (byte)(i >>> 32); buf[oldCount + 4] = (byte)(i >>> 24); buf[oldCount + 5] = (byte)(i >>> 16); buf[oldCount + 6] = (byte)(i >>> 8); buf[oldCount + 7] = (byte)i; count = oldCount + 8; } public void writeFloat(float v) { writeInt(Float.floatToIntBits(v)); } public void writeDouble(double v) { writeLong(Double.doubleToLongBits(v)); } public void writeUTF(String s) { int sLen = s.length(); int pos = count; enlarge(sLen + 2); byte[] buffer = buf; buffer[pos++] = (byte)(sLen >>> 8); buffer[pos++] = (byte)sLen; for (int i = 0; i < sLen; ++i) { char c = s.charAt(i); if (0x01 <= c && c <= 0x7f) buffer[pos++] = (byte)c; else { writeUTF2(s, sLen, i); return; } } count = pos; } private void writeUTF2(String s, int sLen, int offset) { int size = sLen; for (int i = offset; i < sLen; i++) { int c = s.charAt(i); if (c > 0x7ff) size += 2; // 3 bytes code else if (c == 0 || c > 0x7f) ++size; // 2 bytes code } if (size > 65535) throw new RuntimeException( "encoded string too long: " + sLen + size + " bytes"); enlarge(size + 2); int pos = count; byte[] buffer = buf; buffer[pos] = (byte)(size >>> 8); buffer[pos + 1] = (byte)size; pos += 2 + offset; for (int j = offset; j < sLen; ++j) { int c = s.charAt(j); if (0x01 <= c && c <= 0x7f) buffer[pos++] = (byte) c; else if (c > 0x07ff) { buffer[pos] = (byte)(0xe0 | ((c >> 12) & 0x0f)); buffer[pos + 1] = (byte)(0x80 | ((c >> 6) & 0x3f)); buffer[pos + 2] = (byte)(0x80 | (c & 0x3f)); pos += 3; } else { buffer[pos] = (byte)(0xc0 | ((c >> 6) & 0x1f)); buffer[pos + 1] = (byte)(0x80 | (c & 0x3f)); pos += 2; } } count = pos; } public void write(int pos, int value) { buf[pos] = (byte)value; } public void writeShort(int pos, int value) { buf[pos] = (byte)(value >>> 8); buf[pos + 1] = (byte)value; } public void writeInt(int pos, int value) { buf[pos] = (byte)(value >>> 24); buf[pos + 1] = (byte)(value >>> 16); buf[pos + 2] = (byte)(value >>> 8); buf[pos + 3] = (byte)value; } public byte[] toByteArray() { byte[] buf2 = new byte[count]; System.arraycopy(buf, 0, buf2, 0, count); return buf2; } public void writeTo(OutputStream out) throws IOException { out.write(buf, 0, count); } public void enlarge(int delta) { int newCount = count + delta; if (newCount > buf.length) { int newLen = buf.length << 1; byte[] newBuf = new byte[newLen > newCount ? newLen : newCount]; System.arraycopy(buf, 0, newBuf, 0, count); buf = newBuf; } } } javassist-3.12.1.ga/src/main/javassist/bytecode/SyntheticAttribute.java0000644000175000017500000000314010630701321026141 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.IOException; import java.util.Map; /** * Synthetic_attribute. */ public class SyntheticAttribute extends AttributeInfo { /** * The name of this attribute "Synthetic". */ 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. */ 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); } } javassist-3.12.1.ga/src/main/javassist/bytecode/ClassFile.java0000644000175000017500000006156311366572525024210 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; import java.util.Map; import javassist.CannotCompileException; /** * ClassFile represents a Java .class file, which * consists of a constant pool, methods, fields, and attributes. * * @see javassist.CtClass#getClassFile() */ public final class ClassFile { int major, minor; // version number ConstPool constPool; int thisClass; int accessFlags; int superClass; int[] interfaces; ArrayList fields; ArrayList methods; ArrayList attributes; String thisclassname; // not JVM-internal name String[] cachedInterfaces; String cachedSuperclass; /** * The major version number of class files * for JDK 1.1. */ public static final int JAVA_1 = 45; /** * The major version number of class files * for JDK 1.2. */ public static final int JAVA_2 = 46; /** * The major version number of class files * for JDK 1.3. */ public static final int JAVA_3 = 47; /** * The major version number of class files * for JDK 1.4. */ public static final int JAVA_4 = 48; /** * The major version number of class files * for JDK 1.5. */ public static final int JAVA_5 = 49; /** * The major version number of class files * for JDK 1.6. */ public static final int JAVA_6 = 50; /** * The major version number of class files * for JDK 1.7. */ public static final int JAVA_7 = 51; /** * The major version number of class files created * from scratch. The default value is 47 (JDK 1.3) * or 49 (JDK 1.5) if the JVM supports java.lang.StringBuilder. */ public static int MAJOR_VERSION = JAVA_3; static { try { Class.forName("java.lang.StringBuilder"); MAJOR_VERSION = JAVA_5; } catch (Throwable t) {} } /** * 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) { major = MAJOR_VERSION; minor = 0; // JDK 1.3 or later constPool = new ConstPool(classname); thisClass = constPool.getThisClassInfo(); if (isInterface) accessFlags = AccessFlag.INTERFACE | AccessFlag.ABSTRACT; else accessFlags = AccessFlag.SUPER; initSuperclass(superclass); interfaces = null; fields = new ArrayList(); methods = new ArrayList(); thisclassname = classname; attributes = new ArrayList(); attributes.add(new SourceFileAttribute(constPool, getSourcefileName(thisclassname))); } private void initSuperclass(String superclass) { if (superclass != null) { this.superClass = constPool.addClassInfo(superclass); cachedSuperclass = superclass; } else { this.superClass = constPool.addClassInfo("java.lang.Object"); cachedSuperclass = "java.lang.Object"; } } private static String getSourcefileName(String qname) { int index = qname.lastIndexOf('.'); if (index >= 0) qname = qname.substring(index + 1); return qname + ".java"; } /** * Eliminates dead constant pool items. If a method or a field is removed, * the constant pool items used by that method/field become dead items. This * method recreates a constant pool. */ public void compact() { ConstPool cp = compact0(); ArrayList list = methods; int n = list.size(); for (int i = 0; i < n; ++i) { MethodInfo minfo = (MethodInfo)list.get(i); minfo.compact(cp); } list = fields; n = list.size(); for (int i = 0; i < n; ++i) { FieldInfo finfo = (FieldInfo)list.get(i); finfo.compact(cp); } attributes = AttributeInfo.copyAll(attributes, cp); constPool = cp; } private ConstPool compact0() { ConstPool cp = new ConstPool(thisclassname); thisClass = cp.getThisClassInfo(); String sc = getSuperclass(); if (sc != null) superClass = cp.addClassInfo(getSuperclass()); if (interfaces != null) { int n = interfaces.length; for (int i = 0; i < n; ++i) interfaces[i] = cp.addClassInfo(constPool.getClassInfo(interfaces[i])); } return cp; } /** * Discards all attributes, associated with both the class file and the * members such as a code attribute and exceptions attribute. The unused * constant pool entries are also discarded (a new packed constant pool is * constructed). */ public void prune() { ConstPool cp = compact0(); ArrayList newAttributes = new ArrayList(); AttributeInfo invisibleAnnotations = getAttribute(AnnotationsAttribute.invisibleTag); if (invisibleAnnotations != null) { invisibleAnnotations = invisibleAnnotations.copy(cp, null); newAttributes.add(invisibleAnnotations); } AttributeInfo visibleAnnotations = getAttribute(AnnotationsAttribute.visibleTag); if (visibleAnnotations != null) { visibleAnnotations = visibleAnnotations.copy(cp, null); newAttributes.add(visibleAnnotations); } AttributeInfo signature = getAttribute(SignatureAttribute.tag); if (signature != null) { signature = signature.copy(cp, null); newAttributes.add(signature); } ArrayList list = methods; int n = list.size(); for (int i = 0; i < n; ++i) { MethodInfo minfo = (MethodInfo)list.get(i); minfo.prune(cp); } list = fields; n = list.size(); for (int i = 0; i < n; ++i) { FieldInfo finfo = (FieldInfo)list.get(i); finfo.prune(cp); } attributes = newAttributes; constPool = cp; } /** * 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) { if ((acc & AccessFlag.INTERFACE) == 0) acc |= AccessFlag.SUPER; accessFlags = acc; } /** * Returns access and property flags of this nested class. * This method returns -1 if the class is not a nested class. * *

    The returned value is obtained from inner_class_access_flags * of the entry representing this nested class itself * in InnerClasses_attribute>. */ public int getInnerAccessFlags() { InnerClassesAttribute ica = (InnerClassesAttribute)getAttribute(InnerClassesAttribute.tag); if (ica == null) return -1; String name = getName(); int n = ica.tableLength(); for (int i = 0; i < n; ++i) if (name.equals(ica.innerClass(i))) return ica.accessFlags(i); return -1; } /** * 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() { if (cachedSuperclass == null) cachedSuperclass = constPool.getClassInfo(superClass); return cachedSuperclass; } /** * Returns the index of the constant pool entry representing the super * class. */ public int getSuperclassId() { return superClass; } /** * Sets the super class. * *

    * The new super class should inherit from the old super class. * This method modifies constructors so that they call constructors declared * in the new super class. */ public void setSuperclass(String superclass) throws CannotCompileException { if (superclass == null) superclass = "java.lang.Object"; try { this.superClass = constPool.addClassInfo(superclass); ArrayList list = methods; int n = list.size(); for (int i = 0; i < n; ++i) { MethodInfo minfo = (MethodInfo)list.get(i); minfo.setSuperclass(superclass); } } catch (BadBytecode e) { throw new CannotCompileException(e); } cachedSuperclass = superclass; } /** * Replaces all occurrences of a class name in the class file. * *

    * 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) { ArrayList 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); AttributeInfo.renameClass(attributes, 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)); AttributeInfo.renameClass(minfo.getAttributes(), 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)); AttributeInfo.renameClass(finfo.getAttributes(), 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 java/lang/Object. * @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); AttributeInfo.renameClass(attributes, classnames); ArrayList 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)); AttributeInfo.renameClass(minfo.getAttributes(), 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)); AttributeInfo.renameClass(finfo.getAttributes(), classnames); } } /** * Returns the names of the interfaces implemented by the class. * The returned array is read only. */ public String[] getInterfaces() { if (cachedInterfaces != null) return cachedInterfaces; String[] rtn = null; if (interfaces == null) rtn = 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]); rtn = list; } cachedInterfaces = rtn; return rtn; } /** * Sets the interfaces. * * @param nameList * the names of the interfaces. */ public void setInterfaces(String[] nameList) { cachedInterfaces = null; 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) { cachedInterfaces = null; 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 FieldInfo. * @see FieldInfo */ public List getFields() { return fields; } /** * Appends a field to the class. * * @throws DuplicateMemberException when the field is already included. */ public void addField(FieldInfo finfo) throws DuplicateMemberException { testExistingField(finfo.getName(), finfo.getDescriptor()); fields.add(finfo); } /** * Just appends a field to the class. * It does not check field duplication. * Use this method only when minimizing performance overheads * is seriously required. * * @since 3.13 */ public final void addField2(FieldInfo finfo) { fields.add(finfo); } private void testExistingField(String name, String descriptor) throws DuplicateMemberException { ListIterator it = fields.listIterator(0); while (it.hasNext()) { FieldInfo minfo = (FieldInfo)it.next(); if (minfo.getName().equals(name)) throw new DuplicateMemberException("duplicate field: " + name); } } /** * Returns all the methods declared in the class. * * @return a list of MethodInfo. * @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) { ArrayList 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. * If there is a bridge method with the same name and signature, * then the bridge method is removed before a new method is added. * * @throws DuplicateMemberException when the method is already included. */ public void addMethod(MethodInfo minfo) throws DuplicateMemberException { testExistingMethod(minfo); methods.add(minfo); } /** * Just appends a method to the class. * It does not check method duplication or remove a bridge method. * Use this method only when minimizing performance overheads * is seriously required. * * @since 3.13 */ public final void addMethod2(MethodInfo minfo) { methods.add(minfo); } private void testExistingMethod(MethodInfo newMinfo) throws DuplicateMemberException { String name = newMinfo.getName(); String descriptor = newMinfo.getDescriptor(); ListIterator it = methods.listIterator(0); while (it.hasNext()) if (isDuplicated(newMinfo, name, descriptor, (MethodInfo)it.next(), it)) throw new DuplicateMemberException("duplicate method: " + name + " in " + this.getName()); } private static boolean isDuplicated(MethodInfo newMethod, String newName, String newDesc, MethodInfo minfo, ListIterator it) { if (!minfo.getName().equals(newName)) return false; String desc = minfo.getDescriptor(); if (!Descriptor.eqParamTypes(desc, newDesc)) return false; if (desc.equals(newDesc)) { if (notBridgeMethod(minfo)) return true; else { it.remove(); return false; } } else return notBridgeMethod(minfo) && notBridgeMethod(newMethod); } /* For a bridge method, see Sec. 15.12.4.5 of JLS 3rd Ed. */ private static boolean notBridgeMethod(MethodInfo minfo) { return (minfo.getAccessFlags() & AccessFlag.BRIDGE) == 0; } /** * Returns all the attributes. The returned List object * is shared with this object. If you add a new attribute to the list, * the attribute is also added to the classs file represented by this * object. If you remove an attribute from the list, it is also removed * from the class file. * * @return a list of AttributeInfo objects. * @see AttributeInfo */ public List getAttributes() { return attributes; } /** * Returns the attribute with the specified name. If there are multiple * attributes with that name, this method returns either of them. It * returns null if the specified attributed is not found. * * @param name attribute name * @see #getAttributes() */ public AttributeInfo getAttribute(String name) { ArrayList 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. * * @see #getAttributes() */ 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("bad magic number: " + Integer.toHexString(magic)); minor = in.readUnsignedShort(); major = 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 ArrayList(); for (i = 0; i < n; ++i) addField2(new FieldInfo(cp, in)); n = in.readUnsignedShort(); methods = new ArrayList(); for (i = 0; i < n; ++i) addMethod2(new MethodInfo(cp, in)); attributes = new ArrayList(); 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(minor); // minor version out.writeShort(major); // major 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]); ArrayList 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); } /** * Get the Major version. * * @return the major version */ public int getMajorVersion() { return major; } /** * Set the major version. * * @param major * the major version */ public void setMajorVersion(int major) { this.major = major; } /** * Get the minor version. * * @return the minor version */ public int getMinorVersion() { return minor; } /** * Set the minor version. * * @param minor * the minor version */ public void setMinorVersion(int minor) { this.minor = minor; } /** * Sets the major and minor version to Java 5. * * If the major version is older than 49, Java 5 * extensions such as annotations are ignored * by the JVM. */ public void setVersionToJava5() { this.major = 49; this.minor = 0; } } javassist-3.12.1.ga/src/main/javassist/bytecode/EnclosingMethodAttribute.java0000644000175000017500000001034110630701321027252 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.IOException; import java.util.Map; /** * EnclosingMethod_attribute. */ public class EnclosingMethodAttribute extends AttributeInfo { /** * The name of this attribute "EnclosingMethod". */ public static final String tag = "EnclosingMethod"; EnclosingMethodAttribute(ConstPool cp, int n, DataInputStream in) throws IOException { super(cp, n, in); } /** * Constructs an EnclosingMethod attribute. * * @param cp a constant pool table. * @param className the name of the innermost enclosing class. * @param methodName the name of the enclosing method. * @param methodDesc the descriptor of the enclosing method. */ public EnclosingMethodAttribute(ConstPool cp, String className, String methodName, String methodDesc) { super(cp, tag); int ci = cp.addClassInfo(className); int ni = cp.addNameAndTypeInfo(methodName, methodDesc); byte[] bvalue = new byte[4]; bvalue[0] = (byte)(ci >>> 8); bvalue[1] = (byte)ci; bvalue[2] = (byte)(ni >>> 8); bvalue[3] = (byte)ni; set(bvalue); } /** * Constructs an EnclosingMethod attribute. * The value of method_index is set to 0. * * @param cp a constant pool table. * @param className the name of the innermost enclosing class. */ public EnclosingMethodAttribute(ConstPool cp, String className) { super(cp, tag); int ci = cp.addClassInfo(className); int ni = 0; byte[] bvalue = new byte[4]; bvalue[0] = (byte)(ci >>> 8); bvalue[1] = (byte)ci; bvalue[2] = (byte)(ni >>> 8); bvalue[3] = (byte)ni; set(bvalue); } /** * Returns the value of class_index. */ public int classIndex() { return ByteArray.readU16bit(get(), 0); } /** * Returns the value of method_index. */ public int methodIndex() { return ByteArray.readU16bit(get(), 2); } /** * Returns the name of the class specified by class_index. */ public String className() { return getConstPool().getClassInfo(classIndex()); } /** * Returns the method name specified by method_index. */ public String methodName() { ConstPool cp = getConstPool(); int mi = methodIndex(); int ni = cp.getNameAndTypeName(mi); return cp.getUtf8Info(ni); } /** * Returns the method descriptor specified by method_index. */ public String methodDescriptor() { ConstPool cp = getConstPool(); int mi = methodIndex(); int ti = cp.getNameAndTypeDescriptor(mi); return cp.getUtf8Info(ti); } /** * Makes a copy. Class names are replaced according to the * given Map 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) { if (methodIndex() == 0) return new EnclosingMethodAttribute(newCp, className()); else return new EnclosingMethodAttribute(newCp, className(), methodName(), methodDescriptor()); } } javassist-3.12.1.ga/src/main/javassist/bytecode/Mnemonic.java0000644000175000017500000001617110630701321024060 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; /** * JVM Instruction Names. * *

    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). * *

    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*/ }; } javassist-3.12.1.ga/src/main/javassist/bytecode/InstructionPrinter.java0000644000175000017500000002303011130557620026177 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.PrintStream; import javassist.CtMethod; /** * Simple utility class for printing the instructions of a method. * * @author Jason T. Greene */ public class InstructionPrinter implements Opcode { private final static String opcodes[] = Mnemonic.OPCODE; private final PrintStream stream; public InstructionPrinter(PrintStream stream) { this.stream = stream; } public static void print(CtMethod method, PrintStream stream) { (new InstructionPrinter(stream)).print(method); } public void print(CtMethod method) { MethodInfo info = method.getMethodInfo2(); ConstPool pool = info.getConstPool(); CodeAttribute code = info.getCodeAttribute(); if (code == null) return; CodeIterator iterator = code.iterator(); while (iterator.hasNext()) { int pos; try { pos = iterator.next(); } catch (BadBytecode e) { throw new RuntimeException(e); } stream.println(pos + ": " + instructionString(iterator, pos, pool)); } } public static String instructionString(CodeIterator iter, int pos, ConstPool pool) { int opcode = iter.byteAt(pos); if (opcode > opcodes.length || opcode < 0) throw new IllegalArgumentException("Invalid opcode, opcode: " + opcode + " pos: "+ pos); String opstring = opcodes[opcode]; switch (opcode) { case BIPUSH: return opstring + " " + iter.byteAt(pos + 1); case SIPUSH: return opstring + " " + iter.s16bitAt(pos + 1); case LDC: return opstring + " " + ldc(pool, iter.byteAt(pos + 1)); case LDC_W : case LDC2_W : return opstring + " " + ldc(pool, iter.u16bitAt(pos + 1)); case ILOAD: case LLOAD: case FLOAD: case DLOAD: case ALOAD: case ISTORE: case LSTORE: case FSTORE: case DSTORE: case ASTORE: return opstring + " " + iter.byteAt(pos + 1); case IFEQ: case IFGE: case IFGT: case IFLE: case IFLT: case IFNE: case IFNONNULL: case IFNULL: case IF_ACMPEQ: case IF_ACMPNE: case IF_ICMPEQ: case IF_ICMPGE: case IF_ICMPGT: case IF_ICMPLE: case IF_ICMPLT: case IF_ICMPNE: return opstring + " " + (iter.s16bitAt(pos + 1) + pos); case IINC: return opstring + " " + iter.byteAt(pos + 1); case GOTO: case JSR: return opstring + " " + (iter.s16bitAt(pos + 1) + pos); case RET: return opstring + " " + iter.byteAt(pos + 1); case TABLESWITCH: return tableSwitch(iter, pos); case LOOKUPSWITCH: return lookupSwitch(iter, pos); case GETSTATIC: case PUTSTATIC: case GETFIELD: case PUTFIELD: return opstring + " " + fieldInfo(pool, iter.u16bitAt(pos + 1)); case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKESTATIC: return opstring + " " + methodInfo(pool, iter.u16bitAt(pos + 1)); case INVOKEINTERFACE: return opstring + " " + interfaceMethodInfo(pool, iter.u16bitAt(pos + 1)); case 186: throw new RuntimeException("Bad opcode 186"); case NEW: return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); case NEWARRAY: return opstring + " " + arrayInfo(iter.byteAt(pos + 1)); case ANEWARRAY: case CHECKCAST: return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); case WIDE: return wide(iter, pos); case MULTIANEWARRAY: return opstring + " " + classInfo(pool, iter.u16bitAt(pos + 1)); case GOTO_W: case JSR_W: return opstring + " " + (iter.s32bitAt(pos + 1)+ pos); default: return opstring; } } private static String wide(CodeIterator iter, int pos) { int opcode = iter.byteAt(pos + 1); int index = iter.u16bitAt(pos + 2); switch (opcode) { case ILOAD: case LLOAD: case FLOAD: case DLOAD: case ALOAD: case ISTORE: case LSTORE: case FSTORE: case DSTORE: case ASTORE: case IINC: case RET: return opcodes[opcode] + " " + index; default: throw new RuntimeException("Invalid WIDE operand"); } } private static String arrayInfo(int type) { switch (type) { case T_BOOLEAN: return "boolean"; case T_CHAR: return "char"; case T_BYTE: return "byte"; case T_SHORT: return "short"; case T_INT: return "int"; case T_LONG: return "long"; case T_FLOAT: return "float"; case T_DOUBLE: return "double"; default: throw new RuntimeException("Invalid array type"); } } private static String classInfo(ConstPool pool, int index) { return "#" + index + " = Class " + pool.getClassInfo(index); } private static String interfaceMethodInfo(ConstPool pool, int index) { return "#" + index + " = Method " + pool.getInterfaceMethodrefClassName(index) + "." + pool.getInterfaceMethodrefName(index) + "(" + pool.getInterfaceMethodrefType(index) + ")"; } private static String methodInfo(ConstPool pool, int index) { return "#" + index + " = Method " + pool.getMethodrefClassName(index) + "." + pool.getMethodrefName(index) + "(" + pool.getMethodrefType(index) + ")"; } private static String fieldInfo(ConstPool pool, int index) { return "#" + index + " = Field " + pool.getFieldrefClassName(index) + "." + pool.getFieldrefName(index) + "(" + pool.getFieldrefType(index) + ")"; } private static String lookupSwitch(CodeIterator iter, int pos) { StringBuffer buffer = new StringBuffer("lookupswitch {\n"); int index = (pos & ~3) + 4; // default buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n"); int npairs = iter.s32bitAt(index += 4); int end = npairs * 8 + (index += 4); for (; index < end; index += 8) { int match = iter.s32bitAt(index); int target = iter.s32bitAt(index + 4) + pos; buffer.append("\t\t").append(match).append(": ").append(target).append("\n"); } buffer.setCharAt(buffer.length() - 1, '}'); return buffer.toString(); } private static String tableSwitch(CodeIterator iter, int pos) { StringBuffer buffer = new StringBuffer("tableswitch {\n"); int index = (pos & ~3) + 4; // default buffer.append("\t\tdefault: ").append(pos + iter.s32bitAt(index)).append("\n"); int low = iter.s32bitAt(index += 4); int high = iter.s32bitAt(index += 4); int end = (high - low + 1) * 4 + (index += 4); // Offset table for (int key = low; index < end; index += 4, key++) { int target = iter.s32bitAt(index) + pos; buffer.append("\t\t").append(key).append(": ").append(target).append("\n"); } buffer.setCharAt(buffer.length() - 1, '}'); return buffer.toString(); } private static String ldc(ConstPool pool, int index) { int tag = pool.getTag(index); switch (tag) { case ConstPool.CONST_String: return "#" + index + " = \"" + pool.getStringInfo(index) + "\""; case ConstPool.CONST_Integer: return "#" + index + " = int " + pool.getIntegerInfo(index); case ConstPool.CONST_Float: return "#" + index + " = float " + pool.getFloatInfo(index); case ConstPool.CONST_Long: return "#" + index + " = long " + pool.getLongInfo(index); case ConstPool.CONST_Double: return "#" + index + " = int " + pool.getDoubleInfo(index); case ConstPool.CONST_Class: return classInfo(pool, index); default: throw new RuntimeException("bad LDC: " + tag); } } } javassist-3.12.1.ga/src/main/javassist/bytecode/AnnotationsAttribute.java0000644000175000017500000005077411262447632026521 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.util.Map; import java.io.IOException; import java.io.DataInputStream; import java.io.ByteArrayOutputStream; import javassist.bytecode.annotation.*; /** * A class representing * RuntimeVisibleAnnotations_attribute and * RuntimeInvisibleAnnotations_attribute. * *

    To obtain an AnnotationAttribute object, invoke * getAttribute(AnnotationsAttribute.visibleTag) * in ClassFile, MethodInfo, * or FieldInfo. The obtained attribute is a * runtime visible annotations attribute. * If the parameter is * AnnotationAttribute.invisibleTag, then the obtained * attribute is a runtime invisible one. * *

    For example, * *

       * import javassist.bytecode.annotation.Annotation;
       *    :
       * CtMethod m = ... ;
       * MethodInfo minfo = m.getMethodInfo();
       * AnnotationsAttribute attr = (AnnotationsAttribute)
       *         minfo.getAttribute(AnnotationsAttribute.invisibleTag);
       * Annotation an = attr.getAnnotation("Author");
       * String s = ((StringMemberValue)an.getMemberValue("name")).getValue();
       * System.out.println("@Author(name=" + s + ")");
       * 
    * *

    This code snippet retrieves an annotation of the type Author * from the MethodInfo object specified by minfo. * Then, it prints the value of name in Author. * *

    If the annotation type Author is annotated by a meta annotation: * *

       * @Retention(RetentionPolicy.RUNTIME)
       * 
    * *

    Then Author is visible at runtime. Therefore, the third * statement of the code snippet above must be changed into: * *

       * AnnotationsAttribute attr = (AnnotationsAttribute)
       *         minfo.getAttribute(AnnotationsAttribute.visibleTag);
       * 
    * *

    The attribute tag must be visibleTag instead of * invisibleTag. * *

    If the member value of an annotation is not specified, the default value * is used as that member value. If so, getMemberValue() in * Annotation returns null * since the default value is not included in the * AnnotationsAttribute. It is included in the * AnnotationDefaultAttribute of the method declared in the * annotation type. * *

    If you want to record a new AnnotationAttribute object, execute the * following snippet: * *

       * ClassFile cf = ... ;
       * ConstPool cp = cf.getConstPool();
       * AnnotationsAttribute attr
       *     = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag);
       * Annotation a = new Annotation("Author", cp);
       * a.addMemberValue("name", new StringMemberValue("Chiba", cp));
       * attr.setAnnotation(a);
       * cf.addAttribute(attr);
       * cf.setVersionToJava5();
       * 
    * *

    The last statement is necessary if the class file was produced by * Javassist or JDK 1.4. Otherwise, it is not necessary. * * @see AnnotationDefaultAttribute * @see javassist.bytecode.annotation.Annotation */ public class AnnotationsAttribute extends AttributeInfo { /** * The name of the RuntimeVisibleAnnotations attribute. */ public static final String visibleTag = "RuntimeVisibleAnnotations"; /** * The name of the RuntimeInvisibleAnnotations attribute. */ public static final String invisibleTag = "RuntimeInvisibleAnnotations"; /** * Constructs a Runtime(In)VisibleAnnotations_attribute. * * @param cp constant pool * @param attrname attribute name (visibleTag or * invisibleTag). * @param info the contents of this attribute. It does not * include attribute_name_index or * attribute_length. */ public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) { super(cp, attrname, info); } /** * Constructs an empty * Runtime(In)VisibleAnnotations_attribute. * A new annotation can be later added to the created attribute * by setAnnotations(). * * @param cp constant pool * @param attrname attribute name (visibleTag or * invisibleTag). * @see #setAnnotations(Annotation[]) */ public AnnotationsAttribute(ConstPool cp, String attrname) { this(cp, attrname, new byte[] { 0, 0 }); } /** * @param n the attribute name. */ AnnotationsAttribute(ConstPool cp, int n, DataInputStream in) throws IOException { super(cp, n, in); } /** * Returns num_annotations. */ public int numAnnotations() { return ByteArray.readU16bit(info, 0); } /** * Copies this attribute and returns a new copy. */ public AttributeInfo copy(ConstPool newCp, Map classnames) { Copier copier = new Copier(info, constPool, newCp, classnames); try { copier.annotationArray(); return new AnnotationsAttribute(newCp, getName(), copier.close()); } catch (Exception e) { throw new RuntimeException(e.toString()); } } /** * Parses the annotations and returns a data structure representing * the annotation with the specified type. See also * getAnnotations() as to the returned data structure. * * @param type the annotation type. * @return null if the specified annotation type is not included. * @see #getAnnotations() */ public Annotation getAnnotation(String type) { Annotation[] annotations = getAnnotations(); for (int i = 0; i < annotations.length; i++) { if (annotations[i].getTypeName().equals(type)) return annotations[i]; } return null; } /** * Adds an annotation. If there is an annotation with the same type, * it is removed before the new annotation is added. * * @param annotation the added annotation. */ public void addAnnotation(Annotation annotation) { String type = annotation.getTypeName(); Annotation[] annotations = getAnnotations(); for (int i = 0; i < annotations.length; i++) { if (annotations[i].getTypeName().equals(type)) { annotations[i] = annotation; setAnnotations(annotations); return; } } Annotation[] newlist = new Annotation[annotations.length + 1]; System.arraycopy(annotations, 0, newlist, 0, annotations.length); newlist[annotations.length] = annotation; setAnnotations(newlist); } /** * Parses the annotations and returns a data structure representing * that parsed annotations. Note that changes of the node values of the * returned tree are not reflected on the annotations represented by * this object unless the tree is copied back to this object by * setAnnotations(). * * @see #setAnnotations(Annotation[]) */ public Annotation[] getAnnotations() { try { return new Parser(info, constPool).parseAnnotations(); } catch (Exception e) { throw new RuntimeException(e.toString()); } } /** * Changes the annotations represented by this object according to * the given array of Annotation objects. * * @param annotations the data structure representing the * new annotations. */ public void setAnnotations(Annotation[] annotations) { ByteArrayOutputStream output = new ByteArrayOutputStream(); AnnotationsWriter writer = new AnnotationsWriter(output, constPool); try { int n = annotations.length; writer.numAnnotations(n); for (int i = 0; i < n; ++i) annotations[i].write(writer); writer.close(); } catch (IOException e) { throw new RuntimeException(e); // should never reach here. } set(output.toByteArray()); } /** * Changes the annotations. A call to this method is equivalent to: *

      setAnnotations(new Annotation[] { annotation })
    * * @param annotation the data structure representing * the new annotation. */ public void setAnnotation(Annotation annotation) { setAnnotations(new Annotation[] { annotation }); } /** * Returns a string representation of this object. */ public String toString() { Annotation[] a = getAnnotations(); StringBuffer sbuf = new StringBuffer(); int i = 0; while (i < a.length) { sbuf.append(a[i++].toString()); if (i != a.length) sbuf.append(", "); } return sbuf.toString(); } static class Walker { byte[] info; Walker(byte[] attrInfo) { info = attrInfo; } final void parameters() throws Exception { int numParam = info[0] & 0xff; parameters(numParam, 1); } void parameters(int numParam, int pos) throws Exception { for (int i = 0; i < numParam; ++i) pos = annotationArray(pos); } final void annotationArray() throws Exception { annotationArray(0); } final int annotationArray(int pos) throws Exception { int num = ByteArray.readU16bit(info, pos); return annotationArray(pos + 2, num); } int annotationArray(int pos, int num) throws Exception { for (int i = 0; i < num; ++i) pos = annotation(pos); return pos; } final int annotation(int pos) throws Exception { int type = ByteArray.readU16bit(info, pos); int numPairs = ByteArray.readU16bit(info, pos + 2); return annotation(pos + 4, type, numPairs); } int annotation(int pos, int type, int numPairs) throws Exception { for (int j = 0; j < numPairs; ++j) pos = memberValuePair(pos); return pos; } final int memberValuePair(int pos) throws Exception { int nameIndex = ByteArray.readU16bit(info, pos); return memberValuePair(pos + 2, nameIndex); } int memberValuePair(int pos, int nameIndex) throws Exception { return memberValue(pos); } final int memberValue(int pos) throws Exception { int tag = info[pos] & 0xff; if (tag == 'e') { int typeNameIndex = ByteArray.readU16bit(info, pos + 1); int constNameIndex = ByteArray.readU16bit(info, pos + 3); enumMemberValue(typeNameIndex, constNameIndex); return pos + 5; } else if (tag == 'c') { int index = ByteArray.readU16bit(info, pos + 1); classMemberValue(index); return pos + 3; } else if (tag == '@') return annotationMemberValue(pos + 1); else if (tag == '[') { int num = ByteArray.readU16bit(info, pos + 1); return arrayMemberValue(pos + 3, num); } else { // primitive types or String. int index = ByteArray.readU16bit(info, pos + 1); constValueMember(tag, index); return pos + 3; } } void constValueMember(int tag, int index) throws Exception {} void enumMemberValue(int typeNameIndex, int constNameIndex) throws Exception { } void classMemberValue(int index) throws Exception {} int annotationMemberValue(int pos) throws Exception { return annotation(pos); } int arrayMemberValue(int pos, int num) throws Exception { for (int i = 0; i < num; ++i) { pos = memberValue(pos); } return pos; } } static class Copier extends Walker { ByteArrayOutputStream output; AnnotationsWriter writer; ConstPool srcPool, destPool; Map classnames; /** * Constructs a copier. This copier renames some class names * into the new names specified by map when it copies * an annotation attribute. * * @param info the source attribute. * @param src the constant pool of the source class. * @param dest the constant pool of the destination class. * @param map pairs of replaced and substituted class names. * It can be null. */ Copier(byte[] info, ConstPool src, ConstPool dest, Map map) { super(info); output = new ByteArrayOutputStream(); writer = new AnnotationsWriter(output, dest); srcPool = src; destPool = dest; classnames = map; } byte[] close() throws IOException { writer.close(); return output.toByteArray(); } void parameters(int numParam, int pos) throws Exception { writer.numParameters(numParam); super.parameters(numParam, pos); } int annotationArray(int pos, int num) throws Exception { writer.numAnnotations(num); return super.annotationArray(pos, num); } int annotation(int pos, int type, int numPairs) throws Exception { writer.annotation(copy(type), numPairs); return super.annotation(pos, type, numPairs); } int memberValuePair(int pos, int nameIndex) throws Exception { writer.memberValuePair(copy(nameIndex)); return super.memberValuePair(pos, nameIndex); } void constValueMember(int tag, int index) throws Exception { writer.constValueIndex(tag, copy(index)); super.constValueMember(tag, index); } void enumMemberValue(int typeNameIndex, int constNameIndex) throws Exception { writer.enumConstValue(copy(typeNameIndex), copy(constNameIndex)); super.enumMemberValue(typeNameIndex, constNameIndex); } void classMemberValue(int index) throws Exception { writer.classInfoIndex(copy(index)); super.classMemberValue(index); } int annotationMemberValue(int pos) throws Exception { writer.annotationValue(); return super.annotationMemberValue(pos); } int arrayMemberValue(int pos, int num) throws Exception { writer.arrayValue(num); return super.arrayMemberValue(pos, num); } /** * Copies a constant pool entry into the destination constant pool * and returns the index of the copied entry. * * @param srcIndex the index of the copied entry into the source * constant pool. * @return the index of the copied item into the destination * constant pool. */ int copy(int srcIndex) { return srcPool.copy(srcIndex, destPool, classnames); } } static class Parser extends Walker { ConstPool pool; Annotation[][] allParams; // all parameters Annotation[] allAnno; // all annotations Annotation currentAnno; // current annotation MemberValue currentMember; // current member /** * Constructs a parser. This parser constructs a parse tree of * the annotations. * * @param info the attribute. * @param src the constant pool. */ Parser(byte[] info, ConstPool cp) { super(info); pool = cp; } Annotation[][] parseParameters() throws Exception { parameters(); return allParams; } Annotation[] parseAnnotations() throws Exception { annotationArray(); return allAnno; } MemberValue parseMemberValue() throws Exception { memberValue(0); return currentMember; } void parameters(int numParam, int pos) throws Exception { Annotation[][] params = new Annotation[numParam][]; for (int i = 0; i < numParam; ++i) { pos = annotationArray(pos); params[i] = allAnno; } allParams = params; } int annotationArray(int pos, int num) throws Exception { Annotation[] array = new Annotation[num]; for (int i = 0; i < num; ++i) { pos = annotation(pos); array[i] = currentAnno; } allAnno = array; return pos; } int annotation(int pos, int type, int numPairs) throws Exception { currentAnno = new Annotation(type, pool); return super.annotation(pos, type, numPairs); } int memberValuePair(int pos, int nameIndex) throws Exception { pos = super.memberValuePair(pos, nameIndex); currentAnno.addMemberValue(nameIndex, currentMember); return pos; } void constValueMember(int tag, int index) throws Exception { MemberValue m; ConstPool cp = pool; switch (tag) { case 'B' : m = new ByteMemberValue(index, cp); break; case 'C' : m = new CharMemberValue(index, cp); break; case 'D' : m = new DoubleMemberValue(index, cp); break; case 'F' : m = new FloatMemberValue(index, cp); break; case 'I' : m = new IntegerMemberValue(index, cp); break; case 'J' : m = new LongMemberValue(index, cp); break; case 'S' : m = new ShortMemberValue(index, cp); break; case 'Z' : m = new BooleanMemberValue(index, cp); break; case 's' : m = new StringMemberValue(index, cp); break; default : throw new RuntimeException("unknown tag:" + tag); } currentMember = m; super.constValueMember(tag, index); } void enumMemberValue(int typeNameIndex, int constNameIndex) throws Exception { currentMember = new EnumMemberValue(typeNameIndex, constNameIndex, pool); super.enumMemberValue(typeNameIndex, constNameIndex); } void classMemberValue(int index) throws Exception { currentMember = new ClassMemberValue(index, pool); super.classMemberValue(index); } int annotationMemberValue(int pos) throws Exception { Annotation anno = currentAnno; pos = super.annotationMemberValue(pos); currentMember = new AnnotationMemberValue(currentAnno, pool); currentAnno = anno; return pos; } int arrayMemberValue(int pos, int num) throws Exception { ArrayMemberValue amv = new ArrayMemberValue(pool); MemberValue[] elements = new MemberValue[num]; for (int i = 0; i < num; ++i) { pos = memberValue(pos); elements[i] = currentMember; } amv.setValue(elements); currentMember = amv; return pos; } } } javassist-3.12.1.ga/src/main/javassist/bytecode/DuplicateMemberException.java0000644000175000017500000000203110630701321027222 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import javassist.CannotCompileException; /** * An exception thrown when adding a duplicate member is requested. * * @see ClassFile#addMethod(MethodInfo) * @see ClassFile#addField(FieldInfo) */ public class DuplicateMemberException extends CannotCompileException { public DuplicateMemberException(String msg) { super(msg); } } javassist-3.12.1.ga/src/main/javassist/bytecode/AttributeInfo.java0000644000175000017500000002220511366006523025076 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.Map; import java.util.ArrayList; import java.util.ListIterator; import java.util.List; import java.util.Iterator; // Note: if you define a new subclass of AttributeInfo, then // update AttributeInfo.read(), .copy(), and (maybe) write(). /** * attribute_info 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 attribute_info structure. * * @param cp constant pool table * @param attrname attribute name * @param attrinfo info field * of attribute_info 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.charAt(0) < 'L') { if (nameStr.equals(AnnotationDefaultAttribute.tag)) return new AnnotationDefaultAttribute(cp, name, in); else if (nameStr.equals(CodeAttribute.tag)) return new CodeAttribute(cp, name, in); else if (nameStr.equals(ConstantAttribute.tag)) return new ConstantAttribute(cp, name, in); else if (nameStr.equals(DeprecatedAttribute.tag)) return new DeprecatedAttribute(cp, name, in); else if (nameStr.equals(EnclosingMethodAttribute.tag)) return new EnclosingMethodAttribute(cp, name, in); else if (nameStr.equals(ExceptionsAttribute.tag)) return new ExceptionsAttribute(cp, name, in); else if (nameStr.equals(InnerClassesAttribute.tag)) return new InnerClassesAttribute(cp, name, in); } else { /* Note that the names of Annotations attributes begin with 'R'. */ if (nameStr.equals(LineNumberAttribute.tag)) return new LineNumberAttribute(cp, name, in); else if (nameStr.equals(LocalVariableAttribute.tag)) return new LocalVariableAttribute(cp, name, in); else if (nameStr.equals(LocalVariableTypeAttribute.tag)) return new LocalVariableTypeAttribute(cp, name, in); else if (nameStr.equals(AnnotationsAttribute.visibleTag) || nameStr.equals(AnnotationsAttribute.invisibleTag)) { // RuntimeVisibleAnnotations or RuntimeInvisibleAnnotations return new AnnotationsAttribute(cp, name, in); } else if (nameStr.equals(ParameterAnnotationsAttribute.visibleTag) || nameStr.equals(ParameterAnnotationsAttribute.invisibleTag)) return new ParameterAnnotationsAttribute(cp, name, in); else if (nameStr.equals(SignatureAttribute.tag)) return new SignatureAttribute(cp, name, in); else if (nameStr.equals(SourceFileAttribute.tag)) return new SourceFileAttribute(cp, name, in); else if (nameStr.equals(SyntheticAttribute.tag)) return new SyntheticAttribute(cp, name, in); else if (nameStr.equals(StackMap.tag)) return new StackMap(cp, name, in); else if (nameStr.equals(StackMapTable.tag)) return new StackMapTable(cp, name, in); } 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 attribute_info * structure. * The returned value is attribute_length + 6. */ public int length() { return info.length + 6; } /** * Returns the info field * of this attribute_info structure. * *

    This method is not available if the object is an instance * of CodeAttribute. */ public byte[] get() { return info; } /** * Sets the info field * of this attribute_info structure. * *

    This method is not available if the object is an instance * of CodeAttribute. */ public void set(byte[] newinfo) { info = newinfo; } /** * Makes a copy. Class names are replaced according to the * given Map 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[] srcInfo = info; byte[] newInfo = new byte[s]; for (int i = 0; i < s; ++i) newInfo[i] = srcInfo[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(ArrayList 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(ArrayList 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 synchronized void remove(ArrayList 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 void writeAll(ArrayList 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); } } static ArrayList copyAll(ArrayList list, ConstPool cp) { if (list == null) return null; ArrayList newList = new ArrayList(); int n = list.size(); for (int i = 0; i < n; ++i) { AttributeInfo attr = (AttributeInfo)list.get(i); newList.add(attr.copy(cp, null)); } return newList; } /* The following two methods are used to implement * ClassFile.renameClass(). * Only CodeAttribute and LocalVariableAttribute override * this method. */ void renameClass(String oldname, String newname) {} void renameClass(Map classnames) {} static void renameClass(List attributes, String oldname, String newname) { Iterator iterator = attributes.iterator(); while (iterator.hasNext()) { AttributeInfo ai = (AttributeInfo)iterator.next(); ai.renameClass(oldname, newname); } } static void renameClass(List attributes, Map classnames) { Iterator iterator = attributes.iterator(); while (iterator.hasNext()) { AttributeInfo ai = (AttributeInfo)iterator.next(); ai.renameClass(classnames); } } } javassist-3.12.1.ga/src/main/javassist/bytecode/StackMap.java0000644000175000017500000003735011361635765024044 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.IOException; import java.util.Map; import javassist.CannotCompileException; import javassist.bytecode.StackMapTable.InsertLocal; import javassist.bytecode.StackMapTable.NewRemover; import javassist.bytecode.StackMapTable.Shifter; /** * Another stack_map attribute defined in CLDC 1.1 for J2ME. * *

    This is an entry in the attributes table of a Code attribute. * It was introduced by J2ME CLDC 1.1 (JSR 139) for pre-verification. * *

    According to the CLDC specification, the sizes of some fields are not 16bit * but 32bit if the code size is more than 64K or the number of the local variables * is more than 64K. However, for the J2ME CLDC technology, they are always 16bit. * The implementation of the StackMap class assumes they are 16bit. * * @see MethodInfo#doPreverify * @see StackMapTable * @since 3.12 */ public class StackMap extends AttributeInfo { /** * The name of this attribute "StackMap". */ public static final String tag = "StackMap"; /** * Constructs a stack_map attribute. */ StackMap(ConstPool cp, byte[] newInfo) { super(cp, tag, newInfo); } StackMap(ConstPool cp, int name_id, DataInputStream in) throws IOException { super(cp, name_id, in); } /** * Returns number_of_entries. */ public int numOfEntries() { return ByteArray.readU16bit(info, 0); } /** * Top_variable_info.tag. */ public static final int TOP = 0; /** * Integer_variable_info.tag. */ public static final int INTEGER = 1; /** * Float_variable_info.tag. */ public static final int FLOAT = 2; /** * Double_variable_info.tag. */ public static final int DOUBLE = 3; /** * Long_variable_info.tag. */ public static final int LONG = 4; /** * Null_variable_info.tag. */ public static final int NULL = 5; /** * UninitializedThis_variable_info.tag. */ public static final int THIS = 6; /** * Object_variable_info.tag. */ public static final int OBJECT = 7; /** * Uninitialized_variable_info.tag. */ public static final int UNINIT = 8; /** * Makes a copy. */ public AttributeInfo copy(ConstPool newCp, Map classnames) { Copier copier = new Copier(this, newCp, classnames); copier.visit(); return copier.getStackMap(); } /** * A code walker for a StackMap attribute. */ public static class Walker { byte[] info; /** * Constructs a walker. */ public Walker(StackMap sm) { info = sm.get(); } /** * Visits each entry of the stack map frames. */ public void visit() { int num = ByteArray.readU16bit(info, 0); int pos = 2; for (int i = 0; i < num; i++) { int offset = ByteArray.readU16bit(info, pos); int numLoc = ByteArray.readU16bit(info, pos + 2); pos = locals(pos + 4, offset, numLoc); int numStack = ByteArray.readU16bit(info, pos); pos = stack(pos + 2, offset, numStack); } } /** * Invoked when locals of stack_map_frame * is visited. */ public int locals(int pos, int offset, int num) { return typeInfoArray(pos, offset, num, true); } /** * Invoked when stack of stack_map_frame * is visited. */ public int stack(int pos, int offset, int num) { return typeInfoArray(pos, offset, num, false); } /** * Invoked when an array of verification_type_info is * visited. * * @param num the number of elements. * @param isLocals true if this array is for locals. * false if it is for stack. */ public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { for (int k = 0; k < num; k++) pos = typeInfoArray2(k, pos); return pos; } int typeInfoArray2(int k, int pos) { byte tag = info[pos]; if (tag == OBJECT) { int clazz = ByteArray.readU16bit(info, pos + 1); objectVariable(pos, clazz); pos += 3; } else if (tag == UNINIT) { int offsetOfNew = ByteArray.readU16bit(info, pos + 1); uninitialized(pos, offsetOfNew); pos += 3; } else { typeInfo(pos, tag); pos++; } return pos; } /** * Invoked when an element of verification_type_info * (except Object_variable_info and * Uninitialized_variable_info) is visited. */ public void typeInfo(int pos, byte tag) {} /** * Invoked when an element of type Object_variable_info * is visited. */ public void objectVariable(int pos, int clazz) {} /** * Invoked when an element of type Uninitialized_variable_info * is visited. */ public void uninitialized(int pos, int offset) {} } static class Copier extends Walker { byte[] dest; ConstPool srcCp, destCp; Map classnames; Copier(StackMap map, ConstPool newCp, Map classnames) { super(map); srcCp = map.getConstPool(); dest = new byte[info.length]; destCp = newCp; this.classnames = classnames; } public void visit() { int num = ByteArray.readU16bit(info, 0); ByteArray.write16bit(num, dest, 0); super.visit(); } public int locals(int pos, int offset, int num) { ByteArray.write16bit(offset, dest, pos - 4); return super.locals(pos, offset, num); } public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { ByteArray.write16bit(num, dest, pos - 2); return super.typeInfoArray(pos, offset, num, isLocals); } public void typeInfo(int pos, byte tag) { dest[pos] = tag; } public void objectVariable(int pos, int clazz) { dest[pos] = OBJECT; int newClazz = srcCp.copy(clazz, destCp, classnames); ByteArray.write16bit(newClazz, dest, pos + 1); } public void uninitialized(int pos, int offset) { dest[pos] = UNINIT; ByteArray.write16bit(offset, dest, pos + 1); } public StackMap getStackMap() { return new StackMap(destCp, dest); } } /** * Updates this stack map table when a new local variable is inserted * for a new parameter. * * @param index the index of the added local variable. * @param tag the type tag of that local variable. * It is available by StackMapTable.typeTagOf(char). * @param classInfo the index of the CONSTANT_Class_info structure * in a constant pool table. This should be zero unless the tag * is ITEM_Object. * * @see javassist.CtBehavior#addParameter(javassist.CtClass) * @see StackMapTable#typeTagOf(char) * @see ConstPool */ public void insertLocal(int index, int tag, int classInfo) throws BadBytecode { byte[] data = new InsertLocal(this, index, tag, classInfo).doit(); this.set(data); } static class SimpleCopy extends Walker { Writer writer; SimpleCopy(StackMap map) { super(map); writer = new Writer(); } byte[] doit() { visit(); return writer.toByteArray(); } public void visit() { int num = ByteArray.readU16bit(info, 0); writer.write16bit(num); super.visit(); } public int locals(int pos, int offset, int num) { writer.write16bit(offset); return super.locals(pos, offset, num); } public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { writer.write16bit(num); return super.typeInfoArray(pos, offset, num, isLocals); } public void typeInfo(int pos, byte tag) { writer.writeVerifyTypeInfo(tag, 0); } public void objectVariable(int pos, int clazz) { writer.writeVerifyTypeInfo(OBJECT, clazz); } public void uninitialized(int pos, int offset) { writer.writeVerifyTypeInfo(UNINIT, offset); } } static class InsertLocal extends SimpleCopy { private int varIndex; private int varTag, varData; InsertLocal(StackMap map, int varIndex, int varTag, int varData) { super(map); this.varIndex = varIndex; this.varTag = varTag; this.varData = varData; } public int typeInfoArray(int pos, int offset, int num, boolean isLocals) { if (!isLocals || num < varIndex) return super.typeInfoArray(pos, offset, num, isLocals); writer.write16bit(num + 1); for (int k = 0; k < num; k++) { if (k == varIndex) writeVarTypeInfo(); pos = typeInfoArray2(k, pos); } if (num == varIndex) writeVarTypeInfo(); return pos; } private void writeVarTypeInfo() { if (varTag == OBJECT) writer.writeVerifyTypeInfo(OBJECT, varData); else if (varTag == UNINIT) writer.writeVerifyTypeInfo(UNINIT, varData); else writer.writeVerifyTypeInfo(varTag, 0); } } void shiftPc(int where, int gapSize, boolean exclusive) throws BadBytecode { new Shifter(this, where, gapSize, exclusive).visit(); } static class Shifter extends Walker { private int where, gap; private boolean exclusive; public Shifter(StackMap smt, int where, int gap, boolean exclusive) { super(smt); this.where = where; this.gap = gap; this.exclusive = exclusive; } public int locals(int pos, int offset, int num) { if (exclusive ? where <= offset : where < offset) ByteArray.write16bit(offset + gap, info, pos - 4); return super.locals(pos, offset, num); } } /** * Undocumented method. Do not use; internal-use only. * *

    This method is for javassist.convert.TransformNew. * It is called to update the stack map when * the NEW opcode (and the following DUP) is removed. * * @param where the position of the removed NEW opcode. */ public void removeNew(int where) throws CannotCompileException { byte[] data = new NewRemover(this, where).doit(); this.set(data); } static class NewRemover extends SimpleCopy { int posOfNew; NewRemover(StackMap map, int where) { super(map); posOfNew = where; } public int stack(int pos, int offset, int num) { return stackTypeInfoArray(pos, offset, num); } private int stackTypeInfoArray(int pos, int offset, int num) { int p = pos; int count = 0; for (int k = 0; k < num; k++) { byte tag = info[p]; if (tag == OBJECT) p += 3; else if (tag == UNINIT) { int offsetOfNew = ByteArray.readU16bit(info, p + 1); if (offsetOfNew == posOfNew) count++; p += 3; } else p++; } writer.write16bit(num - count); for (int k = 0; k < num; k++) { byte tag = info[pos]; if (tag == OBJECT) { int clazz = ByteArray.readU16bit(info, pos + 1); objectVariable(pos, clazz); pos += 3; } else if (tag == UNINIT) { int offsetOfNew = ByteArray.readU16bit(info, pos + 1); if (offsetOfNew != posOfNew) uninitialized(pos, offsetOfNew); pos += 3; } else { typeInfo(pos, tag); pos++; } } return pos; } } /** * Prints this stack map. */ public void print(java.io.PrintWriter out) { new Printer(this, out).print(); } static class Printer extends Walker { private java.io.PrintWriter writer; public Printer(StackMap map, java.io.PrintWriter out) { super(map); writer = out; } public void print() { int num = ByteArray.readU16bit(info, 0); writer.println(num + " entries"); visit(); } public int locals(int pos, int offset, int num) { writer.println(" * offset " + offset); return super.locals(pos, offset, num); } } /** * Internal use only. */ public static class Writer { // see javassist.bytecode.stackmap.MapMaker private ByteArrayOutputStream output; /** * Constructs a writer. */ public Writer() { output = new ByteArrayOutputStream(); } /** * Converts the written data into a byte array. */ public byte[] toByteArray() { return output.toByteArray(); } /** * Converts to a StackMap attribute. */ public StackMap toStackMap(ConstPool cp) { return new StackMap(cp, output.toByteArray()); } /** * Writes a union verification_type_info value. * * @param data cpool_index or offset. */ public void writeVerifyTypeInfo(int tag, int data) { output.write(tag); if (tag == StackMap.OBJECT || tag == StackMap.UNINIT) write16bit(data); } /** * Writes a 16bit value. */ public void write16bit(int value) { output.write((value >>> 8) & 0xff); output.write(value & 0xff); } } } javassist-3.12.1.ga/src/main/javassist/bytecode/Descriptor.java0000644000175000017500000006152111033365276024445 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import javassist.ClassPool; import javassist.CtClass; import javassist.CtPrimitiveType; import javassist.NotFoundException; import java.util.Map; /** * A support class for dealing with descriptors. * *

    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. * *

    Note that toJvmName(toJvmName(s)) is equivalent * to toJvmName(s). */ 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. * This method does not deal with an array type name such as * "[Ljava/lang/Object;" and "[I;". For such names, use * toClassName(). * * @see #toClassName(String) */ 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()); } /** * Converts to a Java class name from a descriptor. * * @param descriptor type descriptor. */ public static String toClassName(String descriptor) { int arrayDim = 0; int i = 0; char c = descriptor.charAt(0); while (c == '[') { ++arrayDim; c = descriptor.charAt(++i); } String name; if (c == 'L') { int i2 = descriptor.indexOf(';', i++); name = descriptor.substring(i, i2).replace('/', '.'); i = i2; } else if (c == 'V') name = "void"; else if (c == 'I') name = "int"; else if (c == 'B') name = "byte"; else if (c == 'J') name = "long"; else if (c == 'D') name = "double"; else if (c == 'F') name = "float"; else if (c == 'C') name = "char"; else if (c == 'S') name = "short"; else if (c == 'Z') name = "boolean"; else throw new RuntimeException("bad descriptor: " + descriptor); if (i + 1 != descriptor.length()) throw new RuntimeException("multiple descriptors?: " + descriptor); if (arrayDim == 0) return name; else { StringBuffer sbuf = new StringBuffer(name); do { sbuf.append("[]"); } while (--arrayDim > 0); return sbuf.toString(); } } /** * Converts to a descriptor from a Java class name */ public static String of(String classname) { if (classname.equals("void")) return "V"; else if (classname.equals("int")) return "I"; else if (classname.equals("byte")) return "B"; else if (classname.equals("long")) return "J"; else if (classname.equals("double")) return "D"; else if (classname.equals("float")) return "F"; else if (classname.equals("char")) return "C"; else if (classname.equals("short")) return "S"; else if (classname.equals("boolean")) return "Z"; else return "L" + toJvmName(classname) + ";"; } /** * 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 map. * * @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 int, * then this method returns "(II)". * * @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. * *

    classname 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. * *

    classname 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); } /** * Appends a parameter type to the parameter list represented * by the given descriptor. The appended parameter becomes * the last parameter. * * @param type the type of the appended parameter. * @param descriptor the original descriptor. */ public static String appendParameter(CtClass type, String descriptor) { int i = descriptor.indexOf(')'); if (i < 0) return descriptor; else { StringBuffer newdesc = new StringBuffer(); newdesc.append(descriptor.substring(0, i)); toDescriptor(newdesc, type); newdesc.append(descriptor.substring(i)); return newdesc.toString(); } } /** * Inserts a parameter type at the beginning of the parameter * list represented * by the given descriptor. * * @param type the type of the inserted parameter. * @param descriptor the descriptor of the method. */ public static String insertParameter(CtClass type, String descriptor) { if (descriptor.charAt(0) != '(') return descriptor; else return "(" + of(type) + descriptor.substring(1); } /** * Changes the return type included in the given descriptor. * *

    classname 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 CtClass objects representing the parameter * types specified by the given descriptor. * * @param desc descriptor * @param cp the class pool used for obtaining * a CtClass 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 true if the list of the parameter types of desc1 is equal to * that of desc2. * For example, "(II)V" and "(II)I" are equal. */ public static boolean eqParamTypes(String desc1, String desc2) { if (desc1.charAt(0) != '(') return false; for (int i = 0; true; ++i) { char c = desc1.charAt(i); if (c != desc2.charAt(i)) return false; if (c == ')') return true; } } /** * Returns the signature of the given descriptor. The signature does * not include the return type. For example, the signature of "(I)V" * is "(I)". */ public static String getParamDescriptor(String decl) { return decl.substring(0, decl.indexOf(')') + 1); } /** * Returns the CtClass object representing the return * type specified by the given descriptor. * * @param desc descriptor * @param cp the class pool used for obtaining * a CtClass 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 CtClass object representing the type * specified by the given descriptor. * *

    This method works even if the package-class separator is * not / but . (period). For example, * it accepts Ljava.lang.Object; * as well as Ljava/lang/Object;. * * @param desc descriptor. * @param cp the class pool used for obtaining * a CtClass 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; } 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 dimension of the array represented by the given * descriptor. For example, if the descriptor is "[[I", * then this method returns 2. * * @param desc the descriptor. * @return 0 if the descriptor does not represent an array type. */ public static int arrayDimension(String desc) { int dim = 0; while (desc.charAt(dim) == '[') ++dim; return dim; } /** * Returns the descriptor of the type of the array component. * For example, if the given descriptor is * "[[Ljava/lang/String;" and the given dimension is 2, * then this method returns "Ljava/lang/String;". * * @param desc the descriptor. * @param dim the array dimension. */ public static String toArrayComponent(String desc, int dim) { return desc.substring(dim); } /** * Computes the data size specified by the given descriptor. * For example, if the descriptor is "D", this method returns 2. * *

    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) { return dataSize(desc, true); } /** * Computes the data size of parameters. * If one of the parameters is double type, the size of that parameter * is 2 words. For example, if the given descriptor is * "(IJ)D", then this method returns 3. The size of the * return type is not computed. * * @param desc a method descriptor. */ public static int paramSize(String desc) { return -dataSize(desc, false); } private static int dataSize(String desc, boolean withRet) { 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 (withRet) if (c == 'J' || c == 'D') n += 2; else if (c != 'V') ++n; return n; } /** * Returns a human-readable representation of the * given descriptor. For example, Ljava/lang/Object; * is converted into java.lang.Object. * (I[I)V is converted into (int, int[]) * (the return type is ignored). */ public static String toString(String desc) { return PrettyPrinter.toString(desc); } static class PrettyPrinter { static String toString(String desc) { StringBuffer sbuf = new StringBuffer(); if (desc.charAt(0) == '(') { int pos = 1; sbuf.append('('); while (desc.charAt(pos) != ')') { if (pos > 1) sbuf.append(','); pos = readType(sbuf, pos, desc); } sbuf.append(')'); } else readType(sbuf, 0, desc); return sbuf.toString(); } static int readType(StringBuffer sbuf, int pos, String desc) { char c = desc.charAt(pos); int arrayDim = 0; while (c == '[') { arrayDim++; c = desc.charAt(++pos); } if (c == 'L') while (true) { c = desc.charAt(++pos); if (c == ';') break; if (c == '/') c = '.'; sbuf.append(c); } else { CtClass t = toPrimitiveClass(c); sbuf.append(t.getName()); } while (arrayDim-- > 0) sbuf.append("[]"); return pos + 1; } } /** * An Iterator over a descriptor. */ public static class Iterator { private String desc; private int index, curPos; private boolean param; /** * Constructs an iterator. * * @param s descriptor. */ public Iterator(String s) { desc = s; index = curPos = 0; param = false; } /** * Returns true if the iteration has more elements. */ public boolean hasNext() { return index < desc.length(); } /** * Returns true if the current element is a parameter type. */ public boolean isParameter() { return param; } /** * Returns the first character of the current element. */ public char currentChar() { return desc.charAt(curPos); } /** * Returns true if the current element is double or long type. */ public boolean is2byte() { char c = currentChar(); return c == 'D' || c == 'J'; } /** * Returns the position of the next type character. * That type character becomes a new current element. */ public int next() { int nextPos = index; char c = desc.charAt(nextPos); if (c == '(') { ++index; c = desc.charAt(++nextPos); param = true; } if (c == ')') { ++index; c = desc.charAt(++nextPos); param = false; } while (c == '[') c = desc.charAt(++nextPos); if (c == 'L') { nextPos = desc.indexOf(';', nextPos) + 1; if (nextPos <= 0) throw new IndexOutOfBoundsException("bad descriptor"); } else ++nextPos; curPos = index; index = nextPos; return curPos; } } } javassist-3.12.1.ga/src/main/javassist/bytecode/DeprecatedAttribute.java0000644000175000017500000000315010630701321026230 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.IOException; import java.util.Map; /** * Deprecated_attribute. */ public class DeprecatedAttribute extends AttributeInfo { /** * The name of this attribute "Deprecated". */ public static final String tag = "Deprecated"; DeprecatedAttribute(ConstPool cp, int n, DataInputStream in) throws IOException { super(cp, n, in); } /** * Constructs a Deprecated attribute. * * @param cp a constant pool table. */ public DeprecatedAttribute(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 DeprecatedAttribute(newCp); } } javassist-3.12.1.ga/src/main/javassist/bytecode/MethodInfo.java0000644000175000017500000004041111366006523024352 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import javassist.ClassPool; import javassist.bytecode.stackmap.MapMaker; /** * method_info structure. * * @see javassist.CtMethod#getMethodInfo() * @see javassist.CtConstructor#getMethodInfo() */ public class MethodInfo { ConstPool constPool; int accessFlags; int name; String cachedName; int descriptor; ArrayList attribute; // may be null /** * If this value is true, Javassist maintains a StackMap attribute * generated by the preverify tool of J2ME (CLDC). The initial * value of this field is false. */ public static boolean doPreverify = false; /** * The name of constructors: <init>. */ public static final String nameInit = ""; /** * The name of class initializer (static initializer): * <clinit>. */ public static final String nameClinit = ""; private MethodInfo(ConstPool cp) { constPool = cp; attribute = null; } /** * Constructs a method_info structure. The initial value of * access_flags is zero. * * @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); cachedName = methodname; descriptor = constPool.addUtf8Info(desc); } MethodInfo(ConstPool cp, DataInputStream in) throws IOException { this(cp); read(in); } /** * Constructs a copy of method_info structure. Class names * appearing in the source method_info are renamed according * to classnameMap. * *

    * Note: only Code and Exceptions 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 method_info * @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 string representation of the object. */ public String toString() { return getName() + " " + getDescriptor(); } /** * Copies all constant pool items to a given new constant pool * and replaces the original items with the new ones. * This is used for garbage collecting the items of removed fields * and methods. * * @param cp the destination */ void compact(ConstPool cp) { name = cp.addUtf8Info(getName()); descriptor = cp.addUtf8Info(getDescriptor()); attribute = AttributeInfo.copyAll(attribute, cp); constPool = cp; } void prune(ConstPool cp) { ArrayList newAttributes = new ArrayList(); AttributeInfo invisibleAnnotations = getAttribute(AnnotationsAttribute.invisibleTag); if (invisibleAnnotations != null) { invisibleAnnotations = invisibleAnnotations.copy(cp, null); newAttributes.add(invisibleAnnotations); } AttributeInfo visibleAnnotations = getAttribute(AnnotationsAttribute.visibleTag); if (visibleAnnotations != null) { visibleAnnotations = visibleAnnotations.copy(cp, null); newAttributes.add(visibleAnnotations); } AttributeInfo parameterInvisibleAnnotations = getAttribute(ParameterAnnotationsAttribute.invisibleTag); if (parameterInvisibleAnnotations != null) { parameterInvisibleAnnotations = parameterInvisibleAnnotations.copy(cp, null); newAttributes.add(parameterInvisibleAnnotations); } AttributeInfo parameterVisibleAnnotations = getAttribute(ParameterAnnotationsAttribute.visibleTag); if (parameterVisibleAnnotations != null) { parameterVisibleAnnotations = parameterVisibleAnnotations.copy(cp, null); newAttributes.add(parameterVisibleAnnotations); } AnnotationDefaultAttribute defaultAttribute = (AnnotationDefaultAttribute) getAttribute(AnnotationDefaultAttribute.tag); if (defaultAttribute != null) newAttributes.add(defaultAttribute); ExceptionsAttribute ea = getExceptionsAttribute(); if (ea != null) newAttributes.add(ea); AttributeInfo signature = getAttribute(SignatureAttribute.tag); if (signature != null) { signature = signature.copy(cp, null); newAttributes.add(signature); } attribute = newAttributes; name = cp.addUtf8Info(getName()); descriptor = cp.addUtf8Info(getDescriptor()); constPool = cp; } /** * Returns a method name. */ public String getName() { if (cachedName == null) cachedName = constPool.getUtf8Info(name); return cachedName; } /** * Sets a method name. */ public void setName(String newName) { name = constPool.addUtf8Info(newName); cachedName = 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. The returned List object * is shared with this object. If you add a new attribute to the list, * the attribute is also added to the method represented by this * object. If you remove an attribute from the list, it is also removed * from the method. * * @return a list of AttributeInfo objects. * @see AttributeInfo */ public List getAttributes() { if (attribute == null) attribute = new ArrayList(); return attribute; } /** * Returns the attribute with the specified name. If it is not found, this * method returns null. * * @param name attribute name * @return an AttributeInfo object or null. * @see #getAttributes() */ 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. * * @see #getAttributes() */ public void addAttribute(AttributeInfo info) { if (attribute == null) attribute = new ArrayList(); 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.tag); 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.tag); return (CodeAttribute)info; } /** * Removes an Exception attribute. */ public void removeExceptionsAttribute() { AttributeInfo.remove(attribute, ExceptionsAttribute.tag); } /** * Adds an Exception attribute. * *

    * The added attribute must share the same constant pool table as this * method_info structure. */ public void setExceptionsAttribute(ExceptionsAttribute cattr) { removeExceptionsAttribute(); if (attribute == null) attribute = new ArrayList(); attribute.add(cattr); } /** * Removes a Code attribute. */ public void removeCodeAttribute() { AttributeInfo.remove(attribute, CodeAttribute.tag); } /** * Adds a Code attribute. * *

    * The added attribute must share the same constant pool table as this * method_info structure. */ public void setCodeAttribute(CodeAttribute cattr) { removeCodeAttribute(); if (attribute == null) attribute = new ArrayList(); attribute.add(cattr); } /** * Rebuilds a stack map table if the class file is for Java 6 * or later. Java 5 or older Java VMs do not recognize a stack * map table. If doPreverify is true, this method * also rebuilds a stack map for J2ME (CLDC). * * @param pool used for making type hierarchy. * @param cf rebuild if this class file is for Java 6 or later. * @see #rebuildStackMap(ClassPool) * @see #rebuildStackMapForME(ClassPool) * @since 3.6 */ public void rebuildStackMapIf6(ClassPool pool, ClassFile cf) throws BadBytecode { if (cf.getMajorVersion() >= ClassFile.JAVA_6) rebuildStackMap(pool); if (doPreverify) rebuildStackMapForME(pool); } /** * Rebuilds a stack map table. If no stack map table is included, * a new one is created. If this MethodInfo does not * include a code attribute, nothing happens. * * @param pool used for making type hierarchy. * @see StackMapTable * @since 3.6 */ public void rebuildStackMap(ClassPool pool) throws BadBytecode { CodeAttribute ca = getCodeAttribute(); if (ca != null) { StackMapTable smt = MapMaker.make(pool, this); ca.setAttribute(smt); } } /** * Rebuilds a stack map table for J2ME (CLDC). If no stack map table is included, * a new one is created. If this MethodInfo does not * include a code attribute, nothing happens. * * @param pool used for making type hierarchy. * @see StackMapTable * @since 3.12 */ public void rebuildStackMapForME(ClassPool pool) throws BadBytecode { CodeAttribute ca = getCodeAttribute(); if (ca != null) { StackMap sm = MapMaker.make2(pool, this); ca.setAttribute(sm); } } /** * Returns the line number of the source line corresponding to the specified * bytecode contained in this method. * * @param pos * the position of the bytecode (>= 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. * *

    * This method modifies a call to super(), 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 actual parameters. * Hence the new super class must have a constructor with the same signature * as the original one. * *

    * This method should be called when the super class of the class declaring * this method is changed. * *

    * This method does not perform anything unless this MethodInfo * 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); cachedName = methodname; ConstPool srcCp = src.constPool; String desc = srcCp.getUtf8Info(src.descriptor); String desc2 = Descriptor.rename(desc, classnames); descriptor = destCp.addUtf8Info(desc2); attribute = new ArrayList(); 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 ArrayList(); 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); } } } javassist-3.12.1.ga/src/main/javassist/bytecode/ClassFileWriter.java0000644000175000017500000005665611371307074025403 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.OutputStream; import java.io.DataOutputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; /** * A quick class-file writer. This is useful when a generated * class file is simple and the code generation should be fast. * *

    Example: * *

     * ClassFileWriter cfw = new ClassFileWriter(ClassFile.JAVA_4, 0);
     * ConstPoolWriter cpw = cfw.getConstPool();
     *
     * FieldWriter fw = cfw.getFieldWriter();
     * fw.add(AccessFlag.PUBLIC, "value", "I", null);
     * fw.add(AccessFlag.PUBLIC, "value2", "J", null);
     *
     * int thisClass = cpw.addClassInfo("sample/Test");
     * int superClass = cpw.addClassInfo("java/lang/Object");
     *
     * MethodWriter mw = cfw.getMethodWriter();
     *
     * mw.begin(AccessFlag.PUBLIC, MethodInfo.nameInit, "()V", null, null);
     * mw.add(Opcode.ALOAD_0);
     * mw.add(Opcode.INVOKESPECIAL);
     * int signature = cpw.addNameAndTypeInfo(MethodInfo.nameInit, "()V");
     * mw.add16(cpw.addMethodrefInfo(superClass, signature));
     * mw.add(Opcode.RETURN);
     * mw.codeEnd(1, 1);
     * mw.end(null, null);
     *
     * mw.begin(AccessFlag.PUBLIC, "one", "()I", null, null);
     * mw.add(Opcode.ICONST_1);
     * mw.add(Opcode.IRETURN);
     * mw.codeEnd(1, 1);
     * mw.end(null, null);
     *
     * byte[] classfile = cfw.end(AccessFlag.PUBLIC, thisClass, superClass,
     *                            null, null);
     * 
    * *

    The code above generates the following class: * *

     * package sample;
     * public class Test {
     *     public int value;
     *     public long value2;
     *     public Test() { super(); }
     *     public one() { return 1; }
     * }
     * 
    * * @since 3.13 */ public class ClassFileWriter { private ByteStream output; private ConstPoolWriter constPool; private FieldWriter fields; private MethodWriter methods; int thisClass, superClass; /** * Constructs a class file writer. * * @param major the major version ({@link ClassFile#JAVA_4}, {@link ClassFile#JAVA_5}, ...). * @param minor the minor version (0 for JDK 1.3 and later). */ public ClassFileWriter(int major, int minor) { output = new ByteStream(512); output.writeInt(0xCAFEBABE); // magic output.writeShort(minor); output.writeShort(major); constPool = new ConstPoolWriter(output); fields = new FieldWriter(constPool); methods = new MethodWriter(constPool); } /** * Returns a constant pool. */ public ConstPoolWriter getConstPool() { return constPool; } /** * Returns a filed writer. */ public FieldWriter getFieldWriter() { return fields; } /** * Returns a method writer. */ public MethodWriter getMethodWriter() { return methods; } /** * Ends writing and returns the contents of the class file. * * @param accessFlags access flags. * @param thisClass this class. an index indicating its CONSTANT_Class_info. * @param superClass super class. an index indicating its CONSTANT_Class_info. * @param interfaces implemented interfaces. * index numbers indicating their ClassInfo. * It may be null. * @param aw attributes of the class file. May be null. * * @see AccessFlag */ public byte[] end(int accessFlags, int thisClass, int superClass, int[] interfaces, AttributeWriter aw) { constPool.end(); output.writeShort(accessFlags); output.writeShort(thisClass); output.writeShort(superClass); if (interfaces == null) output.writeShort(0); else { int n = interfaces.length; output.writeShort(n); for (int i = 0; i < n; i++) output.writeShort(interfaces[i]); } output.enlarge(fields.dataSize() + methods.dataSize() + 6); try { output.writeShort(fields.size()); fields.write(output); output.writeShort(methods.size()); methods.write(output); } catch (IOException e) {} writeAttribute(output, aw, 0); return output.toByteArray(); } /** * Ends writing and writes the contents of the class file into the * given output stream. * * @param accessFlags access flags. * @param thisClass this class. an index indicating its CONSTANT_Class_info. * @param superClass super class. an index indicating its CONSTANT_Class_info. * @param interfaces implemented interfaces. * index numbers indicating their CONSTATNT_Class_info. * It may be null. * @param aw attributes of the class file. May be null. * * @see AccessFlag */ public void end(DataOutputStream out, int accessFlags, int thisClass, int superClass, int[] interfaces, AttributeWriter aw) throws IOException { constPool.end(); output.writeTo(out); out.writeShort(accessFlags); out.writeShort(thisClass); out.writeShort(superClass); if (interfaces == null) out.writeShort(0); else { int n = interfaces.length; out.writeShort(n); for (int i = 0; i < n; i++) out.writeShort(interfaces[i]); } out.writeShort(fields.size()); fields.write(out); out.writeShort(methods.size()); methods.write(out); if (aw == null) out.writeShort(0); else { out.writeShort(aw.size()); aw.write(out); } } /** * This writes attributes. * *

    For example, the following object writes a synthetic attribute: * *

         * ConstPoolWriter cpw = ...;
         * final int tag = cpw.addUtf8Info("Synthetic");
         * AttributeWriter aw = new AttributeWriter() {
         *     public int size() {
         *         return 1;
         *     }
         *     public void write(DataOutputStream out) throws java.io.IOException {
         *         out.writeShort(tag);
         *         out.writeInt(0);
         *     }
         * };
         * 
    */ public static interface AttributeWriter { /** * Returns the number of attributes that this writer will * write. */ public int size(); /** * Writes all the contents of the attributes. The binary representation * of the contents is an array of attribute_info. */ public void write(DataOutputStream out) throws IOException; } static void writeAttribute(ByteStream bs, AttributeWriter aw, int attrCount) { if (aw == null) { bs.writeShort(attrCount); return; } bs.writeShort(aw.size() + attrCount); DataOutputStream dos = new DataOutputStream(bs); try { aw.write(dos); dos.flush(); } catch (IOException e) {} } /** * Field. */ public static final class FieldWriter { protected ByteStream output; protected ConstPoolWriter constPool; private int fieldCount; FieldWriter(ConstPoolWriter cp) { output = new ByteStream(128); constPool = cp; fieldCount = 0; } /** * Adds a new field. * * @param accessFlags access flags. * @param name the field name. * @param descriptor the field type. * @param aw the attributes of the field. may be null. * @see AccessFlag */ public void add(int accessFlags, String name, String descriptor, AttributeWriter aw) { int nameIndex = constPool.addUtf8Info(name); int descIndex = constPool.addUtf8Info(descriptor); add(accessFlags, nameIndex, descIndex, aw); } /** * Adds a new field. * * @param accessFlags access flags. * @param name the field name. an index indicating its CONSTANT_Utf8_info. * @param descriptor the field type. an index indicating its CONSTANT_Utf8_info. * @param aw the attributes of the field. may be null. * @see AccessFlag */ public void add(int accessFlags, int name, int descriptor, AttributeWriter aw) { ++fieldCount; output.writeShort(accessFlags); output.writeShort(name); output.writeShort(descriptor); writeAttribute(output, aw, 0); } int size() { return fieldCount; } int dataSize() { return output.size(); } /** * Writes the added fields. */ void write(OutputStream out) throws IOException { output.writeTo(out); } } /** * Method. */ public static final class MethodWriter { protected ByteStream output; protected ConstPoolWriter constPool; private int methodCount; protected int codeIndex; protected int throwsIndex; protected int stackIndex; private int startPos; private boolean isAbstract; private int catchPos; private int catchCount; MethodWriter(ConstPoolWriter cp) { output = new ByteStream(256); constPool = cp; methodCount = 0; codeIndex = 0; throwsIndex = 0; stackIndex = 0; } /** * Starts Adding a new method. * * @param accessFlags access flags. * @param name the method name. * @param descriptor the method signature. * @param exceptions throws clause. It may be null. * The class names must be the JVM-internal * representations like java/lang/Exception. * @param aw attributes to the Method_info. */ public void begin(int accessFlags, String name, String descriptor, String[] exceptions, AttributeWriter aw) { int nameIndex = constPool.addUtf8Info(name); int descIndex = constPool.addUtf8Info(descriptor); int[] intfs; if (exceptions == null) intfs = null; else intfs = constPool.addClassInfo(exceptions); begin(accessFlags, nameIndex, descIndex, intfs, aw); } /** * Starts adding a new method. * * @param accessFlags access flags. * @param name the method name. an index indicating its CONSTANT_Utf8_info. * @param descriptor the field type. an index indicating its CONSTANT_Utf8_info. * @param exceptions throws clause. indexes indicating CONSTANT_Class_infos. * It may be null. * @param aw attributes to the Method_info. */ public void begin(int accessFlags, int name, int descriptor, int[] exceptions, AttributeWriter aw) { ++methodCount; output.writeShort(accessFlags); output.writeShort(name); output.writeShort(descriptor); isAbstract = (accessFlags & AccessFlag.ABSTRACT) != 0; int attrCount = isAbstract ? 0 : 1; if (exceptions != null) ++attrCount; writeAttribute(output, aw, attrCount); if (exceptions != null) writeThrows(exceptions); if (!isAbstract) { if (codeIndex == 0) codeIndex = constPool.addUtf8Info(CodeAttribute.tag); startPos = output.getPos(); output.writeShort(codeIndex); output.writeBlank(12); // attribute_length, maxStack, maxLocals, code_lenth } catchPos = -1; catchCount = 0; } private void writeThrows(int[] exceptions) { if (throwsIndex == 0) throwsIndex = constPool.addUtf8Info(ExceptionsAttribute.tag); output.writeShort(throwsIndex); output.writeInt(exceptions.length * 2 + 2); output.writeShort(exceptions.length); for (int i = 0; i < exceptions.length; i++) output.writeShort(exceptions[i]); } /** * Appends an 8bit value of bytecode. * * @see Opcode */ public void add(int b) { output.write(b); } /** * Appends a 16bit value of bytecode. */ public void add16(int b) { output.writeShort(b); } /** * Appends a 32bit value of bytecode. */ public void add32(int b) { output.writeInt(b); } /** * Appends a invokevirtual, inovkespecial, or invokestatic bytecode. * * @see Opcode */ public void addInvoke(int opcode, String targetClass, String methodName, String descriptor) { int target = constPool.addClassInfo(targetClass); int nt = constPool.addNameAndTypeInfo(methodName, descriptor); int method = constPool.addMethodrefInfo(target, nt); add(opcode); add16(method); } /** * Ends appending bytecode. */ public void codeEnd(int maxStack, int maxLocals) { if (!isAbstract) { output.writeShort(startPos + 6, maxStack); output.writeShort(startPos + 8, maxLocals); output.writeInt(startPos + 10, output.getPos() - startPos - 14); // code_length catchPos = output.getPos(); catchCount = 0; output.writeShort(0); // number of catch clauses } } /** * Appends an exception_table entry to the * Code_attribute. This method is available * only after the codeEnd method is called. * * @param catchType an index indicating a CONSTANT_Class_info. */ public void addCatch(int startPc, int endPc, int handlerPc, int catchType) { ++catchCount; output.writeShort(startPc); output.writeShort(endPc); output.writeShort(handlerPc); output.writeShort(catchType); } /** * Ends adding a new method. The add method must be * called before the end method is called. * * @param smap a stack map table. may be null. * @param aw attributes to the Code_attribute. * may be null. */ public void end(StackMapTable.Writer smap, AttributeWriter aw) { if (isAbstract) return; // exception_table_length output.writeShort(catchPos, catchCount); int attrCount = smap == null ? 0 : 1; writeAttribute(output, aw, attrCount); if (smap != null) { if (stackIndex == 0) stackIndex = constPool.addUtf8Info(StackMapTable.tag); output.writeShort(stackIndex); byte[] data = smap.toByteArray(); output.writeInt(data.length); output.write(data); } // Code attribute_length output.writeInt(startPos + 2, output.getPos() - startPos - 6); } int size() { return methodCount; } int dataSize() { return output.size(); } /** * Writes the added methods. */ void write(OutputStream out) throws IOException { output.writeTo(out); } } /** * Constant Pool. */ public static final class ConstPoolWriter { ByteStream output; protected int startPos; protected int num; ConstPoolWriter(ByteStream out) { output = out; startPos = out.getPos(); num = 1; output.writeShort(1); // number of entries } /** * Makes CONSTANT_Class_info objects for each class name. * * @return an array of indexes indicating CONSTANT_Class_infos. */ public int[] addClassInfo(String[] classNames) { int n = classNames.length; int[] result = new int[n]; for (int i = 0; i < n; i++) result[i] = addClassInfo(classNames[i]); return result; } /** * Adds a new CONSTANT_Class_info structure. * *

    This also adds a CONSTANT_Utf8_info structure * for storing the class name. * * @param jvmname the JVM-internal representation of a class name. * e.g. java/lang/Object. * @return the index of the added entry. */ public int addClassInfo(String jvmname) { int utf8 = addUtf8Info(jvmname); output.write(ClassInfo.tag); output.writeShort(utf8); return num++; } /** * Adds a new CONSTANT_Class_info structure. * * @param name name_index * @return the index of the added entry. */ public int addClassInfo(int name) { output.write(ClassInfo.tag); output.writeShort(name); return num++; } /** * Adds a new CONSTANT_NameAndType_info structure. * * @param name name_index * @param type descriptor_index * @return the index of the added entry. */ public int addNameAndTypeInfo(String name, String type) { return addNameAndTypeInfo(addUtf8Info(name), addUtf8Info(type)); } /** * Adds a new CONSTANT_NameAndType_info structure. * * @param name name_index * @param type descriptor_index * @return the index of the added entry. */ public int addNameAndTypeInfo(int name, int type) { output.write(NameAndTypeInfo.tag); output.writeShort(name); output.writeShort(type); return num++; } /** * Adds a new CONSTANT_Fieldref_info structure. * * @param classInfo class_index * @param nameAndTypeInfo name_and_type_index. * @return the index of the added entry. */ public int addFieldrefInfo(int classInfo, int nameAndTypeInfo) { output.write(FieldrefInfo.tag); output.writeShort(classInfo); output.writeShort(nameAndTypeInfo); return num++; } /** * Adds a new CONSTANT_Methodref_info structure. * * @param classInfo class_index * @param nameAndTypeInfo name_and_type_index. * @return the index of the added entry. */ public int addMethodrefInfo(int classInfo, int nameAndTypeInfo) { output.write(MethodrefInfo.tag); output.writeShort(classInfo); output.writeShort(nameAndTypeInfo); return num++; } /** * Adds a new CONSTANT_InterfaceMethodref_info * structure. * * @param classInfo class_index * @param nameAndTypeInfo name_and_type_index. * @return the index of the added entry. */ public int addInterfaceMethodrefInfo(int classInfo, int nameAndTypeInfo) { output.write(InterfaceMethodrefInfo.tag); output.writeShort(classInfo); output.writeShort(nameAndTypeInfo); return num++; } /** * Adds a new CONSTANT_String_info * structure. * *

    This also adds a new CONSTANT_Utf8_info * structure. * * @return the index of the added entry. */ public int addStringInfo(String str) { output.write(StringInfo.tag); output.writeShort(addUtf8Info(str)); return num++; } /** * Adds a new CONSTANT_Integer_info * structure. * * @return the index of the added entry. */ public int addIntegerInfo(int i) { output.write(IntegerInfo.tag); output.writeInt(i); return num++; } /** * Adds a new CONSTANT_Float_info * structure. * * @return the index of the added entry. */ public int addFloatInfo(float f) { output.write(FloatInfo.tag); output.writeFloat(f); return num++; } /** * Adds a new CONSTANT_Long_info * structure. * * @return the index of the added entry. */ public int addLongInfo(long l) { output.write(LongInfo.tag); output.writeLong(l); int n = num; num += 2; return n; } /** * Adds a new CONSTANT_Double_info * structure. * * @return the index of the added entry. */ public int addDoubleInfo(double d) { output.write(DoubleInfo.tag); output.writeDouble(d); int n = num; num += 2; return n; } /** * Adds a new CONSTANT_Utf8_info * structure. * * @return the index of the added entry. */ public int addUtf8Info(String utf8) { output.write(Utf8Info.tag); output.writeUTF(utf8); return num++; } /** * Writes the contents of this class pool. */ void end() { output.writeShort(startPos, num); } } } javassist-3.12.1.ga/src/main/javassist/bytecode/InnerClassesAttribute.java0000644000175000017500000001620610630701321026567 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.util.Map; import java.io.IOException; /** * InnerClasses_attribute. */ public class InnerClassesAttribute extends AttributeInfo { /** * The name of this attribute "InnerClasses". */ 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); } /** * Constructs an empty InnerClasses attribute. * * @see #append(String, String, String, int) */ public InnerClassesAttribute(ConstPool cp) { super(cp, tag, new byte[2]); ByteArray.write16bit(0, get(), 0); } /** * Returns number_of_classes. */ public int tableLength() { return ByteArray.readU16bit(get(), 0); } /** * Returns classes[nth].inner_class_info_index. */ public int innerClassIndex(int nth) { return ByteArray.readU16bit(get(), nth * 8 + 2); } /** * Returns the class name indicated * by classes[nth].inner_class_info_index. * * @return null or the class name. */ public String innerClass(int nth) { int i = innerClassIndex(nth); if (i == 0) return null; else return constPool.getClassInfo(i); } /** * Sets classes[nth].inner_class_info_index to * the given index. */ public void setInnerClassIndex(int nth, int index) { ByteArray.write16bit(index, get(), nth * 8 + 2); } /** * Returns classes[nth].outer_class_info_index. */ public int outerClassIndex(int nth) { return ByteArray.readU16bit(get(), nth * 8 + 4); } /** * Returns the class name indicated * by classes[nth].outer_class_info_index. * * @return null or the class name. */ public String outerClass(int nth) { int i = outerClassIndex(nth); if (i == 0) return null; else return constPool.getClassInfo(i); } /** * Sets classes[nth].outer_class_info_index to * the given index. */ public void setOuterClassIndex(int nth, int index) { ByteArray.write16bit(index, get(), nth * 8 + 4); } /** * Returns classes[nth].inner_name_index. */ public int innerNameIndex(int nth) { return ByteArray.readU16bit(get(), nth * 8 + 6); } /** * Returns the simple class name indicated * by classes[nth].inner_name_index. * * @return null or the class name. */ public String innerName(int nth) { int i = innerNameIndex(nth); if (i == 0) return null; else return constPool.getUtf8Info(i); } /** * Sets classes[nth].inner_name_index to * the given index. */ public void setInnerNameIndex(int nth, int index) { ByteArray.write16bit(index, get(), nth * 8 + 6); } /** * Returns classes[nth].inner_class_access_flags. */ public int accessFlags(int nth) { return ByteArray.readU16bit(get(), nth * 8 + 8); } /** * Sets classes[nth].inner_class_access_flags to * the given index. */ public void setAccessFlags(int nth, int flags) { ByteArray.write16bit(flags, get(), nth * 8 + 8); } /** * Appends a new entry. * * @param inner inner_class_info_index * @param outer outer_class_info_index * @param name inner_name_index * @param flags inner_class_access_flags */ public void append(String inner, String outer, String name, int flags) { int i = constPool.addClassInfo(inner); int o = constPool.addClassInfo(outer); int n = constPool.addUtf8Info(name); append(i, o, n, flags); } /** * Appends a new entry. * * @param inner inner_class_info_index * @param outer outer_class_info_index * @param name inner_name_index * @param flags inner_class_access_flags */ public void append(int inner, int outer, int name, int flags) { byte[] data = get(); int len = data.length; byte[] newData = new byte[len + 8]; for (int i = 2; i < len; ++i) newData[i] = data[i]; int n = ByteArray.readU16bit(data, 0); ByteArray.write16bit(n + 1, newData, 0); ByteArray.write16bit(inner, newData, len); ByteArray.write16bit(outer, newData, len + 2); ByteArray.write16bit(name, newData, len + 4); ByteArray.write16bit(flags, newData, len + 6); set(newData); } /** * Makes a copy. Class names are replaced according to the * given Map 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; } } javassist-3.12.1.ga/src/main/javassist/bytecode/ExceptionTable.java0000644000175000017500000002144311213421076025223 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.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; } } /** * exception_table[] of Code_attribute. */ public class ExceptionTable implements Cloneable { private ConstPool constPool; private ArrayList entries; /** * Constructs an exception_table[]. * * @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; } /** * Creates and returns a copy of this object. * The constant pool object is shared between this object * and the cloned object. */ public Object clone() throws CloneNotSupportedException { ExceptionTable r = (ExceptionTable)super.clone(); r.entries = new ArrayList(entries); return r; } /** * Returns exception_table_length, which is the number * of entries in the exception_table[]. */ public int size() { return entries.size(); } /** * Returns startPc of the n-th entry. * * @param nth the n-th (>= 0). */ public int startPc(int nth) { ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); return e.startPc; } /** * Sets startPc of the n-th entry. * * @param nth the n-th (>= 0). * @param value new value. */ public void setStartPc(int nth, int value) { ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); e.startPc = value; } /** * Returns endPc of the n-th entry. * * @param nth the n-th (>= 0). */ public int endPc(int nth) { ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); return e.endPc; } /** * Sets endPc of the n-th entry. * * @param nth the n-th (>= 0). * @param value new value. */ public void setEndPc(int nth, int value) { ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); e.endPc = value; } /** * Returns handlerPc of the n-th entry. * * @param nth the n-th (>= 0). */ public int handlerPc(int nth) { ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); return e.handlerPc; } /** * Sets handlerPc of the n-th entry. * * @param nth the n-th (>= 0). * @param value new value. */ public void setHandlerPc(int nth, int value) { ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); e.handlerPc = value; } /** * Returns catchType of the n-th entry. * * @param nth the n-th (>= 0). * @return an index into the constant_pool table, * or zero if this exception handler is for all exceptions. */ public int catchType(int nth) { ExceptionTableEntry e = (ExceptionTableEntry)entries.get(nth); return e.catchType; } /** * Sets catchType of the n-th entry. * * @param nth the n-th (>= 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 (>= 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 (>= 0) at which the entry is to be inserted. * @param start startPc * @param end endPc * @param handler handlerPc * @param type catchType */ public void add(int index, int start, int end, int handler, int type) { if (start < end) entries.add(index, new ExceptionTableEntry(start, end, handler, type)); } /** * Appends a new entry at the end of the table. * * @param start startPc * @param end endPc * @param handler handlerPc * @param type catchType */ public void add(int start, int end, int handler, int type) { if (start < end) 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 exception_table[]. * Class names are replaced according to the * given Map 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); } } } javassist-3.12.1.ga/src/main/javassist/bytecode/SourceFileAttribute.java0000644000175000017500000000430710630701321026235 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.IOException; import java.util.Map; /** * SourceFile_attribute. */ public class SourceFileAttribute extends AttributeInfo { /** * The name of this attribute "SourceFile". */ 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 sourcefile_index. */ public String getFileName() { return getConstPool().getUtf8Info(ByteArray.readU16bit(get(), 0)); } /** * Makes a copy. Class names are replaced according to the * given Map 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()); } } javassist-3.12.1.ga/src/main/javassist/bytecode/LocalVariableAttribute.java0000644000175000017500000002573711277026442026723 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.IOException; import java.util.Map; /** * LocalVariableTable_attribute. */ public class LocalVariableAttribute extends AttributeInfo { /** * The name of this attribute "LocalVariableTable". */ public static final String tag = "LocalVariableTable"; /** * The name of the attribute "LocalVariableTypeTable". */ public static final String typeTag = "LocalVariableTypeTable"; /** * Constructs an empty LocalVariableTable. */ public LocalVariableAttribute(ConstPool cp) { super(cp, tag, new byte[2]); ByteArray.write16bit(0, info, 0); } /** * Constructs an empty LocalVariableTable. * * @param name the attribute name. * LocalVariableAttribute.tag or * LocalVariableAttribute.typeTag. * @see #tag * @see #typeTag * @since 3.1 * @deprecated */ public LocalVariableAttribute(ConstPool cp, String name) { super(cp, name, new byte[2]); ByteArray.write16bit(0, info, 0); } LocalVariableAttribute(ConstPool cp, int n, DataInputStream in) throws IOException { super(cp, n, in); } LocalVariableAttribute(ConstPool cp, String name, byte[] i) { super(cp, name, i); } /** * Appends a new entry to local_variable_table. * * @param startPc start_pc * @param length length * @param nameIndex name_index * @param descriptorIndex descriptor_index * @param index index */ public void addEntry(int startPc, int length, int nameIndex, int descriptorIndex, int index) { int size = info.length; byte[] newInfo = new byte[size + 10]; ByteArray.write16bit(tableLength() + 1, newInfo, 0); for (int i = 2; i < size; ++i) newInfo[i] = info[i]; ByteArray.write16bit(startPc, newInfo, size); ByteArray.write16bit(length, newInfo, size + 2); ByteArray.write16bit(nameIndex, newInfo, size + 4); ByteArray.write16bit(descriptorIndex, newInfo, size + 6); ByteArray.write16bit(index, newInfo, size + 8); info = newInfo; } void renameClass(String oldname, String newname) { ConstPool cp = getConstPool(); int n = tableLength(); for (int i = 0; i < n; ++i) { int pos = i * 10 + 2; int index = ByteArray.readU16bit(info, pos + 6); if (index != 0) { String desc = cp.getUtf8Info(index); desc = renameEntry(desc, oldname, newname); ByteArray.write16bit(cp.addUtf8Info(desc), info, pos + 6); } } } String renameEntry(String desc, String oldname, String newname) { return Descriptor.rename(desc, oldname, newname); } void renameClass(Map classnames) { ConstPool cp = getConstPool(); int n = tableLength(); for (int i = 0; i < n; ++i) { int pos = i * 10 + 2; int index = ByteArray.readU16bit(info, pos + 6); if (index != 0) { String desc = cp.getUtf8Info(index); desc = renameEntry(desc, classnames); ByteArray.write16bit(cp.addUtf8Info(desc), info, pos + 6); } } } String renameEntry(String desc, Map classnames) { return Descriptor.rename(desc, classnames); } /** * For each local_variable_table[i].index, * this method increases index by delta. * * @param lessThan the index does not change if it * is less than this value. */ public void shiftIndex(int lessThan, int delta) { int size = info.length; for (int i = 2; i < size; i += 10){ int org = ByteArray.readU16bit(info, i + 8); if (org >= lessThan) ByteArray.write16bit(org + delta, info, i + 8); } } /** * Returns local_variable_table_length. * This represents the number of entries in the table. */ public int tableLength() { return ByteArray.readU16bit(info, 0); } /** * Returns local_variable_table[i].start_pc. * This represents the index into the code array from which the local * variable is effective. * * @param i the i-th entry. */ public int startPc(int i) { return ByteArray.readU16bit(info, i * 10 + 2); } /** * Returns local_variable_table[i].length. * This represents the length of the code region in which the local * variable is effective. * * @param i the i-th entry. */ public int codeLength(int i) { return ByteArray.readU16bit(info, i * 10 + 4); } /** * Adjusts start_pc and length if bytecode is inserted in a method body. */ void shiftPc(int where, int gapLength, boolean exclusive) { int n = tableLength(); for (int i = 0; i < n; ++i) { int pos = i * 10 + 2; int pc = ByteArray.readU16bit(info, pos); int len = ByteArray.readU16bit(info, pos + 2); /* if pc == 0, then the local variable is a method parameter. */ if (pc > where || (exclusive && pc == where && pc != 0)) ByteArray.write16bit(pc + gapLength, info, pos); else if (pc + len > where || (exclusive && pc + len == where)) ByteArray.write16bit(len + gapLength, info, pos + 2); } } /** * Returns the value of local_variable_table[i].name_index. * This represents the name of the local variable. * * @param i the i-th entry. */ public int nameIndex(int i) { return ByteArray.readU16bit(info, i * 10 + 6); } /** * Returns the name of the local variable * specified by local_variable_table[i].name_index. * * @param i the i-th entry. */ public String variableName(int i) { return getConstPool().getUtf8Info(nameIndex(i)); } /** * Returns the value of * local_variable_table[i].descriptor_index. * This represents the type descriptor of the local variable. *

    * If this attribute represents a LocalVariableTypeTable attribute, * this method returns the value of * local_variable_type_table[i].signature_index. * It represents the type of the local variable. * * @param i the i-th entry. */ public int descriptorIndex(int i) { return ByteArray.readU16bit(info, i * 10 + 8); } /** * This method is equivalent to descriptorIndex(). * If this attribute represents a LocalVariableTypeTable attribute, * this method should be used instead of descriptorIndex() * since the method name is more appropriate. * * @param i the i-th entry. * @see #descriptorIndex(int) * @see SignatureAttribute#toFieldSignature(String) */ public int signatureIndex(int i) { return descriptorIndex(i); } /** * Returns the type descriptor of the local variable * specified by local_variable_table[i].descriptor_index. *

    * If this attribute represents a LocalVariableTypeTable attribute, * this method returns the type signature of the local variable * specified by local_variable_type_table[i].signature_index. * * @param i the i-th entry. */ public String descriptor(int i) { return getConstPool().getUtf8Info(descriptorIndex(i)); } /** * This method is equivalent to descriptor(). * If this attribute represents a LocalVariableTypeTable attribute, * this method should be used instead of descriptor() * since the method name is more appropriate. * *

    To parse the string, call toFieldSignature(String) * in SignatureAttribute. * * @param i the i-th entry. * @see #descriptor(int) * @see SignatureAttribute#toFieldSignature(String) */ public String signature(int i) { return descriptor(i); } /** * Returns local_variable_table[i].index. * This represents the index of the local variable. * * @param i the i-th entry. */ public int index(int i) { return ByteArray.readU16bit(info, i * 10 + 10); } /** * 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 = get(); byte[] dest = new byte[src.length]; ConstPool cp = getConstPool(); LocalVariableAttribute attr = makeThisAttr(newCp, dest); int n = ByteArray.readU16bit(src, 0); ByteArray.write16bit(n, dest, 0); int j = 2; for (int i = 0; i < n; ++i) { int start = ByteArray.readU16bit(src, j); int len = ByteArray.readU16bit(src, j + 2); int name = ByteArray.readU16bit(src, j + 4); int type = ByteArray.readU16bit(src, j + 6); int index = ByteArray.readU16bit(src, j + 8); ByteArray.write16bit(start, dest, j); ByteArray.write16bit(len, dest, j + 2); if (name != 0) name = cp.copy(name, newCp, null); ByteArray.write16bit(name, dest, j + 4); if (type != 0) { String sig = cp.getUtf8Info(type); sig = Descriptor.rename(sig, classnames); type = newCp.addUtf8Info(sig); } ByteArray.write16bit(type, dest, j + 6); ByteArray.write16bit(index, dest, j + 8); j += 10; } return attr; } // LocalVariableTypeAttribute overrides this method. LocalVariableAttribute makeThisAttr(ConstPool cp, byte[] dest) { return new LocalVariableAttribute(cp, tag, dest); } } javassist-3.12.1.ga/src/main/javassist/bytecode/package.html0000644000175000017500000000072207651544225023745 0ustar twernertwerner Bytecode-level API.

    This package provides low-level API for editing a raw class file. It allows the users to read and modify a constant pool entry, a single bytecode instruction, and so on.

    The users of this package must know the specifications of class file and Java bytecode. For more details, read this book:

      Tim Lindholm and Frank Yellin, "The Java Virtual Machine Specification 2nd Ed.", Addison-Wesley, 1999.
    javassist-3.12.1.ga/src/main/javassist/bytecode/BadBytecode.java0000644000175000017500000000202411015721760024457 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.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); } public BadBytecode(String msg, Throwable cause) { super(msg, cause); } } javassist-3.12.1.ga/src/main/javassist/bytecode/CodeIterator.java0000644000175000017500000015047311331331273024707 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.util.ArrayList; /** * An iterator for editing a code attribute. * *

    If there are multiple CodeIterators referring to the * same Code_attribute, then inserting a gap by one * CodeIterator will break the other * CodeIterator. * *

    This iterator does not provide remove(). * If a piece of code in a Code_attribute is unnecessary, * it should be overwritten with NOP. * * @see CodeAttribute#iterator() */ public class CodeIterator implements Opcode { protected CodeAttribute codeAttr; protected byte[] bytecode; protected int endPos; protected int currentPos; protected int mark; protected CodeIterator(CodeAttribute ca) { codeAttr = ca; bytecode = ca.getCode(); begin(); } /** * Moves to the first instruction. */ public void begin() { currentPos = mark = 0; endPos = getCodeLength(); } /** * Moves to the given index. * *

    The index of the next instruction is set to the given index. * The successive call to next() * returns the index that has been given to move(). * *

    Note that the index is into the byte array returned by * get().getCode(). * * @see CodeAttribute#getCode() */ public void move(int index) { currentPos = index; } /** * Sets a mark to the bytecode at the given index. * The mark can be used to track the position of that bytecode * when code blocks are inserted. * If a code block is inclusively inserted at the position of the * bytecode, the mark is set to the inserted code block. * * @see #getMark() * @since 3.11 */ public void setMark(int index) { mark = index; } /** * Gets the index of the position of the mark set by * setMark. * * @return the index of the position. * @see #setMark(int) * @since 3.11 */ public int getMark() { return mark; } /** * Returns a Code attribute read with this iterator. */ public CodeAttribute get() { return codeAttr; } /** * Returns code_length of Code_attribute. */ 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. * * @param code may be a zero-length array. */ 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 operand following the current opcode). * *

    Note that the index is into the byte array returned by * get().getCode(). * * @see CodeAttribute#getCode() * @see CodeIterator#byteAt(int) */ public int next() throws BadBytecode { int pos = currentPos; currentPos = nextOpcode(bytecode, pos); return pos; } /** * Obtains the value that the next call * to next() will return. * *

    This method is side-effects free. * Successive calls to lookAhead() return the * same value until next() is called. */ public int lookAhead() { return currentPos; } /** * Moves to the instruction for * either super() or this(). * *

    This method skips all the instructions for computing arguments * to super() or this(), which should be * placed at the beginning of a constructor body. * *

    This method returns the index of INVOKESPECIAL instruction * executing super() or this(). * A successive call to next() returns the * index of the next instruction following that INVOKESPECIAL. * *

    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 instruction for super(). * *

    This method skips all the instructions for computing arguments to * super(), which should be * placed at the beginning of a constructor body. * *

    This method returns the index of INVOKESPECIAL instruction * executing super(). * A successive call to next() returns the * index of the next instruction following that INVOKESPECIAL. * *

    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 this() is found. */ public int skipSuperConstructor() throws BadBytecode { return skipSuperConstructor0(0); } /** * Moves to the instruction for this(). * *

    This method skips all the instructions for computing arguments to * this(), which should be * placed at the beginning of a constructor body. * *

    This method returns the index of INVOKESPECIAL instruction * executing this(). * A successive call to next() returns the * index of the next instruction following that INVOKESPECIAL. * *

    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 super() 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 * next() (not before the instruction returned * by the last call to next()). * Branch offsets and the exception table are also updated. * *

    If the next instruction is at the beginning of a block statement, * then the bytecode is inserted within that block. * *

    An extra gap may be inserted at the end of the inserted * bytecode sequence for adjusting alignment if the code attribute * includes LOOKUPSWITCH or TABLESWITCH. * * @param code inserted bytecode sequence. * @return the index indicating the first byte of the * inserted byte sequence. */ public int insert(byte[] code) throws BadBytecode { return insert0(currentPos, code, false); } /** * Inserts the given bytecode sequence * before the instruction at the given index pos. * Branch offsets and the exception table are also updated. * *

    If the instruction at the given index is at the beginning * of a block statement, * then the bytecode is inserted within that block. * *

    An extra gap may be inserted at the end of the inserted * bytecode sequence for adjusting alignment if the code attribute * includes LOOKUPSWITCH or TABLESWITCH. * *

    The index at which the byte sequence is actually inserted * might be different from pos since some other bytes might be * inserted at other positions (e.g. to change GOTO * to GOTO_W). * * @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 * before the instruction at the given index pos. * Branch offsets and the exception table are also updated. * *

    If the instruction at the given index is at the beginning * of a block statement, * then the bytecode is inserted within that block. * *

    An extra gap may be inserted at the end of the inserted * bytecode sequence for adjusting alignment if the code attribute * includes LOOKUPSWITCH or TABLESWITCH. * * @param pos the index at which a byte sequence is inserted. * @param code inserted bytecode sequence. * @return the index indicating the first byte of the * inserted byte sequence, which might be * different from pos. * @since 3.11 */ public int insertAt(int pos, byte[] code) throws BadBytecode { return insert0(pos, code, false); } /** * Inserts the given bytecode sequence exclusively * before the next instruction that would be returned by * next() (not before the instruction returned * by tha last call to next()). * Branch offsets and the exception table are also updated. * *

    If the next instruction is at the beginning of a block statement, * then the bytecode is excluded from that block. * *

    An extra gap may be inserted at the end of the inserted * bytecode sequence for adjusting alignment if the code attribute * includes LOOKUPSWITCH or TABLESWITCH. * * @param code inserted bytecode sequence. * @return the index indicating the first byte of the * inserted byte sequence. */ public int insertEx(byte[] code) throws BadBytecode { return insert0(currentPos, code, true); } /** * Inserts the given bytecode sequence exclusively * before the instruction at the given index pos. * Branch offsets and the exception table are also updated. * *

    If the instruction at the given index is at the beginning * of a block statement, * then the bytecode is excluded from that block. * *

    An extra gap may be inserted at the end of the inserted * bytecode sequence for adjusting alignment if the code attribute * includes LOOKUPSWITCH or TABLESWITCH. * *

    The index at which the byte sequence is actually inserted * might be different from pos since some other bytes might be * inserted at other positions (e.g. to change GOTO * to GOTO_W). * * @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); } /** * Inserts the given bytecode sequence exclusively * before the instruction at the given index pos. * Branch offsets and the exception table are also updated. * *

    If the instruction at the given index is at the beginning * of a block statement, * then the bytecode is excluded from that block. * *

    An extra gap may be inserted at the end of the inserted * bytecode sequence for adjusting alignment if the code attribute * includes LOOKUPSWITCH or TABLESWITCH. * * @param pos the index at which a byte sequence is inserted. * @param code inserted bytecode sequence. * @return the index indicating the first byte of the * inserted byte sequence, which might be * different from pos. * @since 3.11 */ public int insertExAt(int pos, byte[] code) throws BadBytecode { return insert0(pos, code, true); } /** * @return the index indicating the first byte of the * inserted byte sequence. */ private int insert0(int pos, byte[] code, boolean exclusive) throws BadBytecode { int len = code.length; if (len <= 0) return pos; // currentPos will change. pos = insertGapAt(pos, len, exclusive).position; int p = pos; for (int j = 0; j < len; ++j) bytecode[p++] = code[j]; return pos; } /** * Inserts a gap * before the next instruction that would be returned by * next() (not before the instruction returned * by the last call to next()). * 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. * *

    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 { return insertGapAt(currentPos, length, false).position; } /** * Inserts a gap in front of the instruction at the given * index pos. * 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. * *

    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. * @return the length of the inserted gap. * It might be bigger than length. */ public int insertGap(int pos, int length) throws BadBytecode { return insertGapAt(pos, length, false).length; } /** * Inserts an exclusive gap * before the next instruction that would be returned by * next() (not before the instruction returned * by the last call to next()). * 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. * *

    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 { return insertGapAt(currentPos, length, true).position; } /** * Inserts an exclusive gap in front of the instruction at the given * index pos. * 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. * *

    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. * @return the length of the inserted gap. * It might be bigger than length. */ public int insertExGap(int pos, int length) throws BadBytecode { return insertGapAt(pos, length, true).length; } /** * An inserted gap. * * @since 3.11 */ public static class Gap { /** * The position of the gap. */ public int position; /** * The length of the gap. */ public int length; } /** * Inserts an inclusive or exclusive gap in front of the instruction * at the given index pos. * Branch offsets and the exception table in the method body * are also updated. The inserted gap is filled with NOP. * The gap length may be extended to a multiple of 4. * *

    Suppose that the instruction at the given index is at the * beginning of a block statement. If the gap is inclusive, * then it is included within that block. If the gap is exclusive, * then it is excluded from that block. * *

    The index at which the gap is actually inserted * might be different from pos since some other bytes might be * inserted at other positions (e.g. to change GOTO * to GOTO_W). The index is available from the Gap * object returned by this method. * *

    Suppose that the gap is inserted at the position of * the next instruction that would be returned by * next() (not the last instruction returned * by the last call to next()). The next * instruction returned by next() after the gap is * inserted is still the same instruction. It is not NOP * at the first byte of the inserted gap. * * @param pos the index at which a gap is inserted. * @param length gap length. * @param exclusive true if exclusive, otherwise false. * @return the position and the length of the inserted gap. * @since 3.11 */ public Gap insertGapAt(int pos, int length, boolean exclusive) throws BadBytecode { /** * cursorPos indicates the next bytecode whichever exclusive is * true or false. */ Gap gap = new Gap(); if (length <= 0) { gap.position = pos; gap.length = 0; return gap; } byte[] c; int length2; if (bytecode.length + length > Short.MAX_VALUE) { // currentPos might change after calling insertGapCore0w(). c = insertGapCore0w(bytecode, pos, length, exclusive, get().getExceptionTable(), codeAttr, gap); pos = gap.position; length2 = length; // == gap.length } else { int cur = currentPos; c = insertGapCore0(bytecode, pos, length, exclusive, get().getExceptionTable(), codeAttr); // insertGapCore0() never changes pos. length2 = c.length - bytecode.length; gap.position = pos; gap.length = length2; if (cur >= pos) currentPos = cur + length2; if (mark > pos || (mark == pos && exclusive)) mark += length2; } codeAttr.setCode(c); bytecode = c; endPos = getCodeLength(); updateCursors(pos, length2); return gap; } /** * Is called when a gap is inserted. The default implementation is empty. * A subclass can override this method so that cursors will be updated. * * @param pos the position where a gap is inserted. * @param length the length of the gap. */ protected void updateCursors(int pos, int length) { // empty } /** * 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 gapLength 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(). static class AlignmentException extends Exception {} /** * insertGapCore0() inserts a gap (some NOPs). * It cannot handle a long code sequence more than 32K. All branch offsets must be * signed 16bits. * * If "where" is the beginning of a block statement and exclusive is false, * 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. * * This method was also called from CodeAttribute.LdcEntry.doit(). * * @param where It must indicate the first byte of an opcode. */ static byte[] insertGapCore0(byte[] code, int where, int gapLength, boolean exclusive, ExceptionTable etable, CodeAttribute ca) throws BadBytecode { if (gapLength <= 0) return code; try { return insertGapCore1(code, where, gapLength, exclusive, etable, ca); } catch (AlignmentException e) { try { return insertGapCore1(code, where, (gapLength + 3) & ~3, exclusive, etable, ca); } catch (AlignmentException e2) { throw new RuntimeException("fatal error?"); } } } private static byte[] insertGapCore1(byte[] code, int where, int gapLength, boolean exclusive, ExceptionTable etable, CodeAttribute ca) 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); LineNumberAttribute na = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); if (na != null) na.shiftPc(where, gapLength, exclusive); LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute( LocalVariableAttribute.tag); if (va != null) va.shiftPc(where, gapLength, exclusive); LocalVariableAttribute vta = (LocalVariableAttribute)ca.getAttribute( LocalVariableAttribute.typeTag); if (vta != null) vta.shiftPc(where, gapLength, exclusive); StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag); if (smt != null) smt.shiftPc(where, gapLength, exclusive); StackMap sm = (StackMap)ca.getAttribute(StackMap.tag); if (sm != null) sm.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, if_icmp, if_acmp, 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 (i != j && (gapLength & 3) != 0) throw new AlignmentException(); int i2 = (i & ~3) + 4; // 0-3 byte padding // IBM JVM 1.4.2 cannot run the following code: // int i0 = i; // while (i0 < i2) // newcode[j++] = code[i0++]; // So extracting this code into an external method. // see JIRA JASSIST-74. j = copyGapBytes(newcode, j, code, i, i2); 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; int 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 (i != j && (gapLength & 3) != 0) throw new AlignmentException(); int i2 = (i & ~3) + 4; // 0-3 byte padding // IBM JVM 1.4.2 cannot run the following code: // int i0 = i; // while (i0 < i2) // newcode[j++] = code[i0++]; // So extracting this code into an external method. // see JIRA JASSIST-74. j = copyGapBytes(newcode, j, code, i, i2); 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; int 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 copyGapBytes(byte[] newcode, int j, byte[] code, int i, int iEnd) { switch (iEnd - i) { case 4: newcode[j++] = code[i++]; case 3: newcode[j++] = code[i++]; case 2: newcode[j++] = code[i++]; case 1: newcode[j++] = code[i++]; default: } return j; } 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 (i == where) { if (target < where && exclusive) offset -= gapLength; else if (where < target && !exclusive) offset += gapLength; } else if (target < where || (!exclusive && where == target)) offset -= gapLength; return offset; } static class Pointers { int cursor; int mark0, mark; ExceptionTable etable; LineNumberAttribute line; LocalVariableAttribute vars, types; StackMapTable stack; StackMap stack2; Pointers(int cur, int m, int m0, ExceptionTable et, CodeAttribute ca) { cursor = cur; mark = m; mark0 = m0; etable = et; // non null line = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); vars = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.tag); types = (LocalVariableAttribute)ca.getAttribute(LocalVariableAttribute.typeTag); stack = (StackMapTable)ca.getAttribute(StackMapTable.tag); stack2 = (StackMap)ca.getAttribute(StackMap.tag); } void shiftPc(int where, int gapLength, boolean exclusive) throws BadBytecode { if (where < cursor || (where == cursor && exclusive)) cursor += gapLength; if (where < mark || (where == mark && exclusive)) mark += gapLength; if (where < mark0 || (where == mark0 && exclusive)) mark0 += gapLength; etable.shiftPc(where, gapLength, exclusive); if (line != null) line.shiftPc(where, gapLength, exclusive); if (vars != null) vars.shiftPc(where, gapLength, exclusive); if (types != null) types.shiftPc(where, gapLength, exclusive); if (stack != null) stack.shiftPc(where, gapLength, exclusive); if (stack2 != null) stack2.shiftPc(where, gapLength, exclusive); } } /* * This method is called from CodeAttribute.LdcEntry.doit(). */ static byte[] changeLdcToLdcW(byte[] code, ExceptionTable etable, CodeAttribute ca, CodeAttribute.LdcEntry ldcs) throws BadBytecode { ArrayList jumps = makeJumpList(code, code.length); while (ldcs != null) { addLdcW(ldcs, jumps); ldcs = ldcs.next; } Pointers pointers = new Pointers(0, 0, 0, etable, ca); byte[] r = insertGap2w(code, 0, 0, false, jumps, pointers); return r; } private static void addLdcW(CodeAttribute.LdcEntry ldcs, ArrayList jumps) { int where = ldcs.where; LdcW ldcw = new LdcW(where, ldcs.index); int s = jumps.size(); for (int i = 0; i < s; i++) if (where < ((Branch)jumps.get(i)).orgPos) { jumps.add(i, ldcw); return; } jumps.add(ldcw); } /* * insertGapCore0w() can handle a long code sequence more than 32K. * It guarantees that the length of the inserted gap (NOPs) is equal to * gapLength. No other NOPs except some NOPs following TABLESWITCH or * LOOKUPSWITCH will not be inserted. * * Note: currentPos might be moved. * * @param where It must indicate the first byte of an opcode. * @param newWhere It contains the updated index of the position where a gap * is inserted and the length of the gap. * It must not be null. */ private byte[] insertGapCore0w(byte[] code, int where, int gapLength, boolean exclusive, ExceptionTable etable, CodeAttribute ca, Gap newWhere) throws BadBytecode { if (gapLength <= 0) return code; ArrayList jumps = makeJumpList(code, code.length); Pointers pointers = new Pointers(currentPos, mark, where, etable, ca); byte[] r = insertGap2w(code, where, gapLength, exclusive, jumps, pointers); currentPos = pointers.cursor; mark = pointers.mark; int where2 = pointers.mark0; if (where2 == currentPos && !exclusive) currentPos += gapLength; if (exclusive) where2 -= gapLength; newWhere.position = where2; newWhere.length = gapLength; return r; } private static byte[] insertGap2w(byte[] code, int where, int gapLength, boolean exclusive, ArrayList jumps, Pointers ptrs) throws BadBytecode { int n = jumps.size(); if (gapLength > 0) { ptrs.shiftPc(where, gapLength, exclusive); for (int i = 0; i < n; i++) ((Branch)jumps.get(i)).shift(where, gapLength, exclusive); } boolean unstable = true; do { while (unstable) { unstable = false; for (int i = 0; i < n; i++) { Branch b = (Branch)jumps.get(i); if (b.expanded()) { unstable = true; int p = b.pos; int delta = b.deltaSize(); ptrs.shiftPc(p, delta, false); for (int j = 0; j < n; j++) ((Branch)jumps.get(j)).shift(p, delta, false); } } } for (int i = 0; i < n; i++) { Branch b = (Branch)jumps.get(i); int diff = b.gapChanged(); if (diff > 0) { unstable = true; int p = b.pos; ptrs.shiftPc(p, diff, false); for (int j = 0; j < n; j++) ((Branch)jumps.get(j)).shift(p, diff, false); } } } while (unstable); return makeExapndedCode(code, jumps, where, gapLength); } private static ArrayList makeJumpList(byte[] code, int endPos) throws BadBytecode { ArrayList jumps = new ArrayList(); int nextPos; for (int i = 0; i < endPos; i = nextPos) { nextPos = nextOpcode(code, i); int inst = code[i] & 0xff; // if, if_icmp, if_acmp, goto, jsr if ((153 <= inst && inst <= 168) || inst == IFNULL || inst == IFNONNULL) { /* 2bytes *signed* offset */ int offset = (code[i + 1] << 8) | (code[i + 2] & 0xff); Branch b; if (inst == GOTO || inst == JSR) b = new Jump16(i, offset); else b = new If16(i, offset); jumps.add(b); } else if (inst == GOTO_W || inst == JSR_W) { /* 4bytes offset */ int offset = ByteArray.read32bit(code, i + 1); jumps.add(new Jump32(i, offset)); } else if (inst == TABLESWITCH) { int i2 = (i & ~3) + 4; // 0-3 byte padding int defaultbyte = ByteArray.read32bit(code, i2); int lowbyte = ByteArray.read32bit(code, i2 + 4); int highbyte = ByteArray.read32bit(code, i2 + 8); int i0 = i2 + 12; int size = highbyte - lowbyte + 1; int[] offsets = new int[size]; for (int j = 0; j < size; j++) { offsets[j] = ByteArray.read32bit(code, i0); i0 += 4; } jumps.add(new Table(i, defaultbyte, lowbyte, highbyte, offsets)); } else if (inst == LOOKUPSWITCH) { int i2 = (i & ~3) + 4; // 0-3 byte padding int defaultbyte = ByteArray.read32bit(code, i2); int npairs = ByteArray.read32bit(code, i2 + 4); int i0 = i2 + 8; int[] matches = new int[npairs]; int[] offsets = new int[npairs]; for (int j = 0; j < npairs; j++) { matches[j] = ByteArray.read32bit(code, i0); offsets[j] = ByteArray.read32bit(code, i0 + 4); i0 += 8; } jumps.add(new Lookup(i, defaultbyte, matches, offsets)); } } return jumps; } private static byte[] makeExapndedCode(byte[] code, ArrayList jumps, int where, int gapLength) throws BadBytecode { int n = jumps.size(); int size = code.length + gapLength; for (int i = 0; i < n; i++) { Branch b = (Branch)jumps.get(i); size += b.deltaSize(); } byte[] newcode = new byte[size]; int src = 0, dest = 0, bindex = 0; int len = code.length; Branch b; int bpos; if (0 < n) { b = (Branch)jumps.get(0); bpos = b.orgPos; } else { b = null; bpos = len; // src will be never equal to bpos } while (src < len) { if (src == where) { int pos2 = dest + gapLength; while (dest < pos2) newcode[dest++] = NOP; } if (src != bpos) newcode[dest++] = code[src++]; else { int s = b.write(src, code, dest, newcode); src += s; dest += s + b.deltaSize(); if (++bindex < n) { b = (Branch)jumps.get(bindex); bpos = b.orgPos; } else { b = null; bpos = len; } } } return newcode; } static abstract class Branch { int pos, orgPos; Branch(int p) { pos = orgPos = p; } void shift(int where, int gapLength, boolean exclusive) { if (where < pos || (where == pos && exclusive)) pos += gapLength; } boolean expanded() { return false; } int gapChanged() { return 0; } int deltaSize() { return 0; } // newSize - oldSize // This returns the original instruction size. abstract int write(int srcPos, byte[] code, int destPos, byte[] newcode); } /* used by changeLdcToLdcW() and CodeAttribute.LdcEntry. */ static class LdcW extends Branch { int index; boolean state; LdcW(int p, int i) { super(p); index = i; state = true; } boolean expanded() { if (state) { state = false; return true; } else return false; } int deltaSize() { return 1; } int write(int srcPos, byte[] code, int destPos, byte[] newcode) { newcode[destPos] = LDC_W; ByteArray.write16bit(index, newcode, destPos + 1); return 2; } } static abstract class Branch16 extends Branch { int offset; int state; static final int BIT16 = 0; static final int EXPAND = 1; static final int BIT32 = 2; Branch16(int p, int off) { super(p); offset = off; state = BIT16; } void shift(int where, int gapLength, boolean exclusive) { offset = newOffset(pos, offset, where, gapLength, exclusive); super.shift(where, gapLength, exclusive); if (state == BIT16) if (offset < Short.MIN_VALUE || Short.MAX_VALUE < offset) state = EXPAND; } boolean expanded() { if (state == EXPAND) { state = BIT32; return true; } else return false; } abstract int deltaSize(); abstract void write32(int src, byte[] code, int dest, byte[] newcode); int write(int src, byte[] code, int dest, byte[] newcode) { if (state == BIT32) write32(src, code, dest, newcode); else { newcode[dest] = code[src]; ByteArray.write16bit(offset, newcode, dest + 1); } return 3; } } // GOTO or JSR static class Jump16 extends Branch16 { Jump16(int p, int off) { super(p, off); } int deltaSize() { return state == BIT32 ? 2 : 0; } void write32(int src, byte[] code, int dest, byte[] newcode) { newcode[dest] = (byte)(((code[src] & 0xff) == GOTO) ? GOTO_W : JSR_W); ByteArray.write32bit(offset, newcode, dest + 1); } } // if, if_icmp, or if_acmp static class If16 extends Branch16 { If16(int p, int off) { super(p, off); } int deltaSize() { return state == BIT32 ? 5 : 0; } void write32(int src, byte[] code, int dest, byte[] newcode) { newcode[dest] = (byte)opcode(code[src] & 0xff); newcode[dest + 1] = 0; newcode[dest + 2] = 8; // branch_offset = 8 newcode[dest + 3] = (byte)GOTO_W; ByteArray.write32bit(offset - 3, newcode, dest + 4); } int opcode(int op) { if (op == IFNULL) return IFNONNULL; else if (op == IFNONNULL) return IFNULL; else { if (((op - IFEQ) & 1) == 0) return op + 1; else return op - 1; } } } static class Jump32 extends Branch { int offset; Jump32(int p, int off) { super(p); offset = off; } void shift(int where, int gapLength, boolean exclusive) { offset = newOffset(pos, offset, where, gapLength, exclusive); super.shift(where, gapLength, exclusive); } int write(int src, byte[] code, int dest, byte[] newcode) { newcode[dest] = code[src]; ByteArray.write32bit(offset, newcode, dest + 1); return 5; } } static abstract class Switcher extends Branch { int gap, defaultByte; int[] offsets; Switcher(int pos, int defaultByte, int[] offsets) { super(pos); this.gap = 3 - (pos & 3); this.defaultByte = defaultByte; this.offsets = offsets; } void shift(int where, int gapLength, boolean exclusive) { int p = pos; defaultByte = newOffset(p, defaultByte, where, gapLength, exclusive); int num = offsets.length; for (int i = 0; i < num; i++) offsets[i] = newOffset(p, offsets[i], where, gapLength, exclusive); super.shift(where, gapLength, exclusive); } int gapChanged() { int newGap = 3 - (pos & 3); if (newGap > gap) { int diff = newGap - gap; gap = newGap; return diff; } return 0; } int deltaSize() { return gap - (3 - (orgPos & 3)); } int write(int src, byte[] code, int dest, byte[] newcode) { int padding = 3 - (pos & 3); int nops = gap - padding; int bytecodeSize = 5 + (3 - (orgPos & 3)) + tableSize(); adjustOffsets(bytecodeSize, nops); newcode[dest++] = code[src]; while (padding-- > 0) newcode[dest++] = 0; ByteArray.write32bit(defaultByte, newcode, dest); int size = write2(dest + 4, newcode); dest += size + 4; while (nops-- > 0) newcode[dest++] = NOP; return 5 + (3 - (orgPos & 3)) + size; } abstract int write2(int dest, byte[] newcode); abstract int tableSize(); /* If the new bytecode size is shorter than the original, some NOPs * are appended after this branch instruction (tableswitch or * lookupswitch) to fill the gap. * This method changes a branch offset to point to the first NOP * if the offset originally points to the bytecode next to this * branch instruction. Otherwise, the bytecode would contain * dead code. It complicates the generation of StackMap and * StackMapTable. */ void adjustOffsets(int size, int nops) { if (defaultByte == size) defaultByte -= nops; for (int i = 0; i < offsets.length; i++) if (offsets[i] == size) offsets[i] -= nops; } } static class Table extends Switcher { int low, high; Table(int pos, int defaultByte, int low, int high, int[] offsets) { super(pos, defaultByte, offsets); this.low = low; this.high = high; } int write2(int dest, byte[] newcode) { ByteArray.write32bit(low, newcode, dest); ByteArray.write32bit(high, newcode, dest + 4); int n = offsets.length; dest += 8; for (int i = 0; i < n; i++) { ByteArray.write32bit(offsets[i], newcode, dest); dest += 4; } return 8 + 4 * n; } int tableSize() { return 8 + 4 * offsets.length; } } static class Lookup extends Switcher { int[] matches; Lookup(int pos, int defaultByte, int[] matches, int[] offsets) { super(pos, defaultByte, offsets); this.matches = matches; } int write2(int dest, byte[] newcode) { int n = matches.length; ByteArray.write32bit(n, newcode, dest); dest += 4; for (int i = 0; i < n; i++) { ByteArray.write32bit(matches[i], newcode, dest); ByteArray.write32bit(offsets[i], newcode, dest + 4); dest += 8; } return 4 + 8 * n; } int tableSize() { return 4 + 8 * matches.length; } } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/0000755000175000017500000000000011637463415023635 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/bytecode/annotation/IntegerMemberValue.java0000644000175000017500000000576710307445766030243 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; import java.lang.reflect.Method; /** * Integer constant value. * * @author Bill Burke * @author Shigeru Chiba */ public class IntegerMemberValue extends MemberValue { int valueIndex; /** * Constructs an int constant value. The initial value is specified * by the constant pool entry at the given index. * * @param index the index of a CONSTANT_Integer_info structure. */ public IntegerMemberValue(int index, ConstPool cp) { super('I', cp); this.valueIndex = index; } /** * Constructs an int constant value. * Note that this constructor receives the initial value * as the second parameter * unlike the corresponding constructors in the sibling classes. * This is for making a difference from the constructor that receives * an index into the constant pool table as the first parameter. * Note that the index is also int type. * * @param value the initial value. */ public IntegerMemberValue(ConstPool cp, int value) { super('I', cp); setValue(value); } /** * Constructs an int constant value. The initial value is 0. */ public IntegerMemberValue(ConstPool cp) { super('I', cp); setValue(0); } Object getValue(ClassLoader cl, ClassPool cp, Method m) { return new Integer(getValue()); } Class getType(ClassLoader cl) { return int.class; } /** * Obtains the value of the member. */ public int getValue() { return cp.getIntegerInfo(valueIndex); } /** * Sets the value of the member. */ public void setValue(int newValue) { valueIndex = cp.addIntegerInfo(newValue); } /** * Obtains the string representation of this object. */ public String toString() { return Integer.toString(getValue()); } /** * Writes the value. */ public void write(AnnotationsWriter writer) throws IOException { writer.constValueIndex(getValue()); } /** * Accepts a visitor. */ public void accept(MemberValueVisitor visitor) { visitor.visitIntegerMemberValue(this); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/BooleanMemberValue.java0000644000175000017500000000526510307445766030216 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; import java.lang.reflect.Method; /** * Boolean constant value. * * @author Bill Burke * @author Shigeru Chiba */ public class BooleanMemberValue extends MemberValue { int valueIndex; /** * Constructs a boolean constant value. The initial value is specified * by the constant pool entry at the given index. * * @param index the index of a CONSTANT_Integer_info structure. */ public BooleanMemberValue(int index, ConstPool cp) { super('Z', cp); this.valueIndex = index; } /** * Constructs a boolean constant value. * * @param b the initial value. */ public BooleanMemberValue(boolean b, ConstPool cp) { super('Z', cp); setValue(b); } /** * Constructs a boolean constant value. The initial value is false. */ public BooleanMemberValue(ConstPool cp) { super('Z', cp); setValue(false); } Object getValue(ClassLoader cl, ClassPool cp, Method m) { return new Boolean(getValue()); } Class getType(ClassLoader cl) { return boolean.class; } /** * Obtains the value of the member. */ public boolean getValue() { return cp.getIntegerInfo(valueIndex) != 0; } /** * Sets the value of the member. */ public void setValue(boolean newValue) { valueIndex = cp.addIntegerInfo(newValue ? 1 : 0); } /** * Obtains the string representation of this object. */ public String toString() { return getValue() ? "true" : "false"; } /** * Writes the value. */ public void write(AnnotationsWriter writer) throws IOException { writer.constValueIndex(getValue()); } /** * Accepts a visitor. */ public void accept(MemberValueVisitor visitor) { visitor.visitBooleanMemberValue(this); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/LongMemberValue.java0000644000175000017500000000516310307445766027533 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; import java.lang.reflect.Method; /** * Long integer constant value. * * @author Bill Burke * @author Shigeru Chiba */ public class LongMemberValue extends MemberValue { int valueIndex; /** * Constructs a long constant value. The initial value is specified * by the constant pool entry at the given index. * * @param index the index of a CONSTANT_Long_info structure. */ public LongMemberValue(int index, ConstPool cp) { super('J', cp); this.valueIndex = index; } /** * Constructs a long constant value. * * @param j the initial value. */ public LongMemberValue(long j, ConstPool cp) { super('J', cp); setValue(j); } /** * Constructs a long constant value. The initial value is 0. */ public LongMemberValue(ConstPool cp) { super('J', cp); setValue(0L); } Object getValue(ClassLoader cl, ClassPool cp, Method m) { return new Long(getValue()); } Class getType(ClassLoader cl) { return long.class; } /** * Obtains the value of the member. */ public long getValue() { return cp.getLongInfo(valueIndex); } /** * Sets the value of the member. */ public void setValue(long newValue) { valueIndex = cp.addLongInfo(newValue); } /** * Obtains the string representation of this object. */ public String toString() { return Long.toString(getValue()); } /** * Writes the value. */ public void write(AnnotationsWriter writer) throws IOException { writer.constValueIndex(getValue()); } /** * Accepts a visitor. */ public void accept(MemberValueVisitor visitor) { visitor.visitLongMemberValue(this); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/ByteMemberValue.java0000644000175000017500000000517610307445766027543 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; import java.lang.reflect.Method; /** * Byte constant value. * * @author Bill Burke * @author Shigeru Chiba */ public class ByteMemberValue extends MemberValue { int valueIndex; /** * Constructs a byte constant value. The initial value is specified * by the constant pool entry at the given index. * * @param index the index of a CONSTANT_Integer_info structure. */ public ByteMemberValue(int index, ConstPool cp) { super('B', cp); this.valueIndex = index; } /** * Constructs a byte constant value. * * @param b the initial value. */ public ByteMemberValue(byte b, ConstPool cp) { super('B', cp); setValue(b); } /** * Constructs a byte constant value. The initial value is 0. */ public ByteMemberValue(ConstPool cp) { super('B', cp); setValue((byte)0); } Object getValue(ClassLoader cl, ClassPool cp, Method m) { return new Byte(getValue()); } Class getType(ClassLoader cl) { return byte.class; } /** * Obtains the value of the member. */ public byte getValue() { return (byte)cp.getIntegerInfo(valueIndex); } /** * Sets the value of the member. */ public void setValue(byte newValue) { valueIndex = cp.addIntegerInfo(newValue); } /** * Obtains the string representation of this object. */ public String toString() { return Byte.toString(getValue()); } /** * Writes the value. */ public void write(AnnotationsWriter writer) throws IOException { writer.constValueIndex(getValue()); } /** * Accepts a visitor. */ public void accept(MemberValueVisitor visitor) { visitor.visitByteMemberValue(this); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/NoSuchClassError.java0000644000175000017500000000227611276236321027677 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2009 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; /** * Thrown if the linkage fails. * It keeps the name of the class that caused this error. */ public class NoSuchClassError extends Error { private String className; /** * Constructs an exception. */ public NoSuchClassError(String className, Error cause) { super(cause.toString(), cause); this.className = className; } /** * Returns the name of the class not found. */ public String getClassName() { return className; } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/CharMemberValue.java0000644000175000017500000000520510307445766027506 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; import java.lang.reflect.Method; /** * Char constant value. * * @author Bill Burke * @author Shigeru Chiba */ public class CharMemberValue extends MemberValue { int valueIndex; /** * Constructs a char constant value. The initial value is specified * by the constant pool entry at the given index. * * @param index the index of a CONSTANT_Integer_info structure. */ public CharMemberValue(int index, ConstPool cp) { super('C', cp); this.valueIndex = index; } /** * Constructs a char constant value. * * @param c the initial value. */ public CharMemberValue(char c, ConstPool cp) { super('C', cp); setValue(c); } /** * Constructs a char constant value. The initial value is '\0'. */ public CharMemberValue(ConstPool cp) { super('C', cp); setValue('\0'); } Object getValue(ClassLoader cl, ClassPool cp, Method m) { return new Character(getValue()); } Class getType(ClassLoader cl) { return char.class; } /** * Obtains the value of the member. */ public char getValue() { return (char)cp.getIntegerInfo(valueIndex); } /** * Sets the value of the member. */ public void setValue(char newValue) { valueIndex = cp.addIntegerInfo(newValue); } /** * Obtains the string representation of this object. */ public String toString() { return Character.toString(getValue()); } /** * Writes the value. */ public void write(AnnotationsWriter writer) throws IOException { writer.constValueIndex(getValue()); } /** * Accepts a visitor. */ public void accept(MemberValueVisitor visitor) { visitor.visitCharMemberValue(this); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/AnnotationsWriter.java0000644000175000017500000002553110630701321030157 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import java.io.*; import javassist.bytecode.ByteArray; import javassist.bytecode.ConstPool; /** * A convenience class for constructing a * ..Annotations_attribute. * See the source code of the AnnotationsAttribute.Copier class. * *

    The following code snippet is an example of use of this class: * *

       * ConstPool pool = ...;
       * output = new ByteArrayOutputStream();
       * writer = new AnnotationsWriter(output, pool);
       *
       * writer.numAnnotations(1);
       * writer.annotation("Author", 2);
       * writer.memberValuePair("name");
       * writer.constValueIndex("chiba");
       * writer.memberValuePair("address");
       * writer.constValueIndex("tokyo");
       *
       * writer.close();
       * byte[] attribute_info = output.toByteArray();
       * AnnotationsAttribute anno
       *     = new AnnotationsAttribute(pool, AnnotationsAttribute.visibleTag,
       *                                attribute_info);
       * 
    * *

    The code snippet above generates the annotation attribute * corresponding to this annotation: * *

       *  @Author(name = "chiba", address = "tokyo")
       * 
    * * @see javassist.bytecode.AnnotationsAttribute * @see javassist.bytecode.ParameterAnnotationsAttribute */ public class AnnotationsWriter { private OutputStream output; private ConstPool pool; /** * Constructs with the given output stream. * * @param os the output stream. * @param cp the constant pool. */ public AnnotationsWriter(OutputStream os, ConstPool cp) { output = os; pool = cp; } /** * Obtains the constant pool given to the constructor. */ public ConstPool getConstPool() { return pool; } /** * Closes the output stream. * */ public void close() throws IOException { output.close(); } /** * Writes num_parameters in * Runtime(In)VisibleParameterAnnotations_attribute. * This method must be followed by num calls to * numAnnotations(). */ public void numParameters(int num) throws IOException { output.write(num); } /** * Writes num_annotations in * Runtime(In)VisibleAnnotations_attribute. * This method must be followed by num calls to * annotation(). */ public void numAnnotations(int num) throws IOException { write16bit(num); } /** * Writes annotation. * This method must be followed by numMemberValuePairs * calls to memberValuePair(). * * @param type the annotation interface name. * @param numMemberValuePairs num_member_value_pairs * in annotation. */ public void annotation(String type, int numMemberValuePairs) throws IOException { annotation(pool.addUtf8Info(type), numMemberValuePairs); } /** * Writes annotation. * This method must be followed by numMemberValuePairs * calls to memberValuePair(). * * @param typeIndex type_index in annotation. * @param numMemberValuePairs num_member_value_pairs * in annotation. */ public void annotation(int typeIndex, int numMemberValuePairs) throws IOException { write16bit(typeIndex); write16bit(numMemberValuePairs); } /** * Writes an element of a member_value_pairs array * in annotation. * This method must be followed by a * call to constValueIndex(), enumConstValue(), * etc. * * @param memberName the name of the annotation type member. */ public void memberValuePair(String memberName) throws IOException { memberValuePair(pool.addUtf8Info(memberName)); } /** * Writes an element of a member_value_pairs array * in annotation. * This method must be followed by a * call to constValueIndex(), enumConstValue(), * etc. * * @param memberNameIndex member_name_index * in member_value_pairs array. */ public void memberValuePair(int memberNameIndex) throws IOException { write16bit(memberNameIndex); } /** * Writes tag and const_value_index * in member_value. * * @param value the constant value. */ public void constValueIndex(boolean value) throws IOException { constValueIndex('Z', pool.addIntegerInfo(value ? 1 : 0)); } /** * Writes tag and const_value_index * in member_value. * * @param value the constant value. */ public void constValueIndex(byte value) throws IOException { constValueIndex('B', pool.addIntegerInfo(value)); } /** * Writes tag and const_value_index * in member_value. * * @param value the constant value. */ public void constValueIndex(char value) throws IOException { constValueIndex('C', pool.addIntegerInfo(value)); } /** * Writes tag and const_value_index * in member_value. * * @param value the constant value. */ public void constValueIndex(short value) throws IOException { constValueIndex('S', pool.addIntegerInfo(value)); } /** * Writes tag and const_value_index * in member_value. * * @param value the constant value. */ public void constValueIndex(int value) throws IOException { constValueIndex('I', pool.addIntegerInfo(value)); } /** * Writes tag and const_value_index * in member_value. * * @param value the constant value. */ public void constValueIndex(long value) throws IOException { constValueIndex('J', pool.addLongInfo(value)); } /** * Writes tag and const_value_index * in member_value. * * @param value the constant value. */ public void constValueIndex(float value) throws IOException { constValueIndex('F', pool.addFloatInfo(value)); } /** * Writes tag and const_value_index * in member_value. * * @param value the constant value. */ public void constValueIndex(double value) throws IOException { constValueIndex('D', pool.addDoubleInfo(value)); } /** * Writes tag and const_value_index * in member_value. * * @param value the constant value. */ public void constValueIndex(String value) throws IOException { constValueIndex('s', pool.addUtf8Info(value)); } /** * Writes tag and const_value_index * in member_value. * * @param tag tag in member_value. * @param index const_value_index * in member_value. */ public void constValueIndex(int tag, int index) throws IOException { output.write(tag); write16bit(index); } /** * Writes tag and enum_const_value * in member_value. * * @param typeName the type name of the enum constant. * @param constName the simple name of the enum constant. */ public void enumConstValue(String typeName, String constName) throws IOException { enumConstValue(pool.addUtf8Info(typeName), pool.addUtf8Info(constName)); } /** * Writes tag and enum_const_value * in member_value. * * @param typeNameIndex type_name_index * in member_value. * @param constNameIndex const_name_index * in member_value. */ public void enumConstValue(int typeNameIndex, int constNameIndex) throws IOException { output.write('e'); write16bit(typeNameIndex); write16bit(constNameIndex); } /** * Writes tag and class_info_index * in member_value. * * @param name the class name. */ public void classInfoIndex(String name) throws IOException { classInfoIndex(pool.addUtf8Info(name)); } /** * Writes tag and class_info_index * in member_value. * * @param index class_info_index */ public void classInfoIndex(int index) throws IOException { output.write('c'); write16bit(index); } /** * Writes tag and annotation_value * in member_value. * This method must be followed by a call to annotation(). */ public void annotationValue() throws IOException { output.write('@'); } /** * Writes tag and array_value * in member_value. * This method must be followed by numValues calls * to constValueIndex(), enumConstValue(), * etc. * * @param numValues num_values * in array_value. */ public void arrayValue(int numValues) throws IOException { output.write('['); write16bit(numValues); } private void write16bit(int value) throws IOException { byte[] buf = new byte[2]; ByteArray.write16bit(value, buf, 0); output.write(buf); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/package.html0000644000175000017500000000021310361120146026072 0ustar twernertwerner Bytecode-level Annotations API.

    This package provides low-level API for editing annotations attributes. javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/EnumMemberValue.java0000644000175000017500000000674510307445766027547 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import java.io.IOException; import java.lang.reflect.Method; import javassist.ClassPool; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; /** * Enum constant value. * * @author Bill Burke * @author Shigeru Chiba */ public class EnumMemberValue extends MemberValue { int typeIndex, valueIndex; /** * Constructs an enum constant value. The initial value is specified * by the constant pool entries at the given indexes. * * @param type the index of a CONSTANT_Utf8_info structure * representing the enum type. * @param value the index of a CONSTANT_Utf8_info structure. * representing the enum value. */ public EnumMemberValue(int type, int value, ConstPool cp) { super('e', cp); this.typeIndex = type; this.valueIndex = value; } /** * Constructs an enum constant value. * The initial value is not specified. */ public EnumMemberValue(ConstPool cp) { super('e', cp); typeIndex = valueIndex = 0; } Object getValue(ClassLoader cl, ClassPool cp, Method m) throws ClassNotFoundException { try { return getType(cl).getField(getValue()).get(null); } catch (NoSuchFieldException e) { throw new ClassNotFoundException(getType() + "." + getValue()); } catch (IllegalAccessException e) { throw new ClassNotFoundException(getType() + "." + getValue()); } } Class getType(ClassLoader cl) throws ClassNotFoundException { return loadClass(cl, getType()); } /** * Obtains the enum type name. * * @return a fully-qualified type name. */ public String getType() { return Descriptor.toClassName(cp.getUtf8Info(typeIndex)); } /** * Changes the enum type name. * * @param typename a fully-qualified type name. */ public void setType(String typename) { typeIndex = cp.addUtf8Info(Descriptor.of(typename)); } /** * Obtains the name of the enum constant value. */ public String getValue() { return cp.getUtf8Info(valueIndex); } /** * Changes the name of the enum constant value. */ public void setValue(String name) { valueIndex = cp.addUtf8Info(name); } public String toString() { return getType() + "." + getValue(); } /** * Writes the value. */ public void write(AnnotationsWriter writer) throws IOException { writer.enumConstValue(cp.getUtf8Info(typeIndex), getValue()); } /** * Accepts a visitor. */ public void accept(MemberValueVisitor visitor) { visitor.visitEnumMemberValue(this); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/AnnotationImpl.java0000644000175000017500000002457211024446264027437 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import javassist.ClassPool; import javassist.CtClass; import javassist.NotFoundException; import javassist.bytecode.AnnotationDefaultAttribute; import javassist.bytecode.ClassFile; import javassist.bytecode.MethodInfo; /** * Internal-use only. This is a helper class internally used for implementing * toAnnotationType() in Annotation. * * @author Shigeru Chiba * @author Bill Burke * @author Adrian Brock */ public class AnnotationImpl implements InvocationHandler { private static final String JDK_ANNOTATION_CLASS_NAME = "java.lang.annotation.Annotation"; private static Method JDK_ANNOTATION_TYPE_METHOD = null; private Annotation annotation; private ClassPool pool; private ClassLoader classLoader; private transient Class annotationType; private transient int cachedHashCode = Integer.MIN_VALUE; static { // Try to resolve the JDK annotation type method try { Class clazz = Class.forName(JDK_ANNOTATION_CLASS_NAME); JDK_ANNOTATION_TYPE_METHOD = clazz.getMethod("annotationType", (Class[])null); } catch (Exception ignored) { // Probably not JDK5+ } } /** * Constructs an annotation object. * * @param cl class loader for obtaining annotation types. * @param clazz the annotation type. * @param cp class pool for containing an annotation * type (or null). * @param anon the annotation. * @return the annotation */ public static Object make(ClassLoader cl, Class clazz, ClassPool cp, Annotation anon) { AnnotationImpl handler = new AnnotationImpl(anon, cp, cl); return Proxy.newProxyInstance(cl, new Class[] { clazz }, handler); } private AnnotationImpl(Annotation a, ClassPool cp, ClassLoader loader) { annotation = a; pool = cp; classLoader = loader; } /** * Obtains the name of the annotation type. * * @return the type name */ public String getTypeName() { return annotation.getTypeName(); } /** * Get the annotation type * * @return the annotation class * @throws NoClassDefFoundError when the class could not loaded */ private Class getAnnotationType() { if (annotationType == null) { String typeName = annotation.getTypeName(); try { annotationType = classLoader.loadClass(typeName); } catch (ClassNotFoundException e) { NoClassDefFoundError error = new NoClassDefFoundError("Error loading annotation class: " + typeName); error.setStackTrace(e.getStackTrace()); throw error; } } return annotationType; } /** * Obtains the internal data structure representing the annotation. * * @return the annotation */ public Annotation getAnnotation() { return annotation; } /** * Executes a method invocation on a proxy instance. * The implementations of toString(), equals(), * and hashCode() are directly supplied by the * AnnotationImpl. The annotationType() method * is also available on the proxy instance. */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName(); if (Object.class == method.getDeclaringClass()) { if ("equals".equals(name)) { Object obj = args[0]; return new Boolean(checkEquals(obj)); } else if ("toString".equals(name)) return annotation.toString(); else if ("hashCode".equals(name)) return new Integer(hashCode()); } else if ("annotationType".equals(name) && method.getParameterTypes().length == 0) return getAnnotationType(); MemberValue mv = annotation.getMemberValue(name); if (mv == null) return getDefault(name, method); else return mv.getValue(classLoader, pool, method); } private Object getDefault(String name, Method method) throws ClassNotFoundException, RuntimeException { String classname = annotation.getTypeName(); if (pool != null) { try { CtClass cc = pool.get(classname); ClassFile cf = cc.getClassFile2(); MethodInfo minfo = cf.getMethod(name); if (minfo != null) { AnnotationDefaultAttribute ainfo = (AnnotationDefaultAttribute) minfo.getAttribute(AnnotationDefaultAttribute.tag); if (ainfo != null) { MemberValue mv = ainfo.getDefaultValue(); return mv.getValue(classLoader, pool, method); } } } catch (NotFoundException e) { throw new RuntimeException("cannot find a class file: " + classname); } } throw new RuntimeException("no default value: " + classname + "." + name + "()"); } /** * Returns a hash code value for this object. */ public int hashCode() { if (cachedHashCode == Integer.MIN_VALUE) { int hashCode = 0; // Load the annotation class getAnnotationType(); Method[] methods = annotationType.getDeclaredMethods(); for (int i = 0; i < methods.length; ++ i) { String name = methods[i].getName(); int valueHashCode = 0; // Get the value MemberValue mv = annotation.getMemberValue(name); Object value = null; try { if (mv != null) value = mv.getValue(classLoader, pool, methods[i]); if (value == null) value = getDefault(name, methods[i]); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e); } // Calculate the hash code if (value != null) { if (value.getClass().isArray()) valueHashCode = arrayHashCode(value); else valueHashCode = value.hashCode(); } hashCode += 127 * name.hashCode() ^ valueHashCode; } cachedHashCode = hashCode; } return cachedHashCode; } /** * Check that another annotation equals ourselves. * * @param obj the other annotation * @return the true when equals false otherwise * @throws Exception for any problem */ private boolean checkEquals(Object obj) throws Exception { if (obj == null) return false; // Optimization when the other is one of ourselves if (obj instanceof Proxy) { InvocationHandler ih = Proxy.getInvocationHandler(obj); if (ih instanceof AnnotationImpl) { AnnotationImpl other = (AnnotationImpl) ih; return annotation.equals(other.annotation); } } Class otherAnnotationType = (Class) JDK_ANNOTATION_TYPE_METHOD.invoke(obj, (Object[])null); if (getAnnotationType().equals(otherAnnotationType) == false) return false; Method[] methods = annotationType.getDeclaredMethods(); for (int i = 0; i < methods.length; ++ i) { String name = methods[i].getName(); // Get the value MemberValue mv = annotation.getMemberValue(name); Object value = null; Object otherValue = null; try { if (mv != null) value = mv.getValue(classLoader, pool, methods[i]); if (value == null) value = getDefault(name, methods[i]); otherValue = methods[i].invoke(obj, (Object[])null); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException("Error retrieving value " + name + " for annotation " + annotation.getTypeName(), e); } if (value == null && otherValue != null) return false; if (value != null && value.equals(otherValue) == false) return false; } return true; } /** * Calculates the hashCode of an array using the same * algorithm as java.util.Arrays.hashCode() * * @param object the object * @return the hashCode */ private static int arrayHashCode(Object object) { if (object == null) return 0; int result = 1; Object[] array = (Object[]) object; for (int i = 0; i < array.length; ++i) { int elementHashCode = 0; if (array[i] != null) elementHashCode = array[i].hashCode(); result = 31 * result + elementHashCode; } return result; } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/StringMemberValue.java0000644000175000017500000000520110307445766030073 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; import java.lang.reflect.Method; /** * String constant value. * * @author Bill Burke * @author Shigeru Chiba */ public class StringMemberValue extends MemberValue { int valueIndex; /** * Constructs a string constant value. The initial value is specified * by the constant pool entry at the given index. * * @param index the index of a CONSTANT_Utf8_info structure. */ public StringMemberValue(int index, ConstPool cp) { super('s', cp); this.valueIndex = index; } /** * Constructs a string constant value. * * @param str the initial value. */ public StringMemberValue(String str, ConstPool cp) { super('s', cp); setValue(str); } /** * Constructs a string constant value. The initial value is "". */ public StringMemberValue(ConstPool cp) { super('s', cp); setValue(""); } Object getValue(ClassLoader cl, ClassPool cp, Method m) { return getValue(); } Class getType(ClassLoader cl) { return String.class; } /** * Obtains the value of the member. */ public String getValue() { return cp.getUtf8Info(valueIndex); } /** * Sets the value of the member. */ public void setValue(String newValue) { valueIndex = cp.addUtf8Info(newValue); } /** * Obtains the string representation of this object. */ public String toString() { return "\"" + getValue() + "\""; } /** * Writes the value. */ public void write(AnnotationsWriter writer) throws IOException { writer.constValueIndex(getValue()); } /** * Accepts a visitor. */ public void accept(MemberValueVisitor visitor) { visitor.visitStringMemberValue(this); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/MemberValueVisitor.java0000644000175000017500000000330610300660321030243 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; /** * Visitor for traversing member values included in an annotation. * * @see MemberValue#accept(MemberValueVisitor) * @author Bill Burke */ public interface MemberValueVisitor { public void visitAnnotationMemberValue(AnnotationMemberValue node); public void visitArrayMemberValue(ArrayMemberValue node); public void visitBooleanMemberValue(BooleanMemberValue node); public void visitByteMemberValue(ByteMemberValue node); public void visitCharMemberValue(CharMemberValue node); public void visitDoubleMemberValue(DoubleMemberValue node); public void visitEnumMemberValue(EnumMemberValue node); public void visitFloatMemberValue(FloatMemberValue node); public void visitIntegerMemberValue(IntegerMemberValue node); public void visitLongMemberValue(LongMemberValue node); public void visitShortMemberValue(ShortMemberValue node); public void visitStringMemberValue(StringMemberValue node); public void visitClassMemberValue(ClassMemberValue node); } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/AnnotationMemberValue.java0000644000175000017500000000477710307445766030760 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; import java.lang.reflect.Method; /** * Nested annotation. * * @author Bill Burke * @author Shigeru Chiba */ public class AnnotationMemberValue extends MemberValue { Annotation value; /** * Constructs an annotation member. The initial value is not specified. */ public AnnotationMemberValue(ConstPool cp) { this(null, cp); } /** * Constructs an annotation member. The initial value is specified by * the first parameter. */ public AnnotationMemberValue(Annotation a, ConstPool cp) { super('@', cp); value = a; } Object getValue(ClassLoader cl, ClassPool cp, Method m) throws ClassNotFoundException { return AnnotationImpl.make(cl, getType(cl), cp, value); } Class getType(ClassLoader cl) throws ClassNotFoundException { if (value == null) throw new ClassNotFoundException("no type specified"); else return loadClass(cl, value.getTypeName()); } /** * Obtains the value. */ public Annotation getValue() { return value; } /** * Sets the value of this member. */ public void setValue(Annotation newValue) { value = newValue; } /** * Obtains the string representation of this object. */ public String toString() { return value.toString(); } /** * Writes the value. */ public void write(AnnotationsWriter writer) throws IOException { writer.annotationValue(); value.write(writer); } /** * Accepts a visitor. */ public void accept(MemberValueVisitor visitor) { visitor.visitAnnotationMemberValue(this); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/ClassMemberValue.java0000644000175000017500000000733610467654047027706 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.ClassPool; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import java.io.IOException; import java.lang.reflect.Method; /** * Class value. * * @author Bill Burke * @author Shigeru Chiba */ public class ClassMemberValue extends MemberValue { int valueIndex; /** * Constructs a class value. The initial value is specified * by the constant pool entry at the given index. * * @param index the index of a CONSTANT_Utf8_info structure. */ public ClassMemberValue(int index, ConstPool cp) { super('c', cp); this.valueIndex = index; } /** * Constructs a class value. * * @param className the initial value. */ public ClassMemberValue(String className, ConstPool cp) { super('c', cp); setValue(className); } /** * Constructs a class value. * The initial value is java.lang.Class. */ public ClassMemberValue(ConstPool cp) { super('c', cp); setValue("java.lang.Class"); } Object getValue(ClassLoader cl, ClassPool cp, Method m) throws ClassNotFoundException { final String classname = getValue(); if (classname.equals("void")) return void.class; else if (classname.equals("int")) return int.class; else if (classname.equals("byte")) return byte.class; else if (classname.equals("long")) return long.class; else if (classname.equals("double")) return double.class; else if (classname.equals("float")) return float.class; else if (classname.equals("char")) return char.class; else if (classname.equals("short")) return short.class; else if (classname.equals("boolean")) return boolean.class; else return loadClass(cl, classname); } Class getType(ClassLoader cl) throws ClassNotFoundException { return loadClass(cl, "java.lang.Class"); } /** * Obtains the value of the member. * * @return fully-qualified class name. */ public String getValue() { String v = cp.getUtf8Info(valueIndex); return Descriptor.toClassName(v); } /** * Sets the value of the member. * * @param newClassName fully-qualified class name. */ public void setValue(String newClassName) { String setTo = Descriptor.of(newClassName); valueIndex = cp.addUtf8Info(setTo); } /** * Obtains the string representation of this object. */ public String toString() { return "<" + getValue() + " class>"; } /** * Writes the value. */ public void write(AnnotationsWriter writer) throws IOException { writer.classInfoIndex(cp.getUtf8Info(valueIndex)); } /** * Accepts a visitor. */ public void accept(MemberValueVisitor visitor) { visitor.visitClassMemberValue(this); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/Annotation.java0000644000175000017500000002632211361634473026615 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.NotFoundException; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Set; import java.util.Iterator; /** * The annotation structure. * *

    An instance of this class is returned by * getAnnotations() in AnnotationsAttribute * or in ParameterAnnotationsAttribute. * * @see javassist.bytecode.AnnotationsAttribute#getAnnotations() * @see javassist.bytecode.ParameterAnnotationsAttribute#getAnnotations() * @see MemberValue * @see MemberValueVisitor * @see AnnotationsWriter * * @author Bill Burke * @author Shigeru Chiba * @author Adrian Brock */ public class Annotation { static class Pair { int name; MemberValue value; } ConstPool pool; int typeIndex; LinkedHashMap members; // this sould be LinkedHashMap // but it is not supported by JDK 1.3. /** * Constructs an annotation including no members. A member can be * later added to the created annotation by addMemberValue(). * * @param type the index into the constant pool table. * the entry at that index must be the * CONSTANT_Utf8_Info structure * repreenting the name of the annotation interface type. * @param cp the constant pool table. * * @see #addMemberValue(String, MemberValue) */ public Annotation(int type, ConstPool cp) { pool = cp; typeIndex = type; members = null; } /** * Constructs an annotation including no members. A member can be * later added to the created annotation by addMemberValue(). * * @param typeName the name of the annotation interface type. * @param cp the constant pool table. * * @see #addMemberValue(String, MemberValue) */ public Annotation(String typeName, ConstPool cp) { this(cp.addUtf8Info(Descriptor.of(typeName)), cp); } /** * Constructs an annotation that can be accessed through the interface * represented by clazz. The values of the members are * not specified. * * @param cp the constant pool table. * @param clazz the interface. * @throws NotFoundException when the clazz is not found */ public Annotation(ConstPool cp, CtClass clazz) throws NotFoundException { // todo Enums are not supported right now. this(cp.addUtf8Info(Descriptor.of(clazz.getName())), cp); if (!clazz.isInterface()) throw new RuntimeException( "Only interfaces are allowed for Annotation creation."); CtMethod methods[] = clazz.getDeclaredMethods(); if (methods.length > 0) { members = new LinkedHashMap(); } for (int i = 0; i < methods.length; i++) { CtClass returnType = methods[i].getReturnType(); addMemberValue(methods[i].getName(), createMemberValue(cp, returnType)); } } /** * Makes an instance of MemberValue. * * @param cp the constant pool table. * @param type the type of the member. * @return the member value * @throws NotFoundException when the type is not found */ public static MemberValue createMemberValue(ConstPool cp, CtClass type) throws NotFoundException { if (type == CtClass.booleanType) return new BooleanMemberValue(cp); else if (type == CtClass.byteType) return new ByteMemberValue(cp); else if (type == CtClass.charType) return new CharMemberValue(cp); else if (type == CtClass.shortType) return new ShortMemberValue(cp); else if (type == CtClass.intType) return new IntegerMemberValue(cp); else if (type == CtClass.longType) return new LongMemberValue(cp); else if (type == CtClass.floatType) return new FloatMemberValue(cp); else if (type == CtClass.doubleType) return new DoubleMemberValue(cp); else if (type.getName().equals("java.lang.Class")) return new ClassMemberValue(cp); else if (type.getName().equals("java.lang.String")) return new StringMemberValue(cp); else if (type.isArray()) { CtClass arrayType = type.getComponentType(); MemberValue member = createMemberValue(cp, arrayType); return new ArrayMemberValue(member, cp); } else if (type.isInterface()) { Annotation info = new Annotation(cp, type); return new AnnotationMemberValue(info, cp); } else { // treat as enum. I know this is not typed, // but JBoss has an Annotation Compiler for JDK 1.4 // and I want it to work with that. - Bill Burke EnumMemberValue emv = new EnumMemberValue(cp); emv.setType(type.getName()); return emv; } } /** * Adds a new member. * * @param nameIndex the index into the constant pool table. * The entry at that index must be * a CONSTANT_Utf8_info structure. * structure representing the member name. * @param value the member value. */ public void addMemberValue(int nameIndex, MemberValue value) { Pair p = new Pair(); p.name = nameIndex; p.value = value; addMemberValue(p); } /** * Adds a new member. * * @param name the member name. * @param value the member value. */ public void addMemberValue(String name, MemberValue value) { Pair p = new Pair(); p.name = pool.addUtf8Info(name); p.value = value; if (members == null) members = new LinkedHashMap(); members.put(name, p); } private void addMemberValue(Pair pair) { String name = pool.getUtf8Info(pair.name); if (members == null) members = new LinkedHashMap(); members.put(name, pair); } /** * Returns a string representation of the annotation. */ public String toString() { StringBuffer buf = new StringBuffer("@"); buf.append(getTypeName()); if (members != null) { buf.append("("); Iterator mit = members.keySet().iterator(); while (mit.hasNext()) { String name = (String)mit.next(); buf.append(name).append("=").append(getMemberValue(name)); if (mit.hasNext()) buf.append(", "); } buf.append(")"); } return buf.toString(); } /** * Obtains the name of the annotation type. * * @return the type name */ public String getTypeName() { return Descriptor.toClassName(pool.getUtf8Info(typeIndex)); } /** * Obtains all the member names. * * @return null if no members are defined. */ public Set getMemberNames() { if (members == null) return null; else return members.keySet(); } /** * Obtains the member value with the given name. * *

    If this annotation does not have a value for the * specified member, * this method returns null. It does not return a * MemberValue with the default value. * The default value can be obtained from the annotation type. * * @param name the member name * @return null if the member cannot be found or if the value is * the default value. * * @see javassist.bytecode.AnnotationDefaultAttribute */ public MemberValue getMemberValue(String name) { if (members == null) return null; else { Pair p = (Pair)members.get(name); if (p == null) return null; else return p.value; } } /** * Constructs an annotation-type object representing this annotation. * For example, if this annotation represents @Author, * this method returns an Author object. * * @param cl class loader for loading an annotation type. * @param cp class pool for obtaining class files. * @return the annotation * @throws ClassNotFoundException if the class cannot found. * @throws NoSuchClassError if the class linkage fails. */ public Object toAnnotationType(ClassLoader cl, ClassPool cp) throws ClassNotFoundException, NoSuchClassError { return AnnotationImpl.make(cl, MemberValue.loadClass(cl, getTypeName()), cp, this); } /** * Writes this annotation. * * @param writer the output. * @throws IOException for an error during the write */ public void write(AnnotationsWriter writer) throws IOException { String typeName = pool.getUtf8Info(typeIndex); if (members == null) { writer.annotation(typeName, 0); return; } writer.annotation(typeName, members.size()); Iterator it = members.values().iterator(); while (it.hasNext()) { Pair pair = (Pair)it.next(); writer.memberValuePair(pair.name); pair.value.write(writer); } } /** * Returns true if the given object represents the same annotation * as this object. The equality test checks the member values. */ public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || obj instanceof Annotation == false) return false; Annotation other = (Annotation) obj; if (getTypeName().equals(other.getTypeName()) == false) return false; LinkedHashMap otherMembers = other.members; if (members == otherMembers) return true; else if (members == null) return otherMembers == null; else if (otherMembers == null) return false; else return members.equals(otherMembers); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/FloatMemberValue.java0000644000175000017500000000525610307445766027704 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; import java.lang.reflect.Method; /** * Floating-point number constant value. * * @author Bill Burke * @author Shigeru Chiba * @version $Revision: 1.7 $ */ public class FloatMemberValue extends MemberValue { int valueIndex; /** * Constructs a float constant value. The initial value is specified * by the constant pool entry at the given index. * * @param index the index of a CONSTANT_Float_info structure. */ public FloatMemberValue(int index, ConstPool cp) { super('F', cp); this.valueIndex = index; } /** * Constructs a float constant value. * * @param f the initial value. */ public FloatMemberValue(float f, ConstPool cp) { super('F', cp); setValue(f); } /** * Constructs a float constant value. The initial value is 0.0. */ public FloatMemberValue(ConstPool cp) { super('F', cp); setValue(0.0F); } Object getValue(ClassLoader cl, ClassPool cp, Method m) { return new Float(getValue()); } Class getType(ClassLoader cl) { return float.class; } /** * Obtains the value of the member. */ public float getValue() { return cp.getFloatInfo(valueIndex); } /** * Sets the value of the member. */ public void setValue(float newValue) { valueIndex = cp.addFloatInfo(newValue); } /** * Obtains the string representation of this object. */ public String toString() { return Float.toString(getValue()); } /** * Writes the value. */ public void write(AnnotationsWriter writer) throws IOException { writer.constValueIndex(getValue()); } /** * Accepts a visitor. */ public void accept(MemberValueVisitor visitor) { visitor.visitFloatMemberValue(this); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/ShortMemberValue.java0000644000175000017500000000523010307445766027726 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; import java.lang.reflect.Method; /** * Short integer constant value. * * @author Bill Burke * @author Shigeru Chiba */ public class ShortMemberValue extends MemberValue { int valueIndex; /** * Constructs a short constant value. The initial value is specified * by the constant pool entry at the given index. * * @param index the index of a CONSTANT_Integer_info structure. */ public ShortMemberValue(int index, ConstPool cp) { super('S', cp); this.valueIndex = index; } /** * Constructs a short constant value. * * @param s the initial value. */ public ShortMemberValue(short s, ConstPool cp) { super('S', cp); setValue(s); } /** * Constructs a short constant value. The initial value is 0. */ public ShortMemberValue(ConstPool cp) { super('S', cp); setValue((short)0); } Object getValue(ClassLoader cl, ClassPool cp, Method m) { return new Short(getValue()); } Class getType(ClassLoader cl) { return short.class; } /** * Obtains the value of the member. */ public short getValue() { return (short)cp.getIntegerInfo(valueIndex); } /** * Sets the value of the member. */ public void setValue(short newValue) { valueIndex = cp.addIntegerInfo(newValue); } /** * Obtains the string representation of this object. */ public String toString() { return Short.toString(getValue()); } /** * Writes the value. */ public void write(AnnotationsWriter writer) throws IOException { writer.constValueIndex(getValue()); } /** * Accepts a visitor. */ public void accept(MemberValueVisitor visitor) { visitor.visitShortMemberValue(this); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/DoubleMemberValue.java0000644000175000017500000000530110307445766030040 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; import java.lang.reflect.Method; /** * Double floating-point number constant value. * * @author Bill Burke * @author Shigeru Chiba * @version $Revision: 1.7 $ */ public class DoubleMemberValue extends MemberValue { int valueIndex; /** * Constructs a double constant value. The initial value is specified * by the constant pool entry at the given index. * * @param index the index of a CONSTANT_Double_info structure. */ public DoubleMemberValue(int index, ConstPool cp) { super('D', cp); this.valueIndex = index; } /** * Constructs a double constant value. * * @param d the initial value. */ public DoubleMemberValue(double d, ConstPool cp) { super('D', cp); setValue(d); } /** * Constructs a double constant value. The initial value is 0.0. */ public DoubleMemberValue(ConstPool cp) { super('D', cp); setValue(0.0); } Object getValue(ClassLoader cl, ClassPool cp, Method m) { return new Double(getValue()); } Class getType(ClassLoader cl) { return double.class; } /** * Obtains the value of the member. */ public double getValue() { return cp.getDoubleInfo(valueIndex); } /** * Sets the value of the member. */ public void setValue(double newValue) { valueIndex = cp.addDoubleInfo(newValue); } /** * Obtains the string representation of this object. */ public String toString() { return Double.toString(getValue()); } /** * Writes the value. */ public void write(AnnotationsWriter writer) throws IOException { writer.constValueIndex(getValue()); } /** * Accepts a visitor. */ public void accept(MemberValueVisitor visitor) { visitor.visitDoubleMemberValue(this); } } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/MemberValue.java0000644000175000017500000000506711354324425026705 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.ClassPool; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Method; /** * The value of a member declared in an annotation. * * @see Annotation#getMemberValue(String) * @author Bill Burke * @author Shigeru Chiba */ public abstract class MemberValue { ConstPool cp; char tag; MemberValue(char tag, ConstPool cp) { this.cp = cp; this.tag = tag; } /** * Returns the value. If the value type is a primitive type, the * returned value is boxed. */ abstract Object getValue(ClassLoader cl, ClassPool cp, Method m) throws ClassNotFoundException; abstract Class getType(ClassLoader cl) throws ClassNotFoundException; static Class loadClass(ClassLoader cl, String classname) throws ClassNotFoundException, NoSuchClassError { try { return Class.forName(convertFromArray(classname), true, cl); } catch (LinkageError e) { throw new NoSuchClassError(classname, e); } } private static String convertFromArray(String classname) { int index = classname.indexOf("[]"); if (index != -1) { String rawType = classname.substring(0, index); StringBuffer sb = new StringBuffer(Descriptor.of(rawType)); while (index != -1) { sb.insert(0, "["); index = classname.indexOf("[]", index + 1); } return sb.toString().replace('/', '.'); } return classname; } /** * Accepts a visitor. */ public abstract void accept(MemberValueVisitor visitor); /** * Writes the value. */ public abstract void write(AnnotationsWriter w) throws IOException; } javassist-3.12.1.ga/src/main/javassist/bytecode/annotation/ArrayMemberValue.java0000644000175000017500000000771310307445766027715 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 2004 Bill Burke. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.annotation; import javassist.ClassPool; import javassist.bytecode.ConstPool; import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Method; /** * Array member. * * @author Bill Burke * @author Shigeru Chiba */ public class ArrayMemberValue extends MemberValue { MemberValue type; MemberValue[] values; /** * Constructs an array. The initial value or type are not specified. */ public ArrayMemberValue(ConstPool cp) { super('[', cp); type = null; values = null; } /** * Constructs an array. The initial value is not specified. * * @param t the type of the array elements. */ public ArrayMemberValue(MemberValue t, ConstPool cp) { super('[', cp); type = t; values = null; } Object getValue(ClassLoader cl, ClassPool cp, Method method) throws ClassNotFoundException { if (values == null) throw new ClassNotFoundException( "no array elements found: " + method.getName()); int size = values.length; Class clazz; if (type == null) { clazz = method.getReturnType().getComponentType(); if (clazz == null || size > 0) throw new ClassNotFoundException("broken array type: " + method.getName()); } else clazz = type.getType(cl); Object a = Array.newInstance(clazz, size); for (int i = 0; i < size; i++) Array.set(a, i, values[i].getValue(cl, cp, method)); return a; } Class getType(ClassLoader cl) throws ClassNotFoundException { if (type == null) throw new ClassNotFoundException("no array type specified"); Object a = Array.newInstance(type.getType(cl), 0); return a.getClass(); } /** * Obtains the type of the elements. * * @return null if the type is not specified. */ public MemberValue getType() { return type; } /** * Obtains the elements of the array. */ public MemberValue[] getValue() { return values; } /** * Sets the elements of the array. */ public void setValue(MemberValue[] elements) { values = elements; if (elements != null && elements.length > 0) type = elements[0]; } /** * Obtains the string representation of this object. */ public String toString() { StringBuffer buf = new StringBuffer("{"); if (values != null) { for (int i = 0; i < values.length; i++) { buf.append(values[i].toString()); if (i + 1 < values.length) buf.append(", "); } } buf.append("}"); return buf.toString(); } /** * Writes the value. */ public void write(AnnotationsWriter writer) throws IOException { int num = values.length; writer.arrayValue(num); for (int i = 0; i < num; ++i) values[i].write(writer); } /** * Accepts a visitor. */ public void accept(MemberValueVisitor visitor) { visitor.visitArrayMemberValue(this); } } javassist-3.12.1.ga/src/main/javassist/bytecode/Opcode.java0000644000175000017500000002570710630701321023531 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; /** * JVM Instruction Set. * *

    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 -1, // 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 }; } javassist-3.12.1.ga/src/main/javassist/bytecode/LongVector.java0000644000175000017500000000364311366006523024406 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; final class LongVector { static final int ASIZE = 128; static final int ABITS = 7; // ASIZE = 2^ABITS static final int VSIZE = 8; private ConstInfo[][] objects; private int elements; public LongVector() { objects = new ConstInfo[VSIZE][]; elements = 0; } public LongVector(int initialSize) { int vsize = ((initialSize >> ABITS) & ~(VSIZE - 1)) + VSIZE; objects = new ConstInfo[vsize][]; elements = 0; } public int size() { return elements; } public int capacity() { return objects.length * ASIZE; } public ConstInfo elementAt(int i) { if (i < 0 || elements <= i) return null; return objects[i >> ABITS][i & (ASIZE - 1)]; } public void addElement(ConstInfo value) { int nth = elements >> ABITS; int offset = elements & (ASIZE - 1); int len = objects.length; if (nth >= len) { ConstInfo[][] newObj = new ConstInfo[len + VSIZE][]; System.arraycopy(objects, 0, newObj, 0, len); objects = newObj; } if (objects[nth] == null) objects[nth] = new ConstInfo[ASIZE]; objects[nth][offset] = value; elements++; } } javassist-3.12.1.ga/src/main/javassist/bytecode/ByteArray.java0000644000175000017500000000474510630701321024221 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.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]; } } javassist-3.12.1.ga/src/main/javassist/bytecode/SignatureAttribute.java0000644000175000017500000006103211223113475026142 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.IOException; import java.util.Map; import java.util.ArrayList; import javassist.CtClass; /** * Signature_attribute. */ public class SignatureAttribute extends AttributeInfo { /** * The name of this attribute "Signature". */ public static final String tag = "Signature"; SignatureAttribute(ConstPool cp, int n, DataInputStream in) throws IOException { super(cp, n, in); } /** * Constructs a Signature attribute. * * @param cp a constant pool table. * @param signature the signature represented by this attribute. */ public SignatureAttribute(ConstPool cp, String signature) { super(cp, tag); int index = cp.addUtf8Info(signature); byte[] bvalue = new byte[2]; bvalue[0] = (byte)(index >>> 8); bvalue[1] = (byte)index; set(bvalue); } /** * Returns the signature indicated by signature_index. * * @see #toClassSignature(String) * @see #toMethodSignature(String) */ public String getSignature() { return getConstPool().getUtf8Info(ByteArray.readU16bit(get(), 0)); } /** * Sets signature_index to the index of the given signature, * which is added to a constant pool. * * @param sig new signature. * @since 3.11 */ public void setSignature(String sig) { int index = getConstPool().addUtf8Info(sig); ByteArray.write16bit(index, info, 0); } /** * Makes a copy. Class names are replaced according to the * given Map 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 SignatureAttribute(newCp, getSignature()); } void renameClass(String oldname, String newname) { String sig = renameClass(getSignature(), oldname, newname); setSignature(sig); } void renameClass(Map classnames) { String sig = renameClass(getSignature(), classnames); setSignature(sig); } static String renameClass(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; int k = j; int p = 0; char c; boolean match = true; try { int len = oldname.length(); while (isNamePart(c = desc.charAt(++k))) if (p >= len || c != oldname.charAt(p++)) match = false; } catch (IndexOutOfBoundsException e) { break; } i = k + 1; if (match && p == oldname.length()) { newdesc.append(desc.substring(head, j)); newdesc.append('L'); newdesc.append(newname); newdesc.append(c); head = i; } } if (head == 0) return desc; else { int len = desc.length(); if (head < len) newdesc.append(desc.substring(head, len)); return newdesc.toString(); } } static String renameClass(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; StringBuffer nameBuf = new StringBuffer(); int k = j; char c; try { while (isNamePart(c = desc.charAt(++k))) nameBuf.append(c); } catch (IndexOutOfBoundsException e) { break; } i = k + 1; String name = nameBuf.toString(); String name2 = (String)map.get(name); if (name2 != null) { newdesc.append(desc.substring(head, j)); newdesc.append('L'); newdesc.append(name2); newdesc.append(c); head = i; } } if (head == 0) return desc; else { int len = desc.length(); if (head < len) newdesc.append(desc.substring(head, len)); return newdesc.toString(); } } private static boolean isNamePart(int c) { return c != ';' && c != '<'; } static private class Cursor { int position = 0; int indexOf(String s, int ch) throws BadBytecode { int i = s.indexOf(ch, position); if (i < 0) throw error(s); else { position = i + 1; return i; } } } /** * Class signature. */ public static class ClassSignature { TypeParameter[] params; ClassType superClass; ClassType[] interfaces; ClassSignature(TypeParameter[] p, ClassType s, ClassType[] i) { params = p; superClass = s; interfaces = i; } /** * Returns the type parameters. * * @return a zero-length array if the type parameters are not specified. */ public TypeParameter[] getParameters() { return params; } /** * Returns the super class. */ public ClassType getSuperClass() { return superClass; } /** * Returns the super interfaces. * * @return a zero-length array if the super interfaces are not specified. */ public ClassType[] getInterfaces() { return interfaces; } /** * Returns the string representation. */ public String toString() { StringBuffer sbuf = new StringBuffer(); TypeParameter.toString(sbuf, params); sbuf.append(" extends ").append(superClass); if (interfaces.length > 0) { sbuf.append(" implements "); Type.toString(sbuf, interfaces); } return sbuf.toString(); } } /** * Method type signature. */ public static class MethodSignature { TypeParameter[] typeParams; Type[] params; Type retType; ObjectType[] exceptions; MethodSignature(TypeParameter[] tp, Type[] p, Type ret, ObjectType[] ex) { typeParams = tp; params = p; retType = ret; exceptions = ex; } /** * Returns the formal type parameters. * * @return a zero-length array if the type parameters are not specified. */ public TypeParameter[] getTypeParameters() { return typeParams; } /** * Returns the types of the formal parameters. * * @return a zero-length array if no formal parameter is taken. */ public Type[] getParameterTypes() { return params; } /** * Returns the type of the returned value. */ public Type getReturnType() { return retType; } /** * Returns the types of the exceptions that may be thrown. * * @return a zero-length array if exceptions are never thrown or * the exception types are not parameterized types or type variables. */ public ObjectType[] getExceptionTypes() { return exceptions; } /** * Returns the string representation. */ public String toString() { StringBuffer sbuf = new StringBuffer(); TypeParameter.toString(sbuf, typeParams); sbuf.append(" ("); Type.toString(sbuf, params); sbuf.append(") "); sbuf.append(retType); if (exceptions.length > 0) { sbuf.append(" throws "); Type.toString(sbuf, exceptions); } return sbuf.toString(); } } /** * Formal type parameters. */ public static class TypeParameter { String name; ObjectType superClass; ObjectType[] superInterfaces; TypeParameter(String sig, int nb, int ne, ObjectType sc, ObjectType[] si) { name = sig.substring(nb, ne); superClass = sc; superInterfaces = si; } /** * Returns the name of the type parameter. */ public String getName() { return name; } /** * Returns the class bound of this parameter. * * @return null if the class bound is not specified. */ public ObjectType getClassBound() { return superClass; } /** * Returns the interface bound of this parameter. * * @return a zero-length array if the interface bound is not specified. */ public ObjectType[] getInterfaceBound() { return superInterfaces; } /** * Returns the string representation. */ public String toString() { StringBuffer sbuf = new StringBuffer(getName()); if (superClass != null) sbuf.append(" extends ").append(superClass.toString()); int len = superInterfaces.length; if (len > 0) { for (int i = 0; i < len; i++) { if (i > 0 || superClass != null) sbuf.append(" & "); else sbuf.append(" extends "); sbuf.append(superInterfaces[i].toString()); } } return sbuf.toString(); } static void toString(StringBuffer sbuf, TypeParameter[] tp) { sbuf.append('<'); for (int i = 0; i < tp.length; i++) { if (i > 0) sbuf.append(", "); sbuf.append(tp[i]); } sbuf.append('>'); } } /** * Type argument. */ public static class TypeArgument { ObjectType arg; char wildcard; TypeArgument(ObjectType a, char w) { arg = a; wildcard = w; } /** * Returns the kind of this type argument. * * @return ' ' (not-wildcard), '*' (wildcard), '+' (wildcard with * upper bound), or '-' (wildcard with lower bound). */ public char getKind() { return wildcard; } /** * Returns true if this type argument is a wildcard type * such as ?, ? extends String, or ? super Integer. */ public boolean isWildcard() { return wildcard != ' '; } /** * Returns the type represented by this argument * if the argument is not a wildcard type. Otherwise, this method * returns the upper bound (if the kind is '+'), * the lower bound (if the kind is '-'), or null (if the upper or lower * bound is not specified). */ public ObjectType getType() { return arg; } /** * Returns the string representation. */ public String toString() { if (wildcard == '*') return "?"; String type = arg.toString(); if (wildcard == ' ') return type; else if (wildcard == '+') return "? extends " + type; else return "? super " + type; } } /** * Primitive types and object types. */ public static abstract class Type { static void toString(StringBuffer sbuf, Type[] ts) { for (int i = 0; i < ts.length; i++) { if (i > 0) sbuf.append(", "); sbuf.append(ts[i]); } } } /** * Primitive types. */ public static class BaseType extends Type { char descriptor; BaseType(char c) { descriptor = c; } /** * Returns the descriptor representing this primitive type. * * @see javassist.bytecode.Descriptor */ public char getDescriptor() { return descriptor; } /** * Returns the CtClass representing this * primitive type. */ public CtClass getCtlass() { return Descriptor.toPrimitiveClass(descriptor); } /** * Returns the string representation. */ public String toString() { return Descriptor.toClassName(Character.toString(descriptor)); } } /** * Class types, array types, and type variables. */ public static abstract class ObjectType extends Type {} /** * Class types. */ public static class ClassType extends ObjectType { String name; TypeArgument[] arguments; static ClassType make(String s, int b, int e, TypeArgument[] targs, ClassType parent) { if (parent == null) return new ClassType(s, b, e, targs); else return new NestedClassType(s, b, e, targs, parent); } ClassType(String signature, int begin, int end, TypeArgument[] targs) { name = signature.substring(begin, end).replace('/', '.'); arguments = targs; } /** * Returns the class name. */ public String getName() { return name; } /** * Returns the type arguments. * * @return null if no type arguments are given to this class. */ public TypeArgument[] getTypeArguments() { return arguments; } /** * If this class is a member of another class, returns the * class in which this class is declared. * * @return null if this class is not a member of another class. */ public ClassType getDeclaringClass() { return null; } /** * Returns the string representation. */ public String toString() { StringBuffer sbuf = new StringBuffer(); ClassType parent = getDeclaringClass(); if (parent != null) sbuf.append(parent.toString()).append('.'); sbuf.append(name); if (arguments != null) { sbuf.append('<'); int n = arguments.length; for (int i = 0; i < n; i++) { if (i > 0) sbuf.append(", "); sbuf.append(arguments[i].toString()); } sbuf.append('>'); } return sbuf.toString(); } } /** * Nested class types. */ public static class NestedClassType extends ClassType { ClassType parent; NestedClassType(String s, int b, int e, TypeArgument[] targs, ClassType p) { super(s, b, e, targs); parent = p; } /** * Returns the class that declares this nested class. * This nested class is a member of that declaring class. */ public ClassType getDeclaringClass() { return parent; } } /** * Array types. */ public static class ArrayType extends ObjectType { int dim; Type componentType; public ArrayType(int d, Type comp) { dim = d; componentType = comp; } /** * Returns the dimension of the array. */ public int getDimension() { return dim; } /** * Returns the component type. */ public Type getComponentType() { return componentType; } /** * Returns the string representation. */ public String toString() { StringBuffer sbuf = new StringBuffer(componentType.toString()); for (int i = 0; i < dim; i++) sbuf.append("[]"); return sbuf.toString(); } } /** * Type variables. */ public static class TypeVariable extends ObjectType { String name; TypeVariable(String sig, int begin, int end) { name = sig.substring(begin, end); } /** * Returns the variable name. */ public String getName() { return name; } /** * Returns the string representation. */ public String toString() { return name; } } /** * Parses the given signature string as a class signature. * * @param sig the signature. * @throws BadBytecode thrown when a syntactical error is found. * @since 3.5 */ public static ClassSignature toClassSignature(String sig) throws BadBytecode { try { return parseSig(sig); } catch (IndexOutOfBoundsException e) { throw error(sig); } } /** * Parses the given signature string as a method type signature. * * @param sig the signature. * @throws BadBytecode thrown when a syntactical error is found. * @since 3.5 */ public static MethodSignature toMethodSignature(String sig) throws BadBytecode { try { return parseMethodSig(sig); } catch (IndexOutOfBoundsException e) { throw error(sig); } } /** * Parses the given signature string as a field type signature. * * @param sig the signature string. * @return the field type signature. * @throws BadBytecode thrown when a syntactical error is found. * @since 3.5 */ public static ObjectType toFieldSignature(String sig) throws BadBytecode { try { return parseObjectType(sig, new Cursor(), false); } catch (IndexOutOfBoundsException e) { throw error(sig); } } private static ClassSignature parseSig(String sig) throws BadBytecode, IndexOutOfBoundsException { Cursor cur = new Cursor(); TypeParameter[] tp = parseTypeParams(sig, cur); ClassType superClass = parseClassType(sig, cur); int sigLen = sig.length(); ArrayList ifArray = new ArrayList(); while (cur.position < sigLen && sig.charAt(cur.position) == 'L') ifArray.add(parseClassType(sig, cur)); ClassType[] ifs = (ClassType[])ifArray.toArray(new ClassType[ifArray.size()]); return new ClassSignature(tp, superClass, ifs); } private static MethodSignature parseMethodSig(String sig) throws BadBytecode { Cursor cur = new Cursor(); TypeParameter[] tp = parseTypeParams(sig, cur); if (sig.charAt(cur.position++) != '(') throw error(sig); ArrayList params = new ArrayList(); while (sig.charAt(cur.position) != ')') { Type t = parseType(sig, cur); params.add(t); } cur.position++; Type ret = parseType(sig, cur); int sigLen = sig.length(); ArrayList exceptions = new ArrayList(); while (cur.position < sigLen && sig.charAt(cur.position) == '^') { cur.position++; ObjectType t = parseObjectType(sig, cur, false); if (t instanceof ArrayType) throw error(sig); exceptions.add(t); } Type[] p = (Type[])params.toArray(new Type[params.size()]); ObjectType[] ex = (ObjectType[])exceptions.toArray(new ObjectType[exceptions.size()]); return new MethodSignature(tp, p, ret, ex); } private static TypeParameter[] parseTypeParams(String sig, Cursor cur) throws BadBytecode { ArrayList typeParam = new ArrayList(); if (sig.charAt(cur.position) == '<') { cur.position++; while (sig.charAt(cur.position) != '>') { int nameBegin = cur.position; int nameEnd = cur.indexOf(sig, ':'); ObjectType classBound = parseObjectType(sig, cur, true); ArrayList ifBound = new ArrayList(); while (sig.charAt(cur.position) == ':') { cur.position++; ObjectType t = parseObjectType(sig, cur, false); ifBound.add(t); } TypeParameter p = new TypeParameter(sig, nameBegin, nameEnd, classBound, (ObjectType[])ifBound.toArray(new ObjectType[ifBound.size()])); typeParam.add(p); } cur.position++; } return (TypeParameter[])typeParam.toArray(new TypeParameter[typeParam.size()]); } private static ObjectType parseObjectType(String sig, Cursor c, boolean dontThrow) throws BadBytecode { int i; int begin = c.position; switch (sig.charAt(begin)) { case 'L' : return parseClassType2(sig, c, null); case 'T' : i = c.indexOf(sig, ';'); return new TypeVariable(sig, begin + 1, i); case '[' : return parseArray(sig, c); default : if (dontThrow) return null; else throw error(sig); } } private static ClassType parseClassType(String sig, Cursor c) throws BadBytecode { if (sig.charAt(c.position) == 'L') return parseClassType2(sig, c, null); else throw error(sig); } private static ClassType parseClassType2(String sig, Cursor c, ClassType parent) throws BadBytecode { int start = ++c.position; char t; do { t = sig.charAt(c.position++); } while (t != '$' && t != '<' && t != ';'); int end = c.position - 1; TypeArgument[] targs; if (t == '<') { targs = parseTypeArgs(sig, c); t = sig.charAt(c.position++); } else targs = null; ClassType thisClass = ClassType.make(sig, start, end, targs, parent); if (t == '$') { c.position--; return parseClassType2(sig, c, thisClass); } else return thisClass; } private static TypeArgument[] parseTypeArgs(String sig, Cursor c) throws BadBytecode { ArrayList args = new ArrayList(); char t; while ((t = sig.charAt(c.position++)) != '>') { TypeArgument ta; if (t == '*' ) ta = new TypeArgument(null, '*'); else { if (t != '+' && t != '-') { t = ' '; c.position--; } ta = new TypeArgument(parseObjectType(sig, c, false), t); } args.add(ta); } return (TypeArgument[])args.toArray(new TypeArgument[args.size()]); } private static ObjectType parseArray(String sig, Cursor c) throws BadBytecode { int dim = 1; while (sig.charAt(++c.position) == '[') dim++; return new ArrayType(dim, parseType(sig, c)); } private static Type parseType(String sig, Cursor c) throws BadBytecode { Type t = parseObjectType(sig, c, true); if (t == null) t = new BaseType(sig.charAt(c.position++)); return t; } private static BadBytecode error(String sig) { return new BadBytecode("bad signature: " + sig); } } javassist-3.12.1.ga/src/main/javassist/bytecode/ExceptionsAttribute.java0000644000175000017500000001225610630701321026320 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.IOException; import java.util.Map; /** * Exceptions_attribute. */ public class ExceptionsAttribute extends AttributeInfo { /** * The name of this attribute "Exceptions". */ 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 Map object. * * @param newCp the constant pool table used by the new copy. * @param classnames pairs of replaced and substituted * class names. It can be null. */ 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 exception_index_table[]. */ 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 exception_index_table[]. */ 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 number_of_exceptions. */ public int tableLength() { return info.length / 2 - 1; } /** * Returns the value of exception_index_table[nth]. */ public int getException(int nth) { int index = nth * 2 + 2; // nth >= 0 return ((info[index] & 0xff) << 8) | (info[index + 1] & 0xff); } } javassist-3.12.1.ga/src/main/javassist/bytecode/AccessFlag.java0000644000175000017500000001013010630701321024273 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.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 BRIDGE = 0x0040; // for method_info public static final int TRANSIENT = 0x0080; public static final int VARARGS = 0x0080; // for method_info 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 SYNTHETIC = 0x1000; public static final int ANNOTATION = 0x2000; public static final int ENUM = 0x4000; 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)); } /** * Returns true if the access flags include the public bit. */ public static boolean isPublic(int accflags) { return (accflags & PUBLIC) != 0; } /** * Returns true if the access flags include the protected bit. */ public static boolean isProtected(int accflags) { return (accflags & PROTECTED) != 0; } /** * Returns true if the access flags include the private bit. */ public static boolean isPrivate(int accflags) { return (accflags & PRIVATE) != 0; } /** * Returns true if the access flags include neither public, protected, * or private. */ public static boolean isPackage(int accflags) { return (accflags & (PROTECTED | PUBLIC | PRIVATE)) == 0; } /** * Clears a specified bit in accflags. */ 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; } } javassist-3.12.1.ga/src/main/javassist/bytecode/CodeAnalyzer.java0000644000175000017500000002057310630701321024674 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; /** * Utility for computing max_stack. */ class CodeAnalyzer implements Opcode { private ConstPool constPool; private CodeAttribute codeAttr; public CodeAnalyzer(CodeAttribute ca) { codeAttr = ca; constPool = ca.getConstPool(); } public int computeMaxStack() throws BadBytecode { /* d = stack[i] * d == 0: not visited * d > 0: the depth is d - 1 after executing the bytecode at i. * d < 0: not visited. the initial depth (before execution) is 1 - d. */ CodeIterator ci = codeAttr.iterator(); int length = ci.getCodeLength(); int[] stack = new int[length]; constPool = codeAttr.getConstPool(); initStack(stack, codeAttr); boolean repeat; do { repeat = false; for (int i = 0; i < length; ++i) if (stack[i] < 0) { repeat = true; visitBytecode(ci, stack, i); } } while (repeat); int maxStack = 1; for (int i = 0; i < length; ++i) if (stack[i] > maxStack) maxStack = stack[i]; return maxStack - 1; // the base is 1. } private void initStack(int[] stack, CodeAttribute ca) { stack[0] = -1; ExceptionTable et = ca.getExceptionTable(); if (et != null) { int size = et.size(); for (int i = 0; i < size; ++i) stack[et.handlerPc(i)] = -2; // an exception is on stack } } private void visitBytecode(CodeIterator ci, int[] stack, int index) throws BadBytecode { int codeLength = stack.length; ci.move(index); int stackDepth = -stack[index]; while (ci.hasNext()) { index = ci.next(); stack[index] = stackDepth; int op = ci.byteAt(index); stackDepth = visitInst(op, ci, index, stackDepth); if (stackDepth < 1) throw new BadBytecode("stack underflow at " + index); if (processBranch(op, ci, index, codeLength, stack, stackDepth)) break; if (isEnd(op)) // return, ireturn, athrow, ... break; if (op == JSR || op == JSR_W) --stackDepth; } } private boolean processBranch(int opcode, CodeIterator ci, int index, int codeLength, int[] stack, int stackDepth) throws BadBytecode { if ((IFEQ <= opcode && opcode <= IF_ACMPNE) || opcode == IFNULL || opcode == IFNONNULL) { int target = index + ci.s16bitAt(index + 1); checkTarget(index, target, codeLength, stack, stackDepth); } else { int target, index2; switch (opcode) { case GOTO : target = index + ci.s16bitAt(index + 1); checkTarget(index, target, codeLength, stack, stackDepth); return true; case GOTO_W : target = index + ci.s32bitAt(index + 1); checkTarget(index, target, codeLength, stack, stackDepth); return true; case JSR : case JSR_W : if (opcode == JSR) target = index + ci.s16bitAt(index + 1); else target = index + ci.s32bitAt(index + 1); checkTarget(index, target, codeLength, stack, stackDepth); if (stackDepth == 2) // stackDepth is 1 if empty return false; else throw new BadBytecode( "sorry, cannot compute this data flow due to JSR"); case RET : if (stackDepth == 1) // stackDepth is 1 if empty return true; else throw new BadBytecode( "sorry, cannot compute this data flow due to RET"); case LOOKUPSWITCH : case TABLESWITCH : index2 = (index & ~3) + 4; target = index + ci.s32bitAt(index2); checkTarget(index, target, codeLength, stack, stackDepth); if (opcode == LOOKUPSWITCH) { int npairs = ci.s32bitAt(index2 + 4); index2 += 12; for (int i = 0; i < npairs; ++i) { target = index + ci.s32bitAt(index2); checkTarget(index, target, codeLength, stack, stackDepth); index2 += 8; } } else { int low = ci.s32bitAt(index2 + 4); int high = ci.s32bitAt(index2 + 8); int n = high - low + 1; index2 += 12; for (int i = 0; i < n; ++i) { target = index + ci.s32bitAt(index2); checkTarget(index, target, codeLength, stack, stackDepth); index2 += 4; } } return true; // always branch. } } return false; // may not branch. } private void checkTarget(int opIndex, int target, int codeLength, int[] stack, int stackDepth) throws BadBytecode { if (target < 0 || codeLength <= target) throw new BadBytecode("bad branch offset at " + opIndex); int d = stack[target]; if (d == 0) stack[target] = -stackDepth; else if (d != stackDepth && d != -stackDepth) throw new BadBytecode("verification error (" + stackDepth + "," + d + ") at " + opIndex); } private static boolean isEnd(int opcode) { return (IRETURN <= opcode && opcode <= RETURN) || opcode == ATHROW; } /** * Visits an instruction. */ private int visitInst(int op, CodeIterator ci, int index, int stack) throws BadBytecode { String desc; switch (op) { case GETFIELD : stack += getFieldSize(ci, index) - 1; break; case PUTFIELD : stack -= getFieldSize(ci, index) + 1; break; case GETSTATIC : stack += getFieldSize(ci, index); break; case PUTSTATIC : stack -= getFieldSize(ci, index); break; case INVOKEVIRTUAL : case INVOKESPECIAL : desc = constPool.getMethodrefType(ci.u16bitAt(index + 1)); stack += Descriptor.dataSize(desc) - 1; break; case INVOKESTATIC : desc = constPool.getMethodrefType(ci.u16bitAt(index + 1)); stack += Descriptor.dataSize(desc); break; case INVOKEINTERFACE : desc = constPool.getInterfaceMethodrefType( ci.u16bitAt(index + 1)); stack += Descriptor.dataSize(desc) - 1; break; case ATHROW : stack = 1; // the stack becomes empty (1 means no values). break; case MULTIANEWARRAY : stack += 1 - ci.byteAt(index + 3); break; case WIDE : op = ci.byteAt(index + 1); // don't break here. default : stack += STACK_GROW[op]; } return stack; } private int getFieldSize(CodeIterator ci, int index) { String desc = constPool.getFieldrefType(ci.u16bitAt(index + 1)); return Descriptor.dataSize(desc); } } javassist-3.12.1.ga/src/main/javassist/bytecode/AnnotationDefaultAttribute.java0000644000175000017500000001170510630701321027614 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import javassist.CtClass; import javassist.bytecode.annotation.AnnotationsWriter; import javassist.bytecode.annotation.MemberValue; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.IOException; import java.util.Map; /** * A class representing AnnotationDefault_attribute. * *

    For example, if you declare the following annotation type: * *

       * @interface Author {
       *   String name() default "Shakespeare";
       *   int age() default 99;
       * }
       * 
    * *

    The defautl values of name and age * are stored as annotation default attributes in Author.class. * The following code snippet obtains the default value of name: * *

       * ClassPool pool = ...
       * CtClass cc = pool.get("Author");
       * CtMethod cm = cc.getDeclaredMethod("age");
       * MethodInfo minfo = cm.getMethodInfo();
       * AnnotationDefaultAttribute ada
       *         = (AnnotationDefaultAttribute)
       *           minfo.getAttribute(AnnotationDefaultAttribute.tag);
       * MemberValue value = ada.getDefaultValue());    // default value of age
       * 
    * *

    If the following statement is executed after the code above, * the default value of age is set to 80: * *

       * ada.setDefaultValue(new IntegerMemberValue(minfo.getConstPool(), 80));
       * 
    * * @see AnnotationsAttribute * @see javassist.bytecode.annotation.MemberValue */ public class AnnotationDefaultAttribute extends AttributeInfo { /** * The name of the AnnotationDefault attribute. */ public static final String tag = "AnnotationDefault"; /** * Constructs an AnnotationDefault_attribute. * * @param cp constant pool * @param info the contents of this attribute. It does not * include attribute_name_index or * attribute_length. */ public AnnotationDefaultAttribute(ConstPool cp, byte[] info) { super(cp, tag, info); } /** * Constructs an empty AnnotationDefault_attribute. * The default value can be set by setDefaultValue(). * * @param cp constant pool * @see #setDefaultValue(javassist.bytecode.annotation.MemberValue) */ public AnnotationDefaultAttribute(ConstPool cp) { this(cp, new byte[] { 0, 0 }); } /** * @param n the attribute name. */ AnnotationDefaultAttribute(ConstPool cp, int n, DataInputStream in) throws IOException { super(cp, n, in); } /** * Copies this attribute and returns a new copy. */ public AttributeInfo copy(ConstPool newCp, Map classnames) { AnnotationsAttribute.Copier copier = new AnnotationsAttribute.Copier(info, constPool, newCp, classnames); try { copier.memberValue(0); return new AnnotationDefaultAttribute(newCp, copier.close()); } catch (Exception e) { throw new RuntimeException(e.toString()); } } /** * Obtains the default value represented by this attribute. */ public MemberValue getDefaultValue() { try { return new AnnotationsAttribute.Parser(info, constPool) .parseMemberValue(); } catch (Exception e) { throw new RuntimeException(e.toString()); } } /** * Changes the default value represented by this attribute. * * @param value the new value. * @see javassist.bytecode.annotation.Annotation#createMemberValue(ConstPool, CtClass) */ public void setDefaultValue(MemberValue value) { ByteArrayOutputStream output = new ByteArrayOutputStream(); AnnotationsWriter writer = new AnnotationsWriter(output, constPool); try { value.write(writer); writer.close(); } catch (IOException e) { throw new RuntimeException(e); // should never reach here. } set(output.toByteArray()); } /** * Returns a string representation of this object. */ public String toString() { return getDefaultValue().toString(); } } javassist-3.12.1.ga/src/main/javassist/bytecode/ConstPool.java0000644000175000017500000013540411366326427024255 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javassist.CtClass; /** * Constant pool table. */ public final class ConstPool { LongVector items; int numOfItems; HashMap classes; HashMap strings; ConstInfo[] constInfoCache; int[] constInfoIndexCache; int thisClassInfo; private static final int CACHE_SIZE = 32; /** * A hash function for CACHE_SIZE */ private static int hashFunc(int a, int b) { int h = -2128831035; final int prime = 16777619; h = (h ^ (a & 0xff)) * prime; h = (h ^ (b & 0xff)) * prime; // changing the hash key size from 32bit to 5bit h = (h >> 5) ^ (h & 0x1f); return h & 0x1f; // 0..31 } /** * CONSTANT_Class */ public static final int CONST_Class = ClassInfo.tag; /** * CONSTANT_Fieldref */ public static final int CONST_Fieldref = FieldrefInfo.tag; /** * CONSTANT_Methodref */ public static final int CONST_Methodref = MethodrefInfo.tag; /** * CONSTANT_InterfaceMethodref */ public static final int CONST_InterfaceMethodref = InterfaceMethodrefInfo.tag; /** * CONSTANT_String */ public static final int CONST_String = StringInfo.tag; /** * CONSTANT_Integer */ public static final int CONST_Integer = IntegerInfo.tag; /** * CONSTANT_Float */ public static final int CONST_Float = FloatInfo.tag; /** * CONSTANT_Long */ public static final int CONST_Long = LongInfo.tag; /** * CONSTANT_Double */ public static final int CONST_Double = DoubleInfo.tag; /** * CONSTANT_NameAndType */ public static final int CONST_NameAndType = NameAndTypeInfo.tag; /** * CONSTANT_Utf8 */ 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) { items = new LongVector(); numOfItems = 0; addItem(null); // index 0 is reserved by the JVM. classes = new HashMap(); strings = new HashMap(); constInfoCache = new ConstInfo[CACHE_SIZE]; constInfoIndexCache = new int[CACHE_SIZE]; thisClassInfo = addClassInfo(thisclass); } /** * Constructs a constant pool table from the given byte stream. * * @param in byte stream. */ public ConstPool(DataInputStream in) throws IOException { classes = new HashMap(); strings = new HashMap(); constInfoCache = new ConstInfo[CACHE_SIZE]; constInfoIndexCache = new int[CACHE_SIZE]; thisClassInfo = 0; /* read() initializes items and numOfItems, and do addItem(null). */ read(in); } void prune() { classes = new HashMap(); strings = new HashMap(); constInfoCache = new ConstInfo[CACHE_SIZE]; constInfoIndexCache = new int[CACHE_SIZE]; } /** * Returns the number of entries in this table. */ public int getSize() { return numOfItems; } /** * Returns the name of the class using this constant pool table. */ public String getClassName() { return getClassInfo(thisClassInfo); } /** * Returns the index of CONSTANT_Class_info 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 items.elementAt(n); } /** * Returns the tag field of the constant pool table * entry at the given index. */ public int getTag(int index) { return getItem(index).getTag(); } /** * Reads CONSTANT_Class_info structure * at the given index. * * @return a fully-qualified class or interface name specified * by name_index. If the type is an array * type, this method returns an encoded name like * [java.lang.Object; (note that the separators * are not slashes but dots). * @see javassist.ClassPool#getCtClass(String) */ public String getClassInfo(int index) { ClassInfo c = (ClassInfo)getItem(index); if (c == null) return null; else return Descriptor.toJavaName(getUtf8Info(c.name)); } /** * Reads the name_index field of the * CONSTANT_NameAndType_info structure * at the given index. */ public int getNameAndTypeName(int index) { NameAndTypeInfo ntinfo = (NameAndTypeInfo)getItem(index); return ntinfo.memberName; } /** * Reads the descriptor_index field of the * CONSTANT_NameAndType_info structure * at the given index. */ public int getNameAndTypeDescriptor(int index) { NameAndTypeInfo ntinfo = (NameAndTypeInfo)getItem(index); return ntinfo.typeDescriptor; } /** * Reads the class_index field of the * CONSTANT_Fieldref_info, * CONSTANT_Methodref_info, * or CONSTANT_Interfaceref_info, * structure at the given index. * * @since 3.6 */ public int getMemberClass(int index) { MemberrefInfo minfo = (MemberrefInfo)getItem(index); return minfo.classIndex; } /** * Reads the name_and_type_index field of the * CONSTANT_Fieldref_info, * CONSTANT_Methodref_info, * or CONSTANT_Interfaceref_info, * structure at the given index. * * @since 3.6 */ public int getMemberNameAndType(int index) { MemberrefInfo minfo = (MemberrefInfo)getItem(index); return minfo.nameAndTypeIndex; } /** * Reads the class_index field of the * CONSTANT_Fieldref_info structure * at the given index. */ public int getFieldrefClass(int index) { FieldrefInfo finfo = (FieldrefInfo)getItem(index); return finfo.classIndex; } /** * Reads the class_index field of the * CONSTANT_Fieldref_info structure * at the given index. * * @return the name of the class at that class_index. */ public String getFieldrefClassName(int index) { FieldrefInfo f = (FieldrefInfo)getItem(index); if (f == null) return null; else return getClassInfo(f.classIndex); } /** * Reads the name_and_type_index field of the * CONSTANT_Fieldref_info structure * at the given index. */ public int getFieldrefNameAndType(int index) { FieldrefInfo finfo = (FieldrefInfo)getItem(index); return finfo.nameAndTypeIndex; } /** * Reads the name_index field of the * CONSTANT_NameAndType_info structure * indirectly specified by the given index. * * @param index an index to a CONSTANT_Fieldref_info. * @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 descriptor_index field of the * CONSTANT_NameAndType_info structure * indirectly specified by the given index. * * @param index an index to a CONSTANT_Fieldref_info. * @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 class_index field of the * CONSTANT_Methodref_info structure * at the given index. */ public int getMethodrefClass(int index) { MethodrefInfo minfo = (MethodrefInfo)getItem(index); return minfo.classIndex; } /** * Reads the class_index field of the * CONSTANT_Methodref_info structure * at the given index. * * @return the name of the class at that class_index. */ public String getMethodrefClassName(int index) { MethodrefInfo minfo = (MethodrefInfo)getItem(index); if (minfo == null) return null; else return getClassInfo(minfo.classIndex); } /** * Reads the name_and_type_index field of the * CONSTANT_Methodref_info structure * at the given index. */ public int getMethodrefNameAndType(int index) { MethodrefInfo minfo = (MethodrefInfo)getItem(index); return minfo.nameAndTypeIndex; } /** * Reads the name_index field of the * CONSTANT_NameAndType_info structure * indirectly specified by the given index. * * @param index an index to a CONSTANT_Methodref_info. * @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 descriptor_index field of the * CONSTANT_NameAndType_info structure * indirectly specified by the given index. * * @param index an index to a CONSTANT_Methodref_info. * @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 class_index field of the * CONSTANT_InterfaceMethodref_info structure * at the given index. */ public int getInterfaceMethodrefClass(int index) { InterfaceMethodrefInfo minfo = (InterfaceMethodrefInfo)getItem(index); return minfo.classIndex; } /** * Reads the class_index field of the * CONSTANT_InterfaceMethodref_info structure * at the given index. * * @return the name of the class at that class_index. */ public String getInterfaceMethodrefClassName(int index) { InterfaceMethodrefInfo minfo = (InterfaceMethodrefInfo)getItem(index); return getClassInfo(minfo.classIndex); } /** * Reads the name_and_type_index field of the * CONSTANT_InterfaceMethodref_info structure * at the given index. */ public int getInterfaceMethodrefNameAndType(int index) { InterfaceMethodrefInfo minfo = (InterfaceMethodrefInfo)getItem(index); return minfo.nameAndTypeIndex; } /** * Reads the name_index field of the * CONSTANT_NameAndType_info structure * indirectly specified by the given index. * * @param index an index to * a CONSTANT_InterfaceMethodref_info. * @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 descriptor_index field of the * CONSTANT_NameAndType_info structure * indirectly specified by the given index. * * @param index an index to * a CONSTANT_InterfaceMethodref_info. * @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 CONSTANT_Integer_info, _Float_info, * _Long_info, _Double_info, or * _String_info structure. * These are used with the LDC instruction. * * @return a String 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 CONSTANT_Integer_info 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 CONSTANT_Float_info 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 CONSTANT_Long_info 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 CONSTANT_Double_info 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 CONSTANT_String_info structure * at the given index. * * @return the string specified by string_index. */ public String getStringInfo(int index) { StringInfo si = (StringInfo)getItem(index); return getUtf8Info(si.string); } /** * Reads CONSTANT_utf8_info 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 CONSTANT_Methodref_info * structure at the given index represents the constructor * of the given class. * * @return the descriptor_index specifying * the type descriptor of the that constructor. * If it is not that constructor, * isConstructor() returns 0. */ public int isConstructor(String classname, int index) { return isMember(classname, MethodInfo.nameInit, index); } /** * Determines whether CONSTANT_Methodref_info, * CONSTANT_Fieldref_info, or * CONSTANT_InterfaceMethodref_info 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 descriptor_index specifying * the type descriptor of that member. * If it is not that member, * isMember() 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 } /** * Determines whether CONSTANT_Methodref_info, * CONSTANT_Fieldref_info, or * CONSTANT_InterfaceMethodref_info structure * at the given index has the name and the descriptor * given as the arguments. * * @param membername the member name * @param desc the descriptor of the member. * @param index the index into the constant pool table * * @return the name of the target class specified by * the ..._info structure * at index. * Otherwise, null if that structure does not * match the given member name and descriptor. */ public String eqMember(String membername, String desc, int index) { MemberrefInfo minfo = (MemberrefInfo)getItem(index); NameAndTypeInfo ntinfo = (NameAndTypeInfo)getItem(minfo.nameAndTypeIndex); if (getUtf8Info(ntinfo.memberName).equals(membername) && getUtf8Info(ntinfo.typeDescriptor).equals(desc)) return getClassInfo(minfo.classIndex); else return null; // 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 n-th item * @param dest destination constant pool table * @param classnames the map or null. * @return the index of the copied item into the destination ClassPool. */ 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 CONSTANT_Class_info structure. * *

    This also adds a CONSTANT_Utf8_info 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;" instead of "". // // note: toJvmName(toJvmName(c)) is equal to toJvmName(c). return addClassInfo(Descriptor.toJvmName(c)); } } /** * Adds a new CONSTANT_Class_info structure. * *

    This also adds a CONSTANT_Utf8_info 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 CONSTANT_NameAndType_info structure. * *

    This also adds CONSTANT_Utf8_info structures. * * @param name name_index * @param type descriptor_index * @return the index of the added entry. */ public int addNameAndTypeInfo(String name, String type) { return addNameAndTypeInfo(addUtf8Info(name), addUtf8Info(type)); } /** * Adds a new CONSTANT_NameAndType_info structure. * * @param name name_index * @param type descriptor_index * @return the index of the added entry. */ public int addNameAndTypeInfo(int name, int type) { int h = hashFunc(name, type); ConstInfo ci = constInfoCache[h]; if (ci != null && ci instanceof NameAndTypeInfo && ci.hashCheck(name, type)) return constInfoIndexCache[h]; else { NameAndTypeInfo item = new NameAndTypeInfo(name, type); constInfoCache[h] = item; int i = addItem(item); constInfoIndexCache[h] = i; return i; } } /** * Adds a new CONSTANT_Fieldref_info structure. * *

    This also adds a new CONSTANT_NameAndType_info * structure. * * @param classInfo class_index * @param name name_index * of CONSTANT_NameAndType_info. * @param type descriptor_index * of CONSTANT_NameAndType_info. * @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 CONSTANT_Fieldref_info structure. * * @param classInfo class_index * @param nameAndTypeInfo name_and_type_index. * @return the index of the added entry. */ public int addFieldrefInfo(int classInfo, int nameAndTypeInfo) { int h = hashFunc(classInfo, nameAndTypeInfo); ConstInfo ci = constInfoCache[h]; if (ci != null && ci instanceof FieldrefInfo && ci.hashCheck(classInfo, nameAndTypeInfo)) return constInfoIndexCache[h]; else { FieldrefInfo item = new FieldrefInfo(classInfo, nameAndTypeInfo); constInfoCache[h] = item; int i = addItem(item); constInfoIndexCache[h] = i; return i; } } /** * Adds a new CONSTANT_Methodref_info structure. * *

    This also adds a new CONSTANT_NameAndType_info * structure. * * @param classInfo class_index * @param name name_index * of CONSTANT_NameAndType_info. * @param type descriptor_index * of CONSTANT_NameAndType_info. * @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 CONSTANT_Methodref_info structure. * * @param classInfo class_index * @param nameAndTypeInfo name_and_type_index. * @return the index of the added entry. */ public int addMethodrefInfo(int classInfo, int nameAndTypeInfo) { int h = hashFunc(classInfo, nameAndTypeInfo); ConstInfo ci = constInfoCache[h]; if (ci != null && ci instanceof MethodrefInfo && ci.hashCheck(classInfo, nameAndTypeInfo)) return constInfoIndexCache[h]; else { MethodrefInfo item = new MethodrefInfo(classInfo, nameAndTypeInfo); constInfoCache[h] = item; int i = addItem(item); constInfoIndexCache[h] = i; return i; } } /** * Adds a new CONSTANT_InterfaceMethodref_info * structure. * *

    This also adds a new CONSTANT_NameAndType_info * structure. * * @param classInfo class_index * @param name name_index * of CONSTANT_NameAndType_info. * @param type descriptor_index * of CONSTANT_NameAndType_info. * @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 CONSTANT_InterfaceMethodref_info * structure. * * @param classInfo class_index * @param nameAndTypeInfo name_and_type_index. * @return the index of the added entry. */ public int addInterfaceMethodrefInfo(int classInfo, int nameAndTypeInfo) { int h = hashFunc(classInfo, nameAndTypeInfo); ConstInfo ci = constInfoCache[h]; if (ci != null && ci instanceof InterfaceMethodrefInfo && ci.hashCheck(classInfo, nameAndTypeInfo)) return constInfoIndexCache[h]; else { InterfaceMethodrefInfo item =new InterfaceMethodrefInfo(classInfo, nameAndTypeInfo); constInfoCache[h] = item; int i = addItem(item); constInfoIndexCache[h] = i; return i; } } /** * Adds a new CONSTANT_String_info * structure. * *

    This also adds a new CONSTANT_Utf8_info * structure. * * @return the index of the added entry. */ public int addStringInfo(String str) { return addItem(new StringInfo(addUtf8Info(str))); } /** * Adds a new CONSTANT_Integer_info * structure. * * @return the index of the added entry. */ public int addIntegerInfo(int i) { return addItem(new IntegerInfo(i)); } /** * Adds a new CONSTANT_Float_info * structure. * * @return the index of the added entry. */ public int addFloatInfo(float f) { return addItem(new FloatInfo(f)); } /** * Adds a new CONSTANT_Long_info * 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 CONSTANT_Double_info * 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 CONSTANT_Utf8_info * structure. * *

    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); } } /** * Get all the class names. * * @return a set of class names */ public Set getClassNames() { HashSet result = new HashSet(); LongVector v = items; int size = numOfItems; for (int i = 1; i < size; ++i) { String className = v.elementAt(i).getClassName(this); if (className != null) result.add(className); } return result; } /** * Replaces all occurrences of a class name. * * @param oldName the replaced name (JVM-internal representation). * @param newName the substituted name (JVM-internal representation). */ public void renameClass(String oldName, String newName) { LongVector v = items; int size = numOfItems; classes = new HashMap(classes.size() * 2); for (int i = 1; i < size; ++i) { ConstInfo ci = v.elementAt(i); ci.renameClass(this, oldName, newName); ci.makeHashtable(this); } } /** * 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; classes = new HashMap(classes.size() * 2); for (int i = 1; i < size; ++i) { ConstInfo ci = v.elementAt(i); ci.renameClass(this, classnames); ci.makeHashtable(this); } } private void read(DataInputStream in) throws IOException { int n = in.readUnsignedShort(); items = new LongVector(n); numOfItems = 0; addItem(null); // index 0 is reserved by the JVM. while (--n > 0) { // index 0 is reserved by JVM int tag = readOne(in); if ((tag == LongInfo.tag) || (tag == DoubleInfo.tag)) { addItem(new ConstInfoPadding()); --n; } } int i = 1; while (true) { ConstInfo info = items.elementAt(i++); if (info == null) break; else info.makeHashtable(this); } } 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(, 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) 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(" "); items.elementAt(i).print(out); } } } abstract class ConstInfo { public abstract int getTag(); public String getClassName(ConstPool cp) { return null; } 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); void makeHashtable(ConstPool cp) {} // called after read() finishes in ConstPool. boolean hashCheck(int a, int b) { return false; } 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 String getClassName(ConstPool cp) { return cp.getUtf8Info(name); }; public void renameClass(ConstPool cp, String oldName, String newName) { String nameStr = cp.getUtf8Info(name); if (nameStr.equals(oldName)) name = cp.addUtf8Info(newName); else if (nameStr.charAt(0) == '[') { String nameStr2 = Descriptor.rename(nameStr, oldName, newName); if (nameStr != nameStr2) name = cp.addUtf8Info(nameStr2); } } public void renameClass(ConstPool cp, Map map) { String oldName = cp.getUtf8Info(name); if (oldName.charAt(0) == '[') { String newName = Descriptor.rename(oldName, map); if (oldName != newName) name = cp.addUtf8Info(newName); } else { 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); } void makeHashtable(ConstPool cp) { String name = Descriptor.toJavaName(getClassName(cp)); cp.classes.put(name, this); } } 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(); } boolean hashCheck(int a, int b) { return a == memberName && b == typeDescriptor; } 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); } boolean hashCheck(int a, int b) { return a == classIndex && b == nameAndTypeIndex; } 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("\""); } } javassist-3.12.1.ga/src/main/javassist/bytecode/stackmap/0000755000175000017500000000000011637463351023265 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/bytecode/stackmap/TypeTag.java0000644000175000017500000000217310630701321025470 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.stackmap; import javassist.bytecode.StackMapTable; public interface TypeTag { TypeData TOP = null; TypeData INTEGER = new TypeData.BasicType("int", StackMapTable.INTEGER); TypeData FLOAT = new TypeData.BasicType("float", StackMapTable.FLOAT); TypeData DOUBLE = new TypeData.BasicType("double", StackMapTable.DOUBLE); TypeData LONG = new TypeData.BasicType("long", StackMapTable.LONG); // and NULL, THIS, OBJECT, UNINIT } javassist-3.12.1.ga/src/main/javassist/bytecode/stackmap/Tracer.java0000644000175000017500000007333711265575121025361 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.stackmap; import javassist.bytecode.ByteArray; import javassist.bytecode.Opcode; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.BadBytecode; import javassist.ClassPool; /* * A class for performing abstract interpretation. * See also MapMaker class. */ public abstract class Tracer implements TypeTag { protected ClassPool classPool; protected ConstPool cpool; protected String returnType; protected int stackTop; protected TypeData[] stackTypes; protected TypeData[] localsTypes; public Tracer(ClassPool classes, ConstPool cp, int maxStack, int maxLocals, String retType) { classPool = classes; cpool = cp; returnType = retType; stackTop = 0; stackTypes = new TypeData[maxStack]; localsTypes = new TypeData[maxLocals]; } public Tracer(Tracer t, boolean copyStack) { classPool = t.classPool; cpool = t.cpool; returnType = t.returnType; stackTop = t.stackTop; int size = t.stackTypes.length; stackTypes = new TypeData[size]; if (copyStack) copyFrom(t.stackTop, t.stackTypes, stackTypes); int size2 = t.localsTypes.length; localsTypes = new TypeData[size2]; copyFrom(size2, t.localsTypes, localsTypes); } protected static int copyFrom(int n, TypeData[] srcTypes, TypeData[] destTypes) { int k = -1; for (int i = 0; i < n; i++) { TypeData t = srcTypes[i]; destTypes[i] = t == TOP ? TOP : t.getSelf(); if (t != TOP) if (t.is2WordType()) k = i + 1; else k = i; } return k + 1; } /** * Does abstract interpretation on the given bytecode instruction. * It records whether or not a local variable (i.e. register) is accessed. * If the instruction requires that a local variable or * a stack element has a more specific type, this method updates the * type of it. * * @param pos the position of the instruction. * @return the size of the instruction at POS. */ protected int doOpcode(int pos, byte[] code) throws BadBytecode { int op = code[pos] & 0xff; if (op < 96) if (op < 54) return doOpcode0_53(pos, code, op); else return doOpcode54_95(pos, code, op); else if (op < 148) return doOpcode96_147(pos, code, op); else return doOpcode148_201(pos, code, op); } protected void visitBranch(int pos, byte[] code, int offset) throws BadBytecode {} protected void visitGoto(int pos, byte[] code, int offset) throws BadBytecode {} protected void visitReturn(int pos, byte[] code) throws BadBytecode {} protected void visitThrow(int pos, byte[] code) throws BadBytecode {} /** * @param pos the position of TABLESWITCH * @param code bytecode * @param n the number of case labels * @param offsetPos the position of the branch-target table. * @param defaultOffset the offset to the default branch target. */ protected void visitTableSwitch(int pos, byte[] code, int n, int offsetPos, int defaultOffset) throws BadBytecode {} /** * @param pos the position of LOOKUPSWITCH * @param code bytecode * @param n the number of case labels * @param offsetPos the position of the table of pairs of a value and a branch target. * @param defaultOffset the offset to the default branch target. */ protected void visitLookupSwitch(int pos, byte[] code, int n, int pairsPos, int defaultOffset) throws BadBytecode {} /** * Invoked when the visited instruction is jsr. */ protected void visitJSR(int pos, byte[] code) throws BadBytecode { throwBadBytecode(pos, "jsr"); } /** * Invoked when the visited instruction is ret or wide ret. */ protected void visitRET(int pos, byte[] code) throws BadBytecode { throwBadBytecode(pos, "ret"); } private void throwBadBytecode(int pos, String name) throws BadBytecode { throw new BadBytecode(name + " at " + pos); } private int doOpcode0_53(int pos, byte[] code, int op) throws BadBytecode { int reg; TypeData[] stackTypes = this.stackTypes; switch (op) { case Opcode.NOP : break; case Opcode.ACONST_NULL : stackTypes[stackTop++] = new TypeData.NullType(); break; case Opcode.ICONST_M1 : case Opcode.ICONST_0 : case Opcode.ICONST_1 : case Opcode.ICONST_2 : case Opcode.ICONST_3 : case Opcode.ICONST_4 : case Opcode.ICONST_5 : stackTypes[stackTop++] = INTEGER; break; case Opcode.LCONST_0 : case Opcode.LCONST_1 : stackTypes[stackTop++] = LONG; stackTypes[stackTop++] = TOP; break; case Opcode.FCONST_0 : case Opcode.FCONST_1 : case Opcode.FCONST_2 : stackTypes[stackTop++] = FLOAT; break; case Opcode.DCONST_0 : case Opcode.DCONST_1 : stackTypes[stackTop++] = DOUBLE; stackTypes[stackTop++] = TOP; break; case Opcode.BIPUSH : case Opcode.SIPUSH : stackTypes[stackTop++] = INTEGER; return op == Opcode.SIPUSH ? 3 : 2; case Opcode.LDC : doLDC(code[pos + 1] & 0xff); return 2; case Opcode.LDC_W : case Opcode.LDC2_W : doLDC(ByteArray.readU16bit(code, pos + 1)); return 3; case Opcode.ILOAD : return doXLOAD(INTEGER, code, pos); case Opcode.LLOAD : return doXLOAD(LONG, code, pos); case Opcode.FLOAD : return doXLOAD(FLOAT, code, pos); case Opcode.DLOAD : return doXLOAD(DOUBLE, code, pos); case Opcode.ALOAD : return doALOAD(code[pos + 1] & 0xff); case Opcode.ILOAD_0 : case Opcode.ILOAD_1 : case Opcode.ILOAD_2 : case Opcode.ILOAD_3 : stackTypes[stackTop++] = INTEGER; break; case Opcode.LLOAD_0 : case Opcode.LLOAD_1 : case Opcode.LLOAD_2 : case Opcode.LLOAD_3 : stackTypes[stackTop++] = LONG; stackTypes[stackTop++] = TOP; break; case Opcode.FLOAD_0 : case Opcode.FLOAD_1 : case Opcode.FLOAD_2 : case Opcode.FLOAD_3 : stackTypes[stackTop++] = FLOAT; break; case Opcode.DLOAD_0 : case Opcode.DLOAD_1 : case Opcode.DLOAD_2 : case Opcode.DLOAD_3 : stackTypes[stackTop++] = DOUBLE; stackTypes[stackTop++] = TOP; break; case Opcode.ALOAD_0 : case Opcode.ALOAD_1 : case Opcode.ALOAD_2 : case Opcode.ALOAD_3 : reg = op - Opcode.ALOAD_0; stackTypes[stackTop++] = localsTypes[reg]; break; case Opcode.IALOAD : stackTypes[--stackTop - 1] = INTEGER; break; case Opcode.LALOAD : stackTypes[stackTop - 2] = LONG; stackTypes[stackTop - 1] = TOP; break; case Opcode.FALOAD : stackTypes[--stackTop - 1] = FLOAT; break; case Opcode.DALOAD : stackTypes[stackTop - 2] = DOUBLE; stackTypes[stackTop - 1] = TOP; break; case Opcode.AALOAD : { int s = --stackTop - 1; TypeData data = stackTypes[s]; if (data == null || !data.isObjectType()) throw new BadBytecode("bad AALOAD"); else stackTypes[s] = new TypeData.ArrayElement(data); break; } case Opcode.BALOAD : case Opcode.CALOAD : case Opcode.SALOAD : stackTypes[--stackTop - 1] = INTEGER; break; default : throw new RuntimeException("fatal"); } return 1; } private void doLDC(int index) { TypeData[] stackTypes = this.stackTypes; int tag = cpool.getTag(index); if (tag == ConstPool.CONST_String) stackTypes[stackTop++] = new TypeData.ClassName("java.lang.String"); else if (tag == ConstPool.CONST_Integer) stackTypes[stackTop++] = INTEGER; else if (tag == ConstPool.CONST_Float) stackTypes[stackTop++] = FLOAT; else if (tag == ConstPool.CONST_Long) { stackTypes[stackTop++] = LONG; stackTypes[stackTop++] = TOP; } else if (tag == ConstPool.CONST_Double) { stackTypes[stackTop++] = DOUBLE; stackTypes[stackTop++] = TOP; } else if (tag == ConstPool.CONST_Class) stackTypes[stackTop++] = new TypeData.ClassName("java.lang.Class"); else throw new RuntimeException("bad LDC: " + tag); } private int doXLOAD(TypeData type, byte[] code, int pos) { int localVar = code[pos + 1] & 0xff; return doXLOAD(localVar, type); } private int doXLOAD(int localVar, TypeData type) { stackTypes[stackTop++] = type; if (type.is2WordType()) stackTypes[stackTop++] = TOP; return 2; } private int doALOAD(int localVar) { // int localVar, TypeData type) { stackTypes[stackTop++] = localsTypes[localVar]; return 2; } private int doOpcode54_95(int pos, byte[] code, int op) throws BadBytecode { TypeData[] localsTypes = this.localsTypes; TypeData[] stackTypes = this.stackTypes; switch (op) { case Opcode.ISTORE : return doXSTORE(pos, code, INTEGER); case Opcode.LSTORE : return doXSTORE(pos, code, LONG); case Opcode.FSTORE : return doXSTORE(pos, code, FLOAT); case Opcode.DSTORE : return doXSTORE(pos, code, DOUBLE); case Opcode.ASTORE : return doASTORE(code[pos + 1] & 0xff); case Opcode.ISTORE_0 : case Opcode.ISTORE_1 : case Opcode.ISTORE_2 : case Opcode.ISTORE_3 : { int var = op - Opcode.ISTORE_0; localsTypes[var] = INTEGER; stackTop--; } break; case Opcode.LSTORE_0 : case Opcode.LSTORE_1 : case Opcode.LSTORE_2 : case Opcode.LSTORE_3 : { int var = op - Opcode.LSTORE_0; localsTypes[var] = LONG; localsTypes[var + 1] = TOP; stackTop -= 2; } break; case Opcode.FSTORE_0 : case Opcode.FSTORE_1 : case Opcode.FSTORE_2 : case Opcode.FSTORE_3 : { int var = op - Opcode.FSTORE_0; localsTypes[var] = FLOAT; stackTop--; } break; case Opcode.DSTORE_0 : case Opcode.DSTORE_1 : case Opcode.DSTORE_2 : case Opcode.DSTORE_3 : { int var = op - Opcode.DSTORE_0; localsTypes[var] = DOUBLE; localsTypes[var + 1] = TOP; stackTop -= 2; } break; case Opcode.ASTORE_0 : case Opcode.ASTORE_1 : case Opcode.ASTORE_2 : case Opcode.ASTORE_3 : { int var = op - Opcode.ASTORE_0; doASTORE(var); break; } case Opcode.IASTORE : case Opcode.LASTORE : case Opcode.FASTORE : case Opcode.DASTORE : stackTop -= (op == Opcode.LASTORE || op == Opcode.DASTORE) ? 4 : 3; break; case Opcode.AASTORE : TypeData.setType(stackTypes[stackTop - 1], TypeData.ArrayElement.getElementType(stackTypes[stackTop - 3].getName()), classPool); stackTop -= 3; break; case Opcode.BASTORE : case Opcode.CASTORE : case Opcode.SASTORE : stackTop -= 3; break; case Opcode.POP : stackTop--; break; case Opcode.POP2 : stackTop -= 2; break; case Opcode.DUP : { int sp = stackTop; stackTypes[sp] = stackTypes[sp - 1]; stackTop = sp + 1; break; } case Opcode.DUP_X1 : case Opcode.DUP_X2 : { int len = op - Opcode.DUP_X1 + 2; doDUP_XX(1, len); int sp = stackTop; stackTypes[sp - len] = stackTypes[sp]; stackTop = sp + 1; break; } case Opcode.DUP2 : doDUP_XX(2, 2); stackTop += 2; break; case Opcode.DUP2_X1 : case Opcode.DUP2_X2 : { int len = op - Opcode.DUP2_X1 + 3; doDUP_XX(2, len); int sp = stackTop; stackTypes[sp - len] = stackTypes[sp]; stackTypes[sp - len + 1] = stackTypes[sp + 1]; stackTop = sp + 2; break; } case Opcode.SWAP : { int sp = stackTop - 1; TypeData t = stackTypes[sp]; stackTypes[sp] = stackTypes[sp - 1]; stackTypes[sp - 1] = t; break; } default : throw new RuntimeException("fatal"); } return 1; } private int doXSTORE(int pos, byte[] code, TypeData type) { int index = code[pos + 1] & 0xff; return doXSTORE(index, type); } private int doXSTORE(int index, TypeData type) { stackTop--; localsTypes[index] = type; if (type.is2WordType()) { stackTop--; localsTypes[index + 1] = TOP; } return 2; } private int doASTORE(int index) { stackTop--; // implicit upcast might be done. localsTypes[index] = stackTypes[stackTop].copy(); return 2; } private void doDUP_XX(int delta, int len) { TypeData types[] = stackTypes; int sp = stackTop - 1; int end = sp - len; while (sp > end) { types[sp + delta] = types[sp]; sp--; } } private int doOpcode96_147(int pos, byte[] code, int op) { if (op <= Opcode.LXOR) { // IADD...LXOR stackTop += Opcode.STACK_GROW[op]; return 1; } switch (op) { case Opcode.IINC : // this does not call writeLocal(). return 3; case Opcode.I2L : stackTypes[stackTop] = LONG; stackTypes[stackTop - 1] = TOP; stackTop++; break; case Opcode.I2F : stackTypes[stackTop - 1] = FLOAT; break; case Opcode.I2D : stackTypes[stackTop] = DOUBLE; stackTypes[stackTop - 1] = TOP; stackTop++; break; case Opcode.L2I : stackTypes[--stackTop - 1] = INTEGER; break; case Opcode.L2F : stackTypes[--stackTop - 1] = FLOAT; break; case Opcode.L2D : stackTypes[stackTop - 1] = DOUBLE; break; case Opcode.F2I : stackTypes[stackTop - 1] = INTEGER; break; case Opcode.F2L : stackTypes[stackTop - 1] = TOP; stackTypes[stackTop++] = LONG; break; case Opcode.F2D : stackTypes[stackTop - 1] = TOP; stackTypes[stackTop++] = DOUBLE; break; case Opcode.D2I : stackTypes[--stackTop - 1] = INTEGER; break; case Opcode.D2L : stackTypes[stackTop - 1] = LONG; break; case Opcode.D2F : stackTypes[--stackTop - 1] = FLOAT; break; case Opcode.I2B : case Opcode.I2C : case Opcode.I2S : break; default : throw new RuntimeException("fatal"); } return 1; } private int doOpcode148_201(int pos, byte[] code, int op) throws BadBytecode { switch (op) { case Opcode.LCMP : stackTypes[stackTop - 4] = INTEGER; stackTop -= 3; break; case Opcode.FCMPL : case Opcode.FCMPG : stackTypes[--stackTop - 1] = INTEGER; break; case Opcode.DCMPL : case Opcode.DCMPG : stackTypes[stackTop - 4] = INTEGER; stackTop -= 3; break; case Opcode.IFEQ : case Opcode.IFNE : case Opcode.IFLT : case Opcode.IFGE : case Opcode.IFGT : case Opcode.IFLE : stackTop--; // branch visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1)); return 3; case Opcode.IF_ICMPEQ : case Opcode.IF_ICMPNE : case Opcode.IF_ICMPLT : case Opcode.IF_ICMPGE : case Opcode.IF_ICMPGT : case Opcode.IF_ICMPLE : case Opcode.IF_ACMPEQ : case Opcode.IF_ACMPNE : stackTop -= 2; // branch visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1)); return 3; case Opcode.GOTO : visitGoto(pos, code, ByteArray.readS16bit(code, pos + 1)); return 3; // branch case Opcode.JSR : stackTypes[stackTop++] = TOP; // not allowed? visitJSR(pos, code); return 3; // branch case Opcode.RET : visitRET(pos, code); return 2; // not allowed? case Opcode.TABLESWITCH : { stackTop--; // branch int pos2 = (pos & ~3) + 8; int low = ByteArray.read32bit(code, pos2); int high = ByteArray.read32bit(code, pos2 + 4); int n = high - low + 1; visitTableSwitch(pos, code, n, pos2 + 8, ByteArray.read32bit(code, pos2 - 4)); return n * 4 + 16 - (pos & 3); } case Opcode.LOOKUPSWITCH : { stackTop--; // branch int pos2 = (pos & ~3) + 8; int n = ByteArray.read32bit(code, pos2); visitLookupSwitch(pos, code, n, pos2 + 4, ByteArray.read32bit(code, pos2 - 4)); return n * 8 + 12 - (pos & 3); } case Opcode.IRETURN : stackTop--; visitReturn(pos, code); break; case Opcode.LRETURN : stackTop -= 2; visitReturn(pos, code); break; case Opcode.FRETURN : stackTop--; visitReturn(pos, code); break; case Opcode.DRETURN : stackTop -= 2; visitReturn(pos, code); break; case Opcode.ARETURN : TypeData.setType(stackTypes[--stackTop], returnType, classPool); visitReturn(pos, code); break; case Opcode.RETURN : visitReturn(pos, code); break; case Opcode.GETSTATIC : return doGetField(pos, code, false); case Opcode.PUTSTATIC : return doPutField(pos, code, false); case Opcode.GETFIELD : return doGetField(pos, code, true); case Opcode.PUTFIELD : return doPutField(pos, code, true); case Opcode.INVOKEVIRTUAL : case Opcode.INVOKESPECIAL : return doInvokeMethod(pos, code, true); case Opcode.INVOKESTATIC : return doInvokeMethod(pos, code, false); case Opcode.INVOKEINTERFACE : return doInvokeIntfMethod(pos, code); case 186 : throw new RuntimeException("bad opcode 186"); case Opcode.NEW : { int i = ByteArray.readU16bit(code, pos + 1); stackTypes[stackTop++] = new TypeData.UninitData(pos, cpool.getClassInfo(i)); return 3; } case Opcode.NEWARRAY : return doNEWARRAY(pos, code); case Opcode.ANEWARRAY : { int i = ByteArray.readU16bit(code, pos + 1); String type = cpool.getClassInfo(i).replace('.', '/'); if (type.charAt(0) == '[') type = "[" + type; else type = "[L" + type + ";"; stackTypes[stackTop - 1] = new TypeData.ClassName(type); return 3; } case Opcode.ARRAYLENGTH : TypeData.setType(stackTypes[stackTop - 1], "[Ljava.lang.Object;", classPool); stackTypes[stackTop - 1] = INTEGER; break; case Opcode.ATHROW : TypeData.setType(stackTypes[--stackTop], "java.lang.Throwable", classPool); visitThrow(pos, code); break; case Opcode.CHECKCAST : { // TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool); int i = ByteArray.readU16bit(code, pos + 1); stackTypes[stackTop - 1] = new TypeData.ClassName(cpool.getClassInfo(i)); return 3; } case Opcode.INSTANCEOF : // TypeData.setType(stackTypes[stackTop - 1], "java.lang.Object", classPool); stackTypes[stackTop - 1] = INTEGER; return 3; case Opcode.MONITORENTER : case Opcode.MONITOREXIT : stackTop--; // TypeData.setType(stackTypes[stackTop], "java.lang.Object", classPool); break; case Opcode.WIDE : return doWIDE(pos, code); case Opcode.MULTIANEWARRAY : return doMultiANewArray(pos, code); case Opcode.IFNULL : case Opcode.IFNONNULL : stackTop--; // branch visitBranch(pos, code, ByteArray.readS16bit(code, pos + 1)); return 3; case Opcode.GOTO_W : visitGoto(pos, code, ByteArray.read32bit(code, pos + 1)); return 5; // branch case Opcode.JSR_W : stackTypes[stackTop++] = TOP; // not allowed? visitJSR(pos, code); return 5; } return 1; } private int doWIDE(int pos, byte[] code) throws BadBytecode { int op = code[pos + 1] & 0xff; switch (op) { case Opcode.ILOAD : doWIDE_XLOAD(pos, code, INTEGER); break; case Opcode.LLOAD : doWIDE_XLOAD(pos, code, LONG); break; case Opcode.FLOAD : doWIDE_XLOAD(pos, code, FLOAT); break; case Opcode.DLOAD : doWIDE_XLOAD(pos, code, DOUBLE); break; case Opcode.ALOAD : { int index = ByteArray.readU16bit(code, pos + 2); doALOAD(index); break; } case Opcode.ISTORE : doWIDE_STORE(pos, code, INTEGER); break; case Opcode.LSTORE : doWIDE_STORE(pos, code, LONG); break; case Opcode.FSTORE : doWIDE_STORE(pos, code, FLOAT); break; case Opcode.DSTORE : doWIDE_STORE(pos, code, DOUBLE); break; case Opcode.ASTORE : { int index = ByteArray.readU16bit(code, pos + 2); doASTORE(index); break; } case Opcode.IINC : // this does not call writeLocal(). return 6; case Opcode.RET : visitRET(pos, code); break; default : throw new RuntimeException("bad WIDE instruction: " + op); } return 4; } private void doWIDE_XLOAD(int pos, byte[] code, TypeData type) { int index = ByteArray.readU16bit(code, pos + 2); doXLOAD(index, type); } private void doWIDE_STORE(int pos, byte[] code, TypeData type) { int index = ByteArray.readU16bit(code, pos + 2); doXSTORE(index, type); } private int doPutField(int pos, byte[] code, boolean notStatic) throws BadBytecode { int index = ByteArray.readU16bit(code, pos + 1); String desc = cpool.getFieldrefType(index); stackTop -= Descriptor.dataSize(desc); char c = desc.charAt(0); if (c == 'L') TypeData.setType(stackTypes[stackTop], getFieldClassName(desc, 0), classPool); else if (c == '[') TypeData.setType(stackTypes[stackTop], desc, classPool); setFieldTarget(notStatic, index); return 3; } private int doGetField(int pos, byte[] code, boolean notStatic) throws BadBytecode { int index = ByteArray.readU16bit(code, pos + 1); setFieldTarget(notStatic, index); String desc = cpool.getFieldrefType(index); pushMemberType(desc); return 3; } private void setFieldTarget(boolean notStatic, int index) throws BadBytecode { if (notStatic) { String className = cpool.getFieldrefClassName(index); TypeData.setType(stackTypes[--stackTop], className, classPool); } } private int doNEWARRAY(int pos, byte[] code) { int s = stackTop - 1; String type; switch (code[pos + 1] & 0xff) { case Opcode.T_BOOLEAN : type = "[Z"; break; case Opcode.T_CHAR : type = "[C"; break; case Opcode.T_FLOAT : type = "[F"; break; case Opcode.T_DOUBLE : type = "[D"; break; case Opcode.T_BYTE : type = "[B"; break; case Opcode.T_SHORT : type = "[S"; break; case Opcode.T_INT : type = "[I"; break; case Opcode.T_LONG : type = "[J"; break; default : throw new RuntimeException("bad newarray"); } stackTypes[s] = new TypeData.ClassName(type); return 2; } private int doMultiANewArray(int pos, byte[] code) { int i = ByteArray.readU16bit(code, pos + 1); int dim = code[pos + 3] & 0xff; stackTop -= dim - 1; String type = cpool.getClassInfo(i).replace('.', '/'); stackTypes[stackTop - 1] = new TypeData.ClassName(type); return 4; } private int doInvokeMethod(int pos, byte[] code, boolean notStatic) throws BadBytecode { int i = ByteArray.readU16bit(code, pos + 1); String desc = cpool.getMethodrefType(i); checkParamTypes(desc, 1); if (notStatic) { String className = cpool.getMethodrefClassName(i); TypeData.setType(stackTypes[--stackTop], className, classPool); } pushMemberType(desc); return 3; } private int doInvokeIntfMethod(int pos, byte[] code) throws BadBytecode { int i = ByteArray.readU16bit(code, pos + 1); String desc = cpool.getInterfaceMethodrefType(i); checkParamTypes(desc, 1); String className = cpool.getInterfaceMethodrefClassName(i); TypeData.setType(stackTypes[--stackTop], className, classPool); pushMemberType(desc); return 5; } private void pushMemberType(String descriptor) { int top = 0; if (descriptor.charAt(0) == '(') { top = descriptor.indexOf(')') + 1; if (top < 1) throw new IndexOutOfBoundsException("bad descriptor: " + descriptor); } TypeData[] types = stackTypes; int index = stackTop; switch (descriptor.charAt(top)) { case '[' : types[index] = new TypeData.ClassName(descriptor.substring(top)); break; case 'L' : types[index] = new TypeData.ClassName(getFieldClassName(descriptor, top)); break; case 'J' : types[index] = LONG; types[index + 1] = TOP; stackTop += 2; return; case 'F' : types[index] = FLOAT; break; case 'D' : types[index] = DOUBLE; types[index + 1] = TOP; stackTop += 2; return; case 'V' : return; default : // C, B, S, I, Z types[index] = INTEGER; break; } stackTop++; } private static String getFieldClassName(String desc, int index) { return desc.substring(index + 1, desc.length() - 1).replace('/', '.'); } private void checkParamTypes(String desc, int i) throws BadBytecode { char c = desc.charAt(i); if (c == ')') return; int k = i; boolean array = false; while (c == '[') { array = true; c = desc.charAt(++k); } if (c == 'L') { k = desc.indexOf(';', k) + 1; if (k <= 0) throw new IndexOutOfBoundsException("bad descriptor"); } else k++; checkParamTypes(desc, k); if (!array && (c == 'J' || c == 'D')) stackTop -= 2; else stackTop--; if (array) TypeData.setType(stackTypes[stackTop], desc.substring(i, k), classPool); else if (c == 'L') TypeData.setType(stackTypes[stackTop], desc.substring(i + 1, k - 1).replace('/', '.'), classPool); } } javassist-3.12.1.ga/src/main/javassist/bytecode/stackmap/Liveness.java0000644000175000017500000002537310630701321025712 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.stackmap; import javassist.bytecode.*; public class Liveness { protected static final byte UNKNOWN = 0; protected static final byte READ = 1; protected static final byte UPDATED = 2; protected byte[] localsUsage; /** * If true, all the arguments become alive within the whole method body. * * To correctly compute a stack map table, all the arguments must * be alive (localsUsage[?] must be READ) at least in the first block. */ public static boolean useArgs = true; public void compute(CodeIterator ci, TypedBlock[] blocks, int maxLocals, TypeData[] args) throws BadBytecode { computeUsage(ci, blocks, maxLocals); if (useArgs) useAllArgs(blocks, args); computeLiveness1(blocks[0]); while (hasChanged(blocks)) computeLiveness2(blocks[0]); } private void useAllArgs(TypedBlock[] blocks, TypeData[] args) { for (int k = 0; k < blocks.length; k++) { byte[] usage = blocks[k].localsUsage; for (int i = 0; i < args.length; i++) if (args[i] != TypeTag.TOP) usage[i] = READ; } } static final int NOT_YET = 0; static final int CHANGED_LAST = 1; static final int DONE = 2; static final int CHANGED_NOW = 3; private void computeLiveness1(TypedBlock tb) { if (tb.updating) { // a loop was detected. computeLiveness1u(tb); return; } if (tb.inputs != null) return; tb.updating = true; byte[] usage = tb.localsUsage; int n = usage.length; boolean[] in = new boolean[n]; for (int i = 0; i < n; i++) in[i] = usage[i] == READ; BasicBlock.Catch handlers = tb.toCatch; while (handlers != null) { TypedBlock h = (TypedBlock)handlers.body; computeLiveness1(h); for (int k = 0; k < n; k++) if (h.inputs[k]) in[k] = true; handlers = handlers.next; } if (tb.exit != null) { for (int i = 0; i < tb.exit.length; i++) { TypedBlock e = (TypedBlock)tb.exit[i]; computeLiveness1(e); for (int k = 0; k < n; k++) if (!in[k]) in[k] = usage[k] == UNKNOWN && e.inputs[k]; } } tb.updating = false; if (tb.inputs == null) { tb.inputs = in; tb.status = DONE; } else { for (int i = 0; i < n; i++) if (in[i] && !tb.inputs[i]) { tb.inputs[i] = true; tb.status = CHANGED_NOW; } } } private void computeLiveness1u(TypedBlock tb) { if (tb.inputs == null) { byte[] usage = tb.localsUsage; int n = usage.length; boolean[] in = new boolean[n]; for (int i = 0; i < n; i++) in[i] = usage[i] == READ; tb.inputs = in; tb.status = DONE; } } private void computeLiveness2(TypedBlock tb) { if (tb.updating || tb.status >= DONE) return; tb.updating = true; if (tb.exit == null) tb.status = DONE; else { boolean changed = false; for (int i = 0; i < tb.exit.length; i++) { TypedBlock e = (TypedBlock)tb.exit[i]; computeLiveness2(e); if (e.status != DONE) changed = true; } if (changed) { changed = false; byte[] usage = tb.localsUsage; int n = usage.length; for (int i = 0; i < tb.exit.length; i++) { TypedBlock e = (TypedBlock)tb.exit[i]; if (e.status != DONE) for (int k = 0; k < n; k++) if (!tb.inputs[k]) { if (usage[k] == UNKNOWN && e.inputs[k]) { tb.inputs[k] = true; changed = true; } } } tb.status = changed ? CHANGED_NOW : DONE; } else tb.status = DONE; } if (computeLiveness2except(tb)) tb.status = CHANGED_NOW; tb.updating = false; } private boolean computeLiveness2except(TypedBlock tb) { BasicBlock.Catch handlers = tb.toCatch; boolean changed = false; while (handlers != null) { TypedBlock h = (TypedBlock)handlers.body; computeLiveness2(h); if (h.status != DONE) { boolean[] in = tb.inputs; int n = in.length; for (int k = 0; k < n; k++) if (!in[k] && h.inputs[k]) { in[k] = true; changed = true; } } handlers = handlers.next; } return changed; } private boolean hasChanged(TypedBlock[] blocks) { int n = blocks.length; boolean changed = false; for (int i = 0; i < n; i++) { TypedBlock tb = blocks[i]; if (tb.status == CHANGED_NOW) { tb.status = CHANGED_LAST; changed = true; } else tb.status = NOT_YET; } return changed; } private void computeUsage(CodeIterator ci, TypedBlock[] blocks, int maxLocals) throws BadBytecode { int n = blocks.length; for (int i = 0; i < n; i++) { TypedBlock tb = blocks[i]; localsUsage = tb.localsUsage = new byte[maxLocals]; int pos = tb.position; analyze(ci, pos, pos + tb.length); localsUsage = null; } } protected final void readLocal(int reg) { if (localsUsage[reg] == UNKNOWN) localsUsage[reg] = READ; } protected final void writeLocal(int reg) { if (localsUsage[reg] == UNKNOWN) localsUsage[reg] = UPDATED; } protected void analyze(CodeIterator ci, int begin, int end) throws BadBytecode { ci.begin(); ci.move(begin); while (ci.hasNext()) { int index = ci.next(); if (index >= end) break; int op = ci.byteAt(index); if (op < 96) if (op < 54) doOpcode0_53(ci, index, op); else doOpcode54_95(ci, index, op); else if (op == Opcode.IINC) { // this does not call writeLocal(). readLocal(ci.byteAt(index + 1)); } else if (op == Opcode.WIDE) doWIDE(ci, index); } } private void doOpcode0_53(CodeIterator ci, int pos, int op) { switch (op) { case Opcode.ILOAD : case Opcode.LLOAD : case Opcode.FLOAD : case Opcode.DLOAD : case Opcode.ALOAD : readLocal(ci.byteAt(pos + 1)); break; case Opcode.ILOAD_0 : case Opcode.ILOAD_1 : case Opcode.ILOAD_2 : case Opcode.ILOAD_3 : readLocal(op - Opcode.ILOAD_0); break; case Opcode.LLOAD_0 : case Opcode.LLOAD_1 : case Opcode.LLOAD_2 : case Opcode.LLOAD_3 : readLocal(op - Opcode.LLOAD_0); break; case Opcode.FLOAD_0 : case Opcode.FLOAD_1 : case Opcode.FLOAD_2 : case Opcode.FLOAD_3 : readLocal(op - Opcode.FLOAD_0); break; case Opcode.DLOAD_0 : case Opcode.DLOAD_1 : case Opcode.DLOAD_2 : case Opcode.DLOAD_3 : readLocal(op - Opcode.DLOAD_0); break; case Opcode.ALOAD_0 : case Opcode.ALOAD_1 : case Opcode.ALOAD_2 : case Opcode.ALOAD_3 : readLocal(op - Opcode.ALOAD_0); break; } } private void doOpcode54_95(CodeIterator ci, int pos, int op) { switch (op) { case Opcode.ISTORE : case Opcode.LSTORE : case Opcode.FSTORE : case Opcode.DSTORE : case Opcode.ASTORE : writeLocal(ci.byteAt(pos + 1)); break; case Opcode.ISTORE_0 : case Opcode.ISTORE_1 : case Opcode.ISTORE_2 : case Opcode.ISTORE_3 : writeLocal(op - Opcode.ISTORE_0); break; case Opcode.LSTORE_0 : case Opcode.LSTORE_1 : case Opcode.LSTORE_2 : case Opcode.LSTORE_3 : writeLocal(op - Opcode.LSTORE_0); break; case Opcode.FSTORE_0 : case Opcode.FSTORE_1 : case Opcode.FSTORE_2 : case Opcode.FSTORE_3 : writeLocal(op - Opcode.FSTORE_0); break; case Opcode.DSTORE_0 : case Opcode.DSTORE_1 : case Opcode.DSTORE_2 : case Opcode.DSTORE_3 : writeLocal(op - Opcode.DSTORE_0); break; case Opcode.ASTORE_0 : case Opcode.ASTORE_1 : case Opcode.ASTORE_2 : case Opcode.ASTORE_3 : writeLocal(op - Opcode.ASTORE_0); break; } } private void doWIDE(CodeIterator ci, int pos) throws BadBytecode { int op = ci.byteAt(pos + 1); int var = ci.u16bitAt(pos + 2); switch (op) { case Opcode.ILOAD : case Opcode.LLOAD : case Opcode.FLOAD : case Opcode.DLOAD : case Opcode.ALOAD : readLocal(var); break; case Opcode.ISTORE : case Opcode.LSTORE : case Opcode.FSTORE : case Opcode.DSTORE : case Opcode.ASTORE : writeLocal(var); break; case Opcode.IINC : readLocal(var); // this does not call writeLocal(). break; } } } javassist-3.12.1.ga/src/main/javassist/bytecode/stackmap/TypedBlock.java0000644000175000017500000001735510630701321026163 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.stackmap; import javassist.bytecode.*; public class TypedBlock extends BasicBlock { public int stackTop, numLocals; public TypeData[] stackTypes, localsTypes; // set by a Liveness object. // inputs[i] is true if the i-th variable is used within this block. public boolean[] inputs; // working area for Liveness class. public boolean updating; public int status; public byte[] localsUsage; /** * Divides the method body into basic blocks. * The type information of the first block is initialized. * * @param optmize if it is true and the method does not include * branches, this method returns null. */ public static TypedBlock[] makeBlocks(MethodInfo minfo, CodeAttribute ca, boolean optimize) throws BadBytecode { TypedBlock[] blocks = (TypedBlock[])new Maker().make(minfo); if (optimize && blocks.length < 2) if (blocks.length == 0 || blocks[0].incoming == 0) return null; ConstPool pool = minfo.getConstPool(); boolean isStatic = (minfo.getAccessFlags() & AccessFlag.STATIC) != 0; blocks[0].initFirstBlock(ca.getMaxStack(), ca.getMaxLocals(), pool.getClassName(), minfo.getDescriptor(), isStatic, minfo.isConstructor()); new Liveness().compute(ca.iterator(), blocks, ca.getMaxLocals(), blocks[0].localsTypes); return blocks; } protected TypedBlock(int pos) { super(pos); localsTypes = null; inputs = null; updating = false; } protected void toString2(StringBuffer sbuf) { super.toString2(sbuf); sbuf.append(",\n stack={"); printTypes(sbuf, stackTop, stackTypes); sbuf.append("}, locals={"); printTypes(sbuf, numLocals, localsTypes); sbuf.append("}, inputs={"); if (inputs != null) for (int i = 0; i < inputs.length; i++) sbuf.append(inputs[i] ? "1, " : "0, "); sbuf.append('}'); } private void printTypes(StringBuffer sbuf, int size, TypeData[] types) { if (types == null) return; for (int i = 0; i < size; i++) { if (i > 0) sbuf.append(", "); TypeData td = types[i]; sbuf.append(td == null ? "<>" : td.toString()); } } public boolean alreadySet() { return localsTypes != null; } public void setStackMap(int st, TypeData[] stack, int nl, TypeData[] locals) throws BadBytecode { stackTop = st; stackTypes = stack; numLocals = nl; localsTypes = locals; } /* * Computes the correct value of numLocals. */ public void resetNumLocals() { if (localsTypes != null) { int nl = localsTypes.length; while (nl > 0 && localsTypes[nl - 1] == TypeTag.TOP) { if (nl > 1) { TypeData td = localsTypes[nl - 2]; if (td == TypeTag.LONG || td == TypeTag.DOUBLE) break; } --nl; } numLocals = nl; } } public static class Maker extends BasicBlock.Maker { protected BasicBlock makeBlock(int pos) { return new TypedBlock(pos); } protected BasicBlock[] makeArray(int size) { return new TypedBlock[size]; } } /** * Initializes the first block by the given method descriptor. * * @param block the first basic block that this method initializes. * @param className a dot-separated fully qualified class name. * For example, javassist.bytecode.stackmap.BasicBlock. * @param methodDesc method descriptor. * @param isStatic true if the method is a static method. * @param isConstructor true if the method is a constructor. */ void initFirstBlock(int maxStack, int maxLocals, String className, String methodDesc, boolean isStatic, boolean isConstructor) throws BadBytecode { if (methodDesc.charAt(0) != '(') throw new BadBytecode("no method descriptor: " + methodDesc); stackTop = 0; stackTypes = new TypeData[maxStack]; TypeData[] locals = new TypeData[maxLocals]; if (isConstructor) locals[0] = new TypeData.UninitThis(className); else if (!isStatic) locals[0] = new TypeData.ClassName(className); int n = isStatic ? -1 : 0; int i = 1; try { while ((i = descToTag(methodDesc, i, ++n, locals)) > 0) if (locals[n].is2WordType()) locals[++n] = TypeTag.TOP; } catch (StringIndexOutOfBoundsException e) { throw new BadBytecode("bad method descriptor: " + methodDesc); } numLocals = n; localsTypes = locals; } private static int descToTag(String desc, int i, int n, TypeData[] types) throws BadBytecode { int i0 = i; int arrayDim = 0; char c = desc.charAt(i); if (c == ')') return 0; while (c == '[') { ++arrayDim; c = desc.charAt(++i); } if (c == 'L') { int i2 = desc.indexOf(';', ++i); if (arrayDim > 0) types[n] = new TypeData.ClassName(desc.substring(i0, ++i2)); else types[n] = new TypeData.ClassName(desc.substring(i0 + 1, ++i2 - 1) .replace('/', '.')); return i2; } else if (arrayDim > 0) { types[n] = new TypeData.ClassName(desc.substring(i0, ++i)); return i; } else { TypeData t = toPrimitiveTag(c); if (t == null) throw new BadBytecode("bad method descriptor: " + desc); types[n] = t; return i + 1; } } private static TypeData toPrimitiveTag(char c) { switch (c) { case 'Z' : case 'C' : case 'B' : case 'S' : case 'I' : return TypeTag.INTEGER; case 'J' : return TypeTag.LONG; case 'F' : return TypeTag.FLOAT; case 'D' : return TypeTag.DOUBLE; case 'V' : default : return null; } } public static String getRetType(String desc) { int i = desc.indexOf(')'); if (i < 0) return "java.lang.Object"; char c = desc.charAt(i + 1); if (c == '[') return desc.substring(i + 1); else if (c == 'L') return desc.substring(i + 2, desc.length() - 1).replace('/', '.'); else return "java.lang.Object"; } } javassist-3.12.1.ga/src/main/javassist/bytecode/stackmap/MapMaker.java0000644000175000017500000004103011275555573025631 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.stackmap; import javassist.ClassPool; import javassist.bytecode.*; /** * Stack map maker. */ public class MapMaker extends Tracer { /* public static void main(String[] args) throws Exception { boolean useMain2 = args[0].equals("0"); if (useMain2 && args.length > 1) { main2(args); return; } for (int i = 0; i < args.length; i++) main1(args[i]); } public static void main1(String className) throws Exception { ClassPool cp = ClassPool.getDefault(); //javassist.CtClass cc = cp.get(className); javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(className)); System.out.println(className); ClassFile cf = cc.getClassFile(); java.util.List minfos = cf.getMethods(); for (int i = 0; i < minfos.size(); i++) { MethodInfo minfo = (MethodInfo)minfos.get(i); CodeAttribute ca = minfo.getCodeAttribute(); if (ca != null) ca.setAttribute(make(cp, minfo)); } cc.writeFile("tmp"); } public static void main2(String[] args) throws Exception { ClassPool cp = ClassPool.getDefault(); //javassist.CtClass cc = cp.get(args[1]); javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(args[1])); MethodInfo minfo; if (args[2].equals("_init_")) minfo = cc.getDeclaredConstructors()[0].getMethodInfo(); // minfo = cc.getClassInitializer().getMethodInfo(); else minfo = cc.getDeclaredMethod(args[2]).getMethodInfo(); CodeAttribute ca = minfo.getCodeAttribute(); if (ca == null) { System.out.println("abstarct method"); return; } TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, false); MapMaker mm = new MapMaker(cp, minfo, ca); mm.make(blocks, ca.getCode()); for (int i = 0; i < blocks.length; i++) System.out.println(blocks[i]); } */ /** * Computes the stack map table of the given method and returns it. * It returns null if the given method does not have to have a * stack map table. */ public static StackMapTable make(ClassPool classes, MethodInfo minfo) throws BadBytecode { CodeAttribute ca = minfo.getCodeAttribute(); if (ca == null) return null; TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, true); if (blocks == null) return null; MapMaker mm = new MapMaker(classes, minfo, ca); mm.make(blocks, ca.getCode()); return mm.toStackMap(blocks); } /** * Computes the stack map table for J2ME. * It returns null if the given method does not have to have a * stack map table. */ public static StackMap make2(ClassPool classes, MethodInfo minfo) throws BadBytecode { CodeAttribute ca = minfo.getCodeAttribute(); if (ca == null) return null; TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, true); if (blocks == null) return null; MapMaker mm = new MapMaker(classes, minfo, ca); mm.make(blocks, ca.getCode()); return mm.toStackMap2(minfo.getConstPool(), blocks); } public MapMaker(ClassPool classes, MethodInfo minfo, CodeAttribute ca) { super(classes, minfo.getConstPool(), ca.getMaxStack(), ca.getMaxLocals(), TypedBlock.getRetType(minfo.getDescriptor())); } protected MapMaker(MapMaker old, boolean copyStack) { super(old, copyStack); } /** * Runs an analyzer (Phase 1 and 2). */ void make(TypedBlock[] blocks, byte[] code) throws BadBytecode { TypedBlock first = blocks[0]; fixParamTypes(first); TypeData[] srcTypes = first.localsTypes; copyFrom(srcTypes.length, srcTypes, this.localsTypes); make(code, first); int n = blocks.length; for (int i = 0; i < n; i++) evalExpected(blocks[i]); } /* * If a parameter type is String but it is used only as Object * within the method body, this MapMaker class will report its type * is Object. To avoid this, fixParamTypes calls TypeData.setType() * on each parameter type. */ private void fixParamTypes(TypedBlock first) throws BadBytecode { TypeData[] types = first.localsTypes; int n = types.length; for (int i = 0; i < n; i++) { TypeData t = types[i]; if (t instanceof TypeData.ClassName) { /* Skip the following statement if t.isNullType() is true * although a parameter type is never null type. */ TypeData.setType(t, t.getName(), classPool); } } } // Phase 1 private void make(byte[] code, TypedBlock tb) throws BadBytecode { BasicBlock.Catch handlers = tb.toCatch; while (handlers != null) { traceException(code, handlers); handlers = handlers.next; } int pos = tb.position; int end = pos + tb.length; while (pos < end) pos += doOpcode(pos, code); if (tb.exit != null) { for (int i = 0; i < tb.exit.length; i++) { TypedBlock e = (TypedBlock)tb.exit[i]; if (e.alreadySet()) mergeMap(e, true); else { recordStackMap(e); MapMaker maker = new MapMaker(this, true); maker.make(code, e); } } } } private void traceException(byte[] code, TypedBlock.Catch handler) throws BadBytecode { TypedBlock tb = (TypedBlock)handler.body; if (tb.alreadySet()) mergeMap(tb, false); else { recordStackMap(tb, handler.typeIndex); MapMaker maker = new MapMaker(this, false); /* the following code is equivalent to maker.copyFrom(this) * except stackTypes are not copied. */ maker.stackTypes[0] = tb.stackTypes[0].getSelf(); maker.stackTop = 1; maker.make(code, tb); } } private void mergeMap(TypedBlock dest, boolean mergeStack) { boolean[] inputs = dest.inputs; int n = inputs.length; for (int i = 0; i < n; i++) if (inputs[i]) merge(localsTypes[i], dest.localsTypes[i]); if (mergeStack) { n = stackTop; for (int i = 0; i < n; i++) merge(stackTypes[i], dest.stackTypes[i]); } } private void merge(TypeData td, TypeData target) { boolean tdIsObj = false; boolean targetIsObj = false; // td or target is null if it is TOP. if (td != TOP && td.isObjectType()) tdIsObj = true; if (target != TOP && target.isObjectType()) targetIsObj = true; if (tdIsObj && targetIsObj) target.merge(td); } private void recordStackMap(TypedBlock target) throws BadBytecode { TypeData[] tStackTypes = new TypeData[stackTypes.length]; int st = stackTop; copyFrom(st, stackTypes, tStackTypes); recordStackMap0(target, st, tStackTypes); } private void recordStackMap(TypedBlock target, int exceptionType) throws BadBytecode { String type; if (exceptionType == 0) type = "java.lang.Throwable"; else type = cpool.getClassInfo(exceptionType); TypeData[] tStackTypes = new TypeData[stackTypes.length]; tStackTypes[0] = new TypeData.ClassName(type); recordStackMap0(target, 1, tStackTypes); } private void recordStackMap0(TypedBlock target, int st, TypeData[] tStackTypes) throws BadBytecode { int n = localsTypes.length; TypeData[] tLocalsTypes = new TypeData[n]; int k = copyFrom(n, localsTypes, tLocalsTypes); boolean[] inputs = target.inputs; for (int i = 0; i < n; i++) if (!inputs[i]) tLocalsTypes[i] = TOP; target.setStackMap(st, tStackTypes, k, tLocalsTypes); } // Phase 2 void evalExpected(TypedBlock target) throws BadBytecode { ClassPool cp = classPool; evalExpected(cp, target.stackTop, target.stackTypes); TypeData[] types = target.localsTypes; if (types != null) // unless this block is dead code evalExpected(cp, types.length, types); } private static void evalExpected(ClassPool cp, int n, TypeData[] types) throws BadBytecode { for (int i = 0; i < n; i++) { TypeData td = types[i]; if (td != null) td.evalExpectedType(cp); } } // Phase 3 public StackMapTable toStackMap(TypedBlock[] blocks) { StackMapTable.Writer writer = new StackMapTable.Writer(32); int n = blocks.length; TypedBlock prev = blocks[0]; int offsetDelta = prev.length; if (prev.incoming > 0) { // the first instruction is a branch target. writer.sameFrame(0); offsetDelta--; } for (int i = 1; i < n; i++) { TypedBlock bb = blocks[i]; if (isTarget(bb, blocks[i - 1])) { bb.resetNumLocals(); int diffL = stackMapDiff(prev.numLocals, prev.localsTypes, bb.numLocals, bb.localsTypes); toStackMapBody(writer, bb, diffL, offsetDelta, prev); offsetDelta = bb.length - 1; prev = bb; } else offsetDelta += bb.length; } return writer.toStackMapTable(cpool); } /** * Returns true if cur is a branch target. */ private boolean isTarget(TypedBlock cur, TypedBlock prev) { int in = cur.incoming; if (in > 1) return true; else if (in < 1) return false; return prev.stop; } private void toStackMapBody(StackMapTable.Writer writer, TypedBlock bb, int diffL, int offsetDelta, TypedBlock prev) { // if diffL is -100, two TypeData arrays do not share // any elements. int stackTop = bb.stackTop; if (stackTop == 0) { if (diffL == 0) { writer.sameFrame(offsetDelta); return; } else if (0 > diffL && diffL >= -3) { writer.chopFrame(offsetDelta, -diffL); return; } else if (0 < diffL && diffL <= 3) { int[] data = new int[diffL]; int[] tags = fillStackMap(bb.numLocals - prev.numLocals, prev.numLocals, data, bb.localsTypes); writer.appendFrame(offsetDelta, tags, data); return; } } else if (stackTop == 1 && diffL == 0) { TypeData td = bb.stackTypes[0]; if (td == TOP) writer.sameLocals(offsetDelta, StackMapTable.TOP, 0); else writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool)); return; } else if (stackTop == 2 && diffL == 0) { TypeData td = bb.stackTypes[0]; if (td != TOP && td.is2WordType()) { // bb.stackTypes[1] must be TOP. writer.sameLocals(offsetDelta, td.getTypeTag(), td.getTypeData(cpool)); return; } } int[] sdata = new int[stackTop]; int[] stags = fillStackMap(stackTop, 0, sdata, bb.stackTypes); int[] ldata = new int[bb.numLocals]; int[] ltags = fillStackMap(bb.numLocals, 0, ldata, bb.localsTypes); writer.fullFrame(offsetDelta, ltags, ldata, stags, sdata); } private int[] fillStackMap(int num, int offset, int[] data, TypeData[] types) { int realNum = diffSize(types, offset, offset + num); ConstPool cp = cpool; int[] tags = new int[realNum]; int j = 0; for (int i = 0; i < num; i++) { TypeData td = types[offset + i]; if (td == TOP) { tags[j] = StackMapTable.TOP; data[j] = 0; } else { tags[j] = td.getTypeTag(); data[j] = td.getTypeData(cp); if (td.is2WordType()) i++; } j++; } return tags; } private static int stackMapDiff(int oldTdLen, TypeData[] oldTd, int newTdLen, TypeData[] newTd) { int diff = newTdLen - oldTdLen; int len; if (diff > 0) len = oldTdLen; else len = newTdLen; if (stackMapEq(oldTd, newTd, len)) if (diff > 0) return diffSize(newTd, len, newTdLen); else return -diffSize(oldTd, len, oldTdLen); else return -100; } private static boolean stackMapEq(TypeData[] oldTd, TypeData[] newTd, int len) { for (int i = 0; i < len; i++) { TypeData td = oldTd[i]; if (td == TOP) { // the next element to LONG/DOUBLE is TOP. if (newTd[i] != TOP) return false; } else if (!oldTd[i].equals(newTd[i])) return false; } return true; } private static int diffSize(TypeData[] types, int offset, int len) { int num = 0; while (offset < len) { TypeData td = types[offset++]; num++; if (td != TOP && td.is2WordType()) offset++; } return num; } // Phase 3 for J2ME public StackMap toStackMap2(ConstPool cp, TypedBlock[] blocks) { StackMap.Writer writer = new StackMap.Writer(); int n = blocks.length; // should be > 0 boolean[] effective = new boolean[n]; TypedBlock prev = blocks[0]; // Is the first instruction a branch target? effective[0] = prev.incoming > 0; int num = effective[0] ? 1 : 0; for (int i = 1; i < n; i++) { TypedBlock bb = blocks[i]; if (effective[i] = isTarget(bb, blocks[i - 1])) { bb.resetNumLocals(); prev = bb; num++; } } if (num == 0) return null; writer.write16bit(num); for (int i = 0; i < n; i++) if (effective[i]) writeStackFrame(writer, cp, blocks[i].position, blocks[i]); return writer.toStackMap(cp); } private void writeStackFrame(StackMap.Writer writer, ConstPool cp, int offset, TypedBlock tb) { writer.write16bit(offset); writeVerifyTypeInfo(writer, cp, tb.localsTypes, tb.numLocals); writeVerifyTypeInfo(writer, cp, tb.stackTypes, tb.stackTop); } private void writeVerifyTypeInfo(StackMap.Writer writer, ConstPool cp, TypeData[] types, int num) { int numDWord = 0; for (int i = 0; i < num; i++) { TypeData td = types[i]; if (td != null && td.is2WordType()) { numDWord++; i++; } } writer.write16bit(num - numDWord); for (int i = 0; i < num; i++) { TypeData td = types[i]; if (td == TOP) writer.writeVerifyTypeInfo(StackMap.TOP, 0); else { writer.writeVerifyTypeInfo(td.getTypeTag(), td.getTypeData(cp)); if (td.is2WordType()) i++; } } } } javassist-3.12.1.ga/src/main/javassist/bytecode/stackmap/BasicBlock.java0000644000175000017500000003251710630701321026114 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.stackmap; import javassist.bytecode.*; import java.util.HashMap; import java.util.ArrayList; public class BasicBlock { public int position, length; public int incoming; // the number of incoming branches. public BasicBlock[] exit; // null if the block is a leaf. public boolean stop; // true if the block ends with an unconditional jump. public Catch toCatch; protected BasicBlock(int pos) { position = pos; length = 0; incoming = 0; } public static BasicBlock find(BasicBlock[] blocks, int pos) throws BadBytecode { for (int i = 0; i < blocks.length; i++) { int iPos = blocks[i].position; if (iPos <= pos && pos < iPos + blocks[i].length) return blocks[i]; } throw new BadBytecode("no basic block at " + pos); } public static class Catch { Catch next; BasicBlock body; int typeIndex; Catch(BasicBlock b, int i, Catch c) { body = b; typeIndex = i; next = c; } } public String toString() { StringBuffer sbuf = new StringBuffer(); String cname = this.getClass().getName(); int i = cname.lastIndexOf('.'); sbuf.append(i < 0 ? cname : cname.substring(i + 1)); sbuf.append("["); toString2(sbuf); sbuf.append("]"); return sbuf.toString(); } protected void toString2(StringBuffer sbuf) { sbuf.append("pos=").append(position).append(", len=") .append(length).append(", in=").append(incoming) .append(", exit{"); if (exit != null) { for (int i = 0; i < exit.length; i++) sbuf.append(exit[i].position).append(", "); } sbuf.append("}, {"); Catch th = toCatch; while (th != null) { sbuf.append("(").append(th.body.position).append(", ") .append(th.typeIndex).append("), "); th = th.next; } sbuf.append("}"); } static class Mark implements Comparable { int position; BasicBlock block; BasicBlock[] jump; boolean alwaysJmp; // true if a unconditional branch. int size; // 0 unless the mark indicates RETURN etc. Catch catcher; Mark(int p) { position = p; block = null; jump = null; alwaysJmp = false; size = 0; catcher = null; } public int compareTo(Object obj) { if (obj instanceof Mark) { int pos = ((Mark)obj).position; return position - pos; } return -1; } void setJump(BasicBlock[] bb, int s, boolean always) { jump = bb; size = s; alwaysJmp = always; } } public static class Maker { /* Override these two methods if a subclass of BasicBlock must be * instantiated. */ protected BasicBlock makeBlock(int pos) { return new BasicBlock(pos); } protected BasicBlock[] makeArray(int size) { return new BasicBlock[size]; } private BasicBlock[] makeArray(BasicBlock b) { BasicBlock[] array = makeArray(1); array[0] = b; return array; } private BasicBlock[] makeArray(BasicBlock b1, BasicBlock b2) { BasicBlock[] array = makeArray(2); array[0] = b1; array[1] = b2; return array; } public BasicBlock[] make(MethodInfo minfo) throws BadBytecode { CodeAttribute ca = minfo.getCodeAttribute(); if (ca == null) return null; CodeIterator ci = ca.iterator(); return make(ci, 0, ci.getCodeLength(), ca.getExceptionTable()); } public BasicBlock[] make(CodeIterator ci, int begin, int end, ExceptionTable et) throws BadBytecode { HashMap marks = makeMarks(ci, begin, end, et); BasicBlock[] bb = makeBlocks(marks); addCatchers(bb, et); return bb; } /* Branch target */ private Mark makeMark(HashMap table, int pos) { return makeMark0(table, pos, true, true); } /* Branch instruction. * size > 0 */ private Mark makeMark(HashMap table, int pos, BasicBlock[] jump, int size, boolean always) { Mark m = makeMark0(table, pos, false, false); m.setJump(jump, size, always); return m; } private Mark makeMark0(HashMap table, int pos, boolean isBlockBegin, boolean isTarget) { Integer p = new Integer(pos); Mark m = (Mark)table.get(p); if (m == null) { m = new Mark(pos); table.put(p, m); } if (isBlockBegin) { if (m.block == null) m.block = makeBlock(pos); if (isTarget) m.block.incoming++; } return m; } private HashMap makeMarks(CodeIterator ci, int begin, int end, ExceptionTable et) throws BadBytecode { ci.begin(); ci.move(begin); HashMap marks = new HashMap(); while (ci.hasNext()) { int index = ci.next(); if (index >= end) break; int op = ci.byteAt(index); if ((Opcode.IFEQ <= op && op <= Opcode.IF_ACMPNE) || op == Opcode.IFNULL || op == Opcode.IFNONNULL) { Mark to = makeMark(marks, index + ci.s16bitAt(index + 1)); Mark next = makeMark(marks, index + 3); makeMark(marks, index, makeArray(to.block, next.block), 3, false); } else if (Opcode.GOTO <= op && op <= Opcode.LOOKUPSWITCH) switch (op) { case Opcode.GOTO : case Opcode.JSR : makeGotoJsr(marks, index, index + ci.s16bitAt(index + 1), op == Opcode.GOTO, 3); break; case Opcode.RET : makeMark(marks, index, null, 1, true); break; case Opcode.TABLESWITCH : { int pos = (index & ~3) + 4; int low = ci.s32bitAt(pos + 4); int high = ci.s32bitAt(pos + 8); int ncases = high - low + 1; BasicBlock[] to = makeArray(ncases + 1); to[0] = makeMark(marks, index + ci.s32bitAt(pos)).block; // default branch target int p = pos + 12; int n = p + ncases * 4; int k = 1; while (p < n) { to[k++] = makeMark(marks, index + ci.s32bitAt(p)).block; p += 4; } makeMark(marks, index, to, n - index, true); break; } case Opcode.LOOKUPSWITCH : { int pos = (index & ~3) + 4; int ncases = ci.s32bitAt(pos + 4); BasicBlock[] to = makeArray(ncases + 1); to[0] = makeMark(marks, index + ci.s32bitAt(pos)).block; // default branch target int p = pos + 8 + 4; int n = p + ncases * 8 - 4; int k = 1; while (p < n) { to[k++] = makeMark(marks, index + ci.s32bitAt(p)).block; p += 8; } makeMark(marks, index, to, n - index, true); break; } } else if ((Opcode.IRETURN <= op && op <= Opcode.RETURN) || op == Opcode.ATHROW) makeMark(marks, index, null, 1, true); else if (op == Opcode.GOTO_W || op == Opcode.JSR_W) makeGotoJsr(marks, index, index + ci.s32bitAt(index + 1), op == Opcode.GOTO_W, 5); else if (op == Opcode.WIDE && ci.byteAt(index + 1) == Opcode.RET) makeMark(marks, index, null, 1, true); } if (et != null) { int i = et.size(); while (--i >= 0) { makeMark0(marks, et.startPc(i), true, false); makeMark(marks, et.handlerPc(i)); } } return marks; } private void makeGotoJsr(HashMap marks, int pos, int target, boolean isGoto, int size) { Mark to = makeMark(marks, target); BasicBlock[] jumps; if (isGoto) jumps = makeArray(to.block); else { Mark next = makeMark(marks, pos + size); jumps = makeArray(to.block, next.block); } makeMark(marks, pos, jumps, size, isGoto); } private BasicBlock[] makeBlocks(HashMap markTable) { Mark[] marks = (Mark[])markTable.values() .toArray(new Mark[markTable.size()]); java.util.Arrays.sort(marks); ArrayList blocks = new ArrayList(); int i = 0; BasicBlock prev; if (marks.length > 0 && marks[0].position == 0 && marks[0].block != null) prev = getBBlock(marks[i++]); else prev = makeBlock(0); blocks.add(prev); while (i < marks.length) { Mark m = marks[i++]; BasicBlock bb = getBBlock(m); if (bb == null) { // the mark indicates a branch instruction if (prev.length > 0) { // the previous mark already has exits. prev = makeBlock(prev.position + prev.length); blocks.add(prev); } prev.length = m.position + m.size - prev.position; prev.exit = m.jump; prev.stop = m.alwaysJmp; } else { // the mark indicates a branch target if (prev.length == 0) { prev.length = m.position - prev.position; bb.incoming++; prev.exit = makeArray(bb); } else { // the previous mark already has exits. int prevPos = prev.position; if (prevPos + prev.length < m.position) { prev = makeBlock(prevPos + prev.length); prev.length = m.position - prevPos; // the incoming flow from dead code is not counted // bb.incoming++; prev.exit = makeArray(bb); } } blocks.add(bb); prev = bb; } } return (BasicBlock[])blocks.toArray(makeArray(blocks.size())); } private static BasicBlock getBBlock(Mark m) { BasicBlock b = m.block; if (b != null && m.size > 0) { b.exit = m.jump; b.length = m.size; b.stop = m.alwaysJmp; } return b; } private void addCatchers(BasicBlock[] blocks, ExceptionTable et) throws BadBytecode { if (et == null) return; int i = et.size(); while (--i >= 0) { BasicBlock handler = find(blocks, et.handlerPc(i)); int start = et.startPc(i); int end = et.endPc(i); int type = et.catchType(i); handler.incoming--; for (int k = 0; k < blocks.length; k++) { BasicBlock bb = blocks[k]; int iPos = bb.position; if (start <= iPos && iPos < end) { bb.toCatch = new Catch(handler, type, bb.toCatch); handler.incoming++; } } } } } } javassist-3.12.1.ga/src/main/javassist/bytecode/stackmap/TypeData.java0000644000175000017500000003624110630701321025631 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.stackmap; import javassist.ClassPool; import javassist.CtClass; import javassist.NotFoundException; import javassist.bytecode.ConstPool; import javassist.bytecode.StackMapTable; import javassist.bytecode.BadBytecode; import java.util.ArrayList; public abstract class TypeData { /* Memo: * array type is a subtype of Cloneable and Serializable */ protected TypeData() {} public abstract void merge(TypeData neighbor); /** * Sets the type name of this object type. If the given type name is * a subclass of the current type name, then the given name becomes * the name of this object type. * * @param className dot-separated name unless the type is an array type. */ static void setType(TypeData td, String className, ClassPool cp) throws BadBytecode { if (td == TypeTag.TOP) throw new BadBytecode("unset variable"); else td.setType(className, cp); } public abstract boolean equals(Object obj); public abstract int getTypeTag(); public abstract int getTypeData(ConstPool cp); /* * See UninitData.getSelf(). */ public TypeData getSelf() { return this; } /* An operand value is copied when it is stored in a * local variable. */ public abstract TypeData copy(); public abstract boolean isObjectType(); public boolean is2WordType() { return false; } public boolean isNullType() { return false; } public abstract String getName() throws BadBytecode; protected abstract void setType(String s, ClassPool cp) throws BadBytecode; public abstract void evalExpectedType(ClassPool cp) throws BadBytecode; public abstract String getExpected() throws BadBytecode; /** * Primitive types. */ protected static class BasicType extends TypeData { private String name; private int typeTag; public BasicType(String type, int tag) { name = type; typeTag = tag; } public void merge(TypeData neighbor) {} public boolean equals(Object obj) { return this == obj; } public int getTypeTag() { return typeTag; } public int getTypeData(ConstPool cp) { return 0; } public boolean isObjectType() { return false; } public boolean is2WordType() { return typeTag == StackMapTable.LONG || typeTag == StackMapTable.DOUBLE; } public TypeData copy() { return this; } public void evalExpectedType(ClassPool cp) throws BadBytecode {} public String getExpected() throws BadBytecode { return name; } public String getName() { return name; } protected void setType(String s, ClassPool cp) throws BadBytecode { throw new BadBytecode("conflict: " + name + " and " + s); } public String toString() { return name; } } protected static abstract class TypeName extends TypeData { protected ArrayList equivalences; protected String expectedName; private CtClass cache; private boolean evalDone; protected TypeName() { equivalences = new ArrayList(); equivalences.add(this); expectedName = null; cache = null; evalDone = false; } public void merge(TypeData neighbor) { if (this == neighbor) return; if (!(neighbor instanceof TypeName)) return; // neighbor might be UninitData TypeName neighbor2 = (TypeName)neighbor; ArrayList list = equivalences; ArrayList list2 = neighbor2.equivalences; if (list == list2) return; int n = list2.size(); for (int i = 0; i < n; i++) { TypeName tn = (TypeName)list2.get(i); add(list, tn); tn.equivalences = list; } } private static void add(ArrayList list, TypeData td) { int n = list.size(); for (int i = 0; i < n; i++) if (list.get(i) == td) return; list.add(td); } /* NullType overrides this method. */ public int getTypeTag() { return StackMapTable.OBJECT; } public int getTypeData(ConstPool cp) { String type; try { type = getExpected(); } catch (BadBytecode e) { throw new RuntimeException("fatal error: ", e); } return getTypeData2(cp, type); } /* NullType overrides this method. */ protected int getTypeData2(ConstPool cp, String type) { return cp.addClassInfo(type); } public boolean equals(Object obj) { if (obj instanceof TypeName) { try { TypeName tn = (TypeName)obj; return getExpected().equals(tn.getExpected()); } catch (BadBytecode e) {} } return false; } public boolean isObjectType() { return true; } protected void setType(String typeName, ClassPool cp) throws BadBytecode { if (update(cp, expectedName, typeName)) expectedName = typeName; } public void evalExpectedType(ClassPool cp) throws BadBytecode { if (this.evalDone) return; ArrayList equiv = this.equivalences; int n = equiv.size(); String name = evalExpectedType2(equiv, n); if (name == null) { name = this.expectedName; for (int i = 0; i < n; i++) { TypeData td = (TypeData)equiv.get(i); if (td instanceof TypeName) { TypeName tn = (TypeName)td; if (update(cp, name, tn.expectedName)) name = tn.expectedName; } } } for (int i = 0; i < n; i++) { TypeData td = (TypeData)equiv.get(i); if (td instanceof TypeName) { TypeName tn = (TypeName)td; tn.expectedName = name; tn.cache = null; tn.evalDone = true; } } } private String evalExpectedType2(ArrayList equiv, int n) throws BadBytecode { String origName = null; for (int i = 0; i < n; i++) { TypeData td = (TypeData)equiv.get(i); if (!td.isNullType()) if (origName == null) origName = td.getName(); else if (!origName.equals(td.getName())) return null; } return origName; } protected boolean isTypeName() { return true; } private boolean update(ClassPool cp, String oldName, String typeName) throws BadBytecode { if (typeName == null) return false; else if (oldName == null) return true; else if (oldName.equals(typeName)) return false; else if (typeName.charAt(0) == '[' && oldName.equals("[Ljava.lang.Object;")) { /* this rule is not correct but Tracer class sets the type of the operand of arraylength to java.lang.Object[]. Thus, int[] etc. must be a subtype of java.lang.Object[]. */ return true; } try { if (cache == null) cache = cp.get(oldName); CtClass cache2 = cp.get(typeName); if (cache2.subtypeOf(cache)) { cache = cache2; return true; } else return false; } catch (NotFoundException e) { throw new BadBytecode("cannot find " + e.getMessage()); } } /* See also NullType.getExpected(). */ public String getExpected() throws BadBytecode { ArrayList equiv = equivalences; if (equiv.size() == 1) return getName(); else { String en = expectedName; if (en == null) return "java.lang.Object"; else return en; } } public String toString() { try { String en = expectedName; if (en != null) return en; String name = getName(); if (equivalences.size() == 1) return name; else return name + "?"; } catch (BadBytecode e) { return "<" + e.getMessage() + ">"; } } } /** * Type data for OBJECT. */ public static class ClassName extends TypeName { private String name; // dot separated. null if this object is a copy of another. public ClassName(String n) { name = n; } public TypeData copy() { return new ClassName(name); } public String getName() { // never returns null. return name; } } /** * Type data for NULL or OBJECT. * The types represented by the instances of this class are * initially NULL but will be OBJECT. */ public static class NullType extends ClassName { public NullType() { super("null"); // type name } public TypeData copy() { return new NullType(); } public boolean isNullType() { return true; } public int getTypeTag() { try { if ("null".equals(getExpected())) return StackMapTable.NULL; else return super.getTypeTag(); } catch (BadBytecode e) { throw new RuntimeException("fatal error: ", e); } } protected int getTypeData2(ConstPool cp, String type) { if ("null".equals(type)) return 0; else return super.getTypeData2(cp, type); } public String getExpected() throws BadBytecode { String en = expectedName; if (en == null) { // ArrayList equiv = equivalences; // if (equiv.size() == 1) // return getName(); // else return "java.lang.Object"; } else return en; } } /** * Type data for OBJECT if the type is an object type and is * derived as an element type from an array type by AALOAD. */ public static class ArrayElement extends TypeName { TypeData array; public ArrayElement(TypeData a) { // a is never null array = a; } public TypeData copy() { return new ArrayElement(array); } protected void setType(String typeName, ClassPool cp) throws BadBytecode { super.setType(typeName, cp); array.setType(getArrayType(typeName), cp); } public String getName() throws BadBytecode { String name = array.getName(); if (name.length() > 1 && name.charAt(0) == '[') { char c = name.charAt(1); if (c == 'L') return name.substring(2, name.length() - 1).replace('/', '.'); else if (c == '[') return name.substring(1); } throw new BadBytecode("bad array type for AALOAD: " + name); } public static String getArrayType(String elementType) { if (elementType.charAt(0) == '[') return "[" + elementType; else return "[L" + elementType.replace('.', '/') + ";"; } public static String getElementType(String arrayType) { char c = arrayType.charAt(1); if (c == 'L') return arrayType.substring(2, arrayType.length() - 1).replace('/', '.'); else if (c == '[') return arrayType.substring(1); else return arrayType; } } /** * Type data for UNINIT. */ public static class UninitData extends TypeData { String className; int offset; boolean initialized; UninitData(int offset, String className) { this.className = className; this.offset = offset; this.initialized = false; } public void merge(TypeData neighbor) {} public int getTypeTag() { return StackMapTable.UNINIT; } public int getTypeData(ConstPool cp) { return offset; } public boolean equals(Object obj) { if (obj instanceof UninitData) { UninitData ud = (UninitData)obj; return offset == ud.offset && className.equals(ud.className); } else return false; } public TypeData getSelf() { if (initialized) return copy(); else return this; } public TypeData copy() { return new ClassName(className); } public boolean isObjectType() { return true; } protected void setType(String typeName, ClassPool cp) throws BadBytecode { initialized = true; } public void evalExpectedType(ClassPool cp) throws BadBytecode {} public String getName() { return className; } public String getExpected() { return className; } public String toString() { return "uninit:" + className + "@" + offset; } } public static class UninitThis extends UninitData { UninitThis(String className) { super(-1, className); } public int getTypeTag() { return StackMapTable.THIS; } public int getTypeData(ConstPool cp) { return 0; } public boolean equals(Object obj) { return obj instanceof UninitThis; } public String toString() { return "uninit:this"; } } } javassist-3.12.1.ga/src/main/javassist/bytecode/ClassFilePrinter.java0000644000175000017500000001267611366572525025555 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.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 ClassFilePrinter { /** * 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("major: " + cf.major + ", minor: " + cf.minor + " modifiers: " + Integer.toHexString(cf.getAccessFlags())); out.println(Modifier.toString(mod) + " class " + cf.getName() + " extends " + cf.getSuperclass()); String[] infs = cf.getInterfaces(); if (infs != null && infs.length > 0) { out.print(" implements "); out.print(infs[0]); for (int i = 1; i < infs.length; ++i) out.print(", " + infs[i]); out.println(); } out.println(); 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, 'f'); } 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, 'm'); out.println(); } out.println(); printAttributes(cf.getAttributes(), out, 'c'); } static void printAttributes(List list, PrintWriter out, char kind) { 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(""); printAttributes(ca.getAttributes(), out, kind); out.println(""); } else if (ai instanceof StackMapTable) { out.println(""); StackMapTable.Printer.print((StackMapTable)ai, out); out.println(""); } else if (ai instanceof StackMap) { out.println(""); ((StackMap)ai).print(out); out.println(""); } else if (ai instanceof SignatureAttribute) { SignatureAttribute sa = (SignatureAttribute)ai; String sig = sa.getSignature(); out.println("signature: " + sig); try { String s; if (kind == 'c') s = SignatureAttribute.toClassSignature(sig).toString(); else if (kind == 'm') s = SignatureAttribute.toMethodSignature(sig).toString(); else s = SignatureAttribute.toFieldSignature(sig).toString(); out.println(" " + s); } catch (BadBytecode e) { out.println(" syntax error"); } } else out.println("attribute: " + ai.getName() + " (" + ai.get().length + " byte): " + ai.getClass().getName()); } } } javassist-3.12.1.ga/src/main/javassist/bytecode/LocalVariableTypeAttribute.java0000644000175000017500000000363511223113475027550 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.IOException; import java.util.Map; /** * LocalVariableTypeTable_attribute. * * @since 3.11 */ public class LocalVariableTypeAttribute extends LocalVariableAttribute { /** * The name of the attribute "LocalVariableTypeTable". */ public static final String tag = LocalVariableAttribute.typeTag; /** * Constructs an empty LocalVariableTypeTable. */ public LocalVariableTypeAttribute(ConstPool cp) { super(cp, tag, new byte[2]); ByteArray.write16bit(0, info, 0); } LocalVariableTypeAttribute(ConstPool cp, int n, DataInputStream in) throws IOException { super(cp, n, in); } private LocalVariableTypeAttribute(ConstPool cp, byte[] dest) { super(cp, tag, dest); } String renameEntry(String desc, String oldname, String newname) { return SignatureAttribute.renameClass(desc, oldname, newname); } String renameEntry(String desc, Map classnames) { return SignatureAttribute.renameClass(desc, classnames); } LocalVariableAttribute makeThisAttr(ConstPool cp, byte[] dest) { return new LocalVariableTypeAttribute(cp, dest); } } javassist-3.12.1.ga/src/main/javassist/bytecode/ParameterAnnotationsAttribute.java0000644000175000017500000001354111262447632030351 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.util.Map; import java.io.IOException; import java.io.DataInputStream; import java.io.ByteArrayOutputStream; import javassist.bytecode.AnnotationsAttribute.Copier; import javassist.bytecode.AnnotationsAttribute.Parser; import javassist.bytecode.annotation.*; /** * A class representing RuntimeVisibleAnnotations_attribute and * RuntimeInvisibleAnnotations_attribute. * *

    To obtain an ParameterAnnotationAttribute object, invoke * getAttribute(ParameterAnnotationsAttribute.invisibleTag) * in MethodInfo. * The obtained attribute is a * runtime invisible annotations attribute. * If the parameter is * ParameterAnnotationAttribute.visibleTag, then the obtained * attribute is a runtime visible one. */ public class ParameterAnnotationsAttribute extends AttributeInfo { /** * The name of the RuntimeVisibleParameterAnnotations * attribute. */ public static final String visibleTag = "RuntimeVisibleParameterAnnotations"; /** * The name of the RuntimeInvisibleParameterAnnotations * attribute. */ public static final String invisibleTag = "RuntimeInvisibleParameterAnnotations"; /** * Constructs * a Runtime(In)VisibleParameterAnnotations_attribute. * * @param cp constant pool * @param attrname attribute name (visibleTag or * invisibleTag). * @param info the contents of this attribute. It does not * include attribute_name_index or * attribute_length. */ public ParameterAnnotationsAttribute(ConstPool cp, String attrname, byte[] info) { super(cp, attrname, info); } /** * Constructs an empty * Runtime(In)VisibleParameterAnnotations_attribute. * A new annotation can be later added to the created attribute * by setAnnotations(). * * @param cp constant pool * @param attrname attribute name (visibleTag or * invisibleTag). * @see #setAnnotations(Annotation[][]) */ public ParameterAnnotationsAttribute(ConstPool cp, String attrname) { this(cp, attrname, new byte[] { 0 }); } /** * @param n the attribute name. */ ParameterAnnotationsAttribute(ConstPool cp, int n, DataInputStream in) throws IOException { super(cp, n, in); } /** * Returns num_parameters. */ public int numParameters() { return info[0] & 0xff; } /** * Copies this attribute and returns a new copy. */ public AttributeInfo copy(ConstPool newCp, Map classnames) { Copier copier = new Copier(info, constPool, newCp, classnames); try { copier.parameters(); return new ParameterAnnotationsAttribute(newCp, getName(), copier.close()); } catch (Exception e) { throw new RuntimeException(e.toString()); } } /** * Parses the annotations and returns a data structure representing * that parsed annotations. Note that changes of the node values of the * returned tree are not reflected on the annotations represented by * this object unless the tree is copied back to this object by * setAnnotations(). * * @return Each element of the returned array represents an array of * annotations that are associated with each method parameter. * * @see #setAnnotations(Annotation[][]) */ public Annotation[][] getAnnotations() { try { return new Parser(info, constPool).parseParameters(); } catch (Exception e) { throw new RuntimeException(e.toString()); } } /** * Changes the annotations represented by this object according to * the given array of Annotation objects. * * @param params the data structure representing the * new annotations. Every element of this array * is an array of Annotation and * it represens annotations of each method parameter. */ public void setAnnotations(Annotation[][] params) { ByteArrayOutputStream output = new ByteArrayOutputStream(); AnnotationsWriter writer = new AnnotationsWriter(output, constPool); try { int n = params.length; writer.numParameters(n); for (int i = 0; i < n; ++i) { Annotation[] anno = params[i]; writer.numAnnotations(anno.length); for (int j = 0; j < anno.length; ++j) anno[j].write(writer); } writer.close(); } catch (IOException e) { throw new RuntimeException(e); // should never reach here. } set(output.toByteArray()); } } javassist-3.12.1.ga/src/main/javassist/bytecode/FieldInfo.java0000644000175000017500000001650411366006523024163 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.List; import java.util.ArrayList; /** * field_info structure. * * @see javassist.CtField#getFieldInfo() */ public final class FieldInfo { ConstPool constPool; int accessFlags; int name; String cachedName; String cachedType; int descriptor; ArrayList attribute; // may be null. private FieldInfo(ConstPool cp) { constPool = cp; accessFlags = 0; attribute = null; } /** * Constructs a field_info 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); cachedName = fieldName; descriptor = cp.addUtf8Info(desc); } FieldInfo(ConstPool cp, DataInputStream in) throws IOException { this(cp); read(in); } /** * Returns a string representation of the object. */ public String toString() { return getName() + " " + getDescriptor(); } /** * Copies all constant pool items to a given new constant pool * and replaces the original items with the new ones. * This is used for garbage collecting the items of removed fields * and methods. * * @param cp the destination */ void compact(ConstPool cp) { name = cp.addUtf8Info(getName()); descriptor = cp.addUtf8Info(getDescriptor()); attribute = AttributeInfo.copyAll(attribute, cp); constPool = cp; } void prune(ConstPool cp) { ArrayList newAttributes = new ArrayList(); AttributeInfo invisibleAnnotations = getAttribute(AnnotationsAttribute.invisibleTag); if (invisibleAnnotations != null) { invisibleAnnotations = invisibleAnnotations.copy(cp, null); newAttributes.add(invisibleAnnotations); } AttributeInfo visibleAnnotations = getAttribute(AnnotationsAttribute.visibleTag); if (visibleAnnotations != null) { visibleAnnotations = visibleAnnotations.copy(cp, null); newAttributes.add(visibleAnnotations); } AttributeInfo signature = getAttribute(SignatureAttribute.tag); if (signature != null) { signature = signature.copy(cp, null); newAttributes.add(signature); } int index = getConstantValue(); if (index != 0) { index = constPool.copy(index, cp, null); newAttributes.add(new ConstantAttribute(cp, index)); } attribute = newAttributes; name = cp.addUtf8Info(getName()); descriptor = cp.addUtf8Info(getDescriptor()); constPool = cp; } /** * Returns the constant pool table used * by this field_info. */ public ConstPool getConstPool() { return constPool; } /** * Returns the field name. */ public String getName() { if (cachedName == null) cachedName = constPool.getUtf8Info(name); return cachedName; } /** * Sets the field name. */ public void setName(String newName) { name = constPool.addUtf8Info(newName); cachedName = 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); } /** * Finds a ConstantValue attribute and returns the index into * the constant_pool table. * * @return 0 if a ConstantValue attribute is not found. */ public int getConstantValue() { if ((accessFlags & AccessFlag.STATIC) == 0) return 0; ConstantAttribute attr = (ConstantAttribute)getAttribute(ConstantAttribute.tag); if (attr == null) return 0; else return attr.getConstantValue(); } /** * Returns all the attributes. The returned List object * is shared with this object. If you add a new attribute to the list, * the attribute is also added to the field represented by this * object. If you remove an attribute from the list, it is also removed * from the field. * * @return a list of AttributeInfo objects. * @see AttributeInfo */ public List getAttributes() { if (attribute == null) attribute = new ArrayList(); return attribute; } /** * Returns the attribute with the specified name. * It returns null if the specified attribute is not found. * * @param name attribute name * @see #getAttributes() */ 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. * * @see #getAttributes() */ public void addAttribute(AttributeInfo info) { if (attribute == null) attribute = new ArrayList(); 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 ArrayList(); 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); } } } javassist-3.12.1.ga/src/main/javassist/bytecode/analysis/0000755000175000017500000000000011637463367023314 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/bytecode/analysis/Executor.java0000644000175000017500000010661311025546046025747 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.analysis; import javassist.ClassPool; import javassist.CtClass; import javassist.NotFoundException; import javassist.bytecode.BadBytecode; import javassist.bytecode.CodeIterator; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.MethodInfo; import javassist.bytecode.Opcode; /** * Executor is responsible for modeling the effects of a JVM instruction on a frame. * * @author Jason T. Greene */ public class Executor implements Opcode { private final ConstPool constPool; private final ClassPool classPool; private final Type STRING_TYPE; private final Type CLASS_TYPE; private final Type THROWABLE_TYPE; private int lastPos; public Executor(ClassPool classPool, ConstPool constPool) { this.constPool = constPool; this.classPool = classPool; try { STRING_TYPE = getType("java.lang.String"); CLASS_TYPE = getType("java.lang.Class"); THROWABLE_TYPE = getType("java.lang.Throwable"); } catch (Exception e) { throw new RuntimeException(e); } } /** * Execute the instruction, modeling the effects on the specified frame and subroutine. * If a subroutine is passed, the access flags will be modified if this instruction accesses * the local variable table. * * @param method the method containing the instruction * @param pos the position of the instruction in the method * @param iter the code iterator used to find the instruction * @param frame the frame to modify to represent the result of the instruction * @param subroutine the optional subroutine this instruction belongs to. * @throws BadBytecode if the bytecode violates the jvm spec */ public void execute(MethodInfo method, int pos, CodeIterator iter, Frame frame, Subroutine subroutine) throws BadBytecode { this.lastPos = pos; int opcode = iter.byteAt(pos); // Declared opcode in order switch (opcode) { case NOP: break; case ACONST_NULL: frame.push(Type.UNINIT); break; case ICONST_M1: case ICONST_0: case ICONST_1: case ICONST_2: case ICONST_3: case ICONST_4: case ICONST_5: frame.push(Type.INTEGER); break; case LCONST_0: case LCONST_1: frame.push(Type.LONG); frame.push(Type.TOP); break; case FCONST_0: case FCONST_1: case FCONST_2: frame.push(Type.FLOAT); break; case DCONST_0: case DCONST_1: frame.push(Type.DOUBLE); frame.push(Type.TOP); break; case BIPUSH: case SIPUSH: frame.push(Type.INTEGER); break; case LDC: evalLDC(iter.byteAt(pos + 1), frame); break; case LDC_W : case LDC2_W : evalLDC(iter.u16bitAt(pos + 1), frame); break; case ILOAD: evalLoad(Type.INTEGER, iter.byteAt(pos + 1), frame, subroutine); break; case LLOAD: evalLoad(Type.LONG, iter.byteAt(pos + 1), frame, subroutine); break; case FLOAD: evalLoad(Type.FLOAT, iter.byteAt(pos + 1), frame, subroutine); break; case DLOAD: evalLoad(Type.DOUBLE, iter.byteAt(pos + 1), frame, subroutine); break; case ALOAD: evalLoad(Type.OBJECT, iter.byteAt(pos + 1), frame, subroutine); break; case ILOAD_0: case ILOAD_1: case ILOAD_2: case ILOAD_3: evalLoad(Type.INTEGER, opcode - ILOAD_0, frame, subroutine); break; case LLOAD_0: case LLOAD_1: case LLOAD_2: case LLOAD_3: evalLoad(Type.LONG, opcode - LLOAD_0, frame, subroutine); break; case FLOAD_0: case FLOAD_1: case FLOAD_2: case FLOAD_3: evalLoad(Type.FLOAT, opcode - FLOAD_0, frame, subroutine); break; case DLOAD_0: case DLOAD_1: case DLOAD_2: case DLOAD_3: evalLoad(Type.DOUBLE, opcode - DLOAD_0, frame, subroutine); break; case ALOAD_0: case ALOAD_1: case ALOAD_2: case ALOAD_3: evalLoad(Type.OBJECT, opcode - ALOAD_0, frame, subroutine); break; case IALOAD: evalArrayLoad(Type.INTEGER, frame); break; case LALOAD: evalArrayLoad(Type.LONG, frame); break; case FALOAD: evalArrayLoad(Type.FLOAT, frame); break; case DALOAD: evalArrayLoad(Type.DOUBLE, frame); break; case AALOAD: evalArrayLoad(Type.OBJECT, frame); break; case BALOAD: case CALOAD: case SALOAD: evalArrayLoad(Type.INTEGER, frame); break; case ISTORE: evalStore(Type.INTEGER, iter.byteAt(pos + 1), frame, subroutine); break; case LSTORE: evalStore(Type.LONG, iter.byteAt(pos + 1), frame, subroutine); break; case FSTORE: evalStore(Type.FLOAT, iter.byteAt(pos + 1), frame, subroutine); break; case DSTORE: evalStore(Type.DOUBLE, iter.byteAt(pos + 1), frame, subroutine); break; case ASTORE: evalStore(Type.OBJECT, iter.byteAt(pos + 1), frame, subroutine); break; case ISTORE_0: case ISTORE_1: case ISTORE_2: case ISTORE_3: evalStore(Type.INTEGER, opcode - ISTORE_0, frame, subroutine); break; case LSTORE_0: case LSTORE_1: case LSTORE_2: case LSTORE_3: evalStore(Type.LONG, opcode - LSTORE_0, frame, subroutine); break; case FSTORE_0: case FSTORE_1: case FSTORE_2: case FSTORE_3: evalStore(Type.FLOAT, opcode - FSTORE_0, frame, subroutine); break; case DSTORE_0: case DSTORE_1: case DSTORE_2: case DSTORE_3: evalStore(Type.DOUBLE, opcode - DSTORE_0, frame, subroutine); break; case ASTORE_0: case ASTORE_1: case ASTORE_2: case ASTORE_3: evalStore(Type.OBJECT, opcode - ASTORE_0, frame, subroutine); break; case IASTORE: evalArrayStore(Type.INTEGER, frame); break; case LASTORE: evalArrayStore(Type.LONG, frame); break; case FASTORE: evalArrayStore(Type.FLOAT, frame); break; case DASTORE: evalArrayStore(Type.DOUBLE, frame); break; case AASTORE: evalArrayStore(Type.OBJECT, frame); break; case BASTORE: case CASTORE: case SASTORE: evalArrayStore(Type.INTEGER, frame); break; case POP: if (frame.pop() == Type.TOP) throw new BadBytecode("POP can not be used with a category 2 value, pos = " + pos); break; case POP2: frame.pop(); frame.pop(); break; case DUP: { Type type = frame.peek(); if (type == Type.TOP) throw new BadBytecode("DUP can not be used with a category 2 value, pos = " + pos); frame.push(frame.peek()); break; } case DUP_X1: case DUP_X2: { Type type = frame.peek(); if (type == Type.TOP) throw new BadBytecode("DUP can not be used with a category 2 value, pos = " + pos); int end = frame.getTopIndex(); int insert = end - (opcode - DUP_X1) - 1; frame.push(type); while (end > insert) { frame.setStack(end, frame.getStack(end - 1)); end--; } frame.setStack(insert, type); break; } case DUP2: frame.push(frame.getStack(frame.getTopIndex() - 1)); frame.push(frame.getStack(frame.getTopIndex() - 1)); break; case DUP2_X1: case DUP2_X2: { int end = frame.getTopIndex(); int insert = end - (opcode - DUP2_X1) - 1; Type type1 = frame.getStack(frame.getTopIndex() - 1); Type type2 = frame.peek(); frame.push(type1); frame.push(type2); while (end > insert) { frame.setStack(end, frame.getStack(end - 2)); end--; } frame.setStack(insert, type2); frame.setStack(insert - 1, type1); break; } case SWAP: { Type type1 = frame.pop(); Type type2 = frame.pop(); if (type1.getSize() == 2 || type2.getSize() == 2) throw new BadBytecode("Swap can not be used with category 2 values, pos = " + pos); frame.push(type1); frame.push(type2); break; } // Math case IADD: evalBinaryMath(Type.INTEGER, frame); break; case LADD: evalBinaryMath(Type.LONG, frame); break; case FADD: evalBinaryMath(Type.FLOAT, frame); break; case DADD: evalBinaryMath(Type.DOUBLE, frame); break; case ISUB: evalBinaryMath(Type.INTEGER, frame); break; case LSUB: evalBinaryMath(Type.LONG, frame); break; case FSUB: evalBinaryMath(Type.FLOAT, frame); break; case DSUB: evalBinaryMath(Type.DOUBLE, frame); break; case IMUL: evalBinaryMath(Type.INTEGER, frame); break; case LMUL: evalBinaryMath(Type.LONG, frame); break; case FMUL: evalBinaryMath(Type.FLOAT, frame); break; case DMUL: evalBinaryMath(Type.DOUBLE, frame); break; case IDIV: evalBinaryMath(Type.INTEGER, frame); break; case LDIV: evalBinaryMath(Type.LONG, frame); break; case FDIV: evalBinaryMath(Type.FLOAT, frame); break; case DDIV: evalBinaryMath(Type.DOUBLE, frame); break; case IREM: evalBinaryMath(Type.INTEGER, frame); break; case LREM: evalBinaryMath(Type.LONG, frame); break; case FREM: evalBinaryMath(Type.FLOAT, frame); break; case DREM: evalBinaryMath(Type.DOUBLE, frame); break; // Unary case INEG: verifyAssignable(Type.INTEGER, simplePeek(frame)); break; case LNEG: verifyAssignable(Type.LONG, simplePeek(frame)); break; case FNEG: verifyAssignable(Type.FLOAT, simplePeek(frame)); break; case DNEG: verifyAssignable(Type.DOUBLE, simplePeek(frame)); break; // Shifts case ISHL: evalShift(Type.INTEGER, frame); break; case LSHL: evalShift(Type.LONG, frame); break; case ISHR: evalShift(Type.INTEGER, frame); break; case LSHR: evalShift(Type.LONG, frame); break; case IUSHR: evalShift(Type.INTEGER,frame); break; case LUSHR: evalShift(Type.LONG, frame); break; // Bitwise Math case IAND: evalBinaryMath(Type.INTEGER, frame); break; case LAND: evalBinaryMath(Type.LONG, frame); break; case IOR: evalBinaryMath(Type.INTEGER, frame); break; case LOR: evalBinaryMath(Type.LONG, frame); break; case IXOR: evalBinaryMath(Type.INTEGER, frame); break; case LXOR: evalBinaryMath(Type.LONG, frame); break; case IINC: { int index = iter.byteAt(pos + 1); verifyAssignable(Type.INTEGER, frame.getLocal(index)); access(index, Type.INTEGER, subroutine); break; } // Conversion case I2L: verifyAssignable(Type.INTEGER, simplePop(frame)); simplePush(Type.LONG, frame); break; case I2F: verifyAssignable(Type.INTEGER, simplePop(frame)); simplePush(Type.FLOAT, frame); break; case I2D: verifyAssignable(Type.INTEGER, simplePop(frame)); simplePush(Type.DOUBLE, frame); break; case L2I: verifyAssignable(Type.LONG, simplePop(frame)); simplePush(Type.INTEGER, frame); break; case L2F: verifyAssignable(Type.LONG, simplePop(frame)); simplePush(Type.FLOAT, frame); break; case L2D: verifyAssignable(Type.LONG, simplePop(frame)); simplePush(Type.DOUBLE, frame); break; case F2I: verifyAssignable(Type.FLOAT, simplePop(frame)); simplePush(Type.INTEGER, frame); break; case F2L: verifyAssignable(Type.FLOAT, simplePop(frame)); simplePush(Type.LONG, frame); break; case F2D: verifyAssignable(Type.FLOAT, simplePop(frame)); simplePush(Type.DOUBLE, frame); break; case D2I: verifyAssignable(Type.DOUBLE, simplePop(frame)); simplePush(Type.INTEGER, frame); break; case D2L: verifyAssignable(Type.DOUBLE, simplePop(frame)); simplePush(Type.LONG, frame); break; case D2F: verifyAssignable(Type.DOUBLE, simplePop(frame)); simplePush(Type.FLOAT, frame); break; case I2B: case I2C: case I2S: verifyAssignable(Type.INTEGER, frame.peek()); break; case LCMP: verifyAssignable(Type.LONG, simplePop(frame)); verifyAssignable(Type.LONG, simplePop(frame)); frame.push(Type.INTEGER); break; case FCMPL: case FCMPG: verifyAssignable(Type.FLOAT, simplePop(frame)); verifyAssignable(Type.FLOAT, simplePop(frame)); frame.push(Type.INTEGER); break; case DCMPL: case DCMPG: verifyAssignable(Type.DOUBLE, simplePop(frame)); verifyAssignable(Type.DOUBLE, simplePop(frame)); frame.push(Type.INTEGER); break; // Control flow case IFEQ: case IFNE: case IFLT: case IFGE: case IFGT: case IFLE: verifyAssignable(Type.INTEGER, simplePop(frame)); break; case IF_ICMPEQ: case IF_ICMPNE: case IF_ICMPLT: case IF_ICMPGE: case IF_ICMPGT: case IF_ICMPLE: verifyAssignable(Type.INTEGER, simplePop(frame)); verifyAssignable(Type.INTEGER, simplePop(frame)); break; case IF_ACMPEQ: case IF_ACMPNE: verifyAssignable(Type.OBJECT, simplePop(frame)); verifyAssignable(Type.OBJECT, simplePop(frame)); break; case GOTO: break; case JSR: frame.push(Type.RETURN_ADDRESS); break; case RET: verifyAssignable(Type.RETURN_ADDRESS, frame.getLocal(iter.byteAt(pos + 1))); break; case TABLESWITCH: case LOOKUPSWITCH: case IRETURN: verifyAssignable(Type.INTEGER, simplePop(frame)); break; case LRETURN: verifyAssignable(Type.LONG, simplePop(frame)); break; case FRETURN: verifyAssignable(Type.FLOAT, simplePop(frame)); break; case DRETURN: verifyAssignable(Type.DOUBLE, simplePop(frame)); break; case ARETURN: try { CtClass returnType = Descriptor.getReturnType(method.getDescriptor(), classPool); verifyAssignable(Type.get(returnType), simplePop(frame)); } catch (NotFoundException e) { throw new RuntimeException(e); } break; case RETURN: break; case GETSTATIC: evalGetField(opcode, iter.u16bitAt(pos + 1), frame); break; case PUTSTATIC: evalPutField(opcode, iter.u16bitAt(pos + 1), frame); break; case GETFIELD: evalGetField(opcode, iter.u16bitAt(pos + 1), frame); break; case PUTFIELD: evalPutField(opcode, iter.u16bitAt(pos + 1), frame); break; case INVOKEVIRTUAL: case INVOKESPECIAL: case INVOKESTATIC: evalInvokeMethod(opcode, iter.u16bitAt(pos + 1), frame); break; case INVOKEINTERFACE: evalInvokeIntfMethod(opcode, iter.u16bitAt(pos + 1), frame); break; case 186: throw new RuntimeException("Bad opcode 186"); case NEW: frame.push(resolveClassInfo(constPool.getClassInfo(iter.u16bitAt(pos + 1)))); break; case NEWARRAY: evalNewArray(pos, iter, frame); break; case ANEWARRAY: evalNewObjectArray(pos, iter, frame); break; case ARRAYLENGTH: { Type array = simplePop(frame); if (! array.isArray() && array != Type.UNINIT) throw new BadBytecode("Array length passed a non-array [pos = " + pos + "]: " + array); frame.push(Type.INTEGER); break; } case ATHROW: verifyAssignable(THROWABLE_TYPE, simplePop(frame)); break; case CHECKCAST: verifyAssignable(Type.OBJECT, simplePop(frame)); frame.push(typeFromDesc(constPool.getClassInfo(iter.u16bitAt(pos + 1)))); break; case INSTANCEOF: verifyAssignable(Type.OBJECT, simplePop(frame)); frame.push(Type.INTEGER); break; case MONITORENTER: case MONITOREXIT: verifyAssignable(Type.OBJECT, simplePop(frame)); break; case WIDE: evalWide(pos, iter, frame, subroutine); break; case MULTIANEWARRAY: evalNewObjectArray(pos, iter, frame); break; case IFNULL: case IFNONNULL: verifyAssignable(Type.OBJECT, simplePop(frame)); break; case GOTO_W: break; case JSR_W: frame.push(Type.RETURN_ADDRESS); break; } } private Type zeroExtend(Type type) { if (type == Type.SHORT || type == Type.BYTE || type == Type.CHAR || type == Type.BOOLEAN) return Type.INTEGER; return type; } private void evalArrayLoad(Type expectedComponent, Frame frame) throws BadBytecode { Type index = frame.pop(); Type array = frame.pop(); // Special case, an array defined by aconst_null // TODO - we might need to be more inteligent about this if (array == Type.UNINIT) { verifyAssignable(Type.INTEGER, index); if (expectedComponent == Type.OBJECT) { simplePush(Type.UNINIT, frame); } else { simplePush(expectedComponent, frame); } return; } Type component = array.getComponent(); if (component == null) throw new BadBytecode("Not an array! [pos = " + lastPos + "]: " + component); component = zeroExtend(component); verifyAssignable(expectedComponent, component); verifyAssignable(Type.INTEGER, index); simplePush(component, frame); } private void evalArrayStore(Type expectedComponent, Frame frame) throws BadBytecode { Type value = simplePop(frame); Type index = frame.pop(); Type array = frame.pop(); if (array == Type.UNINIT) { verifyAssignable(Type.INTEGER, index); return; } Type component = array.getComponent(); if (component == null) throw new BadBytecode("Not an array! [pos = " + lastPos + "]: " + component); component = zeroExtend(component); verifyAssignable(expectedComponent, component); verifyAssignable(Type.INTEGER, index); // This intentionally only checks for Object on aastore // downconverting of an array (no casts) // e.g. Object[] blah = new String[]; // blah[2] = (Object) "test"; // blah[3] = new Integer(); // compiler doesnt catch it (has legal bytecode), // // but will throw arraystoreexception if (expectedComponent == Type.OBJECT) { verifyAssignable(expectedComponent, value); } else { verifyAssignable(component, value); } } private void evalBinaryMath(Type expected, Frame frame) throws BadBytecode { Type value2 = simplePop(frame); Type value1 = simplePop(frame); verifyAssignable(expected, value2); verifyAssignable(expected, value1); simplePush(value1, frame); } private void evalGetField(int opcode, int index, Frame frame) throws BadBytecode { String desc = constPool.getFieldrefType(index); Type type = zeroExtend(typeFromDesc(desc)); if (opcode == GETFIELD) { Type objectType = resolveClassInfo(constPool.getFieldrefClassName(index)); verifyAssignable(objectType, simplePop(frame)); } simplePush(type, frame); } private void evalInvokeIntfMethod(int opcode, int index, Frame frame) throws BadBytecode { String desc = constPool.getInterfaceMethodrefType(index); Type[] types = paramTypesFromDesc(desc); int i = types.length; while (i > 0) verifyAssignable(zeroExtend(types[--i]), simplePop(frame)); String classInfo = constPool.getInterfaceMethodrefClassName(index); Type objectType = resolveClassInfo(classInfo); verifyAssignable(objectType, simplePop(frame)); Type returnType = returnTypeFromDesc(desc); if (returnType != Type.VOID) simplePush(zeroExtend(returnType), frame); } private void evalInvokeMethod(int opcode, int index, Frame frame) throws BadBytecode { String desc = constPool.getMethodrefType(index); Type[] types = paramTypesFromDesc(desc); int i = types.length; while (i > 0) verifyAssignable(zeroExtend(types[--i]), simplePop(frame)); if (opcode != INVOKESTATIC) { Type objectType = resolveClassInfo(constPool.getMethodrefClassName(index)); verifyAssignable(objectType, simplePop(frame)); } Type returnType = returnTypeFromDesc(desc); if (returnType != Type.VOID) simplePush(zeroExtend(returnType), frame); } private void evalLDC(int index, Frame frame) throws BadBytecode { int tag = constPool.getTag(index); Type type; switch (tag) { case ConstPool.CONST_String: type = STRING_TYPE; break; case ConstPool.CONST_Integer: type = Type.INTEGER; break; case ConstPool.CONST_Float: type = Type.FLOAT; break; case ConstPool.CONST_Long: type = Type.LONG; break; case ConstPool.CONST_Double: type = Type.DOUBLE; break; case ConstPool.CONST_Class: type = CLASS_TYPE; break; default: throw new BadBytecode("bad LDC [pos = " + lastPos + "]: " + tag); } simplePush(type, frame); } private void evalLoad(Type expected, int index, Frame frame, Subroutine subroutine) throws BadBytecode { Type type = frame.getLocal(index); verifyAssignable(expected, type); simplePush(type, frame); access(index, type, subroutine); } private void evalNewArray(int pos, CodeIterator iter, Frame frame) throws BadBytecode { verifyAssignable(Type.INTEGER, simplePop(frame)); Type type = null; int typeInfo = iter.byteAt(pos + 1); switch (typeInfo) { case T_BOOLEAN: type = getType("boolean[]"); break; case T_CHAR: type = getType("char[]"); break; case T_BYTE: type = getType("byte[]"); break; case T_SHORT: type = getType("short[]"); break; case T_INT: type = getType("int[]"); break; case T_LONG: type = getType("long[]"); break; case T_FLOAT: type = getType("float[]"); break; case T_DOUBLE: type = getType("double[]"); break; default: throw new BadBytecode("Invalid array type [pos = " + pos + "]: " + typeInfo); } frame.push(type); } private void evalNewObjectArray(int pos, CodeIterator iter, Frame frame) throws BadBytecode { // Convert to x[] format Type type = resolveClassInfo(constPool.getClassInfo(iter.u16bitAt(pos + 1))); String name = type.getCtClass().getName(); int opcode = iter.byteAt(pos); int dimensions; if (opcode == MULTIANEWARRAY) { dimensions = iter.byteAt(pos + 3); } else { name = name + "[]"; dimensions = 1; } while (dimensions-- > 0) { verifyAssignable(Type.INTEGER, simplePop(frame)); } simplePush(getType(name), frame); } private void evalPutField(int opcode, int index, Frame frame) throws BadBytecode { String desc = constPool.getFieldrefType(index); Type type = zeroExtend(typeFromDesc(desc)); verifyAssignable(type, simplePop(frame)); if (opcode == PUTFIELD) { Type objectType = resolveClassInfo(constPool.getFieldrefClassName(index)); verifyAssignable(objectType, simplePop(frame)); } } private void evalShift(Type expected, Frame frame) throws BadBytecode { Type value2 = simplePop(frame); Type value1 = simplePop(frame); verifyAssignable(Type.INTEGER, value2); verifyAssignable(expected, value1); simplePush(value1, frame); } private void evalStore(Type expected, int index, Frame frame, Subroutine subroutine) throws BadBytecode { Type type = simplePop(frame); // RETURN_ADDRESS is allowed by ASTORE if (! (expected == Type.OBJECT && type == Type.RETURN_ADDRESS)) verifyAssignable(expected, type); simpleSetLocal(index, type, frame); access(index, type, subroutine); } private void evalWide(int pos, CodeIterator iter, Frame frame, Subroutine subroutine) throws BadBytecode { int opcode = iter.byteAt(pos + 1); int index = iter.u16bitAt(pos + 2); switch (opcode) { case ILOAD: evalLoad(Type.INTEGER, index, frame, subroutine); break; case LLOAD: evalLoad(Type.LONG, index, frame, subroutine); break; case FLOAD: evalLoad(Type.FLOAT, index, frame, subroutine); break; case DLOAD: evalLoad(Type.DOUBLE, index, frame, subroutine); break; case ALOAD: evalLoad(Type.OBJECT, index, frame, subroutine); break; case ISTORE: evalStore(Type.INTEGER, index, frame, subroutine); break; case LSTORE: evalStore(Type.LONG, index, frame, subroutine); break; case FSTORE: evalStore(Type.FLOAT, index, frame, subroutine); break; case DSTORE: evalStore(Type.DOUBLE, index, frame, subroutine); break; case ASTORE: evalStore(Type.OBJECT, index, frame, subroutine); break; case IINC: verifyAssignable(Type.INTEGER, frame.getLocal(index)); break; case RET: verifyAssignable(Type.RETURN_ADDRESS, frame.getLocal(index)); break; default: throw new BadBytecode("Invalid WIDE operand [pos = " + pos + "]: " + opcode); } } private Type getType(String name) throws BadBytecode { try { return Type.get(classPool.get(name)); } catch (NotFoundException e) { throw new BadBytecode("Could not find class [pos = " + lastPos + "]: " + name); } } private Type[] paramTypesFromDesc(String desc) throws BadBytecode { CtClass classes[] = null; try { classes = Descriptor.getParameterTypes(desc, classPool); } catch (NotFoundException e) { throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); } if (classes == null) throw new BadBytecode("Could not obtain parameters for descriptor [pos = " + lastPos + "]: " + desc); Type[] types = new Type[classes.length]; for (int i = 0; i < types.length; i++) types[i] = Type.get(classes[i]); return types; } private Type returnTypeFromDesc(String desc) throws BadBytecode { CtClass clazz = null; try { clazz = Descriptor.getReturnType(desc, classPool); } catch (NotFoundException e) { throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); } if (clazz == null) throw new BadBytecode("Could not obtain return type for descriptor [pos = " + lastPos + "]: " + desc); return Type.get(clazz); } private Type simplePeek(Frame frame) { Type type = frame.peek(); return (type == Type.TOP) ? frame.getStack(frame.getTopIndex() - 1) : type; } private Type simplePop(Frame frame) { Type type = frame.pop(); return (type == Type.TOP) ? frame.pop() : type; } private void simplePush(Type type, Frame frame) { frame.push(type); if (type.getSize() == 2) frame.push(Type.TOP); } private void access(int index, Type type, Subroutine subroutine) { if (subroutine == null) return; subroutine.access(index); if (type.getSize() == 2) subroutine.access(index + 1); } private void simpleSetLocal(int index, Type type, Frame frame) { frame.setLocal(index, type); if (type.getSize() == 2) frame.setLocal(index + 1, Type.TOP); } private Type resolveClassInfo(String info) throws BadBytecode { CtClass clazz = null; try { if (info.charAt(0) == '[') { clazz = Descriptor.toCtClass(info, classPool); } else { clazz = classPool.get(info); } } catch (NotFoundException e) { throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); } if (clazz == null) throw new BadBytecode("Could not obtain type for descriptor [pos = " + lastPos + "]: " + info); return Type.get(clazz); } private Type typeFromDesc(String desc) throws BadBytecode { CtClass clazz = null; try { clazz = Descriptor.toCtClass(desc, classPool); } catch (NotFoundException e) { throw new BadBytecode("Could not find class in descriptor [pos = " + lastPos + "]: " + e.getMessage()); } if (clazz == null) throw new BadBytecode("Could not obtain type for descriptor [pos = " + lastPos + "]: " + desc); return Type.get(clazz); } private void verifyAssignable(Type expected, Type type) throws BadBytecode { if (! expected.isAssignableFrom(type)) throw new BadBytecode("Expected type: " + expected + " Got: " + type + " [pos = " + lastPos + "]"); } } javassist-3.12.1.ga/src/main/javassist/bytecode/analysis/MultiArrayType.java0000644000175000017500000000670211015721760027077 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.analysis; import javassist.ClassPool; import javassist.CtClass; import javassist.NotFoundException; /** * Represents an array of {@link MultiType} instances. * * @author Jason T. Greene */ public class MultiArrayType extends Type { private MultiType component; private int dims; public MultiArrayType(MultiType component, int dims) { super(null); this.component = component; this.dims = dims; } public CtClass getCtClass() { CtClass clazz = component.getCtClass(); if (clazz == null) return null; ClassPool pool = clazz.getClassPool(); if (pool == null) pool = ClassPool.getDefault(); String name = arrayName(clazz.getName(), dims); try { return pool.get(name); } catch (NotFoundException e) { throw new RuntimeException(e); } } boolean popChanged() { return component.popChanged(); } public int getDimensions() { return dims; } public Type getComponent() { return dims == 1 ? (Type)component : new MultiArrayType(component, dims - 1); } public int getSize() { return 1; } public boolean isArray() { return true; } public boolean isAssignableFrom(Type type) { throw new UnsupportedOperationException("Not implemented"); } public boolean isReference() { return true; } public boolean isAssignableTo(Type type) { if (eq(type.getCtClass(), Type.OBJECT.getCtClass())) return true; if (eq(type.getCtClass(), Type.CLONEABLE.getCtClass())) return true; if (eq(type.getCtClass(), Type.SERIALIZABLE.getCtClass())) return true; if (! type.isArray()) return false; Type typeRoot = getRootComponent(type); int typeDims = type.getDimensions(); if (typeDims > dims) return false; if (typeDims < dims) { if (eq(typeRoot.getCtClass(), Type.OBJECT.getCtClass())) return true; if (eq(typeRoot.getCtClass(), Type.CLONEABLE.getCtClass())) return true; if (eq(typeRoot.getCtClass(), Type.SERIALIZABLE.getCtClass())) return true; return false; } return component.isAssignableTo(typeRoot); } public boolean equals(Object o) { if (! (o instanceof MultiArrayType)) return false; MultiArrayType multi = (MultiArrayType)o; return component.equals(multi.component) && dims == multi.dims; } public String toString() { // follows the same detailed formating scheme as component return arrayName(component.toString(), dims); } } javassist-3.12.1.ga/src/main/javassist/bytecode/analysis/SubroutineScanner.java0000644000175000017500000001137111024246704027613 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.analysis; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javassist.bytecode.BadBytecode; import javassist.bytecode.CodeAttribute; import javassist.bytecode.CodeIterator; import javassist.bytecode.ExceptionTable; import javassist.bytecode.MethodInfo; import javassist.bytecode.Opcode; /** * Discovers the subroutines in a method, and tracks all callers. * * @author Jason T. Greene */ public class SubroutineScanner implements Opcode { private Subroutine[] subroutines; Map subTable = new HashMap(); Set done = new HashSet(); public Subroutine[] scan(MethodInfo method) throws BadBytecode { CodeAttribute code = method.getCodeAttribute(); CodeIterator iter = code.iterator(); subroutines = new Subroutine[code.getCodeLength()]; subTable.clear(); done.clear(); scan(0, iter, null); ExceptionTable exceptions = code.getExceptionTable(); for (int i = 0; i < exceptions.size(); i++) { int handler = exceptions.handlerPc(i); // If an exception is thrown in subroutine, the handler // is part of the same subroutine. scan(handler, iter, subroutines[exceptions.startPc(i)]); } return subroutines; } private void scan(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { // Skip already processed blocks if (done.contains(new Integer(pos))) return; done.add(new Integer(pos)); int old = iter.lookAhead(); iter.move(pos); boolean next; do { pos = iter.next(); next = scanOp(pos, iter, sub) && iter.hasNext(); } while (next); iter.move(old); } private boolean scanOp(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { subroutines[pos] = sub; int opcode = iter.byteAt(pos); if (opcode == TABLESWITCH) { scanTableSwitch(pos, iter, sub); return false; } if (opcode == LOOKUPSWITCH) { scanLookupSwitch(pos, iter, sub); return false; } // All forms of return and throw end current code flow if (Util.isReturn(opcode) || opcode == RET || opcode == ATHROW) return false; if (Util.isJumpInstruction(opcode)) { int target = Util.getJumpTarget(pos, iter); if (opcode == JSR || opcode == JSR_W) { Subroutine s = (Subroutine) subTable.get(new Integer(target)); if (s == null) { s = new Subroutine(target, pos); subTable.put(new Integer(target), s); scan(target, iter, s); } else { s.addCaller(pos); } } else { scan(target, iter, sub); // GOTO ends current code flow if (Util.isGoto(opcode)) return false; } } return true; } private void scanLookupSwitch(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { int index = (pos & ~3) + 4; // default scan(pos + iter.s32bitAt(index), iter, sub); int npairs = iter.s32bitAt(index += 4); int end = npairs * 8 + (index += 4); // skip "match" for (index += 4; index < end; index += 8) { int target = iter.s32bitAt(index) + pos; scan(target, iter, sub); } } private void scanTableSwitch(int pos, CodeIterator iter, Subroutine sub) throws BadBytecode { // Skip 4 byte alignment padding int index = (pos & ~3) + 4; // default scan(pos + iter.s32bitAt(index), iter, sub); int low = iter.s32bitAt(index += 4); int high = iter.s32bitAt(index += 4); int end = (high - low + 1) * 4 + (index += 4); // Offset table for (; index < end; index += 4) { int target = iter.s32bitAt(index) + pos; scan(target, iter, sub); } } } javassist-3.12.1.ga/src/main/javassist/bytecode/analysis/Analyzer.java0000644000175000017500000003505311024246704025732 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.analysis; import java.util.Iterator; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.NotFoundException; import javassist.bytecode.AccessFlag; import javassist.bytecode.BadBytecode; import javassist.bytecode.CodeAttribute; import javassist.bytecode.CodeIterator; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.ExceptionTable; import javassist.bytecode.MethodInfo; import javassist.bytecode.Opcode; /** * A data-flow analyzer that determines the type state of the stack and local * variable table at every reachable instruction in a method. During analysis, * bytecode verification is performed in a similar manner to that described * in the JVM specification. * *

    Example:

    * *
     * // Method to analyze
     * public Object doSomething(int x) {
     *     Number n;
     *     if (x < 5) {
     *        n = new Double(0);
     *     } else {
     *        n = new Long(0);
     *     }
     *
     *     return n;
     * }
     *
     * // Which compiles to:
     * // 0:   iload_1
     * // 1:   iconst_5
     * // 2:   if_icmpge   17
     * // 5:   new #18; //class java/lang/Double
     * // 8:   dup
     * // 9:   dconst_0
     * // 10:  invokespecial   #44; //Method java/lang/Double."":(D)V
     * // 13:  astore_2
     * // 14:  goto    26
     * // 17:  new #16; //class java/lang/Long
     * // 20:  dup
     * // 21:  lconst_1
     * // 22:  invokespecial   #47; //Method java/lang/Long."":(J)V
     * // 25:  astore_2
     * // 26:  aload_2
     * // 27:  areturn
     *
     * public void analyzeIt(CtClass clazz, MethodInfo method) {
     *     Analyzer analyzer = new Analyzer();
     *     Frame[] frames = analyzer.analyze(clazz, method);
     *     frames[0].getLocal(0).getCtClass(); // returns clazz;
     *     frames[0].getLocal(1).getCtClass(); // returns java.lang.String
     *     frames[1].peek(); // returns Type.INTEGER
     *     frames[27].peek().getCtClass(); // returns java.lang.Number
     * }
     * 
    * * @see FramePrinter * @author Jason T. Greene */ public class Analyzer implements Opcode { private final SubroutineScanner scanner = new SubroutineScanner(); private CtClass clazz; private ExceptionInfo[] exceptions; private Frame[] frames; private Subroutine[] subroutines; private static class ExceptionInfo { private int end; private int handler; private int start; private Type type; private ExceptionInfo(int start, int end, int handler, Type type) { this.start = start; this.end = end; this.handler = handler; this.type = type; } } /** * Performs data-flow analysis on a method and returns an array, indexed by * instruction position, containing the starting frame state of all reachable * instructions. Non-reachable code, and illegal code offsets are represented * as a null in the frame state array. This can be used to detect dead code. * * If the method does not contain code (it is either native or abstract), null * is returned. * * @param clazz the declaring class of the method * @param method the method to analyze * @return an array, indexed by instruction position, of the starting frame state, * or null if this method doesn't have code * @throws BadBytecode if the bytecode does not comply with the JVM specification */ public Frame[] analyze(CtClass clazz, MethodInfo method) throws BadBytecode { this.clazz = clazz; CodeAttribute codeAttribute = method.getCodeAttribute(); // Native or Abstract if (codeAttribute == null) return null; int maxLocals = codeAttribute.getMaxLocals(); int maxStack = codeAttribute.getMaxStack(); int codeLength = codeAttribute.getCodeLength(); CodeIterator iter = codeAttribute.iterator(); IntQueue queue = new IntQueue(); exceptions = buildExceptionInfo(method); subroutines = scanner.scan(method); Executor executor = new Executor(clazz.getClassPool(), method.getConstPool()); frames = new Frame[codeLength]; frames[iter.lookAhead()] = firstFrame(method, maxLocals, maxStack); queue.add(iter.next()); while (!queue.isEmpty()) { analyzeNextEntry(method, iter, queue, executor); } return frames; } /** * Performs data-flow analysis on a method and returns an array, indexed by * instruction position, containing the starting frame state of all reachable * instructions. Non-reachable code, and illegal code offsets are represented * as a null in the frame state array. This can be used to detect dead code. * * If the method does not contain code (it is either native or abstract), null * is returned. * * @param method the method to analyze * @return an array, indexed by instruction position, of the starting frame state, * or null if this method doesn't have code * @throws BadBytecode if the bytecode does not comply with the JVM specification */ public Frame[] analyze(CtMethod method) throws BadBytecode { return analyze(method.getDeclaringClass(), method.getMethodInfo2()); } private void analyzeNextEntry(MethodInfo method, CodeIterator iter, IntQueue queue, Executor executor) throws BadBytecode { int pos = queue.take(); iter.move(pos); iter.next(); Frame frame = frames[pos].copy(); Subroutine subroutine = subroutines[pos]; try { executor.execute(method, pos, iter, frame, subroutine); } catch (RuntimeException e) { throw new BadBytecode(e.getMessage() + "[pos = " + pos + "]", e); } int opcode = iter.byteAt(pos); if (opcode == TABLESWITCH) { mergeTableSwitch(queue, pos, iter, frame); } else if (opcode == LOOKUPSWITCH) { mergeLookupSwitch(queue, pos, iter, frame); } else if (opcode == RET) { mergeRet(queue, iter, pos, frame, subroutine); } else if (Util.isJumpInstruction(opcode)) { int target = Util.getJumpTarget(pos, iter); if (Util.isJsr(opcode)) { // Merge the state before the jsr into the next instruction mergeJsr(queue, frames[pos], subroutines[target], pos, lookAhead(iter, pos)); } else if (! Util.isGoto(opcode)) { merge(queue, frame, lookAhead(iter, pos)); } merge(queue, frame, target); } else if (opcode != ATHROW && ! Util.isReturn(opcode)) { // Can advance to next instruction merge(queue, frame, lookAhead(iter, pos)); } // Merge all exceptions that are reachable from this instruction. // The redundancy is intentional, since the state must be based // on the current instruction frame. mergeExceptionHandlers(queue, method, pos, frame); } private ExceptionInfo[] buildExceptionInfo(MethodInfo method) { ConstPool constPool = method.getConstPool(); ClassPool classes = clazz.getClassPool(); ExceptionTable table = method.getCodeAttribute().getExceptionTable(); ExceptionInfo[] exceptions = new ExceptionInfo[table.size()]; for (int i = 0; i < table.size(); i++) { int index = table.catchType(i); Type type; try { type = index == 0 ? Type.THROWABLE : Type.get(classes.get(constPool.getClassInfo(index))); } catch (NotFoundException e) { throw new IllegalStateException(e.getMessage()); } exceptions[i] = new ExceptionInfo(table.startPc(i), table.endPc(i), table.handlerPc(i), type); } return exceptions; } private Frame firstFrame(MethodInfo method, int maxLocals, int maxStack) { int pos = 0; Frame first = new Frame(maxLocals, maxStack); if ((method.getAccessFlags() & AccessFlag.STATIC) == 0) { first.setLocal(pos++, Type.get(clazz)); } CtClass[] parameters; try { parameters = Descriptor.getParameterTypes(method.getDescriptor(), clazz.getClassPool()); } catch (NotFoundException e) { throw new RuntimeException(e); } for (int i = 0; i < parameters.length; i++) { Type type = zeroExtend(Type.get(parameters[i])); first.setLocal(pos++, type); if (type.getSize() == 2) first.setLocal(pos++, Type.TOP); } return first; } private int getNext(CodeIterator iter, int of, int restore) throws BadBytecode { iter.move(of); iter.next(); int next = iter.lookAhead(); iter.move(restore); iter.next(); return next; } private int lookAhead(CodeIterator iter, int pos) throws BadBytecode { if (! iter.hasNext()) throw new BadBytecode("Execution falls off end! [pos = " + pos + "]"); return iter.lookAhead(); } private void merge(IntQueue queue, Frame frame, int target) { Frame old = frames[target]; boolean changed; if (old == null) { frames[target] = frame.copy(); changed = true; } else { changed = old.merge(frame); } if (changed) { queue.add(target); } } private void mergeExceptionHandlers(IntQueue queue, MethodInfo method, int pos, Frame frame) { for (int i = 0; i < exceptions.length; i++) { ExceptionInfo exception = exceptions[i]; // Start is inclusive, while end is exclusive! if (pos >= exception.start && pos < exception.end) { Frame newFrame = frame.copy(); newFrame.clearStack(); newFrame.push(exception.type); merge(queue, newFrame, exception.handler); } } } private void mergeJsr(IntQueue queue, Frame frame, Subroutine sub, int pos, int next) throws BadBytecode { if (sub == null) throw new BadBytecode("No subroutine at jsr target! [pos = " + pos + "]"); Frame old = frames[next]; boolean changed = false; if (old == null) { old = frames[next] = frame.copy(); changed = true; } else { for (int i = 0; i < frame.localsLength(); i++) { // Skip everything accessed by a subroutine, mergeRet must handle this if (!sub.isAccessed(i)) { Type oldType = old.getLocal(i); Type newType = frame.getLocal(i); if (oldType == null) { old.setLocal(i, newType); changed = true; continue; } newType = oldType.merge(newType); // Always set the type, in case a multi-type switched to a standard type. old.setLocal(i, newType); if (!newType.equals(oldType) || newType.popChanged()) changed = true; } } } if (! old.isJsrMerged()) { old.setJsrMerged(true); changed = true; } if (changed && old.isRetMerged()) queue.add(next); } private void mergeLookupSwitch(IntQueue queue, int pos, CodeIterator iter, Frame frame) throws BadBytecode { int index = (pos & ~3) + 4; // default merge(queue, frame, pos + iter.s32bitAt(index)); int npairs = iter.s32bitAt(index += 4); int end = npairs * 8 + (index += 4); // skip "match" for (index += 4; index < end; index += 8) { int target = iter.s32bitAt(index) + pos; merge(queue, frame, target); } } private void mergeRet(IntQueue queue, CodeIterator iter, int pos, Frame frame, Subroutine subroutine) throws BadBytecode { if (subroutine == null) throw new BadBytecode("Ret on no subroutine! [pos = " + pos + "]"); Iterator callerIter = subroutine.callers().iterator(); while (callerIter.hasNext()) { int caller = ((Integer) callerIter.next()).intValue(); int returnLoc = getNext(iter, caller, pos); boolean changed = false; Frame old = frames[returnLoc]; if (old == null) { old = frames[returnLoc] = frame.copyStack(); changed = true; } else { changed = old.mergeStack(frame); } for (Iterator i = subroutine.accessed().iterator(); i.hasNext(); ) { int index = ((Integer)i.next()).intValue(); Type oldType = old.getLocal(index); Type newType = frame.getLocal(index); if (oldType != newType) { old.setLocal(index, newType); changed = true; } } if (! old.isRetMerged()) { old.setRetMerged(true); changed = true; } if (changed && old.isJsrMerged()) queue.add(returnLoc); } } private void mergeTableSwitch(IntQueue queue, int pos, CodeIterator iter, Frame frame) throws BadBytecode { // Skip 4 byte alignment padding int index = (pos & ~3) + 4; // default merge(queue, frame, pos + iter.s32bitAt(index)); int low = iter.s32bitAt(index += 4); int high = iter.s32bitAt(index += 4); int end = (high - low + 1) * 4 + (index += 4); // Offset table for (; index < end; index += 4) { int target = iter.s32bitAt(index) + pos; merge(queue, frame, target); } } private Type zeroExtend(Type type) { if (type == Type.SHORT || type == Type.BYTE || type == Type.CHAR || type == Type.BOOLEAN) return Type.INTEGER; return type; } } javassist-3.12.1.ga/src/main/javassist/bytecode/analysis/FramePrinter.java0000644000175000017500000001064611152365552026551 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.analysis; import java.io.PrintStream; import javassist.CtClass; import javassist.CtMethod; import javassist.Modifier; import javassist.NotFoundException; import javassist.bytecode.BadBytecode; import javassist.bytecode.CodeAttribute; import javassist.bytecode.CodeIterator; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.InstructionPrinter; import javassist.bytecode.MethodInfo; /** * A utility class for printing a merged view of the frame state and the * instructions of a method. * * @author Jason T. Greene */ public final class FramePrinter { private final PrintStream stream; /** * Constructs a bytecode printer. */ public FramePrinter(PrintStream stream) { this.stream = stream; } /** * Prints all the methods declared in the given class. */ public static void print(CtClass clazz, PrintStream stream) { (new FramePrinter(stream)).print(clazz); } /** * Prints all the methods declared in the given class. */ public void print(CtClass clazz) { CtMethod[] methods = clazz.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { print(methods[i]); } } private String getMethodString(CtMethod method) { try { return Modifier.toString(method.getModifiers()) + " " + method.getReturnType().getName() + " " + method.getName() + Descriptor.toString(method.getSignature()) + ";"; } catch (NotFoundException e) { throw new RuntimeException(e); } } /** * Prints the instructions and the frame states of the given method. */ public void print(CtMethod method) { stream.println("\n" + getMethodString(method)); MethodInfo info = method.getMethodInfo2(); ConstPool pool = info.getConstPool(); CodeAttribute code = info.getCodeAttribute(); if (code == null) return; Frame[] frames; try { frames = (new Analyzer()).analyze(method.getDeclaringClass(), info); } catch (BadBytecode e) { throw new RuntimeException(e); } int spacing = String.valueOf(code.getCodeLength()).length(); CodeIterator iterator = code.iterator(); while (iterator.hasNext()) { int pos; try { pos = iterator.next(); } catch (BadBytecode e) { throw new RuntimeException(e); } stream.println(pos + ": " + InstructionPrinter.instructionString(iterator, pos, pool)); addSpacing(spacing + 3); Frame frame = frames[pos]; if (frame == null) { stream.println("--DEAD CODE--"); continue; } printStack(frame); addSpacing(spacing + 3); printLocals(frame); } } private void printStack(Frame frame) { stream.print("stack ["); int top = frame.getTopIndex(); for (int i = 0; i <= top; i++) { if (i > 0) stream.print(", "); Type type = frame.getStack(i); stream.print(type); } stream.println("]"); } private void printLocals(Frame frame) { stream.print("locals ["); int length = frame.localsLength(); for (int i = 0; i < length; i++) { if (i > 0) stream.print(", "); Type type = frame.getLocal(i); stream.print(type == null ? "empty" : type.toString()); } stream.println("]"); } private void addSpacing(int count) { while (count-- > 0) stream.print(' '); } } javassist-3.12.1.ga/src/main/javassist/bytecode/analysis/MultiType.java0000644000175000017500000002214711017125753026103 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.analysis; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javassist.CtClass; /** * MultiType represents an unresolved type. Whenever two Type * instances are merged, if they share more than one super type (either an * interface or a superclass), then a MultiType is used to * represent the possible super types. The goal of a MultiType * is to reduce the set of possible types down to a single resolved type. This * is done by eliminating non-assignable types from the typeset when the * MultiType is passed as an argument to * {@link Type#isAssignableFrom(Type)}, as well as removing non-intersecting * types during a merge. * * Note: Currently the MultiType instance is reused as much * as possible so that updates are visible from all frames. In addition, all * MultiType merge paths are also updated. This is somewhat * hackish, but it appears to handle most scenarios. * * @author Jason T. Greene */ /* TODO - A better, but more involved, approach would be to track the instruction * offset that resulted in the creation of this type, and * whenever the typeset changes, to force a merge on that position. This * would require creating a new MultiType instance every time the typeset * changes, and somehow communicating assignment changes to the Analyzer */ public class MultiType extends Type { private Map interfaces; private Type resolved; private Type potentialClass; private MultiType mergeSource; private boolean changed = false; public MultiType(Map interfaces) { this(interfaces, null); } public MultiType(Map interfaces, Type potentialClass) { super(null); this.interfaces = interfaces; this.potentialClass = potentialClass; } /** * Gets the class that corresponds with this type. If this information * is not yet known, java.lang.Object will be returned. */ public CtClass getCtClass() { if (resolved != null) return resolved.getCtClass(); return Type.OBJECT.getCtClass(); } /** * Always returns null since this type is never used for an array. */ public Type getComponent() { return null; } /** * Always returns 1, since this type is a reference. */ public int getSize() { return 1; } /** * Always reutnrs false since this type is never used for an array */ public boolean isArray() { return false; } /** * Returns true if the internal state has changed. */ boolean popChanged() { boolean changed = this.changed; this.changed = false; return changed; } public boolean isAssignableFrom(Type type) { throw new UnsupportedOperationException("Not implemented"); } public boolean isAssignableTo(Type type) { if (resolved != null) return type.isAssignableFrom(resolved); if (Type.OBJECT.equals(type)) return true; if (potentialClass != null && !type.isAssignableFrom(potentialClass)) potentialClass = null; Map map = mergeMultiAndSingle(this, type); if (map.size() == 1 && potentialClass == null) { // Update previous merge paths to the same resolved type resolved = Type.get((CtClass)map.values().iterator().next()); propogateResolved(); return true; } // Keep all previous merge paths up to date if (map.size() >= 1) { interfaces = map; propogateState(); return true; } if (potentialClass != null) { resolved = potentialClass; propogateResolved(); return true; } return false; } private void propogateState() { MultiType source = mergeSource; while (source != null) { source.interfaces = interfaces; source.potentialClass = potentialClass; source = source.mergeSource; } } private void propogateResolved() { MultiType source = mergeSource; while (source != null) { source.resolved = resolved; source = source.mergeSource; } } /** * Always returns true, since this type is always a reference. * * @return true */ public boolean isReference() { return true; } private Map getAllMultiInterfaces(MultiType type) { Map map = new HashMap(); Iterator iter = type.interfaces.values().iterator(); while (iter.hasNext()) { CtClass intf = (CtClass)iter.next(); map.put(intf.getName(), intf); getAllInterfaces(intf, map); } return map; } private Map mergeMultiInterfaces(MultiType type1, MultiType type2) { Map map1 = getAllMultiInterfaces(type1); Map map2 = getAllMultiInterfaces(type2); return findCommonInterfaces(map1, map2); } private Map mergeMultiAndSingle(MultiType multi, Type single) { Map map1 = getAllMultiInterfaces(multi); Map map2 = getAllInterfaces(single.getCtClass(), null); return findCommonInterfaces(map1, map2); } private boolean inMergeSource(MultiType source) { while (source != null) { if (source == this) return true; source = source.mergeSource; } return false; } public Type merge(Type type) { if (this == type) return this; if (type == UNINIT) return this; if (type == BOGUS) return BOGUS; if (type == null) return this; if (resolved != null) return resolved.merge(type); if (potentialClass != null) { Type mergePotential = potentialClass.merge(type); if (! mergePotential.equals(potentialClass) || mergePotential.popChanged()) { potentialClass = Type.OBJECT.equals(mergePotential) ? null : mergePotential; changed = true; } } Map merged; if (type instanceof MultiType) { MultiType multi = (MultiType)type; if (multi.resolved != null) { merged = mergeMultiAndSingle(this, multi.resolved); } else { merged = mergeMultiInterfaces(multi, this); if (! inMergeSource(multi)) mergeSource = multi; } } else { merged = mergeMultiAndSingle(this, type); } // Keep all previous merge paths up to date if (merged.size() > 1 || (merged.size() == 1 && potentialClass != null)) { // Check for changes if (merged.size() != interfaces.size()) { changed = true; } else if (changed == false){ Iterator iter = merged.keySet().iterator(); while (iter.hasNext()) if (! interfaces.containsKey(iter.next())) changed = true; } interfaces = merged; propogateState(); return this; } if (merged.size() == 1) { resolved = Type.get((CtClass) merged.values().iterator().next()); } else if (potentialClass != null){ resolved = potentialClass; } else { resolved = OBJECT; } propogateResolved(); return resolved; } public boolean equals(Object o) { if (! (o instanceof MultiType)) return false; MultiType multi = (MultiType) o; if (resolved != null) return resolved.equals(multi.resolved); else if (multi.resolved != null) return false; return interfaces.keySet().equals(multi.interfaces.keySet()); } public String toString() { if (resolved != null) return resolved.toString(); StringBuffer buffer = new StringBuffer("{"); Iterator iter = interfaces.keySet().iterator(); while (iter.hasNext()) { buffer.append(iter.next()); buffer.append(", "); } buffer.setLength(buffer.length() - 2); if (potentialClass != null) buffer.append(", *").append(potentialClass.toString()); buffer.append("}"); return buffer.toString(); } } javassist-3.12.1.ga/src/main/javassist/bytecode/analysis/Type.java0000644000175000017500000004721511374204734025076 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.analysis; import java.util.ArrayList; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.Map; import javassist.ClassPool; import javassist.CtClass; import javassist.NotFoundException; /** * Represents a JVM type in data-flow analysis. This abstraction is necessary since * a JVM type not only includes all normal Java types, but also a few special types * that are used by the JVM internally. See the static field types on this class for * more info on these special types. * * All primitive and special types reuse the same instance, so identity comparison can * be used when examining them. Normal java types must use {@link #equals(Object)} to * compare type instances. * * In most cases, applications which consume this API, only need to call {@link #getCtClass()} * to obtain the needed type information. * * @author Jason T. Greene */ public class Type { private final CtClass clazz; private final boolean special; private static final Map prims = new IdentityHashMap(); /** Represents the double primitive type */ public static final Type DOUBLE = new Type(CtClass.doubleType); /** Represents the boolean primitive type */ public static final Type BOOLEAN = new Type(CtClass.booleanType); /** Represents the long primitive type */ public static final Type LONG = new Type(CtClass.longType); /** Represents the char primitive type */ public static final Type CHAR = new Type(CtClass.charType); /** Represents the byte primitive type */ public static final Type BYTE = new Type(CtClass.byteType); /** Represents the short primitive type */ public static final Type SHORT = new Type(CtClass.shortType); /** Represents the integer primitive type */ public static final Type INTEGER = new Type(CtClass.intType); /** Represents the float primitive type */ public static final Type FLOAT = new Type(CtClass.floatType); /** Represents the void primitive type */ public static final Type VOID = new Type(CtClass.voidType); /** * Represents an unknown, or null type. This occurs when aconst_null is used. * It is important not to treat this type as java.lang.Object, since a null can * be assigned to any reference type. The analyzer will replace these with * an actual known type if it can be determined by a merged path with known type * information. If this type is encountered on a frame then it is guaranteed to * be null, and the type information is simply not available. Any attempts to * infer the type, without further information from the compiler would be a guess. */ public static final Type UNINIT = new Type(null); /** * Represents an internal JVM return address, which is used by the RET * instruction to return to a JSR that invoked the subroutine. */ public static final Type RETURN_ADDRESS = new Type(null, true); /** A placeholder used by the analyzer for the second word position of a double-word type */ public static final Type TOP = new Type(null, true); /** * Represents a non-accessible value. Code cannot access the value this type * represents. It occurs when bytecode reuses a local variable table * position with non-mergable types. An example would be compiled code which * uses the same position for a primitive type in one branch, and a reference type * in another branch. */ public static final Type BOGUS = new Type(null, true); /** Represents the java.lang.Object reference type */ public static final Type OBJECT = lookupType("java.lang.Object"); /** Represents the java.io.Serializable reference type */ public static final Type SERIALIZABLE = lookupType("java.io.Serializable"); /** Represents the java.lang.Coneable reference type */ public static final Type CLONEABLE = lookupType("java.lang.Cloneable"); /** Represents the java.lang.Throwable reference type */ public static final Type THROWABLE = lookupType("java.lang.Throwable"); static { prims.put(CtClass.doubleType, DOUBLE); prims.put(CtClass.longType, LONG); prims.put(CtClass.charType, CHAR); prims.put(CtClass.shortType, SHORT); prims.put(CtClass.intType, INTEGER); prims.put(CtClass.floatType, FLOAT); prims.put(CtClass.byteType, BYTE); prims.put(CtClass.booleanType, BOOLEAN); prims.put(CtClass.voidType, VOID); } /** * Obtain the Type for a given class. If the class is a primitive, * the the unique type instance for the primitive will be returned. * Otherwise a new Type instance representing the class is returned. * * @param clazz The java class * @return a type instance for this class */ public static Type get(CtClass clazz) { Type type = (Type)prims.get(clazz); return type != null ? type : new Type(clazz); } private static Type lookupType(String name) { try { return new Type(ClassPool.getDefault().get(name)); } catch (NotFoundException e) { throw new RuntimeException(e); } } Type(CtClass clazz) { this(clazz, false); } private Type(CtClass clazz, boolean special) { this.clazz = clazz; this.special = special; } // Used to indicate a merge internally triggered a change boolean popChanged() { return false; } /** * Gets the word size of this type. Double-word types, such as long and double * will occupy two positions on the local variable table or stack. * * @return the number of words needed to hold this type */ public int getSize() { return clazz == CtClass.doubleType || clazz == CtClass.longType || this == TOP ? 2 : 1; } /** * Returns the class this type represents. If the type is special, null will be returned. * * @return the class for this type, or null if special */ public CtClass getCtClass() { return clazz; } /** * Returns whether or not this type is a normal java reference, i.e. it is or extends java.lang.Object. * * @return true if a java reference, false if a primitive or special */ public boolean isReference() { return !special && (clazz == null || !clazz.isPrimitive()); } /** * Returns whether or not the type is special. A special type is one that is either used * for internal tracking, or is only used internally by the JVM. * * @return true if special, false if not */ public boolean isSpecial() { return special; } /** * Returns whether or not this type is an array. * * @return true if an array, false if not */ public boolean isArray() { return clazz != null && clazz.isArray(); } /** * Returns the number of dimensions of this array. If the type is not an * array zero is returned. * * @return zero if not an array, otherwise the number of array dimensions. */ public int getDimensions() { if (!isArray()) return 0; String name = clazz.getName(); int pos = name.length() - 1; int count = 0; while (name.charAt(pos) == ']' ) { pos -= 2; count++; } return count; } /** * Returns the array component if this type is an array. If the type * is not an array null is returned. * * @return the array component if an array, otherwise null */ public Type getComponent() { if (this.clazz == null || !this.clazz.isArray()) return null; CtClass component; try { component = this.clazz.getComponentType(); } catch (NotFoundException e) { throw new RuntimeException(e); } Type type = (Type)prims.get(component); return (type != null) ? type : new Type(component); } /** * Determines whether this type is assignable, to the passed type. * A type is assignable to another if it is either the same type, or * a sub-type. * * @param type the type to test assignability to * @return true if this is assignable to type, otherwise false */ public boolean isAssignableFrom(Type type) { if (this == type) return true; if ((type == UNINIT && isReference()) || this == UNINIT && type.isReference()) return true; if (type instanceof MultiType) return ((MultiType)type).isAssignableTo(this); if (type instanceof MultiArrayType) return ((MultiArrayType)type).isAssignableTo(this); // Primitives and Special types must be identical if (clazz == null || clazz.isPrimitive()) return false; try { return type.clazz.subtypeOf(clazz); } catch (Exception e) { throw new RuntimeException(e); } } /** * Finds the common base type, or interface which both this and the specified * type can be assigned. If there is more than one possible answer, then a {@link MultiType}, * or a {@link MultiArrayType} is returned. Multi-types have special rules, * and successive merges and assignment tests on them will alter their internal state, * as well as other multi-types they have been merged with. This method is used by * the data-flow analyzer to merge the type state from multiple branches. * * @param type the type to merge with * @return the merged type */ public Type merge(Type type) { if (type == this) return this; if (type == null) return this; if (type == Type.UNINIT) return this; if (this == Type.UNINIT) return type; // Unequal primitives and special types can not be merged if (! type.isReference() || ! this.isReference()) return BOGUS; // Centralize merging of multi-interface types if (type instanceof MultiType) return type.merge(this); if (type.isArray() && this.isArray()) return mergeArray(type); try { return mergeClasses(type); } catch (NotFoundException e) { throw new RuntimeException(e); } } Type getRootComponent(Type type) { while (type.isArray()) type = type.getComponent(); return type; } private Type createArray(Type rootComponent, int dims) { if (rootComponent instanceof MultiType) return new MultiArrayType((MultiType) rootComponent, dims); String name = arrayName(rootComponent.clazz.getName(), dims); Type type; try { type = Type.get(getClassPool(rootComponent).get(name)); } catch (NotFoundException e) { throw new RuntimeException(e); } return type; } String arrayName(String component, int dims) { // Using char[] since we have no StringBuilder in JDK4, and StringBuffer is slow. // Although, this is more efficient even if we did have one. int i = component.length(); int size = i + dims * 2; char[] string = new char[size]; component.getChars(0, i, string, 0); while (i < size) { string[i++] = '['; string[i++] = ']'; } component = new String(string); return component; } private ClassPool getClassPool(Type rootComponent) { ClassPool pool = rootComponent.clazz.getClassPool(); return pool != null ? pool : ClassPool.getDefault(); } private Type mergeArray(Type type) { Type typeRoot = getRootComponent(type); Type thisRoot = getRootComponent(this); int typeDims = type.getDimensions(); int thisDims = this.getDimensions(); // Array commponents can be merged when the dimensions are equal if (typeDims == thisDims) { Type mergedComponent = thisRoot.merge(typeRoot); // If the components can not be merged (a primitive component mixed with a different type) // then Object is the common type. if (mergedComponent == Type.BOGUS) return Type.OBJECT; return createArray(mergedComponent, thisDims); } Type targetRoot; int targetDims; if (typeDims < thisDims) { targetRoot = typeRoot; targetDims = typeDims; } else { targetRoot = thisRoot; targetDims = thisDims; } // Special case, arrays are cloneable and serializable, so prefer them when dimensions differ if (eq(CLONEABLE.clazz, targetRoot.clazz) || eq(SERIALIZABLE.clazz, targetRoot.clazz)) return createArray(targetRoot, targetDims); return createArray(OBJECT, targetDims); } private static CtClass findCommonSuperClass(CtClass one, CtClass two) throws NotFoundException { CtClass deep = one; CtClass shallow = two; CtClass backupShallow = shallow; CtClass backupDeep = deep; // Phase 1 - Find the deepest hierarchy, set deep and shallow correctly for (;;) { // In case we get lucky, and find a match early if (eq(deep, shallow) && deep.getSuperclass() != null) return deep; CtClass deepSuper = deep.getSuperclass(); CtClass shallowSuper = shallow.getSuperclass(); if (shallowSuper == null) { // right, now reset shallow shallow = backupShallow; break; } if (deepSuper == null) { // wrong, swap them, since deep is now useless, its our tmp before we swap it deep = backupDeep; backupDeep = backupShallow; backupShallow = deep; deep = shallow; shallow = backupShallow; break; } deep = deepSuper; shallow = shallowSuper; } // Phase 2 - Move deepBackup up by (deep end - deep) for (;;) { deep = deep.getSuperclass(); if (deep == null) break; backupDeep = backupDeep.getSuperclass(); } deep = backupDeep; // Phase 3 - The hierarchy positions are now aligned // The common super class is easy to find now while (!eq(deep, shallow)) { deep = deep.getSuperclass(); shallow = shallow.getSuperclass(); } return deep; } private Type mergeClasses(Type type) throws NotFoundException { CtClass superClass = findCommonSuperClass(this.clazz, type.clazz); // If its Object, then try and find a common interface(s) if (superClass.getSuperclass() == null) { Map interfaces = findCommonInterfaces(type); if (interfaces.size() == 1) return new Type((CtClass) interfaces.values().iterator().next()); if (interfaces.size() > 1) return new MultiType(interfaces); // Only Object is in common return new Type(superClass); } // Check for a common interface that is not on the found supertype Map commonDeclared = findExclusiveDeclaredInterfaces(type, superClass); if (commonDeclared.size() > 0) { return new MultiType(commonDeclared, new Type(superClass)); } return new Type(superClass); } private Map findCommonInterfaces(Type type) { Map typeMap = getAllInterfaces(type.clazz, null); Map thisMap = getAllInterfaces(this.clazz, null); return findCommonInterfaces(typeMap, thisMap); } private Map findExclusiveDeclaredInterfaces(Type type, CtClass exclude) { Map typeMap = getDeclaredInterfaces(type.clazz, null); Map thisMap = getDeclaredInterfaces(this.clazz, null); Map excludeMap = getAllInterfaces(exclude, null); Iterator i = excludeMap.keySet().iterator(); while (i.hasNext()) { Object intf = i.next(); typeMap.remove(intf); thisMap.remove(intf); } return findCommonInterfaces(typeMap, thisMap); } Map findCommonInterfaces(Map typeMap, Map alterMap) { Iterator i = alterMap.keySet().iterator(); while (i.hasNext()) { if (! typeMap.containsKey(i.next())) i.remove(); } // Reduce to subinterfaces // This does not need to be recursive since we make a copy, // and that copy contains all super types for the whole hierarchy i = new ArrayList(alterMap.values()).iterator(); while (i.hasNext()) { CtClass intf = (CtClass) i.next(); CtClass[] interfaces; try { interfaces = intf.getInterfaces(); } catch (NotFoundException e) { throw new RuntimeException(e); } for (int c = 0; c < interfaces.length; c++) alterMap.remove(interfaces[c].getName()); } return alterMap; } Map getAllInterfaces(CtClass clazz, Map map) { if (map == null) map = new HashMap(); if (clazz.isInterface()) map.put(clazz.getName(), clazz); do { try { CtClass[] interfaces = clazz.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { CtClass intf = interfaces[i]; map.put(intf.getName(), intf); getAllInterfaces(intf, map); } clazz = clazz.getSuperclass(); } catch (NotFoundException e) { throw new RuntimeException(e); } } while (clazz != null); return map; } Map getDeclaredInterfaces(CtClass clazz, Map map) { if (map == null) map = new HashMap(); if (clazz.isInterface()) map.put(clazz.getName(), clazz); CtClass[] interfaces; try { interfaces = clazz.getInterfaces(); } catch (NotFoundException e) { throw new RuntimeException(e); } for (int i = 0; i < interfaces.length; i++) { CtClass intf = interfaces[i]; map.put(intf.getName(), intf); getDeclaredInterfaces(intf, map); } return map; } public boolean equals(Object o) { if (! (o instanceof Type)) return false; return o.getClass() == getClass() && eq(clazz, ((Type)o).clazz); } static boolean eq(CtClass one, CtClass two) { return one == two || (one != null && two != null && one.getName().equals(two.getName())); } public String toString() { if (this == BOGUS) return "BOGUS"; if (this == UNINIT) return "UNINIT"; if (this == RETURN_ADDRESS) return "RETURN ADDRESS"; if (this == TOP) return "TOP"; return clazz == null ? "null" : clazz.getName(); } } javassist-3.12.1.ga/src/main/javassist/bytecode/analysis/package.html0000644000175000017500000000115611015721760025560 0ustar twernertwerner Bytecode Analysis API.

    This package provides an API for performing data-flow analysis on a method's bytecode. This allows the user to determine the type state of the stack and local variable table at the start of every instruction. In addition this API can be used to validate bytecode, find dead bytecode, and identify unnecessary checkcasts.

    The users of this package must know the specifications of class file and Java bytecode. For more details, read this book:

      Tim Lindholm and Frank Yellin, "The Java Virtual Machine Specification 2nd Ed.", Addison-Wesley, 1999.
    javassist-3.12.1.ga/src/main/javassist/bytecode/analysis/Subroutine.java0000644000175000017500000000343711024246704026305 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.analysis; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Represents a nested method subroutine (marked by JSR and RET). * * @author Jason T. Greene */ public class Subroutine { //private Set callers = new HashSet(); private List callers = new ArrayList(); private Set access = new HashSet(); private int start; public Subroutine(int start, int caller) { this.start = start; callers.add(new Integer(caller)); } public void addCaller(int caller) { callers.add(new Integer(caller)); } public int start() { return start; } public void access(int index) { access.add(new Integer(index)); } public boolean isAccessed(int index) { return access.contains(new Integer(index)); } public Collection accessed() { return access; } public Collection callers() { return callers; } public String toString() { return "start = " + start + " callers = " + callers.toString(); } }javassist-3.12.1.ga/src/main/javassist/bytecode/analysis/IntQueue.java0000644000175000017500000000277511015721760025711 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.analysis; import java.util.NoSuchElementException; class IntQueue { private static class Entry { private IntQueue.Entry next; private int value; private Entry(int value) { this.value = value; } } private IntQueue.Entry head; private IntQueue.Entry tail; void add(int value) { IntQueue.Entry entry = new Entry(value); if (tail != null) tail.next = entry; tail = entry; if (head == null) head = entry; } boolean isEmpty() { return head == null; } int take() { if (head == null) throw new NoSuchElementException(); int value = head.value; head = head.next; if (head == null) tail = null; return value; } }javassist-3.12.1.ga/src/main/javassist/bytecode/analysis/Frame.java0000644000175000017500000002042011015721760025167 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.analysis; /** * Represents the stack frame and local variable table at a particular point in time. * * @author Jason T. Greene */ public class Frame { private Type[] locals; private Type[] stack; private int top; private boolean jsrMerged; private boolean retMerged; /** * Create a new frame with the specified local variable table size, and max stack size * * @param locals the number of local variable table entries * @param stack the maximum stack size */ public Frame(int locals, int stack) { this.locals = new Type[locals]; this.stack = new Type[stack]; } /** * Returns the local varaible table entry at index. * * @param index the position in the table * @return the type if one exists, or null if the position is empty */ public Type getLocal(int index) { return locals[index]; } /** * Sets the local variable table entry at index to a type. * * @param index the position in the table * @param type the type to set at the position */ public void setLocal(int index, Type type) { locals[index] = type; } /** * Returns the type on the stack at the specified index. * * @param index the position on the stack * @return the type of the stack position */ public Type getStack(int index) { return stack[index]; } /** * Sets the type of the stack position * * @param index the position on the stack * @param type the type to set */ public void setStack(int index, Type type) { stack[index] = type; } /** * Empties the stack */ public void clearStack() { top = 0; } /** * Gets the index of the type sitting at the top of the stack. * This is not to be confused with a length operation which * would return the number of elements, not the position of * the last element. * * @return the position of the element at the top of the stack */ public int getTopIndex() { return top - 1; } /** * Returns the number of local variable table entries, specified * at construction. * * @return the number of local variable table entries */ public int localsLength() { return locals.length; } /** * Gets the top of the stack without altering it * * @return the top of the stack */ public Type peek() { if (top < 1) throw new IndexOutOfBoundsException("Stack is empty"); return stack[top - 1]; } /** * Alters the stack to contain one less element and return it. * * @return the element popped from the stack */ public Type pop() { if (top < 1) throw new IndexOutOfBoundsException("Stack is empty"); return stack[--top]; } /** * Alters the stack by placing the passed type on the top * * @param type the type to add to the top */ public void push(Type type) { stack[top++] = type; } /** * Makes a shallow copy of this frame, i.e. the type instances will * remain the same. * * @return the shallow copy */ public Frame copy() { Frame frame = new Frame(locals.length, stack.length); System.arraycopy(locals, 0, frame.locals, 0, locals.length); System.arraycopy(stack, 0, frame.stack, 0, stack.length); frame.top = top; return frame; } /** * Makes a shallow copy of the stack portion of this frame. The local * variable table size will be copied, but its contents will be empty. * * @return the shallow copy of the stack */ public Frame copyStack() { Frame frame = new Frame(locals.length, stack.length); System.arraycopy(stack, 0, frame.stack, 0, stack.length); frame.top = top; return frame; } /** * Merges all types on the stack of this frame instance with that of the specified frame. * The local variable table is left untouched. * * @param frame the frame to merge the stack from * @return true if any changes where made */ public boolean mergeStack(Frame frame) { boolean changed = false; if (top != frame.top) throw new RuntimeException("Operand stacks could not be merged, they are different sizes!"); for (int i = 0; i < top; i++) { if (stack[i] != null) { Type prev = stack[i]; Type merged = prev.merge(frame.stack[i]); if (merged == Type.BOGUS) throw new RuntimeException("Operand stacks could not be merged due to differing primitive types: pos = " + i); stack[i] = merged; // always replace the instance in case a multi-interface type changes to a normal Type if ((! merged.equals(prev)) || merged.popChanged()) { changed = true; } } } return changed; } /** * Merges all types on the stack and local variable table of this frame with that of the specified * type. * * @param frame the frame to merge with * @return true if any changes to this frame where made by this merge */ public boolean merge(Frame frame) { boolean changed = false; // Local variable table for (int i = 0; i < locals.length; i++) { if (locals[i] != null) { Type prev = locals[i]; Type merged = prev.merge(frame.locals[i]); // always replace the instance in case a multi-interface type changes to a normal Type locals[i] = merged; if (! merged.equals(prev) || merged.popChanged()) { changed = true; } } else if (frame.locals[i] != null) { locals[i] = frame.locals[i]; changed = true; } } changed |= mergeStack(frame); return changed; } public String toString() { StringBuffer buffer = new StringBuffer(); buffer.append("locals = ["); for (int i = 0; i < locals.length; i++) { buffer.append(locals[i] == null ? "empty" : locals[i].toString()); if (i < locals.length - 1) buffer.append(", "); } buffer.append("] stack = ["); for (int i = 0; i < top; i++) { buffer.append(stack[i]); if (i < top - 1) buffer.append(", "); } buffer.append("]"); return buffer.toString(); } /** * Whether or not state from the source JSR instruction has been merged * * @return true if JSR state has been merged */ boolean isJsrMerged() { return jsrMerged; } /** * Sets whether of not the state from the source JSR instruction has been merged * * @param jsrMerged true if merged, otherwise false */ void setJsrMerged(boolean jsrMerged) { this.jsrMerged = jsrMerged; } /** * Whether or not state from the RET instruction, of the subroutine that was jumped * to has been merged. * * @return true if RET state has been merged */ boolean isRetMerged() { return retMerged; } /** * Sets whether or not state from the RET instruction, of the subroutine that was jumped * to has been merged. * * @param retMerged true if RET state has been merged */ void setRetMerged(boolean retMerged) { this.retMerged = retMerged; } } javassist-3.12.1.ga/src/main/javassist/bytecode/analysis/Util.java0000644000175000017500000000317511015721760025062 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.analysis; import javassist.bytecode.CodeIterator; import javassist.bytecode.Opcode; /** * A set of common utility methods. * * @author Jason T. Greene */ public class Util implements Opcode { public static int getJumpTarget(int pos, CodeIterator iter) { int opcode = iter.byteAt(pos); pos += (opcode == JSR_W || opcode == GOTO_W) ? iter.s32bitAt(pos + 1) : iter.s16bitAt(pos + 1); return pos; } public static boolean isJumpInstruction(int opcode) { return (opcode >= IFEQ && opcode <= JSR) || opcode == IFNULL || opcode == IFNONNULL || opcode == JSR_W || opcode == GOTO_W; } public static boolean isGoto(int opcode) { return opcode == GOTO || opcode == GOTO_W; } public static boolean isJsr(int opcode) { return opcode == JSR || opcode == JSR_W; } public static boolean isReturn(int opcode) { return (opcode >= IRETURN && opcode <= RETURN); } } javassist-3.12.1.ga/src/main/javassist/bytecode/Bytecode.java0000644000175000017500000012036211366572525024072 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import javassist.CtClass; import javassist.CtPrimitiveType; class ByteVector implements Cloneable { private byte[] buffer; private int size; public ByteVector() { buffer = new byte[64]; size = 0; } public Object clone() throws CloneNotSupportedException { ByteVector bv = (ByteVector)super.clone(); bv.buffer = (byte[])buffer.clone(); return bv; } public final int getSize() { return size; } public final byte[] copy() { byte[] b = new byte[size]; System.arraycopy(buffer, 0, b, 0, size); return b; } public int read(int offset) { if (offset < 0 || size <= offset) throw new ArrayIndexOutOfBoundsException(offset); return buffer[offset]; } public void write(int offset, int value) { if (offset < 0 || size <= offset) throw new ArrayIndexOutOfBoundsException(offset); buffer[offset] = (byte)value; } public void add(int code) { addGap(1); buffer[size - 1] = (byte)code; } public void add(int b1, int b2) { addGap(2); buffer[size - 2] = (byte)b1; buffer[size - 1] = (byte)b2; } public void add(int b1, int b2, int b3, int b4) { addGap(4); buffer[size - 4] = (byte)b1; buffer[size - 3] = (byte)b2; buffer[size - 2] = (byte)b3; buffer[size - 1] = (byte)b4; } public void addGap(int length) { if (size + length > buffer.length) { int newSize = size << 1; if (newSize < size + length) newSize = size + length; byte[] newBuf = new byte[newSize]; System.arraycopy(buffer, 0, newBuf, 0, size); buffer = newBuf; } size += length; } } /** * A utility class for producing a bytecode sequence. * *

    A Bytecode object is an unbounded array * containing bytecode. For example, * *

      ConstPool cp = ...;    // constant pool table
       * Bytecode b = new Bytecode(cp, 1, 0);
       * b.addIconst(3);
       * b.addReturn(CtClass.intType);
       * CodeAttribute ca = b.toCodeAttribute();
    * *

    This program produces a Code attribute including a bytecode * sequence: * *

      iconst_3
       * ireturn
    * * @see ConstPool * @see CodeAttribute */ public class Bytecode extends ByteVector implements Cloneable, Opcode { /** * Represents the CtClass file using the * constant pool table given to this Bytecode object. */ public static final CtClass THIS = ConstPool.THIS; ConstPool constPool; int maxStack, maxLocals; ExceptionTable tryblocks; private int stackDepth; /** * Constructs a Bytecode object with an empty bytecode * sequence. * *

    The parameters stacksize and localvars * specify initial values * of max_stack and max_locals. * They can be changed later. * * @param cp constant pool table. * @param stacksize max_stack. * @param localvars max_locals. */ public Bytecode(ConstPool cp, int stacksize, int localvars) { constPool = cp; maxStack = stacksize; maxLocals = localvars; tryblocks = new ExceptionTable(cp); stackDepth = 0; } /** * Constructs a Bytecode object with an empty bytecode * sequence. The initial values of max_stack and * max_locals are zero. * * @param cp constant pool table. * @see Bytecode#setMaxStack(int) * @see Bytecode#setMaxLocals(int) */ public Bytecode(ConstPool cp) { this(cp, 0, 0); } /** * Creates and returns a copy of this object. * The constant pool object is shared between this object * and the cloned object. */ public Object clone() { try { Bytecode bc = (Bytecode)super.clone(); bc.tryblocks = (ExceptionTable)tryblocks.clone(); return bc; } catch (CloneNotSupportedException cnse) { throw new RuntimeException(cnse); } } /** * Gets a constant pool table. */ public ConstPool getConstPool() { return constPool; } /** * Returns exception_table. */ public ExceptionTable getExceptionTable() { return tryblocks; } /** * Converts to a CodeAttribute. */ public CodeAttribute toCodeAttribute() { return new CodeAttribute(constPool, maxStack, maxLocals, get(), tryblocks); } /** * Returns the length of the bytecode sequence. */ public int length() { return getSize(); } /** * Returns the produced bytecode sequence. */ public byte[] get() { return copy(); } /** * Gets max_stack. */ public int getMaxStack() { return maxStack; } /** * Sets max_stack. * *

    This value may be automatically updated when an instruction * is appended. A Bytecode object maintains the current * stack depth whenever an instruction is added * by addOpcode(). For example, if DUP is appended, * the current stack depth is increased by one. If the new stack * depth is more than max_stack, then it is assigned * to max_stack. 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 max_locals. */ public int getMaxLocals() { return maxLocals; } /** * Sets max_locals. */ public void setMaxLocals(int size) { maxLocals = size; } /** * Sets max_locals. * *

    This computes the number of local variables * used to pass method parameters and sets max_locals * to that number plus locals. * * @param isStatic true if params 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 max_locals. */ public void incMaxLocals(int diff) { maxLocals += diff; } /** * Adds a new entry of exception_table. */ public void addExceptionHandler(int start, int end, int handler, CtClass type) { addExceptionHandler(start, end, handler, constPool.addClassInfo(type)); } /** * Adds a new entry of exception_table. * * @param type the fully-qualified name of a throwable class. */ public void addExceptionHandler(int start, int end, int handler, String type) { addExceptionHandler(start, end, handler, constPool.addClassInfo(type)); } /** * Adds a new entry of exception_table. */ 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() { return getSize(); } /** * 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) { return super.read(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); } /** * Reads a signed 32bit value at the offset from the beginning of the * bytecode sequence. */ public int read32bit(int offset) { int v1 = read16bit(offset); int v2 = read16bit(offset + 2); return (v1 << 16) + (v2 & 0xffff); } /** * 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) { super.write(offset, value); } /** * 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); } /** * Writes an 32bit value at the offset from the beginning of the * bytecode sequence. */ public void write32bit(int offset, int value) { write16bit(offset, value >> 16); write16bit(offset + 2, value); } /** * Appends an 8bit value to the end of the bytecode sequence. */ public void add(int code) { super.add(code); } /** * Appends a 32bit value to the end of the bytecode sequence. */ public void add32bit(int value) { add(value >> 24, value >> 16, value >> 8, value); } /** * Appends the length-byte gap to the end of the bytecode sequence. * * @param length the gap length in byte. */ public void addGap(int length) { super.addGap(length); } /** * Appends an 8bit opcode to the end of the bytecode sequence. * The current stack depth is updated. * max_stack is updated if the current stack depth * is the deepest so far. * *

    Note: some instructions such as INVOKEVIRTUAL does not * update the current stack depth since the increment depends * on the method signature. * growStack() must be explicitly called. */ public void addOpcode(int code) { add(code); growStack(STACK_GROW[code]); } /** * Increases the current stack depth. * It also updates max_stack 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 max_stack 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, index); } /** * Appends ALOAD or (WIDE) ALOAD_<n> * * @param n an index into the local variable array. */ public void addAload(int n) { if (n < 4) addOpcode(42 + n); // aload_ else if (n < 0x100) { addOpcode(ALOAD); // aload add(n); } else { addOpcode(WIDE); addOpcode(ALOAD); addIndex(n); } } /** * Appends ASTORE or (WIDE) ASTORE_<n> * * @param n an index into the local variable array. */ public void addAstore(int n) { if (n < 4) addOpcode(75 + n); // astore_ else if (n < 0x100) { addOpcode(ASTORE); // astore add(n); } else { addOpcode(WIDE); addOpcode(ASTORE); addIndex(n); } } /** * Appends ICONST or ICONST_<n> * * @param n the pushed integer constant. */ public void addIconst(int n) { if (n < 6 && -2 < n) addOpcode(3 + n); // iconst_ -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 an instruction for pushing zero or null on the stack. * If the type is void, this method does not append any instruction. * * @param type the type of the zero value (or null). */ public void addConstZero(CtClass type) { if (type.isPrimitive()) { if (type == CtClass.longType) addOpcode(LCONST_0); else if (type == CtClass.floatType) addOpcode(FCONST_0); else if (type == CtClass.doubleType) addOpcode(DCONST_0); else if (type == CtClass.voidType) throw new RuntimeException("void type?"); else addOpcode(ICONST_0); } else addOpcode(ACONST_NULL); } /** * Appends ILOAD or (WIDE) ILOAD_<n> * * @param n an index into the local variable array. */ public void addIload(int n) { if (n < 4) addOpcode(26 + n); // iload_ else if (n < 0x100) { addOpcode(ILOAD); // iload add(n); } else { addOpcode(WIDE); addOpcode(ILOAD); addIndex(n); } } /** * Appends ISTORE or (WIDE) ISTORE_<n> * * @param n an index into the local variable array. */ public void addIstore(int n) { if (n < 4) addOpcode(59 + n); // istore_ else if (n < 0x100) { addOpcode(ISTORE); // istore add(n); } else { addOpcode(WIDE); addOpcode(ISTORE); addIndex(n); } } /** * Appends LCONST or LCONST_<n> * * @param n the pushed long integer constant. */ public void addLconst(long n) { if (n == 0 || n == 1) addOpcode(9 + (int)n); // lconst_ else addLdc2w(n); } /** * Appends LLOAD or (WIDE) LLOAD_<n> * * @param n an index into the local variable array. */ public void addLload(int n) { if (n < 4) addOpcode(30 + n); // lload_ else if (n < 0x100) { addOpcode(LLOAD); // lload add(n); } else { addOpcode(WIDE); addOpcode(LLOAD); addIndex(n); } } /** * Appends LSTORE or LSTORE_<n> * * @param n an index into the local variable array. */ public void addLstore(int n) { if (n < 4) addOpcode(63 + n); // lstore_ else if (n < 0x100) { addOpcode(LSTORE); // lstore add(n); } else { addOpcode(WIDE); addOpcode(LSTORE); addIndex(n); } } /** * Appends DCONST or DCONST_<n> * * @param d the pushed double constant. */ public void addDconst(double d) { if (d == 0.0 || d == 1.0) addOpcode(14 + (int)d); // dconst_ else addLdc2w(d); } /** * Appends DLOAD or (WIDE) DLOAD_<n> * * @param n an index into the local variable array. */ public void addDload(int n) { if (n < 4) addOpcode(38 + n); // dload_ else if (n < 0x100) { addOpcode(DLOAD); // dload add(n); } else { addOpcode(WIDE); addOpcode(DLOAD); addIndex(n); } } /** * Appends DSTORE or (WIDE) DSTORE_<n> * * @param n an index into the local variable array. */ public void addDstore(int n) { if (n < 4) addOpcode(71 + n); // dstore_ else if (n < 0x100) { addOpcode(DSTORE); // dstore add(n); } else { addOpcode(WIDE); addOpcode(DSTORE); addIndex(n); } } /** * Appends FCONST or FCONST_<n> * * @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_ else addLdc(constPool.addFloatInfo(f)); } /** * Appends FLOAD or (WIDE) FLOAD_<n> * * @param n an index into the local variable array. */ public void addFload(int n) { if (n < 4) addOpcode(34 + n); // fload_ else if (n < 0x100) { addOpcode(FLOAD); // fload add(n); } else { addOpcode(WIDE); addOpcode(FLOAD); addIndex(n); } } /** * Appends FSTORE or FSTORE_<n> * * @param n an index into the local variable array. */ public void addFstore(int n) { if (n < 4) addOpcode(67 + n); // fstore_ 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 n. * * @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 n. * * @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. * * @param offset the index of the first parameter. It is 0 * if the method is static. Otherwise, it is 1. */ public int addLoadParameters(CtClass[] params, int offset) { int stacksize = 0; if (params != null) { int n = params.length; for (int i = 0; i < n; ++i) stacksize += addLoad(stacksize + offset, 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 GETFIELD. * * @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 addGetfield(String 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 CONSTANT_Class_info * 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 CONSTANT_Class_info * 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. * *

    The specified method must not be an inherited method. * It must be directly declared in the class specified * in clazz. * * @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. * *

    The specified method must not be an inherited method. * It must be directly declared in the class specified * in clazz. * * @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. * *

    The specified method must not be an inherited method. * It must be directly declared in the class specified * in classname. * * @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. * *

    The specified method must not be an inherited method. * It must be directly declared in the class specified * by clazz. * * @param clazz the index of CONSTANT_Class_info * 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 CONSTANT_Class_info * 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 String * 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 T_BOOLEAN, T_CHAR, ... * @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 dimensions. */ 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 dim. */ 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 dim. */ 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) { addPutfield0(c, null, name, desc); } /** * Appends PUTFIELD. * * @param classname the fully-qualified name of the target class. * @param name the field name. * @param desc the descriptor of the field type. */ public void addPutfield(String classname, String name, String desc) { // if classnaem is null, the target class is THIS. addPutfield0(null, classname, name, desc); } private void addPutfield0(CtClass target, String classname, String name, String desc) { add(PUTFIELD); // target is null if it represents THIS. int ci = classname == null ? constPool.addClassInfo(target) : constPool.addClassInfo(classname); 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) { addPutstatic0(c, null, name, desc); } /** * Appends PUTSTATIC. * * @param classname the fully-qualified name of the target class. * @param fieldName the field name. * @param desc the descriptor of the field type. */ public void addPutstatic(String classname, String fieldName, String desc) { // if classname is null, the target class is THIS. addPutstatic0(null, classname, fieldName, desc); } private void addPutstatic0(CtClass target, String classname, String fieldName, String desc) { add(PUTSTATIC); // target is null if it represents THIS. int ci = classname == null ? constPool.addClassInfo(target) : constPool.addClassInfo(classname); addIndex(constPool.addFieldrefInfo(ci, fieldName, 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 * java.lang.System.println(message). * * @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"); } } javassist-3.12.1.ga/src/main/javassist/bytecode/ConstantAttribute.java0000644000175000017500000000444210630701321025766 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode; import java.io.DataInputStream; import java.util.Map; import java.io.IOException; /** * ConstantValue_attribute. */ public class ConstantAttribute extends AttributeInfo { /** * The name of this attribute "ConstantValue". */ 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 constantvalue_index * of ConstantValue_attribute. */ 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 constantvalue_index. */ public int getConstantValue() { return ByteArray.readU16bit(get(), 0); } /** * Makes a copy. Class names are replaced according to the * given Map 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); } } javassist-3.12.1.ga/src/main/javassist/CtConstructor.java0000644000175000017500000003655711221067671023355 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import javassist.bytecode.*; import javassist.compiler.Javac; import javassist.compiler.CompileError; /** * An instance of CtConstructor represents a constructor. * It may represent a static constructor * (class initializer). To distinguish a constructor and a class * initializer, call isClassInitializer(). * *

    See the super class CtBehavior as well since * a number of useful methods are in CtBehavior. * * @see CtClass#getDeclaredConstructors() * @see CtClass#getClassInitializer() * @see CtNewConstructor */ public final class CtConstructor extends CtBehavior { protected CtConstructor(MethodInfo minfo, CtClass declaring) { super(declaring, minfo); } /** * Creates a constructor with no constructor body. * The created constructor * must be added to a class with CtClass.addConstructor(). * *

    The created constructor does not include a constructor body, * which must be specified with setBody(). * * @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, "", desc); setModifiers(Modifier.PUBLIC); } /** * Creates a copy of a CtConstructor object. * The created constructor must be * added to a class with CtClass.addConstructor(). * *

    All occurrences of class names in the created constructor * are replaced with names specified by * map if map is not null. * *

    By default, all the occurrences of the names of the class * declaring src 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 map is null or not. * To prevent this replacement, call ClassMap.fix() * or put() to explicitly specify replacement. * *

    Note: if the .class notation (for example, * String.class) 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 Class.forName() 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 null. * * @see CtClass#addConstructor(CtConstructor) * @see ClassMap#fix(String) */ public CtConstructor(CtConstructor src, CtClass declaring, ClassMap map) throws CannotCompileException { this((MethodInfo)null, declaring); copy(src, true, map); } /** * 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(); } /** * Returns the constructor name followed by parameter types * such as javassist.CtConstructor(CtClass[],CtClass). * * @since 3.5 */ public String getLongName() { return getDeclaringClass().getName() + (isConstructor() ? Descriptor.toString(getSignature()) : ("." + MethodInfo.nameClinit + "()")); } /** * 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 "<clinit>". */ public String getName() { if (methodInfo.isStaticInitializer()) return MethodInfo.nameClinit; else return declaringClass.getSimpleName(); } /** * Returns true if the constructor (or static initializer) * is the default one. This method returns true if the constructor * takes some arguments but it does not perform anything except * calling super() (the no-argument constructor of * the super class). */ public boolean isEmpty() { CodeAttribute ca = getMethodInfo2().getCodeAttribute(); if (ca == null) return false; // native or abstract?? // they are not allowed, though. ConstPool cp = ca.getConstPool(); CodeIterator it = ca.iterator(); try { int pos, desc; int op0 = it.byteAt(it.next()); return op0 == Opcode.RETURN // empty static initializer || (op0 == Opcode.ALOAD_0 && it.byteAt(pos = it.next()) == Opcode.INVOKESPECIAL && (desc = cp.isConstructor(getSuperclassName(), it.u16bitAt(pos + 1))) != 0 && "()V".equals(cp.getUtf8Info(desc)) && it.byteAt(it.next()) == Opcode.RETURN && !it.hasNext()); } catch (BadBytecode e) {} return false; } private String getSuperclassName() { ClassFile cf = declaringClass.getClassFile2(); return cf.getSuperclass(); } /** * Returns true if this constructor calls a constructor * of the super class. This method returns false if it * calls another constructor of this class by this(). */ public boolean callsSuper() throws CannotCompileException { CodeAttribute codeAttr = methodInfo.getCodeAttribute(); if (codeAttr != null) { CodeIterator it = codeAttr.iterator(); try { int index = it.skipSuperConstructor(); return index >= 0; } catch (BadBytecode e) { throw new CannotCompileException(e); } } return false; } /** * Sets a constructor body. * * @param src the source code representing the constructor body. * It must be a single statement or block. * If it is null, the substituted * constructor body does nothing except calling * super(). */ public void setBody(String src) throws CannotCompileException { if (src == null) if (isClassInitializer()) src = ";"; else src = "super();"; super.setBody(src); } /** * Copies a constructor body from another constructor. * *

    All occurrences of the class names in the copied body * are replaced with the names specified by * map if map is not null. * * @param src the method that the body is copied from. * @param map the hashtable associating original class names * with substituted names. * It can be null. */ public void setBody(CtConstructor src, ClassMap map) throws CannotCompileException { setBody0(src.declaringClass, src.methodInfo, declaringClass, methodInfo, map); } /** * Inserts bytecode just after another constructor in the super class * or this class is called. * It does not work if this object represents a class initializer. * * @param src the source code representing the inserted bytecode. * It must be a single statement or block. */ public void insertBeforeBody(String src) throws CannotCompileException { CtClass cc = declaringClass; cc.checkModify(); if (isClassInitializer()) throw new CannotCompileException("class initializer"); 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, cc); 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); methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (CompileError e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException(e); } } /* This method is called by addCatch() in CtBehavior. * super() and this() must not be in a try statement. */ int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException { CodeIterator ci = ca.iterator(); try { ci.skipConstructor(); return ci.next(); } catch (BadBytecode e) { throw new CannotCompileException(e); } } /** * Makes a copy of this constructor and converts it into a method. * The signature of the mehtod is the same as the that of this constructor. * The return type is void. The resulting method must be * appended to the class specified by declaring. * If this constructor is a static initializer, the resulting method takes * no parameter. * *

    An occurrence of another constructor call this() * or a super constructor call super() is * eliminated from the resulting method. * *

    The immediate super class of the class declaring this constructor * must be also a super class of the class declaring the resulting method. * If the constructor accesses a field, the class declaring the resulting method * must also declare a field with the same name and type. * * @param name the name of the resulting method. * @param declaring the class declaring the resulting method. */ public CtMethod toMethod(String name, CtClass declaring) throws CannotCompileException { return toMethod(name, declaring, null); } /** * Makes a copy of this constructor and converts it into a method. * The signature of the method is the same as the that of this constructor. * The return type is void. The resulting method must be * appended to the class specified by declaring. * If this constructor is a static initializer, the resulting method takes * no parameter. * *

    An occurrence of another constructor call this() * or a super constructor call super() is * eliminated from the resulting method. * *

    The immediate super class of the class declaring this constructor * must be also a super class of the class declaring the resulting method * (this is obviously true if the second parameter declaring is * the same as the class declaring this constructor). * If the constructor accesses a field, the class declaring the resulting method * must also declare a field with the same name and type. * * @param name the name of the resulting method. * @param declaring the class declaring the resulting method. * It is normally the same as the class declaring this * constructor. * @param map the hash table associating original class names * with substituted names. The original class names will be * replaced while making a copy. * map can be null. */ public CtMethod toMethod(String name, CtClass declaring, ClassMap map) throws CannotCompileException { CtMethod method = new CtMethod(null, declaring); method.copy(this, false, map); if (isConstructor()) { MethodInfo minfo = method.getMethodInfo2(); CodeAttribute ca = minfo.getCodeAttribute(); if (ca != null) { removeConsCall(ca); try { methodInfo.rebuildStackMapIf6(declaring.getClassPool(), declaring.getClassFile2()); } catch (BadBytecode e) { throw new CannotCompileException(e); } } } method.setName(name); return method; } private static void removeConsCall(CodeAttribute ca) throws CannotCompileException { CodeIterator iterator = ca.iterator(); try { int pos = iterator.skipConstructor(); if (pos >= 0) { int mref = iterator.u16bitAt(pos + 1); String desc = ca.getConstPool().getMethodrefType(mref); int num = Descriptor.numOfParameters(desc) + 1; if (num > 3) pos = iterator.insertGapAt(pos, num - 3, false).position; iterator.writeByte(Opcode.POP, pos++); // this iterator.writeByte(Opcode.NOP, pos); iterator.writeByte(Opcode.NOP, pos + 1); Descriptor.Iterator it = new Descriptor.Iterator(desc); while (true) { it.next(); if (it.isParameter()) iterator.writeByte(it.is2byte() ? Opcode.POP2 : Opcode.POP, pos++); else break; } } } catch (BadBytecode e) { throw new CannotCompileException(e); } } } javassist-3.12.1.ga/src/main/javassist/CtClassType.java0000644000175000017500000014726211221164554022731 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import java.lang.ref.WeakReference; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Set; import javassist.bytecode.AccessFlag; import javassist.bytecode.AttributeInfo; import javassist.bytecode.AnnotationsAttribute; import javassist.bytecode.BadBytecode; import javassist.bytecode.Bytecode; import javassist.bytecode.ClassFile; import javassist.bytecode.CodeAttribute; import javassist.bytecode.ConstantAttribute; import javassist.bytecode.CodeIterator; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.EnclosingMethodAttribute; import javassist.bytecode.FieldInfo; import javassist.bytecode.InnerClassesAttribute; import javassist.bytecode.MethodInfo; import javassist.bytecode.ParameterAnnotationsAttribute; import javassist.bytecode.annotation.Annotation; import javassist.compiler.AccessorMaker; import javassist.compiler.CompileError; import javassist.compiler.Javac; import javassist.expr.ExprEditor; /** * Class types. */ class CtClassType extends CtClass { ClassPool classPool; boolean wasChanged; private boolean wasFrozen; boolean wasPruned; boolean gcConstPool; // if true, the constant pool entries will be garbage collected. ClassFile classfile; byte[] rawClassfile; // backup storage private WeakReference memberCache; private AccessorMaker accessors; private FieldInitLink fieldInitializers; private Hashtable hiddenMethods; // must be synchronous private int uniqueNumberSeed; private boolean doPruning = ClassPool.doPruning; private int getCount; private static final int GET_THRESHOLD = 2; // see compress() CtClassType(String name, ClassPool cp) { super(name); classPool = cp; wasChanged = wasFrozen = wasPruned = gcConstPool = false; classfile = null; rawClassfile = null; memberCache = null; accessors = null; fieldInitializers = null; hiddenMethods = null; uniqueNumberSeed = 0; getCount = 0; } CtClassType(InputStream ins, ClassPool cp) throws IOException { this((String)null, cp); classfile = new ClassFile(new DataInputStream(ins)); qualifiedName = classfile.getName(); } protected void extendToString(StringBuffer buffer) { if (wasChanged) buffer.append("changed "); if (wasFrozen) buffer.append("frozen "); if (wasPruned) buffer.append("pruned "); buffer.append(Modifier.toString(getModifiers())); buffer.append(" class "); buffer.append(getName()); try { CtClass ext = getSuperclass(); if (ext != null) { String name = ext.getName(); if (!name.equals("java.lang.Object")) buffer.append(" extends " + ext.getName()); } } catch (NotFoundException e) { buffer.append(" extends ??"); } try { CtClass[] intf = getInterfaces(); if (intf.length > 0) buffer.append(" implements "); for (int i = 0; i < intf.length; ++i) { buffer.append(intf[i].getName()); buffer.append(", "); } } catch (NotFoundException e) { buffer.append(" extends ??"); } CtMember.Cache memCache = getMembers(); exToString(buffer, " fields=", memCache.fieldHead(), memCache.lastField()); exToString(buffer, " constructors=", memCache.consHead(), memCache.lastCons()); exToString(buffer, " methods=", memCache.methodHead(), memCache.lastMethod()); } private void exToString(StringBuffer buffer, String msg, CtMember head, CtMember tail) { buffer.append(msg); while (head != tail) { head = head.next(); buffer.append(head); buffer.append(", "); } } public AccessorMaker getAccessorMaker() { if (accessors == null) accessors = new AccessorMaker(this); return accessors; } public ClassFile getClassFile2() { ClassFile cfile = classfile; if (cfile != null) return cfile; classPool.compress(); if (rawClassfile != null) { try { classfile = new ClassFile(new DataInputStream( new ByteArrayInputStream(rawClassfile))); rawClassfile = null; getCount = GET_THRESHOLD; return classfile; } catch (IOException e) { throw new RuntimeException(e.toString(), e); } } InputStream fin = null; try { fin = classPool.openClassfile(getName()); if (fin == null) throw new NotFoundException(getName()); fin = new BufferedInputStream(fin); ClassFile cf = new ClassFile(new DataInputStream(fin)); if (!cf.getName().equals(qualifiedName)) throw new RuntimeException("cannot find " + qualifiedName + ": " + cf.getName() + " found in " + qualifiedName.replace('.', '/') + ".class"); classfile = cf; return cf; } catch (NotFoundException e) { throw new RuntimeException(e.toString(), e); } catch (IOException e) { throw new RuntimeException(e.toString(), e); } finally { if (fin != null) try { fin.close(); } catch (IOException e) {} } } /* Inherited from CtClass. Called by get() in ClassPool. * * @see javassist.CtClass#incGetCounter() * @see #toBytecode(DataOutputStream) */ final void incGetCounter() { ++getCount; } /** * Invoked from ClassPool#compress(). * It releases the class files that have not been recently used * if they are unmodified. */ void compress() { if (getCount < GET_THRESHOLD) if (!isModified() && ClassPool.releaseUnmodifiedClassFile) removeClassFile(); else if (isFrozen() && !wasPruned) saveClassFile(); getCount = 0; } /** * Converts a ClassFile object into a byte array * for saving memory space. */ private synchronized void saveClassFile() { /* getMembers() and releaseClassFile() are also synchronized. */ if (classfile == null || hasMemberCache() != null) return; ByteArrayOutputStream barray = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(barray); try { classfile.write(out); barray.close(); rawClassfile = barray.toByteArray(); classfile = null; } catch (IOException e) {} } private synchronized void removeClassFile() { if (classfile != null && !isModified() && hasMemberCache() == null) classfile = null; } public ClassPool getClassPool() { return classPool; } void setClassPool(ClassPool cp) { classPool = cp; } public URL getURL() throws NotFoundException { URL url = classPool.find(getName()); if (url == null) throw new NotFoundException(getName()); else return url; } public boolean isModified() { return wasChanged; } public boolean isFrozen() { return wasFrozen; } public void freeze() { wasFrozen = true; } void checkModify() throws RuntimeException { if (isFrozen()) { String msg = getName() + " class is frozen"; if (wasPruned) msg += " and pruned"; throw new RuntimeException(msg); } wasChanged = true; } public void defrost() { checkPruned("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; // check this in advance although classNameChanged() below does. classPool.checkNotFrozen(name); ClassFile cf = getClassFile2(); super.setName(name); cf.setName(name); nameReplaced(); 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); // check this in advance although classNameChanged() below does. classPool.checkNotFrozen(newClassName); } super.replaceClassName(classnames); ClassFile cf = getClassFile2(); cf.renameClass(classnames); nameReplaced(); 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); nameReplaced(); } } public boolean isInterface() { return Modifier.isInterface(getModifiers()); } public boolean isAnnotation() { return Modifier.isAnnotation(getModifiers()); } public boolean isEnum() { return Modifier.isEnum(getModifiers()); } public int getModifiers() { ClassFile cf = getClassFile2(); int acc = cf.getAccessFlags(); acc = AccessFlag.clear(acc, AccessFlag.SUPER); int inner = cf.getInnerAccessFlags(); if (inner != -1 && (inner & AccessFlag.STATIC) != 0) acc |= AccessFlag.STATIC; return AccessFlag.toModifier(acc); } public CtClass[] getNestedClasses() throws NotFoundException { ClassFile cf = getClassFile2(); InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute(InnerClassesAttribute.tag); if (ica == null) return new CtClass[0]; String thisName = cf.getName(); int n = ica.tableLength(); ArrayList list = new ArrayList(n); for (int i = 0; i < n; i++) { String outer = ica.outerClass(i); /* * If a nested class is local or anonymous, * the outer_class_info_index is 0. */ if (outer == null || outer.equals(thisName)) { String inner = ica.innerClass(i); if (inner != null) list.add(classPool.get(inner)); } } return (CtClass[])list.toArray(new CtClass[list.size()]); } public void setModifiers(int mod) { ClassFile cf = getClassFile2(); if (Modifier.isStatic(mod)) { int flags = cf.getInnerAccessFlags(); if (flags != -1 && (flags & AccessFlag.STATIC) != 0) mod = mod & ~Modifier.STATIC; else throw new RuntimeException("cannot change " + getName() + " into a static class"); } checkModify(); cf.setAccessFlags(AccessFlag.of(mod)); } public boolean hasAnnotation(Class clz) { ClassFile cf = getClassFile2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.visibleTag); return hasAnnotationType(clz, getClassPool(), ainfo, ainfo2); } static boolean hasAnnotationType(Class clz, ClassPool cp, AnnotationsAttribute a1, AnnotationsAttribute a2) { Annotation[] anno1, anno2; if (a1 == null) anno1 = null; else anno1 = a1.getAnnotations(); if (a2 == null) anno2 = null; else anno2 = a2.getAnnotations(); String typeName = clz.getName(); if (anno1 != null) for (int i = 0; i < anno1.length; i++) if (anno1[i].getTypeName().equals(typeName)) return true; if (anno2 != null) for (int i = 0; i < anno2.length; i++) if (anno2[i].getTypeName().equals(typeName)) return true; return false; } public Object getAnnotation(Class clz) throws ClassNotFoundException { ClassFile cf = getClassFile2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.visibleTag); return getAnnotationType(clz, getClassPool(), ainfo, ainfo2); } static Object getAnnotationType(Class clz, ClassPool cp, AnnotationsAttribute a1, AnnotationsAttribute a2) throws ClassNotFoundException { Annotation[] anno1, anno2; if (a1 == null) anno1 = null; else anno1 = a1.getAnnotations(); if (a2 == null) anno2 = null; else anno2 = a2.getAnnotations(); String typeName = clz.getName(); if (anno1 != null) for (int i = 0; i < anno1.length; i++) if (anno1[i].getTypeName().equals(typeName)) return toAnnoType(anno1[i], cp); if (anno2 != null) for (int i = 0; i < anno2.length; i++) if (anno2[i].getTypeName().equals(typeName)) return toAnnoType(anno2[i], cp); return null; } public Object[] getAnnotations() throws ClassNotFoundException { return getAnnotations(false); } public Object[] getAvailableAnnotations(){ try { return getAnnotations(true); } catch (ClassNotFoundException e) { throw new RuntimeException("Unexpected exception ", e); } } private Object[] getAnnotations(boolean ignoreNotFound) throws ClassNotFoundException { ClassFile cf = getClassFile2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.visibleTag); return toAnnotationType(ignoreNotFound, getClassPool(), ainfo, ainfo2); } static Object[] toAnnotationType(boolean ignoreNotFound, ClassPool cp, AnnotationsAttribute a1, AnnotationsAttribute a2) throws ClassNotFoundException { Annotation[] anno1, anno2; int size1, size2; if (a1 == null) { anno1 = null; size1 = 0; } else { anno1 = a1.getAnnotations(); size1 = anno1.length; } if (a2 == null) { anno2 = null; size2 = 0; } else { anno2 = a2.getAnnotations(); size2 = anno2.length; } if (!ignoreNotFound){ Object[] result = new Object[size1 + size2]; for (int i = 0; i < size1; i++) result[i] = toAnnoType(anno1[i], cp); for (int j = 0; j < size2; j++) result[j + size1] = toAnnoType(anno2[j], cp); return result; } else{ ArrayList annotations = new ArrayList(); for (int i = 0 ; i < size1 ; i++){ try{ annotations.add(toAnnoType(anno1[i], cp)); } catch(ClassNotFoundException e){} } for (int j = 0; j < size2; j++) { try{ annotations.add(toAnnoType(anno2[j], cp)); } catch(ClassNotFoundException e){} } return annotations.toArray(); } } static Object[][] toAnnotationType(boolean ignoreNotFound, ClassPool cp, ParameterAnnotationsAttribute a1, ParameterAnnotationsAttribute a2, MethodInfo minfo) throws ClassNotFoundException { int numParameters = 0; if (a1 != null) numParameters = a1.numParameters(); else if (a2 != null) numParameters = a2.numParameters(); else numParameters = Descriptor.numOfParameters(minfo.getDescriptor()); Object[][] result = new Object[numParameters][]; for (int i = 0; i < numParameters; i++) { Annotation[] anno1, anno2; int size1, size2; if (a1 == null) { anno1 = null; size1 = 0; } else { anno1 = a1.getAnnotations()[i]; size1 = anno1.length; } if (a2 == null) { anno2 = null; size2 = 0; } else { anno2 = a2.getAnnotations()[i]; size2 = anno2.length; } if (!ignoreNotFound){ result[i] = new Object[size1 + size2]; for (int j = 0; j < size1; ++j) result[i][j] = toAnnoType(anno1[j], cp); for (int j = 0; j < size2; ++j) result[i][j + size1] = toAnnoType(anno2[j], cp); } else{ ArrayList annotations = new ArrayList(); for (int j = 0 ; j < size1 ; j++){ try{ annotations.add(toAnnoType(anno1[j], cp)); } catch(ClassNotFoundException e){} } for (int j = 0; j < size2; j++){ try{ annotations.add(toAnnoType(anno2[j], cp)); } catch(ClassNotFoundException e){} } result[i] = annotations.toArray(); } } return result; } private static Object toAnnoType(Annotation anno, ClassPool cp) throws ClassNotFoundException { try { ClassLoader cl = cp.getClassLoader(); return anno.toAnnotationType(cl, cp); } catch (ClassNotFoundException e) { ClassLoader cl2 = cp.getClass().getClassLoader(); return anno.toAnnotationType(cl2, cp); } } public boolean subclassOf(CtClass superclass) { if (superclass == null) return false; String superName = superclass.getName(); CtClass curr = this; try { while (curr != null) { if (curr.getName().equals(superName)) 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(); if (isInterface()) addInterface(clazz); else 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 CtClass getDeclaringClass() throws NotFoundException { ClassFile cf = getClassFile2(); InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute( InnerClassesAttribute.tag); if (ica == null) return null; String name = getName(); int n = ica.tableLength(); for (int i = 0; i < n; ++i) if (name.equals(ica.innerClass(i))) { String outName = ica.outerClass(i); if (outName != null) return classPool.get(outName); else { // maybe anonymous or local class. EnclosingMethodAttribute ema = (EnclosingMethodAttribute)cf.getAttribute( EnclosingMethodAttribute.tag); if (ema != null) return classPool.get(ema.className()); } } return null; } public CtMethod getEnclosingMethod() throws NotFoundException { ClassFile cf = getClassFile2(); EnclosingMethodAttribute ema = (EnclosingMethodAttribute)cf.getAttribute( EnclosingMethodAttribute.tag); if (ema != null) { CtClass enc = classPool.get(ema.className()); return enc.getMethod(ema.methodName(), ema.methodDescriptor()); } return null; } public CtClass makeNestedClass(String name, boolean isStatic) { if (!isStatic) throw new RuntimeException( "sorry, only nested static class is supported"); checkModify(); CtClass c = classPool.makeNestedClass(getName() + "$" + name); ClassFile cf = getClassFile2(); ClassFile cf2 = c.getClassFile2(); InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute( InnerClassesAttribute.tag); if (ica == null) { ica = new InnerClassesAttribute(cf.getConstPool()); cf.addAttribute(ica); } ica.append(c.getName(), this.getName(), name, (cf2.getAccessFlags() & ~AccessFlag.SUPER) | AccessFlag.STATIC); cf2.addAttribute(ica.copy(cf2.getConstPool(), null)); return c; } /* flush cached names. */ private void nameReplaced() { CtMember.Cache cache = hasMemberCache(); if (cache != null) { CtMember mth = cache.methodHead(); CtMember tail = cache.lastMethod(); while (mth != tail) { mth = mth.next(); mth.nameReplaced(); } } } /** * Returns null if members are not cached. */ protected CtMember.Cache hasMemberCache() { if (memberCache != null) return (CtMember.Cache)memberCache.get(); else return null; } protected synchronized CtMember.Cache getMembers() { CtMember.Cache cache = null; if (memberCache == null || (cache = (CtMember.Cache)memberCache.get()) == null) { cache = new CtMember.Cache(this); makeFieldCache(cache); makeBehaviorCache(cache); memberCache = new WeakReference(cache); } return cache; } private void makeFieldCache(CtMember.Cache cache) { List list = getClassFile2().getFields(); int n = list.size(); for (int i = 0; i < n; ++i) { FieldInfo finfo = (FieldInfo)list.get(i); CtField newField = new CtField(finfo, this); cache.addField(newField); } } private void makeBehaviorCache(CtMember.Cache cache) { List list = getClassFile2().getMethods(); int n = list.size(); for (int i = 0; i < n; ++i) { MethodInfo minfo = (MethodInfo)list.get(i); if (minfo.isMethod()) { CtMethod newMethod = new CtMethod(minfo, this); cache.addMethod(newMethod); } else { CtConstructor newCons = new CtConstructor(minfo, this); cache.addConstructor(newCons); } } } 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) {} CtMember.Cache memCache = ((CtClassType)cc).getMembers(); CtMember field = memCache.fieldHead(); CtMember tail = memCache.lastField(); while (field != tail) { field = field.next(); if (!Modifier.isPrivate(field.getModifiers())) alist.add(field); } } public CtField getField(String name) throws NotFoundException { CtField f = getField2(name); if (f == null) throw new NotFoundException("field: " + name + " in " + getName()); else return f; } CtField getField2(String name) { CtField df = getDeclaredField2(name); if (df != null) return df; try { CtClass[] ifs = getInterfaces(); int num = ifs.length; for (int i = 0; i < num; ++i) { CtField f = ifs[i].getField2(name); if (f != null) return f; } CtClass s = getSuperclass(); if (s != null) return s.getField2(name); } catch (NotFoundException e) {} return null; } public CtField[] getDeclaredFields() { CtMember.Cache memCache = getMembers(); CtMember field = memCache.fieldHead(); CtMember tail = memCache.lastField(); int num = CtMember.Cache.count(field, tail); CtField[] cfs = new CtField[num]; int i = 0; while (field != tail) { field = field.next(); cfs[i++] = (CtField)field; } return cfs; } public CtField getDeclaredField(String name) throws NotFoundException { CtField f = getDeclaredField2(name); if (f == null) throw new NotFoundException("field: " + name + " in " + getName()); else return f; } private CtField getDeclaredField2(String name) { CtMember.Cache memCache = getMembers(); CtMember field = memCache.fieldHead(); CtMember tail = memCache.lastField(); while (field != tail) { field = field.next(); if (field.getName().equals(name)) return (CtField)field; } return null; } public CtBehavior[] getDeclaredBehaviors() { CtMember.Cache memCache = getMembers(); CtMember cons = memCache.consHead(); CtMember consTail = memCache.lastCons(); int cnum = CtMember.Cache.count(cons, consTail); CtMember mth = memCache.methodHead(); CtMember mthTail = memCache.lastMethod(); int mnum = CtMember.Cache.count(mth, mthTail); CtBehavior[] cb = new CtBehavior[cnum + mnum]; int i = 0; while (cons != consTail) { cons = cons.next(); cb[i++] = (CtBehavior)cons; } while (mth != mthTail) { mth = mth.next(); cb[i++] = (CtBehavior)mth; } return cb; } public CtConstructor[] getConstructors() { CtMember.Cache memCache = getMembers(); CtMember cons = memCache.consHead(); CtMember consTail = memCache.lastCons(); int n = 0; CtMember mem = cons; while (mem != consTail) { mem = mem.next(); if (isPubCons((CtConstructor)mem)) n++; } CtConstructor[] result = new CtConstructor[n]; int i = 0; mem = cons; while (mem != consTail) { mem = mem.next(); CtConstructor cc = (CtConstructor)mem; if (isPubCons(cc)) result[i++] = cc; } return result; } private static boolean isPubCons(CtConstructor cons) { return !Modifier.isPrivate(cons.getModifiers()) && cons.isConstructor(); } public CtConstructor getConstructor(String desc) throws NotFoundException { CtMember.Cache memCache = getMembers(); CtMember cons = memCache.consHead(); CtMember consTail = memCache.lastCons(); while (cons != consTail) { cons = cons.next(); CtConstructor cc = (CtConstructor)cons; if (cc.getMethodInfo2().getDescriptor().equals(desc) && cc.isConstructor()) return cc; } return super.getConstructor(desc); } public CtConstructor[] getDeclaredConstructors() { CtMember.Cache memCache = getMembers(); CtMember cons = memCache.consHead(); CtMember consTail = memCache.lastCons(); int n = 0; CtMember mem = cons; while (mem != consTail) { mem = mem.next(); CtConstructor cc = (CtConstructor)mem; if (cc.isConstructor()) n++; } CtConstructor[] result = new CtConstructor[n]; int i = 0; mem = cons; while (mem != consTail) { mem = mem.next(); CtConstructor cc = (CtConstructor)mem; if (cc.isConstructor()) result[i++] = cc; } return result; } public CtConstructor getClassInitializer() { CtMember.Cache memCache = getMembers(); CtMember cons = memCache.consHead(); CtMember consTail = memCache.lastCons(); while (cons != consTail) { cons = cons.next(); CtConstructor cc = (CtConstructor)cons; if (cc.isClassInitializer()) return cc; } return null; } public CtMethod[] getMethods() { HashMap h = new HashMap(); getMethods0(h, this); return (CtMethod[])h.values().toArray(new CtMethod[h.size()]); } 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) { CtMember.Cache memCache = ((CtClassType)cc).getMembers(); CtMember mth = memCache.methodHead(); CtMember mthTail = memCache.lastMethod(); while (mth != mthTail) { mth = mth.next(); if (!Modifier.isPrivate(mth.getModifiers())) h.put(((CtMethod)mth).getStringRep(), mth); } } } 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) { CtMember.Cache memCache = ((CtClassType)cc).getMembers(); CtMember mth = memCache.methodHead(); CtMember mthTail = memCache.lastMethod(); while (mth != mthTail) { mth = mth.next(); if (mth.getName().equals(name) && ((CtMethod)mth).getMethodInfo2().getDescriptor().equals(desc)) return (CtMethod)mth; } } 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() { CtMember.Cache memCache = getMembers(); CtMember mth = memCache.methodHead(); CtMember mthTail = memCache.lastMethod(); int num = CtMember.Cache.count(mth, mthTail); CtMethod[] cms = new CtMethod[num]; int i = 0; while (mth != mthTail) { mth = mth.next(); cms[i++] = (CtMethod)mth; } return cms; } public CtMethod getDeclaredMethod(String name) throws NotFoundException { CtMember.Cache memCache = getMembers(); CtMember mth = memCache.methodHead(); CtMember mthTail = memCache.lastMethod(); while (mth != mthTail) { mth = mth.next(); if (mth.getName().equals(name)) return (CtMethod)mth; } throw new NotFoundException(name + "(..) is not found in " + getName()); } public CtMethod getDeclaredMethod(String name, CtClass[] params) throws NotFoundException { String desc = Descriptor.ofParameters(params); CtMember.Cache memCache = getMembers(); CtMember mth = memCache.methodHead(); CtMember mthTail = memCache.lastMethod(); while (mth != mthTail) { mth = mth.next(); if (mth.getName().equals(name) && ((CtMethod)mth).getMethodInfo2().getDescriptor().startsWith(desc)) return (CtMethod)mth; } throw new NotFoundException(name + "(..) is not found in " + getName()); } 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(); if (init != null) { init.check(f.getSignature()); int mod = f.getModifiers(); if (Modifier.isStatic(mod) && Modifier.isFinal(mod)) try { ConstPool cp = getClassFile2().getConstPool(); int index = init.getConstantValue(cp, f.getType()); if (index != 0) { f.getFieldInfo2().addAttribute(new ConstantAttribute(cp, index)); init = null; } } catch (NotFoundException e) {} } getMembers().addField(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 removeField(CtField f) throws NotFoundException { checkModify(); FieldInfo fi = f.getFieldInfo2(); ClassFile cf = getClassFile2(); if (cf.getFields().remove(fi)) { getMembers().remove(f); gcConstPool = true; } else throw new NotFoundException(f.toString()); } public CtConstructor makeClassInitializer() throws CannotCompileException { CtConstructor clinit = getClassInitializer(); if (clinit != null) return clinit; checkModify(); ClassFile cf = getClassFile2(); Bytecode code = new Bytecode(cf.getConstPool(), 0, 0); modifyClassConstructor(cf, code, 0, 0); return getClassInitializer(); } public void addConstructor(CtConstructor c) throws CannotCompileException { checkModify(); if (c.getDeclaringClass() != this) throw new CannotCompileException("cannot add"); getMembers().addConstructor(c); getClassFile2().addMethod(c.getMethodInfo2()); } public void removeConstructor(CtConstructor m) throws NotFoundException { checkModify(); MethodInfo mi = m.getMethodInfo2(); ClassFile cf = getClassFile2(); if (cf.getMethods().remove(mi)) { getMembers().remove(m); gcConstPool = true; } else throw new NotFoundException(m.toString()); } public void addMethod(CtMethod m) throws CannotCompileException { checkModify(); if (m.getDeclaringClass() != this) throw new CannotCompileException("bad declaring class"); int mod = m.getModifiers(); if ((getModifiers() & Modifier.INTERFACE) != 0) { m.setModifiers(mod | Modifier.PUBLIC); if ((mod & Modifier.ABSTRACT) == 0) throw new CannotCompileException( "an interface method must be abstract: " + m.toString()); } getMembers().addMethod(m); getClassFile2().addMethod(m.getMethodInfo2()); if ((mod & Modifier.ABSTRACT) != 0) setModifiers(getModifiers() | Modifier.ABSTRACT); } public void removeMethod(CtMethod m) throws NotFoundException { checkModify(); MethodInfo mi = m.getMethodInfo2(); ClassFile cf = getClassFile2(); if (cf.getMethods().remove(mi)) { getMembers().remove(m); gcConstPool = true; } else throw new NotFoundException(m.toString()); } 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); } } /** * @see javassist.CtClass#prune() * @see javassist.CtClass#stopPruning(boolean) */ public void prune() { if (wasPruned) return; wasPruned = wasFrozen = true; getClassFile2().prune(); } public void rebuildClassFile() { gcConstPool = true; } public void toBytecode(DataOutputStream out) throws CannotCompileException, IOException { try { if (isModified()) { checkPruned("toBytecode"); ClassFile cf = getClassFile2(); if (gcConstPool) { cf.compact(); gcConstPool = false; } modifyClassConstructor(cf); modifyConstructors(cf); cf.write(out); out.flush(); fieldInitializers = null; if (doPruning) { // to save memory cf.prune(); wasPruned = true; } } else { classPool.writeClassfile(getName(), out); // to save memory // classfile = null; } getCount = 0; wasFrozen = true; } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (IOException e) { throw new CannotCompileException(e); } } /* See also checkModified() */ private void checkPruned(String method) { if (wasPruned) throw new RuntimeException(method + "(): " + getName() + " was pruned."); } public boolean stopPruning(boolean stop) { boolean prev = !doPruning; doPruning = !stop; return prev; } private void modifyClassConstructor(ClassFile cf) throws CannotCompileException, NotFoundException { if (fieldInitializers == null) return; Bytecode code = new Bytecode(cf.getConstPool(), 0, 0); Javac jv = new Javac(code, this); int stacksize = 0; boolean doInit = false; for (FieldInitLink fi = fieldInitializers; fi != null; fi = fi.next) { CtField f = fi.field; if (Modifier.isStatic(f.getModifiers())) { doInit = true; int s = fi.init.compileIfStatic(f.getType(), f.getName(), code, jv); if (stacksize < s) stacksize = s; } } if (doInit) // need an initializer for static fileds. modifyClassConstructor(cf, code, stacksize, 0); } private void modifyClassConstructor(ClassFile cf, Bytecode code, int stacksize, int localsize) throws CannotCompileException { MethodInfo m = cf.getStaticInitializer(); if (m == null) { code.add(Bytecode.RETURN); code.setMaxStack(stacksize); code.setMaxLocals(localsize); m = new MethodInfo(cf.getConstPool(), "", "()V"); m.setAccessFlags(AccessFlag.STATIC); m.setCodeAttribute(code.toCodeAttribute()); cf.addMethod(m); CtMember.Cache cache = hasMemberCache(); if (cache != null) cache.addConstructor(new CtConstructor(m, this)); } else { CodeAttribute codeAttr = m.getCodeAttribute(); if (codeAttr == null) throw new CannotCompileException("empty "); 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); int maxlocals = codeAttr.getMaxLocals(); if (maxlocals < localsize) codeAttr.setMaxLocals(localsize); } catch (BadBytecode e) { throw new CannotCompileException(e); } } try { m.rebuildStackMapIf6(classPool, cf); } catch (BadBytecode e) { throw new CannotCompileException(e); } } private 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); minfo.rebuildStackMapIf6(classPool, cf); } 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++; } public String makeUniqueName(String prefix) { HashMap table = new HashMap(); makeMemberList(table); Set keys = table.keySet(); String[] methods = new String[keys.size()]; keys.toArray(methods); if (notFindInArray(prefix, methods)) return prefix; int i = 100; String name; do { if (i > 999) throw new RuntimeException("too many unique name"); name = prefix + i++; } while (!notFindInArray(name, methods)); return name; } private static boolean notFindInArray(String prefix, String[] values) { int len = values.length; for (int i = 0; i < len; i++) if (values[i].startsWith(prefix)) return false; return true; } private void makeMemberList(HashMap table) { int mod = getModifiers(); if (Modifier.isAbstract(mod) || Modifier.isInterface(mod)) try { CtClass[] ifs = getInterfaces(); int size = ifs.length; for (int i = 0; i < size; i++) { CtClass ic =ifs[i]; if (ic != null && ic instanceof CtClassType) ((CtClassType)ic).makeMemberList(table); } } catch (NotFoundException e) {} try { CtClass s = getSuperclass(); if (s != null && s instanceof CtClassType) ((CtClassType)s).makeMemberList(table); } catch (NotFoundException e) {} List list = getClassFile2().getMethods(); int n = list.size(); for (int i = 0; i < n; i++) { MethodInfo minfo = (MethodInfo)list.get(i); table.put(minfo.getName(), this); } list = getClassFile2().getFields(); n = list.size(); for (int i = 0; i < n; i++) { FieldInfo finfo = (FieldInfo)list.get(i); table.put(finfo.getName(), this); } } } class FieldInitLink { FieldInitLink next; CtField field; CtField.Initializer init; FieldInitLink(CtField f, CtField.Initializer i) { next = null; field = f; init = i; } } javassist-3.12.1.ga/src/main/javassist/CtNewConstructor.java0000644000175000017500000003027211316070253024006 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import javassist.bytecode.*; import javassist.compiler.Javac; import javassist.compiler.CompileError; import javassist.CtMethod.ConstParameter; /** * A collection of static methods for creating a CtConstructor. * An instance of this class does not make any sense. * *

    A class initializer (static constructor) cannot be created by the * methods in this class. Call makeClassInitializer() in * CtClass and append code snippet to the body of the class * initializer obtained by makeClassInitializer(). * * @see CtClass#addConstructor(CtConstructor) * @see CtClass#makeClassInitializer() */ 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 * Object and passed to a super-class' * constructor. */ public static final int PASS_ARRAY = 1; // an array of parameters /** * Specifies that parameters are passed as is * 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) { // a stack map table has been already created. return (CtConstructor)obj; } } catch (CompileError e) { throw new CannotCompileException(e); } throw new CannotCompileException("not a constructor"); } /** * Creates a public constructor. * * @param parameters a list of the parameter types. * @param exceptions a list of the exception types. * @param body the source text of the constructor body. * It must be a block surrounded by {}. * If it is null, the substituted * constructor body does nothing except calling * super(). * @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); } } /** * Creates a copy of a constructor. * This is a convenience method for calling * {@link CtConstructor#CtConstructor(CtConstructor, CtClass, ClassMap) this constructor}. * See the description of the constructor for particular behavior of the copying. * * @param c the copied constructor. * @param declaring the class to which the created method is added. * @param map the hash table associating original class names * with substituted names. * It can be null. * * @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. * *

    The created constructor takes no parameter. It calls * super(). */ 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(), "", "()V"); } catch (NotFoundException e) { throw new CannotCompileException(e); } code.add(Bytecode.RETURN); // no need to construct a stack map table. 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 parameters but calls the super's * constructor without those parameters (that is, it calls the default * constructor). * *

    The parameters passed to the created constructor should be * used for field initialization. CtField.Initializer * 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 parameters 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. * *

    If howto is PASS_PARAMS, * 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. * *

    If howto is PASS_NONE, * the created constructor calls the super's default constructor. * The superclass must contain a constructor taking no parameters. * *

    If howto is PASS_ARRAY, * the created constructor calls the super's constructor * with the given parameters in the form of an array of * Object. The signature of the super's constructor * must be: * *

      constructor(Object[] params, <type> cvalue) *
    * *

    Here, cvalue is the constant value specified * by cparam. * *

    If cparam is null, the signature * must be: * *

      constructor(Object[] params)
    * *

    If body 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 body must not * be a constructor but a method. * *

    Since the embedded method is wrapped * in parameter-conversion code * as in CtNewMethod.wrapped(), * the constructor parameters are * passed in the form of an array of Object. * The method specified by body must have the * signature shown below: * *

      Object method(Object[] params, <type> cvalue) *
    * *

    If cparam is null, the signature * must be: * *

      Object method(Object[] params)
    * *

    Although the type of the returned value is Object, * the value must be always null. * *

    Example: * *

      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);
    * *

    where the class Sample is as follows: * *

      public class Sample {
           *     public Object m(Object[] args, String msg) {
           *         System.out.println(msg);
           *         return null;
           *     }
           * }
    * *

    This program produces the following class: * *

      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
           *     }
           * }
    * * @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 (PASS_NONE, * PASS_ARRAY, * or PASS_PARAMS) * @param body appended body (may be null). * It must be not a constructor but a method. * @param cparam constant parameter (may be null.) * @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); } } javassist-3.12.1.ga/src/main/javassist/ClassPool.java0000644000175000017500000011375111373521602022425 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; import java.net.URL; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.security.ProtectionDomain; import java.util.Hashtable; import java.util.Iterator; import java.util.ArrayList; import java.util.Enumeration; import javassist.bytecode.Descriptor; /** * A container of CtClass objects. * A CtClass object must be obtained from this object. * If get() is called on this object, * it searches various sources represented by ClassPath * to find a class file and then it creates a CtClass object * representing that class file. The created object is returned to the * caller. * *

    Memory consumption memo: * *

    ClassPool objects hold all the CtClasses * that have been created so that the consistency among modified classes * can be guaranteed. Thus if a large number of CtClasses * are processed, the ClassPool will consume a huge amount * of memory. To avoid this, a ClassPool object * should be recreated, for example, every hundred classes processed. * Note that getDefault() is a singleton factory. * Otherwise, detach() in CtClass should be used * to avoid huge memory consumption. * *

    ClassPool hierarchy: * *

    ClassPools can make a parent-child hierarchy as * java.lang.ClassLoaders. If a ClassPool has * a parent pool, get() first asks the parent pool to find * a class file. Only if the parent could not find the class file, * get() searches the ClassPaths of * the child ClassPool. This search order is reversed if * ClassPath.childFirstLookup is true. * * @see javassist.CtClass * @see javassist.ClassPath */ public class ClassPool { // used by toClass(). private static java.lang.reflect.Method defineClass1, defineClass2; static { try { AccessController.doPrivileged(new PrivilegedExceptionAction(){ public Object run() throws Exception{ Class cl = Class.forName("java.lang.ClassLoader"); defineClass1 = cl.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class }); defineClass2 = cl.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class, ProtectionDomain.class }); return null; } }); } catch (PrivilegedActionException pae) { throw new RuntimeException("cannot initialize ClassPool", pae.getException()); } } /** * Determines the search order. * *

    If this field is true, get() first searches the * class path associated to this ClassPool and then * the class path associated with the parent ClassPool. * Otherwise, the class path associated with the parent is searched * first. * *

    The default value is false. */ public boolean childFirstLookup = false; /** * Turning the automatic pruning on/off. * *

    If this field is true, CtClass objects are * automatically pruned by default when toBytecode() etc. * are called. The automatic pruning can be turned on/off individually * for each CtClass object. * *

    The initial value is false. * * @see CtClass#prune() * @see CtClass#stopPruning(boolean) * @see CtClass#detach() */ public static boolean doPruning = false; private int compressCount; private static final int COMPRESS_THRESHOLD = 100; /* releaseUnmodifiedClassFile was introduced for avoiding a bug of JBoss AOP. So the value should be true except for JBoss AOP. */ /** * If true, unmodified and not-recently-used class files are * periodically released for saving memory. * *

    The initial value is true. */ public static boolean releaseUnmodifiedClassFile = true; protected ClassPoolTail source; protected ClassPool parent; protected Hashtable classes; // should be synchronous /** * Table of registered cflow variables. */ private Hashtable cflow = null; // should be synchronous. private static final int INIT_HASH_SIZE = 191; private ArrayList importedPackages; /** * Creates a root class pool. No parent class pool is specified. */ public ClassPool() { this(null); } /** * Creates a root class pool. If useDefaultPath is * true, appendSystemPath() is called. Otherwise, * this constructor is equivalent to the constructor taking no * parameter. * * @param useDefaultPath true if the system search path is * appended. */ public ClassPool(boolean useDefaultPath) { this(null); if (useDefaultPath) appendSystemPath(); } /** * Creates a class pool. * * @param parent the parent of this class pool. If this is a root * class pool, this parameter must be null. * @see javassist.ClassPool#getDefault() */ public ClassPool(ClassPool parent) { this.classes = new Hashtable(INIT_HASH_SIZE); this.source = new ClassPoolTail(); this.parent = parent; if (parent == null) { CtClass[] pt = CtClass.primitiveTypes; for (int i = 0; i < pt.length; ++i) classes.put(pt[i].getName(), pt[i]); } this.cflow = null; this.compressCount = 0; clearImportedPackages(); } /** * Returns the default class pool. * The returned object is always identical since this method is * a singleton factory. * *

    The default class pool searches the system search path, * which usually includes the platform library, extension * libraries, and the search path specified by the * -classpath option or the CLASSPATH * environment variable. * *

    When this method is called for the first time, the default * class pool is created with the following code snippet: * *

      ClassPool cp = new ClassPool(); * cp.appendSystemPath(); *
    * *

    If the default class pool cannot find any class files, * try ClassClassPath and LoaderClassPath. * * @see ClassClassPath * @see LoaderClassPath */ public static synchronized ClassPool getDefault() { if (defaultPool == null) { defaultPool = new ClassPool(null); defaultPool.appendSystemPath(); } return defaultPool; } private static ClassPool defaultPool = null; /** * Provide a hook so that subclasses can do their own * caching of classes. * * @see #cacheCtClass(String,CtClass,boolean) * @see #removeCached(String) */ protected CtClass getCached(String classname) { return (CtClass)classes.get(classname); } /** * Provides a hook so that subclasses can do their own * caching of classes. * * @see #getCached(String) * @see #removeCached(String,CtClass) */ protected void cacheCtClass(String classname, CtClass c, boolean dynamic) { classes.put(classname, c); } /** * Provide a hook so that subclasses can do their own * caching of classes. * * @see #getCached(String) * @see #cacheCtClass(String,CtClass,boolean) */ protected CtClass removeCached(String classname) { return (CtClass)classes.remove(classname); } /** * Returns the class search path. */ public String toString() { return source.toString(); } /** * This method is periodically invoked so that memory * footprint will be minimized. */ void compress() { if (compressCount++ > COMPRESS_THRESHOLD) { compressCount = 0; Enumeration e = classes.elements(); while (e.hasMoreElements()) ((CtClass)e.nextElement()).compress(); } } /** * Record a package name so that the Javassist compiler searches * the package to resolve a class name. * Don't record the java.lang package, which has * been implicitly recorded by default. * *

    Note that get() in ClassPool does * not search the recorded package. Only the compiler searches it. * * @param packageName the package name. * It must not include the last '.' (dot). * For example, "java.util" is valid but "java.util." is wrong. * @since 3.1 */ public void importPackage(String packageName) { importedPackages.add(packageName); } /** * Clear all the package names recorded by importPackage(). * The java.lang package is not removed. * * @see #importPackage(String) * @since 3.1 */ public void clearImportedPackages() { importedPackages = new ArrayList(); importedPackages.add("java.lang"); } /** * Returns all the package names recorded by importPackage(). * * @see #importPackage(String) * @since 3.1 */ public Iterator getImportedPackages() { return importedPackages.iterator(); } /** * Records a name that never exists. * For example, a package name can be recorded by this method. * This would improve execution performance * since get() does not search the class path at all * if the given name is an invalid name recorded by this method. * Note that searching the class path takes relatively long time. * * @param name a class name (separeted by dot). */ public void recordInvalidClassName(String name) { source.recordInvalidClassName(name); } /** * Records the $cflow variable for the field specified * by cname and fname. * * @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 $cflow variable */ public Object[] lookupCflow(String name) { if (cflow == null) cflow = new Hashtable(); return (Object[])cflow.get(name); } /** * Reads a class file and constructs a CtClass * object with a new name. * This method is useful if you want to generate a new class as a copy * of another class (except the class name). For example, * *

           * getAndRename("Point", "Pair")
           * 
    * * returns a CtClass object representing Pair * class. The definition of Pair is the same as that of * Point class except the class name since Pair * is defined by reading Point.class. * * @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, false); if (clazz == null) throw new NotFoundException(orgName); if (clazz instanceof CtClassType) ((CtClassType)clazz).setClassPool(this); clazz.setName(newName); // indirectly calls // classNameChanged() in this class return clazz; } /* * This method is invoked by CtClassType.setName(). It removes a * CtClass object from the hash table and inserts it with the new * name. Don't delegate to the parent. */ synchronized void classNameChanged(String oldname, CtClass clazz) { CtClass c = (CtClass)getCached(oldname); if (c == clazz) // must check this equation. removeCached(oldname); // see getAndRename(). String newName = clazz.getName(); checkNotFrozen(newName); cacheCtClass(newName, clazz, false); } /** * Reads a class file from the source and returns a reference * to the CtClass * object representing that class file. If that class file has been * already read, this method returns a reference to the * CtClass created when that class file was read at the * first time. * *

    If classname ends with "[]", then this method * returns a CtClass object for that array type. * *

    To obtain an inner class, use "$" instead of "." for separating * the enclosing class name and the inner class name. * * @param classname a fully-qualified class name. */ public CtClass get(String classname) throws NotFoundException { CtClass clazz; if (classname == null) clazz = null; else clazz = get0(classname, true); if (clazz == null) throw new NotFoundException(classname); else { clazz.incGetCounter(); return clazz; } } /** * Reads a class file from the source and returns a reference * to the CtClass * object representing that class file. * This method is equivalent to get except * that it returns null when a class file is * not found and it never throws an exception. * * @param classname a fully-qualified class name. * @return a CtClass object or null. * @see #get(String) * @see #find(String) * @since 3.13 */ public CtClass getOrNull(String classname) { CtClass clazz = null; if (classname == null) clazz = null; else try { /* ClassPool.get0() never throws an exception but its subclass may implement get0 that may throw an exception. */ clazz = get0(classname, true); } catch (NotFoundException e){} if (clazz != null) clazz.incGetCounter(); return clazz; } /** * Returns a CtClass object with the given name. * This is almost equivalent to get(String) except * that classname can be an array-type "descriptor" (an encoded * type name) such as [Ljava/lang/Object;. * *

    Using this method is not recommended; this method should be * used only to obtain the CtClass object * with a name returned from getClassInfo in * javassist.bytecode.ClassPool. getClassInfo * returns a fully-qualified class name but, if the class is an array * type, it returns a descriptor. * * @param classname a fully-qualified class name or a descriptor * representing an array type. * @see #get(String) * @see javassist.bytecode.ConstPool#getClassInfo(int) * @see javassist.bytecode.Descriptor#toCtClass(String, ClassPool) * @since 3.8.1 */ public CtClass getCtClass(String classname) throws NotFoundException { if (classname.charAt(0) == '[') return Descriptor.toCtClass(classname, this); else return get(classname); } /** * @param useCache false if the cached CtClass must be ignored. * @param searchParent false if the parent class pool is not searched. * @return null if the class could not be found. */ protected synchronized CtClass get0(String classname, boolean useCache) throws NotFoundException { CtClass clazz = null; if (useCache) { clazz = getCached(classname); if (clazz != null) return clazz; } if (!childFirstLookup && parent != null) { clazz = parent.get0(classname, useCache); if (clazz != null) return clazz; } clazz = createCtClass(classname, useCache); if (clazz != null) { // clazz.getName() != classname if classname is "[L;". if (useCache) cacheCtClass(clazz.getName(), clazz, false); return clazz; } if (childFirstLookup && parent != null) clazz = parent.get0(classname, useCache); return clazz; } /** * Creates a CtClass object representing the specified class. * It first examines whether or not the corresponding class * file exists. If yes, it creates a CtClass object. * * @return null if the class file could not be found. */ protected CtClass createCtClass(String classname, boolean useCache) { // accept "[L;" as a class name. if (classname.charAt(0) == '[') classname = Descriptor.toClassName(classname); if (classname.endsWith("[]")) { String base = classname.substring(0, classname.indexOf('[')); if ((!useCache || getCached(base) == null) && find(base) == null) return null; else return new CtArray(classname, this); } else if (find(classname) == null) return null; else return new CtClassType(classname, this); } /** * Searches the class path to obtain the URL of the class file * specified by classname. It is also used to determine whether * the class file exists. * * @param classname a fully-qualified class name. * @return null if the class file could not be found. * @see CtClass#getURL() */ public URL find(String classname) { return source.find(classname); } /* * Is invoked by CtClassType.setName() and methods in this class. * This method throws an exception if the class is already frozen or * if this class pool cannot edit the class since it is in a parent * class pool. * * @see checkNotExists(String) */ void checkNotFrozen(String classname) throws RuntimeException { CtClass clazz = getCached(classname); if (clazz == null) { if (!childFirstLookup && parent != null) { try { clazz = parent.get0(classname, true); } catch (NotFoundException e) {} if (clazz != null) throw new RuntimeException(classname + " is in a parent ClassPool. Use the parent."); } } else if (clazz.isFrozen()) throw new RuntimeException(classname + ": frozen class (cannot edit)"); } /* * This method returns null if this or its parent class pool does * not contain a CtClass object with the class name. * * @see checkNotFrozen(String) */ CtClass checkNotExists(String classname) { CtClass clazz = getCached(classname); if (clazz == null) if (!childFirstLookup && parent != null) { try { clazz = parent.get0(classname, true); } catch (NotFoundException e) {} } return clazz; } /* for CtClassType.getClassFile2(). Don't delegate to the parent. */ InputStream openClassfile(String classname) throws NotFoundException { return source.openClassfile(classname); } void writeClassfile(String classname, OutputStream out) throws NotFoundException, IOException, CannotCompileException { source.writeClassfile(classname, out); } /** * Reads class files from the source and returns an array of * CtClass * objects representing those class files. * *

    If an element of classnames ends with "[]", * then this method * returns a CtClass 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 class (or interface) from the given class file. * If there already exists a class with the same name, the new class * overwrites that previous class. * *

    This method is used for creating a CtClass object * directly from a class file. The qualified class name is obtained * from the class file; you do not have to explicitly give the name. * * @param classfile class file. * @throws RuntimeException if there is a frozen class with the * the same name. * @see #makeClassIfNew(InputStream) * @see javassist.ByteArrayClassPath */ public CtClass makeClass(InputStream classfile) throws IOException, RuntimeException { return makeClass(classfile, true); } /** * Creates a new class (or interface) from the given class file. * If there already exists a class with the same name, the new class * overwrites that previous class. * *

    This method is used for creating a CtClass object * directly from a class file. The qualified class name is obtained * from the class file; you do not have to explicitly give the name. * * @param classfile class file. * @param ifNotFrozen throws a RuntimeException if this parameter is true * and there is a frozen class with the same name. * @see javassist.ByteArrayClassPath */ public CtClass makeClass(InputStream classfile, boolean ifNotFrozen) throws IOException, RuntimeException { compress(); classfile = new BufferedInputStream(classfile); CtClass clazz = new CtClassType(classfile, this); clazz.checkModify(); String classname = clazz.getName(); if (ifNotFrozen) checkNotFrozen(classname); cacheCtClass(classname, clazz, true); return clazz; } /** * Creates a new class (or interface) from the given class file. * If there already exists a class with the same name, this method * returns the existing class; a new class is never created from * the given class file. * *

    This method is used for creating a CtClass object * directly from a class file. The qualified class name is obtained * from the class file; you do not have to explicitly give the name. * * @param classfile the class file. * @see #makeClass(InputStream) * @see javassist.ByteArrayClassPath * @since 3.9 */ public CtClass makeClassIfNew(InputStream classfile) throws IOException, RuntimeException { compress(); classfile = new BufferedInputStream(classfile); CtClass clazz = new CtClassType(classfile, this); clazz.checkModify(); String classname = clazz.getName(); CtClass found = checkNotExists(classname); if (found != null) return found; else { cacheCtClass(classname, clazz, true); return clazz; } } /** * Creates a new public class. * If there already exists a class with the same name, the new class * overwrites that previous class. * *

    If no constructor is explicitly added to the created new * class, Javassist generates constructors and adds it when * the class file is generated. It generates a new constructor * for each constructor of the super class. The new constructor * takes the same set of parameters and invokes the * corresponding constructor of the super class. All the received * parameters are passed to it. * * @param classname a fully-qualified class name. * @throws 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. * *

    If no constructor is explicitly added to the created new * class, Javassist generates constructors and adds it when * the class file is generated. It generates a new constructor * for each constructor of the super class. The new constructor * takes the same set of parameters and invokes the * corresponding constructor of the super class. All the received * parameters are passed to it. * * @param classname a fully-qualified class name. * @param superclass the super class. * @throws RuntimeException if the existing class is frozen. */ public synchronized CtClass makeClass(String classname, CtClass superclass) throws RuntimeException { checkNotFrozen(classname); CtClass clazz = new CtNewClass(classname, this, false, superclass); cacheCtClass(classname, clazz, true); return clazz; } /** * Creates a new public nested class. * This method is called by CtClassType.makeNestedClass(). * * @param classname a fully-qualified class name. * @return the nested class. */ synchronized CtClass makeNestedClass(String classname) { checkNotFrozen(classname); CtClass clazz = new CtNewNestedClass(classname, this, false, null); cacheCtClass(classname, clazz, true); 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. * @throws 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. * @throws RuntimeException if the existing interface is frozen. */ public synchronized CtClass makeInterface(String name, CtClass superclass) throws RuntimeException { checkNotFrozen(name); CtClass clazz = new CtNewClass(name, this, true, superclass); cacheCtClass(name, clazz, true); return clazz; } /** * 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 * -classpath option or the CLASSPATH * environment variable. * * @return the appended class path. */ public ClassPath appendSystemPath() { return source.appendSystemPath(); } /** * Insert a ClassPath object at the head of the * search path. * * @return the inserted class path. * @see javassist.ClassPath * @see javassist.URLClassPath * @see javassist.ByteArrayClassPath */ public ClassPath insertClassPath(ClassPath cp) { return source.insertClassPath(cp); } /** * Appends a ClassPath object to the end of the * search path. * * @return the appended class path. * @see javassist.ClassPath * @see javassist.URLClassPath * @see javassist.ByteArrayClassPath */ public ClassPath appendClassPath(ClassPath cp) { return 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 ("/"). * If the path name ends with "/*", then all the * jar files matching the path name are inserted. * * @return the inserted class path. * @throws NotFoundException if the jar file is not found. */ public ClassPath insertClassPath(String pathname) throws NotFoundException { return 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 ("/"). * If the path name ends with "/*", then all the * jar files matching the path name are appended. * * @return the appended class path. * @throws NotFoundException if the jar file is not found. */ public ClassPath appendClassPath(String pathname) throws NotFoundException { return source.appendClassPath(pathname); } /** * Detatches the ClassPath object from the search path. * The detached ClassPath object cannot be added * to the pathagain. */ public void removeClassPath(ClassPath cp) { source.removeClassPath(cp); } /** * Appends directories and jar files for search. * *

    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 ("/"). * @throws 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; } } } /** * Converts the given class to a java.lang.Class object. * Once this method is called, further modifications are not * allowed any more. * To load the class, this method uses the context class loader * of the current thread. It is obtained by calling * getClassLoader(). * *

    This behavior can be changed by subclassing the pool and changing * the getClassLoader() method. * If the program is running on some application * server, the context class loader might be inappropriate to load the * class. * *

    This method is provided for convenience. If you need more * complex functionality, you should write your own class loader. * *

    Warining: A Class object returned by this method may not * work with a security manager or a signed jar file because a * protection domain is not specified. * * @see #toClass(CtClass, java.lang.ClassLoader, ProtectionDomain) * @see #getClassLoader() */ public Class toClass(CtClass clazz) throws CannotCompileException { // Some subclasses of ClassPool may override toClass(CtClass,ClassLoader). // So we should call that method instead of toClass(.., ProtectionDomain). return toClass(clazz, getClassLoader()); } /** * Get the classloader for toClass(), getAnnotations() in * CtClass, etc. * *

    The default is the context class loader. * * @return the classloader for the pool * @see #toClass(CtClass) * @see CtClass#getAnnotations() */ public ClassLoader getClassLoader() { return getContextClassLoader(); } /** * Obtains a class loader that seems appropriate to look up a class * by name. */ static ClassLoader getContextClassLoader() { return Thread.currentThread().getContextClassLoader(); } /** * Converts the class to a java.lang.Class object. * Do not override this method any more at a subclass because * toClass(CtClass) never calls this method. * *

    Warining: A Class object returned by this method may not * work with a security manager or a signed jar file because a * protection domain is not specified. * * @deprecated Replaced by {@link #toClass(CtClass,ClassLoader,ProtectionDomain)}. * A subclass of ClassPool that has been * overriding this method should be modified. It should override * {@link #toClass(CtClass,ClassLoader,ProtectionDomain)}. */ public Class toClass(CtClass ct, ClassLoader loader) throws CannotCompileException { return toClass(ct, loader, null); } /** * Converts the class to a java.lang.Class object. * Once this method is called, further modifications are not allowed * any more. * *

    The class file represented by the given CtClass is * loaded by the given class loader to construct a * java.lang.Class object. Since a private method * on the class loader is invoked through the reflection API, * the caller must have permissions to do that. * *

    An easy way to obtain ProtectionDomain object is * to call getProtectionDomain() * in java.lang.Class. It returns the domain that the * class belongs to. * *

    This method is provided for convenience. If you need more * complex functionality, you should write your own class loader. * * @param loader the class loader used to load this class. * For example, the loader returned by * getClassLoader() can be used * for this parameter. * @param domain the protection domain for the class. * If it is null, the default domain created * by java.lang.ClassLoader is used. * * @see #getClassLoader() * @since 3.3 */ public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain) throws CannotCompileException { try { byte[] b = ct.toBytecode(); java.lang.reflect.Method method; Object[] args; if (domain == null) { method = defineClass1; args = new Object[] { ct.getName(), b, new Integer(0), new Integer(b.length)}; } else { method = defineClass2; args = new Object[] { ct.getName(), b, new Integer(0), new Integer(b.length), domain}; } return toClass2(method, loader, args); } catch (RuntimeException e) { throw e; } catch (java.lang.reflect.InvocationTargetException e) { throw new CannotCompileException(e.getTargetException()); } catch (Exception e) { throw new CannotCompileException(e); } } private static synchronized Class toClass2(Method method, ClassLoader loader, Object[] args) throws Exception { method.setAccessible(true); try { return (Class)method.invoke(loader, args); } finally { method.setAccessible(false); } } } javassist-3.12.1.ga/src/main/javassist/CodeConverter.java0000644000175000017500000007054311170755271023277 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import javassist.bytecode.*; import javassist.convert.*; /** * Simple translator of method bodies * (also see the javassist.expr package). * *

    Instances of this class specifies how to instrument of the * bytecodes representing a method body. They are passed to * CtClass.instrument() or * CtMethod.instrument() as a parameter. * *

    Example: *

       * 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);
       * 
    * *

    This program substitutes "Singleton.makePoint()" * for all occurrences of "new Point()" * appearing in methods declared in a Client class. * * @see javassist.CtClass#instrument(CodeConverter) * @see javassist.CtMethod#instrument(CodeConverter) * @see javassist.expr.ExprEditor */ public class CodeConverter { protected 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, * replaceNew(ctPoint, ctSingleton, "createPoint") * (where ctPoint and ctSingleton are * compile-time classes for class Point and class * Singleton, respectively) * replaces all occurrences of: * *

      new Point(x, y)
    * * in the method body with: * *
      Singleton.createPoint(x, y)
    * *

    This enables to intercept instantiation of Point * and change the samentics. For example, the following * createPoint() implements the singleton pattern: * *

      public static Point createPoint(int x, int y) {
           *     if (aPoint == null)
           *         aPoint = new Point(x, y);
           *     return aPoint;
           * }
           * 
    * *

    The static method call substituted for the original new * 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. * *

    The return type of the substituted static method must be * the exactly same as the type of the instantiated class specified by * newClass. * * @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 instantiation of the class * specified by oldClass * is replaced with instantiation of another class newClass. * For example, * replaceNew(ctPoint, ctPoint2) * (where ctPoint and ctPoint2 are * compile-time classes for class Point and class * Point2, respectively) * replaces all occurrences of: * *

      new Point(x, y)
    * * in the method body with: * *
      new Point2(x, y)
    * *

    Note that Point2 must be type-compatible with Point. * It must have the same set of methods, fields, and constructors as the * replaced class. */ public void replaceNew(CtClass oldClass, CtClass newClass) { transformers = new TransformNewClass(transformers, oldClass.getName(), newClass.getName()); } /** * Modify a method body so that field read/write expressions access * a different field from the original one. * *

    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. * *

    Also, clazz and newClass 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 static 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. * *

    For example, the program below * *

      Point p = new Point();
           * int newX = p.x + 3;
    * *

    can be translated into: * *

      Point p = new Point();
           * int newX = Accessor.readX(p) + 3;
    * *

    where * *

      public class Accessor {
           *     public static int readX(Object target) { ... }
           * }
    * *

    The type of the parameter of readX() must * be java.lang.Object independently of the actual * type of target. 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 void. * *

    For example, the program below * *

      Point p = new Point();
           * p.x = 3;
    * *

    can be translated into: * *

      Point p = new Point();
           * Accessor.writeX(3);
    * *

    where * *

      public class Accessor {
           *     public static void writeX(Object target, int value) { ... }
           * }
    * *

    The type of the first parameter of writeX() must * be java.lang.Object independently of the actual * type of target. 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 a method body, so that ALL accesses to an array are replaced with * calls to static methods within another class. In the case of reading an * element from the array, this is replaced with a call to a static method with * the array and the index as arguments, the return value is the value read from * the array. If writing to an array, this is replaced with a call to a static * method with the array, index and new value as parameters, the return value of * the static method is void. * *

    The calledClass parameter is the class containing the static methods to be used * for array replacement. The names parameter points to an implementation of * ArrayAccessReplacementMethodNames which specifies the names of the method to be * used for access for each type of array. For example reading from an int[] will * require a different method than if writing to an int[], and writing to a long[] * will require a different method than if writing to a byte[]. If the implementation * of ArrayAccessReplacementMethodNames does not contain the name for access for a * type of array, that access is not replaced. * *

    A default implementation of ArrayAccessReplacementMethodNames called * DefaultArrayAccessReplacementMethodNames has been provided and is what is used in the * following example. This also assumes that 'foo.ArrayAdvisor' is the name of the * CtClass passed in. * *

    If we have the following class: *

    class POJO{
         *    int[] ints = new int[]{1, 2, 3, 4, 5};
         *    long[] longs = new int[]{10, 20, 30};
         *    Object objects = new Object[]{true, false};
         *    Integer[] integers = new Integer[]{new Integer(10)};
         * }
         * 
    * and this is accessed as: *
    POJO p = new POJO();
         * 
         * //Write to int array
         * p.ints[2] = 7;
         * 
         * //Read from int array
         * int i = p.ints[2];
         * 
         * //Write to long array
         * p.longs[2] = 1000L;
         * 
         * //Read from long array
         * long l = p.longs[2];
         * 
         * //Write to Object array
         * p.objects[2] = "Hello";
         * 
         * //Read from Object array
         * Object o = p.objects[2];
         * 
         * //Write to Integer array
         * Integer integer = new Integer(5);
         * p.integers[0] = integer;
         * 
         * //Read from Object array
         * integer = p.integers[0];
         * 
    * * Following instrumentation we will have *
    POJO p = new POJO();
         * 
         * //Write to int array
         * ArrayAdvisor.arrayWriteInt(p.ints, 2, 7);
         * 
         * //Read from int array
         * int i = ArrayAdvisor.arrayReadInt(p.ints, 2);
         * 
         * //Write to long array
         * ArrayAdvisor.arrayWriteLong(p.longs, 2, 1000L);
         * 
         * //Read from long array
         * long l = ArrayAdvisor.arrayReadLong(p.longs, 2);
         * 
         * //Write to Object array
         * ArrayAdvisor.arrayWriteObject(p.objects, 2, "Hello");
         * 
         * //Read from Object array
         * Object o = ArrayAdvisor.arrayReadObject(p.objects, 2);
         * 
         * //Write to Integer array
         * Integer integer = new Integer(5);
         * ArrayAdvisor.arrayWriteObject(p.integers, 0, integer);
         * 
         * //Read from Object array
         * integer = ArrayAdvisor.arrayWriteObject(p.integers, 0);
         * 
    * * @see DefaultArrayAccessReplacementMethodNames * * @param calledClass the class containing the static methods. * @param names contains the names of the methods to replace * the different kinds of array access with. */ public void replaceArrayAccess(CtClass calledClass, ArrayAccessReplacementMethodNames names) throws NotFoundException { transformers = new TransformAccessArrayField(transformers, calledClass.getName(), names); } /** * Modify method invocations in a method body so that a different * method will be invoked. * *

    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: " + substMethod.getLongName()); int mod1 = origMethod.getModifiers(); int mod2 = substMethod.getModifiers(); if (Modifier.isStatic(mod1) != Modifier.isStatic(mod2) || (Modifier.isPrivate(mod1) && !Modifier.isPrivate(mod2)) || origMethod.getDeclaringClass().isInterface() != substMethod.getDeclaringClass().isInterface()) throw new CannotCompileException("invoke-type mismatch " + substMethod.getLongName()); transformers = new TransformCall(transformers, origMethod, substMethod); } /** * Correct invocations to a method that has been renamed. * If a method is renamed, calls to that method must be also * modified so that the method with the new name will be called. * *

    The method must be declared in the same class before and * after it is renamed. * *

    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. * * @param oldMethodName the old name of the method. * @param newMethod the method with the new name. * @see javassist.CtMethod#setName(String) */ public void redirectMethodCall(String oldMethodName, CtMethod newMethod) throws CannotCompileException { transformers = new TransformCall(transformers, oldMethodName, newMethod); } /** * Insert a call to another method before an existing method call. * That "before" method must be static. The return type must be * void. 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 * move(): * *

      class Point {
           *     Point move(int x, int y) { ... }
           * }
    * *

    Then the before method must be something like this: * *

      class Verbose {
           *     static void print(Point target, int x, int y) { ... }
           * }
    * *

    The CodeConverter would translate bytecode * equivalent to: * *

      Point p2 = p.move(x + y, 0);
    * *

    into the bytecode equivalent to: * *

      int tmp1 = x + y;
           * int tmp2 = 0;
           * Verbose.print(p, tmp1, tmp2);
           * Point p2 = p.move(tmp1, tmp2);
    * * @param origMethod the method originally invoked. * @param beforeMethod the method invoked before * origMethod. */ 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 * void. 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 * move(): * *
      class Point {
           *     Point move(int x, int y) { ... }
           * }
    * *

    Then the after method must be something like this: * *

      class Verbose {
           *     static void print(Point target, int x, int y) { ... }
           * }
    * *

    The CodeConverter would translate bytecode * equivalent to: * *

      Point p2 = p.move(x + y, 0);
    * *

    into the bytecode equivalent to: * *

      int tmp1 = x + y;
           * int tmp2 = 0;
           * Point p2 = p.move(tmp1, tmp2);
           * Verbose.print(p, tmp1, tmp2);
    * * @param origMethod the method originally invoked. * @param afterMethod the method invoked after * origMethod. */ 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. */ protected 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, clazz, minfo); 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; int stack = 0; for (t = transformers; t != null; t = t.getNext()) { int s = t.extraLocals(); if (s > locals) locals = s; s = t.extraStack(); if (s > stack) stack = s; } for (t = transformers; t != null; t = t.getNext()) t.clean(); if (locals > 0) codeAttr.setMaxLocals(codeAttr.getMaxLocals() + locals); if (stack > 0) codeAttr.setMaxStack(codeAttr.getMaxStack() + stack); } /** * Interface containing the method names to be used * as array access replacements. * * @author Kabir Khan * @version $Revision: 1.16 $ */ public interface ArrayAccessReplacementMethodNames { /** * Returns the name of a static method with the signature * (Ljava/lang/Object;I)B to replace reading from a byte[]. */ String byteOrBooleanRead(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;IB)V to replace writing to a byte[]. */ String byteOrBooleanWrite(); /** * @return the name of a static method with the signature * (Ljava/lang/Object;I)C to replace reading from a char[]. */ String charRead(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;IC)V to replace writing to a byte[]. */ String charWrite(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;I)D to replace reading from a double[]. */ String doubleRead(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;ID)V to replace writing to a double[]. */ String doubleWrite(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;I)F to replace reading from a float[]. */ String floatRead(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;IF)V to replace writing to a float[]. */ String floatWrite(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;I)I to replace reading from a int[]. */ String intRead(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;II)V to replace writing to a int[]. */ String intWrite(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;I)J to replace reading from a long[]. */ String longRead(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;IJ)V to replace writing to a long[]. */ String longWrite(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;I)Ljava/lang/Object; * to replace reading from a Object[] (or any subclass of object). */ String objectRead(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;ILjava/lang/Object;)V * to replace writing to a Object[] (or any subclass of object). */ String objectWrite(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;I)S to replace reading from a short[]. */ String shortRead(); /** * Returns the name of a static method with the signature * (Ljava/lang/Object;IS)V to replace writing to a short[]. */ String shortWrite(); } /** * Default implementation of the ArrayAccessReplacementMethodNames * interface giving default values for method names to be used for replacing * accesses to array elements. * * @author Kabir Khan * @version $Revision: 1.16 $ */ public static class DefaultArrayAccessReplacementMethodNames implements ArrayAccessReplacementMethodNames { /** * Returns "arrayReadByteOrBoolean" as the name of the static method with the signature * (Ljava/lang/Object;I)B to replace reading from a byte[]. */ public String byteOrBooleanRead() { return "arrayReadByteOrBoolean"; } /** * Returns "arrayWriteByteOrBoolean" as the name of the static method with the signature * (Ljava/lang/Object;IB)V to replace writing to a byte[]. */ public String byteOrBooleanWrite() { return "arrayWriteByteOrBoolean"; } /** * Returns "arrayReadChar" as the name of the static method with the signature * (Ljava/lang/Object;I)C to replace reading from a char[]. */ public String charRead() { return "arrayReadChar"; } /** * Returns "arrayWriteChar" as the name of the static method with the signature * (Ljava/lang/Object;IC)V to replace writing to a byte[]. */ public String charWrite() { return "arrayWriteChar"; } /** * Returns "arrayReadDouble" as the name of the static method with the signature * (Ljava/lang/Object;I)D to replace reading from a double[]. */ public String doubleRead() { return "arrayReadDouble"; } /** * Returns "arrayWriteDouble" as the name of the static method with the signature * (Ljava/lang/Object;ID)V to replace writing to a double[]. */ public String doubleWrite() { return "arrayWriteDouble"; } /** * Returns "arrayReadFloat" as the name of the static method with the signature * (Ljava/lang/Object;I)F to replace reading from a float[]. */ public String floatRead() { return "arrayReadFloat"; } /** * Returns "arrayWriteFloat" as the name of the static method with the signature * (Ljava/lang/Object;IF)V to replace writing to a float[]. */ public String floatWrite() { return "arrayWriteFloat"; } /** * Returns "arrayReadInt" as the name of the static method with the signature * (Ljava/lang/Object;I)I to replace reading from a int[]. */ public String intRead() { return "arrayReadInt"; } /** * Returns "arrayWriteInt" as the name of the static method with the signature * (Ljava/lang/Object;II)V to replace writing to a int[]. */ public String intWrite() { return "arrayWriteInt"; } /** * Returns "arrayReadLong" as the name of the static method with the signature * (Ljava/lang/Object;I)J to replace reading from a long[]. */ public String longRead() { return "arrayReadLong"; } /** * Returns "arrayWriteLong" as the name of the static method with the signature * (Ljava/lang/Object;IJ)V to replace writing to a long[]. */ public String longWrite() { return "arrayWriteLong"; } /** * Returns "arrayReadObject" as the name of the static method with the signature * (Ljava/lang/Object;I)Ljava/lang/Object; to replace reading from a Object[] (or any subclass of object). */ public String objectRead() { return "arrayReadObject"; } /** * Returns "arrayWriteObject" as the name of the static method with the signature * (Ljava/lang/Object;ILjava/lang/Object;)V to replace writing to a Object[] (or any subclass of object). */ public String objectWrite() { return "arrayWriteObject"; } /** * Returns "arrayReadShort" as the name of the static method with the signature * (Ljava/lang/Object;I)S to replace reading from a short[]. */ public String shortRead() { return "arrayReadShort"; } /** * Returns "arrayWriteShort" as the name of the static method with the signature * (Ljava/lang/Object;IS)V to replace writing to a short[]. */ public String shortWrite() { return "arrayWriteShort"; } } } javassist-3.12.1.ga/src/main/javassist/runtime/0000755000175000017500000000000011637463227021351 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/runtime/Desc.java0000644000175000017500000001157110770207070023064 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.runtime; /** * A support class for implementing $sig and * $type. * This support class is required at runtime * only if $sig or $type is used. */ public class Desc { /** * Specifies how a java.lang.Class object is loaded. * *

    If true, it is loaded by: *

      Thread.currentThread().getContextClassLoader().loadClass()
    *

    If false, it is loaded by Class.forName(). * 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 $class. */ public static Class getClazz(String name) { try { return getClassObject(name); } catch (ClassNotFoundException e) { throw new RuntimeException( "$class: internal error, could not find class '" + name + "' (Desc.useContextClassLoader: " + Boolean.toString(useContextClassLoader) + ")", e); } } /** * Interprets the given type descriptor representing a method * signature. It is used for implementing $sig. */ 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 $type. */ 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) { // "new RuntimeException(e)" is not available in JDK 1.3. throw new RuntimeException(e.getMessage()); } return result; } } javassist-3.12.1.ga/src/main/javassist/runtime/DotClass.java0000644000175000017500000000205110630701321023704 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.runtime; /** * A support class for implementing .class notation. * This is required at runtime * only if .class notation is used in source code given * to the Javassist compiler. */ public class DotClass { public static NoClassDefFoundError fail(ClassNotFoundException e) { return new NoClassDefFoundError(e.getMessage()); } } javassist-3.12.1.ga/src/main/javassist/runtime/Cflow.java0000644000175000017500000000277610630701321023260 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.runtime; /** * A support class for implementing $cflow. * This support class is required at runtime * only if $cflow 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(); } } javassist-3.12.1.ga/src/main/javassist/runtime/package.html0000644000175000017500000000055707651544225023640 0ustar twernertwerner Runtime support classes required by modified bytecode.

    This package includes support classes that may be required by classes modified with Javassist. Note that most of the modified classes do not require these support classes. See the documentation of every support class to know which kind of modification needs a support class. javassist-3.12.1.ga/src/main/javassist/runtime/Inner.java0000644000175000017500000000163010630701321023245 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.runtime; /** * A support class for compiling a method declared in an inner class. * This support class is required at runtime * only if the method calls a private constructor in the enclosing class. */ public class Inner { } javassist-3.12.1.ga/src/main/javassist/util/0000755000175000017500000000000011637463437020646 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/util/proxy/0000755000175000017500000000000011637463436022026 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/util/proxy/SerializedProxy.java0000644000175000017500000000665411357307760026035 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.util.proxy; import java.io.Serializable; import java.io.ObjectStreamException; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.security.ProtectionDomain; /** * A proxy object is converted into an instance of this class * when it is written to an output stream. * * @see RuntimeSupport#makeSerializedProxy(Object) */ class SerializedProxy implements Serializable { private String superClass; private String[] interfaces; private byte[] filterSignature; private MethodHandler handler; SerializedProxy(Class proxy, byte[] sig, MethodHandler h) { filterSignature = sig; handler = h; superClass = proxy.getSuperclass().getName(); Class[] infs = proxy.getInterfaces(); int n = infs.length; interfaces = new String[n - 1]; String setterInf = ProxyObject.class.getName(); for (int i = 0; i < n; i++) { String name = infs[i].getName(); if (!name.equals(setterInf)) interfaces[i] = name; } } /** * Load class. * * @param className the class name * @return loaded class * @throws ClassNotFoundException for any error */ protected Class loadClass(final String className) throws ClassNotFoundException { try { return (Class)AccessController.doPrivileged(new PrivilegedExceptionAction(){ public Object run() throws Exception{ ClassLoader cl = Thread.currentThread().getContextClassLoader(); return Class.forName(className, true, cl); } }); } catch (PrivilegedActionException pae) { throw new RuntimeException("cannot load the class: " + className, pae.getException()); } } Object readResolve() throws ObjectStreamException { try { int n = interfaces.length; Class[] infs = new Class[n]; for (int i = 0; i < n; i++) infs[i] = loadClass(interfaces[i]); ProxyFactory f = new ProxyFactory(); f.setSuperclass(loadClass(superClass)); f.setInterfaces(infs); ProxyObject proxy = (ProxyObject)f.createClass(filterSignature).newInstance(); proxy.setHandler(handler); return proxy; } catch (ClassNotFoundException e) { throw new java.io.InvalidClassException(e.getMessage()); } catch (InstantiationException e2) { throw new java.io.InvalidObjectException(e2.getMessage()); } catch (IllegalAccessException e3) { throw new java.io.InvalidClassException(e3.getMessage()); } } } javassist-3.12.1.ga/src/main/javassist/util/proxy/SecurityActions.java0000755000175000017500000001166110764760400026020 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.util.proxy; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; class SecurityActions { static Method[] getDeclaredMethods(final Class clazz) { if (System.getSecurityManager() == null) return clazz.getDeclaredMethods(); else { return (Method[]) AccessController .doPrivileged(new PrivilegedAction() { public Object run() { return clazz.getDeclaredMethods(); } }); } } static Constructor[] getDeclaredConstructors(final Class clazz) { if (System.getSecurityManager() == null) return clazz.getDeclaredConstructors(); else { return (Constructor[]) AccessController .doPrivileged(new PrivilegedAction() { public Object run() { return clazz.getDeclaredConstructors(); } }); } } static Method getDeclaredMethod(final Class clazz, final String name, final Class[] types) throws NoSuchMethodException { if (System.getSecurityManager() == null) return clazz.getDeclaredMethod(name, types); else { try { return (Method) AccessController .doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { return clazz.getDeclaredMethod(name, types); } }); } catch (PrivilegedActionException e) { if (e.getCause() instanceof NoSuchMethodException) throw (NoSuchMethodException) e.getCause(); throw new RuntimeException(e.getCause()); } } } static Constructor getDeclaredConstructor(final Class clazz, final Class[] types) throws NoSuchMethodException { if (System.getSecurityManager() == null) return clazz.getDeclaredConstructor(types); else { try { return (Constructor) AccessController .doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { return clazz.getDeclaredConstructor(types); } }); } catch (PrivilegedActionException e) { if (e.getCause() instanceof NoSuchMethodException) throw (NoSuchMethodException) e.getCause(); throw new RuntimeException(e.getCause()); } } } static void setAccessible(final AccessibleObject ao, final boolean accessible) { if (System.getSecurityManager() == null) ao.setAccessible(accessible); else { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { ao.setAccessible(accessible); return null; } }); } } static void set(final Field fld, final Object target, final Object value) throws IllegalAccessException { if (System.getSecurityManager() == null) fld.set(target, value); else { try { AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { fld.set(target, value); return null; } }); } catch (PrivilegedActionException e) { if (e.getCause() instanceof NoSuchMethodException) throw (IllegalAccessException) e.getCause(); throw new RuntimeException(e.getCause()); } } } } javassist-3.12.1.ga/src/main/javassist/util/proxy/MethodFilter.java0000644000175000017500000000174610630701321025243 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.util.proxy; import java.lang.reflect.Method; /** * Selector of the methods implemented by a handler. * * @see ProxyFactory#setFilter(MethodFilter) */ public interface MethodFilter { /** * Returns true if the given method is implemented by a handler. */ boolean isHandled(Method m); } javassist-3.12.1.ga/src/main/javassist/util/proxy/ProxyFactory.java0000644000175000017500000014234711361031277025341 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.util.proxy; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Constructor; import java.lang.reflect.Member; import java.lang.reflect.Modifier; import java.security.ProtectionDomain; import java.util.*; import java.lang.ref.WeakReference; import javassist.CannotCompileException; import javassist.bytecode.*; /* * This class is implemented only with the lower-level API of Javassist. * This design decision is for maximizing performance. */ /** * Factory of dynamic proxy classes. * *

    This factory generates a class that extends the given super class and implements * the given interfaces. The calls of the methods inherited from the super class are * forwarded and then invoke() is called on the method handler * associated with instances of the generated class. The calls of the methods from * the interfaces are also forwarded to the method handler. * *

    For example, if the following code is executed, * *

       * ProxyFactory f = new ProxyFactory();
       * f.setSuperclass(Foo.class);
       * f.setFilter(new MethodFilter() {
       *     public boolean isHandled(Method m) {
       *         // ignore finalize()
       *         return !m.getName().equals("finalize");
       *     }
       * });
       * Class c = f.createClass();
       * MethodHandler mi = new MethodHandler() {
       *     public Object invoke(Object self, Method m, Method proceed,
       *                          Object[] args) throws Throwable {
       *         System.out.println("Name: " + m.getName());
       *         return proceed.invoke(self, args);  // execute the original method.
       *     }
       * };
       * Foo foo = (Foo)c.newInstance();
       * ((ProxyObject)foo).setHandler(mi);
       * 
    * *

    Then, the following method call will be forwarded to MethodHandler * mi and prints a message before executing the originally called method * bar() in Foo. * *

       * foo.bar();
       * 
    * *

    The last three lines of the code shown above can be replaced with a call to * the helper method create, which generates a proxy class, instantiates * it, and sets the method handler of the instance: * *

       *     :
       * Foo foo = (Foo)f.create(new Class[0], new Object[0], mi);
       * 
    * *

    To change the method handler during runtime, * execute the following code: * *

       * MethodHandler mi = ... ;    // alternative handler
       * ((ProxyObject)foo).setHandler(mi);
       * 
    * *

    If setHandler is never called for a proxy instance then it will * employ the default handler which proceeds by invoking the original method. * The behaviour of the default handler is identical to the following * handler: * *

       * class EmptyHandler implements MethodHandler {
       *     public Object invoke(Object self, Method m,
       *                          Method proceed, Object[] args) throws Exception {
       *         return proceed.invoke(self, args);
       *     }
       * }
       * 
    * *

    A proxy factory caches and reuses proxy classes by default. It is possible to reset * this default globally by setting static field {@link ProxyFactory#useCache} to false. * Caching may also be configured for a specific factory by calling instance method * {@link ProxyFactory#setUseCache(boolean)}. It is strongly recommended that new clients * of class ProxyFactory enable caching. Failure to do so may lead to exhaustion of * the heap memory area used to store classes. * *

    Caching is automatically disabled for any given proxy factory if deprecated instance * method {@link ProxyFactory#setHandler(MethodHandler)} is called. This method was * used to specify a default handler which newly created proxy classes should install * when they create their instances. It is only retained to provide backward compatibility * with previous releases of javassist. Unfortunately,this legacy behaviour makes caching * and reuse of proxy classes impossible. The current programming model expects javassist * clients to set the handler of a proxy instance explicitly by calling method * {@link ProxyObject#setHandler(MethodHandler)} as shown in the sample code above. New * clients are strongly recommended to use this model rather than calling * {@link ProxyFactory#setHandler(MethodHandler)}. * *

    A proxy object generated by ProxyFactory is serializable * if its super class or any of its interfaces implement java.io.Serializable. * However, a serialized proxy object may not be compatible with future releases. * The serialization support should be used for short-term storage or RMI. * *

    For compatibility with older releases serialization of proxy objects is implemented by * adding a writeReplace method to the proxy class. This allows a proxy to be serialized * to a conventional {@link java.io.ObjectOutputStream} and deserialized from a corresponding * {@link java.io.ObjectInputStream}. However this method suffers from several problems, the most * notable one being that it fails to serialize state inherited from the proxy's superclass. *

    * An alternative method of serializing proxy objects is available which fixes these problems. It * requires inhibiting generation of the writeReplace method and instead using instances of * {@link javassist.util.proxy.ProxyObjectOutputStream} and {@link javassist.util.proxy.ProxyObjectInputStream} * (which are subclasses of {@link java.io.ObjectOutputStream} and {@link java.io.ObjectInputStream}) * to serialize and deserialize, respectively, the proxy. These streams recognise javassist proxies and ensure * that they are serialized and deserialized without the need for the proxy class to implement special methods * such as writeReplace. Generation of the writeReplace method can be disabled globally by setting static field * {@link ProxyFactory#useWriteReplace} to false. Alternatively, it may be * configured per factory by calling instance method {@link ProxyFactory#setUseWriteReplace(boolean)}. * * @see MethodHandler * @since 3.1 * @author Muga Nishizawa * @author Shigeru Chiba * @author Andrew Dinn */ public class ProxyFactory { private Class superClass; private Class[] interfaces; private MethodFilter methodFilter; private MethodHandler handler; // retained for legacy usage private List signatureMethods; private byte[] signature; private String classname; private String basename; private String superName; private Class thisClass; /** * per factory setting initialised from current setting for useCache but able to be reset before each create call */ private boolean factoryUseCache; /** * per factory setting initialised from current setting for useWriteReplace but able to be reset before each create call */ private boolean factoryWriteReplace; /** * If the value of this variable is not null, the class file of * the generated proxy class is written under the directory specified * by this variable. For example, if the value is * ".", then the class file is written under the current * directory. This method is for debugging. * *

    The default value is null. */ public String writeDirectory; private static final Class OBJECT_TYPE = Object.class; private static final String HOLDER = "_methods_"; private static final String HOLDER_TYPE = "[Ljava/lang/reflect/Method;"; private static final String FILTER_SIGNATURE_FIELD = "_filter_signature"; private static final String FILTER_SIGNATURE_TYPE = "[B"; private static final String HANDLER = "handler"; private static final String NULL_INTERCEPTOR_HOLDER = "javassist.util.proxy.RuntimeSupport"; private static final String DEFAULT_INTERCEPTOR = "default_interceptor"; private static final String HANDLER_TYPE = 'L' + MethodHandler.class.getName().replace('.', '/') + ';'; private static final String HANDLER_SETTER = "setHandler"; private static final String HANDLER_SETTER_TYPE = "(" + HANDLER_TYPE + ")V"; private static final String HANDLER_GETTER = "getHandler"; private static final String HANDLER_GETTER_TYPE = "()" + HANDLER_TYPE; private static final String SERIAL_VERSION_UID_FIELD = "serialVersionUID"; private static final String SERIAL_VERSION_UID_TYPE = "J"; private static final int SERIAL_VERSION_UID_VALUE = -1; /** * If true, a generated proxy class is cached and it will be reused * when generating the proxy class with the same properties is requested. * The default value is true. * * Note that this value merely specifies the initial setting employed by any newly created * proxy factory. The factory setting may be overwritten by calling factory instance method * {@link #setUseCache(boolean)} * * @since 3.4 */ public static volatile boolean useCache = true; /** * If true, a generated proxy class will implement method writeReplace enabling * serialization of its proxies to a conventional ObjectOutputStream. this (default) * setting retains the old javassist behaviour which has the advantage that it * retains compatibility with older releases and requires no extra work on the part * of the client performing the serialization. However, it has the disadvantage that * state inherited from the superclasses of the proxy is lost during serialization. * if false then serialization/deserialization of the proxy instances will preserve * all fields. However, serialization must be performed via a {@link ProxyObjectOutputStream} * and deserialization must be via {@link ProxyObjectInputStream}. Any attempt to serialize * proxies whose class was created with useWriteReplace set to false via a normal * {@link java.io.ObjectOutputStream} will fail. * * Note that this value merely specifies the initial setting employed by any newly created * proxy factory. The factory setting may be overwritten by calling factory instance method * {@link #setUseWriteReplace(boolean)} * * @since 3.4 */ public static volatile boolean useWriteReplace = true; /* * methods allowing individual factory settings for factoryUseCache and factoryWriteReplace to be reset */ /** * test whether this factory uses the proxy cache * @return true if this factory uses the proxy cache otherwise false */ public boolean isUseCache() { return factoryUseCache; } /** * configure whether this factory should use the proxy cache * @param useCache true if this factory should use the proxy cache and false if it should not use the cache * @throws RuntimeException if a default interceptor has been set for the factory */ public void setUseCache(boolean useCache) { // we cannot allow caching to be used if the factory is configured to install a default interceptor // field into generated classes if (handler != null && useCache) { throw new RuntimeException("caching cannot be enabled if the factory default interceptor has been set"); } factoryUseCache = useCache; } /** * test whether this factory installs a writeReplace method in created classes * @return true if this factory installs a writeReplace method in created classes otherwise false */ public boolean isUseWriteReplace() { return factoryWriteReplace; } /** * configure whether this factory should add a writeReplace method to created classes * @param useWriteReplace true if this factory should add a writeReplace method to created classes and false if it * should not add a writeReplace method */ public void setUseWriteReplace(boolean useWriteReplace) { factoryWriteReplace = useWriteReplace; } private static WeakHashMap proxyCache = new WeakHashMap(); /** * determine if a class is a javassist proxy class * @param cl * @return true if the class is a javassist proxy class otherwise false */ public static boolean isProxyClass(Class cl) { // all proxies implement ProxyObject. nothing else should. return (ProxyObject.class.isAssignableFrom(cl)); } /** * used to store details of a specific proxy class in the second tier of the proxy cache. this entry * will be located in a hashmap keyed by the unique identifying name of the proxy class. the hashmap is * located in a weak hashmap keyed by the classloader common to all proxy classes in the second tier map. */ static class ProxyDetails { /** * the unique signature of any method filter whose behaviour will be met by this class. each bit in * the byte array is set if the filter redirects the corresponding super or interface method and clear * if it does not redirect it. */ byte[] signature; /** * a hexadecimal string representation of the signature bit sequence. this string also forms part * of the proxy class name. */ WeakReference proxyClass; /** * a flag which is true this class employs writeReplace to perform serialization of its instances * and false if serialization must employ of a ProxyObjectOutputStream and ProxyObjectInputStream */ boolean isUseWriteReplace; ProxyDetails(byte[] signature, Class proxyClass, boolean isUseWriteReplace) { this.signature = signature; this.proxyClass = new WeakReference(proxyClass); this.isUseWriteReplace = isUseWriteReplace; } } /** * Constructs a factory of proxy class. */ public ProxyFactory() { superClass = null; interfaces = null; methodFilter = null; handler = null; signature = null; signatureMethods = null; thisClass = null; writeDirectory = null; factoryUseCache = useCache; factoryWriteReplace = useWriteReplace; } /** * Sets the super class of a proxy class. */ public void setSuperclass(Class clazz) { superClass = clazz; // force recompute of signature signature = null; } /** * Obtains the super class set by setSuperclass(). * * @since 3.4 */ public Class getSuperclass() { return superClass; } /** * Sets the interfaces of a proxy class. */ public void setInterfaces(Class[] ifs) { interfaces = ifs; // force recompute of signature signature = null; } /** * Obtains the interfaces set by setInterfaces. * * @since 3.4 */ public Class[] getInterfaces() { return interfaces; } /** * Sets a filter that selects the methods that will be controlled by a handler. */ public void setFilter(MethodFilter mf) { methodFilter = mf; // force recompute of signature signature = null; } /** * Generates a proxy class using the current filter. */ public Class createClass() { if (signature == null) { computeSignature(methodFilter); } return createClass1(); } /** * Generates a proxy class using the supplied filter. */ public Class createClass(MethodFilter filter) { computeSignature(filter); return createClass1(); } /** * Generates a proxy class with a specific signature. * access is package local so ProxyObjectInputStream can use this * @param signature * @return */ Class createClass(byte[] signature) { installSignature(signature); return createClass1(); } private Class createClass1() { if (thisClass == null) { ClassLoader cl = getClassLoader(); synchronized (proxyCache) { if (factoryUseCache) createClass2(cl); else createClass3(cl); } } // don't retain any unwanted references Class result = thisClass; thisClass = null; return result; } private static char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; public String getKey(Class superClass, Class[] interfaces, byte[] signature, boolean useWriteReplace) { StringBuffer sbuf = new StringBuffer(); if (superClass != null){ sbuf.append(superClass.getName()); } sbuf.append(":"); for (int i = 0; i < interfaces.length; i++) { sbuf.append(interfaces[i].getName()); sbuf.append(":"); } for (int i = 0; i < signature.length; i++) { byte b = signature[i]; int lo = b & 0xf; int hi = (b >> 4) & 0xf; sbuf.append(hexDigits[lo]); sbuf.append(hexDigits[hi]); } if (useWriteReplace) { sbuf.append(":w"); } return sbuf.toString(); } private void createClass2(ClassLoader cl) { String key = getKey(superClass, interfaces, signature, factoryWriteReplace); /* * Excessive concurrency causes a large memory footprint and slows the * execution speed down (with JDK 1.5). Thus, we use a jumbo lock for * reducing concrrency. */ // synchronized (proxyCache) { HashMap cacheForTheLoader = (HashMap)proxyCache.get(cl); ProxyDetails details; if (cacheForTheLoader == null) { cacheForTheLoader = new HashMap(); proxyCache.put(cl, cacheForTheLoader); } details = (ProxyDetails)cacheForTheLoader.get(key); if (details != null) { WeakReference reference = details.proxyClass; thisClass = (Class)reference.get(); if (thisClass != null) { return; } } createClass3(cl); details = new ProxyDetails(signature, thisClass, factoryWriteReplace); cacheForTheLoader.put(key, details); // } } private void createClass3(ClassLoader cl) { // we need a new class so we need a new class name allocateClassName(); try { ClassFile cf = make(); if (writeDirectory != null) FactoryHelper.writeFile(cf, writeDirectory); thisClass = FactoryHelper.toClass(cf, cl, getDomain()); setField(FILTER_SIGNATURE_FIELD, signature); // legacy behaviour : we only set the default interceptor static field if we are not using the cache if (!factoryUseCache) { setField(DEFAULT_INTERCEPTOR, handler); } } catch (CannotCompileException e) { throw new RuntimeException(e.getMessage(), e); } } private void setField(String fieldName, Object value) { if (thisClass != null && value != null) try { Field f = thisClass.getField(fieldName); SecurityActions.setAccessible(f, true); f.set(null, value); SecurityActions.setAccessible(f, false); } catch (Exception e) { throw new RuntimeException(e); } } static byte[] getFilterSignature(Class clazz) { return (byte[])getField(clazz, FILTER_SIGNATURE_FIELD); } private static Object getField(Class clazz, String fieldName) { try { Field f = clazz.getField(fieldName); f.setAccessible(true); Object value = f.get(null); f.setAccessible(false); return value; } catch (Exception e) { throw new RuntimeException(e); } } /** * A provider of class loaders. * * @see #classLoaderProvider * @since 3.4 */ public static interface ClassLoaderProvider { /** * Returns a class loader. * * @param pf a proxy factory that is going to obtain a class loader. */ public ClassLoader get(ProxyFactory pf); } /** * A provider used by createClass() for obtaining * a class loader. * get() on this ClassLoaderProvider object * is called to obtain a class loader. * *

    The value of this field can be updated for changing the default * implementation. * *

    Example: *

           * ProxyFactory.classLoaderProvider = new ProxyFactory.ClassLoaderProvider() {
           *     public ClassLoader get(ProxyFactory pf) {
           *         return Thread.currentThread().getContextClassLoader();
           *     }
           * };
           * 
    * * @since 3.4 */ public static ClassLoaderProvider classLoaderProvider = new ClassLoaderProvider() { public ClassLoader get(ProxyFactory pf) { return pf.getClassLoader0(); } }; protected ClassLoader getClassLoader() { return classLoaderProvider.get(this); } protected ClassLoader getClassLoader0() { ClassLoader loader = null; if (superClass != null && !superClass.getName().equals("java.lang.Object")) loader = superClass.getClassLoader(); else if (interfaces != null && interfaces.length > 0) loader = interfaces[0].getClassLoader(); if (loader == null) { loader = getClass().getClassLoader(); // In case javassist is in the endorsed dir if (loader == null) { loader = Thread.currentThread().getContextClassLoader(); if (loader == null) loader = ClassLoader.getSystemClassLoader(); } } return loader; } protected ProtectionDomain getDomain() { Class clazz; if (superClass != null && !superClass.getName().equals("java.lang.Object")) clazz = superClass; else if (interfaces != null && interfaces.length > 0) clazz = interfaces[0]; else clazz = this.getClass(); return clazz.getProtectionDomain(); } /** * Creates a proxy class and returns an instance of that class. * * @param paramTypes parameter types for a constructor. * @param args arguments passed to a constructor. * @param mh the method handler for the proxy class. * @since 3.4 */ public Object create(Class[] paramTypes, Object[] args, MethodHandler mh) throws NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { Object obj = create(paramTypes, args); ((ProxyObject)obj).setHandler(mh); return obj; } /** * Creates a proxy class and returns an instance of that class. * * @param paramTypes parameter types for a constructor. * @param args arguments passed to a constructor. */ public Object create(Class[] paramTypes, Object[] args) throws NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { Class c = createClass(); Constructor cons = c.getConstructor(paramTypes); return cons.newInstance(args); } /** * Sets the default invocation handler. This invocation handler is shared * among all the instances of a proxy class unless another is explicitly * specified. * @deprecated since 3.12 * use of this method is incompatible with proxy class caching. * instead clients should call method {@link ProxyObject#setHandler(MethodHandler)} to set the handler * for each newly created proxy instance. * calling this method will automatically disable caching of classes created by the proxy factory. */ public void setHandler(MethodHandler mi) { // if we were using the cache and the handler is non-null then we must stop caching if (factoryUseCache && mi != null) { factoryUseCache = false; // clear any currently held class so we don't try to reuse it or set its handler field thisClass = null; } handler = mi; // this retains the behaviour of the old code which resets any class we were holding on to // this is probably not what is wanted setField(DEFAULT_INTERCEPTOR, handler); } private static int counter = 0; private static synchronized String makeProxyName(String classname) { return classname + "_$$_javassist_" + counter++; } private ClassFile make() throws CannotCompileException { ClassFile cf = new ClassFile(false, classname, superName); cf.setAccessFlags(AccessFlag.PUBLIC); setInterfaces(cf, interfaces); ConstPool pool = cf.getConstPool(); // legacy: we only add the static field for the default interceptor if caching is disabled if (!factoryUseCache) { FieldInfo finfo = new FieldInfo(pool, DEFAULT_INTERCEPTOR, HANDLER_TYPE); finfo.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); cf.addField(finfo); } // handler is per instance FieldInfo finfo2 = new FieldInfo(pool, HANDLER, HANDLER_TYPE); finfo2.setAccessFlags(AccessFlag.PRIVATE); cf.addField(finfo2); // filter signature is per class FieldInfo finfo3 = new FieldInfo(pool, FILTER_SIGNATURE_FIELD, FILTER_SIGNATURE_TYPE); finfo3.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC); cf.addField(finfo3); // the proxy class serial uid must always be a fixed value FieldInfo finfo4 = new FieldInfo(pool, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE); finfo4.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC| AccessFlag.FINAL); cf.addField(finfo4); // HashMap allMethods = getMethods(superClass, interfaces); // int size = allMethods.size(); makeConstructors(classname, cf, pool, classname); int s = overrideMethods(cf, pool, classname); addMethodsHolder(cf, pool, classname, s); addSetter(classname, cf, pool); addGetter(classname, cf, pool); if (factoryWriteReplace) { try { cf.addMethod(makeWriteReplace(pool)); } catch (DuplicateMemberException e) { // writeReplace() is already declared in the super class/interfaces. } } thisClass = null; return cf; } private void checkClassAndSuperName() { if (interfaces == null) interfaces = new Class[0]; if (superClass == null) { superClass = OBJECT_TYPE; superName = superClass.getName(); basename = interfaces.length == 0 ? superName : interfaces[0].getName(); } else { superName = superClass.getName(); basename = superName; } if (Modifier.isFinal(superClass.getModifiers())) throw new RuntimeException(superName + " is final"); if (basename.startsWith("java.")) basename = "org.javassist.tmp." + basename; } private void allocateClassName() { classname = makeProxyName(basename); } private static Comparator sorter = new Comparator() { public int compare(Object o1, Object o2) { Map.Entry e1 = (Map.Entry)o1; Map.Entry e2 = (Map.Entry)o2; String key1 = (String)e1.getKey(); String key2 = (String)e2.getKey(); return key1.compareTo(key2); } }; private void makeSortedMethodList() { checkClassAndSuperName(); HashMap allMethods = getMethods(superClass, interfaces); signatureMethods = new ArrayList(allMethods.entrySet()); Collections.sort(signatureMethods, sorter); } private void computeSignature(MethodFilter filter) // throws CannotCompileException { makeSortedMethodList(); int l = signatureMethods.size(); int maxBytes = ((l + 7) >> 3); signature = new byte[maxBytes]; for (int idx = 0; idx < l; idx++) { Map.Entry e = (Map.Entry)signatureMethods.get(idx); Method m = (Method)e.getValue(); int mod = m.getModifiers(); if (!Modifier.isFinal(mod) && !Modifier.isStatic(mod) && isVisible(mod, basename, m) && (filter == null || filter.isHandled(m))) { setBit(signature, idx); } } } private void installSignature(byte[] signature) // throws CannotCompileException { makeSortedMethodList(); int l = signatureMethods.size(); int maxBytes = ((l + 7) >> 3); if (signature.length != maxBytes) { throw new RuntimeException("invalid filter signature length for deserialized proxy class"); } this.signature = signature; } private boolean testBit(byte[] signature, int idx) { int byteIdx = idx >> 3; if (byteIdx > signature.length) { return false; } else { int bitIdx = idx & 0x7; int mask = 0x1 << bitIdx; int sigByte = signature[byteIdx]; return ((sigByte & mask) != 0); } } private void setBit(byte[] signature, int idx) { int byteIdx = idx >> 3; if (byteIdx < signature.length) { int bitIdx = idx & 0x7; int mask = 0x1 << bitIdx; int sigByte = signature[byteIdx]; signature[byteIdx] = (byte)(sigByte | mask); } } private static void setInterfaces(ClassFile cf, Class[] interfaces) { String setterIntf = ProxyObject.class.getName(); String[] list; if (interfaces == null || interfaces.length == 0) list = new String[] { setterIntf }; else { list = new String[interfaces.length + 1]; for (int i = 0; i < interfaces.length; i++) list[i] = interfaces[i].getName(); list[interfaces.length] = setterIntf; } cf.setInterfaces(list); } private static void addMethodsHolder(ClassFile cf, ConstPool cp, String classname, int size) throws CannotCompileException { FieldInfo finfo = new FieldInfo(cp, HOLDER, HOLDER_TYPE); finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.STATIC); cf.addField(finfo); MethodInfo minfo = new MethodInfo(cp, "", "()V"); minfo.setAccessFlags(AccessFlag.STATIC); Bytecode code = new Bytecode(cp, 0, 0); code.addIconst(size * 2); code.addAnewarray("java.lang.reflect.Method"); code.addPutstatic(classname, HOLDER, HOLDER_TYPE); // also need to set serial version uid code.addLconst(-1L); code.addPutstatic(classname, SERIAL_VERSION_UID_FIELD, SERIAL_VERSION_UID_TYPE); code.addOpcode(Bytecode.RETURN); minfo.setCodeAttribute(code.toCodeAttribute()); cf.addMethod(minfo); } private static void addSetter(String classname, ClassFile cf, ConstPool cp) throws CannotCompileException { MethodInfo minfo = new MethodInfo(cp, HANDLER_SETTER, HANDLER_SETTER_TYPE); minfo.setAccessFlags(AccessFlag.PUBLIC); Bytecode code = new Bytecode(cp, 2, 2); code.addAload(0); code.addAload(1); code.addPutfield(classname, HANDLER, HANDLER_TYPE); code.addOpcode(Bytecode.RETURN); minfo.setCodeAttribute(code.toCodeAttribute()); cf.addMethod(minfo); } private static void addGetter(String classname, ClassFile cf, ConstPool cp) throws CannotCompileException { MethodInfo minfo = new MethodInfo(cp, HANDLER_GETTER, HANDLER_GETTER_TYPE); minfo.setAccessFlags(AccessFlag.PUBLIC); Bytecode code = new Bytecode(cp, 1, 1); code.addAload(0); code.addGetfield(classname, HANDLER, HANDLER_TYPE); code.addOpcode(Bytecode.ARETURN); minfo.setCodeAttribute(code.toCodeAttribute()); cf.addMethod(minfo); } private int overrideMethods(ClassFile cf, ConstPool cp, String className) throws CannotCompileException { String prefix = makeUniqueName("_d", signatureMethods); Iterator it = signatureMethods.iterator(); int index = 0; while (it.hasNext()) { Map.Entry e = (Map.Entry)it.next(); String key = (String)e.getKey(); Method meth = (Method)e.getValue(); int mod = meth.getModifiers(); if (testBit(signature, index)) { override(className, meth, prefix, index, keyToDesc(key), cf, cp); } index++; } return index; } private void override(String thisClassname, Method meth, String prefix, int index, String desc, ClassFile cf, ConstPool cp) throws CannotCompileException { Class declClass = meth.getDeclaringClass(); String delegatorName = prefix + index + meth.getName(); if (Modifier.isAbstract(meth.getModifiers())) delegatorName = null; else { MethodInfo delegator = makeDelegator(meth, desc, cp, declClass, delegatorName); // delegator is not a bridge method. See Sec. 15.12.4.5 of JLS 3rd Ed. delegator.setAccessFlags(delegator.getAccessFlags() & ~AccessFlag.BRIDGE); cf.addMethod(delegator); } MethodInfo forwarder = makeForwarder(thisClassname, meth, desc, cp, declClass, delegatorName, index); cf.addMethod(forwarder); } private void makeConstructors(String thisClassName, ClassFile cf, ConstPool cp, String classname) throws CannotCompileException { Constructor[] cons = SecurityActions.getDeclaredConstructors(superClass); // legacy: if we are not caching then we need to initialise the default handler boolean doHandlerInit = !factoryUseCache; for (int i = 0; i < cons.length; i++) { Constructor c = cons[i]; int mod = c.getModifiers(); if (!Modifier.isFinal(mod) && !Modifier.isPrivate(mod) && isVisible(mod, basename, c)) { MethodInfo m = makeConstructor(thisClassName, c, cp, superClass, doHandlerInit); cf.addMethod(m); } } } private static String makeUniqueName(String name, List sortedMethods) { if (makeUniqueName0(name, sortedMethods.iterator())) return name; for (int i = 100; i < 999; i++) { String s = name + i; if (makeUniqueName0(s, sortedMethods.iterator())) return s; } throw new RuntimeException("cannot make a unique method name"); } private static boolean makeUniqueName0(String name, Iterator it) { while (it.hasNext()) { Map.Entry e = (Map.Entry)it.next(); String key = (String)e.getKey(); if (key.startsWith(name)) return false; } return true; } /** * Returns true if the method is visible from the package. * * @param mod the modifiers of the method. */ private static boolean isVisible(int mod, String from, Member meth) { if ((mod & Modifier.PRIVATE) != 0) return false; else if ((mod & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) return true; else { String p = getPackageName(from); String q = getPackageName(meth.getDeclaringClass().getName()); if (p == null) return q == null; else return p.equals(q); } } private static String getPackageName(String name) { int i = name.lastIndexOf('.'); if (i < 0) return null; else return name.substring(0, i); } private static HashMap getMethods(Class superClass, Class[] interfaceTypes) { HashMap hash = new HashMap(); for (int i = 0; i < interfaceTypes.length; i++) getMethods(hash, interfaceTypes[i]); getMethods(hash, superClass); return hash; } private static void getMethods(HashMap hash, Class clazz) { Class[] ifs = clazz.getInterfaces(); for (int i = 0; i < ifs.length; i++) getMethods(hash, ifs[i]); Class parent = clazz.getSuperclass(); if (parent != null) getMethods(hash, parent); Method[] methods = SecurityActions.getDeclaredMethods(clazz); for (int i = 0; i < methods.length; i++) if (!Modifier.isPrivate(methods[i].getModifiers())) { Method m = methods[i]; String key = m.getName() + ':' + RuntimeSupport.makeDescriptor(m); // JIRA JASSIST-85 // put the method to the cache, retrieve previous definition (if any) Method oldMethod = (Method)hash.put(key, methods[i]); // check if visibility has been reduced if (null != oldMethod && Modifier.isPublic(oldMethod.getModifiers()) && !Modifier.isPublic(methods[i].getModifiers()) ) { // we tried to overwrite a public definition with a non-public definition, // use the old definition instead. hash.put(key, oldMethod); } } } private static String keyToDesc(String key) { return key.substring(key.indexOf(':') + 1); } private static MethodInfo makeConstructor(String thisClassName, Constructor cons, ConstPool cp, Class superClass, boolean doHandlerInit) { String desc = RuntimeSupport.makeDescriptor(cons.getParameterTypes(), Void.TYPE); MethodInfo minfo = new MethodInfo(cp, "", desc); minfo.setAccessFlags(Modifier.PUBLIC); // cons.getModifiers() & ~Modifier.NATIVE setThrows(minfo, cp, cons.getExceptionTypes()); Bytecode code = new Bytecode(cp, 0, 0); // legacy: if we are not using caching then we initialise the instance's handler // from the class's static default interceptor and skip the next few instructions if // it is non-null if (doHandlerInit) { code.addAload(0); code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE); code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE); code.addGetstatic(thisClassName, DEFAULT_INTERCEPTOR, HANDLER_TYPE); code.addOpcode(Opcode.IFNONNULL); code.addIndex(10); } // if caching is enabled then we don't have a handler to initialise so this else branch will install // the handler located in the static field of class RuntimeSupport. code.addAload(0); code.addGetstatic(NULL_INTERCEPTOR_HOLDER, DEFAULT_INTERCEPTOR, HANDLER_TYPE); code.addPutfield(thisClassName, HANDLER, HANDLER_TYPE); int pc = code.currentPc(); code.addAload(0); int s = addLoadParameters(code, cons.getParameterTypes(), 1); code.addInvokespecial(superClass.getName(), "", desc); code.addOpcode(Opcode.RETURN); code.setMaxLocals(s + 1); CodeAttribute ca = code.toCodeAttribute(); minfo.setCodeAttribute(ca); StackMapTable.Writer writer = new StackMapTable.Writer(32); writer.sameFrame(pc); ca.setAttribute(writer.toStackMapTable(cp)); return minfo; } private static MethodInfo makeDelegator(Method meth, String desc, ConstPool cp, Class declClass, String delegatorName) { MethodInfo delegator = new MethodInfo(cp, delegatorName, desc); delegator.setAccessFlags(Modifier.FINAL | Modifier.PUBLIC | (meth.getModifiers() & ~(Modifier.PRIVATE | Modifier.PROTECTED | Modifier.ABSTRACT | Modifier.NATIVE | Modifier.SYNCHRONIZED))); setThrows(delegator, cp, meth); Bytecode code = new Bytecode(cp, 0, 0); code.addAload(0); int s = addLoadParameters(code, meth.getParameterTypes(), 1); code.addInvokespecial(declClass.getName(), meth.getName(), desc); addReturn(code, meth.getReturnType()); code.setMaxLocals(++s); delegator.setCodeAttribute(code.toCodeAttribute()); return delegator; } /** * @param delegatorName null if the original method is abstract. */ private static MethodInfo makeForwarder(String thisClassName, Method meth, String desc, ConstPool cp, Class declClass, String delegatorName, int index) { MethodInfo forwarder = new MethodInfo(cp, meth.getName(), desc); forwarder.setAccessFlags(Modifier.FINAL | (meth.getModifiers() & ~(Modifier.ABSTRACT | Modifier.NATIVE | Modifier.SYNCHRONIZED))); setThrows(forwarder, cp, meth); int args = Descriptor.paramSize(desc); Bytecode code = new Bytecode(cp, 0, args + 2); /* * if (methods[index * 2] == null) { * methods[index * 2] * = RuntimeSupport.findSuperMethod(this, , ); * methods[index * 2 + 1] * = RuntimeSupport.findMethod(this, , ); * or = null // the original method is abstract. * } * return ($r)handler.invoke(this, methods[index * 2], * methods[index * 2 + 1], $args); */ int origIndex = index * 2; int delIndex = index * 2 + 1; int arrayVar = args + 1; code.addGetstatic(thisClassName, HOLDER, HOLDER_TYPE); code.addAstore(arrayVar); callFind2Methods(code, meth.getName(), delegatorName, origIndex, desc, arrayVar); code.addAload(0); code.addGetfield(thisClassName, HANDLER, HANDLER_TYPE); code.addAload(0); code.addAload(arrayVar); code.addIconst(origIndex); code.addOpcode(Opcode.AALOAD); code.addAload(arrayVar); code.addIconst(delIndex); code.addOpcode(Opcode.AALOAD); makeParameterList(code, meth.getParameterTypes()); code.addInvokeinterface(MethodHandler.class.getName(), "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", 5); Class retType = meth.getReturnType(); addUnwrapper(code, retType); addReturn(code, retType); CodeAttribute ca = code.toCodeAttribute(); forwarder.setCodeAttribute(ca); return forwarder; } private static void setThrows(MethodInfo minfo, ConstPool cp, Method orig) { Class[] exceptions = orig.getExceptionTypes(); setThrows(minfo, cp, exceptions); } private static void setThrows(MethodInfo minfo, ConstPool cp, Class[] exceptions) { if (exceptions.length == 0) return; String[] list = new String[exceptions.length]; for (int i = 0; i < exceptions.length; i++) list[i] = exceptions[i].getName(); ExceptionsAttribute ea = new ExceptionsAttribute(cp); ea.setExceptions(list); minfo.setExceptionsAttribute(ea); } private static int addLoadParameters(Bytecode code, Class[] params, int offset) { int stacksize = 0; int n = params.length; for (int i = 0; i < n; ++i) stacksize += addLoad(code, stacksize + offset, params[i]); return stacksize; } private static int addLoad(Bytecode code, int n, Class type) { if (type.isPrimitive()) { if (type == Long.TYPE) { code.addLload(n); return 2; } else if (type == Float.TYPE) code.addFload(n); else if (type == Double.TYPE) { code.addDload(n); return 2; } else code.addIload(n); } else code.addAload(n); return 1; } private static int addReturn(Bytecode code, Class type) { if (type.isPrimitive()) { if (type == Long.TYPE) { code.addOpcode(Opcode.LRETURN); return 2; } else if (type == Float.TYPE) code.addOpcode(Opcode.FRETURN); else if (type == Double.TYPE) { code.addOpcode(Opcode.DRETURN); return 2; } else if (type == Void.TYPE) { code.addOpcode(Opcode.RETURN); return 0; } else code.addOpcode(Opcode.IRETURN); } else code.addOpcode(Opcode.ARETURN); return 1; } private static void makeParameterList(Bytecode code, Class[] params) { int regno = 1; int n = params.length; code.addIconst(n); code.addAnewarray("java/lang/Object"); for (int i = 0; i < n; i++) { code.addOpcode(Opcode.DUP); code.addIconst(i); Class type = params[i]; if (type.isPrimitive()) regno = makeWrapper(code, type, regno); else { code.addAload(regno); regno++; } code.addOpcode(Opcode.AASTORE); } } private static int makeWrapper(Bytecode code, Class type, int regno) { int index = FactoryHelper.typeIndex(type); String wrapper = FactoryHelper.wrapperTypes[index]; code.addNew(wrapper); code.addOpcode(Opcode.DUP); addLoad(code, regno, type); code.addInvokespecial(wrapper, "", FactoryHelper.wrapperDesc[index]); return regno + FactoryHelper.dataSize[index]; } /** * @param thisMethod might be null. */ private static void callFind2Methods(Bytecode code, String superMethod, String thisMethod, int index, String desc, int arrayVar) { String findClass = RuntimeSupport.class.getName(); String findDesc = "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;[Ljava/lang/reflect/Method;)V"; code.addAload(0); code.addLdc(superMethod); if (thisMethod == null) code.addOpcode(Opcode.ACONST_NULL); else code.addLdc(thisMethod); code.addIconst(index); code.addLdc(desc); code.addAload(arrayVar); code.addInvokestatic(findClass, "find2Methods", findDesc); } private static void addUnwrapper(Bytecode code, Class type) { if (type.isPrimitive()) { if (type == Void.TYPE) code.addOpcode(Opcode.POP); else { int index = FactoryHelper.typeIndex(type); String wrapper = FactoryHelper.wrapperTypes[index]; code.addCheckcast(wrapper); code.addInvokevirtual(wrapper, FactoryHelper.unwarpMethods[index], FactoryHelper.unwrapDesc[index]); } } else code.addCheckcast(type.getName()); } private static MethodInfo makeWriteReplace(ConstPool cp) { MethodInfo minfo = new MethodInfo(cp, "writeReplace", "()Ljava/lang/Object;"); String[] list = new String[1]; list[0] = "java.io.ObjectStreamException"; ExceptionsAttribute ea = new ExceptionsAttribute(cp); ea.setExceptions(list); minfo.setExceptionsAttribute(ea); Bytecode code = new Bytecode(cp, 0, 1); code.addAload(0); code.addInvokestatic("javassist.util.proxy.RuntimeSupport", "makeSerializedProxy", "(Ljava/lang/Object;)Ljavassist/util/proxy/SerializedProxy;"); code.addOpcode(Opcode.ARETURN); minfo.setCodeAttribute(code.toCodeAttribute()); return minfo; } } javassist-3.12.1.ga/src/main/javassist/util/proxy/MethodHandler.java0000644000175000017500000000403210630701321025362 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.util.proxy; import java.lang.reflect.Method; /** * The interface implemented by the invocation handler of a proxy * instance. * * @see ProxyFactory#setHandler(MethodHandler) */ public interface MethodHandler { /** * Is called when a method is invoked on a proxy instance associated * with this handler. This method must process that method invocation. * * @param self the proxy instance. * @param thisMethod the overridden method declared in the super * class or interface. * @param proceed the forwarder method for invoking the overridden * method. It is null if the overridden mehtod is * abstract or declared in the interface. * @param args an array of objects containing the values of * the arguments passed in the method invocation * on the proxy instance. If a parameter type is * a primitive type, the type of the array element * is a wrapper class. * @return the resulting value of the method invocation. * * @throws Throwable if the method invocation fails. */ Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable; } javassist-3.12.1.ga/src/main/javassist/util/proxy/ProxyObject.java0000644000175000017500000000216111345215350025123 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.util.proxy; /** * The interface implemented by proxy classes. * * @see ProxyFactory */ public interface ProxyObject { /** * Sets a handler. It can be used for changing handlers * during runtime. */ void setHandler(MethodHandler mi); /** * Get the handler. * This can be used to access values of the underlying MethodHandler * or to serialize it properly. */ MethodHandler getHandler(); } javassist-3.12.1.ga/src/main/javassist/util/proxy/ProxyObjectOutputStream.java0000644000175000017500000000532511361244304027524 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.util.proxy; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.io.OutputStream; /** * An input stream class which knows how to serialize proxies created via {@link ProxyFactory}. It must * be used when serialising proxies created from a proxy factory configured with * {@link ProxyFactory#useWriteReplace} set to false. Subsequent deserialization of the serialized data * must employ a {@link ProxyObjectInputStream} * * @author Andrew Dinn */ public class ProxyObjectOutputStream extends ObjectOutputStream { /** * create an output stream which can be used to serialize an object graph which includes proxies created * using class ProxyFactory * @param out * @throws IOException whenever ObjectOutputStream would also do so * @throws SecurityException whenever ObjectOutputStream would also do so * @throws NullPointerException if out is null */ public ProxyObjectOutputStream(OutputStream out) throws IOException { super(out); } protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException { Class cl = desc.forClass(); if (ProxyFactory.isProxyClass(cl)) { writeBoolean(true); Class superClass = cl.getSuperclass(); Class[] interfaces = cl.getInterfaces(); byte[] signature = ProxyFactory.getFilterSignature(cl); String name = superClass.getName(); writeObject(name); // we don't write the marker interface ProxyObject writeInt(interfaces.length - 1); for (int i = 0; i < interfaces.length; i++) { Class interfaze = interfaces[i]; if (interfaze != ProxyObject.class) { name = interfaces[i].getName(); writeObject(name); } } writeInt(signature.length); write(signature); } else { writeBoolean(false); super.writeClassDescriptor(desc); } } } javassist-3.12.1.ga/src/main/javassist/util/proxy/package.html0000644000175000017500000000026510376735653024314 0ustar twernertwerner Dynamic proxy (similar to Enhancer of cglib). See ProxyFactory for more details. javassist-3.12.1.ga/src/main/javassist/util/proxy/RuntimeSupport.java0000644000175000017500000001544211357307760025713 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.util.proxy; import java.lang.reflect.Method; import java.io.Serializable; /** * Runtime support routines that the classes generated by ProxyFactory use. * * @see ProxyFactory */ public class RuntimeSupport { /** * A method handler that only executes a method. */ public static MethodHandler default_interceptor = new DefaultMethodHandler(); static class DefaultMethodHandler implements MethodHandler, Serializable { public Object invoke(Object self, Method m, Method proceed, Object[] args) throws Exception { return proceed.invoke(self, args); } }; /** * Finds two methods specified by the parameters and stores them * into the given array. * * @throws RuntimeException if the methods are not found. * @see javassist.util.proxy.ProxyFactory */ public static void find2Methods(Object self, String superMethod, String thisMethod, int index, String desc, java.lang.reflect.Method[] methods) { synchronized (methods) { if (methods[index] == null) { methods[index + 1] = thisMethod == null ? null : findMethod(self, thisMethod, desc); methods[index] = findSuperMethod(self, superMethod, desc); } } } /** * Finds a method with the given name and descriptor. * It searches only the class of self. * * @throws RuntimeException if the method is not found. */ public static Method findMethod(Object self, String name, String desc) { Method m = findMethod2(self.getClass(), name, desc); if (m == null) error(self, name, desc); return m; } /** * Finds a method that has the given name and descriptor and is declared * in the super class. * * @throws RuntimeException if the method is not found. */ public static Method findSuperMethod(Object self, String name, String desc) { Class clazz = self.getClass(); Method m = findSuperMethod2(clazz.getSuperclass(), name, desc); if (m == null) m = searchInterfaces(clazz, name, desc); if (m == null) error(self, name, desc); return m; } private static void error(Object self, String name, String desc) { throw new RuntimeException("not found " + name + ":" + desc + " in " + self.getClass().getName()); } private static Method findSuperMethod2(Class clazz, String name, String desc) { Method m = findMethod2(clazz, name, desc); if (m != null) return m; Class superClass = clazz.getSuperclass(); if (superClass != null) { m = findSuperMethod2(superClass, name, desc); if (m != null) return m; } return searchInterfaces(clazz, name, desc); } private static Method searchInterfaces(Class clazz, String name, String desc) { Method m = null; Class[] interfaces = clazz.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { m = findSuperMethod2(interfaces[i], name, desc); if (m != null) return m; } return m; } private static Method findMethod2(Class clazz, String name, String desc) { Method[] methods = SecurityActions.getDeclaredMethods(clazz); int n = methods.length; for (int i = 0; i < n; i++) if (methods[i].getName().equals(name) && makeDescriptor(methods[i]).equals(desc)) return methods[i]; return null; } /** * Makes a descriptor for a given method. */ public static String makeDescriptor(Method m) { Class[] params = m.getParameterTypes(); return makeDescriptor(params, m.getReturnType()); } /** * Makes a descriptor for a given method. * * @param params parameter types. * @param retType return type. */ public static String makeDescriptor(Class[] params, Class retType) { StringBuffer sbuf = new StringBuffer(); sbuf.append('('); for (int i = 0; i < params.length; i++) makeDesc(sbuf, params[i]); sbuf.append(')'); makeDesc(sbuf, retType); return sbuf.toString(); } private static void makeDesc(StringBuffer sbuf, Class type) { if (type.isArray()) { sbuf.append('['); makeDesc(sbuf, type.getComponentType()); } else if (type.isPrimitive()) { if (type == Void.TYPE) sbuf.append('V'); else if (type == Integer.TYPE) sbuf.append('I'); else if (type == Byte.TYPE) sbuf.append('B'); else if (type == Long.TYPE) sbuf.append('J'); else if (type == Double.TYPE) sbuf.append('D'); else if (type == Float.TYPE) sbuf.append('F'); else if (type == Character.TYPE) sbuf.append('C'); else if (type == Short.TYPE) sbuf.append('S'); else if (type == Boolean.TYPE) sbuf.append('Z'); else throw new RuntimeException("bad type: " + type.getName()); } else sbuf.append('L').append(type.getName().replace('.', '/')) .append(';'); } /** * Converts a proxy object to an object that is writable to an * object stream. This method is called by writeReplace() * in a proxy class. * * @since 3.4 */ public static SerializedProxy makeSerializedProxy(Object proxy) throws java.io.InvalidClassException { Class clazz = proxy.getClass(); MethodHandler methodHandler = null; if (proxy instanceof ProxyObject) methodHandler = ((ProxyObject)proxy).getHandler(); return new SerializedProxy(clazz, ProxyFactory.getFilterSignature(clazz), methodHandler); } } javassist-3.12.1.ga/src/main/javassist/util/proxy/ProxyObjectInputStream.java0000644000175000017500000000762511361244304027330 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.util.proxy; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; /** * An input stream class which knows how to deserialize proxies created via {@link ProxyFactory} and * serializedo via a {@link ProxyObjectOutputStream}. It must be used when deserialising proxies created * from a proxy factory configured with {@link ProxyFactory#useWriteReplace} set to false. * * @author Andrew Dinn */ public class ProxyObjectInputStream extends ObjectInputStream { /** * create an input stream which can be used to deserialize an object graph which includes proxies created * using class ProxyFactory. the classloader used to resolve proxy superclass and interface names * read from the input stream will default to the current thread's context class loader or the system * classloader if the context class loader is null. * @param in * @throws java.io.StreamCorruptedException whenever ObjectInputStream would also do so * @throws IOException whenever ObjectInputStream would also do so * @throws SecurityException whenever ObjectInputStream would also do so * @throws NullPointerException if in is null */ public ProxyObjectInputStream(InputStream in) throws IOException { super(in); loader = Thread.currentThread().getContextClassLoader(); if (loader == null) { loader = ClassLoader.getSystemClassLoader(); } } /** * Reset the loader to be * @param loader */ public void setClassLoader(ClassLoader loader) { if (loader != null) { this.loader = loader; } else { loader = ClassLoader.getSystemClassLoader(); } } protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException { boolean isProxy = readBoolean(); if (isProxy) { String name = (String)readObject(); Class superClass = loader.loadClass(name); int length = readInt(); Class[] interfaces = new Class[length]; for (int i = 0; i < length; i++) { name = (String)readObject(); interfaces[i] = loader.loadClass(name); } length = readInt(); byte[] signature = new byte[length]; read(signature); ProxyFactory factory = new ProxyFactory(); // we must always use the cache and never use writeReplace when using // ProxyObjectOutputStream and ProxyObjectInputStream factory.setUseCache(true); factory.setUseWriteReplace(false); factory.setSuperclass(superClass); factory.setInterfaces(interfaces); Class proxyClass = factory.createClass(signature); return ObjectStreamClass.lookup(proxyClass); } else { return super.readClassDescriptor(); } } /** * the loader to use to resolve classes for proxy superclass and interface names read * from the stream. defaults to the context class loader of the thread which creates * the input stream or the system class loader if the context class loader is null. */ private ClassLoader loader; }javassist-3.12.1.ga/src/main/javassist/util/proxy/FactoryHelper.java0000644000175000017500000001701710754365662025447 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.util.proxy; import java.lang.reflect.Method; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.security.ProtectionDomain; import javassist.CannotCompileException; import javassist.bytecode.ClassFile; /** * A helper class for implementing ProxyFactory. * The users of ProxyFactory do not have to see this class. * * @see ProxyFactory */ public class FactoryHelper { private static java.lang.reflect.Method defineClass1, defineClass2; static { try { Class cl = Class.forName("java.lang.ClassLoader"); defineClass1 = SecurityActions.getDeclaredMethod( cl, "defineClass", new Class[] { String.class, byte[].class, int.class, int.class }); defineClass2 = SecurityActions.getDeclaredMethod( cl, "defineClass", new Class[] { String.class, byte[].class, int.class, int.class, ProtectionDomain.class }); } catch (Exception e) { throw new RuntimeException("cannot initialize"); } } /** * Returns an index for accessing arrays in this class. * * @throws RuntimeException if a given type is not a primitive type. */ public static final int typeIndex(Class type) { Class[] list = primitiveTypes; int n = list.length; for (int i = 0; i < n; i++) if (list[i] == type) return i; throw new RuntimeException("bad type:" + type.getName()); } /** * Class objects representing primitive types. */ public static final Class[] primitiveTypes = { Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE }; /** * The fully-qualified names of wrapper classes for primitive types. */ public static final String[] wrapperTypes = { "java.lang.Boolean", "java.lang.Byte", "java.lang.Character", "java.lang.Short", "java.lang.Integer", "java.lang.Long", "java.lang.Float", "java.lang.Double", "java.lang.Void" }; /** * The descriptors of the constructors of wrapper classes. */ public static final String[] wrapperDesc = { "(Z)V", "(B)V", "(C)V", "(S)V", "(I)V", "(J)V", "(F)V", "(D)V" }; /** * The names of methods for obtaining a primitive value * from a wrapper object. For example, intValue() * is such a method for obtaining an integer value from a * java.lang.Integer object. */ public static final String[] unwarpMethods = { "booleanValue", "byteValue", "charValue", "shortValue", "intValue", "longValue", "floatValue", "doubleValue" }; /** * The descriptors of the unwrapping methods contained * in unwrapMethods. */ public static final String[] unwrapDesc = { "()Z", "()B", "()C", "()S", "()I", "()J", "()F", "()D" }; /** * The data size of primitive types. long * and double are 2; the others are 1. */ public static final int[] dataSize = { 1, 1, 1, 1, 1, 2, 1, 2 }; /** * Loads a class file by a given class loader. * This method uses a default protection domain for the class * but it may not work with a security manager or a sigend jar file. * * @see #toClass(ClassFile,ClassLoader,ProtectionDomain) */ public static Class toClass(ClassFile cf, ClassLoader loader) throws CannotCompileException { return toClass(cf, loader, null); } /** * Loads a class file by a given class loader. * * @param domain if it is null, a default domain is used. * @since 3.3 */ public static Class toClass(ClassFile cf, ClassLoader loader, ProtectionDomain domain) throws CannotCompileException { try { byte[] b = toBytecode(cf); Method method; Object[] args; if (domain == null) { method = defineClass1; args = new Object[] { cf.getName(), b, new Integer(0), new Integer(b.length) }; } else { method = defineClass2; args = new Object[] { cf.getName(), b, new Integer(0), new Integer(b.length), domain }; } return toClass2(method, loader, args); } catch (RuntimeException e) { throw e; } catch (java.lang.reflect.InvocationTargetException e) { throw new CannotCompileException(e.getTargetException()); } catch (Exception e) { throw new CannotCompileException(e); } } private static synchronized Class toClass2(Method method, ClassLoader loader, Object[] args) throws Exception { SecurityActions.setAccessible(method, true); Class clazz = (Class)method.invoke(loader, args); SecurityActions.setAccessible(method, false); return clazz; } private static byte[] toBytecode(ClassFile cf) throws IOException { ByteArrayOutputStream barray = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(barray); try { cf.write(out); } finally { out.close(); } return barray.toByteArray(); } /** * Writes a class file. */ public static void writeFile(ClassFile cf, String directoryName) throws CannotCompileException { try { writeFile0(cf, directoryName); } catch (IOException e) { throw new CannotCompileException(e); } } private static void writeFile0(ClassFile cf, String directoryName) throws CannotCompileException, IOException { String classname = cf.getName(); 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 FileOutputStream(filename))); try { cf.write(out); } catch (IOException e) { throw e; } finally { out.close(); } } } javassist-3.12.1.ga/src/main/javassist/util/package.html0000644000175000017500000000005710361120146023105 0ustar twernertwerner Utility classes. javassist-3.12.1.ga/src/main/javassist/util/HotSwapper.java0000644000175000017500000002027510630701321023566 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.util; import com.sun.jdi.*; import com.sun.jdi.connect.*; import com.sun.jdi.event.*; import com.sun.jdi.request.*; import java.io.*; import java.util.*; class Trigger { void doSwap() {} } /** * A utility class for dynamically reloading a class by * the Java Platform Debugger Architecture (JPDA), or HotSwap
    . * It works only with JDK 1.4 and later. * *

    Note: The new definition of the reloaded class must declare * the same set of methods and fields as the original definition. The * schema change between the original and new definitions is not allowed * by the JPDA. * *

    To use this class, the JVM must be launched with the following * command line options: * *

      *

      For Java 1.4,
      *

      java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
      *

      For Java 5,
      *

      java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
      *
    * *

    Note that 8000 is the port number used by HotSwapper. * Any port number can be specified. Since HotSwapper does not * launch another JVM for running a target application, this port number * is used only for inter-thread communication. * *

    Furthermore, JAVA_HOME/lib/tools.jar must be included * in the class path. * *

    Using HotSwapper is easy. See the following example: * *

       * CtClass clazz = ...
       * byte[] classFile = clazz.toBytecode();
       * HotSwapper hs = new HostSwapper(8000);  // 8000 is a port number.
       * hs.reload("Test", classFile);
       * 
    * *

    reload() * first unload the Test class and load a new version of * the Test class. * classFile is a byte array containing the new contents of * the class file for the Test class. The developers can * repatedly call reload() on the same HotSwapper * object so that they can reload a number of classes. * * @since 3.1 */ public class HotSwapper { private VirtualMachine jvm; private MethodEntryRequest request; private Map newClassFiles; private Trigger trigger; private static final String HOST_NAME = "localhost"; private static final String TRIGGER_NAME = Trigger.class.getName(); /** * Connects to the JVM. * * @param port the port number used for the connection to the JVM. */ public HotSwapper(int port) throws IOException, IllegalConnectorArgumentsException { this(Integer.toString(port)); } /** * Connects to the JVM. * * @param port the port number used for the connection to the JVM. */ public HotSwapper(String port) throws IOException, IllegalConnectorArgumentsException { jvm = null; request = null; newClassFiles = null; trigger = new Trigger(); AttachingConnector connector = (AttachingConnector)findConnector("com.sun.jdi.SocketAttach"); Map arguments = connector.defaultArguments(); ((Connector.Argument)arguments.get("hostname")).setValue(HOST_NAME); ((Connector.Argument)arguments.get("port")).setValue(port); jvm = connector.attach(arguments); EventRequestManager manager = jvm.eventRequestManager(); request = methodEntryRequests(manager, TRIGGER_NAME); } private Connector findConnector(String connector) throws IOException { List connectors = Bootstrap.virtualMachineManager().allConnectors(); Iterator iter = connectors.iterator(); while (iter.hasNext()) { Connector con = (Connector)iter.next(); if (con.name().equals(connector)) { return con; } } throw new IOException("Not found: " + connector); } private static MethodEntryRequest methodEntryRequests( EventRequestManager manager, String classpattern) { MethodEntryRequest mereq = manager.createMethodEntryRequest(); mereq.addClassFilter(classpattern); mereq.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD); return mereq; } /* Stops triggering a hotswapper when reload() is called. */ private void deleteEventRequest(EventRequestManager manager, MethodEntryRequest request) { manager.deleteEventRequest(request); } /** * Reloads a class. * * @param className the fully-qualified class name. * @param classFile the contents of the class file. */ public void reload(String className, byte[] classFile) { ReferenceType classtype = toRefType(className); Map map = new HashMap(); map.put(classtype, classFile); reload2(map, className); } /** * Reloads a class. * * @param classFiles a map between fully-qualified class names * and class files. The type of the class names * is String and the type of the * class files is byte[]. */ public void reload(Map classFiles) { Set set = classFiles.entrySet(); Iterator it = set.iterator(); Map map = new HashMap(); String className = null; while (it.hasNext()) { Map.Entry e = (Map.Entry)it.next(); className = (String)e.getKey(); map.put(toRefType(className), e.getValue()); } if (className != null) reload2(map, className + " etc."); } private ReferenceType toRefType(String className) { List list = jvm.classesByName(className); if (list == null || list.isEmpty()) throw new RuntimeException("no such a class: " + className); else return (ReferenceType)list.get(0); } private void reload2(Map map, String msg) { synchronized (trigger) { startDaemon(); newClassFiles = map; request.enable(); trigger.doSwap(); request.disable(); Map ncf = newClassFiles; if (ncf != null) { newClassFiles = null; throw new RuntimeException("failed to reload: " + msg); } } } private void startDaemon() { new Thread() { private void errorMsg(Throwable e) { System.err.print("Exception in thread \"HotSwap\" "); e.printStackTrace(System.err); } public void run() { EventSet events = null; try { events = waitEvent(); EventIterator iter = events.eventIterator(); while (iter.hasNext()) { Event event = iter.nextEvent(); if (event instanceof MethodEntryEvent) { hotswap(); break; } } } catch (Throwable e) { errorMsg(e); } try { if (events != null) events.resume(); } catch (Throwable e) { errorMsg(e); } } }.start(); } EventSet waitEvent() throws InterruptedException { EventQueue queue = jvm.eventQueue(); return queue.remove(); } void hotswap() { Map map = newClassFiles; jvm.redefineClasses(map); newClassFiles = null; } } javassist-3.12.1.ga/src/main/javassist/NotFoundException.java0000644000175000017500000000172710630701321024131 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; /** * 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()); } } javassist-3.12.1.ga/src/main/javassist/CtPrimitiveType.java0000644000175000017500000000711611373532244023630 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; /** * An instance of CtPrimitiveType represents a primitive type. * It is obtained from CtClass. */ 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 true 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 modifiers for this type. * For decoding, use javassist.Modifier. * * @see Modifier */ public int getModifiers() { return Modifier.PUBLIC | Modifier.FINAL; } /** * 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 * java.lang.Integer. */ 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 * intValue. */ 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 * ()I. */ 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 * javassit.bytecode.Opcode.IRETURN. */ 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 * javassit.bytecode.Opcode.T_INT. */ 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; } } javassist-3.12.1.ga/src/main/javassist/ClassMap.java0000644000175000017500000001300510771471405022226 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import javassist.bytecode.Descriptor; /** * A hash table associating class names with different names. * *

    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, * *

      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);
       *   }
       * }
    * *

    This subclass maps java.lang.String to * java2.lang.String. Note that get() * receives and returns the internal representation of a class name. * For example, the internal representation of java.lang.String * is java/lang/String. * * @see #get(Object) * @see CtClass#replaceClassName(ClassMap) * @see CtNewMethod#copy(CtMethod,String,CtClass,ClassMap) */ public class ClassMap extends java.util.HashMap { private ClassMap parent; /** * Constructs a hash table. */ public ClassMap() { parent = null; } ClassMap(ClassMap map) { parent = map; } /** * Maps a class name to another name in this hashtable. * The names are obtained with calling Class.getName(). * 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. * If the hashtable contains another mapping from the same * class name, the old mapping is replaced. * This method translates the given class names into the * internal form used in the JVM before putting it in * the hashtable. * *

    If oldname is identical to * newname, then this method does not * perform anything; it does not record the mapping from * oldname to newname. See * fix method. * * @param oldname the original class name. * @param newname the substituted class name. * @see #fix(String) */ 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)); } /** * Is equivalent to put() except that * the given mapping is not recorded into the hashtable * if another mapping from oldname is * already included. * * @param oldname the original class name. * @param newname the substituted class name. */ public void putIfNone(String oldname, String newname) { if (oldname == newname) return; String oldname2 = toJvmName(oldname); String s = (String)get(oldname2); if (s == null) 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 jvmClassName * is mapped. A subclass of this class should override this method. * *

    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) { Object found = super.get(jvmClassName); if (found == null && parent != null) return parent.get(jvmClassName); else return found; } /** * 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); } } javassist-3.12.1.ga/src/main/javassist/ClassClassPath.java0000644000175000017500000000604110630701321023360 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import java.io.InputStream; import java.net.URL; /** * A search-path for obtaining a class file * by getResourceAsStream() in java.lang.Class. * *

    Try adding a ClassClassPath when a program is running * with a user-defined class loader and any class files are not found with * the default ClassPool. For example, * *

       * ClassPool cp = ClassPool.getDefault();
       * cp.insertClassPath(new ClassClassPath(this.getClass()));
       * 
    * * This code snippet permanently adds a ClassClassPath * to the default ClassPool. Note that the default * ClassPool is a singleton. The added * ClassClassPath uses a class object representing * the class including the code snippet above. * * @see ClassPool#insertClassPath(ClassPath) * @see ClassPool#appendClassPath(ClassPath) * @see LoaderClassPath */ public class ClassClassPath implements ClassPath { private Class thisClass; /** Creates a search path. * * @param c the Class object used to obtain a class * file. getResourceAsStream() is called on * this object. */ public ClassClassPath(Class c) { thisClass = c; } ClassClassPath() { /* 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). */ this(java.lang.Object.class); } /** * Obtains a class file by getResourceAsStream(). */ public InputStream openClassfile(String classname) { String jarname = "/" + classname.replace('.', '/') + ".class"; return thisClass.getResourceAsStream(jarname); } /** * Obtains the URL of the specified class file. * * @return null if the class file could not be found. */ public URL find(String classname) { String jarname = "/" + classname.replace('.', '/') + ".class"; return thisClass.getResource(jarname); } /** * Does nothing. */ public void close() { } public String toString() { return thisClass.getName() + ".class"; } } javassist-3.12.1.ga/src/main/javassist/CtNewWrappedConstructor.java0000644000175000017500000000763610630701321025334 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; 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, "", "()V"); } else if (howToCallSuper == PASS_PARAMS) { stacksize = code.addLoadParameters(parameters, 1) + 1; code.addInvokespecial(superclazz, "", 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, "", 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; } } javassist-3.12.1.ga/src/main/javassist/LoaderClassPath.java0000644000175000017500000000562610630701321023531 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import java.io.InputStream; import java.net.URL; import java.lang.ref.WeakReference; /** * A class search-path representing a class loader. * *

    It is used for obtaining a class file from the given * class loader by getResourceAsStream(). * The LoaderClassPath refers to the class loader through * WeakReference. If the class loader is garbage collected, * the other search pathes are examined. * *

    The given class loader must have both getResourceAsStream() * and getResource(). * * @author Bill Burke * @author Shigeru Chiba * * @see ClassPool#insertClassPath(ClassPath) * @see ClassPool#appendClassPath(ClassPath) * @see ClassClassPath */ public class LoaderClassPath implements ClassPath { private WeakReference clref; /** * Creates a search path representing a class loader. */ public LoaderClassPath(ClassLoader cl) { clref = new WeakReference(cl); } public String toString() { Object cl = null; if (clref != null) cl = clref.get(); return cl == null ? "" : cl.toString(); } /** * Obtains a class file from the class loader. * This method calls getResourceAsStream(String) * on the class loader. */ public InputStream openClassfile(String classname) { String cname = classname.replace('.', '/') + ".class"; ClassLoader cl = (ClassLoader)clref.get(); if (cl == null) return null; // not found else return cl.getResourceAsStream(cname); } /** * Obtains the URL of the specified class file. * This method calls getResource(String) * on the class loader. * * @return null if the class file could not be found. */ public URL find(String classname) { String cname = classname.replace('.', '/') + ".class"; ClassLoader cl = (ClassLoader)clref.get(); if (cl == null) return null; // not found else return cl.getResource(cname); } /** * Closes this class path. */ public void close() { clref = null; } } javassist-3.12.1.ga/src/main/javassist/CtClass.java0000644000175000017500000013077211361635321022065 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URL; import java.security.ProtectionDomain; import java.util.Collection; import javassist.bytecode.ClassFile; import javassist.bytecode.Descriptor; import javassist.bytecode.Opcode; import javassist.expr.ExprEditor; /* Note: * * This class is an abstract class and several methods just return null * or throw an exception. Those methods are overridden in subclasses * of this class. Read the source code of CtClassType if you are * interested in the implementation of Javassist. * * Subclasses of CtClass are CtClassType, CtPrimitiveType, and CtArray. */ /** * An instance of CtClass represents a class. * It is obtained from ClassPool. * * @see ClassPool#get(String) */ public abstract class CtClass { protected String qualifiedName; /** * The version number of this release. */ public static final String version = "3.12.0.GA"; /** * Prints the version number and the copyright notice. * *

    The following command invokes this method: * *

      java -jar javassist.jar
    */ public static void main(String[] args) { System.out.println("Javassist version " + CtClass.version); System.out.println("Copyright (C) 1999-2010 Shigeru Chiba." + " All Rights Reserved."); } static final String javaLangObject = "java.lang.Object"; /** * The CtClass object representing * the boolean type. */ public static CtClass booleanType; /** * The CtClass object representing * the char type. */ public static CtClass charType; /** * The CtClass object representing * the byte type. */ public static CtClass byteType; /** * The CtClass object representing * the short type. */ public static CtClass shortType; /** * The CtClass object representing * the int type. */ public static CtClass intType; /** * The CtClass object representing * the long type. */ public static CtClass longType; /** * The CtClass object representing * the float type. */ public static CtClass floatType; /** * The CtClass object representing * the double type. */ public static CtClass doubleType; /** * The CtClass object representing * the void 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; } /** * Converts the object to a string. */ public String toString() { StringBuffer buf = new StringBuffer(getClass().getName()); buf.append("@"); buf.append(Integer.toHexString(hashCode())); buf.append("["); extendToString(buf); buf.append("]"); return buf.toString(); } /** * Implemented in subclasses to add to the {@link #toString()} result. * Subclasses should put a space before each token added to the buffer. */ protected void extendToString(StringBuffer buffer) { buffer.append(getName()); } /** * Returns a ClassPool for this class. */ public ClassPool getClassPool() { return null; } /** * Returns a class file for this class. * *

    This method is not available if isFrozen() * is true. */ public ClassFile getClassFile() { checkModify(); return getClassFile2(); } /** * Returns a class file for this class (read only). * Normal applications do not need calling this method. Use * getClassFile(). * *

    The ClassFile object obtained by this method * is read only. Changes to this object might not be reflected * on a class file generated by toBytecode(), * toClass(), etc. * *

    This method is available even if isFrozen() * is true. However, if the class is frozen, it might be also * pruned. * * @see CtClass#getClassFile() * @see CtClass#isFrozen() * @see CtClass#prune() */ public ClassFile getClassFile2() { return null; } /** * Undocumented method. Do not use; internal-use only. */ public javassist.compiler.AccessorMaker getAccessorMaker() { return null; } /** * Returns the uniform resource locator (URL) of the class file. */ public URL getURL() throws NotFoundException { throw new NotFoundException(getName()); } /** * Returns true if the definition of the class has been modified. */ public boolean isModified() { return false; } /** * Returns true if the class has been loaded or written out * and thus it cannot be modified any more. * * @see #defrost() * @see #detach() */ public boolean isFrozen() { return true; } /** * Makes the class frozen. * * @see #isFrozen() * @see #defrost() * @since 3.6 */ public void freeze() {} /* Note: this method is overridden by CtClassType */ void checkModify() throws RuntimeException { if (isFrozen()) throw new RuntimeException(getName() + " class is frozen"); // isModified() must return true after this method is invoked. } /** * Defrosts the class so that the class can be modified again. * *

    To avoid changes that will be 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. * *

    If defrost() will be called later, pruning * must be disallowed in advance. * * @see #isFrozen() * @see #stopPruning(boolean) * @see #detach() */ public void defrost() { throw new RuntimeException("cannot defrost " + getName()); } /** * Returns true if this object represents a primitive * Java type: boolean, byte, char, short, int, long, float, double, * or void. */ public boolean isPrimitive() { return false; } /** * Returns true 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 null. */ public CtClass getComponentType() throws NotFoundException { return null; } /** * Returns true if this class extends or implements * clazz. It also returns true if * this class is the same as clazz. */ 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 null. */ 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 newName for all occurrences of a class * name oldName 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 map. * *

    All the class names appearing in the class file are tested * with map 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 ClassMap so that get() * 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. * *

    This method may return null. */ public synchronized 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 true if this object represents an interface. */ public boolean isInterface() { return false; } /** * Determines whether this object represents an annotation type. * It returns true if this object represents an annotation type. * * @since 3.2 */ public boolean isAnnotation() { return false; } /** * Determines whether this object represents an enum. * It returns true if this object represents an enum. * * @since 3.2 */ public boolean isEnum() { return false; } /** * Returns the modifiers for this class, encoded in an integer. * For decoding, use javassist.Modifier. * *

    If the class is a static nested class (a.k.a. static inner class), * the returned modifiers include Modifier.STATIC. * * @see Modifier */ public int getModifiers() { return 0; } /** * Returns true if the class has the specified annotation class. * * @param clz the annotation class. * @return true if the annotation is found, otherwise false. * @since 3.11 */ public boolean hasAnnotation(Class clz) { return false; } /** * Returns the annotation if the class has the specified annotation class. * For example, if an annotation @Author is associated * with this class, an Author object is returned. * The member values can be obtained by calling methods on * the Author object. * * @param clz the annotation class. * @return the annotation if found, otherwise null. * @since 3.11 */ public Object getAnnotation(Class clz) throws ClassNotFoundException { return null; } /** * Returns the annotations associated with this class. * For example, if an annotation @Author is associated * with this class, the returned array contains an Author * object. The member values can be obtained by calling methods on * the Author object. * * @return an array of annotation-type objects. * @see CtMember#getAnnotations() * @since 3.1 */ public Object[] getAnnotations() throws ClassNotFoundException { return new Object[0]; } /** * Returns the annotations associated with this class. * This method is equivalent to getAnnotations() * except that, if any annotations are not on the classpath, * they are not included in the returned array. * * @return an array of annotation-type objects. * @see #getAnnotations() * @see CtMember#getAvailableAnnotations() * @since 3.3 */ public Object[] getAvailableAnnotations(){ return new Object[0]; } /** * Returns an array of nested classes declared in the class. * Nested classes are inner classes, anonymous classes, local classes, * and static nested classes. * * @since 3.2 */ public CtClass[] getNestedClasses() throws NotFoundException { return new CtClass[0]; } /** * Sets the modifiers. * *

    If the class is a nested class, this method also modifies * the class declaring that nested class (i.e. the enclosing * class is modified). * * @param mod modifiers encoded by * javassist.Modifier * @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. * *

    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 this object represents the * java.lang.Object class and thus it does not have * the super class. * *

    If this object represents an interface, this method * always returns the java.lang.Object class. * To obtain the super interfaces * extended by that interface, call getInterfaces(). */ public CtClass getSuperclass() throws NotFoundException { return null; } /** * Changes a super class unless this object represents an interface. * The new super class must be compatible with the old one; for example, * it should inherit from the old super class. * *

    If this object represents an interface, this method is equivalent * to addInterface(); it appends clazz to * the list of the super interfaces extended by that interface. * Note that an interface can extend multiple super interfaces. * * @see #replaceClassName(String, String) * @see #replaceClassName(ClassMap) */ public void setSuperclass(CtClass clazz) throws CannotCompileException { checkModify(); } /** * Obtains the class objects representing the interfaces implemented * by the class or, if this object represents an interface, the interfaces * extended by that interface. */ public CtClass[] getInterfaces() throws NotFoundException { return new CtClass[0]; } /** * Sets implemented interfaces. If this object represents an interface, * this method sets the interfaces extended by that interface. * * @param list a list of the CtClass objects * representing interfaces, or * null 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(); } /** * If this class is a member class or interface of another class, * then the class enclosing this class is returned. * * @return null if this class is a top-level class or an anonymous class. */ public CtClass getDeclaringClass() throws NotFoundException { return null; } /** * Returns the immediately enclosing method of this class. * This method works only with JDK 1.5 or later. * * @return null if this class is not a local class or an anonymous * class. */ public CtMethod getEnclosingMethod() throws NotFoundException { return null; } /** * Makes a new public nested class. If this method is called, * the CtClass, which encloses the nested class, is modified * since a class file includes a list of nested classes. * *

    The current implementation only supports a static nested class. * isStatic must be true. * * @param name the simple name of the nested class. * @param isStatic true if the nested class is static. */ public CtClass makeNestedClass(String name, boolean isStatic) { throw new RuntimeException(getName() + " is not a class"); } /** * Returns an array containing CtField objects * representing all the non-private fields of the class. * That array includes non-private fields inherited from the * superclasses. */ public CtField[] getFields() { return new CtField[0]; } /** * 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); } /** * @return null if the specified field is not found. */ CtField getField2(String name) { return null; } /** * Gets all the fields declared in the class. The inherited fields * are not included. * *

    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. * *

    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 CtConstructor objects * representing all the non-private 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 javassist.bytecode.Descriptor. * * @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 null if * no class initializer is not declared. * * @see #makeClassInitializer() * @see javassist.CtConstructor */ public CtConstructor getClassInitializer() { return null; } /** * Returns an array containing CtMethod objects * representing all the non-private methods of the class. * That array includes non-private 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 CtBehavior#getSignature() * @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. * *

    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. * *

    Note: this method does not search the superclasses. * * @see javassist.CtMethod */ public CtMethod getDeclaredMethod(String name) throws NotFoundException { throw new NotFoundException(name); } /** * Makes an empty class initializer (static constructor). * If the class already includes a class initializer, * this method returns it. * * @see #getClassInitializer() */ public CtConstructor makeClassInitializer() throws CannotCompileException { throw new CannotCompileException("not a class"); } /** * Adds a constructor. To add a class initializer (static constructor), * call makeClassInitializer(). * * @see #makeClassInitializer() */ public void addConstructor(CtConstructor c) throws CannotCompileException { checkModify(); } /** * Removes a constructor declared in this class. * * @param c removed constructor. * @throws NotFoundException if the constructor is not found. */ public void removeConstructor(CtConstructor c) throws NotFoundException { checkModify(); } /** * Adds a method. */ public void addMethod(CtMethod m) throws CannotCompileException { checkModify(); } /** * Removes a method declared in this class. * * @param m removed method. * @throws NotFoundException if the method is not found. */ public void removeMethod(CtMethod m) throws NotFoundException { checkModify(); } /** * Adds a field. * *

    The CtField belonging to another * CtClass 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. * *

    The CtField belonging to another * CtClass cannot be directly added to this class. * Only a field created for this class can be added. * *

    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. * *

           * cc.addField(f, "0")               // the initial value is 0.
           * cc.addField(f, "i + 1")           // i + 1.
           * cc.addField(f, "new Point()");    // a Point object.
           * 
    * *

    Here, the type of variable cc is CtClass. * The type of f is CtField. * *

    Note: do not change the modifier of the field * (in particular, do not add or remove static * to/from the modifier) * after it is added to the class by addField(). * * @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. * *

    The CtField belonging to another * CtClass cannot be directly added to this class. * Only a field created for this class can be added. * *

    For example, * *

           * CtClass cc = ...;
           * addField(new CtField(CtClass.intType, "i", cc),
           *          CtField.Initializer.constant(1));
           * 
    * *

    This code adds an int 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(); } /** * Removes a field declared in this class. * * @param f removed field. * @throws NotFoundException if the field is not found. */ public void removeField(CtField f) throws NotFoundException { checkModify(); } /** * Obtains an attribute with the given name. * If that attribute is not found in the class file, this * method returns null. * *

    This is a convenient method mainly for obtaining * a user-defined attribute. For dealing with attributes, see the * javassist.bytecode package. For example, the following * expression returns all the attributes of a class file. * *

           * getClassFile().getAttributes()
           * 
    * * @param name attribute name * @see javassist.bytecode.AttributeInfo */ 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. * *

    This is a convenient method mainly for adding * a user-defined attribute. For dealing with attributes, see the * javassist.bytecode package. For example, the following * expression adds an attribute info to a class file. * *

           * getClassFile().addAttribute(info)
           * 
    * * @param name attribute name * @param data attribute value * @see javassist.bytecode.AttributeInfo */ public void setAttribute(String name, byte[] data) { checkModify(); } /** * Applies the given converter to all methods and constructors * declared in the class. This method calls instrument() * on every CtMethod and CtConstructor 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 instrument() * on every CtMethod and CtConstructor object * in the class. * * @param editor specifies how to modify. */ public void instrument(ExprEditor editor) throws CannotCompileException { checkModify(); } /** * Converts this class to a java.lang.Class object. * Once this method is called, further modifications are not * allowed any more. * To load the class, this method uses the context class loader * of the current thread. If the program is running on some application * server, the context class loader might be inappropriate to load the * class. * *

    This method is provided for convenience. If you need more * complex functionality, you should write your own class loader. * *

    Note: this method calls toClass() * in ClassPool. * *

    Warining: A Class object returned by this method may not * work with a security manager or a signed jar file because a * protection domain is not specified. * * @see #toClass(java.lang.ClassLoader,ProtectionDomain) * @see ClassPool#toClass(CtClass) */ public Class toClass() throws CannotCompileException { return getClassPool().toClass(this); } /** * Converts this class to a java.lang.Class object. * Once this method is called, further modifications are not allowed * any more. * *

    The class file represented by this CtClass is * loaded by the given class loader to construct a * java.lang.Class object. Since a private method * on the class loader is invoked through the reflection API, * the caller must have permissions to do that. * *

    An easy way to obtain ProtectionDomain object is * to call getProtectionDomain() * in java.lang.Class. It returns the domain that * the class belongs to. * *

    This method is provided for convenience. If you need more * complex functionality, you should write your own class loader. * *

    Note: this method calls toClass() * in ClassPool. * * @param loader the class loader used to load this class. * If it is null, the class loader returned by * {@link ClassPool#getClassLoader()} is used. * @param domain the protection domain that the class belongs to. * If it is null, the default domain created * by java.lang.ClassLoader is used. * @see ClassPool#toClass(CtClass,java.lang.ClassLoader) * @since 3.3 */ public Class toClass(ClassLoader loader, ProtectionDomain domain) throws CannotCompileException { ClassPool cp = getClassPool(); if (loader == null) loader = cp.getClassLoader(); return cp.toClass(this, loader, domain); } /** * Converts this class to a java.lang.Class object. * *

    Warining: A Class object returned by this method may not * work with a security manager or a signed jar file because a * protection domain is not specified. * * @deprecated Replaced by {@link #toClass(ClassLoader,ProtectionDomain)} */ public final Class toClass(ClassLoader loader) throws CannotCompileException { return getClassPool().toClass(this, loader); } /** * Removes this CtClass object from the * ClassPool. * After this method is called, any method cannot be called on the * removed CtClass object. * *

    If get() in ClassPool is called * with the name of the removed method, * the ClassPool will read the class file again * and constructs another CtClass object representing * the same class. */ public void detach() { ClassPool cp = getClassPool(); CtClass obj = cp.removeCached(getName()); if (obj != this) cp.cacheCtClass(getName(), obj, false); } /** * Disallows (or allows) automatically pruning this CtClass * object. * *

    * Javassist can automatically prune a CtClass object * when toBytecode() (or toClass(), * writeFile()) is called. * Since a ClassPool holds all instances of CtClass * even after toBytecode() (or toClass(), * writeFile()) is called, pruning may significantly * save memory consumption. * *

    If ClassPool.doPruning is true, the automatic pruning * is on by default. Otherwise, it is off. The default value of * ClassPool.doPruning is false. * * @param stop disallow pruning if true. Otherwise, allow. * @return the previous status of pruning. true if pruning is already stopped. * * @see #detach() * @see #prune() * @see ClassPool#doPruning */ public boolean stopPruning(boolean stop) { return true; } /** * Discards unnecessary attributes, in particular, * CodeAttributes (method bodies) of the class, * to minimize the memory footprint. * After calling this method, the class is read only. * It cannot be modified any more. * Furthermore, toBytecode(), * writeFile(), toClass(), * or instrument() cannot be called. * However, the method names and signatures in the class etc. * are still accessible. * *

    toBytecode(), writeFile(), and * toClass() internally call this method if * automatic pruning is on. * *

    According to some experiments, pruning does not really reduce * memory consumption. Only about 20%. Since pruning takes time, * it might not pay off. So the automatic pruning is off by default. * * @see #stopPruning(boolean) * @see #detach() * @see ClassPool#doPruning * * @see #toBytecode() * @see #toClass() * @see #writeFile() * @see #instrument(CodeConverter) * @see #instrument(ExprEditor) */ public void prune() {} /* Called by get() in ClassPool. * CtClassType overrides this method. */ void incGetCounter() {} /** * If this method is called, the class file will be * rebuilt when it is finally generated by * toBytecode() and writeFile(). * For a performance reason, the symbol table of the * class file may contain unused entries, for example, * after a method or a filed is deleted. * This method * removes those unused entries. This removal will * minimize the size of the class file. * * @since 3.8.1 */ public void rebuildClassFile() {} /** * Converts this class to a class file. * Once this method is called, further modifications are not * possible any more. * * @return the contents of the class file. */ public byte[] toBytecode() throws IOException, CannotCompileException { ByteArrayOutputStream barray = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(barray); try { toBytecode(out); } finally { out.close(); } return barray.toByteArray(); } /** * Writes a class file represented by this CtClass * object in the current directory. * Once this method is called, further modifications are not * possible any more. * * @see #debugWriteFile() */ public void writeFile() throws NotFoundException, IOException, CannotCompileException { writeFile("."); } /** * Writes a class file represented by this CtClass * object on a local disk. * Once this method is called, further modifications are not * possible any more. * * @param directoryName it must end without a directory separator. * @see #debugWriteFile(String) */ public void writeFile(String directoryName) throws CannotCompileException, IOException { String classname = getName(); 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))); try { toBytecode(out); } finally { out.close(); } } /** * Writes a class file as writeFile() does although this * method does not prune or freeze the class after writing the class * file. Note that, once writeFile() or toBytecode() * is called, it cannot be called again since the class is pruned and frozen. * This method would be useful for debugging. */ public void debugWriteFile() { debugWriteFile("."); } /** * Writes a class file as writeFile() does although this * method does not prune or freeze the class after writing the class * file. Note that, once writeFile() or toBytecode() * is called, it cannot be called again since the class is pruned and frozen. * This method would be useful for debugging. * * @param directoryName it must end without a directory separator. */ public void debugWriteFile(String directoryName) { try { boolean p = stopPruning(true); writeFile(directoryName); defrost(); stopPruning(p); } catch (Exception e) { throw new RuntimeException(e); } } 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(); } } /** * Converts this class to a class file. * Once this method is called, further modifications are not * possible any more. * *

    This method dose not close the output stream in the end. * * @param out the output stream that a class file is written to. */ public void toBytecode(DataOutputStream out) throws CannotCompileException, IOException { throw new CannotCompileException("not a class"); } /** * Makes a unique member name. This method guarantees that * the returned name is not used as a prefix of any methods * or fields visible in this class. * If the returned name is XYZ, then any method or field names * in this class do not start with XYZ. * * @param prefix the prefix of the member name. */ public String makeUniqueName(String prefix) { throw new RuntimeException("not available in " + getName()); } /* Invoked from ClassPool#compress(). * This method is overridden by CtClassType. */ void compress() {} } javassist-3.12.1.ga/src/main/javassist/Translator.java0000644000175000017500000000547210630701321022650 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; /** * An observer of Loader. * The users can define a class implementing this * interface and attach an instance of that class to a * Loader object so that it can translate a class file * when the class file is loaded into the JVM. * * @see Loader#addTranslator(ClassPool, Translator) */ public interface Translator { /** * Is invoked by a Loader for initialization * when the object is attached to the Loader object. * This method can be used for getting (for caching) some * CtClass objects that will be accessed * in onLoad() in Translator. * * @param pool the ClassPool that this translator * should use. * @see Loader * @throws NotFoundException if a CtClass cannot be found. * @throws CannotCompileException if the initialization by this method * fails. */ void start(ClassPool pool) throws NotFoundException, CannotCompileException; /** * Is invoked by a Loader for notifying that * a class is loaded. The Loader calls * *

           * pool.get(classname).toBytecode()
    * * to read the class file after onLoad() returns. * *

    classname may be the name of a class * that has not been created yet. * If so, onLoad() must create that class so that * the Loader can read it after onLoad() * returns. * * @param pool the ClassPool that this translator * should use. * @param classname the name of the class being loaded. * @see Loader * @throws NotFoundException if a CtClass cannot be found. * @throws CannotCompileException if the code transformation * by this method fails. */ void onLoad(ClassPool pool, String classname) throws NotFoundException, CannotCompileException; } javassist-3.12.1.ga/src/main/javassist/CtField.java0000644000175000017500000014027711221164554022044 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import javassist.bytecode.*; import javassist.compiler.Javac; import javassist.compiler.SymbolTable; import javassist.compiler.CompileError; import javassist.compiler.ast.ASTree; import javassist.compiler.ast.IntConst; import javassist.compiler.ast.DoubleConst; import javassist.compiler.ast.StringL; /** * An instance of CtField represents a field. * * @see CtClass#getDeclaredFields() */ public class CtField extends CtMember { static final String javaLangString = "java.lang.String"; protected FieldInfo fieldInfo; /** * Creates a CtField object. * The created field must be added to a class * with CtClass.addField(). * An initial value of the field is specified * by a CtField.Initializer object. * *

    If getter and setter methods are needed, * call CtNewMethod.getter() and * CtNewMethod.setter(). * * @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 CtClass.addField(). * An initial value of the field is specified * by a CtField.Initializer object. * *

    If getter and setter methods are needed, * call CtNewMethod.getter() and * CtNewMethod.setter(). * * @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); java.util.ListIterator iterator = src.fieldInfo.getAttributes().listIterator(); FieldInfo fi = fieldInfo; fi.setAccessFlags(src.fieldInfo.getAccessFlags()); ConstPool cp = fi.getConstPool(); while (iterator.hasNext()) { AttributeInfo ainfo = (AttributeInfo)iterator.next(); fi.addAttribute(ainfo.copy(cp, null)); } } private CtField(String typeDesc, String name, CtClass clazz) throws CannotCompileException { super(clazz); 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; } /** * Returns a String representation of the object. */ public String toString() { return getDeclaringClass().getName() + "." + getName() + ":" + fieldInfo.getDescriptor(); } protected void extendToString(StringBuffer buffer) { buffer.append(' '); buffer.append(getName()); buffer.append(' '); buffer.append(fieldInfo.getDescriptor()); } /* 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: * *

           * "public String name;"
           * "public int k = 3;"
    * *

    Note that the source code ends with ';' * (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"); } /** * Returns the FieldInfo representing the field in the class file. */ public FieldInfo getFieldInfo() { declaringClass.checkModify(); return fieldInfo; } /** * Returns the FieldInfo representing the field in the class * file (read only). * Normal applications do not need calling this method. Use * getFieldInfo(). * *

    The FieldInfo object obtained by this method * is read only. Changes to this object might not be reflected * on a class file generated by toBytecode(), * toClass(), etc in CtClass. * *

    This method is available even if the CtClass * containing this field is frozen. However, if the class is * frozen, the FieldInfo might be also pruned. * * @see #getFieldInfo() * @see CtClass#isFrozen() * @see CtClass#prune() */ 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) { declaringClass.checkModify(); 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) { declaringClass.checkModify(); fieldInfo.setAccessFlags(AccessFlag.of(mod)); } /** * Returns true if the class has the specified annotation class. * * @param clz the annotation class. * @return true if the annotation is found, otherwise false. * @since 3.11 */ public boolean hasAnnotation(Class clz) { FieldInfo fi = getFieldInfo2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) fi.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) fi.getAttribute(AnnotationsAttribute.visibleTag); return CtClassType.hasAnnotationType(clz, getDeclaringClass().getClassPool(), ainfo, ainfo2); } /** * Returns the annotation if the class has the specified annotation class. * For example, if an annotation @Author is associated * with this field, an Author object is returned. * The member values can be obtained by calling methods on * the Author object. * * @param clz the annotation class. * @return the annotation if found, otherwise null. * @since 3.11 */ public Object getAnnotation(Class clz) throws ClassNotFoundException { FieldInfo fi = getFieldInfo2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) fi.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) fi.getAttribute(AnnotationsAttribute.visibleTag); return CtClassType.getAnnotationType(clz, getDeclaringClass().getClassPool(), ainfo, ainfo2); } /** * Returns the annotations associated with this field. * * @return an array of annotation-type objects. * @see #getAvailableAnnotations() * @since 3.1 */ public Object[] getAnnotations() throws ClassNotFoundException { return getAnnotations(false); } /** * Returns the annotations associated with this field. * If any annotations are not on the classpath, they are not included * in the returned array. * * @return an array of annotation-type objects. * @see #getAnnotations() * @since 3.3 */ public Object[] getAvailableAnnotations(){ try { return getAnnotations(true); } catch (ClassNotFoundException e) { throw new RuntimeException("Unexpected exception", e); } } private Object[] getAnnotations(boolean ignoreNotFound) throws ClassNotFoundException { FieldInfo fi = getFieldInfo2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) fi.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) fi.getAttribute(AnnotationsAttribute.visibleTag); return CtClassType.toAnnotationType(ignoreNotFound, getDeclaringClass().getClassPool(), ainfo, ainfo2); } /** * Returns the character string representing the type of the field. * If two fields have the same type, * getSignature() returns the same string. * *

    Note that the returned string is not the type signature * contained in the SignatureAttirbute. It is * a descriptor. To obtain a type signature, call the following * methods: * *

      getFieldInfo().getAttribute(SignatureAttribute.tag)
           * 
    * * @see javassist.bytecode.Descriptor * @see javassist.bytecode.SignatureAttribute */ public String getSignature() { return fieldInfo.getDescriptor(); } /** * 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)); } /** * Returns the value of this field if it is a constant field. * This method works only if the field type is a primitive type * or String type. Otherwise, it returns null. * A constant field is static and final. * * @return a Integer, Long, Float, * Double, Boolean, * or String object * representing the constant value. * null if it is not a constant field * or if the field type is not a primitive type * or String. */ public Object getConstantValue() { // When this method is modified, // see also getConstantFieldValue() in TypeChecker. int index = fieldInfo.getConstantValue(); if (index == 0) return null; ConstPool cp = fieldInfo.getConstPool(); switch (cp.getTag(index)) { case ConstPool.CONST_Long : return new Long(cp.getLongInfo(index)); case ConstPool.CONST_Float : return new Float(cp.getFloatInfo(index)); case ConstPool.CONST_Double : return new Double(cp.getDoubleInfo(index)); case ConstPool.CONST_Integer : int value = cp.getIntegerInfo(index); // "Z" means boolean type. if ("Z".equals(fieldInfo.getDescriptor())) return new Boolean(value != 0); else return new Integer(value); case ConstPool.CONST_String : return cp.getStringInfo(index); default : throw new RuntimeException("bad tag: " + cp.getTag(index) + " at " + index); } } /** * Obtains an attribute with the given name. * If that attribute is not found in the class file, this * method returns null. * *

    Note that an attribute is a data block specified by * the class file format. * See {@link javassist.bytecode.AttributeInfo}. * * @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. * *

    Note that an attribute is a data block specified by * the class file format. * See {@link javassist.bytecode.AttributeInfo}. * * @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. * Initializer is passed to * CtClass.addField() with a CtField. * *

    This class cannot be instantiated with the new operator. * Factory methods such as byParameter() and * byNew * 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, short, char, or byte type. */ public static Initializer constant(int i) { return new IntInitializer(i); } /** * Makes an initializer that assigns a constant boolean value. * The field must be boolean type. */ public static Initializer constant(boolean b) { return new IntInitializer(b ? 1 : 0); } /** * 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 float value. * The field must be float type. */ public static Initializer constant(float l) { return new FloatInitializer(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 java.lang.String type. */ public static Initializer constant(String s) { return new StringInitializer(s); } /** * Makes an initializer using a constructor parameter. * *

    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 (>= 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. * *

    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: * *

      Object obj - the object including the field.
      *
    * *

    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. * *

    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: * *

      Object obj - the object including the field.
      * String[] strs - the character strings specified * by stringParams
      *
    * *

    If the initialized field is static, then the constructor * receives only strs. * * @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. * *

    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: * *

      Object obj - the object including the field.
      * Object[] args - the parameters passed to the * constructor of the object including the * filed. *
    * *

    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. * *

    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: * *

      Object obj - the object including the field.
      * String[] strs - the character strings specified * by stringParams
      * Object[] args - the parameters passed to the * constructor of the object including the * filed. *
    * *

    If the initialized field is static, then the constructor receives * only strs. * * @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. * *

    This initializer calls a static method and uses the returned * value as the initial value of the field. * The called method receives the parameters: * *

      Object obj - the object including the field.
      *
    * *

    If the initialized field is static, then the method does * not receive any parameters. * *

    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. * *

    This initializer calls a static method and uses the returned * value as the initial value of the field. The called method * receives the parameters: * *

      Object obj - the object including the field.
      * String[] strs - the character strings specified * by stringParams
      *
    * *

    If the initialized field is static, then the method * receive only strs. * *

    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. * *

    This initializer calls a static method and uses the returned * value as the initial value of the field. The called method * receives the parameters: * *

      Object obj - the object including the field.
      * Object[] args - the parameters passed to the * constructor of the object including the * filed. *
    * *

    If the initialized field is static, then the method does * not receive any parameters. * *

    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. * *

    This initializer calls a static method and uses the returned * value as the initial value of the field. The called method * receives the parameters: * *

      Object obj - the object including the field.
      * String[] strs - the character strings specified * by stringParams
      * Object[] args - the parameters passed to the * constructor of the object including the * filed. *
    * *

    If the initialized field is static, then the method * receive only strs. * *

    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 int 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(String desc) 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; // returns the index of CONSTANT_Integer_info etc // if the value is constant. Otherwise, 0. int getConstantValue(ConstPool cp, CtClass type) { return 0; } } 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); } } int getConstantValue2(ConstPool cp, CtClass type, ASTree tree) { if (type.isPrimitive()) { if (tree instanceof IntConst) { long value = ((IntConst)tree).get(); if (type == CtClass.doubleType) return cp.addDoubleInfo((double)value); else if (type == CtClass.floatType) return cp.addFloatInfo((float)value); else if (type == CtClass.longType) return cp.addLongInfo(value); else if (type != CtClass.voidType) return cp.addIntegerInfo((int)value); } else if (tree instanceof DoubleConst) { double value = ((DoubleConst)tree).get(); if (type == CtClass.floatType) return cp.addFloatInfo((float)value); else if (type == CtClass.doubleType) return cp.addDoubleInfo(value); } } else if (tree instanceof StringL && type.getName().equals(javaLangString)) return cp.addStringInfo(((StringL)tree).get()); return 0; } } static class CodeInitializer extends CodeInitializer0 { private String expression; CodeInitializer(String expr) { expression = expr; } void compileExpr(Javac drv) throws CompileError { drv.compileExpr(expression); } int getConstantValue(ConstPool cp, CtClass type) { try { ASTree t = Javac.parseExpr(expression, new SymbolTable()); return getConstantValue2(cp, type, t); } catch (CompileError e) { return 0; } } } static class PtreeInitializer extends CodeInitializer0 { private ASTree expression; PtreeInitializer(ASTree expr) { expression = expr; } void compileExpr(Javac drv) throws CompileError { drv.compileExpr(expression); } int getConstantValue(ConstPool cp, CtClass type) { return getConstantValue2(cp, type, 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, "", 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, "", 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(javaLangString); for (int j = 0; j < nparam; ++j) { code.add(Bytecode.DUP); // dup code.addIconst(j); // iconst_ 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(String desc) throws CannotCompileException { char c = desc.charAt(0); if (c != 'I' && c != 'S' && c != 'B' && c != 'C' && c != 'Z') 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 } int getConstantValue(ConstPool cp, CtClass type) { return cp.addIntegerInfo(value); } } static class LongInitializer extends Initializer { long value; LongInitializer(long v) { value = v; } void check(String desc) throws CannotCompileException { if (!desc.equals("J")) 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 } int getConstantValue(ConstPool cp, CtClass type) { if (type == CtClass.longType) return cp.addLongInfo(value); else return 0; } } static class FloatInitializer extends Initializer { float value; FloatInitializer(float v) { value = v; } void check(String desc) throws CannotCompileException { if (!desc.equals("F")) throw new CannotCompileException("type mismatch"); } int compile(CtClass type, String name, Bytecode code, CtClass[] parameters, Javac drv) throws CannotCompileException { code.addAload(0); code.addFconst(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.addFconst(value); code.addPutstatic(Bytecode.THIS, name, Descriptor.of(type)); return 2; // stack size } int getConstantValue(ConstPool cp, CtClass type) { if (type == CtClass.floatType) return cp.addFloatInfo(value); else return 0; } } static class DoubleInitializer extends Initializer { double value; DoubleInitializer(double v) { value = v; } void check(String desc) throws CannotCompileException { if (!desc.equals("D")) 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 } int getConstantValue(ConstPool cp, CtClass type) { if (type == CtClass.doubleType) return cp.addDoubleInfo(value); else return 0; } } static class StringInitializer extends Initializer { String value; StringInitializer(String v) { value = v; } 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 } int getConstantValue(ConstPool cp, CtClass type) { if (type.getName().equals(javaLangString)) return cp.addStringInfo(value); else return 0; } } 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(String desc) throws CannotCompileException { if (desc.charAt(0) != '[') 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 } } } javassist-3.12.1.ga/src/main/javassist/CtBehavior.java0000644000175000017500000012277111275555573022575 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import javassist.bytecode.*; import javassist.compiler.Javac; import javassist.compiler.CompileError; import javassist.expr.ExprEditor; /** * CtBehavior represents a method, a constructor, * or a static constructor (class initializer). * It is the abstract super class of * CtMethod and CtConstructor. */ public abstract class CtBehavior extends CtMember { protected MethodInfo methodInfo; protected CtBehavior(CtClass clazz, MethodInfo minfo) { super(clazz); methodInfo = minfo; } /** * @param isCons true if this is a constructor. */ void copy(CtBehavior src, boolean isCons, ClassMap map) throws CannotCompileException { CtClass declaring = declaringClass; MethodInfo srcInfo = src.methodInfo; CtClass srcClass = src.getDeclaringClass(); ConstPool cp = declaring.getClassFile2().getConstPool(); map = new ClassMap(map); map.put(srcClass.getName(), declaring.getName()); try { boolean patch = false; CtClass srcSuper = srcClass.getSuperclass(); CtClass destSuper = declaring.getSuperclass(); String destSuperName = null; if (srcSuper != null && destSuper != null) { String srcSuperName = srcSuper.getName(); destSuperName = destSuper.getName(); if (!srcSuperName.equals(destSuperName)) if (srcSuperName.equals(CtClass.javaLangObject)) patch = true; else map.putIfNone(srcSuperName, destSuperName); } // a stack map table is copied from srcInfo. methodInfo = new MethodInfo(cp, srcInfo.getName(), srcInfo, map); if (isCons && patch) methodInfo.setSuperclass(destSuperName); } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException(e); } } protected void extendToString(StringBuffer buffer) { buffer.append(' '); buffer.append(getName()); buffer.append(' '); buffer.append(methodInfo.getDescriptor()); } /** * Returns the method or constructor name followed by parameter types * such as javassist.CtBehavior.stBody(String). * * @since 3.5 */ public abstract String getLongName(); /** * Returns the MethodInfo representing this method/constructor in the * class file. */ public MethodInfo getMethodInfo() { declaringClass.checkModify(); return methodInfo; } /** * Returns the MethodInfo representing the method/constructor in the * class file (read only). * Normal applications do not need calling this method. Use * getMethodInfo(). * *

    The MethodInfo object obtained by this method * is read only. Changes to this object might not be reflected * on a class file generated by toBytecode(), * toClass(), etc in CtClass. * *

    This method is available even if the CtClass * containing this method is frozen. However, if the class is * frozen, the MethodInfo might be also pruned. * * @see #getMethodInfo() * @see CtClass#isFrozen() * @see CtClass#prune() */ public MethodInfo getMethodInfo2() { return methodInfo; } /** * Obtains the modifiers of the method/constructor. * * @return modifiers encoded with * javassist.Modifier. * @see Modifier */ public int getModifiers() { return AccessFlag.toModifier(methodInfo.getAccessFlags()); } /** * Sets the encoded modifiers of the method/constructor. * *

    Changing the modifiers may cause a problem. * For example, if a non-static method is changed to static, * the method will be rejected by the bytecode verifier. * * @see Modifier */ public void setModifiers(int mod) { declaringClass.checkModify(); methodInfo.setAccessFlags(AccessFlag.of(mod)); } /** * Returns true if the class has the specified annotation class. * * @param clz the annotation class. * @return true if the annotation is found, * otherwise false. * @since 3.11 */ public boolean hasAnnotation(Class clz) { MethodInfo mi = getMethodInfo2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.visibleTag); return CtClassType.hasAnnotationType(clz, getDeclaringClass().getClassPool(), ainfo, ainfo2); } /** * Returns the annotation if the class has the specified annotation class. * For example, if an annotation @Author is associated * with this method/constructor, an Author object is returned. * The member values can be obtained by calling methods on * the Author object. * * @param clz the annotation class. * @return the annotation if found, otherwise null. * @since 3.11 */ public Object getAnnotation(Class clz) throws ClassNotFoundException { MethodInfo mi = getMethodInfo2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.visibleTag); return CtClassType.getAnnotationType(clz, getDeclaringClass().getClassPool(), ainfo, ainfo2); } /** * Returns the annotations associated with this method or constructor. * * @return an array of annotation-type objects. * @see #getAvailableAnnotations() * @since 3.1 */ public Object[] getAnnotations() throws ClassNotFoundException { return getAnnotations(false); } /** * Returns the annotations associated with this method or constructor. * If any annotations are not on the classpath, they are not included * in the returned array. * * @return an array of annotation-type objects. * @see #getAnnotations() * @since 3.3 */ public Object[] getAvailableAnnotations(){ try{ return getAnnotations(true); } catch (ClassNotFoundException e){ throw new RuntimeException("Unexpected exception", e); } } private Object[] getAnnotations(boolean ignoreNotFound) throws ClassNotFoundException { MethodInfo mi = getMethodInfo2(); AnnotationsAttribute ainfo = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.invisibleTag); AnnotationsAttribute ainfo2 = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.visibleTag); return CtClassType.toAnnotationType(ignoreNotFound, getDeclaringClass().getClassPool(), ainfo, ainfo2); } /** * Returns the parameter annotations associated with this method or constructor. * * @return an array of annotation-type objects. The length of the returned array is * equal to the number of the formal parameters. If each parameter has no * annotation, the elements of the returned array are empty arrays. * * @see #getAvailableParameterAnnotations() * @see #getAnnotations() * @since 3.1 */ public Object[][] getParameterAnnotations() throws ClassNotFoundException { return getParameterAnnotations(false); } /** * Returns the parameter annotations associated with this method or constructor. * If any annotations are not on the classpath, they are not included in the * returned array. * * @return an array of annotation-type objects. The length of the returned array is * equal to the number of the formal parameters. If each parameter has no * annotation, the elements of the returned array are empty arrays. * * @see #getParameterAnnotations() * @see #getAvailableAnnotations() * @since 3.3 */ public Object[][] getAvailableParameterAnnotations(){ try { return getParameterAnnotations(true); } catch(ClassNotFoundException e) { throw new RuntimeException("Unexpected exception", e); } } Object[][] getParameterAnnotations(boolean ignoreNotFound) throws ClassNotFoundException { MethodInfo mi = getMethodInfo2(); ParameterAnnotationsAttribute ainfo = (ParameterAnnotationsAttribute) mi.getAttribute(ParameterAnnotationsAttribute.invisibleTag); ParameterAnnotationsAttribute ainfo2 = (ParameterAnnotationsAttribute) mi.getAttribute(ParameterAnnotationsAttribute.visibleTag); return CtClassType.toAnnotationType(ignoreNotFound, getDeclaringClass().getClassPool(), ainfo, ainfo2, mi); } /** * Obtains parameter types of this method/constructor. */ 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 method signature (the parameter types * and the return type). * The method signature is represented by a character string * called method descriptor, which is defined in the JVM specification. * If two methods/constructors have * the same parameter types * and the return type, getSignature() returns the * same string (the return type of constructors is void). * *

    Note that the returned string is not the type signature * contained in the SignatureAttirbute. It is * a descriptor. To obtain a type signature, call the following * methods: * *

      getMethodInfo().getAttribute(SignatureAttribute.tag)
           * 
    * * @see javassist.bytecode.Descriptor * @see javassist.bytecode.SignatureAttribute */ public String getSignature() { return methodInfo.getDescriptor(); } /** * Obtains exceptions that this method/constructor may throw. * * @return a zero-length array if there is no throws clause. */ 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 method/constructor may throw. */ public void setExceptionTypes(CtClass[] types) throws NotFoundException { declaringClass.checkModify(); if (types == null || types.length == 0) { 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); } /** * Returns true if the body is empty. */ public abstract boolean isEmpty(); /** * Sets a method/constructor body. * * @param src the source code representing the body. * It must be a single statement or block. * If it is null, the substituted * body does nothing except returning zero or null. */ public void setBody(String src) throws CannotCompileException { setBody(src, null, null); } /** * Sets a method/constructor body. * * @param src the source code representing the body. * It must be a single statement or block. * If it is null, the substituted * body does nothing except returning zero or null. * @param delegateObj the source text specifying the object * that is called on by $proceed(). * @param delegateMethod the name of the method * that is called by $proceed(). */ public void setBody(String src, String delegateObj, String delegateMethod) throws CannotCompileException { CtClass cc = declaringClass; cc.checkModify(); try { Javac jv = new Javac(cc); if (delegateMethod != null) jv.recordProceed(delegateObj, delegateMethod); Bytecode b = jv.compileBody(this, src); methodInfo.setCodeAttribute(b.toCodeAttribute()); methodInfo.setAccessFlags(methodInfo.getAccessFlags() & ~AccessFlag.ABSTRACT); methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); declaringClass.rebuildClassFile(); } catch (CompileError e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException(e); } } static void setBody0(CtClass srcClass, MethodInfo srcInfo, CtClass destClass, MethodInfo destInfo, ClassMap map) throws CannotCompileException { destClass.checkModify(); map = new ClassMap(map); 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); // a stack map table is copied to destInfo. } } catch (CodeAttribute.RuntimeCopyException e) { /* the exception may be thrown by copy() in CodeAttribute. */ throw new CannotCompileException(e); } destInfo.setAccessFlags(destInfo.getAccessFlags() & ~AccessFlag.ABSTRACT); destClass.rebuildClassFile(); } /** * Obtains an attribute with the given name. * If that attribute is not found in the class file, this * method returns null. * *

    Note that an attribute is a data block specified by * the class file format. It is not an annotation. * See {@link javassist.bytecode.AttributeInfo}. * * @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. * *

    Note that an attribute is a data block specified by * the class file format. It is not an annotation. * See {@link javassist.bytecode.AttributeInfo}. * * @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 $cflow for this method/constructor. * If $cflow is used, the class files modified * with Javassist requires a support class * javassist.runtime.Cflow at runtime * (other Javassist classes are not required at runtime). * *

    Every $cflow variable is given a unique name. * For example, if the given name is "Point.paint", * then the variable is indicated by $cflow(Point.paint). * * @param name $cflow name. It can include * alphabets, numbers, _, * $, and . (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();", false); String src = fname + ".exit();"; insertAfter(src, true); } catch (NotFoundException e) { throw new CannotCompileException(e); } } /** * Declares a new local variable. The scope of this variable is the * whole method body. The initial value of that variable is not set. * The declared variable can be accessed in the code snippet inserted * by insertBefore(), insertAfter(), etc. * *

    If the second parameter asFinally to * insertAfter() is true, the declared local variable * is not visible from the code inserted by insertAfter(). * * @param name the name of the variable * @param type the type of the variable * @see #insertBefore(String) * @see #insertAfter(String) */ public void addLocalVariable(String name, CtClass type) throws CannotCompileException { declaringClass.checkModify(); ConstPool cp = methodInfo.getConstPool(); CodeAttribute ca = methodInfo.getCodeAttribute(); if (ca == null) throw new CannotCompileException("no method body"); LocalVariableAttribute va = (LocalVariableAttribute)ca.getAttribute( LocalVariableAttribute.tag); if (va == null) { va = new LocalVariableAttribute(cp); ca.getAttributes().add(va); } int maxLocals = ca.getMaxLocals(); String desc = Descriptor.of(type); va.addEntry(0, ca.getCodeLength(), cp.addUtf8Info(name), cp.addUtf8Info(desc), maxLocals); ca.setMaxLocals(maxLocals + Descriptor.dataSize(desc)); } /** * Inserts a new parameter, which becomes the first parameter. */ public void insertParameter(CtClass type) throws CannotCompileException { declaringClass.checkModify(); String desc = methodInfo.getDescriptor(); String desc2 = Descriptor.insertParameter(type, desc); try { addParameter2(Modifier.isStatic(getModifiers()) ? 0 : 1, type, desc); } catch (BadBytecode e) { throw new CannotCompileException(e); } methodInfo.setDescriptor(desc2); } /** * Appends a new parameter, which becomes the last parameter. */ public void addParameter(CtClass type) throws CannotCompileException { declaringClass.checkModify(); String desc = methodInfo.getDescriptor(); String desc2 = Descriptor.appendParameter(type, desc); int offset = Modifier.isStatic(getModifiers()) ? 0 : 1; try { addParameter2(offset + Descriptor.paramSize(desc), type, desc); } catch (BadBytecode e) { throw new CannotCompileException(e); } methodInfo.setDescriptor(desc2); } private void addParameter2(int where, CtClass type, String desc) throws BadBytecode { CodeAttribute ca = methodInfo.getCodeAttribute(); if (ca != null) { int size = 1; char typeDesc = 'L'; int classInfo = 0; if (type.isPrimitive()) { CtPrimitiveType cpt = (CtPrimitiveType)type; size = cpt.getDataSize(); typeDesc = cpt.getDescriptor(); } else classInfo = methodInfo.getConstPool().addClassInfo(type); ca.insertLocalVar(where, size); LocalVariableAttribute va = (LocalVariableAttribute) ca.getAttribute(LocalVariableAttribute.tag); if (va != null) va.shiftIndex(where, size); StackMapTable smt = (StackMapTable)ca.getAttribute(StackMapTable.tag); if (smt != null) smt.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo); StackMap sm = (StackMap)ca.getAttribute(StackMap.tag); if (sm != null) sm.insertLocal(where, StackMapTable.typeTagOf(typeDesc), classInfo); } } /** * Modifies the method/constructor 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 method/constructor body. * * @param editor specifies how to modify. */ public void instrument(ExprEditor editor) throws CannotCompileException { // if the class is not frozen, // does not turn the modified flag on. if (declaringClass.isFrozen()) declaringClass.checkModify(); if (editor.doit(declaringClass, methodInfo)) declaringClass.checkModify(); } /** * Inserts bytecode at the beginning of the body. * *

    If this object represents a constructor, * the bytecode is inserted before * a constructor in the super class or this class is called. * Therefore, the inserted 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 fields or methods although * it may assign a value to an instance field directly declared in this * class. Accessing static fields and methods is allowed. * Use insertBeforeBody() in CtConstructor. * * @param src the source code representing the inserted bytecode. * It must be a single statement or block. * @see CtConstructor#insertBeforeBody(String) */ public void insertBefore(String src) throws CannotCompileException { insertBefore(src, true); } private void insertBefore(String src, boolean rebuild) throws CannotCompileException { CtClass cc = declaringClass; cc.checkModify(); CodeAttribute ca = methodInfo.getCodeAttribute(); if (ca == null) throw new CannotCompileException("no method body"); CodeIterator iterator = ca.iterator(); Javac jv = new Javac(cc); try { int nvars = jv.recordParams(getParameterTypes(), Modifier.isStatic(getModifiers())); jv.recordParamNames(ca, nvars); jv.recordLocalVariables(ca, 0); jv.recordType(getReturnType0()); 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); if (rebuild) methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); } 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. * If this parameter is true, the inserted code cannot * access local variables. */ public void insertAfter(String src, boolean asFinally) throws CannotCompileException { CtClass cc = declaringClass; cc.checkModify(); ConstPool pool = methodInfo.getConstPool(); CodeAttribute ca = methodInfo.getCodeAttribute(); if (ca == null) throw new CannotCompileException("no method body"); CodeIterator iterator = ca.iterator(); int retAddr = ca.getMaxLocals(); Bytecode b = new Bytecode(pool, 0, retAddr + 1); b.setStackDepth(ca.getMaxStack() + 1); Javac jv = new Javac(b, cc); try { int nvars = jv.recordParams(getParameterTypes(), Modifier.isStatic(getModifiers())); jv.recordParamNames(ca, nvars); CtClass rtype = getReturnType0(); int varNo = jv.recordReturnType(rtype, true); jv.recordLocalVariables(ca, 0); // finally clause for exceptions int handlerLen = insertAfterHandler(asFinally, b, rtype, varNo, jv, src); // finally clause for normal termination insertAfterAdvice(b, jv, src, pool, rtype, varNo); ca.setMaxStack(b.getMaxStack()); ca.setMaxLocals(b.getMaxLocals()); int gapPos = iterator.append(b.get()); iterator.append(b.getExceptionTable(), gapPos); if (asFinally) ca.getExceptionTable().add(getStartPosOfBody(ca), 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) { insertGoto(iterator, subr, pos); subr = iterator.getCodeLength() - gapLen; } } methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (CompileError e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException(e); } } private void insertAfterAdvice(Bytecode code, Javac jv, String src, ConstPool cp, CtClass rtype, int varNo) throws CompileError { if (rtype == CtClass.voidType) { code.addOpcode(Opcode.ACONST_NULL); code.addAstore(varNo); jv.compileStmnt(src); code.addOpcode(Opcode.RETURN); if (code.getMaxLocals() < 1) code.setMaxLocals(1); } else { code.addStore(varNo, rtype); jv.compileStmnt(src); code.addLoad(varNo, rtype); if (rtype.isPrimitive()) code.addOpcode(((CtPrimitiveType)rtype).getReturnOp()); else code.addOpcode(Opcode.ARETURN); } } /* * assert subr > pos */ private void insertGoto(CodeIterator iterator, int subr, int pos) throws BadBytecode { iterator.setMark(subr); // the gap length might be a multiple of 4. iterator.writeByte(Opcode.NOP, pos); boolean wide = subr + 2 - pos > Short.MAX_VALUE; pos = iterator.insertGapAt(pos, wide ? 4 : 2, false).position; int offset = iterator.getMark() - pos; if (wide) { iterator.writeByte(Opcode.GOTO_W, pos); iterator.write32bit(offset, pos + 1); } else if (offset <= Short.MAX_VALUE) { iterator.writeByte(Opcode.GOTO, pos); iterator.write16bit(offset, pos + 1); } else { pos = iterator.insertGapAt(pos, 2, false).position; iterator.writeByte(Opcode.GOTO_W, pos); iterator.write32bit(iterator.getMark() - pos, pos + 1); } } /* insert a finally clause */ private int insertAfterHandler(boolean asFinally, Bytecode b, CtClass rtype, int returnVarNo, Javac javac, String src) throws CompileError { if (!asFinally) return 0; int var = b.getMaxLocals(); b.incMaxLocals(1); int pc = b.currentPc(); b.addAstore(var); // store an exception if (rtype.isPrimitive()) { char c = ((CtPrimitiveType)rtype).getDescriptor(); if (c == 'D') { b.addDconst(0.0); b.addDstore(returnVarNo); } else if (c == 'F') { b.addFconst(0); b.addFstore(returnVarNo); } else if (c == 'J') { b.addLconst(0); b.addLstore(returnVarNo); } else if (c == 'V') { b.addOpcode(Opcode.ACONST_NULL); b.addAstore(returnVarNo); } else { // int, boolean, char, short, ... b.addIconst(0); b.addIstore(returnVarNo); } } else { b.addOpcode(Opcode.ACONST_NULL); b.addAstore(returnVarNo); } javac.compileStmnt(src); b.addAload(var); b.addOpcode(Opcode.ATHROW); return b.currentPc() - 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. */ public void addCatch(String src, CtClass exceptionType) throws CannotCompileException { addCatch(src, exceptionType, "$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, for example, * $e. */ public void addCatch(String src, CtClass exceptionType, String exceptionName) throws CannotCompileException { CtClass cc = declaringClass; cc.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, cc); 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(getStartPosOfBody(ca), len, len, cp.addClassInfo(exceptionType)); iterator.append(b.getExceptionTable(), pos); methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (CompileError e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException(e); } } /* CtConstructor overrides this method. */ int getStartPosOfBody(CodeAttribute ca) throws CannotCompileException { return 0; } /** * Inserts bytecode at the specified line in the body. * It is equivalent to: * *
    insertAt(lineNum, true, src) * *
    See this method as well. * * @param lineNum the line number. The bytecode is inserted at the * beginning of the code at the line specified by this * line number. * @param src the source code representing the inserted bytecode. * It must be a single statement or block. * @return the line number at which the bytecode has been inserted. * * @see CtBehavior#insertAt(int,boolean,String) */ public int insertAt(int lineNum, String src) throws CannotCompileException { return insertAt(lineNum, true, src); } /** * Inserts bytecode at the specified line in the body. * *

    If there is not * a statement at the specified line, the bytecode might be inserted * at the line including the first statement after that line specified. * For example, if there is only a closing brace at that line, the * bytecode would be inserted at another line below. * To know exactly where the bytecode will be inserted, call with * modify set to false. * * @param lineNum the line number. The bytecode is inserted at the * beginning of the code at the line specified by this * line number. * @param modify if false, this method does not insert the bytecode. * It instead only returns the line number at which * the bytecode would be inserted. * @param src the source code representing the inserted bytecode. * It must be a single statement or block. * If modify is false, the value of src can be null. * @return the line number at which the bytecode has been inserted. */ public int insertAt(int lineNum, boolean modify, String src) throws CannotCompileException { CodeAttribute ca = methodInfo.getCodeAttribute(); if (ca == null) throw new CannotCompileException("no method body"); LineNumberAttribute ainfo = (LineNumberAttribute)ca.getAttribute(LineNumberAttribute.tag); if (ainfo == null) throw new CannotCompileException("no line number info"); LineNumberAttribute.Pc pc = ainfo.toNearPc(lineNum); lineNum = pc.line; int index = pc.index; if (!modify) return lineNum; CtClass cc = declaringClass; cc.checkModify(); CodeIterator iterator = ca.iterator(); Javac jv = new Javac(cc); try { jv.recordLocalVariables(ca, index); jv.recordParams(getParameterTypes(), Modifier.isStatic(getModifiers())); jv.setMaxLocals(ca.getMaxLocals()); jv.compileStmnt(src); Bytecode b = jv.getBytecode(); int locals = b.getMaxLocals(); int stack = b.getMaxStack(); ca.setMaxLocals(locals); /* We assume that there is no values in the operand stack * at the position where the bytecode is inserted. */ if (stack > ca.getMaxStack()) ca.setMaxStack(stack); index = iterator.insertAt(index, b.get()); iterator.insert(b.getExceptionTable(), index); methodInfo.rebuildStackMapIf6(cc.getClassPool(), cc.getClassFile2()); return lineNum; } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (CompileError e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException(e); } } } javassist-3.12.1.ga/src/main/javassist/CtArray.java0000644000175000017500000000560411373532244022074 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; /** * 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; } private CtClass[] interfaces = null; public int getModifiers() { int mod = Modifier.FINAL; try { mod |= getComponentType().getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC | Modifier.PRIVATE); } catch (NotFoundException e) {} return mod; } public CtClass[] getInterfaces() throws NotFoundException { if (interfaces == null) interfaces = new CtClass[] { pool.get("java.lang.Cloneable"), pool.get("java.io.Serializable") }; return interfaces; } 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") || cname.equals("java.io.Serializable")) 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(); } } } javassist-3.12.1.ga/src/main/javassist/Loader.java0000644000175000017500000003526610630701321021731 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import java.io.*; import java.util.Hashtable; import java.util.Vector; import java.security.ProtectionDomain; /** * The class loader for Javassist. * *

    This is a sample class loader using ClassPool. * Unlike a regular class loader, this class loader obtains bytecode * from a ClassPool. * *

    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. * *

    Suppose that an instance of MyTranslator implementing * the interface Translator is responsible for modifying * class files. * The startup program of an application using MyTranslator * should be something like this: * *

       * import javassist.*;
       *
       * public class Main {
       *   public static void main(String[] args) throws Throwable {
       *     MyTranslator myTrans = new MyTranslator();
       *     ClassPool cp = ClassPool.getDefault();
       *     Loader cl = new Loader(cp);
       *     cl.addTranslator(cp, myTrans);
       *     cl.run("MyApp", args);
       *   }
       * }
       * 
    * *

    Class MyApp is the main program of the application. * *

    This program should be executed as follows: * *

       * % java Main arg1 arg2...
       * 
    * *

    It modifies the class MyApp with a MyTranslator * object before the JVM loads it. * Then it calls main() in MyApp with arguments * arg1, arg2, ... * *

    This program execution is equivalent to: * *

       * % java MyApp arg1 arg2...
       * 
    * *

    except that classes are translated by MyTranslator * at load time. * *

    If only a particular class must be modified when it is loaded, * the startup program can be simpler; MyTranslator is * unnecessary. For example, if only a class test.Rectangle * is modified, the main() method above will be the following: * *

       * ClassPool cp = ClassPool.getDefault();
       * Loader cl = new Loader(cp);
       * CtClass ct = cp.get("test.Rectangle");
       * ct.setSuperclass(cp.get("test.Point"));
       * cl.run("MyApp", args);
    * *

    This program changes the super class of the test.Rectangle * class. * *

    Note 1: * *

    This class loader does not allow the users to intercept the loading * of java.* and javax.* classes (and * sun.*, org.xml.*, ...) unless * Loader.doDelegation is false. This is because * the JVM prohibits a user class loader from loading a system class. * Also see Note 2. * If this behavior is not appropriate, a subclass of Loader * must be defined and loadClassByDelegation() must be overridden. * *

    Note 2: * *

    If classes are loaded with different class loaders, they belong to * separate name spaces. If class C is loaded by a class * loader CL, all classes that the class C * refers to are also loaded by CL. However, if CL * delegates the loading of the class C to CL', * then those classes that the class C refers to * are loaded by a parent class loader CL' * instead of CL. * *

    If an object of class C is assigned * to a variable of class C belonging to a different name * space, then a ClassCastException is thrown. * *

    Because of the fact above, this loader delegates only the loading of * javassist.Loader * and classes included in package java.* and * javax.* to the parent class * loader. Other classes are directly loaded by this loader. * *

    For example, suppose that java.lang.String would be loaded * by this loader while java.io.File is loaded by the parent * class loader. If the constructor of java.io.File is called * with an instance of java.lang.String, then it may throw * an exception since it accepts an instance of only the * java.lang.String 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; private Translator translator; private ProtectionDomain domain; /** * Specifies the algorithm of class loading. * *

    This class loader uses the parent class loader for * java.* and javax.* classes. * If this variable doDelegation * is false, this class loader does not delegate those * classes to the parent class loader. * *

    The default value is true. */ 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) { init(cp); } /** * Creates a new class loader * using the specified parent class loader for delegation. * * @param parent the parent class loader. * @param cp the source of class files. */ public Loader(ClassLoader parent, ClassPool cp) { super(parent); init(cp); } private void init(ClassPool cp) { notDefinedHere = new Hashtable(); notDefinedPackages = new Vector(); source = cp; translator = null; domain = null; delegateLoadingOf("javassist.Loader"); } /** * Records a class so that the loading of that class is delegated * to the parent class loader. * *

    If the given class name ends with . (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 protection domain for the classes handled by this class * loader. Without registering an appropriate protection domain, * the program loaded by this loader will not work with a security * manager or a signed jar file. */ public void setDomain(ProtectionDomain d) { domain = d; } /** * Sets the soruce ClassPool. */ public void setClassPool(ClassPool cp) { source = cp; } /** * Adds a translator, which is called whenever a class is loaded. * * @param cp the ClassPool object for obtaining * a class file. * @param t a translator. * @throws NotFoundException if t.start() throws an exception. * @throws CannotCompileException if t.start() throws an exception. */ public void addTranslator(ClassPool cp, Translator t) throws NotFoundException, CannotCompileException { source = cp; translator = t; t.start(cp); } /** * Loads a class with an instance of Loader * and calls main() of that class. * *

    This method calls run(). * * @param args command line parameters. *

      * args[0] is the class name to be loaded. *
      args[1..n] are parameters passed * to the target main(). *
    * * @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 main() in that class. * * @param args command line parameters. *
      * args[0] is the class name to be loaded. *
      args[1..n] are parameters passed * to the target main(). *
    */ 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 main() in that class. * * @param classname the loaded class. * @param args parameters passed to main(). */ 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 { name = name.intern(); synchronized (name) { 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 ClassPath. * If the source throws an exception, this returns null. * *

    This method can be overridden by a subclass of * Loader. Note that the overridden method must not throw * an exception when it just fails to find a class file. * * @return null if the specified class could not be found. * @throws ClassNotFoundException if an exception is thrown while * obtaining a class file. */ protected Class findClass(String name) throws ClassNotFoundException { byte[] classfile; try { if (source != null) { if (translator != null) translator.onLoad(source, name); try { classfile = source.get(name).toBytecode(); } catch (NotFoundException e) { return null; } } else { String jarname = "/" + name.replace('.', '/') + ".class"; InputStream in = this.getClass().getResourceAsStream(jarname); if (in == null) return null; classfile = ClassPoolTail.readStream(in); } } catch (Exception e) { throw new ClassNotFoundException( "caught an exception while obtaining a class file for " + name, e); } int i = name.lastIndexOf('.'); if (i != -1) { String pname = name.substring(0, i); if (getPackage(pname) == null) try { definePackage( pname, null, null, null, null, null, null, null); } catch (IllegalArgumentException e) { // ignore. maybe the package object for the same // name has been created just right away. } } if (domain == null) return defineClass(name, classfile, 0, classfile.length); else return defineClass(name, classfile, 0, classfile.length, domain); } protected 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.") || name.startsWith("org.w3c.") || name.startsWith("org.xml.") || 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; } protected Class delegateToParent(String classname) throws ClassNotFoundException { ClassLoader cl = getParent(); if (cl != null) return cl.loadClass(classname); else return findSystemClass(classname); } protected Package getPackage(String name) { return super.getPackage(name); } /* // Package p = super.getPackage(name); Package p = null; if (p == null) return definePackage(name, null, null, null, null, null, null, null); else return p; } */ } javassist-3.12.1.ga/src/main/javassist/package.html0000644000175000017500000000112710013664324022134 0ustar twernertwerner The Javassist Core API.

    Javassist (Java programming assistant) makes bytecode engineering simple. It is a class library for editing bytecode in Java; it enables Java programs to define a new class at runtime and to modify a given class file when the JVM loads it.

    The most significant class of this package is CtClass. See the description of this class first.

    To know the version number of this package, type the following command:

      java -jar javassist.jar
      

    It prints the version number on the console. javassist-3.12.1.ga/src/main/javassist/ByteArrayClassPath.java0000644000175000017500000000602010630701321024212 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import java.io.*; import java.net.URL; import java.net.MalformedURLException; /** * A ByteArrayClassPath contains bytes that is served as * a class file to a ClassPool. It is useful to convert * a byte array to a CtClass object. * *

    For example, if you want to convert a byte array b * into a CtClass object representing the class with a name * classname, then do as following: * *

       * ClassPool cp = ClassPool.getDefault();
       * cp.insertClassPath(new ByteArrayClassPath(classname, b));
       * CtClass cc = cp.get(classname);
       * 
    * *

    The ClassPool object cp uses the created * ByteArrayClassPath object as the source of the class file. * *

    A ByteArrayClassPath must be instantiated for every * class. It contains only a single class file. * * @see javassist.ClassPath * @see ClassPool#insertClassPath(ClassPath) * @see ClassPool#appendClassPath(ClassPath) * @see ClassPool#makeClass(InputStream) */ public class ByteArrayClassPath implements ClassPath { protected String classname; protected byte[] classfile; /* * Creates a ByteArrayClassPath 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; } /** * Closes this class path. */ public void close() {} public String toString() { return "byte[]:" + classname; } /** * Opens the class file. */ public InputStream openClassfile(String classname) { if(this.classname.equals(classname)) return new ByteArrayInputStream(classfile); else return null; } /** * Obtains the URL. */ public URL find(String classname) { if(this.classname.equals(classname)) { String cname = classname.replace('.', '/') + ".class"; try { // return new File(cname).toURL(); return new URL("file:/ByteArrayClassPath/" + cname); } catch (MalformedURLException e) {} } return null; } } javassist-3.12.1.ga/src/main/javassist/convert/0000755000175000017500000000000011637463326021346 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/convert/TransformFieldAccess.java0000644000175000017500000000550510630701321026235 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.convert; import javassist.bytecode.*; import javassist.CtClass; import javassist.CtField; import javassist.Modifier; 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; } } javassist-3.12.1.ga/src/main/javassist/convert/TransformAccessArrayField.java0000644000175000017500000002213011221067671027237 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.convert; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.NotFoundException; import javassist.CodeConverter.ArrayAccessReplacementMethodNames; import javassist.bytecode.BadBytecode; import javassist.bytecode.CodeIterator; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.MethodInfo; import javassist.bytecode.analysis.Analyzer; import javassist.bytecode.analysis.Frame; /** * A transformer which replaces array access with static method invocations. * * @author Kabir Khan * @author Jason T. Greene * @version $Revision: 1.8 $ */ public final class TransformAccessArrayField extends Transformer { private final String methodClassname; private final ArrayAccessReplacementMethodNames names; private Frame[] frames; private int offset; public TransformAccessArrayField(Transformer next, String methodClassname, ArrayAccessReplacementMethodNames names) throws NotFoundException { super(next); this.methodClassname = methodClassname; this.names = names; } public void initialize(ConstPool cp, CtClass clazz, MethodInfo minfo) throws CannotCompileException { /* * This transformer must be isolated from other transformers, since some * of them affect the local variable and stack maximums without updating * the code attribute to reflect the changes. This screws up the * data-flow analyzer, since it relies on consistent code state. Even * if the attribute values were updated correctly, we would have to * detect it, and redo analysis, which is not cheap. Instead, we are * better off doing all changes in initialize() before everyone else has * a chance to muck things up. */ CodeIterator iterator = minfo.getCodeAttribute().iterator(); while (iterator.hasNext()) { try { int pos = iterator.next(); int c = iterator.byteAt(pos); if (c == AALOAD) initFrames(clazz, minfo); if (c == AALOAD || c == BALOAD || c == CALOAD || c == DALOAD || c == FALOAD || c == IALOAD || c == LALOAD || c == SALOAD) { pos = replace(cp, iterator, pos, c, getLoadReplacementSignature(c)); } else if (c == AASTORE || c == BASTORE || c == CASTORE || c == DASTORE || c == FASTORE || c == IASTORE || c == LASTORE || c == SASTORE) { pos = replace(cp, iterator, pos, c, getStoreReplacementSignature(c)); } } catch (Exception e) { throw new CannotCompileException(e); } } } public void clean() { frames = null; offset = -1; } public int transform(CtClass tclazz, int pos, CodeIterator iterator, ConstPool cp) throws BadBytecode { // Do nothing, see above comment return pos; } private Frame getFrame(int pos) throws BadBytecode { return frames[pos - offset]; // Adjust pos } private void initFrames(CtClass clazz, MethodInfo minfo) throws BadBytecode { if (frames == null) { frames = ((new Analyzer())).analyze(clazz, minfo); offset = 0; // start tracking changes } } private int updatePos(int pos, int increment) { if (offset > -1) offset += increment; return pos + increment; } private String getTopType(int pos) throws BadBytecode { Frame frame = getFrame(pos); if (frame == null) return null; CtClass clazz = frame.peek().getCtClass(); return clazz != null ? Descriptor.toJvmName(clazz) : null; } private int replace(ConstPool cp, CodeIterator iterator, int pos, int opcode, String signature) throws BadBytecode { String castType = null; String methodName = getMethodName(opcode); if (methodName != null) { // See if the object must be cast if (opcode == AALOAD) { castType = getTopType(iterator.lookAhead()); // Do not replace an AALOAD instruction that we do not have a type for // This happens when the state is guaranteed to be null (Type.UNINIT) // So we don't really care about this case. if (castType == null) return pos; if ("java/lang/Object".equals(castType)) castType = null; } // The gap may include extra padding // Write a nop in case the padding pushes the instruction forward iterator.writeByte(NOP, pos); CodeIterator.Gap gap = iterator.insertGapAt(pos, castType != null ? 5 : 2, false); pos = gap.position; int mi = cp.addClassInfo(methodClassname); int methodref = cp.addMethodrefInfo(mi, methodName, signature); iterator.writeByte(INVOKESTATIC, pos); iterator.write16bit(methodref, pos + 1); if (castType != null) { int index = cp.addClassInfo(castType); iterator.writeByte(CHECKCAST, pos + 3); iterator.write16bit(index, pos + 4); } pos = updatePos(pos, gap.length); } return pos; } private String getMethodName(int opcode) { String methodName = null; switch (opcode) { case AALOAD: methodName = names.objectRead(); break; case BALOAD: methodName = names.byteOrBooleanRead(); break; case CALOAD: methodName = names.charRead(); break; case DALOAD: methodName = names.doubleRead(); break; case FALOAD: methodName = names.floatRead(); break; case IALOAD: methodName = names.intRead(); break; case SALOAD: methodName = names.shortRead(); break; case LALOAD: methodName = names.longRead(); break; case AASTORE: methodName = names.objectWrite(); break; case BASTORE: methodName = names.byteOrBooleanWrite(); break; case CASTORE: methodName = names.charWrite(); break; case DASTORE: methodName = names.doubleWrite(); break; case FASTORE: methodName = names.floatWrite(); break; case IASTORE: methodName = names.intWrite(); break; case SASTORE: methodName = names.shortWrite(); break; case LASTORE: methodName = names.longWrite(); break; } if (methodName.equals("")) methodName = null; return methodName; } private String getLoadReplacementSignature(int opcode) throws BadBytecode { switch (opcode) { case AALOAD: return "(Ljava/lang/Object;I)Ljava/lang/Object;"; case BALOAD: return "(Ljava/lang/Object;I)B"; case CALOAD: return "(Ljava/lang/Object;I)C"; case DALOAD: return "(Ljava/lang/Object;I)D"; case FALOAD: return "(Ljava/lang/Object;I)F"; case IALOAD: return "(Ljava/lang/Object;I)I"; case SALOAD: return "(Ljava/lang/Object;I)S"; case LALOAD: return "(Ljava/lang/Object;I)J"; } throw new BadBytecode(opcode); } private String getStoreReplacementSignature(int opcode) throws BadBytecode { switch (opcode) { case AASTORE: return "(Ljava/lang/Object;ILjava/lang/Object;)V"; case BASTORE: return "(Ljava/lang/Object;IB)V"; case CASTORE: return "(Ljava/lang/Object;IC)V"; case DASTORE: return "(Ljava/lang/Object;ID)V"; case FASTORE: return "(Ljava/lang/Object;IF)V"; case IASTORE: return "(Ljava/lang/Object;II)V"; case SASTORE: return "(Ljava/lang/Object;IS)V"; case LASTORE: return "(Ljava/lang/Object;IJ)V"; } throw new BadBytecode(opcode); } } javassist-3.12.1.ga/src/main/javassist/convert/TransformReadField.java0000644000175000017500000000661611221067671025725 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.convert; import javassist.bytecode.*; import javassist.ClassPool; import javassist.CtClass; import javassist.CtField; 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 (c == fclass || (!is_private && isFieldInSuper(c, fclass, fname))) return cp.getFieldrefType(index); } catch (NotFoundException e) {} return null; } static boolean isFieldInSuper(CtClass clazz, CtClass fclass, String fname) { if (!clazz.subclassOf(fclass)) return false; try { CtField f = clazz.getField(fname); return f.getDeclaringClass() == fclass; } catch (NotFoundException e) {} return false; } 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); 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; } } javassist-3.12.1.ga/src/main/javassist/convert/Transformer.java0000644000175000017500000000346511170755271024516 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.convert; import javassist.CannotCompileException; import javassist.CtClass; import javassist.bytecode.BadBytecode; import javassist.bytecode.CodeAttribute; import javassist.bytecode.CodeIterator; import javassist.bytecode.ConstPool; import javassist.bytecode.MethodInfo; import javassist.bytecode.Opcode; /** * 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 initialize(ConstPool cp, CtClass clazz, MethodInfo minfo) throws CannotCompileException { initialize(cp, minfo.getCodeAttribute()); } public void clean() {} public abstract int transform(CtClass clazz, int pos, CodeIterator it, ConstPool cp) throws CannotCompileException, BadBytecode; public int extraLocals() { return 0; } public int extraStack() { return 0; } } javassist-3.12.1.ga/src/main/javassist/convert/TransformAfter.java0000644000175000017500000000321111221067671025133 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.convert; import javassist.CtMethod; import javassist.NotFoundException; import javassist.bytecode.*; 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.setMark(p); iterator.insert(loadCode); pos = iterator.next(); p = iterator.getMark(); 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(); } } javassist-3.12.1.ga/src/main/javassist/convert/TransformWriteField.java0000644000175000017500000000554611221067671026145 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.convert; import javassist.CtClass; import javassist.CtField; import javassist.bytecode.*; 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. pos = 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. pos = 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; } } javassist-3.12.1.ga/src/main/javassist/convert/TransformNew.java0000644000175000017500000000671111275554016024636 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.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; StackMapTable smt = (StackMapTable)iterator.get().getAttribute(StackMapTable.tag); if (smt != null) smt.removeNew(pos); StackMap sm = (StackMap)iterator.get().getAttribute(StackMap.tag); if (sm != null) sm.removeNew(pos); } } 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)); } } javassist-3.12.1.ga/src/main/javassist/convert/TransformBefore.java0000644000175000017500000000674111221067671025307 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.convert; import javassist.CtClass; import javassist.CtMethod; import javassist.NotFoundException; import javassist.bytecode.*; 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); // override methodDescriptor = origMethod.getMethodInfo2().getDescriptor(); 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; } } javassist-3.12.1.ga/src/main/javassist/convert/TransformCall.java0000644000175000017500000001107510647635446024770 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.convert; import javassist.CtClass; import javassist.CtMethod; import javassist.ClassPool; import javassist.Modifier; import javassist.NotFoundException; import javassist.bytecode.*; public class TransformCall extends Transformer { protected String classname, methodname, methodDescriptor; protected String newClassname, newMethodname; protected boolean newMethodIsPrivate; /* cache */ protected int newIndex; protected ConstPool constPool; public TransformCall(Transformer next, CtMethod origMethod, CtMethod substMethod) { this(next, origMethod.getName(), substMethod); classname = origMethod.getDeclaringClass().getName(); } public TransformCall(Transformer next, String oldMethodName, CtMethod substMethod) { super(next); methodname = oldMethodName; methodDescriptor = substMethod.getMethodInfo2().getDescriptor(); classname = newClassname = substMethod.getDeclaringClass().getName(); newMethodname = substMethod.getName(); constPool = null; newMethodIsPrivate = Modifier.isPrivate(substMethod.getModifiers()); } 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. The class name in the operand * of these instructions might be a subclass of the target class specified * by classname. This method transforms the instruction * in that case unless the subclass overrides the target method. */ 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); String cname = cp.eqMember(methodname, methodDescriptor, index); if (cname != null && matchClass(cname, clazz.getClassPool())) { int ntinfo = cp.getMemberNameAndType(index); pos = match(c, pos, iterator, cp.getNameAndTypeDescriptor(ntinfo), cp); } } return pos; } private boolean matchClass(String name, ClassPool pool) { if (classname.equals(name)) return true; try { CtClass clazz = pool.get(name); CtClass declClazz = pool.get(classname); if (clazz.subtypeOf(declClazz)) try { CtMethod m = clazz.getMethod(methodname, methodDescriptor); return m.getDeclaringClass().getName().equals(classname); } catch (NotFoundException e) { // maybe the original method has been removed. return true; } } catch (NotFoundException e) { return false; } return false; } 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 { if (newMethodIsPrivate && c == INVOKEVIRTUAL) iterator.writeByte(INVOKESPECIAL, pos); newIndex = cp.addMethodrefInfo(ci, nt); } constPool = cp; } iterator.write16bit(newIndex, pos + 1); return pos; } } javassist-3.12.1.ga/src/main/javassist/convert/TransformNewClass.java0000644000175000017500000000540411061202362025605 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.convert; import javassist.bytecode.*; import javassist.CtClass; import javassist.CannotCompileException; final public class TransformNewClass extends Transformer { private int nested; private String classname, newClassName; private int newClassIndex, newMethodNTIndex, newMethodIndex; public TransformNewClass(Transformer next, String classname, String newClassName) { super(next); this.classname = classname; this.newClassName = newClassName; } public void initialize(ConstPool cp, CodeAttribute attr) { nested = 0; newClassIndex = newMethodNTIndex = newMethodIndex = 0; } /** * Modifies a sequence of * NEW classname * DUP * ... * INVOKESPECIAL classname:method */ 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"); if (newClassIndex == 0) newClassIndex = cp.addClassInfo(newClassName); iterator.write16bit(newClassIndex, pos + 1); ++nested; } } else if (c == INVOKESPECIAL) { index = iterator.u16bitAt(pos + 1); int typedesc = cp.isConstructor(classname, index); if (typedesc != 0 && nested > 0) { int nt = cp.getMethodrefNameAndType(index); if (newMethodNTIndex != nt) { newMethodNTIndex = nt; newMethodIndex = cp.addMethodrefInfo(newClassIndex, nt); } iterator.write16bit(newMethodIndex, pos + 1); --nested; } } return pos; } } javassist-3.12.1.ga/src/main/javassist/ClassPath.java0000644000175000017500000000464710630701321022404 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import java.io.InputStream; import java.net.URL; /** * ClassPath is an interface implemented by objects * representing a class search path. * ClassPool uses those objects for reading class files. * *

    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 ClassPool#removeClassPath(ClassPath) */ public interface ClassPath { /** * Opens a class file. * This method may be called just to examine whether the class file * exists as well as to read the contents of the file. * *

    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 will be terminated. * *

    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 * @see javassist.Translator */ InputStream openClassfile(String classname) throws NotFoundException; /** * Returns the uniform resource locator (URL) of the class file * with the specified name. * * @param classname a fully-qualified class name. * @return null if the specified class file could not be found. */ URL find(String classname); /** * This method is invoked when the ClassPath object is * detached from the search path. It will be an empty method in most of * classes. */ void close(); } javassist-3.12.1.ga/src/main/javassist/ClassPoolTail.java0000644000175000017500000003052711360650440023235 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import java.io.*; import java.util.jar.*; import java.net.MalformedURLException; import java.net.URL; import java.util.Hashtable; final class ClassPathList { ClassPathList next; ClassPath path; ClassPathList(ClassPath p, ClassPathList n) { next = n; path = p; } } 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 URL find(String classname) { char sep = File.separatorChar; String filename = directory + sep + classname.replace('.', sep) + ".class"; File f = new File(filename); if (f.exists()) try { return f.getCanonicalFile().toURI().toURL(); } catch (MalformedURLException e) {} catch (IOException e) {} return null; } public void close() {} public String toString() { return directory; } } final class JarDirClassPath implements ClassPath { JarClassPath[] jars; JarDirClassPath(String dirName) throws NotFoundException { File[] files = new File(dirName).listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { name = name.toLowerCase(); return name.endsWith(".jar") || name.endsWith(".zip"); } }); if (files != null) { jars = new JarClassPath[files.length]; for (int i = 0; i < files.length; i++) jars[i] = new JarClassPath(files[i].getPath()); } } public InputStream openClassfile(String classname) throws NotFoundException { if (jars != null) for (int i = 0; i < jars.length; i++) { InputStream is = jars[i].openClassfile(classname); if (is != null) return is; } return null; // not found } public URL find(String classname) { if (jars != null) for (int i = 0; i < jars.length; i++) { URL url = jars[i].find(classname); if (url != null) return url; } return null; // not found } public void close() { if (jars != null) for (int i = 0; i < jars.length; i++) jars[i].close(); } } final class JarClassPath implements ClassPath { JarFile jarfile; String jarfileURL; JarClassPath(String pathname) throws NotFoundException { try { jarfile = new JarFile(pathname); jarfileURL = new File(pathname).getCanonicalFile() .toURI().toURL().toString(); return; } catch (IOException e) {} throw new NotFoundException(pathname); } public InputStream openClassfile(String classname) throws NotFoundException { try { String jarname = classname.replace('.', '/') + ".class"; JarEntry je = jarfile.getJarEntry(jarname); if (je != null) return jarfile.getInputStream(je); else return null; // not found } catch (IOException e) {} throw new NotFoundException("broken jar file?: " + jarfile.getName()); } public URL find(String classname) { String jarname = classname.replace('.', '/') + ".class"; JarEntry je = jarfile.getJarEntry(jarname); if (je != null) try { return new URL("jar:" + jarfileURL + "!/" + jarname); } catch (MalformedURLException e) {} return null; // not found } public void close() { try { jarfile.close(); jarfile = null; } catch (IOException e) {} } public String toString() { return jarfile == null ? "" : jarfile.toString(); } } final class ClassPoolTail { protected ClassPathList pathList; private Hashtable packages; // should be synchronized. public ClassPoolTail() { pathList = null; packages = new Hashtable(); } 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 synchronized ClassPath insertClassPath(ClassPath cp) { pathList = new ClassPathList(cp, pathList); return cp; } public synchronized ClassPath 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; } return cp; } public synchronized void removeClassPath(ClassPath cp) { ClassPathList list = pathList; if (list != null) if (list.path == cp) pathList = list.next; else { while (list.next != null) if (list.next.path == cp) list.next = list.next.next; else list = list.next; } cp.close(); } public ClassPath appendSystemPath() { return appendClassPath(new ClassClassPath()); } public ClassPath insertClassPath(String pathname) throws NotFoundException { return insertClassPath(makePathObject(pathname)); } public ClassPath appendClassPath(String pathname) throws NotFoundException { return appendClassPath(makePathObject(pathname)); } private static ClassPath makePathObject(String pathname) throws NotFoundException { String lower = pathname.toLowerCase(); if (lower.endsWith(".jar") || lower.endsWith(".zip")) return new JarClassPath(pathname); int len = pathname.length(); if (len > 2 && pathname.charAt(len - 1) == '*' && (pathname.charAt(len - 2) == '/' || pathname.charAt(len - 2) == File.separatorChar)) { String dir = pathname.substring(0, len - 2); return new JarDirClassPath(dir); } return new DirClassPath(pathname); } /** * You can record "System" so that java.lang.System can be quickly * found although "System" is not a package name. */ public void recordInvalidClassName(String name) { packages.put(name, name); } /** * This method does not close the output stream. */ void writeClassfile(String classname, OutputStream out) throws NotFoundException, IOException, CannotCompileException { InputStream fin = openClassfile(classname); if (fin == null) throw new NotFoundException(classname); try { copyStream(fin, out); } finally { fin.close(); } } /* -- faster version -- void checkClassName(String classname) throws NotFoundException { if (find(classname) == null) throw new NotFoundException(classname); } -- slower version -- void checkClassName(String classname) throws NotFoundException { InputStream fin = openClassfile(classname); try { fin.close(); } catch (IOException e) {} } */ /** * Opens the class file for the class specified by * classname. * * @param classname a fully-qualified class name * @return null if the file has not been found. * @throws NotFoundException if any error is reported by ClassPath. */ InputStream openClassfile(String classname) throws NotFoundException { if (packages.get(classname) != null) return null; // not found 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 return null; // not found } /** * Searches the class path to obtain the URL of the class file * specified by classname. It is also used to determine whether * the class file exists. * * @param classname a fully-qualified class name. * @return null if the class file could not be found. */ public URL find(String classname) { if (packages.get(classname) != null) return null; ClassPathList list = pathList; URL url = null; while (list != null) { url = list.path.find(classname); if (url == null) list = list.next; else return url; } return null; } /** * Reads from 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"); } /** * Reads from an input stream and write to an output stream * until it reaches the end. This method does not close the * streams. */ public static void copyStream(InputStream fin, OutputStream fout) throws IOException { int bufsize = 4096; for (int i = 0; i < 8; ++i) { byte[] buf = new byte[bufsize]; int size = 0; int len = 0; do { len = fin.read(buf, size, bufsize - size); if (len >= 0) size += len; else { fout.write(buf, 0, size); return; } } while (size < bufsize); fout.write(buf); bufsize *= 2; } throw new IOException("too much data"); } } javassist-3.12.1.ga/src/main/javassist/CtMethod.java0000644000175000017500000003344311152365552022241 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import javassist.bytecode.*; /** * An instance of CtMethod represents a method. * *

    See the super class CtBehavior since * a number of useful methods are in CtBehavior. * A number of useful factory methods are in CtNewMethod. * * @see CtClass#getDeclaredMethods() * @see CtNewMethod */ public final class CtMethod extends CtBehavior { protected String cachedStringRep; /** * @see #make(MethodInfo minfo, CtClass declaring) */ CtMethod(MethodInfo minfo, CtClass declaring) { super(declaring, minfo); cachedStringRep = null; } /** * Creates a public abstract method. The created method must be * added to a class with CtClass.addMethod(). * * @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 CtMethod object. * The created method must be * added to a class with CtClass.addMethod(). * *

    All occurrences of class names in the created method * are replaced with names specified by * map if map is not null. * *

    For example, suppose that a method at() is as * follows: * *

      public X at(int i) {
           *     return (X)super.elementAt(i);
           * }
    * *

    (X is a class name.) If map substitutes * String for X, then the created method is: * *

      public String at(int i) {
           *     return (String)super.elementAt(i);
           * }
    * *

    By default, all the occurrences of the names of the class * declaring at() 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 map is null or not. * To prevent this replacement, call ClassMap.fix() * or put() to explicitly specify replacement. * *

    Note: if the .class notation (for example, * String.class) 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 Class.forName() 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 null. * * @see CtClass#addMethod(CtMethod) * @see ClassMap#fix(String) */ public CtMethod(CtMethod src, CtClass declaring, ClassMap map) throws CannotCompileException { this(null, declaring); copy(src, false, map); } /** * Compiles the given source code and creates a method. * This method simply delegates to make() in * CtNewMethod. See it for more details. * CtNewMethod has a number of useful factory methods. * * @param src the source text. * @param declaring the class to which the created method is added. * @see CtNewMethod#make(String, CtClass) */ public static CtMethod make(String src, CtClass declaring) throws CannotCompileException { return CtNewMethod.make(src, declaring); } /** * Creates a method from a MethodInfo object. * * @param declaring the class declaring the method. * @throws CannotCompileException if the the MethodInfo * object and the declaring class have different * ConstPool objects * @since 3.6 */ public static CtMethod make(MethodInfo minfo, CtClass declaring) throws CannotCompileException { if (declaring.getClassFile2().getConstPool() != minfo.getConstPool()) throw new CannotCompileException("bad declaring class"); return new CtMethod(minfo, declaring); } /** * 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() { return getStringRep().hashCode(); } /** * This method is invoked when setName() or replaceClassName() * in CtClass is called. */ void nameReplaced() { cachedStringRep = null; } /* This method is also called by CtClassType.getMethods0(). */ final String getStringRep() { if (cachedStringRep == null) cachedStringRep = methodInfo.getName() + Descriptor.getParamDescriptor(methodInfo.getDescriptor()); return cachedStringRep; } /** * Indicates whether obj has the same name and the * same signature as this method. */ public boolean equals(Object obj) { return obj != null && obj instanceof CtMethod && ((CtMethod)obj).getStringRep().equals(getStringRep()); } /** * Returns the method name followed by parameter types * such as javassist.CtMethod.setBody(String). * * @since 3.5 */ public String getLongName() { return getDeclaringClass().getName() + "." + getName() + Descriptor.toString(getSignature()); } /** * 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); } /** * Obtains the type of the returned value. */ public CtClass getReturnType() throws NotFoundException { return getReturnType0(); } /** * Returns true if the method body is empty, that is, {}. * It also returns true if the method is an abstract method. */ public boolean isEmpty() { CodeAttribute ca = getMethodInfo2().getCodeAttribute(); if (ca == null) // abstract or native return (getModifiers() & Modifier.ABSTRACT) != 0; CodeIterator it = ca.iterator(); try { return it.hasNext() && it.byteAt(it.next()) == Opcode.RETURN && !it.hasNext(); } catch (BadBytecode e) {} return false; } /** * Copies a method body from another method. * If this method is abstract, the abstract modifier is removed * after the method body is copied. * *

    All occurrences of the class names in the copied method body * are replaced with the names specified by * map if map is not null. * * @param src the method that the body is copied from. * @param map the hashtable associating original class names * with substituted names. * It can be null. */ 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 null). * * @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); // rebuilding a stack map table is not needed. } // inner classes /** * Instances of this class represent a constant parameter. * They are used to specify the parameter given to the methods * created by CtNewMethod.wrapped(). * * @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 a long integer constant. * * @param i the constant value. */ public static ConstParameter integer(long i) { return new LongConstParameter(i); } /** * Makes an String constant. * * @param s the constant value. */ public static ConstParameter string(String s) { return new StringConstParameter(s); } ConstParameter() {} /** * @return the size of the stack consumption. */ int compile(Bytecode code) throws CannotCompileException { return 0; } String descriptor() { return defaultDescriptor(); } /** * @see CtNewWrappedMethod */ static String defaultDescriptor() { return "([Ljava/lang/Object;)Ljava/lang/Object;"; } /** * Returns the descriptor for constructors. * * @see CtNewWrappedConstructor */ String constDescriptor() { return defaultConstDescriptor(); } /** * Returns the default 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 LongConstParameter extends ConstParameter { long param; LongConstParameter(long l) { param = l; } int compile(Bytecode code) throws CannotCompileException { code.addLconst(param); return 2; } String descriptor() { return "([Ljava/lang/Object;J)Ljava/lang/Object;"; } String constDescriptor() { return "([Ljava/lang/Object;J)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"; } } } javassist-3.12.1.ga/src/main/javassist/CtNewClass.java0000644000175000017500000000753611227562743022550 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; 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; String superName; if (isInterface || superclass == null) superName = null; else superName = superclass.getName(); classfile = new ClassFile(isInterface, name, superName); if (isInterface && superclass != null) classfile.setInterfaces(new String[] { superclass.getName() }); setModifiers(Modifier.setPublic(getModifiers())); hasConstructor = isInterface; } protected void extendToString(StringBuffer buffer) { if (hasConstructor) buffer.append("hasConstructor "); super.extendToString(buffer); } public void addConstructor(CtConstructor c) throws CannotCompileException { hasConstructor = true; super.addConstructor(c); } public void toBytecode(DataOutputStream out) throws CannotCompileException, IOException { if (!hasConstructor) try { inheritAllConstructors(); hasConstructor = true; } catch (NotFoundException e) { throw new CannotCompileException(e); } super.toBytecode(out); } /** * Adds constructors inhrited from the super class. * *

    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]; int mod = c.getModifiers(); if (isInheritable(mod, superclazz)) { CtConstructor cons = CtNewConstructor.make(c.getParameterTypes(), c.getExceptionTypes(), this); cons.setModifiers(mod & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE)); addConstructor(cons); ++n; } } if (n < 1) throw new CannotCompileException( "no inheritable constructor in " + superclazz.getName()); } private boolean isInheritable(int mod, CtClass superclazz) { if (Modifier.isPrivate(mod)) return false; if (Modifier.isPackage(mod)) { String pname = getPackageName(); String pname2 = superclazz.getPackageName(); if (pname == null) return pname2 == null; else return pname.equals(pname2); } return true; } } javassist-3.12.1.ga/src/main/javassist/compiler/0000755000175000017500000000000011637463307021477 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/compiler/JvstTypeChecker.java0000644000175000017500000002175710630701321025412 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import javassist.*; import javassist.compiler.ast.*; /* Type checker accepting extended Java syntax for Javassist. */ public class JvstTypeChecker extends TypeChecker { private JvstCodeGen codeGen; public JvstTypeChecker(CtClass cc, ClassPool cp, JvstCodeGen gen) { super(cc, cp); codeGen = gen; } /* If the type of the expression compiled last is void, * add ACONST_NULL and change exprType, arrayDim, className. */ public void addNullIfVoid() { if (exprType == VOID) { 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(codeGen.paramArrayName)) { exprType = CLASS; arrayDim = 1; className = jvmJavaLangObject; } else if (name.equals(JvstCodeGen.sigName)) { exprType = CLASS; arrayDim = 1; className = "java/lang/Class"; } else if (name.equals(JvstCodeGen.dollarTypeName) || name.equals(JvstCodeGen.clazzName)) { exprType = CLASS; arrayDim = 0; className = "java/lang/Class"; } else super.atMember(mem); } protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right) throws CompileError { if (left instanceof Member && ((Member)left).get().equals(codeGen.paramArrayName)) { right.accept(this); CtClass[] params = codeGen.paramTypeList; if (params == null) return; int n = params.length; for (int i = 0; i < n; ++i) compileUnwrapValue(params[i]); } else super.atFieldAssign(expr, op, left, right); } 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(codeGen.returnCastName)) { atCastToRtype(expr); return; } else if (typename.equals(JvstCodeGen.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 { CtClass returnType = codeGen.returnType; expr.getOprand().accept(this); if (exprType == VOID || CodeGen.isRefType(exprType) || arrayDim > 0) compileUnwrapValue(returnType); else if (returnType instanceof CtPrimitiveType) { CtPrimitiveType pt = (CtPrimitiveType)returnType; int destType = MemberResolver.descToType(pt.getDescriptor()); exprType = destType; arrayDim = 0; className = null; } } protected void atCastToWrapper(CastExpr expr) throws CompileError { expr.getOprand().accept(this); if (CodeGen.isRefType(exprType) || arrayDim > 0) return; // Object type. do nothing. CtClass clazz = resolver.lookupClass(exprType, arrayDim, className); if (clazz instanceof CtPrimitiveType) { exprType = CLASS; arrayDim = 0; className = jvmJavaLangObject; } } /* Delegates to a ProcHandler object if the method call is * $proceed(). It may process $cflow(). */ public void atCallExpr(CallExpr expr) throws CompileError { ASTree method = expr.oprand1(); if (method instanceof Member) { String name = ((Member)method).get(); if (codeGen.procHandler != null && name.equals(codeGen.proceedName)) { codeGen.procHandler.setReturnType(this, (ASTList)expr.oprand2()); return; } else if (name.equals(JvstCodeGen.cflowName)) { atCflow((ASTList)expr.oprand2()); return; } } super.atCallExpr(expr); } /* To support $cflow(). */ protected void atCflow(ASTList cname) throws CompileError { exprType = INT; arrayDim = 0; className = null; } /* 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 (codeGen.paramTypeList != null && args != null && args.tail() == null) { ASTree left = args.head(); return (left instanceof Member && ((Member)left).get().equals(codeGen.paramListName)); } else return false; } public int getMethodArgsLength(ASTList args) { String pname = codeGen.paramListName; int n = 0; while (args != null) { ASTree a = args.head(); if (a instanceof Member && ((Member)a).get().equals(pname)) { if (codeGen.paramTypeList != null) n += codeGen.paramTypeList.length; } else ++n; args = args.tail(); } return n; } public void atMethodArgs(ASTList args, int[] types, int[] dims, String[] cnames) throws CompileError { CtClass[] params = codeGen.paramTypeList; String pname = codeGen.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; for (int k = 0; k < n; ++k) { CtClass p = params[k]; 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(); } } /* called by Javac#recordSpecialProceed(). */ void compileInvokeSpecial(ASTree target, String classname, String methodname, String descriptor, ASTList args) throws CompileError { target.accept(this); int nargs = getMethodArgsLength(args); atMethodArgs(args, new int[nargs], new int[nargs], new String[nargs]); setReturnType(descriptor); addNullIfVoid(); } protected void compileUnwrapValue(CtClass type) throws CompileError { if (type == CtClass.voidType) addNullIfVoid(); else 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 = MemberResolver.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 = MemberResolver.javaToJvmName(type.getName()); } } } javassist-3.12.1.ga/src/main/javassist/compiler/Javac.java0000644000175000017500000005125210630701321023352 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import javassist.CtClass; import javassist.CtPrimitiveType; import javassist.CtMember; import javassist.CtField; import javassist.CtBehavior; import javassist.CtMethod; import javassist.CtConstructor; import javassist.CannotCompileException; import javassist.Modifier; import javassist.bytecode.Bytecode; import javassist.bytecode.CodeAttribute; import javassist.bytecode.LocalVariableAttribute; import javassist.bytecode.BadBytecode; import javassist.bytecode.Opcode; 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 Bytecode object * specified by b. * * @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. * *

    In a method or constructor body, $0, $1, ... and $_ * are not available. * * @return a CtMethod, CtConstructor, * or CtField 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 { CtBehavior cb = compileMethod(p, (MethodDecl)mem); CtClass decl = cb.getDeclaringClass(); cb.getMethodInfo2() .rebuildStackMapIf6(decl.getClassPool(), decl.getClassFile2()); return cb; } } catch (BadBytecode bb) { throw new CompileError(bb.getMessage()); } 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.resolver.lookupClass(d), d.getVariable().get(), gen.getThisClass()); f.setModifiers(MemberResolver.getModifiers(fd.getModifiers())); if (fd.getInit() != null) f.setInit(fd.getInit()); return f; } private CtBehavior compileMethod(Parser p, MethodDecl md) throws CompileError { int mod = MemberResolver.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.resolver.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. * * @src a single statement or a block. * If null, this method produces a body returning zero or null. */ 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; if (src == null) makeDefaultBody(bytecode, rtype); else { Parser p = new Parser(new Lex(src)); SymbolTable stb = new SymbolTable(stable); Stmnt s = p.parseStatement(stb); if (p.hasMore()) throw new CompileError( "the method/constructor body must be surrounded by {}"); boolean callSuper = false; if (method instanceof CtConstructor) callSuper = !((CtConstructor)method).isClassInitializer(); gen.atMethodBody(s, callSuper, isVoid); } return bytecode; } catch (NotFoundException e) { throw new CompileError(e.toString()); } } private static void makeDefaultBody(Bytecode b, CtClass type) { int op; int value; if (type instanceof CtPrimitiveType) { CtPrimitiveType pt = (CtPrimitiveType)type; op = pt.getReturnOp(); if (op == Opcode.DRETURN) value = Opcode.DCONST_0; else if (op == Opcode.FRETURN) value = Opcode.FCONST_0; else if (op == Opcode.LRETURN) value = Opcode.LCONST_0; else if (op == Opcode.RETURN) value = Opcode.NOP; else value = Opcode.ICONST_0; } else { op = Opcode.ARETURN; value = Opcode.ACONST_NULL; } if (value != Opcode.NOP) b.addOpcode(value); b.addOpcode(op); } /** * Records local variables available at the specified program counter. * If the LocalVariableAttribute is not available, this method does not * record any local variable. It only returns false. * * @param pc program counter (>= 0) * @return false if the CodeAttribute does not include a * LocalVariableAttribute. */ public boolean recordLocalVariables(CodeAttribute ca, int pc) throws CompileError { LocalVariableAttribute va = (LocalVariableAttribute) ca.getAttribute(LocalVariableAttribute.tag); if (va == null) return false; int n = va.tableLength(); for (int i = 0; i < n; ++i) { int start = va.startPc(i); int len = va.codeLength(i); if (start <= pc && pc < start + len) gen.recordVariable(va.descriptor(i), va.variableName(i), va.index(i), stable); } return true; } /** * Records parameter names if the LocalVariableAttribute is available. * It returns false unless the LocalVariableAttribute is available. * * @param numOfLocalVars the number of local variables used * for storing the parameters. * @return false if the CodeAttribute does not include a * LocalVariableAttribute. */ public boolean recordParamNames(CodeAttribute ca, int numOfLocalVars) throws CompileError { LocalVariableAttribute va = (LocalVariableAttribute) ca.getAttribute(LocalVariableAttribute.tag); if (va == null) return false; int n = va.tableLength(); for (int i = 0; i < n; ++i) { int index = va.index(i); if (index < numOfLocalVars) gen.recordVariable(va.descriptor(i), va.variableName(i), index, stable); } return true; } /** * 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. * *

    This must be called before calling compileStmnt() and * compileExpr(). The correct value of * isStatic must be recorded before compilation. * maxLocals is updated to include $0,... */ public int recordParams(CtClass[] params, boolean isStatic) throws CompileError { return 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). * $class is also made available. * *

    This must be called before calling compileStmnt() and * compileExpr(). The correct value of * isStatic must be recorded before compilation. * maxLocals is updated to include $0,... * * @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). * It is used as the name of the type represented * by $class. * @param isStatic true if the method in which the compiled bytecode * is embedded is static. */ public int recordParams(String target, CtClass[] params, boolean use0, int varNo, boolean isStatic) throws CompileError { return gen.recordParams(params, isStatic, "$", "$args", "$$", use0, varNo, target, stable); } /** * Sets maxLocals to max. * This method tells the compiler the local variables that have been * allocated for the rest of the code. When the compiler needs * new local variables, the local variables at the index max, * max + 1, ... are assigned. * *

    This method is indirectly called by recordParams. */ public void setMaxLocals(int max) { gen.setMaxLocals(max); } /** * Prepares to use cast $r, $w, $_, and $type. * $type is made to represent the specified return type. * It also enables to write a return statement with a return value * for void method. * *

    If the return type is void, ($r) does nothing. * The type of $_ is java.lang.Object. * * @param type the return type. * @param useResultVar true if $_ is used. * @return -1 or the variable index assigned to $_. * @see #recordType(CtClass) */ 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. * * @param t the type represented by $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 = CallExpr.makeCall(expr, args); gen.compileExpr(expr); gen.addNullIfVoid(); } public void setReturnType(JvstTypeChecker check, ASTList args) throws CompileError { ASTree expr = new Member(m); if (texpr != null) expr = Expr.make('.', texpr, expr); expr = CallExpr.makeCall(expr, args); expr.accept(check); check.addNullIfVoid(); } }; gen.setProceedHandler(h, proceedName); } /** * Prepares to use $proceed() representing a static method. * If the return type of $proceed() is void, null is pushed on the * stack. * * @param targetClass the fully-qualified dot-separated name * of the class declaring the method. * @param method the method name. */ public void recordStaticProceed(String targetClass, String method) throws CompileError { final String c = targetClass; final String m = method; ProceedHandler h = new ProceedHandler() { public void doit(JvstCodeGen gen, Bytecode b, ASTList args) throws CompileError { Expr expr = Expr.make(TokenId.MEMBER, new Symbol(c), new Member(m)); expr = CallExpr.makeCall(expr, args); gen.compileExpr(expr); gen.addNullIfVoid(); } public void setReturnType(JvstTypeChecker check, ASTList args) throws CompileError { Expr expr = Expr.make(TokenId.MEMBER, new Symbol(c), new Member(m)); expr = CallExpr.makeCall(expr, args); expr.accept(check); check.addNullIfVoid(); } }; gen.setProceedHandler(h, proceedName); } /** * Prepares to use $proceed() representing a private/super's method. * If the return type of $proceed() is void, null is pushed on the * stack. This method is for methods invoked by INVOKESPECIAL. * * @param target an expression specifying the target object. * if null, "this" is the target. * @param classname the class name declaring the method. * @param methodname the method name. * @param descriptor the method descriptor. */ public void recordSpecialProceed(String target, String classname, String methodname, String descriptor) throws CompileError { Parser p = new Parser(new Lex(target)); final ASTree texpr = p.parseExpression(stable); final String cname = classname; final String method = methodname; final String desc = descriptor; ProceedHandler h = new ProceedHandler() { public void doit(JvstCodeGen gen, Bytecode b, ASTList args) throws CompileError { gen.compileInvokeSpecial(texpr, cname, method, desc, args); } public void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError { c.compileInvokeSpecial(texpr, cname, method, desc, args); } }; gen.setProceedHandler(h, proceedName); } /** * Prepares to use $proceed(). */ public void recordProceed(ProceedHandler h) { gen.setProceedHandler(h, proceedName); } /** * Compiles a statement (or a block). * recordParams() must be called before invoking * this method. * *

    Local variables that are not declared * in the compiled source text might not be 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. recordParams() must be * called before invoking this method. * *

    Local variables are not accessible * within the compiled source text. Fields and method parameters * ($0, $1, ..) are available if recordParams() * have been invoked. */ public void compileExpr(String src) throws CompileError { ASTree e = parseExpr(src, stable); compileExpr(e); } /** * Parsers an expression. */ public static ASTree parseExpr(String src, SymbolTable st) throws CompileError { Parser p = new Parser(new Lex(src)); return p.parseExpression(st); } /** * Compiles an exression. recordParams() must be * called before invoking this method. * *

    Local variables are not accessible * within the compiled source text. Fields and method parameters * ($0, $1, ..) are available if recordParams() * have been invoked. */ public void compileExpr(ASTree e) throws CompileError { if (e != null) gen.compileExpr(e); } } javassist-3.12.1.ga/src/main/javassist/compiler/MemberCodeGen.java0000644000175000017500000011117711165302214024767 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import javassist.*; import javassist.bytecode.*; import javassist.compiler.ast.*; import java.util.ArrayList; /* Code generator methods depending on javassist.* classes. */ public class MemberCodeGen extends CodeGen { protected MemberResolver resolver; protected CtClass thisClass; protected MethodInfo thisMethod; protected boolean resultStatic; public MemberCodeGen(Bytecode b, CtClass cc, ClassPool cp) { super(b); resolver = new MemberResolver(cp); thisClass = cc; thisMethod = null; } /** * Returns the major version of the class file * targeted by this compilation. */ public int getMajorVersion() { ClassFile cf = thisClass.getClassFile2(); if (cf == null) return ClassFile.MAJOR_VERSION; // JDK 1.3 else return cf.getMajorVersion(); } /** * Records the currently compiled method. */ public void setThisMethod(CtMethod m) { thisMethod = m.getMethodInfo2(); if (typeChecker != null) typeChecker.setThisMethod(thisMethod); } public CtClass getThisClass() { return thisClass; } /** * Returns the JVM-internal representation of this class name. */ protected String getThisName() { return MemberResolver.javaToJvmName(thisClass.getName()); } /** * Returns the JVM-internal representation of this super class name. */ protected String getSuperName() throws CompileError { return MemberResolver.javaToJvmName( MemberResolver.getSuperclass(thisClass).getName()); } protected void insertDefaultSuperCall() throws CompileError { bytecode.addAload(0); bytecode.addInvokespecial(MemberResolver.getSuperclass(thisClass), "", "()V"); } static class JsrHook extends ReturnHook { ArrayList jsrList; CodeGen cgen; int var; JsrHook(CodeGen gen) { super(gen); jsrList = new ArrayList(); cgen = gen; var = -1; } private int getVar(int size) { if (var < 0) { var = cgen.getMaxLocals(); cgen.incMaxLocals(size); } return var; } private void jsrJmp(Bytecode b) { b.addOpcode(Opcode.GOTO); jsrList.add(new int[] {b.currentPc(), var}); b.addIndex(0); } protected boolean doit(Bytecode b, int opcode) { switch (opcode) { case Opcode.RETURN : jsrJmp(b); break; case ARETURN : b.addAstore(getVar(1)); jsrJmp(b); b.addAload(var); break; case IRETURN : b.addIstore(getVar(1)); jsrJmp(b); b.addIload(var); break; case LRETURN : b.addLstore(getVar(2)); jsrJmp(b); b.addLload(var); break; case DRETURN : b.addDstore(getVar(2)); jsrJmp(b); b.addDload(var); break; case FRETURN : b.addFstore(getVar(1)); jsrJmp(b); b.addFload(var); break; default : throw new RuntimeException("fatal"); } return false; } } static class JsrHook2 extends ReturnHook { int var; int target; JsrHook2(CodeGen gen, int[] retTarget) { super(gen); target = retTarget[0]; var = retTarget[1]; } protected boolean doit(Bytecode b, int opcode) { switch (opcode) { case Opcode.RETURN : break; case ARETURN : b.addAstore(var); break; case IRETURN : b.addIstore(var); break; case LRETURN : b.addLstore(var); break; case DRETURN : b.addDstore(var); break; case FRETURN : b.addFstore(var); break; default : throw new RuntimeException("fatal"); } b.addOpcode(Opcode.GOTO); b.addIndex(target - b.currentPc() + 3); return true; } } protected void atTryStmnt(Stmnt st) throws CompileError { Bytecode bc = bytecode; Stmnt body = (Stmnt)st.getLeft(); if (body == null) return; ASTList catchList = (ASTList)st.getRight().getLeft(); Stmnt finallyBlock = (Stmnt)st.getRight().getRight().getLeft(); ArrayList gotoList = new ArrayList(); JsrHook jsrHook = null; if (finallyBlock != null) jsrHook = new JsrHook(this); int start = bc.currentPc(); body.accept(this); int end = bc.currentPc(); if (start == end) throw new CompileError("empty try block"); boolean tryNotReturn = !hasReturned; if (tryNotReturn) { bc.addOpcode(Opcode.GOTO); gotoList.add(new Integer(bc.currentPc())); bc.addIndex(0); // correct later } int var = getMaxLocals(); incMaxLocals(1); while (catchList != null) { // catch clause Pair p = (Pair)catchList.head(); catchList = catchList.tail(); Declarator decl = (Declarator)p.getLeft(); Stmnt block = (Stmnt)p.getRight(); decl.setLocalVar(var); CtClass type = resolver.lookupClassByJvmName(decl.getClassName()); decl.setClassName(MemberResolver.javaToJvmName(type.getName())); bc.addExceptionHandler(start, end, bc.currentPc(), type); bc.growStack(1); bc.addAstore(var); hasReturned = false; if (block != null) block.accept(this); if (!hasReturned) { bc.addOpcode(Opcode.GOTO); gotoList.add(new Integer(bc.currentPc())); bc.addIndex(0); // correct later tryNotReturn = true; } } if (finallyBlock != null) { jsrHook.remove(this); // catch (any) clause int pcAnyCatch = bc.currentPc(); bc.addExceptionHandler(start, pcAnyCatch, pcAnyCatch, 0); bc.growStack(1); bc.addAstore(var); hasReturned = false; finallyBlock.accept(this); if (!hasReturned) { bc.addAload(var); bc.addOpcode(ATHROW); } addFinally(jsrHook.jsrList, finallyBlock); } int pcEnd = bc.currentPc(); patchGoto(gotoList, pcEnd); hasReturned = !tryNotReturn; if (finallyBlock != null) { if (tryNotReturn) finallyBlock.accept(this); } } /** * Adds a finally clause for earch return statement. */ private void addFinally(ArrayList returnList, Stmnt finallyBlock) throws CompileError { Bytecode bc = bytecode; int n = returnList.size(); for (int i = 0; i < n; ++i) { final int[] ret = (int[])returnList.get(i); int pc = ret[0]; bc.write16bit(pc, bc.currentPc() - pc + 1); ReturnHook hook = new JsrHook2(this, ret); finallyBlock.accept(this); hook.remove(this); if (!hasReturned) { bc.addOpcode(Opcode.GOTO); bc.addIndex(pc + 3 - bc.currentPc()); } } } public void atNewExpr(NewExpr expr) throws CompileError { if (expr.isArray()) atNewArrayExpr(expr); else { CtClass clazz = resolver.lookupClassByName(expr.getClassName()); String cname = clazz.getName(); ASTList args = expr.getArguments(); bytecode.addNew(cname); bytecode.addOpcode(DUP); atMethodCallCore(clazz, MethodInfo.nameInit, args, false, true, -1, null); exprType = CLASS; arrayDim = 0; className = MemberResolver.javaToJvmName(cname); } } public void atNewArrayExpr(NewExpr expr) throws CompileError { int type = expr.getArrayType(); ASTList size = expr.getArraySize(); ASTList classname = expr.getClassName(); ArrayInit init = expr.getInitializer(); if (size.length() > 1) { if (init != null) throw new CompileError( "sorry, multi-dimensional array initializer " + "for new is not supported"); atMultiNewArray(type, classname, size); return; } ASTree sizeExpr = size.head(); atNewArrayExpr2(type, sizeExpr, Declarator.astToClassName(classname, '/'), init); } private void atNewArrayExpr2(int type, ASTree sizeExpr, String jvmClassname, ArrayInit init) throws CompileError { if (init == null) if (sizeExpr == null) throw new CompileError("no array size"); else sizeExpr.accept(this); else if (sizeExpr == null) { int s = init.length(); bytecode.addIconst(s); } else throw new CompileError("unnecessary array size specified for new"); String elementClass; if (type == CLASS) { elementClass = resolveClassName(jvmClassname); bytecode.addAnewarray(MemberResolver.jvmToJavaName(elementClass)); } else { elementClass = 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); } if (init != null) { int s = init.length(); ASTList list = init; for (int i = 0; i < s; i++) { bytecode.addOpcode(DUP); bytecode.addIconst(i); list.head().accept(this); if (!isRefType(type)) atNumCastExpr(exprType, type); bytecode.addOpcode(getArrayWriteOp(type, 0)); list = list.tail(); } } exprType = type; arrayDim = 1; className = elementClass; } private static void badNewExpr() throws CompileError { throw new CompileError("bad new expression"); } protected void atArrayVariableAssign(ArrayInit init, int varType, int varArray, String varClass) throws CompileError { atNewArrayExpr2(varType, null, varClass, init); } public void atArrayInit(ArrayInit init) throws CompileError { throw new CompileError("array initializer is not supported"); } 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); } public void atCallExpr(CallExpr expr) throws CompileError { String mname = null; CtClass targetClass = null; ASTree method = expr.oprand1(); ASTList args = (ASTList)expr.oprand2(); boolean isStatic = false; boolean isSpecial = false; int aload0pos = -1; MemberResolver.Method cached = expr.getMethod(); if (method instanceof Member) { mname = ((Member)method).get(); targetClass = thisClass; if (inStaticMethod || (cached != null && cached.isStatic())) isStatic = true; // should be static else { aload0pos = bytecode.currentPc(); bytecode.addAload(0); // this } } else if (method instanceof Keyword) { // constructor isSpecial = true; mname = MethodInfo.nameInit; // targetClass = thisClass; if (inStaticMethod) throw new CompileError("a constructor cannot be static"); else bytecode.addAload(0); // this if (((Keyword)method).get() == SUPER) targetClass = MemberResolver.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 = resolver.lookupClass(((Symbol)e.oprand1()).get(), false); 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 resolver.recordPackage(className); isStatic = true; } if (arrayDim > 0) targetClass = resolver.lookupClass(javaLangObject, true); else if (exprType == CLASS /* && arrayDim == 0 */) targetClass = resolver.lookupClassByJvmName(className); else badMethod(); } else badMethod(); } else fatal(); atMethodCallCore(targetClass, mname, args, isStatic, isSpecial, aload0pos, cached); } private static void badMethod() throws CompileError { throw new CompileError("bad method"); } /* * atMethodCallCore() is also called by doit() in NewExpr.ProceedForNew * * @param targetClass the class at which method lookup starts. * @param found not null if the method look has been already done. */ public void atMethodCallCore(CtClass targetClass, String mname, ASTList args, boolean isStatic, boolean isSpecial, int aload0pos, MemberResolver.Method found) throws CompileError { int nargs = getMethodArgsLength(args); int[] types = new int[nargs]; int[] dims = new int[nargs]; String[] cnames = new String[nargs]; if (!isStatic && found != null && found.isStatic()) { bytecode.addOpcode(POP); isStatic = true; } int stack = bytecode.getStackDepth(); // generate code for evaluating arguments. atMethodArgs(args, types, dims, cnames); // used by invokeinterface int count = bytecode.getStackDepth() - stack + 1; if (found == null) found = resolver.lookupMethod(targetClass, thisClass, thisMethod, mname, types, dims, cnames); 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); } atMethodCallCore2(targetClass, mname, isStatic, isSpecial, aload0pos, count, found); } private void atMethodCallCore2(CtClass targetClass, String mname, boolean isStatic, boolean isSpecial, int aload0pos, int count, MemberResolver.Method found) throws CompileError { CtClass declClass = found.declaring; MethodInfo minfo = found.info; String desc = minfo.getDescriptor(); int acc = minfo.getAccessFlags(); if (mname.equals(MethodInfo.nameInit)) { isSpecial = true; if (declClass != targetClass) throw new CompileError("no such a constructor"); if (declClass != thisClass && AccessFlag.isPrivate(acc)) { desc = getAccessibleConstructor(desc, declClass, minfo); bytecode.addOpcode(Opcode.ACONST_NULL); // the last parameter } } else if (AccessFlag.isPrivate(acc)) if (declClass == thisClass) isSpecial = true; else { isSpecial = false; isStatic = true; String origDesc = desc; if ((acc & AccessFlag.STATIC) == 0) desc = Descriptor.insertParameter(declClass.getName(), origDesc); acc = AccessFlag.setPackage(acc) | AccessFlag.STATIC; mname = getAccessiblePrivate(mname, origDesc, desc, minfo, declClass); } 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. If aload0pos >= 0, then the target object was pushed by aload_0. It is overwritten by NOP. */ isStatic = true; if (aload0pos >= 0) bytecode.write(aload0pos, NOP); else popTarget = true; } bytecode.addInvokestatic(declClass, mname, desc); } else if (isSpecial) // if (isSpecial && notStatic(acc)) bytecode.addInvokespecial(declClass, mname, desc); else { if (!Modifier.isPublic(declClass.getModifiers()) || declClass.isInterface() != targetClass.isInterface()) declClass = targetClass; if (declClass.isInterface()) bytecode.addInvokeinterface(declClass, mname, desc, count); else if (isStatic) throw new CompileError(mname + " is not static"); else bytecode.addInvokevirtual(declClass, mname, desc); } setReturnType(desc, isStatic, popTarget); } /* * Finds (or adds if necessary) a hidden accessor if the method * is in an enclosing class. * * @param desc the descriptor of the method. * @param declClass the class declaring the method. */ protected String getAccessiblePrivate(String methodName, String desc, String newDesc, MethodInfo minfo, CtClass declClass) throws CompileError { if (isEnclosing(declClass, thisClass)) { AccessorMaker maker = declClass.getAccessorMaker(); if (maker != null) return maker.getMethodAccessor(methodName, desc, newDesc, minfo); } throw new CompileError("Method " + methodName + " is private"); } /* * Finds (or adds if necessary) a hidden constructor if the given * constructor is in an enclosing class. * * @param desc the descriptor of the constructor. * @param declClass the class declaring the constructor. * @param minfo the method info of the constructor. * @return the descriptor of the hidden constructor. */ protected String getAccessibleConstructor(String desc, CtClass declClass, MethodInfo minfo) throws CompileError { if (isEnclosing(declClass, thisClass)) { AccessorMaker maker = declClass.getAccessorMaker(); if (maker != null) return maker.getConstructor(declClass, desc, minfo); } throw new CompileError("the called constructor is private in " + declClass.getName()); } private boolean isEnclosing(CtClass outer, CtClass inner) { try { while (inner != null) { inner = inner.getDeclaringClass(); if (inner == outer) return true; } } catch (NotFoundException e) {} return false; } public int getMethodArgsLength(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(); } } 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 = MemberResolver.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); } } } } protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right, boolean doDup) throws CompileError { CtField f = fieldAccess(left, false); boolean is_static = resultStatic; if (op != '=' && !is_static) bytecode.addOpcode(DUP); int fi; if (op == '=') { FieldInfo finfo = f.getFieldInfo2(); setFieldType(finfo); AccessorMaker maker = isAccessibleField(f, finfo); if (maker == null) fi = addFieldrefInfo(f, finfo); else fi = 0; } else fi = atFieldRead(f, is_static); 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); } atFieldAssignCore(f, is_static, fi, is2w); exprType = fType; arrayDim = fDim; className = cname; } /* If fi == 0, the field must be a private field in an enclosing class. */ private void atFieldAssignCore(CtField f, boolean is_static, int fi, boolean is2byte) throws CompileError { if (fi != 0) { if (is_static) { bytecode.add(PUTSTATIC); bytecode.growStack(is2byte ? -2 : -1); } else { bytecode.add(PUTFIELD); bytecode.growStack(is2byte ? -3 : -2); } bytecode.addIndex(fi); } else { CtClass declClass = f.getDeclaringClass(); AccessorMaker maker = declClass.getAccessorMaker(); // make should be non null. FieldInfo finfo = f.getFieldInfo2(); MethodInfo minfo = maker.getFieldSetter(finfo, is_static); bytecode.addInvokestatic(declClass, minfo.getName(), minfo.getDescriptor()); } } /* overwritten in JvstCodeGen. */ public void atMember(Member mem) throws CompileError { atFieldRead(mem); } protected void atFieldRead(ASTree expr) throws CompileError { CtField f = fieldAccess(expr, true); if (f == null) { atArrayLength(expr); return; } boolean is_static = resultStatic; ASTree cexpr = TypeChecker.getConstantFieldValue(f); if (cexpr == null) atFieldRead(f, is_static); else { cexpr.accept(this); setFieldType(f.getFieldInfo2()); } } private void atArrayLength(ASTree expr) throws CompileError { if (arrayDim == 0) throw new CompileError(".length applied to a non array"); bytecode.addOpcode(ARRAYLENGTH); exprType = INT; arrayDim = 0; } /** * Generates bytecode for reading a field value. * It returns a fieldref_info index or zero if the field is a private * one declared in an enclosing class. */ private int atFieldRead(CtField f, boolean isStatic) throws CompileError { FieldInfo finfo = f.getFieldInfo2(); boolean is2byte = setFieldType(finfo); AccessorMaker maker = isAccessibleField(f, finfo); if (maker != null) { MethodInfo minfo = maker.getFieldGetter(finfo, isStatic); bytecode.addInvokestatic(f.getDeclaringClass(), minfo.getName(), minfo.getDescriptor()); return 0; } else { int fi = addFieldrefInfo(f, finfo); if (isStatic) { bytecode.add(GETSTATIC); bytecode.growStack(is2byte ? 2 : 1); } else { bytecode.add(GETFIELD); bytecode.growStack(is2byte ? 1 : 0); } bytecode.addIndex(fi); return fi; } } /** * Returns null if the field is accessible. Otherwise, it throws * an exception or it returns AccessorMaker if the field is a private * one declared in an enclosing class. */ private AccessorMaker isAccessibleField(CtField f, FieldInfo finfo) throws CompileError { if (AccessFlag.isPrivate(finfo.getAccessFlags()) && f.getDeclaringClass() != thisClass) { CtClass declClass = f.getDeclaringClass(); if (isEnclosing(declClass, thisClass)) { AccessorMaker maker = declClass.getAccessorMaker(); if (maker != null) return maker; else throw new CompileError("fatal error. bug?"); } else throw new CompileError("Field " + f.getName() + " in " + declClass.getName() + " is private."); } return null; // accessible field } /** * Sets exprType, arrayDim, and className. * * @return true if the field type is long or double. */ private boolean setFieldType(FieldInfo finfo) throws CompileError { String type = finfo.getDescriptor(); int i = 0; int dim = 0; char c = type.charAt(i); while (c == '[') { ++dim; c = type.charAt(++i); } arrayDim = dim; exprType = MemberResolver.descToType(c); if (c == 'L') className = type.substring(i + 1, type.indexOf(';', i + 1)); else className = null; boolean is2byte = (c == 'J' || c == 'D'); return is2byte; } private int addFieldrefInfo(CtField f, FieldInfo finfo) { ConstPool cp = bytecode.getConstPool(); String cname = f.getDeclaringClass().getName(); int ci = cp.addClassInfo(cname); String name = finfo.getName(); String type = finfo.getDescriptor(); return cp.addFieldrefInfo(ci, name, type); } protected void atClassObject2(String cname) throws CompileError { if (getMajorVersion() < ClassFile.JAVA_5) super.atClassObject2(cname); else bytecode.addLdc(bytecode.getConstPool().addClassInfo(cname)); } protected void atFieldPlusPlus(int token, boolean isPost, ASTree oprand, Expr expr, boolean doDup) throws CompileError { CtField f = fieldAccess(oprand, false); boolean is_static = resultStatic; if (!is_static) bytecode.addOpcode(DUP); int fi = atFieldRead(f, is_static); 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); atFieldAssignCore(f, is_static, fi, is2w); } /* This method also returns a value in resultStatic. * * @param acceptLength true if array length is acceptable */ protected CtField fieldAccess(ASTree expr, boolean acceptLength) throws CompileError { if (expr instanceof Member) { String name = ((Member)expr).get(); CtField f = null; try { f = thisClass.getField(name); } catch (NotFoundException e) { // EXPR might be part of a static member access? throw new NoFieldException(name, expr); } boolean 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 resultStatic = is_static; return f; } else if (expr instanceof Expr) { Expr e = (Expr)expr; int op = e.getOperator(); if (op == MEMBER) { /* static member by # (extension by Javassist) * For example, if int.class is parsed, the resulting tree * is (# "java.lang.Integer" "TYPE"). */ CtField f = resolver.lookupField(((Symbol)e.oprand1()).get(), (Symbol)e.oprand2()); resultStatic = true; return f; } else if (op == '.') { CtField f = null; try { e.oprand1().accept(this); /* Don't call lookupFieldByJvmName2(). * The left operand of . is not a class name but * a normal expression. */ if (exprType == CLASS && arrayDim == 0) f = resolver.lookupFieldByJvmName(className, (Symbol)e.oprand2()); else if (acceptLength && arrayDim > 0 && ((Symbol)e.oprand2()).get().equals("length")) return null; // expr is an array length. else badLvalue(); boolean is_static = Modifier.isStatic(f.getModifiers()); if (is_static) bytecode.addOpcode(POP); resultStatic = is_static; return f; } catch (NoFieldException nfe) { if (nfe.getExpr() != e.oprand1()) throw nfe; /* EXPR should be a static field. * If EXPR might be part of a qualified class name, * lookupFieldByJvmName2() throws NoFieldException. */ Symbol fname = (Symbol)e.oprand2(); String cname = nfe.getField(); f = resolver.lookupFieldByJvmName2(cname, fname, expr); resolver.recordPackage(cname); resultStatic = true; return f; } } else badLvalue(); } else badLvalue(); resultStatic = false; return null; // never reach } 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++] = resolver.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++] = resolver.lookupClassByName((ASTList)list.head()); list = list.tail(); } return clist; } } /* 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 { return resolver.resolveClassName(name); } /* Expands a simple class name to java.lang.*. * For example, this converts Object into java/lang/Object. */ protected String resolveClassName(String jvmName) throws CompileError { return resolver.resolveJvmClassName(jvmName); } } javassist-3.12.1.ga/src/main/javassist/compiler/Lex.java0000644000175000017500000003670510630701321023064 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; 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 (Character.isJavaIdentifierStart((char)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 == 'D' || c2 == 'd' || 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' && c != 'D' && c != 'd') { 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 { if (c != 'D' && c != 'd') 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 (Character.isJavaIdentifierPart((char)c)); 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("strictfp", 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; } public String getTextAround() { int begin = position - 10; if (begin < 0) begin = 0; int end = position + 10; if (end > maxlen) end = maxlen; return input.substring(begin, end); } private int getc() { if (lastChar < 0) if (position < maxlen) return input.charAt(position++); else return -1; else { int c = lastChar; lastChar = -1; return c; } } } javassist-3.12.1.ga/src/main/javassist/compiler/MemberResolver.java0000644000175000017500000004270011007766754025301 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import java.util.List; import java.util.Iterator; import javassist.*; import javassist.bytecode.*; import javassist.compiler.ast.*; /* Code generator methods depending on javassist.* classes. */ public class MemberResolver implements TokenId { private ClassPool classPool; public MemberResolver(ClassPool cp) { classPool = cp; } public ClassPool getClassPool() { return classPool; } private static void fatal() throws CompileError { throw new CompileError("fatal"); } /** * @param jvmClassName a class name. Not a package name. */ public void recordPackage(String jvmClassName) { String classname = jvmToJavaName(jvmClassName); for (;;) { int i = classname.lastIndexOf('.'); if (i > 0) { classname = classname.substring(0, i); classPool.recordInvalidClassName(classname); } else break; } } public static class Method { public CtClass declaring; public MethodInfo info; public int notmatch; public Method(CtClass c, MethodInfo i, int n) { declaring = c; info = i; notmatch = n; } /** * Returns true if the invoked method is static. */ public boolean isStatic() { int acc = info.getAccessFlags(); return (acc & AccessFlag.STATIC) != 0; } } public Method lookupMethod(CtClass clazz, CtClass currentClass, MethodInfo current, String methodName, int[] argTypes, int[] argDims, String[] argClassNames) throws CompileError { Method maybe = null; // to enable the creation of a recursively called method if (current != null && clazz == currentClass) if (current.getName().equals(methodName)) { int res = compareSignature(current.getDescriptor(), argTypes, argDims, argClassNames); if (res != NO) { Method r = new Method(clazz, current, res); if (res == YES) return r; else maybe = r; } } Method m = lookupMethod(clazz, methodName, argTypes, argDims, argClassNames, maybe != null); if (m != null) return m; else return maybe; } private Method lookupMethod(CtClass clazz, String methodName, int[] argTypes, int[] argDims, String[] argClassNames, boolean onlyExact) throws CompileError { Method maybe = null; ClassFile cf = clazz.getClassFile2(); // If the class is an array type, the class file is null. // If so, search the super class java.lang.Object for clone() etc. if (cf != null) { List list = cf.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); if (res != NO) { Method r = new Method(clazz, minfo, res); if (res == YES) return r; else if (maybe == null || maybe.notmatch > res) maybe = r; } } } } if (onlyExact) maybe = null; else onlyExact = maybe != null; int mod = clazz.getModifiers(); boolean isIntf = Modifier.isInterface(mod); try { // skip searching java.lang.Object if clazz is an interface type. if (!isIntf) { CtClass pclazz = clazz.getSuperclass(); if (pclazz != null) { Method r = lookupMethod(pclazz, methodName, argTypes, argDims, argClassNames, onlyExact); if (r != null) return r; } } } catch (NotFoundException e) {} if (isIntf || Modifier.isAbstract(mod)) try { CtClass[] ifs = clazz.getInterfaces(); int size = ifs.length; for (int i = 0; i < size; ++i) { Method r = lookupMethod(ifs[i], methodName, argTypes, argDims, argClassNames, onlyExact); if (r != null) return r; } if (isIntf) { // finally search java.lang.Object. CtClass pclazz = clazz.getSuperclass(); if (pclazz != null) { Method r = lookupMethod(pclazz, methodName, argTypes, argDims, argClassNames, onlyExact); if (r != null) return r; } } } catch (NotFoundException e) {} return maybe; } private static final int YES = 0; private static final int NO = -1; /* * 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. * * If some of the parameter types exactly match but others are subtypes of * the corresponding type in the signature, this method returns the number * of parameter types that do not exactly match. */ 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; if (c == 'L') i = desc.indexOf(';', i) + 1; } 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++; 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 = lookupClassByJvmName(argClassNames[n]); try { if (clazz.subtypeOf(lookupClassByJvmName(cname))) result++; else return NO; } catch (NotFoundException e) { result++; // 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++; else return NO; } } return NO; } /** * Only used by fieldAccess() in MemberCodeGen and TypeChecker. * * @param jvmClassName a JVM class name. e.g. java/lang/String */ public CtField lookupFieldByJvmName2(String jvmClassName, Symbol fieldSym, ASTree expr) throws NoFieldException { String field = fieldSym.get(); CtClass cc = null; try { cc = lookupClass(jvmToJavaName(jvmClassName), true); } catch (CompileError e) { // EXPR might be part of a qualified class name. throw new NoFieldException(jvmClassName + "/" + field, expr); } try { return cc.getField(field); } catch (NotFoundException e) { // maybe an inner class. jvmClassName = javaToJvmName(cc.getName()); throw new NoFieldException(jvmClassName + "$" + field, expr); } } /** * @param jvmClassName a JVM class name. e.g. java/lang/String */ public CtField lookupFieldByJvmName(String jvmClassName, Symbol fieldName) throws CompileError { return lookupField(jvmToJavaName(jvmClassName), fieldName); } /** * @param name a qualified class name. e.g. java.lang.String */ public CtField lookupField(String className, Symbol fieldName) throws CompileError { CtClass cc = lookupClass(className, false); try { return cc.getField(fieldName.get()); } catch (NotFoundException e) {} throw new CompileError("no such field: " + fieldName.get()); } public CtClass lookupClassByName(ASTList name) throws CompileError { return lookupClass(Declarator.astToClassName(name, '.'), false); } public CtClass lookupClassByJvmName(String jvmName) throws CompileError { return lookupClass(jvmToJavaName(jvmName), false); } public CtClass lookupClass(Declarator decl) throws CompileError { return lookupClass(decl.getType(), decl.getArrayDim(), decl.getClassName()); } /** * @parma classname jvm class name. */ public CtClass lookupClass(int type, int dim, String classname) throws CompileError { String cname = ""; CtClass clazz; if (type == CLASS) { clazz = lookupClassByJvmName(classname); if (dim > 0) cname = clazz.getName(); else return clazz; } else cname = getTypeName(type); while (dim-- > 0) cname += "[]"; return lookupClass(cname, false); } /* * type cannot be CLASS */ static String getTypeName(int type) throws CompileError { String cname = ""; switch (type) { 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(); } return cname; } /** * @param name a qualified class name. e.g. java.lang.String */ public CtClass lookupClass(String name, boolean notCheckInner) throws CompileError { try { return lookupClass0(name, notCheckInner); } catch (NotFoundException e) { return searchImports(name); } } private CtClass searchImports(String orgName) throws CompileError { if (orgName.indexOf('.') < 0) { Iterator it = classPool.getImportedPackages(); while (it.hasNext()) { String pac = (String)it.next(); String fqName = pac + '.' + orgName; try { CtClass cc = classPool.get(fqName); // if the class is found, classPool.recordInvalidClassName(orgName); return cc; } catch (NotFoundException e) { classPool.recordInvalidClassName(fqName); } } } throw new CompileError("no such class: " + orgName); } private CtClass lookupClass0(String classname, boolean notCheckInner) throws NotFoundException { CtClass cc = null; do { try { cc = classPool.get(classname); } catch (NotFoundException e) { int i = classname.lastIndexOf('.'); if (notCheckInner || i < 0) throw e; else { StringBuffer sbuf = new StringBuffer(classname); sbuf.setCharAt(i, '$'); classname = sbuf.toString(); } } } while (cc == null); return cc; } /* 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. */ public String resolveClassName(ASTList name) throws CompileError { if (name == null) return null; else return javaToJvmName(lookupClassByName(name).getName()); } /* Expands a simple class name to java.lang.*. * For example, this converts Object into java/lang/Object. */ public String resolveJvmClassName(String jvmName) throws CompileError { if (jvmName == null) return null; else return javaToJvmName(lookupClassByJvmName(jvmName).getName()); } public static CtClass getSuperclass(CtClass c) throws CompileError { try { CtClass sc = c.getSuperclass(); if (sc != null) return sc; } catch (NotFoundException e) {} throw new CompileError("cannot find the super class of " + c.getName()); } public static String javaToJvmName(String classname) { return classname.replace('.', '/'); } public static String jvmToJavaName(String classname) { return classname.replace('/', '.'); } public 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; // never reach here } } 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; } } javassist-3.12.1.ga/src/main/javassist/compiler/AccessorMaker.java0000644000175000017500000002223310771471405025062 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import javassist.*; import javassist.bytecode.*; import java.util.HashMap; /** * AccessorMaker maintains accessors to private members of an enclosing * class. It is necessary for compiling a method in an inner class. */ public class AccessorMaker { private CtClass clazz; private int uniqueNumber; private HashMap accessors; static final String lastParamType = "javassist.runtime.Inner"; public AccessorMaker(CtClass c) { clazz = c; uniqueNumber = 1; accessors = new HashMap(); } public String getConstructor(CtClass c, String desc, MethodInfo orig) throws CompileError { String key = ":" + desc; String consDesc = (String)accessors.get(key); if (consDesc != null) return consDesc; // already exists. consDesc = Descriptor.appendParameter(lastParamType, desc); ClassFile cf = clazz.getClassFile(); // turn on the modified flag. try { ConstPool cp = cf.getConstPool(); ClassPool pool = clazz.getClassPool(); MethodInfo minfo = new MethodInfo(cp, MethodInfo.nameInit, consDesc); minfo.setAccessFlags(0); minfo.addAttribute(new SyntheticAttribute(cp)); ExceptionsAttribute ea = orig.getExceptionsAttribute(); if (ea != null) minfo.addAttribute(ea.copy(cp, null)); CtClass[] params = Descriptor.getParameterTypes(desc, pool); Bytecode code = new Bytecode(cp); code.addAload(0); int regno = 1; for (int i = 0; i < params.length; ++i) regno += code.addLoad(regno, params[i]); code.setMaxLocals(regno + 1); // the last parameter is added. code.addInvokespecial(clazz, MethodInfo.nameInit, desc); code.addReturn(null); minfo.setCodeAttribute(code.toCodeAttribute()); cf.addMethod(minfo); } catch (CannotCompileException e) { throw new CompileError(e); } catch (NotFoundException e) { throw new CompileError(e); } accessors.put(key, consDesc); return consDesc; } /** * Returns the name of the method for accessing a private method. * * @param name the name of the private method. * @param desc the descriptor of the private method. * @param accDesc the descriptor of the accessor method. The first * parameter type is clazz. * If the private method is static, * accDesc must be identical to desc. * * @param orig the method info of the private method. * @return */ public String getMethodAccessor(String name, String desc, String accDesc, MethodInfo orig) throws CompileError { String key = name + ":" + desc; String accName = (String)accessors.get(key); if (accName != null) return accName; // already exists. ClassFile cf = clazz.getClassFile(); // turn on the modified flag. accName = findAccessorName(cf); try { ConstPool cp = cf.getConstPool(); ClassPool pool = clazz.getClassPool(); MethodInfo minfo = new MethodInfo(cp, accName, accDesc); minfo.setAccessFlags(AccessFlag.STATIC); minfo.addAttribute(new SyntheticAttribute(cp)); ExceptionsAttribute ea = orig.getExceptionsAttribute(); if (ea != null) minfo.addAttribute(ea.copy(cp, null)); CtClass[] params = Descriptor.getParameterTypes(accDesc, pool); int regno = 0; Bytecode code = new Bytecode(cp); for (int i = 0; i < params.length; ++i) regno += code.addLoad(regno, params[i]); code.setMaxLocals(regno); if (desc == accDesc) code.addInvokestatic(clazz, name, desc); else code.addInvokevirtual(clazz, name, desc); code.addReturn(Descriptor.getReturnType(desc, pool)); minfo.setCodeAttribute(code.toCodeAttribute()); cf.addMethod(minfo); } catch (CannotCompileException e) { throw new CompileError(e); } catch (NotFoundException e) { throw new CompileError(e); } accessors.put(key, accName); return accName; } /** * Returns the method_info representing the added getter. */ public MethodInfo getFieldGetter(FieldInfo finfo, boolean is_static) throws CompileError { String fieldName = finfo.getName(); String key = fieldName + ":getter"; Object res = accessors.get(key); if (res != null) return (MethodInfo)res; // already exists. ClassFile cf = clazz.getClassFile(); // turn on the modified flag. String accName = findAccessorName(cf); try { ConstPool cp = cf.getConstPool(); ClassPool pool = clazz.getClassPool(); String fieldType = finfo.getDescriptor(); String accDesc; if (is_static) accDesc = "()" + fieldType; else accDesc = "(" + Descriptor.of(clazz) + ")" + fieldType; MethodInfo minfo = new MethodInfo(cp, accName, accDesc); minfo.setAccessFlags(AccessFlag.STATIC); minfo.addAttribute(new SyntheticAttribute(cp)); Bytecode code = new Bytecode(cp); if (is_static) { code.addGetstatic(Bytecode.THIS, fieldName, fieldType); } else { code.addAload(0); code.addGetfield(Bytecode.THIS, fieldName, fieldType); code.setMaxLocals(1); } code.addReturn(Descriptor.toCtClass(fieldType, pool)); minfo.setCodeAttribute(code.toCodeAttribute()); cf.addMethod(minfo); accessors.put(key, minfo); return minfo; } catch (CannotCompileException e) { throw new CompileError(e); } catch (NotFoundException e) { throw new CompileError(e); } } /** * Returns the method_info representing the added setter. */ public MethodInfo getFieldSetter(FieldInfo finfo, boolean is_static) throws CompileError { String fieldName = finfo.getName(); String key = fieldName + ":setter"; Object res = accessors.get(key); if (res != null) return (MethodInfo)res; // already exists. ClassFile cf = clazz.getClassFile(); // turn on the modified flag. String accName = findAccessorName(cf); try { ConstPool cp = cf.getConstPool(); ClassPool pool = clazz.getClassPool(); String fieldType = finfo.getDescriptor(); String accDesc; if (is_static) accDesc = "(" + fieldType + ")V"; else accDesc = "(" + Descriptor.of(clazz) + fieldType + ")V"; MethodInfo minfo = new MethodInfo(cp, accName, accDesc); minfo.setAccessFlags(AccessFlag.STATIC); minfo.addAttribute(new SyntheticAttribute(cp)); Bytecode code = new Bytecode(cp); int reg; if (is_static) { reg = code.addLoad(0, Descriptor.toCtClass(fieldType, pool)); code.addPutstatic(Bytecode.THIS, fieldName, fieldType); } else { code.addAload(0); reg = code.addLoad(1, Descriptor.toCtClass(fieldType, pool)) + 1; code.addPutfield(Bytecode.THIS, fieldName, fieldType); } code.addReturn(null); code.setMaxLocals(reg); minfo.setCodeAttribute(code.toCodeAttribute()); cf.addMethod(minfo); accessors.put(key, minfo); return minfo; } catch (CannotCompileException e) { throw new CompileError(e); } catch (NotFoundException e) { throw new CompileError(e); } } private String findAccessorName(ClassFile cf) { String accName; do { accName = "access$" + uniqueNumber++; } while (cf.getMethod(accName) != null); return accName; } } javassist-3.12.1.ga/src/main/javassist/compiler/KeywordTable.java0000644000175000017500000000206610630701321024721 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; public 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)); } } javassist-3.12.1.ga/src/main/javassist/compiler/SyntaxError.java0000644000175000017500000000154610630701321024627 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; public class SyntaxError extends CompileError { public SyntaxError(Lex lexer) { super("syntax error near \"" + lexer.getTextAround() + "\"", lexer); } } javassist-3.12.1.ga/src/main/javassist/compiler/CodeGen.java0000644000175000017500000016624511213440265023650 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import java.util.ArrayList; import java.util.Arrays; 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; TypeChecker typeChecker; /** * true if the last visited node is a return statement. */ protected boolean hasReturned; /** * Must be true if compilation is for a static method. */ public boolean inStaticMethod; protected ArrayList breakList, continueList; /** * doit() in ReturnHook is called from atReturn(). */ protected static abstract class ReturnHook { ReturnHook next; /** * Returns true if the generated code ends with return, * throw, or goto. */ protected abstract boolean doit(Bytecode b, int opcode); protected ReturnHook(CodeGen gen) { next = gen.returnHooks; gen.returnHooks = this; } protected void remove(CodeGen gen) { gen.returnHooks = next; } } protected ReturnHook returnHooks; /* 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; typeChecker = null; hasReturned = false; inStaticMethod = false; breakList = null; continueList = null; returnHooks = null; } public void setTypeChecker(TypeChecker checker) { typeChecker = checker; } 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 compileExpr(ASTree expr) throws CompileError { doTypeCheck(expr); expr.accept(this); } public boolean compileBooleanExpr(boolean branchIf, ASTree expr) throws CompileError { doTypeCheck(expr); return booleanExpr(branchIf, expr); } public void doTypeCheck(ASTree expr) throws CompileError { if (typeChecker != null) expr.accept(typeChecker); } 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); } /** * @param isCons true if super() must be called. * false if the method is a class initializer. */ public void atMethodBody(Stmnt s, boolean isCons, boolean isVoid) throws CompileError { if (s == null) return; if (isCons && needsSuperCall(s)) insertDefaultSuperCall(); hasReturned = false; s.accept(this); if (!hasReturned) if (isVoid) { bytecode.addOpcode(Opcode.RETURN); hasReturned = true; } else throw new CompileError("no return statement"); } private boolean needsSuperCall(Stmnt body) throws CompileError { if (body.getOperator() == BLOCK) body = (Stmnt)body.head(); if (body != null && body.getOperator() == EXPR) { ASTree expr = body.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(); doTypeCheck(expr); 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 if (op == SWITCH) atSwitchStmnt(st); else if (op == SYNCHRONIZED) atSyncStmnt(st); else { // LABEL, SWITCH label stament might be null?. hasReturned = false; 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(); compileBooleanExpr(false, expr); int pc = bytecode.currentPc(); int pc2 = 0; bytecode.addIndex(0); // correct later hasReturned = false; if (thenp != null) thenp.accept(this); boolean thenHasReturned = hasReturned; hasReturned = false; if (elsep != null && !thenHasReturned) { bytecode.addOpcode(Opcode.GOTO); pc2 = bytecode.currentPc(); bytecode.addIndex(0); } bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); if (elsep != null) { elsep.accept(this); if (!thenHasReturned) bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); hasReturned = thenHasReturned && hasReturned; } } 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); boolean alwaysBranch = compileBooleanExpr(true, expr); bytecode.addIndex(pc2 - bytecode.currentPc() + 1); patchGoto(breakList, bytecode.currentPc()); patchGoto(continueList, pc3); continueList = prevContList; breakList = prevBreakList; hasReturned = alwaysBranch; } protected 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) { compileBooleanExpr(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; hasReturned = false; } private void atSwitchStmnt(Stmnt st) throws CompileError { compileExpr(st.head()); ArrayList prevBreakList = breakList; breakList = new ArrayList(); int opcodePc = bytecode.currentPc(); bytecode.addOpcode(LOOKUPSWITCH); int npads = 3 - (opcodePc & 3); while (npads-- > 0) bytecode.add(0); Stmnt body = (Stmnt)st.tail(); int npairs = 0; for (ASTList list = body; list != null; list = list.tail()) if (((Stmnt)list.head()).getOperator() == CASE) ++npairs; // opcodePc2 is the position at which the default jump offset is. int opcodePc2 = bytecode.currentPc(); bytecode.addGap(4); bytecode.add32bit(npairs); bytecode.addGap(npairs * 8); long[] pairs = new long[npairs]; int ipairs = 0; int defaultPc = -1; for (ASTList list = body; list != null; list = list.tail()) { Stmnt label = (Stmnt)list.head(); int op = label.getOperator(); if (op == DEFAULT) defaultPc = bytecode.currentPc(); else if (op != CASE) fatal(); else { pairs[ipairs++] = ((long)computeLabel(label.head()) << 32) + ((long)(bytecode.currentPc() - opcodePc) & 0xffffffff); } hasReturned = false; ((Stmnt)label.tail()).accept(this); } Arrays.sort(pairs); int pc = opcodePc2 + 8; for (int i = 0; i < npairs; ++i) { bytecode.write32bit(pc, (int)(pairs[i] >>> 32)); bytecode.write32bit(pc + 4, (int)pairs[i]); pc += 8; } if (defaultPc < 0 || breakList.size() > 0) hasReturned = false; int endPc = bytecode.currentPc(); if (defaultPc < 0) defaultPc = endPc; bytecode.write32bit(opcodePc2, defaultPc - opcodePc); patchGoto(breakList, endPc); breakList = prevBreakList; } private int computeLabel(ASTree expr) throws CompileError { doTypeCheck(expr); expr = TypeChecker.stripPlusExpr(expr); if (expr instanceof IntConst) return (int)((IntConst)expr).get(); else throw new CompileError("bad case label"); } 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 { compileExpr(result); 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; } } for (ReturnHook har = returnHooks; har != null; har = har.next) if (har.doit(bytecode, op)) { hasReturned = true; return; } bytecode.addOpcode(op); hasReturned = true; } private void atThrowStmnt(Stmnt st) throws CompileError { ASTree e = st.getLeft(); compileExpr(e); if (exprType != CLASS || arrayDim > 0) throw new CompileError("bad throw statement"); bytecode.addOpcode(ATHROW); hasReturned = true; } /* overridden in MemberCodeGen */ protected void atTryStmnt(Stmnt st) throws CompileError { hasReturned = false; } private void atSyncStmnt(Stmnt st) throws CompileError { int nbreaks = getListSize(breakList); int ncontinues = getListSize(continueList); compileExpr(st.head()); if (exprType != CLASS && arrayDim == 0) throw new CompileError("bad type expr for synchronized block"); Bytecode bc = bytecode; final int var = bc.getMaxLocals(); bc.incMaxLocals(1); bc.addOpcode(DUP); bc.addAstore(var); bc.addOpcode(MONITORENTER); ReturnHook rh = new ReturnHook(this) { protected boolean doit(Bytecode b, int opcode) { b.addAload(var); b.addOpcode(MONITOREXIT); return false; } }; int pc = bc.currentPc(); Stmnt body = (Stmnt)st.tail(); if (body != null) body.accept(this); int pc2 = bc.currentPc(); int pc3 = 0; if (!hasReturned) { rh.doit(bc, 0); // the 2nd arg is ignored. bc.addOpcode(Opcode.GOTO); pc3 = bc.currentPc(); bc.addIndex(0); } if (pc < pc2) { // if the body is not empty int pc4 = bc.currentPc(); rh.doit(bc, 0); // the 2nd arg is ignored. bc.addOpcode(ATHROW); bc.addExceptionHandler(pc, pc2, pc4, 0); } if (!hasReturned) bc.write16bit(pc3, bc.currentPc() - pc3 + 1); rh.remove(this); if (getListSize(breakList) != nbreaks || getListSize(continueList) != ncontinues) throw new CompileError( "sorry, cannot break/continue in synchronized block"); } private static int getListSize(ArrayList list) { return list == null ? 0 : list.size(); } 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) { doTypeCheck(init); atVariableAssign(null, '=', null, d, init, false); } } public abstract void atNewExpr(NewExpr n) throws CompileError; public abstract void atArrayInit(ArrayInit init) 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); // expr is null if the caller is atDeclarator(). if (expr == null && right instanceof ArrayInit) atArrayVariableAssign((ArrayInit)right, varType, varArray, varClass); else 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; } protected abstract void atArrayVariableAssign(ArrayInit init, int varType, int varArray, String varClass) throws CompileError; 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 { if (op == PLUS_E && dim == 0 && type == CLASS) atStringPlusEq(expr, type, dim, cname, right); else { right.accept(this); if (invalidDim(exprType, arrayDim, className, type, dim, cname, false) || (op != '=' && dim > 0)) badAssign(expr); 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 void atStringPlusEq(Expr expr, int type, int dim, String cname, ASTree right) throws CompileError { if (!jvmJavaLangString.equals(cname)) badAssign(expr); convToString(type, dim); // the value might be null. right.accept(this); convToString(exprType, arrayDim); bytecode.addInvokevirtual(javaLangString, "concat", "(Ljava/lang/String;)Ljava/lang/String;"); exprType = CLASS; arrayDim = 0; className = jvmJavaLangString; } 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); int dim1 = arrayDim; bytecode.addOpcode(Opcode.GOTO); int pc2 = bytecode.currentPc(); bytecode.addIndex(0); bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); expr.elseExpr().accept(this); if (dim1 != arrayDim) throw new CompileError("type mismatch in ?:"); bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1); } static 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 }; static 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); ASTree right = expr.oprand2(); if (right == null) return; // see TypeChecker.atBinExpr(). int type1 = exprType; int dim1 = arrayDim; String cname1 = className; right.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); } else { /* 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 != BOOLEAN) 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); // even if type1 is String, the left operand might be null. 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. * * @return true if the compiled code is GOTO (always branch). */ private boolean booleanExpr(boolean branchIf, ASTree expr) throws CompileError { boolean isAndAnd; int op = getCompOperator(expr); if (op == EQ) { // ==, !=, ... BinExpr bexpr = (BinExpr)expr; int type1 = compileOprands(bexpr); // here, arrayDim might represent the array dim. of the left oprand // if the right oprand is NULL. 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 if (isAlwaysBranch(expr, branchIf)) { bytecode.addOpcode(Opcode.GOTO); return true; // always branch } else { // others expr.accept(this); if (exprType != BOOLEAN || arrayDim != 0) throw new CompileError("boolean expr is required"); bytecode.addOpcode(branchIf ? IFNE : IFEQ); } exprType = BOOLEAN; arrayDim = 0; return false; } private static boolean isAlwaysBranch(ASTree expr, boolean branchIf) { if (expr instanceof Keyword) { int t = ((Keyword)expr).get(); return branchIf ? t == TRUE : t == FALSE; } return false; } 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) if (type1 != NULL && exprType != NULL) throw new CompileError("incompatible array types"); else if (exprType == NULL) arrayDim = dim1; if (type1 == NULL) return exprType; else return type1; } private static 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 static 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 } // used in TypeChecker. static boolean isP_INT(int type) { return typePrecedence(type) == P_INT; } // used in TypeChecker. static boolean rightIsStrong(int type1, int type2) { int type1_p = typePrecedence(type1); int type2_p = typePrecedence(type2); return type1_p >= 0 && type2_p >= 0 && type1_p > type2_p; } 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) { if (type1_p == P_LONG) { bytecode.addOpcode(DUP_X2); bytecode.addOpcode(POP); } else 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 { // array access, member access, // (unary) +, (unary) -, ++, --, !, ~ int token = expr.getOperator(); ASTree oprand = expr.oprand1(); if (token == '.') { String member = ((Symbol)expr.oprand2()).get(); if (member.equals("class")) atClassObject(expr); // .class else atFieldRead(expr); } else if (token == MEMBER) { // field read /* MEMBER ('#') is an extension by Javassist. * The compiler internally uses # for compiling .class * expressions such as "int.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 if (token == CALL) // method call fatal(); 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()); } public abstract void atCallExpr(CallExpr expr) throws CompileError; protected abstract void atFieldRead(ASTree expr) throws CompileError; public void atClassObject(Expr expr) throws CompileError { ASTree op1 = expr.oprand1(); if (!(op1 instanceof Symbol)) throw new CompileError("fatal error: badly parsed .class expr"); String cname = ((Symbol)op1).get(); if (cname.startsWith("[")) { int i = cname.indexOf("[L"); if (i >= 0) { String name = cname.substring(i + 2, cname.length() - 1); String name2 = resolveClassName(name); if (!name.equals(name2)) { /* For example, to obtain String[].class, * "[Ljava.lang.String;" (not "[Ljava/lang/String"!) * must be passed to Class.forName(). */ name2 = MemberResolver.jvmToJavaName(name2); StringBuffer sbuf = new StringBuffer(); while (i-- >= 0) sbuf.append('['); sbuf.append('L').append(name2).append(';'); cname = sbuf.toString(); } } } else { cname = resolveClassName(MemberResolver.javaToJvmName(cname)); cname = MemberResolver.jvmToJavaName(cname); } atClassObject2(cname); exprType = CLASS; arrayDim = 0; className = "java/lang/Class"; } /* MemberCodeGen overrides this method. */ protected void atClassObject2(String cname) throws CompileError { int start = bytecode.currentPc(); bytecode.addLdc(cname); bytecode.addInvokestatic("java.lang.Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;"); int end = bytecode.currentPc(); bytecode.addOpcode(Opcode.GOTO); int pc = bytecode.currentPc(); bytecode.addIndex(0); // correct later bytecode.addExceptionHandler(start, end, bytecode.currentPc(), "java.lang.ClassNotFoundException"); /* -- the following code is for inlining a call to DotClass.fail(). int var = getMaxLocals(); incMaxLocals(1); bytecode.growStack(1); bytecode.addAstore(var); bytecode.addNew("java.lang.NoClassDefFoundError"); bytecode.addOpcode(DUP); bytecode.addAload(var); bytecode.addInvokevirtual("java.lang.ClassNotFoundException", "getMessage", "()Ljava/lang/String;"); bytecode.addInvokespecial("java.lang.NoClassDefFoundError", "", "(Ljava/lang/String;)V"); */ bytecode.growStack(1); bytecode.addInvokestatic("javassist.runtime.DotClass", "fail", "(Ljava/lang/ClassNotFoundException;)" + "Ljava/lang/NoClassDefFoundError;"); bytecode.addOpcode(ATHROW); bytecode.write16bit(pc, bytecode.currentPc() - pc + 1); } public void atArrayRead(ASTree array, ASTree index) throws CompileError { 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) { 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) { if (dim > 0) return AASTORE; switch (type) { case DOUBLE : return DASTORE; case FLOAT : return FASTORE; case LONG : return LASTORE; case INT : return IASTORE; case SHORT : return SASTORE; 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); int delta = token == PLUSPLUS ? 1 : -1; if (var > 0xff) { bytecode.addOpcode(WIDE); bytecode.addOpcode(IINC); bytecode.addIndex(var); bytecode.addIndex(delta); } else { bytecode.addOpcode(IINC); bytecode.add(var); bytecode.add(delta); } 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 = jvmJavaLangString; 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()); } } } javassist-3.12.1.ga/src/main/javassist/compiler/JvstCodeGen.java0000644000175000017500000005745211021202354024506 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import javassist.*; import javassist.bytecode.*; import javassist.compiler.ast.*; /* Code generator accepting extended Java syntax for Javassist. */ public class JvstCodeGen extends MemberCodeGen { String paramArrayName = null; String paramListName = null; 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 public static final String sigName = "$sig"; public static final String dollarTypeName = "$type"; public static final String clazzName = "$class"; private CtClass dollarType = null; CtClass returnType = null; String returnCastName = null; private String returnVarName = null; // null if $_ is not used. public static final String wrapperCastName = "$w"; String proceedName = null; public static final String cflowName = "$cflow"; ProceedHandler procHandler = null; // null if not used. public JvstCodeGen(Bytecode b, CtClass cc, ClassPool cp) { super(b, cc, cp); setTypeChecker(new JvstTypeChecker(cc, cp, this)); } /* 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(dollarTypeName + " 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"; } 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 (exprType == VOID || isRefType(exprType) || arrayDim > 0) compileUnwrapValue(returnType, bytecode); else if (returnType instanceof CtPrimitiveType) { CtPrimitiveType pt = (CtPrimitiveType)returnType; int destType = MemberResolver.descToType(pt.getDescriptor()); atNumCastExpr(exprType, destType); exprType = destType; arrayDim = 0; className = null; } else throw new CompileError("invalid cast"); } protected void atCastToWrapper(CastExpr expr) throws CompileError { expr.getOprand().accept(this); if (isRefType(exprType) || arrayDim > 0) return; // Object type. do nothing. CtClass clazz = resolver.lookupClass(exprType, arrayDim, className); if (clazz instanceof CtPrimitiveType) { CtPrimitiveType pt = (CtPrimitiveType)clazz; String wrapper = pt.getWrapperName(); bytecode.addNew(wrapper); // new 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, "", "(" + 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(). */ public void atCallExpr(CallExpr 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.atCallExpr(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 = resolver.getClassPool().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 '(' ')' * : ('.' )* */ 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 getMethodArgsLength(ASTList args) { if (!isParamListName(args)) return super.getMethodArgsLength(args); return paramTypeList.length; } */ public int getMethodArgsLength(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; } } */ /* called by Javac#recordSpecialProceed(). */ void compileInvokeSpecial(ASTree target, String classname, String methodname, String descriptor, ASTList args) throws CompileError { target.accept(this); int nargs = getMethodArgsLength(args); atMethodArgs(args, new int[nargs], new int[nargs], new String[nargs]); bytecode.addInvokespecial(classname, methodname, descriptor); setReturnType(descriptor, false, false); addNullIfVoid(); } /* * Makes it valid to write "return ;" for a void method. */ protected void atReturnStmnt(Stmnt st) throws CompileError { ASTree result = st.getLeft(); if (result != null && returnType == CtClass.voidType) { compileExpr(result); 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 $_. * *

    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 $class available. * $0 is equivalent to THIS if the method is not static. Otherwise, * if the method is static, then $0 is not available. */ public int recordParams(CtClass[] params, boolean isStatic, String prefix, String paramVarName, String paramsName, SymbolTable tbl) throws CompileError { return recordParams(params, isStatic, prefix, paramVarName, paramsName, !isStatic, 0, getThisName(), tbl); } /** * Makes method parameters $0, $1, ..., $args, $$, and $class available. * $0 is available only if use0 is true. It might not be equivalent * to THIS. * * @param params the parameter types (the types of $1, $2, ..) * @param prefix it must be "$" (the first letter of $0, $1, ...) * @param paramVarName it must be "$args" * @param paramsName it must be "$$" * @param 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. The value of "target" is also used * as the name of the type represented by $class. * @param isStatic true if the method in which the compiled bytecode * is embedded is static. */ public int 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; if (target != null) param0Type = MemberResolver.jvmToJavaName(target); inStaticMethod = isStatic; varNo = paramBase; if (use0) { String varName = prefix + "0"; Declarator decl = new Declarator(CLASS, MemberResolver.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); return 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; } /** * Makes the given variable name available. * * @param typeDesc the type descriptor of the variable * @param varName variable name * @param varNo an index into the local variable array */ public void recordVariable(String typeDesc, String varName, int varNo, SymbolTable tbl) throws CompileError { char c; int dim = 0; while ((c = typeDesc.charAt(dim)) == '[') ++dim; int type = MemberResolver.descToType(c); String cname = null; if (type == CLASS) { if (dim == 0) cname = typeDesc.substring(1, typeDesc.length() - 1); else cname = typeDesc.substring(dim + 1, typeDesc.length() - 1); } Declarator decl = new Declarator(type, cname, dim, varNo, new Symbol(varName)); tbl.append(varName, decl); } /* 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_ code.addAnewarray(javaLangObject); // anewarray Object for (int i = 0; i < n; ++i) { code.addOpcode(Bytecode.DUP); // dup code.addIconst(i); // iconst_ if (params[i].isPrimitive()) { CtPrimitiveType pt = (CtPrimitiveType)params[i]; String wrapper = pt.getWrapperName(); code.addNew(wrapper); // new code.addOpcode(Bytecode.DUP); // dup int s = code.addLoad(regno, pt); // ?load regno += s; args[0] = pt; code.addInvokespecial(wrapper, "", Descriptor.ofMethod(CtClass.voidType, args)); // invokespecial } else { code.addAload(regno); // aload ++regno; } code.addOpcode(Bytecode.AASTORE); // aastore } return 8; } } protected void compileUnwrapValue(CtClass type, Bytecode code) throws CompileError { if (type == CtClass.voidType) { addNullIfVoid(); return; } if (exprType == VOID) throw new CompileError("invalid type for " + returnCastName); if (type instanceof CtPrimitiveType) { CtPrimitiveType pt = (CtPrimitiveType)type; // pt is not 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 = MemberResolver.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 = MemberResolver.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, MemberResolver.descToType(pt.getDescriptor())); } else throw new CompileError("type mismatch"); } } javassist-3.12.1.ga/src/main/javassist/compiler/TypeChecker.java0000644000175000017500000010022010630701321024522 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import javassist.CtClass; import javassist.CtField; import javassist.ClassPool; import javassist.Modifier; import javassist.NotFoundException; import javassist.compiler.ast.*; import javassist.bytecode.*; public class TypeChecker extends Visitor implements Opcode, TokenId { static final String javaLangObject = "java.lang.Object"; static final String jvmJavaLangObject = "java/lang/Object"; static final String jvmJavaLangString = "java/lang/String"; static final String jvmJavaLangClass = "java/lang/Class"; /* 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 protected MemberResolver resolver; protected CtClass thisClass; protected MethodInfo thisMethod; public TypeChecker(CtClass cc, ClassPool cp) { resolver = new MemberResolver(cp); thisClass = cc; thisMethod = null; } /* * Converts an array of tuples of exprType, arrayDim, and className * into a String object. */ protected static String argTypesToString(int[] types, int[] dims, String[] cnames) { StringBuffer sbuf = new StringBuffer(); sbuf.append('('); int n = types.length; if (n > 0) { int i = 0; while (true) { typeToString(sbuf, types[i], dims[i], cnames[i]); if (++i < n) sbuf.append(','); else break; } } sbuf.append(')'); return sbuf.toString(); } /* * Converts a tuple of exprType, arrayDim, and className * into a String object. */ protected static StringBuffer typeToString(StringBuffer sbuf, int type, int dim, String cname) { String s; if (type == CLASS) s = MemberResolver.jvmToJavaName(cname); else if (type == NULL) s = "Object"; else try { s = MemberResolver.getTypeName(type); } catch (CompileError e) { s = "?"; } sbuf.append(s); while (dim-- > 0) sbuf.append("[]"); return sbuf; } /** * Records the currently compiled method. */ public void setThisMethod(MethodInfo m) { thisMethod = m; } protected static void fatal() throws CompileError { throw new CompileError("fatal"); } /** * Returns the JVM-internal representation of this class name. */ protected String getThisName() { return MemberResolver.javaToJvmName(thisClass.getName()); } /** * Returns the JVM-internal representation of this super class name. */ protected String getSuperName() throws CompileError { return MemberResolver.javaToJvmName( MemberResolver.getSuperclass(thisClass).getName()); } /* 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 { return resolver.resolveClassName(name); } /* Expands a simple class name to java.lang.*. * For example, this converts Object into java/lang/Object. */ protected String resolveClassName(String jvmName) throws CompileError { return resolver.resolveJvmClassName(jvmName); } public void atNewExpr(NewExpr expr) throws CompileError { if (expr.isArray()) atNewArrayExpr(expr); else { CtClass clazz = resolver.lookupClassByName(expr.getClassName()); String cname = clazz.getName(); ASTList args = expr.getArguments(); atMethodCallCore(clazz, MethodInfo.nameInit, args); exprType = CLASS; arrayDim = 0; className = MemberResolver.javaToJvmName(cname); } } public void atNewArrayExpr(NewExpr expr) throws CompileError { int type = expr.getArrayType(); ASTList size = expr.getArraySize(); ASTList classname = expr.getClassName(); ASTree init = expr.getInitializer(); if (init != null) init.accept(this); if (size.length() > 1) atMultiNewArray(type, classname, size); else { ASTree sizeExpr = size.head(); if (sizeExpr != null) sizeExpr.accept(this); exprType = type; arrayDim = 1; if (type == CLASS) className = resolveClassName(classname); else className = null; } } public void atArrayInit(ArrayInit init) throws CompileError { ASTList list = init; while (list != null) { ASTree h = list.head(); list = list.tail(); if (h != null) h.accept(this); } } 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); } exprType = type; arrayDim = dim; if (type == CLASS) className = resolveClassName(classname); else className = null; } public void atAssignExpr(AssignExpr expr) 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); else { if (left instanceof Expr) { Expr e = (Expr)left; if (e.getOperator() == ARRAY) { atArrayAssign(expr, op, (Expr)left, right); return; } } atFieldAssign(expr, op, left, right); } } /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=. * * expr and var can be null. */ private void atVariableAssign(Expr expr, int op, Variable var, Declarator d, ASTree right) throws CompileError { int varType = d.getType(); int varArray = d.getArrayDim(); String varClass = d.getClassName(); if (op != '=') atVariable(var); right.accept(this); exprType = varType; arrayDim = varArray; className = varClass; } private void atArrayAssign(Expr expr, int op, Expr array, ASTree right) throws CompileError { atArrayRead(array.oprand1(), array.oprand2()); int aType = exprType; int aDim = arrayDim; String cname = className; right.accept(this); exprType = aType; arrayDim = aDim; className = cname; } protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right) throws CompileError { CtField f = fieldAccess(left); atFieldRead(f); int fType = exprType; int fDim = arrayDim; String cname = className; right.accept(this); exprType = fType; arrayDim = fDim; className = cname; } public void atCondExpr(CondExpr expr) throws CompileError { booleanExpr(expr.condExpr()); expr.thenExpr().accept(this); int type1 = exprType; int dim1 = arrayDim; String cname1 = className; expr.elseExpr().accept(this); if (dim1 == 0 && dim1 == arrayDim) if (CodeGen.rightIsStrong(type1, exprType)) expr.setThen(new CastExpr(exprType, 0, expr.thenExpr())); else if (CodeGen.rightIsStrong(exprType, type1)) { expr.setElse(new CastExpr(type1, 0, expr.elseExpr())); exprType = type1; } } /* * If atBinExpr() substitutes a new expression for the original * binary-operator expression, it changes the operator name to '+' * (if the original is not '+') and sets the new expression to the * left-hand-side expression and null to the right-hand-side expression. */ public void atBinExpr(BinExpr expr) throws CompileError { int token = expr.getOperator(); int k = CodeGen.lookupBinOp(token); if (k >= 0) { /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>> */ if (token == '+') { Expr e = atPlusExpr(expr); if (e != null) { /* String concatenation has been translated into * an expression using StringBuffer. */ e = CallExpr.makeCall(Expr.make('.', e, new Member("toString")), null); expr.setOprand1(e); expr.setOprand2(null); // <---- look at this! className = jvmJavaLangString; } } else { ASTree left = expr.oprand1(); ASTree right = expr.oprand2(); left.accept(this); int type1 = exprType; right.accept(this); if (!isConstant(expr, token, left, right)) computeBinExprType(expr, token, type1); } } else { /* equation: &&, ||, ==, !=, <=, >=, <, > */ booleanExpr(expr); } } /* EXPR must be a + expression. * atPlusExpr() returns non-null if the given expression is string * concatenation. The returned value is "new StringBuffer().append..". */ private Expr atPlusExpr(BinExpr expr) throws CompileError { ASTree left = expr.oprand1(); ASTree right = expr.oprand2(); if (right == null) { // this expression has been already type-checked. // see atBinExpr() above. left.accept(this); return null; } if (isPlusExpr(left)) { Expr newExpr = atPlusExpr((BinExpr)left); if (newExpr != null) { right.accept(this); exprType = CLASS; arrayDim = 0; className = "java/lang/StringBuffer"; return makeAppendCall(newExpr, right); } } else left.accept(this); int type1 = exprType; int dim1 = arrayDim; String cname = className; right.accept(this); if (isConstant(expr, '+', left, right)) return null; if ((type1 == CLASS && dim1 == 0 && jvmJavaLangString.equals(cname)) || (exprType == CLASS && arrayDim == 0 && jvmJavaLangString.equals(className))) { ASTList sbufClass = ASTList.make(new Symbol("java"), new Symbol("lang"), new Symbol("StringBuffer")); ASTree e = new NewExpr(sbufClass, null); exprType = CLASS; arrayDim = 0; className = "java/lang/StringBuffer"; return makeAppendCall(makeAppendCall(e, left), right); } else { computeBinExprType(expr, '+', type1); return null; } } private boolean isConstant(BinExpr expr, int op, ASTree left, ASTree right) throws CompileError { left = stripPlusExpr(left); right = stripPlusExpr(right); ASTree newExpr = null; if (left instanceof StringL && right instanceof StringL && op == '+') newExpr = new StringL(((StringL)left).get() + ((StringL)right).get()); else if (left instanceof IntConst) newExpr = ((IntConst)left).compute(op, right); else if (left instanceof DoubleConst) newExpr = ((DoubleConst)left).compute(op, right); if (newExpr == null) return false; // not a constant expression else { expr.setOperator('+'); expr.setOprand1(newExpr); expr.setOprand2(null); newExpr.accept(this); // for setting exprType, arrayDim, ... return true; } } /* CodeGen.atSwitchStmnt() also calls stripPlusExpr(). */ static ASTree stripPlusExpr(ASTree expr) { if (expr instanceof BinExpr) { BinExpr e = (BinExpr)expr; if (e.getOperator() == '+' && e.oprand2() == null) return e.getLeft(); } else if (expr instanceof Expr) { // note: BinExpr extends Expr. Expr e = (Expr)expr; int op = e.getOperator(); if (op == MEMBER) { ASTree cexpr = getConstantFieldValue((Member)e.oprand2()); if (cexpr != null) return cexpr; } else if (op == '+' && e.getRight() == null) return e.getLeft(); } else if (expr instanceof Member) { ASTree cexpr = getConstantFieldValue((Member)expr); if (cexpr != null) return cexpr; } return expr; } /** * If MEM is a static final field, this method returns a constant * expression representing the value of that field. */ private static ASTree getConstantFieldValue(Member mem) { return getConstantFieldValue(mem.getField()); } public static ASTree getConstantFieldValue(CtField f) { if (f == null) return null; Object value = f.getConstantValue(); if (value == null) return null; if (value instanceof String) return new StringL((String)value); else if (value instanceof Double || value instanceof Float) { int token = (value instanceof Double) ? DoubleConstant : FloatConstant; return new DoubleConst(((Number)value).doubleValue(), token); } else if (value instanceof Number) { int token = (value instanceof Long) ? LongConstant : IntConstant; return new IntConst(((Number)value).longValue(), token); } else if (value instanceof Boolean) return new Keyword(((Boolean)value).booleanValue() ? TokenId.TRUE : TokenId.FALSE); else return null; } private static boolean isPlusExpr(ASTree expr) { if (expr instanceof BinExpr) { BinExpr bexpr = (BinExpr)expr; int token = bexpr.getOperator(); return token == '+'; } return false; } private static Expr makeAppendCall(ASTree target, ASTree arg) { return CallExpr.makeCall(Expr.make('.', target, new Member("append")), new ASTList(arg)); } private void computeBinExprType(BinExpr expr, int token, int type1) throws CompileError { // arrayDim should be 0. int type2 = exprType; if (token == LSHIFT || token == RSHIFT || token == ARSHIFT) exprType = type1; else insertCast(expr, type1, type2); if (CodeGen.isP_INT(exprType)) exprType = INT; // type1 may be BYTE, ... } private void booleanExpr(ASTree expr) throws CompileError { int op = CodeGen.getCompOperator(expr); if (op == EQ) { // ==, !=, ... BinExpr bexpr = (BinExpr)expr; bexpr.oprand1().accept(this); int type1 = exprType; int dim1 = arrayDim; bexpr.oprand2().accept(this); if (dim1 == 0 && arrayDim == 0) insertCast(bexpr, type1, exprType); } else if (op == '!') ((Expr)expr).oprand1().accept(this); else if (op == ANDAND || op == OROR) { BinExpr bexpr = (BinExpr)expr; bexpr.oprand1().accept(this); bexpr.oprand2().accept(this); } else // others expr.accept(this); exprType = BOOLEAN; arrayDim = 0; } private void insertCast(BinExpr expr, int type1, int type2) throws CompileError { if (CodeGen.rightIsStrong(type1, type2)) expr.setLeft(new CastExpr(type2, 0, expr.oprand1())); else exprType = type1; } public void atCastExpr(CastExpr expr) throws CompileError { String cname = resolveClassName(expr.getClassName()); expr.getOprand().accept(this); exprType = expr.getType(); arrayDim = expr.getArrayDim(); className = cname; } public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError { expr.getOprand().accept(this); exprType = BOOLEAN; arrayDim = 0; } public void atExpr(Expr expr) throws CompileError { // array access, member access, // (unary) +, (unary) -, ++, --, !, ~ int token = expr.getOperator(); ASTree oprand = expr.oprand1(); if (token == '.') { String member = ((Symbol)expr.oprand2()).get(); if (member.equals("length")) atArrayLength(expr); else if (member.equals("class")) atClassObject(expr); // .class else atFieldRead(expr); } else if (token == MEMBER) { // field read String member = ((Symbol)expr.oprand2()).get(); if (member.equals("class")) atClassObject(expr); // .class else atFieldRead(expr); } else if (token == ARRAY) atArrayRead(oprand, expr.oprand2()); else if (token == PLUSPLUS || token == MINUSMINUS) atPlusPlus(token, oprand, expr); else if (token == '!') booleanExpr(expr); else if (token == CALL) // method call fatal(); else { oprand.accept(this); if (!isConstant(expr, token, oprand)) if (token == '-' || token == '~') if (CodeGen.isP_INT(exprType)) exprType = INT; // type may be BYTE, ... } } private boolean isConstant(Expr expr, int op, ASTree oprand) { oprand = stripPlusExpr(oprand); if (oprand instanceof IntConst) { IntConst c = (IntConst)oprand; long v = c.get(); if (op == '-') v = -v; else if (op == '~') v = ~v; else return false; c.set(v); } else if (oprand instanceof DoubleConst) { DoubleConst c = (DoubleConst)oprand; if (op == '-') c.set(-c.get()); else return false; } else return false; expr.setOperator('+'); return true; } public void atCallExpr(CallExpr expr) throws CompileError { String mname = null; CtClass targetClass = null; ASTree method = expr.oprand1(); ASTList args = (ASTList)expr.oprand2(); if (method instanceof Member) { mname = ((Member)method).get(); targetClass = thisClass; } else if (method instanceof Keyword) { // constructor mname = MethodInfo.nameInit; // if (((Keyword)method).get() == SUPER) targetClass = MemberResolver.getSuperclass(thisClass); else targetClass = thisClass; } else if (method instanceof Expr) { Expr e = (Expr)method; mname = ((Symbol)e.oprand2()).get(); int op = e.getOperator(); if (op == MEMBER) // static method targetClass = resolver.lookupClass(((Symbol)e.oprand1()).get(), false); else if (op == '.') { ASTree target = e.oprand1(); 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 e.setOperator(MEMBER); e.setOprand1(new Symbol(MemberResolver.jvmToJavaName( className))); } if (arrayDim > 0) targetClass = resolver.lookupClass(javaLangObject, true); else if (exprType == CLASS /* && arrayDim == 0 */) targetClass = resolver.lookupClassByJvmName(className); else badMethod(); } else badMethod(); } else fatal(); MemberResolver.Method minfo = atMethodCallCore(targetClass, mname, args); expr.setMethod(minfo); } private static void badMethod() throws CompileError { throw new CompileError("bad method"); } /** * @return a pair of the class declaring the invoked method * and the MethodInfo of that method. Never null. */ public MemberResolver.Method atMethodCallCore(CtClass targetClass, String mname, ASTList args) throws CompileError { int nargs = getMethodArgsLength(args); int[] types = new int[nargs]; int[] dims = new int[nargs]; String[] cnames = new String[nargs]; atMethodArgs(args, types, dims, cnames); MemberResolver.Method found = resolver.lookupMethod(targetClass, thisClass, thisMethod, mname, types, dims, cnames); if (found == null) { String clazz = targetClass.getName(); String signature = argTypesToString(types, dims, cnames); String msg; if (mname.equals(MethodInfo.nameInit)) msg = "cannot find constructor " + clazz + signature; else msg = mname + signature + " not found in " + clazz; throw new CompileError(msg); } String desc = found.info.getDescriptor(); setReturnType(desc); return found; } public int getMethodArgsLength(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(); } } void setReturnType(String desc) 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 = MemberResolver.descToType(c); className = null; } } private void atFieldRead(ASTree expr) throws CompileError { atFieldRead(fieldAccess(expr)); } private void atFieldRead(CtField f) throws CompileError { FieldInfo finfo = f.getFieldInfo2(); String type = finfo.getDescriptor(); int i = 0; int dim = 0; char c = type.charAt(i); while (c == '[') { ++dim; c = type.charAt(++i); } arrayDim = dim; exprType = MemberResolver.descToType(c); if (c == 'L') className = type.substring(i + 1, type.indexOf(';', i + 1)); else className = null; } /* if EXPR is to access a static field, fieldAccess() translates EXPR * into an expression using '#' (MEMBER). For example, it translates * java.lang.Integer.TYPE into java.lang.Integer#TYPE. This translation * speeds up type resolution by MemberCodeGen. */ protected CtField fieldAccess(ASTree expr) throws CompileError { if (expr instanceof Member) { Member mem = (Member)expr; String name = mem.get(); try { CtField f = thisClass.getField(name); if (Modifier.isStatic(f.getModifiers())) mem.setField(f); return f; } catch (NotFoundException e) { // EXPR might be part of a static member access? throw new NoFieldException(name, expr); } } else if (expr instanceof Expr) { Expr e = (Expr)expr; int op = e.getOperator(); if (op == MEMBER) { Member mem = (Member)e.oprand2(); CtField f = resolver.lookupField(((Symbol)e.oprand1()).get(), mem); mem.setField(f); return f; } else if (op == '.') { try { e.oprand1().accept(this); } catch (NoFieldException nfe) { if (nfe.getExpr() != e.oprand1()) throw nfe; /* EXPR should be a static field. * If EXPR might be part of a qualified class name, * lookupFieldByJvmName2() throws NoFieldException. */ return fieldAccess2(e, nfe.getField()); } CompileError err = null; try { if (exprType == CLASS && arrayDim == 0) return resolver.lookupFieldByJvmName(className, (Symbol)e.oprand2()); } catch (CompileError ce) { err = ce; } /* If a filed name is the same name as a package's, * a static member of a class in that package is not * visible. For example, * * class Foo { * int javassist; * } * * It is impossible to add the following method: * * String m() { return javassist.CtClass.intType.toString(); } * * because javassist is a field name. However, this is * often inconvenient, this compiler allows it. The following * code is for that. */ ASTree oprnd1 = e.oprand1(); if (oprnd1 instanceof Symbol) return fieldAccess2(e, ((Symbol)oprnd1).get()); if (err != null) throw err; } } throw new CompileError("bad filed access"); } private CtField fieldAccess2(Expr e, String jvmClassName) throws CompileError { Member fname = (Member)e.oprand2(); CtField f = resolver.lookupFieldByJvmName2(jvmClassName, fname, e); e.setOperator(MEMBER); e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(jvmClassName))); fname.setField(f); return f; } public void atClassObject(Expr expr) throws CompileError { exprType = CLASS; arrayDim = 0; className =jvmJavaLangClass; } public void atArrayLength(Expr expr) throws CompileError { expr.oprand1().accept(this); exprType = INT; arrayDim = 0; } public void atArrayRead(ASTree array, ASTree index) throws CompileError { array.accept(this); int type = exprType; int dim = arrayDim; String cname = className; index.accept(this); exprType = type; arrayDim = dim - 1; className = cname; } private void atPlusPlus(int token, ASTree oprand, Expr expr) throws CompileError { boolean isPost = oprand == null; // ++i or i++? if (isPost) oprand = expr.oprand2(); if (oprand instanceof Variable) { Declarator d = ((Variable)oprand).getDeclarator(); exprType = d.getType(); arrayDim = d.getArrayDim(); } else { if (oprand instanceof Expr) { Expr e = (Expr)oprand; if (e.getOperator() == ARRAY) { atArrayRead(e.oprand1(), e.oprand2()); // arrayDim should be 0. int t = exprType; if (t == INT || t == BYTE || t == CHAR || t == SHORT) exprType = INT; return; } } atFieldPlusPlus(oprand); } } protected void atFieldPlusPlus(ASTree oprand) throws CompileError { CtField f = fieldAccess(oprand); atFieldRead(f); int t = exprType; if (t == INT || t == BYTE || t == CHAR || t == SHORT) exprType = INT; } public void atMember(Member mem) throws CompileError { atFieldRead(mem); } public void atVariable(Variable v) throws CompileError { Declarator d = v.getDeclarator(); exprType = d.getType(); arrayDim = d.getArrayDim(); className = d.getClassName(); } public void atKeyword(Keyword k) throws CompileError { arrayDim = 0; int token = k.get(); switch (token) { case TRUE : case FALSE : exprType = BOOLEAN; break; case NULL : exprType = NULL; break; case THIS : case SUPER : 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 = jvmJavaLangString; } public void atIntConst(IntConst i) throws CompileError { arrayDim = 0; int type = i.getType(); if (type == IntConstant || type == CharConstant) exprType = (type == IntConstant ? INT : CHAR); else exprType = LONG; } public void atDoubleConst(DoubleConst d) throws CompileError { arrayDim = 0; if (d.getType() == DoubleConstant) exprType = DOUBLE; else exprType = FLOAT; } } javassist-3.12.1.ga/src/main/javassist/compiler/Parser.java0000644000175000017500000012110610630701321023556 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import javassist.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 == SYNCHRONIZED) return parseSynchronized(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 ASTree expr = parseParExpression(tbl); 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 ASTree expr = parseParExpression(tbl); 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 { int t = lex.get(); // SWITCH ASTree expr = parseParExpression(tbl); Stmnt body = parseSwitchBlock(tbl); return new Stmnt(t, expr, body); } private Stmnt parseSwitchBlock(SymbolTable tbl) throws CompileError { if (lex.get() != '{') throw new SyntaxError(lex); SymbolTable tbl2 = new SymbolTable(tbl); Stmnt s = parseStmntOrCase(tbl2); if (s == null) throw new CompileError("empty switch block", lex); int op = s.getOperator(); if (op != CASE && op != DEFAULT) throw new CompileError("no case or default in a switch block", lex); Stmnt body = new Stmnt(BLOCK, s); while (lex.lookAhead() != '}') { Stmnt s2 = parseStmntOrCase(tbl2); if (s2 != null) { int op2 = s2.getOperator(); if (op2 == CASE || op2 == DEFAULT) { body = (Stmnt)ASTList.concat(body, new Stmnt(BLOCK, s2)); s = s2; } else s = (Stmnt)ASTList.concat(s, new Stmnt(BLOCK, s2)); } } lex.get(); // '}' return body; } private Stmnt parseStmntOrCase(SymbolTable tbl) throws CompileError { int t = lex.lookAhead(); if (t != CASE && t != DEFAULT) return parseStatement(tbl); lex.get(); Stmnt s; if (t == CASE) s = new Stmnt(t, parseExpression(tbl)); else s = new Stmnt(DEFAULT); if (lex.get() != ':') throw new CompileError(": is missing", lex); return s; } /* synchronized.statement : * SYNCHRONIZED "(" expression ")" block.statement */ private Stmnt parseSynchronized(SymbolTable tbl) throws CompileError { int t = lex.get(); // SYNCHRONIZED if (lex.get() != '(') throw new SyntaxError(lex); ASTree expr = parseExpression(tbl); if (lex.get() != ')') throw new SyntaxError(lex); Stmnt body = parseBlock(tbl); return new Stmnt(t, expr, body); } /* 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 ArrayInit parseArrayInitializer(SymbolTable tbl) throws CompileError { lex.get(); // '{' ASTree expr = parseExpression(tbl); ArrayInit init = new ArrayInit(expr); while (lex.lookAhead() == ',') { lex.get(); expr = parseExpression(tbl); ASTList.append(init, expr); } if (lex.get() != '}') throw new SyntaxError(lex); return init; } /* par.expression : '(' expression ')' */ private ASTree parseParExpression(SymbolTable tbl) throws CompileError { if (lex.get() != '(') throw new SyntaxError(lex); ASTree expr = parseExpression(tbl); if (lex.get() != ')') throw new SyntaxError(lex); return expr; } /* 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(); if (t == '-') { int t2 = lex.lookAhead(); switch (t2) { case LongConstant : case IntConstant : case CharConstant : lex.get(); return new IntConst(-lex.getLong(), t2); case DoubleConstant : case FloatConstant : lex.get(); return new DoubleConst(-lex.getDouble(), t2); default : break; } } return Expr.make(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. Either "(int.class)" or "(String[].class)" is a not cast expression. */ private ASTree parseCast(SymbolTable tbl) throws CompileError { int t = lex.lookAhead(1); if (isBuiltinType(t) && nextIsBuiltinCast()) { 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 nextIsBuiltinCast() { int t; int i = 2; while ((t = lex.lookAhead(i++)) == '[') if (lex.lookAhead(i++) != ']') return false; return lex.lookAhead(i - 1) == ')'; } 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 ( "[" "]" )* "." CLASS * | 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 * can 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) { // see also parseUnaryExpr() 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 '[' : if (lex.lookAhead(1) == ']') { int dim = parseArrayDimension(); if (lex.get() != '.' || lex.get() != CLASS) throw new SyntaxError(lex); expr = parseDotClass(expr, dim); } else { 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(); t = lex.get(); if (t == CLASS) { expr = parseDotClass(expr, 0); } else if (t == Identifier) { str = lex.getString(); expr = Expr.make('.', expr, new Member(str)); } else throw new CompileError("missing member name", lex); break; case '#' : lex.get(); t = lex.get(); if (t != Identifier) throw new CompileError("missing static member name", lex); str = lex.getString(); expr = Expr.make(MEMBER, new Symbol(toClassName(expr)), new Member(str)); break; default : return expr; } } } /* Parse a .class expression on a class type. For example, * String.class => ('.' "String" "class") * String[].class => ('.' "[LString;" "class") */ private ASTree parseDotClass(ASTree className, int dim) throws CompileError { String cname = toClassName(className); if (dim > 0) { StringBuffer sbuf = new StringBuffer(); while (dim-- > 0) sbuf.append('['); sbuf.append('L').append(cname.replace('.', '/')).append(';'); cname = sbuf.toString(); } return Expr.make('.', new Symbol(cname), new Member("class")); } /* Parses a .class expression on a built-in type. For example, * int.class => ('#' "java.lang.Integer" "TYPE") * int[].class => ('.' "[I", "class") */ private ASTree parseDotClass(int builtinType, int dim) throws CompileError { if (dim > 0) { String cname = CodeGen.toJvmTypeName(builtinType, dim); return Expr.make('.', new Symbol(cname), new Member("class")); } else { String cname; switch(builtinType) { case BOOLEAN : cname = "java.lang.Boolean"; break; case BYTE : cname = "java.lang.Byte"; break; case CHAR : cname = "java.lang.Character"; break; case SHORT : cname = "java.lang.Short"; break; case INT : cname = "java.lang.Integer"; break; case LONG : cname = "java.lang.Long"; break; case FLOAT : cname = "java.lang.Float"; break; case DOUBLE : cname = "java.lang.Double"; break; case VOID : cname = "java.lang.Void"; break; default : throw new CompileError("invalid builtin type: " + builtinType); } return Expr.make(MEMBER, new Symbol(cname), new Member("TYPE")); } } /* 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 CallExpr.makeCall(expr, parseArgumentList(tbl)); } private String toClassName(ASTree name) throws CompileError { StringBuffer sbuf = new StringBuffer(); toClassName(name, sbuf); return sbuf.toString(); } private void toClassName(ASTree name, StringBuffer sbuf) throws CompileError { if (name instanceof Symbol) { sbuf.append(((Symbol)name).get()); return; } else if (name instanceof Expr) { Expr expr = (Expr)name; if (expr.getOperator() == '.') { toClassName(expr.oprand1(), sbuf); sbuf.append('.'); toClassName(expr.oprand2(), sbuf); return; } } throw new CompileError("bad static member access", lex); } /* primary.expr : THIS | SUPER | TRUE | FALSE | NULL * | StringL * | Identifier * | NEW new.expr * | "(" expression ")" * | builtin.type ( "[" "]" )* "." CLASS * * 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 : if (isBuiltinType(t) || t == VOID) { int dim = parseArrayDimension(); if (lex.get() == '.' && lex.get() == CLASS) return parseDotClass(t, dim); } 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 { ArrayInit 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; } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/0000755000175000017500000000000011637463276022273 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/compiler/ast/CastExpr.java0000644000175000017500000000331310630701321024641 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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 void setOprand(ASTree t) { getRight().setLeft(t); } public String getTag() { return "cast:" + castType + ":" + arrayDim; } public void accept(Visitor v) throws CompileError { v.atCastExpr(this); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/ASTree.java0000644000175000017500000000356310630701321024242 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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 getLeft() * and getRight() 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 * atXXX() on the given visitor, where * XXX 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 * toString(). */ protected String getTag() { String name = getClass().getName(); return name.substring(name.lastIndexOf('.') + 1); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/Variable.java0000644000175000017500000000223310630701321024635 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/Expr.java0000644000175000017500000000453310630701321024033 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.ast; import javassist.compiler.TokenId; import javassist.compiler.CompileError; /** * Expression. */ public class Expr extends ASTList implements TokenId { /* operator must be either of: * (unary) +, (unary) -, ++, --, !, ~, * ARRAY, . (dot), MEMBER (static member access). * Otherwise, the object should be an instance of a subclass. */ protected int operatorId; Expr(int op, ASTree _head, ASTList _tail) { super(_head, _tail); operatorId = op; } 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 static Expr make(int op, ASTree oprand1) { return new Expr(op, oprand1); } public int getOperator() { return operatorId; } public void setOperator(int op) { operatorId = op; } public ASTree oprand1() { return getLeft(); } public void setOprand1(ASTree expr) { setLeft(expr); } public ASTree oprand2() { return getRight().getLeft(); } public void setOprand2(ASTree expr) { getRight().setLeft(expr); } 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(); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/FieldDecl.java0000644000175000017500000000223210630701321024722 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/Member.java0000644000175000017500000000233510630701321024322 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.ast; import javassist.compiler.CompileError; import javassist.CtField; /** * Member name. */ public class Member extends Symbol { // cache maintained by fieldAccess() in TypeChecker. // this is used to obtain the value of a static final field. private CtField field; public Member(String name) { super(name); field = null; } public void setField(CtField f) { field = f; } public CtField getField() { return field; } public void accept(Visitor v) throws CompileError { v.atMember(this); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/DoubleConst.java0000644000175000017500000000535110630701321025335 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.ast; import javassist.compiler.CompileError; import javassist.compiler.TokenId; /** * 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; } public void set(double v) { value = v; } /* 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); } public ASTree compute(int op, ASTree right) { if (right instanceof IntConst) return compute0(op, (IntConst)right); else if (right instanceof DoubleConst) return compute0(op, (DoubleConst)right); else return null; } private DoubleConst compute0(int op, DoubleConst right) { int newType; if (this.type == TokenId.DoubleConstant || right.type == TokenId.DoubleConstant) newType = TokenId.DoubleConstant; else newType = TokenId.FloatConstant; return compute(op, this.value, right.value, newType); } private DoubleConst compute0(int op, IntConst right) { return compute(op, this.value, (double)right.value, this.type); } private static DoubleConst compute(int op, double value1, double value2, int newType) { double newValue; switch (op) { case '+' : newValue = value1 + value2; break; case '-' : newValue = value1 - value2; break; case '*' : newValue = value1 * value2; break; case '/' : newValue = value1 / value2; break; case '%' : newValue = value1 % value2; break; default : return null; } return new DoubleConst(newValue, newType); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/NewExpr.java0000644000175000017500000000442710630701321024507 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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, ArrayInit init) { super(null, new ASTList(arraySize)); newArray = true; arrayType = type; if (init != null) append(this, init); } public static NewExpr makeObjectArray(ASTList className, ASTList arraySize, ArrayInit 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 ArrayInit getInitializer() { ASTree t = getRight().getRight(); if (t == null) return null; else return (ArrayInit)t.getLeft(); } public void accept(Visitor v) throws CompileError { v.atNewExpr(this); } protected String getTag() { return newArray ? "new[]" : "new"; } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/MethodDecl.java0000644000175000017500000000276010630701321025125 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.ast; import javassist.compiler.CompileError; public class MethodDecl extends ASTList { public static final String initName = ""; 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); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/StringL.java0000644000175000017500000000206510630701321024475 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/Declarator.java0000644000175000017500000000716010630701321025174 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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); 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(); astToClassName(sbuf, name, sep); return sbuf.toString(); } private static void astToClassName(StringBuffer sbuf, ASTList name, char sep) { for (;;) { ASTree h = name.head(); if (h instanceof Symbol) sbuf.append(((Symbol)h).get()); else if (h instanceof ASTList) astToClassName(sbuf, (ASTList)h, sep); name = name.tail(); if (name == null) break; sbuf.append(sep); } } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/InstanceOfExpr.java0000644000175000017500000000231710630701321026003 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/Symbol.java0000644000175000017500000000207410630701321024360 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/CondExpr.java0000644000175000017500000000261510630701321024636 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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 void setCond(ASTree t) { setHead(t); } public ASTree thenExpr() { return tail().head(); } public void setThen(ASTree t) { tail().setHead(t); } public ASTree elseExpr() { return tail().tail().head(); } public void setElse(ASTree t) { tail().tail().setHead(t); } public String getTag() { return "?:"; } public void accept(Visitor v) throws CompileError { v.atCondExpr(this); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/CallExpr.java0000644000175000017500000000267110630701321024630 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.ast; import javassist.compiler.CompileError; import javassist.compiler.TokenId; import javassist.compiler.MemberResolver; /** * Method call expression. */ public class CallExpr extends Expr { private MemberResolver.Method method; // cached result of lookupMethod() private CallExpr(ASTree _head, ASTList _tail) { super(TokenId.CALL, _head, _tail); method = null; } public void setMethod(MemberResolver.Method m) { method = m; } public MemberResolver.Method getMethod() { return method; } public static CallExpr makeCall(ASTree target, ASTree args) { return new CallExpr(target, new ASTList(args)); } public void accept(Visitor v) throws CompileError { v.atCallExpr(this); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/Visitor.java0000644000175000017500000000423210630701321024550 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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 atCallExpr(CallExpr 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 {} public void atArrayInit(ArrayInit n) throws CompileError {} } javassist-3.12.1.ga/src/main/javassist/compiler/ast/IntConst.java0000644000175000017500000000761310630701321024660 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.ast; import javassist.compiler.CompileError; import javassist.compiler.TokenId; /** * 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; } public void set(long v) { value = v; } /* 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); } public ASTree compute(int op, ASTree right) { if (right instanceof IntConst) return compute0(op, (IntConst)right); else if (right instanceof DoubleConst) return compute0(op, (DoubleConst)right); else return null; } private IntConst compute0(int op, IntConst right) { int type1 = this.type; int type2 = right.type; int newType; if (type1 == TokenId.LongConstant || type2 == TokenId.LongConstant) newType = TokenId.LongConstant; else if (type1 == TokenId.CharConstant && type2 == TokenId.CharConstant) newType = TokenId.CharConstant; else newType = TokenId.IntConstant; long value1 = this.value; long value2 = right.value; long newValue; switch (op) { case '+' : newValue = value1 + value2; break; case '-' : newValue = value1 - value2; break; case '*' : newValue = value1 * value2; break; case '/' : newValue = value1 / value2; break; case '%' : newValue = value1 % value2; break; case '|' : newValue = value1 | value2; break; case '^' : newValue = value1 ^ value2; break; case '&' : newValue = value1 & value2; break; case TokenId.LSHIFT : newValue = value << (int)value2; newType = type1; break; case TokenId.RSHIFT : newValue = value >> (int)value2; newType = type1; break; case TokenId.ARSHIFT : newValue = value >>> (int)value2; newType = type1; break; default : return null; } return new IntConst(newValue, newType); } private DoubleConst compute0(int op, DoubleConst right) { double value1 = (double)this.value; double value2 = right.value; double newValue; switch (op) { case '+' : newValue = value1 + value2; break; case '-' : newValue = value1 - value2; break; case '*' : newValue = value1 * value2; break; case '/' : newValue = value1 / value2; break; case '%' : newValue = value1 % value2; break; default : return null; } return new DoubleConst(newValue, right.type); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/BinExpr.java0000644000175000017500000000251110630701321024456 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.ast; import javassist.compiler.CompileError; /** * Binary expression. * *

    If the operator is +, the right node might be null. * See TypeChecker.atBinExpr(). */ public class BinExpr extends Expr { /* operator must be either of: * ||, &&, |, ^, &, ==, !=, <=, >=, <, >, * <<, >>, >>>, +, -, *, /, % */ private 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); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/ArrayInit.java0000644000175000017500000000204310630701321025011 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.ast; import javassist.compiler.CompileError; /** * Array initializer such as { 1, 2, 3 }. */ public class ArrayInit extends ASTList { public ArrayInit(ASTree firstElement) { super(firstElement); } public void accept(Visitor v) throws CompileError { v.atArrayInit(this); } public String getTag() { return "array"; } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/AssignExpr.java0000644000175000017500000000243710630701321025201 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.ast; import javassist.compiler.CompileError; /** * Assignment expression. */ public class AssignExpr extends Expr { /* operator must be either of: * =, %=, &=, *=, +=, -=, /=, ^=, |=, <<=, >>=, >>>= */ private 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); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/Pair.java0000644000175000017500000000317110630701321024005 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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("( "); sbuf.append(left == null ? "" : left.toString()); sbuf.append(" . "); sbuf.append(right == 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; } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/Stmnt.java0000644000175000017500000000333210630701321024216 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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; } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/Keyword.java0000644000175000017500000000206310630701321024535 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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); } } javassist-3.12.1.ga/src/main/javassist/compiler/ast/ASTList.java0000644000175000017500000000750410630701321024401 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler.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 ? "" : 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 newObj for oldObj 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; } } } javassist-3.12.1.ga/src/main/javassist/compiler/NoFieldException.java0000644000175000017500000000241410630701321025521 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import javassist.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; } } javassist-3.12.1.ga/src/main/javassist/compiler/CompileError.java0000644000175000017500000000261310630701321024725 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import javassist.CannotCompileException; import javassist.NotFoundException; 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 CompileError(CannotCompileException e) { this(e.getReason()); } public CompileError(NotFoundException e) { this("cannot find " + e.getMessage()); } public Lex getLex() { return lex; } public String getMessage() { return reason; } public String toString() { return "compile error: " + reason; } } javassist-3.12.1.ga/src/main/javassist/compiler/ProceedHandler.java0000644000175000017500000000223010630701321025175 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import javassist.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; void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError; } javassist-3.12.1.ga/src/main/javassist/compiler/SymbolTable.java0000644000175000017500000000250410630701321024537 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import 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); } } javassist-3.12.1.ga/src/main/javassist/compiler/TokenId.java0000644000175000017500000000702310630701321023660 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; 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; } javassist-3.12.1.ga/src/main/javassist/scopedpool/0000755000175000017500000000000011637463425022035 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/scopedpool/ScopedClassPoolRepositoryImpl.java0000644000175000017500000001303110630701321030633 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.scopedpool; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.WeakHashMap; import javassist.ClassPool; import javassist.LoaderClassPath; /** * An implementation of ScopedClassPoolRepository. * It is an singleton. * * @author Kabir Khan * @version $Revision: 1.4 $ */ public class ScopedClassPoolRepositoryImpl implements ScopedClassPoolRepository { /** The instance */ private static final ScopedClassPoolRepositoryImpl instance = new ScopedClassPoolRepositoryImpl(); /** Whether to prune */ private boolean prune = true; /** Whether to prune when added to the classpool's cache */ boolean pruneWhenCached; /** The registered classloaders */ protected Map registeredCLs = Collections .synchronizedMap(new WeakHashMap()); /** The default class pool */ protected ClassPool classpool; /** The factory for creating class pools */ protected ScopedClassPoolFactory factory = new ScopedClassPoolFactoryImpl(); /** * Get the instance. * * @return the instance. */ public static ScopedClassPoolRepository getInstance() { return instance; } /** * Singleton. */ private ScopedClassPoolRepositoryImpl() { classpool = ClassPool.getDefault(); // FIXME This doesn't look correct ClassLoader cl = Thread.currentThread().getContextClassLoader(); classpool.insertClassPath(new LoaderClassPath(cl)); } /** * Returns the value of the prune attribute. * * @return the prune. */ public boolean isPrune() { return prune; } /** * Set the prune attribute. * * @param prune a new value. */ public void setPrune(boolean prune) { this.prune = prune; } /** * Create a scoped classpool. * * @param cl the classloader. * @param src the original classpool. * @return the classpool */ public ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src) { return factory.create(cl, src, this); } public ClassPool findClassPool(ClassLoader cl) { if (cl == null) return registerClassLoader(ClassLoader.getSystemClassLoader()); return registerClassLoader(cl); } /** * Register a classloader. * * @param ucl the classloader. * @return the classpool */ public ClassPool registerClassLoader(ClassLoader ucl) { synchronized (registeredCLs) { // FIXME: Probably want to take this method out later // so that AOP framework can be independent of JMX // This is in here so that we can remove a UCL from the ClassPool as // a // ClassPool.classpath if (registeredCLs.containsKey(ucl)) { return (ClassPool)registeredCLs.get(ucl); } ScopedClassPool pool = createScopedClassPool(ucl, classpool); registeredCLs.put(ucl, pool); return pool; } } /** * Get the registered classloaders. */ public Map getRegisteredCLs() { clearUnregisteredClassLoaders(); return registeredCLs; } /** * This method will check to see if a register classloader has been * undeployed (as in JBoss) */ public void clearUnregisteredClassLoaders() { ArrayList toUnregister = null; synchronized (registeredCLs) { Iterator it = registeredCLs.values().iterator(); while (it.hasNext()) { ScopedClassPool pool = (ScopedClassPool)it.next(); if (pool.isUnloadedClassLoader()) { it.remove(); ClassLoader cl = pool.getClassLoader(); if (cl != null) { if (toUnregister == null) { toUnregister = new ArrayList(); } toUnregister.add(cl); } } } if (toUnregister != null) { for (int i = 0; i < toUnregister.size(); i++) { unregisterClassLoader((ClassLoader)toUnregister.get(i)); } } } } public void unregisterClassLoader(ClassLoader cl) { synchronized (registeredCLs) { ScopedClassPool pool = (ScopedClassPool)registeredCLs.remove(cl); if (pool != null) pool.close(); } } public void insertDelegate(ScopedClassPoolRepository delegate) { // Noop - this is the end } public void setClassPoolFactory(ScopedClassPoolFactory factory) { this.factory = factory; } public ScopedClassPoolFactory getClassPoolFactory() { return factory; } } javassist-3.12.1.ga/src/main/javassist/scopedpool/ScopedClassPoolFactoryImpl.java0000644000175000017500000000262410630701321030071 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.scopedpool; import javassist.ClassPool; /** * An implementation of factory. * * @author Kabir Khan * @version $Revision: 1.5 $ */ public class ScopedClassPoolFactoryImpl implements ScopedClassPoolFactory { /** * Makes an instance. */ public ScopedClassPool create(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository) { return new ScopedClassPool(cl, src, repository, false); } /** * Makes an instance. */ public ScopedClassPool create(ClassPool src, ScopedClassPoolRepository repository) { return new ScopedClassPool(null, src, repository, true); } } javassist-3.12.1.ga/src/main/javassist/scopedpool/ScopedClassPool.java0000644000175000017500000002201410630701321025712 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.scopedpool; import java.lang.ref.WeakReference; import java.security.ProtectionDomain; import java.util.Iterator; import java.util.Map; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.LoaderClassPath; import javassist.NotFoundException; /** * A scoped class pool. * * @author Bill Burke * @author Adrian Brock * @author Kabir Khan * @version $Revision: 1.8 $ */ public class ScopedClassPool extends ClassPool { protected ScopedClassPoolRepository repository; protected WeakReference classLoader; protected LoaderClassPath classPath; protected SoftValueHashMap softcache = new SoftValueHashMap(); boolean isBootstrapCl = true; static { ClassPool.doPruning = false; ClassPool.releaseUnmodifiedClassFile = false; } /** * Create a new ScopedClassPool. * * @param cl * the classloader * @param src * the original class pool * @param repository * the repository *@deprecated */ protected ScopedClassPool(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository) { this(cl, src, repository, false); } /** * Create a new ScopedClassPool. * * @param cl * the classloader * @param src * the original class pool * @param repository * the repository * @param isTemp * Whether this is a temporary pool used to resolve references */ protected ScopedClassPool(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository, boolean isTemp) { super(src); this.repository = repository; this.classLoader = new WeakReference(cl); if (cl != null) { classPath = new LoaderClassPath(cl); this.insertClassPath(classPath); } childFirstLookup = true; if (!isTemp && cl == null) { isBootstrapCl = true; } } /** * Get the class loader * * @return the class loader */ public ClassLoader getClassLoader() { ClassLoader cl = getClassLoader0(); if (cl == null && !isBootstrapCl) { throw new IllegalStateException( "ClassLoader has been garbage collected"); } return cl; } protected ClassLoader getClassLoader0() { return (ClassLoader)classLoader.get(); } /** * Close the class pool */ public void close() { this.removeClassPath(classPath); classPath.close(); classes.clear(); softcache.clear(); } /** * Flush a class * * @param classname * the class to flush */ public synchronized void flushClass(String classname) { classes.remove(classname); softcache.remove(classname); } /** * Soften a class * * @param clazz * the class */ public synchronized void soften(CtClass clazz) { if (repository.isPrune()) clazz.prune(); classes.remove(clazz.getName()); softcache.put(clazz.getName(), clazz); } /** * Whether the classloader is loader * * @return false always */ public boolean isUnloadedClassLoader() { return false; } /** * Get the cached class * * @param classname * the class name * @return the class */ protected CtClass getCached(String classname) { CtClass clazz = getCachedLocally(classname); if (clazz == null) { boolean isLocal = false; ClassLoader dcl = getClassLoader0(); if (dcl != null) { final int lastIndex = classname.lastIndexOf('$'); String classResourceName = null; if (lastIndex < 0) { classResourceName = classname.replaceAll("[\\.]", "/") + ".class"; } else { classResourceName = classname.substring(0, lastIndex) .replaceAll("[\\.]", "/") + classname.substring(lastIndex) + ".class"; } isLocal = dcl.getResource(classResourceName) != null; } if (!isLocal) { Map registeredCLs = repository.getRegisteredCLs(); synchronized (registeredCLs) { Iterator it = registeredCLs.values().iterator(); while (it.hasNext()) { ScopedClassPool pool = (ScopedClassPool)it.next(); if (pool.isUnloadedClassLoader()) { repository.unregisterClassLoader(pool .getClassLoader()); continue; } clazz = pool.getCachedLocally(classname); if (clazz != null) { return clazz; } } } } } // *NOTE* NEED TO TEST WHEN SUPERCLASS IS IN ANOTHER UCL!!!!!! return clazz; } /** * Cache a class * * @param classname * the class name * @param c * the ctClass * @param dynamic * whether the class is dynamically generated */ protected void cacheCtClass(String classname, CtClass c, boolean dynamic) { if (dynamic) { super.cacheCtClass(classname, c, dynamic); } else { if (repository.isPrune()) c.prune(); softcache.put(classname, c); } } /** * Lock a class into the cache * * @param c * the class */ public void lockInCache(CtClass c) { super.cacheCtClass(c.getName(), c, false); } /** * Whether the class is cached in this pooled * * @param classname * the class name * @return the cached class */ protected CtClass getCachedLocally(String classname) { CtClass cached = (CtClass)classes.get(classname); if (cached != null) return cached; synchronized (softcache) { return (CtClass)softcache.get(classname); } } /** * Get any local copy of the class * * @param classname * the class name * @return the class * @throws NotFoundException * when the class is not found */ public synchronized CtClass getLocally(String classname) throws NotFoundException { softcache.remove(classname); CtClass clazz = (CtClass)classes.get(classname); if (clazz == null) { clazz = createCtClass(classname, true); if (clazz == null) throw new NotFoundException(classname); super.cacheCtClass(classname, clazz, false); } return clazz; } /** * Convert a javassist class to a java class * * @param ct * the javassist class * @param loader * the loader * @throws CannotCompileException * for any error */ public Class toClass(CtClass ct, ClassLoader loader, ProtectionDomain domain) throws CannotCompileException { // We need to pass up the classloader stored in this pool, as the // default implementation uses the Thread context cl. // In the case of JSP's in Tomcat, // org.apache.jasper.servlet.JasperLoader will be stored here, while // it's parent // org.jboss.web.tomcat.tc5.WebCtxLoader$ENCLoader is used as the Thread // context cl. The invocation class needs to // be generated in the JasperLoader classloader since in the case of // method invocations, the package name will be // the same as for the class generated from the jsp, i.e. // org.apache.jsp. For classes belonging to org.apache.jsp, // JasperLoader does NOT delegate to its parent if it cannot find them. lockInCache(ct); return super.toClass(ct, getClassLoader0(), domain); } } javassist-3.12.1.ga/src/main/javassist/scopedpool/package.html0000644000175000017500000000016310457172101024301 0ustar twernertwerner

    A custom class pool for several JBoss products. It is not part of Javassist.

    javassist-3.12.1.ga/src/main/javassist/scopedpool/ScopedClassPoolFactory.java0000644000175000017500000000227610630701321027252 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.scopedpool; import javassist.ClassPool; /** * A factory interface. * * @author Kabir Khan * @version $Revision: 1.4 $ */ public interface ScopedClassPoolFactory { /** * Makes an instance. */ ScopedClassPool create(ClassLoader cl, ClassPool src, ScopedClassPoolRepository repository); /** * Makes an instance. */ ScopedClassPool create(ClassPool src, ScopedClassPoolRepository repository); } javassist-3.12.1.ga/src/main/javassist/scopedpool/SoftValueHashMap.java0000644000175000017500000001617310630701321026040 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.scopedpool; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.AbstractMap; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * This Map will remove entries when the value in the map has been cleaned from * garbage collection * * @version $Revision: 1.4 $ * @author Bill Burke */ public class SoftValueHashMap extends AbstractMap implements Map { private static class SoftValueRef extends SoftReference { public Object key; private SoftValueRef(Object key, Object val, ReferenceQueue q) { super(val, q); this.key = key; } private static SoftValueRef create(Object key, Object val, ReferenceQueue q) { if (val == null) return null; else return new SoftValueRef(key, val, q); } } /** * Returns a set of the mappings contained in this hash table. */ public Set entrySet() { processQueue(); return hash.entrySet(); } /* Hash table mapping WeakKeys to values */ private Map hash; /* Reference queue for cleared WeakKeys */ private ReferenceQueue queue = new ReferenceQueue(); /* * Remove all invalidated entries from the map, that is, remove all entries * whose values have been discarded. */ private void processQueue() { SoftValueRef ref; while ((ref = (SoftValueRef)queue.poll()) != null) { if (ref == (SoftValueRef)hash.get(ref.key)) { // only remove if it is the *exact* same WeakValueRef // hash.remove(ref.key); } } } /* -- Constructors -- */ /** * Constructs a new, empty WeakHashMap with the given initial * capacity and the given load factor. * * @param initialCapacity * The initial capacity of the WeakHashMap * * @param loadFactor * The load factor of the WeakHashMap * * @throws IllegalArgumentException * If the initial capacity is less than zero, or if the load * factor is nonpositive */ public SoftValueHashMap(int initialCapacity, float loadFactor) { hash = new HashMap(initialCapacity, loadFactor); } /** * Constructs a new, empty WeakHashMap with the given initial * capacity and the default load factor, which is 0.75. * * @param initialCapacity * The initial capacity of the WeakHashMap * * @throws IllegalArgumentException * If the initial capacity is less than zero */ public SoftValueHashMap(int initialCapacity) { hash = new HashMap(initialCapacity); } /** * Constructs a new, empty WeakHashMap with the default * initial capacity and the default load factor, which is 0.75. */ public SoftValueHashMap() { hash = new HashMap(); } /** * Constructs a new WeakHashMap with the same mappings as the * specified Map. The WeakHashMap is created with * an initial capacity of twice the number of mappings in the specified map * or 11 (whichever is greater), and a default load factor, which is * 0.75. * * @param t the map whose mappings are to be placed in this map. */ public SoftValueHashMap(Map t) { this(Math.max(2 * t.size(), 11), 0.75f); putAll(t); } /* -- Simple queries -- */ /** * Returns the number of key-value mappings in this map. Note: * In contrast with most implementations of the * Map interface, the time required by this operation is * linear in the size of the map. */ public int size() { processQueue(); return hash.size(); } /** * Returns true if this map contains no key-value mappings. */ public boolean isEmpty() { processQueue(); return hash.isEmpty(); } /** * Returns true if this map contains a mapping for the * specified key. * * @param key * The key whose presence in this map is to be tested. */ public boolean containsKey(Object key) { processQueue(); return hash.containsKey(key); } /* -- Lookup and modification operations -- */ /** * Returns the value to which this map maps the specified key. * If this map does not contain a value for this key, then return * null. * * @param key * The key whose associated value, if any, is to be returned. */ public Object get(Object key) { processQueue(); SoftReference ref = (SoftReference)hash.get(key); if (ref != null) return ref.get(); return null; } /** * Updates this map so that the given key maps to the given * value. If the map previously contained a mapping for * key then that mapping is replaced and the previous value * is returned. * * @param key * The key that is to be mapped to the given value * @param value * The value to which the given key is to be * mapped * * @return The previous value to which this key was mapped, or * null if if there was no mapping for the key */ public Object put(Object key, Object value) { processQueue(); Object rtn = hash.put(key, SoftValueRef.create(key, value, queue)); if (rtn != null) rtn = ((SoftReference)rtn).get(); return rtn; } /** * Removes the mapping for the given key from this map, if * present. * * @param key * The key whose mapping is to be removed. * * @return The value to which this key was mapped, or null if * there was no mapping for the key. */ public Object remove(Object key) { processQueue(); return hash.remove(key); } /** * Removes all mappings from this map. */ public void clear() { processQueue(); hash.clear(); } } javassist-3.12.1.ga/src/main/javassist/scopedpool/ScopedClassPoolRepository.java0000644000175000017500000000477410630701321030027 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.scopedpool; import java.util.Map; import javassist.ClassPool; /** * An interface to ScopedClassPoolRepositoryImpl. * * @author Kabir Khan * @version $Revision: 1.4 $ */ public interface ScopedClassPoolRepository { /** * Records a factory. */ void setClassPoolFactory(ScopedClassPoolFactory factory); /** * Obtains the recorded factory. */ ScopedClassPoolFactory getClassPoolFactory(); /** * Returns whether or not the class pool is pruned. * * @return the prune. */ boolean isPrune(); /** * Sets the prune flag. * * @param prune a new value. */ void setPrune(boolean prune); /** * Create a scoped classpool. * * @param cl the classloader. * @param src the original classpool. * @return the classpool. */ ScopedClassPool createScopedClassPool(ClassLoader cl, ClassPool src); /** * Finds a scoped classpool registered under the passed in classloader. * * @param cl the classloader. * @return the classpool. */ ClassPool findClassPool(ClassLoader cl); /** * Register a classloader. * * @param ucl the classloader. * @return the classpool. */ ClassPool registerClassLoader(ClassLoader ucl); /** * Get the registered classloaders. * * @return the registered classloaders. */ Map getRegisteredCLs(); /** * This method will check to see if a register classloader has been * undeployed (as in JBoss). */ void clearUnregisteredClassLoaders(); /** * Unregisters a classpool and unregisters its classloader. * * @param cl the classloader the pool is stored under. */ void unregisterClassLoader(ClassLoader cl); } javassist-3.12.1.ga/src/main/javassist/tools/0000755000175000017500000000000011637463254021026 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/tools/reflect/0000755000175000017500000000000011637463243022450 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/tools/reflect/Metaobject.java0000644000175000017500000001705710630701321025362 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.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. * *

    A Metaobject is created for * every object at the base level. A different reflective object is * associated with a different metaobject. * *

    The metaobject intercepts method calls * on the reflective object at the base-level. To change the behavior * of the method calls, a subclass of Metaobject * should be defined. * *

    To obtain a metaobject, calls _getMetaobject() * on a reflective object. For example, * *

      Metaobject m = ((Metalevel)reflectiveObject)._getMetaobject();
       * 
    * * @see javassist.tools.reflect.ClassMetaobject * @see javassist.tools.reflect.Metalevel */ public class Metaobject implements Serializable { protected ClassMetaobject classmetaobject; protected Metalevel baseobject; protected Method[] methods; /** * Constructs a Metaobject. 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 * self. */ public Metaobject(Object self, Object[] args) { baseobject = (Metalevel)self; classmetaobject = baseobject._getClass(); methods = classmetaobject.getReflectiveMethods(); } /** * Constructs a Metaobject 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.tools.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 identifier. */ 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 Class objects representing the * formal parameter types of the method specified * by identifier. */ public final Class[] getParameterTypes(int identifier) { return methods[identifier].getParameterTypes(); } /** * Returns a Class objects representing the * return type of the method specified by identifier. */ 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. * *

    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. * *

    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. * *

    Every subclass of this class should redefine this method. * *

    Note: this method is not invoked if the base-level method * is invoked by a constructor in the super class. For example, * *

      abstract class A {
           *   abstract void initialize();
           *   A() {
           *       initialize();    // not intercepted
           *   }
           * }
           *
           * class B extends A {
           *   void initialize() { System.out.println("initialize()"); }
           *   B() {
           *       super();
           *       initialize();    // intercepted
           *   }
           * }
    * *

    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); } } } javassist-3.12.1.ga/src/main/javassist/tools/reflect/ClassMetaobject.java0000644000175000017500000002777210770210317026362 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.reflect; import java.lang.reflect.*; import java.util.Arrays; import java.io.Serializable; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * A runtime class metaobject. * *

    A ClassMetaobject is created for every * class of reflective objects. It can be used to hold values * shared among the reflective objects of the same class. * *

    To obtain a class metaobject, calls _getClass() * on a reflective object. For example, * *

      ClassMetaobject cm = ((Metalevel)reflectiveObject)._getClass();
       * 
    * * @see javassist.tools.reflect.Metaobject * @see javassist.tools.reflect.Metalevel */ public class ClassMetaobject implements Serializable { /** * The base-level methods controlled by a metaobject * are renamed so that they begin with * methodPrefix "_m_". */ static final String methodPrefix = "_m_"; static final int methodPrefixLen = 3; private Class javaClass; private Constructor[] constructors; private Method[] methods; /** * Specifies how a java.lang.Class object is loaded. * *

    If true, it is loaded by: *

      Thread.currentThread().getContextClassLoader().loadClass()
    *

    If false, it is loaded by Class.forName(). * The default value is false. */ public static boolean useContextClassLoader = false; /** * Constructs a ClassMetaobject. * * @param params params[0] is the name of the class * of the reflective objects. */ public ClassMetaobject(String[] params) { try { javaClass = getClassObject(params[0]); } catch (ClassNotFoundException e) { throw new RuntimeException("not found: " + params[0] + ", useContextClassLoader: " + Boolean.toString(useContextClassLoader), e); } 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 java.lang.Class 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 obj 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 static fields of the base-level * class are read and the runtime system intercepts it. * This method simply returns the value of the field. * *

    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 static fields of the base-level * class are modified and the runtime system intercepts it. * This method simply sets the field to the given value. * *

    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 * methodPrefix "_m_" 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 static 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. * *

    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.getDeclaredMethods(); int n = allmethods.length; int[] index = new int[n]; int max = 0; for (int i = 0; i < n; ++i) { Method m = allmethods[i]; 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; } index[i] = ++k; if (k > max) max = k; } } methods = new Method[max]; for (int i = 0; i < n; ++i) if (index[i] > 0) methods[index[i] - 1] = allmethods[i]; return methods; } /** * Returns the java.lang.reflect.Method object representing * the method specified by identifier. * *

    Note that the actual method returned will be have an altered, * reflective name i.e. _m_2_... * * @param identifier the identifier index * given to trapMethodcall() etc. * @see Metaobject#trapMethodcall(int,Object[]) * @see #trapMethodcall(int,Object[]) */ public final Method getMethod(int identifier) { return getReflectiveMethods()[identifier]; } /** * Returns the name of the method specified * by identifier. */ 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 Class objects representing the * formal parameter types of the method specified * by identifier. */ public final Class[] getParameterTypes(int identifier) { return getReflectiveMethods()[identifier].getParameterTypes(); } /** * Returns a Class objects representing the * return type of the method specified by identifier. */ public final Class getReturnType(int identifier) { return getReflectiveMethods()[identifier].getReturnType(); } /** * Returns the identifier index of the method, as identified by its * original name. * *

    This method is useful, in conjuction with * ClassMetaobject#getMethod(), to obtain a quick reference * to the original method in the reflected class (i.e. not the proxy * method), using the original name of the method. * *

    Written by Brett Randall and Shigeru Chiba. * * @param originalName The original name of the reflected method * @param argTypes array of Class specifying the method signature * @return the identifier index of the original method * @throws NoSuchMethodException if the method does not exist * * @see ClassMetaobject#getMethod(int) */ public final int getMethodIndex(String originalName, Class[] argTypes) throws NoSuchMethodException { Method[] mthds = getReflectiveMethods(); for (int i = 0; i < mthds.length; i++) { if (mthds[i] == null) continue; // check name and parameter types match if (getMethodName(i).equals(originalName) && Arrays.equals(argTypes, mthds[i].getParameterTypes())) return i; } throw new NoSuchMethodException("Method " + originalName + " not found"); } } javassist-3.12.1.ga/src/main/javassist/tools/reflect/CannotInvokeException.java0000644000175000017500000000423110630701321027550 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.reflect; import java.lang.reflect.InvocationTargetException; import java.lang.IllegalAccessException; /** * Thrown when method invocation using the reflection API has thrown * an exception. * * @see javassist.tools.reflect.Metaobject#trapMethodcall(int, Object[]) * @see javassist.tools.reflect.ClassMetaobject#trapMethodcall(int, Object[]) * @see javassist.tools.reflect.ClassMetaobject#invoke(Object, int, Object[]) */ public class CannotInvokeException extends RuntimeException { private Throwable err = null; /** * Returns the cause of this exception. It may return null. */ public Throwable getReason() { return err; } /** * 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; } } javassist-3.12.1.ga/src/main/javassist/tools/reflect/Metalevel.java0000644000175000017500000000230210630701321025206 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.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); } javassist-3.12.1.ga/src/main/javassist/tools/reflect/Loader.java0000644000175000017500000001357410630701321024513 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.reflect; import javassist.CannotCompileException; import javassist.NotFoundException; import javassist.ClassPool; /** * A class loader for reflection. * *

    To run a program, say MyApp, * including a reflective class, * you must write a start-up program as follows: * *

       * public class Main {
       *   public static void main(String[] args) throws Throwable {
       *     javassist.tools.reflect.Loader cl
       *         = (javassist.tools.reflect.Loader)Main.class.getClassLoader();
       *     cl.makeReflective("Person", "MyMetaobject",
       *                       "javassist.tools.reflect.ClassMetaobject");
       *     cl.run("MyApp", args);
       *   }
       * }
       * 
    * *

    Then run this program as follows: * *

      % java javassist.tools.reflect.Loader Main arg1, ...
    * *

    This command runs Main.main() with arg1, ... * and Main.main() runs MyApp.main() with * arg1, ... * The Person class is modified * to be a reflective class. Method calls on a Person * object are intercepted by an instance of MyMetaobject. * *

    Also, you can run MyApp in a slightly different way: * *

       * public class Main2 {
       *   public static void main(String[] args) throws Throwable {
       *     javassist.tools.reflect.Loader cl = new javassist.tools.reflect.Loader();
       *     cl.makeReflective("Person", "MyMetaobject",
       *                       "javassist.tools.reflect.ClassMetaobject");
       *     cl.run("MyApp", args);
       *   }
       * }
       * 
    * *

    This program is run as follows: * *

      % java Main2 arg1, ...
    * *

    The difference from the former one is that the class Main * is loaded by javassist.tools.reflect.Loader whereas the class * Main2 is not. Thus, Main belongs * to the same name space (security domain) as MyApp * whereas Main2 does not; Main2 belongs * to the same name space as javassist.tools.reflect.Loader. * For more details, * see the notes in the manual page of javassist.Loader. * *

    The class Main2 is equivalent to this class: * *

       * 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.tools.reflect.ClassMetaobject");
       *     cl.run("MyApp", args);
       *   }
       * }
       * 
    * *

    Note: * *

    javassist.tools.reflect.Loader does not make a class reflective * if that class is in a java.* or * javax.* 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. * *

    To avoid this limitation, those classes should be statically * modified with javassist.tools.reflect.Compiler and the original * class files should be replaced. * * @see javassist.tools.reflect.Reflection * @see javassist.tools.reflect.Compiler * @see javassist.Loader */ public class Loader extends javassist.Loader { protected Reflection reflection; /** * Loads a class with an instance of Loader * and calls main() in that class. * * @param args command line parameters. *

      * args[0] is the class name to be loaded. *
      args[1..n] are parameters passed * to the target main(). *
    */ public static void main(String[] args) throws Throwable { Loader cl = new Loader(); cl.run(args); } /** * Constructs a new class loader. */ public Loader() throws CannotCompileException, NotFoundException { super(); delegateLoadingOf("javassist.tools.reflect.Loader"); reflection = new Reflection(); ClassPool pool = ClassPool.getDefault(); addTranslator(pool, 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 * Metaobject. * @param metaclass the class of the class metaobject. * It must be a subclass of * ClassMetaobject. * @return false if the class is already reflective. * * @see javassist.tools.reflect.Metaobject * @see javassist.tools.reflect.ClassMetaobject */ public boolean makeReflective(String clazz, String metaobject, String metaclass) throws CannotCompileException, NotFoundException { return reflection.makeReflective(clazz, metaobject, metaclass); } } javassist-3.12.1.ga/src/main/javassist/tools/reflect/package.html0000644000175000017500000000237110361120146024715 0ustar twernertwerner Runtime Behavioral Reflection.

    (also recently known as interceptors or AOP?)

    This package enables a metaobject to trap method calls and field accesses on a regular Java object. It provides a class Reflection, which is a main module for implementing runtime behavioral reflection. It also provides a class Loader and Compiler as utilities for dynamically or statically translating a regular class into a reflective class.

    An instance of the reflective class is associated with a runtime metaobject and a runtime class metaobject, which control the behavior of that instance. The runtime metaobject is created for every (base-level) instance but the runtime class metaobject is created for every (base-level) class. Metaobject is the root class of the runtime metaobject and ClassMetaobject is the root class of the runtime class metaobject.

    This package is provided as a sample implementation of the reflection mechanism with Javassist. All the programs in this package uses only the regular Javassist API; they never call any hidden methods.

    The most significant class in this package is Reflection. See the description of this class first. javassist-3.12.1.ga/src/main/javassist/tools/reflect/Reflection.java0000644000175000017500000003412210630701321025367 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.reflect; import javassist.*; import javassist.CtMethod.ConstParameter; /** * The class implementing the behavioral reflection mechanism. * *

    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. The methods inherited from the * super classes are also intercepted except final methods. To intercept * a final method in a super class, that super class must be also reflective. * *

    To do this, the original class file representing a reflective class: * *

       * class Person {
       *   public int f(int i) { return i + 1; }
       *   public int value;
       * }
       * 
    * *

    is modified so that it represents a class: * *

       * class Person implements Metalevel {
       *   public int _original_f(int i) { return i + 1; }
       *   public int f(int i) { delegate to the metaobject }
       *
       *   public int value;
       *   public int _r_value() { read "value" }
       *   public void _w_value(int v) { write "value" }
       *
       *   public ClassMetaobject _getClass() { return a class metaobject }
       *   public Metaobject _getMetaobject() { return a metaobject }
       *   public void _setMetaobject(Metaobject m) { change a metaobject }
       * }
       * 
    * * @see javassist.tools.reflect.ClassMetaobject * @see javassist.tools.reflect.Metaobject * @see javassist.tools.reflect.Loader * @see javassist.tools.reflect.Compiler */ 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_"; static final String metaobjectClassName = "javassist.tools.reflect.Metaobject"; static final String classMetaobjectClassName = "javassist.tools.reflect.ClassMetaobject"; 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 Reflection object. */ public Reflection() { classPool = null; converter = new CodeConverter(); } /** * Initializes the object. */ public void start(ClassPool pool) throws NotFoundException { classPool = pool; final String msg = "javassist.tools.reflect.Sample is not found or broken."; try { CtClass c = classPool.get("javassist.tools.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 onLoad(ClassPool pool, String classname) throws CannotCompileException, NotFoundException { CtClass clazz = pool.get(classname); clazz.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 false if the class is already reflective. * * @see javassist.tools.reflect.Metaobject * @see javassist.tools.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 * Metaobject. * @param metaclass the class of the class metaobject. * It must be a subclass of * ClassMetaobject. * @return false if the class is already reflective. * * @see javassist.tools.reflect.Metaobject * @see javassist.tools.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 * CtClass 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 * Metaobject. * @param metaclass the class of the class metaobject. * It must be a subclass of * ClassMetaobject. * @return false if the class is already reflective. * * @see javassist.tools.reflect.Metaobject * @see javassist.tools.reflect.ClassMetaobject */ public boolean makeReflective(CtClass clazz, CtClass metaobject, CtClass metaclass) throws CannotCompileException, CannotReflectException, NotFoundException { if (clazz.isInterface()) throw new CannotReflectException( "Cannot reflect an interface: " + clazz.getName()); if (clazz.subclassOf(classPool.get(classMetaobjectClassName))) throw new CannotReflectException( "Cannot reflect a subclass of ClassMetaobject: " + clazz.getName()); if (clazz.subclassOf(classPool.get(metaobjectClassName))) throw new CannotReflectException( "Cannot reflect a subclass of Metaobject: " + clazz.getName()); 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.tools.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.tools.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.tools.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(); 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; if (Modifier.isFinal(mod)) { mod &= ~Modifier.FINAL; m2.setModifiers(mod); } } 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); } } } } javassist-3.12.1.ga/src/main/javassist/tools/reflect/CannotReflectException.java0000644000175000017500000000235310630701321027704 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.reflect; import javassist.CannotCompileException; /** * Thrown by makeReflective() in Reflection * when there is an attempt to reflect * a class that is either an interface or a subclass of * either ClassMetaobject or Metaobject. * * @author Brett Randall * @see javassist.tools.reflect.Reflection#makeReflective(CtClass,CtClass,CtClass) * @see javassist.CannotCompileException */ public class CannotReflectException extends CannotCompileException { public CannotReflectException(String msg) { super(msg); } } javassist-3.12.1.ga/src/main/javassist/tools/reflect/Compiler.java0000644000175000017500000001323110630701321025045 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.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. * *

    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 javassist.tools.reflect.Loader * or any other user-defined class loader. * *

    The modified class files are given as the command-line parameters, * which are a sequence of fully-qualified class names followed by options: * *

    -m classname : specifies the class of the * metaobjects associated with instances of the class followed by * this option. The default is javassit.reflect.Metaobject. * *

    -c classname : specifies the class of the * class metaobjects associated with instances of the class followed by * this option. The default is javassit.reflect.ClassMetaobject. * *

    If a class name is not followed by any options, the class indicated * by that class name is not reflective. * *

    For example, *

      % java Compiler Dog -m MetaDog -c CMetaDog Cat -m MetaCat Cow
       * 
    * *

    modifies class files Dog.class, Cat.class, * and Cow.class. * 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. * *

    Note that if the super class is also made reflective, it must be done * before the sub class. * * @see javassist.tools.reflect.Metaobject * @see javassist.tools.reflect.ClassMetaobject * @see javassist.tools.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.start(pool); 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.tools.reflect.Metaobject"; else metaobj = entries[i].metaobject; if (entries[i].classobject == null) classobj = "javassist.tools.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) { implementor.onLoad(pool, entries[i].classname); pool.get(entries[i].classname).writeFile(); } } 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.tools.reflect.Compiler"); out.println(" ( [-m ] [-c ])+"); } } javassist-3.12.1.ga/src/main/javassist/tools/reflect/Sample.java0000644000175000017500000000353210630701321024517 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.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; } } javassist-3.12.1.ga/src/main/javassist/tools/reflect/CannotCreateException.java0000644000175000017500000000174610630701321027530 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.reflect; /** * Signals that ClassMetaobject.newInstance() fails. */ public class CannotCreateException extends Exception { public CannotCreateException(String s) { super(s); } public CannotCreateException(Exception e) { super("by " + e.toString()); } } javassist-3.12.1.ga/src/main/javassist/tools/package.html0000644000175000017500000000006010361120146023262 0ustar twernertwerner Covenient tools. javassist-3.12.1.ga/src/main/javassist/tools/rmi/0000755000175000017500000000000011637463251021612 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/tools/rmi/RemoteRef.java0000644000175000017500000000211710630701321024327 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.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; } } javassist-3.12.1.ga/src/main/javassist/tools/rmi/StubGenerator.java0000644000175000017500000002164010630701321025225 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.rmi; 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. * *

    The proxy class for class A is as follows: * *

      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 ...
       * }
    * *

    Instances of the proxy class is created by an * ObjectImporter 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.tools.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(); } /** * Initializes the object. * This 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.tools.rmi.ObjectImporter", "int" }); interfacesForProxy = pool.get(new String[] { "java.io.Serializable", "javassist.tools.rmi.Proxy" }); exceptionForProxy = new CtClass[] { pool.get("javassist.tools.rmi.RemoteException") }; } /** * Does nothing. * This is a method declared in javassist.Translator. * @see javassist.Translator#onLoad(ClassPool,String) */ public void onLoad(ClassPool pool, String classname) {} /** * Returns true if the specified class is a proxy class * recorded by makeProxyClass(). * * @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 false 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.tools.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; try { superclazz.getDeclaredConstructor(null); break; // the constructor with no arguments is found. } catch (NotFoundException e) { } superclazz.addConstructor( CtNewConstructor.defaultConstructor(superclazz)); } } } javassist-3.12.1.ga/src/main/javassist/tools/rmi/ObjectImporter.java0000644000175000017500000002465210630701321025377 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.rmi; import java.io.*; import java.net.*; import java.applet.Applet; import java.lang.reflect.*; /** * The object importer enables applets to call a method on a remote * object running on the Webserver (the main class of this * package). * *

    To access the remote * object, the applet first calls lookupObject() 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. * *

    The methods remotely called by the applet must be public. * This is true even if the applet's class and the remote object's classs * belong to the same package. * *

    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. * *

    The parameters to a remote method is passed in the call-by-value * manner. Thus all the parameter classes must implement * java.io.Serializable. 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. * *

    Because of the limitations of the current implementation, *

      *
    • The parameter objects cannot contain the proxy * object as a field value. *
    • If class C is of the remote object, then * the applet cannot instantiate C locally or remotely. *
    * *

    All the exceptions thrown by the remote object are converted * into RemoteException. Since this exception is a subclass * of RuntimeException, the caller method does not need * to catch the exception. However, good programs should catch * the RuntimeException. * * @see javassist.tools.rmi.AppletServer * @see javassist.tools.rmi.RemoteException * @see javassist.tools.web.Viewer */ public class ObjectImporter implements java.io.Serializable { private final byte[] endofline = { 0x0d, 0x0a }; private String servername, orgServername; private int port, orgPort; protected byte[] lookupCommand = "POST /lookup HTTP/1.0".getBytes(); protected byte[] rmiCommand = "POST /rmi HTTP/1.0".getBytes(); /** * Constructs an object importer. * *

    Remote objects are imported from the web server that the given * applet has been loaded from. * * @param applet the applet loaded from the Webserver. */ public ObjectImporter(Applet applet) { URL codebase = applet.getCodeBase(); orgServername = servername = codebase.getHost(); orgPort = port = codebase.getPort(); } /** * Constructs an object importer. * *

    If you run a program with javassist.tools.web.Viewer, * you can construct an object importer as follows: * *

           * Viewer v = (Viewer)this.getClass().getClassLoader();
           * ObjectImporter oi = new ObjectImporter(v.getServer(), v.getPort());
           * 
    * * @see javassist.tools.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). * *

    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]); } } javassist-3.12.1.ga/src/main/javassist/tools/rmi/Proxy.java0000644000175000017500000000152410630701321023561 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.rmi; /** * An interface implemented by proxy classes. * * @see javassist.tools.rmi.StubGenerator */ public interface Proxy { int _getObjectId(); } javassist-3.12.1.ga/src/main/javassist/tools/rmi/package.html0000644000175000017500000000073410361120146024061 0ustar twernertwerner Sample implementation of remote method invocation.

    This package enables applets to access remote objects running on the web server with regular Java syntax. It is provided as a sample implementation with Javassist. All the programs in this package uses only the regular Javassist API; they never call any hidden methods.

    The most significant class of this package is ObjectImporter. See the description of this class first. javassist-3.12.1.ga/src/main/javassist/tools/rmi/RemoteException.java0000644000175000017500000000177510630701321025562 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.rmi; /** * RemoteException 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()); } } javassist-3.12.1.ga/src/main/javassist/tools/rmi/AppletServer.java0000644000175000017500000001662710630701321025066 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.rmi; import java.io.*; import javassist.tools.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 * exportObject() 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.tools.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, StubGenerator gen, int port) throws IOException, NotFoundException, CannotCompileException { super(port); exportedNames = new Hashtable(); exportedObjects = new Vector(); stubGen = gen; addTranslator(loader, gen); } /** * 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 obj the exported object. * @return the object identifier * * @see javassist.tools.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; } javassist-3.12.1.ga/src/main/javassist/tools/rmi/ObjectNotFoundException.java0000644000175000017500000000172610630701321027206 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.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()); } } javassist-3.12.1.ga/src/main/javassist/tools/rmi/Sample.java0000644000175000017500000000232110630701321023655 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.rmi; /** * A template used for defining a proxy class. * The class file of this class is read by the StubGenerator * 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."); } } javassist-3.12.1.ga/src/main/javassist/tools/framedump.java0000644000175000017500000000314111015721760023635 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba, and others. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools; import javassist.ClassPool; import javassist.CtClass; import javassist.bytecode.analysis.FramePrinter; /** * framedump is a tool for viewing a merged combination of the instructions and frame state * of all methods in a class. * *

    For example, *

      % java javassist.tools.framedump foo.class
    */ public class framedump { private framedump() {} /** * Main method. * * @param args args[0] is the class file name. */ public static void main(String[] args) throws Exception { if (args.length != 1) { System.err.println("Usage: java javassist.tools.framedump "); return; } ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(args[0]); System.out.println("Frame Dump of " + clazz.getName() + ":"); FramePrinter.print(clazz, System.out); } } javassist-3.12.1.ga/src/main/javassist/tools/web/0000755000175000017500000000000011637463253021602 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/tools/web/Viewer.java0000644000175000017500000001437610630701321023700 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.web; import java.io.*; import java.net.*; /** * A sample applet viewer. * *

    This is a sort of applet viewer that can run any program even if * the main class is not a subclass of Applet. * This viewwer first calls main() in the main class. * *

    To run, you should type: * *

      % java javassist.tools.web.Viewer host port Main arg1, ...
    * *

    This command calls Main.main() with arg1,... * All classes including Main are fetched from * a server http://host:port. * Only the class file for Viewer must exist * on a local file system at the client side; even other * javassist.* classes are not needed at the client side. * Viewer uses only Java core API classes. * *

    Note: since a Viewer object is a class loader, * a program loaded by this object can call a method in Viewer. * For example, you can write something like this: * *

       * Viewer v = (Viewer)this.getClass().getClassLoader();
       * String port = v.getPort();
       * 
    * */ 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.tools.web.Viewer 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 classname. * * @param classname executed class * @param args the arguments passed to main(). */ 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 java.*, javax.*, or * Viewer, then it is loaded by the parent class * loader. * *

    This method can be overridden by a subclass of * Viewer. */ protected Class findClass(String name) throws ClassNotFoundException { Class c = null; if (name.startsWith("java.") || name.startsWith("javax.") || name.equals("javassist.tools.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; } } javassist-3.12.1.ga/src/main/javassist/tools/web/Webserver.java0000644000175000017500000002763310630701321024403 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.web; import java.net.*; import java.io.*; import java.util.Date; import javassist.*; /** * A web server for running sample programs. * *

    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. * *

    Note: 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; protected Translator translator; private final static byte[] endofline = { 0x0d, 0x0a }; 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; /** * If this field is not null, the class files taken from * ClassPool 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. * *

    If the given URL indicates a class file and the class file * is not found under the directory specified by this variable, * then Class.getResourceAsStream() 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.tools.web.Webserver "); } /** * 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; translator = null; } /** * Requests the web server to use the specified * ClassPool object for obtaining a class file. */ public void setClassPool(ClassPool loader) { classPool = loader; } /** * Adds a translator, which is called whenever a client requests * a class file. * * @param cp the ClassPool object for obtaining * a class file. * @param t a translator. */ public void addTranslator(ClassPool cp, Translator t) throws NotFoundException, CannotCompileException { classPool = cp; translator = t; t.start(classPool); } /** * 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); byte[] filebuffer = new byte[4096]; 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(); byte[] filebuffer = new byte[4096]; 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 { if (translator != null) translator.onLoad(classPool, classname); CtClass c = classPool.get(classname); classfile = c.toBytecode(); if (debugDir != null) c.writeFile(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("

    Bad Request

    ".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) { } } } javassist-3.12.1.ga/src/main/javassist/tools/web/package.html0000644000175000017500000000021410361120146024040 0ustar twernertwerner Simple web server for running sample code.

    This package provides a simple web server for sample packages. javassist-3.12.1.ga/src/main/javassist/tools/web/BadHttpRequest.java0000644000175000017500000000206310630701321025324 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools.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(); } } javassist-3.12.1.ga/src/main/javassist/tools/Dump.java0000644000175000017500000000356211371307074022574 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.tools; import java.io.*; import javassist.bytecode.ClassFile; import javassist.bytecode.ClassFilePrinter; /** * 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. * *

    For example, *

      % java javassist.tools.Dump foo.class
    * *

    prints the contents of the constant pool and the list of methods * and fields. */ public class Dump { private Dump() {} /** * Main method. * * @param args args[0] is the class file name. */ public static void main(String[] args) throws Exception { if (args.length != 1) { System.err.println("Usage: java Dump "); 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 ***"); ClassFilePrinter.print(w, out); } } javassist-3.12.1.ga/src/main/javassist/SerialVersionUID.java0000644000175000017500000001716011116250741023650 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import java.io.*; import java.lang.reflect.Modifier; 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); CtMethod[] methods = clazz.getDeclaredMethods(); // class modifiers. int classMods = clazz.getModifiers(); if ((classMods & Modifier.INTERFACE) != 0) if (methods.length > 0) classMods = classMods | Modifier.ABSTRACT; else classMods = classMods & ~Modifier.ABSTRACT; out.writeInt(classMods); // 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(""); 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(""); out.writeInt(mods); out.writeUTF(constructor.getMethodInfo2() .getDescriptor().replace('/', '.')); } } // methods. 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() & (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED | Modifier.NATIVE | Modifier.ABSTRACT | Modifier.STRICT); 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)); } } javassist-3.12.1.ga/src/main/javassist/CannotCompileException.java0000644000175000017500000000627210630701321025130 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import javassist.compiler.CompileError; /** * Thrown when bytecode transformation has failed. */ public class CannotCompileException extends Exception { private Throwable myCause; /** * Gets the cause of this throwable. * It is for JDK 1.3 compatibility. */ public Throwable getCause() { return (myCause == this ? null : myCause); } /** * Initializes the cause of this throwable. * It is for JDK 1.3 compatibility. */ public synchronized Throwable initCause(Throwable cause) { myCause = cause; return this; } private String message; /** * Gets a long message if it is available. */ public String getReason() { if (message != null) return message; else return this.toString(); } /** * Constructs a CannotCompileException with a message. * * @param msg the message. */ public CannotCompileException(String msg) { super(msg); message = msg; initCause(null); } /** * Constructs a CannotCompileException with an Exception * representing the cause. * * @param e the cause. */ public CannotCompileException(Throwable e) { super("by " + e.toString()); message = null; initCause(e); } /** * Constructs a CannotCompileException with a detailed message * and an Exception representing the cause. * * @param msg the message. * @param e the cause. */ public CannotCompileException(String msg, Throwable e) { this(msg); initCause(e); } /** * Constructs a CannotCompileException with a * NotFoundException. */ public CannotCompileException(NotFoundException e) { this("cannot find " + e.getMessage(), e); } /** * Constructs a CannotCompileException with an CompileError. */ public CannotCompileException(CompileError e) { this("[source error] " + e.getMessage(), e); } /** * Constructs a CannotCompileException * with a ClassNotFoundException. */ public CannotCompileException(ClassNotFoundException e, String name) { this("cannot find " + name, e); } /** * Constructs a CannotCompileException with a ClassFormatError. */ public CannotCompileException(ClassFormatError e, String name) { this("invalid class format: " + name, e); } } javassist-3.12.1.ga/src/main/javassist/CtNewMethod.java0000644000175000017500000004536611316070253022713 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import javassist.bytecode.*; import javassist.compiler.Javac; import javassist.compiler.CompileError; import javassist.CtMethod.ConstParameter; /** * A collection of static methods for creating a CtMethod. * 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, * *

      "public Object id(Object obj) { return obj; }"
    * * @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, * *
      "public Object id(Object obj) { return obj; }"
    * *

    If the source code includes $proceed(), 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 $proceed(). * @param delegateMethod the name of the method * that is called by $proceed(). */ 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 (non-static) method. The created method cannot * be changed to a static method later. * * @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 source text of the method body. * It must be a block surrounded by {}. * If it is null, the created method * does nothing except returning zero or null. * @param declaring the class to which the created method is added. * @see #make(int, CtClass, String, CtClass[], CtClass[], String, CtClass) */ public static CtMethod make(CtClass returnType, String mname, CtClass[] parameters, CtClass[] exceptions, String body, CtClass declaring) throws CannotCompileException { return make(Modifier.PUBLIC, returnType, mname, parameters, exceptions, body, declaring); } /** * Creates a method. modifiers can contain * Modifier.STATIC. * * @param modifiers access modifiers. * @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 source text of the method body. * It must be a block surrounded by {}. * If it is null, the created method * does nothing except returning zero or null. * @param declaring the class to which the created method is added. * * @see Modifier */ public static CtMethod make(int modifiers, CtClass returnType, String mname, CtClass[] parameters, CtClass[] exceptions, String body, CtClass declaring) throws CannotCompileException { try { CtMethod cm = new CtMethod(returnType, mname, parameters, declaring); cm.setModifiers(modifiers); 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. * This is a convenience method for calling * {@link CtMethod#CtMethod(CtMethod, CtClass, ClassMap) this constructor}. * See the description of the constructor for particular behavior of the copying. * * @param src the source method. * @param declaring the class to which the created method is added. * @param map the hash table associating original class names * with substituted names. * It can be null. * * @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. * This is a convenience method for calling * {@link CtMethod#CtMethod(CtMethod, CtClass, ClassMap) this constructor}. * See the description of the constructor for particular behavior of the copying. * * @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 hash table associating original class names * with substituted names. * It can be null. * * @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 not static even if the field is * static. You may not change it to be static * by setModifiers() in CtBehavior. * * @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(1, 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 delegate 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. * *

    The following method is an example of the created method. * *

      int f(int p, int q) {
           *     return super.f(p, q);
           * }
    * *

    The name of the created method can be changed by * setName(). * * @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, 0); code.addInvokestatic(deleClass, methodName, desc); } else { code.addLoad(0, deleClass); s = code.addLoadParameters(params, 1); 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 Object. * *

    The body of the created method is a copy of the body of the method * specified by body. However, it is wrapped in * parameter-conversion code. * *

    The method specified by body must have this singature: * *

      Object method(Object[] params, <type> cvalue) *
    * *

    The type of the cvalue depends on * constParam. * If constParam is null, the signature * must be: * *

      Object method(Object[] params)
    * *

    The method body copied from body is wrapped in * parameter-conversion code, which converts parameters specified by * parameterTypes into an array of Object. * The returned value is also converted from the Object * type to the type specified by returnType. Thus, * the resulting method body is as follows: * *

      Object[] params = new Object[] { p0, p1, ... };
           * <type> cvalue = <constant-value>;
           *  ... copied method body ...
           * Object result = <returned value>
           * return (<returnType>)result;
           * 
    * *

    The variables p0, p2, ... represent * formal parameters of the created method. * The value of cvalue is specified by * constParam. * *

    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 * java.lang.Integer. If the type of the returned value * is void, the returned value is discarded. * *

    Example: * *

      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);
    * *

    where the class Sample is as follows: * *

      public class Sample extends java.util.Vector {
           *     public Object add0(Object[] args) {
           *         super.addElement(args[0]);
           *         return null;
           *     }
           * }
    * *

    This program produces a class intVector: * *

      public class intVector extends java.util.Vector {
           *     public void add(int p0) {
           *         Object[] args = new Object[] { p0 };
           *         // begin of the copied body
           *         super.addElement(args[0]);
           *         Object result = null;
           *         // end
           *     }
           * }
    * *

    Note that the type of the parameter to add() depends * only on the value of argTypes passed to * CtNewMethod.wrapped(). Thus, it is easy to * modify this program to produce a * StringVector class, which is a vector containing * only String objects, and other vector classes. * * @param returnType the type of the returned value. * @param mname the method name. * @param parameterTypes a list of the parameter types. * @param exceptionTypes a list of the exception types. * @param body the method body * (must not be a static method). * @param constParam the constant parameter * (maybe null). * @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); } } javassist-3.12.1.ga/src/main/javassist/CtNewNestedClass.java0000644000175000017500000000467010630701321023667 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; import javassist.bytecode.ClassFile; import javassist.bytecode.AccessFlag; import javassist.bytecode.InnerClassesAttribute; /** * A newly created public nested class. */ class CtNewNestedClass extends CtNewClass { CtNewNestedClass(String realName, ClassPool cp, boolean isInterface, CtClass superclass) { super(realName, cp, isInterface, superclass); } /** * This method does not change the STATIC bit. The original value is kept. */ public void setModifiers(int mod) { mod = mod & ~Modifier.STATIC; super.setModifiers(mod); updateInnerEntry(mod, getName(), this, true); } private static void updateInnerEntry(int mod, String name, CtClass clazz, boolean outer) { ClassFile cf = clazz.getClassFile2(); InnerClassesAttribute ica = (InnerClassesAttribute)cf.getAttribute( InnerClassesAttribute.tag); if (ica == null) return; int n = ica.tableLength(); for (int i = 0; i < n; i++) if (name.equals(ica.innerClass(i))) { int acc = ica.accessFlags(i) & AccessFlag.STATIC; ica.setAccessFlags(i, mod | acc); String outName = ica.outerClass(i); if (outName != null && outer) try { CtClass parent = clazz.getClassPool().get(outName); updateInnerEntry(mod, name, parent, false); } catch (NotFoundException e) { throw new RuntimeException("cannot find the declaring class: " + outName); } break; } } } javassist-3.12.1.ga/src/main/javassist/CtMember.java0000644000175000017500000002300111221164554022211 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; /** * An instance of CtMember represents a field, a constructor, * or a method. */ public abstract class CtMember { CtMember next; // for internal use protected CtClass declaringClass; /* Make a circular link of CtMembers declared in the * same class so that they are garbage-collected together * at the same time. */ static class Cache extends CtMember { protected void extendToString(StringBuffer buffer) {} public boolean hasAnnotation(Class clz) { return false; } public Object getAnnotation(Class clz) throws ClassNotFoundException { return null; } public Object[] getAnnotations() throws ClassNotFoundException { return null; } public byte[] getAttribute(String name) { return null; } public Object[] getAvailableAnnotations() { return null; } public int getModifiers() { return 0; } public String getName() { return null; } public String getSignature() { return null; } public void setAttribute(String name, byte[] data) {} public void setModifiers(int mod) {} private CtMember methodTail; private CtMember consTail; // constructor tail private CtMember fieldTail; Cache(CtClassType decl) { super(decl); methodTail = this; consTail = this; fieldTail = this; fieldTail.next = this; } CtMember methodHead() { return this; } CtMember lastMethod() { return methodTail; } CtMember consHead() { return methodTail; } // may include a static initializer CtMember lastCons() { return consTail; } CtMember fieldHead() { return consTail; } CtMember lastField() { return fieldTail; } void addMethod(CtMember method) { method.next = methodTail.next; methodTail.next = method; if (methodTail == consTail) { consTail = method; if (methodTail == fieldTail) fieldTail = method; } methodTail = method; } /* Both constructors and a class initializer. */ void addConstructor(CtMember cons) { cons.next = consTail.next; consTail.next = cons; if (consTail == fieldTail) fieldTail = cons; consTail = cons; } void addField(CtMember field) { field.next = this; // or fieldTail.next fieldTail.next = field; fieldTail = field; } static int count(CtMember head, CtMember tail) { int n = 0; while (head != tail) { n++; head = head.next; } return n; } void remove(CtMember mem) { CtMember m = this; CtMember node; while ((node = m.next) != this) { if (node == mem) { m.next = node.next; if (node == methodTail) methodTail = m; if (node == consTail) consTail = m; if (node == fieldTail) fieldTail = m; break; } else m = m.next; } } } protected CtMember(CtClass clazz) { declaringClass = clazz; next = null; } final CtMember next() { return next; } /** * This method is invoked when setName() or replaceClassName() * in CtClass is called. * * @see CtMethod#nameReplaced() */ void nameReplaced() {} public String toString() { StringBuffer buffer = new StringBuffer(getClass().getName()); buffer.append("@"); buffer.append(Integer.toHexString(hashCode())); buffer.append("["); buffer.append(Modifier.toString(getModifiers())); extendToString(buffer); buffer.append("]"); return buffer.toString(); } /** * Invoked by {@link #toString()} to add to the buffer and provide the * complete value. Subclasses should invoke this method, adding a * space before each token. The modifiers for the member are * provided first; subclasses should provide additional data such * as return type, field or method name, etc. */ protected abstract void extendToString(StringBuffer buffer); /** * Returns the class that declares this member. */ public CtClass getDeclaringClass() { return declaringClass; } /** * Returns true if this member is accessible from the given class. */ public boolean visibleFrom(CtClass clazz) { int mod = getModifiers(); if (Modifier.isPublic(mod)) return true; else if (Modifier.isPrivate(mod)) return clazz == declaringClass; else { // package or protected String declName = declaringClass.getPackageName(); String fromName = clazz.getPackageName(); boolean visible; if (declName == null) visible = fromName == null; else visible = declName.equals(fromName); if (!visible && Modifier.isProtected(mod)) return clazz.subclassOf(declaringClass); return visible; } } /** * Obtains the modifiers of the member. * * @return modifiers encoded with * javassist.Modifier. * @see Modifier */ public abstract int getModifiers(); /** * Sets the encoded modifiers of the member. * * @see Modifier */ public abstract void setModifiers(int mod); /** * Returns true if the class has the specified annotation class. * * @param clz the annotation class. * @return true if the annotation is found, otherwise false. * @since 3.11 */ public abstract boolean hasAnnotation(Class clz); /** * Returns the annotation if the class has the specified annotation class. * For example, if an annotation @Author is associated * with this member, an Author object is returned. * The member values can be obtained by calling methods on * the Author object. * * @param clz the annotation class. * @return the annotation if found, otherwise null. * @since 3.11 */ public abstract Object getAnnotation(Class clz) throws ClassNotFoundException; /** * Returns the annotations associated with this member. * For example, if an annotation @Author is associated * with this member, the returned array contains an Author * object. The member values can be obtained by calling methods on * the Author object. * * @return an array of annotation-type objects. * @see CtClass#getAnnotations() */ public abstract Object[] getAnnotations() throws ClassNotFoundException; /** * Returns the annotations associated with this member. * This method is equivalent to getAnnotations() * except that, if any annotations are not on the classpath, * they are not included in the returned array. * * @return an array of annotation-type objects. * @see #getAnnotations() * @see CtClass#getAvailableAnnotations() * @since 3.3 */ public abstract Object[] getAvailableAnnotations(); /** * Obtains the name of the member. * *

    As for constructor names, see getName() * in CtConstructor. * * @see CtConstructor#getName() */ public abstract String getName(); /** * Returns the character string representing the signature of the member. * If two members have the same signature (parameter types etc.), * getSignature() returns the same string. */ public abstract String getSignature(); /** * Obtains a user-defined attribute with the given name. * If that attribute is not found in the class file, this * method returns null. * *

    Note that an attribute is a data block specified by * the class file format. * See {@link javassist.bytecode.AttributeInfo}. * * @param name attribute name */ public abstract byte[] getAttribute(String name); /** * Adds a user-defined attribute. The attribute is saved in the class file. * *

    Note that an attribute is a data block specified by * the class file format. * See {@link javassist.bytecode.AttributeInfo}. * * @param name attribute name * @param data attribute value */ public abstract void setAttribute(String name, byte[] data); } javassist-3.12.1.ga/src/main/javassist/expr/0000755000175000017500000000000011637463316020643 5ustar twernertwernerjavassist-3.12.1.ga/src/main/javassist/expr/MethodCall.java0000644000175000017500000001774410636733630023534 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.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. */ protected MethodCall(int pos, CodeIterator i, CtClass declaring, MethodInfo m) { super(pos, i, declaring, m); } private int getNameAndType(ConstPool cp) { 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. */ protected 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); if (cname.charAt(0) == '[') cname = Descriptor.toClassName(cname); 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(), getSignature()); } /** * Returns the method signature (the parameter types * and the return type). * The method signature is represented by a character string * called method descriptor, which is defined in the JVM specification. * * @see javassist.CtBehavior#getSignature() * @see javassist.bytecode.Descriptor * @since 3.1 */ public String getSignature() { 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 true if the called method is of a superclass of the current * class. */ public boolean isSuper() { return iterator.byteAt(currentPos) == INVOKESPECIAL && !where().getDeclaringClass().getName().equals(getClassName()); } /* * 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. * *

    $0 is available even if the called method is static. * * @param statement a Java statement. */ public void replace(String statement) throws CannotCompileException { thisClass.getClassFile(); // to call checkModify(). 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); if (c == INVOKESTATIC) jc.recordStaticProceed(classname, methodname); else if (c == INVOKESPECIAL) jc.recordSpecialProceed(Javac.param0Name, classname, methodname, signature); else 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.recordLocalVariables(ca, pos); if (retType != CtClass.voidType) { bytecode.addConstZero(retType); bytecode.addStore(retVar, retType); // initialize $_ } 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"); } } } javassist-3.12.1.ga/src/main/javassist/expr/Expr.java0000644000175000017500000002473311221067671022426 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.expr; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtBehavior; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtPrimitiveType; import javassist.NotFoundException; import javassist.bytecode.AccessFlag; import javassist.bytecode.BadBytecode; import javassist.bytecode.Bytecode; import javassist.bytecode.ClassFile; import javassist.bytecode.CodeAttribute; import javassist.bytecode.CodeIterator; import javassist.bytecode.ConstPool; import javassist.bytecode.ExceptionTable; import javassist.bytecode.ExceptionsAttribute; import javassist.bytecode.MethodInfo; import javassist.bytecode.Opcode; import javassist.compiler.Javac; import java.util.Iterator; import java.util.LinkedList; /** * Expression. */ public 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"; /** * Undocumented constructor. Do not use; internal-use only. */ protected Expr(int pos, CodeIterator i, CtClass declaring, MethodInfo m) { currentPos = pos; iterator = i; thisClass = declaring; thisMethod = m; } /** * Returns the class that declares the method enclosing * this expression. * * @since 3.7 */ public CtClass getEnclosingClass() { return thisClass; } protected final ConstPool getConstPool() { return thisMethod.getConstPool(); } protected final boolean edited() { return edited; } protected final int locals() { return maxLocals; } protected final int stack() { return maxStack; } /** * Returns true if this method is static. */ protected 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].getMethodInfo2() == mi) return cb[i]; CtConstructor init = thisClass.getClassInitializer(); if (init != null && init.getMethodInfo2() == mi) return init; /* getDeclaredBehaviors() returns a list of methods/constructors. * Although the list is cached in a CtClass object, it might be * recreated for some reason. Thus, the member name and the signature * must be also checked. */ for (int i = cb.length - 1; i >= 0; --i) { if (thisMethod.getName().equals(cb[i].getMethodInfo2().getName()) && thisMethod.getDescriptor() .equals(cb[i].getMethodInfo2().getDescriptor())) { 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 index of the bytecode corresponding to the expression. It is * the index into the byte array containing the Java bytecode that * implements the method. */ public int indexOfBytecode() { return currentPos; } /** * 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); } } // The implementation of replace() should call thisClass.checkModify() // so that isModify() will return true. Otherwise, thisClass.classfile // might be released during compilation and the compiler might generate // bytecode with a wrong copy of ConstPool. /** * Replaces this expression with the bytecode derived from * the given source text. * * @param statement a Java statement. */ public abstract void replace(String statement) throws CannotCompileException; /** * Replaces this expression with the bytecode derived from * the given source text and ExprEditor. * * @param statement a Java statement. * @param recursive if not null, the substituted bytecode * is recursively processed by the given * ExprEditor. * @since 3.1 */ public void replace(String statement, ExprEditor recursive) throws CannotCompileException { replace(statement); if (recursive != null) runEditor(recursive, iterator); } protected void replace0(int pos, Bytecode bytecode, int size) throws BadBytecode { byte[] code = bytecode.get(); edited = true; int gap = code.length - size; for (int i = 0; i < size; ++i) iterator.writeByte(NOP, pos + i); if (gap > 0) pos = iterator.insertGapAt(pos, gap, false).position; iterator.write(code, pos); iterator.insert(bytecode.getExceptionTable(), pos); maxLocals = bytecode.getMaxLocals(); maxStack = bytecode.getMaxStack(); } protected void runEditor(ExprEditor ed, CodeIterator oldIterator) throws CannotCompileException { CodeAttribute codeAttr = oldIterator.get(); int orgLocals = codeAttr.getMaxLocals(); int orgStack = codeAttr.getMaxStack(); int newLocals = locals(); codeAttr.setMaxStack(stack()); codeAttr.setMaxLocals(newLocals); ExprEditor.LoopContext context = new ExprEditor.LoopContext(newLocals); int size = oldIterator.getCodeLength(); int endPos = oldIterator.lookAhead(); oldIterator.move(currentPos); if (ed.doit(thisClass, thisMethod, context, oldIterator, endPos)) edited = true; oldIterator.move(endPos + oldIterator.getCodeLength() - size); codeAttr.setMaxLocals(orgLocals); codeAttr.setMaxStack(orgStack); maxLocals = context.maxLocals; maxStack += context.maxStack; } } javassist-3.12.1.ga/src/main/javassist/expr/ExprEditor.java0000644000175000017500000002526010630701321023557 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.expr; import javassist.bytecode.*; import javassist.CtClass; import javassist.CannotCompileException; /** * A translator of method bodies. * *

    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. * *

    If instrument() is called in * CtMethod, the method body is scanned from the beginning * to the end. * Whenever an expression, such as a method call and a new * expression (object creation), * is found, edit() is called in ExprEdit. * edit() can inspect and modify the given expression. * The modification is reflected on the original method body. If * edit() does nothing, the original method body is not * changed. * *

    The following code is an example: * *

       * 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());
       *     }
       * });
       * 
    * *

    This code inspects all method calls appearing in the method represented * by cm and it prints the names and the line numbers of the * methods declared in class Point. This code does not modify * the body of the method represented by cm. If the method * body must be modified, call replace() * in MethodCall. * * @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() {} /** * 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; LoopContext context = new LoopContext(codeAttr.getMaxLocals()); while (iterator.hasNext()) if (loopBody(iterator, clazz, minfo, context)) edited = true; ExceptionTable et = codeAttr.getExceptionTable(); int n = et.size(); for (int i = 0; i < n; ++i) { Handler h = new Handler(et, i, iterator, clazz, minfo); edit(h); if (h.edited()) { edited = true; context.updateMax(h.locals(), h.stack()); } } // codeAttr might be modified by other partiess // so I check the current value of max-locals. if (codeAttr.getMaxLocals() < context.maxLocals) codeAttr.setMaxLocals(context.maxLocals); codeAttr.setMaxStack(codeAttr.getMaxStack() + context.maxStack); try { if (edited) minfo.rebuildStackMapIf6(clazz.getClassPool(), clazz.getClassFile2()); } catch (BadBytecode b) { throw new CannotCompileException(b.getMessage(), b); } return edited; } /** * Visits each bytecode in the given range. */ boolean doit(CtClass clazz, MethodInfo minfo, LoopContext context, CodeIterator iterator, int endPos) throws CannotCompileException { boolean edited = false; while (iterator.hasNext() && iterator.lookAhead() < endPos) { int size = iterator.getCodeLength(); if (loopBody(iterator, clazz, minfo, context)) { edited = true; int size2 = iterator.getCodeLength(); if (size != size2) // the body was modified. endPos += size2 - size; } } return edited; } final static class NewOp { NewOp next; int pos; String type; NewOp(NewOp n, int p, String t) { next = n; pos = p; type = t; } } final static class LoopContext { NewOp newList; int maxLocals; int maxStack; LoopContext(int locals) { maxLocals = locals; maxStack = 0; newList = null; } void updateMax(int locals, int stack) { if (maxLocals < locals) maxLocals = locals; if (maxStack < stack) maxStack = stack; } } final boolean loopBody(CodeIterator iterator, CtClass clazz, MethodInfo minfo, LoopContext context) throws CannotCompileException { try { Expr expr = null; int pos = iterator.next(); int c = iterator.byteAt(pos); if (c < Opcode.GETSTATIC) // c < 178 /* skip */; else if (c < Opcode.NEWARRAY) { // c < 188 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); context.newList = new NewOp(context.newList, pos, minfo.getConstPool().getClassInfo(index)); } else if (c == Opcode.INVOKESPECIAL) { NewOp newList = context.newList; if (newList != null && minfo.getConstPool().isConstructor(newList.type, iterator.u16bitAt(pos + 1)) > 0) { expr = new NewExpr(pos, iterator, clazz, minfo, newList.type, newList.pos); edit((NewExpr)expr); context.newList = newList.next; } else { MethodCall mcall = new MethodCall(pos, iterator, clazz, minfo); if (mcall.getMethodName().equals(MethodInfo.nameInit)) { ConstructorCall ccall = new ConstructorCall(pos, iterator, clazz, minfo); expr = ccall; edit(ccall); } else { expr = mcall; edit(mcall); } } } } else { // c >= 188 if (c == Opcode.NEWARRAY || c == Opcode.ANEWARRAY || c == Opcode.MULTIANEWARRAY) { expr = new NewArray(pos, iterator, clazz, minfo, c); edit((NewArray)expr); } 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()) { context.updateMax(expr.locals(), expr.stack()); return true; } else return false; } catch (BadBytecode e) { throw new CannotCompileException(e); } } /** * Edits a new expression (overridable). * The default implementation performs nothing. * * @param e the new expression creating an object. */ public void edit(NewExpr e) throws CannotCompileException {} /** * Edits an expression for array creation (overridable). * The default implementation performs nothing. * * @param a the new expression for creating an array. * @throws CannotCompileException */ public void edit(NewArray a) throws CannotCompileException {} /** * Edits a method call (overridable). * * The default implementation performs nothing. */ public void edit(MethodCall m) throws CannotCompileException {} /** * Edits a constructor call (overridable). * The constructor call is either * super() or this() * included in a constructor body. * * The default implementation performs nothing. * * @see #edit(NewExpr) */ public void edit(ConstructorCall c) 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 {} /** * Edits a catch clause (overridable). * The default implementation performs nothing. */ public void edit(Handler h) throws CannotCompileException {} } javassist-3.12.1.ga/src/main/javassist/expr/Instanceof.java0000644000175000017500000001267411033365276023605 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.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. */ protected 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 CtClass 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 thisClass.getClassPool().getCtClass(name); } /** * 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. * *

    $0 is available but the value is null. * * @param statement a Java statement. */ public void replace(String statement) throws CannotCompileException { thisClass.getClassFile(); // to call checkModify(). 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.recordLocalVariables(ca, pos); bytecode.addConstZero(retType); bytecode.addStore(retVar, retType); // initialize $_ 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.getMethodArgsLength(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); } public void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError { c.atMethodArgs(args, new int[1], new int[1], new String[1]); c.setType(CtClass.booleanType); } } } javassist-3.12.1.ga/src/main/javassist/expr/NewExpr.java0000644000175000017500000001760210765461047023104 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.expr; import javassist.*; import javassist.bytecode.*; import javassist.compiler.*; import javassist.compiler.ast.ASTList; /** * Object creation (new expression). */ public class NewExpr extends Expr { String newTypeName; int newPos; /** * Undocumented constructor. Do not use; internal-use only. */ protected NewExpr(int pos, CodeIterator i, CtClass declaring, MethodInfo m, String type, int np) { super(pos, i, declaring, m); newTypeName = type; newPos = np; } /* * Not used * private int getNameAndType(ConstPool cp) { 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 new * expression represented by this object. */ public CtBehavior where() { return super.where(); } /** * Returns the line number of the source line containing the * new expression. * * @return -1 if this information is not available. */ public int getLineNumber() { return super.getLineNumber(); } /** * Returns the source file containing the new 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; } /** * Get the signature of the constructor * * The signature is represented by a character string * called method descriptor, which is defined in the JVM specification. * * @see javassist.CtBehavior#getSignature() * @see javassist.bytecode.Descriptor * @return the signature */ public String getSignature() { ConstPool constPool = getConstPool(); int methodIndex = iterator.u16bitAt(currentPos + 1); // constructor return constPool.getMethodrefType(methodIndex); } /** * 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 return 3; // for Eclipse. The generated code may include no DUP. // throw new CannotCompileException( // "sorry, cannot edit NEW followed by no DUP"); } /** * Replaces the new expression with the bytecode derived from * the given source text. * *

    $0 is available but the value is null. * * @param statement a Java statement. */ public void replace(String statement) throws CannotCompileException { thisClass.getClassFile(); // to call checkModify(). 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 codeSize = canReplace(); int end = pos + codeSize; 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.recordLocalVariables(ca, pos); bytecode.addConstZero(newType); bytecode.addStore(retVar, newType); // initialize $_ jc.compileStmnt(statement); if (codeSize > 3) // if the original code includes DUP. 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.atMethodCallCore(newType, MethodInfo.nameInit, args, false, true, -1, null); gen.setType(newType); } public void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError { c.atMethodCallCore(newType, MethodInfo.nameInit, args); c.setType(newType); } } } javassist-3.12.1.ga/src/main/javassist/expr/FieldAccess.java0000644000175000017500000002362010636733630023653 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.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; protected 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 signature of the field type. * The signature is represented by a character string * called field descriptor, which is defined in the JVM specification. * * @see javassist.bytecode.Descriptor#toCtClass(String, ClassPool) * @since 3.1 */ public String getSignature() { int index = iterator.u16bitAt(currentPos + 1); return getConstPool().getFieldrefType(index); } /** * Replaces the method call with the bytecode derived from * the given source text. * *

    $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 { thisClass.getClassFile(); // to call checkModify(). 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); if (read) included = true; 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.recordLocalVariables(ca, pos); if (included) if (retType == CtClass.voidType) { bytecode.addOpcode(ACONST_NULL); bytecode.addAstore(retVar); } else { bytecode.addConstZero(retType); bytecode.addStore(retVar, retType); // initialize $_ } 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"); } } /* $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); } public void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError { c.setType(fieldType); } } /* void $proceed() * 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.getMethodArgsLength(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(); } public void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError { c.atMethodArgs(args, new int[1], new int[1], new String[1]); c.setType(CtClass.voidType); c.addNullIfVoid(); } } } javassist-3.12.1.ga/src/main/javassist/expr/package.html0000644000175000017500000000024407651544225023124 0ustar twernertwerner

    This package contains the classes for modifying a method body. See ExprEditor (expression editor) for more details. javassist-3.12.1.ga/src/main/javassist/expr/Handler.java0000644000175000017500000001055411213421076023053 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.expr; import javassist.*; import javassist.bytecode.*; import javassist.compiler.*; /** * A catch clause or a finally block. */ public class Handler extends Expr { private static String EXCEPTION_NAME = "$1"; private ExceptionTable etable; private int index; /** * Undocumented constructor. Do not use; internal-use only. */ protected Handler(ExceptionTable et, int nth, CodeIterator it, CtClass declaring, MethodInfo m) { super(et.handlerPc(nth), it, declaring, m); etable = et; index = nth; } /** * Returns the method or constructor containing the catch clause. */ public CtBehavior where() { return super.where(); } /** * Returns the source line number of the catch clause. * * @return -1 if this information is not available. */ public int getLineNumber() { return super.getLineNumber(); } /** * Returns the source file containing the catch clause. * * @return null if this information is not available. */ public String getFileName() { return super.getFileName(); } /** * Returns the list of exceptions that the catch clause may throw. */ public CtClass[] mayThrow() { return super.mayThrow(); } /** * Returns the type handled by the catch clause. * If this is a finally block, null is returned. */ public CtClass getType() throws NotFoundException { int type = etable.catchType(index); if (type == 0) return null; else { ConstPool cp = getConstPool(); String name = cp.getClassInfo(type); return thisClass.getClassPool().getCtClass(name); } } /** * Returns true if this is a finally block. */ public boolean isFinally() { return etable.catchType(index) == 0; } /** * This method has not been implemented yet. * * @param statement a Java statement. */ public void replace(String statement) throws CannotCompileException { throw new RuntimeException("not implemented yet"); } /** * Inserts bytecode at the beginning of the catch clause. * The caught exception is stored in $1. * * @param src the source code representing the inserted bytecode. * It must be a single statement or block. */ public void insertBefore(String src) throws CannotCompileException { edited = true; ConstPool cp = getConstPool(); CodeAttribute ca = iterator.get(); Javac jv = new Javac(thisClass); Bytecode b = jv.getBytecode(); b.setStackDepth(1); b.setMaxLocals(ca.getMaxLocals()); try { CtClass type = getType(); int var = jv.recordVariable(type, EXCEPTION_NAME); jv.recordReturnType(type, false); b.addAstore(var); jv.compileStmnt(src); b.addAload(var); int oldHandler = etable.handlerPc(index); b.addOpcode(Opcode.GOTO); b.addIndex(oldHandler - iterator.getCodeLength() - b.currentPc() + 1); maxStack = b.getMaxStack(); maxLocals = b.getMaxLocals(); int pos = iterator.append(b.get()); iterator.append(b.getExceptionTable(), pos); etable.setHandlerPc(index, pos); } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (CompileError e) { throw new CannotCompileException(e); } } } javassist-3.12.1.ga/src/main/javassist/expr/ConstructorCall.java0000644000175000017500000000413510630701321024611 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.expr; import javassist.CtClass; import javassist.CtConstructor; import javassist.CtMethod; import javassist.NotFoundException; import javassist.bytecode.CodeIterator; import javassist.bytecode.MethodInfo; /** * Constructor call such as this() and super() * within a constructor body. * * @see NewExpr */ public class ConstructorCall extends MethodCall { /** * Undocumented constructor. Do not use; internal-use only. */ protected ConstructorCall(int pos, CodeIterator i, CtClass decl, MethodInfo m) { super(pos, i, decl, m); } /** * Returns "super" or ""this". */ public String getMethodName() { return isSuper() ? "super" : "this"; } /** * Always throws a NotFoundException. * * @see #getConstructor() */ public CtMethod getMethod() throws NotFoundException { throw new NotFoundException("this is a constructor call. Call getConstructor()."); } /** * Returns the called constructor. */ public CtConstructor getConstructor() throws NotFoundException { return getCtClass().getConstructor(getSignature()); } /** * Returns true if the called constructor is not this() * but super() (a constructor declared in the super class). */ public boolean isSuper() { return super.isSuper(); } } javassist-3.12.1.ga/src/main/javassist/expr/NewArray.java0000644000175000017500000002213610636733630023237 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.expr; import javassist.*; import javassist.bytecode.*; import javassist.compiler.*; import javassist.compiler.ast.ASTList; /** * Array creation. * *

    This class does not provide methods for obtaining the initial * values of array elements. */ public class NewArray extends Expr { int opcode; protected NewArray(int pos, CodeIterator i, CtClass declaring, MethodInfo m, int op) { super(pos, i, declaring, m); opcode = op; } /** * Returns the method or constructor containing the array creation * represented by this object. */ public CtBehavior where() { return super.where(); } /** * Returns the line number of the source line containing the * array creation. * * @return -1 if this information is not available. */ public int getLineNumber() { return super.getLineNumber(); } /** * Returns the source file containing the array creation. * * @return null if this information is not available. */ public String getFileName() { return super.getFileName(); } /** * 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 array components. If the created array is * a two-dimensional array of int, * the type returned by this method is * not int[] but int. */ public CtClass getComponentType() throws NotFoundException { if (opcode == Opcode.NEWARRAY) { int atype = iterator.byteAt(currentPos + 1); return getPrimitiveType(atype); } else if (opcode == Opcode.ANEWARRAY || opcode == Opcode.MULTIANEWARRAY) { int index = iterator.u16bitAt(currentPos + 1); String desc = getConstPool().getClassInfo(index); int dim = Descriptor.arrayDimension(desc); desc = Descriptor.toArrayComponent(desc, dim); return Descriptor.toCtClass(desc, thisClass.getClassPool()); } else throw new RuntimeException("bad opcode: " + opcode); } CtClass getPrimitiveType(int atype) { switch (atype) { case Opcode.T_BOOLEAN : return CtClass.booleanType; case Opcode.T_CHAR : return CtClass.charType; case Opcode.T_FLOAT : return CtClass.floatType; case Opcode.T_DOUBLE : return CtClass.doubleType; case Opcode.T_BYTE : return CtClass.byteType; case Opcode.T_SHORT : return CtClass.shortType; case Opcode.T_INT : return CtClass.intType; case Opcode.T_LONG : return CtClass.longType; default : throw new RuntimeException("bad atype: " + atype); } } /** * Returns the dimension of the created array. */ public int getDimension() { if (opcode == Opcode.NEWARRAY) return 1; else if (opcode == Opcode.ANEWARRAY || opcode == Opcode.MULTIANEWARRAY) { int index = iterator.u16bitAt(currentPos + 1); String desc = getConstPool().getClassInfo(index); return Descriptor.arrayDimension(desc) + (opcode == Opcode.ANEWARRAY ? 1 : 0); } else throw new RuntimeException("bad opcode: " + opcode); } /** * Returns the number of dimensions of arrays to be created. * If the opcode is multianewarray, this method returns the second * operand. Otherwise, it returns 1. */ public int getCreatedDimensions() { if (opcode == Opcode.MULTIANEWARRAY) return iterator.byteAt(currentPos + 3); else return 1; } /** * Replaces the array creation with the bytecode derived from * the given source text. * *

    $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 { try { replace2(statement); } catch (CompileError e) { throw new CannotCompileException(e); } catch (NotFoundException e) { throw new CannotCompileException(e); } catch (BadBytecode e) { throw new CannotCompileException("broken method"); } } private void replace2(String statement) throws CompileError, NotFoundException, BadBytecode, CannotCompileException { thisClass.getClassFile(); // to call checkModify(). ConstPool constPool = getConstPool(); int pos = currentPos; CtClass retType; int codeLength; int index = 0; int dim = 1; String desc; if (opcode == Opcode.NEWARRAY) { index = iterator.byteAt(currentPos + 1); // atype CtPrimitiveType cpt = (CtPrimitiveType)getPrimitiveType(index); desc = "[" + cpt.getDescriptor(); codeLength = 2; } else if (opcode == Opcode.ANEWARRAY) { index = iterator.u16bitAt(pos + 1); desc = constPool.getClassInfo(index); if (desc.startsWith("[")) desc = "[" + desc; else desc = "[L" + desc + ";"; codeLength = 3; } else if (opcode == Opcode.MULTIANEWARRAY) { index = iterator.u16bitAt(currentPos + 1); desc = constPool.getClassInfo(index); dim = iterator.byteAt(currentPos + 3); codeLength = 4; } else throw new RuntimeException("bad opcode: " + opcode); retType = Descriptor.toCtClass(desc, thisClass.getClassPool()); Javac jc = new Javac(thisClass); CodeAttribute ca = iterator.get(); CtClass[] params = new CtClass[dim]; for (int i = 0; i < dim; ++i) params[i] = CtClass.intType; int paramVar = ca.getMaxLocals(); jc.recordParams(javaLangObject, params, true, paramVar, withinStatic()); /* Is $_ included in the source code? */ checkResultValue(retType, statement); int retVar = jc.recordReturnType(retType, true); jc.recordProceed(new ProceedForArray(retType, opcode, index, dim)); Bytecode bytecode = jc.getBytecode(); storeStack(params, true, paramVar, bytecode); jc.recordLocalVariables(ca, pos); bytecode.addOpcode(ACONST_NULL); // initialize $_ bytecode.addAstore(retVar); jc.compileStmnt(statement); bytecode.addAload(retVar); replace0(pos, bytecode, codeLength); } /* $proceed( ..) */ static class ProceedForArray implements ProceedHandler { CtClass arrayType; int opcode; int index, dimension; ProceedForArray(CtClass type, int op, int i, int dim) { arrayType = type; opcode = op; index = i; dimension = dim; } public void doit(JvstCodeGen gen, Bytecode bytecode, ASTList args) throws CompileError { int num = gen.getMethodArgsLength(args); if (num != dimension) throw new CompileError(Javac.proceedName + "() with a wrong number of parameters"); gen.atMethodArgs(args, new int[num], new int[num], new String[num]); bytecode.addOpcode(opcode); if (opcode == Opcode.ANEWARRAY) bytecode.addIndex(index); else if (opcode == Opcode.NEWARRAY) bytecode.add(index); else /* if (opcode == Opcode.MULTIANEWARRAY) */ { bytecode.addIndex(index); bytecode.add(dimension); bytecode.growStack(1 - dimension); } gen.setType(arrayType); } public void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError { c.setType(arrayType); } } } javassist-3.12.1.ga/src/main/javassist/expr/Cast.java0000644000175000017500000001243311033365276022377 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.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. */ protected 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 CtClass 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 thisClass.getClassPool().getCtClass(name); } /** * 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. * *

    $0 is available but the value is null. * * @param statement a Java statement. */ public void replace(String statement) throws CannotCompileException { thisClass.getClassFile(); // to call checkModify(). 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.recordLocalVariables(ca, pos); bytecode.addConstZero(retType); bytecode.addStore(retVar, retType); // initialize $_ 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"); } } /* $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.getMethodArgsLength(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); } public void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError { c.atMethodArgs(args, new int[1], new int[1], new String[1]); c.setType(retType); } } } javassist-3.12.1.ga/src/main/javassist/CtNewWrappedMethod.java0000644000175000017500000001660711061450245024232 0ustar twernertwerner/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist; 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; } /* The generated method body does not need a stack map table * because it does not contain a branch instruction. */ 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, CannotCompileException { 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)); body.addAttribute(new SyntheticAttribute(classfile.getConstPool())); // a stack map is copied. rebuilding it is not needed. classfile.addMethod(body); bodies.put(src, bodyname); CtMember.Cache cache = clazz.hasMemberCache(); if (cache != null) cache.addMethod(new CtMethod(body, clazz)); } 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); } } } javassist-3.12.1.ga/build.xml0000644000175000017500000002455411363550310015761 0ustar twernertwerner To run the sample programs without ant, change the current directory to ${build.classes.dir}. Javassist]]> Javassist, a Java-bytecode translator toolkit.
    Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.
    ]]> ** please run sample-rmi, sample-evolve, and sample-hotswap (or -hotswap5) separately ** run sample.duplicate.Viewer without reflection run sample.duplicate.Viewer with reflection sample.preproc.Compiler sample/vector/Test.j javac sample/vector/Test.java ** Please open sample/rmi/webdemo.html with your browser ** ** Please open http://localhost:5003/demo.html with your browser ** ** JAVA_HOME/lib/tools.jar must be included in CLASS_PATH ** for JDK 1.4 ** JAVA_HOME/lib/tools.jar must be included in CLASS_PATH ** for JDK 1.5 or later javassist-3.12.1.ga/lib/0000755000175000017500000000000011637463211014703 5ustar twernertwernerjavassist-3.12.1.ga/Readme.html0000644000175000017500000006234311361635321016225 0ustar twernertwerner Read Me First

    Javassist version 3

    Copyright (C) 1999-2010 by Shigeru Chiba, All rights reserved.


    Javassist (JAVA programming ASSISTant) makes Java bytecode manipulation simple. 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. You can even specify inserted bytecode in the form of source text; Javassist compiles it on the fly. On the other hand, the bytecode-level API allows the users to directly edit a class file as other editors.


    Files


    How to run sample programs

    JDK 1.4 or later is needed.

    0. If you have Apache Ant

    Run the sample-all task. Otherwise, follow the instructions below.

    1. Move to the directory where this Readme.html file is located.

    In the following instructions, we assume that the javassist.jar file is included in the class path. For example, the javac and java commands must receive the following classpath option:

      -classpath ".:javassist.jar"
      

    If the operating system is Windows, the path separator must be not : (colon) but ; (semicolon). The java command can receive the -cp option as well as -classpath.

    If you don't want to use the class-path option, you can make javassist.jar included in the CLASSPATH environment:

      export CLASSPATH=.:javassist.jar
      

    or if the operating system is Windows:

      set CLASSPATH=.;javassist.jar
      

    Otherwise, you can copy javassist.jar to the directory

      <java-home>/jre/lib/ext.

    <java-home> depends on the system. It is usually /usr/local/java, c:\j2sdk1.4\, etc.

    2. sample/Test.java

    This is a very simple program using Javassist.

    To run, type the commands:

      % javac sample/Test.java
      % java sample.Test
      

    For more details, see sample/Test.java

    3. sample/reflect/*.java

    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.

    To run, type the commands:

      % javac sample/reflect/*.java
      % java javassist.tools.reflect.Loader sample.reflect.Main Joe
      

    Compare this result with that of the regular execution without reflection:

      % java sample.reflect.Person Joe

    For more details, see sample/reflect/Main.java

    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:

      % java javassist.tools.reflect.Compiler sample.reflect.Person -m sample.reflect.VerboseMetaobj
      

    Then,

      % java sample.reflect.Person Joe
      

    4. sample/duplicate/*.java

    This is another example of reflective programming.

    To run, type the commands:

      % javac sample/duplicate/*.java
      % java sample.duplicate.Main
      

    Compare this result with that of the regular execution without reflection:

      % java sample.duplicate.Viewer

    For more details, see sample/duplicate/Main.java

    5. sample/vector/*.java

    This example shows the use of Javassit for producing a class representing a vector of a given type at compile time.

    To run, type the commands:

      % javac sample/vector/*.java
      % java sample.preproc.Compiler sample/vector/Test.j
      % javac sample/vector/Test.java
      % java sample.vector.Test
      

    Note: javassist.jar is unnecessary to compile and execute sample/vector/Test.java. For more details, see sample/vector/Test.j and sample/vector/VectorAssistant.java

    6. sample/rmi/*.java

    This demonstrates the javassist.rmi package.

    To run, type the commands:

      % javac sample/rmi/*.java
      % java sample.rmi.Counter 5001
      

    The second line starts a web server listening to port 5001.

    Then, open sample/rmi/webdemo.html with a web browser running on the local host. (webdemo.html trys to fetch an applet from http://localhost:5001/, which is the web server we started above.)

    Otherwise, run sample.rmi.CountApplet as an application:

      % java javassist.web.Viewer localhost 5001 sample.rmi.CountApplet
      

    7. sample/evolve/*.java

    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.

    To run, type the commands:

      % javac sample/evolve/*.java
      % java sample.evolve.DemoLoader 5003
      

    The second line starts a class loader DemoLoader, which runs a web server DemoServer listening to port 5003.

    Then, open http://localhost:5003/demo.html with a web browser running on the local host. (Or, see sample/evolve/start.html.)

    8. sample/hotswap/*.java

    This shows dynamic class reloading by the JPDA. It needs JDK 1.4 or later. To run, first type the following commands:

      % cd sample/hotswap
      % javac *.java
      % cd logging
      % javac *.java
      % cd ..
      

    If your Java is 1.4, then type:

      % java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000 Test
      

    If you are using Java 5, then type:

      % java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 Test
      

    Note that the class path must include JAVA_HOME/lib/tools.jar.

    Hints

    To know the version number, type this command:

      % java -jar javassist.jar
      

    Javassist provides a class file viewer for debugging. For more details, see javassist.Dump.


    Changes

    -version 3.12 on April 16, 2010

    -version 3.11 on July 3, 2009

    • JIRA JASSIST-67, 68, 74, 75, 76, 77, 81, 83, 84, 85, 86, 87 were fixed.
    • Now javassist.bytecode.CodeIterator can insert a gap into a large method body more than 32KB. (JIRA JASSIST-79, 80)

    -version 3.10 on March 5, 2009

    • JIRA JASSIST-69, 70, 71 were fixed.

    -version 3.9 on October 9, 2008

    • ClassPool.makeClassIfNew(InputStream) was implemented.
    • CtNewMethod.wrapped(..) and CtNewConstructor.wrapped(..) implicitly append a method like _added_m$0. This method now has a synthetic attribute.
    • JIRA JASSIST-66 has been fixed.

    -version 3.8.1 on July 17, 2008

    • CtClass.rebuildClassFile() has been added.
    • A few bugs of javassist.bytecode.analysis have been fixed. 3.8.0 could not correctly deal with one letter class name such as I and J.

    -version 3.8.0 on June 13, 2008

    • javassist.bytecode.analysis was implemented.
    • JASSIST-45, 47, 51, 54-57, 60, 62 were fixed.

    -version 3.7.1 on March 10, 2008

    • a bug of javassist.util.proxy has been fixed.

    -version 3.7 on January 20, 2008

    • Several minor bugs have been fixed.

    -version 3.6.0 on September 13, 2007

    -version 3.6.0.CR1 on July 27, 2007

    • The stack map table introduced since Java 6 has been supported.
    • CtClass#getDeclaredBehaviors() now returns a class initializer as well as methods and constructors.
    • The default status of automatic pruning was made off. Instead of pruning, this version of Javassist compresses the data structure of a class file after toBytecode() is called. The compressed class file is automatically decompressed when needed. This saves memory space better than pruning.
    • JIRA JASSIST-33 has been fixed.

    -version 3.5 on April 29, 2007

    • Various minor updates.

    -version 3.4 on November 17, 2006

    • A bug in CodeConverter#replaceFieldRead() and CodeConverter#replaceFieldWrite() was fixed. JBAOP-284.
    • A synchronization bug and a performance bug in javassist.util.proxy have been fixed (JASSIST-28). Now generated proxy classes are cached. To turn the caching off, set ProxyFactory.useCache to false.

    -version 3.3 on August 17, 2006

    • CtClass#toClass() and ClassPool#toClass() were modified to accept a ProtectionDomain (JASSIST-23). Now ClassPool#toClass(CtClass, ClassLoader) should not be overridden. All subclasses of ClassPool must override toClass(CtClass, ClassLoader, ProtectionDomain).
    • CtClass#getAvailableAnnotations() etc. have been implemented.
    • A bug related to a way of dealing with a bridge method was fixed (HIBERNATE-37).
    • javassist.scopedpool package was added.

    -version 3.2 on June 21, 2006

    • The behavior of CtBehavior#getParameterAnnotations() has been changed. It is now compatible to Java Reflection API (JASSIST-19).

    - version 3.2.0.CR2 on May 9, 2006

    • A bug of replace(String,ExprEditor) in javassist.expr.Expr has been fixed.
    • Updated ProxyFactory getClassLoader to choose the javassit class loader when the proxy superclass has a null class loader (a jdk/endorsed class) (JASSIST-18).
    • Updated the throws clause of the javassist.util.proxy.MethodHandler to be Throwable rather than Exception (JASSIST-16).

    - version 3.2.0.CR1 on March 18, 2006

    • Annotations enhancements to javassist.bytecode.MethodInfo.
    • Allow a ClassPool to override the "guess" at the classloader to use.

    - version 3.1 on February 23, 2006

    • getFields(), getMethods(), and getConstructors() in CtClass were changed to return non-private memebers instead of only public members.
    • getEnclosingClass() in javassist.CtClass was renamed to getEnclosingMethod().
    • getModifiers() was extended to return Modifier.STATIC if the class is a static inner class.
    • The return type of CtClass.stopPruning() was changed from void to boolean.
    • toMethod() in javassist.CtConstructor has been implemented.
    • It includes new javassist.util.proxy package similar to Enhancer of CGLIB.

    • The subpackages of Javassist were restructured.
      • javassist.tool package was renamed to javassist.tools.
      • HotSwapper was moved to javassist.util.
      • Several subpackages were moved to javassist.tools.
      • javassist.preproc package was elminated and the source was moved to the sample directory.

    - version 3.1 RC2 on September 7, 2005

    • RC2 is released mainly for an administrative reason.
    • A few bugs have been fixed.

    - version 3.1 RC1 on August 29, 2005

    • Better annotation supports. See CtClass.getAnnotations()
    • javassist.tool.HotSwapper was added.
    • javassist.ClassPool.importPackage() was added.
    • The compiler now accepts array initializers (only one dimensional arrays).
    • javassist.Dump was moved to javassist.tool.Dump.
    • Many bugs were fixed.

    - version 3.0 on January 18, 2005

    • The compiler now supports synchronized statements and finally clauses.
    • You can now remove a method and a field.

    - version 3.0 RC1 on September 13, 2004.

    • CtClass.toClass() has been reimplemented. The behavior has been changed.
    • javassist.expr.NewArray has been implemented. It enables modifying an expression for array creation.
    • .class notation has been supported. The modified class file needs javassist.runtime.DotClass at runtime.
    • a bug in CtClass.getMethods() has been fixed.
    • The compiler supports a switch statement.

    - version 3.0 beta on May 18th, 2004.

    • The ClassPool framework has been redesigned.
      • writeFile(), write(), ... in ClassPool have been moved to CtClass.
      • The design of javassist.Translator has been changed.
    • javassist.bytecode.annotation has been added for meta tags.
    • CtClass.makeNestedClass() has been added.
    • The methods declared in javassist.bytecode.InnerClassesAttribute have been renamed a bit.
    • Now local variables were made available in the source text passed to CtBehavior.insertBefore(), MethodCall.replace(), etc.
    • CtClass.main(), which prints the version number, has been added.
    • ClassPool.SimpleLoader has been public.
    • javassist.bytecode.DeprecatedAttribute has been added.
    • javassist.bytecode.LocalVariableAttribute has been added.
    • CtClass.getURL() and javassist.ClassPath.find() has been added.
    • CtBehavior.insertAt() has been added.
    • CtClass.detach() has been added.
    • CodeAttribute.computeMaxStack() has been added.

    - version 2.6 in August, 2003.

    • The behavior of CtClass.setSuperclass() was changed. To obtain the previous behavior, call CtClass.replaceClassName().
    • CtConstructor.setBody() now works for class initializers.
    • CtNewMethod.delegator() now works for static methods.
    • javassist.expr.Expr.indexOfBytecode() has been added.
    • javassist.Loader has been modified so that getPackage() returns a package object.
    • Now, the compiler can correctly compile a try statement and an infinite while-loop.

    - version 2.5.1 in May, 2003.
    Simple changes for integration with JBoss AOP

    • Made ClassPool.get0 protected so that subclasses of ClassPool can call it.
    • Moved all access to the class cache (the field ClassPool.classes) to a method called getCached(String classname). This is so subclasses of ClassPool can override this behavior.

    - version 2.5 in May, 2003.
    From this version, Javassist is part of the JBoss project.

    • The license was changed from MPL to MPL/LGPL dual.
    • ClassPool.removeClassPath() and ClassPath.close() have been added.
    • ClassPool.makeClass(InputStream) has been added.
    • CtClass.makeClassInitializer() has been added.
    • javassist.expr.Expr has been changed to a public class.
    • javassist.expr.Handler has been added.
    • javassist.expr.MethodCall.isSuper() has been added.
    • CtMethod.isEmpty() and CtConstructor.isEmpty() have been added.
    • LoaderClassPath has been implemented.

    - version 2.4 in February, 2003.

    • The compiler included in Javassist did not correctly work with interface methods. This bug was fixed.
    • Now javassist.bytecode.Bytecode allows more than 255 local variables in the same method.
    • javassist.expr.Instanceof and Cast have been added.
    • javassist.expr.{MethodCall,NewExpr,FieldAccess,Instanceof,Cast}.where() have been added. They return the caller-side method surrounding the expression.
    • javassist.expr.{MethodCall,NewExpr,FieldAccess,Instanceof,Cast}.mayThrow() have been added.
    • $class has been introduced.
    • The parameters to replaceFieldRead(), replaceFieldWrite(), and redirectFieldAccess() in javassist.CodeConverter are changed.
    • The compiler could not correctly handle a try-catch statement. This bug has been fixed.

    - version 2.3 in December, 2002.

    • The tutorial has been revised a bit.
    • SerialVersionUID class was donated by Bob Lee. Thanks.
    • CtMethod.setBody() and CtConstructor.setBody() have been added.
    • 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.
    • $sig and $type are now available.
    • Bugs in Bytecode.write() and read() have been fixed.

    - version 2.2 in October, 2002.

    • The tutorial has been revised.
    • A new package javassist.expr has been added. This is replacement of classic CodeConverter.
    • javassist.ConstParameter was changed into javassist.CtMethod.ConstParameter.
    • javassist.FieldInitializer was renamed into javassist.CtField.Initializer.
    • A bug in javassist.bytecode.Bytecode.addInvokeinterface() has been fixed.
    • In javassist.bytecode.Bytecode, addGetfield(), addGetstatic(), addInvokespecial(), addInvokestatic(), addInvokevirtual(), and addInvokeinterface() have been modified to update the current statck depth.

    - version 2.1 in July, 2002.

    • javassist.CtMember and javassist.CtBehavior have been added.
    • javassist.CtClass.toBytecode() has been added.
    • javassist.CtClass.toClass() and javassist.ClassPool.writeAsClass() has been added.
    • javassist.ByteArrayClassPath has been added.
    • javassist.bytecode.Mnemonic has been added.
    • Several bugs have been fixed.

    - version 2.0 (major update) in November, 2001.

    • 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.
    • The mechanism for creating CtClass objects have been changed.
    • javassist.tool.Dump moves to the javassist package.

    version 1.0 in July, 2001.

    • javassist.reflect.Metaobject and ClassMetaobject was changed. Now they throw the same exception that they receive from a base-level object.

    - version 0.8

    • javassist.tool.Dump was added. It is a class file viewer.
    • javassist.FiledInitializer.byNewArray() was added. It is for initializing a field with an array object.
    • javassist.CodeConverter.redirectMethodCall() was added.
    • javassist.Run was added.

    - version 0.7

    • 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).
    • 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.
    • Constructors are now represented by CtConstructor objects. CtConstructor is a subclass of CtMethod.
    • CtClass.getUserAttribute() was removed. Use CtClass.getAttribute().
    • javassist.rmi.RmiLoader was added.
    • javassist.reflect.Metalevel._setMetaobject() was added. Now metaobjects can be replaced at runtime.

    - version 0.6

    • Javassist was modified to correctly deal with array types appearing in signatures.
    • A bug crashed resulting bytecode if a class includes a private static filed. It has been fixed.
    • javassist.CtNewInterface was added.
    • javassist.Loader.recordClass() was renamed into makeClass().
    • javassist.UserLoader.loadClass() was changed to take the second parameter.

    - version 0.5

    • a bug-fix version.

    - version 0.4

    • Major update again. Many classes and methods were changed. Most of methods taking java.lang.Class have been changed to take javassist.CtClass.

    - version 0.3

    • Major update. Many classes and methods were changed.

    - version 0.2

    • Jar/zip files are supported.

    -version 0.1 on April 16, 1999.

    • The first release.


    Bug reports etc.

    Bug reports:
    Post your reports at Forums or directly send an email to:
      chiba@acm.org or chiba@is.titech.ac.jp

    The home page of Javassist:
    Visit www.javassist.org and www.jboss.org


    Copyright notices

    Javassist, a Java-bytecode translator toolkit.
    Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.

    The contents of this software, Javassist, are subject to the Mozilla Public License Version 1.1 (the "License");
    you may not use this software except in compliance with the License. You may obtain a copy of the License at
    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 the Initial Developer are
      Copyright (C) 1999-2010 Shigeru Chiba. All Rights Reserved.

    Contributor(s): ______________________________________.

    Alternatively, the contents of this software may be used under the terms of the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which case the provisions of the LGPL are applicable instead of those above. If you wish to allow use of your version of this software only under the terms of the LGPL, and not to allow others to use your version of this software under the terms of the MPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the LGPL. If you do not delete the provisions above, a recipient may use your version of this software under the terms of either the MPL or the LGPL.

    If you obtain this software as part of JBoss, the contents of this software may be used under only the terms of the LGPL. To use them under the MPL, you must obtain a separate package including only Javassist but not the other part of JBoss.

    All the contributors to the original source tree must agree to the original license term described above.


    Acknowledgments

    The development of this software is sponsored in part by the PRESTO and CREST programs of Japan Science and Technology Corporation.

    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, Dante Torres estrada, S. Pam, Nuno Santos, Denis Taye, Colin Sampaleanu, Robert Bialek, Asato Shimotaki, Howard Lewis Ship, Richard Jones, Marjan Sterjev, Bruce McDonald, Mark Brennan, Vlad Skarzhevskyy, Brett Randall, Tsuyoshi Murakami, Nathan Meyers, Yoshiyuki Usui Yutaka Sunaga, Arjan van der Meer, Bruce Eckel, Guillaume Pothier, Kumar Matcha, Andreas Salathe, Renat Zubairov, Armin Haaf, Emmanuel Bernard and all other contributors for their contributions.



    Shigeru Chiba (Email: chiba@acm.org)
    Dept. of Math. and Computing Sciences, Tokyo Institute of Technology javassist-3.12.1.ga/.project0000644000175000017500000000055310563133741015606 0ustar twernertwerner jvst org.eclipse.jdt.core.javabuilder org.eclipse.jdt.core.javanature