mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 06:05:01 +07:00
speedup functional expression search (IDEA-159107)
* filter inapplicable expressions without loading AST if possible, via approximate resolve * for that, store the approximate call chains in index * iterate over files once, not for each empty marker Runnable interface separately * don't rely on generic method parameter index: it's huge, memory-hungry and works only in Java
This commit is contained in:
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.psi.impl.java;
|
||||
|
||||
import com.intellij.lang.LighterAST;
|
||||
import com.intellij.lang.LighterASTNode;
|
||||
import com.intellij.psi.impl.source.JavaLightTreeUtil;
|
||||
import com.intellij.psi.impl.source.tree.RecursiveLighterASTNodeWalkingVisitor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static com.intellij.psi.impl.source.tree.JavaElementType.*;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
public class StreamApiDetector {
|
||||
public static final Set<String> STREAM_INTERMEDIATE_METHODS = ContainerUtil.newHashSet("filter",
|
||||
"map", "mapToInt", "mapToLong", "mapToDouble",
|
||||
"flatMap", "flatMapToInt", "flatMapToLong", "flatMapToDouble",
|
||||
"distinct", "sorted", "peek", "limit", "skip",
|
||||
"forEach", "forEachOrdered",
|
||||
"reduce", "collect", "max",
|
||||
"anyMatch", "allMatch", "noneMatch",
|
||||
"findFirst", "findAny");
|
||||
|
||||
static boolean isStreamApiCall(LighterAST tree, LighterASTNode methodCall) {
|
||||
LighterASTNode methodExpr = tree.getChildren(methodCall).get(0);
|
||||
String name = JavaLightTreeUtil.getNameIdentifierText(tree, methodExpr);
|
||||
if (STREAM_INTERMEDIATE_METHODS.contains(name)) {
|
||||
LighterASTNode qualifier = tree.getChildren(methodExpr).get(0);
|
||||
return qualifier.getTokenType() == METHOD_CALL_EXPRESSION && isStreamApiCall(tree, qualifier);
|
||||
}
|
||||
return isRootCall(tree, methodCall, methodExpr, name);
|
||||
}
|
||||
|
||||
private static boolean isRootCall(LighterAST tree, LighterASTNode methodCall, LighterASTNode methodExpr, String name) {
|
||||
if ("stream".equals(name) || "parallelStream".equals(name)) {
|
||||
List<LighterASTNode> argList = JavaLightTreeUtil.getArgList(tree, methodCall);
|
||||
if (argList == null) return false;
|
||||
return argList.isEmpty() || !argList.isEmpty() && hasQualifier(tree, methodExpr, "Arrays");
|
||||
}
|
||||
if ("of".equals(name)) {
|
||||
List<LighterASTNode> argList = JavaLightTreeUtil.getArgList(tree, methodCall);
|
||||
return argList != null && !argList.isEmpty() && hasQualifier(tree, methodExpr, "Stream");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean hasQualifier(LighterAST tree, LighterASTNode methodExpr, String refName) {
|
||||
LighterASTNode node = tree.getChildren(methodExpr).get(0);
|
||||
return node.getTokenType() == REFERENCE_EXPRESSION &&
|
||||
refName.equals(JavaLightTreeUtil.getNameIdentifierText(tree, node)) &&
|
||||
!hasContextVariable(tree, node, refName);
|
||||
}
|
||||
|
||||
private static boolean hasContextVariable(final LighterAST tree, LighterASTNode node, final String varName) {
|
||||
final AtomicBoolean result = new AtomicBoolean(false);
|
||||
new RecursiveLighterASTNodeWalkingVisitor(tree) {
|
||||
@Override
|
||||
public void visitNode(@NotNull LighterASTNode element) {
|
||||
if (element.getTokenType() == LOCAL_VARIABLE && varName.equals(JavaLightTreeUtil.getNameIdentifierText(tree, element))) {
|
||||
result.set(true);
|
||||
}
|
||||
super.visitNode(element);
|
||||
}
|
||||
}.visitNode(tree.getRoot());
|
||||
return result.get();
|
||||
}
|
||||
}
|
||||
+11
-150
@@ -16,10 +16,12 @@
|
||||
package com.intellij.psi.impl.java.stubs;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.io.IOUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
@@ -32,47 +34,26 @@ public class FunctionalExpressionKey {
|
||||
public static final int UNKNOWN_PARAM_COUNT = -1;
|
||||
private final int lambdaParameterCount;
|
||||
private final CoarseType lambdaReturnType;
|
||||
public final Location location;
|
||||
@NotNull private final String knownType;
|
||||
|
||||
public FunctionalExpressionKey(int lambdaParameterCount, @NotNull CoarseType lambdaReturnType, @NotNull Location location) {
|
||||
this.location = location;
|
||||
public FunctionalExpressionKey(int lambdaParameterCount, @NotNull CoarseType lambdaReturnType, @Nullable String knownFunExprType) {
|
||||
this.lambdaParameterCount = lambdaParameterCount;
|
||||
this.lambdaReturnType = lambdaReturnType;
|
||||
this.knownType = StringUtil.notNullize(knownFunExprType);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static FunctionalExpressionKey deserializeKey(@NotNull DataInput dataStream) throws IOException {
|
||||
int parameterCount = dataStream.readByte();
|
||||
CoarseType type = CoarseType.values()[dataStream.readByte()];
|
||||
return new FunctionalExpressionKey(parameterCount, type, deserializeLocation(dataStream));
|
||||
String knownType = IOUtil.readUTF(dataStream);
|
||||
return new FunctionalExpressionKey(parameterCount, type, knownType);
|
||||
}
|
||||
|
||||
public void serializeKey(@NotNull DataOutput dataStream) throws IOException {
|
||||
dataStream.writeByte(lambdaParameterCount);
|
||||
dataStream.writeByte(lambdaReturnType.ordinal());
|
||||
serializeLocation(dataStream);
|
||||
}
|
||||
|
||||
private static Location deserializeLocation(DataInput dataStream) throws IOException {
|
||||
byte locationType = dataStream.readByte();
|
||||
if (locationType == 0) return Location.UNKNOWN;
|
||||
if (locationType == 1) return CallLocation.deserializeCall(dataStream);
|
||||
if (locationType == 2) return TypedLocation.deserializeField(dataStream);
|
||||
throw new AssertionError(locationType);
|
||||
}
|
||||
|
||||
private void serializeLocation(@NotNull DataOutput dataStream) throws IOException {
|
||||
if (location == Location.UNKNOWN) {
|
||||
dataStream.writeByte(0);
|
||||
}
|
||||
else if (location instanceof CallLocation) {
|
||||
dataStream.writeByte(1);
|
||||
((CallLocation)location).serializeCall(dataStream);
|
||||
}
|
||||
else if (location instanceof TypedLocation) {
|
||||
dataStream.writeByte(2);
|
||||
((TypedLocation)location).serializeVariable(dataStream);
|
||||
}
|
||||
IOUtil.writeUTF(dataStream, knownType);
|
||||
}
|
||||
|
||||
public boolean canRepresent(int samParamCount, boolean booleanCompatible, boolean isVoid) {
|
||||
@@ -99,7 +80,7 @@ public class FunctionalExpressionKey {
|
||||
|
||||
if (lambdaParameterCount != key.lambdaParameterCount) return false;
|
||||
if (lambdaReturnType != key.lambdaReturnType) return false;
|
||||
if (!location.equals(key.location)) return false;
|
||||
if (!knownType.equals(key.knownType)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -108,7 +89,7 @@ public class FunctionalExpressionKey {
|
||||
public int hashCode() {
|
||||
int result = lambdaParameterCount;
|
||||
result = 31 * result + lambdaReturnType.ordinal();
|
||||
result = 31 * result + location.hashCode();
|
||||
result = 31 * result + knownType.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -117,130 +98,10 @@ public class FunctionalExpressionKey {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("lambdaParameterCount", lambdaParameterCount)
|
||||
.add("type", lambdaReturnType)
|
||||
.add("location", location)
|
||||
.add("knownType", knownType)
|
||||
.toString();
|
||||
}
|
||||
|
||||
public interface Location {
|
||||
Location UNKNOWN = new Location() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static class CallLocation implements Location {
|
||||
public static final int MAX_ARG_COUNT = 10;
|
||||
@NotNull public final String methodName;
|
||||
public final int methodArgsLength;
|
||||
public final int callArgIndex;
|
||||
public final boolean streamApi;
|
||||
|
||||
public CallLocation(@NotNull String methodName, int methodArgsLength, int callArgIndex, boolean streamApi) {
|
||||
this.methodName = methodName;
|
||||
this.methodArgsLength = Math.min(methodArgsLength, MAX_ARG_COUNT);
|
||||
this.callArgIndex = Math.min(callArgIndex, MAX_ARG_COUNT - 1);
|
||||
this.streamApi = streamApi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof CallLocation)) return false;
|
||||
|
||||
CallLocation location = (CallLocation)o;
|
||||
|
||||
if (methodArgsLength != location.methodArgsLength) return false;
|
||||
if (callArgIndex != location.callArgIndex) return false;
|
||||
if (!methodName.equals(location.methodName)) return false;
|
||||
if (streamApi != location.streamApi) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = methodName.hashCode();
|
||||
result = 31 * result + methodArgsLength;
|
||||
result = 31 * result + callArgIndex;
|
||||
result = 31 * result + (streamApi ? 0 : 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("methodName", methodName)
|
||||
.add("methodArgsLength", methodArgsLength)
|
||||
.add("callArgIndex", callArgIndex)
|
||||
.add("streamApi", streamApi)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Location deserializeCall(DataInput dataStream) throws IOException {
|
||||
String methodName = IOUtil.readUTF(dataStream);
|
||||
int methodArgsLength = dataStream.readByte();
|
||||
int argIndex = dataStream.readByte();
|
||||
boolean streamApi = dataStream.readBoolean();
|
||||
return new CallLocation(methodName, methodArgsLength, argIndex, streamApi);
|
||||
}
|
||||
|
||||
private void serializeCall(@NotNull DataOutput dataStream) throws IOException {
|
||||
IOUtil.writeUTF(dataStream, methodName);
|
||||
dataStream.writeByte(methodArgsLength);
|
||||
dataStream.writeByte(callArgIndex);
|
||||
dataStream.writeBoolean(streamApi);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class TypedLocation implements Location {
|
||||
@NotNull public final String varType;
|
||||
|
||||
public TypedLocation(@NotNull String varType) {
|
||||
this.varType = varType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof TypedLocation)) return false;
|
||||
|
||||
TypedLocation location = (TypedLocation)o;
|
||||
|
||||
if (!varType.equals(location.varType)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return varType.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("fieldType", varType)
|
||||
.toString();
|
||||
}
|
||||
|
||||
public static TypedLocation deserializeField(DataInput dataStream) throws IOException {
|
||||
return new TypedLocation(IOUtil.readUTF(dataStream));
|
||||
}
|
||||
|
||||
public void serializeVariable(DataOutput dataStream) throws IOException {
|
||||
IOUtil.writeUTF(dataStream, varType);
|
||||
}
|
||||
}
|
||||
|
||||
public enum CoarseType {
|
||||
VOID, UNKNOWN, BOOLEAN, NON_VOID
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user