Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1405,15 +1405,10 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c
int rs = bytecode[pc++];
RuntimeBase value = registers[rs];

// Only dereference if it's a RuntimeScalar with REFERENCE type
if (value instanceof RuntimeScalar) {
RuntimeScalar scalar = (RuntimeScalar) value;
if (scalar.type == RuntimeScalarType.REFERENCE) {
registers[rd] = scalar.scalarDeref();
} else {
// Non-reference scalar, just copy
registers[rd] = value;
}
if (value instanceof RuntimeScalar scalar) {
// Always go through scalarDeref() so non-reference types throw
// "Not a SCALAR reference" (matches JVM path behaviour)
registers[rd] = scalar.scalarDeref();
} else {
// RuntimeList or other types, pass through
registers[rd] = value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,22 @@ public static void compileAssignmentOperator(BytecodeCompiler bytecodeCompiler,
} else {
bytecodeCompiler.throwCompilerException("Assignment to unsupported array dereference");
}
} else if (leftOp.operator.equals("*")) {
// Glob assignment: *foo = *bar or 'foo'->** = ...
// Compile LHS glob, compile RHS, emit STORE_GLOB
int savedCtx = bytecodeCompiler.currentCallContext;
bytecodeCompiler.currentCallContext = RuntimeContextType.LIST;
node.left.accept(bytecodeCompiler);
bytecodeCompiler.currentCallContext = savedCtx;
int globReg = bytecodeCompiler.lastResultReg;

node.right.accept(bytecodeCompiler);
int valueReg2 = bytecodeCompiler.lastResultReg;

bytecodeCompiler.emit(Opcodes.STORE_GLOB);
bytecodeCompiler.emitReg(globReg);
bytecodeCompiler.emitReg(valueReg2);
bytecodeCompiler.lastResultReg = globReg;
} else {
// chop/chomp cannot be used as lvalues (matches JVM compiler message)
if (leftOp.operator.equals("chop") || leftOp.operator.equals("chomp")) {
Expand Down
47 changes: 10 additions & 37 deletions src/main/java/org/perlonjava/backend/bytecode/CompileOperator.java
Original file line number Diff line number Diff line change
Expand Up @@ -2693,51 +2693,24 @@ public static void visitOperator(BytecodeCompiler bytecodeCompiler, OperatorNode
bytecodeCompiler.throwCompilerException("atan2 requires two arguments");
}
} else if (op.equals("each")) {
// each %hash or each @array - needs the container itself, not flattened
// Format: OPCODE rd argsReg ctx

// each %hash or each @array - needs the container itself, not flattened.
// Compile operand in LIST context so %h stays a RuntimeHash (not scalar size).
// Mirrors JVM handleEach which uses RuntimeContextType.LIST.
if (node.operand == null) {
bytecodeCompiler.throwCompilerException("each requires an argument");
}

int containerReg;
// Check if operand is a hash/array variable dereference (% or @)
if (node.operand instanceof OperatorNode) {
OperatorNode opNode = (OperatorNode) node.operand;
String varOp = opNode.operator;
if ((varOp.equals("%") || varOp.equals("@")) && opNode.operand instanceof IdentifierNode) {
// Direct hash/array variable: %h or @a
// Load the variable container directly
IdentifierNode varName = (IdentifierNode) opNode.operand;
String fullVarName = varOp + varName.name;

if (bytecodeCompiler.hasVariable(fullVarName)) {
containerReg = bytecodeCompiler.getVariableRegister(fullVarName);
} else {
bytecodeCompiler.throwCompilerException("Variable " + fullVarName + " not found");
return; // unreachable
}
} else {
// Complex expression - compile normally
node.operand.accept(bytecodeCompiler);
containerReg = bytecodeCompiler.lastResultReg;
}
} else {
// Not an operator node - compile normally
node.operand.accept(bytecodeCompiler);
containerReg = bytecodeCompiler.lastResultReg;
}

// Wrap container in a list for the handler
int argsReg = bytecodeCompiler.allocateRegister();
bytecodeCompiler.emit(Opcodes.SCALAR_TO_LIST);
bytecodeCompiler.emitReg(argsReg);
bytecodeCompiler.emitReg(containerReg);
int savedContext = bytecodeCompiler.currentCallContext;
bytecodeCompiler.currentCallContext = RuntimeContextType.LIST;
node.operand.accept(bytecodeCompiler);
bytecodeCompiler.currentCallContext = savedContext;
int containerReg = bytecodeCompiler.lastResultReg;

// Pass container directly to EACH
int rd = bytecodeCompiler.allocateRegister();
bytecodeCompiler.emitWithToken(Opcodes.EACH, node.getIndex());
bytecodeCompiler.emitReg(rd);
bytecodeCompiler.emitReg(argsReg);
bytecodeCompiler.emitReg(containerReg);
bytecodeCompiler.emit(bytecodeCompiler.currentCallContext);
bytecodeCompiler.lastResultReg = rd;
} else if (op.equals("chmod") || op.equals("unlink") || op.equals("utime") ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ public static int execute(int opcode, int[] bytecode, int pc, RuntimeBase[] regi
int argsReg = bytecode[pc++];
int ctx = bytecode[pc++];

// EACH receives the container directly (RuntimeHash or RuntimeArray), not a RuntimeList
if (opcode == Opcodes.EACH) {
registers[rd] = registers[argsReg].each(ctx);
return pc;
}

RuntimeList args = (RuntimeList) registers[argsReg];
RuntimeBase[] argsArray = args.elements.toArray(new RuntimeBase[0]);

Expand All @@ -45,17 +51,7 @@ public static int execute(int opcode, int[] bytecode, int pc, RuntimeBase[] regi
}
case Opcodes.SYSTEM -> SystemOperator.system(args, false, ctx);
case Opcodes.CALLER -> RuntimeCode.caller(args, ctx);
case Opcodes.EACH -> {
// EACH needs the container directly, not wrapped in a list
// The argsReg should contain a list with one element (the container)
if (!args.elements.isEmpty()) {
RuntimeBase container = args.elements.get(0);
// Call each on the container
yield container.each(ctx);
} else {
throw new RuntimeException("each requires a hash or array argument");
}
}
// EACH is handled above before the RuntimeList cast
case Opcodes.PACK -> Pack.pack(args);
case Opcodes.UNPACK -> Unpack.unpack(ctx, argsArray);
case Opcodes.VEC -> Vec.vec(args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ public static int executeChop(int[] bytecode, int pc, RuntimeBase[] registers) {
int rd = bytecode[pc++];
int scalarReg = bytecode[pc++];

RuntimeScalar scalar = (RuntimeScalar) registers[scalarReg];
registers[rd] = StringOperators.chopScalar(scalar);
registers[rd] = registers[scalarReg].chop();
return pc;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ public static int executeDerefScalarStrict(

int rd = bytecode[pc++];
int rs = bytecode[pc++];
registers[rd] = ((RuntimeScalar) registers[rs]).scalarDeref();
registers[rd] = registers[rs].scalar().scalarDeref();
return pc;
}

Expand All @@ -373,7 +373,7 @@ public static int executeDerefScalarNonStrict(
int rs = bytecode[pc++];
int pkgIdx = bytecode[pc++];
String pkg = code.stringPool[pkgIdx];
registers[rd] = ((RuntimeScalar) registers[rs]).scalarDerefNonStrict(pkg);
registers[rd] = registers[rs].scalar().scalarDerefNonStrict(pkg);
return pc;
}

Expand Down