diff --git a/src/main/java/org/perlonjava/app/scriptengine/PerlLanguageProvider.java b/src/main/java/org/perlonjava/app/scriptengine/PerlLanguageProvider.java index ab9523fce..a967a8c93 100644 --- a/src/main/java/org/perlonjava/app/scriptengine/PerlLanguageProvider.java +++ b/src/main/java/org/perlonjava/app/scriptengine/PerlLanguageProvider.java @@ -314,7 +314,7 @@ private static RuntimeList executeCode(RuntimeCode runtimeCode, EmitterContext c * @throws Exception if compilation fails */ private static RuntimeCode compileToExecutable(Node ast, EmitterContext ctx) throws Exception { - if (ctx.compilerOptions.useInterpreter) { + if (ctx.compilerOptions.useInterpreter || RuntimeCode.FORCE_INTERPRETER) { // Interpreter path - returns InterpretedCode (extends RuntimeCode) ctx.logDebug("Compiling to bytecode interpreter"); BytecodeCompiler compiler = new BytecodeCompiler( diff --git a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java index e7792cbf6..e678164dc 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java +++ b/src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java @@ -1475,7 +1475,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c case Opcodes.CHMOD, Opcodes.UNLINK, Opcodes.UTIME, Opcodes.RENAME, Opcodes.LINK, Opcodes.READLINK, Opcodes.UMASK, Opcodes.GETC, Opcodes.FILENO, Opcodes.QX, Opcodes.SYSTEM, Opcodes.CALLER, Opcodes.EACH, Opcodes.PACK, Opcodes.UNPACK, - Opcodes.VEC, Opcodes.LOCALTIME, Opcodes.GMTIME, Opcodes.RESET, Opcodes.CRYPT, + Opcodes.VEC, Opcodes.LOCALTIME, Opcodes.GMTIME, Opcodes.RESET, Opcodes.TIMES, Opcodes.CRYPT, Opcodes.CLOSE, Opcodes.BINMODE, Opcodes.SEEK, Opcodes.EOF_OP, Opcodes.SYSREAD, Opcodes.SYSWRITE, Opcodes.SYSOPEN, Opcodes.SOCKET, Opcodes.BIND, Opcodes.CONNECT, Opcodes.LISTEN, Opcodes.WRITE, Opcodes.FORMLINE, Opcodes.PRINTF, Opcodes.ACCEPT, diff --git a/src/main/java/org/perlonjava/backend/bytecode/CompileBinaryOperator.java b/src/main/java/org/perlonjava/backend/bytecode/CompileBinaryOperator.java index 0149c0084..951f2ee41 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/CompileBinaryOperator.java +++ b/src/main/java/org/perlonjava/backend/bytecode/CompileBinaryOperator.java @@ -19,8 +19,11 @@ static void visitBinaryOperator(BytecodeCompiler bytecodeCompiler, BinaryOperato node.left.accept(bytecodeCompiler); int filehandleReg = bytecodeCompiler.lastResultReg; - // Compile the content (right operand) + // Compile the content (right operand) in LIST context + int savedContext = bytecodeCompiler.currentCallContext; + bytecodeCompiler.currentCallContext = RuntimeContextType.LIST; node.right.accept(bytecodeCompiler); + bytecodeCompiler.currentCallContext = savedContext; int contentReg = bytecodeCompiler.lastResultReg; // Emit PRINT or SAY with both registers @@ -704,13 +707,18 @@ private static void compileBinaryAsListOp(BytecodeCompiler bytecodeCompiler, Bin java.util.List argRegs = new java.util.ArrayList<>(); argRegs.add(fhReg); + int savedContext = bytecodeCompiler.currentCallContext; if (node.right instanceof ListNode argsList) { for (Node arg : argsList.elements) { + bytecodeCompiler.currentCallContext = RuntimeContextType.LIST; arg.accept(bytecodeCompiler); + bytecodeCompiler.currentCallContext = savedContext; argRegs.add(bytecodeCompiler.lastResultReg); } } else { + bytecodeCompiler.currentCallContext = RuntimeContextType.LIST; node.right.accept(bytecodeCompiler); + bytecodeCompiler.currentCallContext = savedContext; argRegs.add(bytecodeCompiler.lastResultReg); } diff --git a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java index bc88c1798..25ad9a685 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java +++ b/src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java @@ -2608,7 +2608,7 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode op.equals("rename") || op.equals("link") || op.equals("readlink") || 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("reset") || + op.equals("localtime") || op.equals("gmtime") || op.equals("caller") || op.equals("reset") || op.equals("times") || op.equals("fileno") || op.equals("getc") || op.equals("qx") || op.equals("close") || op.equals("binmode") || op.equals("seek") || @@ -2670,6 +2670,7 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode case "localtime" -> Opcodes.LOCALTIME; case "gmtime" -> Opcodes.GMTIME; case "reset" -> Opcodes.RESET; + case "times" -> Opcodes.TIMES; case "crypt" -> Opcodes.CRYPT; case "close" -> Opcodes.CLOSE; case "binmode" -> Opcodes.BINMODE; diff --git a/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java b/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java index 9a356763d..0a82b0c9b 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java +++ b/src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java @@ -1554,6 +1554,7 @@ public String disassemble() { case Opcodes.LOCALTIME: case Opcodes.GMTIME: case Opcodes.RESET: + case Opcodes.TIMES: case Opcodes.CHMOD: case Opcodes.UNLINK: case Opcodes.UTIME: @@ -1577,6 +1578,7 @@ public String disassemble() { case Opcodes.LOCALTIME -> "localtime"; case Opcodes.GMTIME -> "gmtime"; case Opcodes.RESET -> "reset"; + case Opcodes.TIMES -> "times"; case Opcodes.CHMOD -> "chmod"; case Opcodes.UNLINK -> "unlink"; case Opcodes.UTIME -> "utime"; diff --git a/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java b/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java index 5a555022a..f696235f7 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java +++ b/src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java @@ -51,6 +51,7 @@ public static int execute(int opcode, int[] bytecode, int pc, RuntimeBase[] regi case Opcodes.LOCALTIME -> Time.localtime(args, ctx); case Opcodes.GMTIME -> Time.gmtime(args, ctx); case Opcodes.RESET -> Operator.reset(args, ctx); + case Opcodes.TIMES -> Time.times(ctx); case Opcodes.CRYPT -> Crypt.crypt(args); // I/O operators case Opcodes.CLOSE -> IOOperator.close(ctx, argsArray); diff --git a/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java b/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java index b5282ce18..40e468685 100644 --- a/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java +++ b/src/main/java/org/perlonjava/backend/bytecode/Opcodes.java @@ -1811,6 +1811,10 @@ public class Opcodes { * Format: LOAD_BYTE_STRING rd strIndex */ public static final short LOAD_BYTE_STRING = 372; + /** + * times: Format: TIMES rd argsReg ctx + */ + public static final short TIMES = 373; private Opcodes() { } // Utility class - no instantiation diff --git a/src/main/java/org/perlonjava/backend/jvm/EmitterMethodCreator.java b/src/main/java/org/perlonjava/backend/jvm/EmitterMethodCreator.java index 373895c85..b179d465c 100644 --- a/src/main/java/org/perlonjava/backend/jvm/EmitterMethodCreator.java +++ b/src/main/java/org/perlonjava/backend/jvm/EmitterMethodCreator.java @@ -1497,6 +1497,9 @@ public static RuntimeCode createRuntimeCode( // For anonymous subs this is set by SubroutineNode constructor, but for named subs the block // is passed directly here without going through SubroutineNode. ast.setAnnotation("blockIsSubroutine", true); + if (ctx.compilerOptions.useInterpreter || RuntimeCode.FORCE_INTERPRETER) { + return compileToInterpreter(ast, ctx, useTryCatch); + } try { // Try compiler path Class generatedClass = createClassWithMethod(ctx, ast, useTryCatch); diff --git a/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeCode.java b/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeCode.java index ce06a675f..ac7d877fa 100644 --- a/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeCode.java +++ b/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeCode.java @@ -118,6 +118,8 @@ protected boolean removeEldestEntry(Map.Entry, MethodHandle> eldest) { System.getenv("JPERL_DISASSEMBLE") != null; public static boolean USE_INTERPRETER = System.getenv("JPERL_INTERPRETER") != null; + public static final boolean FORCE_INTERPRETER = + System.getenv("JPERL_INTERPRETER") != null; public static MethodType methodType = MethodType.methodType(RuntimeList.class, RuntimeArray.class, int.class); // Temporary storage for anonymous subroutines and eval string compiler context public static HashMap> anonSubs = new HashMap<>(); // temp storage for makeCodeObject() @@ -186,6 +188,7 @@ public static void setUseInterpreter(boolean value) { USE_INTERPRETER = value; } + /** * Get the current eval runtime context for accessing variable runtime values during parsing. * This is called by SpecialBlockParser when setting up BEGIN blocks.