Initial commit from SVN.

This commit is contained in:
wcrisman
2014-05-30 10:31:51 -07:00
commit b45e56b890
1968 changed files with 370949 additions and 0 deletions

View File

@@ -0,0 +1,646 @@
package com.de22.javabytecode.code;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.common.io.*;
import com.common.util.*;
import com.common.util.optimized.IntArray;
import com.common.debug.*;
import com.de22.javabytecode.*;
import com.de22.javabytecode.Type.TypeMetadata;
/**
* Copyright Wynne Crisman 1999,2006<p>
*/
public class FormatSupport {
private static final int LINE_SPACING = 12;
private static final int SHORT_LINE_DESCRIPTION_SPACING = 32;
private static final int LINE_DESCRIPTION_SPACING = 36;
private static final boolean DISPLAY_PARAMETER_BYTES = false;
private static final char FILE_SEPARATOR = System.getProperty("file.separator").charAt(0);
private static final char[] hexCharacters = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
/**
* FormatSupport constructor.
*/
public FormatSupport() {
super();
}//FormatSupport()//
/**
* Appends the byte to the buffer as a hex string.
* @param value The byte value to append.
* @param buffer The string buffer append to.
*/
public static void appendByteAsHex(byte value, StringBuffer buffer) {
byte highBits = (byte) ((value >> 4) & 0x0F);
byte lowBits = (byte) (value & 0x0F);
buffer.append(hexCharacters[highBits]);
buffer.append(hexCharacters[lowBits]);
}//appendByteAsHex()//
/**
* Adds the specified number of tabs to the buffer.
* @param tabCount The number of tabs to append.
* @param buffer The string buffer to add the tabs to.
*/
public static void appendTabs(int tabCount, StringBuffer buffer) {
while(tabCount-- != 0) {
buffer.append('\t');
}//while//
}//appendTabs()//
/**
* Formats the hex codes of a class file so that they may be easily read by humans.
* @param type The type metadata used to access the constant pool.
* @param codes The array of all class bytes. If the byte codes are not properly formatted then this method may not be able to format everything and will return what was formatted.
* @param tabLevel How many tabs to add to the beginning of every line. Any non-negative value may be used.
* @return The formatted code as a simple string.
*/
public static String formatCode(Type type, byte[] codes, int tabLevel) {
StringBuffer buffer = new StringBuffer(10000);
IList opCodes = OpCode.getOpCodes();
try {
for(int index = 0; index < codes.length; index++) {
int code = codes[index] & 0xFF;
String indexString = Integer.toString(index);
OpCode opCode = (OpCode) opCodes.get(code);
if(codes[index] == -86) { //switch statement//
int startingNumber;
int endingNumber;
int jumpOffset;
appendTabs(tabLevel, buffer);
buffer.append(" line " + indexString);
for(int t = indexString.length(); t < LINE_SPACING; t++) {buffer.append(' ');} //Space evenly//
buffer.append("0x");
appendByteAsHex(codes[index], buffer);
buffer.append(' ');
buffer.append(opCode.toString());
buffer.append('\r');
buffer.append('\n');
//Spaces//
while((++index) % 4 != 0) {
formatLine(codes[index], index, "Spacing", tabLevel, buffer);
}//while//
jumpOffset = StreamSupport.readInt(codes, index); //The default jump offset.//
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "Default Jump Offset = " + jumpOffset, tabLevel, buffer);
startingNumber = StreamSupport.readInt(codes, index); //The first number in the switch statement.//
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "Starting Value = " + startingNumber, tabLevel, buffer);
endingNumber = StreamSupport.readInt(codes, index); //The last number in the switch statement.//
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "Ending Value = " + endingNumber, tabLevel, buffer);
for(int entryIndex = startingNumber; entryIndex <= endingNumber; entryIndex++) {
jumpOffset = StreamSupport.readInt(codes, index); //The default jump offset.//
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "Jump Offset #" + entryIndex + " = " + jumpOffset, tabLevel, buffer);
}//for//
index--;
}//if//
else if(codes[index] == -85) { //lookup switch statement//
int numberOfCases;
int caseValue;
int caseOffset;
int defaultCaseOffset;
appendTabs(tabLevel, buffer);
buffer.append(" line ");
buffer.append(indexString);
for(int t = indexString.length(); t < LINE_SPACING; t++) {buffer.append(' ');} //Space evenly//
buffer.append("0x");
appendByteAsHex(codes[index], buffer);
buffer.append(' ');
buffer.append(opCode.toString());
buffer.append('\r');
buffer.append('\n');
//Spaces//
while((++index) % 4 != 0) {
formatLine(codes[index], index, "Spacing", tabLevel, buffer);
}//while//
defaultCaseOffset = StreamSupport.readInt(codes, index); //The default jump offset.//
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "Default Jump Offset = " + defaultCaseOffset, tabLevel, buffer);
numberOfCases = StreamSupport.readInt(codes, index); //The first number in the switch statement.//
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "Number of cases = " + numberOfCases, tabLevel, buffer);
for(int caseIndex = 0; caseIndex < numberOfCases; caseIndex++) {
caseValue = StreamSupport.readInt(codes, index); //The last number in the switch statement.//
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "Case" + caseIndex + " value = " + caseValue, tabLevel, buffer);
caseOffset = StreamSupport.readInt(codes, index); //The last number in the switch statement.//
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "", tabLevel, buffer);
formatLine(codes[index], index++, "Case" + caseIndex + " offset = " + caseOffset, tabLevel, buffer);
}//for//
index--;
}//else if//
else {
int lineDataCharacterCount;
appendTabs(tabLevel, buffer);
lineDataCharacterCount = buffer.length();
buffer.append(" line ");
buffer.append(indexString);
for(int t = indexString.length(); t < LINE_SPACING; t++) {buffer.append(' ');} //Space evenly//
if(DISPLAY_PARAMETER_BYTES) {
buffer.append("0x");
appendByteAsHex(codes[index], buffer);
buffer.append(' ');
}//if//
buffer.append(opCode.getName());
lineDataCharacterCount = buffer.length() - lineDataCharacterCount + 1;
for(int t = lineDataCharacterCount; t < LINE_DESCRIPTION_SPACING; t++) {buffer.append(' ');} //Space evenly//
buffer.append(opCode.getDescription());
buffer.append('\r');
buffer.append('\n');
if(DISPLAY_PARAMETER_BYTES) {
for(int param = 0; param < opCode.getParameterByteDescriptions().getSize(); param++) {
indexString = new Integer(index + param + 1).toString();
code = (codes[index + param + 1] < 0 ? ((int) codes[index + param + 1]) + 256 : (int) codes[index + param + 1]);
appendTabs(tabLevel, buffer);
buffer.append(" line ");
buffer.append(indexString);
for(int t = indexString.length(); t < LINE_SPACING; t++) {buffer.append(' ');} //Space evenly//
buffer.append("0x");
appendByteAsHex(codes[index + param + 1], buffer);
buffer.append(" ");
buffer.append(opCode.getParameterByteDescriptions().get(param));
buffer.append('\r');
buffer.append('\n');
}//for//
}//if//
else {
int codeIndex = index + 1;
for(int param = 0; param < opCode.getParameterDescriptions().getSize(); param++) {
int paramType = opCode.getParameterTypes()[param];
long value = 0;
int paramByteCount;
int lineDisplayCharacterCount = 0;
boolean isConstantRef = false;
appendTabs(tabLevel, buffer);
lineDataCharacterCount = buffer.length();
switch(paramType) {
case OpCode.SHORT_CONSTANT_REF: {
value = (long) (codes[codeIndex] & 0xFF);
//if(((short) value) < 0) {
// buffer.append(type.getConstant((short) value));
//}
paramByteCount = 1;
isConstantRef = true;
break;
}//case//
case OpCode.CONSTANT_REF: {
value = (long) ((((codes[codeIndex] & 0xFF) << 8) | (codes[codeIndex + 1] & 0xFF)) & 0xFFFF);
//if(((short) value) < 0) {
// buffer.append(type.getConstant((short) value));
//}
paramByteCount = 2;
isConstantRef = true;
break;
}//case//
case OpCode.SIGNED_BYTE: {
value = (long) codes[codeIndex];
paramByteCount = 1;
break;
}//case//
case OpCode.UNSIGNED_BYTE: {
value = (long) (codes[codeIndex] & 0xFF);
paramByteCount = 1;
break;
}//case//
case OpCode.SIGNED_SHORT: {
value = (long) (((codes[codeIndex] & 0xFF) << 8) | (codes[codeIndex + 1] & 0xFF));
paramByteCount = 2;
break;
}//case//
case OpCode.UNSIGNED_SHORT: {
value = (long) ((((codes[codeIndex] & 0xFF) << 8) | (codes[codeIndex + 1] & 0xFF)) & 0xFFFF);
paramByteCount = 2;
break;
}//case//
case OpCode.SIGNED_INT: {
value = (long) (((codes[codeIndex] & 0xFF) << 24) | ((codes[codeIndex + 1] & 0xFF) << 16) | ((codes[codeIndex + 2] & 0xFF) << 8) | (codes[codeIndex + 3] & 0xFF));
paramByteCount = 4;
break;
}//case//
case OpCode.UNSIGNED_INT: {
value = (long) ((((codes[codeIndex] & 0xFF) << 24) | ((codes[codeIndex + 1] & 0xFF) << 16) | ((codes[codeIndex + 2] & 0xFF) << 8) | (codes[codeIndex + 3] & 0xFF)) & 0xFFFF);
paramByteCount = 4;
break;
}//case//
default:
throw new RuntimeException("Unexpected opcode parameter type.");
}//switch//
if(paramByteCount == 1) {
buffer.append(" line ");
lineDisplayCharacterCount = buffer.length();
buffer.append(codeIndex);
lineDisplayCharacterCount = buffer.length() - lineDisplayCharacterCount;
}//if//
else {
buffer.append(" lines ");
lineDisplayCharacterCount = buffer.length();
buffer.append(codeIndex);
buffer.append("-");
buffer.append(codeIndex + paramByteCount - 1);
lineDisplayCharacterCount = buffer.length() - lineDisplayCharacterCount + 1;
}//else//
for(int t = lineDisplayCharacterCount; t < LINE_SPACING; t++) {buffer.append(' ');} //Space evenly//
buffer.append(value);
lineDataCharacterCount = buffer.length() - lineDataCharacterCount + 1;
if(isConstantRef) {
for(int t = lineDataCharacterCount; t < SHORT_LINE_DESCRIPTION_SPACING; t++) {buffer.append(' ');} //Space evenly//
buffer.append('{');
buffer.append(type.getConstant((short) value).getPrettyValue());
buffer.append('}');
buffer.append(' ');
}//if//
else {
for(int t = lineDataCharacterCount; t < LINE_DESCRIPTION_SPACING; t++) {buffer.append(' ');} //Space evenly//
}//else//
buffer.append(opCode.getParameterDescriptions().get(param));
buffer.append('\r');
buffer.append('\n');
codeIndex += paramByteCount;
}//for//
}//else//
index += opCode.getParameterByteDescriptions().getSize();
}//else//
}//for//
}//try//
catch(Throwable e) {
Debug.log(e);
Debug.halt();
}//catch//
return buffer.toString();
}//formatCode()//
/**
* Will format a descriptor string using the parameters and return type of the method.
* This is used to create method descriptors for a class file.
* <p>Example: void methodName(double[] a, String b) -> ([DLjava/lang/String;)V;
* @param returnType The descriptor's return type.
* @param parameterTypes The descriptor's parameter types (in order of parameter).
* @param buffer The buffer to write the output to.
*/
public static void formatDescriptor(Class returnType, Class[] parameterTypes, StringBuffer buffer) {
buffer.append('(');
if(parameterTypes != null) {
for(int index = 0; index < parameterTypes.length; index++) {
formatType(parameterTypes[index], buffer);
}//for//
}//if//
buffer.append(')');
formatType(returnType, buffer);
}//formatDescriptor()//
/**
* Used to format a line when outputing the interperated class file's hex codes.
*/
public static void formatLine(byte code, int index, String message, int tabLevel, StringBuffer buffer) {
String indexString = new Integer(index).toString();
appendTabs(tabLevel, buffer);
buffer.append(" line ");
buffer.append(indexString);
for(int t = indexString.length(); t < 4; t++) {buffer.append(' ');} //Space evenly//
appendByteAsHex(code, buffer);
buffer.append(' ');
buffer.append(message);
buffer.append('\r');
buffer.append('\n');
}//formatLine()//
/**
* Formats a non-primitive class to a class file compatible string.
* <p>NOTE: This method will not place an "L" at the beginning or a ";" at the end of the formatted string.
* @param type Class The non-primitive class to convert to a string.
* @param buffer The buffer to write the string to.
*/
public static void formatNonPrimitiveType(Class type, StringBuffer buffer) {
formatNonPrimitiveType(type.getName(), buffer);
}//formatNonPrimitiveType()//
/**
* Formats a non-primitive class to a class file compatible string.
* <p>NOTE: This method will not place an "L" at the beginning or a ";" at the end of the formatted string.
* @param type The non-primitive class to convert to a string.
* @param buffer The buffer to write the string to.
*/
public static void formatNonPrimitiveType(String qualifiedTypeName, StringBuffer buffer) {
StringTokenizer tokenizer = new StringTokenizer(qualifiedTypeName, ".");
boolean isFirst = true;
while(tokenizer.hasMoreTokens()) {
if(!isFirst) {
buffer.append('/');
}//if//
else {
isFirst = false;
}//else//
buffer.append(tokenizer.nextToken());
}//while//
}//formatNonPrimitiveType()//
/**
* Formats a class to a class file compatible string.
* @param type The class to convert to a string.
* @param buffer The buffer to write the output to.
*/
public static void formatType(Class type, StringBuffer buffer) {
if(type != null) {
while(type.isArray()) {
buffer.append('[');
type = type.getComponentType();
}//while//
}//if//
if((type == null) || (type.equals(Void.TYPE))) {
buffer.append('V');
}//if//
else if(type.isPrimitive()) {
if(type.equals(Integer.TYPE)) {
buffer.append('I');
}//if//
else if(type.equals(Byte.TYPE)) {
buffer.append('B');
}//else if//
else if(type.equals(Character.TYPE)) {
buffer.append('C');
}//else if//
else if(type.equals(Short.TYPE)) {
buffer.append('S');
}//else if//
else if(type.equals(Boolean.TYPE)) {
buffer.append('Z');
}//else if//
else if(type.equals(Double.TYPE)) {
buffer.append('D');
}//else if//
else if(type.equals(Float.TYPE)) {
buffer.append('F');
}//else if//
else if(type.equals(Long.TYPE)) {
buffer.append('J');
}//else if//
}//else if//
else {
buffer.append('L');
formatNonPrimitiveType(type.getName(), buffer);
buffer.append(';');
}//else//
}//formatType()//
/**
* Reverse engineers a class file and prints the output to the console.
* @param args The array of initialization parameters.
* args[0] = The path and file name of the class or jar or path to a class file directory to reverse engineer.
* args[1] = The optional path (must exist) where the output will be stored. If the files exist they will be over written. If not specified the output will go to the console.
*/
public static void main(String[] args) {
String debugText = null;
long t = System.currentTimeMillis();
try {
File file = new File(args[0]);
File path = args.length > 1 ? new File(args[1]) : null;
if(path != null && (path.isFile() || (!path.exists() && !path.mkdirs()))) {
path = null;
}//if//
if(file.isDirectory()) {
LiteList directoryStack = new LiteList(10, 20);
LiteList listingStack = new LiteList(10, 20);
IntArray listingIndexStack = new IntArray(10, 20);
File directory = file;
File[] files = directory.listFiles();
for(int index = 0; index < files.length; index++) {
if(files[index].isDirectory()) {
File[] subfiles = files[index].listFiles();
if(subfiles.length > 0) {
directoryStack.add(directory);
listingStack.add(files);
listingIndexStack.add(index);
directory = files[index];
files = subfiles;
index = 0;
}//if//
}//if//
else if(files[index].getName().endsWith(".class")) {
debugText = files[index].getPath();
processClassFile(new FileInputStream(files[index]), path);
}//else if//
if((index == files.length - 1) && (directoryStack.getSize() > 0)) {
directory = (File) directoryStack.remove(directoryStack.getSize() - 1);
files = (File[]) listingStack.remove(listingStack.getSize() - 1);
index = listingIndexStack.remove(listingIndexStack.getSize() - 1);
}//if//
}//for//
}//if//
else if(file.getName().endsWith(".class")) {
debugText = file.getPath();
byte[] bytes = StreamSupport.readBytes(new FileInputStream(file));
processClassFile(new ByteArrayInputStream(bytes), path);
}//else if//
else if(file.getName().endsWith(".jar") || file.getName().endsWith(".zip")) {
ZipFile zip = null;
Enumeration enumeration = null;
if(file.getName().endsWith("jar")) {
zip = new JarFile(file, false, JarFile.OPEN_READ);
}//if//
else {
zip = new ZipFile(file, ZipFile.OPEN_READ);
}//else//
try {
enumeration = zip.entries();
while(enumeration.hasMoreElements()) {
ZipEntry entry = (ZipEntry) enumeration.nextElement();
if(!entry.isDirectory()) {
if(entry.getName().endsWith(".class")) {
InputStream in = zip.getInputStream(entry);
byte[] bytes = new byte[in.available()];
ByteArrayInputStream bin;
int readCount = 0;
debugText = entry.getName();
while(readCount < bytes.length) {
readCount += in.read(bytes, readCount, bytes.length - readCount);
}//while//
//Convert the zip input stream to a byte array stream.//
bin = new ByteArrayInputStream(bytes);
processClassFile(bin, path);
in.close();
}//if//
}//if//
}//while//
}//try//
finally {
zip.close();
}//finally//
}//else if//
}//try//
catch(Throwable e) {
Debug.log(e, "Caught while processing the class file: " + debugText);
}//catch//
Debug.log("Total time (milliseconds): " + (System.currentTimeMillis() - t));
System.exit(0);
}//main()//
/**
* Processes a class file and writes out the byte codes in a semi-readable form.
* @param bytesIn The input stream containing the bytes codes. This stream will always be closed by this method.
* @param outputPath The optional path where the class output should be written to (under its packaging of course). The console will be used otherwise.
*/
private static void processClassFile(InputStream bytesIn, File outputPath) throws Throwable {
StringBuffer buffer = null;
ObjectInputStream in = null;
Type type = null;
try {
File outputFile = null;
TypeMetadata metadata = null;
in = new ObjectInputStream(bytesIn, null, null);
buffer = new StringBuffer(5000);
type = new Type();
type.readExternal(in);
type.toString(buffer, 0);
metadata = type.collectMetadata();
if(outputPath != null) {
String fileName = metadata.getTypeName().replace('.', FILE_SEPARATOR) + ".txt";
String directoryName = fileName.lastIndexOf(FILE_SEPARATOR) != -1 ? fileName.substring(0, fileName.lastIndexOf(FILE_SEPARATOR)) : "";
File outputDirectory = new File(outputPath, directoryName);
outputFile = new File(outputPath, fileName);
if(!outputDirectory.exists()) {
outputDirectory.mkdirs();
}//if//
if(!outputFile.exists()) {
outputFile.createNewFile();
}//if//
}//if//
if(outputFile == null) {
Debug.log(buffer.toString());
}//if//
else {
FileOutputStream fout = null;
try {
fout = new FileOutputStream(outputFile.getAbsolutePath(), false);
fout.write(buffer.toString().getBytes());
}//try//
catch(Throwable e) {
Debug.log(e);
}//catch//
finally {
if(fout != null) {
try {
fout.close();
}//try//
catch(Throwable e) {
//Ignored.//
}//catch//
}//if//
}//finally//
}//else//
}//try//
catch(Throwable e) {
//Fill in as much of the buffer as we can so we can have some kind of output.//
if((type != null) && (buffer.length() == 0)) {
try {
type.toString(buffer, 0);
}//try//
catch(Throwable e2) {
//Ignore//
}//catch//
}//if//
System.out.println(buffer);
throw e;
}//catch//
finally {
if(in != null) {
try {
in.close();
}//try//
catch(Throwable e) {
}//catch//
}//if//
if(bytesIn != null) {
try {
bytesIn.close();
}//try//
catch(Throwable e) {
}//catch//
}//if//
}//finally//
}//processClassFile()//
}//FormatSupport//

View File

@@ -0,0 +1,672 @@
package com.de22.javabytecode.code;
import java.io.IOException;
import com.common.debug.*;
import com.common.io.StandardOutputStream;
import com.de22.javabytecode.*;
import com.de22.javabytecode.util.*;
/**
* Copyright Wynne Crisman 1999,2006<p>
* Supports method building.
*/
public class MethodSupport {
public static final int I = 0; //Integer
public static final int L = 1; //Long
public static final int F = 2; //Float
public static final int D = 3; //Double
public static final int A = 4; //Object
public static final byte INVOKE_INTERFACE = 1;
public static final byte INVOKE_VIRTUAL = 2;
public static final byte INVOKE_SPECIAL = 3;
public static final byte INVOKE_STATIC = 4;
private Type type = null;
/**
* MethodSupport constructor.
*/
private MethodSupport() {
super();
}//MethodSupport()//
/**
* MethodSupport constructor.
*/
public MethodSupport(Type type) {
super();
this.type = type;
}//MethodSupport()//
/**
* Casts the item on the top of the stack to the desired type.
* The stack size is unaffected.
* @param typeIndex The index of the cast type in the ConstantPool.
* @param out The output stream to write to.
* @param stack The stack tracker that will track the stack size.
*/
public void cast(short typeIndex, StandardOutputStream out, StackTracker stack) throws IOException {
if(typeIndex != 0) {
out.writeByte((byte) 192); //checkcast
out.writeShort(typeIndex);
}//if//
}//cast()//
/**
* Pops two long values from the stack and compares them, and pushes the result on the stack.
* The stack is decremented by 4 and incremented by 1. The result is pushed on the stack as 1 where long1>long2, -1 where long1<long2, and 0 where long1==long2.
* @param out The output stream to write to.
* @param stack The stack tracker that will track the stack size.
*/
public void compareLongs(StandardOutputStream out, StackTracker stack) throws IOException {
out.writeByte((byte) 148); //lcmp
stack.decrement(3);
}//compareLongs()//
/**
* Creates an array and pushes it on the stack.
* The array size is pushed on the stack and then popped off and the new array is pushed on the stack.
* @param arraySize The size of the array.
* @param arrayType The array's type which must be one of the TYPE_* identifiers defined in this class.
* @param out The output stream to write to.
* @param stack The stack tracker that will track the stack size.
* @see #TYPE_BOOLEAN
* @see #TYPE_BYTE
* @see #TYPE_CHAR
* @see #TYPE_SHORT
* @see #TYPE_INT
* @see #TYPE_FLOAT
* @see #TYPE_LONG
* @see #TYPE_DOUBLE
*/
public void createArray(int arraySize, byte arrayType, StandardOutputStream out, StackTracker stack) throws IOException {
push(arraySize, out, stack);
out.writeByte((byte) 188);
out.writeByte((byte) arrayType);
stack.increment();
}//createArray()//
/**
* Pops two int values from the stack and computes the remainder of dividing the first by the second i1 % i2 them, and pushes the result on the stack.
* The stack is decremented by 2 and incremented by 1.
* @param out The output stream to write to.
* @param stack The stack tracker that will track the stack size.
*/
public void imod(StandardOutputStream out, StackTracker stack) throws IOException {
out.writeByte((byte) 112); //irem - Gets the remainder using: value1 % value2//
stack.decrement();
}//imod()//
/**
* Invokes the specified method using the object reference and arguments on the stack.
* <p>The caller should already have placed the object reference (except if this is a static method call) and parameters on the stack.<p>
* @param type The type object representing the compiling class.
* @param method The method being called.
* @param out The output stream for the class byte codes.
* @param stack The stack tracker that is keeping track of the current stack size.
* @return The number of of method return value words placed on the stack.
*/
public int invoke(java.lang.reflect.Method method, StandardOutputStream out, StackTracker stack) throws IOException {
Class[] parameters = method.getParameterTypes();
short methodIndex = type.addConstantMethod(method.getDeclaringClass(), method.getReturnType(), method.getName(), parameters);
int retVal = 0;
int argumentSize = 0;
for(int index = 0; index < parameters.length; index++) {
argumentSize += ((parameters[index].equals(Long.TYPE)) || (parameters[index].equals(Double.TYPE))) ? 2 : 1;
}//for//
if(method.getDeclaringClass().isInterface()) {
//Interface//
out.writeByte((byte) 185); //(-argumentSize -1 +returnValueSize) invokeinterface.//
out.writeShort((short) methodIndex);
out.writeByte((byte) (argumentSize + 1)); //The number of words to pull off the stack.//
out.writeByte((byte) 0); //Required to be 0.//
stack.decrement(argumentSize + 1);
}//if//
else if((method.getName().equals("<init>")) || (method.getName().equals("<cinit>")) || (java.lang.reflect.Modifier.isPrivate(method.getModifiers())) || (method.getClass().equals(type.getClass().getSuperclass()))) {
//Special//
out.writeByte((byte) 183); //(-argumentSize -1 +returnValueSize) invokevirtual.//
out.writeShort((short) methodIndex);
stack.decrement(argumentSize + 1);
}//else if//
else if(java.lang.reflect.Modifier.isStatic(method.getModifiers())) {
//Static//
out.writeByte((byte) 184); //(-argumentSize +returnValueSize) invokestatic.//
out.writeShort((short) methodIndex);
stack.decrement(argumentSize);
}//else if//
else {
//Virtual//
out.writeByte((byte) 182); //(-argumentSize -1 +returnValueSize) invokevirtual.//
out.writeShort((short) methodIndex);
stack.decrement(argumentSize + 1);
}//else//
//Increment the stack to reflect the methods return value.//
if((method.getReturnType().equals(Long.TYPE)) || (method.getReturnType().equals(Double.TYPE))) {
stack.increment(2);
retVal = 2;
}//if//
else if(!method.getReturnType().equals(Void.TYPE)) {
stack.increment();
retVal = 1;
}//else if//
return retVal;
}//invoke()//
/**
* Invokes the specified method on the class being built using the object reference and arguments on the stack.
* <p>The caller should already have placed the object reference (except if this is a static method call) and parameters on the stack.<p>
* @param methodName The name of the method being called.
* @param parameters The parameter types that the method defines.
* @param returnType The method's return type.
* @param out The output stream for the class byte codes.
* @param stack The stack tracker that is keeping track of the current stack size.
* @return The number of of method return value words placed on the stack.
*/
public int invokeSuper(String methodName, Class[] parameters, Class returnType, boolean isStatic, StandardOutputStream out, StackTracker stack) throws IOException {
short methodIndex = type.addConstantMethod(null, returnType, methodName, parameters);
int retVal = 0;
int argumentSize = 0;
for(int index = 0; index < parameters.length; index++) {
argumentSize += ((parameters[index].equals(Long.TYPE)) || (parameters[index].equals(Double.TYPE))) ? 2 : 1;
}//for//
if((methodName.equals("<init>")) || (methodName.equals("<cinit>")) || (!isStatic)) {
//Special//
out.writeByte((byte) 183); //(-argumentSize -1 +returnValueSize) invokespecial.//
out.writeShort((short) methodIndex);
stack.decrement(argumentSize + 1);
}//if//
else {
//Static//
out.writeByte((byte) 184); //(-argumentSize +returnValueSize) invokestatic.//
out.writeShort((short) methodIndex);
stack.decrement(argumentSize);
}//else//
//Increment the stack to reflect the methods return value.//
if((returnType.equals(Long.TYPE)) || (returnType.equals(Double.TYPE))) {
stack.increment(2);
retVal = 2;
}//if//
else if(!returnType.equals(Void.TYPE)) {
stack.increment();
retVal = 1;
}//else if//
return retVal;
}//invokeSuper()//
/**
* Invokes the specified method on the class being built using the object reference and arguments on the stack.
* <p>The caller should already have placed the object reference (except if this is a static method call) and parameters on the stack.<p>
* @param methodName The name of the method being called.
* @param parameters The parameter types that the method defines.
* @param returnType The method's return type.
* @param out The output stream for the class byte codes.
* @param stack The stack tracker that is keeping track of the current stack size.
* @return The number of of method return value words placed on the stack.
*/
public int invokeThis(String methodName, Class[] parameters, Class returnType, boolean isStatic, StandardOutputStream out, StackTracker stack) throws IOException {
short methodIndex = type.addConstantMethod(null, returnType, methodName, parameters);
int retVal = 0;
int argumentSize = 0;
for(int index = 0; index < parameters.length; index++) {
argumentSize += ((parameters[index].equals(Long.TYPE)) || (parameters[index].equals(Double.TYPE))) ? 2 : 1;
}//for//
if((methodName.equals("<init>")) || (methodName.equals("<cinit>"))) {
//Special//
out.writeByte((byte) 183); //(-argumentSize -1 +returnValueSize) invokespecial.//
out.writeShort((short) methodIndex);
stack.decrement(argumentSize + 1);
}//if//
else if(isStatic) {
//Static//
out.writeByte((byte) 184); //(-argumentSize +returnValueSize) invokestatic.//
out.writeShort((short) methodIndex);
stack.decrement(argumentSize);
}//else if//
else {
//Virtual//
out.writeByte((byte) 182); //(-argumentSize -1 +returnValueSize) invokevirtual.//
out.writeShort((short) methodIndex);
stack.decrement(argumentSize + 1);
}//else//
//Increment the stack to reflect the methods return value.//
if((returnType.equals(Long.TYPE)) || (returnType.equals(Double.TYPE))) {
stack.increment(2);
retVal = 2;
}//if//
else if(!returnType.equals(Void.TYPE)) {
stack.increment();
retVal = 1;
}//else if//
return retVal;
}//invokeThis()//
/**
* Jumps the code pointer by <code>offset</code> amount.
* The stack is unaffected.
* <p>Warning: The jump offset will be adjusted to take into account the bytes of code that perform the jump. The caller should not need to take this into account.
* @param offset The amount to jump where a negative value would jump backward in the code, and a positive value jumps forward.
* @param out The output stream to write to.
* @param stack The stack tracker that will track the stack size.
*/
public void jump(int offset, StandardOutputStream out, StackTracker stack) throws IOException {
//Adjust the offset to account for the added code of performing the jump.//
if(offset >= 0) {
offset += 3;
if(offset > 32767) {
offset += 2;
}//if//
}//if//
//Used the short jump when possible.//
if((offset < 32768) && (offset > -32769)) {
out.writeByte((byte) 167); //goto//
out.writeShort((short) offset);
}//if//
else {
out.writeByte((byte) 200); //goto_w//
out.writeInt(offset);
}//else//
}//jump()//
/**
* Pops two long values from the stack and computes the remainder of dividing the first by the second l1 % l2 them, and pushes the result on the stack.
* The stack is decremented by 4 and incremented by 2.
* @param out The output stream to write to.
* @param stack The stack tracker that will track the stack size.
*/
public void lmod(StandardOutputStream out, StackTracker stack) throws IOException {
out.writeByte((byte) 113); //lrem - Gets the remainder using: value1 % value2//
stack.decrement(2);
}//lmod()//
/**
* Pops the value of a local variable onto the stack.
* The stack is incremented by one or two depending on the local variable's type.
* @param local The number identifying the local variable.
* @param type The local variable's type. All valid types are defined by this class as identifiers and are listed below.
* @param out The output stream to write to.
* @param stack The stack tracker that will track the stack size.
* @see #I
* @see #L
* @see #F
* @see #D
* @see #A
*/
public void load(byte local, int type, StandardOutputStream out, StackTracker stack) throws IOException {
switch(type) {
case I: {
switch(local) {
case 0: {
out.writeByte((byte) 26); //iload_0//
break;
}//case//
case 1: {
out.writeByte((byte) 27); //iload_1//
break;
}//case//
case 2: {
out.writeByte((byte) 28); //iload_2//
break;
}//case//
case 3: {
out.writeByte((byte) 29); //iload_3//
break;
}//case//
default: {
out.writeByte((byte) 21); //iload//
out.writeByte((byte) local);
break;
}//default//
}//switch//
stack.increment();
break;
}//case//
case L: {
switch(local) {
case 0: {
out.writeByte((byte) 30); //lload_0//
break;
}//case//
case 1: {
out.writeByte((byte) 31); //lload_1//
break;
}//case//
case 2: {
out.writeByte((byte) 32); //lload_2//
break;
}//case//
case 3: {
out.writeByte((byte) 33); //lload_3//
break;
}//case//
default: {
out.writeByte((byte) 22); //lload//
out.writeByte((byte) local);
break;
}//default//
}//switch//
stack.increment(2);
break;
}//case//
case F: {
switch(local) {
case 0: {
out.writeByte((byte) 34); //fload_0//
break;
}//case//
case 1: {
out.writeByte((byte) 35); //fload_1//
break;
}//case//
case 2: {
out.writeByte((byte) 36); //fload_2//
break;
}//case//
case 3: {
out.writeByte((byte) 37); //fload_3//
break;
}//case//
default: {
out.writeByte((byte) 23); //fload//
out.writeByte((byte) local);
break;
}//default//
}//switch//
stack.increment();
break;
}//case//
case D: {
switch(local) {
case 0: {
out.writeByte((byte) 38); //dload_0//
break;
}//case//
case 1: {
out.writeByte((byte) 39); //dload_1//
break;
}//case//
case 2: {
out.writeByte((byte) 40); //dload_2//
break;
}//case//
case 3: {
out.writeByte((byte) 41); //dload_3//
break;
}//case//
default: {
out.writeByte((byte) 24); //dload//
out.writeByte((byte) local);
break;
}//default//
}//switch//
stack.increment(2);
break;
}//case//
case A: {
switch(local) {
case 0: {
out.writeByte((byte) 42); //aload_0//
break;
}//case//
case 1: {
out.writeByte((byte) 43); //aload_1//
break;
}//case//
case 2: {
out.writeByte((byte) 44); //aload_2//
break;
}//case//
case 3: {
out.writeByte((byte) 45); //aload_3//
break;
}//case//
default: {
out.writeByte((byte) 25); //aload//
out.writeByte((byte) local);
break;
}//default//
}//switch//
stack.increment();
break;
}//case//
default: {
//Must be I, L, F, D, or A//
Debug.halt();
throw new RuntimeException("Error: Unable to continue processing the class due to an internal error.");
}//default//
}//switch//
}//load()//
/**
* @see load(byte, Class, StandardOutputStream, StackTracker)
*/
public void load(byte local, Class type, StandardOutputStream out, StackTracker stack) throws IOException {
int typeCode;
if(type.isPrimitive()) {
if((type.equals(Integer.TYPE)) || (type.equals(Character.TYPE)) || (type.equals(Short.TYPE)) || (type.equals(Boolean.TYPE)) || (type.equals(Byte.TYPE))) {
typeCode = I;
}//if//
else if(type.equals(Long.TYPE)) {
typeCode = L;
}//else if//
else if(type.equals(Float.TYPE)) {
typeCode = F;
}//else if//
else if(type.equals(Double.TYPE)) {
typeCode = D;
}//else if//
else {
Debug.halt();
typeCode = A;
}//else//
}//if//
else {
typeCode = A;
}//else//
load(local, typeCode, out, stack);
}//load()//
/**
* Converts a long value on the stack to an int value.
* Pops a long value from the stack and pushes an int value which decrements the stack size by one.
* @param out The output stream to write to.
* @param stack The stack tracker that will track the stack size.
*/
public void long2int(StandardOutputStream out, StackTracker stack) throws IOException {
out.writeByte((byte) 136); //l2i//
stack.decrement(1);
}//long2int()//
/**
* Adds a return statement to the type to return the method's return value from the stack.
* The return value will be popped from the stack. The stack will be incremented by one or two (for a long our double return value).
* @param type The method's return type.
* @param out The output stream to write to.
* @param stack The stack tracker that will track the stack size.
*/
public void methodReturn(Class type, StandardOutputStream out, StackTracker stack) throws IOException {
boolean decrement = true;
if(type.isPrimitive()) {
if(type.equals(Integer.TYPE)) {
out.writeByte((byte) 172); //(-1) ireturn -> Return integer//
}//if//
else if(type.equals(Boolean.TYPE)) {
out.writeByte((byte) 172); //(-1) ireturn -> Return integer//
}//else if//
else if(type.equals(Long.TYPE)) {
out.writeByte((byte) 173); //(-1) lreturn -> Return long//
stack.decrement(); //Account for double size value
}//else if//
else if(type.equals(Short.TYPE)) {
out.writeByte((byte) 172); //(-1) ireturn -> Return integer//
}//else if//
else if(type.equals(Float.TYPE)) {
out.writeByte((byte) 174); //(-1) freturn -> Return float//
}//else if//
else if(type.equals(Double.TYPE)) {
out.writeByte((byte) 175); //(-1) dreturn -> Return double//
stack.decrement(); //Account for double size value//
}//else if//
else if(type.equals(Character.TYPE)) {
out.writeByte((byte) 172); //(-1) ireturn -> Return integer//
}//else if//
else if(type.equals(Byte.TYPE)) {
out.writeByte((byte) 172); //(-1) ireturn -> Return integer//
}//else if//
else {
//Must be VOID.//
if(stack.getStackSize() == 1) {
out.writeByte((byte) 87); //(0) pop -> Remove the remaining item on the local stack//
}//if//
else if(stack.getStackSize() > 1) {
Debug.log("Error: The stack size should be one or zero when appending a method return.");
}//else if//
else {
decrement = false;
}//else//
out.writeByte((byte) 177); //(0) return -> Return nothing//
}//else//
}//if//
else {
//An object type of return.//
out.writeByte((byte) 192); //(0) checkcast -> cast the local object to it's proper type//
out.writeShort((short) this.type.addConstantType(type)); //The index of the class to cast to//
out.writeByte((byte) 176); //(-1) areturn -> Return object reference//
}//else//
if(decrement) {
stack.decrement();
}//if//
}//methodReturn()//
/**
* Pushes a constant value onto the stack.
* The constant value or a reference to the constant value in the constant pool will be pushed on the stack. The stack will be incremented by one.
* @param value The value to place on the stack.
* @param out The output stream to write to.
* @param stack The stack tracker that will track the stack size.
*/
public void push(int value, StandardOutputStream out, StackTracker stack) throws IOException {
switch(value) {
case -1: {
out.writeByte((byte) 2); //iconst_m1
stack.increment();
break;
}//case//
case 0: {
out.writeByte((byte) 3); //iconst_0
stack.increment();
break;
}//case//
case 1: {
out.writeByte((byte) 4); //iconst_1
stack.increment();
break;
}//case//
case 2: {
out.writeByte((byte) 5); //iconst_2
stack.increment();
break;
}//case//
case 3: {
out.writeByte((byte) 6); //iconst_3
stack.increment();
break;
}//case//
case 4: {
out.writeByte((byte) 7); //iconst_4
stack.increment();
break;
}//case//
case 5: {
out.writeByte((byte) 8); //iconst_5
stack.increment();
break;
}//case//
default: {
if((value < 128) && (value > -129)) {
out.writeByte((byte) 16); //bipush//
out.writeByte((byte) value);
stack.increment();
}//if//
else if((value < 32768) && (value > -32769)) {
out.writeByte((byte) 17); //sipush//
out.writeShort((short) value);
stack.increment();
}//else if//
else {
short index = type.addConstantInt(value);
pushConstant(index, out, stack);
}//else//
break;
}//default//
}//switch//
}//push()//
/**
* Pushes a constant long (wide) value onto the stack.
* The constant value or a reference to the constant value in the constant pool will be pushed on the stack. The stack will be incremented by two.
* @param value The value to place on the stack.
* @param out The output stream to write to.
* @param stack The stack tracker that will track the stack size.
*/
public void push(long value, StandardOutputStream out, StackTracker stack) throws IOException {
if(value == 0) {
out.writeByte((byte) 9); //lconst_0//
stack.increment(2);
}//if//
else if(value == 1) {
out.writeByte((byte) 10); //lconst_1//
stack.increment(2);
}//else if//
else {
short index = type.addConstantLong(value);
pushWideConstant(index, out, stack);
}//else//
}//push()//
/**
* Pushes a constant value onto the stack using a constant value in the constant pool.
* A reference to the constant will be used to push the value on the stack. The stack will be incremented by one.
* @param index The value's index in the constant pool.
* @param out The output stream to write to.
* @param stack The stack tracker that will track the stack size.
*/
public void pushConstant(short index, StandardOutputStream out, StackTracker stack) throws IOException {
if((index < 128) && (index > -129)) {
out.writeByte((byte) 18); //ldc//
out.writeByte((byte) index);
}//if//
else {
out.writeByte((byte) 19); //ldc_w//
out.writeShort(index);
}//else//
stack.increment();
}//pushConstant()//
/**
* Pushes a wide constant value onto the stack using a constant value in the constant pool.
* A reference to the constant will be used to push the wide (8 byte) value on the stack. The stack will be incremented by two.
* @param index The value's index in the constant pool.
* @param out The output stream to write to.
* @param stack The stack tracker that will track the stack size.
*/
public void pushWideConstant(short index, StandardOutputStream out, StackTracker stack) throws IOException {
out.writeByte((byte) 20); //ldc2_w//
out.writeShort(index);
stack.increment(2);
}//pushWideConstant()//
}//MethodSupport//

View File

@@ -0,0 +1,397 @@
package com.de22.javabytecode.code;
import com.common.util.*;
/**
* Copyright Wynne Crisman 1999,2006<p>
* Defines an operation code.
*/
public class OpCode {
public static final int SIGNED_INT = 0;
public static final int UNSIGNED_INT = 1;
public static final int SIGNED_SHORT = 2;
public static final int UNSIGNED_SHORT = 3;
public static final int SIGNED_BYTE = 4;
public static final int UNSIGNED_BYTE = 5;
public static final int SHORT_CONSTANT_REF = 6; //Same as an UNSIGNED_BYTE, but identifies the param as being an index into the constant pool.//
public static final int CONSTANT_REF = 7; //Same as an UNSIGNED_SHORT, but identifies the param as being an index into the constant pool.//
private static final int[] EMPTY_INT_ARRAY = new int[0];
/** The opcode's name. */
private String name = null;
/** The opcode's general description. */
private String description = null;
/** The collection of parameter types, one for each parameter in the opcode. */
private int[] parameterTypes = null;
/** The collection of parameter descriptions, one for each parameter in the opcode. */
private IList parameterDescriptions = null;
/** The collection of parameter byte descriptions, one for each byte that follows the opcode. */
private IList parameterByteDescriptions = null;
private static IList opCodes = null;
/**
* Defines the array of all operation codes in Java.
*/
private static final OpCode[] opCodeArray = new OpCode[] {
new OpCode("nop", "Do nothing.", -9999), //00
new OpCode("aconst_null", "Push a null object reference onto the operand stack.", -9999), //01
new OpCode("iconst_m1", "Push the int constant -1 onto the operand stack.", -9999), //02
new OpCode("iconst_0", "Push the int constant 0 onto the operand stack.", -9999), //03
new OpCode("iconst_1", "Push the int constant 1 onto the operand stack.", -9999), //04
new OpCode("iconst_2", "Push the int constant 2 onto the operand stack.", -9999), //05
new OpCode("iconst_3", "Push the int constant 3 onto the operand stack.", -9999), //06
new OpCode("iconst_4", "Push the int constant 4 onto the operand stack.", -9999), //07
new OpCode("iconst_5", "Push the int constant 5 onto the operand stack.", -9999), //08
new OpCode("lconst_0", "Push the long constant 0 onto the operand stack.", -9999), //09
new OpCode("lconst_1", "Push the long constant 1 onto the operand stack.", -9999), //0A
new OpCode("fconst_0", "Push the float constant 0 onto the operand stack.", -9999), //0B
new OpCode("fconst_1", "Push the float constant 1 onto the operand stack.", -9999), //0C
new OpCode("fconst_2", "Push the float constant 2 onto the operand stack.", -9999), //0D
new OpCode("dconst_0", "Push the double constant 0 onto the operand stack.", -9999), //0E
new OpCode("dconst_1", "Push the double constant 1 onto the operand stack.", -9999), //0F
new OpCode("bipush", "Pushes a specified byte onto the operand stack as a signed int.", new int[] {SIGNED_BYTE}, new String[] {"The signed byte value."}, new String[] {"A signed byte."}, -9999), //10
new OpCode("sipush", "Pushes a specified short onto the operand stack as a signed int.", new int[] {SIGNED_SHORT}, new String[] {"The signed short value."}, new String[] {"The high byte.", "The low byte. ((byte1<<8)|byte2 = short)"}, -9999), //11
new OpCode("ldc", "Pushes a constant onto the operand stack (may be int, float, or string reference).", new int[] {SHORT_CONSTANT_REF}, new String[] {"The index in the ClassFile's constantPool."}, new String[] {"An index in the ClassFile's constantPool."}, -9999), //12
new OpCode("ldc_w", "Pushes a constant onto the operand stack (may be int, float, or string reference).", new int[] {CONSTANT_REF}, new String[] {"The index in the ClassFile's constantPool."}, new String[] {"The high byte of the index in the ClassFile's constantPool.", "The low byte ((highByte<<8)|lowByte = index)."}, -9999), //13
new OpCode("ldc2_w", "Pushes a constant onto the operand stack (may be long, or double).", new int[] {CONSTANT_REF}, new String[] {"The index in the ClassFile's constantPool."}, new String[] {"The high byte of the index in the ClassFile's constantPool.", "The low byte ((highByte<<8)|lowByte = index)."}, -9999), //14
new OpCode("iload", "Load an int from a local variable onto the operand stack.", new int[] {UNSIGNED_BYTE}, new String[] {"The index of the local variable."}, new String[] {"The index of the local variable."}, -9999), //15
new OpCode("lload", "Load a long from a local variable onto the operand stack.", new int[] {UNSIGNED_BYTE}, new String[] {"The index of the local variable."}, new String[] {"The index of the local variable."}, -9999), //16
new OpCode("fload", "Load a float from a local variable onto the operand stack.", new int[] {UNSIGNED_BYTE}, new String[] {"The index of the local variable."}, new String[] {"The index of the local variable."}, -9999), //17
new OpCode("dload", "Load a double from a local variable onto the operand stack.", new int[] {UNSIGNED_BYTE}, new String[] {"The index of the local variable."}, new String[] {"The index of the local variable."}, -9999), //18
new OpCode("aload", "Load an object reference from a local variable onto the operand stack.", new int[] {UNSIGNED_BYTE}, new String[] {"The index of the local variable."}, new String[] {"The index of the local variable."}, -9999), //19
new OpCode("iload_0", "Load an int from the local variable 0 onto the operand stack.", -9999), //1A
new OpCode("iload_1", "Load an int from the local variable 1 onto the operand stack.", -9999), //1B
new OpCode("iload_2", "Load an int from the local variable 2 onto the operand stack.", -9999), //1C
new OpCode("iload_3", "Load an int from the local variable 3 onto the operand stack.", -9999), //1D
new OpCode("lload_0", "Load a long from the local variable 0 onto the operand stack.", -9999), //1E
new OpCode("lload_1", "Load a long from the local variable 1 onto the operand stack.", -9999), //1F
new OpCode("lload_2", "Load a long from the local variable 2 onto the operand stack.", -9999), //20
new OpCode("lload_3", "Load a long from the local variable 3 onto the operand stack.", -9999), //21
new OpCode("fload_0", "Load a float from the local variable 0 onto the operand stack.", -9999), //22
new OpCode("fload_1", "Load a float from the local variable 1 onto the operand stack.", -9999), //23
new OpCode("fload_2", "Load a float from the local variable 2 onto the operand stack.", -9999), //24
new OpCode("fload_3", "Load a float from the local variable 3 onto the operand stack.", -9999), //25
new OpCode("dload_0", "Load a double from the local variable 0 onto the operand stack.", -9999), //26
new OpCode("dload_1", "Load a double from the local variable 1 onto the operand stack.", -9999), //27
new OpCode("dload_2", "Load a double from the local variable 2 onto the operand stack.", -9999), //28
new OpCode("dload_3", "Load a double from the local variable 3 onto the operand stack.", -9999), //29
new OpCode("aload_0", "Load an object reference from the local variable 0 onto the operand stack.", -9999), //2A
new OpCode("aload_1", "Load an object reference from the local variable 1 onto the operand stack.", -9999), //2B
new OpCode("aload_2", "Load an object reference from the local variable 2 onto the operand stack.", -9999), //2C
new OpCode("aload_3", "Load an object reference from the local variable 3 onto the operand stack.", -9999), //2D
new OpCode("iaload", "", -9999), //2E
new OpCode("laload", "", -9999), //2F
new OpCode("faload", "", -9999), //30
new OpCode("daload", "", -9999), //31
new OpCode("aaload", "", -9999), //32
new OpCode("baload", "", -9999), //33
new OpCode("caload", "", -9999), //34
new OpCode("saload", "", -9999), //35
new OpCode("istore", "Pops an int off the operand stack & stores it at the specified local variable index.", new int[] {UNSIGNED_BYTE}, new String[] {"The index of the local variable to be used."}, new String[] {"The index of the local variable to be used."}, -9999), //36
new OpCode("lstore", "Pops a long off the operand stack & stores it at the specified local variable index.", new int[] {UNSIGNED_BYTE}, new String[] {"The index of the local variable to be used."}, new String[] {"The index of the local variable to be used."}, -9999), //37
new OpCode("fstore", "Pops a float off the operand stack & stores it at the specified local variable index.", new int[] {UNSIGNED_BYTE}, new String[] {"The index of the local variable to be used."}, new String[] {"The index of the local variable to be used."}, -9999), //38
new OpCode("dstore", "Pops a double off the operand stack & stores it at the specified local variable index.", new int[] {UNSIGNED_BYTE}, new String[] {"The index of the local variable to be used."}, new String[] {"The index of the local variable to be used."}, -9999), //39
new OpCode("astore", "Pops an object reference off the operand stack & stores it at the specified local variable index.", new int[] {UNSIGNED_BYTE}, new String[] {"The index of the local variable to be used."}, new String[] {"The index of the local variable to be used."}, -9999), //3A
new OpCode("istore_0", "Pops an int off the operand stack & stores it in the local variable 0.", -9999), //3B
new OpCode("istore_1", "Pops an int off the operand stack & stores it in the local variable 1.", -9999), //3C
new OpCode("istore_2", "Pops an int off the operand stack & stores it in the local variable 2.", -9999), //3D
new OpCode("istore_3", "Pops an int off the operand stack & stores it in the local variable 3.", -9999), //3E
new OpCode("lstore_0", "Pops an int off the operand stack & stores it in the local variable 0.", -9999), //3F
new OpCode("lstore_1", "Pops a long off the operand stack & stores it in the local variable 1.", -9999), //40
new OpCode("lstore_2", "Pops a long off the operand stack & stores it in the local variable 2.", -9999), //41
new OpCode("lstore_3", "Pops a long off the operand stack & stores it in the local variable 3.", -9999), //42
new OpCode("fstore_0", "Pops a float off the operand stack & stores it in the local variable 0.", -9999), //43
new OpCode("fstore_1", "Pops a float off the operand stack & stores it in the local variable 1.", -9999), //44
new OpCode("fstore_2", "Pops a float off the operand stack & stores it in the local variable 2.", -9999), //45
new OpCode("fstore_3", "Pops a float off the operand stack & stores it in the local variable 3.", -9999), //46
new OpCode("dstore_0", "Pops a double off the operand stack & stores it in the local variable 0.", -9999), //47
new OpCode("dstore_1", "Pops a double off the operand stack & stores it in the local variable 1.", -9999), //48
new OpCode("dstore_2", "Pops a double off the operand stack & stores it in the local variable 2.", -9999), //49
new OpCode("dstore_3", "Pops a double off the operand stack & stores it in the local variable 3.", -9999), //4A
new OpCode("astore_0", "Pops an object reference off the operand stack & stores it in the local variable 0.", -9999), //4B
new OpCode("astore_1", "Pops an object reference off the operand stack & stores it in the local variable 1.", -9999), //4C
new OpCode("astore_2", "Pops an object reference off the operand stack & stores it in the local variable 2.", -9999), //4D
new OpCode("astore_3", "Pops an object reference off the operand stack & stores it in the local variable 3.", -9999), //4E
new OpCode("iastore", "Stores a value into an int array at an index (...,arrayref,index,value -> ...).", -9999), //4F
new OpCode("lastore", "Stores a value into a long array at an index (...,arrayref,index,value -> ...).", -9999), //50
new OpCode("fastore", "Stores a value into a float array at an index (...,arrayref,index,value -> ...).", -9999), //51
new OpCode("dastore", "Stores a value into a double array at an index (...,arrayref,index,value -> ...).", -9999), //52
new OpCode("aastore", "Stores a value into an object array at an index (...,arrayref,index,value -> ...).", -9999), //53
new OpCode("bastore", "Stores a value into a byte/boolean array at an index (...,arrayref,index,value -> ...).", -9999), //54
new OpCode("castore", "Stores a value into a char array at an index (...,arrayref,index,value -> ...).", -9999), //55
new OpCode("sastore", "Stores a value into a short array at an index (...,arrayref,index,value -> ...).", -9999), //56
new OpCode("pop", "Pops the top word off the operand stack.", -9999), //57
new OpCode("pop2", "Pops the top 2 words off the operand stack.", -9999), //58
new OpCode("dup", "Duplicates the top word on the operand stack.", -9999), //59
new OpCode("dup_x1", "Duplicates the top word on the operand stack & puts the duplicate one down.", -9999), //5A
new OpCode("dup_x2", "Duplicates the top word on the operand stack & puts the duplicate two down.", -9999), //5B
new OpCode("dup2", "Duplicates the top 2 words on the operand stack.", -9999), //5C
new OpCode("dup2_x1", "Duplicates the top 2 words on the operand stack & puts the duplicates one down.", -9999), //5D
new OpCode("dup2_x2", "Duplicates the top 2 words on the operand stack & puts the duplicates two down.", -9999), //5E
new OpCode("swap", "Swaps the top two words on the operand stack.", -9999), //5F
new OpCode("iadd", "Pops two int values off the operand stack and pushes back (value1+value2).", -9999), //60
new OpCode("ladd", "Pops two long values off the operand stack and pushes back (value1+value2).", -9999), //61
new OpCode("fadd", "Pops two float values off the operand stack and pushes back (value1+value2).", -9999), //62
new OpCode("dadd", "Pops two double values off the operand stack and pushes back (value1+value2).", -9999), //63
new OpCode("isub", "Pops two int values off the operand stack and pushes back (value1-value2).", -9999), //64
new OpCode("lsub", "Pops two long values off the operand stack and pushes back (value1-value2).", -9999), //65
new OpCode("fsub", "Pops two float values off the operand stack and pushes back (value1-value2).", -9999), //66
new OpCode("dsub", "Pops two double values off the operand stack and pushes back (value1-value2).", -9999), //67
new OpCode("imul", "Pops two int values off the operand stack and pushes back (value1*value2).", -9999), //68
new OpCode("lmul", "Pops two long values off the operand stack and pushes back (value1*value2).", -9999), //69
new OpCode("fmul", "Pops two float values off the operand stack and pushes back (value1*value2).", -9999), //6A
new OpCode("dmul", "Pops two double values off the operand stack and pushes back (value1*value2).", -9999), //6B
new OpCode("idiv", "Pops two int values off the operand stack and pushes back (value1/value2).", -9999), //6C
new OpCode("ldiv", "Pops two long values off the operand stack and pushes back (value1/value2).", -9999), //6D
new OpCode("fdiv", "Pops two float values off the operand stack and pushes back (value1/value2).", -9999), //6E
new OpCode("ddiv", "Pops two double values off the operand stack and pushes back (value1/value2).", -9999), //6F
new OpCode("irem", "Pops two int values off the operand stack and pushes back (value1%value2).", -9999), //70
new OpCode("lrem", "Pops two long values off the operand stack and pushes back (value1%value2).", -9999), //71
new OpCode("frem", "Pops two float values off the operand stack and pushes back (value1%value2).", -9999), //72
new OpCode("drem", "Pops two double values off the operand stack and pushes back (value1%value2).", -9999), //73
new OpCode("ineg", "Pops an int value off the operand stack and pushes back (0 - value).", -9999), //74
new OpCode("lneg", "Pops a long value off the operand stack and pushes back (0 - value).", -9999), //75
new OpCode("fneg", "Pops a float value off the operand stack and pushes back (0 - value).", -9999), //76
new OpCode("dneg", "Pops a double value off the operand stack and pushes back (0 - value).", -9999), //77
new OpCode("ishl", "", -9999), //78
new OpCode("lshl", "", -9999), //79
new OpCode("ishr", "", -9999), //7A
new OpCode("lshr", "", -9999), //7B
new OpCode("iushr", "", -9999), //7C
new OpCode("lushr", "", -9999), //7D
new OpCode("iand", "", -9999), //7E
new OpCode("land", "", -9999), //7F
new OpCode("ior", "", -9999), //80
new OpCode("lor", "", -9999), //81
new OpCode("ixor", "", -9999), //82
new OpCode("lxor", "", -9999), //83
new OpCode("iinc", "Increments a local variable by a given constant.", new int[] {UNSIGNED_BYTE, SIGNED_BYTE}, new String[] {"The index to an integer local variable in the current frame.", "The signed byte (converted to a sign extended integer) used to modify the local variable referenced by the first parameter."}, new String[] {"The local variable index (int).", "The signed constant value (int)"}, -9999), //84
new OpCode("i2l", "", -9999), //85
new OpCode("i2f", "", -9999), //86
new OpCode("i2d", "", -9999), //87
new OpCode("l2i", "", -9999), //88
new OpCode("l2f", "", -9999), //89
new OpCode("l2d", "", -9999), //8A
new OpCode("f2i", "", -9999), //8B
new OpCode("f2l", "", -9999), //8C
new OpCode("f2d", "", -9999), //8D
new OpCode("d2i", "", -9999), //8E
new OpCode("d2l", "", -9999), //8F
new OpCode("d2f", "", -9999), //90
new OpCode("i2b", "", -9999), //91
new OpCode("i2c", "", -9999), //92
new OpCode("i2s", "", -9999), //93
new OpCode("lcmp", "", -9999), //94
new OpCode("fcmpl", "", -9999), //95
new OpCode("fcmpg", "", -9999), //96
new OpCode("dcmpl", "", -9999), //97
new OpCode("dcmpg", "", -9999), //98
new OpCode("ifeq", "Pops an int value off the operand stack and checks for (value == 0).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //99
new OpCode("ifne", "Pops an int value off the operand stack and checks for (value != 0).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //9A
new OpCode("iflt", "Pops an int value off the operand stack and checks for (value < 0).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //9B
new OpCode("ifge", "Pops an int value off the operand stack and checks for (value >= 0).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //9C
new OpCode("ifgt", "Pops an int value off the operand stack and checks for (value > 0).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //9D
new OpCode("ifle", "Pops an int value off the operand stack and checks for (value <= 0).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //9E
new OpCode("if_icmpeq", "Pops two int values off the operand stack and checks for (value1 == value2).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //9F
new OpCode("if_icmpne", "Pops two int values off the operand stack and checks for (value1 != value2).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //A0
new OpCode("if_icmplt", "Pops two int values off the operand stack and checks for (value1 < value2).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //A1
new OpCode("if_icmpge", "Pops two int values off the operand stack and checks for (value1 >= value2).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //A2
new OpCode("if_icmpgt", "Pops two int values off the operand stack and checks for (value1 > value2).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //A3
new OpCode("if_icmple", "Pops two int values off the operand stack and checks for (value1 <= value2).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //A4
new OpCode("if_acmpeq", "Pops two object references off the operand stack and checks for (value1 == value2).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //A5
new OpCode("if_acmpne", "Pops two object references off the operand stack and checks for (value1 != value2).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //A6
new OpCode("goto", "Performs a branch operation.", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset."}, new String[] {"The high byte of the signed 16bit jump offset.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //A7
new OpCode("jsr", "", -9999), //A8
new OpCode("ret", "", -9999), //A9
new OpCode("tableswitch", "", -9999), //AA
new OpCode("lookupswitch", "", -9999), //AB
new OpCode("ireturn", "Return an int (off the operand stack).", -9999), //AC
new OpCode("lreturn", "Return a long (off the operand stack).", -9999), //AD
new OpCode("freturn", "Return a float (off the operand stack).", -9999), //AE
new OpCode("dreturn", "Return a double (off the operand stack).", -9999), //AF
new OpCode("areturn", "Return a reference (off the operand stack).", -9999), //B0
new OpCode("return", "Return from this method.", -9999), //B1
new OpCode("getstatic", "Pops an object reference off the operand stack and pushes the value (1 or 2 words) of that object's static attribute (CodeAttribute determined by the ConstantFieldReference indexed in the ClassFile's constantPool)", new int[] {CONSTANT_REF}, new String[] {"The index into the ClassFile's constantPool (Should index a ConstantFieldReference)."}, new String[] {"The high byte of an index into the ClassFile's constantPool (Should index a ConstantFieldReference).", "The low byte of the offset. ((highbyte<<8)|lowbyte = index)"}, -9999), //B2
new OpCode("putstatic", "Pops an object reference and the value (1 or 2 words) off the operand stack and sets the value of that object's static attribute (CodeAttribute determined by the ConstantFieldReference indexed in the ClassFile's constantPool)", new int[] {CONSTANT_REF}, new String[] {"The index into the ClassFile's constantPool (Should index a ConstantFieldReference)."}, new String[] {"The high byte of an index into the ClassFile's constantPool (Should index a ConstantFieldReference).", "The low byte of the offset. ((highbyte<<8)|lowbyte = index)"}, -9999), //B3
new OpCode("getfield", "Pops an object reference off the operand stack and pushes the value (1 or 2 words) of that object's attribute (CodeAttribute determined by the ConstantFieldReference indexed in the ClassFile's constantPool)", new int[] {CONSTANT_REF}, new String[] {"The index into the ClassFile's constantPool (Should index a ConstantFieldReference)."}, new String[] {"The high byte of an index into the ClassFile's constantPool (Should index a ConstantFieldReference).", "The low byte of the offset. ((highbyte<<8)|lowbyte = index)"}, -9999), //B4
new OpCode("putfield", "Pops an object reference and the value (1 or 2 words) off the operand stack and sets the value of that object's attribute (CodeAttribute determined by the ConstantFieldReference indexed in the ClassFile's constantPool)", new int[] {CONSTANT_REF}, new String[] {"The index into the ClassFile's constantPool (Should index a ConstantFieldReference)."}, new String[] {"The high byte of an index into the ClassFile's constantPool (Should index a ConstantFieldReference).", "The low byte of the offset. ((highbyte<<8)|lowbyte = index)"}, -9999), //B5
new OpCode("invokevirtual", "Pops an object reference, all method parameters and invokes the indexed method.", new int[] {CONSTANT_REF}, new String[] {"The index of a ConstantMethodReference in the ClassFile's constantPool."}, new String[] {"The highbyte of an index of a ConstantMethodReference in the ClassFile's constantPool.", "The lowbyte ((highbyte<<8)|lowbyte = index)."}, -9999), //B6
new OpCode("invokespecial", "Pops an object reference, all method parameters and invokes the indexed method.", new int[] {CONSTANT_REF}, new String[] {"The index of a ConstantMethodReference in the ClassFile's constantPool."}, new String[] {"The highbyte of an index of a ConstantMethodReference in the ClassFile's constantPool.", "The lowbyte ((highbyte<<8)|lowbyte = index)."}, -9999), //B7
new OpCode("invokestatic", "Pops all method parameters and invokes the indexed method.", new int[] {CONSTANT_REF}, new String[] {"The index of a ConstantMethodReference in the ClassFile's constantPool."}, new String[] {"The highbyte of an index of a ConstantMethodReference in the ClassFile's constantPool.", "The lowbyte ((highbyte<<8)|lowbyte = index)."}, -9999), //B8
new OpCode("invokeinterface", "Pops an object reference, all method parameters and invokes the indexed method.", new int[] {CONSTANT_REF, UNSIGNED_BYTE, UNSIGNED_BYTE}, new String[] {"The index of a ConstantInterfaceMethodReference in the ClassFile's constantPool.", "The number of words (plus one) that should be popped off the operand stack (to be used as parameter(s)).", "Reserved; Must be 0."}, new String[] {"The highbyte of an index of a ConstantInterfaceMethodReference in the ClassFile's constantPool.", "The lowbyte ((highbyte<<8)|lowbyte = index).", "The number of words (plus one) that should be popped off the operand stack (to be used as parameter(s)).", "Reserved; Must be 0."}, -9999), //B9
new OpCode("xxxunusedxxx", "", -9999), //BA
new OpCode("new", "Creates a new object of the given type.", new int[] {CONSTANT_REF}, new String[] {"The constant reference to the object type."}, new String[] {"", ""}, -9999), //BB
new OpCode("newarray", "Creates a new array whose size is an int popped off the stack.", new int[] {UNSIGNED_BYTE}, new String[] {"The array type code."}, new String[] {"The array type code."}, -9999), //BC
new OpCode("anewarray", "Pops an int (count) off the stack & creates a new array of size count.", new int[] {CONSTANT_REF}, new String[] {"The index of a ConstantClass in the ClassFile's constantPool (The type of the array)."}, new String[] {"The highbyte of an index of a ConstantClass in the ClassFile's constantPool (The type of the array).", "The lowbyte ((highbyte<<8)|lowbyte = index)."}, -9999), //BD
new OpCode("arraylength", "", -9999), //BE
new OpCode("athrow", "", -9999), //BF
new OpCode("checkcast", "Checks the object reference on the operand stack is castable to the given ConstantClass", new int[] {CONSTANT_REF}, new String[] {"The index of a ConstantClass in the ClassFile's constantPool."}, new String[] {"The highbyte of an index of a ConstantClass in the ClassFile's constantPool.", "The lowbyte ((highbyte<<8)|lowbyte = index)."}, -9999), //C0
new OpCode("instanceof", "", new int[] {CONSTANT_REF}, new String[] {"The index in the ClassFile's constantPool."}, new String[] {"The high byte of the index in the ClassFile's constantPool.", "The low byte ((highByte<<8)|lowByte = index)."}, -9999), //C1
new OpCode("monitorenter", "", -9999), //C2
new OpCode("monitorexit", "", -9999), //C3
new OpCode("wide", "", -9999), //C4
new OpCode("multianewarray", "", -9999), //C5
new OpCode("ifnull", "Pops an object reference value off the operand stack and checks for (value == null).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //C6
new OpCode("ifnonnull", "Pops an object reference value off the operand stack and checks for (value != null).", new int[] {SIGNED_SHORT}, new String[] {"The signed 16bit jump offset used when the statement resolves to true."}, new String[] {"The high byte of the signed 16bit jump offset used when the statement resolves to true.", "The low byte of the offset. ((highbyte<<8)|lowbyte = offset)"}, -9999), //C7
new OpCode("goto_w", "", new int[] {SIGNED_INT}, new String[] {"The signed 32bit jump offset."}, new String[] {"The first (high) byte of the signed 32bit jump offset.", "The second byte of the offset.", "The third byte of the offset.", "The fourth (low) byte of the offset. ((byte1<<24)|(byte2<<24)|(byte3<<24)|(byte4) = offset)"}, -9999), //C8
new OpCode("jsr_w", "", -9999), //C9
new OpCode("breakpoint", "Reserved", -9999), //CA
new OpCode("ldc_quick", "", -9999), //CB
new OpCode("ldc_w_quick", "", -9999), //CC
new OpCode("ldc2_w_quick", "", -9999), //CD
new OpCode("getfield_quick", "", -9999), //CE
new OpCode("putfield_quick", "", -9999), //CF
new OpCode("getfield2_quick", "", -9999), //D0
new OpCode("putfield2_quick", "", -9999), //D1
new OpCode("getstatic_quick", "", -9999), //D2
new OpCode("putstatic_quick", "", -9999), //D3
new OpCode("getstatic2_quick", "", -9999), //D4
new OpCode("putstatic2_quick", "", -9999), //D5
new OpCode("invokevirtual_quick", "", -9999), //D6
new OpCode("invokenonvirtual_quick", "", -9999), //D7
new OpCode("invokesuper_quick", "", -9999), //D8
new OpCode("invokestatic_quick", "", -9999), //D9
new OpCode("invokeinterface_quick", "", -9999), //DA
new OpCode("invokevirtualobject_quick", "", -9999), //DB
new OpCode("xxx", "", -9999), //DC
new OpCode("new_quick", "", -9999), //DD
new OpCode("anewarray_quick", "", -9999), //DE
new OpCode("multianewarray_quick", "", -9999), //DF
new OpCode("checkcast_quick", "", -9999), //E0
new OpCode("instanceof_quick", "", -9999), //E1
new OpCode("invokevirtual_quick_w", "", -9999), //E2
new OpCode("getfield_quick_w", "", -9999), //E3
new OpCode("putfield_quick_w", "", -9999), //E4
new OpCode("xxx", "", -9999), //E5
new OpCode("xxx", "", -9999), //E6
new OpCode("xxx", "", -9999), //E7
new OpCode("xxx", "", -9999), //E8
new OpCode("xxx", "", -9999), //E9
new OpCode("xxx", "", -9999), //EA
new OpCode("xxx", "", -9999), //EB
new OpCode("xxx", "", -9999), //EC
new OpCode("xxx", "", -9999), //ED
new OpCode("xxx", "", -9999), //EE
new OpCode("xxx", "", -9999), //EF
new OpCode("xxx", "", -9999), //F0
new OpCode("xxx", "", -9999), //F1
new OpCode("xxx", "", -9999), //F2
new OpCode("xxx", "", -9999), //F3
new OpCode("xxx", "", -9999), //F4
new OpCode("xxx", "", -9999), //F5
new OpCode("xxx", "", -9999), //F6
new OpCode("xxx", "", -9999), //F7
new OpCode("xxx", "", -9999), //F8
new OpCode("xxx", "", -9999), //F9
new OpCode("xxx", "", -9999), //FA
new OpCode("xxx", "", -9999), //FB
new OpCode("xxx", "", -9999), //FC
new OpCode("xxx", "", -9999), //FD
new OpCode("impdep1", "Reserved", -9999), //FE
new OpCode("impdep2", "Reserved", -9999) //FF
};//opCodes//
/**
* OpCode constructor.
*/
private OpCode() {
super();
}//OpCode()//
/**
* OpCode constructor.
* @param name The operational code name.
* @param description The useful description of this op code.
* @param extra The number of extra bytes following the op code.
*/
private OpCode(String name, String description, int extra) {
this.name = name;
this.description = description;
this.parameterTypes = EMPTY_INT_ARRAY;
this.parameterDescriptions = LiteList.EMPTY_LIST;
this.parameterByteDescriptions = LiteList.EMPTY_LIST;
}//OpCode()//
/**
* OpCode constructor.
* @param name The operational code name.
* @param description The useful description of this op code.
* @param parameterTypes
* @param parameterDescriptions
* @param parameterByteDescriptions
* @param extra The number of extra bytes following the op code.
*/
private OpCode(String name, String description, int[] parameterTypes, String[] parameterDescriptions, String[] parameterByteDescriptions, int extra) {
this.name = name;
this.description = description;
this.parameterTypes = parameterTypes;
this.parameterDescriptions = parameterDescriptions != null ? new LiteList(parameterDescriptions) : LiteList.EMPTY_LIST;
this.parameterDescriptions.isChangeable(false);
this.parameterByteDescriptions = parameterByteDescriptions != null ? new LiteList(parameterByteDescriptions) : LiteList.EMPTY_LIST;
this.parameterByteDescriptions.isChangeable(false);
}//OpCode()//
/**
* Gets the operational code's description.
* @return The description of the OpCode.
*/
public String getDescription() {
return description;
}//getDescription()//
/**
* Gets the operational code's name.
* @return The name of the OpCode.
*/
public String getName() {
return name;
}//getName()//
/**
* Gets the OpCode objects that represent all valid java operational codes.
* @return The ordered array of all OpCodes.
*/
public static IList getOpCodes() {
if(opCodes == null) {
synchronized(OpCode.class) {
if(opCodes == null) {
opCodes = new LiteList(opCodeArray);
opCodes.isChangeable(false);
}//if//
}//synchronized//
}//if//
return opCodes;
}//getOpCodes()//
/**
* Gets the parameter type codes (see OpCode identifiers), one for each parameter to the opcode.
* @return The collection of parameter types.
*/
public int[] getParameterTypes() {
return parameterTypes;
}//getParameterTypes()//
/**
* Gets the descriptions for each parameter in order that they occur.
* @return The ordered collection of parameter descriptions.
*/
public IList getParameterDescriptions() {
return parameterDescriptions;
}//getParameterDescriptions()//
/**
* Gets the descriptions for each parameter byte in order that they occur.
* @return The ordered collection of descriptions for each byte that follows the opcode.
*/
public IList getParameterByteDescriptions() {
return parameterByteDescriptions;
}//getParameterByteDescriptions()//
/**
* Provides a human readable representation of the OpCode.
* @return A string representation.
*/
public String toString() {
String retVal = "";
retVal += name;
for(int t = name.length(); t < 12; t++) {
retVal += " ";
}//for//
retVal += " " + description;
return retVal;
}//toString()//
}//OpCode//

View File

@@ -0,0 +1,170 @@
package com.de22.javabytecode.code;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import com.common.debug.Debug;
import com.common.io.ByteArrayInputStream;
import com.common.io.ByteArrayOutputStream;
import com.common.io.ObjectInputStream;
import com.common.io.ObjectOutputStream;
import com.de22.javabytecode.Type;
/**
* Copyright Declarative Engineering LLC 2007<p>
* A simple test class that reads in a class file and then writes it out without any modifications.
* This class tests the ability to read and write a class file without introducing errors.
*/
public class Test {
/**
* Reverse engineers a class file and writes it back out to 'output.class'.
* @param args The array of initialization parameters.
* args[0] = The path and file name of the class to reverse engineer.
* args[1] = The path (must exist) where the output.class file will be stored. If the file exists it will be over written.
*/
public static void main(String[] args) {
String debugText = null;
long t = System.currentTimeMillis();
try {
File file = new File(args[0]);
File path = args.length > 1 ? new File(args[1]) : null;
if(path != null && (path.isFile() || (!path.exists() && !path.mkdirs()))) {
path = null;
}//if//
if(file.getName().endsWith(".class")) {
debugText = file.getPath();
processClassFile(file, new File(path, "output.class"));
compareClassFiles(file, new File(path, "output.class"));
}//if//
}//try//
catch(Throwable e) {
Debug.log(e, "Caught while processing the class file: " + debugText);
}//catch//
Debug.log("Total time (milliseconds): " + (System.currentTimeMillis() - t));
System.exit(0);
}//main()//
/**
* Writes a file.
* @param file The file to write.
* @param bytes The bytes to place in the file.
*/
public static void writeFile(File file, byte[] bytes) {
FileOutputStream fout = null;
try {
fout = new FileOutputStream(file);
fout.write(bytes);
}//try//
catch(Throwable e) {
e.printStackTrace();
}//catch//
finally {
if(fout != null) {
try {
fout.close();
}//try//
catch(Throwable e) {
}//catch//
}//if//
}//finally//
}//writeFile()//
/**
* Reads a file and returns the bytes contained there in.
* @param file The file to read.
* @return The bytes contained in the file.
*/
public static byte[] readFile(File file) {
FileInputStream fin = null;
byte[] bytes = null;
int counter = 0;
try {
fin = new FileInputStream(file);
bytes = new byte[fin.available()];
while(counter < bytes.length) {
counter += fin.read(bytes, counter, bytes.length - counter);
}//while//
fin.close();
}//try//
catch(Throwable e) {
e.printStackTrace();
}//catch//
finally {
if(fin != null) {
try {
fin.close();
}//try//
catch(Throwable e) {
}//catch//
}//if//
}//finally//
return bytes;
}//readFile()//
/**
* Processes a class file and writes out the byte codes in a semi-readable form.
* @param bytesIn The input stream containing the bytes codes. This stream will always be closed by this method.
* @param outputPath The optional path where the class output should be written to (under its packaging of course). The console will be used otherwise.
*/
private static void processClassFile(File inputFile, File outputFile) throws Throwable {
ByteArrayInputStream bin = null;
ObjectInputStream in = null;
Type type = null;
ByteArrayOutputStream bout = null;
ObjectOutputStream out = null;
try {
bin = new ByteArrayInputStream(readFile(inputFile));
in = new ObjectInputStream(bin, null, null);
type = new Type();
type.readExternal(in);
bout = new ByteArrayOutputStream(100000, false);
out = new ObjectOutputStream(bout, null);
type.writeExternal(out);
out.flush();
writeFile(outputFile, bout.toByteArray());
}//try//
catch(Throwable e) {
e.printStackTrace();
throw e;
}//catch//
}//processClassFile()//
/**
* Processes a class file and writes out the byte codes in a semi-readable form.
* @param bytesIn The input stream containing the bytes codes. This stream will always be closed by this method.
* @param outputPath The optional path where the class output should be written to (under its packaging of course). The console will be used otherwise.
*/
private static void compareClassFiles(File inputFile, File outputFile) throws Throwable {
byte[] originalBytes;
byte[] modifiedBytes;
int byteLength;
originalBytes = readFile(inputFile);
modifiedBytes = readFile(outputFile);
byteLength = Math.min(originalBytes.length, modifiedBytes.length);
for(int index = 0; index < byteLength; index++) {
if(originalBytes[index] != modifiedBytes[index]) {
Debug.log("Files diverge at index " + index);
return;
}//if//
}//for//
if(originalBytes.length != modifiedBytes.length) {
Debug.log("One file has more bytes than the other.");
}//if//
else {
Debug.log("Files are exactly the same.");
}//else//
}//processClassFile()//
}//Test//