Skip to content

Commit aaa331e

Browse files
authored
Merge pull request #65 from fglock/fix/tr-and-multideref
Fix tr.t and multideref.t: Unicode support + strict-refs
2 parents caf7ec8 + 70add3e commit aaa331e

File tree

4 files changed

+151
-23
lines changed

4 files changed

+151
-23
lines changed

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

Lines changed: 53 additions & 19 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")) {
@@ -526,15 +543,32 @@ public static void handleArrowHashDeref(EmitterVisitor emitterVisitor, BinaryOpe
526543
emitterVisitor.ctx.logDebug("visit -> (HashLiteralNode) autoquote " + node.right);
527544
nodeRight.accept(emitterVisitor.with(RuntimeContextType.SCALAR));
528545

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);
546+
// Check if strict refs is enabled at compile time
547+
if (emitterVisitor.ctx.symbolTable.isStrictOptionEnabled(Strict.HINT_STRICT_REFS)) {
548+
// Use strict version (throws error on symbolic references)
549+
String methodName = switch (hashOperation) {
550+
case "get" -> "hashDerefGet";
551+
case "delete" -> "hashDerefDelete";
552+
case "exists" -> "hashDerefExists";
553+
default ->
554+
throw new PerlCompilerException(node.tokenIndex, "Not implemented: hash operation: " + hashOperation, emitterVisitor.ctx.errorUtil);
555+
};
556+
emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar",
557+
methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;)Lorg/perlonjava/runtime/RuntimeScalar;", false);
558+
} else {
559+
// Use non-strict version (allows symbolic references)
560+
String methodName = switch (hashOperation) {
561+
case "get" -> "hashDerefGetNonStrict";
562+
case "delete" -> "hashDerefDeleteNonStrict";
563+
case "exists" -> "hashDerefExistsNonStrict";
564+
default ->
565+
throw new PerlCompilerException(node.tokenIndex, "Not implemented: hash operation: " + hashOperation, emitterVisitor.ctx.errorUtil);
566+
};
567+
// Push the current package name for symbolic reference resolution
568+
emitterVisitor.pushCurrentPackage();
569+
emitterVisitor.ctx.mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar",
570+
methodName, "(Lorg/perlonjava/runtime/RuntimeScalar;Ljava/lang/String;)Lorg/perlonjava/runtime/RuntimeScalar;", false);
571+
}
538572
EmitOperator.handleVoidContext(emitterVisitor);
539573
}
540574
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,9 @@ static void handleVariableOperator(EmitterVisitor emitterVisitor, OperatorNode n
315315
emitterVisitor.pushCurrentPackage();
316316
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeScalar", "hashDerefNonStrict", "(Ljava/lang/String;)Lorg/perlonjava/runtime/RuntimeHash;", false);
317317
}
318+
if (emitterVisitor.ctx.contextType == RuntimeContextType.SCALAR) {
319+
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "org/perlonjava/runtime/RuntimeHash", "scalar", "()Lorg/perlonjava/runtime/RuntimeScalar;", false);
320+
}
318321
return;
319322
case "$":
320323
// `$$a`

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

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

203+
// Non-strict versions (allow symbolic references)
204+
// Note: We don't call vivify() here because for symbolic references (STRING/BYTE_STRING),
205+
// we're not modifying the scalar - we're just using its string value to look up a global variable.
206+
// For RuntimeScalarReadOnly (immutable scalars like string literals), lvalue is null and we can't vivify.
207+
// Instead, we call the method directly on 'this' which will handle symbolic references correctly.
208+
public RuntimeScalar hashDerefGetNonStrict(RuntimeScalar index, String currentPackage) {
209+
if (lvalue == null) {
210+
// For read-only scalars, call the method on 'this' directly
211+
// This will use the RuntimeScalar implementation which handles symbolic references
212+
return super.hashDerefGetNonStrict(index, currentPackage);
213+
}
214+
return lvalue.hashDerefGetNonStrict(index, currentPackage);
215+
}
216+
217+
public RuntimeScalar hashDerefDeleteNonStrict(RuntimeScalar index, String currentPackage) {
218+
if (lvalue == null) {
219+
return super.hashDerefDeleteNonStrict(index, currentPackage);
220+
}
221+
return lvalue.hashDerefDeleteNonStrict(index, currentPackage);
222+
}
223+
224+
public RuntimeScalar hashDerefExistsNonStrict(RuntimeScalar index, String currentPackage) {
225+
if (lvalue == null) {
226+
return super.hashDerefExistsNonStrict(index, currentPackage);
227+
}
228+
return lvalue.hashDerefExistsNonStrict(index, currentPackage);
229+
}
230+
231+
public RuntimeScalar arrayDerefGetNonStrict(RuntimeScalar index, String currentPackage) {
232+
if (lvalue == null) {
233+
return super.arrayDerefGetNonStrict(index, currentPackage);
234+
}
235+
return lvalue.arrayDerefGetNonStrict(index, currentPackage);
236+
}
237+
238+
public RuntimeScalar arrayDerefDeleteNonStrict(RuntimeScalar index, String currentPackage) {
239+
if (lvalue == null) {
240+
return super.arrayDerefDeleteNonStrict(index, currentPackage);
241+
}
242+
return lvalue.arrayDerefDeleteNonStrict(index, currentPackage);
243+
}
244+
245+
public RuntimeScalar arrayDerefExistsNonStrict(RuntimeScalar index, String currentPackage) {
246+
if (lvalue == null) {
247+
return super.arrayDerefExistsNonStrict(index, currentPackage);
248+
}
249+
return lvalue.arrayDerefExistsNonStrict(index, currentPackage);
250+
}
251+
203252
/**
204253
* Performs a pre-increment operation on the underlying scalar.
205254
*

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

Lines changed: 46 additions & 4 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
@@ -942,12 +972,18 @@ public RuntimeHash hashDerefNonStrict(String packageName) {
942972
yield AutovivificationHash.createAutovivifiedHash(this);
943973
}
944974
case HASHREFERENCE -> (RuntimeHash) value;
945-
case TIED_SCALAR -> tiedFetch().hashDerefNonStrict(packageName);
946-
default -> {
975+
case GLOB -> {
976+
// When dereferencing a typeglob as a hash, return the hash slot
977+
RuntimeGlob glob = (RuntimeGlob) value;
978+
yield GlobalVariable.getGlobalHash(glob.globName);
979+
}
980+
case STRING, BYTE_STRING -> {
947981
// Symbolic reference: treat the scalar's string value as a variable name
948982
String varName = NameNormalizer.normalizeVariableName(this.toString(), packageName);
949983
yield GlobalVariable.getGlobalHash(varName);
950984
}
985+
case TIED_SCALAR -> tiedFetch().hashDerefNonStrict(packageName);
986+
default -> throw new PerlCompilerException("Not a HASH reference");
951987
};
952988
}
953989

@@ -978,12 +1014,18 @@ public RuntimeArray arrayDerefNonStrict(String packageName) {
9781014
yield AutovivificationArray.createAutovivifiedArray(this);
9791015
}
9801016
case ARRAYREFERENCE -> (RuntimeArray) value;
981-
case TIED_SCALAR -> tiedFetch().arrayDerefNonStrict(packageName);
982-
default -> {
1017+
case GLOB -> {
1018+
// When dereferencing a typeglob as an array, return the array slot
1019+
RuntimeGlob glob = (RuntimeGlob) value;
1020+
yield GlobalVariable.getGlobalArray(glob.globName);
1021+
}
1022+
case STRING, BYTE_STRING -> {
9831023
// Symbolic reference: treat the scalar's string value as a variable name
9841024
String varName = NameNormalizer.normalizeVariableName(this.toString(), packageName);
9851025
yield GlobalVariable.getGlobalArray(varName);
9861026
}
1027+
case TIED_SCALAR -> tiedFetch().arrayDerefNonStrict(packageName);
1028+
default -> throw new PerlCompilerException("Not an ARRAY reference");
9871029
};
9881030
}
9891031

0 commit comments

Comments
 (0)