Skip to content

Commit 48ecb86

Browse files
authored
Merge pull request #42439 from mkouba/issue-42411
Qute template records: fix the way the canonical constructor is found
2 parents 7145415 + c05d0b1 commit 48ecb86

File tree

3 files changed

+93
-71
lines changed

3 files changed

+93
-71
lines changed

extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -434,9 +434,9 @@ && isNotLocatedByCustomTemplateLocator(locatorPatternsBuildItem.getLocationPatte
434434
if (!recordClass.isRecord()) {
435435
continue;
436436
}
437-
Type[] componentTypes = recordClass.recordComponents().stream().map(RecordComponentInfo::type)
438-
.toArray(Type[]::new);
439-
MethodInfo canonicalConstructor = recordClass.method(MethodDescriptor.INIT, componentTypes);
437+
MethodInfo canonicalConstructor = recordClass.method(MethodDescriptor.INIT,
438+
recordClass.unsortedRecordComponents().stream().map(RecordComponentInfo::type)
439+
.toArray(Type[]::new));
440440

441441
AnnotationInstance checkedTemplateAnnotation = recordClass.declaredAnnotation(Names.CHECKED_TEMPLATE);
442442
String fragmentId = getCheckedFragmentId(recordClass, checkedTemplateAnnotation);
@@ -509,7 +509,8 @@ && isNotLocatedByCustomTemplateLocator(locatorPatternsBuildItem.getLocationPatte
509509
requireTypeSafeExpressions != null ? requireTypeSafeExpressions.asBoolean() : true));
510510
transformers.produce(new BytecodeTransformerBuildItem(recordClass.name().toString(),
511511
new TemplateRecordEnhancer(recordInterface, recordClass, templatePath, fragmentId,
512-
canonicalConstructor.parameters(), adaptors.get(recordInterfaceName))));
512+
canonicalConstructor.descriptor(), canonicalConstructor.parameters(),
513+
adaptors.get(recordInterfaceName))));
513514
}
514515
}
515516

extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/TemplateRecordEnhancer.java

Lines changed: 63 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,17 @@ class TemplateRecordEnhancer implements BiFunction<String, ClassVisitor, ClassVi
3434
private final String recordClassName;
3535
private final String templateId;
3636
private final String fragmentId;
37+
private final String canonicalConstructorDescriptor;
3738
private final List<MethodParameterInfo> parameters;
3839
private final CheckedTemplateAdapter adapter;
3940

4041
TemplateRecordEnhancer(ClassInfo interfaceToImplement, ClassInfo recordClass, String templateId, String fragmentId,
41-
List<MethodParameterInfo> parameters, CheckedTemplateAdapter adapter) {
42+
String canonicalConstructorDescriptor, List<MethodParameterInfo> parameters, CheckedTemplateAdapter adapter) {
4243
this.interfaceToImplement = interfaceToImplement;
4344
this.recordClassName = recordClass.name().toString();
4445
this.templateId = templateId;
4546
this.fragmentId = fragmentId;
47+
this.canonicalConstructorDescriptor = canonicalConstructorDescriptor;
4648
this.parameters = parameters;
4749
this.adapter = adapter;
4850
}
@@ -61,7 +63,7 @@ public TemplateRecordClassVisitor(ClassVisitor outputClassVisitor) {
6163
@Override
6264
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
6365
MethodVisitor ret = super.visitMethod(access, name, descriptor, signature, exceptions);
64-
if (name.equals(MethodDescriptor.INIT)) {
66+
if (name.equals(MethodDescriptor.INIT) && descriptor.equals(canonicalConstructorDescriptor)) {
6567
return new TemplateRecordConstructorVisitor(ret);
6668
}
6769
return ret;
@@ -126,74 +128,69 @@ public TemplateRecordConstructorVisitor(MethodVisitor outputVisitor) {
126128

127129
@Override
128130
public void visitInsn(int opcode) {
129-
if (opcode != Opcodes.RETURN) {
130-
super.visitInsn(opcode);
131-
}
132-
}
131+
if (opcode == Opcodes.RETURN) {
132+
visitVarInsn(Opcodes.ALOAD, 0);
133+
134+
MethodDescriptor arcContainer = MethodDescriptor.ofMethod(Arc.class, "container", ArcContainer.class);
135+
MethodDescriptor arcContainerInstance = MethodDescriptor.ofMethod(ArcContainer.class, "instance",
136+
InstanceHandle.class, Class.class, Annotation[].class);
137+
MethodDescriptor instanceHandleGet = MethodDescriptor.ofMethod(InstanceHandle.class, "get", Object.class);
138+
MethodDescriptor templateProducerGetInjectableTemplate = MethodDescriptor.ofMethod(TemplateProducer.class,
139+
"getInjectableTemplate", Template.class, String.class);
140+
MethodDescriptor templateInstance = MethodDescriptor.ofMethod(Template.class, "instance",
141+
TemplateInstance.class);
142+
MethodDescriptor templateGetFragment = MethodDescriptor.ofMethod(Template.class, "getFragment",
143+
Template.Fragment.class, String.class);
144+
MethodDescriptor templateInstanceData = MethodDescriptor.ofMethod(TemplateInstance.class, "data",
145+
TemplateInstance.class, String.class, Object.class);
146+
147+
// Template template = Arc.container().instance(TemplateProducer.class).get().getInjectableTemplate("HelloResource/typedTemplate");
148+
visitMethodInsn(Opcodes.INVOKESTATIC, arcContainer.getDeclaringClass(), arcContainer.getName(),
149+
arcContainer.getDescriptor(),
150+
false);
151+
visitLdcInsn(org.objectweb.asm.Type.getType(TemplateProducer.class));
152+
visitLdcInsn(0);
153+
visitTypeInsn(Opcodes.ANEWARRAY, toInternalClassName(Annotation.class));
154+
invokeInterface(this, arcContainerInstance);
155+
invokeInterface(this, instanceHandleGet);
156+
visitTypeInsn(Opcodes.CHECKCAST, toInternalClassName(TemplateProducer.class));
157+
visitLdcInsn(templateId);
158+
visitMethodInsn(Opcodes.INVOKEVIRTUAL, templateProducerGetInjectableTemplate.getDeclaringClass(),
159+
templateProducerGetInjectableTemplate.getName(),
160+
templateProducerGetInjectableTemplate.getDescriptor(), false);
161+
if (fragmentId != null) {
162+
// template = template.getFragment(id);
163+
visitLdcInsn(fragmentId);
164+
invokeInterface(this, templateGetFragment);
165+
}
166+
// templateInstance = template.instance();
167+
invokeInterface(this, templateInstance);
133168

134-
@Override
135-
public void visitEnd() {
136-
visitCode();
137-
visitVarInsn(Opcodes.ALOAD, 0);
138-
139-
MethodDescriptor arcContainer = MethodDescriptor.ofMethod(Arc.class, "container", ArcContainer.class);
140-
MethodDescriptor arcContainerInstance = MethodDescriptor.ofMethod(ArcContainer.class, "instance",
141-
InstanceHandle.class, Class.class, Annotation[].class);
142-
MethodDescriptor instanceHandleGet = MethodDescriptor.ofMethod(InstanceHandle.class, "get", Object.class);
143-
MethodDescriptor templateProducerGetInjectableTemplate = MethodDescriptor.ofMethod(TemplateProducer.class,
144-
"getInjectableTemplate", Template.class, String.class);
145-
MethodDescriptor templateInstance = MethodDescriptor.ofMethod(Template.class, "instance", TemplateInstance.class);
146-
MethodDescriptor templateGetFragment = MethodDescriptor.ofMethod(Template.class, "getFragment",
147-
Template.Fragment.class, String.class);
148-
MethodDescriptor templateInstanceData = MethodDescriptor.ofMethod(TemplateInstance.class, "data",
149-
TemplateInstance.class, String.class, Object.class);
150-
151-
// Template template = Arc.container().instance(TemplateProducer.class).get().getInjectableTemplate("HelloResource/typedTemplate");
152-
visitMethodInsn(Opcodes.INVOKESTATIC, arcContainer.getDeclaringClass(), arcContainer.getName(),
153-
arcContainer.getDescriptor(),
154-
false);
155-
visitLdcInsn(org.objectweb.asm.Type.getType(TemplateProducer.class));
156-
visitLdcInsn(0);
157-
visitTypeInsn(Opcodes.ANEWARRAY, toInternalClassName(Annotation.class));
158-
invokeInterface(this, arcContainerInstance);
159-
invokeInterface(this, instanceHandleGet);
160-
visitTypeInsn(Opcodes.CHECKCAST, toInternalClassName(TemplateProducer.class));
161-
visitLdcInsn(templateId);
162-
visitMethodInsn(Opcodes.INVOKEVIRTUAL, templateProducerGetInjectableTemplate.getDeclaringClass(),
163-
templateProducerGetInjectableTemplate.getName(),
164-
templateProducerGetInjectableTemplate.getDescriptor(), false);
165-
if (fragmentId != null) {
166-
// template = template.getFragment(id);
167-
visitLdcInsn(fragmentId);
168-
invokeInterface(this, templateGetFragment);
169-
}
170-
// templateInstance = template.instance();
171-
invokeInterface(this, templateInstance);
169+
if (adapter != null) {
170+
adapter.convertTemplateInstance(this);
171+
templateInstanceData = MethodDescriptor.ofMethod(adapter.templateInstanceBinaryName(), "data",
172+
adapter.templateInstanceBinaryName(), String.class, Object.class);
173+
}
172174

173-
if (adapter != null) {
174-
adapter.convertTemplateInstance(this);
175-
templateInstanceData = MethodDescriptor.ofMethod(adapter.templateInstanceBinaryName(), "data",
176-
adapter.templateInstanceBinaryName(), String.class, Object.class);
177-
}
175+
int slot = 1;
176+
for (int i = 0; i < parameters.size(); i++) {
177+
// instance = instance.data("name", value);
178+
Type paramType = parameters.get(i).type();
179+
visitLdcInsn(parameters.get(i).name());
180+
visitVarInsn(AsmUtil.getLoadOpcode(paramType), slot);
181+
AsmUtil.boxIfRequired(this, paramType);
182+
invokeInterface(this, templateInstanceData);
183+
slot += AsmUtil.getParameterSize(paramType);
184+
}
178185

179-
int slot = 1;
180-
for (int i = 0; i < parameters.size(); i++) {
181-
// instance = instance.data("name", value);
182-
Type paramType = parameters.get(i).type();
183-
visitLdcInsn(parameters.get(i).name());
184-
visitVarInsn(AsmUtil.getLoadOpcode(paramType), slot);
185-
AsmUtil.boxIfRequired(this, paramType);
186-
invokeInterface(this, templateInstanceData);
187-
slot += AsmUtil.getParameterSize(paramType);
186+
FieldDescriptor quteTemplateInstanceDescriptor = FieldDescriptor.of(recordClassName, "qute$templateInstance",
187+
interfaceToImplement.name().toString());
188+
visitFieldInsn(Opcodes.PUTFIELD, quteTemplateInstanceDescriptor.getDeclaringClass(),
189+
quteTemplateInstanceDescriptor.getName(), quteTemplateInstanceDescriptor.getType());
190+
super.visitInsn(Opcodes.RETURN);
191+
} else {
192+
super.visitInsn(opcode);
188193
}
189-
190-
FieldDescriptor quteTemplateInstanceDescriptor = FieldDescriptor.of(recordClassName, "qute$templateInstance",
191-
interfaceToImplement.name().toString());
192-
visitFieldInsn(Opcodes.PUTFIELD, quteTemplateInstanceDescriptor.getDeclaringClass(),
193-
quteTemplateInstanceDescriptor.getName(), quteTemplateInstanceDescriptor.getType());
194-
super.visitInsn(Opcodes.RETURN);
195-
super.visitEnd();
196-
visitMaxs(-1, -1);
197194
}
198195

199196
}

extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/records/TemplateRecordTest.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static org.junit.jupiter.api.Assertions.assertFalse;
55
import static org.junit.jupiter.api.Assertions.assertNotNull;
66
import static org.junit.jupiter.api.Assertions.assertNull;
7+
import static org.junit.jupiter.api.Assertions.assertThrows;
78
import static org.junit.jupiter.api.Assertions.assertTrue;
89
import static org.junit.jupiter.api.Assertions.fail;
910

@@ -34,7 +35,9 @@ public class TemplateRecordTest {
3435
.addAsResource(new StringAsset("Hello {name}!"),
3536
"templates/hello_world.txt")
3637
.addAsResource(new StringAsset("Hello {#fragment id=name}{name}{/fragment}!"),
37-
"templates/hello.txt"));
38+
"templates/hello.txt")
39+
.addAsResource(new StringAsset("{alpha}:{bravo}:{charlie}"),
40+
"templates/TemplateRecordTest/multiParams.txt"));
3841

3942
@Inject
4043
Engine engine;
@@ -69,6 +72,9 @@ public void testTemplateRecords() throws InterruptedException, ExecutionExceptio
6972

7073
assertEquals("Hello Lu!", new helloWorld("Lu").render());
7174
assertEquals("Lu", new hello$name("Lu").render());
75+
76+
assertEquals("15:true:foo", new multiParams(true, 15, "foo").render());
77+
assertThrows(IllegalArgumentException.class, () -> new multiParams(false, 50, null));
7278
}
7379

7480
record HelloInt(int val) implements TemplateInstance {
@@ -82,4 +88,22 @@ record helloWorld(String name) implements TemplateInstance {
8288
record hello$name(String name) implements TemplateInstance {
8389
}
8490

91+
record multiParams(boolean bravo, int alpha, String charlie) implements TemplateInstance {
92+
93+
public multiParams {
94+
if (alpha > 20) {
95+
throw new IllegalArgumentException();
96+
}
97+
}
98+
99+
public multiParams(String delta) {
100+
this(false, 1, delta);
101+
}
102+
103+
public multiParams(int alpha, boolean bravo, String charlie) {
104+
this(bravo, alpha, charlie);
105+
throw new IllegalArgumentException("");
106+
}
107+
}
108+
85109
}

0 commit comments

Comments
 (0)