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
84 changes: 63 additions & 21 deletions src/main/java/org/perlonjava/codegen/Dereference.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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")) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}
}
64 changes: 64 additions & 0 deletions src/main/java/org/perlonjava/runtime/RuntimeBaseProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/org/perlonjava/runtime/RuntimeScalar.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down