use of io.quarkus.qute.EvalContext in project quarkus by quarkusio.
the class ExtensionMethodGenerator method implementResolve.
private void implementResolve(ClassCreator valueResolver, ClassInfo declaringClass, MethodInfo method, String matchName, FieldDescriptor patternField, Parameters params) {
MethodCreator resolve = valueResolver.getMethodCreator("resolve", CompletionStage.class, EvalContext.class).setModifiers(ACC_PUBLIC);
ResultHandle evalContext = resolve.getMethodParam(0);
ResultHandle base = resolve.invokeInterfaceMethod(Descriptors.GET_BASE, evalContext);
boolean matchAnyOrRegex = patternField != null || matchName.equals(TemplateExtension.ANY);
boolean returnsCompletionStage = method.returnType().kind() != Kind.PRIMITIVE && ValueResolverGenerator.hasCompletionStageInTypeClosure(index.getClassByName(method.returnType().name()), index);
ResultHandle ret;
if (!params.needsEvaluation()) {
// No parameter needs to be evaluated
ResultHandle[] args = new ResultHandle[params.size()];
for (int i = 0; i < params.size(); i++) {
Param param = params.get(i);
if (param.kind == ParamKind.BASE) {
args[i] = base;
} else if (param.kind == ParamKind.NAME) {
args[i] = resolve.invokeInterfaceMethod(Descriptors.GET_NAME, evalContext);
} else if (param.kind == ParamKind.ATTR) {
args[i] = resolve.invokeInterfaceMethod(Descriptors.GET_ATTRIBUTE, evalContext, resolve.load(param.name));
}
}
// Invoke the extension method
ResultHandle result = resolve.invokeStaticMethod(MethodDescriptor.ofMethod(declaringClass.name().toString(), method.name(), method.returnType().name().toString(), params.parameterTypesAsStringArray()), args);
if (returnsCompletionStage) {
ret = result;
} else {
ret = resolve.invokeStaticMethod(Descriptors.COMPLETED_STAGE, result);
}
} else {
ret = resolve.newInstance(MethodDescriptor.ofConstructor(CompletableFuture.class));
// Evaluate params first
ResultHandle name = resolve.invokeInterfaceMethod(Descriptors.GET_NAME, evalContext);
// The CompletionStage upon which we invoke whenComplete()
ResultHandle evaluatedParamsHandle = resolve.invokeStaticMethod(Descriptors.EVALUATED_PARAMS_EVALUATE, evalContext);
ResultHandle paramsReadyHandle = resolve.readInstanceField(Descriptors.EVALUATED_PARAMS_STAGE, evaluatedParamsHandle);
// Function that is called when params are evaluated
FunctionCreator whenCompleteFun = resolve.createFunction(BiConsumer.class);
resolve.invokeInterfaceMethod(Descriptors.CF_WHEN_COMPLETE, paramsReadyHandle, whenCompleteFun.getInstance());
BytecodeCreator whenComplete = whenCompleteFun.getBytecode();
AssignableResultHandle whenBase = whenComplete.createVariable(Object.class);
whenComplete.assign(whenBase, base);
AssignableResultHandle whenName = null;
if (matchAnyOrRegex) {
whenName = whenComplete.createVariable(String.class);
whenComplete.assign(whenName, name);
}
AssignableResultHandle whenRet = whenComplete.createVariable(CompletableFuture.class);
whenComplete.assign(whenRet, ret);
AssignableResultHandle whenEvaluatedParams = whenComplete.createVariable(EvaluatedParams.class);
whenComplete.assign(whenEvaluatedParams, evaluatedParamsHandle);
AssignableResultHandle whenEvalContext = whenComplete.createVariable(EvalContext.class);
whenComplete.assign(whenEvalContext, evalContext);
BranchResult throwableIsNull = whenComplete.ifNull(whenComplete.getMethodParam(1));
BytecodeCreator success = throwableIsNull.trueBranch();
boolean isVarArgs = ValueResolverGenerator.isVarArgs(method);
// Check type parameters and return NO_RESULT if failed
List<Param> evaluated = params.evaluated();
ResultHandle paramTypesHandle = success.newArray(Class.class, evaluated.size());
int idx = 0;
for (Param p : evaluated) {
success.writeArrayValue(paramTypesHandle, idx++, ValueResolverGenerator.loadParamType(success, p.type));
}
BytecodeCreator typeMatchFailed = success.ifNonZero(success.invokeVirtualMethod(Descriptors.EVALUATED_PARAMS_PARAM_TYPES_MATCH, whenEvaluatedParams, success.load(isVarArgs), paramTypesHandle)).falseBranch();
typeMatchFailed.invokeVirtualMethod(Descriptors.COMPLETABLE_FUTURE_COMPLETE, whenRet, typeMatchFailed.invokeStaticMethod(Descriptors.NOT_FOUND_FROM_EC, whenEvalContext));
typeMatchFailed.returnValue(null);
// 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());
// Collect the params
ResultHandle[] args = new ResultHandle[params.size()];
int evalIdx = 0;
int lastIdx = params.size() - 1;
for (int i = 0; i < params.size(); i++) {
Param param = params.get(i);
if (param.kind == ParamKind.BASE) {
args[i] = whenBase;
} else if (param.kind == ParamKind.NAME) {
args[i] = whenName;
} else if (param.kind == ParamKind.ATTR) {
args[i] = tryCatch.invokeInterfaceMethod(Descriptors.GET_ATTRIBUTE, whenEvalContext, tryCatch.load(param.name));
} else {
if (isVarArgs && i == lastIdx) {
// Last param is varargs
Type varargsParam = params.get(lastIdx).type;
ResultHandle componentType = tryCatch.loadClassFromTCCL(varargsParam.asArrayType().component().name().toString());
ResultHandle varargsResults = tryCatch.invokeVirtualMethod(Descriptors.EVALUATED_PARAMS_GET_VARARGS_RESULTS, evaluatedParamsHandle, tryCatch.load(evaluated.size()), componentType);
args[i] = varargsResults;
} else {
args[i] = tryCatch.invokeVirtualMethod(Descriptors.EVALUATED_PARAMS_GET_RESULT, whenEvaluatedParams, tryCatch.load(evalIdx++));
}
}
}
// Invoke the extension method
ResultHandle invokeRet = tryCatch.invokeStaticMethod(MethodDescriptor.ofMethod(declaringClass.name().toString(), method.name(), method.returnType().name().toString(), params.parameterTypesAsStringArray()), args);
tryCatch.invokeVirtualMethod(Descriptors.COMPLETABLE_FUTURE_COMPLETE, whenRet, invokeRet);
BytecodeCreator failure = throwableIsNull.falseBranch();
failure.invokeVirtualMethod(Descriptors.COMPLETABLE_FUTURE_COMPLETE_EXCEPTIONALLY, whenRet, whenComplete.getMethodParam(1));
whenComplete.returnValue(null);
}
resolve.returnValue(ret);
}
use of io.quarkus.qute.EvalContext in project quarkus by quarkusio.
the class ValueResolverGenerator method implementNamespaceResolve.
private boolean implementNamespaceResolve(ClassCreator valueResolver, String clazzName, ClassInfo clazz, Predicate<AnnotationTarget> filter) {
MethodCreator resolve = valueResolver.getMethodCreator("resolve", CompletionStage.class, EvalContext.class).setModifiers(ACC_PUBLIC);
ResultHandle evalContext = resolve.getMethodParam(0);
ResultHandle base = resolve.invokeInterfaceMethod(Descriptors.GET_BASE, evalContext);
ResultHandle name = resolve.invokeInterfaceMethod(Descriptors.GET_NAME, evalContext);
ResultHandle params = resolve.invokeInterfaceMethod(Descriptors.GET_PARAMS, evalContext);
ResultHandle paramsCount = resolve.invokeInterfaceMethod(Descriptors.COLLECTION_SIZE, params);
// First collect static members
List<MethodKey> methods = clazz.methods().stream().filter(filter::test).map(MethodKey::new).sorted().collect(Collectors.toList());
List<FieldInfo> fields = clazz.fields().stream().filter(filter::test).collect(Collectors.toList());
if (methods.isEmpty() && fields.isEmpty()) {
return false;
}
// Static fields
if (!fields.isEmpty()) {
BytecodeCreator zeroParamsBranch = resolve.ifNonZero(paramsCount).falseBranch();
for (FieldInfo field : fields) {
LOGGER.debugf("Static field added: %s", field);
// Match field name
BytecodeCreator fieldMatch = zeroParamsBranch.ifNonZero(zeroParamsBranch.invokeVirtualMethod(Descriptors.EQUALS, resolve.load(field.name()), name)).trueBranch();
ResultHandle value = fieldMatch.readStaticField(FieldDescriptor.of(clazzName, field.name(), field.type().name().toString()));
fieldMatch.returnValue(fieldMatch.invokeStaticMethod(Descriptors.COMPLETED_STAGE, value));
}
}
// Static methods
if (!methods.isEmpty()) {
// name, number of params -> list of methods
Map<Match, List<MethodInfo>> matches = new HashMap<>();
Map<Match, List<MethodInfo>> varargsMatches = new HashMap<>();
for (MethodKey methodKey : methods) {
MethodInfo method = methodKey.method;
List<Type> methodParams = method.parameters();
if (methodParams.isEmpty()) {
// No params - just invoke the method
LOGGER.debugf("Static method added %s", method);
try (BytecodeCreator matchScope = createMatchScope(resolve, method.name(), 0, method.returnType(), name, params, paramsCount)) {
ResultHandle ret;
boolean hasCompletionStage = !skipMemberType(method.returnType()) && hasCompletionStageInTypeClosure(index.getClassByName(method.returnType().name()), index);
ResultHandle invokeRet;
if (Modifier.isInterface(clazz.flags())) {
invokeRet = matchScope.invokeStaticInterfaceMethod(MethodDescriptor.of(method));
} else {
invokeRet = matchScope.invokeStaticMethod(MethodDescriptor.of(method));
}
if (hasCompletionStage) {
ret = invokeRet;
} else {
ret = matchScope.invokeStaticMethod(Descriptors.COMPLETED_STAGE, invokeRet);
}
matchScope.returnValue(ret);
}
} else {
// Collect methods with params
Match match = new Match(method.name(), method.parameters().size());
List<MethodInfo> methodMatches = matches.get(match);
if (methodMatches == null) {
methodMatches = new ArrayList<>();
matches.put(match, methodMatches);
}
methodMatches.add(method);
if (isVarArgs(method)) {
// The last argument is a sequence of arguments -> match name and min number of params
// getList(int age, String... names) -> "getList", 1
match = new Match(method.name(), method.parameters().size() - 1);
methodMatches = varargsMatches.get(match);
if (methodMatches == null) {
methodMatches = new ArrayList<>();
varargsMatches.put(match, methodMatches);
}
methodMatches.add(method);
}
}
}
// Match methods by name and number of params
for (Entry<Match, List<MethodInfo>> entry : matches.entrySet()) {
Match match = entry.getKey();
// The set of matching methods is made up of the methods matching the name and number of params + varargs methods matching the name and minimal number of params
// For example both the methods getList(int age, String... names) and getList(int age) match "getList" and 1 param
Set<MethodInfo> methodMatches = new HashSet<>(entry.getValue());
varargsMatches.entrySet().stream().filter(e -> e.getKey().name.equals(match.name) && e.getKey().paramsCount >= match.paramsCount).forEach(e -> methodMatches.addAll(e.getValue()));
if (methodMatches.size() == 1) {
// Single method matches the name and number of params
matchMethod(methodMatches.iterator().next(), clazz, resolve, base, name, params, paramsCount, evalContext);
} else {
// Multiple methods match the name and number of params
matchMethods(match.name, match.paramsCount, methodMatches, clazz, resolve, base, name, params, paramsCount, evalContext);
}
}
// For varargs methods we also need to match name and any number of params
Map<String, List<MethodInfo>> varargsMap = new HashMap<>();
for (Entry<Match, List<MethodInfo>> entry : varargsMatches.entrySet()) {
List<MethodInfo> list = varargsMap.get(entry.getKey().name);
if (list == null) {
list = new ArrayList<>();
varargsMap.put(entry.getKey().name, list);
}
list.addAll(entry.getValue());
}
for (Entry<String, List<MethodInfo>> entry : varargsMap.entrySet()) {
matchMethods(entry.getKey(), Integer.MIN_VALUE, entry.getValue(), clazz, resolve, base, name, params, paramsCount, evalContext);
}
}
resolve.returnValue(resolve.invokeStaticMethod(Descriptors.RESULTS_NOT_FOUND_EC, evalContext));
return true;
}
use of io.quarkus.qute.EvalContext in project quarkus by quarkusio.
the class ValueResolverGenerator method implementAppliesTo.
private void implementAppliesTo(ClassCreator valueResolver, ClassInfo clazz) {
MethodCreator appliesTo = valueResolver.getMethodCreator("appliesTo", boolean.class, EvalContext.class).setModifiers(ACC_PUBLIC);
ResultHandle evalContext = appliesTo.getMethodParam(0);
ResultHandle base = appliesTo.invokeInterfaceMethod(Descriptors.GET_BASE, evalContext);
BranchResult baseTest = appliesTo.ifNull(base);
BytecodeCreator baseNotNullBranch = baseTest.falseBranch();
// Test base object class
ResultHandle baseClass = baseNotNullBranch.invokeVirtualMethod(Descriptors.GET_CLASS, base);
ResultHandle testClass = baseNotNullBranch.loadClassFromTCCL(clazz.name().toString());
ResultHandle test = baseNotNullBranch.invokeVirtualMethod(Descriptors.IS_ASSIGNABLE_FROM, testClass, baseClass);
BytecodeCreator baseAssignableBranch = baseNotNullBranch.ifNonZero(test).trueBranch();
baseAssignableBranch.returnValue(baseAssignableBranch.load(true));
appliesTo.returnValue(appliesTo.load(false));
}
use of io.quarkus.qute.EvalContext in project quarkus by quarkusio.
the class QuteErrorPageSetup method generatePage.
String generatePage(Throwable exception) {
Escaper escaper = Escaper.builder().add('"', """).add('\'', "'").add('&', "&").add('<', "<").add('>', ">").build();
Template problemTemplate = Engine.builder().addDefaults().addValueResolver(new ReflectionValueResolver()).addValueResolver(new ValueResolver() {
public boolean appliesTo(EvalContext context) {
return context.getName().equals("pad");
}
@Override
public CompletionStage<Object> resolve(EvalContext context) {
return CompletableFuture.completedFuture(htmlPadRight(context.getBase().toString(), 5));
}
}).build().parse(PROBLEM_TEMPLATE);
TemplateHtmlBuilder builder;
List<Throwable> problems;
Throwable[] suppressed = exception.getSuppressed();
if (suppressed.length == 0) {
problems = Collections.singletonList(exception);
} else {
problems = Arrays.asList(suppressed);
}
String problemsFound = "Found " + problems.size() + " Qute problems";
builder = new TemplateHtmlBuilder("Error restarting Quarkus", problemsFound, problemsFound);
// Attempt to sort problems by line
problems.sort(new Comparator<Throwable>() {
@Override
public int compare(Throwable t1, Throwable t2) {
Object o1 = getOrigin(t1);
Object o2 = getOrigin(t2);
if (o1 == o2) {
return 0;
} else if (o1 == null && o2 != null) {
return -1;
} else if (o1 != null && o2 == null) {
return 1;
}
return Integer.compare(getLine(o1), getLine(o2));
}
});
for (ListIterator<Throwable> it = problems.listIterator(); it.hasNext(); ) {
Throwable problem = it.next();
builder.append(getProblemInfo(it.previousIndex() + 1, problem, problemTemplate, escaper));
}
return builder.toString();
}
use of io.quarkus.qute.EvalContext in project quarkus by quarkusio.
the class MessageBundles method setupNamespaceResolvers.
static void setupNamespaceResolvers(@Observes EngineBuilder builder, BundleContext context) {
// Avoid injecting "Instance<Object> instance" which prevents unused beans removal
ArcContainer container = Arc.container();
// For every bundle register a new resolver
for (Entry<String, Map<String, Class<?>>> entry : context.getBundleInterfaces().entrySet()) {
final String bundle = entry.getKey();
final Map<String, Resolver> interfaces = new HashMap<>();
Resolver resolver = null;
for (Entry<String, Class<?>> locEntry : entry.getValue().entrySet()) {
if (locEntry.getKey().equals(DEFAULT_LOCALE)) {
resolver = (Resolver) container.select(locEntry.getValue(), Default.Literal.INSTANCE).get();
continue;
}
Instance<?> found = container.select(locEntry.getValue(), new Localized.Literal(locEntry.getKey()));
if (found.isUnsatisfied()) {
throw new IllegalStateException(Qute.fmt("Bean not found for localized interface [{e.value}] and locale [{e.key}]").data("e", locEntry).render());
}
if (found.isAmbiguous()) {
throw new IllegalStateException(Qute.fmt("Multiple beans found for localized interface [{e.value}] and locale [{e.key}]").data("e", locEntry).render());
}
interfaces.put(locEntry.getKey(), (Resolver) found.get());
}
final Resolver defaultResolver = resolver;
builder.addNamespaceResolver(new NamespaceResolver() {
@Override
public CompletionStage<Object> resolve(EvalContext context) {
Object locale = context.getAttribute(ATTRIBUTE_LOCALE);
if (locale == null) {
Object selectedVariant = context.getAttribute(TemplateInstance.SELECTED_VARIANT);
if (selectedVariant != null) {
locale = ((Variant) selectedVariant).getLocale();
}
if (locale == null) {
return defaultResolver.resolve(context);
}
}
// First try the exact match
Resolver localeResolver = interfaces.get(locale instanceof Locale ? ((Locale) locale).toLanguageTag() : locale.toString());
if (localeResolver == null && locale instanceof Locale) {
// Next try the language
localeResolver = interfaces.get(((Locale) locale).getLanguage());
}
return localeResolver != null ? localeResolver.resolve(context) : defaultResolver.resolve(context);
}
@Override
public String getNamespace() {
return bundle;
}
});
}
}
Aggregations