diff --git a/src/main/java/org/perlonjava/codegen/Dereference.java b/src/main/java/org/perlonjava/codegen/Dereference.java index 98370fb27..0ade03652 100644 --- a/src/main/java/org/perlonjava/codegen/Dereference.java +++ b/src/main/java/org/perlonjava/codegen/Dereference.java @@ -4,6 +4,7 @@ import org.objectweb.asm.Opcodes; import org.perlonjava.astnode.*; import org.perlonjava.astvisitor.EmitterVisitor; +import org.perlonjava.perlmodule.Strict; import org.perlonjava.runtime.PerlCompilerException; import org.perlonjava.runtime.RuntimeContextType; @@ -469,16 +470,32 @@ public static void handleArrowArrayDeref(EmitterVisitor emitterVisitor, BinaryOp Node elem = right.elements.getFirst(); elem.accept(emitterVisitor.with(RuntimeContextType.SCALAR)); - String methodName = switch (arrayOperation) { - case "get" -> "arrayDerefGet"; - case "delete" -> "arrayDerefDelete"; - case "exists" -> "arrayDerefExists"; - default -> - throw new PerlCompilerException(node.tokenIndex, "Not implemented: array operation: " + arrayOperation, emitterVisitor.ctx.errorUtil); - }; - - emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar", - methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;)Lorg/perlonjava/runtime/RuntimeScalar;", false); + // Check if strict refs is enabled at compile time + if (emitterVisitor.ctx.symbolTable.isStrictOptionEnabled(Strict.HINT_STRICT_REFS)) { + // Use strict version (throws error on symbolic references) + String methodName = switch (arrayOperation) { + case "get" -> "arrayDerefGet"; + case "delete" -> "arrayDerefDelete"; + case "exists" -> "arrayDerefExists"; + default -> + throw new PerlCompilerException(node.tokenIndex, "Not implemented: array operation: " + arrayOperation, emitterVisitor.ctx.errorUtil); + }; + emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar", + methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;)Lorg/perlonjava/runtime/RuntimeScalar;", false); + } else { + // Use non-strict version (allows symbolic references) + String methodName = switch (arrayOperation) { + case "get" -> "arrayDerefGetNonStrict"; + case "delete" -> "arrayDerefDeleteNonStrict"; + case "exists" -> "arrayDerefExistsNonStrict"; + default -> + throw new PerlCompilerException(node.tokenIndex, "Not implemented: array operation: " + arrayOperation, emitterVisitor.ctx.errorUtil); + }; + // Push the current package name for symbolic reference resolution + emitterVisitor.pushCurrentPackage(); + emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar", + methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;Ljava/lang/String;)Lorg/perlonjava/runtime/RuntimeScalar;", false); + } } else { // Multiple indices: use slice method (only for get operation) if (!arrayOperation.equals("get")) { @@ -489,8 +506,18 @@ public static void handleArrowArrayDeref(EmitterVisitor emitterVisitor, BinaryOp ListNode nodeRight = right.asListNode(); nodeRight.accept(emitterVisitor.with(RuntimeContextType.LIST)); - emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar", - "arrayDerefGetSlice", "(Lorg/perlonjava/runtime/RuntimeList;)Lorg/perlonjava/runtime/RuntimeList;", false); + // Check if strict refs is enabled at compile time + if (emitterVisitor.ctx.symbolTable.isStrictOptionEnabled(Strict.HINT_STRICT_REFS)) { + // Use strict version (throws error on symbolic references) + emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar", + "arrayDerefGetSlice", "(Lorg/perlonjava/runtime/RuntimeList;)Lorg/perlonjava/runtime/RuntimeList;", false); + } else { + // Use non-strict version (allows symbolic references) + // Push the current package name for symbolic reference resolution + emitterVisitor.pushCurrentPackage(); + emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar", + "arrayDerefGetSliceNonStrict", "(Lorg/perlonjava/runtime/RuntimeList;Ljava/lang/String;)Lorg/perlonjava/runtime/RuntimeList;", false); + } // Context conversion: list slice in scalar/void contexts if (emitterVisitor.ctx.contextType == RuntimeContextType.SCALAR) { @@ -526,15 +553,30 @@ public static void handleArrowHashDeref(EmitterVisitor emitterVisitor, BinaryOpe emitterVisitor.ctx.logDebug("visit -> (HashLiteralNode) autoquote " + node.right); nodeRight.accept(emitterVisitor.with(RuntimeContextType.SCALAR)); - String methodName = switch (hashOperation) { - case "get" -> "hashDerefGet"; - case "delete" -> "hashDerefDelete"; - case "exists" -> "hashDerefExists"; - default -> - throw new PerlCompilerException(node.tokenIndex, "Not implemented: hash operation: " + hashOperation, emitterVisitor.ctx.errorUtil); - }; - - emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar", methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;)Lorg/perlonjava/runtime/RuntimeScalar;", false); + // Check if strict refs is enabled at compile time + if (emitterVisitor.ctx.symbolTable.isStrictOptionEnabled(Strict.HINT_STRICT_REFS)) { + // Use strict version (throws error on symbolic references) + String methodName = switch (hashOperation) { + case "get" -> "hashDerefGet"; + case "delete" -> "hashDerefDelete"; + case "exists" -> "hashDerefExists"; + default -> + throw new PerlCompilerException(node.tokenIndex, "Not implemented: hash operation: " + hashOperation, emitterVisitor.ctx.errorUtil); + }; + emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar", methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;)Lorg/perlonjava/runtime/RuntimeScalar;", false); + } else { + // Use non-strict version (allows symbolic references) + String methodName = switch (hashOperation) { + case "get" -> "hashDerefGetNonStrict"; + case "delete" -> "hashDerefDeleteNonStrict"; + case "exists" -> "hashDerefExistsNonStrict"; + default -> + throw new PerlCompilerException(node.tokenIndex, "Not implemented: hash operation: " + hashOperation, emitterVisitor.ctx.errorUtil); + }; + // Push the current package name for symbolic reference resolution + emitterVisitor.pushCurrentPackage(); + emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar", methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;Ljava/lang/String;)Lorg/perlonjava/runtime/RuntimeScalar;", false); + } EmitOperator.handleVoidContext(emitterVisitor); } } diff --git a/src/main/java/org/perlonjava/runtime/RuntimeBaseProxy.java b/src/main/java/org/perlonjava/runtime/RuntimeBaseProxy.java index b77edae12..45cffce48 100644 --- a/src/main/java/org/perlonjava/runtime/RuntimeBaseProxy.java +++ b/src/main/java/org/perlonjava/runtime/RuntimeBaseProxy.java @@ -200,6 +200,70 @@ public RuntimeScalar hashDerefExists(RuntimeScalar index) { return ret; } + // Method to implement `$v->{key}`, when "no strict refs" is in effect + @Override + public RuntimeScalar hashDerefGetNonStrict(RuntimeScalar index, String packageName) { + vivify(); + RuntimeScalar ret = lvalue.hashDerefGetNonStrict(index, packageName); + this.type = lvalue.type; + this.value = lvalue.value; + return ret; + } + + // Method to implement `delete $v->{key}`, when "no strict refs" is in effect + @Override + public RuntimeScalar hashDerefDeleteNonStrict(RuntimeScalar index, String packageName) { + vivify(); + RuntimeScalar ret = lvalue.hashDerefDeleteNonStrict(index, packageName); + this.type = lvalue.type; + this.value = lvalue.value; + return ret; + } + + // Method to implement `exists $v->{key}`, when "no strict refs" is in effect + @Override + public RuntimeScalar hashDerefExistsNonStrict(RuntimeScalar index, String packageName) { + vivify(); + RuntimeScalar ret = lvalue.hashDerefExistsNonStrict(index, packageName); + this.type = lvalue.type; + this.value = lvalue.value; + return ret; + } + + // Method to implement `$v->[index]`, when "no strict refs" is in effect + @Override + public RuntimeScalar arrayDerefGetNonStrict(RuntimeScalar index, String packageName) { + // Don't vivify read-only scalars (like constants from constant subroutines) + if (lvalue == null && this instanceof RuntimeScalarReadOnly) { + return RuntimeScalarCache.scalarUndef; + } + vivify(); + RuntimeScalar ret = lvalue.arrayDerefGetNonStrict(index, packageName); + this.type = lvalue.type; + this.value = lvalue.value; + return ret; + } + + // Method to implement `delete $v->[index]`, when "no strict refs" is in effect + @Override + public RuntimeScalar arrayDerefDeleteNonStrict(RuntimeScalar index, String packageName) { + vivify(); + RuntimeScalar ret = lvalue.arrayDerefDeleteNonStrict(index, packageName); + this.type = lvalue.type; + this.value = lvalue.value; + return ret; + } + + // Method to implement `exists $v->[index]`, when "no strict refs" is in effect + @Override + public RuntimeScalar arrayDerefExistsNonStrict(RuntimeScalar index, String packageName) { + vivify(); + RuntimeScalar ret = lvalue.arrayDerefExistsNonStrict(index, packageName); + this.type = lvalue.type; + this.value = lvalue.value; + return ret; + } + /** * Performs a pre-increment operation on the underlying scalar. * diff --git a/src/main/java/org/perlonjava/runtime/RuntimeScalar.java b/src/main/java/org/perlonjava/runtime/RuntimeScalar.java index 3894c354d..58b7c1431 100644 --- a/src/main/java/org/perlonjava/runtime/RuntimeScalar.java +++ b/src/main/java/org/perlonjava/runtime/RuntimeScalar.java @@ -730,31 +730,61 @@ public RuntimeScalar hashDerefDelete(RuntimeScalar index) { return this.hashDeref().delete(index); } + // Method to implement `delete $v->{key}`, when "no strict refs" is in effect + public RuntimeScalar hashDerefDeleteNonStrict(RuntimeScalar index, String packageName) { + return this.hashDerefNonStrict(packageName).delete(index); + } + // Method to implement `exists $v->{key}` public RuntimeScalar hashDerefExists(RuntimeScalar index) { return this.hashDeref().exists(index); } + // Method to implement `exists $v->{key}`, when "no strict refs" is in effect + public RuntimeScalar hashDerefExistsNonStrict(RuntimeScalar index, String packageName) { + return this.hashDerefNonStrict(packageName).exists(index); + } + // Method to implement `$v->[10]` public RuntimeScalar arrayDerefGet(RuntimeScalar index) { return this.arrayDeref().get(index); } + // Method to implement `$v->[10]`, when "no strict refs" is in effect + public RuntimeScalar arrayDerefGetNonStrict(RuntimeScalar index, String packageName) { + return this.arrayDerefNonStrict(packageName).get(index); + } + // Method to implement `$v->[10, 20]` (slice) public RuntimeList arrayDerefGetSlice(RuntimeList indices) { return this.arrayDeref().getSlice(indices); } + // Method to implement `$v->[10, 20]` (slice), when "no strict refs" is in effect + public RuntimeList arrayDerefGetSliceNonStrict(RuntimeList indices, String packageName) { + return this.arrayDerefNonStrict(packageName).getSlice(indices); + } + // Method to implement `delete $v->[10]` public RuntimeScalar arrayDerefDelete(RuntimeScalar index) { return this.arrayDeref().delete(index); } + // Method to implement `delete $v->[10]`, when "no strict refs" is in effect + public RuntimeScalar arrayDerefDeleteNonStrict(RuntimeScalar index, String packageName) { + return this.arrayDerefNonStrict(packageName).delete(index); + } + // Method to implement `exists $v->[10]` public RuntimeScalar arrayDerefExists(RuntimeScalar index) { return this.arrayDeref().exists(index); } + // Method to implement `exists $v->[10]`, when "no strict refs" is in effect + public RuntimeScalar arrayDerefExistsNonStrict(RuntimeScalar index, String packageName) { + return this.arrayDerefNonStrict(packageName).exists(index); + } + // Method to implement `@$v` public RuntimeArray arrayDeref() { // Check if object is eligible for overloading