Java tutorial
/* * Copyright (c) 1996 Artima Software Company. All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for EVALUATION purposes only * is hereby granted provided that this copyright notice * appears in all copies. "Evaluation purposes" are any uses which * do not constitute the sale, sharing, or redistribution of this * software with or to any other persons in any medium. * * ARTIMA SOFTWARE COMPANY MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT * THE SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. ARTIMA SOFTWARE COMPANY * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. * */ /* * LogicalResults.java * * This file contains all the code for the java virtual machine simulation * applet, named Three Dimensional Array, that accompanies the JavaWorld Under The Hood * article titled,"Objecs and Arrays". * * Bill Venners, October 1996 * */ import java.awt.BorderLayout; import java.awt.Button; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Event; import java.awt.Font; import java.awt.GridLayout; import java.awt.Insets; import java.awt.Label; import java.awt.LayoutManager; import java.awt.Panel; /** * An applet that simulates the Java virtual machine executing a * sequence of bytecodes. * * @author Bill Venners */ public class JVMSimulator extends java.applet.Applet implements Runnable { // Vars for the three outer panels that are contained inside the Applet's panel. // twoParts contains the stack and the method area. simulationController // contains the Step and Reset buttons and the hint label. private ThreeParts threeParts; private RegisterPanel registers; private ControlPanel simulationController; // Local reference to buttons on control panel allows for easy enabling and // disabling of buttons. private Button stepButton; private Button resetButton; private Button runButton; private Button stopButton; // If the "run" button is pushed, a separate thread will be invoked that // will cause the JVM to execute until the "stop" button is pressed. private Thread runner; private final int millisecondDelayBetweenSteps = 250; // Vars that implement the Java stack private final int stackBase = 0x33330000; private StackMemorySection stackMemorySection = new StackMemorySection(stackBase, SimData.stackMemorySectionSize); private StackMemoryView stackMemoryView; // Vars that implement the method area of the JVM private final int methodAreaBase = 0x44440000; private MemorySection methodAreaMemorySection = new MemorySection(methodAreaBase, SimData.methodAreaMemorySectionSize); private MemoryView methodAreaMemoryView; // Vars that implement the Java registers private int pcRegister; private int optopRegister; private int frameRegister; private int varsRegister; public void init() { setBackground(SimData.appletBackgroundColor); setLayout(new BorderLayout(5, 5)); threeParts = new ThreeParts(SimData.methodAreaMemorySectionSize); simulationController = new ControlPanel(); stepButton = simulationController.getStepButton(); resetButton = simulationController.getResetButton(); runButton = simulationController.getRunButton(); stopButton = simulationController.getStopButton(); ColoredLabel title = new ColoredLabel(SimData.appletTitle, Label.CENTER, SimData.titleColor); title.setFont(new Font("Helvetica", Font.BOLD, 12)); add("North", title); add("South", simulationController); add("Center", threeParts); // Get a reference to the UI objects that are actually manipulated by // the handlers of the Step and Reset buttons. These aren't available // without this explicit get() because these objects are buried several // levels down in embedded panels. stackMemoryView = threeParts.getStackMemoryViewReference(); methodAreaMemoryView = threeParts.getMethodAreaMemoryViewReference(); registers = threeParts.getRegisterPanel(); // Place the bytecodes into the method area for (int i = 0; i < SimData.methodAreaMemorySectionSize; ++i) { methodAreaMemorySection.setAtAddress(methodAreaBase + i, SimData.theProgram[i]); methodAreaMemorySection.setLogicalValueAtAddress(methodAreaBase + i, SimData.byteCodeMnemonics[i]); } ResetState(); UpdateStateDisplay(); } public boolean action(Event evt, Object arg) { if (evt.target instanceof Button) { String bname = (String) arg; if (bname.equals(StringTable.reset)) { stopButton.disable(); runButton.enable(); stepButton.enable(); resetButton.disable(); ResetState(); UpdateStateDisplay(); } else if (bname.equals(StringTable.step)) { resetButton.enable(); ExecuteNextInstruction(); UpdateStateDisplay(); } else if (bname.equals(StringTable.run)) { stopButton.enable(); runButton.disable(); stepButton.disable(); resetButton.disable(); if (runner == null) { runner = new Thread(this); runner.start(); } } else if (bname.equals(StringTable.stop)) { runButton.enable(); stepButton.enable(); resetButton.enable(); stopButton.disable(); if (runner != null) { runner.stop(); runner = null; } } } return true; } // ExecuteNextInstruction() grabs the instruction pointed to by the program // counter, decodes it via the switch statement, and executes it by running the // code under the appropriate case statement. The program counter is always // set to the next instruction that should be executed, naturally. Only those // bytecodes that appear in the short sequence presented in this simulation // are interpreted here to save time (your time in downloading and my time // in writing.) void ExecuteNextInstruction() { int a, b, result, i, operand0, operand1, operand2, offset; float fa, fb, fresult; Float f; int nextOpCode = methodAreaMemorySection.getAtAddress(pcRegister); switch (nextOpCode) { case OpCode.AALOAD: executeAaload(); break; case OpCode.ALOAD_0: executeAload_n(0); break; case OpCode.ASTORE_0: executeAstore_n(0); break; case OpCode.BIPUSH: operand0 = methodAreaMemorySection.getAtAddress(pcRegister + 1); stackMemorySection.setAtAddress(optopRegister, operand0); stackMemorySection.setLogicalValueAtAddress(optopRegister, Integer.toString(operand0)); optopRegister += 4; pcRegister += 2; break; // The BREAKPOINT opcode will serve as a stop sign for a running simulation. case OpCode.BREAKPOINT: stopButton.disable(); runButton.disable(); stepButton.disable(); resetButton.enable(); if (runner != null) { // If runner is not null, then this is probably the thread that // we want to stop. Therefore, as soon as stop has been executed, // nothing else will happen. So we must set runner to null before // we call runner.stop(). Therefore I copy runner to temp, assign // null to runner, and call stop() on temp. Thread temp = runner; runner = null; temp.stop(); } break; case OpCode.FCONST_0: stackMemorySection.setAtAddress(optopRegister, Float.floatToIntBits((float) 0)); stackMemorySection.setLogicalValueAtAddress(optopRegister, "0"); optopRegister += 4; ++pcRegister; break; case OpCode.FCONST_2: stackMemorySection.setAtAddress(optopRegister, Float.floatToIntBits((float) 2)); stackMemorySection.setLogicalValueAtAddress(optopRegister, "2"); optopRegister += 4; ++pcRegister; break; case OpCode.FLOAD_0: a = stackMemorySection.getAtAddress(varsRegister); stackMemorySection.setAtAddress(optopRegister, a); fa = Float.intBitsToFloat(a); stackMemorySection.setLogicalValueAtAddress(optopRegister, Float.toString(fa)); optopRegister += 4; ++pcRegister; break; case OpCode.FMUL: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); fa = Float.intBitsToFloat(a); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); optopRegister -= 4; b = stackMemorySection.getAtAddress(optopRegister); fb = Float.intBitsToFloat(b); fresult = fa * fb; result = Float.floatToIntBits(fresult); stackMemorySection.setAtAddress(optopRegister, result); stackMemorySection.setLogicalValueAtAddress(optopRegister, Float.toString(fresult)); optopRegister += 4; ++pcRegister; break; case OpCode.FSTORE_0: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); stackMemorySection.setAtAddress(varsRegister, a); fa = Float.intBitsToFloat(a); stackMemorySection.setLogicalValueAtAddress(varsRegister, Float.toString(fa)); ++pcRegister; break; case OpCode.FSUB: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); fa = Float.intBitsToFloat(a); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); optopRegister -= 4; b = stackMemorySection.getAtAddress(optopRegister); fb = Float.intBitsToFloat(b); fresult = fb - fa; result = Float.floatToIntBits(fresult); stackMemorySection.setAtAddress(optopRegister, result); stackMemorySection.setLogicalValueAtAddress(optopRegister, Float.toString(fresult)); optopRegister += 4; ++pcRegister; break; case OpCode.GOTO: operand1 = methodAreaMemorySection.getAtAddress(pcRegister + 1); operand0 = methodAreaMemorySection.getAtAddress(pcRegister + 2); offset = (operand1 << 8) | (operand0 & 0xff); pcRegister += offset; break; case OpCode.IADD: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); optopRegister -= 4; b = stackMemorySection.getAtAddress(optopRegister); result = a + b; stackMemorySection.setAtAddress(optopRegister, result); stackMemorySection.setLogicalValueAtAddress(optopRegister, Integer.toString(result)); optopRegister += 4; ++pcRegister; break; case OpCode.IAND: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); optopRegister -= 4; b = stackMemorySection.getAtAddress(optopRegister); result = a & b; stackMemorySection.setAtAddress(optopRegister, result); stackMemorySection.setLogicalValueAtAddress(optopRegister, Integer.toString(result)); optopRegister += 4; ++pcRegister; break; case OpCode.IASTORE: executeIastore(); break; case OpCode.ICONST_M1: stackMemorySection.setAtAddress(optopRegister, -1); stackMemorySection.setLogicalValueAtAddress(optopRegister, "-1"); optopRegister += 4; ++pcRegister; break; case OpCode.ICONST_0: stackMemorySection.setAtAddress(optopRegister, 0); stackMemorySection.setLogicalValueAtAddress(optopRegister, "0"); optopRegister += 4; ++pcRegister; break; case OpCode.ICONST_1: stackMemorySection.setAtAddress(optopRegister, 1); stackMemorySection.setLogicalValueAtAddress(optopRegister, "1"); optopRegister += 4; ++pcRegister; break; case OpCode.ICONST_2: stackMemorySection.setAtAddress(optopRegister, 2); stackMemorySection.setLogicalValueAtAddress(optopRegister, "2"); optopRegister += 4; ++pcRegister; break; case OpCode.ICONST_3: stackMemorySection.setAtAddress(optopRegister, 3); stackMemorySection.setLogicalValueAtAddress(optopRegister, "3"); optopRegister += 4; ++pcRegister; break; case OpCode.ICONST_4: stackMemorySection.setAtAddress(optopRegister, 4); stackMemorySection.setLogicalValueAtAddress(optopRegister, "4"); optopRegister += 4; ++pcRegister; break; case OpCode.ICONST_5: stackMemorySection.setAtAddress(optopRegister, 5); stackMemorySection.setLogicalValueAtAddress(optopRegister, "5"); optopRegister += 4; ++pcRegister; break; case OpCode.IFNE: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); // If a != 0 jump, else go on if (a != 0) { operand1 = methodAreaMemorySection.getAtAddress(pcRegister + 1); operand0 = methodAreaMemorySection.getAtAddress(pcRegister + 2); offset = (operand1 << 8) | (operand0 & 0xff); pcRegister += offset; } else { pcRegister += 3; } break; case OpCode.IF_ICMPLT: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); optopRegister -= 4; b = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); // If b < a jump, else go on if (b < a) { operand1 = methodAreaMemorySection.getAtAddress(pcRegister + 1); operand0 = methodAreaMemorySection.getAtAddress(pcRegister + 2); offset = (operand1 << 8) | (operand0 & 0xff); pcRegister += offset; } else { pcRegister += 3; } break; case OpCode.IINC: operand0 = methodAreaMemorySection.getAtAddress(pcRegister + 1); operand1 = methodAreaMemorySection.getAtAddress(pcRegister + 2); a = stackMemorySection.getAtAddress(varsRegister + (operand0 * 4)); a += operand1; stackMemorySection.setAtAddress(varsRegister + (operand0 * 4), a); stackMemorySection.setLogicalValueAtAddress(varsRegister + (operand0 * 4), Integer.toString(a)); pcRegister += 3; break; case OpCode.ILOAD_0: a = stackMemorySection.getAtAddress(varsRegister); stackMemorySection.setAtAddress(optopRegister, a); stackMemorySection.setLogicalValueAtAddress(optopRegister, Integer.toString(a)); optopRegister += 4; ++pcRegister; break; case OpCode.ILOAD_1: a = stackMemorySection.getAtAddress(varsRegister + 4); stackMemorySection.setAtAddress(optopRegister, a); stackMemorySection.setLogicalValueAtAddress(optopRegister, Integer.toString(a)); optopRegister += 4; ++pcRegister; break; case OpCode.ILOAD_2: a = stackMemorySection.getAtAddress(varsRegister + 8); stackMemorySection.setAtAddress(optopRegister, a); stackMemorySection.setLogicalValueAtAddress(optopRegister, Integer.toString(a)); optopRegister += 4; ++pcRegister; break; case OpCode.ILOAD_3: a = stackMemorySection.getAtAddress(varsRegister + 12); stackMemorySection.setAtAddress(optopRegister, a); stackMemorySection.setLogicalValueAtAddress(optopRegister, Integer.toString(a)); optopRegister += 4; ++pcRegister; break; case OpCode.IMUL: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); optopRegister -= 4; b = stackMemorySection.getAtAddress(optopRegister); result = a * b; stackMemorySection.setAtAddress(optopRegister, result); stackMemorySection.setLogicalValueAtAddress(optopRegister, Integer.toString(result)); optopRegister += 4; ++pcRegister; break; case OpCode.INT2BYTE: a = stackMemorySection.getAtAddress(optopRegister - 4); a = (byte) a; stackMemorySection.setAtAddress(optopRegister - 4, a); stackMemorySection.setLogicalValueAtAddress(optopRegister - 4, Integer.toString(a)); ++pcRegister; break; case OpCode.IOR: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); optopRegister -= 4; b = stackMemorySection.getAtAddress(optopRegister); result = a | b; stackMemorySection.setAtAddress(optopRegister, result); stackMemorySection.setLogicalValueAtAddress(optopRegister, Integer.toString(result)); optopRegister += 4; ++pcRegister; break; case OpCode.ISHL: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); optopRegister -= 4; b = stackMemorySection.getAtAddress(optopRegister); result = b << (a & 0x1f); stackMemorySection.setAtAddress(optopRegister, result); stackMemorySection.setLogicalValueAtAddress(optopRegister, Integer.toString(result)); optopRegister += 4; ++pcRegister; break; case OpCode.ISTORE_0: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); stackMemorySection.setAtAddress(varsRegister, a); stackMemorySection.setLogicalValueAtAddress(varsRegister, Integer.toString(a)); ++pcRegister; break; case OpCode.ISTORE_1: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); stackMemorySection.setAtAddress(varsRegister + 4, a); stackMemorySection.setLogicalValueAtAddress(varsRegister + 4, Integer.toString(a)); ++pcRegister; break; case OpCode.ISTORE_2: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); stackMemorySection.setAtAddress(varsRegister + 8, a); stackMemorySection.setLogicalValueAtAddress(varsRegister + 8, Integer.toString(a)); ++pcRegister; break; case OpCode.ISTORE_3: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); stackMemorySection.setAtAddress(varsRegister + 12, a); stackMemorySection.setLogicalValueAtAddress(varsRegister + 12, Integer.toString(a)); ++pcRegister; break; case OpCode.IXOR: optopRegister -= 4; a = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); optopRegister -= 4; b = stackMemorySection.getAtAddress(optopRegister); result = a ^ b; stackMemorySection.setAtAddress(optopRegister, result); stackMemorySection.setLogicalValueAtAddress(optopRegister, Integer.toString(result)); optopRegister += 4; ++pcRegister; break; case OpCode.MULTIANEWARRAY: executeMultianewarray(); pcRegister += 4; break; } } // Pushing the Reset button will cause ResetState() to be executed, which will // reset all the data to its initial values. void ResetState() { pcRegister = methodAreaBase; optopRegister = stackBase + SimData.optopOffset; frameRegister = stackBase + SimData.frameOffset; varsRegister = stackBase; int i; for (i = 0; i < SimData.stackMemorySectionSize; ++i) { stackMemorySection.setLogicalValueAtAddress(stackBase + (i * 4), ""); stackMemorySection.setAtAddress(stackBase + (i * 4), 0); } methodAreaMemoryView.update(methodAreaMemorySection, methodAreaBase); } // UpdateStateDisplay writes the current state of the JVM to the UI. void UpdateStateDisplay() { registers.setPcRegister(pcRegister); registers.setOptopRegister(optopRegister); registers.setFrameRegister(frameRegister); registers.setVarsRegister(varsRegister); stackMemoryView.update(stackMemorySection, stackBase); methodAreaMemoryView.updateProgramCounter(pcRegister - methodAreaBase, methodAreaMemorySection); stackMemoryView.clearPointers(); stackMemoryView.updatePointer((varsRegister - stackBase) / 4, StringTable.varsPointer); stackMemoryView.updatePointer((frameRegister - stackBase) / 4, StringTable.framePointer); stackMemoryView.updatePointer((optopRegister - stackBase) / 4, StringTable.optopPointer); int nextOpCode = methodAreaMemorySection.getAtAddress(pcRegister); switch (nextOpCode) { case OpCode.AALOAD: simulationController.setExplanationText(StringTable.aaloadText); break; case OpCode.ALOAD_0: simulationController.setExplanationText(StringTable.aload_0Text); break; case OpCode.ASTORE_0: simulationController.setExplanationText(StringTable.astore_0Text); break; case OpCode.BIPUSH: simulationController.setExplanationText(StringTable.bipushText); break; case OpCode.BREAKPOINT: simulationController.setExplanationText(StringTable.breakpointText); break; case OpCode.FCONST_0: simulationController.setExplanationText(StringTable.fconst_0Text); break; case OpCode.FCONST_2: simulationController.setExplanationText(StringTable.fconst_2Text); break; case OpCode.FLOAD_0: simulationController.setExplanationText(StringTable.fload_0Text); break; case OpCode.FMUL: simulationController.setExplanationText(StringTable.fmulText); break; case OpCode.FSTORE_0: simulationController.setExplanationText(StringTable.fstore_0Text); break; case OpCode.FSUB: simulationController.setExplanationText(StringTable.fsubText); break; case OpCode.GOTO: simulationController.setExplanationText(StringTable.gotoText); break; case OpCode.IADD: simulationController.setExplanationText(StringTable.iaddText); break; case OpCode.IAND: simulationController.setExplanationText(StringTable.iandText); break; case OpCode.IASTORE: simulationController.setExplanationText(StringTable.iastoreText); break; case OpCode.ICONST_M1: simulationController.setExplanationText(StringTable.iconst_m1Text); break; case OpCode.ICONST_0: simulationController.setExplanationText(StringTable.iconst_0Text); break; case OpCode.ICONST_1: simulationController.setExplanationText(StringTable.iconst_1Text); break; case OpCode.ICONST_2: simulationController.setExplanationText(StringTable.iconst_2Text); break; case OpCode.ICONST_3: simulationController.setExplanationText(StringTable.iconst_3Text); break; case OpCode.ICONST_4: simulationController.setExplanationText(StringTable.iconst_4Text); break; case OpCode.ICONST_5: simulationController.setExplanationText(StringTable.iconst_5Text); break; case OpCode.IF_ICMPLT: simulationController.setExplanationText(StringTable.if_icmpltText); break; case OpCode.IFNE: simulationController.setExplanationText(StringTable.ifneText); break; case OpCode.IINC: simulationController.setExplanationText(StringTable.iincText); break; case OpCode.ILOAD_0: simulationController.setExplanationText(StringTable.iload_0Text); break; case OpCode.ILOAD_1: simulationController.setExplanationText(StringTable.iload_1Text); break; case OpCode.ILOAD_2: simulationController.setExplanationText(StringTable.iload_2Text); break; case OpCode.ILOAD_3: simulationController.setExplanationText(StringTable.iload_3Text); break; case OpCode.IMUL: simulationController.setExplanationText(StringTable.imulText); break; case OpCode.INT2BYTE: simulationController.setExplanationText(StringTable.int2byteText); break; case OpCode.IOR: simulationController.setExplanationText(StringTable.iorText); break; case OpCode.ISHL: simulationController.setExplanationText(StringTable.ishlText); break; case OpCode.ISTORE_0: simulationController.setExplanationText(StringTable.istore_0Text); break; case OpCode.ISTORE_1: simulationController.setExplanationText(StringTable.istore_1Text); break; case OpCode.ISTORE_2: simulationController.setExplanationText(StringTable.istore_2Text); break; case OpCode.ISTORE_3: simulationController.setExplanationText(StringTable.istore_3Text); break; case OpCode.IXOR: simulationController.setExplanationText(StringTable.ixorText); break; case OpCode.MULTIANEWARRAY: simulationController.setExplanationText(StringTable.multianewarrayText); break; default: simulationController.setExplanationText(""); break; } } // Make pretty border around entire applet panel public Insets insets() { return new Insets(5, 5, 5, 5); } public void stop() { if (runner != null) { runner.stop(); runner = null; } } public void run() { while (true) { ExecuteNextInstruction(); UpdateStateDisplay(); try { Thread.sleep(millisecondDelayBetweenSteps); } catch (InterruptedException e) { } } } void executeAaload() { // Pop array index. optopRegister -= 4; int index = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); // Pop reference to array of object references. // Cast generic object reference to a reference to an array of objects. This // will cause the JVM to do a checkcast instruction to make sure this is a // valid operation. An exception will be thrown if I've got any other kind // of array or object reference. Once this succeeds, I can use the arrayRef // as an array to get the index'th object reference and push it. optopRegister -= 4; Object objRef = stackMemorySection.getObjectAtAddress(optopRegister); Object[] arrayRef = (Object[]) objRef; // Push the object reference at arrayRef[index]. stackMemorySection.setObjectAtAddress(optopRegister, arrayRef[index]); stackMemorySection.setLogicalValueAtAddress(optopRegister, StringTable.objectReference); optopRegister += 4; ++pcRegister; } void executeAload_n(int locVar) { Object objRef = stackMemorySection.getObjectAtAddress(varsRegister + (4 * locVar)); stackMemorySection.setObjectAtAddress(optopRegister, objRef); stackMemorySection.setLogicalValueAtAddress(optopRegister, StringTable.objectReference); optopRegister += 4; ++pcRegister; } void executeAstore_n(int locVar) { optopRegister -= 4; Object objRef = stackMemorySection.getObjectAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); stackMemorySection.setObjectAtAddress(varsRegister + (4 * locVar), objRef); stackMemorySection.setLogicalValueAtAddress(varsRegister + (4 * locVar), StringTable.objectReference); ++pcRegister; } void executeIastore() { // Pop int value. optopRegister -= 4; int value = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); // Pop index. optopRegister -= 4; int index = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); // Pop reference to an array of integers. Must cast the generic object // reference to a reference to an array of integers, then use that // to assign arrayRef[index] = value. optopRegister -= 4; Object objRef = stackMemorySection.getObjectAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); int[] arrayRef = (int[]) objRef; arrayRef[index] = value; ++pcRegister; } void executeMultianewarray() { int indexbyte1 = methodAreaMemorySection.getAtAddress(pcRegister + 1); int indexbyte0 = methodAreaMemorySection.getAtAddress(pcRegister + 2); int dim = methodAreaMemorySection.getAtAddress(pcRegister + 3); if (dim < 1) { // this is an exception return; } // Fill an array with the sizes of the various arrays. The sizes go into the // array in the order in which they appear in the declaration, left to right. // This was the same order in which they were pushed onto the stack. Therefore, // the first element is assigned the value most buried (furthest down) in the // stack. int[] size = new int[dim]; for (int i = dim - 1; i >= 0; --i) { optopRegister -= 4; size[i] = stackMemorySection.getAtAddress(optopRegister); stackMemorySection.setLogicalValueAtAddress(optopRegister, ""); } // This time around, I'll just assume it's an array of ints. In the future, I'll // need to check the constant pool and pass down the type. Object result = createMultiDimArray(size); stackMemorySection.setObjectAtAddress(optopRegister, result); stackMemorySection.setLogicalValueAtAddress(optopRegister, StringTable.objectReference); optopRegister += 4; } Object createMultiDimArray(int[] size) { Object result; if (size.length == 1) { result = new int[size[0]]; } else { // Create and initialize an array of arrays Object[] arrayOfArrays = new Object[size[0]]; result = arrayOfArrays; // As soon as a size of zero is hit for the next array, we are done. This // will be the case if some of the square brackets were left empty in // the declaration, as in "new int[5][4][][]," in which the third and fourth // sizes will be zero. if (size[1] != 0) { // Create and initialize an array of sizes to be passed to a recursive call // to createMultiDimArray(). This array is identical to the array passed // to this function with the first element clipped off. int[] nextSize = new int[size.length - 1]; for (int i = 1; i < size.length; ++i) { nextSize[i - 1] = size[i]; } // Call this function recursively to create initialize this array // of array with the sub-arrays. for (int i = 0; i < size[0]; ++i) { arrayOfArrays[i] = createMultiDimArray(nextSize); } } } return result; } } // I used this class because I can't seem to set the background color of // a label. I only want a label, but I want the backgound to be gray. class ColoredLabel extends Panel { private Label theLabel; ColoredLabel(String label, int alignment, Color color) { setLayout(new GridLayout(1, 1)); setBackground(color); theLabel = new Label(label, alignment); add(theLabel); } public void setLabelText(String s) { theLabel.setText(s); } public Insets insets() { return new Insets(0, 0, 0, 0); } } class ControlPanel extends Panel { private ColoredLabel explanationLabel; private GrayButton stepButton = new GrayButton(StringTable.step); private GrayButton resetButton = new GrayButton(StringTable.reset); private GrayButton runButton = new GrayButton(StringTable.run); private GrayButton stopButton = new GrayButton(StringTable.stop); ControlPanel() { setLayout(new BorderLayout(5, 5)); Panel leftButtonPanel = new Panel(); leftButtonPanel.setLayout(new GridLayout(2, 2, 5, 5)); leftButtonPanel.add(stepButton); resetButton.disable(); leftButtonPanel.add(runButton); leftButtonPanel.add(resetButton); leftButtonPanel.add(stopButton); stopButton.disable(); explanationLabel = new ColoredLabel("This is where the explanation goes...", Label.CENTER, Color.lightGray); explanationLabel.setBackground(SimData.explanationLabel); Font plainFont = new Font("TimesRoman", Font.ITALIC, 12); explanationLabel.setFont(plainFont); add("West", leftButtonPanel); add("Center", explanationLabel); } public void setExplanationText(String explanation) { explanationLabel.setLabelText(explanation); } public Button getStepButton() { return stepButton; } public Button getResetButton() { return resetButton; } public Button getRunButton() { return runButton; } public Button getStopButton() { return stopButton; } public Insets insets() { // top, left, bottom, right return new Insets(0, 0, 0, 0); } } class GrayButton extends Button { GrayButton(String label) { super(label); setBackground(Color.lightGray); } } // GridSnapLayout lays out components in a grid that can have columns of // varying width. This is not a very general purpose layout manager. It // solves the specific problem of getting all the information I want to display about // the stack and method areas in a nice grid. Because some columns of info need // more room than others, and space is limited on a web page, I needed to be // able to specify varying widths of columns in a grid. class GridSnapLayout implements LayoutManager { // rows and cols are the number of rows and columns of the grid upon // which the components are placed. Components are always one row // in height, but may be more than one column in width. The number // of columns width of each component is stored in hComponentCellWidths. // The array length of hComponentCellWidths indicates the number of // components that will appear on each row. private int rows; private int cols; private int[] hComponentCellWidths; public GridSnapLayout(int rows, int cols, int[] hComponentCellWidths) { this.rows = rows; this.cols = cols; this.hComponentCellWidths = hComponentCellWidths; } public void addLayoutComponent(String name, Component comp) { } public void removeLayoutComponent(Component comp) { } // Calculate preferred size as if each component were taking an equal // share of the width of a row. public Dimension preferredLayoutSize(Container parent) { int rowCount = rows; int colCount = cols; Insets parentInsets = parent.insets(); int componentCount = parent.countComponents(); if (rowCount > 0) { colCount = (componentCount + rowCount - 1) / rowCount; } else { rowCount = (componentCount + colCount - 1) / colCount; } // Find the maximum preferred width and the maximum preferred height // of any component. int w = 0; int h = 0; for (int i = 0; i < componentCount; i++) { Component comp = parent.getComponent(i); Dimension d = comp.preferredSize(); if (w < d.width) { w = d.width; } if (h < d.height) { h = d.height; } } // Return the maximum preferred component width and height times the number // of columns and rows, respectively, plus any insets in the parent. return new Dimension(parentInsets.left + parentInsets.right + colCount * w, parentInsets.top + parentInsets.bottom + rowCount * h); } // Calculate minimum size as if each component were taking an equal // share of the width of a row. public Dimension minimumLayoutSize(Container parent) { Insets parentInsets = parent.insets(); int componentCount = parent.countComponents(); int rowCount = rows; int colCount = cols; if (rowCount > 0) { colCount = (componentCount + rowCount - 1) / rowCount; } else { rowCount = (componentCount + colCount - 1) / colCount; } // Find the maximum "minimum width" and the maximum "minimum height" // of any component. int w = 0; int h = 0; for (int i = 0; i < componentCount; i++) { Component comp = parent.getComponent(i); Dimension d = comp.minimumSize(); if (w < d.width) { w = d.width; } if (h < d.height) { h = d.height; } } // Return the maximum "minimum component width and height" times the number // of columns and rows, respectively, plus any insets in the parent. return new Dimension(parentInsets.left + parentInsets.right + colCount * w, parentInsets.top + parentInsets.bottom + rowCount * h); } // Layout the container such that the widths of columns correspond // to the number of columns in that components hComponentCellWidth // array element. For example, if the public void layoutContainer(Container parent) { int rowCount = rows; int colCount = hComponentCellWidths.length; Insets parentInsets = parent.insets(); int componentCount = parent.countComponents(); if (componentCount == 0) { return; } // Calculate the width and height of each grid cell. The height will // be the height of each component, but the width may not. The width // of a component will be some multiple of a grid cell width. The // number of grid cells for each component is defined by the // hComponentCellWidths array. w is width of each grid cell. h is // height of each grid cell. Dimension parentDim = parent.size(); int w = parentDim.width - (parentInsets.left + parentInsets.right); int h = parentDim.height - (parentInsets.top + parentInsets.bottom); w /= cols; h /= rowCount; // For each row and column of components (not grid cells) position // the component. for (int c = 0, x = parentInsets.left; c < colCount; c++) { for (int r = 0, y = parentInsets.top; r < rowCount; r++) { int i = r * colCount + c; if (i < componentCount) { parent.getComponent(i).reshape(x, y, w * hComponentCellWidths[c], h); } y += h; } x += (w * hComponentCellWidths[c]); } } } class HexString { private final String hexChar = "0123456789abcdef"; private StringBuffer buf = new StringBuffer(); void Convert(int val, int maxNibblesToConvert) { buf.setLength(0); int v = val; for (int i = 0; i < maxNibblesToConvert; ++i) { if (v == 0) { if (i == 0) { buf.insert(0, '0'); } break; } // Get lowest nibble int remainder = v & 0xf; // Convert nibble to a character and insert it into the beginning of the string buf.insert(0, hexChar.charAt(remainder)); // Shift the int to the right four bits v >>>= 4; } } HexString(int val, int minWidth) { Convert(val, minWidth); int charsNeeded = minWidth - buf.length(); for (int i = 0; i < charsNeeded; ++i) { buf.insert(0, '0'); } } public String getString() { return buf.toString(); } } class LabeledRegister extends Panel { private ColoredLabel registerContents; LabeledRegister(String labelText) { setLayout(new BorderLayout(5, 5)); registerContents = new ColoredLabel("00000000", Label.CENTER, Color.lightGray); registerContents.setFont(new Font("TimesRoman", Font.PLAIN, 11)); Label title = new Label(labelText, Label.RIGHT); title.setFont(new Font("Helvetica", Font.ITALIC, 11)); add("East", registerContents); add("Center", title); } public void setRegister(int val) { HexString hexString = new HexString(val, 8); registerContents.setLabelText(hexString.getString()); } public Insets insets() { return new Insets(0, 0, 0, 0); } } // MemorySection is just used for the method area in this applet. This implements // the functionality of the method area and has nothing to do with the UI. class MemorySection { private int[] memory; private int baseAddress; private String[] logicalValueString; MemorySection(int base, int size) { baseAddress = base; memory = new int[size]; logicalValueString = new String[size]; for (int i = 0; i < size; ++i) { logicalValueString[i] = new String(); } } int getBaseAddress() { return baseAddress; } public int getAtAddress(int address) { return memory[address - baseAddress]; } public String getLogicalValueAtAddress(int address) { return logicalValueString[address - baseAddress]; } public void setAtAddress(int address, int value) { memory[address - baseAddress] = value; } public void setLogicalValueAtAddress(int address, String s) { logicalValueString[address - baseAddress] = s; } int getSize() { return memory.length; } } // MemoryView is just used for the method area in this applet. It implements the // UI of the method area. class MemoryView extends Panel { private final int memoryLocationsVisibleCount = SimData.methodAreaMemoryLocationsVisibleCount; private Label[] pointer = new Label[memoryLocationsVisibleCount]; private Label[] address = new Label[memoryLocationsVisibleCount]; private Label[] byteValue = new Label[memoryLocationsVisibleCount]; private Label[] logicalValue = new Label[memoryLocationsVisibleCount]; private int firstVisibleRow; private int currentProgramCounterRow; MemoryView(int methodAreaMemSectionSize) { int[] hComponentCellWidths = new int[4]; hComponentCellWidths[0] = 2; hComponentCellWidths[1] = 2; hComponentCellWidths[2] = 2; hComponentCellWidths[3] = 3; setLayout(new GridSnapLayout(memoryLocationsVisibleCount, 9, hComponentCellWidths)); setBackground(Color.lightGray); Font plainFont = new Font("TimesRoman", Font.PLAIN, 11); setFont(plainFont); Font italicFont = new Font("TimesRoman", Font.ITALIC, 11); for (int i = 0; i < memoryLocationsVisibleCount; ++i) { pointer[i] = new Label("", Label.RIGHT); pointer[i].setFont(italicFont); add(pointer[i]); address[i] = new Label("", Label.CENTER); add(address[i]); byteValue[i] = new Label("", Label.CENTER); add(byteValue[i]); logicalValue[i] = new Label("", Label.LEFT); add(logicalValue[i]); } } public void setAt(int i, int addressValue, int value, String logicalValueStr) { HexString addressValueHexString = new HexString(addressValue, 8); HexString byteValueHexString = new HexString(value, 2); address[i].setText(addressValueHexString.getString()); byteValue[i].setText(byteValueHexString.getString()); logicalValue[i].setText(logicalValueStr); } public void update(MemorySection memorySection, int initialAddress) { for (int i = 0; i < memoryLocationsVisibleCount; ++i) { int theByte = memorySection.getAtAddress(initialAddress + i); String logicalValue = memorySection.getLogicalValueAtAddress(initialAddress + i); setAt(i, initialAddress + i, theByte, logicalValue); } } public void clearPointers() { for (int i = 0; i < memoryLocationsVisibleCount; ++i) { pointer[i].setText(""); } } public void updateProgramCounter(int i, MemorySection memorySection) { pointer[currentProgramCounterRow - firstVisibleRow].setText(""); if (i - firstVisibleRow >= memoryLocationsVisibleCount) { firstVisibleRow = i; if (firstVisibleRow > memorySection.getSize() - memoryLocationsVisibleCount) { firstVisibleRow = memorySection.getSize() - memoryLocationsVisibleCount; } update(memorySection, memorySection.getBaseAddress() + firstVisibleRow); } else if (i < firstVisibleRow) { firstVisibleRow = i; update(memorySection, memorySection.getBaseAddress() + firstVisibleRow); } pointer[i - firstVisibleRow].setText("pc >"); currentProgramCounterRow = i; } public Insets insets() { // top, left, bottom, right return new Insets(0, 0, 0, 0); } } class MemoryViewTitlePanel extends Panel { MemoryViewTitlePanel() { int[] hComponentCellWidths = new int[4]; hComponentCellWidths[0] = 2; hComponentCellWidths[1] = 2; hComponentCellWidths[2] = 2; hComponentCellWidths[3] = 3; setLayout(new GridSnapLayout(1, 9, hComponentCellWidths)); setFont(new Font("Helvetica", Font.ITALIC, 11)); add(new Label("", Label.CENTER)); add(new Label(StringTable.address, Label.CENTER)); add(new Label(StringTable.bytecodes, Label.CENTER)); add(new Label(StringTable.mnemonics, Label.LEFT)); } public Insets insets() { // top, left, bottom, right return new Insets(0, 0, 0, 0); } } class MemoryViewWithTitles extends Panel { private MemoryView memoryView; MemoryViewWithTitles(int methodAreaMemorySectionSize) { memoryView = new MemoryView(methodAreaMemorySectionSize); setLayout(new BorderLayout()); add("North", new MemoryViewTitlePanel()); add("Center", memoryView); } public MemoryView getMemoryViewReference() { return memoryView; } public Insets insets() { // top, left, bottom, right return new Insets(0, 0, 0, 0); } } class MethodAreaPanel extends Panel { private Label title; private MemoryViewWithTitles memoryView; MethodAreaPanel(int methodAreaMemorySectionSize) { memoryView = new MemoryViewWithTitles(methodAreaMemorySectionSize); setLayout(new BorderLayout()); title = new Label("Method Area", Label.CENTER); title.setFont(new Font("Helvetica", Font.BOLD, 11)); add("North", title); add("Center", memoryView); } public MemoryView getMemoryViewReference() { return memoryView.getMemoryViewReference(); } public Insets insets() { return new Insets(5, 5, 5, 5); } } class OpCode { final static int NOP = 0; final static int ACONST_NULL = 1; final static int ICONST_M1 = 2; final static int ICONST_0 = 3; final static int ICONST_1 = 4; final static int ICONST_2 = 5; final static int ICONST_3 = 6; final static int ICONST_4 = 7; final static int ICONST_5 = 8; final static int LCONST_0 = 9; final static int LCONST_1 = 10; final static int FCONST_0 = 11; final static int FCONST_1 = 12; final static int FCONST_2 = 13; final static int DCONST_0 = 14; final static int DCONST_1 = 15; final static int BIPUSH = 16; final static int SIPUSH = 17; final static int LDC1 = 18; final static int LDC2 = 19; final static int LDC2W = 20; final static int ILOAD = 21; final static int LLOAD = 22; final static int FLOAD = 23; final static int DLOAD = 24; final static int ALOAD = 25; final static int ILOAD_0 = 26; final static int ILOAD_1 = 27; final static int ILOAD_2 = 28; final static int ILOAD_3 = 29; final static int LLOAD_0 = 30; final static int LLOAD_1 = 31; final static int LLOAD_2 = 32; final static int LLOAD_3 = 33; final static int FLOAD_0 = 34; final static int FLOAD_1 = 35; final static int FLOAD_2 = 36; final static int FLOAD_3 = 37; final static int DLOAD_0 = 38; final static int DLOAD_1 = 39; final static int DLOAD_2 = 40; final static int DLOAD_3 = 41; final static int ALOAD_0 = 42; final static int ALOAD_1 = 43; final static int ALOAD_2 = 44; final static int ALOAD_3 = 45; final static int IALOAD = 46; final static int LALOAD = 47; final static int FALOAD = 48; final static int DALOAD = 49; final static int AALOAD = 50; final static int BALOAD = 51; final static int CALOAD = 52; final static int SALOAD = 53; final static int ISTORE = 54; final static int LSTORE = 55; final static int FSTORE = 56; final static int DSTORE = 57; final static int ASTORE = 58; final static int ISTORE_0 = 59; final static int ISTORE_1 = 60; final static int ISTORE_2 = 61; final static int ISTORE_3 = 62; final static int LSTORE_0 = 63; final static int LSTORE_1 = 64; final static int LSTORE_2 = 65; final static int LSTORE_3 = 66; final static int FSTORE_0 = 67; final static int FSTORE_1 = 68; final static int FSTORE_2 = 69; final static int FSTORE_3 = 70; final static int DSTORE_0 = 71; final static int DSTORE_1 = 72; final static int DSTORE_2 = 73; final static int DSTORE_3 = 74; final static int ASTORE_0 = 75; final static int ASTORE_1 = 76; final static int ASTORE_2 = 77; final static int ASTORE_3 = 78; final static int IASTORE = 79; final static int LASTORE = 80; final static int FASTORE = 81; final static int DASTORE = 82; final static int AASTORE = 83; final static int BASTORE = 84; final static int CASTORE = 85; final static int SASTORE = 86; final static int POP = 87; final static int POP2 = 88; final static int DUP = 89; final static int DUP_X1 = 90; final static int DUP_X2 = 91; final static int DUP2 = 92; final static int DUP2_X1 = 93; final static int DUP2_X2 = 94; final static int SWAP = 95; final static int IADD = 96; final static int LADD = 97; final static int FADD = 98; final static int DADD = 99; final static int ISUB = 100; final static int LSUB = 101; final static int FSUB = 102; final static int DSUB = 103; final static int IMUL = 104; final static int LMUL = 105; final static int FMUL = 106; final static int DMUL = 107; final static int IDIV = 108; final static int LDIV = 109; final static int FDIV = 110; final static int DDIV = 111; final static int IREM = 112; final static int LREM = 113; final static int FREM = 114; final static int DREM = 115; final static int INEG = 116; final static int LNEG = 117; final static int FNEG = 118; final static int DNEG = 119; final static int ISHL = 120; final static int LSHL = 121; final static int ISHR = 122; final static int LSHR = 123; final static int IUSHR = 124; final static int LUSHR = 125; final static int IAND = 126; final static int LAND = 127; final static int IOR = 128; final static int LOR = 129; final static int IXOR = 130; final static int LXOR = 131; final static int IINC = 132; final static int I2L = 133; final static int I2F = 134; final static int I2D = 135; final static int L2I = 136; final static int L2F = 137; final static int L2D = 138; final static int F2I = 139; final static int F2L = 140; final static int F2D = 141; final static int D2I = 142; final static int D2L = 143; final static int D2F = 144; final static int INT2BYTE = 145; final static int INT2CHAR = 146; final static int INT2SHORT = 147; final static int LCMP = 148; final static int FCMPL = 149; final static int FCMPG = 150; final static int DCMPL = 151; final static int DCMPG = 152; final static int IFEQ = 153; final static int IFNE = 154; final static int IFLT = 155; final static int IFGE = 156; final static int IFGT = 157; final static int IFLE = 158; final static int IF_ICMPEQ = 159; final static int IF_ICMPNE = 160; final static int IF_ICMPLT = 161; final static int IF_ICMPGT = 163; final static int IF_ICMPLE = 164; final static int IF_ICMPGE = 162; final static int IF_ACMPEQ = 165; final static int IF_ACMPNE = 166; final static int GOTO = 167; final static int JSR = 168; final static int RET = 169; final static int TABLESWITCH = 170; final static int LOOKUPSWITCH = 171; final static int IRETURN = 172; final static int LRETURN = 173; final static int FRETURN = 174; final static int DRETURN = 175; final static int ARETURN = 176; final static int RETURN = 177; final static int INVOKEVIRTUAL = 182; final static int INVOKENONVIRTUAL = 183; final static int INVOKESTATIC = 184; final static int INVOKEINTERFACE = 185; final static int NEW = 187; final static int NEWARRAY = 188; final static int ANEWARRAY = 189; final static int ARRAYLENGTH = 190; final static int ATHROW = 191; final static int CHECKCAST = 192; final static int INSTANCEOF = 193; final static int MONITORENTER = 194; final static int MONITOREXIT = 195; final static int WIDE = 196; final static int MULTIANEWARRAY = 197; final static int IFNULL = 198; final static int IFNONNULL = 199; final static int GOTO_W = 200; final static int JSR_W = 201; final static int BREAKPOINT = 202; final static int RET_W = 209; } class RegisterPanel extends Panel { private LabeledRegister pcRegister; private LabeledRegister optopRegister; private LabeledRegister frameRegister; private LabeledRegister varsRegister; RegisterPanel() { setLayout(new BorderLayout(5, 5)); pcRegister = new LabeledRegister(StringTable.pc); optopRegister = new LabeledRegister(StringTable.optop); frameRegister = new LabeledRegister(StringTable.frame); varsRegister = new LabeledRegister(StringTable.vars); setBackground(SimData.registersAreaColor); Panel labeledRegisterPanel = new Panel(); labeledRegisterPanel.setLayout(new GridLayout(1, 4, 5, 5)); labeledRegisterPanel.add(pcRegister); labeledRegisterPanel.add(optopRegister); labeledRegisterPanel.add(frameRegister); labeledRegisterPanel.add(varsRegister); Label title = new Label(StringTable.Registers, Label.CENTER); title.setFont(new Font("Helvetica", Font.BOLD, 11)); add("West", title); add("Center", labeledRegisterPanel); } public void setPcRegister(int val) { pcRegister.setRegister(val); } public void setOptopRegister(int val) { optopRegister.setRegister(val); } public void setFrameRegister(int val) { frameRegister.setRegister(val); } public void setVarsRegister(int val) { varsRegister.setRegister(val); } public Insets insets() { // top, left, bottom, right return new Insets(5, 5, 5, 5); } } class RepeaterButton extends GrayButton { RepeaterButton(String label) { super(label); } } // SimData is like a personality module for the JVMSimulator. It contains all // the data that is unique to this particular simulation applet. class SimData { public final static String appletTitle = "THREE DIMENSIONAL ARRAY"; // stackSize, localsSize, and argsSize define the size of the stack for // one method, which each simulator executes. These three sizes vary for // each method and can be found for a method by running javap -v on the // class file. The execEnvSize is constant for every method and only // depends on how the JVM was implemented. static final int stackSize = 4; static final int localsSize = 4; static final int argsSize = 0; static final int execEnvSize = 4; // methodArea sizes are based on the length of the bytecode stream for // this method. static final int methodAreaMemorySectionSize = 60; static final int methodAreaMemoryLocationsVisibleCount = 13; // stack sizes are based on the sizes of each portion of the stack (local // variables, execution environment, and operands) that are defined above // for the method simulated by this applet. One is added to stackMemorySectionSize // because this JVM implementation has the optop register pointing to the // next available slot in the stack instead of the current top. This means // that when the operand stack is full, I still need one more slot to show // the location that is pointed to by the optop register, even though we // know nothing will ever be pushed there by this method. static final int stackMemorySectionSize = stackSize + localsSize + argsSize + execEnvSize + 1; static final int stackMemoryLocationsVisibleCount = stackMemorySectionSize; static final int frameOffset = (4 * localsSize) + (4 * argsSize); // 4 bytes for each local variable static final int optopOffset = frameOffset + (4 * execEnvSize); // 4 bytes for each loc var & exec env slot static int[] theProgram = { OpCode.ICONST_5, OpCode.ICONST_4, OpCode.ICONST_3, OpCode.MULTIANEWARRAY, (byte) 0x00, (byte) 0x02, (byte) 0x03, OpCode.ASTORE_0, OpCode.ICONST_0, OpCode.ISTORE_1, OpCode.GOTO, (byte) 0x00, (byte) 0x2c, OpCode.ICONST_0, OpCode.ISTORE_2, OpCode.GOTO, (byte) 0x00, (byte) 0x1f, OpCode.ICONST_0, OpCode.ISTORE_3, OpCode.GOTO, (byte) 0x00, (byte) 0x12, OpCode.ALOAD_0, OpCode.ILOAD_1, OpCode.AALOAD, OpCode.ILOAD_2, OpCode.AALOAD, OpCode.ILOAD_3, OpCode.ILOAD_1, OpCode.ILOAD_2, OpCode.IADD, OpCode.ILOAD_3, OpCode.IADD, OpCode.IASTORE, OpCode.IINC, (byte) 0x03, (byte) 0x01, OpCode.ILOAD_3, OpCode.ICONST_3, OpCode.IF_ICMPLT, (byte) 0xff, (byte) 0xef, OpCode.IINC, (byte) 0x02, (byte) 0x01, OpCode.ILOAD_2, OpCode.ICONST_4, OpCode.IF_ICMPLT, (byte) 0xff, (byte) 0xe2, OpCode.IINC, (byte) 0x01, (byte) 0x01, OpCode.ILOAD_1, OpCode.ICONST_5, OpCode.IF_ICMPLT, (byte) 0xff, (byte) 0xd5, OpCode.BREAKPOINT }; static String[] byteCodeMnemonics = { "iconst_5", "iconst_4", "iconst_3", "multianewarray 2 3", "", "", "", "astore_0", "iconst_0", "istore_1", "goto +44", "", "", "iconst_0", "istore_2", "goto +31", "", "", "iconst_0", "istore_3", "goto +18", "", "", "aload_0", "iload_1", "aaload", "iload_2", "aaload", "iload_3", "iload_1", "iload_2", "iadd", "iload_3", "iadd", "iastore", "iinc 3 1", "", "", "iload_3", "iconst_3", "if_icmplt -17", "", "", "iinc 2 1", "", "", "iload_2", "iconst_4", "if_icmplt -30", "", "", "iinc 1 1", "", "", "iload_1", "iconst_5", "if_icmplt -43", "", "", "breakpoint" }; static final Color appletBackgroundColor = Color.blue; static final Color registersAreaColor = Color.cyan; static final Color stackAreaColor = Color.cyan; static final Color methodAreaColor = Color.cyan; static final Color titleColor = Color.green; static final Color explanationLabel = Color.green; } // StackMemorySection is just used for the stack in this applet. This implements // the functionality of the stack and has nothing to do with the UI. A separate // array is used for primitive types and object references because there is no // way to convert between an object reference and a primitive type (that would // be a pointer.) The int array (memory) is used to store types boolean, byte, // char, short, int, long, float, and double. The object reference array (objectMemory) // is used to store references to objects and arrays. class StackMemorySection { private int[] memory; private Object[] objectMemory; private int baseAddress; private String[] logicalValueString; StackMemorySection(int base, int size) { baseAddress = base; memory = new int[size]; objectMemory = new Object[size]; logicalValueString = new String[size]; for (int i = 0; i < size; ++i) { memory[i] = 0; logicalValueString[i] = new String(); } } public int getAtAddress(int address) { return memory[(address - baseAddress) / 4]; } public Object getObjectAtAddress(int address) { return objectMemory[(address - baseAddress) / 4]; } public String getLogicalValueAtAddress(int address) { return logicalValueString[(address - baseAddress) / 4]; } public void setAtAddress(int address, int value) { memory[(address - baseAddress) / 4] = value; } public void setObjectAtAddress(int address, Object value) { objectMemory[(address - baseAddress) / 4] = value; } public void setLogicalValueAtAddress(int address, String s) { logicalValueString[(address - baseAddress) / 4] = s; } } // StackMemoryView is just used for the stack in this applet. It implements the // UI of the stack. class StackMemoryView extends Panel { private final int memoryLocationsVisibleCount = SimData.stackMemoryLocationsVisibleCount; private Label[] pointer = new Label[memoryLocationsVisibleCount]; private Label[] address = new Label[memoryLocationsVisibleCount]; private Label[] wordValue = new Label[memoryLocationsVisibleCount]; private Label[] logicalValue = new Label[memoryLocationsVisibleCount]; StackMemoryView() { int[] hComponentCellWidths = new int[4]; hComponentCellWidths[0] = 2; hComponentCellWidths[1] = 2; hComponentCellWidths[2] = 2; hComponentCellWidths[3] = 3; setLayout(new GridSnapLayout(memoryLocationsVisibleCount, 9, hComponentCellWidths)); setBackground(Color.lightGray); Font plainFont = new Font("TimesRoman", Font.PLAIN, 11); setFont(plainFont); Font italicFont = new Font("TimesRoman", Font.ITALIC, 11); for (int i = memoryLocationsVisibleCount - 1; i >= 0; --i) { pointer[i] = new Label("", Label.RIGHT); pointer[i].setFont(italicFont); add(pointer[i]); address[i] = new Label("", Label.CENTER); add(address[i]); wordValue[i] = new Label("", Label.CENTER); add(wordValue[i]); logicalValue[i] = new Label("", Label.CENTER); add(logicalValue[i]); } } public void setAt(int i, int addressValue, int value, String logicalValueString) { HexString addressValueString = new HexString(addressValue, 8); HexString wordValueString = new HexString(value, 8); address[memoryLocationsVisibleCount - 1 - i].setText(addressValueString.getString()); wordValue[memoryLocationsVisibleCount - 1 - i].setText(wordValueString.getString()); logicalValue[memoryLocationsVisibleCount - 1 - i].setText(logicalValueString); } public void update(StackMemorySection memorySection, int initialAddress) { for (int i = 0; i < memoryLocationsVisibleCount; ++i) { int theWord = memorySection.getAtAddress(initialAddress + (i * 4)); String logicalValue = memorySection.getLogicalValueAtAddress(initialAddress + (i * 4)); setAt(i, initialAddress + (i * 4), theWord, logicalValue); } } public void clearPointers() { for (int i = 0; i < memoryLocationsVisibleCount; ++i) { pointer[i].setText(""); } } public void updatePointer(int i, String pointerString) { pointer[memoryLocationsVisibleCount - 1 - i].setText(pointerString); } public Insets insets() { // top, left, bottom, right return new Insets(0, 0, 0, 0); } } class StackMemoryViewTitlePanel extends Panel { StackMemoryViewTitlePanel() { int[] hComponentCellWidths = new int[4]; hComponentCellWidths[0] = 2; hComponentCellWidths[1] = 2; hComponentCellWidths[2] = 2; hComponentCellWidths[3] = 3; setLayout(new GridSnapLayout(1, 9, hComponentCellWidths)); setFont(new Font("Helvetica", Font.ITALIC, 11)); add(new Label("", Label.CENTER)); add(new Label(StringTable.address, Label.CENTER)); add(new Label(StringTable.hexValue, Label.CENTER)); add(new Label(StringTable.value, Label.CENTER)); } public Insets insets() { // top, left, bottom, right return new Insets(0, 0, 0, 0); } } class StackMemoryViewWithTitles extends Panel { private StackMemoryView memoryView = new StackMemoryView(); StackMemoryViewWithTitles() { setLayout(new BorderLayout()); add("North", new StackMemoryViewTitlePanel()); add("Center", memoryView); } public StackMemoryView getMemoryViewReference() { return memoryView; } public Insets insets() { // top, left, bottom, right return new Insets(0, 0, 0, 0); } } class StackPanel extends Panel { private Label title; private StackMemoryViewWithTitles memoryView = new StackMemoryViewWithTitles(); StackPanel() { setLayout(new BorderLayout()); title = new Label("Stack", Label.CENTER); title.setFont(new Font("Helvetica", Font.BOLD, 11)); add("North", title); add("Center", memoryView); } public StackMemoryView getMemoryViewReference() { return memoryView.getMemoryViewReference(); } public Insets insets() { return new Insets(5, 5, 5, 5); } } class StringTable { public final static String step = "Step"; public final static String reset = "Reset"; public final static String run = "Run"; public final static String stop = "Stop"; public final static String operand = "operand"; public final static String execEnv = "exec env"; public final static String localVars = "local vars"; public final static String varsPointer = "vars >"; public final static String framePointer = "frame >"; public final static String optopPointer = "optop >"; public final static String address = "address"; public final static String bytecodes = "bytecode"; public final static String mnemonics = "mnemonic"; public final static String hexValue = "hex value"; public final static String value = "value"; public final static String Registers = "Registers"; public final static String pc = "pc"; public final static String optop = "optop"; public final static String frame = "frame"; public final static String vars = "vars"; public final static String objectReference = "object"; public final static String objectRefHexRepresentation = "OBJ REF"; public final static String aaloadText = "aaload will pop an index and array reference and push the object ref at that index of the array."; public final static String aload_0Text = "aload_0 will push the object ref at local variable 0 onto the stack."; public final static String astore_0Text = "astore_0 will pop the object ref off the top of the stack and store it in local variable 0."; public final static String bipushText = "bipush will expand the next byte to an int and push it onto the stack."; public final static String breakpointText = "breakpoint will stop the simulation."; public final static String fconst_0Text = "fconst_0 will push float 0.0 onto the stack."; public final static String fconst_2Text = "fconst_2 will push float 2.0 onto the stack."; public final static String fload_0Text = "fload_0 will push the float at local variable 0 onto the stack."; public final static String fmulText = "fmul will pop two floats, multiply them, and push the result."; public final static String fstore_0Text = "fstore_0 will pop the float off the top of the stack and store it in local variable 0."; public final static String fsubText = "fsub will pop two floats, subtract them, and push the result."; public final static String gotoText = "goto will cause a jump to the specified offset."; public final static String iaddText = "iadd will pop the top two ints off the stack, add them, and push the result back onto the stack."; public final static String iandText = "iand will pop the top two ints off the stack, bitwise-and them, and push the result back onto the stack."; public final static String iastoreText = "iastore will pop an int value, an index, and an arrayref and assign arrayref[index] = value."; public final static String iconst_m1Text = "iconst_m1 will push -1 onto the stack."; public final static String iconst_0Text = "iconst_0 will push 0 onto the stack."; public final static String iconst_1Text = "iconst_1 will push 1 onto the stack."; public final static String iconst_2Text = "iconst_2 will push 2 onto the stack."; public final static String iconst_3Text = "iconst_3 will push 3 onto the stack."; public final static String iconst_4Text = "iconst_4 will push 4 onto the stack."; public final static String iconst_5Text = "iconst_5 will push 5 onto the stack."; public final static String if_icmpltText = "if_icmplt will branch if the next to topmost int is less than the topmost int."; public final static String ifneText = "ifne will branch if the topmost int is not equal to zero."; public final static String iincText = "iinc will increment the specified local variable by the specified amount."; public final static String iload_0Text = "iload_0 will push the integer at local variable 0 onto the stack."; public final static String iload_1Text = "iload_1 will push the integer at local variable 1 onto the stack."; public final static String iload_2Text = "iload_2 will push the integer at local variable 2 onto the stack."; public final static String iload_3Text = "iload_3 will push the integer at local variable 3 onto the stack."; public final static String imulText = "imul will pop two integers, multiply them, and push the result."; public final static String int2byteText = "int2byte will convert the topmost int on the stack to a value valid for the byte type."; public final static String iorText = "ior will pop the top two ints off the stack, bitwise-or them, and push the result back onto the stack."; public final static String ishlText = "ishl will shift the next to topmost int to the left by amount indicated by topmost int."; public final static String istore_0Text = "istore_0 will pop the integer off the top of the stack and store it in local variable 0."; public final static String istore_1Text = "istore_1 will pop the integer off the top of the stack and store it in local variable 1."; public final static String istore_2Text = "istore_2 will pop the integer off the top of the stack and store it in local variable 2."; public final static String istore_3Text = "istore_3 will pop the integer off the top of the stack and store it in local variable 3."; public final static String ixorText = "ixor will pop the top two ints off the stack, biwise-xor them, and push the result back onto the stack."; public final static String multianewarrayText = "multianewarray will allocate memory for a new multi-dim array and push reference."; } class ThreeParts extends Panel { private RegisterPanel registers; private TwoParts twoParts; ThreeParts(int methodAreaMemorySectionSize) { setLayout(new BorderLayout(5, 5)); registers = new RegisterPanel(); twoParts = new TwoParts(methodAreaMemorySectionSize); add("North", registers); add("Center", twoParts); } StackMemoryView getStackMemoryViewReference() { return twoParts.getStackMemoryViewReference(); } MemoryView getMethodAreaMemoryViewReference() { return twoParts.getMethodAreaMemoryViewReference(); } RegisterPanel getRegisterPanel() { return registers; } } // TwoParts is the panel that contains the Stack and Method Area panels class TwoParts extends Panel { private StackPanel stack; private MethodAreaPanel methodArea; TwoParts(int methodAreaMemorySectionSize) { setLayout(new GridLayout(1, 2, 5, 5)); stack = new StackPanel(); methodArea = new MethodAreaPanel(methodAreaMemorySectionSize); stack.setBackground(SimData.stackAreaColor); methodArea.setBackground(SimData.methodAreaColor); add(stack); add(methodArea); } public StackMemoryView getStackMemoryViewReference() { return stack.getMemoryViewReference(); } public MemoryView getMethodAreaMemoryViewReference() { return methodArea.getMemoryViewReference(); } // top, left, bottom, right // Want a 10 pixel separation between the twoparts and the register panel // above and the control panel below. public Insets insets() { return new Insets(0, 0, 0, 0); } }