- * This command facilitates method invocation by creating a new stack frame, transferring arguments
- * from the operand stack to the callee's local variable store, and jumping to the specified target address.
- *
+ * Implements the CALL instruction for the virtual machine.
*
- *
Parses the target address and the number of arguments from the instruction parameters.
- *
Validates the operands and checks for correct argument count.
- *
Builds a new local variable store for the callee and transfers arguments from the operand stack
- * (left-to-right order, where the top of the stack is the last argument).
- *
Pushes a new stack frame onto the call stack, saving the return address and local variables.
- *
Jumps to the specified target address to begin execution of the callee function.
- *
If any error occurs (e.g., malformed operands, stack underflow), an exception is thrown.
+ *
{@code CALL } — static/known target
*
+ * The arguments are pushed to the operand stack from left to right before the call,
+ * so the last argument is on the top of the stack when this instruction executes.
+ *
*/
public class CallCommand implements Command {
/**
- * Executes the CALL instruction, initiating a subroutine/function call within the virtual machine.
+ * Executes the CALL instruction.
+ *
*
- * This method handles the creation of a new stack frame for the callee, argument passing,
- * and control transfer to the target function address.
+ * The CALL instruction transfers control to a subroutine at the given address,
+ * passing arguments from the operand stack to a new local variable store.
*
*
- * @param parts The instruction parameters. Must include:
- *
- *
{@code parts[0]}: The "CALL" operator.
- *
{@code parts[1]}: The target address of the callee function.
- *
{@code parts[2]}: The number of arguments to pass.
- *
- * @param currentPC The current program counter, used to record the return address for after the call.
- * @param operandStack The operand stack manager. Arguments are popped from this stack.
- * @param callerLVS The local variable store of the caller function (not directly modified here).
- * @param callStack The virtual machine's call stack manager, used to push the new stack frame.
- * @return The new program counter value, which is the address of the callee function (i.e., jump target).
- * The VM should transfer control to this address after setting up the call frame.
- * @throws IllegalArgumentException If the instruction parameters are malformed or missing.
- * @throws IllegalStateException If the operand stack does not contain enough arguments.
+ * @param parts the parts of the instruction; expects: [CALL, targetAddress, nArgs]
+ * @param currentPC the current program counter (PC)
+ * @param operandStack the VM's operand stack (used for passing arguments)
+ * @param ignoredCallerLVS the local variable store of the caller (unused in this implementation)
+ * @param callStack the VM's call stack (where new frames are pushed)
+ * @return the target address to transfer control to (i.e., jump to)
+ * @throws IllegalStateException if the instruction format is invalid
*/
@Override
public int execute(String[] parts,
int currentPC,
OperandStack operandStack,
- LocalVariableStore callerLVS,
+ LocalVariableStore /* caller LVT, unused */ ignoredCallerLVS,
CallStack callStack) {
- if (parts.length < 3)
- throw new IllegalArgumentException("CALL: need ");
+ // 1. Validate instruction arguments.
+ if (parts.length < 3) {
+ throw new IllegalStateException("CALL requires 2 operands: target and nArgs");
+ }
- /* ----------- Parse target address / method signature ----------- */
- final String targetToken = parts[1]; // Can be a numeric address or "@Class::method"
- final int nArgs = Integer.parseInt(parts[2]);
+ final String rawTarget = parts[1].trim();
+ final int nArgs = Integer.parseInt(parts[2].trim());
- /* ----------- Build callee's local variable store ----------- */
- LocalVariableStore calleeLVS = new LocalVariableStore();
- for (int slot = nArgs - 1; slot >= 0; slot--) {
- if (operandStack.isEmpty())
- throw new IllegalStateException("CALL: operand stack underflow");
- calleeLVS.setVariable(slot, operandStack.pop());
+ // 2. Pop arguments from the operand stack and restore left-to-right order.
+ // Arguments are pushed left-to-right, so we pop them and reverse into the args array.
+ final Object[] args = new Object[nArgs];
+ for (int i = nArgs - 1; i >= 0; i--) {
+ args[i] = operandStack.pop();
}
- /* ----------- Handle virtual call ----------- */
- int targetAddr;
- if (targetToken.startsWith("@")) { // "@Class::method" convention indicates a virtual call
- Object thisRef = calleeLVS.getVariable(0); // By convention, slot-0 stores "this"
- if (!(thisRef instanceof Instance inst))
- throw new IllegalStateException("VCALL: slot-0 is not an object reference");
+ // 3. Resolve the target address for the subroutine (currently supports only static calls).
+ int targetAddr = Integer.parseInt(rawTarget);
+ String methodNameForCtx = "subroutine@" + targetAddr;
+ print("\nCALL -> " + targetAddr);
- String methodSig = targetToken.substring(1); // Remove '@'
- targetAddr = inst.vtable().lookup(methodSig);
- } else {
- /* Direct/static call (numeric address) */
- targetAddr = Integer.parseInt(targetToken);
+ // 4. Build the callee's local variable store and copy arguments into it.
+ LocalVariableStore calleeLVS = new LocalVariableStore();
+ for (int i = 0; i < nArgs; i++) {
+ calleeLVS.setVariable(i, args[i]);
}
- /* ----------- Push new stack frame and jump ----------- */
+ // 5. Create a new stack frame for the callee and push it onto the call stack.
+ // The return address is set to the next instruction after CALL.
StackFrame newFrame = new StackFrame(
currentPC + 1,
calleeLVS,
- new MethodContext("subroutine@" + targetAddr, null)
+ new MethodContext(methodNameForCtx, null) // Don't log full args to avoid heavy logs
);
callStack.pushFrame(newFrame);
- print("\nCalling function at address: " + targetAddr);
- return targetAddr; // jump
+ // 6. Transfer control to the target address (subroutine entry point).
+ return targetAddr;
}
}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/flow/control/JumpCommand.java b/src/main/java/org/jcnc/snow/vm/commands/flow/control/JumpCommand.java
index 7f538908754c9b04511ce3b1e4a3e847bcebd012..b570ae41201e8b5d3d2c5e8b6234d4ec624392a8 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/flow/control/JumpCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/flow/control/JumpCommand.java
@@ -1,10 +1,10 @@
package org.jcnc.snow.vm.commands.flow.control;
import org.jcnc.snow.vm.interfaces.Command;
-import org.jcnc.snow.vm.utils.LoggingUtils;
import org.jcnc.snow.vm.module.CallStack;
import org.jcnc.snow.vm.module.LocalVariableStore;
import org.jcnc.snow.vm.module.OperandStack;
+import org.jcnc.snow.vm.utils.LoggingUtils;
/**
* The JumpCommand class implements the {@link Command} interface and represents an unconditional jump instruction in the virtual machine.
diff --git a/src/main/java/org/jcnc/snow/vm/commands/ref/control/RLoadCommand.java b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RLoadCommand.java
index d7e314d7426fb9117a911ed98ebfcfe56658b8b6..85693ac4dfed8536034bb7ea63fb8a8115d07549 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/ref/control/RLoadCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RLoadCommand.java
@@ -33,10 +33,10 @@ public final class RLoadCommand implements Command {
* Executes the {@code R_LOAD} instruction, loading a reference from the local variable table and pushing it onto the operand stack.
*
* @param parts The instruction parameters. {@code parts[0]} is the operator ("R_LOAD"), {@code parts[1]} is the slot index.
- * @param pc The current program counter value, indicating the instruction address being executed.
+ * @param pc The current program counter value, indicating the instruction address being executed.
* @param stack The operand stack manager. The loaded reference will be pushed onto this stack.
- * @param lvs The local variable store. (Not used directly, as this command uses the store from the current stack frame.)
- * @param cs The call stack manager. The reference will be loaded from the local variable store of the top stack frame.
+ * @param lvs The local variable store. (Not used directly, as this command uses the store from the current stack frame.)
+ * @param cs The call stack manager. The reference will be loaded from the local variable store of the top stack frame.
* @return The next program counter value ({@code pc + 1}), pointing to the next instruction.
* @throws NumberFormatException if the slot parameter cannot be parsed as an integer.
*/
diff --git a/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
index 228f957dbdf9482d7dca0f479b562a0b3426e153..741fc928cd2f1aa05c1fa20f72207526707b7fea 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
@@ -38,107 +38,9 @@ import java.util.List;
*/
public class RPushCommand implements Command {
- /**
- * Executes the {@code R_PUSH} instruction. Parses the given literal parameter and pushes it onto the operand stack.
- *
Quoted string literals (e.g., {@code "abc\n"}), parsed with escape sequence support
- *
Unquoted raw strings, numbers, and atoms
- *
- *
- * @param parts The instruction split into parts (opcode and arguments)
- * @param pc The current program counter
- * @param stack The operand stack to push the value onto
- * @param local The local variable store (unused)
- * @param callStack The call stack (unused)
- * @return The next program counter (pc + 1)
- * @throws IllegalStateException if the R_PUSH parameter is missing or parsing fails
- */
- @Override
- public int execute(String[] parts, int pc, OperandStack stack, LocalVariableStore local, CallStack callStack) {
- if (parts.length < 2)
- throw new IllegalStateException("R_PUSH missing parameter");
-
- // Join all arguments into a complete literal string
- StringBuilder sb = new StringBuilder();
- for (int i = 1; i < parts.length; i++) {
- if (i > 1) sb.append(' ');
- sb.append(parts[i]);
- }
- String literal = sb.toString().trim();
-
- // Handle array literal
- if (literal.startsWith("[") && literal.endsWith("]")) {
- Object parsed = parseValue(new Cursor(literal));
- if (!(parsed instanceof List> list)) {
- stack.push(parsed);
- } else {
- stack.push(deepMutable(list));
- }
- }
- // String literal with quotes and escapes
- else if (literal.length() >= 2 && literal.startsWith("\"") && literal.endsWith("\"")) {
- String decoded = parseQuoted(new Cursor(literal));
- stack.push(decoded);
- }
- // Raw atom or string
- else {
- stack.push(literal);
- }
- return pc + 1;
- }
-
- /**
- * Utility class for string parsing, used by the array and string literal parsers.
- */
- static class Cursor {
- final String s;
- int i;
-
- /**
- * Constructs a cursor over the provided string.
- * @param s the input string to parse
- */
- Cursor(String s) { this.s = s; this.i = 0; }
-
- /**
- * Advances the cursor by one character.
- */
- void skip() { i++; }
-
- /**
- * Returns true if the cursor has reached the end of the string.
- * @return true if end of string
- */
- boolean end() { return i >= s.length(); }
-
- /**
- * Returns the current character at the cursor position.
- * @return the current character
- */
- char ch() { return s.charAt(i); }
- }
-
- /**
- * Parses a value from the current cursor position.
- * Supports arrays, quoted strings, or atoms.
- *
- * @param c the parsing cursor
- * @return the parsed object (List, String, Number, Boolean, or String fallback)
- */
- Object parseValue(Cursor c) {
- skipWs(c);
- if (c.end()) return "";
- char ch = c.ch();
- if (ch == '[') return parseArray(c);
- if (ch == '\"') return parseQuoted(c);
- return parseAtom(c);
- }
-
/**
* Skips whitespace characters at the cursor.
+ *
* @param c the parsing cursor
*/
private static void skipWs(Cursor c) {
@@ -149,33 +51,6 @@ public class RPushCommand implements Command {
}
}
- /**
- * Parses an array literal of the form [elem1, elem2, ...] (may be nested).
- * Recursively parses elements using {@link #parseValue(Cursor)}.
- *
- * @param c the parsing cursor
- * @return a List of parsed elements
- */
- private Object parseArray(Cursor c) {
- c.skip(); // skip '['
- List
- *
+ *
* Root-frame contract:
*
* A root stack frame is pushed once via
@@ -31,10 +31,14 @@ public class VirtualMachineEngine {
/* ---------- Constants ---------- */
- /** Sentinel PC value that signals “terminate program gracefully”. */
+ /**
+ * Sentinel PC value that signals “terminate program gracefully”.
+ */
private static final int PROGRAM_END = Integer.MAX_VALUE;
- /** Sentinel returned by {@link CommandExecutionHandler#handle} to halt immediately. */
+ /**
+ * Sentinel returned by {@link CommandExecutionHandler#handle} to halt immediately.
+ */
private static final int HALT = -1;
/* ---------- Runtime state ---------- */
@@ -52,8 +56,8 @@ public class VirtualMachineEngine {
* Builds a VM engine with fresh runtime structures.
*/
public VirtualMachineEngine() {
- this.operandStack = new OperandStack();
- this.callStack = new CallStack();
+ this.operandStack = new OperandStack();
+ this.callStack = new CallStack();
this.localVariableStore = new LocalVariableStore(); // shared with root frame
this.commandExecutionHandler =
new CommandExecutionHandler(operandStack, localVariableStore, callStack);
@@ -61,7 +65,9 @@ public class VirtualMachineEngine {
}
/* package-private accessor used by debug helpers */
- CallStack getCallStack() { return callStack; }
+ CallStack getCallStack() {
+ return callStack;
+ }
/* ---------- Execution ---------- */
@@ -144,19 +150,23 @@ public class VirtualMachineEngine {
/* The returnAddress of the root frame must be PROGRAM_END so that the main loop can exit correctly when the root function RETs.*/
MethodContext rootCtx = new MethodContext("root", null);
- StackFrame rootFrame = new StackFrame(PROGRAM_END, localVariableStore, rootCtx);
+ StackFrame rootFrame = new StackFrame(PROGRAM_END, localVariableStore, rootCtx);
callStack.pushFrame(rootFrame);
}
/* ---------- Debug helpers ---------- */
- /** Prints operand stack + call-stack snapshot. */
+ /**
+ * Prints operand stack + call-stack snapshot.
+ */
public void printStack() {
operandStack.printOperandStack();
callStack.printCallStack();
}
- /** Prints the local-variable table of the current top frame. */
+ /**
+ * Prints the local-variable table of the current top frame.
+ */
public void printLocalVariables() {
if (callStack.isEmpty()) {
System.out.println("Local variable table is empty");
@@ -167,7 +177,9 @@ public class VirtualMachineEngine {
/* ---------- Utility ---------- */
- /** Parses textual opcode to integer. */
+ /**
+ * Parses textual opcode to integer.
+ */
private int parseOpCode(String opCodeStr) {
try {
return Integer.parseInt(opCodeStr);
diff --git a/src/main/java/org/jcnc/snow/vm/execution/CommandLoader.java b/src/main/java/org/jcnc/snow/vm/execution/CommandLoader.java
index 70d6c9b0f09550994bf350ff939f7587a38c1f12..afe4a31e59a18f4884fe14e6fc0e967bae05b846 100644
--- a/src/main/java/org/jcnc/snow/vm/execution/CommandLoader.java
+++ b/src/main/java/org/jcnc/snow/vm/execution/CommandLoader.java
@@ -1,9 +1,8 @@
package org.jcnc.snow.vm.execution;
-
-import org.jcnc.snow.vm.utils.LoggingUtils;
import org.jcnc.snow.vm.io.FileIOUtils;
+import org.jcnc.snow.vm.utils.LoggingUtils;
import java.util.List;
diff --git a/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java b/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java
index fc4a3edd94f215baaa9e57a05b7c685f5b05fe34..a9bc7a32603db25c7a48b505c2d3f1c0c49e2ec3 100644
--- a/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java
+++ b/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java
@@ -1,54 +1,24 @@
package org.jcnc.snow.vm.factories;
-import org.jcnc.snow.vm.commands.system.control.SyscallCommand;
-import org.jcnc.snow.vm.commands.type.control.byte8.*;
-import org.jcnc.snow.vm.commands.type.control.double64.*;
-import org.jcnc.snow.vm.commands.type.control.float32.*;
-import org.jcnc.snow.vm.commands.type.control.int32.*;
-import org.jcnc.snow.vm.commands.type.control.long64.*;
+import org.jcnc.snow.vm.commands.flow.control.CallCommand;
+import org.jcnc.snow.vm.commands.flow.control.JumpCommand;
+import org.jcnc.snow.vm.commands.flow.control.RetCommand;
import org.jcnc.snow.vm.commands.ref.control.RLoadCommand;
import org.jcnc.snow.vm.commands.ref.control.RPushCommand;
import org.jcnc.snow.vm.commands.ref.control.RStoreCommand;
-import org.jcnc.snow.vm.commands.type.control.short16.*;
-import org.jcnc.snow.vm.commands.type.control.byte8.BAndCommand;
-import org.jcnc.snow.vm.commands.type.control.byte8.BOrCommand;
-import org.jcnc.snow.vm.commands.type.control.byte8.BXorCommand;
-import org.jcnc.snow.vm.commands.type.control.int32.IAndCommand;
-import org.jcnc.snow.vm.commands.type.control.int32.IOrCommand;
-import org.jcnc.snow.vm.commands.type.control.int32.IXorCommand;
-import org.jcnc.snow.vm.commands.type.control.long64.LAndCommand;
-import org.jcnc.snow.vm.commands.type.control.long64.LOrCommand;
-import org.jcnc.snow.vm.commands.type.control.long64.LXorCommand;
-import org.jcnc.snow.vm.commands.type.control.short16.SAndCommand;
-import org.jcnc.snow.vm.commands.type.control.short16.SOrCommand;
-import org.jcnc.snow.vm.commands.type.control.short16.SXorCommand;
-import org.jcnc.snow.vm.commands.flow.control.JumpCommand;
-import org.jcnc.snow.vm.commands.flow.control.CallCommand;
-import org.jcnc.snow.vm.commands.flow.control.RetCommand;
import org.jcnc.snow.vm.commands.register.control.MovCommand;
-import org.jcnc.snow.vm.commands.type.control.byte8.BLoadCommand;
-import org.jcnc.snow.vm.commands.type.control.byte8.BStoreCommand;
-import org.jcnc.snow.vm.commands.type.control.double64.DLoadCommand;
-import org.jcnc.snow.vm.commands.type.control.double64.DStoreCommand;
-import org.jcnc.snow.vm.commands.type.control.float32.FLoadCommand;
-import org.jcnc.snow.vm.commands.type.control.float32.FStoreCommand;
-import org.jcnc.snow.vm.commands.type.control.int32.ILoadCommand;
-import org.jcnc.snow.vm.commands.type.control.int32.IStoreCommand;
-import org.jcnc.snow.vm.commands.type.control.long64.LLoadCommand;
-import org.jcnc.snow.vm.commands.type.control.long64.LStoreCommand;
-import org.jcnc.snow.vm.commands.type.control.short16.SLoadCommand;
-import org.jcnc.snow.vm.commands.type.control.short16.SStoreCommand;
import org.jcnc.snow.vm.commands.stack.control.DupCommand;
import org.jcnc.snow.vm.commands.stack.control.PopCommand;
import org.jcnc.snow.vm.commands.stack.control.SwapCommand;
-import org.jcnc.snow.vm.commands.type.control.byte8.BPushCommand;
-import org.jcnc.snow.vm.commands.type.control.double64.DPushCommand;
-import org.jcnc.snow.vm.commands.type.control.float32.FPushCommand;
-import org.jcnc.snow.vm.commands.type.control.int32.IPushCommand;
-import org.jcnc.snow.vm.commands.type.control.long64.LPushCommand;
-import org.jcnc.snow.vm.commands.type.control.short16.SPushCommand;
-import org.jcnc.snow.vm.commands.type.conversion.*;
import org.jcnc.snow.vm.commands.system.control.HaltCommand;
+import org.jcnc.snow.vm.commands.system.control.SyscallCommand;
+import org.jcnc.snow.vm.commands.type.control.byte8.*;
+import org.jcnc.snow.vm.commands.type.control.double64.*;
+import org.jcnc.snow.vm.commands.type.control.float32.*;
+import org.jcnc.snow.vm.commands.type.control.int32.*;
+import org.jcnc.snow.vm.commands.type.control.long64.*;
+import org.jcnc.snow.vm.commands.type.control.short16.*;
+import org.jcnc.snow.vm.commands.type.conversion.*;
import org.jcnc.snow.vm.engine.VMOpCode;
import org.jcnc.snow.vm.interfaces.Command;
@@ -60,7 +30,9 @@ import java.util.Optional;
*
This class uses an array for fast, constant-time access to corresponding command instances.
*/
public class CommandFactory {
- /** Complete command table. 0x0000 – 0x04FF (inclusive). */
+ /**
+ * Complete command table. 0x0000 – 0x04FF (inclusive).
+ */
private static final Command[] COMMANDS = new Command[0x0500];
static {
@@ -68,146 +40,146 @@ public class CommandFactory {
// region Type Control (0x0000-0x00BF)
// region Byte8 (0x0000-0x001F)
- COMMANDS[VMOpCode.B_ADD] = new BAddCommand();
- COMMANDS[VMOpCode.B_SUB] = new BSubCommand();
- COMMANDS[VMOpCode.B_MUL] = new BMulCommand();
- COMMANDS[VMOpCode.B_DIV] = new BDivCommand();
- COMMANDS[VMOpCode.B_MOD] = new BModCommand();
- COMMANDS[VMOpCode.B_NEG] = new BNegCommand();
- COMMANDS[VMOpCode.B_INC] = new BIncCommand();
-
- COMMANDS[VMOpCode.B_AND] = new BAndCommand();
- COMMANDS[VMOpCode.B_OR] = new BOrCommand();
- COMMANDS[VMOpCode.B_XOR] = new BXorCommand();
-
- COMMANDS[VMOpCode.B_PUSH] = new BPushCommand();
- COMMANDS[VMOpCode.B_LOAD] = new BLoadCommand();
+ COMMANDS[VMOpCode.B_ADD] = new BAddCommand();
+ COMMANDS[VMOpCode.B_SUB] = new BSubCommand();
+ COMMANDS[VMOpCode.B_MUL] = new BMulCommand();
+ COMMANDS[VMOpCode.B_DIV] = new BDivCommand();
+ COMMANDS[VMOpCode.B_MOD] = new BModCommand();
+ COMMANDS[VMOpCode.B_NEG] = new BNegCommand();
+ COMMANDS[VMOpCode.B_INC] = new BIncCommand();
+
+ COMMANDS[VMOpCode.B_AND] = new BAndCommand();
+ COMMANDS[VMOpCode.B_OR] = new BOrCommand();
+ COMMANDS[VMOpCode.B_XOR] = new BXorCommand();
+
+ COMMANDS[VMOpCode.B_PUSH] = new BPushCommand();
+ COMMANDS[VMOpCode.B_LOAD] = new BLoadCommand();
COMMANDS[VMOpCode.B_STORE] = new BStoreCommand();
- COMMANDS[VMOpCode.B_CE] = new BCECommand();
- COMMANDS[VMOpCode.B_CNE] = new BCNECommand();
- COMMANDS[VMOpCode.B_CG] = new BCGCommand();
- COMMANDS[VMOpCode.B_CGE] = new BCGECommand();
- COMMANDS[VMOpCode.B_CL] = new BCLCommand();
- COMMANDS[VMOpCode.B_CLE] = new BCLECommand();
+ COMMANDS[VMOpCode.B_CE] = new BCECommand();
+ COMMANDS[VMOpCode.B_CNE] = new BCNECommand();
+ COMMANDS[VMOpCode.B_CG] = new BCGCommand();
+ COMMANDS[VMOpCode.B_CGE] = new BCGECommand();
+ COMMANDS[VMOpCode.B_CL] = new BCLCommand();
+ COMMANDS[VMOpCode.B_CLE] = new BCLECommand();
// endregion
// region Short16 (0x0020-0x003F)
- COMMANDS[VMOpCode.S_ADD] = new SAddCommand();
- COMMANDS[VMOpCode.S_SUB] = new SSubCommand();
- COMMANDS[VMOpCode.S_MUL] = new SMulCommand();
- COMMANDS[VMOpCode.S_DIV] = new SDivCommand();
- COMMANDS[VMOpCode.S_MOD] = new SModCommand();
- COMMANDS[VMOpCode.S_NEG] = new SNegCommand();
- COMMANDS[VMOpCode.S_INC] = new SIncCommand();
-
- COMMANDS[VMOpCode.S_AND] = new SAndCommand();
- COMMANDS[VMOpCode.S_OR] = new SOrCommand();
- COMMANDS[VMOpCode.S_XOR] = new SXorCommand();
-
- COMMANDS[VMOpCode.S_PUSH] = new SPushCommand();
- COMMANDS[VMOpCode.S_LOAD] = new SLoadCommand();
+ COMMANDS[VMOpCode.S_ADD] = new SAddCommand();
+ COMMANDS[VMOpCode.S_SUB] = new SSubCommand();
+ COMMANDS[VMOpCode.S_MUL] = new SMulCommand();
+ COMMANDS[VMOpCode.S_DIV] = new SDivCommand();
+ COMMANDS[VMOpCode.S_MOD] = new SModCommand();
+ COMMANDS[VMOpCode.S_NEG] = new SNegCommand();
+ COMMANDS[VMOpCode.S_INC] = new SIncCommand();
+
+ COMMANDS[VMOpCode.S_AND] = new SAndCommand();
+ COMMANDS[VMOpCode.S_OR] = new SOrCommand();
+ COMMANDS[VMOpCode.S_XOR] = new SXorCommand();
+
+ COMMANDS[VMOpCode.S_PUSH] = new SPushCommand();
+ COMMANDS[VMOpCode.S_LOAD] = new SLoadCommand();
COMMANDS[VMOpCode.S_STORE] = new SStoreCommand();
- COMMANDS[VMOpCode.S_CE] = new SCECommand();
- COMMANDS[VMOpCode.S_CNE] = new SCNECommand();
- COMMANDS[VMOpCode.S_CG] = new SCGCommand();
- COMMANDS[VMOpCode.S_CGE] = new SCGECommand();
- COMMANDS[VMOpCode.S_CL] = new SCLCommand();
- COMMANDS[VMOpCode.S_CLE] = new SCLECommand();
+ COMMANDS[VMOpCode.S_CE] = new SCECommand();
+ COMMANDS[VMOpCode.S_CNE] = new SCNECommand();
+ COMMANDS[VMOpCode.S_CG] = new SCGCommand();
+ COMMANDS[VMOpCode.S_CGE] = new SCGECommand();
+ COMMANDS[VMOpCode.S_CL] = new SCLCommand();
+ COMMANDS[VMOpCode.S_CLE] = new SCLECommand();
// endregion
// region Int32 (0x0040-0x005F)
- COMMANDS[VMOpCode.I_ADD] = new IAddCommand();
- COMMANDS[VMOpCode.I_SUB] = new ISubCommand();
- COMMANDS[VMOpCode.I_MUL] = new IMulCommand();
- COMMANDS[VMOpCode.I_DIV] = new IDivCommand();
- COMMANDS[VMOpCode.I_MOD] = new IModCommand();
- COMMANDS[VMOpCode.I_NEG] = new INegCommand();
- COMMANDS[VMOpCode.I_INC] = new IIncCommand();
-
- COMMANDS[VMOpCode.I_AND] = new IAndCommand();
- COMMANDS[VMOpCode.I_OR] = new IOrCommand();
- COMMANDS[VMOpCode.I_XOR] = new IXorCommand();
-
- COMMANDS[VMOpCode.I_PUSH] = new IPushCommand();
- COMMANDS[VMOpCode.I_LOAD] = new ILoadCommand();
+ COMMANDS[VMOpCode.I_ADD] = new IAddCommand();
+ COMMANDS[VMOpCode.I_SUB] = new ISubCommand();
+ COMMANDS[VMOpCode.I_MUL] = new IMulCommand();
+ COMMANDS[VMOpCode.I_DIV] = new IDivCommand();
+ COMMANDS[VMOpCode.I_MOD] = new IModCommand();
+ COMMANDS[VMOpCode.I_NEG] = new INegCommand();
+ COMMANDS[VMOpCode.I_INC] = new IIncCommand();
+
+ COMMANDS[VMOpCode.I_AND] = new IAndCommand();
+ COMMANDS[VMOpCode.I_OR] = new IOrCommand();
+ COMMANDS[VMOpCode.I_XOR] = new IXorCommand();
+
+ COMMANDS[VMOpCode.I_PUSH] = new IPushCommand();
+ COMMANDS[VMOpCode.I_LOAD] = new ILoadCommand();
COMMANDS[VMOpCode.I_STORE] = new IStoreCommand();
- COMMANDS[VMOpCode.I_CE] = new ICECommand();
- COMMANDS[VMOpCode.I_CNE] = new ICNECommand();
- COMMANDS[VMOpCode.I_CG] = new ICGCommand();
- COMMANDS[VMOpCode.I_CGE] = new ICGECommand();
- COMMANDS[VMOpCode.I_CL] = new ICLCommand();
- COMMANDS[VMOpCode.I_CLE] = new ICLECommand();
+ COMMANDS[VMOpCode.I_CE] = new ICECommand();
+ COMMANDS[VMOpCode.I_CNE] = new ICNECommand();
+ COMMANDS[VMOpCode.I_CG] = new ICGCommand();
+ COMMANDS[VMOpCode.I_CGE] = new ICGECommand();
+ COMMANDS[VMOpCode.I_CL] = new ICLCommand();
+ COMMANDS[VMOpCode.I_CLE] = new ICLECommand();
// endregion
// region Long64 (0x0060-0x007F)
- COMMANDS[VMOpCode.L_ADD] = new LAddCommand();
- COMMANDS[VMOpCode.L_SUB] = new LSubCommand();
- COMMANDS[VMOpCode.L_MUL] = new LMulCommand();
- COMMANDS[VMOpCode.L_DIV] = new LDivCommand();
- COMMANDS[VMOpCode.L_MOD] = new LModCommand();
- COMMANDS[VMOpCode.L_NEG] = new LNegCommand();
- COMMANDS[VMOpCode.L_INC] = new LIncCommand();
-
- COMMANDS[VMOpCode.L_AND] = new LAndCommand();
- COMMANDS[VMOpCode.L_OR] = new LOrCommand();
- COMMANDS[VMOpCode.L_XOR] = new LXorCommand();
-
- COMMANDS[VMOpCode.L_PUSH] = new LPushCommand();
- COMMANDS[VMOpCode.L_LOAD] = new LLoadCommand();
+ COMMANDS[VMOpCode.L_ADD] = new LAddCommand();
+ COMMANDS[VMOpCode.L_SUB] = new LSubCommand();
+ COMMANDS[VMOpCode.L_MUL] = new LMulCommand();
+ COMMANDS[VMOpCode.L_DIV] = new LDivCommand();
+ COMMANDS[VMOpCode.L_MOD] = new LModCommand();
+ COMMANDS[VMOpCode.L_NEG] = new LNegCommand();
+ COMMANDS[VMOpCode.L_INC] = new LIncCommand();
+
+ COMMANDS[VMOpCode.L_AND] = new LAndCommand();
+ COMMANDS[VMOpCode.L_OR] = new LOrCommand();
+ COMMANDS[VMOpCode.L_XOR] = new LXorCommand();
+
+ COMMANDS[VMOpCode.L_PUSH] = new LPushCommand();
+ COMMANDS[VMOpCode.L_LOAD] = new LLoadCommand();
COMMANDS[VMOpCode.L_STORE] = new LStoreCommand();
- COMMANDS[VMOpCode.L_CE] = new LCECommand();
- COMMANDS[VMOpCode.L_CNE] = new LCNECommand();
- COMMANDS[VMOpCode.L_CG] = new LCGCommand();
- COMMANDS[VMOpCode.L_CGE] = new LCGECommand();
- COMMANDS[VMOpCode.L_CL] = new LCLCommand();
- COMMANDS[VMOpCode.L_CLE] = new LCLECommand();
+ COMMANDS[VMOpCode.L_CE] = new LCECommand();
+ COMMANDS[VMOpCode.L_CNE] = new LCNECommand();
+ COMMANDS[VMOpCode.L_CG] = new LCGCommand();
+ COMMANDS[VMOpCode.L_CGE] = new LCGECommand();
+ COMMANDS[VMOpCode.L_CL] = new LCLCommand();
+ COMMANDS[VMOpCode.L_CLE] = new LCLECommand();
// endregion
// region Float32 (0x0080-0x009F)
- COMMANDS[VMOpCode.F_ADD] = new FAddCommand();
- COMMANDS[VMOpCode.F_SUB] = new FSubCommand();
- COMMANDS[VMOpCode.F_MUL] = new FMulCommand();
- COMMANDS[VMOpCode.F_DIV] = new FDivCommand();
- COMMANDS[VMOpCode.F_MOD] = new FModCommand();
- COMMANDS[VMOpCode.F_NEG] = new FNegCommand();
- COMMANDS[VMOpCode.F_INC] = new FIncCommand();
-
- COMMANDS[VMOpCode.F_PUSH] = new FPushCommand();
- COMMANDS[VMOpCode.F_LOAD] = new FLoadCommand();
+ COMMANDS[VMOpCode.F_ADD] = new FAddCommand();
+ COMMANDS[VMOpCode.F_SUB] = new FSubCommand();
+ COMMANDS[VMOpCode.F_MUL] = new FMulCommand();
+ COMMANDS[VMOpCode.F_DIV] = new FDivCommand();
+ COMMANDS[VMOpCode.F_MOD] = new FModCommand();
+ COMMANDS[VMOpCode.F_NEG] = new FNegCommand();
+ COMMANDS[VMOpCode.F_INC] = new FIncCommand();
+
+ COMMANDS[VMOpCode.F_PUSH] = new FPushCommand();
+ COMMANDS[VMOpCode.F_LOAD] = new FLoadCommand();
COMMANDS[VMOpCode.F_STORE] = new FStoreCommand();
- COMMANDS[VMOpCode.F_CE] = new FCECommand();
- COMMANDS[VMOpCode.F_CNE] = new FCNECommand();
- COMMANDS[VMOpCode.F_CG] = new FCGCommand();
- COMMANDS[VMOpCode.F_CGE] = new FCGECommand();
- COMMANDS[VMOpCode.F_CL] = new FCLCommand();
- COMMANDS[VMOpCode.F_CLE] = new FCLECommand();
+ COMMANDS[VMOpCode.F_CE] = new FCECommand();
+ COMMANDS[VMOpCode.F_CNE] = new FCNECommand();
+ COMMANDS[VMOpCode.F_CG] = new FCGCommand();
+ COMMANDS[VMOpCode.F_CGE] = new FCGECommand();
+ COMMANDS[VMOpCode.F_CL] = new FCLCommand();
+ COMMANDS[VMOpCode.F_CLE] = new FCLECommand();
// endregion
// region Double64 (0x00A0-0x00BF)
- COMMANDS[VMOpCode.D_ADD] = new DAddCommand();
- COMMANDS[VMOpCode.D_SUB] = new DSubCommand();
- COMMANDS[VMOpCode.D_MUL] = new DMulCommand();
- COMMANDS[VMOpCode.D_DIV] = new DDivCommand();
- COMMANDS[VMOpCode.D_MOD] = new DModCommand();
- COMMANDS[VMOpCode.D_NEG] = new DNegCommand();
- COMMANDS[VMOpCode.D_INC] = new DIncCommand();
-
- COMMANDS[VMOpCode.D_PUSH] = new DPushCommand();
- COMMANDS[VMOpCode.D_LOAD] = new DLoadCommand();
+ COMMANDS[VMOpCode.D_ADD] = new DAddCommand();
+ COMMANDS[VMOpCode.D_SUB] = new DSubCommand();
+ COMMANDS[VMOpCode.D_MUL] = new DMulCommand();
+ COMMANDS[VMOpCode.D_DIV] = new DDivCommand();
+ COMMANDS[VMOpCode.D_MOD] = new DModCommand();
+ COMMANDS[VMOpCode.D_NEG] = new DNegCommand();
+ COMMANDS[VMOpCode.D_INC] = new DIncCommand();
+
+ COMMANDS[VMOpCode.D_PUSH] = new DPushCommand();
+ COMMANDS[VMOpCode.D_LOAD] = new DLoadCommand();
COMMANDS[VMOpCode.D_STORE] = new DStoreCommand();
- COMMANDS[VMOpCode.D_CE] = new DCECommand();
- COMMANDS[VMOpCode.D_CNE] = new DCNECommand();
- COMMANDS[VMOpCode.D_CG] = new DCGCommand();
- COMMANDS[VMOpCode.D_CGE] = new DCGECommand();
- COMMANDS[VMOpCode.D_CL] = new DCLCommand();
- COMMANDS[VMOpCode.D_CLE] = new DCLECommand();
+ COMMANDS[VMOpCode.D_CE] = new DCECommand();
+ COMMANDS[VMOpCode.D_CNE] = new DCNECommand();
+ COMMANDS[VMOpCode.D_CG] = new DCGCommand();
+ COMMANDS[VMOpCode.D_CGE] = new DCGECommand();
+ COMMANDS[VMOpCode.D_CL] = new DCLCommand();
+ COMMANDS[VMOpCode.D_CLE] = new DCLECommand();
// endregion
// endregion
@@ -257,24 +229,24 @@ public class CommandFactory {
// endregion
// region Stack Control (0x0100-0x01FF)
- COMMANDS[VMOpCode.POP] = new PopCommand();
- COMMANDS[VMOpCode.DUP] = new DupCommand();
+ COMMANDS[VMOpCode.POP] = new PopCommand();
+ COMMANDS[VMOpCode.DUP] = new DupCommand();
COMMANDS[VMOpCode.SWAP] = new SwapCommand();
// endregion
// region Flow Control (0x0200-0x02FF)
COMMANDS[VMOpCode.JUMP] = new JumpCommand();
COMMANDS[VMOpCode.CALL] = new CallCommand();
- COMMANDS[VMOpCode.RET] = new RetCommand();
+ COMMANDS[VMOpCode.RET] = new RetCommand();
// endregion
// region Register Control (0x0300-0x03FF)
- COMMANDS[VMOpCode.MOV] = new MovCommand();
+ COMMANDS[VMOpCode.MOV] = new MovCommand();
// endregion
// region System Control (0x0400-0x04FF)
- COMMANDS[VMOpCode.HALT] = new HaltCommand();
- COMMANDS[VMOpCode.SYSCALL] = new SyscallCommand();
+ COMMANDS[VMOpCode.HALT] = new HaltCommand();
+ COMMANDS[VMOpCode.SYSCALL] = new SyscallCommand();
// COMMANDS[VMOpCode.DEBUG_TRAP] = new DebugTrapCommand();
// endregion
diff --git a/src/main/java/org/jcnc/snow/vm/io/FDTable.java b/src/main/java/org/jcnc/snow/vm/io/FDTable.java
index 0cd40b0e3ffdd6a25c1986a8f473bb20556076cf..1293e4d93b7e5a21624044c8e4ef7dbc658540bc 100644
--- a/src/main/java/org/jcnc/snow/vm/io/FDTable.java
+++ b/src/main/java/org/jcnc/snow/vm/io/FDTable.java
@@ -3,7 +3,8 @@ package org.jcnc.snow.vm.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
-import java.nio.channels.*;
+import java.nio.channels.Channel;
+import java.nio.channels.Channels;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@@ -19,11 +20,13 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public final class FDTable {
- private FDTable() {}
-
- /** Next available fd (0‒2 are reserved for standard streams) */
+ /**
+ * Next available fd (0‒2 are reserved for standard streams)
+ */
private static final AtomicInteger NEXT_FD = new AtomicInteger(3);
- /** Main mapping table: fd → Channel */
+ /**
+ * Main mapping table: fd → Channel
+ */
private static final ConcurrentHashMap MAP = new ConcurrentHashMap<>();
static {
@@ -33,8 +36,12 @@ public final class FDTable {
MAP.put(2, Channels.newChannel(new BufferedOutputStream(System.err)));
}
+ private FDTable() {
+ }
+
/**
* Register a new Channel, returning the allocated virtual fd.
+ *
* @param ch Channel to register
* @return allocated fd
*/
@@ -46,6 +53,7 @@ public final class FDTable {
/**
* Retrieve the Channel by fd; returns null if fd does not exist.
+ *
* @param fd file descriptor
* @return Channel or null if not found
*/
@@ -55,6 +63,7 @@ public final class FDTable {
/**
* Close and remove the fd (0‒2 are ignored).
+ *
* @param fd file descriptor to close
* @throws IOException if an I/O error occurs
*/
@@ -66,6 +75,7 @@ public final class FDTable {
/**
* Similar to dup(oldfd) — returns a new fd referring to the same Channel.
+ *
* @param oldfd old file descriptor to duplicate
* @return new fd referring to the same Channel
* @throws IllegalArgumentException if fd does not exist
diff --git a/src/main/java/org/jcnc/snow/vm/module/CallStack.java b/src/main/java/org/jcnc/snow/vm/module/CallStack.java
index cd982d389383647d70e5c50ff427b8229c49db8b..473a34bd7431b8c0c9e66b3ba8caf57b24031a3e 100644
--- a/src/main/java/org/jcnc/snow/vm/module/CallStack.java
+++ b/src/main/java/org/jcnc/snow/vm/module/CallStack.java
@@ -11,6 +11,7 @@ import java.util.Deque;
public class CallStack {
private static final int MAX_STACK_DEPTH = 1024; // Stack overflow protection
private final Deque stack = new ArrayDeque<>();
+
/**
* Default constructor for creating an instance of CallStack.
* This constructor is empty as no specific initialization is required.
diff --git a/src/main/java/org/jcnc/snow/vm/module/Instance.java b/src/main/java/org/jcnc/snow/vm/module/Instance.java
deleted file mode 100644
index 04c94513728c3cb478c2a2a28f1cbc99aaa88943..0000000000000000000000000000000000000000
--- a/src/main/java/org/jcnc/snow/vm/module/Instance.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package org.jcnc.snow.vm.module;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * The {@code Instance} class represents a runtime object instance
- * in the virtual machine.
- *
- * Each instance maintains:
- *
- *
A reference to its {@link VirtualTable} (vtable), which provides
- * dynamic method dispatch and runtime method lookup.
- *
A map of field values, where each field is identified by name
- * and can hold an arbitrary object reference.
- *
- *
- *
Key responsibilities:
- *
- *
Encapsulate the runtime state of an object.
- *
Enable field storage and retrieval through {@link #setField(String, Object)}
- * and {@link #getField(String)}.
- *
Provide access to the instance’s virtual table for method resolution.
- *
- */
-public final class Instance {
-
- /** The virtual table associated with this instance (immutable). */
- private final VirtualTable vtable;
-
- /** A mapping of field names to their runtime values. */
- private final Map fields = new HashMap<>();
-
- /**
- * Constructs a new {@code Instance} with the specified virtual table.
- *
- * @param vtable The virtual table that defines the method dispatch behavior
- * for this instance. Must not be {@code null}.
- */
- public Instance(VirtualTable vtable) {
- this.vtable = vtable;
- }
-
- /* ---------- Virtual Table ---------- */
-
- /**
- * Returns the virtual table associated with this instance.
- *
- * The virtual table is used for runtime method resolution
- * (dynamic dispatch).
- *
- *
- * @return The {@link VirtualTable} of this instance.
- */
- public VirtualTable vtable() {
- return vtable;
- }
-
- /* ---------- Fields ---------- */
-
- /**
- * Sets the value of a field in this instance.
- *
- * If the field does not already exist, it is created.
- * If the field exists, its value is overwritten.
- *
- *
- * @param name The name of the field to set. Must not be {@code null}.
- * @param value The value to assign to the field. Can be {@code null}.
- */
- public void setField(String name, Object value) {
- fields.put(name, value);
- }
-
- /**
- * Retrieves the value of a field in this instance.
- *
- * The value is cast to the expected type at runtime.
- * If the field is not present, this method returns {@code null}.
- *
- *
- * @param name The name of the field to retrieve. Must not be {@code null}.
- * @param The expected type of the field value.
- * @return The field value cast to type {@code T}, or {@code null} if absent.
- * @throws ClassCastException If the stored value cannot be cast to {@code T}.
- */
- @SuppressWarnings("unchecked")
- public T getField(String name) {
- return (T) fields.get(name);
- }
-}
diff --git a/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java b/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java
index 2904d04a2f0019674ca3f7894670a45163a6056e..47d9fca9f173150118aa3aaf41394b5289bab310 100644
--- a/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java
+++ b/src/main/java/org/jcnc/snow/vm/module/LocalVariableStore.java
@@ -9,70 +9,113 @@ import java.util.ArrayList;
import static org.jcnc.snow.vm.utils.VMUtils.isNativeImage;
/**
- * The {@code LocalVariableStore} represents a simple dynamically-sized
- * local-variable table (frame locals) of the VM.
+ * The {@code LocalVariableStore} represents a simple, dynamically-sized
+ * local-variable table (frame locals) for the virtual machine (VM).
*
- *
It supports random access via {@link #setVariable(int, Object)}
- * / {@link #getVariable(int)} and can compact itself
- * by trimming trailing {@code null} slots after execution has finished.
+ *
This class supports random access for storing and retrieving variables
+ * via {@link #setVariable(int, Object)} and {@link #getVariable(int)}.
+ * It can also compact itself by trimming trailing {@code null}
+ * slots after execution, for cleaner debug output.
+ *
+ *
Internally, it uses an {@link ArrayList} to store variables, automatically
+ * expanding as needed to support random index writes. This structure is designed
+ * for VM stack frame management and debug inspection.
*/
public class LocalVariableStore {
+ /**
+ * The backing list storing the local variables.
+ */
private final ArrayList localVariables;
- /* ---------- construction ---------- */
+ /* ---------- Construction ---------- */
+
+ /**
+ * Constructs a new {@code LocalVariableStore} with the given initial capacity.
+ *
+ * @param initialCapacity the initial capacity for the local variable list
+ */
public LocalVariableStore(int initialCapacity) {
this.localVariables = new ArrayList<>(initialCapacity);
handleMode();
}
+ /**
+ * Constructs a new {@code LocalVariableStore} with default capacity.
+ */
public LocalVariableStore() {
this.localVariables = new ArrayList<>();
handleMode();
}
- /* ---------- public API ---------- */
+ /* ---------- Public API ---------- */
/**
- * Sets the value at {@code index}, expanding the list if necessary.
+ * Sets the value at the specified index, expanding the list if necessary.
+ *
If the list is smaller than {@code index + 1}, it will be padded
+ * with {@code null} until the required capacity is reached.
+ *
+ * @param index the index to set (0-based)
+ * @param value the value to store at the specified index
+ * @throws IndexOutOfBoundsException if {@code index} is negative
*/
public void setVariable(int index, Object value) {
ensureCapacity(index + 1);
localVariables.set(index, value);
}
- /* ------------------------------------------------------------
- * Backward compatibility: VM instruction decoder can directly call
- * store / load methods without caring about internal naming differences.
- * ------------------------------------------------------------ */
+ /**
+ * Stores a value at the specified index (alias for {@link #setVariable}).
+ *
This method is provided for VM instruction decoder compatibility.
+ *
+ * @param index the index to set
+ * @param value the value to store
+ */
public void store(int index, Object value) {
setVariable(index, value);
}
+ /**
+ * Loads the value from the specified index (alias for {@link #getVariable}).
+ *
This method is provided for VM instruction decoder compatibility.
+ *
+ * @param index the index to retrieve
+ * @return the value at the given index
+ */
public Object load(int index) {
return getVariable(index);
}
/**
- * Returns the value at {@code index}.
+ * Returns the value at the specified index.
+ *
If the list is not large enough, it is automatically expanded,
+ * filling new slots with {@code null} values.
+ *
+ * @param index the index to retrieve (0-based)
+ * @return the value at the specified index, or {@code null} if not set
+ * @throws IndexOutOfBoundsException if {@code index} is negative
*/
public Object getVariable(int index) {
- /* 修改点 #1 —— 自动扩容以避免 LOAD 越界异常 */
+ // Automatic expansion to avoid LOAD out-of-bounds exception.
if (index < 0)
throw new IndexOutOfBoundsException("Negative LV index: " + index);
ensureCapacity(index + 1);
- return localVariables.get(index); // 可能为 null,符合 JVM 语义
+ return localVariables.get(index);
}
/**
- * Exposes the backing list (read-only preferred).
+ * Returns the backing {@link ArrayList} storing the local variables.
+ *
Direct modification is not recommended. Prefer read-only usage.
+ *
+ * @return the internal list of local variables
*/
public ArrayList getLocalVariables() {
return localVariables;
}
/**
- * Prints every slot to the logger.
+ * Prints the contents of each slot in the local variable table to the logger.
+ *
If the table is empty, a corresponding message is printed instead.
*/
public void printLv() {
if (localVariables.isEmpty()) {
@@ -86,24 +129,23 @@ public class LocalVariableStore {
}
}
-
- /* ---------- internal helpers ---------- */
+ /* ---------- Internal Helpers ---------- */
/**
- * Clears all variables (used when a stack frame is popped).
+ * Clears all variables in the table.
+ *
This method is typically called when a stack frame is popped.
*/
public void clearVariables() {
localVariables.clear();
}
/**
- * Compacts the table by removing trailing {@code null} slots.
- *
Call this once after program termination (e.g. in
- * {@code VirtualMachineEngine.execute()} before printing) to get
- * cleaner debug output without affecting execution-time indices.
+ * Compacts the table by removing only trailing {@code null} slots.
+ *
This should be called after program termination, for cleaner debug output.
+ * Does not affect non-null slots or internal indices during execution.
*/
public void compact() {
- /* 修改点 #2 —— 仅删除“尾部” null,而不是整表过滤 */
+ // Only delete the "tail" null values, not filter the entire table.
int i = localVariables.size() - 1;
while (i >= 0 && localVariables.get(i) == null) {
localVariables.remove(i);
@@ -112,22 +154,22 @@ public class LocalVariableStore {
}
/**
- * Ensures backing list can hold {@code minCapacity} slots.
+ * Ensures that the backing list has at least the specified minimum capacity.
+ *
New slots are filled with {@code null} values if the list needs to grow.
+ *
+ * @param minCapacity the minimum capacity required
*/
private void ensureCapacity(int minCapacity) {
- /* 修改点 #3 —— 使用 while 循环填充 null,确保 slot 可随机写入 */
while (localVariables.size() < minCapacity) {
localVariables.add(null);
}
}
/**
- * Mode-specific UI hook for debugging.
- *
- * If debug mode is enabled and not running inside a GraalVM native-image,
- * this method will open the Swing-based variable inspector window.
- * In native-image environments (where AWT/Swing is unavailable),
- * the window will not be displayed.
+ * Handles debug mode hooks. If debug mode is enabled and not running inside a
+ * GraalVM native-image, this method will open a Swing-based variable inspector
+ * window for debugging purposes.
+ *
In native-image environments, this window is not displayed.
*/
private void handleMode() {
if (SnowConfig.isDebug()) {
diff --git a/src/main/java/org/jcnc/snow/vm/module/StackFrame.java b/src/main/java/org/jcnc/snow/vm/module/StackFrame.java
index 39e708c83383802638cbd85b2b9ae525bc195013..4def3341ab2adcb9d566a9d62e7f7f4a4ccfbb7d 100644
--- a/src/main/java/org/jcnc/snow/vm/module/StackFrame.java
+++ b/src/main/java/org/jcnc/snow/vm/module/StackFrame.java
@@ -1,7 +1,5 @@
package org.jcnc.snow.vm.module;
-import org.jcnc.snow.vm.utils.LoggingUtils;
-
/**
* The {@code StackFrame} class represents a single frame in the call stack during program execution.
* It holds the execution context for a method invocation, including information such as the return
diff --git a/src/main/java/org/jcnc/snow/vm/module/VirtualTable.java b/src/main/java/org/jcnc/snow/vm/module/VirtualTable.java
deleted file mode 100644
index f7e0fe6abfec6678b18a09052c15c599e621ed54..0000000000000000000000000000000000000000
--- a/src/main/java/org/jcnc/snow/vm/module/VirtualTable.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.jcnc.snow.vm.module;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * The {@code VirtualTable} class represents a runtime virtual function table (vtable)
- * used by the virtual machine to support dynamic method dispatch.
- *
- * A vtable is a mapping from method signatures to their corresponding
- * entry addresses in the VM code segment. Each class or instance can have
- * its own vtable that determines which concrete method implementations
- * are invoked at runtime.
- *
- *
- *
Key details:
- *
- *
Key: A method signature, such as {@code "Person::getName"}.
- *
Value: The entry point address of the method implementation
- * within the VM code segment.
- *
- */
-public final class VirtualTable {
-
- /** Mapping from method signature to its entry address in the VM code segment. */
- private final Map table = new HashMap<>();
-
- /**
- * Registers a new method implementation in the virtual table, or overrides
- * an existing entry if the method signature is already present.
- *
- * @param methodSig The method signature (e.g., {@code "Person::getName"}).
- * @param addr The entry address of the method implementation in the VM code segment.
- */
- public void register(String methodSig, int addr) {
- table.put(methodSig, addr);
- }
-
- /**
- * Looks up the entry address for the given method signature.
- *
- * If the method is not found in the virtual table, an {@link IllegalStateException}
- * is thrown to aid debugging.
- *
- *
- * @param methodSig The method signature to look up.
- * @return The entry address of the corresponding method implementation.
- * @throws IllegalStateException If no mapping exists for the given method signature.
- */
- public int lookup(String methodSig) {
- Integer addr = table.get(methodSig);
- if (addr == null)
- throw new IllegalStateException("VTable missing entry: " + methodSig);
- return addr;
- }
-
- /**
- * Returns a read-only snapshot of the current virtual table.
- *
- * This is primarily intended for debugging or inspection purposes,
- * allowing external code to see the current state of the vtable
- * without modifying it.
- *
- *
- * @return An unmodifiable copy of the method signature to entry address mappings.
- */
- public Map snapshot() {
- return Map.copyOf(table);
- }
-}