From 5f6d0a72d2d7428cd246d90099262e011a79b822 Mon Sep 17 00:00:00 2001 From: Martin Kouba Date: Wed, 18 May 2022 11:39:04 +0200 Subject: [PATCH] Qute - optimize build steps - round 2 --- .../qute/deployment/QuteProcessor.java | 98 +++++++++++++------ .../generator/ValueResolverGenerator.java | 80 ++++++++++----- 2 files changed, 123 insertions(+), 55 deletions(-) diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java index 99060ea228e0c..10368db18c7a5 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java @@ -528,20 +528,28 @@ public String apply(String id) { // Map implicit class -> set of used members Map> implicitClassToMembersUsed = new HashMap<>(); - Map namespaceTemplateData = templateData.stream() - .filter(TemplateDataBuildItem::hasNamespace) - .collect(Collectors.toMap(TemplateDataBuildItem::getNamespace, Function.identity())); + Map namespaceTemplateData = new HashMap<>(); + for (TemplateDataBuildItem td : templateData) { + if (td.hasNamespace()) { + namespaceTemplateData.put(td.getNamespace(), td); + } + } Map> namespaceExtensionMethods = templateExtensionMethods.stream() .filter(TemplateExtensionMethodBuildItem::hasNamespace) .sorted(Comparator.comparingInt(TemplateExtensionMethodBuildItem::getPriority).reversed()) .collect(Collectors.groupingBy(TemplateExtensionMethodBuildItem::getNamespace)); - List regularExtensionMethods = templateExtensionMethods.stream() - .filter(Predicate.not(TemplateExtensionMethodBuildItem::hasNamespace)).collect(Collectors.toUnmodifiableList()); + List regularExtensionMethods = new ArrayList<>(); + for (TemplateExtensionMethodBuildItem extensionMethod : templateExtensionMethods) { + if (!extensionMethod.hasNamespace()) { + regularExtensionMethods.add(extensionMethod); + } + } LookupConfig lookupConfig = new FixedLookupConfig(index, initDefaultMembersFilter(), false); Map assignableCache = new HashMap<>(); + int expressionsValidated = 0; for (TemplateAnalysis templateAnalysis : templatesAnalysis.getAnalysis()) { // The relevant checked template, may be null @@ -568,7 +576,7 @@ public String apply(String id) { if (defaultValue != null) { Match match; if (defaultValue.isLiteral()) { - match = new Match(index); + match = new Match(index, assignableCache); setMatchValues(match, defaultValue, generatedIdsToMatches, index); } else { match = generatedIdsToMatches.get(defaultValue.getGeneratedId()); @@ -598,8 +606,12 @@ public String apply(String id) { expressionMatches .produce(new TemplateExpressionMatchesBuildItem(templateAnalysis.generatedId, generatedIdsToMatches)); + + expressionsValidated += generatedIdsToMatches.size(); } + LOGGER.debugf("Validated %s expressions", expressionsValidated); + // Register an implicit value resolver for the classes collected during validation for (Entry> entry : implicitClassToMembersUsed.entrySet()) { if (entry.getValue().isEmpty()) { @@ -690,7 +702,7 @@ static Match validateNestedExpressions(QuteConfig config, TemplateAnalysis templ } } - Match match = new Match(index); + Match match = new Match(index, assignableCache); String namespace = expression.getNamespace(); TemplateDataBuildItem templateData = null; @@ -1712,25 +1724,19 @@ static void processLoopElementHint(Match match, IndexView index, Expression expr } else if (match.isClass() || match.isParameterizedType()) { Set closure = Types.getTypeClosure(match.clazz, Types.buildResolvedMap( match.getParameterizedTypeArguments(), match.getTypeParameters(), new HashMap<>(), index), index); - Function firstParamType = t -> t.asParameterizedType().arguments().get(0); // Iterable => Item - matchType = extractMatchType(closure, Names.ITERABLE, firstParamType); + matchType = extractMatchType(closure, Names.ITERABLE, FIRST_PARAM_TYPE_EXTRACT_FUN); if (matchType == null) { // Stream => Long - matchType = extractMatchType(closure, Names.STREAM, firstParamType); + matchType = extractMatchType(closure, Names.STREAM, FIRST_PARAM_TYPE_EXTRACT_FUN); } if (matchType == null) { // Entry => Entry - matchType = extractMatchType(closure, Names.MAP, t -> { - Type[] args = new Type[2]; - args[0] = t.asParameterizedType().arguments().get(0); - args[1] = t.asParameterizedType().arguments().get(1); - return ParameterizedType.create(Names.MAP_ENTRY, args, null); - }); + matchType = extractMatchType(closure, Names.MAP, MAP_ENTRY_EXTRACT_FUN); } if (matchType == null) { // Iterator => Item - matchType = extractMatchType(closure, Names.ITERATOR, firstParamType); + matchType = extractMatchType(closure, Names.ITERATOR, FIRST_PARAM_TYPE_EXTRACT_FUN); } } @@ -1743,19 +1749,48 @@ static void processLoopElementHint(Match match, IndexView index, Expression expr } } + static final Function FIRST_PARAM_TYPE_EXTRACT_FUN = new Function() { + + @Override + public Type apply(Type type) { + return type.asParameterizedType().arguments().get(0); + } + + }; + + static final Function MAP_ENTRY_EXTRACT_FUN = new Function() { + + @Override + public Type apply(Type type) { + Type[] args = new Type[2]; + args[0] = type.asParameterizedType().arguments().get(0); + args[1] = type.asParameterizedType().arguments().get(1); + return ParameterizedType.create(Names.MAP_ENTRY, args, null); + } + + }; + static Type extractMatchType(Set closure, DotName matchName, Function extractFun) { - Type type = closure.stream().filter(t -> t.name().equals(matchName)).findFirst().orElse(null); + Type type = null; + for (Type t : closure) { + if (t.name().equals(matchName)) { + type = t; + } + } return type != null ? extractFun.apply(type) : null; } static class Match { private final IndexView index; + private final Map assignableCache; + private ClassInfo clazz; private Type type; - Match(IndexView index) { + Match(IndexView index, Map assignableCache) { this.index = index; + this.assignableCache = assignableCache; } List getParameterizedTypeArguments() { @@ -1808,17 +1843,20 @@ boolean isEmpty() { } void autoExtractType() { - boolean hasCompletionStage = ValueResolverGenerator.hasCompletionStageInTypeClosure(clazz, index); - boolean hasUni = hasCompletionStage ? false - : ValueResolverGenerator.hasClassInTypeClosure(clazz, Names.UNI, index); - if (hasCompletionStage || hasUni) { - Set closure = Types.getTypeClosure(clazz, Types.buildResolvedMap( - getParameterizedTypeArguments(), getTypeParameters(), new HashMap<>(), index), index); - Function firstParamType = t -> t.asParameterizedType().arguments().get(0); - // CompletionStage> => List - // Uni> => List - this.type = extractMatchType(closure, hasCompletionStage ? Names.COMPLETION_STAGE : Names.UNI, firstParamType); - this.clazz = index.getClassByName(type.name()); + if (clazz != null) { + boolean hasCompletionStage = Types.isAssignableFrom(Names.COMPLETION_STAGE, clazz.name(), index, + assignableCache); + boolean hasUni = hasCompletionStage ? false + : Types.isAssignableFrom(Names.UNI, clazz.name(), index, assignableCache); + if (hasCompletionStage || hasUni) { + Set closure = Types.getTypeClosure(clazz, Types.buildResolvedMap( + getParameterizedTypeArguments(), getTypeParameters(), new HashMap<>(), index), index); + // CompletionStage> => List + // Uni> => List + this.type = extractMatchType(closure, hasCompletionStage ? Names.COMPLETION_STAGE : Names.UNI, + FIRST_PARAM_TYPE_EXTRACT_FUN); + this.clazz = index.getClassByName(type.name()); + } } } } diff --git a/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java b/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java index acbd29e41db70..177ffddf2f1e0 100644 --- a/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java +++ b/independent-projects/qute/generator/src/main/java/io/quarkus/qute/generator/ValueResolverGenerator.java @@ -39,7 +39,6 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Pattern; -import java.util.stream.Collectors; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.AnnotationTarget.Kind; @@ -273,15 +272,24 @@ private boolean implementResolve(ClassCreator valueResolver, String clazzName, C Function fieldToGetterFun = forceGettersFunction != null ? forceGettersFunction.apply(clazz) : null; // First collect and sort methods (getters must come before is/has properties, etc.) - List methods = clazz.methods().stream().filter(filter::test).map(MethodKey::new).sorted() - .collect(Collectors.toList()); + List methods = new ArrayList<>(); + for (MethodInfo method : clazz.methods()) { + if (filter.test(method)) { + methods.add(new MethodKey(method)); + } + } + methods.sort(null); + if (!ignoreSuperclasses && !clazz.isEnum()) { DotName superName = clazz.superName(); while (superName != null && !superName.equals(DotNames.OBJECT)) { ClassInfo superClass = index.getClassByName(superName); if (superClass != null) { - methods.addAll( - superClass.methods().stream().filter(filter::test).map(MethodKey::new).collect(Collectors.toSet())); + for (MethodInfo method : superClass.methods()) { + if (filter.test(method)) { + methods.add(new MethodKey(method)); + } + } superName = superClass.superName(); } else { superName = null; @@ -290,7 +298,12 @@ private boolean implementResolve(ClassCreator valueResolver, String clazzName, C } } - List fields = clazz.fields().stream().filter(filter::test).collect(Collectors.toList()); + List fields = new ArrayList<>(); + for (FieldInfo field : clazz.fields()) { + if (filter.test(field)) { + fields.add(field); + } + } if (!fields.isEmpty()) { BytecodeCreator zeroParamsBranch = resolve.ifNonZero(paramsCount).falseBranch(); for (FieldInfo field : fields) { @@ -437,24 +450,28 @@ private boolean implementNamespaceResolve(ClassCreator valueResolver, String cla ResultHandle paramsCount = resolve.invokeInterfaceMethod(Descriptors.COLLECTION_SIZE, params); // First collect static members - List methods = clazz.methods().stream() - .filter(filter::test) - .map(MethodKey::new) - .sorted() - .collect(Collectors.toList()); + List staticMethods = new ArrayList<>(); + for (MethodInfo method : clazz.methods()) { + if (filter.test(method)) { + staticMethods.add(new MethodKey(method)); + } + } + staticMethods.sort(null); - List fields = clazz.fields().stream() - .filter(filter::test) - .collect(Collectors.toList()); + List staticFields = new ArrayList<>(); + for (FieldInfo field : clazz.fields()) { + if (filter.test(field)) { + staticFields.add(field); + } + } - if (methods.isEmpty() && fields.isEmpty()) { + if (staticMethods.isEmpty() && staticFields.isEmpty()) { return false; } - // Static fields - if (!fields.isEmpty()) { + if (!staticFields.isEmpty()) { BytecodeCreator zeroParamsBranch = resolve.ifNonZero(paramsCount).falseBranch(); - for (FieldInfo field : fields) { + for (FieldInfo field : staticFields) { LOGGER.debugf("Static field added: %s", field); // Match field name BytecodeCreator fieldMatch = zeroParamsBranch @@ -468,13 +485,12 @@ private boolean implementNamespaceResolve(ClassCreator valueResolver, String cla } } - // Static methods - if (!methods.isEmpty()) { + if (!staticMethods.isEmpty()) { // name, number of params -> list of methods Map> matches = new HashMap<>(); Map> varargsMatches = new HashMap<>(); - for (MethodKey methodKey : methods) { + for (MethodKey methodKey : staticMethods) { MethodInfo method = methodKey.method; List methodParams = method.parameters(); if (methodParams.isEmpty()) { @@ -991,13 +1007,27 @@ private Predicate initFilters(AnnotationInstance templateData) // @TemplateData is present AnnotationValue ignoreValue = templateData.value(IGNORE); if (ignoreValue != null) { - List ignore = Arrays.asList(ignoreValue.asStringArray()).stream().map(Pattern::compile) - .collect(Collectors.toList()); + List ignores = new ArrayList<>(); + for (String pattern : Arrays.asList(ignoreValue.asStringArray())) { + ignores.add(Pattern.compile(pattern)); + } filter = filter.and(t -> { if (t.kind() == Kind.FIELD) { - return !ignore.stream().anyMatch(p -> p.matcher(t.asField().name()).matches()); + String fieldName = t.asField().name(); + for (Pattern p : ignores) { + if (p.matcher(fieldName).matches()) { + return false; + } + } + return true; } else { - return !ignore.stream().anyMatch(p -> p.matcher(t.asMethod().name()).matches()); + String methodName = t.asMethod().name(); + for (Pattern p : ignores) { + if (p.matcher(methodName).matches()) { + return false; + } + } + return true; } }); }