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
Original file line number Diff line number Diff line change
Expand Up @@ -396,8 +396,8 @@ boolean shouldBlockGlobalUnderStrictVars(String varName) {
return false;
}

// Allow regex capture variables ($1, $2, etc.)
if (ScalarUtils.isInteger(bareVarName)) {
// Allow regex capture variables ($1, $2, etc.) but not leading-zero variants ($01, $02)
if (ScalarUtils.isInteger(bareVarName) && !bareVarName.startsWith("0")) {
return false;
}

Expand All @@ -423,6 +423,39 @@ boolean shouldBlockGlobalUnderStrictVars(String varName) {
return false;
}

// Allow variables that already exist in the global registry
// (e.g., created by `use vars` at parse time)
// This mirrors the allowIfAlreadyExists logic in EmitVariable.java
String normalizedName = NameNormalizer.normalizeVariableName(bareVarName, getCurrentPackage());
boolean allowIfAlreadyExists = false;
if (sigil.equals("$") && GlobalVariable.existsGlobalVariable(normalizedName)) {
allowIfAlreadyExists = true;
}
if (sigil.equals("@") && GlobalVariable.existsGlobalArray(normalizedName)) {
allowIfAlreadyExists = true;
}
if (sigil.equals("%") && !normalizedName.endsWith("::") && GlobalVariable.existsGlobalHash(normalizedName)) {
allowIfAlreadyExists = true;
}

// Perl's strict 'vars' requires declaration for unqualified single-letter globals
// even if they were previously created under 'no strict'.
// This mirrors EmitVariable.java lines 349-359.
boolean isSpecialSortVar = sigil.equals("$")
&& (bareVarName.equals("a") || bareVarName.equals("b"));
if (sigil.equals("$")
&& bareVarName != null
&& bareVarName.length() == 1
&& Character.isLetter(bareVarName.charAt(0))
&& !bareVarName.contains("::")
&& !isSpecialSortVar) {
allowIfAlreadyExists = false;
}

if (allowIfAlreadyExists) {
return false;
}

// BLOCK: Unqualified variable under strict vars
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/perlonjava/backend/jvm/EmitVariable.java
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ static void handleVariableOperator(EmitterVisitor emitterVisitor, OperatorNode n

// Compute createIfNotExists flag - determines if variable can be auto-vivified
boolean createIfNotExists = name.contains("::") // Fully qualified: $Package::var
|| ScalarUtils.isInteger(name) // Regex capture: $1, $2, etc.
|| (ScalarUtils.isInteger(name) && !name.startsWith("0")) // Regex capture: $1, $2, etc.
|| isSpecialSortVar // Sort variables: $a, $b
|| isBuiltinSpecialLengthOneVar(sigil, name) // $%, $-, $[, $}, etc.
|| isBuiltinSpecialScalarVar(sigil, name) // ${^GLOBAL_PHASE}, $ARGV, $ENV, etc.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ private Set<StandardOpenOption> convertMode(String mode) {

Set<StandardOpenOption> options = MODE_OPTIONS.get(mode);
if (options == null) {
throw new PerlCompilerException("Unsupported file mode: " + mode);
throw new PerlCompilerException("Unknown open() mode '" + mode + "'");
}
return new HashSet<>(options);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,8 @@ public RuntimeScalar scalar() {
return scalarUndef; // Return undefined if empty
}
// XXX expand the last element
return elements.getLast().scalar();
RuntimeBase last = elements.getLast();
return (last == null) ? scalarUndef : last.scalar();
}

/**
Expand Down Expand Up @@ -464,8 +465,8 @@ public RuntimeArray setFromList(RuntimeList value) {
rhsIndex++;
}
} else if (elem instanceof RuntimeScalar runtimeScalar) {
RuntimeScalar assigned = (rhsIndex < rhsSize) ? rhsElements.get(rhsIndex++) : new RuntimeScalar();
runtimeScalar.set(assigned);
RuntimeScalar assigned = (rhsIndex < rhsSize) ? rhsElements.get(rhsIndex++) : null;
runtimeScalar.set(assigned != null ? assigned : new RuntimeScalar());
result.elements.add(runtimeScalar); // Add reference to the variable itself
} else if (elem instanceof RuntimeArray runtimeArray) {
List<RuntimeScalar> remaining = (rhsIndex < rhsSize)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,11 @@ public RuntimeScalar addToScalar(RuntimeScalar scalar) {

// Setters
public RuntimeScalar set(RuntimeScalar value) {
if (value == null) {
this.type = RuntimeScalarType.UNDEF;
this.value = null;
return this;
}
if (value.type == TIED_SCALAR) {
return set(value.tiedFetch());
}
Expand Down