/*******************************************************************************
* 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)
* * Atze van der Ploeg - Atze.van.der.Ploeg@cwi.nl (CWI)
* * Arnold Lankamp - Arnold.Lankamp@cwi.nl
*******************************************************************************/
package org.rascalmpl.library.lang.jvm.transform;
import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.InsnList;
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.IEvaluatorContext;
import org.rascalmpl.interpreter.utils.RuntimeExceptionFactory;
import org.rascalmpl.uri.URIResolverRegistry;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IInteger;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.IReal;
import org.rascalmpl.value.ISourceLocation;
import org.rascalmpl.value.IString;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.values.ValueFactoryFactory;
public class SerializeClass {
public SerializeClass(IValueFactory values) {
super();
}
public void serialize(IConstructor c, ISourceLocation path,IEvaluatorContext ctx){
try {
OutputStream output = URIResolverRegistry.getInstance().getOutputStream(path, false);
new SerializeClassImplementation().serialize(c, output);
} catch (FileNotFoundException e) {
throw RuntimeExceptionFactory.pathNotFound(path, null, null);
} catch (IOException e) {
throw RuntimeExceptionFactory.io(ValueFactoryFactory.getValueFactory().string(e.getMessage()), null, null);
} catch(Exception e){
String msg = e.getMessage();
ctx.getStdErr().print(msg);
throw RuntimeExceptionFactory.io(ValueFactoryFactory.getValueFactory().string(msg != null ? msg : e.getClass().getSimpleName()), null, null);
}
}
private class SerializeClassImplementation {
private HashMap<Integer, LabelNode> _labels;
SerializeClassImplementation(){
_labels = new HashMap<Integer, LabelNode>();
}
void serialize(IConstructor c,OutputStream output) throws IOException {
ClassNode cn = buildClass(c);
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
cn.accept(cw);
output.write(cw.toByteArray());
output.close();
}
ClassNode buildClass(IConstructor c) {
ClassNode classNode = new ClassNode();
classNode.version = ((IInteger)c.get(0)).intValue();
classNode.access = ((IInteger)c.get(1)).intValue();
classNode.name = ((IString)c.get(2)).getValue();
classNode.signature = emptyIsNull(((IString)c.get(3)).getValue());
classNode.superName = ((IString)c.get(4)).getValue();
ArrayList<String> interfaces = new ArrayList<String>();
for (IValue v : ((IList)c.get(5))) {
interfaces.add(((IString)v).getValue());
}
classNode.interfaces = interfaces;
classNode.sourceFile = emptyIsNull(((IString)c.get(6)).getValue());
classNode.sourceDebug = emptyIsNull(((IString)c.get(7)).getValue());
classNode.outerClass = emptyIsNull(((IString)c.get(8)).getValue());
classNode.outerMethod = emptyIsNull(((IString)c.get(9)).getValue());
classNode.outerMethodDesc = emptyIsNull(((IString)c.get(10)).getValue());
classNode.innerClasses = buildInnerClasses(((IList)c.get(11)));
classNode.fields = buildFields(((IList)c.get(12)));
classNode.methods = buildMethods(((IList)c.get(13)));
return classNode;
}
List<InnerClassNode> buildInnerClasses(IList iList) {
ArrayList<InnerClassNode> al = new ArrayList<InnerClassNode>();
for (IValue v : iList) {
al.add(new InnerClassNode(((IString)((IConstructor)v).get(0)).getValue(),
emptyIsNull(((IString)((IConstructor)v).get(1)).getValue()),
emptyIsNull(((IString)((IConstructor)v).get(2)).getValue()),
((IInteger)((IConstructor)v).get(3)).intValue()));
}
return al;
}
List<FieldNode> buildFields(IList iList) {
ArrayList<FieldNode> al = new ArrayList<FieldNode>();
for (IValue v : iList) {
String desc = ((IString)((IConstructor)v).get(2)).getValue();
Object initialValue = null;
if (((IConstructor)v).arity() > 4) {
if (desc.equals("I")) {
initialValue = new Integer(((IInteger)((IConstructor)v).get(4)).intValue());
} else if (desc.equals("J")) {
initialValue = new Long(((IInteger)((IConstructor)v).get(4)).longValue());
} else if (desc.equals("F")) {
initialValue = new Float(((IReal)((IConstructor)v).get(4)).floatValue());
} else if (desc.equals("D")) {
initialValue = new Double(((IReal)((IConstructor)v).get(4)).doubleValue());
} else if (desc.equals("Ljava/lang/String;")) {
initialValue = ((IString)((IConstructor)v).get(4)).getValue();
}
}
al.add(new FieldNode(((IInteger)((IConstructor)v).get(0)).intValue(),
((IString)((IConstructor)v).get(1)).getValue(),
((IString)((IConstructor)v).get(2)).getValue(),
emptyIsNull(((IString)((IConstructor)v).get(3)).getValue()),
initialValue));
}
return al;
}
List<MethodNode> buildMethods(IList iList) {
ArrayList<MethodNode> ml = new ArrayList<MethodNode>();
for (IValue v : iList) {
IList eil = ((IList)((IConstructor)v).get(4));
String ea[] = new String[eil.length()];
for (int i = 0; i < eil.length(); i++) {
ea[i] = ((IString)eil.get(i)).getValue();
}
MethodNode mn = new MethodNode(((IInteger)((IConstructor)v).get(0)).intValue(),
((IString)((IConstructor)v).get(1)).getValue(),
((IString)((IConstructor)v).get(2)).getValue(),
emptyIsNull(((IString)((IConstructor)v).get(3)).getValue()),
ea);
mn.instructions = buildInstructions((IList)((IConstructor)v).get(5));
mn.tryCatchBlocks = buildTryCatchBlocks((IList)((IConstructor)v).get(6));
mn.localVariables = buildLocalVariables((IList)((IConstructor)v).get(7));
ml.add(mn);
}
return ml;
}
InsnList buildInstructions(IList iList) {
InsnList il = new InsnList();
for (IValue v : iList) {
if (((IConstructor)v).getName().equals("fieldRef")) {
il.add(new FieldInsnNode(((IInteger)((IConstructor)v).get(0)).intValue(),
((IString)((IConstructor)v).get(1)).getValue(),
((IString)((IConstructor)v).get(2)).getValue(),
((IString)((IConstructor)v).get(3)).getValue()));
} else if (((IConstructor)v).getName().equals("increment")) {
il.add(new IincInsnNode(((IInteger)((IConstructor)v).get(0)).intValue(),
((IInteger)((IConstructor)v).get(1)).intValue()));
} else if (((IConstructor)v).getName().equals("instruction")) {
il.add(new InsnNode(((IInteger)((IConstructor)v).get(0)).intValue()));
} else if (((IConstructor)v).getName().equals("integer")) {
il.add(new IntInsnNode(((IInteger)((IConstructor)v).get(0)).intValue(),
((IInteger)((IConstructor)v).get(1)).intValue()));
} else if (((IConstructor)v).getName().equals("jump")) {
il.add(new JumpInsnNode(((IInteger)((IConstructor)v).get(0)).intValue(),
getLabel(((IInteger)((IConstructor)v).get(1)).intValue())));
} else if (((IConstructor)v).getName().equals("label")) {
il.add(getLabel(((IInteger)((IConstructor)v).get(0)).intValue()));
} else if (((IConstructor)v).getName().equals("lineNumber")) {
il.add(new LineNumberNode(((IInteger)((IConstructor)v).get(0)).intValue(),
getLabel(((IInteger)((IConstructor)v).get(1)).intValue())));
} else if (((IConstructor)v).getName().equals("localVariable")) {
il.add(new VarInsnNode(((IInteger)((IConstructor)v).get(0)).intValue(),
((IInteger)((IConstructor)v).get(1)).intValue()));
} else if (((IConstructor)v).getName().equals("loadConstantString")) {
il.add(new LdcInsnNode(((IString)((IConstructor)v).get(0)).getValue()));
} else if (((IConstructor)v).getName().equals("loadConstantInteger")) {
il.add(new LdcInsnNode(((IInteger)((IConstructor)v).get(0)).intValue()));
} else if (((IConstructor)v).getName().equals("loadConstantLong")) {
il.add(new LdcInsnNode(((IInteger)((IConstructor)v).get(0)).longValue()));
} else if (((IConstructor)v).getName().equals("loadConstantFloat")) {
il.add(new LdcInsnNode(((IReal)((IConstructor)v).get(0)).floatValue()));
} else if (((IConstructor)v).getName().equals("loadConstantDouble")) {
il.add(new LdcInsnNode(((IReal)((IConstructor)v).get(0)).doubleValue()));
} else if (((IConstructor)v).getName().equals("lookupSwitch")) {
IList kl = (IList)((IConstructor)v).get(1);
int ka[] = new int[kl.length()];
for (int i = 0; i < kl.length(); i++) {
ka[i] = ((IInteger)kl.get(i)).intValue();
}
IList ll = (IList)((IConstructor)v).get(2);
LabelNode la[] = new LabelNode[ll.length()];
for (int i = 0; i < ll.length(); i++) {
la[i] = getLabel(((IInteger)ll.get(i)).intValue());
}
il.add(new LookupSwitchInsnNode(getLabel(((IInteger)((IConstructor)v).get(0)).intValue()),
ka,
la));
} else if (((IConstructor)v).getName().equals("method")) {
il.add(new MethodInsnNode(((IInteger)((IConstructor)v).get(0)).intValue(),
((IString)((IConstructor)v).get(1)).getValue(),
((IString)((IConstructor)v).get(2)).getValue(),
((IString)((IConstructor)v).get(3)).getValue()));
} else if (((IConstructor)v).getName().equals("multiANewArray")) {
il.add(new MultiANewArrayInsnNode(((IString)((IConstructor)v).get(0)).getValue(),
((IInteger)((IConstructor)v).get(1)).intValue()));
} else if (((IConstructor)v).getName().equals("tableSwitch")) {
IList ll = (IList)((IConstructor)v).get(3);
LabelNode la[] = new LabelNode[ll.length()];
for (int i = 0; i < ll.length(); i++) {
la[i] = getLabel(((IInteger)ll.get(i)).intValue());
}
il.add(new TableSwitchInsnNode(((IInteger)((IConstructor)v).get(0)).intValue(),
((IInteger)((IConstructor)v).get(1)).intValue(),
getLabel(((IInteger)((IConstructor)v).get(2)).intValue()),
la));
} else if (((IConstructor)v).getName().equals("type")) {
il.add(new TypeInsnNode(((IInteger)((IConstructor)v).get(0)).intValue(),
((IString)((IConstructor)v).get(1)).getValue()));
}
}
return il;
}
List<TryCatchBlockNode> buildTryCatchBlocks(IList iList) {
ArrayList<TryCatchBlockNode> al = new ArrayList<TryCatchBlockNode>();
for (IValue v : iList) {
String type = null;
if (iList.length() > 3) {
type = ((IString)((IConstructor)v).get(3)).getValue();
}
al.add(new TryCatchBlockNode(getLabel(((IInteger)((IConstructor)v).get(0)).intValue()),
getLabel(((IInteger)((IConstructor)v).get(1)).intValue()),
getLabel(((IInteger)((IConstructor)v).get(2)).intValue()),
type));
}
return al;
}
LabelNode getLabel(int intValue) {
if (!_labels.containsKey(intValue)) {
_labels.put(intValue, new LabelNode());
}
return _labels.get(intValue);
}
List<LocalVariableNode> buildLocalVariables(IList iList) {
ArrayList<LocalVariableNode> al = new ArrayList<LocalVariableNode>();
for (IValue v : iList) {
al.add(new LocalVariableNode(((IString)((IConstructor)v).get(0)).getValue(),
((IString)((IConstructor)v).get(1)).getValue(),
emptyIsNull(((IString)((IConstructor)v).get(2)).getValue()),
getLabel(((IInteger)((IConstructor)v).get(3)).intValue()),
getLabel(((IInteger)((IConstructor)v).get(4)).intValue()),
((IInteger)((IConstructor)v).get(5)).intValue()));
}
return al;
}
String emptyIsNull(String s) {
if (s.equals("")) {
return null;
}
return s;
}
}
}