|
4 | 4 | import static io.quarkus.deployment.util.AsmUtil.unboxIfRequired; |
5 | 5 | import static io.quarkus.gizmo.Gizmo.ASM_API_VERSION; |
6 | 6 | import static io.quarkus.panache.common.deployment.PanacheConstants.DOTNAME_GENERATE_BRIDGE; |
7 | | -import static java.lang.String.format; |
8 | 7 | import static java.util.stream.Collectors.toList; |
9 | 8 | import static org.objectweb.asm.Opcodes.ACC_BRIDGE; |
10 | 9 | import static org.objectweb.asm.Opcodes.ARRAYLENGTH; |
|
25 | 24 | import java.util.ArrayList; |
26 | 25 | import java.util.Collections; |
27 | 26 | import java.util.HashMap; |
| 27 | +import java.util.HashSet; |
28 | 28 | import java.util.List; |
29 | 29 | import java.util.Map; |
| 30 | +import java.util.Set; |
30 | 31 | import java.util.StringJoiner; |
31 | | -import java.util.TreeMap; |
32 | 32 | import java.util.function.Function; |
33 | 33 |
|
34 | 34 | import org.jboss.jandex.AnnotationInstance; |
@@ -68,10 +68,11 @@ public class KotlinPanacheClassOperationGenerationVisitor extends ClassVisitor { |
68 | 68 | protected final Function<String, Type> argMapper; |
69 | 69 | protected final ClassInfo classInfo; |
70 | 70 | protected final ByteCodeType entityUpperBound; |
| 71 | + // These are type arguments to the Panache base entity/repo, not to the current or intermediate type |
71 | 72 | protected final Map<String, ByteCodeType> typeArguments = new HashMap<>(); |
72 | 73 | private final ByteCodeType baseType; |
73 | | - private final Map<String, MethodInfo> definedMethods = new TreeMap<>(); |
74 | | - |
| 74 | + private final Set<String> userMethods = new HashSet<>(); |
| 75 | + private final Set<String> baseTypeMethods = new HashSet<>(); |
75 | 76 | private final Map<String, String> erasures = new HashMap<>(); |
76 | 77 | private final IndexView indexView; |
77 | 78 | protected List<PanacheMethodCustomizer> methodCustomizers; |
@@ -101,10 +102,20 @@ public KotlinPanacheClassOperationGenerationVisitor(ClassVisitor outputClassVisi |
101 | 102 | ? byteCodeType.get() |
102 | 103 | : null; |
103 | 104 | }; |
| 105 | + loadBaseTypeMethods(); |
| 106 | + } |
104 | 107 |
|
105 | | - collectMethods(classInfo); |
106 | | - filterNonOverrides(); |
107 | | - |
| 108 | + /** |
| 109 | + * This loads the signatures of every base type method that requires a bridge |
| 110 | + */ |
| 111 | + private void loadBaseTypeMethods() { |
| 112 | + for (MethodInfo method : indexView.getClassByName(baseType.dotName()).methods()) { |
| 113 | + String descriptor = method.descriptor(type -> typeArguments.getOrDefault(type, OBJECT).get()); |
| 114 | + AnnotationInstance bridge = method.annotation(DOTNAME_GENERATE_BRIDGE); |
| 115 | + if (bridge != null) { |
| 116 | + baseTypeMethods.add(method.name() + "/" + descriptor); |
| 117 | + } |
| 118 | + } |
108 | 119 | } |
109 | 120 |
|
110 | 121 | public static List<ByteCodeType> recursivelyFindEntityTypeArguments(IndexView indexView, DotName clazz, |
@@ -217,27 +228,6 @@ private void checkCast(MethodVisitor mv, Type returnType, String operationReturn |
217 | 228 | } |
218 | 229 | } |
219 | 230 |
|
220 | | - private void collectMethods(ClassInfo classInfo) { |
221 | | - if (classInfo != null && !classInfo.name().equals(baseType.dotName())) { |
222 | | - classInfo.methods() |
223 | | - .forEach(method -> { |
224 | | - String descriptor = method.descriptor(m -> { |
225 | | - ByteCodeType byteCodeType = typeArguments.get(m); |
226 | | - return byteCodeType != null ? byteCodeType.get() : OBJECT.get(); |
227 | | - }); |
228 | | - MethodInfo prior = definedMethods.put(method.name() + descriptor, method); |
229 | | - if (prior != null && !isBridgeMethod(method)) { |
230 | | - throw new IllegalStateException(format("Should not run in to duplicate " + |
231 | | - "mappings: \n\t%s\n\t%s\n\t%s", method, descriptor, prior)); |
232 | | - } |
233 | | - }); |
234 | | - DotName superName = classInfo.superName(); |
235 | | - if (superName != null) { |
236 | | - collectMethods(indexView.getClassByName(superName)); |
237 | | - } |
238 | | - } |
239 | | - } |
240 | | - |
241 | 231 | private String desc(String name) { |
242 | 232 | String s = name.replace(".", "/"); |
243 | 233 | return s.startsWith("[") ? s : "L" + s + ";"; |
@@ -313,16 +303,6 @@ private Label endLabel() { |
313 | 303 | return labels.get(labels.size() - 1); |
314 | 304 | } |
315 | 305 |
|
316 | | - private void filterNonOverrides() { |
317 | | - new ArrayList<>(definedMethods.values()) |
318 | | - .forEach(method -> { |
319 | | - AnnotationInstance generateBridge = method.annotation(DOTNAME_GENERATE_BRIDGE); |
320 | | - if (generateBridge != null) { |
321 | | - definedMethods.remove(method.name() + method.descriptor()); |
322 | | - } |
323 | | - }); |
324 | | - } |
325 | | - |
326 | 306 | private void generate(MethodInfo method) { |
327 | 307 | // Note: we can't use SYNTHETIC here because otherwise Mockito will never mock these methods |
328 | 308 | MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, method.name(), |
@@ -460,10 +440,6 @@ private void invokeOperation(MethodVisitor mv, MethodInfo method) { |
460 | 440 | mv.visitInsn(AsmUtil.getReturnInstruction(returnType)); |
461 | 441 | } |
462 | 442 |
|
463 | | - private boolean isBridgeMethod(MethodInfo method) { |
464 | | - return (method.flags() & ACC_BRIDGE) != ACC_BRIDGE; |
465 | | - } |
466 | | - |
467 | 443 | private org.objectweb.asm.Type asmType(Type methodParameter) { |
468 | 444 | org.objectweb.asm.Type parameter; |
469 | 445 | if (methodParameter.kind() == Type.Kind.TYPE_VARIABLE) { |
@@ -531,38 +507,30 @@ public String toString() { |
531 | 507 | @Override |
532 | 508 | public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, |
533 | 509 | String[] exceptions) { |
534 | | - |
535 | | - MethodInfo methodInfo = definedMethods.entrySet().stream() |
536 | | - .filter(e -> e.getKey().equals(name + descriptor)) |
537 | | - .map(e -> e.getValue()) |
538 | | - .findFirst() |
539 | | - .orElse(null); |
540 | | - if (methodInfo != null && !methodInfo.hasAnnotation(DOTNAME_GENERATE_BRIDGE)) { |
541 | | - return super.visitMethod(access, name, descriptor, signature, exceptions); |
542 | | - } else if (name.contains("$")) { |
543 | | - //some agents such as jacoco add new methods, they generally have $ in the name |
544 | | - return super.visitMethod(access, name, descriptor, signature, exceptions); |
545 | | - } else if (name.equals(CTOR_METHOD_NAME) || name.equals(CLINIT_METHOD_NAME)) { |
546 | | - //Arc can add no-args constructors to support intercepted beans |
547 | | - // Logging with Panache can add a class initializer |
548 | | - return super.visitMethod(access, name, descriptor, signature, exceptions); |
| 510 | + // Kotlinc or something will add bridge methods for the base type methods, these are not user methods |
| 511 | + // so we filter them out since we add them back in visitEnd() |
| 512 | + String sig = name + "/" + descriptor; |
| 513 | + if ((access & Opcodes.ACC_BRIDGE) != 0 |
| 514 | + && baseTypeMethods.contains(sig)) { |
| 515 | + return null; |
549 | 516 | } |
550 | | - return null; |
| 517 | + userMethods.add(sig); |
| 518 | + return super.visitMethod(access, name, descriptor, signature, exceptions); |
551 | 519 | } |
552 | 520 |
|
553 | 521 | @Override |
554 | 522 | public void visitEnd() { |
555 | 523 | for (MethodInfo method : indexView.getClassByName(baseType.dotName()).methods()) { |
556 | 524 | String descriptor = method.descriptor(type -> typeArguments.getOrDefault(type, OBJECT).get()); |
557 | 525 | AnnotationInstance bridge = method.annotation(DOTNAME_GENERATE_BRIDGE); |
558 | | - if (!definedMethods.containsKey(method.name() + descriptor) && bridge != null) { |
| 526 | + if (!userMethods.contains(method.name() + "/" + descriptor) && bridge != null) { |
559 | 527 | generate(method); |
560 | 528 | if (needsJvmBridge(method)) { |
561 | 529 | String bridgeDescriptor = bridgeMethodDescriptor(method, type -> { |
562 | 530 | ByteCodeType mapped = typeArguments.get(type); |
563 | 531 | return mapped != null ? mapped.get() : null; |
564 | 532 | }); |
565 | | - if (!definedMethods.containsKey(method.name() + bridgeDescriptor)) { |
| 533 | + if (!userMethods.contains(method.name() + "/" + bridgeDescriptor)) { |
566 | 534 | generateBridge(method, bridgeDescriptor); |
567 | 535 | } |
568 | 536 |
|
|
0 commit comments