|
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