/*
* XMLVM --- An XML-based Programming Language Copyright (c) 2004-2005 by Arno
* Puder
*
* 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., 675 Mass
* Ave, Cambridge, MA 02139, USA.
*
* For more information, visit the XMLVM Home Page at
* http://www.xml11.org/xmlvm/
*/
/*
* Created on Aug 2, 2007 by Ilias
*/
package org.xmlvm;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import edu.arizona.cs.mbel.mbel.AssemblyInfo;
import edu.arizona.cs.mbel.mbel.AssemblyRefInfo;
import edu.arizona.cs.mbel.mbel.AssemblyTypeRef;
import edu.arizona.cs.mbel.mbel.FieldRef;
import edu.arizona.cs.mbel.mbel.Method;
import edu.arizona.cs.mbel.mbel.Module;
import edu.arizona.cs.mbel.mbel.TypeDef;
import edu.arizona.cs.mbel.mbel.TypeRef;
import edu.arizona.cs.mbel.instructions.Instruction;
import edu.arizona.cs.mbel.instructions.RET;
import edu.arizona.cs.mbel.parse.*;
import edu.arizona.cs.mbel.signature.*;
import edu.arizona.cs.mbel.instructions.*;
import edu.arizona.cs.mbel.mbel.*;
import edu.arizona.cs.mbel.emit.*;
public class GenCIL
{
private InstructionList il;
private InstructionHandlerManagerCIL instructionHandlerManager;
private Document xmlvm;
private Namespace nsXMLVM;
private Module module;
private AssemblyInfo assembly;
private AssemblyRefInfo compatJavaLibAssembly;
private TypeDef currentTypeDef;
private MethodBody body;
public GenCIL(Document xmlvm)
{
this.xmlvm = xmlvm;
nsXMLVM = Namespace.getNamespace("vm", "http://xmlvm.org");
}
public void create(OutputStream out, String assemblyName)
throws IllegalXMLVMException, IOException
{
compatJavaLibAssembly = new AssemblyRefInfo(0, 0, 0, 0, 0, null,
"CompatJavaLib", null, null);
module = new Module(assemblyName + ".exe", new byte[16],
PE_Header.PE_SUBSYSTEM_WINDOWS_CUI);
assembly = new AssemblyInfo(AssemblyInfo.SHA1, 1, 0, 1, 1, 0, null,
assemblyName, "");
module.setAssemblyInfo(assembly);
Element root = xmlvm.getRootElement();
if (!root.getName().equals("xmlvm"))
throw new IllegalXMLVMException("Root element needs to be <xmlvm>");
List<Element> clazzes = root.getChildren("class", nsXMLVM);
for (Element clazz : clazzes) {
String ns = clazz.getAttributeValue("package");
String name = clazz.getAttributeValue("name");
currentTypeDef = new TypeDef(ns, name, TypeDef.NotPublic);
TypeRef baseClass = getTypeRef(clazz.getAttributeValue("extends"));
currentTypeDef.setSuperClass(baseClass);
module.addTypeDef(currentTypeDef);
for (Object o : clazz.getChildren()) {
Element decl = (Element) o;
String tag = decl.getName();
if (tag.equals("method"))
createMethod(decl);
else if (tag.equals("field"))
createField(decl);
else
throw new IllegalXMLVMException(
"Unknown class declaration '"
+ tag + "'");
}
}
Emitter emitter = new Emitter(module);
emitter.emitModule(out);
}
private void createMethod(Element method)
throws IllegalXMLVMException
{
try {
String methodName = method.getAttributeValue("name");
Element signature = method.getChild("signature", nsXMLVM);
ReturnTypeSignature retType = collectReturnTypeSignature(signature);
ParameterSignature[] argTypes = collectParameterSignature(signature);
if (argTypes != null) {
for (int i = 0; i < argTypes.length; i++)
argTypes[i].setParameterInfo(new ParameterInfo("n" + i, i));
}
int accessFlags = getMethodAccessFlags(method);
if (methodName.equals("<init>")) {
methodName = ".ctor";
accessFlags |= Method.RTSpecialName;
accessFlags |= Method.SpecialName;
}
accessFlags |= Method.HideBySig;
boolean hasThis = (accessFlags & Method.Static) == 0;
MethodSignature methodSignature = new MethodSignature(
hasThis,
false,
CallingConvention.DEFAULT,
retType,
argTypes);
Method m = new Method(methodName, 0, accessFlags, methodSignature);
currentTypeDef.addMethod(m);
if (m.getName().equals("Main")) {
module.setEntryPoint(new EntryPoint(m));
}
body = new MethodBody(true, Integer.parseInt(method
.getAttributeValue("stack")), null);
m.setMethodBody(body);
il = body.getInstructionList();
instructionHandlerManager = new InstructionHandlerManagerCIL(il);
Element code = method.getChild("code", nsXMLVM);
createCode(code);
}
catch (SignatureException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void createField(Element field)
throws IllegalXMLVMException
{
try {
String name = field.getAttributeValue("name");
TypeSignature t = parseTypeSignature(field
.getAttributeValue("type"));
int flags = getFieldAccessFlags(field);
FieldSignature fs = new FieldSignature(t);
Field f = new Field(name, flags, fs);
currentTypeDef.addField(f);
}
catch (SignatureException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private TypeRef getTypeRef(String typeRef)
{
// Decompose typeRef into namespace and name
int i = typeRef.indexOf('.');
if (i == -1) {
System.err.println("No '.'");
System.exit(-1);
}
i = typeRef.lastIndexOf('.');
String ns = typeRef.substring(0, i);
String name = typeRef.substring(i + 1);
// Check if this is a refence into our own module
TypeRef ref = module.getTypeDefByName(ns, name);
if (ref != null)
return ref;
// Check for Java compatibility assembly
if (ns.startsWith("java."))
return new AssemblyTypeRef(compatJavaLibAssembly, ns, name);
System.err.println("Unknown assembly " + typeRef);
System.exit(-1);
return null;
}
private ReturnTypeSignature collectReturnTypeSignature(Element signature)
throws IllegalXMLVMException, SignatureException
{
Element ret = signature.getChild("return", nsXMLVM);
String t = ret.getAttributeValue("type");
if (t.equals("void"))
return new ReturnTypeSignature(
ReturnTypeSignature.ELEMENT_TYPE_VOID);
else
return new ReturnTypeSignature(parseTypeSignature(t), false);
}
private ParameterSignature[] collectParameterSignature(Element signature)
throws IllegalXMLVMException, SignatureException
{
List<Element> params = signature.getChildren("parameter", nsXMLVM);
if (params.isEmpty())
return null;
List<ParameterSignature> argTypes = new ArrayList<ParameterSignature>();
for (Element p : params) {
String t = p.getAttributeValue("type");
TypeSignature type = parseTypeSignature(t);
argTypes.add(new ParameterSignature(type, false));
}
return argTypes.toArray(new ParameterSignature[0]);
}
private TypeSignature parseTypeSignature(String type)
throws IllegalXMLVMException, SignatureException
{
int arrayDimension = 0;
while (type.endsWith("[]")) {
arrayDimension++;
type = type.substring(0, type.length() - 2);
}
TypeSignature baseType = null;
if (type.equals("System.String"))
baseType = TypeSignature.STRING;
else if (type.equals("string"))
baseType = TypeSignature.STRING;
else if (type.equals("boolean"))
baseType = TypeSignature.BOOLEAN;
else if (type.equals("int"))
baseType = TypeSignature.I;
else if (type.equals("float"))
baseType = TypeSignature.R4;
else if (type.equals("double"))
baseType = TypeSignature.R8;
else if (type.equals("System.Object")) // java.lang.Object removed
baseType = TypeSignature.OBJECT;
else
// Assume a TypeRef
baseType = new ClassTypeSignature(getTypeRef(type));
// if (baseType == null)
// baseType = new ObjectType(type);
if (arrayDimension == 0)
return baseType;
else if (arrayDimension == 1)
return new SZArrayTypeSignature(baseType);
else
return new ArrayTypeSignature(
baseType,
new ArrayShapeSignature(
arrayDimension,
null, null));
}
private void createCode(Element code)
throws IllegalXMLVMException
{
List<Element> instructions = code.getChildren();
for (Element inst : instructions) {
String name = inst.getName().toUpperCase();
String opcMethodName = "createInstruction"
+ name.substring(0, 1).toUpperCase()
+ name.substring(1).toLowerCase();
// + name;
Class appClazz;
java.lang.reflect.Method opcMeth;
Class[] paramTypes = {Element.class};
Object[] params = {inst};
appClazz = this.getClass();
Object newInstr = null;
try {
opcMeth = appClazz.getMethod(opcMethodName, paramTypes);
newInstr = opcMeth.invoke(this, params);
}
catch (SecurityException e) {
throw new IllegalXMLVMException("Illegal instruction '" + name
+ "'");
}
catch (NoSuchMethodException e) {
throw new IllegalXMLVMException("Illegal instruction '" + name
+ "'");
}
catch (IllegalArgumentException e) {
throw new IllegalXMLVMException("Illegal instruction '" + name
+ "'");
}
catch (IllegalAccessException e) {
throw new IllegalXMLVMException("Illegal instruction '" + name
+ "'");
}
catch (InvocationTargetException e) {
throw new IllegalXMLVMException("Illegal instruction '" + name
+ "'");
}
if (newInstr != null) {
InstructionHandle ih = null;
if (newInstr instanceof BranchInstruction)
ih = il.append((BranchInstruction) newInstr);
// else if (newInstr instanceof CompoundInstruction)
// ih = il.append((CompoundInstruction) newInstr);
else if (newInstr instanceof Instruction)
ih = il.append((Instruction) newInstr);
instructionHandlerManager.registerInstructionHandle(ih);
}
}
}
private int getMethodAccessFlags(Element elem)
{
short af = 0;
af |= checkAccessFlag(elem, "isPublic", Method.Public);
af |= checkAccessFlag(elem, "isPrivate", Method.Private);
// af |= checkAccessFlag(elem, "isProtected", Constants.ACC_PROTECTED);
af |= checkAccessFlag(elem, "isSynchronized", Method.Synchronized);
af |= checkAccessFlag(elem, "isStatic", Method.Static);
return af;
}
private int getFieldAccessFlags(Element elem)
{
short af = 0;
af |= checkAccessFlag(elem, "isPublic", Field.Public);
af |= checkAccessFlag(elem, "isPrivate", Field.Private);
// af |= checkAccessFlag(elem, "isProtected", Constants.ACC_PROTECTED);
af |= checkAccessFlag(elem, "isStatic", Field.Static);
return af;
}
private int checkAccessFlag(Element elem, String flag, int jvmFlag)
{
String val = elem.getAttributeValue(flag);
if (val == null)
return 0;
return val.equals("true") ? jvmFlag : 0;
}
public Instruction createInstructionLdarg(Element inst)
{
Instruction newInstr = null;
if (inst.getAttributeValue("index").equals("0")) {
newInstr = new LDARG(LDARG.LDARG_0);
}
else if (inst.getAttributeValue("index").equals("1")) {
newInstr = new LDARG(LDARG.LDARG_1);
}
else if (inst.getAttributeValue("index").equals("2")) {
newInstr = new LDARG(LDARG.LDARG_2);
}
else if (inst.getAttributeValue("index").equals("3")) {
newInstr = new LDARG(LDARG.LDARG_3);
}
else {
// Throw exception or do default
}
return newInstr;
}
public Instruction createInstructionLdc(Element inst)
{
Instruction newInstr = null;
if (inst.getAttributeValue("type").equals("System.String")) {
newInstr = new LDSTR(inst.getAttributeValue("value"));
}
else if (inst.getAttributeValue("type").equals("float")) {
newInstr = new LDC(Integer
.parseInt(inst.getAttributeValue("value").substring(0,
inst.getAttributeValue("value").length() - 2)));
}
return newInstr;
}
public Instruction createInstructionLdsfld(Element inst)
throws IllegalXMLVMException, SignatureException
{
String name = inst.getAttributeValue("field");
TypeRef fieldRef = getTypeRef(inst.getAttributeValue("type"));
TypeRef classRef = getTypeRef(inst.getAttributeValue("class-type"));
FieldSignature fieldSig = new FieldSignature(
new ClassTypeSignature(
fieldRef));
FieldRef field = new FieldRef(name, fieldSig, classRef);
return new LDSFLD(field);
}
public Instruction createInstructionLdstr(Element inst)
{
String str = inst.getAttributeValue("value");
return new LDSTR(str);
}
public Instruction createInstructionLdnull(Element inst)
{
return new LDNULL();
}
private MethodRef collectMethodReference(Element inst)
throws IllegalXMLVMException, SignatureException
{
String methodName = inst.getAttributeValue("method");
if (methodName.equals("<init>"))
methodName = ".ctor";
TypeRef classType = getTypeRef(inst.getAttributeValue("class-type"));
Element signature = inst.getChild("signature", nsXMLVM);
ReturnTypeSignature retType = collectReturnTypeSignature(signature);
ParameterSignature[] argTypes = collectParameterSignature(signature);
boolean hasThis = inst.getAttributeValue("has-this").equals("true");
MethodSignature methodSignature = new MethodSignature(
hasThis,
false,
CallingConvention.DEFAULT,
retType, argTypes);
return new MethodRef(methodName, classType, methodSignature);
}
public Instruction createInstructionCallvirt(Element inst)
throws SignatureException, IllegalXMLVMException
{
return new CALLVIRT(collectMethodReference(inst));
}
public Instruction createInstructionCall(Element inst)
throws SignatureException, IllegalXMLVMException
{
return new CALL(collectMethodReference(inst));
}
public Instruction createInstructionReturn(Element inst)
{
Instruction newInstr = null;
newInstr = new RET();
return newInstr;
}
public Instruction createInstructionNewobj(Element inst)
throws IllegalXMLVMException, SignatureException
{
String methodName = ".ctor";
TypeRef classType = getTypeRef(inst.getAttributeValue("type"));
Element signature = inst.getChild("signature", nsXMLVM);
ReturnTypeSignature retType = collectReturnTypeSignature(signature);
ParameterSignature[] argTypes = collectParameterSignature(signature);
MethodSignature methodSignature = new MethodSignature(
true,
false,
CallingConvention.DEFAULT,
retType, argTypes);
MethodRef methodRef = new MethodRef(methodName, classType,
methodSignature);
return new NEWOBJ(methodRef);
}
}