From 046a96525b8e3d64cf1a4ac4a0c0c20fed1025d8 Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Mon, 23 Feb 2026 13:51:15 +0100 Subject: [PATCH 1/9] Fix interpreter shift/pop with default @_/@ARGV argument The parser injects the default array (e.g. @main::ARGV at top level, @_ inside a sub) directly as OperatorNode("@", IdentifierNode) on the shift/pop node's operand. The bytecode compiler handlers were expecting the operand to always be a ListNode wrapper, causing: shift requires array argument at -e line 2, near "say shift" Fix: accept operand as either: - OperatorNode("@", ...) directly (default @_/@ARGV case) - ListNode containing OperatorNode("@", ...) (explicit array case) Verified: ./jperl --interpreter -E 'say shift' 123 => 123 ./jperl --interpreter -E 'say pop' 123 => 123 sub foo { say shift } foo("hello") => hello my @a=(1,2,3); say shift @a; say pop @a => 1 / 3 --- .../backend/bytecode/CompileOperator.java | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java index 4f2a84a88..29cf81ebc 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java +++ b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java @@ -894,19 +894,24 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode bytecodeCompiler.lastResultReg = rd; } else if (op.equals("pop")) { // Array pop: $x = pop @array or $x = pop @$ref - // operand: ListNode containing OperatorNode("@", IdentifierNode or OperatorNode) - if (node.operand == null || !(node.operand instanceof ListNode)) { + // operand: OperatorNode("@", ...) directly (default @_/@ARGV injected by parser) + // or ListNode containing OperatorNode("@", IdentifierNode or OperatorNode) + if (node.operand == null) { bytecodeCompiler.throwCompilerException("pop requires array argument"); } - ListNode list = (ListNode) node.operand; - if (list.elements.isEmpty() || !(list.elements.get(0) instanceof OperatorNode)) { - bytecodeCompiler.throwCompilerException("pop requires array variable"); - } - - OperatorNode arrayOp = (OperatorNode) list.elements.get(0); - if (!arrayOp.operator.equals("@")) { + OperatorNode arrayOp; + if (node.operand instanceof OperatorNode directOp && directOp.operator.equals("@")) { + // Direct OperatorNode("@", ...) - default array case + arrayOp = directOp; + } else if (node.operand instanceof ListNode list && !list.elements.isEmpty() + && list.elements.get(0) instanceof OperatorNode listOp + && listOp.operator.equals("@")) { + // ListNode-wrapped OperatorNode("@", ...) - explicit array case + arrayOp = listOp; + } else { bytecodeCompiler.throwCompilerException("pop requires array variable: pop @array"); + return; } int arrayReg = -1; // Will be assigned in if/else blocks @@ -953,19 +958,24 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode bytecodeCompiler.lastResultReg = rd; } else if (op.equals("shift")) { // Array shift: $x = shift @array or $x = shift @$ref - // operand: ListNode containing OperatorNode("@", IdentifierNode or OperatorNode) - if (node.operand == null || !(node.operand instanceof ListNode)) { + // operand: OperatorNode("@", ...) directly (default @_/@ARGV injected by parser) + // or ListNode containing OperatorNode("@", IdentifierNode or OperatorNode) + if (node.operand == null) { bytecodeCompiler.throwCompilerException("shift requires array argument"); } - ListNode list = (ListNode) node.operand; - if (list.elements.isEmpty() || !(list.elements.get(0) instanceof OperatorNode)) { - bytecodeCompiler.throwCompilerException("shift requires array variable"); - } - - OperatorNode arrayOp = (OperatorNode) list.elements.get(0); - if (!arrayOp.operator.equals("@")) { + OperatorNode arrayOp; + if (node.operand instanceof OperatorNode directOp && directOp.operator.equals("@")) { + // Direct OperatorNode("@", ...) - default array case + arrayOp = directOp; + } else if (node.operand instanceof ListNode list && !list.elements.isEmpty() + && list.elements.get(0) instanceof OperatorNode listOp + && listOp.operator.equals("@")) { + // ListNode-wrapped OperatorNode("@", ...) - explicit array case + arrayOp = listOp; + } else { bytecodeCompiler.throwCompilerException("shift requires array variable: shift @array"); + return; } int arrayReg = -1; // Will be assigned in if/else blocks From ea3404e52136d2e97e724260e203eb6836ed8548 Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Mon, 23 Feb 2026 13:57:01 +0100 Subject: [PATCH 2/9] Add unpack operator to bytecode interpreter pack was already implemented but unpack was missing, causing: Unsupported operator: unpack at ExifTool.pm line 2257 Changes: - Opcodes.java: add UNPACK = 305 - CompileOperator.java: add 'unpack' to the generic list-op handler - MiscOpcodeHandler.java: add UNPACK -> Unpack.unpack(ctx, argsArray) - BytecodeInterpreter.java: add UNPACK to the MiscOpcodeHandler case group - InterpretedCode.java: add disassembly for all misc list operators (PACK, UNPACK, CRYPT, LOCALTIME, GMTIME, CHMOD, UNLINK, etc.) Verified: pack('CC', 65, 66) => 2-byte string unpack('CC', $packed) => (65, 66) --- .../backend/bytecode/BytecodeInterpreter.java | 1 + .../backend/bytecode/CompileOperator.java | 7 +-- .../backend/bytecode/InterpretedCode.java | 49 +++++++++++++++++++ .../backend/bytecode/MiscOpcodeHandler.java | 2 + .../perlonjava/backend/bytecode/Opcodes.java | 4 ++ 5 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java index d169ef0a3..c0faf64ca 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java +++ b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java @@ -1997,6 +1997,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c case Opcodes.CALLER: case Opcodes.EACH: case Opcodes.PACK: + case Opcodes.UNPACK: case Opcodes.VEC: case Opcodes.LOCALTIME: case Opcodes.GMTIME: diff --git a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java index 29cf81ebc..f794df4c6 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java +++ b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java @@ -2708,9 +2708,9 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode } else if (op.equals("chmod") || op.equals("unlink") || op.equals("utime") || op.equals("rename") || op.equals("link") || op.equals("readlink") || op.equals("umask") || op.equals("system") || op.equals("pack") || - op.equals("vec") || op.equals("crypt") || op.equals("localtime") || - op.equals("gmtime") || op.equals("caller") || op.equals("fileno") || - op.equals("getc") || op.equals("qx")) { + op.equals("unpack") || op.equals("vec") || op.equals("crypt") || + op.equals("localtime") || op.equals("gmtime") || op.equals("caller") || + op.equals("fileno") || op.equals("getc") || op.equals("qx")) { // Generic handler for operators that take arguments and call runtime methods // Format: OPCODE rd argsReg ctx // argsReg must be a RuntimeList @@ -2753,6 +2753,7 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode case "caller" -> Opcodes.CALLER; case "each" -> Opcodes.EACH; case "pack" -> Opcodes.PACK; + case "unpack" -> Opcodes.UNPACK; case "vec" -> Opcodes.VEC; case "localtime" -> Opcodes.LOCALTIME; case "gmtime" -> Opcodes.GMTIME; diff --git a/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java b/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java index d14d144d2..541699e0f 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java +++ b/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java @@ -1286,6 +1286,55 @@ public String disassemble() { .append(".next(), alias $").append(stringPool[nameIdx]).append(" and goto ").append(fgBody).append("\n"); break; } + // Misc list operators: OPCODE rd argsReg ctx + case Opcodes.PACK: + case Opcodes.UNPACK: + case Opcodes.CRYPT: + case Opcodes.LOCALTIME: + case Opcodes.GMTIME: + case Opcodes.CHMOD: + case Opcodes.UNLINK: + case Opcodes.UTIME: + case Opcodes.RENAME: + case Opcodes.LINK: + case Opcodes.READLINK: + case Opcodes.UMASK: + case Opcodes.GETC: + case Opcodes.FILENO: + case Opcodes.SYSTEM: + case Opcodes.CALLER: + case Opcodes.EACH: + case Opcodes.VEC: { + rd = bytecode[pc++]; + int miscArgsReg = bytecode[pc++]; + int miscCtx = bytecode[pc++]; + String miscName = switch (opcode) { + case Opcodes.PACK -> "pack"; + case Opcodes.UNPACK -> "unpack"; + case Opcodes.CRYPT -> "crypt"; + case Opcodes.LOCALTIME -> "localtime"; + case Opcodes.GMTIME -> "gmtime"; + case Opcodes.CHMOD -> "chmod"; + case Opcodes.UNLINK -> "unlink"; + case Opcodes.UTIME -> "utime"; + case Opcodes.RENAME -> "rename"; + case Opcodes.LINK -> "link"; + case Opcodes.READLINK -> "readlink"; + case Opcodes.UMASK -> "umask"; + case Opcodes.GETC -> "getc"; + case Opcodes.FILENO -> "fileno"; + case Opcodes.SYSTEM -> "system"; + case Opcodes.CALLER -> "caller"; + case Opcodes.EACH -> "each"; + case Opcodes.VEC -> "vec"; + default -> "misc_op_" + opcode; + }; + sb.append(miscName).append(" r").append(rd) + .append(" = ").append(miscName).append("(r").append(miscArgsReg) + .append(", ctx=").append(miscCtx).append(")\n"); + break; + } + // DEPRECATED: SLOW_OP case removed - opcode 87 is no longer emitted // All operations now use direct opcodes (114-154) diff --git a/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java b/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java index a37514af6..4e94adb65 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java +++ b/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java @@ -2,6 +2,7 @@ import org.perlonjava.runtime.nativ.NativeUtils; import org.perlonjava.runtime.operators.*; +import org.perlonjava.runtime.operators.Unpack; import org.perlonjava.runtime.runtimetypes.RuntimeBase; import org.perlonjava.runtime.runtimetypes.RuntimeCode; import org.perlonjava.runtime.runtimetypes.RuntimeList; @@ -53,6 +54,7 @@ public static int execute(short opcode, short[] bytecode, int pc, RuntimeBase[] } } case Opcodes.PACK -> Pack.pack(args); + case Opcodes.UNPACK -> Unpack.unpack(ctx, argsArray); case Opcodes.VEC -> Vec.vec(args); case Opcodes.LOCALTIME -> Time.localtime(args, ctx); case Opcodes.GMTIME -> Time.gmtime(args, ctx); diff --git a/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java b/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java index c9784cce9..f6fe89f47 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java +++ b/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java @@ -1004,5 +1004,9 @@ public class Opcodes { * Format: FOREACH_GLOBAL_NEXT_OR_EXIT varReg iterReg nameIdx exitTarget */ public static final short FOREACH_GLOBAL_NEXT_OR_EXIT = 304; + /** Unpack binary data into a list of scalars. + * Format: UNPACK rd argsReg ctx */ + public static final short UNPACK = 305; + private Opcodes() {} // Utility class - no instantiation } From 9d9d1ece38fd9f4dc7a1fe97ce960628852d7b33 Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Mon, 23 Feb 2026 14:30:42 +0100 Subject: [PATCH 3/9] Fix interpreter push/unshift: return array size, handle @{expr} operand Three fixes: 1. push/unshift return value was lastResultReg=-1 (crash when result used) - ARRAY_PUSH/ARRAY_UNSHIFT now emit ARRAY_SIZE after the push so lastResultReg holds the new array size, matching Perl semantics - Fixes: my $n = push @a, 1 and push @a, $x unless grep ... 2. push/unshift @{expr} with arbitrary expression (e.g. @{{key}}) - Previously only handled @$scalar, not @{block_expr} - Now evaluates any non-IdentifierNode operand of @ and derefs it - Fixes: push @{{$baseType}}, $type (ExifTool.pm) 3. Removed temporary debug code (JPERL_DEBUG printStackTrace) --- .../backend/bytecode/BytecodeCompiler.java | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java b/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java index 098b97112..45ab3c506 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java +++ b/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java @@ -1476,23 +1476,17 @@ void handlePushUnshift(BinaryOperatorNode node) { emitReg(arrayReg); emit(nameIdx); } - } else if (leftOp.operator.equals("@") && leftOp.operand instanceof OperatorNode) { - // Array dereference: @$arrayref - OperatorNode derefOp = (OperatorNode) leftOp.operand; - if (derefOp.operator.equals("$")) { - // Compile the scalar reference - derefOp.accept(this); - int refReg = lastResultReg; - - // Dereference to get the array - arrayReg = allocateRegister(); - emitWithToken(Opcodes.DEREF_ARRAY, node.getIndex()); - emitReg(arrayReg); - emitReg(refReg); - } else { - throwCompilerException("push/unshift requires array or array reference"); - return; - } + } else if (leftOp.operator.equals("@") && !(leftOp.operand instanceof IdentifierNode)) { + // Array dereference: @$arrayref or @{expr} + // Evaluate the operand expression to get the reference, then deref + leftOp.operand.accept(this); + int refReg = lastResultReg; + + // Dereference to get the array + arrayReg = allocateRegister(); + emitWithToken(Opcodes.DEREF_ARRAY, node.getIndex()); + emitReg(arrayReg); + emitReg(refReg); } else { throwCompilerException("push/unshift requires array or array reference"); return; @@ -1518,8 +1512,12 @@ void handlePushUnshift(BinaryOperatorNode node) { emitReg(arrayReg); emitReg(valuesReg); - // push/unshift return the new array size - lastResultReg = -1; // No result register needed + // push/unshift return the new array size in scalar context + int rd = allocateRegister(); + emit(Opcodes.ARRAY_SIZE); + emitReg(rd); + emitReg(arrayReg); + lastResultReg = rd; } @Override From bd7889f1876e0d4e6ab2ee2f6b84df7497159d66 Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Mon, 23 Feb 2026 15:00:10 +0100 Subject: [PATCH 4/9] Fix runtime package tracking in interpreter for caller() and eval STRING Add SET_PACKAGE and PUSH_PACKAGE opcodes emitted by the bytecode compiler when package declarations execute. The interpreter handles these by updating InterpreterState.currentPackage (a ThreadLocal RuntimeScalar), also managed via DynamicVariableManager for scoped package blocks (package Foo { }). This fixes: - caller() returning wrong package in interpreter mode (Exporter::export_ok_tags) - eval STRING compiling in wrong package in interpreter mode - ExceptionFormatter now uses runtime package for innermost interpreter frame - ExceptionFormatter consumes InterpreterState frames in order instead of always using current() which always returned the same topmost frame --- .../backend/bytecode/BytecodeInterpreter.java | 18 +++++++ .../backend/bytecode/CompileOperator.java | 16 ++++++- .../backend/bytecode/EvalStringHandler.java | 6 +++ .../backend/bytecode/InterpreterState.java | 17 +++++++ .../perlonjava/backend/bytecode/Opcodes.java | 15 ++++++ .../runtime/perlmodule/Exporter.java | 8 ++-- .../runtimetypes/ExceptionFormatter.java | 47 +++++++++++++------ .../runtime/runtimetypes/RuntimeCode.java | 10 +--- 8 files changed, 107 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java index c0faf64ca..271118600 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java +++ b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java @@ -2005,6 +2005,24 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c pc = MiscOpcodeHandler.execute(opcode, bytecode, pc, registers); break; + case Opcodes.SET_PACKAGE: { + // Non-scoped package declaration: package Foo; + // Update the runtime current-package tracker so caller() returns the right package. + int nameIdx = bytecode[pc++]; + InterpreterState.currentPackage.get().set(code.stringPool[nameIdx]); + break; + } + + case Opcodes.PUSH_PACKAGE: { + // Scoped package block entry: package Foo { ... + // Save current package via DynamicVariableManager so it is restored + // automatically when the scope exits via POP_LOCAL_LEVEL. + int nameIdx = bytecode[pc++]; + DynamicVariableManager.pushLocalVariable(InterpreterState.currentPackage.get()); + InterpreterState.currentPackage.get().set(code.stringPool[nameIdx]); + break; + } + default: // Unknown opcode int opcodeInt = opcode & 0xFF; diff --git a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java index f794df4c6..2b1ab8493 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java +++ b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java @@ -75,8 +75,7 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode .set(new RuntimeScalar(version)); } - // Update the current package/class in symbol table - // This tracks package name, isClass flag, and version + // Update the current package/class in symbol table (compile-time tracking) bytecodeCompiler.symbolTable.setCurrentPackage(packageName, isClass); // Register as Perl 5.38+ class for proper stringification if needed @@ -84,6 +83,19 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode ClassRegistry.registerClass(packageName); } + // Emit runtime package tracking opcode so caller() and eval STRING work. + // Scoped blocks (package Foo { }) use PUSH_PACKAGE so DynamicVariableManager + // can restore the previous package when the scope exits. + // Non-scoped (package Foo;) use SET_PACKAGE which just overwrites. + boolean isScoped = Boolean.TRUE.equals(node.getAnnotation("isScoped")); + int nameIdx = bytecodeCompiler.addToStringPool(packageName); + if (isScoped) { + bytecodeCompiler.emit(Opcodes.PUSH_PACKAGE); + } else { + bytecodeCompiler.emit(Opcodes.SET_PACKAGE); + } + bytecodeCompiler.emit(nameIdx); + bytecodeCompiler.lastResultReg = -1; // No runtime value } else { bytecodeCompiler.throwCompilerException(op + " operator requires an identifier"); diff --git a/src/main/java/org/perlonjava/backend/bytecode/EvalStringHandler.java b/src/main/java/org/perlonjava/backend/bytecode/EvalStringHandler.java index ad85c2c85..b3700b7d7 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/EvalStringHandler.java +++ b/src/main/java/org/perlonjava/backend/bytecode/EvalStringHandler.java @@ -76,6 +76,12 @@ public static RuntimeScalar evalString(String perlCode, symbolTable.warningFlagsStack.push((java.util.BitSet) currentCode.warningFlags.clone()); } + // Inherit the runtime current package so eval STRING compiles in the right package. + // InterpreterState.currentPackage is updated by SET_PACKAGE/PUSH_PACKAGE opcodes + // as "package Foo;" declarations execute at runtime. + String runtimePackage = InterpreterState.currentPackage.get().toString(); + symbolTable.setCurrentPackage(runtimePackage, false); + ErrorMessageUtil errorUtil = new ErrorMessageUtil(sourceName, tokens); EmitterContext ctx = new EmitterContext( new JavaClassInfo(), diff --git a/src/main/java/org/perlonjava/backend/bytecode/InterpreterState.java b/src/main/java/org/perlonjava/backend/bytecode/InterpreterState.java index 480084fe5..72c6656d1 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/InterpreterState.java +++ b/src/main/java/org/perlonjava/backend/bytecode/InterpreterState.java @@ -1,5 +1,6 @@ package org.perlonjava.backend.bytecode; +import org.perlonjava.runtime.runtimetypes.RuntimeScalar; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; @@ -17,6 +18,22 @@ public class InterpreterState { private static final ThreadLocal> frameStack = ThreadLocal.withInitial(ArrayDeque::new); + /** + * Thread-local RuntimeScalar holding the runtime current package name. + * + * This is the single source of truth for the current package at runtime. + * It is used by: + * - caller() to return the correct calling package + * - eval STRING to compile code in the right package + * - SET_PACKAGE opcode (non-scoped: package Foo;) — sets it directly + * - PUSH_PACKAGE opcode (scoped: package Foo { }) — saves via DynamicVariableManager then sets + * + * Scoped package blocks are automatically restored when the scope exits via + * the existing POP_LOCAL_LEVEL opcode (DynamicVariableManager.popToLocalLevel). + */ + public static final ThreadLocal currentPackage = + ThreadLocal.withInitial(() -> new RuntimeScalar("main")); + /** * Represents a single interpreter call frame. * Contains minimal information needed for stack trace formatting. diff --git a/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java b/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java index f6fe89f47..33dc96e45 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java +++ b/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java @@ -1008,5 +1008,20 @@ public class Opcodes { * Format: UNPACK rd argsReg ctx */ public static final short UNPACK = 305; + /** Set current package at runtime (non-scoped: package Foo;). + * Format: SET_PACKAGE nameIdx + * Effect: Updates InterpreterState current frame's packageName to stringPool[nameIdx] */ + public static final short SET_PACKAGE = 306; + + /** Enter scoped package block (package Foo { ...). + * Format: PUSH_PACKAGE nameIdx + * Effect: Saves current packageName, sets new one */ + public static final short PUSH_PACKAGE = 307; + + /** Exit scoped package block (closing } of package Foo { ...). + * Format: POP_PACKAGE + * Effect: Restores previous packageName */ + public static final short POP_PACKAGE = 308; + private Opcodes() {} // Utility class - no instantiation } diff --git a/src/main/java/org/perlonjava/runtime/perlmodule/Exporter.java b/src/main/java/org/perlonjava/runtime/perlmodule/Exporter.java index 2d49acbcc..9debb3ce2 100644 --- a/src/main/java/org/perlonjava/runtime/perlmodule/Exporter.java +++ b/src/main/java/org/perlonjava/runtime/perlmodule/Exporter.java @@ -248,7 +248,7 @@ public static RuntimeList exportToLevel(RuntimeArray args, int ctx) { public static RuntimeList exportTags(RuntimeArray args, int ctx) { // Extract the package name from caller - RuntimeScalar packageScalar = RuntimeCode.caller(new RuntimeList(), SCALAR).getFirst().scalar(); + String packageScalar = RuntimeCode.caller(new RuntimeList(), SCALAR).getFirst().scalar().toString(); // Retrieve the export lists and tags from the package RuntimeArray export = GlobalVariable.getGlobalArray(packageScalar + "::EXPORT"); RuntimeHash exportTags = GlobalVariable.getGlobalHash(packageScalar + "::EXPORT_TAGS"); @@ -264,11 +264,11 @@ public static RuntimeList exportTags(RuntimeArray args, int ctx) { public static RuntimeList exportOkTags(RuntimeArray args, int ctx) { // Extract the package name from caller RuntimeScalar packageScalar = RuntimeCode.caller(new RuntimeList(), SCALAR).getFirst().scalar(); - // System.out.println("exportOkTags " + packageScalar + "::EXPORT_OK " + packageScalar + "::EXPORT_TAGS"); + String packageName = packageScalar.toString(); // Retrieve the export lists and tags from the package - RuntimeArray exportOk = GlobalVariable.getGlobalArray(packageScalar + "::EXPORT_OK"); - RuntimeHash exportTags = GlobalVariable.getGlobalHash(packageScalar + "::EXPORT_TAGS"); + RuntimeArray exportOk = GlobalVariable.getGlobalArray(packageName + "::EXPORT_OK"); + RuntimeHash exportTags = GlobalVariable.getGlobalHash(packageName + "::EXPORT_TAGS"); for (RuntimeBase elem : args.elements) { RuntimeArray tags = exportTags.get(elem.toString()).arrayDeref(); for (RuntimeScalar tag : tags.elements) { diff --git a/src/main/java/org/perlonjava/runtime/runtimetypes/ExceptionFormatter.java b/src/main/java/org/perlonjava/runtime/runtimetypes/ExceptionFormatter.java index 2f7aa92fc..d699a6170 100644 --- a/src/main/java/org/perlonjava/runtime/runtimetypes/ExceptionFormatter.java +++ b/src/main/java/org/perlonjava/runtime/runtimetypes/ExceptionFormatter.java @@ -51,6 +51,12 @@ private static ArrayList> formatThrowable(Throwable t) { var locationToClassName = new HashMap(); + // Snapshot interpreter frames so we can consume them in order. + // Each BytecodeInterpreter.execute() JVM frame corresponds to one Perl call + // level; consuming them in order gives the correct nested call stack. + var interpreterFrames = InterpreterState.getStack(); + int interpreterFrameIndex = 0; + for (var element : t.getStackTrace()) { if (element.getClassName().equals("org.perlonjava.frontend.parser.StatementParser") && element.getMethodName().equals("parseUseDeclaration")) { @@ -68,22 +74,33 @@ private static ArrayList> formatThrowable(Throwable t) { } } else if (element.getClassName().equals("org.perlonjava.backend.bytecode.BytecodeInterpreter") && element.getMethodName().equals("execute")) { - // Interpreter frame - get information from InterpreterState - var frame = InterpreterState.current(); - if (frame != null && frame.code != null) { - // Format the interpreter frame as a Perl stack entry - String subName = frame.subroutineName; - if (subName != null && !subName.isEmpty() && !subName.contains("::")) { - subName = frame.packageName + "::" + subName; - } + // Consume the next interpreter frame in order. + // Using current() always returned the same topmost frame; consuming + // in order correctly maps each JVM execute() frame to its Perl level. + if (interpreterFrameIndex < interpreterFrames.size()) { + var frame = interpreterFrames.get(interpreterFrameIndex); + if (frame != null && frame.code != null) { + // For the innermost frame (index 0), use the runtime current package + // tracked by SET_PACKAGE/PUSH_PACKAGE opcodes, which reflects runtime + // "package Foo;" declarations. Outer frames still use compile-time names. + String pkg = (interpreterFrameIndex == 0) + ? InterpreterState.currentPackage.get().toString() + : frame.packageName; + interpreterFrameIndex++; - var entry = new ArrayList(); - entry.add(frame.packageName); - entry.add(frame.code.sourceName); - entry.add(String.valueOf(frame.code.sourceLine)); - entry.add(subName); // Subroutine name - stackTrace.add(entry); - lastFileName = frame.code.sourceName != null ? frame.code.sourceName : ""; + String subName = frame.subroutineName; + if (subName != null && !subName.isEmpty() && !subName.contains("::")) { + subName = pkg + "::" + subName; + } + + var entry = new ArrayList(); + entry.add(pkg); + entry.add(frame.code.sourceName); + entry.add(String.valueOf(frame.code.sourceLine)); + entry.add(subName); + stackTrace.add(entry); + lastFileName = frame.code.sourceName != null ? frame.code.sourceName : ""; + } } } else if (element.getClassName().contains("org.perlonjava.anon") || element.getClassName().contains("org.perlonjava.runtime.perlmodule")) { diff --git a/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeCode.java b/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeCode.java index 71e5b074f..55163c53e 100644 --- a/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeCode.java +++ b/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeCode.java @@ -16,6 +16,7 @@ import org.perlonjava.frontend.semantic.SymbolTable; import org.perlonjava.backend.bytecode.BytecodeCompiler; import org.perlonjava.backend.bytecode.InterpretedCode; +import org.perlonjava.backend.bytecode.InterpreterState; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; @@ -1190,15 +1191,6 @@ public static RuntimeList caller(RuntimeList args, int ctx) { frame++; } -// // Show debug info -// System.err.println("# Runtime stack trace: frame=" + frame + " size=" + stackTraceSize); -// for (int i = 0; i < stackTraceSize; i++) { -// ArrayList entry = stackTrace.get(i); -// String subName = entry.size() > 3 ? entry.get(3) : "NO_SUB"; -// System.err.println("# " + i + ": pkg=" + entry.get(0) + " file=" + entry.get(1) + " line=" + entry.get(2) + " sub=" + subName); -// } -// System.err.println(); - if (frame >= 0 && frame < stackTraceSize) { // Runtime stack trace if (ctx == RuntimeContextType.SCALAR) { From c02edda3000bb8b3484c308ebcf0c0fc4207889b Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Mon, 23 Feb 2026 15:11:28 +0100 Subject: [PATCH 5/9] Add bulk I/O opcodes to interpreter: close, binmode, seek, eof, sysread/write/open, socket, bind, connect, listen, write, formline, printf, accept, sysseek, truncate, read --- .../backend/bytecode/BytecodeInterpreter.java | 18 +++++++ .../backend/bytecode/CompileOperator.java | 27 +++++++++- .../backend/bytecode/MiscOpcodeHandler.java | 19 +++++++ .../perlonjava/backend/bytecode/Opcodes.java | 41 ++++++++++++++++ .../runtime/operators/IOOperator.java | 49 +++++++++++++++++++ 5 files changed, 153 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java index 271118600..adc7efcd8 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java +++ b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java @@ -2002,6 +2002,24 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c case Opcodes.LOCALTIME: case Opcodes.GMTIME: case Opcodes.CRYPT: + case Opcodes.CLOSE: + case Opcodes.BINMODE: + case Opcodes.SEEK: + case Opcodes.EOF_OP: + case Opcodes.SYSREAD: + case Opcodes.SYSWRITE: + case Opcodes.SYSOPEN: + case Opcodes.SOCKET: + case Opcodes.BIND: + case Opcodes.CONNECT: + case Opcodes.LISTEN: + case Opcodes.WRITE: + case Opcodes.FORMLINE: + case Opcodes.PRINTF: + case Opcodes.ACCEPT: + case Opcodes.SYSSEEK: + case Opcodes.TRUNCATE: + case Opcodes.READ: pc = MiscOpcodeHandler.execute(opcode, bytecode, pc, registers); break; diff --git a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java index 2b1ab8493..8db1e2a77 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java +++ b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java @@ -2722,7 +2722,14 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode op.equals("umask") || op.equals("system") || op.equals("pack") || op.equals("unpack") || op.equals("vec") || op.equals("crypt") || op.equals("localtime") || op.equals("gmtime") || op.equals("caller") || - op.equals("fileno") || op.equals("getc") || op.equals("qx")) { + op.equals("fileno") || op.equals("getc") || op.equals("qx") || + op.equals("close") || + op.equals("binmode") || op.equals("seek") || + op.equals("eof") || op.equals("sysread") || op.equals("syswrite") || + op.equals("sysopen") || op.equals("socket") || op.equals("bind") || + op.equals("connect") || op.equals("listen") || op.equals("write") || + op.equals("formline") || op.equals("printf") || op.equals("accept") || + op.equals("sysseek") || op.equals("truncate") || op.equals("read")) { // Generic handler for operators that take arguments and call runtime methods // Format: OPCODE rd argsReg ctx // argsReg must be a RuntimeList @@ -2770,6 +2777,24 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode case "localtime" -> Opcodes.LOCALTIME; case "gmtime" -> Opcodes.GMTIME; case "crypt" -> Opcodes.CRYPT; + case "close" -> Opcodes.CLOSE; + case "binmode" -> Opcodes.BINMODE; + case "seek" -> Opcodes.SEEK; + case "eof" -> Opcodes.EOF_OP; + case "sysread" -> Opcodes.SYSREAD; + case "syswrite" -> Opcodes.SYSWRITE; + case "sysopen" -> Opcodes.SYSOPEN; + case "socket" -> Opcodes.SOCKET; + case "bind" -> Opcodes.BIND; + case "connect" -> Opcodes.CONNECT; + case "listen" -> Opcodes.LISTEN; + case "write" -> Opcodes.WRITE; + case "formline" -> Opcodes.FORMLINE; + case "printf" -> Opcodes.PRINTF; + case "accept" -> Opcodes.ACCEPT; + case "sysseek" -> Opcodes.SYSSEEK; + case "truncate" -> Opcodes.TRUNCATE; + case "read" -> Opcodes.READ; default -> throw new IllegalStateException("Unexpected operator: " + op); }; diff --git a/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java b/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java index 4e94adb65..5d58318b9 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java +++ b/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java @@ -59,6 +59,25 @@ public static int execute(short opcode, short[] bytecode, int pc, RuntimeBase[] case Opcodes.LOCALTIME -> Time.localtime(args, ctx); case Opcodes.GMTIME -> Time.gmtime(args, ctx); case Opcodes.CRYPT -> Crypt.crypt(args); + // I/O operators + case Opcodes.CLOSE -> IOOperator.close(ctx, argsArray); + case Opcodes.BINMODE -> IOOperator.binmode(ctx, argsArray); + case Opcodes.SEEK -> IOOperator.seek(ctx, argsArray); + case Opcodes.EOF_OP -> IOOperator.eof(ctx, argsArray); + case Opcodes.SYSREAD -> IOOperator.sysread(ctx, argsArray); + case Opcodes.SYSWRITE -> IOOperator.syswrite(ctx, argsArray); + case Opcodes.SYSOPEN -> IOOperator.sysopen(ctx, argsArray); + case Opcodes.SOCKET -> IOOperator.socket(ctx, argsArray); + case Opcodes.BIND -> IOOperator.bind(ctx, argsArray); + case Opcodes.CONNECT -> IOOperator.connect(ctx, argsArray); + case Opcodes.LISTEN -> IOOperator.listen(ctx, argsArray); + case Opcodes.WRITE -> IOOperator.write(ctx, argsArray); + case Opcodes.FORMLINE -> IOOperator.formline(ctx, argsArray); + case Opcodes.PRINTF -> IOOperator.printf(ctx, argsArray); + case Opcodes.ACCEPT -> IOOperator.accept(ctx, argsArray); + case Opcodes.SYSSEEK -> IOOperator.sysseek(ctx, argsArray); + case Opcodes.TRUNCATE -> IOOperator.truncate(ctx, argsArray); + case Opcodes.READ -> IOOperator.read(ctx, argsArray); default -> throw new IllegalStateException("Unknown opcode in MiscOpcodeHandler: " + opcode); }; diff --git a/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java b/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java index 33dc96e45..dd7ac7b97 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java +++ b/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java @@ -1013,6 +1013,47 @@ public class Opcodes { * Effect: Updates InterpreterState current frame's packageName to stringPool[nameIdx] */ public static final short SET_PACKAGE = 306; + // ================================================================= + // I/O OPERATORS (309-329) - truly new ones not already defined above + // Note: OPEN=165, READLINE=166, TELL=LASTOP+37 already exist + // ================================================================= + /** close FILEHANDLE: Format: CLOSE rd argsReg ctx */ + public static final short CLOSE = 309; + /** binmode FILEHANDLE,LAYER: Format: BINMODE rd argsReg ctx */ + public static final short BINMODE = 312; + /** seek FILEHANDLE,POS,WHENCE: Format: SEEK rd argsReg ctx */ + public static final short SEEK = 313; + /** eof FILEHANDLE: Format: EOF_OP rd argsReg ctx */ + public static final short EOF_OP = 315; + /** sysread FILEHANDLE,SCALAR,LENGTH: Format: SYSREAD rd argsReg ctx */ + public static final short SYSREAD = 316; + /** syswrite FILEHANDLE,SCALAR: Format: SYSWRITE rd argsReg ctx */ + public static final short SYSWRITE = 317; + /** sysopen FILEHANDLE,FILENAME,MODE: Format: SYSOPEN rd argsReg ctx */ + public static final short SYSOPEN = 318; + /** socket SOCKET,DOMAIN,TYPE,PROTOCOL: Format: SOCKET rd argsReg ctx */ + public static final short SOCKET = 319; + /** bind SOCKET,NAME: Format: BIND rd argsReg ctx */ + public static final short BIND = 320; + /** connect SOCKET,NAME: Format: CONNECT rd argsReg ctx */ + public static final short CONNECT = 321; + /** listen SOCKET,QUEUESIZE: Format: LISTEN rd argsReg ctx */ + public static final short LISTEN = 322; + /** write FILEHANDLE: Format: WRITE rd argsReg ctx */ + public static final short WRITE = 323; + /** formline PICTURE,LIST: Format: FORMLINE rd argsReg ctx */ + public static final short FORMLINE = 324; + /** printf FILEHANDLE,FORMAT,LIST: Format: PRINTF rd argsReg ctx */ + public static final short PRINTF = 325; + /** accept NEWSOCKET,GENERICSOCKET: Format: ACCEPT rd argsReg ctx */ + public static final short ACCEPT = 326; + /** sysseek FILEHANDLE,POS,WHENCE: Format: SYSSEEK rd argsReg ctx */ + public static final short SYSSEEK = 327; + /** truncate FILEHANDLE,LENGTH: Format: TRUNCATE rd argsReg ctx */ + public static final short TRUNCATE = 328; + /** read FILEHANDLE,SCALAR,LENGTH: Format: READ rd argsReg ctx */ + public static final short READ = 329; + /** Enter scoped package block (package Foo { ...). * Format: PUSH_PACKAGE nameIdx * Effect: Saves current packageName, sets new one */ diff --git a/src/main/java/org/perlonjava/runtime/operators/IOOperator.java b/src/main/java/org/perlonjava/runtime/operators/IOOperator.java index 0e5957327..78aeb24d1 100644 --- a/src/main/java/org/perlonjava/runtime/operators/IOOperator.java +++ b/src/main/java/org/perlonjava/runtime/operators/IOOperator.java @@ -2093,4 +2093,53 @@ public static RuntimeScalar socketpair(int ctx, RuntimeBase... args) { } } + // ================================================================= + // Adapter overloads for MiscOpcodeHandler (int ctx, RuntimeBase... args) signature + // ================================================================= + + public static RuntimeScalar seek(int ctx, RuntimeBase... args) { + if (args.length < 3) throw new PerlCompilerException("Not enough arguments for seek"); + RuntimeList list = new RuntimeList(); + for (int i = 1; i < args.length; i++) list.add(args[i]); + return seek(args[0].scalar(), list); + } + + public static RuntimeScalar tell(int ctx, RuntimeBase... args) { + RuntimeScalar fh = args.length > 0 ? args[0].scalar() : new RuntimeScalar(); + return tell(fh); + } + + public static RuntimeScalar binmode(int ctx, RuntimeBase... args) { + if (args.length < 1) throw new PerlCompilerException("Not enough arguments for binmode"); + RuntimeList list = new RuntimeList(); + for (int i = 1; i < args.length; i++) list.add(args[i]); + return binmode(args[0].scalar(), list); + } + + public static RuntimeScalar eof(int ctx, RuntimeBase... args) { + RuntimeScalar fh = args.length > 0 ? args[0].scalar() : new RuntimeScalar(); + return eof(fh); + } + + public static RuntimeScalar printf(int ctx, RuntimeBase... args) { + if (args.length < 1) throw new PerlCompilerException("Not enough arguments for printf"); + RuntimeScalar fh = args[0].scalar(); + RuntimeList list = new RuntimeList(); + for (int i = 1; i < args.length; i++) list.add(args[i]); + return printf(list, fh); + } + + public static RuntimeScalar readline(int ctx, RuntimeBase... args) { + RuntimeScalar fh = args.length > 0 ? args[0].scalar() : new RuntimeScalar("main::STDIN"); + return (RuntimeScalar) Readline.readline(fh, ctx); + } + + public static RuntimeScalar sysseek(int ctx, RuntimeBase... args) { + return seek(ctx, args); + } + + public static RuntimeScalar read(int ctx, RuntimeBase... args) { + return sysread(ctx, args); + } + } From 302d820daea5c3c420fec0679e981401ad6b039a Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Mon, 23 Feb 2026 15:13:46 +0100 Subject: [PATCH 6/9] Fix 97288ref{key}=val and $ref->{key}=val assignment in interpreter bytecode compiler --- .../backend/bytecode/CompileAssignment.java | 121 +++++++++++++++--- 1 file changed, 102 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/perlonjava/backend/bytecode/CompileAssignment.java b/src/main/java/org/perlonjava/backend/bytecode/CompileAssignment.java index 360bdc478..3ee669681 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/CompileAssignment.java +++ b/src/main/java/org/perlonjava/backend/bytecode/CompileAssignment.java @@ -1203,28 +1203,34 @@ public static void compileAssignmentOperator(BytecodeCompiler bytecodeCompiler, bytecodeCompiler.currentCallContext = savedContext; return; } else if (hashOp.operator.equals("$")) { - // $hash{key} - dereference to get hash - if (!(hashOp.operand instanceof IdentifierNode)) { - bytecodeCompiler.throwCompilerException("Hash assignment requires identifier"); - return; - } - String varName = ((IdentifierNode) hashOp.operand).name; - String hashVarName = "%" + varName; - - if (bytecodeCompiler.hasVariable(hashVarName)) { - // Lexical hash - hashReg = bytecodeCompiler.getVariableRegister(hashVarName); + // $hash{key} or $$ref{key} - dereference to get hash + if (hashOp.operand instanceof IdentifierNode) { + String varName = ((IdentifierNode) hashOp.operand).name; + String hashVarName = "%" + varName; + + if (bytecodeCompiler.hasVariable(hashVarName)) { + // Lexical hash + hashReg = bytecodeCompiler.getVariableRegister(hashVarName); + } else { + // Global hash - load it + hashReg = bytecodeCompiler.allocateRegister(); + String globalHashName = NameNormalizer.normalizeVariableName( + varName, + bytecodeCompiler.getCurrentPackage() + ); + int nameIdx = bytecodeCompiler.addToStringPool(globalHashName); + bytecodeCompiler.emit(Opcodes.LOAD_GLOBAL_HASH); + bytecodeCompiler.emitReg(hashReg); + bytecodeCompiler.emit(nameIdx); + } } else { - // Global hash - load it + // $$ref{key} = value — compile the scalar ref expression and deref to hash + hashOp.operand.accept(bytecodeCompiler); + int scalarReg = bytecodeCompiler.lastResultReg; hashReg = bytecodeCompiler.allocateRegister(); - String globalHashName = NameNormalizer.normalizeVariableName( - varName, - bytecodeCompiler.getCurrentPackage() - ); - int nameIdx = bytecodeCompiler.addToStringPool(globalHashName); - bytecodeCompiler.emit(Opcodes.LOAD_GLOBAL_HASH); + bytecodeCompiler.emitWithToken(Opcodes.DEREF_HASH, node.getIndex()); bytecodeCompiler.emitReg(hashReg); - bytecodeCompiler.emit(nameIdx); + bytecodeCompiler.emitReg(scalarReg); } } else { bytecodeCompiler.throwCompilerException("Hash assignment requires scalar dereference: $var{key}"); @@ -1290,6 +1296,83 @@ public static void compileAssignmentOperator(BytecodeCompiler bytecodeCompiler, return; } + // Handle arrow dereference assignment: $ref->{key} = value or $$ref{key} = value + // These parse as BinaryOperatorNode("->", expr, HashLiteralNode/ArrayLiteralNode) + if (leftBin.operator.equals("->")) { + Node rightSide = leftBin.right; + if (rightSide instanceof HashLiteralNode hashKey) { + // $ref->{key} = value — hash element via reference + leftBin.left.accept(bytecodeCompiler); + int refReg = bytecodeCompiler.lastResultReg; + + // Dereference to get the hash + int hashReg = bytecodeCompiler.allocateRegister(); + bytecodeCompiler.emitWithToken(Opcodes.DEREF_HASH, node.getIndex()); + bytecodeCompiler.emitReg(hashReg); + bytecodeCompiler.emitReg(refReg); + + // Compile key + int keyReg; + if (!hashKey.elements.isEmpty()) { + Node keyElement = hashKey.elements.get(0); + if (keyElement instanceof IdentifierNode) { + String keyString = ((IdentifierNode) keyElement).name; + keyReg = bytecodeCompiler.allocateRegister(); + int keyIdx = bytecodeCompiler.addToStringPool(keyString); + bytecodeCompiler.emit(Opcodes.LOAD_STRING); + bytecodeCompiler.emitReg(keyReg); + bytecodeCompiler.emit(keyIdx); + } else { + keyElement.accept(bytecodeCompiler); + keyReg = bytecodeCompiler.lastResultReg; + } + } else { + bytecodeCompiler.throwCompilerException("Hash key required for arrow assignment"); + return; + } + + // Compile RHS and emit HASH_SET + node.right.accept(bytecodeCompiler); + int valReg = bytecodeCompiler.lastResultReg; + bytecodeCompiler.emit(Opcodes.HASH_SET); + bytecodeCompiler.emitReg(hashReg); + bytecodeCompiler.emitReg(keyReg); + bytecodeCompiler.emitReg(valReg); + bytecodeCompiler.lastResultReg = valReg; + bytecodeCompiler.currentCallContext = savedContext; + return; + } else if (rightSide instanceof ArrayLiteralNode arrayIdx) { + // $ref->[index] = value — array element via reference + leftBin.left.accept(bytecodeCompiler); + int refReg = bytecodeCompiler.lastResultReg; + + // Dereference to get the array + int arrayReg = bytecodeCompiler.allocateRegister(); + bytecodeCompiler.emitWithToken(Opcodes.DEREF_ARRAY, node.getIndex()); + bytecodeCompiler.emitReg(arrayReg); + bytecodeCompiler.emitReg(refReg); + + // Compile index + if (arrayIdx.elements.isEmpty()) { + bytecodeCompiler.throwCompilerException("Array index required for arrow assignment"); + return; + } + arrayIdx.elements.get(0).accept(bytecodeCompiler); + int idxReg = bytecodeCompiler.lastResultReg; + + // Compile RHS and emit ARRAY_SET + node.right.accept(bytecodeCompiler); + int valReg = bytecodeCompiler.lastResultReg; + bytecodeCompiler.emit(Opcodes.ARRAY_SET); + bytecodeCompiler.emitReg(arrayReg); + bytecodeCompiler.emitReg(idxReg); + bytecodeCompiler.emitReg(valReg); + bytecodeCompiler.lastResultReg = valReg; + bytecodeCompiler.currentCallContext = savedContext; + return; + } + } + // Handle lvalue subroutine: f() = value // When a function is called in lvalue context, it returns a RuntimeBaseProxy // that wraps a mutable reference. We can assign to it using SET_SCALAR. From 00356a3eb0849a4214b085c0fdf5f2ccfc72ef00 Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Mon, 23 Feb 2026 15:21:02 +0100 Subject: [PATCH 7/9] Change bytecode array from short[] to int[] to fix register overflow for large files --- .../backend/bytecode/BytecodeCompiler.java | 58 +++++++--- .../backend/bytecode/BytecodeInterpreter.java | 32 +++--- .../backend/bytecode/InterpretedCode.java | 24 ++--- .../backend/bytecode/MiscOpcodeHandler.java | 2 +- .../bytecode/OpcodeHandlerExtended.java | 102 +++++++++--------- .../bytecode/OpcodeHandlerFileTest.java | 2 +- .../bytecode/ScalarBinaryOpcodeHandler.java | 4 +- .../bytecode/ScalarUnaryOpcodeHandler.java | 4 +- .../backend/bytecode/SlowOpcodeHandler.java | 88 +++++++-------- 9 files changed, 169 insertions(+), 147 deletions(-) diff --git a/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java b/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java index 45ab3c506..ffcce66d8 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java +++ b/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java @@ -25,7 +25,7 @@ public class BytecodeCompiler implements Visitor { // Pre-allocate with reasonable initial capacity to reduce resizing // Typical small eval/subroutine needs 20-50 bytecodes, 5-10 constants, 3-8 strings - final List bytecode = new ArrayList<>(64); + final List bytecode = new ArrayList<>(64); private final List constants = new ArrayList<>(16); private final List stringPool = new ArrayList<>(16); private final Map stringPoolIndex = new HashMap<>(16); // O(1) lookup @@ -2649,6 +2649,32 @@ void compileVariableReference(OperatorNode node, String op) { } else { lastResultReg = hashReg; } + } else if (node.operand instanceof OperatorNode refOp) { + // %$ref or %{expr} — dereference scalar to hash + refOp.accept(this); + int scalarReg = lastResultReg; + int hashReg = allocateRegister(); + emitWithToken(Opcodes.DEREF_HASH, node.getIndex()); + emitReg(hashReg); + emitReg(scalarReg); + if (currentCallContext == RuntimeContextType.SCALAR) { + int rd = allocateRegister(); + emit(Opcodes.ARRAY_SIZE); + emitReg(rd); + emitReg(hashReg); + lastResultReg = rd; + } else { + lastResultReg = hashReg; + } + } else if (node.operand instanceof BlockNode blockNode) { + // %{ block } — evaluate block and dereference to hash + blockNode.accept(this); + int scalarReg = lastResultReg; + int hashReg = allocateRegister(); + emitWithToken(Opcodes.DEREF_HASH, node.getIndex()); + emitReg(hashReg); + emitReg(scalarReg); + lastResultReg = hashReg; } else { throwCompilerException("Unsupported % operand: " + node.operand.getClass().getSimpleName()); } @@ -2833,7 +2859,7 @@ private int addToConstantPool(Object obj) { } void emit(short opcode) { - bytecode.add(opcode); + bytecode.add((int) opcode); } /** @@ -2843,23 +2869,23 @@ void emit(short opcode) { void emitWithToken(short opcode, int tokenIndex) { int pc = bytecode.size(); pcToTokenIndex.put(pc, tokenIndex); - bytecode.add(opcode); + bytecode.add((int) opcode); } void emit(int value) { - bytecode.add((short)value); + bytecode.add(value); } void emitInt(int value) { - bytecode.add((short)(value >> 16)); // High 16 bits - bytecode.add((short)value); // Low 16 bits + bytecode.add(value >> 16); // High 16 bits + bytecode.add(value & 0xFFFF); // Low 16 bits } /** * Emit a short value (register index, small immediate, etc.). */ private void emitShort(int value) { - bytecode.add((short)value); + bytecode.add(value); } /** @@ -2867,7 +2893,7 @@ private void emitShort(int value) { * Registers are now 16-bit (0-65535) instead of 8-bit (0-255). */ void emitReg(int register) { - bytecode.add((short)register); + bytecode.add(register); } /** @@ -2875,9 +2901,9 @@ void emitReg(int register) { * Used for forward jumps where the target is unknown at emit time. */ void patchIntOffset(int position, int target) { - // Store absolute target address (not relative offset) as 2 shorts - bytecode.set(position, (short)((target >> 16) & 0xFFFF)); // High 16 bits - bytecode.set(position + 1, (short)(target & 0xFFFF)); // Low 16 bits + // Store absolute target address (not relative offset) as 2 ints + bytecode.set(position, (target >> 16) & 0xFFFF); // High 16 bits + bytecode.set(position + 1, target & 0xFFFF); // Low 16 bits } /** @@ -2896,14 +2922,14 @@ private void patchJump(int placeholderPos, int target) { * Used for forward jumps where the target is unknown at emit time. */ private void patchShortOffset(int position, int target) { - bytecode.set(position, (short)target); + bytecode.set(position, target); } /** - * Convert List bytecode to short[] array. + * Convert List bytecode to int[] array. */ - private short[] toShortArray() { - short[] result = new short[bytecode.size()]; + private int[] toShortArray() { + int[] result = new int[bytecode.size()]; for (int i = 0; i < bytecode.size(); i++) { result[i] = bytecode.get(i); } @@ -3878,7 +3904,7 @@ int getSourceLine() { * Get the bytecode list for position tracking. * Used by refactored compiler classes for jump patching. */ - List getBytecode() { + List getBytecode() { return bytecode; } diff --git a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java index adc7efcd8..db1a370df 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java +++ b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java @@ -62,7 +62,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c } int pc = 0; // Program counter - short[] bytecode = code.bytecode; + final int[] bytecode = code.bytecode; // Eval block exception handling: stack of catch PCs // When EVAL_TRY is executed, push the catch PC onto this stack @@ -72,7 +72,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c try { // Main dispatch loop - JVM JIT optimizes switch to tableswitch (O(1) jump) while (pc < bytecode.length) { - short opcode = bytecode[pc++]; + int opcode = bytecode[pc++]; switch (opcode) { // ================================================================= @@ -2127,7 +2127,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c * * @return Updated program counter */ - private static int executeTypeOps(short opcode, short[] bytecode, int pc, + private static int executeTypeOps(int opcode, int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { switch (opcode) { case Opcodes.CREATE_LAST: { @@ -2323,7 +2323,7 @@ private static int executeTypeOps(short opcode, short[] bytecode, int pc, * * @return Updated program counter */ - private static int executeCollections(short opcode, short[] bytecode, int pc, + private static int executeCollections(int opcode, int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { switch (opcode) { case Opcodes.ARRAY_SET: { @@ -2497,7 +2497,7 @@ private static int executeCollections(short opcode, short[] bytecode, int pc, * * @return Updated program counter */ - private static int executeArithmetic(short opcode, short[] bytecode, int pc, + private static int executeArithmetic(int opcode, int[] bytecode, int pc, RuntimeBase[] registers) { switch (opcode) { case Opcodes.MUL_SCALAR: { @@ -2774,7 +2774,7 @@ private static int executeArithmetic(short opcode, short[] bytecode, int pc, * * @return Updated program counter */ - private static int executeComparisons(short opcode, short[] bytecode, int pc, + private static int executeComparisons(int opcode, int[] bytecode, int pc, RuntimeBase[] registers) { switch (opcode) { case Opcodes.COMPARE_NUM: { @@ -2957,7 +2957,7 @@ private static int executeComparisons(short opcode, short[] bytecode, int pc, * Handles: DEREF_ARRAY, DEREF_HASH, *_SLICE, *_SLICE_SET, *_SLICE_DELETE, LIST_SLICE_FROM * Direct dispatch to SlowOpcodeHandler methods (Phase 2 complete). */ - private static int executeSliceOps(short opcode, short[] bytecode, int pc, + private static int executeSliceOps(int opcode, int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { // Direct method calls - no SLOWOP_* constants needed! switch (opcode) { @@ -2986,7 +2986,7 @@ private static int executeSliceOps(short opcode, short[] bytecode, int pc, * Execute array/string operations (opcodes 122-127). * Handles: SPLICE, REVERSE, SPLIT, LENGTH_OP, EXISTS, DELETE */ - private static int executeArrayStringOps(short opcode, short[] bytecode, int pc, + private static int executeArrayStringOps(int opcode, int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { switch (opcode) { case Opcodes.SPLICE: @@ -3010,7 +3010,7 @@ private static int executeArrayStringOps(short opcode, short[] bytecode, int pc, * Execute closure/scope operations (opcodes 128-131). * Handles: RETRIEVE_BEGIN_*, LOCAL_SCALAR */ - private static int executeScopeOps(short opcode, short[] bytecode, int pc, + private static int executeScopeOps(int opcode, int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { switch (opcode) { case Opcodes.RETRIEVE_BEGIN_SCALAR: @@ -3031,7 +3031,7 @@ private static int executeScopeOps(short opcode, short[] bytecode, int pc, * Handles: CHOWN, WAITPID, FORK, GETPPID, *PGRP, *PRIORITY, *SOCKOPT, * SYSCALL, SEMGET, SEMOP, MSGGET, MSGSND, MSGRCV, SHMGET, SHMREAD, SHMWRITE */ - private static int executeSystemOps(short opcode, short[] bytecode, int pc, + private static int executeSystemOps(int opcode, int[] bytecode, int pc, RuntimeBase[] registers) { switch (opcode) { case Opcodes.CHOWN: @@ -3081,7 +3081,7 @@ private static int executeSystemOps(short opcode, short[] bytecode, int pc, * Execute special I/O operations (opcodes 151-154). * Handles: EVAL_STRING, SELECT_OP, LOAD_GLOB, SLEEP_OP */ - private static int executeSpecialIO(short opcode, short[] bytecode, int pc, + private static int executeSpecialIO(int opcode, int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { switch (opcode) { case Opcodes.EVAL_STRING: @@ -3098,13 +3098,11 @@ private static int executeSpecialIO(short opcode, short[] bytecode, int pc, } /** - * Read a 32-bit integer from bytecode (stored as 2 shorts: high 16 bits, low 16 bits). - * Uses unsigned short values to reconstruct the full 32-bit integer. + * Read a 32-bit integer from bytecode (stored as 2 ints: high 16 bits, low 16 bits). + * With int[] storage, values are already full ints — no masking needed. */ - private static int readInt(short[] bytecode, int pc) { - int high = bytecode[pc] & 0xFFFF; // Keep mask here - need full 32-bit range - int low = bytecode[pc + 1] & 0xFFFF; // Keep mask here - need full 32-bit range - return (high << 16) | low; + private static int readInt(int[] bytecode, int pc) { + return (bytecode[pc] << 16) | bytecode[pc + 1]; } /** diff --git a/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java b/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java index 541699e0f..79e4726a4 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java +++ b/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java @@ -22,7 +22,7 @@ */ public class InterpretedCode extends RuntimeCode { // Bytecode and metadata - public final short[] bytecode; // Instruction stream (opcodes + operands as shorts) + public final int[] bytecode; // Instruction stream (opcodes + operands as ints) public final Object[] constants; // Constant pool (RuntimeBase objects) public final String[] stringPool; // String constants (variable names, etc.) public final int maxRegisters; // Number of registers needed @@ -57,7 +57,7 @@ public class InterpretedCode extends RuntimeCode { * @param featureFlags Feature flags at compile time (for eval STRING inheritance) * @param warningFlags Warning flags at compile time (for eval STRING inheritance) */ - public InterpretedCode(short[] bytecode, Object[] constants, String[] stringPool, + public InterpretedCode(int[] bytecode, Object[] constants, String[] stringPool, int maxRegisters, RuntimeBase[] capturedVars, String sourceName, int sourceLine, TreeMap pcToTokenIndex, @@ -81,7 +81,7 @@ public InterpretedCode(short[] bytecode, Object[] constants, String[] stringPool } // Legacy constructor for backward compatibility - public InterpretedCode(short[] bytecode, Object[] constants, String[] stringPool, + public InterpretedCode(int[] bytecode, Object[] constants, String[] stringPool, int maxRegisters, RuntimeBase[] capturedVars, String sourceName, int sourceLine, java.util.Map pcToTokenIndex) { @@ -92,7 +92,7 @@ public InterpretedCode(short[] bytecode, Object[] constants, String[] stringPool } // Legacy constructor with variableRegistry but no errorUtil - public InterpretedCode(short[] bytecode, Object[] constants, String[] stringPool, + public InterpretedCode(int[] bytecode, Object[] constants, String[] stringPool, int maxRegisters, RuntimeBase[] capturedVars, String sourceName, int sourceLine, java.util.Map pcToTokenIndex, @@ -220,7 +220,7 @@ public String disassemble() { int pc = 0; while (pc < bytecode.length) { int startPc = pc; - short opcode = bytecode[pc++]; + int opcode = bytecode[pc++]; sb.append(String.format("%4d: ", startPc)); switch (opcode) { @@ -1407,20 +1407,18 @@ public String disassemble() { } /** - * Read a 32-bit integer from bytecode (stored as 2 shorts: high 16 bits, low 16 bits). - * Uses unsigned short values to reconstruct the full 32-bit integer. + * Read a 32-bit integer from bytecode (stored as 2 ints: high 16 bits, low 16 bits). + * With int[] storage, values are already full ints — no masking needed. */ - private static int readInt(short[] bytecode, int pc) { - int high = bytecode[pc] & 0xFFFF; // Keep mask here - need full 32-bit range - int low = bytecode[pc + 1] & 0xFFFF; // Keep mask here - need full 32-bit range - return (high << 16) | low; + private static int readInt(int[] bytecode, int pc) { + return (bytecode[pc] << 16) | bytecode[pc + 1]; } /** * Builder class for constructing InterpretedCode instances. */ public static class Builder { - private short[] bytecode; + private int[] bytecode; private Object[] constants = new Object[0]; private String[] stringPool = new String[0]; private int maxRegisters = 10; @@ -1428,7 +1426,7 @@ public static class Builder { private String sourceName = ""; private int sourceLine = 1; - public Builder bytecode(short[] bytecode) { + public Builder bytecode(int[] bytecode) { this.bytecode = bytecode; return this; } diff --git a/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java b/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java index 5d58318b9..b002b5e28 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java +++ b/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java @@ -18,7 +18,7 @@ public class MiscOpcodeHandler { * Execute miscellaneous operators that take arguments and call runtime methods. * Format: OPCODE rd argsReg ctx */ - public static int execute(short opcode, short[] bytecode, int pc, RuntimeBase[] registers) { + public static int execute(int opcode, int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int argsReg = bytecode[pc++]; int ctx = bytecode[pc++]; diff --git a/src/main/java/org/perlonjava/backend/bytecode/OpcodeHandlerExtended.java b/src/main/java/org/perlonjava/backend/bytecode/OpcodeHandlerExtended.java index 95318bb18..b9c5cc77c 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/OpcodeHandlerExtended.java +++ b/src/main/java/org/perlonjava/backend/bytecode/OpcodeHandlerExtended.java @@ -24,7 +24,7 @@ public class OpcodeHandlerExtended { * @param registers Register file * @return Updated program counter */ - public static int executeSprintf(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeSprintf(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int formatReg = bytecode[pc++]; int argsListReg = bytecode[pc++]; @@ -45,7 +45,7 @@ public static int executeSprintf(short[] bytecode, int pc, RuntimeBase[] registe * @param registers Register file * @return Updated program counter */ - public static int executeChop(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeChop(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int scalarReg = bytecode[pc++]; @@ -63,7 +63,7 @@ public static int executeChop(short[] bytecode, int pc, RuntimeBase[] registers) * @param registers Register file * @return Updated program counter */ - public static int executeGetReplacementRegex(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeGetReplacementRegex(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int patternReg = bytecode[pc++]; int replacementReg = bytecode[pc++]; @@ -86,7 +86,7 @@ public static int executeGetReplacementRegex(short[] bytecode, int pc, RuntimeBa * @param registers Register file * @return Updated program counter */ - public static int executeSubstrVar(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeSubstrVar(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int argsListReg = bytecode[pc++]; int ctx = bytecode[pc++]; @@ -107,7 +107,7 @@ public static int executeSubstrVar(short[] bytecode, int pc, RuntimeBase[] regis * @param registers Register file * @return Updated program counter */ - public static int executeRepeatAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeRepeatAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; RuntimeBase result = Operator.repeat( @@ -128,7 +128,7 @@ public static int executeRepeatAssign(short[] bytecode, int pc, RuntimeBase[] re * @param registers Register file * @return Updated program counter */ - public static int executePowAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executePowAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; RuntimeBase val1 = registers[rd]; @@ -149,7 +149,7 @@ public static int executePowAssign(short[] bytecode, int pc, RuntimeBase[] regis * @param registers Register file * @return Updated program counter */ - public static int executeLeftShiftAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeLeftShiftAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; RuntimeScalar s1 = (RuntimeScalar) registers[rd]; @@ -168,7 +168,7 @@ public static int executeLeftShiftAssign(short[] bytecode, int pc, RuntimeBase[] * @param registers Register file * @return Updated program counter */ - public static int executeRightShiftAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeRightShiftAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; RuntimeScalar s1 = (RuntimeScalar) registers[rd]; @@ -187,7 +187,7 @@ public static int executeRightShiftAssign(short[] bytecode, int pc, RuntimeBase[ * @param registers Register file * @return Updated program counter */ - public static int executeLogicalAndAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeLogicalAndAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; RuntimeScalar s1 = ((RuntimeBase) registers[rd]).scalar(); @@ -210,7 +210,7 @@ public static int executeLogicalAndAssign(short[] bytecode, int pc, RuntimeBase[ * @param registers Register file * @return Updated program counter */ - public static int executeLogicalOrAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeLogicalOrAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; RuntimeScalar s1 = ((RuntimeBase) registers[rd]).scalar(); @@ -233,7 +233,7 @@ public static int executeLogicalOrAssign(short[] bytecode, int pc, RuntimeBase[] * @param registers Register file * @return Updated program counter */ - public static int executeStringConcatAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeStringConcatAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; RuntimeScalar result = StringOperators.stringConcat( @@ -253,7 +253,7 @@ public static int executeStringConcatAssign(short[] bytecode, int pc, RuntimeBas * @param registers Register file * @return Updated program counter */ - public static int executeBitwiseAndAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeBitwiseAndAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; RuntimeScalar result = BitwiseOperators.bitwiseAnd( @@ -273,7 +273,7 @@ public static int executeBitwiseAndAssign(short[] bytecode, int pc, RuntimeBase[ * @param registers Register file * @return Updated program counter */ - public static int executeBitwiseOrAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeBitwiseOrAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; RuntimeScalar result = BitwiseOperators.bitwiseOrBinary( @@ -293,7 +293,7 @@ public static int executeBitwiseOrAssign(short[] bytecode, int pc, RuntimeBase[] * @param registers Register file * @return Updated program counter */ - public static int executeBitwiseXorAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeBitwiseXorAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; RuntimeScalar result = BitwiseOperators.bitwiseXorBinary( @@ -313,7 +313,7 @@ public static int executeBitwiseXorAssign(short[] bytecode, int pc, RuntimeBase[ * @param registers Register file * @return Updated program counter */ - public static int executeStringBitwiseAndAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeStringBitwiseAndAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; RuntimeScalar result = BitwiseOperators.bitwiseAndDot( @@ -333,7 +333,7 @@ public static int executeStringBitwiseAndAssign(short[] bytecode, int pc, Runtim * @param registers Register file * @return Updated program counter */ - public static int executeStringBitwiseOrAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeStringBitwiseOrAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; RuntimeScalar result = BitwiseOperators.bitwiseOrDot( @@ -353,7 +353,7 @@ public static int executeStringBitwiseOrAssign(short[] bytecode, int pc, Runtime * @param registers Register file * @return Updated program counter */ - public static int executeStringBitwiseXorAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeStringBitwiseXorAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; RuntimeScalar result = BitwiseOperators.bitwiseXorDot( @@ -370,7 +370,7 @@ public static int executeStringBitwiseXorAssign(short[] bytecode, int pc, Runtim * Execute bitwise AND binary operation. * Format: BITWISE_AND_BINARY rd rs1 rs2 */ - public static int executeBitwiseAndBinary(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeBitwiseAndBinary(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs1 = bytecode[pc++]; int rs2 = bytecode[pc++]; @@ -385,7 +385,7 @@ public static int executeBitwiseAndBinary(short[] bytecode, int pc, RuntimeBase[ * Execute bitwise OR binary operation. * Format: BITWISE_OR_BINARY rd rs1 rs2 */ - public static int executeBitwiseOrBinary(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeBitwiseOrBinary(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs1 = bytecode[pc++]; int rs2 = bytecode[pc++]; @@ -400,7 +400,7 @@ public static int executeBitwiseOrBinary(short[] bytecode, int pc, RuntimeBase[] * Execute bitwise XOR binary operation. * Format: BITWISE_XOR_BINARY rd rs1 rs2 */ - public static int executeBitwiseXorBinary(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeBitwiseXorBinary(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs1 = bytecode[pc++]; int rs2 = bytecode[pc++]; @@ -415,7 +415,7 @@ public static int executeBitwiseXorBinary(short[] bytecode, int pc, RuntimeBase[ * Execute string bitwise AND operation. * Format: STRING_BITWISE_AND rd rs1 rs2 */ - public static int executeStringBitwiseAnd(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeStringBitwiseAnd(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs1 = bytecode[pc++]; int rs2 = bytecode[pc++]; @@ -430,7 +430,7 @@ public static int executeStringBitwiseAnd(short[] bytecode, int pc, RuntimeBase[ * Execute string bitwise OR operation. * Format: STRING_BITWISE_OR rd rs1 rs2 */ - public static int executeStringBitwiseOr(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeStringBitwiseOr(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs1 = bytecode[pc++]; int rs2 = bytecode[pc++]; @@ -445,7 +445,7 @@ public static int executeStringBitwiseOr(short[] bytecode, int pc, RuntimeBase[] * Execute string bitwise XOR operation. * Format: STRING_BITWISE_XOR rd rs1 rs2 */ - public static int executeStringBitwiseXor(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeStringBitwiseXor(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs1 = bytecode[pc++]; int rs2 = bytecode[pc++]; @@ -460,7 +460,7 @@ public static int executeStringBitwiseXor(short[] bytecode, int pc, RuntimeBase[ * Execute bitwise NOT binary operation. * Format: BITWISE_NOT_BINARY rd rs */ - public static int executeBitwiseNotBinary(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeBitwiseNotBinary(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; registers[rd] = BitwiseOperators.bitwiseNotBinary((RuntimeScalar) registers[rs]); @@ -471,7 +471,7 @@ public static int executeBitwiseNotBinary(short[] bytecode, int pc, RuntimeBase[ * Execute bitwise NOT string operation. * Format: BITWISE_NOT_STRING rd rs */ - public static int executeBitwiseNotString(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeBitwiseNotString(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; registers[rd] = BitwiseOperators.bitwiseNotDot((RuntimeScalar) registers[rs]); @@ -482,7 +482,7 @@ public static int executeBitwiseNotString(short[] bytecode, int pc, RuntimeBase[ * Execute stat operation. * Format: STAT rd rs ctx */ - public static int executeStat(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeStat(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; int ctx = bytecode[pc++]; @@ -494,7 +494,7 @@ public static int executeStat(short[] bytecode, int pc, RuntimeBase[] registers) * Execute lstat operation. * Format: LSTAT rd rs ctx */ - public static int executeLstat(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeLstat(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; int ctx = bytecode[pc++]; @@ -506,7 +506,7 @@ public static int executeLstat(short[] bytecode, int pc, RuntimeBase[] registers * Execute print operation. * Format: PRINT contentReg filehandleReg */ - public static int executePrint(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executePrint(int[] bytecode, int pc, RuntimeBase[] registers) { int contentReg = bytecode[pc++]; int filehandleReg = bytecode[pc++]; @@ -544,7 +544,7 @@ public static int executePrint(short[] bytecode, int pc, RuntimeBase[] registers * Execute say operation. * Format: SAY contentReg filehandleReg */ - public static int executeSay(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeSay(int[] bytecode, int pc, RuntimeBase[] registers) { int contentReg = bytecode[pc++]; int filehandleReg = bytecode[pc++]; @@ -582,7 +582,7 @@ public static int executeSay(short[] bytecode, int pc, RuntimeBase[] registers) * Execute chomp operation. * Format: CHOMP rd rs */ - public static int executeChomp(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeChomp(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; registers[rd] = registers[rs].chomp(); @@ -593,7 +593,7 @@ public static int executeChomp(short[] bytecode, int pc, RuntimeBase[] registers * Execute wantarray operation. * Format: WANTARRAY rd wantarrayReg */ - public static int executeWantarray(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeWantarray(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int wantarrayReg = bytecode[pc++]; int ctx = ((RuntimeScalar) registers[wantarrayReg]).getInt(); @@ -605,7 +605,7 @@ public static int executeWantarray(short[] bytecode, int pc, RuntimeBase[] regis * Execute require operation. * Format: REQUIRE rd rs */ - public static int executeRequire(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeRequire(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; registers[rd] = ModuleOperators.require((RuntimeScalar) registers[rs]); @@ -616,7 +616,7 @@ public static int executeRequire(short[] bytecode, int pc, RuntimeBase[] registe * Execute pos operation. * Format: POS rd rs */ - public static int executePos(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executePos(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; registers[rd] = ((RuntimeScalar) registers[rs]).pos(); @@ -627,7 +627,7 @@ public static int executePos(short[] bytecode, int pc, RuntimeBase[] registers) * Execute index operation. * Format: INDEX rd strReg substrReg posReg */ - public static int executeIndex(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeIndex(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int strReg = bytecode[pc++]; int substrReg = bytecode[pc++]; @@ -644,7 +644,7 @@ public static int executeIndex(short[] bytecode, int pc, RuntimeBase[] registers * Execute rindex operation. * Format: RINDEX rd strReg substrReg posReg */ - public static int executeRindex(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeRindex(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int strReg = bytecode[pc++]; int substrReg = bytecode[pc++]; @@ -661,7 +661,7 @@ public static int executeRindex(short[] bytecode, int pc, RuntimeBase[] register * Execute pre-increment operation. * Format: PRE_AUTOINCREMENT rd */ - public static int executePreAutoIncrement(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executePreAutoIncrement(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; ((RuntimeScalar) registers[rd]).preAutoIncrement(); return pc; @@ -671,7 +671,7 @@ public static int executePreAutoIncrement(short[] bytecode, int pc, RuntimeBase[ * Execute post-increment operation. * Format: POST_AUTOINCREMENT rd rs */ - public static int executePostAutoIncrement(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executePostAutoIncrement(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; registers[rd] = ((RuntimeScalar) registers[rs]).postAutoIncrement(); @@ -682,7 +682,7 @@ public static int executePostAutoIncrement(short[] bytecode, int pc, RuntimeBase * Execute pre-decrement operation. * Format: PRE_AUTODECREMENT rd */ - public static int executePreAutoDecrement(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executePreAutoDecrement(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; ((RuntimeScalar) registers[rd]).preAutoDecrement(); return pc; @@ -692,7 +692,7 @@ public static int executePreAutoDecrement(short[] bytecode, int pc, RuntimeBase[ * Execute post-decrement operation. * Format: POST_AUTODECREMENT rd rs */ - public static int executePostAutoDecrement(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executePostAutoDecrement(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; registers[rd] = ((RuntimeScalar) registers[rs]).postAutoDecrement(); @@ -703,7 +703,7 @@ public static int executePostAutoDecrement(short[] bytecode, int pc, RuntimeBase * Execute open operation. * Format: OPEN rd ctx argsReg */ - public static int executeOpen(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeOpen(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int ctx = bytecode[pc++]; int argsReg = bytecode[pc++]; @@ -717,7 +717,7 @@ public static int executeOpen(short[] bytecode, int pc, RuntimeBase[] registers) * Execute readline operation. * Format: READLINE rd fhReg ctx */ - public static int executeReadline(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeReadline(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int fhReg = bytecode[pc++]; int ctx = bytecode[pc++]; @@ -729,7 +729,7 @@ public static int executeReadline(short[] bytecode, int pc, RuntimeBase[] regist * Execute match regex operation. * Format: MATCH_REGEX rd stringReg regexReg ctx */ - public static int executeMatchRegex(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeMatchRegex(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int stringReg = bytecode[pc++]; int regexReg = bytecode[pc++]; @@ -746,7 +746,7 @@ public static int executeMatchRegex(short[] bytecode, int pc, RuntimeBase[] regi * Execute negated match regex operation. * Format: MATCH_REGEX_NOT rd stringReg regexReg ctx */ - public static int executeMatchRegexNot(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeMatchRegexNot(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int stringReg = bytecode[pc++]; int regexReg = bytecode[pc++]; @@ -765,7 +765,7 @@ public static int executeMatchRegexNot(short[] bytecode, int pc, RuntimeBase[] r * Execute create closure operation. * Format: CREATE_CLOSURE rd template_idx num_captures reg1 reg2 ... */ - public static int executeCreateClosure(short[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { + public static int executeCreateClosure(int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { int rd = bytecode[pc++]; int templateIdx = bytecode[pc++]; int numCaptures = bytecode[pc++]; @@ -802,7 +802,7 @@ public static int executeCreateClosure(short[] bytecode, int pc, RuntimeBase[] r * Execute iterator create operation. * Format: ITERATOR_CREATE rd rs */ - public static int executeIteratorCreate(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeIteratorCreate(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; @@ -819,7 +819,7 @@ public static int executeIteratorCreate(short[] bytecode, int pc, RuntimeBase[] * Execute iterator has next operation. * Format: ITERATOR_HAS_NEXT rd iterReg */ - public static int executeIteratorHasNext(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeIteratorHasNext(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int iterReg = bytecode[pc++]; @@ -837,7 +837,7 @@ public static int executeIteratorHasNext(short[] bytecode, int pc, RuntimeBase[] * Execute iterator next operation. * Format: ITERATOR_NEXT rd iterReg */ - public static int executeIteratorNext(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeIteratorNext(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int iterReg = bytecode[pc++]; @@ -855,7 +855,7 @@ public static int executeIteratorNext(short[] bytecode, int pc, RuntimeBase[] re * Execute subtract assign operation. * Format: SUBTRACT_ASSIGN rd rs */ - public static int executeSubtractAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeSubtractAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; @@ -872,7 +872,7 @@ public static int executeSubtractAssign(short[] bytecode, int pc, RuntimeBase[] * Execute multiply assign operation. * Format: MULTIPLY_ASSIGN rd rs */ - public static int executeMultiplyAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeMultiplyAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; @@ -889,7 +889,7 @@ public static int executeMultiplyAssign(short[] bytecode, int pc, RuntimeBase[] * Execute divide assign operation. * Format: DIVIDE_ASSIGN rd rs */ - public static int executeDivideAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeDivideAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; @@ -906,7 +906,7 @@ public static int executeDivideAssign(short[] bytecode, int pc, RuntimeBase[] re * Execute modulus assign operation. * Format: MODULUS_ASSIGN rd rs */ - public static int executeModulusAssign(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeModulusAssign(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; diff --git a/src/main/java/org/perlonjava/backend/bytecode/OpcodeHandlerFileTest.java b/src/main/java/org/perlonjava/backend/bytecode/OpcodeHandlerFileTest.java index 50fbe5d1b..9e6b5971f 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/OpcodeHandlerFileTest.java +++ b/src/main/java/org/perlonjava/backend/bytecode/OpcodeHandlerFileTest.java @@ -22,7 +22,7 @@ public class OpcodeHandlerFileTest { * @param opcode The file test opcode (190-216) * @return Updated program counter */ - public static int executeFileTest(short[] bytecode, int pc, RuntimeBase[] registers, short opcode) { + public static int executeFileTest(int[] bytecode, int pc, RuntimeBase[] registers, int opcode) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; diff --git a/src/main/java/org/perlonjava/backend/bytecode/ScalarBinaryOpcodeHandler.java b/src/main/java/org/perlonjava/backend/bytecode/ScalarBinaryOpcodeHandler.java index 61967aa61..55722acdd 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/ScalarBinaryOpcodeHandler.java +++ b/src/main/java/org/perlonjava/backend/bytecode/ScalarBinaryOpcodeHandler.java @@ -17,7 +17,7 @@ public class ScalarBinaryOpcodeHandler { /** * Execute scalar binary operations (atan2, eq, ne, lt, le, gt, ge, cmp, etc.) operation. */ - public static int execute(int opcode, short[] bytecode, int pc, + public static int execute(int opcode, int[] bytecode, int pc, RuntimeBase[] registers) { // Read registers (shared by all opcodes in this group) int rd = bytecode[pc++]; @@ -47,7 +47,7 @@ public static int execute(int opcode, short[] bytecode, int pc, /** * Disassemble scalar binary operations (atan2, eq, ne, lt, le, gt, ge, cmp, etc.) operation. */ - public static int disassemble(int opcode, short[] bytecode, int pc, + public static int disassemble(int opcode, int[] bytecode, int pc, StringBuilder sb) { int rd = bytecode[pc++]; int rs1 = bytecode[pc++]; diff --git a/src/main/java/org/perlonjava/backend/bytecode/ScalarUnaryOpcodeHandler.java b/src/main/java/org/perlonjava/backend/bytecode/ScalarUnaryOpcodeHandler.java index 0e52fa990..971aacd13 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/ScalarUnaryOpcodeHandler.java +++ b/src/main/java/org/perlonjava/backend/bytecode/ScalarUnaryOpcodeHandler.java @@ -22,7 +22,7 @@ public class ScalarUnaryOpcodeHandler { /** * Execute scalar unary operations (chr, ord, abs, sin, cos, lc, uc, etc.) operation. */ - public static int execute(int opcode, short[] bytecode, int pc, + public static int execute(int opcode, int[] bytecode, int pc, RuntimeBase[] registers) { // Read registers (shared by all opcodes in this group) int rd = bytecode[pc++]; @@ -70,7 +70,7 @@ public static int execute(int opcode, short[] bytecode, int pc, /** * Disassemble scalar unary operations (chr, ord, abs, sin, cos, lc, uc, etc.) operation. */ - public static int disassemble(int opcode, short[] bytecode, int pc, + public static int disassemble(int opcode, int[] bytecode, int pc, StringBuilder sb) { int rd = bytecode[pc++]; int rs = bytecode[pc++]; diff --git a/src/main/java/org/perlonjava/backend/bytecode/SlowOpcodeHandler.java b/src/main/java/org/perlonjava/backend/bytecode/SlowOpcodeHandler.java index cbffa1891..80918b991 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/SlowOpcodeHandler.java +++ b/src/main/java/org/perlonjava/backend/bytecode/SlowOpcodeHandler.java @@ -66,7 +66,7 @@ public class SlowOpcodeHandler { * Format: [SLOW_CHOWN] [rs_list] [rs_uid] [rs_gid] * Effect: Changes ownership of files in list */ - public static int executeChown(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeChown(int[] bytecode, int pc, RuntimeBase[] registers) { int listReg = bytecode[pc++]; int uidReg = bytecode[pc++]; int gidReg = bytecode[pc++]; @@ -83,7 +83,7 @@ public static int executeChown(short[] bytecode, int pc, RuntimeBase[] registers * Format: [SLOW_WAITPID] [rd] [rs_pid] [rs_flags] * Effect: Waits for child process and returns status */ - public static int executeWaitpid(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeWaitpid(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int pidReg = bytecode[pc++]; int flagsReg = bytecode[pc++]; @@ -102,7 +102,7 @@ public static int executeWaitpid(short[] bytecode, int pc, RuntimeBase[] registe * Format: [SLOW_SETSOCKOPT] [rs_socket] [rs_level] [rs_optname] [rs_optval] * Effect: Sets socket option */ - public static int executeSetsockopt(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeSetsockopt(int[] bytecode, int pc, RuntimeBase[] registers) { int socketReg = bytecode[pc++]; int levelReg = bytecode[pc++]; int optnameReg = bytecode[pc++]; @@ -119,7 +119,7 @@ public static int executeSetsockopt(short[] bytecode, int pc, RuntimeBase[] regi * Format: [SLOW_GETSOCKOPT] [rd] [rs_socket] [rs_level] [rs_optname] * Effect: Gets socket option value */ - public static int executeGetsockopt(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeGetsockopt(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int socketReg = bytecode[pc++]; int levelReg = bytecode[pc++]; @@ -136,7 +136,7 @@ public static int executeGetsockopt(short[] bytecode, int pc, RuntimeBase[] regi * Format: [SLOW_GETPRIORITY] [rd] [rs_which] [rs_who] * Effect: Gets process priority */ - public static int executeGetpriority(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeGetpriority(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int whichReg = bytecode[pc++]; int whoReg = bytecode[pc++]; @@ -151,7 +151,7 @@ public static int executeGetpriority(short[] bytecode, int pc, RuntimeBase[] reg * Format: [SLOW_SETPRIORITY] [rs_which] [rs_who] [rs_priority] * Effect: Sets process priority */ - public static int executeSetpriority(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeSetpriority(int[] bytecode, int pc, RuntimeBase[] registers) { int whichReg = bytecode[pc++]; int whoReg = bytecode[pc++]; int priorityReg = bytecode[pc++]; @@ -166,7 +166,7 @@ public static int executeSetpriority(short[] bytecode, int pc, RuntimeBase[] reg * Format: [SLOW_GETPGRP] [rd] [rs_pid] * Effect: Gets process group ID */ - public static int executeGetpgrp(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeGetpgrp(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int pidReg = bytecode[pc++]; @@ -180,7 +180,7 @@ public static int executeGetpgrp(short[] bytecode, int pc, RuntimeBase[] registe * Format: [SLOW_SETPGRP] [rs_pid] [rs_pgrp] * Effect: Sets process group ID */ - public static int executeSetpgrp(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeSetpgrp(int[] bytecode, int pc, RuntimeBase[] registers) { int pidReg = bytecode[pc++]; int pgrpReg = bytecode[pc++]; @@ -193,7 +193,7 @@ public static int executeSetpgrp(short[] bytecode, int pc, RuntimeBase[] registe * Format: [SLOW_GETPPID] [rd] * Effect: Gets parent process ID */ - public static int executeGetppid(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeGetppid(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; // Java 9+ has ProcessHandle.current().parent() @@ -207,7 +207,7 @@ public static int executeGetppid(short[] bytecode, int pc, RuntimeBase[] registe * Format: [SLOW_FORK] [rd] * Effect: Forks process (not supported in Java) */ - public static int executeFork(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeFork(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; // fork() is not supported in Java - return -1 (error) @@ -222,7 +222,7 @@ public static int executeFork(short[] bytecode, int pc, RuntimeBase[] registers) * Format: [SLOW_SEMGET] [rd] [rs_key] [rs_nsems] [rs_flags] * Effect: Gets semaphore set identifier */ - public static int executeSemget(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeSemget(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int keyReg = bytecode[pc++]; int nsemsReg = bytecode[pc++]; @@ -237,7 +237,7 @@ public static int executeSemget(short[] bytecode, int pc, RuntimeBase[] register * Format: [SLOW_SEMOP] [rd] [rs_semid] [rs_opstring] * Effect: Performs semaphore operations */ - public static int executeSemop(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeSemop(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int semidReg = bytecode[pc++]; int opstringReg = bytecode[pc++]; @@ -251,7 +251,7 @@ public static int executeSemop(short[] bytecode, int pc, RuntimeBase[] registers * Format: [SLOW_MSGGET] [rd] [rs_key] [rs_flags] * Effect: Gets message queue identifier */ - public static int executeMsgget(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeMsgget(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int keyReg = bytecode[pc++]; int flagsReg = bytecode[pc++]; @@ -265,7 +265,7 @@ public static int executeMsgget(short[] bytecode, int pc, RuntimeBase[] register * Format: [SLOW_MSGSND] [rd] [rs_id] [rs_msg] [rs_flags] * Effect: Sends message to queue */ - public static int executeMsgsnd(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeMsgsnd(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int idReg = bytecode[pc++]; int msgReg = bytecode[pc++]; @@ -280,7 +280,7 @@ public static int executeMsgsnd(short[] bytecode, int pc, RuntimeBase[] register * Format: [SLOW_MSGRCV] [rd] [rs_id] [rs_size] [rs_type] [rs_flags] * Effect: Receives message from queue */ - public static int executeMsgrcv(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeMsgrcv(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int idReg = bytecode[pc++]; int sizeReg = bytecode[pc++]; @@ -296,7 +296,7 @@ public static int executeMsgrcv(short[] bytecode, int pc, RuntimeBase[] register * Format: [SLOW_SHMGET] [rd] [rs_key] [rs_size] [rs_flags] * Effect: Gets shared memory segment */ - public static int executeShmget(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeShmget(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int keyReg = bytecode[pc++]; int sizeReg = bytecode[pc++]; @@ -311,7 +311,7 @@ public static int executeShmget(short[] bytecode, int pc, RuntimeBase[] register * Format: [SLOW_SHMREAD] [rd] [rs_id] [rs_pos] [rs_size] * Effect: Reads from shared memory */ - public static int executeShmread(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeShmread(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int idReg = bytecode[pc++]; int posReg = bytecode[pc++]; @@ -326,7 +326,7 @@ public static int executeShmread(short[] bytecode, int pc, RuntimeBase[] registe * Format: [SLOW_SHMWRITE] [rs_id] [rs_pos] [rs_string] * Effect: Writes to shared memory */ - public static int executeShmwrite(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeShmwrite(int[] bytecode, int pc, RuntimeBase[] registers) { int idReg = bytecode[pc++]; int posReg = bytecode[pc++]; int stringReg = bytecode[pc++]; @@ -340,7 +340,7 @@ public static int executeShmwrite(short[] bytecode, int pc, RuntimeBase[] regist * Format: [SLOW_SYSCALL] [rd] [rs_number] [arg_count] [rs_arg1] [rs_arg2] ... * Effect: Makes arbitrary system call */ - public static int executeSyscall(short[] bytecode, int pc, RuntimeBase[] registers) { + public static int executeSyscall(int[] bytecode, int pc, RuntimeBase[] registers) { int rd = bytecode[pc++]; int numberReg = bytecode[pc++]; int argCount = bytecode[pc++]; @@ -360,7 +360,7 @@ public static int executeSyscall(short[] bytecode, int pc, RuntimeBase[] registe * Effect: Dynamically evaluates Perl code string */ public static int executeEvalString( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { @@ -398,7 +398,7 @@ public static int executeEvalString( * Effect: Sets or gets the default output filehandle */ public static int executeSelect( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -423,7 +423,7 @@ public static int executeSelect( * Effect: Loads a glob/filehandle from global variables */ public static int executeLoadGlob( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { @@ -450,7 +450,7 @@ public static int executeLoadGlob( * @return The new program counter */ public static int executeSleep( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -478,7 +478,7 @@ public static int executeSleep( * @return Updated program counter */ public static int executeDerefArray( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -509,7 +509,7 @@ public static int executeDerefArray( * Effect: rd = PersistentVariable.retrieveBeginScalar(stringPool[nameIdx], begin_id) */ public static int executeRetrieveBeginScalar( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { @@ -531,7 +531,7 @@ public static int executeRetrieveBeginScalar( * Effect: rd = PersistentVariable.retrieveBeginArray(stringPool[nameIdx], begin_id) */ public static int executeRetrieveBeginArray( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { @@ -553,7 +553,7 @@ public static int executeRetrieveBeginArray( * Effect: rd = PersistentVariable.retrieveBeginHash(stringPool[nameIdx], begin_id) */ public static int executeRetrieveBeginHash( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { @@ -575,7 +575,7 @@ public static int executeRetrieveBeginHash( * Effect: rd = GlobalRuntimeScalar.makeLocal(stringPool[nameIdx]) */ public static int executeLocalScalar( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { @@ -597,7 +597,7 @@ public static int executeLocalScalar( * In scalar context, returns last element removed (or undef if no elements removed) */ public static int executeSplice( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -630,7 +630,7 @@ public static int executeSplice( * Effect: rd = array.getSlice(indices) */ public static int executeArraySlice( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -653,7 +653,7 @@ public static int executeArraySlice( * Effect: rd = Operator.reverse(ctx, args...) */ public static int executeReverse( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -676,7 +676,7 @@ public static int executeReverse( * Effect: Sets array elements at indices to values */ public static int executeArraySliceSet( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -699,7 +699,7 @@ public static int executeArraySliceSet( * Effect: rd = Operator.split(pattern, args, ctx) */ public static int executeSplit( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -723,7 +723,7 @@ public static int executeSplit( * Effect: rd = exists operand (fallback for non-simple cases) */ public static int executeExists( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -743,7 +743,7 @@ public static int executeExists( * Effect: rd = delete operand (fallback for non-simple cases) */ public static int executeDelete( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -767,7 +767,7 @@ public static int executeDelete( * @return Updated program counter */ public static int executeDerefHash( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -798,7 +798,7 @@ public static int executeDerefHash( * Effect: rd = RuntimeArray of values for the given keys */ public static int executeHashSlice( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -828,7 +828,7 @@ public static int executeHashSlice( * Effect: rd = RuntimeList of deleted values */ public static int executeHashSliceDelete( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -858,7 +858,7 @@ public static int executeHashSliceDelete( * Effect: Assign values to multiple hash keys (slice assignment) */ public static int executeHashSliceSet( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -898,7 +898,7 @@ public static int executeHashSliceSet( * Format: [SLOWOP_LIST_SLICE_FROM] [rd] [listReg] [startIndex] */ public static int executeListSliceFrom( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -959,7 +959,7 @@ public static int executeListSliceFrom( * Format: [SLOWOP_LENGTH] [rd] [stringReg] */ public static int executeLength( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -982,7 +982,7 @@ public static int executeLength( * Returns: Count of transliterated characters */ public static int executeTransliterate( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { @@ -1013,7 +1013,7 @@ public static int executeTransliterate( * Effect: Applies file test operator to cached filehandle from last stat/lstat */ public static int executeFiletestLastHandle( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers, InterpretedCode code) { @@ -1036,7 +1036,7 @@ public static int executeFiletestLastHandle( * This ensures proper glob slot access without incorrectly dereferencing the glob as a hash */ public static int executeGlobSlotGet( - short[] bytecode, + int[] bytecode, int pc, RuntimeBase[] registers) { From 279b0738e5bd6d961f8c57d8db4d250b43398905 Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Mon, 23 Feb 2026 15:22:36 +0100 Subject: [PATCH 8/9] Simplify bytecode int encoding: use 1 slot per int (was 2 half-slots) --- .../backend/bytecode/BytecodeCompiler.java | 10 +++--- .../backend/bytecode/BytecodeInterpreter.java | 28 +++++++-------- .../backend/bytecode/InterpretedCode.java | 36 +++++++++---------- 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java b/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java index ffcce66d8..27e1de016 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java +++ b/src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java @@ -2877,8 +2877,7 @@ void emit(int value) { } void emitInt(int value) { - bytecode.add(value >> 16); // High 16 bits - bytecode.add(value & 0xFFFF); // Low 16 bits + bytecode.add(value); // Full int in one slot } /** @@ -2901,16 +2900,15 @@ void emitReg(int register) { * Used for forward jumps where the target is unknown at emit time. */ void patchIntOffset(int position, int target) { - // Store absolute target address (not relative offset) as 2 ints - bytecode.set(position, (target >> 16) & 0xFFFF); // High 16 bits - bytecode.set(position + 1, target & 0xFFFF); // Low 16 bits + // Store absolute target address in one slot + bytecode.set(position, target); } /** * Helper for forward jumps - emit placeholder and return position for later patching. */ private void emitJumpPlaceholder() { - emitInt(0); // 2 shorts (4 bytes) placeholder + emitInt(0); // 1 int slot placeholder } private void patchJump(int placeholderPos, int target) { diff --git a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java index db1a370df..954e406f7 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java +++ b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java @@ -115,7 +115,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c // Conditional jump: if (!rs) pc = offset int condReg = bytecode[pc++]; int target = readInt(bytecode, pc); - pc += 2; + pc += 1; // Convert to scalar if needed for boolean test RuntimeBase condBase = registers[condReg]; @@ -133,7 +133,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c // Conditional jump: if (rs) pc = offset int condReg = bytecode[pc++]; int target = readInt(bytecode, pc); - pc += 2; + pc += 1; // Convert to scalar if needed for boolean test RuntimeBase condBase = registers[condReg]; @@ -171,7 +171,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c // Load integer: rd = immediate (create NEW mutable scalar, not cached) int rd = bytecode[pc++]; int value = readInt(bytecode, pc); - pc += 2; + pc += 1; // Create NEW RuntimeScalar (mutable) instead of using cache // This is needed for local variables that may be modified (++/--) registers[rd] = new RuntimeScalar(value); @@ -254,7 +254,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c int iterReg = bytecode[pc++]; int nameIdx = bytecode[pc++]; int bodyTarget = readInt(bytecode, pc); - pc += 2; + pc += 1; String name = code.stringPool[nameIdx]; RuntimeScalar iterScalar = (RuntimeScalar) registers[iterReg]; @@ -490,7 +490,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c int rd = bytecode[pc++]; int rs = bytecode[pc++]; int immediate = readInt(bytecode, pc); - pc += 2; + pc += 1; // Calls specialized unboxed method (rare optimization) registers[rd] = MathOperators.add( (RuntimeScalar) registers[rs], @@ -600,7 +600,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c int rd = bytecode[pc++]; int iterReg = bytecode[pc++]; int bodyTarget = readInt(bytecode, pc); // Absolute target address - pc += 2; // Skip the int we just read + pc += 1; // Skip the int we just read RuntimeScalar iterScalar = (RuntimeScalar) registers[iterReg]; @SuppressWarnings("unchecked") @@ -1087,7 +1087,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c // Add immediate and assign: rd += imm (modifies rd in place) int rd = bytecode[pc++]; int immediate = readInt(bytecode, pc); - pc += 2; + pc += 1; RuntimeScalar result = MathOperators.add((RuntimeScalar) registers[rd], immediate); ((RuntimeScalar) registers[rd]).set(result); break; @@ -1417,7 +1417,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c // catch_target is absolute bytecode address (4 bytes) int catchPc = readInt(bytecode, pc); // Read 4-byte absolute address - pc += 2; // Skip the 2 shorts we just read + pc += 1; // Skip the 2 shorts we just read // Push catch PC onto eval stack evalCatchStack.push(catchPc); @@ -1651,7 +1651,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c int listReg = bytecode[pc++]; int closureReg = bytecode[pc++]; int packageIdx = readInt(bytecode, pc); - pc += 2; + pc += 1; RuntimeBase listBase = registers[listReg]; RuntimeList list = listBase.getList(); @@ -2288,7 +2288,7 @@ private static int executeTypeOps(int opcode, int[] bytecode, int pc, int rd = bytecode[pc++]; int rs = bytecode[pc++]; int packageIdx = readInt(bytecode, pc); - pc += 2; // readInt reads 2 shorts + pc += 1; // readInt reads 2 shorts RuntimeScalar codeRef = (RuntimeScalar) registers[rs]; String packageName = code.stringPool[packageIdx]; registers[rd] = RuntimeCode.prototype(codeRef, packageName); @@ -2559,7 +2559,7 @@ private static int executeArithmetic(int opcode, int[] bytecode, int pc, int rd = bytecode[pc++]; int rs = bytecode[pc++]; int immediate = readInt(bytecode, pc); - pc += 2; + pc += 1; registers[rd] = MathOperators.add( (RuntimeScalar) registers[rs], immediate @@ -3098,11 +3098,11 @@ private static int executeSpecialIO(int opcode, int[] bytecode, int pc, } /** - * Read a 32-bit integer from bytecode (stored as 2 ints: high 16 bits, low 16 bits). - * With int[] storage, values are already full ints — no masking needed. + * Read a 32-bit integer from bytecode (stored as 1 int slot). + * With int[] storage a full int fits in a single slot. */ private static int readInt(int[] bytecode, int pc) { - return (bytecode[pc] << 16) | bytecode[pc + 1]; + return bytecode[pc]; } /** diff --git a/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java b/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java index 79e4726a4..769d812f8 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java +++ b/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java @@ -233,30 +233,30 @@ public String disassemble() { break; case Opcodes.GOTO: sb.append("GOTO ").append(readInt(bytecode, pc)).append("\n"); - pc += 2; + pc += 1; break; case Opcodes.LAST: sb.append("LAST ").append(readInt(bytecode, pc)).append("\n"); - pc += 2; + pc += 1; break; case Opcodes.NEXT: sb.append("NEXT ").append(readInt(bytecode, pc)).append("\n"); - pc += 2; + pc += 1; break; case Opcodes.REDO: sb.append("REDO ").append(readInt(bytecode, pc)).append("\n"); - pc += 2; + pc += 1; break; case Opcodes.GOTO_IF_FALSE: int condReg = bytecode[pc++]; int target = readInt(bytecode, pc); - pc += 2; + pc += 1; sb.append("GOTO_IF_FALSE r").append(condReg).append(" -> ").append(target).append("\n"); break; case Opcodes.GOTO_IF_TRUE: condReg = bytecode[pc++]; target = readInt(bytecode, pc); - pc += 2; + pc += 1; sb.append("GOTO_IF_TRUE r").append(condReg).append(" -> ").append(target).append("\n"); break; case Opcodes.MOVE: @@ -295,7 +295,7 @@ public String disassemble() { case Opcodes.LOAD_INT: rd = bytecode[pc++]; int value = readInt(bytecode, pc); - pc += 2; + pc += 1; sb.append("LOAD_INT r").append(rd).append(" = ").append(value).append("\n"); break; case Opcodes.LOAD_STRING: @@ -398,21 +398,21 @@ public String disassemble() { rd = bytecode[pc++]; int rs = bytecode[pc++]; int imm = readInt(bytecode, pc); - pc += 2; + pc += 1; sb.append("ADD_SCALAR_INT r").append(rd).append(" = r").append(rs).append(" + ").append(imm).append("\n"); break; case Opcodes.SUB_SCALAR_INT: rd = bytecode[pc++]; rs = bytecode[pc++]; int subImm = readInt(bytecode, pc); - pc += 2; + pc += 1; sb.append("SUB_SCALAR_INT r").append(rd).append(" = r").append(rs).append(" - ").append(subImm).append("\n"); break; case Opcodes.MUL_SCALAR_INT: rd = bytecode[pc++]; rs = bytecode[pc++]; int mulImm = readInt(bytecode, pc); - pc += 2; + pc += 1; sb.append("MUL_SCALAR_INT r").append(rd).append(" = r").append(rs).append(" * ").append(mulImm).append("\n"); break; case Opcodes.CONCAT: @@ -473,7 +473,7 @@ public String disassemble() { case Opcodes.ADD_ASSIGN_INT: rd = bytecode[pc++]; imm = readInt(bytecode, pc); - pc += 2; + pc += 1; sb.append("ADD_ASSIGN_INT r").append(rd).append(" += ").append(imm).append("\n"); break; case Opcodes.STRING_CONCAT_ASSIGN: @@ -1033,7 +1033,7 @@ public String disassemble() { rs1 = bytecode[pc++]; // list register rs2 = bytecode[pc++]; // closure register int pkgIdx = readInt(bytecode, pc); - pc += 2; + pc += 1; sb.append("SORT r").append(rd).append(" = sort(r").append(rs1) .append(", r").append(rs2).append(", pkg=").append(stringPool[pkgIdx]).append(")\n"); break; @@ -1110,7 +1110,7 @@ public String disassemble() { rd = bytecode[pc++]; rs = bytecode[pc++]; int packageIdx = readInt(bytecode, pc); - pc += 2; // readInt reads 2 shorts + pc += 1; // readInt reads 2 shorts String packageName = (stringPool != null && packageIdx < stringPool.length) ? stringPool[packageIdx] : ""; sb.append("PROTOTYPE r").append(rd).append(" = prototype(r").append(rs) @@ -1142,7 +1142,7 @@ public String disassemble() { rd = bytecode[pc++]; int iterReg = bytecode[pc++]; int bodyTarget = readInt(bytecode, pc); // Absolute body address - pc += 2; + pc += 1; sb.append("FOREACH_NEXT_OR_EXIT r").append(rd) .append(" = r").append(iterReg).append(".next() and goto ") .append(bodyTarget).append("\n"); @@ -1281,7 +1281,7 @@ public String disassemble() { rd = bytecode[pc++]; int fgIterReg = bytecode[pc++]; nameIdx = bytecode[pc++]; - int fgBody = readInt(bytecode, pc); pc += 2; + int fgBody = readInt(bytecode, pc); pc += 1; sb.append("FOREACH_GLOBAL_NEXT_OR_EXIT r").append(rd).append(" = r").append(fgIterReg) .append(".next(), alias $").append(stringPool[nameIdx]).append(" and goto ").append(fgBody).append("\n"); break; @@ -1407,11 +1407,11 @@ public String disassemble() { } /** - * Read a 32-bit integer from bytecode (stored as 2 ints: high 16 bits, low 16 bits). - * With int[] storage, values are already full ints — no masking needed. + * Read a 32-bit integer from bytecode (stored as 1 int slot). + * With int[] storage a full int fits in a single slot. */ private static int readInt(int[] bytecode, int pc) { - return (bytecode[pc] << 16) | bytecode[pc + 1]; + return bytecode[pc]; } /** From aed196d3448fd1b65de48ef5eeedadf3b3989ecf Mon Sep 17 00:00:00 2001 From: Flavio Soibelmann Glock Date: Mon, 23 Feb 2026 15:39:51 +0100 Subject: [PATCH 9/9] Fix TR_TRANSLITERATE context read: use 1 int slot (was 2-slot short[] pattern) --- .../org/perlonjava/backend/bytecode/SlowOpcodeHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/perlonjava/backend/bytecode/SlowOpcodeHandler.java b/src/main/java/org/perlonjava/backend/bytecode/SlowOpcodeHandler.java index 80918b991..5e577f5f0 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/SlowOpcodeHandler.java +++ b/src/main/java/org/perlonjava/backend/bytecode/SlowOpcodeHandler.java @@ -992,8 +992,8 @@ public static int executeTransliterate( int modifiersReg = bytecode[pc++]; int targetReg = bytecode[pc++]; - // Read context (4 bytes = 1 int) - int context = ((bytecode[pc++] & 0xFFFF) << 16) | (bytecode[pc++] & 0xFFFF); + // Read context (1 int slot) + int context = bytecode[pc++]; RuntimeScalar search = (RuntimeScalar) registers[searchReg]; RuntimeScalar replace = (RuntimeScalar) registers[replaceReg];