Repository: quarkusio/quarkus
Issue: Qute Template Extension with multiple parameters: Resulting in endless wait without error

**Describe the bug**
I'm trying to create a Qute template extension with multiple parameters as described here: https://quarkus.io/guides/qute-reference#method-parameters

When using a method with a single parameter all is working well, when I add a second parameter in the template extension method, it results in an endless loop in the browser.

I tried setting the default logger to DEBUG, but nothing gets logged (see log under "actual behavior"). I also tried to set a breakpoint in the method that gets called, but the error seems to be happening before the breakpoint is reached.

**Expected behavior**
The template extension method is called, the result computed and displayed in my HTML template.

**Actual behavior**
Website loads endlesly in browser. Logfile shows this:

```
2020-02-11 17:00:18,069 INFO  [io.quarkus] (main) Quarkus 1.2.0.Final started in 2.818s. Listening on: http://0.0.0.0:8080
2020-02-11 17:00:18,070 INFO  [io.quarkus] (main) Profile dev activated. Live Coding activated.
2020-02-11 17:00:18,070 INFO  [io.quarkus] (main) Installed features: [cdi, qute, resteasy, resteasy-jackson, resteasy-qute, spring-di, spring-web]
2020-02-11 17:00:30,242 DEBUG [io.net.uti.Recycler] (vert.x-eventloop-thread-1) -Dio.netty.recycler.maxCapacityPerThread: {}
2020-02-11 17:00:30,243 DEBUG [io.net.uti.Recycler] (vert.x-eventloop-thread-1) -Dio.netty.recycler.maxSharedCapacityFactor: {}
2020-02-11 17:00:30,243 DEBUG [io.net.uti.Recycler] (vert.x-eventloop-thread-1) -Dio.netty.recycler.linkCapacity: {}
2020-02-11 17:00:30,243 DEBUG [io.net.uti.Recycler] (vert.x-eventloop-thread-1) -Dio.netty.recycler.ratio: {}
2020-02-11 17:00:30,334 DEBUG [io.net.han.cod.com.ZlibCodecFactory] (vert.x-eventloop-thread-1) -Dio.netty.noJdkZlibDecoder: {}
2020-02-11 17:00:30,334 DEBUG [io.net.han.cod.com.ZlibCodecFactory] (vert.x-eventloop-thread-1) -Dio.netty.noJdkZlibEncoder: {}
2020-02-11 17:00:30,459 DEBUG [org.jbo.res.res.i18n] (executor-thread-1) RESTEASY002315: PathInfo: /
2020-02-11 17:00:31,172 DEBUG [io.qua.qut.run.EngineProducer] (executor-thread-1) Locate template for templates/feed
2020-02-11 17:00:31,175 DEBUG [io.qua.qut.run.EngineProducer] (executor-thread-1) Locate template for templates/feed.qute.html
2020-02-11 17:00:31,176 DEBUG [io.qua.qut.run.EngineProducer] (executor-thread-1) Locate template for templates/feed.qute.txt

<HERE WE HAVE AN ENDLESS WAIT, THEN IT SEEMS TO RESTART THE TASK?>

> Task :quarkusDev

<THEN RELOADING WEBSITE IN BROWSER>

2020-02-11 17:04:48,855 DEBUG [io.qua.resteasy] (vert.x-eventloop-thread-10) IO Exception : io.vertx.core.VertxException: Connection was closed

2020-02-11 17:04:48,892 DEBUG [org.jbo.res.res.i18n] (executor-thread-1) RESTEASY002315: PathInfo: /
2020-02-11 17:04:49,219 DEBUG [io.qua.qut.run.EngineProducer] (executor-thread-1) Locate template for templates/feed
2020-02-11 17:04:49,222 DEBUG [io.qua.qut.run.EngineProducer] (executor-thread-1) Locate template for templates/feed.qute.html
2020-02-11 17:04:49,224 DEBUG [io.qua.qut.run.EngineProducer] (executor-thread-1) Locate template for templates/feed.qute.txt

<AND ENDLESS WAIT AGAIN>
```

**Not working code example**

```
    // My @RestController method, where "downloadFolder" would be a simple String, and "feed" would be a @Component with several methods
    @GetMapping(value = "/", produces = MediaType.TEXT_HTML)
    public TemplateInstance showFeed() {
        return feed.data("feed", feedReader.readFeed())
                .data("downloadFolder", downloadFolder);
    }
```

```
@TemplateExtension
public class DownloadExistsUtil {
    public static boolean isAlreadyDownloaded(FeedItem feedItem, String downloadUrl, String downloadFolder) {
        return false;
    }
}
```

```
<table class="datagrid">
    {#for item in feed}
    <tr>
        <td>
            {#for url in item.urls}
                {#if item.isAlreadyDownloaded(url,downloadFolder) == false}
                    <p>
                        Bla
                    </p>
                {#else}
                    <p>
                        Blub
                    </p>
                {/if}
            {/for}
        </td>
    </tr>
    {/for}
</table>
```

**Working example with just 1 parameter**

```
    // No change from "not working example"
    // My @RestController method, where "downloadFolder" would be a simple String, and "feed" would be a @Component with several methods
    @GetMapping(value = "/", produces = MediaType.TEXT_HTML)
    public TemplateInstance showFeed() {
        return feed.data("feed", feedReader.readFeed())
                .data("downloadFolder", downloadFolder);
    }
```

```
@TemplateExtension
public class DownloadExistsUtil {
    public static boolean isAlreadyDownloaded(FeedItem feedItem, String downloadUrl) {
        return false;
    }
}
```

```
<table class="datagrid">
    {#for item in feed}
    <tr>
        <td>
            {#for url in item.urls}
                {#if item.isAlreadyDownloaded(url) == false}
                    <p>
                        Bla
                    </p>
                {#else}
                    <p>
                        Blub
                    </p>
                {/if}
            {/for}
        </td>
    </tr>
    {/for}
</table>
```

**Configuration**
- Nothing

**Environment (please complete the following information):**
 - Output of `uname -a` or `ver`:  Microsoft Windows [Version 10.0.18363.592]
 - Output of `java -version`:

```
openjdk 11.0.6 2020-01-14
OpenJDK Runtime Environment GraalVM CE 19.3.1 (build 11.0.6+9-jvmci-19.3-b07)
OpenJDK 64-Bit Server VM GraalVM CE 19.3.1 (build 11.0.6+9-jvmci-19.3-b07, mixed mode, sharing)
```

 - GraalVM version (if different from Java): same as java
 - Quarkus version or git rev: 1.2.0.Final

Changed files:
independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ExtensionMethodGenerator.java
independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/MyService.java
independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/SimpleGeneratorTest.java
independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java
independent-projects/qute/core/src/main/java/io/quarkus/qute/EvaluatorImpl.java
independent-projects/qute/core/src/test/java/io/quarkus/qute/LoopSectionTest.java

diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/EvaluatorImpl.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/EvaluatorImpl.java
index c00d6e837a0..6edb2f6f129 100644
--- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/EvaluatorImpl.java
+++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/EvaluatorImpl.java
@@ -69,7 +69,7 @@ private CompletionStage<Object> resolveReference(boolean tryParent, Object ref,
         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 @@ private CompletionStage<Object> resolve(EvalContextImpl evalContext, Iterator<Va
             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);
diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java
index 8e7d061aea6..de7e5f08ca8 100644
--- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java
+++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/LoopSectionHelper.java
@@ -47,7 +47,9 @@ public CompletionStage<ResultNode> resolve(SectionResolutionContext context) {
             } 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()) {
diff --git a/independent-projects/qute/core/src/test/java/io/quarkus/qute/LoopSectionTest.java b/independent-projects/qute/core/src/test/java/io/quarkus/qute/LoopSectionTest.java
index 186959ec04c..a55a87a06d0 100644
--- a/independent-projects/qute/core/src/test/java/io/quarkus/qute/LoopSectionTest.java
+++ b/independent-projects/qute/core/src/test/java/io/quarkus/qute/LoopSectionTest.java
@@ -68,8 +68,8 @@ public void testStream() {
 
     @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 CompletionStage<Object> resolve(EvalContext context) {
                 })
                 .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
diff --git a/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ExtensionMethodGenerator.java b/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ExtensionMethodGenerator.java
index 9948a4ce424..c77b16aebef 100644
--- a/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ExtensionMethodGenerator.java
+++ b/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ExtensionMethodGenerator.java
@@ -8,12 +8,14 @@
 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 @@ private void implementResolve(ClassCreator valueResolver, ClassInfo declaringCla
                 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,
diff --git a/independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/MyService.java b/independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/MyService.java
index 1b9dd37da83..90747afb7c7 100644
--- a/independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/MyService.java
+++ b/independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/MyService.java
@@ -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 CompletionStage<String> getAnotherTestName(String param) {
         return CompletableFuture.completedFuture(param);
     }
 
+    public static List<String> getDummy(MyService service, int limit, String dummy) {
+        return Collections.emptyList();
+    }
+
 }
diff --git a/independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/SimpleGeneratorTest.java b/independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/SimpleGeneratorTest.java
index 995687b0044..427a736ab12 100644
--- a/independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/SimpleGeneratorTest.java
+++ b/independent-projects/qute/generator/src/test/java/io/quarkus/qute/generator/SimpleGeneratorTest.java
@@ -4,6 +4,7 @@
 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 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 static void init() throws IOException {
                 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 void testWithEngine() throws Exception {
         } 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)