diff --git a/src/main/java/org/perlonjava/backend/jvm/EmitSubroutine.java b/src/main/java/org/perlonjava/backend/jvm/EmitSubroutine.java index 8e81ab867..5e7b6537d 100644 --- a/src/main/java/org/perlonjava/backend/jvm/EmitSubroutine.java +++ b/src/main/java/org/perlonjava/backend/jvm/EmitSubroutine.java @@ -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) { @@ -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(); diff --git a/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeGlob.java b/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeGlob.java index c51b5ec89..8b58960ce 100644 --- a/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeGlob.java +++ b/src/main/java/org/perlonjava/runtime/runtimetypes/RuntimeGlob.java @@ -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: