package main.coding_170423.jvm.cmd;
import main.coding_170423.jvm.clz.ClassFile;
import java.util.ArrayList;
import java.util.List;
/**
* Created by peterchen on 2017/4/26.
*/
public class CommandParser {
public static ByteCodeCommand[] parse(ClassFile clzFile, String codes) {
if (codes == null || codes.length() == 0 || (codes.length() % 2) != 0) {
throw new RuntimeException("the original code is not correct");
}
codes = codes.toUpperCase();
CommandIterator commandIterator = new CommandIterator(codes);
List<ByteCodeCommand> byteCodeCommands = new ArrayList<>();
while (commandIterator.hasNext()) {
String opCode = commandIterator.next2CharAsString();
switch (opCode) {
case ByteCodeCommand.new_object:
NewObjectCmd newObjectCmd = new NewObjectCmd(clzFile, opCode);
newObjectCmd.setOprand1(commandIterator.nextCharAsInt());
newObjectCmd.setOprand2(commandIterator.nextCharAsInt());
byteCodeCommands.add(newObjectCmd);
break;
case ByteCodeCommand.invokespecial:
InvokeSpecialCmd invokeSpecialCmd = new InvokeSpecialCmd(clzFile, opCode);
invokeSpecialCmd.setOprand1(commandIterator.nextCharAsInt());
invokeSpecialCmd.setOprand2(commandIterator.nextCharAsInt());
byteCodeCommands.add(invokeSpecialCmd);
break;
case ByteCodeCommand.invokevirtual:
InvokeVirtualCmd invokeVirtualCmd = new InvokeVirtualCmd(clzFile, opCode);
invokeVirtualCmd.setOprand1(commandIterator.nextCharAsInt());
invokeVirtualCmd.setOprand2(commandIterator.nextCharAsInt());
byteCodeCommands.add(invokeVirtualCmd);
break;
case ByteCodeCommand.getfield:
GetFieldCmd getFieldCmd = new GetFieldCmd(clzFile, opCode);
getFieldCmd.setOprand1(commandIterator.nextCharAsInt());
getFieldCmd.setOprand2(commandIterator.nextCharAsInt());
byteCodeCommands.add(getFieldCmd);
break;
case ByteCodeCommand.getstatic:
GetStaticFieldCmd getStaticFieldCmd = new GetStaticFieldCmd(clzFile, opCode);
getStaticFieldCmd.setOprand1(commandIterator.nextCharAsInt());
getStaticFieldCmd.setOprand2(commandIterator.nextCharAsInt());
byteCodeCommands.add(getStaticFieldCmd);
break;
case ByteCodeCommand.putfield:
PutFieldCmd putFieldCmd = new PutFieldCmd(clzFile, opCode);
putFieldCmd.setOprand1(commandIterator.nextCharAsInt());
putFieldCmd.setOprand2(commandIterator.nextCharAsInt());
byteCodeCommands.add(putFieldCmd);
break;
case ByteCodeCommand.ldc:
LdcCmd ldcCmd = new LdcCmd(clzFile, opCode);
ldcCmd.setOperand(commandIterator.nextCharAsInt());
byteCodeCommands.add(ldcCmd);
break;
case ByteCodeCommand.bipush:
BiPushCmd biPushCmd = new BiPushCmd(clzFile, opCode);
biPushCmd.setOperand(commandIterator.nextCharAsInt());
byteCodeCommands.add(biPushCmd);
break;
case ByteCodeCommand.dup:
case ByteCodeCommand.aload_0:
case ByteCodeCommand.aload_1:
case ByteCodeCommand.aload_2:
case ByteCodeCommand.iload_1:
case ByteCodeCommand.iload_2:
case ByteCodeCommand.iload_3:
case ByteCodeCommand.fload_3:
case ByteCodeCommand.voidreturn:
case ByteCodeCommand.astore_1:
NoOperandCmd noOperandCmd = new NoOperandCmd(clzFile, opCode);
byteCodeCommands.add(noOperandCmd);
break;
default:
throw new RuntimeException("Sorry,the instruction " + opCode + " " + "has not implemented");
}
}
calcuateOffset(byteCodeCommands);
ByteCodeCommand[] result = new ByteCodeCommand[byteCodeCommands.size()];
byteCodeCommands.toArray(result);
return result;
}
private static void calcuateOffset(List<ByteCodeCommand> cmds) {
int offset = 0;
for (ByteCodeCommand cmd : cmds) {
cmd.setOffset(offset);
offset += cmd.getLength();
}
}
private static class CommandIterator {
String codes = null;
int pos = 0;
CommandIterator(String code) {
this.codes = codes;
}
public boolean hasNext() {
return pos < this.codes.length();
}
public String next2CharAsString() {
String result = codes.substring(pos, pos + 2);
pos += 2;
return result;
}
public int nextCharAsInt() {
String s = this.next2CharAsString();
return Integer.valueOf(s, 16).intValue();
}
}
}