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 @@ -1899,8 +1899,8 @@ void handleArraySlice(BinaryOperatorNode node, OperatorNode leftOp) {
// The ArrayLiteralNode might contain a range operator (..) or multiple elements
List<Integer> indexRegs = new ArrayList<>();
for (Node indexExpr : indicesNode.elements) {
// Each element could be a simple value or a range expression
indexExpr.accept(this);
// Each element could be a simple value, an expanding array, or a range expression.
compileNode(indexExpr, -1, RuntimeContextType.LIST);
indexRegs.add(lastResultReg);
}

Expand Down Expand Up @@ -2113,8 +2113,8 @@ void handleHashSlice(BinaryOperatorNode node, OperatorNode leftOp) {
emit(keyIdx);
keyRegs.add(keyReg);
} else {
// Expression key - use default context to allow arrays to expand
keyElement.accept(this);
// Expression key - list context lets @keys and list-returning subs expand.
compileNode(keyElement, -1, RuntimeContextType.LIST);
keyRegs.add(lastResultReg);
}
}
Expand Down Expand Up @@ -2221,8 +2221,8 @@ void handleHashKeyValueSlice(BinaryOperatorNode node, OperatorNode leftOp) {
emit(keyIdx);
keyRegs.add(keyReg);
} else {
// Expression key - use default context to allow arrays to expand
keyElement.accept(this);
// Expression key - list context lets @keys and list-returning subs expand.
compileNode(keyElement, -1, RuntimeContextType.LIST);
keyRegs.add(lastResultReg);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2609,6 +2609,7 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c
MortalList.scopeExitCleanupArray(ra);
needsFlush = true;
}
MyVarCleanupStack.unregister(reg);
registers[i] = null;
}
if (needsFlush) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1433,7 +1433,7 @@ public static void compileAssignmentOperator(BytecodeCompiler bytecodeCompiler,
ArrayLiteralNode indicesNode = (ArrayLiteralNode) leftBin.right;
List<Integer> indexRegs = new ArrayList<>();
for (Node indexNode : indicesNode.elements) {
bytecodeCompiler.compileNode(indexNode, -1, rhsContext);
bytecodeCompiler.compileNode(indexNode, -1, RuntimeContextType.LIST);
indexRegs.add(bytecodeCompiler.lastResultReg);
}

Expand All @@ -1446,13 +1446,20 @@ public static void compileAssignmentOperator(BytecodeCompiler bytecodeCompiler,
bytecodeCompiler.emitReg(indexReg);
}

// Emit direct opcode ARRAY_SLICE_SET (use valueReg from line 729)
int sliceValuesReg = bytecodeCompiler.allocateRegister();
bytecodeCompiler.emit(Opcodes.NEW_ARRAY);
bytecodeCompiler.emitReg(sliceValuesReg);
bytecodeCompiler.emit(Opcodes.ARRAY_SET_FROM_LIST);
bytecodeCompiler.emitReg(sliceValuesReg);
bytecodeCompiler.emitReg(valueReg);

// Emit direct opcode ARRAY_SLICE_SET using the materialized RHS values.
bytecodeCompiler.emit(Opcodes.ARRAY_SLICE_SET);
bytecodeCompiler.emitReg(arrayReg);
bytecodeCompiler.emitReg(indicesReg);
bytecodeCompiler.emitReg(valueReg);
bytecodeCompiler.emitReg(sliceValuesReg);

bytecodeCompiler.lastResultReg = arrayReg;
bytecodeCompiler.lastResultReg = sliceValuesReg;

return;
}
Expand Down Expand Up @@ -1628,8 +1635,8 @@ public static void compileAssignmentOperator(BytecodeCompiler bytecodeCompiler,
bytecodeCompiler.emit(keyIdx);
keyRegs.add(keyReg);
} else {
// Expression key - use default context to allow arrays to expand
keyElement.accept(bytecodeCompiler);
// Expression key - list context lets @keys and list-returning subs expand.
bytecodeCompiler.compileNode(keyElement, -1, RuntimeContextType.LIST);
keyRegs.add(bytecodeCompiler.lastResultReg);
}
}
Expand All @@ -1643,13 +1650,20 @@ public static void compileAssignmentOperator(BytecodeCompiler bytecodeCompiler,
bytecodeCompiler.emitReg(keyReg);
}

// Emit direct opcode HASH_SLICE_SET (use valueReg from line 729)
int sliceValuesReg = bytecodeCompiler.allocateRegister();
bytecodeCompiler.emit(Opcodes.NEW_ARRAY);
bytecodeCompiler.emitReg(sliceValuesReg);
bytecodeCompiler.emit(Opcodes.ARRAY_SET_FROM_LIST);
bytecodeCompiler.emitReg(sliceValuesReg);
bytecodeCompiler.emitReg(valueReg);

// Emit direct opcode HASH_SLICE_SET using the materialized RHS values.
bytecodeCompiler.emit(Opcodes.HASH_SLICE_SET);
bytecodeCompiler.emitReg(hashReg);
bytecodeCompiler.emitReg(keysListReg);
bytecodeCompiler.emitReg(valueReg);
bytecodeCompiler.emitReg(sliceValuesReg);

bytecodeCompiler.lastResultReg = valueReg;
bytecodeCompiler.lastResultReg = sliceValuesReg;

return;
} else if (hashOp.operator.equals("$")) {
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/org/perlonjava/backend/bytecode/InterpretedCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,18 @@ public RuntimeList apply(RuntimeArray args, int callContext) {
if (warningBitsString != null) {
WarningBitsRegistry.pushCurrent(warningBitsString);
}
int cleanupMark = MyVarCleanupStack.pushMark();
try {
return RuntimeCode.coerceScalarCallResult(
BytecodeInterpreter.execute(this, args, effectiveContext), effectiveContext, callContext);
} catch (RuntimeException e) {
if (!(e instanceof PerlExitException)) {
MyVarCleanupStack.unwindTo(cleanupMark);
MortalList.flush();
}
throw e;
} finally {
MyVarCleanupStack.popMark(cleanupMark);
if (warningBitsString != null) {
WarningBitsRegistry.popCurrent();
}
Expand All @@ -365,11 +373,19 @@ public RuntimeList apply(String subroutineName, RuntimeArray args, int callConte
if (warningBitsString != null) {
WarningBitsRegistry.pushCurrent(warningBitsString);
}
int cleanupMark = MyVarCleanupStack.pushMark();
try {
return RuntimeCode.coerceScalarCallResult(
BytecodeInterpreter.execute(this, args, effectiveContext, subroutineName),
effectiveContext, callContext);
} catch (RuntimeException e) {
if (!(e instanceof PerlExitException)) {
MyVarCleanupStack.unwindTo(cleanupMark);
MortalList.flush();
}
throw e;
} finally {
MyVarCleanupStack.popMark(cleanupMark);
if (warningBitsString != null) {
WarningBitsRegistry.popCurrent();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,11 @@ private static byte[] getBytecodeInternal(EmitterContext ctx, Node ast, boolean
"org/perlonjava/runtime/runtimetypes/MortalList",
"evalExceptionScopeCleanup",
"(Ljava/lang/Object;)V", false);
mv.visitVarInsn(Opcodes.ALOAD, localIdx);
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"org/perlonjava/runtime/runtimetypes/MyVarCleanupStack",
"unregister",
"(Ljava/lang/Object;)V", false);
mv.visitInsn(Opcodes.ACONST_NULL);
mv.visitVarInsn(Opcodes.ASTORE, localIdx);
}
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/org/perlonjava/frontend/parser/StringParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import org.perlonjava.frontend.lexer.LexerToken;
import org.perlonjava.frontend.lexer.LexerTokenType;
import org.perlonjava.runtime.runtimetypes.PerlCompilerException;
import org.perlonjava.runtime.runtimetypes.GlobalVariable;
import org.perlonjava.runtime.runtimetypes.RuntimeScalar;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -17,6 +19,7 @@
import static org.perlonjava.runtime.perlmodule.Strict.HINT_RE_ASCII;
import static org.perlonjava.runtime.perlmodule.Strict.HINT_RE_EVAL;
import static org.perlonjava.runtime.perlmodule.Strict.HINT_RE_UNICODE;
import static org.perlonjava.runtime.runtimetypes.NameNormalizer.normalizeVariableName;
import static org.perlonjava.runtime.runtimetypes.ScalarUtils.printable;

/*
Expand Down Expand Up @@ -712,6 +715,16 @@ public static Node parseRawString(Parser parser, String operator) {
// before passing to glob(). This ensures variables are interpolated.
Node interpolated = StringDoubleQuoted.parseDoubleQuotedString(
parser.ctx, rawStr, true, true, false, parser.getHeredocNodes(), parser);
RuntimeScalar globOverride = findGlobOverride(parser);
if (globOverride != null) {
OperatorNode codeRefNode = new OperatorNode("&",
new IdentifierNode("glob", rawStr.index),
rawStr.index);
codeRefNode.setAnnotation("parseTimeCodeRef", globOverride);
ListNode arguments = new ListNode(rawStr.index);
arguments.elements.add(interpolated);
return new BinaryOperatorNode("(", codeRefNode, arguments, rawStr.index);
}
ListNode diamondList = new ListNode(rawStr.index);
diamondList.elements.add(interpolated);
return new OperatorNode("<>", diamondList, rawStr.index);
Expand All @@ -726,6 +739,17 @@ public static Node parseRawString(Parser parser, String operator) {
return new OperatorNode(operator, list, rawStr.index);
}

private static RuntimeScalar findGlobOverride(Parser parser) {
String currentGlob = normalizeVariableName("glob", parser.ctx.symbolTable.getCurrentPackage());
if (GlobalVariable.existsGlobalCodeRef(currentGlob)) {
return GlobalVariable.getGlobalCodeRef(currentGlob);
}
if (GlobalVariable.existsGlobalCodeRef("CORE::GLOBAL::glob")) {
return GlobalVariable.getGlobalCodeRef("CORE::GLOBAL::glob");
}
return null;
}

static StringNode parseVstring(Parser parser, String vStringPart, int currentIndex) {
// Start constructing the v-string
StringBuilder vStringBuilder = new StringBuilder();
Expand Down
29 changes: 16 additions & 13 deletions src/main/java/org/perlonjava/runtime/runtimetypes/MortalList.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ public static java.util.List<RuntimeBase> snapshotTemporaryRoots() {
return new java.util.ArrayList<>(temporaryRoots.get());
}

public static boolean hasTemporaryRoots() {
return !temporaryRoots.get().isEmpty();
}

/**
* Phase I: O(1) check whether the given scalar is in
* {@link #deferredCaptures}. Used by the reachability walker to
Expand Down Expand Up @@ -245,9 +249,10 @@ public static void deferDecrementIfTracked(RuntimeScalar scalar) {
* Release a scope-exited closure capture. This is normally the same as
* {@link #deferDecrementIfTracked}, but DBIC's leak tracer can wrap
* Try::Tiny blocks with {@code goto} and weak refs, making a captured
* temporary consume the counted owner of an outer lexical that is still
* live. In that case, transfer the counted owner to the still-live scalar
* by clearing this capture's ownership without decrementing the referent.
* temporary consume the counted owner of package-global metadata. In that
* case, transfer ownership only when the referent is still reachable from a
* non-lexical root; stack-local temporaries must release normally so
* DESTROY fires at lexical scope exit.
*/
public static void releaseCapturedDecrement(RuntimeScalar scalar) {
if (!active || scalar == null) return;
Expand All @@ -256,7 +261,7 @@ public static void releaseCapturedDecrement(RuntimeScalar scalar) {
&& scalar.value instanceof RuntimeBase base
&& base.blessId != 0
&& WeakRefRegistry.hasWeakRefsTo(base)
&& isReachableFromLiveRootForCaptureRelease(base)) {
&& isReachableFromNonLexicalRootForCaptureRelease(base)) {
scalar.refCountOwned = false;
if (base.refCountTrace) {
base.traceRefCount(0, "MortalList.releaseCapturedDecrement (transferred to live scalar)");
Expand Down Expand Up @@ -942,17 +947,14 @@ private static void invalidateDrainReachabilityCaches() {
invalidateLiveRootSnapshot();
}

private static boolean isReachableFromLiveRootForCaptureRelease(RuntimeBase base) {
if (liveRootSnapshot == null) {
liveRootSnapshot = new ReachabilityWalker.LiveRootSnapshot();
}
if (liveRootSnapshot.isReachable(base)) {
private static boolean isReachableFromNonLexicalRootForCaptureRelease(RuntimeBase base) {
if (ReachabilityWalker.isReachableFromTemporaryRoots(base)) {
return true;
}
// Compatibility fallback for uncommon non-flush callers. The hot
// releaseCaptures path runs while MortalList is flushing and must not
// force a JVM GC for every captured scalar.
return !flushing && ReachabilityWalker.isReachableFromLiveScalarRegistry(base);
if (externalRootSnapshot == null) {
externalRootSnapshot = new ReachabilityWalker.ExternalRootSnapshot();
}
return externalRootSnapshot.isReachableFromNonLexicalRoot(base);
}

private static void processDeferredBase(RuntimeBase base, boolean clearWeakRefsForLocalBinding) {
Expand Down Expand Up @@ -1069,6 +1071,7 @@ private static void maybeAutoSweep() {
// defined until the init completes.
if (ModuleInitGuard.inModuleInit()) return;
if (RuntimeCode.argsStackDepth() > 1) return;
if (hasTemporaryRoots()) return;
if (!FORCE_SWEEP_EVERY_FLUSH && !immediateSweep) {
long now = System.nanoTime();
if (now - lastAutoSweepNanos < AUTO_SWEEP_MIN_INTERVAL_NS) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ private static void decLiveCount(Object var) {
private static void noteVarLeftScope(Object var) {
if (var instanceof RuntimeBase base && WeakRefRegistry.hasWeakRefsTo(base)) {
MortalList.requestImmediateWeakSweep();
} else if (var instanceof RuntimeScalar scalar
&& (scalar.type & RuntimeScalarType.REFERENCE_BIT) != 0
&& scalar.value instanceof RuntimeBase base
&& WeakRefRegistry.hasWeakRefsTo(base)) {
MortalList.requestImmediateWeakSweep();
}
}

Expand Down
Loading
Loading