diff --git a/group24/798277403/src/basic/queue/CircleQueue.java b/group24/798277403/src/basic/queue/CircleQueue.java new file mode 100644 index 0000000000..179b75d201 --- /dev/null +++ b/group24/798277403/src/basic/queue/CircleQueue.java @@ -0,0 +1,61 @@ +package basic.queue; + +/** + * 用数组实现循环队列 + * @author liuxin + * @param + */ +public class CircleQueue { + + private final static int DEFAULT_SIZE = 10; + + private int size = 0; + + //用数组来保存循环队列的元素 + private Object[] elementData; + + //队头 + private int front = 0; + //队尾 + private int rear = 0; + + public CircleQueue(){ + elementData = new Object[DEFAULT_SIZE] ; + } + + public CircleQueue(int size){ + elementData = new Object[size]; + } + + public boolean isEmpty() { + return size==0; + } + + public int size() { + return size; + } + + public boolean isFull(){ + return size == elementData.length; + } + + public void enQueue(E data) { + if(isFull()){ + throw new RuntimeException("The queue is full"); + } + elementData[rear] = data; + rear = (rear+1)%elementData.length; + size++; + } + + public E deQueue() { + if(isEmpty()){ + throw new RuntimeException("The queue is empty"); + } + E data = (E)elementData[front]; + elementData[front] = null; + front = (front+1) % elementData.length; + size--; + return data; + } +} diff --git a/group24/798277403/src/basic/queue/CircleQueueTest.java b/group24/798277403/src/basic/queue/CircleQueueTest.java new file mode 100644 index 0000000000..d0181ecaf7 --- /dev/null +++ b/group24/798277403/src/basic/queue/CircleQueueTest.java @@ -0,0 +1,45 @@ +package basic.queue; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + + + +public class CircleQueueTest { + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void test() { + CircleQueue queue = new CircleQueue(5); + Assert.assertTrue(queue.isEmpty()); + Assert.assertFalse(queue.isFull()); + + queue.enQueue("a"); + queue.enQueue("b"); + queue.enQueue("c"); + queue.enQueue("d"); + queue.enQueue("e"); + + Assert.assertTrue(queue.isFull()); + Assert.assertFalse(queue.isEmpty()); + Assert.assertEquals(5, queue.size()); + + Assert.assertEquals("a", queue.deQueue()); + queue.enQueue("f"); + Assert.assertEquals("b", queue.deQueue()); + Assert.assertEquals("c", queue.deQueue()); + Assert.assertEquals("d", queue.deQueue()); + Assert.assertEquals("e", queue.deQueue()); + + } + +} diff --git a/group24/798277403/src/basic/queue/Josephus.java b/group24/798277403/src/basic/queue/Josephus.java new file mode 100644 index 0000000000..527e51517c --- /dev/null +++ b/group24/798277403/src/basic/queue/Josephus.java @@ -0,0 +1,35 @@ +package basic.queue; + +import java.util.ArrayList; +import java.util.List; + +/** + * 用Queue来实现Josephus问题 + * 在这个古老的问题当中, N个深陷绝境的人一致同意用这种方式减少生存人数: N个人围成一圈(位置记为0到N-1), 并且从第一个人报数, 报到M的人会被杀死,然后再由下一个重新报数, 直到最后一个人留下来 + * 该方法返回一个List, 包含了被杀死人的次序 + * @author liuxin + * + */ +public class Josephus { + + public static List execute(int n, int m){ + Queue queue = new Queue(); + List result = new ArrayList(); + + for(int i=0; i { + private Node first; + private Node last; + private int size; + + + private static class Node { + private E item; + private Node next; + } + + + public Queue() { + first = null; + last = null; + size = 0; + } + + + public boolean isEmpty() { + return first == null; + } + + public int size() { + return size; + } + + + + public void enQueue(E data) { + Node oldlast = last; + last = new Node(); + last.item = data; + last.next = null; + if (isEmpty()) { + first = last; + } + else{ + oldlast.next = last; + } + size++; + } + + public E deQueue() { + if (isEmpty()) { + throw new NoSuchElementException("Queue underflow"); + } + E item = first.item; + first = first.next; + size--; + if (isEmpty()) { + last = null; + } + return item; + } + +} diff --git a/group24/798277403/src/basic/queue/QueueWithTwoStacks.java b/group24/798277403/src/basic/queue/QueueWithTwoStacks.java new file mode 100644 index 0000000000..7795b94580 --- /dev/null +++ b/group24/798277403/src/basic/queue/QueueWithTwoStacks.java @@ -0,0 +1,49 @@ +package basic.queue; + +import java.util.Stack; + +/** + * 用两个栈来实现一个队列 + * @author liuxin + * + * @param + */ +public class QueueWithTwoStacks { + private Stack stack1; + private Stack stack2; + + public QueueWithTwoStacks() { + stack1 = new Stack(); + stack2 = new Stack(); + } + + public boolean isEmpty() { + return stack1.isEmpty() && stack2.isEmpty(); + } + + public int size() { + return stack1.size()+stack2.size(); + } + + public void enQueue(E item) { + stack1.push(item); + } + + public E deQueue() { + if(isEmpty()){ + throw new RuntimeException("The queue is empty"); + } + E date = null; + if(!stack2.isEmpty()){ + date = stack2.pop(); + }else{ + while(!stack1.isEmpty()){ + stack2.push(stack1.pop()); + } + date = stack2.pop(); + } + return date; + } + + } + diff --git a/group24/798277403/src/basic/stack/QuickMinStack.java b/group24/798277403/src/basic/stack/QuickMinStack.java new file mode 100644 index 0000000000..6124896e17 --- /dev/null +++ b/group24/798277403/src/basic/stack/QuickMinStack.java @@ -0,0 +1,36 @@ +package basic.stack; + +import java.util.Stack; + +/** + * 设计一个栈,支持栈的push和pop操作,以及第三种操作findMin, 它返回改数据结构中的最小元素 + * finMin操作最坏的情形下时间复杂度应该是O(1) , 简单来讲,操作一次就可以得到最小值 + * @author liuxin + * + */ +public class QuickMinStack { + Stack stackNum = new Stack<>(); + Stack stackMin = new Stack<>(); + public void push(int data){ + stackNum.push(data); + if(stackMin.isEmpty() || stackMin.peek()>=data){ + stackMin.push(data); + } + } + + public int pop(){ + if(stackNum.size()<=0){ + throw new RuntimeException("The stack is empty!"); + } + int result = stackNum.pop(); + if(result==stackMin.peek()){ + stackMin.pop(); + } + return result; + } + + public int findMin(){ + return stackMin.peek(); + } + +} diff --git a/group24/798277403/src/basic/stack/QuickMinStackTest.java b/group24/798277403/src/basic/stack/QuickMinStackTest.java new file mode 100644 index 0000000000..b89e4fffa0 --- /dev/null +++ b/group24/798277403/src/basic/stack/QuickMinStackTest.java @@ -0,0 +1,39 @@ +package basic.stack; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + + +public class QuickMinStackTest { + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void test() { + QuickMinStack stack = new QuickMinStack(); + stack.push(5); + Assert.assertEquals(5, stack.findMin()); + stack.push(6); + Assert.assertEquals(5, stack.findMin()); + stack.push(4); + Assert.assertEquals(4, stack.findMin()); + stack.push(4); + Assert.assertEquals(4, stack.findMin()); + + stack.pop(); + Assert.assertEquals(4, stack.findMin()); + stack.pop(); + Assert.assertEquals(5, stack.findMin()); + stack.pop(); + Assert.assertEquals(5, stack.findMin()); + } + +} diff --git a/group24/798277403/src/basic/stack/StackWithTwoQueues.java b/group24/798277403/src/basic/stack/StackWithTwoQueues.java new file mode 100644 index 0000000000..8901836c34 --- /dev/null +++ b/group24/798277403/src/basic/stack/StackWithTwoQueues.java @@ -0,0 +1,41 @@ +package basic.stack; + + +import java.util.LinkedList; +import java.util.Queue; + +public class StackWithTwoQueues { + + private Queue queue1 = new LinkedList(); + private Queue queue2 = new LinkedList(); + + public void push(int data) { + if(queue1.isEmpty()){ + queue1.add(data); + while(!queue2.isEmpty()){ + queue1.add(queue2.poll()); + } + }else{ + queue2.add(data); + while(!queue1.isEmpty()){ + queue2.add(queue1.poll()); + } + } + } + + public int pop() { + if(queue1.isEmpty() && queue2.isEmpty()){ + throw new RuntimeException("The stack is empty!"); + } + int result = -1; + if(queue1.isEmpty()){ + result = queue2.poll(); + }else{ + result = queue1.poll(); + } + return result; + } + + + +} diff --git a/group24/798277403/src/basic/stack/StackWithTwoQueuesTest.java b/group24/798277403/src/basic/stack/StackWithTwoQueuesTest.java new file mode 100644 index 0000000000..63dcbfda59 --- /dev/null +++ b/group24/798277403/src/basic/stack/StackWithTwoQueuesTest.java @@ -0,0 +1,36 @@ +package basic.stack; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + + + +public class StackWithTwoQueuesTest { + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void test() { + StackWithTwoQueues stack = new StackWithTwoQueues(); + stack.push(1); + stack.push(2); + stack.push(3); + stack.push(4); + Assert.assertEquals(4, stack.pop()); + Assert.assertEquals(3, stack.pop()); + + stack.push(5); + Assert.assertEquals(5, stack.pop()); + Assert.assertEquals(2, stack.pop()); + Assert.assertEquals(1, stack.pop()); + } + +} diff --git a/group24/798277403/src/basic/stack/TwoStackInOneArray.java b/group24/798277403/src/basic/stack/TwoStackInOneArray.java new file mode 100644 index 0000000000..a5b8267cc1 --- /dev/null +++ b/group24/798277403/src/basic/stack/TwoStackInOneArray.java @@ -0,0 +1,116 @@ +package basic.stack; + +/** + * 用一个数组实现两个栈 + * 将数组的起始位置看作是第一个栈的栈底,将数组的尾部看作第二个栈的栈底,压栈时,栈顶指针分别向中间移动,直到两栈顶指针相遇,则扩容。 + * @author liuxin + * + */ +public class TwoStackInOneArray { + private Object[] data ; + private int top1 ; //应该把下标对应数据 + private int top2 ; + + public TwoStackInOneArray(int size){ + data = new Object[size]; + top1 = -1; + top2 = data.length; + } + + public TwoStackInOneArray(){ + data = new Object[10]; + top1 = -1; + top2 = data.length; + } + + /** + * 向第一个栈中压入元素 + * @param o + */ + public void push1(Object o){ + capacity(); + data[++top1] = o; + } + /** + * 从第一个栈中弹出元素 + * @return + */ + public Object pop1(){ + if(top1==-1){ + throw new RuntimeException("The stack is empty!"); + } + Object o = data[top1]; + data[top1] = null; + top1--; + return o; + } + + /** + * 获取第一个栈的栈顶元素 + * @return + */ + public Object peek1(){ + if(top1==-1){ + throw new RuntimeException("The stack is empty!"); + } + return data[top1]; + } + /* + * 向第二个栈压入元素 + */ + public void push2(Object o){ + capacity(); + data[--top2] = o; + } + /** + * 从第二个栈弹出元素 + * @return + */ + public Object pop2(){ + if(top2==data.length){ + throw new RuntimeException("The stack is empty!"); + } + Object o = data[top2]; + data[top2] = null; + top1++; + return o; + } + + /** + * 获取第二个栈的栈顶元素 + * @return + */ + public Object peek2(){ + if(top2==data.length){ + throw new RuntimeException("The stack is empty!"); + } + return data[top2]; + } + + public Object[] stack1ToArray(){ + Object[] result = new Object[top1+1]; + System.arraycopy(data,0,result,0,top1+1); + return result; + } + public Object[] stack2ToArray(){ + int stack2Size = data.length-top2; + Object[] result = new Object[stack2Size]; + for(int i=data.length-1,j=0; i>=top2; i--,j++){ + result[j] = data[i]; + } + return result; + } + + //扩容函数 + private void capacity(){ + if(top2-top1<=1){ + int size = data.length*2+1; + Object[] newData = new Object[size]; + System.arraycopy(data,0,newData,0,top1+1); + int stack2Size = data.length-top2; + System.arraycopy(data,top2,newData,newData.length-stack2Size,stack2Size); + top2 = newData.length-stack2Size; + data = newData; + } + } +} diff --git a/group24/798277403/src/basic/stack/TwoStackInOneArrayTest.java b/group24/798277403/src/basic/stack/TwoStackInOneArrayTest.java new file mode 100644 index 0000000000..27d95352bb --- /dev/null +++ b/group24/798277403/src/basic/stack/TwoStackInOneArrayTest.java @@ -0,0 +1,65 @@ +package basic.stack; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; + + + +public class TwoStackInOneArrayTest { + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void test1() { + TwoStackInOneArray stack = new TwoStackInOneArray(10); + stack.push1(1); + stack.push1(2); + stack.push1(3); + stack.push1(4); + stack.push1(5); + + stack.push2(1); + stack.push2(2); + stack.push2(3); + stack.push2(4); + stack.push2(5); + + for(int i=1;i<=5;i++){ + Assert.assertEquals(stack.peek1(), stack.peek2()); + Assert.assertEquals(stack.pop1(), stack.pop2()); + } + + + } + @Test + public void test2() { + TwoStackInOneArray stack = new TwoStackInOneArray(5); + stack.push1(1); + stack.push1(2); + stack.push1(3); + stack.push1(4); + stack.push1(5); + stack.push1(6); + stack.push1(7); + + stack.push2(1); + stack.push2(2); + stack.push2(3); + stack.push2(4); + + + Assert.assertEquals("[1, 2, 3, 4, 5, 6, 7]",Arrays.toString(stack.stack1ToArray())); + Assert.assertEquals("[1, 2, 3, 4]",Arrays.toString(stack.stack2ToArray())); + } + +} diff --git a/group24/798277403/src/mini_jvm/attr/CodeAttr.java b/group24/798277403/src/mini_jvm/attr/CodeAttr.java index 07df10d251..7d2aa4e760 100644 --- a/group24/798277403/src/mini_jvm/attr/CodeAttr.java +++ b/group24/798277403/src/mini_jvm/attr/CodeAttr.java @@ -86,7 +86,7 @@ public String toString(ConstantPool pool){ StringBuilder buffer = new StringBuilder(); //buffer.append("Code:").append(code).append("\n"); for(int i=0;i")){ + return ; + } + Method nextMethod = MethodArea.getInstance().getMethod(methodRefInfo); + + result.setNextAction(ExecutionResult.PAUSE_AND_RUN_NEW_FRAME); + result.setNextMethod(nextMethod); } diff --git a/group24/798277403/src/mini_jvm/cmd/InvokeVirtualCmd.java b/group24/798277403/src/mini_jvm/cmd/InvokeVirtualCmd.java index 5057132d8e..a4d5b0d239 100644 --- a/group24/798277403/src/mini_jvm/cmd/InvokeVirtualCmd.java +++ b/group24/798277403/src/mini_jvm/cmd/InvokeVirtualCmd.java @@ -2,9 +2,12 @@ import mini_jvm.clz.ClassFile; -import mini_jvm.constant.ConstantPool; +import mini_jvm.constant.MethodRefInfo; import mini_jvm.engine.ExecutionResult; +import mini_jvm.engine.JavaObject; +import mini_jvm.engine.MethodArea; import mini_jvm.engine.StackFrame; +import mini_jvm.method.Method; public class InvokeVirtualCmd extends TwoOperandCmd { @@ -13,14 +16,57 @@ public InvokeVirtualCmd(ClassFile clzFile, String opCode) { } @Override - public String toString(ConstantPool pool) { - - return super.getOperandAsMethod(pool); + public String toString() { + + return super.getOperandAsMethod(); + } + private boolean isSystemOutPrintlnMethod(String className, String methodName){ + return "java/io/PrintStream".equals(className) + && "println".equals(methodName); } + //调用对象的实例方法(多态) @Override public void execute(StackFrame frame, ExecutionResult result) { - + //先得到对该方法的描述 + MethodRefInfo methodRefInfo = (MethodRefInfo)this.getConstantInfo(this.getIndex()); + + String className = methodRefInfo.getClassName(); + String methodName = methodRefInfo.getMethodName(); + + // 我们没有实现System.out.println方法, 所以也不用建立新的栈帧, 直接调用Java的方法, 打印出来即可。 + if(isSystemOutPrintlnMethod(className,methodName)){ + JavaObject jo = (JavaObject)frame.getOprandStack().pop(); + String value = jo.toString(); + System.err.println("-------------------"+value+"----------------"); + // 这里就是那个out对象, 因为是个假的,直接pop出来 + frame.getOprandStack().pop(); + return; + } + + //注意:多态, 这才是真正的对象, 先从该对象的class 中去找对应的方法,找不到的话再去找父类的方法 + JavaObject jo = frame.getOprandStack().peek(); + MethodArea ma = MethodArea.getInstance(); + Method m = null; + String currentClassName = jo.getClassName(); + while(currentClassName != null){ + ClassFile currentClassFile = ma.findClassFile(currentClassName); + m = currentClassFile.getMethod(methodRefInfo.getMethodName(), + methodRefInfo.getParamAndReturnType()); + if(m != null){ + break; + } else{ + //查找父类 + currentClassName = currentClassFile.getSuperClassName(); + } + } + + if(m == null){ + throw new RuntimeException("Can't find method for :" + methodRefInfo.toString()); + } + + result.setNextAction(ExecutionResult.PAUSE_AND_RUN_NEW_FRAME); + result.setNextMethod(m); } diff --git a/group24/798277403/src/mini_jvm/cmd/LdcCmd.java b/group24/798277403/src/mini_jvm/cmd/LdcCmd.java index 25a0ea5753..c21732fcc5 100644 --- a/group24/798277403/src/mini_jvm/cmd/LdcCmd.java +++ b/group24/798277403/src/mini_jvm/cmd/LdcCmd.java @@ -6,6 +6,8 @@ import mini_jvm.constant.ConstantPool; import mini_jvm.constant.StringInfo; import mini_jvm.engine.ExecutionResult; +import mini_jvm.engine.Heap; +import mini_jvm.engine.JavaObject; import mini_jvm.engine.StackFrame; public class LdcCmd extends OneOperandCmd { @@ -13,25 +15,37 @@ public class LdcCmd extends OneOperandCmd { public LdcCmd(ClassFile clzFile, String opCode) { super(clzFile,opCode); } - + @Override - public String toString(ConstantPool pool) { - - ConstantInfo info = (ConstantInfo)pool.getConstantInfo(this.getOperand()); - + public String toString() { + + ConstantInfo info = getConstantInfo(this.getOperand()); + String value = "TBD"; if(info instanceof StringInfo){ StringInfo strInfo = (StringInfo)info; value = strInfo.toString(); } - + return this.getOffset()+":"+this.getOpCode()+" " + this.getReadableCodeText() + " "+ value; - + } @Override public void execute(StackFrame frame, ExecutionResult result) { - + ConstantPool pool = this.getConstantPool(); + ConstantInfo info = (ConstantInfo)pool.getConstantInfo(this.getOperand()); + + if(info instanceof StringInfo){ + StringInfo strInfo = (StringInfo)info; + String value = strInfo.toString(); + JavaObject jo = Heap.getInstance().newString(value); + frame.getOprandStack().push(jo); + } + else{ + //TBD 处理其他类型 + throw new RuntimeException("Only support StringInfo constant"); + } } diff --git a/group24/798277403/src/mini_jvm/cmd/NewObjectCmd.java b/group24/798277403/src/mini_jvm/cmd/NewObjectCmd.java index 56afe06698..3bdfa198ab 100644 --- a/group24/798277403/src/mini_jvm/cmd/NewObjectCmd.java +++ b/group24/798277403/src/mini_jvm/cmd/NewObjectCmd.java @@ -2,8 +2,10 @@ import mini_jvm.clz.ClassFile; -import mini_jvm.constant.ConstantPool; +import mini_jvm.constant.ClassInfo; import mini_jvm.engine.ExecutionResult; +import mini_jvm.engine.Heap; +import mini_jvm.engine.JavaObject; import mini_jvm.engine.StackFrame; public class NewObjectCmd extends TwoOperandCmd{ @@ -13,14 +15,22 @@ public NewObjectCmd(ClassFile clzFile, String opCode){ } @Override - public String toString(ConstantPool pool) { - - return super.getOperandAsClassInfo(pool); + public String toString() { + + return super.getOperandAsClassInfo(); } @Override public void execute(StackFrame frame, ExecutionResult result) { - + //(indexbyte1 << 8)| indexbyte2 得到一个指向常量池的索引 + int index = this.getIndex(); + + ClassInfo info = (ClassInfo)this.getConstantInfo(index); + String clzName = info.getClassName(); + //在Java堆上创建一个实例 + JavaObject jo = Heap.getInstance().newObject(clzName); + + frame.getOprandStack().push(jo); } diff --git a/group24/798277403/src/mini_jvm/cmd/NoOperandCmd.java b/group24/798277403/src/mini_jvm/cmd/NoOperandCmd.java index 0a2f285b12..2c65e40346 100644 --- a/group24/798277403/src/mini_jvm/cmd/NoOperandCmd.java +++ b/group24/798277403/src/mini_jvm/cmd/NoOperandCmd.java @@ -2,32 +2,148 @@ import mini_jvm.clz.ClassFile; -import mini_jvm.constant.ConstantPool; import mini_jvm.engine.ExecutionResult; +import mini_jvm.engine.Heap; +import mini_jvm.engine.JavaObject; import mini_jvm.engine.StackFrame; -public class NoOperandCmd extends ByteCodeCommand{ +public class NoOperandCmd extends ByteCodeCommand { public NoOperandCmd(ClassFile clzFile, String opCode) { super(clzFile, opCode); } @Override - public String toString(ConstantPool pool) { + public String toString() { return this.getOffset()+":" +this.getOpCode() + " "+ this.getReadableCodeText(); } - - + + @Override + public void execute(StackFrame frame, ExecutionResult result) { + + String opCode = this.getOpCode(); + + if(ByteCodeCommand.aload_0.equals(opCode)){ + + JavaObject jo = frame.getLocalVariableValue(0); + + frame.getOprandStack().push(jo); + + } else if(ByteCodeCommand.aload_1.equals(opCode)){ + + JavaObject jo = frame.getLocalVariableValue(1); + + frame.getOprandStack().push(jo); + + } else if(ByteCodeCommand.aload_2.equals(opCode)){ + + JavaObject jo = frame.getLocalVariableValue(2); + + frame.getOprandStack().push(jo); + + }else if(ByteCodeCommand.iload_1.equals(opCode)){ + + JavaObject jo = frame.getLocalVariableValue(1); + + frame.getOprandStack().push(jo); + + } else if (ByteCodeCommand.iload_2.equals(opCode)){ + + JavaObject jo = frame.getLocalVariableValue(2); + + frame.getOprandStack().push(jo); + + } else if (ByteCodeCommand.iload_3.equals(opCode)){ + + JavaObject jo = frame.getLocalVariableValue(3); + + frame.getOprandStack().push(jo); + + }else if (ByteCodeCommand.fload_3.equals(opCode)){ + + JavaObject jo = frame.getLocalVariableValue(3); + + frame.getOprandStack().push(jo); + + } + else if (ByteCodeCommand.voidreturn.equals(opCode)){ + + result.setNextAction(ExecutionResult.EXIT_CURRENT_FRAME); + + } else if(ByteCodeCommand.ireturn.equals(opCode)){ + StackFrame callerFrame = frame.getCallerFrame(); + JavaObject jo = frame.getOprandStack().pop(); + callerFrame.getOprandStack().push(jo); + + } else if(ByteCodeCommand.freturn.equals(opCode)){ + + StackFrame callerFrame = frame.getCallerFrame(); + JavaObject jo = frame.getOprandStack().pop(); + callerFrame.getOprandStack().push(jo); + } + + else if(ByteCodeCommand.astore_1.equals(opCode)){ + + JavaObject jo = frame.getOprandStack().pop(); + + frame.setLocalVariableValue(1, jo); + + } else if(ByteCodeCommand.dup.equals(opCode)){ + + JavaObject jo = frame.getOprandStack().peek(); + frame.getOprandStack().push(jo); + + } else if(ByteCodeCommand.iconst_0.equals(opCode)){ + + JavaObject jo = Heap.getInstance().newInt(0); + + frame.getOprandStack().push(jo); + + } else if(ByteCodeCommand.iconst_1.equals(opCode)){ + + JavaObject jo = Heap.getInstance().newInt(1); + + frame.getOprandStack().push(jo); + + } else if(ByteCodeCommand.istore_1.equals(opCode)){ + + JavaObject jo = frame.getOprandStack().pop(); + + frame.setLocalVariableValue(1, jo); + + } else if(ByteCodeCommand.istore_2.equals(opCode)){ + + JavaObject jo = frame.getOprandStack().pop(); + + frame.setLocalVariableValue(2, jo); + + } else if(ByteCodeCommand.iadd.equals(opCode)){ + + JavaObject jo1 = frame.getOprandStack().pop(); + JavaObject jo2 = frame.getOprandStack().pop(); + + JavaObject sum = Heap.getInstance().newInt(jo1.getIntValue()+jo2.getIntValue()); + + frame.getOprandStack().push(sum); + + } else if (ByteCodeCommand.aconst_null.equals(opCode)){ + + frame.getOprandStack().push(null); + + } + else{ + throw new RuntimeException("you must forget to implement the operation :" + opCode); + } + + + } + + public int getLength(){ return 1; } - @Override - public void execute(StackFrame frame, ExecutionResult result) { - - - } } diff --git a/group24/798277403/src/mini_jvm/cmd/OneOperandCmd.java b/group24/798277403/src/mini_jvm/cmd/OneOperandCmd.java index 65ef9fe5e3..01b4e680bd 100644 --- a/group24/798277403/src/mini_jvm/cmd/OneOperandCmd.java +++ b/group24/798277403/src/mini_jvm/cmd/OneOperandCmd.java @@ -2,7 +2,6 @@ import mini_jvm.clz.ClassFile; -import mini_jvm.constant.ConstantPool; public abstract class OneOperandCmd extends ByteCodeCommand { @@ -25,6 +24,4 @@ public int getLength(){ return 2; } - - public abstract String toString(ConstantPool pool); } diff --git a/group24/798277403/src/mini_jvm/cmd/PutFieldCmd.java b/group24/798277403/src/mini_jvm/cmd/PutFieldCmd.java index 9e2d80a3c9..fa2f21acd6 100644 --- a/group24/798277403/src/mini_jvm/cmd/PutFieldCmd.java +++ b/group24/798277403/src/mini_jvm/cmd/PutFieldCmd.java @@ -2,8 +2,11 @@ import mini_jvm.clz.ClassFile; -import mini_jvm.constant.ConstantPool; +import mini_jvm.constant.ClassInfo; +import mini_jvm.constant.FieldRefInfo; +import mini_jvm.constant.NameAndTypeInfo; import mini_jvm.engine.ExecutionResult; +import mini_jvm.engine.JavaObject; import mini_jvm.engine.StackFrame; public class PutFieldCmd extends TwoOperandCmd { @@ -13,15 +16,26 @@ public PutFieldCmd(ClassFile clzFile, String opCode) { } @Override - public String toString(ConstantPool pool) { - - return super.getOperandAsField(pool); + public String toString() { + return super.getOperandAsField(); } @Override public void execute(StackFrame frame, ExecutionResult result) { - - + + FieldRefInfo fieldRef = (FieldRefInfo)this.getConstantInfo(this.getIndex()); + + ClassInfo clzInfo = (ClassInfo)this.getConstantInfo(fieldRef.getClassInfoIndex()); + NameAndTypeInfo nameTypeInfo = (NameAndTypeInfo)this.getConstantInfo(fieldRef.getNameAndTypeIndex()); + // for example : name + String fieldName = nameTypeInfo.getName(); + // for example : Ljava/lang/String : 注意:我们不再检查类型 + String fieldType = nameTypeInfo.getTypeInfo(); + + JavaObject fieldValue = frame.getOprandStack().pop(); + JavaObject objectRef = frame.getOprandStack().pop(); + + objectRef.setFieldValue(fieldName, fieldValue); } diff --git a/group24/798277403/src/mini_jvm/cmd/TwoOperandCmd.java b/group24/798277403/src/mini_jvm/cmd/TwoOperandCmd.java index 7ec4f5ee38..c49e140b64 100644 --- a/group24/798277403/src/mini_jvm/cmd/TwoOperandCmd.java +++ b/group24/798277403/src/mini_jvm/cmd/TwoOperandCmd.java @@ -5,10 +5,10 @@ import mini_jvm.constant.*; public abstract class TwoOperandCmd extends ByteCodeCommand{ - + int oprand1 = -1; int oprand2 = -1; - + public int getOprand1() { return oprand1; } @@ -24,8 +24,8 @@ public void setOprand2(int oprand2) { public int getOprand2() { return oprand2; } - - public TwoOperandCmd(ClassFile clzFile, String opCode) { + + public TwoOperandCmd(ClassFile clzFile,String opCode) { super(clzFile, opCode); } @@ -35,15 +35,15 @@ public int getIndex(){ int index = oprand1 << 8 | oprand2; return index; } - - protected String getOperandAsClassInfo(ConstantPool pool){ + + protected String getOperandAsClassInfo(){ int index = getIndex(); String codeTxt = getReadableCodeText(); - ClassInfo info = (ClassInfo)pool.getConstantInfo(index); + ClassInfo info = (ClassInfo)getConstantInfo(index); return this.getOffset()+":"+this.getOpCode()+" "+ codeTxt +" "+ info.getClassName(); } - - protected String getOperandAsMethod(ConstantPool pool){ + + protected String getOperandAsMethod(){ int index = getIndex(); String codeTxt = getReadableCodeText(); ConstantInfo constInfo = this.getConstantInfo(index); @@ -51,9 +51,9 @@ protected String getOperandAsMethod(ConstantPool pool){ return this.getOffset()+":"+this.getOpCode()+" " + codeTxt +" "+ info.toString(); } - protected String getOperandAsField(ConstantPool pool){ + protected String getOperandAsField(){ int index = getIndex(); - + String codeTxt = getReadableCodeText(); FieldRefInfo info = (FieldRefInfo)this.getConstantInfo(index); return this.getOffset()+":"+this.getOpCode()+" " + codeTxt +" "+ info.toString(); @@ -62,6 +62,4 @@ public int getLength(){ return 3; } - public abstract String toString(ConstantPool pool); - } diff --git a/group24/798277403/src/mini_jvm/constant/MethodRefInfo.java b/group24/798277403/src/mini_jvm/constant/MethodRefInfo.java index 072378f568..6da55c543f 100644 --- a/group24/798277403/src/mini_jvm/constant/MethodRefInfo.java +++ b/group24/798277403/src/mini_jvm/constant/MethodRefInfo.java @@ -1,5 +1,8 @@ package mini_jvm.constant; +/** + * 常量池中 类中方法的符号引用 + */ public class MethodRefInfo extends ConstantInfo { private int type = ConstantInfo.METHOD_INFO; diff --git a/group24/798277403/src/mini_jvm/engine/ExecutorEngine.java b/group24/798277403/src/mini_jvm/engine/ExecutorEngine.java index 260211376c..379a6cc2cd 100644 --- a/group24/798277403/src/mini_jvm/engine/ExecutorEngine.java +++ b/group24/798277403/src/mini_jvm/engine/ExecutorEngine.java @@ -2,10 +2,14 @@ import mini_jvm.method.Method; +import java.util.ArrayList; +import java.util.List; import java.util.Stack; - +/** + * 维护一个函数帧栈 + */ public class ExecutorEngine { private Stack stack = new Stack(); @@ -15,16 +19,61 @@ public ExecutorEngine() { } public void execute(Method mainMethod){ - - + //为Main函数创建一个函数帧 + StackFrame mainFrame = StackFrame.create(mainMethod); + //将函数帧压入栈中 + stack.push(mainFrame); + + //不断的从函数帧栈中取帧来执行 + while(!stack.isEmpty()){ + StackFrame currentFrame = stack.peek(); + ExecutionResult executionResult = currentFrame.execute(); + + if(executionResult.isPauseAndRunNewFrame()){ + //如果函数帧在执行过程中调用了其他的方法,则开启一个新的函数帧,并压入栈中 + Method nextMethod = executionResult.getNextMethod(); + StackFrame nextFrame = StackFrame.create(nextMethod); + + //为新的函数帧设置回调 + nextFrame.setCallerFrame(currentFrame); + //将参数传递给新的函数 + setupFunctionCallParams(currentFrame,nextFrame); + + stack.push(nextFrame); + }else{ + //否则此函数帧执行完毕,弹出 + stack.pop(); + } + } } private void setupFunctionCallParams(StackFrame currentFrame,StackFrame nextFrame) { - - + + Method nextMethod = nextFrame.getMethod(); + + List paramList = nextMethod.getParameterList(); + + //加上1 是因为要把this也传递过去 + int paramNum = paramList.size() + 1; + + List values = new ArrayList(); + + //把当前函数帧操作数栈的前几个值复制给新的函数帧,相当于传递方法的参数 + //数据结构知识:从栈中取出栈顶的x个元素 + while(paramNum>0){ + values.add(currentFrame.getOprandStack().pop()); + paramNum --; + } + //数据结构知识: 把一个列表倒序排列 + List params = new ArrayList(); + + for(int i=values.size()-1; i>=0 ;i--){ + params.add(values.get(i)); + } + nextFrame.setLocalVariableTable(params); } diff --git a/group24/798277403/src/mini_jvm/engine/MethodArea.java b/group24/798277403/src/mini_jvm/engine/MethodArea.java index fd43904312..382394f965 100644 --- a/group24/798277403/src/mini_jvm/engine/MethodArea.java +++ b/group24/798277403/src/mini_jvm/engine/MethodArea.java @@ -9,6 +9,7 @@ import java.util.Map; + public class MethodArea { public static final MethodArea instance = new MethodArea(); @@ -46,6 +47,7 @@ public ClassFile findClassFile(String className){ return map.get(className); } // 看来该class 文件还没有load过 + System.out.println("load class"); ClassFile clzFile = this.clzLoader.loadClass(className); map.put(className, clzFile); @@ -53,17 +55,29 @@ public ClassFile findClassFile(String className){ return clzFile; } - - + + //根据方法的名称和描述符从方法区获取方法 public Method getMethod(String className, String methodName, String paramAndReturnType){ - - return null; + ClassFile classFile = this.findClassFile(className); + Method m = classFile.getMethod(methodName,paramAndReturnType); + if(m==null){ + throw new RuntimeException("method can't be found : \n" + + "class: " + className + + "method: " + methodName + + "paramAndReturnType: " + paramAndReturnType); + } + return m; } - + //根据类中方法的符号引用从方法区获取方法 public Method getMethod(MethodRefInfo methodRef){ - - return null; - + ClassFile clz = this.findClassFile(methodRef.getClassName()); + + Method m = clz.getMethod(methodRef.getMethodName(), methodRef.getParamAndReturnType()); + + if(m == null){ + throw new RuntimeException("method can't be found : " + methodRef.toString()); + } + return m; } } diff --git a/group24/798277403/src/mini_jvm/engine/MiniJVM.java b/group24/798277403/src/mini_jvm/engine/MiniJVM.java index 6781fed5ad..75a78207f4 100644 --- a/group24/798277403/src/mini_jvm/engine/MiniJVM.java +++ b/group24/798277403/src/mini_jvm/engine/MiniJVM.java @@ -10,20 +10,22 @@ public class MiniJVM { public void run(String[]classPaths , String className) throws FileNotFoundException, IOException{ - + + //装载类 ClassFileLoader loader = new ClassFileLoader(); for(int i=0;i localVariableTable = new ArrayList(); @@ -54,7 +58,6 @@ public Stack getOprandStack(){ } public int getNextCommandIndex(int offset){ - ByteCodeCommand[] cmds = m.getCodeAttr().getCmds(); for(int i=0;i getParameterList(){ + + // e.g. (Ljava/util/List;Ljava/lang/String;II)V + String paramAndType = getParamAndReturnType(); + + int first = paramAndType.indexOf("("); + int last = paramAndType.lastIndexOf(")"); + // e.g. Ljava/util/List;Ljava/lang/String;II + String param = paramAndType.substring(first+1, last); + + List paramList = new ArrayList(); + + if((null == param) || "".equals(param)){ + return paramList; + } + + while(!param.equals("")){ + + int pos = 0; + // 这是一个对象类型 + if(param.charAt(pos) == 'L'){ + + int end = param.indexOf(";"); + + if(end == -1){ + throw new RuntimeException("can't find the ; for a object type"); + } + paramList.add(param.substring(pos+1,end)); + + pos = end + 1; + + } + else if(param.charAt(pos) == 'I'){ + // int + paramList.add("I"); + pos ++; + + } + else if(param.charAt(pos) == 'F'){ + // float + paramList.add("F"); + pos ++; + + } else{ + throw new RuntimeException("the param has unsupported type:" + param); + } + + param = param.substring(pos); + + } + return paramList; + + } } diff --git a/group24/798277403/src/mini_jvm/test/ClassFileloaderTest.java b/group24/798277403/src/mini_jvm/test/ClassFileloaderTest.java index c80e6e5c50..89f20b6ded 100644 --- a/group24/798277403/src/mini_jvm/test/ClassFileloaderTest.java +++ b/group24/798277403/src/mini_jvm/test/ClassFileloaderTest.java @@ -31,7 +31,7 @@ public class ClassFileloaderTest { static { ClassFileLoader loader = new ClassFileLoader(); loader.addClassPath(path2); - String className = "EmployeeV1"; + String className = "com.coderising.jvm.test.EmployeeV1"; clzFile = loader.loadClass(className); //clzFile.print(); @@ -63,22 +63,22 @@ public void testClassPath(){ public void testClassFileLength() { ClassFileLoader loader = new ClassFileLoader(); - loader.addClassPath(path3); + loader.addClassPath(path2); - String className = "week4.test.EmployeeV1"; + String className = "com.coderising.jvm.test.EmployeeV1"; byte[] byteCodes = loader.readBinaryCode(className); // 注意:这个字节数可能和你的JVM版本有关系, 你可以看看编译好的类到底有多大 - Assert.assertEquals(1030, byteCodes.length); + Assert.assertEquals(1056, byteCodes.length); } @Test public void testMagicNumber(){ ClassFileLoader loader = new ClassFileLoader(); - loader.addClassPath(path3); - String className = "week4.test.EmployeeV1"; + loader.addClassPath(path2); + String className = "com.coderising.jvm.test.EmployeeV1"; byte[] byteCodes = loader.readBinaryCode(className); byte[] codes = new byte[]{byteCodes[0],byteCodes[1],byteCodes[2],byteCodes[3]}; diff --git a/group24/798277403/src/mini_jvm/test/MiniJVMTest.java b/group24/798277403/src/mini_jvm/test/MiniJVMTest.java new file mode 100644 index 0000000000..ab12618bd3 --- /dev/null +++ b/group24/798277403/src/mini_jvm/test/MiniJVMTest.java @@ -0,0 +1,28 @@ +package mini_jvm.test; + +import mini_jvm.engine.MiniJVM; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + + +public class MiniJVMTest { + + static final String PATH = "C:\\Users\\zhouliang\\Desktop\\mycoding\\"; + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testMain() throws Exception{ + String[] classPaths = {PATH}; + MiniJVM jvm = new MiniJVM(); + jvm.run(classPaths, "EmployeeV1"); + + } + +}