Skip to content

Commit

Permalink
Merge pull request #16180 from mkouba/qute-micro-optimizations
Browse files Browse the repository at this point in the history
Qute - minor cleanup and micro optimizations
  • Loading branch information
gsmet authored Apr 8, 2021
2 parents e21d31a + ace24ab commit b6ddd49
Show file tree
Hide file tree
Showing 17 changed files with 207 additions and 131 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@Vetoed // Make sure no bean is created from this class
@TemplateExtension
public class MapTemplateExtensions {
@SuppressWarnings({ "rawtypes", "unchecked" })
@SuppressWarnings({ "rawtypes" })
@TemplateExtension(matchName = ANY)
static Object map(Map map, String name) {
switch (name) {
Expand All @@ -29,7 +29,11 @@ static Object map(Map map, String name) {
case "isEmpty":
return map.isEmpty();
default:
return map.getOrDefault(name, Result.NOT_FOUND);
Object val = map.get(name);
if (val == null) {
return map.containsKey(name) ? null : Result.NOT_FOUND;
}
return val;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ class EngineImpl implements Engine {
EngineImpl(EngineBuilder builder) {
this.sectionHelperFactories = Collections.unmodifiableMap(new HashMap<>(builder.sectionHelperFactories));
this.valueResolvers = sort(builder.valueResolvers);
this.namespaceResolvers = ImmutableList.copyOf(builder.namespaceResolvers);
this.evaluator = new EvaluatorImpl(this.valueResolvers);
this.namespaceResolvers = ImmutableList.<NamespaceResolver> builder()
.addAll(builder.namespaceResolvers).add(new TemplateImpl.DataNamespaceResolver()).build();
this.evaluator = new EvaluatorImpl(this.valueResolvers, this.namespaceResolvers);
this.templates = new ConcurrentHashMap<>();
this.locators = sort(builder.locators);
this.resultMappers = sort(builder.resultMappers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,29 @@ public static EvaluatedParams evaluate(EvalContext context) {
return new EvaluatedParams(context.evaluate(params.get(0)));
}
CompletableFuture<?>[] allResults = new CompletableFuture<?>[params.size()];
List<CompletableFuture<?>> results = new LinkedList<>();
List<CompletableFuture<?>> results = null;
int i = 0;
Iterator<Expression> it = params.iterator();
while (it.hasNext()) {
Expression expression = it.next();
CompletableFuture<Object> result = context.evaluate(expression).toCompletableFuture();
allResults[i++] = result;
if (!expression.isLiteral()) {
if (results == null) {
results = new LinkedList<>();
}
results.add(result);
}
}
return new EvaluatedParams(CompletableFuture.allOf(results.toArray(new CompletableFuture[0])), allResults);
CompletionStage<?> cs;
if (results == null) {
cs = Futures.COMPLETED;
} else if (results.size() == 1) {
cs = results.get(0);
} else {
cs = CompletableFuture.allOf(results.toArray(new CompletableFuture[0]));
}
return new EvaluatedParams(cs, allResults);
}

public static EvaluatedParams evaluateMessageKey(EvalContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,25 @@ class EvaluatorImpl implements Evaluator {
private static final Logger LOGGER = Logger.getLogger(EvaluatorImpl.class);

private final List<ValueResolver> resolvers;
private final List<NamespaceResolver> namespaceResolvers;

EvaluatorImpl(List<ValueResolver> valueResolvers) {
EvaluatorImpl(List<ValueResolver> valueResolvers, List<NamespaceResolver> namespaceResolvers) {
this.resolvers = valueResolvers;
this.namespaceResolvers = namespaceResolvers;
}

@Override
public CompletionStage<Object> evaluate(Expression expression, ResolutionContext resolutionContext) {
Iterator<Part> parts;
if (expression.hasNamespace()) {
parts = expression.getParts().iterator();
NamespaceResolver resolver = findNamespaceResolver(expression.getNamespace(), resolutionContext);
NamespaceResolver resolver = null;
for (NamespaceResolver namespaceResolver : namespaceResolvers) {
if (namespaceResolver.getNamespace().equals(expression.getNamespace())) {
resolver = namespaceResolver;
break;
}
}
if (resolver == null) {
LOGGER.errorf("No namespace resolver found for: %s", expression.getNamespace());
return Futures.failure(new TemplateException("No resolver for namespace: " + expression.getNamespace()));
Expand All @@ -53,20 +61,6 @@ public CompletionStage<Object> evaluate(Expression expression, ResolutionContext
}
}

private NamespaceResolver findNamespaceResolver(String namespace, ResolutionContext resolutionContext) {
if (resolutionContext == null) {
return null;
}
if (resolutionContext.getNamespaceResolvers() != null) {
for (NamespaceResolver resolver : resolutionContext.getNamespaceResolvers()) {
if (resolver.getNamespace().equals(namespace)) {
return resolver;
}
}
}
return findNamespaceResolver(namespace, resolutionContext.getParent());
}

private CompletionStage<Object> resolveReference(boolean tryParent, Object ref, Iterator<Part> parts,
ResolutionContext resolutionContext) {
Part part = parts.next();
Expand Down Expand Up @@ -138,7 +132,7 @@ private CompletionStage<Object> resolve(EvalContextImpl evalContext, Iterator<Va
}

@SuppressWarnings("unchecked")
private CompletionStage<Object> toCompletionStage(Object result) {
private static CompletionStage<Object> toCompletionStage(Object result) {
if (result instanceof CompletionStage) {
// If the result is a completion stage return it as is
return (CompletionStage<Object>) result;
Expand All @@ -155,6 +149,8 @@ static class EvalContextImpl implements EvalContext {
final Object base;
final ResolutionContext resolutionContext;
final PartImpl part;
final List<Expression> params;
final String name;

EvalContextImpl(boolean tryParent, Object base, Part part, ResolutionContext resolutionContext) {
this(tryParent, base, resolutionContext, part);
Expand All @@ -165,6 +161,8 @@ static class EvalContextImpl implements EvalContext {
this.base = base;
this.resolutionContext = resolutionContext;
this.part = (PartImpl) part;
this.name = part.getName();
this.params = part.isVirtualMethod() ? part.asVirtualMethod().getParameters() : Collections.emptyList();
}

@Override
Expand All @@ -174,12 +172,12 @@ public Object getBase() {

@Override
public String getName() {
return part.getName();
return name;
}

@Override
public List<Expression> getParams() {
return part.isVirtualMethod() ? part.asVirtualMethod().getParameters() : Collections.emptyList();
return params;
}

@Override
Expand Down Expand Up @@ -209,7 +207,7 @@ void setCachedResolver(ValueResolver valueResolver) {
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("EvalContextImpl [tryParent=").append(tryParent).append(", base=").append(base).append(", name=")
.append(getBase()).append(", params=").append(getParams()).append("]");
.append(getName()).append(", params=").append(getParams()).append("]");
return builder.toString();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

public final class Futures {

static final CompletableFuture<Void> COMPLETED = CompletableFuture.completedFuture(null);

private Futures() {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -152,15 +153,6 @@ interface Condition {

Operator getOperator();

/**
* Short-circuiting evaluation.
*
* @return null if evaluation should continue
*/
default Boolean evaluate(Object value) {
return getOperator() != null ? getOperator().evaluate(value) : null;
}

default boolean isLogicalComplement() {
return Operator.NOT.equals(getOperator());
}
Expand All @@ -169,16 +161,32 @@ default boolean isEmpty() {
return false;
}

default Object getLiteralValue() {
return null;
}

}

static class OperandCondition implements Condition {

final Operator operator;
final Expression expression;
final Object literalValue;

OperandCondition(Operator operator, Expression expression) {
this.operator = operator;
this.expression = expression;
CompletableFuture<Object> literalVal = expression.getLiteralValue();
if (literalVal != null) {
try {
this.literalValue = literalVal.get();
} catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException(e);
}
} else {
this.literalValue = null;
}

}

@Override
Expand All @@ -191,6 +199,11 @@ public Operator getOperator() {
return operator;
}

@Override
public Object getLiteralValue() {
return literalValue;
}

@Override
public String toString() {
return "OperandCondition [operator=" + operator + ", expression=" + expression.toOriginalString() + "]";
Expand All @@ -213,53 +226,27 @@ public CompletionStage<Object> evaluate(SectionResolutionContext context) {
return evaluateNext(context, null, conditions.iterator());
}

CompletionStage<Object> evaluateNext(SectionResolutionContext context, Object value, Iterator<Condition> iter) {
static CompletionStage<Object> evaluateNext(SectionResolutionContext context, Object value, Iterator<Condition> iter) {
CompletableFuture<Object> result = new CompletableFuture<>();
if (!iter.hasNext()) {
result.complete(value);
Condition next = iter.next();
Boolean shortResult = null;
Operator operator = next.getOperator();
if (operator != null && operator.isShortCircuiting()) {
shortResult = operator.evaluate(value);
}
if (shortResult != null) {
// There is no need to continue with the next operand
result.complete(shortResult);
} else {
Condition next = iter.next();
Boolean shortResult = null;
Operator operator = next.getOperator();
if (operator != null && operator.isShortCircuiting()) {
shortResult = operator.evaluate(value);
}
if (shortResult != null) {
// There is no need to continue with the next operand
result.complete(shortResult);
Object literalVal = next.getLiteralValue();
if (literalVal != null) {
processConditionValue(context, next, operator, value, literalVal, result, iter);
} else {
next.evaluate(context).whenComplete((r, t) -> {
if (t != null) {
result.completeExceptionally(t);
} else {
Object val;
if (next.isLogicalComplement()) {
r = Booleans.isFalsy(r) ? Boolean.TRUE : Boolean.FALSE;
}
if (operator == null || !operator.isBinary()) {
val = r;
} else {
try {
if (Result.NOT_FOUND.equals(r)) {
r = null;
}
Object localValue = value;
if (Result.NOT_FOUND.equals(localValue)) {
localValue = null;
}
val = operator.evaluate(localValue, r);
} catch (Throwable e) {
result.completeExceptionally(e);
throw e;
}
}
evaluateNext(context, val, iter).whenComplete((r2, t2) -> {
if (t2 != null) {
result.completeExceptionally(t2);
} else {
result.complete(r2);
}
});
processConditionValue(context, next, operator, value, r, result, iter);
}
});
}
Expand All @@ -282,6 +269,43 @@ public String toString() {
return "CompositeCondition [conditions=" + conditions.size() + ", operator=" + operator + "]";
}

static void processConditionValue(SectionResolutionContext context, Condition condition, Operator operator,
Object previousValue, Object conditionValue, CompletableFuture<Object> result, Iterator<Condition> iter) {
Object val;
if (condition.isLogicalComplement()) {
conditionValue = Booleans.isFalsy(conditionValue) ? Boolean.TRUE : Boolean.FALSE;
}
if (operator == null || !operator.isBinary()) {
val = conditionValue;
} else {
// Binary operator
try {
if (Result.NOT_FOUND.equals(conditionValue)) {
conditionValue = null;
}
Object localValue = previousValue;
if (Result.NOT_FOUND.equals(localValue)) {
localValue = null;
}
val = operator.evaluate(localValue, conditionValue);
} catch (Throwable e) {
result.completeExceptionally(e);
throw e;
}
}
if (!iter.hasNext()) {
result.complete(val);
} else {
evaluateNext(context, val, iter).whenComplete((r2, t2) -> {
if (t2 != null) {
result.completeExceptionally(t2);
} else {
result.complete(r2);
}
});
}
}

}

enum Operator {
Expand Down Expand Up @@ -544,7 +568,7 @@ static Condition createCondition(Object param, SectionBlock block, Operator oper
nextOperator = null;
}
}
condition = new CompositeCondition(operator, conditions);
condition = new CompositeCondition(operator, ImmutableList.copyOf(conditions));
} else {
throw new TemplateException("Unsupported param type: " + param);
}
Expand Down
Loading

0 comments on commit b6ddd49

Please sign in to comment.