diff --git a/src/main/java/org/perlonjava/interpreter/BytecodeCompiler.java b/src/main/java/org/perlonjava/interpreter/BytecodeCompiler.java index ca8c268ee..6e8a00636 100644 --- a/src/main/java/org/perlonjava/interpreter/BytecodeCompiler.java +++ b/src/main/java/org/perlonjava/interpreter/BytecodeCompiler.java @@ -1449,25 +1449,42 @@ private void handleGeneralHashAccess(BinaryOperatorNode node) { keyReg = lastResultReg; } - // The base might be either: - // 1. A RuntimeHash (from %hash which was a hash variable) - // 2. A RuntimeScalar containing a hashref (from $hash{outer}) - // We need to handle both cases. Dereference if needed. + // Check if this is a glob slot access: *X{key} + // In this case, node.left is an OperatorNode with operator "*" + boolean isGlobSlotAccess = (node.left instanceof OperatorNode) && + ((OperatorNode) node.left).operator.equals("*"); + + if (isGlobSlotAccess) { + // For glob slot access, call hashDerefGetNonStrict directly + // This uses RuntimeGlob's override which accesses the slot without dereferencing + int rd = allocateRegister(); + emit(Opcodes.GLOB_SLOT_GET); + emitReg(rd); + emitReg(baseReg); + emitReg(keyReg); - // For now, let's assume it's a scalar with hashref and dereference it first - int hashReg = allocateRegister(); - emit(Opcodes.DEREF_HASH); - emitReg(hashReg); - emitReg(baseReg); + lastResultReg = rd; + } else { + // Normal hash access: dereference first, then get element + // The base might be either: + // 1. A RuntimeHash (from %hash which was a hash variable) + // 2. A RuntimeScalar containing a hashref (from $hash{outer}) + // We need to handle both cases. Dereference if needed. - // Now get the element - int rd = allocateRegister(); - emit(Opcodes.HASH_GET); - emitReg(rd); - emitReg(hashReg); - emitReg(keyReg); + int hashReg = allocateRegister(); + emit(Opcodes.DEREF_HASH); + emitReg(hashReg); + emitReg(baseReg); - lastResultReg = rd; + // Now get the element + int rd = allocateRegister(); + emit(Opcodes.HASH_GET); + emitReg(rd); + emitReg(hashReg); + emitReg(keyReg); + + lastResultReg = rd; + } } else { throwCompilerException("Multi-element hash access not yet implemented"); } diff --git a/src/main/java/org/perlonjava/interpreter/BytecodeInterpreter.java b/src/main/java/org/perlonjava/interpreter/BytecodeInterpreter.java index 3d1318885..ef8d55bb9 100644 --- a/src/main/java/org/perlonjava/interpreter/BytecodeInterpreter.java +++ b/src/main/java/org/perlonjava/interpreter/BytecodeInterpreter.java @@ -2330,6 +2330,13 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c break; } + case Opcodes.GLOB_SLOT_GET: { + // Glob slot access: rd = glob.hashDerefGetNonStrict(key, "main") + // Format: GLOB_SLOT_GET rd globReg keyReg + pc = SlowOpcodeHandler.executeGlobSlotGet(bytecode, pc, registers); + break; + } + // GENERATED_HANDLERS_END default: diff --git a/src/main/java/org/perlonjava/interpreter/InterpretedCode.java b/src/main/java/org/perlonjava/interpreter/InterpretedCode.java index 0cf0117b1..e526d1be6 100644 --- a/src/main/java/org/perlonjava/interpreter/InterpretedCode.java +++ b/src/main/java/org/perlonjava/interpreter/InterpretedCode.java @@ -708,6 +708,12 @@ public String disassemble() { int opStrIdx = bytecode[pc++]; sb.append("FILETEST_LASTHANDLE r").append(rd).append(" = ").append(stringPool[opStrIdx]).append(" _\n"); break; + case Opcodes.GLOB_SLOT_GET: + rd = bytecode[pc++]; + int globReg2 = bytecode[pc++]; + int keyReg = bytecode[pc++]; + sb.append("GLOB_SLOT_GET r").append(rd).append(" = r").append(globReg2).append("{r").append(keyReg).append("}\n"); + break; case Opcodes.PUSH_LOCAL_VARIABLE: rs = bytecode[pc++]; sb.append("PUSH_LOCAL_VARIABLE r").append(rs).append("\n"); diff --git a/src/main/java/org/perlonjava/interpreter/Opcodes.java b/src/main/java/org/perlonjava/interpreter/Opcodes.java index 4a4fa1611..3e3293730 100644 --- a/src/main/java/org/perlonjava/interpreter/Opcodes.java +++ b/src/main/java/org/perlonjava/interpreter/Opcodes.java @@ -855,10 +855,21 @@ public class Opcodes { /** Logical OR assignment: target ||= value */ public static final short LOGICAL_OR_ASSIGN = 229; + // ================================================================= + // MANUAL OPCODE ADDITIONS + // Add new custom opcodes HERE (before LASTOP), not in the generated section below. + // The GENERATED_OPCODES section is automatically regenerated and will overwrite any manual additions. + // After adding an opcode here, increment LASTOP to match the highest opcode number. + // ================================================================= + + /** Glob slot access: rd = glob.hashDerefGetNonStrict(key, "main") + * Used for *X{HASH} style access to glob slots */ + public static final short GLOB_SLOT_GET = 230; + // ================================================================= // BUILT-IN FUNCTION OPCODES - after LASTOP // Last manually-assigned opcode (for tool reference) - private static final short LASTOP = 229; + private static final short LASTOP = 230; // ================================================================= // Generated by dev/tools/generate_opcode_handlers.pl diff --git a/src/main/java/org/perlonjava/interpreter/SlowOpcodeHandler.java b/src/main/java/org/perlonjava/interpreter/SlowOpcodeHandler.java index adcdffdc8..2f3034287 100644 --- a/src/main/java/org/perlonjava/interpreter/SlowOpcodeHandler.java +++ b/src/main/java/org/perlonjava/interpreter/SlowOpcodeHandler.java @@ -1025,6 +1025,34 @@ public static int executeFiletestLastHandle( return pc; } + /** + * GLOB_SLOT_GET: rd = glob.hashDerefGetNonStrict(key, "main") + * Format: [GLOB_SLOT_GET] [rd] [globReg] [keyReg] + * Effect: Access glob slot (like *X{HASH}) using RuntimeGlob's override + * This ensures proper glob slot access without incorrectly dereferencing the glob as a hash + */ + public static int executeGlobSlotGet( + short[] bytecode, + int pc, + RuntimeBase[] registers) { + + int rd = bytecode[pc++]; + int globReg = bytecode[pc++]; + int keyReg = bytecode[pc++]; + + RuntimeBase globBase = registers[globReg]; + RuntimeScalar key = (RuntimeScalar) registers[keyReg]; + + // Convert to scalar if needed + RuntimeScalar glob = globBase.scalar(); + + // Call hashDerefGetNonStrict which for RuntimeGlob accesses the slot directly + // without dereferencing the glob as a hash + registers[rd] = glob.hashDerefGetNonStrict(key, "main"); + + return pc; + } + private SlowOpcodeHandler() { // Utility class - no instantiation }