diff --git a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/staticmethods/InterceptedStaticMethodsProcessor.java b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/staticmethods/InterceptedStaticMethodsProcessor.java index 0e20e735c60e2..94e436db46bab 100644 --- a/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/staticmethods/InterceptedStaticMethodsProcessor.java +++ b/extensions/arc/deployment/src/main/java/io/quarkus/arc/deployment/staticmethods/InterceptedStaticMethodsProcessor.java @@ -33,8 +33,6 @@ import org.jboss.jandex.Type; import org.jboss.logging.Logger; import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; import io.quarkus.arc.InjectableInterceptor; import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem; @@ -61,13 +59,12 @@ import io.quarkus.deployment.builditem.BytecodeTransformerBuildItem; import io.quarkus.deployment.builditem.GeneratedClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveMethodBuildItem; -import io.quarkus.deployment.util.AsmUtil; import io.quarkus.gizmo.BytecodeCreator; import io.quarkus.gizmo.ClassCreator; import io.quarkus.gizmo.ClassOutput; +import io.quarkus.gizmo.ClassTransformer; import io.quarkus.gizmo.DescriptorUtils; import io.quarkus.gizmo.FunctionCreator; -import io.quarkus.gizmo.Gizmo; import io.quarkus.gizmo.MethodCreator; import io.quarkus.gizmo.MethodDescriptor; import io.quarkus.gizmo.ResultHandle; @@ -445,85 +442,33 @@ public InterceptedStaticMethodsEnhancer(String initializerClassName, List methods; - - public InterceptedStaticMethodsClassVisitor(String initializerClassName, ClassVisitor outputClassVisitor, - List methods) { - super(Gizmo.ASM_API_VERSION, outputClassVisitor); - this.methods = methods; - this.initializerClassName = initializerClassName; - } - - @Override - public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { - InterceptedStaticMethodBuildItem method = findMatchingMethod(access, name, descriptor); - if (method != null) { - MethodVisitor copy = super.visitMethod(access, - name + ORIGINAL_METHOD_COPY_SUFFIX, - descriptor, - signature, - exceptions); - MethodVisitor superVisitor = super.visitMethod(access, name, descriptor, signature, exceptions); - return new InterceptedStaticMethodsMethodVisitor(superVisitor, copy, initializerClassName, method); - } else { - return super.visitMethod(access, name, descriptor, signature, exceptions); - } - } - - private InterceptedStaticMethodBuildItem findMatchingMethod(int access, String name, String descriptor) { - if (Modifier.isStatic(access)) { - for (InterceptedStaticMethodBuildItem method : methods) { - if (method.getMethod().name().equals(name) - && MethodDescriptor.of(method.getMethod()).getDescriptor().equals(descriptor)) { - return method; - } + ClassTransformer transformer = new ClassTransformer(className); + for (InterceptedStaticMethodBuildItem interceptedStaticMethod : methods) { + MethodInfo interceptedMethod = interceptedStaticMethod.getMethod(); + MethodDescriptor originalDescriptor = MethodDescriptor.of(interceptedMethod); + // Rename the intercepted method + transformer.modifyMethod(originalDescriptor) + .rename(interceptedMethod.name() + ORIGINAL_METHOD_COPY_SUFFIX); + + // Add the intercepted method again - invoke the initializer in the body, e.g. Foo_InterceptorInitializer.hash("ping") + MethodCreator newMethod = transformer.addMethod(originalDescriptor) + .setModifiers(interceptedMethod.flags()) + .setSignature(interceptedMethod.genericSignatureIfRequired()); + for (Type exceptionType : interceptedMethod.exceptions()) { + newMethod.addException(exceptionType.name().toString()); } + ResultHandle[] args = new ResultHandle[interceptedMethod.parametersCount()]; + for (int i = 0; i < interceptedMethod.parametersCount(); ++i) { + args[i] = newMethod.getMethodParam(i); + } + ResultHandle ret = newMethod.invokeStaticMethod(MethodDescriptor.ofMethod(initializerClassName, + interceptedStaticMethod.getForwardingMethodName(), + interceptedMethod.returnType().descriptor(), + interceptedMethod.parameterTypes().stream().map(Type::descriptor).toArray()), + args); + newMethod.returnValue(ret); } - return null; - } - - } - - static class InterceptedStaticMethodsMethodVisitor extends MethodVisitor { - - private final String initializerClassName; - private final InterceptedStaticMethodBuildItem interceptedStaticMethod; - private final MethodVisitor superVisitor; - - public InterceptedStaticMethodsMethodVisitor(MethodVisitor superVisitor, MethodVisitor copyVisitor, - String initializerClassName, InterceptedStaticMethodBuildItem interceptedStaticMethod) { - super(Gizmo.ASM_API_VERSION, copyVisitor); - this.superVisitor = superVisitor; - this.initializerClassName = initializerClassName; - this.interceptedStaticMethod = interceptedStaticMethod; - } - - @Override - public void visitEnd() { - // Invoke the initializer, i.e. Foo_InterceptorInitializer.hash("ping") - MethodDescriptor descriptor = MethodDescriptor.of(interceptedStaticMethod.getMethod()); - int paramSlot = 0; - for (Type paramType : interceptedStaticMethod.getMethod().parameterTypes()) { - superVisitor.visitIntInsn(AsmUtil.getLoadOpcode(paramType), paramSlot); - paramSlot += AsmUtil.getParameterSize(paramType); - } - superVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, - initializerClassName.replace('.', '/'), interceptedStaticMethod.getForwardingMethodName(), - descriptor.getDescriptor().toString(), - false); - superVisitor.visitInsn(AsmUtil.getReturnInstruction(interceptedStaticMethod.getMethod().returnType())); - superVisitor.visitMaxs(0, 0); - superVisitor.visitEnd(); - - super.visitEnd(); + return transformer.applyTo(outputClassVisitor); } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java index 7a253f3df8751..d60b398a54029 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Beans.java @@ -34,12 +34,13 @@ import org.jboss.jandex.Type.Kind; import org.jboss.logging.Logger; import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.FieldVisitor; -import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import io.quarkus.arc.processor.InjectionPointInfo.TypeAndQualifiers; -import io.quarkus.gizmo.Gizmo; +import io.quarkus.gizmo.ClassTransformer; +import io.quarkus.gizmo.FieldDescriptor; +import io.quarkus.gizmo.MethodCreator; +import io.quarkus.gizmo.MethodDescriptor; public final class Beans { @@ -696,7 +697,7 @@ static void validateInterceptorDecorator(BeanInfo bean, List errors, if (injection.isField() && Modifier.isPrivate(injection.getTarget().asField().flags())) { bytecodeTransformerConsumer .accept(new BytecodeTransformer(bean.getTarget().get().asClass().name().toString(), - new PrivateInjectedFieldTransformFunction(injection.getTarget().asField().name()))); + new PrivateInjectedFieldTransformFunction(injection.getTarget().asField()))); } } } @@ -791,7 +792,7 @@ static void validateBean(BeanInfo bean, List errors, Consumer { - public PrivateInjectedFieldTransformFunction(String fieldName) { - this.fieldName = fieldName; + public PrivateInjectedFieldTransformFunction(FieldInfo field) { + this.field = field; } - private String fieldName; + private FieldInfo field; @Override public ClassVisitor apply(String className, ClassVisitor classVisitor) { - return new ClassVisitor(Gizmo.ASM_API_VERSION, classVisitor) { - - @Override - public FieldVisitor visitField( - int access, - String name, - String descriptor, - String signature, - Object value) { - if (name.equals(fieldName)) { - access = access & (~Opcodes.ACC_PRIVATE); - LOGGER.debugf( - "Changed visibility of an injected private field to package-private. Field name: %s in class: %s", - name, className); - } - return super.visitField(access, name, descriptor, signature, value); - } - }; + ClassTransformer transformer = new ClassTransformer(className); + transformer.modifyField(FieldDescriptor.of(field)).removeModifiers(Opcodes.ACC_PRIVATE); + LOGGER.debugf("Changed visibility of an injected private field to package-private. Field name: %s in class: %s", + field.name(), className); + return transformer.applyTo(classVisitor); } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java index 8421c9aed59ee..9edfed2862acb 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/ClientProxyGenerator.java @@ -28,6 +28,7 @@ import io.quarkus.arc.InjectableContext; import io.quarkus.arc.impl.Mockable; import io.quarkus.arc.processor.BeanGenerator.ProviderType; +import io.quarkus.arc.processor.Methods.MethodKey; import io.quarkus.arc.processor.ResourceOutput.Resource; import io.quarkus.arc.processor.ResourceOutput.Resource.SpecialType; import io.quarkus.gizmo.BytecodeCreator; @@ -332,18 +333,18 @@ Collection getDelegatingMethods(BeanInfo bean, Consumer> methodsFromWhichToRemoveFinal = new HashMap<>(); + Map> methodsFromWhichToRemoveFinal = new HashMap<>(); ClassInfo classInfo = bean.getTarget().get().asClass(); addDelegatesAndTrasformIfNecessary(bytecodeTransformerConsumer, transformUnproxyableClasses, methods, index, methodsFromWhichToRemoveFinal, classInfo); } else if (bean.isProducerMethod()) { - Map> methodsFromWhichToRemoveFinal = new HashMap<>(); + Map> methodsFromWhichToRemoveFinal = new HashMap<>(); MethodInfo producerMethod = bean.getTarget().get().asMethod(); ClassInfo returnTypeClass = getClassByName(index, producerMethod.returnType()); addDelegatesAndTrasformIfNecessary(bytecodeTransformerConsumer, transformUnproxyableClasses, methods, index, methodsFromWhichToRemoveFinal, returnTypeClass); } else if (bean.isProducerField()) { - Map> methodsFromWhichToRemoveFinal = new HashMap<>(); + Map> methodsFromWhichToRemoveFinal = new HashMap<>(); FieldInfo producerField = bean.getTarget().get().asField(); ClassInfo fieldClass = getClassByName(index, producerField.type()); addDelegatesAndTrasformIfNecessary(bytecodeTransformerConsumer, transformUnproxyableClasses, methods, index, @@ -359,15 +360,15 @@ Collection getDelegatingMethods(BeanInfo bean, Consumer bytecodeTransformerConsumer, boolean transformUnproxyableClasses, Map methods, IndexView index, - Map> methodsFromWhichToRemoveFinal, + Map> methodsFromWhichToRemoveFinal, ClassInfo fieldClass) { Methods.addDelegatingMethods(index, fieldClass, methods, methodsFromWhichToRemoveFinal, transformUnproxyableClasses); if (!methodsFromWhichToRemoveFinal.isEmpty()) { - for (Map.Entry> entry : methodsFromWhichToRemoveFinal.entrySet()) { + for (Map.Entry> entry : methodsFromWhichToRemoveFinal.entrySet()) { String className = entry.getKey(); bytecodeTransformerConsumer.accept(new BytecodeTransformer(className, - new Methods.RemoveFinalFromMethod(className, entry.getValue()))); + new Methods.RemoveFinalFromMethod(entry.getValue()))); } } } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java index a5290ed4c1ce9..e807eac73d317 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Methods.java @@ -28,11 +28,9 @@ import org.jboss.jandex.TypeVariable; import org.jboss.logging.Logger; import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; -import io.quarkus.gizmo.DescriptorUtils; -import io.quarkus.gizmo.Gizmo; +import io.quarkus.gizmo.ClassTransformer; import io.quarkus.gizmo.MethodDescriptor; /** @@ -63,7 +61,7 @@ static boolean isBridge(MethodInfo method) { } static void addDelegatingMethods(IndexView index, ClassInfo classInfo, Map methods, - Map> methodsFromWhichToRemoveFinal, boolean transformUnproxyableClasses) { + Map> methodsFromWhichToRemoveFinal, boolean transformUnproxyableClasses) { if (classInfo != null) { // First methods declared on the class for (MethodInfo method : classInfo.methods()) { @@ -104,7 +102,7 @@ static void addDelegatingMethods(IndexView index, ClassInfo classInfo, Map> methodsFromWhichToRemoveFinal) { + Map> methodsFromWhichToRemoveFinal) { if (Modifier.isStatic(method.flags()) || Modifier.isPrivate(method.flags())) { return true; } @@ -120,7 +118,7 @@ private static boolean skipForClientProxy(MethodInfo method, boolean transformUn if (!className.startsWith("java.")) { if (transformUnproxyableClasses && (methodsFromWhichToRemoveFinal != null)) { methodsFromWhichToRemoveFinal.computeIfAbsent(className, (k) -> new HashSet<>()) - .add(NameAndDescriptor.fromMethodInfo(method)); + .add(new MethodKey(method)); return false; } // in case we want to transform classes but are unable to, we log a WARN @@ -184,7 +182,7 @@ private static Set addInterceptedMethodCandidates(BeanDeployment bea boolean transformUnproxyableClasses, SubclassSkipPredicate skipPredicate, boolean ignoreMethodLevelBindings, Set noClassInterceptorsMethods, boolean targetHasAroundInvokes) { - Set methodsFromWhichToRemoveFinal = new HashSet<>(); + Set methodsFromWhichToRemoveFinal = new HashSet<>(); Set finalMethodsFoundAndNotChanged = new HashSet<>(); skipPredicate.startProcessing(classInfo, originalClassInfo); @@ -204,7 +202,7 @@ private static Set addInterceptedMethodCandidates(BeanDeployment bea boolean addToCandidates = true; if (Modifier.isFinal(method.flags()) && possiblyIntercepted) { if (transformUnproxyableClasses && !isNoninterceptableKotlinMethod(method)) { - methodsFromWhichToRemoveFinal.add(NameAndDescriptor.fromMethodInfo(method)); + methodsFromWhichToRemoveFinal.add(new MethodKey(method)); } else { addToCandidates = false; finalMethodsFoundAndNotChanged.add(method); @@ -219,7 +217,7 @@ private static Set addInterceptedMethodCandidates(BeanDeployment bea if (!methodsFromWhichToRemoveFinal.isEmpty()) { bytecodeTransformerConsumer.accept( new BytecodeTransformer(classInfo.name().toString(), - new RemoveFinalFromMethod(classInfo.name().toString(), methodsFromWhichToRemoveFinal))); + new RemoveFinalFromMethod(methodsFromWhichToRemoveFinal))); } if (!classInfo.superName().equals(DotNames.OBJECT)) { @@ -331,43 +329,6 @@ static Set mergeMethodAndClassLevelBindings(Collection { - private final String classToTransform; - private final Set methodsFromWhichToRemoveFinal; + private final Set methodsFromWhichToRemoveFinal; - public RemoveFinalFromMethod(String classToTransform, Set methodsFromWhichToRemoveFinal) { - this.classToTransform = classToTransform; + public RemoveFinalFromMethod(Set methodsFromWhichToRemoveFinal) { this.methodsFromWhichToRemoveFinal = methodsFromWhichToRemoveFinal; } @Override - public ClassVisitor apply(String s, ClassVisitor classVisitor) { - return new ClassVisitor(Gizmo.ASM_API_VERSION, classVisitor) { - @Override - public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, - String[] exceptions) { - if (methodsFromWhichToRemoveFinal.contains(new NameAndDescriptor(name, descriptor))) { - access = access & (~Opcodes.ACC_FINAL); - LOGGER.debug("final modifier removed from method " + name + " of class " + classToTransform); - } - return super.visitMethod(access, name, descriptor, signature, exceptions); - } - }; + public ClassVisitor apply(String className, ClassVisitor classVisitor) { + ClassTransformer transformer = new ClassTransformer(className); + for (MethodKey key : methodsFromWhichToRemoveFinal) { + LOGGER.debug("Final modifier removed from method " + key.name + " of class " + className); + transformer.modifyMethod(MethodDescriptor.of(key.method)).removeModifiers(Opcodes.ACC_FINAL); + } + return transformer.applyTo(classVisitor); } }