13
13
import java .util .ArrayList ;
14
14
import java .util .List ;
15
15
import java .util .Map ;
16
+ import java .util .concurrent .ExecutorService ;
17
+ import java .util .concurrent .Executors ;
18
+ import java .util .concurrent .Future ;
16
19
import java .util .concurrent .Semaphore ;
17
20
18
21
import static org .perlonjava .parser .PrototypeArgs .consumeArgsWithPrototype ;
@@ -185,74 +188,87 @@ public static Node parseSubroutineDefinition(Parser parser, boolean wantName, St
185
188
if (subName == null ) {
186
189
return handleAnonSub (parser , subName , prototype , attributes , block , currentIndex );
187
190
} else {
188
- // - register the subroutine in the namespace
189
- String fullName = NameNormalizer .normalizeVariableName (subName , parser .ctx .symbolTable .getCurrentPackage ());
190
- RuntimeCode code = (RuntimeCode ) GlobalVariable .getGlobalCodeRef (fullName ).value ;
191
- code .prototype = prototype ;
192
- code .attributes = attributes ;
193
-
194
- // Optimization - https://github.com/fglock/PerlOnJava/issues/8
195
- // Prepare capture variables
196
- Map <Integer , SymbolTable .SymbolEntry > outerVars = parser .ctx .symbolTable .getAllVisibleVariables ();
197
- ArrayList <Class > classList = new ArrayList <>();
198
- ArrayList <Object > paramList = new ArrayList <>();
199
- for (SymbolTable .SymbolEntry entry : outerVars .values ()) {
200
- if (!entry .name ().equals ("@_" ) && !entry .decl ().isEmpty ()) {
201
- String sigil = entry .name ().substring (0 , 1 );
202
- String variableName = null ;
203
- if (entry .decl ().equals ("our" )) {
204
- variableName = NameNormalizer .normalizeVariableName (
205
- entry .name ().substring (1 ),
206
- entry .perlPackage ());
207
- } else {
208
- // "my" or "state" variable live in a special BEGIN package
209
- // Retrieve the variable id from the AST; create a new id if needed
210
- OperatorNode ast = entry .ast ();
211
- if (ast .id == 0 ) {
212
- ast .id = EmitterMethodCreator .classCounter ++;
213
- }
214
- variableName = NameNormalizer .normalizeVariableName (
215
- entry .name ().substring (1 ),
216
- PersistentVariable .beginPackage (ast .id ));
191
+ return handleNamedSub (parser , subName , prototype , attributes , block );
192
+ }
193
+ }
194
+
195
+ private static ListNode handleNamedSub (Parser parser , String subName , String prototype , List <String > attributes , BlockNode block ) {
196
+ // - register the subroutine in the namespace
197
+ String fullName = NameNormalizer .normalizeVariableName (subName , parser .ctx .symbolTable .getCurrentPackage ());
198
+ RuntimeCode code = (RuntimeCode ) GlobalVariable .getGlobalCodeRef (fullName ).value ;
199
+ code .prototype = prototype ;
200
+ code .attributes = attributes ;
201
+
202
+ // Optimization - https://github.com/fglock/PerlOnJava/issues/8
203
+ // Prepare capture variables
204
+ Map <Integer , SymbolTable .SymbolEntry > outerVars = parser .ctx .symbolTable .getAllVisibleVariables ();
205
+ ArrayList <Class > classList = new ArrayList <>();
206
+ ArrayList <Object > paramList = new ArrayList <>();
207
+ for (SymbolTable .SymbolEntry entry : outerVars .values ()) {
208
+ if (!entry .name ().equals ("@_" ) && !entry .decl ().isEmpty ()) {
209
+ String sigil = entry .name ().substring (0 , 1 );
210
+ String variableName = null ;
211
+ if (entry .decl ().equals ("our" )) {
212
+ // Normalize variable name for 'our' declarations
213
+ variableName = NameNormalizer .normalizeVariableName (
214
+ entry .name ().substring (1 ),
215
+ entry .perlPackage ());
216
+ } else {
217
+ // Handle "my" or "state" variables which live in a special BEGIN package
218
+ // Retrieve the variable id from the AST; create a new id if needed
219
+ OperatorNode ast = entry .ast ();
220
+ if (ast .id == 0 ) {
221
+ ast .id = EmitterMethodCreator .classCounter ++;
217
222
}
218
- classList .add (
219
- switch (sigil ) {
220
- case "$" -> RuntimeScalar .class ;
221
- case "%" -> RuntimeHash .class ;
222
- case "@" -> RuntimeArray .class ;
223
- default -> throw new IllegalStateException ("Unexpected value: " + sigil );
224
- }
225
- );
226
- paramList .add (
227
- switch (sigil ) {
228
- case "$" -> GlobalVariable .getGlobalVariable (variableName );
229
- case "%" -> GlobalVariable .getGlobalHash (variableName );
230
- case "@" -> GlobalVariable .getGlobalArray (variableName );
231
- default -> throw new IllegalStateException ("Unexpected value: " + sigil );
232
- }
233
- );
234
- // System.out.println("Capture " + entry.decl() + " " + entry.name() + " as " + variableName);
223
+ // Normalize variable name for 'my' or 'state' declarations
224
+ variableName = NameNormalizer .normalizeVariableName (
225
+ entry .name ().substring (1 ),
226
+ PersistentVariable .beginPackage (ast .id ));
235
227
}
228
+ // Determine the class type based on the sigil
229
+ classList .add (
230
+ switch (sigil ) {
231
+ case "$" -> RuntimeScalar .class ;
232
+ case "%" -> RuntimeHash .class ;
233
+ case "@" -> RuntimeArray .class ;
234
+ default -> throw new IllegalStateException ("Unexpected value: " + sigil );
235
+ }
236
+ );
237
+ // Add the corresponding global variable to the parameter list
238
+ paramList .add (
239
+ switch (sigil ) {
240
+ case "$" -> GlobalVariable .getGlobalVariable (variableName );
241
+ case "%" -> GlobalVariable .getGlobalHash (variableName );
242
+ case "@" -> GlobalVariable .getGlobalArray (variableName );
243
+ default -> throw new IllegalStateException ("Unexpected value: " + sigil );
244
+ }
245
+ );
246
+ // System.out.println("Capture " + entry.decl() + " " + entry.name() + " as " + variableName);
236
247
}
237
- EmitterContext newCtx = new EmitterContext (
238
- new JavaClassInfo (),
239
- parser .ctx .symbolTable .snapShot (),
240
- null ,
241
- null ,
242
- RuntimeContextType .RUNTIME ,
243
- true ,
244
- parser .ctx .errorUtil ,
245
- parser .ctx .compilerOptions ,
246
- new RuntimeArray ()
247
- );
248
-
249
- byte [] classData = EmitterMethodCreator .getBytecode (newCtx , block , false );
250
- // System.out.println("Creating subroutine " + fullName);
251
- Class <?> generatedClass = EmitterMethodCreator .loadBytecode (newCtx , classData );
252
-
253
- // Create a Runnable to execute the subroutine creation
254
- Runnable subroutineCreationTask = () -> {
255
-
248
+ }
249
+ // Create a new EmitterContext for generating bytecode
250
+ EmitterContext newCtx = new EmitterContext (
251
+ new JavaClassInfo (),
252
+ parser .ctx .symbolTable .snapShot (),
253
+ null ,
254
+ null ,
255
+ RuntimeContextType .RUNTIME ,
256
+ true ,
257
+ parser .ctx .errorUtil ,
258
+ parser .ctx .compilerOptions ,
259
+ new RuntimeArray ()
260
+ );
261
+
262
+ // Generate bytecode for the subroutine block
263
+ byte [] classData = EmitterMethodCreator .getBytecode (newCtx , block , false );
264
+ // System.out.println("Creating subroutine " + fullName);
265
+ // Load the generated bytecode into a Class object
266
+ Class <?> generatedClass = EmitterMethodCreator .loadBytecode (newCtx , classData );
267
+
268
+ // Create a Runnable to execute the subroutine creation
269
+ Runnable subroutineCreationTask = () -> {
270
+
271
+ // The following commented-out code is a work in progress for handling concurrency
256
272
// Class<?> generatedClass = null;
257
273
// try {
258
274
// semaphore.acquire();
@@ -267,39 +283,41 @@ public static Node parseSubroutineDefinition(Parser parser, boolean wantName, St
267
283
// semaphore.release();
268
284
// }
269
285
270
- // System.out.println("Class " + generatedClass);
271
- // EmitterMethodCreator.debugInspectClass(generatedClass);
272
- try {
273
- Class <?>[] parameterTypes = classList . toArray ( new Class <?>[ 0 ]);
274
- Constructor <?> constructor = generatedClass . getConstructor ( parameterTypes );
275
- // System.out.println("Constructor: " + constructor );
276
-
277
- Object [] parameters = paramList . toArray ();
278
- code . codeObject = constructor . newInstance ( parameters );
279
- // System.out.println("Instance: " + instance );
280
-
281
- code . methodObject = generatedClass . getMethod ( "apply" , RuntimeArray . class , int . class );
282
- // System.out.println("Method: " + method);
283
-
284
- } catch ( Exception e ) {
285
- throw new PerlCompilerException ( "Subroutine error : " + e . getMessage () );
286
- }
287
- // System.out.println("Subroutine " + fullName + " created");
288
-
289
- // Clear the compilerThread once done
290
- code . compilerThread = null ;
291
- } ;
286
+ // System.out.println("Class " + generatedClass);
287
+ // EmitterMethodCreator.debugInspectClass(generatedClass);
288
+ try {
289
+ // Prepare constructor with the captured variable types
290
+ Class <?>[] parameterTypes = classList . toArray ( new Class <?>[ 0 ] );
291
+ Constructor <?> constructor = generatedClass . getConstructor ( parameterTypes );
292
+ // System.out.println("Constructor: " + constructor);
293
+
294
+ // Instantiate the subroutine with the captured variables
295
+ Object [] parameters = paramList . toArray ( );
296
+ code . codeObject = constructor . newInstance ( parameters );
297
+ // System.out.println("Instance: " + instance );
298
+
299
+ // Retrieve the 'apply' method from the generated class
300
+ code . methodObject = generatedClass . getMethod ( "apply" , RuntimeArray . class , int . class );
301
+ // System.out.println("Method : " + method );
302
+
303
+ } catch ( Exception e ) {
304
+ // Handle any exceptions during subroutine creation
305
+ throw new PerlCompilerException ( "Subroutine error: " + e . getMessage ());
306
+ }
307
+ // System.out.println("Subroutine " + fullName + " created") ;
292
308
293
- // Start the subroutine creation in a new thread
294
- Thread subroutineThread = new Thread ( subroutineCreationTask ) ;
295
- subroutineThread . start () ;
309
+ // Clear the compilerThread once done
310
+ code . compilerThread = null ;
311
+ } ;
296
312
297
- // Set the compilerThread in the RuntimeCode instance
298
- code .compilerThread = subroutineThread ;
313
+ // Use an ExecutorService with virtual threads
314
+ ExecutorService executor = Executors .newVirtualThreadPerTaskExecutor ();
315
+ Future <?> future = executor .submit (subroutineCreationTask );
316
+ executor .shutdown ();
317
+ code .compilerThread = future ;
299
318
300
- // return an empty AST list
301
- return new ListNode (parser .tokenIndex );
302
- }
319
+ // return an empty AST list
320
+ return new ListNode (parser .tokenIndex );
303
321
}
304
322
305
323
private static SubroutineNode handleAnonSub (Parser parser , String subName , String prototype , List <String > attributes , BlockNode block , int currentIndex ) {
0 commit comments