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 场景。 *
* - * 栈操作约定: + *参数与栈约定:
*异常与失败处理:
*支持的子命令示例:
*- * 按照 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- * 将 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- * 捕获 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); } /** - * 统一的控制台输出辅助方法,支持: - *+ * 该方法用于 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);