package org.nanovm;
//
// NanoVMTool, Converter and Upload Tool for the NanoVM
// Copyright (C) 2005 by Till Harbaum <Till@Harbaum.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// Parts of this tool are based on public domain code written by Kimberley
// Burchett: http://www.kimbly.com/code/classfile/
//
//
// UVMWriter.java
//
import org.nanovm.converter.Config;
import org.nanovm.converter.UsedFeatures;
import org.nanovm.converter.MethodInfo;
import org.nanovm.converter.MethodIdTable;
import org.nanovm.converter.CodeTranslator;
import org.nanovm.converter.ClassLoader;
import org.nanovm.converter.ClassInfo;
import java.io.*;
import java.util.*;
public class UVMWriter {
static final int MAGIC = 0xBE000000;
static final int VERSION = 2;
byte[] outputBuffer;
int cur;
// write a 8 bit value into buffer and make sure buffer
// end is not overwritten
void write8(int val) throws ConvertException {
if(cur >= Config.getMaxSize())
throw new ConvertException("Converted data too big");
outputBuffer[cur++] = (byte)val;
}
// write a 16 bit value little endian into buffer
void write16(int val) throws ConvertException {
write8(val&0xff);
write8((val>>8)&0xff);
}
// write a 32 bit value little endian into buffer
void write32(int val) throws ConvertException {
write8(val&0xff);
write8((val>>8)&0xff);
write8((val>>16)&0xff);
write8((val>>24)&0xff);
}
void updateHeader() throws ConvertException {
int old_cur=cur;
cur = 0;
writeHeader();
cur = old_cur;
}
// write uvm file header
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
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
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
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
void writeMethods() throws ConvertException {
int codeOffset = 0;
// build the method id table
MethodIdTable.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) +
MethodIdTable.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("");
}
}
public UVMWriter(boolean writeHeader) {
System.out.println("Generating unified class file ...");
// create output buffer and reset output pointer
outputBuffer = new byte[Config.getMaxSize()];
cur = 0;
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
// overwrite target config when -c option was given
if(writeHeader) {
System.out.println("Writing C header file");
String fileName = ClassLoader.getClassInfo(0).getName()+".h";
// if a file name has been specified in the Config file or via the
// command line use that
if(Config.getFileName() != null)
fileName = Config.getFileName();
// write header file to disk
try {
System.out.println("Writing to file " + fileName);
File outputFile = new File(fileName);
FileOutputStream out = new FileOutputStream(outputFile);
out.write(("/* " + fileName + ", autogenerated from " +
ClassLoader.getClassInfo(0).getName() +
" class */\n").getBytes());
out.write("{\n".getBytes());
for(int i=0;i<cur;i++) {
String str = "0" + Integer.toHexString(0xff&(int)outputBuffer[i]);
out.write(("0x" + str.substring(str.length()-2)).getBytes());
if(i < cur-1) out.write(",".getBytes());
if(i%16 == 15) out.write("\n".getBytes());
else out.write(" ".getBytes());
}
out.write("\n};\n".getBytes());
out.close();
} catch(IOException e) {
System.out.println("Error writing header file: " + e.toString());
System.exit(-1);
}
} else {
// do with result what config says
int target = Config.getTarget();
switch(target) {
case Config.TARGET_NONE:
System.out.println("ERROR: no target specified");
System.exit(-1);
break;
case Config.TARGET_FILE:
// write file to disk
try {
String fileName = ClassLoader.getClassInfo(0).getName()+".nvm";
// if a file name has been specified in the Config file or
// via the command line use that
if(Config.getFileName() != null)
fileName = Config.getFileName();
System.out.println("Writing to file " + fileName);
File outputFile = new File(fileName);
FileOutputStream out = new FileOutputStream(outputFile);
out.write(outputBuffer, 0, cur);
out.close();
} catch(IOException e) {
System.out.println("Error writing file: " + e.toString());
System.exit(-1);
}
break;
// case Config.TARGET_NVC1_AUTO:
case Config.TARGET_NVC1_UART:
case Config.TARGET_NVC1_ASURO:
Uploader.setUploader("nvc1");
Uploader.upload(Config.getFileName(), target,
Config.getSpeed(), outputBuffer, cur);
case Config.TARGET_NVC2_AUTO:
Uploader.setUploader("nvc2");
Uploader.upload(Config.getFileName(), target,
Config.getSpeed(), outputBuffer, cur);
break;
case Config.TARGET_CTBOT_AUTO:
Uploader.setUploader("ctbot");
Uploader.upload(Config.getFileName(), target,
Config.getSpeed(), outputBuffer, cur);
break;
}
}
} catch(ConvertException e) {
System.out.println("Conversion failed: " + e.toString());
System.exit(-1);
}
}
}