@@ -279,36 +279,53 @@ private Stream<InjectionTask> handleGenericAnnotations(Parameter parameter,
279279 List <List <AnnotationDescription >> genericAnnotations ) {
280280 TypeDescription .Generic genericType = parameter .genericType ();
281281
282- // Only process if this is a parameterized type
283- if (!genericType .getSort ().isParameterized ()) {
284- return empty ();
285- }
282+ if (genericType .getSort ().isParameterized ()) {
283+ List <TypeDescription .Generic > typeArguments = genericType .getTypeArguments ();
284+ return range (0 , genericAnnotations .size ()).boxed ().flatMap (i -> {
285+ List <AnnotationDescription > typeArgAnnotations = genericAnnotations .get (i );
286+ if (typeArgAnnotations .isEmpty () || i >= typeArguments .size ()) {
287+ return empty ();
288+ }
286289
287- List <TypeDescription .Generic > typeArguments = genericType .getTypeArguments ();
288- return range (0 , genericAnnotations .size ()).boxed ().flatMap (i -> {
289- List <AnnotationDescription > typeArgAnnotations = genericAnnotations .get (i );
290- if (typeArgAnnotations .isEmpty () || i >= typeArguments .size ()) {
291- return empty ();
292- }
290+ TypeDescription .Generic typeArgument = typeArguments .get (i );
291+ if (typeArgument == null ) {
292+ return empty ();
293+ }
293294
294- TypeDescription .Generic typeArgument = typeArguments .get (i );
295- if (typeArgument == null ) {
295+ return typeArgAnnotations .stream ().flatMap (annotation -> {
296+ TypeDescription annotationType = annotation .getAnnotationType ();
297+ if (isStandardJr380Anno (annotationType )) {
298+ Optional <Method > fragmentMethod = codeFragmentMethod (annotationType ,
299+ typeArgument .asErasure ());
300+ if (fragmentMethod .isPresent ()) {
301+ return Stream .of (new GenericTypeInjectionTask (parameter , typeArgument .asErasure (),
302+ fragmentMethod .get (), annotation , i ));
303+ }
304+ }
305+ return empty ();
306+ });
307+ });
308+ } else if (genericType .isArray ()) {
309+ List <AnnotationDescription > typeArgAnnotations = genericAnnotations .get (0 );
310+ if (typeArgAnnotations .isEmpty ()) {
296311 return empty ();
297312 }
298313
299- // Create injection tasks for each annotation on the type argument
314+ TypeDescription . Generic typeArgument = genericType . getComponentType ();
300315 return typeArgAnnotations .stream ().flatMap (annotation -> {
301316 TypeDescription annotationType = annotation .getAnnotationType ();
302317 if (isStandardJr380Anno (annotationType )) {
303318 Optional <Method > fragmentMethod = codeFragmentMethod (annotationType , typeArgument .asErasure ());
304319 if (fragmentMethod .isPresent ()) {
305320 return Stream .of (new GenericTypeInjectionTask (parameter , typeArgument .asErasure (),
306- fragmentMethod .get (), annotation ));
321+ fragmentMethod .get (), annotation , 0 ));
307322 }
308323 }
309324 return empty ();
310325 });
311- });
326+ }
327+
328+ return empty ();
312329 }
313330
314331 private Stream <InjectionTask > jsr380 (Parameter parameter , TypeDescription annotation ,
@@ -401,13 +418,15 @@ private static class GenericTypeInjectionTask implements InjectionTask {
401418 TypeDescription elementType ;
402419 Method fragmentMethod ;
403420 AnnotationDescription annotation ;
421+ int index ;
404422
405423 GenericTypeInjectionTask (Parameter parameter , TypeDescription elementType , Method fragmentMethod ,
406- AnnotationDescription annotation ) {
424+ AnnotationDescription annotation , int index ) {
407425 this .parameter = parameter ;
408426 this .elementType = elementType ;
409427 this .fragmentMethod = fragmentMethod ;
410428 this .annotation = annotation ;
429+ this .index = index ;
411430 }
412431
413432 @ Override
@@ -428,19 +447,97 @@ private void generateIterationWithValidation(ValidationCodeInjector injector, Me
428447 mv .visitVarInsn (ALOAD , containerParam .offset ());
429448 mv .visitJumpInsn (IFNULL , ifNullLabel );
430449
431- // Generate: for (Object e : parameter)
432- generateForEachLoopWithValidation (injector , mv , containerParam , annotation );
450+ TypeDescription containerType = containerParam .type ();
451+ if (containerType .isArray ()) {
452+ generateArrayLoopWithValidation (injector , mv , containerParam , annotation );
453+ } else if (containerType .isAssignableTo (Map .class )) {
454+ generateMapLoopWithValidation (injector , mv , containerParam , annotation );
455+ } else {
456+ // Assume Iterable (Collection, List, Set)
457+ generateForEachLoopWithValidation (injector , mv , containerParam , annotation );
458+ }
433459
434460 mv .visitLabel (ifNullLabel );
435461 }
436462
437- private void generateForEachLoopWithValidation (ValidationCodeInjector injector , MethodVisitor mv ,
463+ private void generateArrayLoopWithValidation (ValidationCodeInjector injector , MethodVisitor mv ,
438464 Parameter containerParam , AnnotationDescription annotation ) {
439- // Get the element type from the container's generic type
440- TypeDescription elementType = getGenericElementType (containerParam );
465+ TypeDescription containerType = containerParam .type ();
466+ TypeDescription elementType = containerType .getComponentType ();
467+
468+ // Load the array
469+ mv .visitVarInsn (ALOAD , containerParam .offset ());
470+ mv .visitInsn (net .bytebuddy .jar .asm .Opcodes .ARRAYLENGTH );
471+
472+ // Store length in a local variable
473+ int lengthVar = containerParam .offset () + 1 ;
474+ mv .visitVarInsn (net .bytebuddy .jar .asm .Opcodes .ISTORE , lengthVar );
441475
476+ // Initialize index in a local variable
477+ int indexVar = lengthVar + 1 ;
478+ mv .visitInsn (net .bytebuddy .jar .asm .Opcodes .ICONST_0 );
479+ mv .visitVarInsn (net .bytebuddy .jar .asm .Opcodes .ISTORE , indexVar );
480+
481+ Label loopStart = new Label ();
482+ Label loopEnd = new Label ();
483+
484+ mv .visitLabel (loopStart );
485+
486+ // Loop condition: if index >= length then goto end
487+ mv .visitVarInsn (net .bytebuddy .jar .asm .Opcodes .ILOAD , indexVar );
488+ mv .visitVarInsn (net .bytebuddy .jar .asm .Opcodes .ILOAD , lengthVar );
489+ mv .visitJumpInsn (net .bytebuddy .jar .asm .Opcodes .IF_ICMPGE , loopEnd );
490+
491+ // Load element from array: array[index]
492+ mv .visitVarInsn (ALOAD , containerParam .offset ());
493+ mv .visitVarInsn (net .bytebuddy .jar .asm .Opcodes .ILOAD , indexVar );
494+
495+ int elementVar = indexVar + 1 ;
496+ if (elementType .isPrimitive ()) {
497+ Type primitiveType = Type .getType (elementType .getDescriptor ());
498+ mv .visitInsn (primitiveType .getOpcode (net .bytebuddy .jar .asm .Opcodes .IALOAD ));
499+ mv .visitVarInsn (primitiveType .getOpcode (net .bytebuddy .jar .asm .Opcodes .ISTORE ), elementVar );
500+ } else {
501+ mv .visitInsn (net .bytebuddy .jar .asm .Opcodes .AALOAD );
502+ mv .visitVarInsn (ASTORE , elementVar );
503+ }
504+
505+ // Call the fragment method using the injector
506+ injectValidation (injector , mv , containerParam , annotation , elementType , elementVar );
507+
508+ // increment index and goto start
509+ mv .visitIincInsn (indexVar , 1 );
510+ mv .visitJumpInsn (GOTO , loopStart );
511+
512+ mv .visitLabel (loopEnd );
513+ }
514+
515+ private void generateMapLoopWithValidation (ValidationCodeInjector injector , MethodVisitor mv ,
516+ Parameter containerParam , AnnotationDescription annotation ) {
517+ // Load the map
518+ mv .visitVarInsn (ALOAD , containerParam .offset ());
519+
520+ if (index == 0 ) {
521+ // Iterate over keys
522+ mv .visitMethodInsn (INVOKEINTERFACE , "java/util/Map" , "keySet" , "()Ljava/util/Set;" , true );
523+ } else {
524+ // Iterate over values
525+ mv .visitMethodInsn (INVOKEINTERFACE , "java/util/Map" , "values" , "()Ljava/util/Collection;" , true );
526+ }
527+
528+ // Now we have an Iterable on the stack, we can use the foreach loop logic
529+ generateIteratorLoopWithValidation (injector , mv , containerParam , annotation , elementType );
530+ }
531+
532+ private void generateForEachLoopWithValidation (ValidationCodeInjector injector , MethodVisitor mv ,
533+ Parameter containerParam , AnnotationDescription annotation ) {
442534 // Load the container and get its iterator
443535 mv .visitVarInsn (ALOAD , containerParam .offset ());
536+ generateIteratorLoopWithValidation (injector , mv , containerParam , annotation , elementType );
537+ }
538+
539+ private void generateIteratorLoopWithValidation (ValidationCodeInjector injector , MethodVisitor mv ,
540+ Parameter containerParam , AnnotationDescription annotation , TypeDescription elementType ) {
444541 mv .visitMethodInsn (INVOKEINTERFACE , "java/lang/Iterable" , "iterator" , "()Ljava/util/Iterator;" , true );
445542
446543 // Store iterator in a local variable
@@ -471,15 +568,8 @@ private void generateForEachLoopWithValidation(ValidationCodeInjector injector,
471568 // Store element in local variable
472569 mv .visitVarInsn (ASTORE , elementVar );
473570
474- // Use the injector to call the fragment method with the element
475- // Create a synthetic parameter representing the element
476- SyntheticElementParameter elementParam = new SyntheticElementParameter (elementVar , elementType );
477-
478571 // Call the fragment method using the injector
479- @ SuppressWarnings ("unchecked" )
480- Class <? extends Jsr380CodeFragment > clazz = (Class <? extends Jsr380CodeFragment >) fragmentMethod
481- .getDeclaringClass ();
482- injector .useFragmentClass (clazz ).inject (mv , elementParam , fragmentMethod , annotation );
572+ injectValidation (injector , mv , containerParam , annotation , elementType , elementVar );
483573
484574 // Loop condition: check if hasNext()
485575 mv .visitLabel (loopTest );
@@ -488,21 +578,28 @@ private void generateForEachLoopWithValidation(ValidationCodeInjector injector,
488578 mv .visitJumpInsn (IFNE , loopStart );
489579 }
490580
491- private TypeDescription getGenericElementType (Parameter containerParam ) {
492- // Get the generic type from the parameter itself, which has the type
493- // information
494- TypeDescription .Generic generic = containerParam .genericType ();
495- return generic == null || generic .getTypeArguments ().isEmpty ()
496- ? TypeDescription .ForLoadedType .of (Object .class )
497- : generic .getTypeArguments ().get (0 ).asErasure ();
581+ private void injectValidation (ValidationCodeInjector injector , MethodVisitor mv , Parameter containerParam ,
582+ AnnotationDescription annotation , TypeDescription elementType , int elementVar ) {
583+ // Create a synthetic parameter representing the element
584+ SyntheticElementParameter elementParam = new SyntheticElementParameter (containerParam , elementVar ,
585+ elementType );
586+
587+ // Call the fragment method using the injector
588+ @ SuppressWarnings ("unchecked" )
589+ Class <? extends Jsr380CodeFragment > clazz = (Class <? extends Jsr380CodeFragment >) fragmentMethod
590+ .getDeclaringClass ();
591+ injector .useFragmentClass (clazz ).inject (mv , elementParam , fragmentMethod , annotation );
498592 }
593+
499594 }
500595
501596 private static class SyntheticElementParameter implements Parameters .Parameter {
597+ private final Parameter containerParam ;
502598 private final int offset ;
503599 private final TypeDescription type ;
504600
505- SyntheticElementParameter (int offset , TypeDescription type ) {
601+ SyntheticElementParameter (Parameter containerParam , int offset , TypeDescription type ) {
602+ this .containerParam = containerParam ;
506603 this .offset = offset ;
507604 this .type = type ;
508605 }
@@ -524,7 +621,7 @@ public TypeDescription type() {
524621
525622 @ Override
526623 public String name () {
527- return "element " ;
624+ return containerParam . name () + "[] " ;
528625 }
529626
530627 @ Override
0 commit comments