package com.coderising.jvm.cmd;
import java.util.ArrayList;
import java.util.List;
import com.coderising.jvm.constant.ConstantPool;
public class CommandParser {
public static final String aconst_null = "01";
public static final String new_object = "BB";
public static final String lstore = "37";
public static final String invokespecial = "B7";
public static final String invokevirtual = "B6";
public static final String getfield = "B4";
public static final String putfield = "B5";
public static final String getstatic = "B2";
public static final String ldc = "12";
public static final String dup = "59";
public static final String bipush = "10";
public static final String aload_0 = "2A";
public static final String aload_1 = "2B";
public static final String aload_2 = "2C";
public static final String iload = "15";
public static final String iload_1 = "1B";
public static final String iload_2 = "1C";
public static final String iload_3 = "1D";
public static final String fload_3 = "25";
public static final String voidreturn = "B1";
public static final String ireturn = "AC";
public static final String freturn = "AE";
public static final String astore_1 = "4C";
public static final String if_icmp_ge = "A2";
public static final String if_icmple = "A4";
public static final String goto_no_condition = "A7";
public static final String iconst_0 = "03";
public static final String iconst_1 = "04";
public static final String istore_1 = "3C";
public static final String istore_2 = "3D";
public static final String iadd = "60";
public static final String iinc = "84";
public static ByteCodeCommand[] parse(ConstantPool constantPool, String code) {
CommandIterator it = new CommandIterator(code);
List<ByteCodeCommand> cmds = new ArrayList<>();
while(it.hasNext()) {
String opCode = it.next2CharAsString().toUpperCase();
if(aload_0.equalsIgnoreCase(opCode) || aload_1.equalsIgnoreCase(opCode) || aload_2.equalsIgnoreCase(opCode)
|| voidreturn.equalsIgnoreCase(opCode) || dup.equalsIgnoreCase(opCode) || astore_1.equalsIgnoreCase(opCode)
|| iload_1.equalsIgnoreCase(opCode) || iload_2.equalsIgnoreCase(opCode) || iload_3.equalsIgnoreCase(opCode)){
NoOperandCmd cmd = new NoOperandCmd();
cmd.setOpCode(opCode);
cmds.add(cmd);
} else if(invokespecial.equalsIgnoreCase(opCode)) {
addTwoOperandCmd(it, cmds, opCode, new InvokeSpecialCmd());
} else if(putfield.equalsIgnoreCase(opCode)) {
addTwoOperandCmd(it, cmds, opCode, new PutFieldCmd());
} else if(getstatic.equalsIgnoreCase(opCode)) {
addTwoOperandCmd(it, cmds, opCode, new GetStaticCmd());
} else if(invokevirtual.equalsIgnoreCase(opCode)) {
addTwoOperandCmd(it, cmds, opCode, new InvokeVirtualCmd());
} else if(new_object.equalsIgnoreCase(opCode)) {
addTwoOperandCmd(it, cmds, opCode, new NewObjectCmd());
} else if(ldc.equalsIgnoreCase(opCode)) {
addOneOperandCmd(it, cmds, opCode, new LdcCmd());
} else if(bipush.equalsIgnoreCase(opCode)) {
addOneOperandCmd(it, cmds, opCode, new BiPushCmd());
} else {
throw new RuntimeException("no implements opCode : " + opCode);
}
}
calcuateOffset(cmds);
return cmds.toArray(new ByteCodeCommand[cmds.size()]);
}
private static void addOneOperandCmd(CommandIterator it, List<ByteCodeCommand> cmds, String opCode, OneOperandCmd cmd) {
cmd.setOpCode(opCode);
cmd.setOperand(it.next2CharAsInt());
cmds.add(cmd);
}
private static void addTwoOperandCmd(CommandIterator it, List<ByteCodeCommand> cmds, String opCode,
TwoOperandCmd cmd) {
cmd.setIndex(it.next4CharAsInt());
cmd.setOpCode(opCode);
cmds.add(cmd);
}
private static void calcuateOffset(List<ByteCodeCommand> cmds) {
int offset = 0;
for (ByteCodeCommand cmd : cmds) {
cmd.setOffset(offset);
offset += cmd.getLength();
}
}
private static class CommandIterator {
private String code;
private int index = 0;
public CommandIterator(String code) {
super();
this.code = code;
}
public boolean hasNext() {
return index < code.length();
}
public String next2CharAsString() {
String result = code.substring(index, index + 2);
index += 2;
return result;
}
public int next2CharAsInt() {
String str = this.next2CharAsString();
return hexStrToInt(str);
}
public int next4CharAsInt() {
String s1 = this.next2CharAsString();
String s2 = this.next2CharAsString();
return ((hexStrToInt(s1) << 8) | hexStrToInt(s2));
}
private int hexStrToInt(String s) {
return Integer.valueOf(s, 16).intValue();
}
}
}