package org.nanovm.converter;
import java.util.Arrays;
/**
* Just to create the NVM bytecode
* todo use logging
*/
public class Converter {
private static final int MAGIC = 0xBE000000;
private static final int VERSION = 2;
private byte[] outputBuffer;
private int cur;
private final ClassLoader classLoader;
public Converter(ClassLoader classLoader, int byteCodeLimit) throws ConvertException, ClassNotFoundException {
outputBuffer = new byte[byteCodeLimit];
this.classLoader = classLoader;
try {
writeHeader(); // write file header
writeClassHeaders(); // write class headers
writeConstantEntries(); // write all 32-bit constants
writeStrings(); // write all string data
writeMethods(); // write method headers and byte code
updateHeader(); // update feature values
} catch (ArrayIndexOutOfBoundsException ex){
throw new ConvertException("Converted data too big");
}
}
public byte[] toByteArray(){
return Arrays.copyOf(outputBuffer, cur);
}
// write uvm file header
private void writeHeader() throws ConvertException {
int offset = 15; // header size: 15 bytes
write32(MAGIC | UsedFeatures.get());
write8(VERSION);
write8(classLoader.totalMethods());
write16(classLoader.getMainIndex());
// offset to constant data
offset += 2 * classLoader.totalClasses(); // class header size: 2bytes
write16(offset);
// offset to string data
offset += 4 * classLoader.totalConstantEntries(); // constant value size: 4bytes
write16(offset);
// offset to method data
offset += 2 * classLoader.totalStrings(); // string indices
offset += classLoader.totalStringSize(); // string data
write16(offset);
write8(classLoader.totalStaticFields()); // static fields
}
// write all class headers
private void writeClassHeaders() throws ConvertException {
for (int i = 0; i < classLoader.totalClasses(); i++) {
ClassInfo classInfo = classLoader.getClassInfo(i);
write8(classInfo.getSuperClassIndex());
write8(classInfo.nonStaticFields());
}
}
// write all 32bit constant values
private void writeConstantEntries() throws ConvertException {
System.out.println("Writing " + classLoader.totalConstantEntries() + " constant entries");
for (int i = 0; i < classLoader.totalConstantEntries(); i++) {
System.out.println(" entry[" + i + "] = 0x" + Integer.toHexString(classLoader.getConstantEntry(i)));
write32(classLoader.getConstantEntry(i));
}
}
// write all string headers and data
private void writeStrings() throws ConvertException {
System.out.println("Writing " + classLoader.totalStrings() + " strings");
// write array of string offsets
int offset = 2 * classLoader.totalStrings();
for (int i = 0; i < classLoader.totalStrings(); i++) {
write16(offset);
offset += classLoader.getString(i).length() + 1;
}
// write the strings itself
for (int i = 0; i < classLoader.totalStrings(); i++) {
String str = classLoader.getString(i);
System.out.println(" entry[" + (i + classLoader.totalConstantEntries()) + "] = \"" + str + "\"");
// write zero terminated c strings
for (int j = 0; j < str.length(); j++) write8(str.charAt(j));
write8(0);
}
}
// write all methods
private void writeMethods() throws ConvertException, ClassNotFoundException {
int codeOffset = 0;
// build the method id table
MethodIdTable mt = new MethodIdTable(classLoader);
mt.build();
// write all Method headers
for (int i = 0; i < classLoader.totalMethods(); i++) {
MethodInfo methodInfo = classLoader.getMethod(i);
// offset from this header to bytecode (this header is 8 bytes
// in size)
write16((classLoader.totalMethods() - i) * 8 + codeOffset); // code_index
write16((classLoader.getClassIndex(i) << 8) + mt.getEntry(i));// id
write8(methodInfo.getName().equals("<clinit>") ? 1 : 0); // flags
write8(methodInfo.getArgs()); // args
write8(methodInfo.getCodeInfo().getMaxLocals()); // max_locals
write8(methodInfo.getCodeInfo().getMaxStack()); // max_stack
codeOffset += methodInfo.getCodeInfo().getBytecode().length;
}
// write bytecode
for (int i = 0; i < classLoader.totalMethods(); i++) {
ClassInfo classInfo = classLoader.getClassInfoFromMethodIndex(i);
MethodInfo methodInfo = classLoader.getMethod(i);
System.out.println("Converting " +
classInfo.getName() + "." +
methodInfo.getName() + ":" +
methodInfo.getSignature());
byte code[] = classLoader.getMethod(i).getCodeInfo().getBytecode();
// adjust references etc
CodeTranslator.translate(classInfo, code);
// and write bytecode
for (int j = 0; j < code.length; j++){
write8(code[j]);
}
System.out.println("");
}
}
private void updateHeader() throws ConvertException {
int old_cur = cur;
cur = 0;
writeHeader();
cur = old_cur;
}
// write a 8 bit value into buffer and make sure buffer
// end is not overwritten
private void write8(int val){
outputBuffer[cur++] = (byte) val;
}
// write a 16 bit value little endian into buffer
private void write16(int val){
write8(val & 0xff);
write8((val >> 8) & 0xff);
}
// write a 32 bit value little endian into buffer
private void write32(int val){
write8(val & 0xff);
write8((val >> 8) & 0xff);
write8((val >> 16) & 0xff);
write8((val >> 24) & 0xff);
}
}