Skip to content
Merged
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
33 changes: 26 additions & 7 deletions src/main/java/org/perlonjava/backend/jvm/EmitSubroutine.java
Original file line number Diff line number Diff line change
Expand Up @@ -364,18 +364,27 @@ static void handleApplyOperator(EmitterVisitor emitterVisitor, BinaryOperatorNod
boolean isScalarVariable = false;
boolean isLexicalSub = false;
OperatorNode scalarOpNode = null;
boolean isBlockDeref = false; // &{expr} syntax - always need to deref for symbolic refs

if (node.left instanceof OperatorNode operatorNode && operatorNode.operator.equals("$")) {
// This is &$var() or $var->() syntax
isScalarVariable = true;
scalarOpNode = operatorNode;
} else if (node.left instanceof BlockNode blockNode &&
blockNode.elements.size() == 1 &&
blockNode.elements.get(0) instanceof OperatorNode opNode &&
opNode.operator.equals("$")) {
// This is &{$var} syntax
isScalarVariable = true;
scalarOpNode = opNode;
} else if (node.left instanceof BlockNode blockNode) {
// This is &{expr} syntax where expr can be:
// - &{$var} - simple variable
// - &{$hash{key}} - hash element
// - &{$array[idx]} - array element
// - &{some_expression} - any expression
// All of these may return a string that needs to be resolved as a symbolic reference
isBlockDeref = true;
if (blockNode.elements.size() == 1 &&
blockNode.elements.get(0) instanceof OperatorNode opNode &&
opNode.operator.equals("$")) {
// Specific case: &{$var}
isScalarVariable = true;
scalarOpNode = opNode;
}
}

if (isScalarVariable && scalarOpNode != null) {
Expand All @@ -396,6 +405,16 @@ static void handleApplyOperator(EmitterVisitor emitterVisitor, BinaryOperatorNod
"(Ljava/lang/String;)Lorg/perlonjava/runtime/runtimetypes/RuntimeScalar;",
false);
}
} else if (isBlockDeref && !emitterVisitor.ctx.symbolTable.isStrictOptionEnabled(HINT_STRICT_REFS)) {
// For &{expr} where expr is not a simple variable (e.g., &{$hash{key}})
// We need to call codeDerefNonStrict to resolve symbolic references
// using the current package
emitterVisitor.pushCurrentPackage();
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"org/perlonjava/runtime/runtimetypes/RuntimeScalar",
"codeDerefNonStrict",
"(Ljava/lang/String;)Lorg/perlonjava/runtime/runtimetypes/RuntimeScalar;",
false);
}

int codeRefSlot = emitterVisitor.ctx.javaClassInfo.acquireSpillSlot();
Expand Down
51 changes: 6 additions & 45 deletions src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeGlob.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,52 +156,13 @@ public RuntimeScalar set(RuntimeScalar value) {
}
return value;
case REFERENCE:
// `*foo = \$bar` aliases the SCALAR slot to $bar, regardless of what $bar contains.
// This must replace the scalar container (alias) rather than storing into
// the existing scalar, otherwise tied scalars would invoke STORE.
// Note: \@array and \%hash come in as ARRAYREFERENCE/HASHREFERENCE types,
// not REFERENCE, so they are handled above in their respective cases.
if (value.value instanceof RuntimeScalar) {
RuntimeScalar deref = value.scalarDeref();
// `*foo = \&bar` creates a constant subroutine returning the code reference
if (deref.type == RuntimeScalarType.CODE) {
RuntimeCode constSub = new RuntimeCode("", null);
constSub.constantValue = deref.getList();
GlobalVariable.getGlobalCodeRef(this.globName).set(new RuntimeScalar(constSub));
InheritanceResolver.invalidateCache();
} else if (deref.type == RuntimeScalarType.ARRAYREFERENCE && deref.value instanceof RuntimeArray arr) {
// `*foo = \@bar` assigns to the ARRAY slot.
// Also update all glob aliases
for (String aliasedName : GlobalVariable.getGlobAliasGroup(this.globName)) {
GlobalVariable.globalArrays.put(aliasedName, arr);
}
} else if (deref.type == RuntimeScalarType.HASHREFERENCE && deref.value instanceof RuntimeHash hash) {
// `*foo = \%bar` assigns to the HASH slot.
// Also update all glob aliases
for (String aliasedName : GlobalVariable.getGlobAliasGroup(this.globName)) {
GlobalVariable.globalHashes.put(aliasedName, hash);
}
} else if (value.type == RuntimeScalarType.REFERENCE && deref.type == RuntimeScalarType.ARRAYREFERENCE) {
// `*foo = \$array_ref` creates a constant subroutine returning the array reference
RuntimeCode constSub = new RuntimeCode("", null);
constSub.constantValue = deref.getList();
GlobalVariable.getGlobalCodeRef(this.globName).set(new RuntimeScalar(constSub));
InheritanceResolver.invalidateCache();
} else if (value.type == RuntimeScalarType.REFERENCE && deref.type == RuntimeScalarType.HASHREFERENCE) {
// `*foo = \$hash_ref` creates a constant subroutine returning the hash reference
RuntimeCode constSub = new RuntimeCode("", null);
constSub.constantValue = deref.getList();
GlobalVariable.getGlobalCodeRef(this.globName).set(new RuntimeScalar(constSub));
InheritanceResolver.invalidateCache();
} else if (deref.type == RuntimeScalarType.STRING || deref.type == RuntimeScalarType.BYTE_STRING) {
// `*foo = \$bar` creates a constant subroutine returning the scalar value
RuntimeCode constSub = new RuntimeCode("", null);
constSub.constantValue = deref.getList();
GlobalVariable.getGlobalCodeRef(this.globName).set(new RuntimeScalar(constSub));
// Also set the SCALAR slot for direct access
GlobalVariable.globalVariables.put(this.globName, deref);
InheritanceResolver.invalidateCache();
} else {
// `*foo = \$bar` (or `*foo = \1`) aliases the SCALAR slot.
// This must replace the scalar container (alias) rather than storing into
// the existing scalar, otherwise tied scalars would invoke STORE.
GlobalVariable.aliasGlobalVariable(this.globName, (RuntimeScalar) value.value);
}
GlobalVariable.aliasGlobalVariable(this.globName, (RuntimeScalar) value.value);
}
return value;
case UNDEF:
Expand Down
Loading