Skip to content

Commit 46f16bd

Browse files
DBIx::Class: B::CV introspection, require paths, Long overflow, STORABLE hooks, interpreter context, ternary-as-lvalue
Changes since squash-merge to master: - Fix B::CV->STASH->NAME and GV->NAME introspection (uses Sub::Util::subname) - Preserve @inc entry relativity in require/use filenames - Fix Long.MIN_VALUE overflow in initializeWithLong - Add STORABLE_freeze/thaw hook support for DBIx::Class serialization - Fix interpreter context propagation for subroutine bodies (wantarray) - Fix ternary-as-lvalue with LIST assignment branches (Class::Accessor::Grouped) - Confirm File::stat VerifyError is resolved - Fix DBI retry logic for stale PreparedStatements after ROLLBACK - Update DBIx::Class design doc through step 5.34 Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent b4dfccb commit 46f16bd

File tree

15 files changed

+599
-82
lines changed

15 files changed

+599
-82
lines changed

dev/modules/dbix_class.md

Lines changed: 100 additions & 52 deletions
Large diffs are not rendered by default.

src/main/java/org/perlonjava/backend/bytecode/BytecodeCompiler.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4626,6 +4626,10 @@ private void visitNamedSubroutine(SubroutineNode node) {
46264626
subCompiler.currentSubroutineBeginId = beginId;
46274627
subCompiler.currentSubroutineClosureVars = new HashSet<>(closureVarNames);
46284628

4629+
// Subroutine bodies should use RUNTIME context so the calling context
4630+
// (VOID/SCALAR/LIST) propagates correctly at runtime via register 2 (wantarray).
4631+
subCompiler.currentCallContext = RuntimeContextType.RUNTIME;
4632+
46294633
// Step 4: Compile the subroutine body
46304634
// Sub-compiler will use RETRIEVE_BEGIN opcodes for closure variables
46314635
InterpretedCode subCode = subCompiler.compile(node.block);
@@ -4728,6 +4732,12 @@ private void visitAnonymousSubroutine(SubroutineNode node) {
47284732
subCompiler.isInDeferBlock = true;
47294733
}
47304734

4735+
// Subroutine bodies should use RUNTIME context so the calling context
4736+
// (VOID/SCALAR/LIST) propagates correctly at runtime via register 2 (wantarray).
4737+
// Without this, the default LIST context is baked into all opcodes,
4738+
// causing incorrect behavior when the sub is called in VOID or SCALAR context.
4739+
subCompiler.currentCallContext = RuntimeContextType.RUNTIME;
4740+
47314741
// Step 4: Compile the subroutine body
47324742
// Sub-compiler will use parentRegistry to resolve captured variables
47334743
InterpretedCode subCode = subCompiler.compile(node.block);

src/main/java/org/perlonjava/backend/bytecode/BytecodeInterpreter.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,15 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c
847847
int argsReg = bytecode[pc++];
848848
int context = bytecode[pc++];
849849

850+
// Resolve RUNTIME context from register 2 (wantarray).
851+
// When a subroutine body is compiled by the interpreter,
852+
// the calling context is not known at compile time, so
853+
// RUNTIME is baked into the bytecode. At execution time,
854+
// resolve it from the actual calling context in register 2.
855+
if (context == RuntimeContextType.RUNTIME) {
856+
context = ((RuntimeScalar) registers[2]).getInt();
857+
}
858+
850859
// Auto-convert coderef to scalar if needed
851860
RuntimeBase codeRefBase = registers[coderefReg];
852861
RuntimeScalar codeRef = (codeRefBase instanceof RuntimeScalar)
@@ -990,6 +999,11 @@ public static RuntimeList execute(InterpretedCode code, RuntimeArray args, int c
990999
int argsReg = bytecode[pc++];
9911000
int context = bytecode[pc++];
9921001

1002+
// Resolve RUNTIME context from register 2 (wantarray)
1003+
if (context == RuntimeContextType.RUNTIME) {
1004+
context = ((RuntimeScalar) registers[2]).getInt();
1005+
}
1006+
9931007
RuntimeScalar invocant = (RuntimeScalar) registers[invocantReg];
9941008
RuntimeScalar method = (RuntimeScalar) registers[methodReg];
9951009
RuntimeScalar currentSub = (RuntimeScalar) registers[currentSubReg];

src/main/java/org/perlonjava/backend/bytecode/InlineOpcodeHandler.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,8 @@ public static int executeMap(int[] bytecode, int pc, RuntimeBase[] registers) {
915915
int closureReg = bytecode[pc++];
916916
int ctx = bytecode[pc++];
917917

918+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
919+
918920
RuntimeBase listBase = registers[listReg];
919921
RuntimeList list = listBase.getList();
920922
RuntimeScalar closure = (RuntimeScalar) registers[closureReg];
@@ -933,6 +935,8 @@ public static int executeGrep(int[] bytecode, int pc, RuntimeBase[] registers) {
933935
int closureReg = bytecode[pc++];
934936
int ctx = bytecode[pc++];
935937

938+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
939+
936940
RuntimeBase listBase = registers[listReg];
937941
RuntimeList list = listBase.getList();
938942
RuntimeScalar closure = (RuntimeScalar) registers[closureReg];
@@ -1225,6 +1229,8 @@ public static int executeTie(int[] bytecode, int pc, RuntimeBase[] registers) {
12251229
int rd = bytecode[pc++];
12261230
int argsReg = bytecode[pc++];
12271231
int ctx = bytecode[pc++];
1232+
1233+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
12281234
RuntimeList tieArgs = (RuntimeList) registers[argsReg];
12291235
registers[rd] = TieOperators.tie(ctx, tieArgs.elements.toArray(new RuntimeBase[0]));
12301236
return pc;
@@ -1234,6 +1240,8 @@ public static int executeUntie(int[] bytecode, int pc, RuntimeBase[] registers)
12341240
int rd = bytecode[pc++];
12351241
int argsReg = bytecode[pc++];
12361242
int ctx = bytecode[pc++];
1243+
1244+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
12371245
RuntimeList untieArgs = (RuntimeList) registers[argsReg];
12381246
registers[rd] = TieOperators.untie(ctx, untieArgs.elements.toArray(new RuntimeBase[0]));
12391247
return pc;
@@ -1243,6 +1251,8 @@ public static int executeTied(int[] bytecode, int pc, RuntimeBase[] registers) {
12431251
int rd = bytecode[pc++];
12441252
int argsReg = bytecode[pc++];
12451253
int ctx = bytecode[pc++];
1254+
1255+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
12461256
RuntimeList tiedArgs = (RuntimeList) registers[argsReg];
12471257
registers[rd] = TieOperators.tied(ctx, tiedArgs.elements.toArray(new RuntimeBase[0]));
12481258
return pc;
@@ -1312,6 +1322,8 @@ public static int executeDoFile(int[] bytecode, int pc, RuntimeBase[] registers)
13121322
int rd = bytecode[pc++];
13131323
int fileReg = bytecode[pc++];
13141324
int ctx = bytecode[pc++];
1325+
1326+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
13151327
RuntimeScalar file = registers[fileReg].scalar();
13161328
registers[rd] = ModuleOperators.doFile(file, ctx);
13171329
return pc;
@@ -1329,6 +1341,8 @@ public static int executeGlobOp(int[] bytecode, int pc, RuntimeBase[] registers)
13291341
int globId = bytecode[pc++];
13301342
int patternReg = bytecode[pc++];
13311343
int ctx = bytecode[pc++];
1344+
1345+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
13321346
registers[rd] = ScalarGlobOperator.evaluate(globId, (RuntimeScalar) registers[patternReg], ctx);
13331347
return pc;
13341348
}

src/main/java/org/perlonjava/backend/bytecode/MiscOpcodeHandler.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public static int execute(int opcode, int[] bytecode, int pc, RuntimeBase[] regi
2020
int argsReg = bytecode[pc++];
2121
int ctx = bytecode[pc++];
2222

23+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
24+
2325
// EACH receives the container directly (RuntimeHash or RuntimeArray), not a RuntimeList
2426
if (opcode == Opcodes.EACH) {
2527
registers[rd] = registers[argsReg].each(ctx);

src/main/java/org/perlonjava/backend/bytecode/OpcodeHandlerExtended.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ public static int executeSubstrVar(int[] bytecode, int pc, RuntimeBase[] registe
9292
int argsListReg = bytecode[pc++];
9393
int ctx = bytecode[pc++];
9494

95+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
96+
9597
RuntimeList argsList = (RuntimeList) registers[argsListReg];
9698
RuntimeBase[] substrArgs = argsList.elements.toArray(new RuntimeBase[0]);
9799

@@ -114,6 +116,8 @@ public static int executeSubstrVarNoWarn(int[] bytecode, int pc, RuntimeBase[] r
114116
int argsListReg = bytecode[pc++];
115117
int ctx = bytecode[pc++];
116118

119+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
120+
117121
RuntimeList argsList = (RuntimeList) registers[argsListReg];
118122
RuntimeBase[] substrArgs = argsList.elements.toArray(new RuntimeBase[0]);
119123

@@ -536,6 +540,8 @@ public static int executeStat(int[] bytecode, int pc, RuntimeBase[] registers) {
536540
int rd = bytecode[pc++];
537541
int rs = bytecode[pc++];
538542
int ctx = bytecode[pc++];
543+
544+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
539545
registers[rd] = Stat.stat((RuntimeScalar) registers[rs], ctx);
540546
return pc;
541547
}
@@ -548,20 +554,26 @@ public static int executeLstat(int[] bytecode, int pc, RuntimeBase[] registers)
548554
int rd = bytecode[pc++];
549555
int rs = bytecode[pc++];
550556
int ctx = bytecode[pc++];
557+
558+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
551559
registers[rd] = Stat.lstat((RuntimeScalar) registers[rs], ctx);
552560
return pc;
553561
}
554562

555563
public static int executeStatLastHandle(int[] bytecode, int pc, RuntimeBase[] registers) {
556564
int rd = bytecode[pc++];
557565
int ctx = bytecode[pc++];
566+
567+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
558568
registers[rd] = Stat.statLastHandle(ctx);
559569
return pc;
560570
}
561571

562572
public static int executeLstatLastHandle(int[] bytecode, int pc, RuntimeBase[] registers) {
563573
int rd = bytecode[pc++];
564574
int ctx = bytecode[pc++];
575+
576+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
565577
registers[rd] = Stat.lstatLastHandle(ctx);
566578
return pc;
567579
}
@@ -782,6 +794,8 @@ public static int executePostAutoDecrement(int[] bytecode, int pc, RuntimeBase[]
782794
public static int executeOpen(int[] bytecode, int pc, RuntimeBase[] registers) {
783795
int rd = bytecode[pc++];
784796
int ctx = bytecode[pc++];
797+
798+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
785799
int argsReg = bytecode[pc++];
786800
RuntimeArray argsArray = (RuntimeArray) registers[argsReg];
787801
RuntimeBase[] argsVarargs = argsArray.elements.toArray(new RuntimeBase[0]);
@@ -797,6 +811,8 @@ public static int executeReadline(int[] bytecode, int pc, RuntimeBase[] register
797811
int rd = bytecode[pc++];
798812
int fhReg = bytecode[pc++];
799813
int ctx = bytecode[pc++];
814+
815+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
800816
RuntimeScalar fh = (RuntimeScalar) registers[fhReg];
801817
// Diamond operator <> passes a plain string scalar (not a glob/IO).
802818
// Route to DiamondIO.readline which manages @ARGV / STDIN iteration.
@@ -817,6 +833,8 @@ public static int executeMatchRegex(int[] bytecode, int pc, RuntimeBase[] regist
817833
int stringReg = bytecode[pc++];
818834
int regexReg = bytecode[pc++];
819835
int ctx = bytecode[pc++];
836+
837+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
820838
registers[rd] = RuntimeRegex.matchRegex(
821839
(RuntimeScalar) registers[regexReg],
822840
(RuntimeScalar) registers[stringReg],
@@ -834,6 +852,8 @@ public static int executeMatchRegexNot(int[] bytecode, int pc, RuntimeBase[] reg
834852
int stringReg = bytecode[pc++];
835853
int regexReg = bytecode[pc++];
836854
int ctx = bytecode[pc++];
855+
856+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
837857
RuntimeBase matchResult = RuntimeRegex.matchRegex(
838858
(RuntimeScalar) registers[regexReg],
839859
(RuntimeScalar) registers[stringReg],

src/main/java/org/perlonjava/backend/bytecode/SlowOpcodeHandler.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ public static int executeEvalString(
279279
if (pc < bytecode.length) {
280280
evalCallContext = bytecode[pc++];
281281
}
282+
if (evalCallContext == RuntimeContextType.RUNTIME) evalCallContext = ((RuntimeScalar) registers[2]).getInt();
282283
int evalSiteIndex = -1;
283284
if (pc < bytecode.length) {
284285
evalSiteIndex = bytecode[pc++];
@@ -691,6 +692,7 @@ public static int executeSplice(
691692
int arrayReg = bytecode[pc++];
692693
int argsReg = bytecode[pc++];
693694
int context = bytecode[pc++];
695+
if (context == RuntimeContextType.RUNTIME) context = ((RuntimeScalar) registers[2]).getInt();
694696

695697
RuntimeArray array = (RuntimeArray) registers[arrayReg];
696698
RuntimeList args = (RuntimeList) registers[argsReg];
@@ -747,6 +749,8 @@ public static int executeReverse(
747749
int argsReg = bytecode[pc++];
748750
int ctx = bytecode[pc++];
749751

752+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
753+
750754
RuntimeList argsList = (RuntimeList) registers[argsReg];
751755
RuntimeBase[] args = argsList.elements.toArray(new RuntimeBase[0]);
752756

@@ -800,6 +804,8 @@ public static int executeSplit(
800804
int argsReg = bytecode[pc++];
801805
int ctx = bytecode[pc++];
802806

807+
if (ctx == RuntimeContextType.RUNTIME) ctx = ((RuntimeScalar) registers[2]).getInt();
808+
803809
RuntimeScalar pattern = (RuntimeScalar) registers[patternReg];
804810
RuntimeBase argsBase = registers[argsReg];
805811
RuntimeList args = (argsBase instanceof RuntimeList)
@@ -1212,6 +1218,7 @@ public static int executeTransliterate(
12121218

12131219
// Read context (1 int slot)
12141220
int context = bytecode[pc++];
1221+
if (context == RuntimeContextType.RUNTIME) context = ((RuntimeScalar) registers[2]).getInt();
12151222

12161223
RuntimeScalar search = (RuntimeScalar) registers[searchReg];
12171224
RuntimeScalar replace = (RuntimeScalar) registers[replaceReg];

0 commit comments

Comments
 (0)