Date: Mon, 21 Jul 2025 17:06:40 +0800
Subject: [PATCH 20/36] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20Expressi?=
=?UTF-8?q?onBuilder=20=E7=B1=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 优化了代码结构,提高了代码的可读性和可维护性
- 添加了详细的注释,解释了各个方法的功能和实现细节
- 改进了对不同表达式类型的处理逻辑,增强了表达式构建的能力
- 优化了寄存器的使用和管理,提高了 IR 指令生成的效率
---
.../ir/builder/ExpressionBuilder.java | 319 ++++++++++--------
1 file changed, 171 insertions(+), 148 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
index 31e2973..58eccfc 100644
--- a/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
+++ b/src/main/java/org/jcnc/snow/compiler/ir/builder/ExpressionBuilder.java
@@ -5,130 +5,117 @@ import org.jcnc.snow.compiler.ir.instruction.CallInstruction;
import org.jcnc.snow.compiler.ir.instruction.LoadConstInstruction;
import org.jcnc.snow.compiler.ir.instruction.UnaryOperationInstruction;
import org.jcnc.snow.compiler.ir.utils.ComparisonUtils;
+import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
import org.jcnc.snow.compiler.ir.value.IRConstant;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
-import org.jcnc.snow.compiler.ir.utils.ExpressionUtils;
import org.jcnc.snow.compiler.parser.ast.*;
import org.jcnc.snow.compiler.parser.ast.base.ExpressionNode;
import java.util.*;
/**
- * 表达式构建器
+ * ExpressionBuilder - 表达式 → IR 构建器
+ *
*
- * 该类负责将抽象语法树(AST)的表达式节点转换为中间表示(IR)指令和虚拟寄存器,
- * 是编译器IR生成阶段的核心工具。
- *
- * 主要职责包括:
+ * 负责将 AST 表达式节点递归转换为 IR 虚拟寄存器操作,并生成对应的 IR 指令序列。
+ * 支持字面量、标识符、二元表达式、一元表达式、函数调用等多种类型表达式。
+ *
+ *
+ *
+ * 主要功能:
*
- * - 将数字字面量、标识符、二元表达式、函数调用等AST表达式节点,翻译为对应的IR指令序列
- * - 管理并分配虚拟寄存器,保证IR操作的数据流正确
+ * - 将表达式节点映射为虚拟寄存器
+ * - 为每种表达式类型生成对应 IR 指令
+ * - 支持表达式嵌套的递归构建
+ * - 支持写入指定目标寄存器,避免冗余的 move 指令
*
- *
+ *
*/
public record ExpressionBuilder(IRContext ctx) {
+ /* ───────────────── 顶层入口 ───────────────── */
+
/**
- * 构建并返回某个表达式节点对应的虚拟寄存器。
+ * 构建任意 AST 表达式节点,自动为其分配一个新的虚拟寄存器,并返回该寄存器。
*
- * 会根据节点的实际类型分别处理:
- *
- * - 数字字面量: 新建常量寄存器
- * - 字符串字面量: 新建常量寄存器(字符串类型)
- * - 布尔字面量: 生成值为 0 或 1 的常量寄存器
- * - 标识符: 查找当前作用域中的寄存器
- * - 二元表达式: 递归处理子表达式并进行相应运算
- * - 一元运算符:
- *
- * -x(取负,生成 NEG_I32 指令)与
- * !x(逻辑非,转换为 x == 0 比较指令)
- *
- *
- * - 函数调用: 生成对应的Call指令
- * - 其它类型不支持,抛出异常
- *
+ *
+ * 这是表达式 IR 生成的核心入口。它会根据不同的表达式类型进行分派,递归构建 IR 指令。
+ *
*
- * @param expr 要转换的表达式AST节点
- * @return 该表达式的计算结果寄存器
- * @throws IllegalStateException 如果遇到未定义的标识符或不支持的表达式类型
+ * @param expr 任意 AST 表达式节点
+ * @return 存储该表达式结果的虚拟寄存器
+ * @throws IllegalStateException 遇到不支持的表达式类型或未定义标识符
*/
public IRVirtualRegister build(ExpressionNode expr) {
return switch (expr) {
- // 数字字面量
+ // 数字字面量,例如 123、3.14
case NumberLiteralNode n -> buildNumberLiteral(n.value());
- // 字符串字面量
+ // 字符串字面量,例如 "abc"
case StringLiteralNode s -> buildStringLiteral(s.value());
- // 布尔字面量
+ // 布尔字面量,例如 true / false
case BoolLiteralNode b -> buildBoolLiteral(b.getValue());
- // 标识符
+ // 标识符(变量名),如 a、b
case IdentifierNode id -> {
+ // 查找当前作用域中的变量寄存器
IRVirtualRegister reg = ctx.getScope().lookup(id.name());
if (reg == null)
throw new IllegalStateException("未定义标识符: " + id.name());
yield reg;
}
- // 二元表达式
+ // 二元表达式(如 a+b, x==y)
case BinaryExpressionNode bin -> buildBinary(bin);
- // 函数调用
- case CallExpressionNode call -> buildCall(call);
- // 一元表达式
- case UnaryExpressionNode u -> buildUnary(u);
+ // 函数/方法调用表达式
+ case CallExpressionNode call -> buildCall(call);
+ // 一元表达式(如 -a, !a)
+ case UnaryExpressionNode un -> buildUnary(un);
+
+ // 默认分支:遇到未知表达式类型则直接抛异常
default -> throw new IllegalStateException(
"不支持的表达式类型: " + expr.getClass().getSimpleName());
};
}
- /** 处理一元表达式 */
- private IRVirtualRegister buildUnary(UnaryExpressionNode un) {
- String op = un.operator();
- IRVirtualRegister val = build(un.operand());
-
- // -x → NEG_*(根据类型自动选择位宽)
- if (op.equals("-")) {
- IRVirtualRegister dest = ctx.newRegister();
- IROpCode code = ExpressionUtils.negOp(un.operand());
- ctx.addInstruction(new UnaryOperationInstruction(code, dest, val));
- return dest;
- }
-
- // !x → (x == 0)
- if (op.equals("!")) {
- IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
- return InstructionFactory.binOp(ctx, IROpCode.CMP_IEQ, val, zero);
- }
-
- throw new IllegalStateException("未知一元运算符: " + op);
- }
+ /* ───────────────── 写入指定寄存器 ───────────────── */
/**
- * 直接将表达式计算结果写入指定的目标寄存器(dest)。
- *
- * 与{@link #build(ExpressionNode)}类似,但支持目标寄存器复用(避免不必要的move)。
+ * 生成表达式,并将其结果直接写入目标寄存器,避免冗余的 move 操作。
*
- * @param node 表达式AST节点
- * @param dest 目标寄存器
- * @throws IllegalStateException 未定义标识符/不支持的表达式类型时报错
+ *
+ * 某些简单表达式(如字面量、变量名)可以直接写入目标寄存器;复杂表达式则会先 build 到新寄存器,再 move 到目标寄存器。
+ *
+ *
+ * @param node 要生成的表达式节点
+ * @param dest 目标虚拟寄存器(用于存储结果)
*/
public void buildInto(ExpressionNode node, IRVirtualRegister dest) {
switch (node) {
- // 数字字面量,直接加载到目标寄存器
+ // 数字字面量:生成 loadConst 指令写入目标寄存器
case NumberLiteralNode n ->
- InstructionFactory.loadConstInto(ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
- // 字符串字面量,直接加载到目标寄存器
+ InstructionFactory.loadConstInto(
+ ctx, dest, ExpressionUtils.buildNumberConstant(ctx, n.value()));
+
+ // 字符串字面量:生成 loadConst 指令写入目标寄存器
case StringLiteralNode s ->
- InstructionFactory.loadConstInto(ctx, dest, new IRConstant(s.value()));
- // 布尔字面量,直接加载到目标寄存器
+ InstructionFactory.loadConstInto(
+ ctx, dest, new IRConstant(s.value()));
+
+ // 布尔字面量:转换为 int 1/0,生成 loadConst
case BoolLiteralNode b ->
- InstructionFactory.loadConstInto(ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
- // 标识符,查找并move到目标寄存器
+ InstructionFactory.loadConstInto(
+ ctx, dest, new IRConstant(b.getValue() ? 1 : 0));
+
+ // 标识符:查表获取原寄存器,然后 move 到目标寄存器
case IdentifierNode id -> {
IRVirtualRegister src = ctx.getScope().lookup(id.name());
- if (src == null) throw new IllegalStateException("未定义标识符: " + id.name());
+ if (src == null)
+ throw new IllegalStateException("未定义标识符: " + id.name());
InstructionFactory.move(ctx, src, dest);
}
- // 二元表达式,直接写入目标寄存器
+
+ // 二元表达式:递归生成并写入目标寄存器
case BinaryExpressionNode bin -> buildBinaryInto(bin, dest);
- // 其他表达式,先递归生成寄存器,再move到目标寄存器
+
+ // 其它复杂情况:先 build 到新寄存器,再 move 到目标寄存器
default -> {
IRVirtualRegister tmp = build(node);
InstructionFactory.move(ctx, tmp, dest);
@@ -136,41 +123,100 @@ public record ExpressionBuilder(IRContext ctx) {
}
}
+ /* ───────────────── 具体表达式类型 ───────────────── */
+
/**
- * 构建二元表达式的IR,生成新寄存器存储结果。
- *
- * 先递归构建左右操作数,之后根据操作符类别(算术或比较)决定生成的IR操作码,
- * 并生成对应的二元运算指令。
+ * 一元表达式构建
*
- * @param bin 二元表达式节点
- * @return 存放结果的虚拟寄存器
+ *
+ * 支持算术取负(-a)、逻辑非(!a)等一元运算符。
+ *
+ *
+ * @param un 一元表达式节点
+ * @return 结果存储的新分配虚拟寄存器
*/
- private IRVirtualRegister buildBinary(BinaryExpressionNode bin) {
- String op = bin.operator();
- IRVirtualRegister left = build(bin.left());
- IRVirtualRegister right = build(bin.right());
+ private IRVirtualRegister buildUnary(UnaryExpressionNode un) {
+ // 递归生成操作数的寄存器
+ IRVirtualRegister src = build(un.operand());
+ // 分配目标寄存器
+ IRVirtualRegister dest = ctx.newRegister();
- // 1. 比较运算
- if (ComparisonUtils.isComparisonOperator(op)) {
- return InstructionFactory.binOp(
- ctx,
- ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
- left, right);
+ switch (un.operator()) {
+ // 算术负号:生成取负指令
+ case "-" -> ctx.addInstruction(
+ new UnaryOperationInstruction(ExpressionUtils.negOp(un.operand()), dest, src));
+ // 逻辑非:等价于 a == 0,生成比较指令
+ case "!" -> {
+ IRVirtualRegister zero = InstructionFactory.loadConst(ctx, 0);
+ return InstructionFactory.binOp(ctx, IROpCode.CMP_IEQ, src, zero);
+ }
+ // 其它一元运算符不支持,抛异常
+ default -> throw new IllegalStateException("未知一元运算符: " + un.operator());
}
-
- // 2. 其他算术 / 逻辑运算
- IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
- if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
- return InstructionFactory.binOp(ctx, code, left, right);
+ return dest;
}
/**
- * 将二元表达式的结果直接写入指定寄存器dest。
- *
- * 结构与{@link #buildBinary(BinaryExpressionNode)}类似,但不会新分配寄存器。
+ * 构建函数或方法调用表达式。
+ *
+ * @param call AST 调用表达式节点
+ * @return 存储调用结果的虚拟寄存器
+ */
+ private IRVirtualRegister buildCall(CallExpressionNode call) {
+ // 递归生成所有参数(实参)对应的寄存器
+ List argv = call.arguments().stream().map(this::build).toList();
+
+ // 解析被调用目标名,支持普通函数/成员方法
+ String callee = switch (call.callee()) {
+ // 成员方法调用,例如 obj.foo()
+ case MemberExpressionNode m when m.object() instanceof IdentifierNode id
+ -> id.name() + "." + m.member();
+ // 普通函数调用
+ case IdentifierNode id -> id.name();
+ // 其它情况暂不支持
+ default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
+ };
+
+ // 为返回值分配新寄存器,生成 Call 指令
+ IRVirtualRegister dest = ctx.newRegister();
+ ctx.addInstruction(new CallInstruction(dest, callee, new ArrayList<>(argv)));
+ return dest;
+ }
+
+ /**
+ * 二元表达式构建,结果存储到新寄存器。
+ *
+ * 支持算术、位运算与比较(==, !=, >, <, ...)。
*
* @param bin 二元表达式节点
- * @param dest 目标寄存器
+ * @return 存储表达式结果的虚拟寄存器
+ */
+ private IRVirtualRegister buildBinary(BinaryExpressionNode bin) {
+ // 递归生成左、右子表达式的寄存器
+ IRVirtualRegister a = build(bin.left());
+ IRVirtualRegister b = build(bin.right());
+ String op = bin.operator();
+
+ // 比较运算符(==、!=、>、< 等),需要生成条件跳转或布尔值寄存器
+ if (ComparisonUtils.isComparisonOperator(op)) {
+ return InstructionFactory.binOp(
+ ctx,
+ // 通过比较工具获得合适的 IR 操作码
+ ComparisonUtils.cmpOp(ctx.getScope().getVarTypes(), op, bin.left(), bin.right()),
+ a, b);
+ }
+
+ // 其它算术/位运算
+ IROpCode code = ExpressionUtils.resolveOpCode(op, bin.left(), bin.right());
+ if (code == null) throw new IllegalStateException("不支持的运算符: " + op);
+ return InstructionFactory.binOp(ctx, code, a, b);
+ }
+
+ /**
+ * 二元表达式构建,结果直接写入目标寄存器(用于赋值左值等优化场景)。
+ *
+ * @param bin 二元表达式节点
+ * @param dest 目标虚拟寄存器
*/
private void buildBinaryInto(BinaryExpressionNode bin, IRVirtualRegister dest) {
IRVirtualRegister a = build(bin.left());
@@ -189,67 +235,44 @@ public record ExpressionBuilder(IRContext ctx) {
}
}
- /**
- * 处理函数调用表达式,生成对应的Call指令和目标寄存器。
- *
- * 支持普通标识符调用和成员调用(如 mod.func),会为每个参数依次生成子表达式的寄存器。
- *
- * @param call 调用表达式AST节点
- * @return 返回结果存放的寄存器
- */
- private IRVirtualRegister buildCall(CallExpressionNode call) {
- // 递归构建所有参数的寄存器
- List argv = call.arguments().stream()
- .map(this::build)
- .toList();
- // 获取完整调用目标名称(支持成员/模块调用和普通调用)
- String fullName = switch (call.callee()) {
- case MemberExpressionNode member when member.object() instanceof IdentifierNode _ ->
- ((IdentifierNode)member.object()).name() + "." + member.member();
- case IdentifierNode id -> id.name();
- default -> throw new IllegalStateException("不支持的调用目标: " + call.callee().getClass().getSimpleName());
- };
- // 申请目标寄存器
- IRVirtualRegister dest = ctx.newRegister();
- // 添加Call指令到IR上下文
- ctx.addInstruction(new CallInstruction(dest, fullName, new ArrayList<>(argv)));
- return dest;
- }
+ /* ───────────────── 字面量辅助方法 ───────────────── */
/**
- * 处理数字字面量,生成常量寄存器和加载指令。
- *
- * 会将字符串型字面量(如 "123", "1.0f")解析为具体的IRConstant,
- * 并分配一个新的虚拟寄存器来存放该常量。
+ * 构建数字字面量表达式(如 123),分配新寄存器并生成 LoadConst 指令。
*
- * @param value 字面量字符串
- * @return 存放该常量的寄存器
+ * @param value 字面量文本(字符串格式)
+ * @return 存储该字面量的寄存器
*/
private IRVirtualRegister buildNumberLiteral(String value) {
- IRConstant constant = ExpressionUtils.buildNumberConstant(ctx, value);
- IRVirtualRegister reg = ctx.newRegister();
- ctx.addInstruction(new LoadConstInstruction(reg, constant));
- return reg;
+ IRConstant c = ExpressionUtils.buildNumberConstant(ctx, value);
+ IRVirtualRegister r = ctx.newRegister();
+ ctx.addInstruction(new LoadConstInstruction(r, c));
+ return r;
}
/**
- * 处理字符串字面量,生成常量寄存器和加载指令。
+ * 构建字符串字面量表达式,分配新寄存器并生成 LoadConst 指令。
*
* @param value 字符串内容
- * @return 存放该字符串常量的寄存器
+ * @return 存储该字符串的寄存器
*/
private IRVirtualRegister buildStringLiteral(String value) {
- IRConstant constant = new IRConstant(value);
- IRVirtualRegister reg = ctx.newRegister();
- ctx.addInstruction(new LoadConstInstruction(reg, constant));
- return reg;
+ IRConstant c = new IRConstant(value);
+ IRVirtualRegister r = ctx.newRegister();
+ ctx.addInstruction(new LoadConstInstruction(r, c));
+ return r;
}
- /** 布尔字面量 → CONST (true=1,false=0)*/
- private IRVirtualRegister buildBoolLiteral(boolean value) {
- IRConstant constant = new IRConstant(value ? 1 : 0);
- IRVirtualRegister reg = ctx.newRegister();
- ctx.addInstruction(new LoadConstInstruction(reg, constant));
- return reg;
+ /**
+ * 构建布尔字面量表达式(true/false),分配新寄存器并生成 LoadConst 指令(1 表示 true,0 表示 false)。
+ *
+ * @param v 布尔值
+ * @return 存储 1/0 的寄存器
+ */
+ private IRVirtualRegister buildBoolLiteral(boolean v) {
+ IRConstant c = new IRConstant(v ? 1 : 0);
+ IRVirtualRegister r = ctx.newRegister();
+ ctx.addInstruction(new LoadConstInstruction(r, c));
+ return r;
}
}
From 6a339149f11f6584959cb7535b868a65c2eff744 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 21 Jul 2025 17:09:26 +0800
Subject: [PATCH 21/36] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=20LoadConstGen?=
=?UTF-8?q?erator=20=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98=E5=8C=96=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=E7=BB=93=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 优化类注释,明确类的功能和额外支持的特性
- 重构 generate 方法,提高代码可读性和维护性- 增加对字符串常量的处理,支持 syscall 降级场景
- 完善类型前缀处理,增加 'R' 前缀用于字符串常量
---
.../backend/generator/LoadConstGenerator.java | 70 ++++++++++---------
1 file changed, 36 insertions(+), 34 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java
index 760422a..a24f6d9 100644
--- a/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java
+++ b/src/main/java/org/jcnc/snow/compiler/backend/generator/LoadConstGenerator.java
@@ -10,16 +10,18 @@ import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
import java.util.Map;
/**
- * 常量加载指令生成器
- * 该类用于生成将常量加载到虚拟机寄存器的指令,包括 PUSH 常量值和 STORE 到指定槽位,
- * 并为每个槽位设置正确的类型前缀(如 'I', 'L', 'F' 等)。
+ * LoadConstGenerator - 将 IR {@code LoadConstInstruction} 生成 VM 指令
+ *
+ *
+ * 本类负责将 IR 层的常量加载指令 {@link LoadConstInstruction} 转换为对应的虚拟机指令。
+ * 额外支持:如果常量类型为 {@code String},会同步登记到
+ * {@link CallGenerator} 的字符串常量池,方便 syscall 降级场景使用。
+ *
*/
public class LoadConstGenerator implements InstructionGenerator {
/**
- * 返回本生成器支持的指令类型,即 LoadConstInstruction。
- *
- * @return 支持的指令类型的 Class 对象
+ * 指定本生成器支持的 IR 指令类型(LoadConstInstruction)
*/
@Override
public Class supportedClass() {
@@ -27,49 +29,49 @@ public class LoadConstGenerator implements InstructionGenerator slotMap,
String currentFn) {
- // 1. 获取常量值(第一个操作数必为常量)
+
+ /* 1. 获取常量值 */
IRConstant constant = (IRConstant) ins.operands().getFirst();
Object value = constant.value();
- // 2. 生成 PUSH 指令,将常量值推入操作数栈
- // 通过 OpHelper 辅助方法获取合适的数据类型前缀
- String pushOp = OpHelper.pushOpcodeFor(value);
- out.emit(pushOp + " " + value);
+ /* 2. 生成 PUSH 指令,将常量值入栈 */
+ out.emit(OpHelper.pushOpcodeFor(value) + " " + value);
- // 3. 生成 STORE 指令,将栈顶的值存入对应槽位(寄存器)
- // 同样通过 OpHelper 获取对应类型的 STORE 指令
- String storeOp = OpHelper.storeOpcodeFor(value);
- // 获取目标虚拟寄存器对应的槽位编号
+ /* 3. STORE 到目标槽位 */
int slot = slotMap.get(ins.dest());
- out.emit(storeOp + " " + slot);
+ out.emit(OpHelper.storeOpcodeFor(value) + " " + slot);
- // 4. 根据常量的 Java 类型,为槽位设置正确的前缀字符
- // 这样在后续类型检查/运行时可用,常见前缀如 'I', 'L', 'F', 'D', 'S', 'B'
+ /* 4. 标记槽位数据类型(用于后续类型推断和 LOAD/STORE 指令选择) */
char prefix = switch (value) {
- case Integer _ -> 'I'; // 整型
- case Long _ -> 'L'; // 长整型
- case Short _ -> 'S'; // 短整型
- case Byte _ -> 'B'; // 字节型
- case Double _ -> 'D'; // 双精度浮点型
- case Float _ -> 'F'; // 单精度浮点型
- case Boolean _ -> 'I'; // 布尔(作为 0/1 整型存储)
- case null, default ->
- throw new IllegalStateException("Unknown const type: " + (value != null ? value.getClass() : null));
+ case Integer _ -> 'I'; // 整型
+ case Long _ -> 'L'; // 长整型
+ case Short _ -> 'S'; // 短整型
+ case Byte _ -> 'B'; // 字节型
+ case Double _ -> 'D'; // 双精度
+ case Float _ -> 'F'; // 单精度
+ case Boolean _ -> 'I'; // 布尔类型用 I 处理
+ case String _ -> 'R'; // 字符串常量
+ case null, default -> // 其它类型异常
+ throw new IllegalStateException("未知的常量类型: "
+ + (value != null ? value.getClass() : null));
};
-
- // 写入槽位类型映射表
out.setSlotType(slot, prefix);
+
+ /* 5. 如果是字符串常量,则登记到 CallGenerator 的常量池,便于 syscall 字符串降级使用 */
+ if (value instanceof String s) {
+ CallGenerator.registerStringConst(ins.dest().id(), s);
+ }
}
}
From 8eeed6f6b999c059c2159eb589ae9b17491e0f2f Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 21 Jul 2025 17:10:20 +0800
Subject: [PATCH 22/36] =?UTF-8?q?refactor:=20=E8=B0=83=E6=95=B4=E6=93=8D?=
=?UTF-8?q?=E4=BD=9C=E6=95=B0=E6=A0=88=E6=89=93=E5=8D=B0=E6=A0=BC=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 在操作数栈状态打印时,增加空行以提高可读性
---
src/main/java/org/jcnc/snow/vm/module/OperandStack.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/jcnc/snow/vm/module/OperandStack.java b/src/main/java/org/jcnc/snow/vm/module/OperandStack.java
index 347387f..1e0d158 100644
--- a/src/main/java/org/jcnc/snow/vm/module/OperandStack.java
+++ b/src/main/java/org/jcnc/snow/vm/module/OperandStack.java
@@ -84,7 +84,7 @@ public class OperandStack {
*
*/
public void printOperandStack() {
- LoggingUtils.logInfo("Operand Stack state:", stack + "\n");
+ LoggingUtils.logInfo("\n\nOperand Stack state:", stack + "\n");
}
/**
From b4c933e3d4c310969a6ac265531e23d6b9d4c6fd Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 21 Jul 2025 17:10:42 +0800
Subject: [PATCH 23/36] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=20Demo14=20?=
=?UTF-8?q?=E7=A4=BA=E4=BE=8B=E7=A8=8B=E5=BA=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 修改导入模块,使用 os 模块替代 BuiltinUtils
- 更新 syscall 调用,增加表达式计算
---
playground/Demo/Demo14/Main.snow | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/playground/Demo/Demo14/Main.snow b/playground/Demo/Demo14/Main.snow
index 42049ea..8e5baff 100644
--- a/playground/Demo/Demo14/Main.snow
+++ b/playground/Demo/Demo14/Main.snow
@@ -1,10 +1,10 @@
module: Main
- import: BuiltinUtils
+ import: os
function: main
return_type: void
body:
- syscall("PRINT",1)
+ syscall("PRINT",2222+1)
end body
end function
end module
\ No newline at end of file
From e7c7451004d8b5d99ee68212771332bf02bfe385 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 21 Jul 2025 17:16:54 +0800
Subject: [PATCH 24/36] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20CallComm?=
=?UTF-8?q?and=20=E7=B1=BB=E5=B9=B6=E4=BC=98=E5=8C=96=E6=96=87=E6=A1=A3?=
=?UTF-8?q?=E6=B3=A8=E9=87=8A=20-=20=E9=87=8D=E6=96=B0=E7=BB=84=E7=BB=87?=
=?UTF-8?q?=E7=B1=BB=E6=96=87=E6=A1=A3=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=AF=B9?=
=?UTF-8?q?=20CallCommand=20=E5=8A=9F=E8=83=BD=E5=92=8C=E8=A1=8C=E4=B8=BA?=
=?UTF-8?q?=E7=9A=84=E8=AF=A6=E7=BB=86=E6=8F=8F=E8=BF=B0=20-=20=E6=B7=BB?=
=?UTF-8?q?=E5=8A=A0=20execute=20=E6=96=B9=E6=B3=95=E7=9A=84=E8=AF=A6?=
=?UTF-8?q?=E7=BB=86=E6=B3=A8=E9=87=8A=EF=BC=8C=E6=98=8E=E7=A1=AE=E5=8F=82?=
=?UTF-8?q?=E6=95=B0=E5=92=8C=E8=BF=94=E5=9B=9E=E5=80=BC=E7=9A=84=E7=94=A8?=
=?UTF-8?q?=E9=80=94=20-=20=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93?=
=?UTF-8?q?=E6=9E=84=EF=BC=8C=E6=8F=90=E9=AB=98=E5=8F=AF=E8=AF=BB=E6=80=A7?=
=?UTF-8?q?=E5=92=8C=E5=8F=AF=E7=BB=B4=E6=8A=A4=E6=80=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../vm/commands/flow/control/CallCommand.java | 42 +++++++++++++++++--
1 file changed, 39 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java b/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java
index befbfea..1171885 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java
@@ -5,12 +5,48 @@ import org.jcnc.snow.vm.interfaces.Command;
import org.jcnc.snow.vm.module.*;
/**
- * CALL addr nArgs — pushes a new stack-frame, transfers the specified
- * argument count from the operand stack into the callee’s local slots
- * 0‥n-1 (left-to-right), then jumps to {@code addr}.
+ * The CallCommand class implements the {@link Command} interface and represents a subroutine/function call
+ * instruction in the virtual machine.
+ *
+ * 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.
+ *
+ *
+ * Specific behavior:
+ *
+ * - 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.
+ *
*/
public class CallCommand implements Command {
+ /**
+ * Executes the CALL instruction, initiating a subroutine/function call within the virtual machine.
+ *
+ * This method handles the creation of a new stack frame for the callee, argument passing,
+ * and control transfer to the target function address.
+ *
+ *
+ * @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.
+ */
@Override
public int execute(String[] parts,
int currentPC,
From 6098a290b1d67686df16098ac4dc6f88ea111378 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 21 Jul 2025 17:18:30 +0800
Subject: [PATCH 25/36] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20R=5FLOAD?=
=?UTF-8?q?=E6=8C=87=E4=BB=A4=E7=9A=84=E5=AE=9E=E7=8E=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 RLoadCommand 类,实现 Command接口
- 该指令用于从局部变量表中加载引用对象,并将其推入操作数栈- 指令格式:R_LOAD
- 其中 是局部变量表中的索引
- 执行过程包括解析索引、获取引用、推入栈顶和更新程序计数器
---
.../vm/commands/ref/control/RLoadCommand.java | 54 +++++++++++++++++++
1 file changed, 54 insertions(+)
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/ref/control/RLoadCommand.java
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
new file mode 100644
index 0000000..d7e314d
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RLoadCommand.java
@@ -0,0 +1,54 @@
+package org.jcnc.snow.vm.commands.ref.control;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * The {@code RLoadCommand} class implements the {@link Command} interface and represents the
+ * reference load instruction ({@code R_LOAD}) in the virtual machine.
+ *
+ *
+ * This instruction loads a reference object from the current stack frame’s local variable store
+ * at the specified slot and pushes it onto the operand stack.
+ *
+ *
+ * Instruction format: {@code R_LOAD }
+ *
+ * - {@code }: The index in the local variable table to load the reference from.
+ *
+ *
+ * Behavior:
+ *
+ * - Parses the slot index from the instruction parameters.
+ * - Fetches the reference object stored at the specified slot in the current stack frame's local variable store.
+ * - Pushes the fetched reference onto the operand stack.
+ * - Increments the program counter to the next instruction.
+ *
+ */
+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 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.
+ * @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.
+ */
+ @Override
+ public int execute(String[] parts, int pc,
+ OperandStack stack,
+ LocalVariableStore lvs,
+ CallStack cs) {
+
+ int slot = Integer.parseInt(parts[1]);
+ Object v = cs.peekFrame().getLocalVariableStore().getVariable(slot);
+ stack.push(v);
+ return pc + 1;
+ }
+}
From a7117717bd76ef791fa565e9e990336c4e10e8fa Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 21 Jul 2025 17:19:05 +0800
Subject: [PATCH 26/36] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E5=91=BD?=
=?UTF-8?q?=E4=BB=A4=E5=B7=A5=E5=8E=82=E4=B8=AD=E7=9A=84=E5=BC=95=E7=94=A8?=
=?UTF-8?q?=E5=91=BD=E4=BB=A4=E8=B7=AF=E5=BE=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 将 RLoadCommand、RPushCommand 和 RStoreCommand 的导入路径从 type.control.ref 改为 ref.control
- 这个改动统一了引用类型命令的包结构,与其他类型命令保持一致
---
.../java/org/jcnc/snow/vm/factories/CommandFactory.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
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 71479a3..fc4a3ed 100644
--- a/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java
+++ b/src/main/java/org/jcnc/snow/vm/factories/CommandFactory.java
@@ -6,9 +6,9 @@ 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.ref.RLoadCommand;
-import org.jcnc.snow.vm.commands.type.control.ref.RPushCommand;
-import org.jcnc.snow.vm.commands.type.control.ref.RStoreCommand;
+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;
From 5ef36d57009702a38722c6af72fa94ae5ff0778b Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 21 Jul 2025 17:23:43 +0800
Subject: [PATCH 27/36] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20R=5FPUSH?=
=?UTF-8?q?=E6=8C=87=E4=BB=A4=E7=B1=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 RPushCommand 类实现 Command 接口,用于处理引用类型推入操作
- 支持将字符串字面量等引用类型数据推入操作栈
---
.../vm/commands/ref/control/RPushCommand.java | 63 +++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
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
new file mode 100644
index 0000000..1386018
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RPushCommand.java
@@ -0,0 +1,63 @@
+package org.jcnc.snow.vm.commands.ref.control;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * The {@code RPushCommand} class implements the {@link Command} interface and represents the
+ * reference push instruction ({@code R_PUSH}) in the virtual machine.
+ *
+ *
+ * This instruction pushes a reference object, such as a String literal, onto the operand stack.
+ *
+ *
+ * Instruction format: {@code R_PUSH }
+ *
+ * - {@code }: The reference value (e.g., string) to be pushed onto the stack.
+ * If the literal contains spaces, all parts after {@code R_PUSH} are joined into a single string.
+ *
+ *
+ * Behavior:
+ *
+ * - Checks that the instruction has at least one parameter after the operator.
+ * - Concatenates all parameters after {@code R_PUSH} into a single string (separated by spaces).
+ * - Pushes the resulting string as a reference object onto the operand stack.
+ * - Increments the program counter to the next instruction.
+ * - Throws an {@code IllegalStateException} if the instruction is missing required parameters.
+ *
+ */
+public final class RPushCommand implements Command {
+
+ /**
+ * Executes the {@code R_PUSH} instruction, pushing a reference (such as a string literal)
+ * onto the operand stack.
+ *
+ * @param parts The instruction parameters. {@code parts[0]} is the operator ("R_PUSH"),
+ * {@code parts[1..]} are the parts of the literal to be concatenated and pushed.
+ * @param pc The current program counter value, indicating the instruction address being executed.
+ * @param stack The operand stack manager. The literal will be pushed onto this stack.
+ * @param lvs The local variable store. (Not used in this instruction.)
+ * @param cs The call stack manager. (Not used in this instruction.)
+ * @return The next program counter value ({@code pc + 1}), pointing to the next instruction.
+ * @throws IllegalStateException if the instruction is missing required parameters.
+ */
+ @Override
+ public int execute(String[] parts, int pc,
+ OperandStack stack,
+ LocalVariableStore lvs,
+ CallStack cs) {
+
+ if (parts.length < 2)
+ throw new IllegalStateException("R_PUSH missing parameter");
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 1; i < parts.length; i++) {
+ if (i > 1) sb.append(' ');
+ sb.append(parts[i]);
+ }
+ stack.push(sb.toString());
+ return pc + 1;
+ }
+}
From 33d89e99083f8244896d2e752c8a1b094caf54cb Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 21 Jul 2025 17:24:57 +0800
Subject: [PATCH 28/36] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20R=5FSTORE?=
=?UTF-8?q?=E6=8C=87=E4=BB=A4=E7=9A=84=E5=AE=9E=E7=8E=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 RStoreCommand 类,实现 Command接口
- 添加 R_STORE指令的执行逻辑
- 更新虚拟机的指令集,支持 R_STORE 指令
---
.../commands/ref/control/RStoreCommand.java | 58 +++++++++++++++++++
1 file changed, 58 insertions(+)
create mode 100644 src/main/java/org/jcnc/snow/vm/commands/ref/control/RStoreCommand.java
diff --git a/src/main/java/org/jcnc/snow/vm/commands/ref/control/RStoreCommand.java b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RStoreCommand.java
new file mode 100644
index 0000000..0237908
--- /dev/null
+++ b/src/main/java/org/jcnc/snow/vm/commands/ref/control/RStoreCommand.java
@@ -0,0 +1,58 @@
+package org.jcnc.snow.vm.commands.ref.control;
+
+import org.jcnc.snow.vm.interfaces.Command;
+import org.jcnc.snow.vm.module.CallStack;
+import org.jcnc.snow.vm.module.LocalVariableStore;
+import org.jcnc.snow.vm.module.OperandStack;
+
+/**
+ * The {@code RStoreCommand} class implements the {@link Command} interface and represents the
+ * reference store instruction ({@code R_STORE}) in the virtual machine.
+ *
+ *
+ * This instruction pops a reference object from the top of the operand stack
+ * and stores it in the local variable table at the specified slot index of the current stack frame.
+ *
+ *
+ * Instruction format: {@code R_STORE }
+ *
+ * - {@code }: The index in the local variable table where the reference will be stored.
+ *
+ *
+ * Behavior:
+ *
+ * - Parses the slot index from the instruction parameters.
+ * - Pops a reference object from the operand stack.
+ * - Stores the popped reference object into the local variable table of the current stack frame at the specified slot.
+ * - Increments the program counter to the next instruction.
+ * - If the operand stack is empty, a {@code java.util.EmptyStackException} may be thrown.
+ *
+ */
+public final class RStoreCommand implements Command {
+
+ /**
+ * Executes the {@code R_STORE} instruction, storing a reference object from the top of the operand stack
+ * into the local variable table of the current stack frame.
+ *
+ * @param parts The instruction parameters. {@code parts[0]} is the operator ("R_STORE"),
+ * {@code parts[1]} is the slot index.
+ * @param pc The current program counter value, indicating the instruction address being executed.
+ * @param stack The operand stack manager. The reference object will be popped from this stack.
+ * @param lvs The local variable store. (Not used directly, as the store from the current stack frame is used.)
+ * @param cs The call stack manager. The reference will be stored in 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.
+ * @throws java.util.EmptyStackException if the operand stack is empty when popping.
+ */
+ @Override
+ public int execute(String[] parts, int pc,
+ OperandStack stack,
+ LocalVariableStore lvs,
+ CallStack cs) {
+
+ int slot = Integer.parseInt(parts[1]);
+ Object v = stack.pop();
+ cs.peekFrame().getLocalVariableStore().setVariable(slot, v);
+ return pc + 1;
+ }
+}
From b30b6aeaaa166414839063f2b26503c22dd44466 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 21 Jul 2025 17:26:42 +0800
Subject: [PATCH 29/36] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=BC=95?=
=?UTF-8?q?=E7=94=A8=E7=B1=BB=E5=9E=8B=E6=8E=A7=E5=88=B6=E6=8C=87=E4=BB=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 R_PUSH、R_LOAD 和 R_STORE 指令,用于处理对象引用类型
- 这些指令分别用于推送、加载和存储对象引用到操作栈或本地变量表
---
.../org/jcnc/snow/vm/engine/VMOpCode.java | 71 +++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java b/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java
index 8e1a880..c333e34 100644
--- a/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java
+++ b/src/main/java/org/jcnc/snow/vm/engine/VMOpCode.java
@@ -1,5 +1,8 @@
package org.jcnc.snow.vm.engine;
+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.system.control.SyscallCommand;
import org.jcnc.snow.vm.commands.type.control.byte8.*;
import org.jcnc.snow.vm.commands.type.control.double64.*;
@@ -2483,6 +2486,74 @@ public class VMOpCode {
// endregion Double64
// endregion Conversion
+ // region Reference Control (0x00E0-0x00EF)
+ /**
+ * R_PUSH Opcode: Represents an operation that pushes an object reference (such as a String or any reference type)
+ * onto the operand stack.
+ * This opcode is implemented by the {@link RPushCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * - Parses the object reference literal (e.g., a string) from the instruction parameters.
+ * - Creates or interprets the reference as an object instance if necessary.
+ * - Pushes the reference object onto the operand stack.
+ * - Increments the program counter (PC) to proceed with the next sequential instruction.
+ *
+ *
+ * This opcode is commonly used for:
+ *
+ * - Pushing string literals, objects, or other references needed for subsequent operations.
+ * - Supplying parameters for method calls that expect reference types.
+ * - Initializing the operand stack with reference values for computation or storage.
+ *
+ */
+ public static final int R_PUSH = 0x00E0;
+ /**
+ * R_LOAD Opcode: Represents an operation that loads an object reference from the local variable table
+ * and pushes it onto the operand stack.
+ * This opcode is implemented by the {@link RLoadCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * - Parses the target slot index from the instruction parameters.
+ * - Retrieves the reference object stored at the specified slot in the local variable table
+ * of the current stack frame.
+ * - Pushes the retrieved reference onto the operand stack.
+ * - Increments the program counter (PC) to proceed with the next sequential instruction.
+ *
+ *
+ * This opcode is commonly used for:
+ *
+ * - Accessing local variables (such as function arguments or local object references) during method execution.
+ * - Preparing reference values for operations or method invocations.
+ * - Reusing objects previously stored in local variables.
+ *
+ */
+ public static final int R_LOAD = 0x00E1;
+ /**
+ * R_STORE Opcode: Represents an operation that pops an object reference from the top of the operand stack
+ * and stores it into the local variable table at the specified slot index.
+ * This opcode is implemented by the {@link RStoreCommand} class, which defines its specific execution logic.
+ *
+ * Execution Steps:
+ *
+ * - Parses the target slot index from the instruction parameters.
+ * - Pops the top reference object from the operand stack.
+ * - Stores the popped reference object into the specified slot in the local variable table
+ * of the current stack frame.
+ * - Increments the program counter (PC) to proceed with the next sequential instruction.
+ *
+ *
+ * This opcode is commonly used for:
+ *
+ * - Storing computation results or intermediate reference values for later use.
+ * - Passing object references to local variables in preparation for further operations or method calls.
+ * - Managing object lifetimes and ensuring correct referencing in scoped execution contexts.
+ *
+ */
+ public static final int R_STORE = 0x00E2;
+ // endregion
+
// region Stack Control (0x0100-0x01FF)
/**
* POP Opcode: Represents a stack operation that removes the top element from the operand stack.
From a454eed26fd825969862d431b4fa5f900350be5b Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 21 Jul 2025 22:52:40 +0800
Subject: [PATCH 30/36] =?UTF-8?q?test:=20=E9=87=8D=E6=9E=84=20Demo14=20?=
=?UTF-8?q?=E6=BC=94=E7=A4=BA=E4=BB=A3=E7=A0=81=20-=20=E7=A7=BB=E9=99=A4?=
=?UTF-8?q?=E4=BA=86=20Main.snow=20=E6=96=87=E4=BB=B6=E4=B8=AD=E7=9A=84?=
=?UTF-8?q?=E7=9B=B4=E6=8E=A5=E7=B3=BB=E7=BB=9F=E8=B0=83=E7=94=A8=20-=20?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=20OS.snow=20=E6=96=87=E4=BB=B6=EF=BC=8C?=
=?UTF-8?q?=E5=AE=9E=E7=8E=B0=20print=20=E5=87=BD=E6=95=B0=E5=B0=81?=
=?UTF-8?q?=E8=A3=85=20-=20=E4=BF=AE=E6=94=B9=20Main.snow=EF=BC=8C?=
=?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=96=B0=E7=9A=84=20print=20=E5=87=BD?=
=?UTF-8?q?=E6=95=B0=E6=9B=BF=E4=BB=A3=E7=B3=BB=E7=BB=9F=E8=B0=83=E7=94=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
playground/Demo/Demo14/Main.snow | 3 +--
playground/Demo/Demo14/OS.snow | 11 +++++++++++
2 files changed, 12 insertions(+), 2 deletions(-)
create mode 100644 playground/Demo/Demo14/OS.snow
diff --git a/playground/Demo/Demo14/Main.snow b/playground/Demo/Demo14/Main.snow
index 8e5baff..7ef2b05 100644
--- a/playground/Demo/Demo14/Main.snow
+++ b/playground/Demo/Demo14/Main.snow
@@ -1,10 +1,9 @@
module: Main
import: os
-
function: main
return_type: void
body:
- syscall("PRINT",2222+1)
+ print(222)
end body
end function
end module
\ No newline at end of file
diff --git a/playground/Demo/Demo14/OS.snow b/playground/Demo/Demo14/OS.snow
new file mode 100644
index 0000000..f985171
--- /dev/null
+++ b/playground/Demo/Demo14/OS.snow
@@ -0,0 +1,11 @@
+module: Main
+ import: os
+ function: print
+ parameter:
+ declare i1: int
+ return_type: void
+ body:
+ syscall("PRINT",i1)
+ end body
+ end function
+end module
\ No newline at end of file
From 074f0b6809bec76c0547f0c467ead58d0e86ba68 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 21 Jul 2025 22:54:49 +0800
Subject: [PATCH 31/36] =?UTF-8?q?chore:=20=E6=B7=BB=E5=8A=A0=E5=BA=93?=
=?UTF-8?q?=E5=87=BD=E6=95=B0=E5=AE=9E=E7=8E=B0=E6=89=93=E5=8D=B0=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lib/os/OS.snow | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 lib/os/OS.snow
diff --git a/lib/os/OS.snow b/lib/os/OS.snow
new file mode 100644
index 0000000..f985171
--- /dev/null
+++ b/lib/os/OS.snow
@@ -0,0 +1,11 @@
+module: Main
+ import: os
+ function: print
+ parameter:
+ declare i1: int
+ return_type: void
+ body:
+ syscall("PRINT",i1)
+ end body
+ end function
+end module
\ No newline at end of file
From e84aedc100f44de9646439e180ddae138f565470 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 21 Jul 2025 23:13:50 +0800
Subject: [PATCH 32/36] =?UTF-8?q?test:=20=E4=BF=AE=E6=94=B9=E6=A8=A1?=
=?UTF-8?q?=E5=9D=97=E5=90=8D=E7=A7=B0=E4=B8=BA=E5=B0=8F=E5=86=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lib/os/OS.snow | 2 +-
playground/Demo/Demo14/OS.snow | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/os/OS.snow b/lib/os/OS.snow
index f985171..6026d43 100644
--- a/lib/os/OS.snow
+++ b/lib/os/OS.snow
@@ -1,4 +1,4 @@
-module: Main
+module: os
import: os
function: print
parameter:
diff --git a/playground/Demo/Demo14/OS.snow b/playground/Demo/Demo14/OS.snow
index f985171..6026d43 100644
--- a/playground/Demo/Demo14/OS.snow
+++ b/playground/Demo/Demo14/OS.snow
@@ -1,4 +1,4 @@
-module: Main
+module: os
import: os
function: print
parameter:
From 6d79e28c51c002ffe8f12ae64589829f4a26bf26 Mon Sep 17 00:00:00 2001
From: Luke
Date: Mon, 21 Jul 2025 23:42:03 +0800
Subject: [PATCH 33/36] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20SyscallC?=
=?UTF-8?q?ommand=20=E7=B1=BB=E5=B9=B6=E4=BC=98=E5=8C=96=E6=96=87=E6=A1=A3?=
=?UTF-8?q?=E6=B3=A8=E9=87=8A-=20=E9=87=8D=E6=96=B0=E7=BB=84=E7=BB=87?=
=?UTF-8?q?=E7=B1=BB=E7=BB=93=E6=9E=84=EF=BC=8C=E4=BC=98=E5=8C=96=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=E5=B8=83=E5=B1=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 更新文档注释,使其更加清晰和详细
- 优化部分方法实现,提高可读性和可维护性
---
.../system/control/SyscallCommand.java | 261 ++++++------------
1 file changed, 88 insertions(+), 173 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java b/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java
index eebcd28..90caf8b 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/system/control/SyscallCommand.java
@@ -16,53 +16,47 @@ import java.util.*;
import static java.nio.file.StandardOpenOption.*;
/**
- * {@code SyscallCommand} —— I/O syscall 子命令集合指令。
+ * SyscallCommand —— 虚拟机系统调用(SYSCALL)指令实现。
*
*
- * 封装类 UNIX 文件描述符(File Descriptor, FD)操作、文件/网络 I/O、管道、fd 复制、多路复用(select)等系统调用能力,
- * 基于 Java NIO 统一实现,供虚拟机指令集以 SYSCALL 形式扩展使用。
+ * 本类负责将虚拟机指令集中的 SYSCALL 进行分派,模拟现实系统常见的文件、网络、管道、标准输出等操作,
+ * 通过操作数栈完成参数、返回值传递,并借助文件描述符表(FDTable)进行底层资源管理。
+ * 所有 I/O 相关功能均基于 Java NIO 实现,兼容多种 I/O 场景。
*
*
- * 栈操作约定:
+ * 参数与栈约定:
*
- * - 参数按“右值先入栈、左值后入栈”的顺序推入操作数栈,执行 {@code SYSCALL } 后出栈。
- * - 即:调用 SYSCALL OPEN path flags mode,入栈顺序为 path(先入)→ flags → mode(后入),出栈时 mode→flags→path 弹出。
+ * - 所有调用参数,均按“右值先入、左值后入”顺序压入 {@link OperandStack}。
+ * - SYSCALL 指令自动弹出参数并处理结果;返回值(如描述符、读取长度、是否成功等)压回栈顶。
*
*
- * 返回值说明:
+ * 异常与失败处理:
*
- * - 成功:压入正值(如 FD、实际数据、字节数、0 表示成功)。
- * - 失败:统一压入 -1,后续可扩展为 errno 机制。
+ * - 系统调用失败或遇到异常时,均向操作数栈压入 {@code -1},以便调用者统一检测。
*
*
- * 支持的子命令(部分):
+ * 支持的子命令示例:
*
* - PRINT / PRINTLN —— 控制台输出
- * - OPEN / CLOSE / READ / WRITE / SEEK —— 文件 I/O 操作
- * - PIPE / DUP —— 管道和 FD 复制
- * - SOCKET / CONNECT / BIND / LISTEN / ACCEPT —— 网络套接字
- * - SELECT —— I/O 多路复用
+ * - OPEN / CLOSE / READ / WRITE / SEEK —— 文件相关操作
+ * - PIPE / DUP —— 管道与文件描述符复制
+ * - SOCKET / CONNECT / BIND / LISTEN / ACCEPT —— 网络通信
+ * - SELECT —— 多通道 I/O 就绪检测
*
*/
public class SyscallCommand implements Command {
-
-
- /*--------------------------------------------------------------------------------*/
-
/**
- * 执行 SYSCALL 子命令:
- *
- * 按照 parts[1] 指定的 SYSCALL 子命令,结合虚拟机栈与资源表完成对应的系统调用模拟。
- * 支持基础文件、网络、管道等 I/O 操作,并对异常统一处理。
- *
+ * 分发并执行 SYSCALL 子命令,根据子命令类型从操作数栈取出参数、操作底层资源,并将结果压回栈顶。
*
- * @param parts 指令字符串数组,parts[1] 为子命令
+ * @param parts 指令及子命令参数分割数组,parts[1]为子命令名
* @param pc 当前指令计数器
* @param stack 操作数栈
* @param locals 局部变量表
* @param callStack 调用栈
- * @return 下一条指令的 pc 值(默认 pc+1)
+ * @return 下一条指令的 pc 值(通常为 pc+1)
+ * @throws IllegalArgumentException 缺少子命令参数时抛出
+ * @throws UnsupportedOperationException 不支持的 SYSCALL 子命令时抛出
*/
@Override
public int execute(String[] parts, int pc,
@@ -70,46 +64,27 @@ public class SyscallCommand implements Command {
LocalVariableStore locals,
CallStack callStack) {
- if (parts.length < 2)
- throw new IllegalArgumentException("SYSCALL needs sub-command");
+ if (parts.length < 2) {
+ throw new IllegalArgumentException("SYSCALL missing subcommand");
+ }
String cmd = parts[1].toUpperCase(Locale.ROOT);
try {
switch (cmd) {
-
- /*==================== 文件 / 目录 ====================*/
-
- /*
- * OPEN —— 打开文件,返回文件描述符(File Descriptor, FD)。
- * 入栈(push)顺序: path(文件路径, String), flags(int), mode(int,文件权限,暂未用)
- * 出栈(pop)顺序: mode → flags → path
- * 成功返回 fd,失败返回 -1。
- */
+ // 文件相关操作
case "OPEN" -> {
- int mode = (Integer) stack.pop(); // 目前未用
+ int mode = (Integer) stack.pop();
int flags = (Integer) stack.pop();
String path = String.valueOf(stack.pop());
FileChannel fc = FileChannel.open(Paths.get(path), flagsToOptions(flags));
stack.push(FDTable.register(fc));
}
- /*
- * CLOSE —— 关闭文件描述符。
- * 入栈顺序: fd(int)
- * 出栈顺序: fd
- * 返回 0 成功,-1 失败
- */
case "CLOSE" -> {
int fd = (Integer) stack.pop();
FDTable.close(fd);
stack.push(0);
}
- /*
- * READ —— 从 fd 读取指定字节数。
- * 入栈顺序: fd(int), count(int,字节数)
- * 出栈顺序: count → fd
- * 返回:byte[](实际读取的数据,EOF 时长度为 0)
- */
case "READ" -> {
int count = (Integer) stack.pop();
int fd = (Integer) stack.pop();
@@ -126,12 +101,6 @@ public class SyscallCommand implements Command {
buf.get(out);
stack.push(out);
}
- /*
- * WRITE —— 向 fd 写数据。
- * 入栈顺序: fd(int), data(byte[] 或 String)
- * 出栈顺序: data → fd
- * 返回写入字节数,失败 -1
- */
case "WRITE" -> {
Object dataObj = stack.pop();
int fd = (Integer) stack.pop();
@@ -146,12 +115,6 @@ public class SyscallCommand implements Command {
int written = wch.write(ByteBuffer.wrap(data));
stack.push(written);
}
- /*
- * SEEK —— 文件定位,移动文件读写指针。
- * 入栈顺序: fd(int), offset(long/int), whence(int, 0=SET,1=CUR,2=END)
- * 出栈顺序: whence → offset → fd
- * 返回新位置(long),失败 -1
- */
case "SEEK" -> {
int whence = (Integer) stack.pop();
long off = ((Number) stack.pop()).longValue();
@@ -165,58 +128,32 @@ public class SyscallCommand implements Command {
case 0 -> sbc.position(off);
case 1 -> sbc.position(sbc.position() + off);
case 2 -> sbc.position(sbc.size() + off);
- default -> throw new IllegalArgumentException("bad whence");
+ default -> throw new IllegalArgumentException("Invalid offset type");
};
stack.push(newPos);
}
- /*==================== 管道 / FD 相关 ====================*/
-
- /*
- * PIPE —— 创建匿名管道。
- * 入栈顺序: (无)
- * 出栈顺序: (无)
- * 返回顺序: write fd(先压栈),read fd(后压栈)
- */
+ // 管道与描述符操作
case "PIPE" -> {
Pipe p = Pipe.open();
- stack.push(FDTable.register(p.sink())); // write fd
- stack.push(FDTable.register(p.source())); // read fd
+ stack.push(FDTable.register(p.sink()));
+ stack.push(FDTable.register(p.source()));
}
- /*
- * DUP —— 复制一个已存在的 fd(dup)。
- * 入栈顺序: oldfd(int)
- * 出栈顺序: oldfd
- * 返回新的 fd,失败 -1
- */
case "DUP" -> {
int oldfd = (Integer) stack.pop();
stack.push(FDTable.dup(oldfd));
}
- /*==================== 网络相关 ====================*/
-
- /*
- * SOCKET —— 创建套接字,支持 stream/dgram。
- * 入栈顺序: domain(int, AF_INET=2等), type(int, 1=STREAM,2=DGRAM), protocol(int, 预留)
- * 出栈顺序: protocol → type → domain
- * 返回 fd
- */
+ // 网络相关
case "SOCKET" -> {
- int proto = (Integer) stack.pop(); // 预留,暂不用
- int type = (Integer) stack.pop(); // 1=STREAM,2=DGRAM
- int domain = (Integer) stack.pop(); // AF_INET=2……
+ int proto = (Integer) stack.pop();
+ int type = (Integer) stack.pop();
+ int domain = (Integer) stack.pop();
Channel ch = (type == 1)
? SocketChannel.open()
: DatagramChannel.open();
stack.push(FDTable.register(ch));
}
- /*
- * CONNECT —— 发起 TCP 连接。
- * 入栈顺序: fd(int), host(String), port(int)
- * 出栈顺序: port → host → fd
- * 返回 0 成功,-1 失败
- */
case "CONNECT" -> {
int port = (Integer) stack.pop();
String host = String.valueOf(stack.pop());
@@ -225,14 +162,10 @@ public class SyscallCommand implements Command {
if (ch instanceof SocketChannel sc) {
sc.connect(new InetSocketAddress(host, port));
stack.push(0);
- } else stack.push(-1);
+ } else {
+ stack.push(-1);
+ }
}
- /*
- * BIND —— 绑定端口。
- * 入栈顺序: fd(int), host(String), port(int)
- * 出栈顺序: port → host → fd
- * 返回 0 成功,-1 失败
- */
case "BIND" -> {
int port = (Integer) stack.pop();
String host = String.valueOf(stack.pop());
@@ -241,45 +174,32 @@ public class SyscallCommand implements Command {
if (ch instanceof ServerSocketChannel ssc) {
ssc.bind(new InetSocketAddress(host, port));
stack.push(0);
- } else stack.push(-1);
+ } else {
+ stack.push(-1);
+ }
}
- /*
- * LISTEN —— 监听 socket,兼容 backlog。
- * 入栈顺序: fd(int), backlog(int)
- * 出栈顺序: backlog → fd
- * 返回 0 成功,-1 失败
- * 注意:Java NIO 打开 ServerSocketChannel 已自动监听,无 backlog 处理,行为和 UNIX 有区别。
- */
case "LISTEN" -> {
int backlog = (Integer) stack.pop();
int fd = (Integer) stack.pop();
Channel ch = FDTable.get(fd);
- if (ch instanceof ServerSocketChannel) stack.push(0);
- else stack.push(-1);
+ if (ch instanceof ServerSocketChannel) {
+ stack.push(0);
+ } else {
+ stack.push(-1);
+ }
}
- /*
- * ACCEPT —— 接收连接。
- * 入栈顺序: fd(int)
- * 出栈顺序: fd
- * 返回新连接 fd,失败 -1
- */
case "ACCEPT" -> {
int fd = (Integer) stack.pop();
Channel ch = FDTable.get(fd);
if (ch instanceof ServerSocketChannel ssc) {
SocketChannel cli = ssc.accept();
stack.push(FDTable.register(cli));
- } else stack.push(-1);
+ } else {
+ stack.push(-1);
+ }
}
- /*==================== 多路复用 ====================*/
-
- /*
- * SELECT —— I/O 多路复用,监视多个 fd 是否可读写。
- * 入栈顺序: fds(List), timeout_ms(long)
- * 出栈顺序: timeout_ms → fds
- * 返回: 就绪 fd 列表(List)
- */
+ // 多路复用
case "SELECT" -> {
long timeout = ((Number) stack.pop()).longValue();
@SuppressWarnings("unchecked")
@@ -298,107 +218,94 @@ public class SyscallCommand implements Command {
int ready = sel.select(timeout);
List readyFds = new ArrayList<>();
if (ready > 0) {
- for (SelectionKey k : sel.selectedKeys())
+ for (SelectionKey k : sel.selectedKeys()) {
readyFds.add((Integer) k.attachment());
+ }
}
stack.push(readyFds);
sel.close();
}
-
- /*==================== 控制台输出 ====================*/
-
- /*
- * PRINT —— 控制台输出(无换行)。
- * 入栈顺序: data(任意基本类型或其包装类型、String、byte[] 等)
- * 出栈顺序: data
- * 返回 0
- */
+ // 控制台输出
case "PRINT" -> {
Object dataObj = stack.pop();
output(dataObj, false);
- stack.push(0); // success
+ stack.push(0);
}
-
- /* PRINTLN —— 控制台输出(自动换行)。*/
case "PRINTLN" -> {
Object dataObj = stack.pop();
output(dataObj, true);
- stack.push(0); // success
+ stack.push(0);
}
-
- /*==================== 其它未实现/扩展命令 ====================*/
-
- /*
- * 其它自定义 syscall 子命令未实现
- */
- default -> throw new UnsupportedOperationException("Unsupported SYSCALL: " + cmd);
+ default -> throw new UnsupportedOperationException("Unsupported SYSCALL subcommand: " + cmd);
}
} catch (Exception e) {
- // 统一异常处理,异常时压入 -1
pushErr(stack, e);
}
- // 默认:下一条指令
return pc + 1;
}
- /*------------------------------------ 工具方法 ------------------------------------*/
-
/**
- * POSIX open 标志到 Java NIO OpenOption 的映射
+ * 根据传入的文件打开标志,构造 NIO {@link OpenOption} 集合。
*
- * 将 Linux/UNIX 的 open 调用 flags 参数,转换为 Java NIO 的 OpenOption 集合。
- * 目前仅支持 WRITE(0x1)、READ、CREATE(0x40)、TRUNCATE(0x200)、APPEND(0x400)等常用标志。
+ * 本方法负责将底层虚拟机传递的 flags 整数型位域,转换为 Java NIO 标准的文件打开选项集合,
+ * 以支持文件读、写、创建、截断、追加等多种访问场景。
+ * 常用于 SYSCALL 的 OPEN 子命令。
*
*
- * @param flags POSIX 风格 open 标志(如 O_WRONLY=0x1, O_CREAT=0x40 等)
- * @return 映射后的 OpenOption 集合
+ * @param flags 文件打开模式标志。各标志可组合使用,具体含义请参见虚拟机文档。
+ * @return 转换后的 OpenOption 集合,可直接用于 FileChannel.open 等 NIO 方法
*/
private static Set flagsToOptions(int flags) {
Set opts = new HashSet<>();
- // 0x1 = WRITE,否则默认 READ
+ // 如果有写入标志,则添加WRITE,否则默认为READ。
if ((flags & 0x1) != 0) opts.add(WRITE);
else opts.add(READ);
+ // 如果包含创建标志,允许创建文件。
if ((flags & 0x40) != 0) opts.add(CREATE);
+ // 包含截断标志,打开时清空内容。
if ((flags & 0x200) != 0) opts.add(TRUNCATE_EXISTING);
+ // 包含追加标志,文件写入时追加到末尾。
if ((flags & 0x400) != 0) opts.add(APPEND);
return opts;
}
/**
- * 统一异常处理:
+ * 捕获所有异常并统一处理,操作数栈压入 -1 代表本次系统调用失败。
*
- * 捕获 syscall 内部所有异常,将 -1 压入操作数栈,表示系统调用失败(暂不区分错误类型)。
- * 常见异常如文件不存在、权限不足、通道类型不符、网络故障等。
+ * 本方法是全局错误屏障,任何命令异常都会转换为虚拟机通用的失败信号,
+ * 保证上层调用逻辑不会被异常打断。实际应用中可拓展错误码机制。
*
*
- * @param stack 当前操作数栈
- * @param e 捕获的异常对象
+ * @param stack 操作数栈,将失败信号写入此栈
+ * @param e 抛出的异常对象,可在调试时输出日志
*/
private static void pushErr(OperandStack stack, Exception e) {
- stack.push(-1); // 目前统一用 -1,后续可按异常类型/errno 映射
+ stack.push(-1);
+ System.err.println("Syscall exception: " + e);
}
/**
- * 统一的控制台输出辅助方法,支持:
- *
- * - 所有基本类型及其包装类(int、double、long、float…)
- * - String、byte[]、char[] 以及其他原生数组
- * - 任意 Object(调用 {@code toString})
- *
+ * 控制台输出通用方法,支持基本类型、字节数组、任意数组、对象等。
+ *
+ * 该方法用于 SYSCALL PRINT/PRINTLN,将任意类型对象转为易读字符串输出到标准输出流。
+ * 字节数组自动按 UTF-8 解码,其它原生数组按格式化字符串输出。
+ *
*
- * @param obj 要输出的对象
- * @param newline 是否追加换行
+ * @param obj 待输出的内容,可以为任何类型(如基本类型、byte[]、数组、对象等)
+ * @param newline 是否自动换行。如果为 true,则在输出后换行;否则直接输出。
*/
private static void output(Object obj, boolean newline) {
String str;
if (obj == null) {
str = "null";
} else if (obj instanceof byte[] bytes) {
+ // 字节数组作为文本输出
str = new String(bytes);
} else if (obj.getClass().isArray()) {
+ // 其它数组格式化输出
str = arrayToString(obj);
} else {
str = obj.toString();
@@ -408,7 +315,15 @@ public class SyscallCommand implements Command {
}
/**
- * 将各种原生数组转换成可读字符串。
+ * 将各种原生数组和对象数组转换为可读字符串,便于控制台输出和调试。
+ *
+ * 本方法针对 int、long、double、float、short、char、byte、boolean 等所有原生数组类型
+ * 以及对象数组都能正确格式化,统一输出格式风格,避免显示为类型 hashCode。
+ * 若为不支持的类型,返回通用提示字符串。
+ *
+ *
+ * @param array 任意原生数组或对象数组
+ * @return 该数组的可读字符串表示
*/
private static String arrayToString(Object array) {
if (array instanceof int[] a) return Arrays.toString(a);
From 07b7d5c40e6f548ea60fb6afb865d4fb3de2c258 Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 22 Jul 2025 16:35:43 +0800
Subject: [PATCH 34/36] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E7=A8=8B?=
=?UTF-8?q?=E5=BA=8F=E7=BB=88=E6=AD=A2=E5=92=8C=E5=87=BD=E6=95=B0=E8=B0=83?=
=?UTF-8?q?=E7=94=A8=E7=9A=84=E8=BE=93=E5=87=BA=E6=A0=BC=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 在 CallCommand 中添加换行符,改善函数调用的输出可读性
- 修改 HaltCommand 中的终止消息格式,提高信息的清晰度
---
.../org/jcnc/snow/vm/commands/flow/control/CallCommand.java | 2 +-
.../org/jcnc/snow/vm/commands/system/control/HaltCommand.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java b/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java
index 1171885..26dda90 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/flow/control/CallCommand.java
@@ -80,7 +80,7 @@ public class CallCommand implements Command {
new MethodContext("subroutine@" + targetAddr, null));
callStack.pushFrame(newFrame);
- System.out.println("Calling function at address: " + targetAddr);
+ System.out.println("\nCalling function at address: " + targetAddr);
return targetAddr; // jump
}
}
diff --git a/src/main/java/org/jcnc/snow/vm/commands/system/control/HaltCommand.java b/src/main/java/org/jcnc/snow/vm/commands/system/control/HaltCommand.java
index 06a5da4..71e208c 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/system/control/HaltCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/system/control/HaltCommand.java
@@ -59,7 +59,7 @@ public class HaltCommand implements Command {
@Override
public int execute(String[] parts, int currentPC, OperandStack operandStack, LocalVariableStore localVariableStore, CallStack callStack) {
// Output the termination message
- LoggingUtils.logInfo("Process has ended", "\n");
+ LoggingUtils.logInfo("\nProcess has ended", "");
// Return -1 to indicate the program termination, and the virtual machine will not continue executing subsequent instructions
return -1;
From 70feb1fe5e94a771d591ad477f6b528562d6fadc Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 22 Jul 2025 16:36:23 +0800
Subject: [PATCH 35/36] =?UTF-8?q?style:=20=E4=BC=98=E5=8C=96=20RetCommand?=
=?UTF-8?q?=20=E7=B1=BB=E7=9A=84=E8=BE=93=E5=87=BA=E6=A0=BC=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/org/jcnc/snow/vm/commands/flow/control/RetCommand.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/org/jcnc/snow/vm/commands/flow/control/RetCommand.java b/src/main/java/org/jcnc/snow/vm/commands/flow/control/RetCommand.java
index 1e4ebd5..505f378 100644
--- a/src/main/java/org/jcnc/snow/vm/commands/flow/control/RetCommand.java
+++ b/src/main/java/org/jcnc/snow/vm/commands/flow/control/RetCommand.java
@@ -44,7 +44,7 @@ public class RetCommand implements Command {
finished.getLocalVariableStore().clearVariables();
int returnAddr = finished.getReturnAddress();
- System.out.println("Return " + returnAddr);
+ System.out.println("\nReturn " + returnAddr);
return returnAddr;
}
}
From 1605390f0863655d6b72781597bd020b8faf2f62 Mon Sep 17 00:00:00 2001
From: Luke
Date: Tue, 22 Jul 2025 16:37:53 +0800
Subject: [PATCH 36/36] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=87=BD?=
=?UTF-8?q?=E6=95=B0=E6=9C=AB=E5=B0=BE=20CALL=20=E6=8C=87=E4=BB=A4?=
=?UTF-8?q?=E7=9A=84=E6=9C=AA=E8=A7=A3=E6=9E=90=E7=AC=A6=E5=8F=B7=E9=97=AE?=
=?UTF-8?q?=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 在函数开始时登记函数入口地址
- 在函数末尾强制添加 RET 或 HALT 指令
- 优化了代码注释和格式
---
.../backend/builder/VMCodeGenerator.java | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/src/main/java/org/jcnc/snow/compiler/backend/builder/VMCodeGenerator.java b/src/main/java/org/jcnc/snow/compiler/backend/builder/VMCodeGenerator.java
index 8370e90..2458b9e 100644
--- a/src/main/java/org/jcnc/snow/compiler/backend/builder/VMCodeGenerator.java
+++ b/src/main/java/org/jcnc/snow/compiler/backend/builder/VMCodeGenerator.java
@@ -1,6 +1,7 @@
package org.jcnc.snow.compiler.backend.builder;
import org.jcnc.snow.compiler.backend.core.InstructionGenerator;
+import org.jcnc.snow.compiler.backend.utils.OpHelper;
import org.jcnc.snow.compiler.ir.core.IRFunction;
import org.jcnc.snow.compiler.ir.core.IRInstruction;
import org.jcnc.snow.compiler.ir.value.IRVirtualRegister;
@@ -16,7 +17,7 @@ import java.util.stream.Collectors;
* 仅负责根据指令类型分发到对应的 {@link InstructionGenerator} 子类完成实际生成。
*
*
- * 工作流程简述:
+ * 工作流程简述:
*
* - 接收一组已注册的 IR 指令生成器,并建立类型到生成器的映射表。
* - 遍历 IR 函数体的每条指令,根据类型找到对应的生成器,调用其 generate 方法生成 VM 指令。
@@ -74,18 +75,26 @@ public final class VMCodeGenerator {
*/
public void generate(IRFunction fn) {
this.currentFn = fn.name();
- out.beginFunction(currentFn); // 输出函数起始
+
+ /* 登记函数入口地址 —— 解决 CALL 未解析符号问题 */
+ out.beginFunction(currentFn);
+
+ /* 逐条分发 IR 指令给对应的生成器 */
for (IRInstruction ins : fn.body()) {
@SuppressWarnings("unchecked")
- // 取得与当前 IR 指令类型匹配的生成器(泛型强转消除类型警告)
InstructionGenerator gen =
(InstructionGenerator) registry.get(ins.getClass());
if (gen == null) {
throw new IllegalStateException("Unsupported IR: " + ins);
}
- // 通过多态分发到实际生成器
gen.generate(ins, out, slotMap, currentFn);
}
- out.endFunction(); // 输出函数结束
+
+ /* 强制补上函数结尾的返回/终止指令 */
+ String retOpcode = "main".equals(currentFn) ? "HALT" : "RET";
+ out.emit(OpHelper.opcode(retOpcode));
+
+ /* 结束函数 */
+ out.endFunction();
}
}