Java Primer
Time to create an overview of how you can make managed languages work on bare hardware
Steps:
- define native ABI for Java
- create bytecode-to-native compiler (uses objectweb asm)
- compile compiler
- compile managed os to bytecode (uses regular javac)
- compile bytecode to native assembly
- create runtime for things that have to be non-native
- assemble os and runtime (uses yasm)
- package the final kernel binary. (uses binutils)
package nl.combuster.minijava;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.*;
import java.util.*;
import java.io.*;
public class Compiler
{
public static byte[] readEntireFile(String filename)
{
File file = new File(filename);
try
{
FileInputStream input = new FileInputStream(file);
byte bytes[] = new byte[(int)file.length()];
input.read(bytes);
return bytes;
}
catch (IOException e)
{
throw new RuntimeException("Unable to read file " + file, e);
}
}
public static void writeOutput(String filename, List<String> lines)
{
try
{
BufferedWriter writer = new BufferedWriter(new FileWriter(new File(filename)));
for (String string : lines)
{
writer.write(string);
writer.newLine();
}
writer.close();
}
catch (IOException e)
{
throw new RuntimeException("Unable to write output file " + filename, e);
}
}
public static String decorate(String classname, String method, String signature)
{
// make all these assembly-friendly names. Note that the
// constructor is for instance called <init>
return classname.replace("/","_") + "__" + method.replace("<","_").replace(">","_");
}
public static void main(String args[])
{
if (args.length != 2) throw new RuntimeException("Usage: compiler input-file output-file");
ClassNode node = new ClassNode();
ClassReader reader = new ClassReader(readEntireFile(args[0]));
reader.accept(node, 0);
List<String> outputdata = new LinkedList<String>();
outputdata.add("section .text");
for (MethodNode method : node.methods)
{
method.visitCode();
String methodname = decorate(node.name, method.name, method.signature);
if ((method.access & Opcodes.ACC_NATIVE) != 0) continue;
// prologue
System.out.println("; attributes: " + method.attrs);
outputdata.add("global " + methodname);
outputdata.add(methodname + ":");
outputdata.add("push ebp");
outputdata.add("mov ebp, esp");
int locals = (method.localVariables == null) ? 0 : method.localVariables.size();
outputdata.add("sub esp, " + locals * 4);
Iterator<AbstractInsnNode> iterator = method.instructions.iterator();
while (iterator.hasNext())
{
AbstractInsnNode insn = iterator.next();
if (insn instanceof LabelNode)
{
LabelNode labelinsn = (LabelNode)insn;
outputdata.add(".l" + labelinsn.getLabel());
}
else if (insn instanceof LineNumberNode)
{
// ignore these
}
else
{
outputdata.add("Can't deal with " + insn.getClass().getSimpleName() + ", fix it (" + insn.getOpcode() + ")");
}
}
// epilogue
outputdata.add("leave");
outputdata.add("ret");
}
writeOutput(args[1], outputdata);
}
}