📄 classfilewriter.java
字号:
package org.mozilla.classfile;import org.mozilla.javascript.ObjToIntMap;import org.mozilla.javascript.ObjArray;import org.mozilla.javascript.UintMap;import java.io.*;/** * ClassFileWriter * * A ClassFileWriter is used to write a Java class file. Methods are provided to * create fields and methods, and within methods to write Java bytecodes. * * @author Roger Lawrence */public class ClassFileWriter { /** * Construct a ClassFileWriter for a class. * * @param className * the name of the class to write, including full package * qualification. * @param superClassName * the name of the superclass of the class to write, including * full package qualification. * @param sourceFileName * the name of the source file to use for producing debug * information, or null if debug information is not desired */ public ClassFileWriter(String className, String superClassName, String sourceFileName) { generatedClassName = className; itsConstantPool = new ConstantPool(this); itsThisClassIndex = itsConstantPool.addClass(className); itsSuperClassIndex = itsConstantPool.addClass(superClassName); if (sourceFileName != null) itsSourceFileNameIndex = itsConstantPool.addUtf8(sourceFileName); itsFlags = ACC_PUBLIC; } public final String getClassName() { return generatedClassName; } /** * Add an interface implemented by this class. * * This method may be called multiple times for classes that implement * multiple interfaces. * * @param interfaceName * a name of an interface implemented by the class being written, * including full package qualification. */ public void addInterface(String interfaceName) { short interfaceIndex = itsConstantPool.addClass(interfaceName); itsInterfaces.add(new Short(interfaceIndex)); } public static final short ACC_PUBLIC = 0x0001, ACC_PRIVATE = 0x0002, ACC_PROTECTED = 0x0004, ACC_STATIC = 0x0008, ACC_FINAL = 0x0010, ACC_SYNCHRONIZED = 0x0020, ACC_VOLATILE = 0x0040, ACC_TRANSIENT = 0x0080, ACC_NATIVE = 0x0100, ACC_ABSTRACT = 0x0400; /** * Set the class's flags. * * Flags must be a set of the following flags, bitwise or'd together: * ACC_PUBLIC ACC_PRIVATE ACC_PROTECTED ACC_FINAL ACC_ABSTRACT TODO: check * that this is the appropriate set * * @param flags * the set of class flags to set */ public void setFlags(short flags) { itsFlags = flags; } static String getSlashedForm(String name) { return name.replace('.', '/'); } /** * Convert Java class name in dot notation into * "Lname-with-dots-replaced-by-slashes;" form suitable for use as JVM type * signatures. */ public static String classNameToSignature(String name) { int nameLength = name.length(); int colonPos = 1 + nameLength; char[] buf = new char[colonPos + 1]; buf[0] = 'L'; buf[colonPos] = ';'; name.getChars(0, nameLength, buf, 1); for (int i = 1; i != colonPos; ++i) { if (buf[i] == '.') { buf[i] = '/'; } } return new String(buf, 0, colonPos + 1); } /** * Add a field to the class. * * @param fieldName * the name of the field * @param type * the type of the field using ... * @param flags * the attributes of the field, such as ACC_PUBLIC, etc. bitwise * or'd together */ public void addField(String fieldName, String type, short flags) { short fieldNameIndex = itsConstantPool.addUtf8(fieldName); short typeIndex = itsConstantPool.addUtf8(type); itsFields.add(new ClassFileField(fieldNameIndex, typeIndex, flags)); } /** * Add a field to the class. * * @param fieldName * the name of the field * @param type * the type of the field using ... * @param flags * the attributes of the field, such as ACC_PUBLIC, etc. bitwise * or'd together * @param value * an initial integral value */ public void addField(String fieldName, String type, short flags, int value) { short fieldNameIndex = itsConstantPool.addUtf8(fieldName); short typeIndex = itsConstantPool.addUtf8(type); ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, flags); field.setAttributes(itsConstantPool.addUtf8("ConstantValue"), (short) 0, (short) 0, itsConstantPool.addConstant(value)); itsFields.add(field); } /** * Add a field to the class. * * @param fieldName * the name of the field * @param type * the type of the field using ... * @param flags * the attributes of the field, such as ACC_PUBLIC, etc. bitwise * or'd together * @param value * an initial long value */ public void addField(String fieldName, String type, short flags, long value) { short fieldNameIndex = itsConstantPool.addUtf8(fieldName); short typeIndex = itsConstantPool.addUtf8(type); ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, flags); field.setAttributes(itsConstantPool.addUtf8("ConstantValue"), (short) 0, (short) 2, itsConstantPool.addConstant(value)); itsFields.add(field); } /** * Add a field to the class. * * @param fieldName * the name of the field * @param type * the type of the field using ... * @param flags * the attributes of the field, such as ACC_PUBLIC, etc. bitwise * or'd together * @param value * an initial double value */ public void addField(String fieldName, String type, short flags, double value) { short fieldNameIndex = itsConstantPool.addUtf8(fieldName); short typeIndex = itsConstantPool.addUtf8(type); ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex, flags); field.setAttributes(itsConstantPool.addUtf8("ConstantValue"), (short) 0, (short) 2, itsConstantPool.addConstant(value)); itsFields.add(field); } /** * Add Information about java variable to use when generating the local * variable table. * * @param name * variable name. * @param type * variable type as bytecode descriptor string. * @param startPC * the starting bytecode PC where this variable is live, or -1 if * it does not have a Java register. * @param register * the Java register number of variable or -1 if it does not have * a Java register. */ public void addVariableDescriptor(String name, String type, int startPC, int register) { int nameIndex = itsConstantPool.addUtf8(name); int descriptorIndex = itsConstantPool.addUtf8(type); int[] chunk = { nameIndex, descriptorIndex, startPC, register }; if (itsVarDescriptors == null) { itsVarDescriptors = new ObjArray(); } itsVarDescriptors.add(chunk); } /** * Add a method and begin adding code. * * This method must be called before other methods for adding code, * exception tables, etc. can be invoked. * * @param methodName * the name of the method * @param type * a string representing the type * @param flags * the attributes of the field, such as ACC_PUBLIC, etc. bitwise * or'd together */ public void startMethod(String methodName, String type, short flags) { short methodNameIndex = itsConstantPool.addUtf8(methodName); short typeIndex = itsConstantPool.addUtf8(type); itsCurrentMethod = new ClassFileMethod(methodNameIndex, typeIndex, flags); itsMethods.add(itsCurrentMethod); } /** * Complete generation of the method. * * After this method is called, no more code can be added to the method * begun with <code>startMethod</code>. * * @param maxLocals * the maximum number of local variable slots (a.k.a. Java * registers) used by the method * @param vars * the array of the variables for the method, or null if none */ public void stopMethod(short maxLocals) { if (itsCurrentMethod == null) throw new IllegalStateException("No method to stop"); fixLabelGotos(); itsMaxLocals = maxLocals; int lineNumberTableLength = 0; if (itsLineNumberTable != null) { // 6 bytes for the attribute header // 2 bytes for the line number count // 4 bytes for each entry lineNumberTableLength = 6 + 2 + (itsLineNumberTableTop * 4); } int variableTableLength = 0; if (itsVarDescriptors != null) { // 6 bytes for the attribute header // 2 bytes for the variable count // 10 bytes for each entry variableTableLength = 6 + 2 + (itsVarDescriptors.size() * 10); } int attrLength = 2 + // attribute_name_index 4 + // attribute_length 2 + // max_stack 2 + // max_locals 4 + // code_length itsCodeBufferTop + 2 + // exception_table_length (itsExceptionTableTop * 8) + 2 + // attributes_count lineNumberTableLength + variableTableLength; byte[] codeAttribute = new byte[attrLength]; int index = 0; int codeAttrIndex = itsConstantPool.addUtf8("Code"); index = putInt16(codeAttrIndex, codeAttribute, index); attrLength -= 6; // discount the attribute header index = putInt32(attrLength, codeAttribute, index); index = putInt16(itsMaxStack, codeAttribute, index); index = putInt16(itsMaxLocals, codeAttribute, index); index = putInt32(itsCodeBufferTop, codeAttribute, index); System.arraycopy(itsCodeBuffer, 0, codeAttribute, index, itsCodeBufferTop); index += itsCodeBufferTop; if (itsExceptionTableTop > 0) { index = putInt16(itsExceptionTableTop, codeAttribute, index); for (int i = 0; i < itsExceptionTableTop; i++) { ExceptionTableEntry ete = itsExceptionTable[i]; short startPC = (short) getLabelPC(ete.itsStartLabel); short endPC = (short) getLabelPC(ete.itsEndLabel); short handlerPC = (short) getLabelPC(ete.itsHandlerLabel); short catchType = ete.itsCatchType; if (startPC == -1) throw new IllegalStateException("start label not defined"); if (endPC == -1) throw new IllegalStateException("end label not defined"); if (handlerPC == -1) throw new IllegalStateException("handler label not defined"); index = putInt16(startPC, codeAttribute, index); index = putInt16(endPC, codeAttribute, index); index = putInt16(handlerPC, codeAttribute, index); index = putInt16(catchType, codeAttribute, index); } } else { // write 0 as exception table length index = putInt16(0, codeAttribute, index); } int attributeCount = 0; if (itsLineNumberTable != null) attributeCount++; if (itsVarDescriptors != null) attributeCount++; index = putInt16(attributeCount, codeAttribute, index); if (itsLineNumberTable != null) { int lineNumberTableAttrIndex = itsConstantPool .addUtf8("LineNumberTable"); index = putInt16(lineNumberTableAttrIndex, codeAttribute, index); int tableAttrLength = 2 + (itsLineNumberTableTop * 4); index = putInt32(tableAttrLength, codeAttribute, index); index = putInt16(itsLineNumberTableTop, codeAttribute, index); for (int i = 0; i < itsLineNumberTableTop; i++) { index = putInt32(itsLineNumberTable[i], codeAttribute, index); } } if (itsVarDescriptors != null) { int variableTableAttrIndex = itsConstantPool .addUtf8("LocalVariableTable"); index = putInt16(variableTableAttrIndex, codeAttribute, index); int varCount = itsVarDescriptors.size(); int tableAttrLength = 2 + (varCount * 10); index = putInt32(tableAttrLength, codeAttribute, index); index = putInt16(varCount, codeAttribute, index); for (int i = 0; i < varCount; i++) { int[] chunk = (int[]) itsVarDescriptors.get(i); int nameIndex = chunk[0]; int descriptorIndex = chunk[1]; int startPC = chunk[2]; int register = chunk[3]; int length = itsCodeBufferTop - startPC; index = putInt16(startPC, codeAttribute, index); index = putInt16(length, codeAttribute, index); index = putInt16(nameIndex, codeAttribute, index); index = putInt16(descriptorIndex, codeAttribute, index); index = putInt16(register, codeAttribute, index); } } itsCurrentMethod.setCodeAttribute(codeAttribute); itsExceptionTable = null; itsExceptionTableTop = 0; itsLineNumberTableTop = 0; itsCodeBufferTop = 0; itsCurrentMethod = null; itsMaxStack = 0; itsStackTop = 0; itsLabelTableTop = 0; itsFixupTableTop = 0; itsVarDescriptors = null; } /** * Add the single-byte opcode to the current method. * * @param theOpCode * the opcode of the bytecode */ public void add(int theOpCode) { if (opcodeCount(theOpCode) != 0) throw new IllegalArgumentException("Unexpected operands"); int newStack = itsStackTop + stackChange(theOpCode); if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); if (DEBUGCODE) System.out.println("Add " + bytecodeStr(theOpCode)); addToCodeBuffer(theOpCode); itsStackTop = (short) newStack; if (newStack > itsMaxStack) itsMaxStack = (short) newStack; if (DEBUGSTACK) { System.out.println("After " + bytecodeStr(theOpCode) + " stack = " + itsStackTop); } } /** * Add a single-operand opcode to the current method. * * @param theOpCode * the opcode of the bytecode * @param theOperand * the operand of the bytecode */ public void add(int theOpCode, int theOperand) { if (DEBUGCODE) { System.out.println("Add " + bytecodeStr(theOpCode) + ", " + Integer.toHexString(theOperand)); } int newStack = itsStackTop + stackChange(theOpCode); if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack); switch (theOpCode) { case ByteCode.GOTO: // fallthru... case ByteCode.IFEQ: case ByteCode.IFNE: case ByteCode.IFLT: case ByteCode.IFGE: case ByteCode.IFGT: case ByteCode.IFLE: case ByteCode.IF_ICMPEQ: case ByteCode.IF_ICMPNE: case ByteCode.IF_ICMPLT: case ByteCode.IF_ICMPGE: case ByteCode.IF_ICMPGT: case ByteCode.IF_ICMPLE: case ByteCode.IF_ACMPEQ: case ByteCode.IF_ACMPNE: case ByteCode.JSR: case ByteCode.IFNULL: case ByteCode.IFNONNULL: { if ((theOperand & 0x80000000) != 0x80000000) { if ((theOperand < 0) || (theOperand > 65535)) throw new IllegalArgumentException("Bad label for branch"); } int branchPC = itsCodeBufferTop; addToCodeBuffer(theOpCode); if ((theOperand & 0x80000000) != 0x80000000) { // hard displacement addToCodeInt16(theOperand); } else { // a label int targetPC = getLabelPC(theOperand); if (DEBUGLABELS) { int theLabel = theOperand & 0x7FFFFFFF; System.out.println("Fixing branch to " + theLabel + " at " + targetPC + " from " + branchPC); } if (targetPC != -1) { int offset = targetPC - branchPC; addToCodeInt16(offset); } else { addLabelFixup(theOperand, branchPC + 1); addToCodeInt16(0); } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -