Skip to content

Commit 4f0328a

Browse files
authored
Merge pull request #62 from fglock/fix/strict-refs-multideref
Fix 'no strict refs' not being honored in hash/array dereferences
2 parents 953e21a + 24f8aa1 commit 4f0328a

File tree

3 files changed

+157
-21
lines changed

3 files changed

+157
-21
lines changed

src/main/java/org/perlonjava/codegen/Dereference.java

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.objectweb.asm.Opcodes;
55
import org.perlonjava.astnode.*;
66
import org.perlonjava.astvisitor.EmitterVisitor;
7+
import org.perlonjava.perlmodule.Strict;
78
import org.perlonjava.runtime.PerlCompilerException;
89
import org.perlonjava.runtime.RuntimeContextType;
910

@@ -469,16 +470,32 @@ public static void handleArrowArrayDeref(EmitterVisitor emitterVisitor, BinaryOp
469470
Node elem = right.elements.getFirst();
470471
elem.accept(emitterVisitor.with(RuntimeContextType.SCALAR));
471472

472-
String methodName = switch (arrayOperation) {
473-
case "get" -> "arrayDerefGet";
474-
case "delete" -> "arrayDerefDelete";
475-
case "exists" -> "arrayDerefExists";
476-
default ->
477-
throw new PerlCompilerException(node.tokenIndex, "Not implemented: array operation: " + arrayOperation, emitterVisitor.ctx.errorUtil);
478-
};
479-
480-
emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar",
481-
methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;)Lorg/perlonjava/runtime/RuntimeScalar;", false);
473+
// Check if strict refs is enabled at compile time
474+
if (emitterVisitor.ctx.symbolTable.isStrictOptionEnabled(Strict.HINT_STRICT_REFS)) {
475+
// Use strict version (throws error on symbolic references)
476+
String methodName = switch (arrayOperation) {
477+
case "get" -> "arrayDerefGet";
478+
case "delete" -> "arrayDerefDelete";
479+
case "exists" -> "arrayDerefExists";
480+
default ->
481+
throw new PerlCompilerException(node.tokenIndex, "Not implemented: array operation: " + arrayOperation, emitterVisitor.ctx.errorUtil);
482+
};
483+
emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar",
484+
methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;)Lorg/perlonjava/runtime/RuntimeScalar;", false);
485+
} else {
486+
// Use non-strict version (allows symbolic references)
487+
String methodName = switch (arrayOperation) {
488+
case "get" -> "arrayDerefGetNonStrict";
489+
case "delete" -> "arrayDerefDeleteNonStrict";
490+
case "exists" -> "arrayDerefExistsNonStrict";
491+
default ->
492+
throw new PerlCompilerException(node.tokenIndex, "Not implemented: array operation: " + arrayOperation, emitterVisitor.ctx.errorUtil);
493+
};
494+
// Push the current package name for symbolic reference resolution
495+
emitterVisitor.pushCurrentPackage();
496+
emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar",
497+
methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;Ljava/lang/String;)Lorg/perlonjava/runtime/RuntimeScalar;", false);
498+
}
482499
} else {
483500
// Multiple indices: use slice method (only for get operation)
484501
if (!arrayOperation.equals("get")) {
@@ -489,8 +506,18 @@ public static void handleArrowArrayDeref(EmitterVisitor emitterVisitor, BinaryOp
489506
ListNode nodeRight = right.asListNode();
490507
nodeRight.accept(emitterVisitor.with(RuntimeContextType.LIST));
491508

492-
emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar",
493-
"arrayDerefGetSlice", "(Lorg/perlonjava/runtime/RuntimeList;)Lorg/perlonjava/runtime/RuntimeList;", false);
509+
// Check if strict refs is enabled at compile time
510+
if (emitterVisitor.ctx.symbolTable.isStrictOptionEnabled(Strict.HINT_STRICT_REFS)) {
511+
// Use strict version (throws error on symbolic references)
512+
emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar",
513+
"arrayDerefGetSlice", "(Lorg/perlonjava/runtime/RuntimeList;)Lorg/perlonjava/runtime/RuntimeList;", false);
514+
} else {
515+
// Use non-strict version (allows symbolic references)
516+
// Push the current package name for symbolic reference resolution
517+
emitterVisitor.pushCurrentPackage();
518+
emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar",
519+
"arrayDerefGetSliceNonStrict", "(Lorg/perlonjava/runtime/RuntimeList;Ljava/lang/String;)Lorg/perlonjava/runtime/RuntimeList;", false);
520+
}
494521

495522
// Context conversion: list slice in scalar/void contexts
496523
if (emitterVisitor.ctx.contextType == RuntimeContextType.SCALAR) {
@@ -526,15 +553,30 @@ public static void handleArrowHashDeref(EmitterVisitor emitterVisitor, BinaryOpe
526553
emitterVisitor.ctx.logDebug("visit -> (HashLiteralNode) autoquote " + node.right);
527554
nodeRight.accept(emitterVisitor.with(RuntimeContextType.SCALAR));
528555

529-
String methodName = switch (hashOperation) {
530-
case "get" -> "hashDerefGet";
531-
case "delete" -> "hashDerefDelete";
532-
case "exists" -> "hashDerefExists";
533-
default ->
534-
throw new PerlCompilerException(node.tokenIndex, "Not implemented: hash operation: " + hashOperation, emitterVisitor.ctx.errorUtil);
535-
};
536-
537-
emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar", methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;)Lorg/perlonjava/runtime/RuntimeScalar;", false);
556+
// Check if strict refs is enabled at compile time
557+
if (emitterVisitor.ctx.symbolTable.isStrictOptionEnabled(Strict.HINT_STRICT_REFS)) {
558+
// Use strict version (throws error on symbolic references)
559+
String methodName = switch (hashOperation) {
560+
case "get" -> "hashDerefGet";
561+
case "delete" -> "hashDerefDelete";
562+
case "exists" -> "hashDerefExists";
563+
default ->
564+
throw new PerlCompilerException(node.tokenIndex, "Not implemented: hash operation: " + hashOperation, emitterVisitor.ctx.errorUtil);
565+
};
566+
emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar", methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;)Lorg/perlonjava/runtime/RuntimeScalar;", false);
567+
} else {
568+
// Use non-strict version (allows symbolic references)
569+
String methodName = switch (hashOperation) {
570+
case "get" -> "hashDerefGetNonStrict";
571+
case "delete" -> "hashDerefDeleteNonStrict";
572+
case "exists" -> "hashDerefExistsNonStrict";
573+
default ->
574+
throw new PerlCompilerException(node.tokenIndex, "Not implemented: hash operation: " + hashOperation, emitterVisitor.ctx.errorUtil);
575+
};
576+
// Push the current package name for symbolic reference resolution
577+
emitterVisitor.pushCurrentPackage();
578+
emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar", methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;Ljava/lang/String;)Lorg/perlonjava/runtime/RuntimeScalar;", false);
579+
}
538580
EmitOperator.handleVoidContext(emitterVisitor);
539581
}
540582
}

src/main/java/org/perlonjava/runtime/RuntimeBaseProxy.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,70 @@ public RuntimeScalar hashDerefExists(RuntimeScalar index) {
200200
return ret;
201201
}
202202

203+
// Method to implement `$v->{key}`, when "no strict refs" is in effect
204+
@Override
205+
public RuntimeScalar hashDerefGetNonStrict(RuntimeScalar index, String packageName) {
206+
vivify();
207+
RuntimeScalar ret = lvalue.hashDerefGetNonStrict(index, packageName);
208+
this.type = lvalue.type;
209+
this.value = lvalue.value;
210+
return ret;
211+
}
212+
213+
// Method to implement `delete $v->{key}`, when "no strict refs" is in effect
214+
@Override
215+
public RuntimeScalar hashDerefDeleteNonStrict(RuntimeScalar index, String packageName) {
216+
vivify();
217+
RuntimeScalar ret = lvalue.hashDerefDeleteNonStrict(index, packageName);
218+
this.type = lvalue.type;
219+
this.value = lvalue.value;
220+
return ret;
221+
}
222+
223+
// Method to implement `exists $v->{key}`, when "no strict refs" is in effect
224+
@Override
225+
public RuntimeScalar hashDerefExistsNonStrict(RuntimeScalar index, String packageName) {
226+
vivify();
227+
RuntimeScalar ret = lvalue.hashDerefExistsNonStrict(index, packageName);
228+
this.type = lvalue.type;
229+
this.value = lvalue.value;
230+
return ret;
231+
}
232+
233+
// Method to implement `$v->[index]`, when "no strict refs" is in effect
234+
@Override
235+
public RuntimeScalar arrayDerefGetNonStrict(RuntimeScalar index, String packageName) {
236+
// Don't vivify read-only scalars (like constants from constant subroutines)
237+
if (lvalue == null && this instanceof RuntimeScalarReadOnly) {
238+
return RuntimeScalarCache.scalarUndef;
239+
}
240+
vivify();
241+
RuntimeScalar ret = lvalue.arrayDerefGetNonStrict(index, packageName);
242+
this.type = lvalue.type;
243+
this.value = lvalue.value;
244+
return ret;
245+
}
246+
247+
// Method to implement `delete $v->[index]`, when "no strict refs" is in effect
248+
@Override
249+
public RuntimeScalar arrayDerefDeleteNonStrict(RuntimeScalar index, String packageName) {
250+
vivify();
251+
RuntimeScalar ret = lvalue.arrayDerefDeleteNonStrict(index, packageName);
252+
this.type = lvalue.type;
253+
this.value = lvalue.value;
254+
return ret;
255+
}
256+
257+
// Method to implement `exists $v->[index]`, when "no strict refs" is in effect
258+
@Override
259+
public RuntimeScalar arrayDerefExistsNonStrict(RuntimeScalar index, String packageName) {
260+
vivify();
261+
RuntimeScalar ret = lvalue.arrayDerefExistsNonStrict(index, packageName);
262+
this.type = lvalue.type;
263+
this.value = lvalue.value;
264+
return ret;
265+
}
266+
203267
/**
204268
* Performs a pre-increment operation on the underlying scalar.
205269
*

src/main/java/org/perlonjava/runtime/RuntimeScalar.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,31 +730,61 @@ public RuntimeScalar hashDerefDelete(RuntimeScalar index) {
730730
return this.hashDeref().delete(index);
731731
}
732732

733+
// Method to implement `delete $v->{key}`, when "no strict refs" is in effect
734+
public RuntimeScalar hashDerefDeleteNonStrict(RuntimeScalar index, String packageName) {
735+
return this.hashDerefNonStrict(packageName).delete(index);
736+
}
737+
733738
// Method to implement `exists $v->{key}`
734739
public RuntimeScalar hashDerefExists(RuntimeScalar index) {
735740
return this.hashDeref().exists(index);
736741
}
737742

743+
// Method to implement `exists $v->{key}`, when "no strict refs" is in effect
744+
public RuntimeScalar hashDerefExistsNonStrict(RuntimeScalar index, String packageName) {
745+
return this.hashDerefNonStrict(packageName).exists(index);
746+
}
747+
738748
// Method to implement `$v->[10]`
739749
public RuntimeScalar arrayDerefGet(RuntimeScalar index) {
740750
return this.arrayDeref().get(index);
741751
}
742752

753+
// Method to implement `$v->[10]`, when "no strict refs" is in effect
754+
public RuntimeScalar arrayDerefGetNonStrict(RuntimeScalar index, String packageName) {
755+
return this.arrayDerefNonStrict(packageName).get(index);
756+
}
757+
743758
// Method to implement `$v->[10, 20]` (slice)
744759
public RuntimeList arrayDerefGetSlice(RuntimeList indices) {
745760
return this.arrayDeref().getSlice(indices);
746761
}
747762

763+
// Method to implement `$v->[10, 20]` (slice), when "no strict refs" is in effect
764+
public RuntimeList arrayDerefGetSliceNonStrict(RuntimeList indices, String packageName) {
765+
return this.arrayDerefNonStrict(packageName).getSlice(indices);
766+
}
767+
748768
// Method to implement `delete $v->[10]`
749769
public RuntimeScalar arrayDerefDelete(RuntimeScalar index) {
750770
return this.arrayDeref().delete(index);
751771
}
752772

773+
// Method to implement `delete $v->[10]`, when "no strict refs" is in effect
774+
public RuntimeScalar arrayDerefDeleteNonStrict(RuntimeScalar index, String packageName) {
775+
return this.arrayDerefNonStrict(packageName).delete(index);
776+
}
777+
753778
// Method to implement `exists $v->[10]`
754779
public RuntimeScalar arrayDerefExists(RuntimeScalar index) {
755780
return this.arrayDeref().exists(index);
756781
}
757782

783+
// Method to implement `exists $v->[10]`, when "no strict refs" is in effect
784+
public RuntimeScalar arrayDerefExistsNonStrict(RuntimeScalar index, String packageName) {
785+
return this.arrayDerefNonStrict(packageName).exists(index);
786+
}
787+
758788
// Method to implement `@$v`
759789
public RuntimeArray arrayDeref() {
760790
// Check if object is eligible for overloading

0 commit comments

Comments
 (0)