/* --- Copyright Jonathan Meyer 1997. All rights reserved. ----------------- > File: jasmin/src/jasmin/ClassFile.java > Purpose: Uses a parser and the JAS package to create Java class files > Author: Jonathan Meyer, 10 July 1996 */ package jasmin; /* * This class is a bit monolithic, and should probably be converted into * several smaller classes. However, for this specific application, * its acceptable. * */ import jas.*; import java.io.InputStream; import java.io.OutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.*; /** * A ClassFile object is used to represent the binary data that makes up a * Java class file - it also serves as the public * API to the Jasmin assembler, though users should beware: the API * is likely to change in future versions (though its such a small API * at the moment that changes are likely to have only a small impact).
* * To assemble a file, you first construct a jasmin.ClassFile object, then * call readJasmin() to read in the contents of a Jasmin assembly file, then * call write() to write out the binary representation of the class file.
*
* There are a few other utility methods as well. See Main.java for an example
* which uses all of the public methods provided in ClassFile.
*
* @author Jonathan Meyer
* @version 1.05, 8 Feb 1997
*/
public class ClassFile {
// state info for the class being built
String filename;
ClassEnv class_env;
String class_name;
String source_name;
Scanner scanner;
// state info for the current method being defined
String method_name;
String method_signature;
short method_access;
ExceptAttr except_attr;
Catchtable catch_table;
LocalVarTableAttr var_table;
LineTableAttr line_table;
CodeAttr code;
Hashtable labels;
int line_label_count, line_num;
boolean auto_number;
// state info for lookupswitch and tableswitch instructions
Vector switch_vec;
int low_value;
int high_value;
static final String BGN_METHOD = "bgnmethod:";
static final String END_METHOD = "endmethod:";
// number of errors reported in a file.
int errors;
//
// Error reporting method
//
void report_error(String msg) {
// Print out filename/linenumber/message
System.err.print(filename + ":");
System.err.print(scanner.line_num);
System.err.println(": " + msg + ".");
if (scanner.char_num >= 0) {
System.err.println(scanner.line.toString());
// Print out where on the line the scanner got to
int i;
for (i = 0; i < scanner.char_num; i++) {
if (scanner.line.charAt(i) == '\t') {
System.err.print("\t");
} else {
System.err.print(" ");
}
}
System.err.println("^");
}
errors++;
}
//
// called by the .source directive
//
void setSource(String name) {
source_name = name;
}
//
// called by the .class directive
//
void setClass(String name, short acc) {
class_name = name;
class_env.setClass(new ClassCP(name));
class_env.setClassAccess(acc);
}
//
// called by the .super directive
//
void setSuperClass(String name) {
class_env.setSuperClass(new ClassCP(name));
}
//
// called by the .implements directive
//
void addInterface(String name) {
class_env.addInterface(new ClassCP(name));
}
//
// called by the .field directive
//
void addField(short access, String name,
String sig, Object value) {
if (value == null) {
// defining a field which doesn't have an initial value
class_env.addField(new Var(access, new AsciiCP(name),
new AsciiCP(sig), null));
} else {
// defining a field with an initial value...
// create a constant pool entry for the initial value
CP cp = null;
if (value instanceof Integer) {
cp = new IntegerCP(((Integer)value).intValue());
} else if (value instanceof Float) {
cp = new FloatCP(((Float)value).floatValue());
} else if (value instanceof Double) {
cp = new DoubleCP(((Double)value).doubleValue());
} else if (value instanceof Long) {
cp = new LongCP(((Long)value).longValue());
} else if (value instanceof String) {
cp = new StringCP((String)value);
}
// add the field
class_env.addField(new Var(access, new AsciiCP(name),
new AsciiCP(sig), new ConstAttr(cp)));
}
}
//
// called by the .method directive to start the definition for a method
//
void newMethod(String name, String signature, int access) {
// set method state variables
labels = new Hashtable();
method_name = name;
code = null;
except_attr = null;
catch_table = null;
var_table = null;
line_table = null;
line_label_count = 0;
method_signature = signature;
method_access = (short)access;
}
//
// called by the .end method directive to end the definition for a method
//
void endMethod() throws jasError {
if (code != null) {
plantLabel(END_METHOD);
if (catch_table != null) {
code.setCatchtable(catch_table);
}
if (var_table != null) {
code.setLocalVarTable(var_table);
}
if (line_table != null) {
code.setLineTable(line_table);
}
}
class_env.addMethod(method_access, method_name, method_signature,
code, except_attr);
// clear method state variables
code = null;
labels = null;
method_name = null;
code = null;
except_attr = null;
catch_table = null;
line_table = null;
var_table = null;
}
//
// plant routines - these use addInsn to add instructions to the
// code for the current method.
//
//
// used for instructions that take no arguments
//
void plant(String name) throws jasError {
InsnInfo insn = InsnInfo.get(name);
autoNumber();
if (insn.args.equals("")) {
_getCode().addInsn(new Insn(insn.opcode));
} else if (insn.name.equals("wide")) {
// don't do anything for this one...
} else {
throw new jasError("Missing arguments for instruction " + name);
}
}
//
// used for iinc
//
void plant(String name, int v1, int v2) throws jasError {
autoNumber();
if (name.equals("iinc")) {
_getCode().addInsn(new IincInsn(v1, v2));
} else {
throw new jasError("Bad arguments for instruction " + name);
}
}
//
// used for instructions that take an integer parameter
//
void plant(String name, int val) throws jasError {
InsnInfo insn = InsnInfo.get(name);
CodeAttr code = _getCode();
autoNumber();
if (insn.args.equals("i")) {
code.addInsn(new Insn(insn.opcode, val));
} else if (insn.args.equals("constant")) {
code.addInsn(new Insn(insn.opcode, new IntegerCP(val)));
} else if (insn.args.equals("bigconstant")) {
code.addInsn(new Insn(insn.opcode, new LongCP(val)));
} else {
throw new jasError("Bad arguments for instruction " + name);
}
}
//
// used for ldc and other instructions that take a numeric argument
//
void plant(String name, Number val) throws jasError {
InsnInfo insn = InsnInfo.get(name);
CodeAttr code = _getCode();
autoNumber();
if (insn.args.equals("i") && (val instanceof Integer)) {
code.addInsn(new Insn(insn.opcode, val.intValue()));
} else if (insn.args.equals("constant")) {
if (val instanceof Integer || val instanceof Long) {
code.addInsn(new Insn(insn.opcode,
new IntegerCP(val.intValue())));
} else if (val instanceof Float || val instanceof Double) {
code.addInsn(new Insn(insn.opcode,
new FloatCP(val.floatValue())));
}
} else if (insn.args.equals("bigconstant")) {
if (val instanceof Integer || val instanceof Long) {
code.addInsn(new Insn(insn.opcode,
new LongCP(val.longValue())));
} else if (val instanceof Float || val instanceof Double) {
code.addInsn(new Insn(insn.opcode,
new DoubleCP(val.doubleValue())));
}
} else {
throw new jasError("Bad arguments for instruction " + name);
}
}
//
// used for ldc