com.feilong.lib.javassist.bytecode.ClassFileWriter Maven / Gradle / Ivy
Show all versions of feilong Show documentation
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- 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,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" 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 com.feilong.lib.javassist.bytecode;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* 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.numOfMethods());
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.numOfMethods());
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 interface AttributeWriter{
/**
* Returns the number of attributes that this writer will
* write.
*/
int size();
/**
* Writes all the contents of the attributes. The binary representation
* of the contents is an array of attribute_info
.
*/
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_info
s.
* 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 exception : exceptions){
output.writeShort(exception);
}
}
/**
* 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);
}
/**
* Returns the length of the bytecode that has been added so far.
*
* @return the length in bytes.
* @since 3.19
*/
public int size(){
return output.getPos() - startPos - 14;
}
int numOfMethods(){
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_info
s.
*/
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_MethodHandle_info
* structure.
*
* @param kind
* reference_kind
* such as {@link ConstPool#REF_invokeStatic REF_invokeStatic
}.
* @param index
* reference_index
.
* @return the index of the added entry.
*
* @since 3.17.1
*/
public int addMethodHandleInfo(int kind,int index){
output.write(MethodHandleInfo.tag);
output.write(kind);
output.writeShort(index);
return num++;
}
/**
* Adds a new CONSTANT_MethodType_info
* structure.
*
* @param desc
* descriptor_index
.
* @return the index of the added entry.
*
* @since 3.17.1
*/
public int addMethodTypeInfo(int desc){
output.write(MethodTypeInfo.tag);
output.writeShort(desc);
return num++;
}
/**
* Adds a new CONSTANT_InvokeDynamic_info
* structure.
*
* @param bootstrap
* bootstrap_method_attr_index
.
* @param nameAndTypeInfo
* name_and_type_index
.
* @return the index of the added entry.
*
* @since 3.17.1
*/
public int addInvokeDynamicInfo(int bootstrap,int nameAndTypeInfo){
output.write(InvokeDynamicInfo.tag);
output.writeShort(bootstrap);
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){
int utf8 = addUtf8Info(str);
output.write(StringInfo.tag);
output.writeShort(utf8);
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);
}
}
}