/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* * Jeroen van den Bos - Jeroen.van.den.Bos@cwi.nl (CWI)
* * Arnold Lankamp - Arnold.Lankamp@cwi.nl
*******************************************************************************/
package org.rascalmpl.library.lang.jvm.transform;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LineNumberNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.LookupSwitchInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.MultiANewArrayInsnNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.rascalmpl.interpreter.utils.RuntimeExceptionFactory;
import org.rascalmpl.uri.URIResolverRegistry;
import org.rascalmpl.value.ISourceLocation;
import org.rascalmpl.value.IString;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.values.ValueFactoryFactory;
public class Rascalify {
private static final URIResolverRegistry _resolver;
private static final ArrayList<LabelNode> _labels;
static {
_resolver = URIResolverRegistry.getInstance();
_labels = new ArrayList<LabelNode>();
}
public Rascalify(IValueFactory values) {
super();
}
@SuppressWarnings("unchecked")
public static void deserializeToDisk(ISourceLocation source, ISourceLocation destination, IString moduleName) {
try (InputStream inputStream = _resolver.getInputStream(source)) {
ClassNode cn = new ClassNode();
new ClassReader(inputStream);
//cr.aaccept(cn, 0);
OutputStreamWriter writer = new OutputStreamWriter(_resolver.getOutputStream(destination, false));
writer.write("module " + moduleName.getValue() + "\n\n");
writer.write("import experiments::JVMBytecode::Opcodes;\n");
writer.write("import experiments::JVMBytecode::SerializeClass;\n\n");
writer.write("public Class generate" + moduleName.getValue() + "Class() {\n");
writer.write("\treturn class(" + cn.version + ", " + cn.access + ", \"" + escape(cn.name) + "\", " + checkNull(cn.signature) + ", " + checkNull(cn.superName) + ",\n\t\t");
writeStrings(writer, cn.interfaces);
writer.write(", " + checkNull(cn.sourceFile) + ", " + checkNull(cn.sourceDebug) + ", " + checkNull(cn.outerClass) + ", " + checkNull(cn.outerMethod) + ", " + checkNull(cn.outerMethodDesc) + ",\n\t\t");
writerInnerClasses(cn, writer);
writer.write(",\n\t\t");
writeFields(cn, writer);
writer.write(",\n\t\t");
writeMethods(cn, writer);
writer.write("\n\t);\n");
writer.write("}");
writer.close();
} catch(FileNotFoundException e) {
throw RuntimeExceptionFactory.pathNotFound(source, null, null);
} catch(IOException e) {
throw RuntimeExceptionFactory.io(ValueFactoryFactory.getValueFactory().string(e.getMessage()), null, null);
}
}
@SuppressWarnings("unchecked")
private static void writeFields(ClassNode cn, OutputStreamWriter writer) throws IOException {
writer.write("[");
boolean first = true;
for (FieldNode fn : (List<FieldNode>)cn.fields) {
if (first) { first = false; } else { writer.write(", "); }
writer.write("\n\t\t\tfield(" + fn.access + ", \"" + escape(fn.name) + "\", \"" + escape(fn.desc) + "\", " + checkNull(fn.signature) + getValue(fn.value) + ")");
}
writer.write("\n\t\t]");
}
private static String getValue(Object value) {
if (value == null) return "";
if (value instanceof String) return ", \"" + escape((String)value) + "\"";
return value.toString();
}
@SuppressWarnings("unchecked")
private static void writerInnerClasses(ClassNode cn, OutputStreamWriter writer) throws IOException {
writer.write("[");
boolean first = true;
for (InnerClassNode icn : (List<InnerClassNode>)cn.innerClasses) {
if (first) { first = false; } else { writer.write(", "); }
writer.write("innerClass(\"" + escape(icn.name) + "\", " + checkNull(icn.outerName) + ", " + checkNull(icn.innerName) + ", " + icn.access + ")");
}
writer.write("]");
}
@SuppressWarnings("unchecked")
private static void writeMethods(ClassNode cn, OutputStreamWriter writer)
throws IOException {
boolean first = true;
writer.write("[\n");
for (MethodNode mn : (List<MethodNode>)cn.methods) {
if (first) { first = false; } else { writer.write(",\n"); }
writer.write("\t\t\tmethod(" + mn.access + ", " + "\"" + escape(mn.name) + "\", " + "\"" + escape(mn.desc) + "\", " + checkNull(mn.signature) + ", ");
writeStrings(writer, mn.exceptions);
writer.write(",\n\t\t\t");
writeInstructions(writer, mn);
writer.write(",\n\t\t\t");
writeTryCatchBlocks(writer, mn);
writer.write(",\n\t\t\t");
writeLocalVariables(writer, mn);
writer.write(")");
}
writer.write("\n\t\t]");
}
@SuppressWarnings("unchecked")
private static void writeTryCatchBlocks(OutputStreamWriter writer, MethodNode mn) throws IOException {
writer.write("[");
boolean first = true;
for (TryCatchBlockNode tn : (List<TryCatchBlockNode>)mn.tryCatchBlocks) {
if (first) { first = false; } else { writer.write(", "); }
if (tn.type != null) {
writer.write("tryCatchBlock(" + getLabelIndex(tn.start) + ", " + getLabelIndex(tn.end) + ", " + getLabelIndex(tn.handler) + ", \"" + escape(tn.type) + "\")");
} else {
writer.write("finallyBlock(" + getLabelIndex(tn.start) + ", " + getLabelIndex(tn.end) + ", " + getLabelIndex(tn.handler) + ")");
}
}
writer.write("]");
}
@SuppressWarnings("unchecked")
private static void writeInstructions(OutputStreamWriter writer, MethodNode mn) throws IOException {
writer.write("[");
boolean first = true;
for (AbstractInsnNode ai : mn.instructions.toArray()) {
if (first) { first = false; } else { writer.write(","); }
if (ai instanceof FieldInsnNode) {
FieldInsnNode n = ((FieldInsnNode)ai);
writer.write("\n\t\t\t\tfieldRef(" + n.getOpcode() + ", \"" + escape(n.owner) + "\", \"" + escape(n.name) + "\", \"" + escape(n.desc) + "\")");
} else if (ai instanceof IincInsnNode) {
IincInsnNode n = ((IincInsnNode)ai);
writer.write("\n\t\t\t\tincrement(" + n.var + ", " + n.incr + ")");
} else if (ai instanceof InsnNode) {
InsnNode n = ((InsnNode)ai);
writer.write("\n\t\t\t\tinstruction(" + n.getOpcode() + ")");
} else if (ai instanceof IntInsnNode) {
IntInsnNode n = ((IntInsnNode)ai);
writer.write("\n\t\t\t\tinteger(" + n.getOpcode() + ", " + n.operand + ")");
} else if (ai instanceof JumpInsnNode) {
JumpInsnNode n = ((JumpInsnNode)ai);
writer.write("\n\t\t\t\tjump(" + n.getOpcode() + ", " + getLabelIndex(n.label) + ")");
} else if (ai instanceof LabelNode) {
LabelNode n = ((LabelNode)ai);
writer.write("\n\t\t\t\tlabel(" + getLabelIndex(n) + ")");
} else if (ai instanceof LineNumberNode) {
LineNumberNode n = ((LineNumberNode)ai);
writer.write("\n\t\t\t\tlineNumber(" + n.line + ", " + getLabelIndex(n.start) + ")");
} else if (ai instanceof VarInsnNode) {
VarInsnNode n = ((VarInsnNode)ai);
writer.write("\n\t\t\t\tlocalVariable(" + n.getOpcode() + ", " + n.var + ")");
} else if (ai instanceof LdcInsnNode) {
LdcInsnNode n = ((LdcInsnNode)ai);
if (n.cst instanceof String) {
writer.write("\n\t\t\t\tloadConstantString(\"" + escape((String)n.cst) + "\")");
} else if (n.cst instanceof Integer) {
writer.write("\n\t\t\t\tloadConstantInteger(" + n.cst + ")");
} else if (n.cst instanceof Long) {
writer.write("\n\t\t\t\tloadConstantLong(" + n.cst + ")");
} else if (n.cst instanceof Float) {
writer.write("\n\t\t\t\tloadConstantFloat(" + n.cst + ")");
} else if (n.cst instanceof Double) {
writer.write("\n\t\t\t\tloadConstantDouble(" + n.cst + ")");
}
} else if (ai instanceof LookupSwitchInsnNode) {
LookupSwitchInsnNode n = ((LookupSwitchInsnNode)ai);
writer.write("\n\t\t\t\tlookupSwitch(" + getLabelIndex(n.dflt) + ", [");
boolean firstKey = true;
for (Integer k : (List<Integer>)n.keys) {
if (firstKey) { firstKey = false; } else { writer.write(", "); }
writer.write(k);
}
writer.write("], [");
boolean firstCase = true;
for (LabelNode l : (List<LabelNode>)n.labels) {
if (firstCase) { firstCase = false; } else { writer.write(", "); }
writer.write("" + getLabelIndex(l));
}
writer.write("])");
} else if (ai instanceof MethodInsnNode) {
MethodInsnNode n = ((MethodInsnNode)ai);
writer.write("\n\t\t\t\tmethod(" + n.getOpcode() + ", \"" + escape(n.owner) + "\", \"" + escape(n.name) + "\", \"" + escape(n.desc) + "\")");
} else if (ai instanceof MultiANewArrayInsnNode) {
MultiANewArrayInsnNode n = ((MultiANewArrayInsnNode)ai);
writer.write("\n\t\t\t\tmultiANewArray(\"" + escape(n.desc) + "\", " + n.dims + ")");
} else if (ai instanceof TableSwitchInsnNode) {
TableSwitchInsnNode n = ((TableSwitchInsnNode)ai);
writer.write("\n\t\t\t\ttableSwitch(" + n.min + ", " + n.max + ", " + getLabelIndex(n.dflt) + ", [");
boolean firstCase = true;
for (LabelNode l : (List<LabelNode>)n.labels) {
if (firstCase) { firstCase = false; } else { writer.write(", "); }
writer.write("" + getLabelIndex(l));
}
writer.write("])");
} else if (ai instanceof TypeInsnNode) {
TypeInsnNode n = ((TypeInsnNode)ai);
writer.write("\n\t\t\t\t\\type(" + n.getOpcode() + ", \"" + escape(n.desc) + "\")");
} else {
if (!(ai instanceof FrameNode)) {
throw new RuntimeException("Error: Unsupported instruction encountered (" + ai.getClass() + ").");
}
first = true;
}
}
writer.write("\n\t\t\t]");
}
private static void writeStrings(OutputStreamWriter writer, List<String> sl)
throws IOException {
writer.write("[");
boolean first = true;
for (String ex : sl) {
if (first) { first = false; } else { writer.write(", "); }
writer.write("\"" + escape(ex) + "\"");
}
writer.write("]");
}
@SuppressWarnings("unchecked")
private static void writeLocalVariables(OutputStreamWriter writer, MethodNode mn)
throws IOException {
writer.write("[");
boolean first = true;
for (LocalVariableNode vn : (List<LocalVariableNode>)mn.localVariables) {
if (first) { first = false; } else { writer.write(","); }
writer.write("\n\t\t\t\tlocalVariable(\"" + escape(vn.name) + "\", \"" + escape(vn.desc) + "\", " + checkNull(vn.signature) + ", " + getLabelIndex(vn.start) + ", " + getLabelIndex(vn.end) + ", " + vn.index + ")");
}
writer.write("\n\t\t\t]");
}
private static String checkNull(String s) {
if (s == null) {
return "\"\"";
}
return "\"" + escape(s) + "\"";
}
private static String escape(String s) {
return s.replace("<", "\\<").replace(">", "\\>");
}
private static int getLabelIndex(LabelNode ln) {
if (!_labels.contains(ln)) {
_labels.add(ln);
}
return _labels.indexOf(ln);
}
}