@@ -55,6 +55,8 @@ protected boolean removeEldestEntry(Map.Entry<Class<?>, MethodHandle> eldest) {
5555 // Temporary storage for anonymous subroutines and eval string compiler context
5656 public static HashMap <String , Class <?>> anonSubs = new HashMap <>(); // temp storage for makeCodeObject()
5757 public static HashMap <String , EmitterContext > evalContext = new HashMap <>(); // storage for eval string compiler context
58+ // Runtime eval counter for generating unique filenames when $^P is set
59+ private static int runtimeEvalCounter = 1 ;
5860 // Method object representing the compiled subroutine
5961 public MethodHandle methodHandle ;
6062 public boolean isStatic ;
@@ -148,11 +150,32 @@ public static Class<?> evalStringHelper(RuntimeScalar code, String evalTag) thro
148150 evalCompilerOptions .isUnicodeSource = true ;
149151 }
150152
153+ // Check $^P to determine if we should use caching
154+ // When debugging is enabled, we want each eval to get a unique filename
155+ int debugFlags = GlobalVariable .getGlobalVariable (GlobalContext .encodeSpecialVar ("P" )).getInt ();
156+ boolean isDebugging = debugFlags != 0 ;
157+
158+ // Override the filename with a runtime-generated eval number when debugging
159+ String actualFileName = evalCompilerOptions .fileName ;
160+ if (isDebugging ) {
161+ synchronized (RuntimeCode .class ) {
162+ actualFileName = "(eval " + runtimeEvalCounter ++ + ")" ;
163+ }
164+ }
165+
151166 // Check if the result is already cached (include hasUnicode in cache key)
167+ // Skip caching when $^P is set, so each eval gets a unique filename
152168 String cacheKey = code .toString () + '\0' + evalTag + '\0' + hasUnicode ;
153- synchronized (evalCache ) {
154- if (evalCache .containsKey (cacheKey )) {
155- return evalCache .get (cacheKey );
169+ Class <?> cachedClass = null ;
170+ if (!isDebugging ) {
171+ synchronized (evalCache ) {
172+ if (evalCache .containsKey (cacheKey )) {
173+ cachedClass = evalCache .get (cacheKey );
174+ }
175+ }
176+
177+ if (cachedClass != null ) {
178+ return cachedClass ;
156179 }
157180 }
158181
@@ -211,14 +234,77 @@ public static Class<?> evalStringHelper(RuntimeScalar code, String evalTag) thro
211234 );
212235 }
213236
214- // Cache the result
215- synchronized (evalCache ) {
216- evalCache .put (cacheKey , generatedClass );
237+ // Cache the result (unless debugging is enabled)
238+ if (!isDebugging ) {
239+ synchronized (evalCache ) {
240+ evalCache .put (cacheKey , generatedClass );
241+ }
217242 }
218243
244+ // Store source lines in symbol table if $^P flags are set
245+ storeSourceLines (evalString , actualFileName , ast );
246+
219247 return generatedClass ;
220248 }
221249
250+ /**
251+ * Stores source lines in the symbol table for debugger support when $^P flags are set.
252+ *
253+ * @param evalString The source code string to store
254+ * @param filename The filename (e.g., "(eval 1)")
255+ * @param ast The AST to check for subroutine definitions
256+ */
257+ private static void storeSourceLines (String evalString , String filename , Node ast ) {
258+ // Check $^P for debugger flags
259+ int debugFlags = GlobalVariable .getGlobalVariable (GlobalContext .encodeSpecialVar ("P" )).getInt ();
260+ // 0x02 (2): Line-by-line debugging (also saves source like 0x400)
261+ // 0x400 (1024): Save source code lines
262+ // 0x800 (2048): Include evals that generate no subroutines
263+ // 0x1000 (4096): Include source that did not compile
264+ boolean shouldSaveSource = (debugFlags & 0x02 ) != 0 || (debugFlags & 0x400 ) != 0 ;
265+ boolean saveWithoutSubs = (debugFlags & 0x800 ) != 0 ;
266+
267+ if (shouldSaveSource ) {
268+ // Note: We can't reliably detect subroutine definitions from the AST because
269+ // subroutines are processed at parse-time and removed from the AST.
270+ // Use a simple heuristic: check if the eval string contains "sub " followed by
271+ // an identifier or block.
272+ boolean definesSubs = evalString .matches ("(?s).*\\ bsub\\ s+(?:\\ w+|\\ {).*" );
273+
274+ // Only save if either:
275+ // - The eval defines subroutines, OR
276+ // - The 0x800 flag is set (save evals without subs)
277+ if (!definesSubs && !saveWithoutSubs ) {
278+ return ; // Skip this eval
279+ }
280+ // Store in the symbol table as @{"_<(eval N)"}
281+ String symbolKey = "_<" + filename ;
282+
283+ // Split the eval string into lines (without including trailing empty strings)
284+ String [] lines = evalString .split ("\n " );
285+
286+ // Create the array with the format expected by the debugger:
287+ // [0] = undef, [1..n] = lines with \n, [n+1] = \n, [n+2] = ;
288+ String arrayKey = "main::" + symbolKey ;
289+ RuntimeArray sourceArray = GlobalVariable .getGlobalArray (arrayKey );
290+ sourceArray .elements .clear ();
291+
292+ // Index 0: undef
293+ sourceArray .elements .add (RuntimeScalarCache .scalarUndef );
294+
295+ // Indexes 1..n: each line with "\n" appended
296+ for (String line : lines ) {
297+ sourceArray .elements .add (new RuntimeScalar (line + "\n " ));
298+ }
299+
300+ // Index n+1: "\n"
301+ sourceArray .elements .add (new RuntimeScalar ("\n " ));
302+
303+ // Index n+2: ";"
304+ sourceArray .elements .add (new RuntimeScalar (";" ));
305+ }
306+ }
307+
222308 // make sure we return a RuntimeScalar from __SUB__
223309 public static RuntimeScalar selfReferenceMaybeNull (RuntimeScalar codeRef ) {
224310 return codeRef == null
0 commit comments