mirror of
https://github.com/jlengrand/quarkus.git
synced 2026-03-10 08:41:22 +00:00
Qute - ExtensionMethodGenerator now handles ClassCastException correctly
- also fix parent context resolution for multiple nested levels - resolves #7144
This commit is contained in:
@@ -69,7 +69,7 @@ class EvaluatorImpl implements Evaluator {
|
||||
return resolve(new EvalContextImpl(tryParent, ref, parts.next(), resolutionContext), resolvers.iterator())
|
||||
.thenCompose(r -> {
|
||||
if (parts.hasNext()) {
|
||||
return resolveReference(false, r, parts, resolutionContext);
|
||||
return resolveReference(tryParent, r, parts, resolutionContext);
|
||||
} else {
|
||||
return CompletableFuture.completedFuture(r);
|
||||
}
|
||||
@@ -82,7 +82,7 @@ class EvaluatorImpl implements Evaluator {
|
||||
if (evalContext.tryParent && parent != null) {
|
||||
// Continue with parent context
|
||||
return resolve(
|
||||
new EvalContextImpl(false, parent.getData(), evalContext.name, parent),
|
||||
new EvalContextImpl(true, parent.getData(), evalContext.name, parent),
|
||||
this.resolvers.iterator());
|
||||
}
|
||||
LOGGER.tracef("Unable to resolve %s", evalContext);
|
||||
|
||||
@@ -47,7 +47,9 @@ public class LoopSectionHelper implements SectionHelper {
|
||||
} else if (it instanceof Integer) {
|
||||
iterator = IntStream.rangeClosed(1, (Integer) it).iterator();
|
||||
} else {
|
||||
throw new IllegalStateException("Cannot iterate over: " + it);
|
||||
throw new IllegalStateException(
|
||||
String.format("Cannot iterate over [%s] resolved for [%s] in template %s on line %s", it,
|
||||
iterable.toOriginalString(), iterable.origin.getTemplateId(), iterable.origin.getLine()));
|
||||
}
|
||||
int idx = 0;
|
||||
while (iterator.hasNext()) {
|
||||
|
||||
@@ -68,8 +68,8 @@ public class LoopSectionTest {
|
||||
|
||||
@Test
|
||||
public void testNestedLoops() {
|
||||
List<String> data = new ArrayList<>();
|
||||
data.add("alpha");
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add("alpha");
|
||||
|
||||
Engine engine = Engine.builder()
|
||||
.addSectionHelper(new LoopSectionHelper.Factory())
|
||||
@@ -92,14 +92,14 @@ public class LoopSectionTest {
|
||||
})
|
||||
.build();
|
||||
|
||||
String template = "{#for name in this}"
|
||||
String template = "{#for name in list}"
|
||||
+ "{count}.{name}: {#for char in name.chars}"
|
||||
+ "{name} - char at {index} = {char}{#if hasNext},{/}"
|
||||
+ "{name} {global} char at {index} = {char}{#if hasNext},{/}"
|
||||
+ "{/}{/}";
|
||||
|
||||
assertEquals(
|
||||
"1.alpha: alpha - char at 0 = a,alpha - char at 1 = l,alpha - char at 2 = p,alpha - char at 3 = h,alpha - char at 4 = a",
|
||||
engine.parse(template).render(data));
|
||||
engine.parse(template).data("global", "-").data("list", list).render());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -8,12 +8,14 @@ import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
|
||||
import io.quarkus.gizmo.AssignableResultHandle;
|
||||
import io.quarkus.gizmo.BranchResult;
|
||||
import io.quarkus.gizmo.BytecodeCreator;
|
||||
import io.quarkus.gizmo.CatchBlockCreator;
|
||||
import io.quarkus.gizmo.ClassCreator;
|
||||
import io.quarkus.gizmo.ClassOutput;
|
||||
import io.quarkus.gizmo.FunctionCreator;
|
||||
import io.quarkus.gizmo.MethodCreator;
|
||||
import io.quarkus.gizmo.MethodDescriptor;
|
||||
import io.quarkus.gizmo.ResultHandle;
|
||||
import io.quarkus.gizmo.TryBlock;
|
||||
import io.quarkus.qute.EvalContext;
|
||||
import io.quarkus.qute.TemplateExtension;
|
||||
import io.quarkus.qute.ValueResolver;
|
||||
@@ -196,12 +198,20 @@ public class ExtensionMethodGenerator {
|
||||
args[i + shift] = success.invokeVirtualMethod(Descriptors.COMPLETABLE_FUTURE_GET, paramResult);
|
||||
}
|
||||
|
||||
ResultHandle invokeRet = success
|
||||
// try
|
||||
TryBlock tryCatch = success.tryBlock();
|
||||
// catch (Throwable e)
|
||||
CatchBlockCreator exception = tryCatch.addCatch(Throwable.class);
|
||||
// CompletableFuture.completeExceptionally(Throwable)
|
||||
exception.invokeVirtualMethod(Descriptors.COMPLETABLE_FUTURE_COMPLETE_EXCEPTIONALLY, whenRet,
|
||||
exception.getCaughtException());
|
||||
|
||||
ResultHandle invokeRet = tryCatch
|
||||
.invokeStaticMethod(MethodDescriptor.ofMethod(declaringClass.name().toString(), method.name(),
|
||||
method.returnType().name().toString(),
|
||||
method.parameters().stream().map(p -> p.name().toString()).collect(Collectors.toList()).toArray()),
|
||||
args);
|
||||
success.invokeVirtualMethod(Descriptors.COMPLETABLE_FUTURE_COMPLETE, whenRet, invokeRet);
|
||||
tryCatch.invokeVirtualMethod(Descriptors.COMPLETABLE_FUTURE_COMPLETE, whenRet, invokeRet);
|
||||
|
||||
BytecodeCreator failure = throwableIsNull.falseBranch();
|
||||
failure.invokeVirtualMethod(Descriptors.COMPLETABLE_FUTURE_COMPLETE_EXCEPTIONALLY, whenRet,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.quarkus.qute.generator;
|
||||
|
||||
import io.quarkus.qute.TemplateData;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -50,4 +51,8 @@ public class MyService {
|
||||
return CompletableFuture.completedFuture(param);
|
||||
}
|
||||
|
||||
public static List<String> getDummy(MyService service, int limit, String dummy) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import io.quarkus.qute.Engine;
|
||||
import io.quarkus.qute.EngineBuilder;
|
||||
import io.quarkus.qute.EvalContext;
|
||||
import io.quarkus.qute.Expression;
|
||||
import io.quarkus.qute.ImmutableList;
|
||||
@@ -11,19 +12,29 @@ import io.quarkus.qute.ValueResolver;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import org.jboss.jandex.ClassInfo;
|
||||
import org.jboss.jandex.DotName;
|
||||
import org.jboss.jandex.Index;
|
||||
import org.jboss.jandex.Indexer;
|
||||
import org.jboss.jandex.MethodInfo;
|
||||
import org.jboss.jandex.PrimitiveType;
|
||||
import org.jboss.jandex.Type;
|
||||
import org.jboss.jandex.Type.Kind;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SimpleGeneratorTest {
|
||||
|
||||
static Set<String> generatedTypes = new HashSet<>();
|
||||
|
||||
@BeforeAll
|
||||
public static void init() throws IOException {
|
||||
TestClassOutput classOutput = new TestClassOutput();
|
||||
@@ -31,11 +42,20 @@ public class SimpleGeneratorTest {
|
||||
CompletionStage.class,
|
||||
List.class);
|
||||
ValueResolverGenerator generator = new ValueResolverGenerator(index, classOutput, Collections.emptyMap());
|
||||
generator.generate(index.getClassByName(DotName.createSimple(MyService.class.getName())));
|
||||
ClassInfo myServiceClazz = index.getClassByName(DotName.createSimple(MyService.class.getName()));
|
||||
generator.generate(myServiceClazz);
|
||||
generator.generate(index.getClassByName(DotName.createSimple(PublicMyService.class.getName())));
|
||||
generator.generate(index.getClassByName(DotName.createSimple(MyItem.class.getName())));
|
||||
generator.generate(index.getClassByName(DotName.createSimple(String.class.getName())));
|
||||
generator.generate(index.getClassByName(DotName.createSimple(List.class.getName())));
|
||||
generatedTypes.addAll(generator.getGeneratedTypes());
|
||||
|
||||
ExtensionMethodGenerator extensionMethodGenerator = new ExtensionMethodGenerator(classOutput);
|
||||
MethodInfo extensionMethod = index.getClassByName(DotName.createSimple(MyService.class.getName())).method(
|
||||
"getDummy", Type.create(myServiceClazz.name(), Kind.CLASS), PrimitiveType.INT,
|
||||
Type.create(DotName.createSimple(String.class.getName()), Kind.CLASS));
|
||||
extensionMethodGenerator.generate(extensionMethod, null);
|
||||
generatedTypes.addAll(extensionMethodGenerator.getGeneratedTypes());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -71,18 +91,38 @@ public class SimpleGeneratorTest {
|
||||
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException expected) {
|
||||
}
|
||||
|
||||
Engine engine = Engine.builder().addDefaults()
|
||||
.addValueResolver(newResolver("io.quarkus.qute.generator.MyService_ValueResolver"))
|
||||
.addValueResolver(newResolver("io.quarkus.qute.generator.PublicMyService_ValueResolver"))
|
||||
.addValueResolver(newResolver("io.quarkus.qute.generator.MyItem_ValueResolver"))
|
||||
.addValueResolver(newResolver("io.quarkus.qute.String_ValueResolver"))
|
||||
.addValueResolver(newResolver("io.quarkus.qute.List_ValueResolver"))
|
||||
.build();
|
||||
EngineBuilder builder = Engine.builder().addDefaults();
|
||||
for (String generatedType : generatedTypes) {
|
||||
builder.addValueResolver(newResolver(generatedType));
|
||||
}
|
||||
Engine engine = builder.build();
|
||||
assertEquals(" FOO ", engine.parse("{#if isActive} {name.toUpperCase} {/if}").render(new MyService()));
|
||||
assertEquals("OK", engine.parse("{#if this.getList(5).size == 5}OK{/if}").render(new MyService()));
|
||||
assertEquals("Martin NOT_FOUND OK NOT_FOUND",
|
||||
engine.parse("{name} {surname} {isStatic ?: 'OK'} {base}").render(new PublicMyService()));
|
||||
assertEquals("foo NOT_FOUND", engine.parse("{id} {bar}").render(new MyItem()));
|
||||
try {
|
||||
engine.parse("{this.getList(5,5)}").render(new MyService());
|
||||
fail();
|
||||
} catch (IllegalStateException e) {
|
||||
assertClassCastException(e);
|
||||
}
|
||||
try {
|
||||
engine.parse("{service.getDummy(5,resultNotFound)}").data("service", new MyService()).render();
|
||||
fail();
|
||||
} catch (IllegalStateException e) {
|
||||
assertClassCastException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertClassCastException(Exception e) {
|
||||
if (e.getCause() instanceof ExecutionException) {
|
||||
ExecutionException ex = (ExecutionException) e.getCause();
|
||||
if (ex.getCause() instanceof ClassCastException) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail("Unexpected exception thrown: ", e);
|
||||
}
|
||||
|
||||
private ValueResolver newResolver(String className)
|
||||
|
||||
Reference in New Issue
Block a user