package bytecode;
import installer.ProgressDialog;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
public class Text2Bytecode {
public static void main(String[] args) throws Exception {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
try (ZipOutputStream out = new ZipOutputStream(System.out)) {
new Text2Bytecode(in, out, null).run();
}
}
private BufferedReader in;
private ZipOutputStream out;
private String line;
private ProgressDialog dlg;
public Text2Bytecode(BufferedReader in, ZipOutputStream out, ProgressDialog dlg) {
this.in = in;
this.out = out;
this.dlg = dlg;
}
private String getline() throws Exception {
line = in.readLine();
if(line != null)
line = line.replace("\r", "");
return line;
}
private ArrayList<Object> objstack = new ArrayList<>();
private Map<String, Label> labels = new HashMap<>();
public void run() throws Exception {
boolean firstFile = true;
while(getline() != null) {
if(line.startsWith("FILE ")) {
if(dlg != null) dlg.incrementProgress(1);
if(firstFile) firstFile = false;
else {
if(objstack.size() != 1)
throw new AssertionError("leftover objects: "+objstack.toString());
out.write(((ClassWriter)objstack.remove(0)).toByteArray());
out.closeEntry();
}
out.putNextEntry(new ZipEntry(line.substring(5)));
//System.err.println(line);
} else if(line.startsWith("CALL ")) {
String[] parts = line.split(" ");
Object object;
if(objstack.size() == 0 && parts[1].equals("PUSH")) {
object = new ClassWriter(ClassWriter.COMPUTE_MAXS);
objstack.add(object);
} else if(parts[1].equals("TOP"))
object = objstack.get(objstack.size()-1);
else if(parts[1].equals("POP")) {
object = objstack.remove(objstack.size()-1);
//System.err.println("pop "+object);
object = objstack.get(objstack.size()-1);
} else
throw new AssertionError("can't get object: "+line);
int nargs = Integer.parseInt(parts[4]);
Object[] args = new Object[nargs];
for(int k = 0; k < nargs; k++)
args[k] = readArg();
String methodName = parts[2];
Method method = getMethod(object.getClass(), methodName);
Object result = method.invoke(object, args);
if(result instanceof MethodVisitor)
labels.clear();
//System.err.println("call "+object+" "+methodName);
if(parts[3].equals("PUSH")) {
objstack.add(result);
//System.err.println("push "+result);
} else if(!parts[3].equals("X"))
throw new AssertionError("invalid return type: "+parts[3]);
} else
throw new Exception("Unexpected call start: " + line);
}
if(!firstFile) {
if(objstack.size() != 1)
throw new AssertionError("leftover objects: "+objstack.toString());
out.write(((ClassWriter)objstack.remove(0)).toByteArray());
out.closeEntry();
}
}
private Method getMethod(Class<?> class1, String methodName) throws Exception {
Method rv = null;
for(Method m : class1.getMethods()) {
if(m.getName().equals(methodName)) {
if(methodName.equals("visitMethodInsn")) {
Class<?>[] argtypes = m.getParameterTypes();
if(argtypes[argtypes.length - 1] != boolean.class)
continue;
}
if(rv != null) throw new RuntimeException("multiple matches for "+methodName);
m.setAccessible(true);
rv = m;
}
}
if(rv != null)
return rv;
//if(class1.getSuperclass() != null)
// return getMethod(class1.getSuperclass(), methodName);
throw new AssertionError("no such method: "+class1.getName()+"."+methodName);
}
private Object readArg() throws Exception {
getline();
if(line.startsWith("I"))
return Integer.parseInt(line.substring(1));
if(line.startsWith("S"))
return URLDecoder.decode(line.substring(line.indexOf(' ')+1), "UTF-8");
if(line.equals("_null"))
return null;
if(line.equals("_true"))
return Boolean.TRUE;
if(line.equals("_false"))
return Boolean.FALSE;
if(line.startsWith("LNEW")) {
Label l = new Label();
labels.put(line.substring(4), l);
return l;
}
if(line.startsWith("L")) {
Label l = labels.get(line.substring(1));
if(l == null) throw new AssertionError("label not defined "+line);
return l;
}
if(line.startsWith("F"))
return Float.intBitsToFloat(Integer.parseInt(line.substring(1)));
if(line.startsWith("T"))
return Type.getType(line.substring(1));
if(line.startsWith("D"))
return Double.longBitsToDouble(Long.parseLong(line.substring(1)));
if(line.startsWith("J"))
return Long.parseLong(line.substring(1));
if(line.startsWith("A")) {
int size = Integer.parseInt(line.substring(1, line.indexOf(' ')));
String type = line.substring(line.indexOf(' ')+1);
if(type.startsWith("[L")) {
Object[] array = (Object[])Array.newInstance(Class.forName(type.substring(2, type.length()-1)), size);
for(int k = 0; k < size; k++)
array[k] = readArg();
return array;
} else if(type.equals("[I")) {
int[] array = new int[size];
for(int k = 0; k < size; k++)
array[k] = (Integer)readArg();
return array;
} else
throw new AssertionError("can't make array of type "+type);
}
throw new Exception("Unexpected value start: " + line);
}
}