optimized version of concreteMethodByName which is much faster in most cases

GitOrigin-RevId: cd49d6e8d5b38eeff1e81a7c8fec461078133d87
This commit is contained in:
Egor Ushakov
2019-07-26 15:02:42 +03:00
committed by intellij-monorepo-bot
parent 68b8d4365d
commit 84e41c9f76
13 changed files with 61 additions and 54 deletions
@@ -1,6 +1,7 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.debugger.engine.evaluation.expression;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
@@ -28,7 +29,7 @@ public class AssertStatementEvaluator implements Evaluator {
throw EvaluateExceptionUtil.NULL_STACK_FRAME;
}
ClassObjectReference classObjectReference = frameProxy.location().declaringType().classObject();
Method method = ((ClassType)classObjectReference.referenceType()).concreteMethodByName("desiredAssertionStatus", "()Z");
Method method = DebuggerUtils.findMethod(classObjectReference.referenceType(), "desiredAssertionStatus", "()Z");
if (method != null) {
Value res = context.getDebugProcess().invokeMethod(context, classObjectReference, method, Collections.emptyList());
if (res instanceof BooleanValue && !((BooleanValue)res).value()) {
@@ -1,7 +1,8 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.debugger.engine.evaluation.expression;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.JVMNameUtil;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
@@ -44,9 +45,9 @@ public class BoxingEvaluator implements Evaluator{
final ClassType wrapperClass = (ClassType)process.findClass(context, wrapperTypeName, null);
final String methodSignature = "(" + JVMNameUtil.getPrimitiveSignature(value.type().name()) + ")L" + wrapperTypeName.replace('.', '/') + ";";
Method method = wrapperClass.concreteMethodByName("valueOf", methodSignature);
Method method = DebuggerUtils.findMethod(wrapperClass, "valueOf", methodSignature);
if (method == null) { // older JDK version
method = wrapperClass.concreteMethodByName(JVMNameUtil.CONSTRUCTOR_NAME, methodSignature);
method = DebuggerUtils.findMethod(wrapperClass, JVMNameUtil.CONSTRUCTOR_NAME, methodSignature);
}
if (method == null) {
throw new EvaluateException("Cannot construct wrapper object for value of type " + value.type() + ": Unable to find either valueOf() or constructor method");
@@ -1,4 +1,4 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
/*
* Class LiteralEvaluator
@@ -7,6 +7,7 @@
package com.intellij.debugger.engine.evaluation.expression;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
@@ -54,7 +55,7 @@ class LiteralEvaluator implements Evaluator {
StringReference str = DebuggerUtilsEx.mirrorOfString((String)myValue, vm, context);
// intern starting from jdk 7
if (Registry.is("debugger.intern.string.literals") && vm.versionHigher("1.7")) {
Method internMethod = ((ClassType)str.referenceType()).concreteMethodByName("intern", "()Ljava/lang/String;");
Method internMethod = DebuggerUtils.findMethod(str.referenceType(), "intern", "()Ljava/lang/String;");
if (internMethod != null) {
return (StringReference)context.getDebugProcess().invokeMethod(context, str, internMethod, Collections.emptyList());
}
@@ -1,20 +1,7 @@
/*
* Copyright 2000-2017 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.
*/
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.debugger.engine.evaluation.expression;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.openapi.diagnostic.Logger;
@@ -83,7 +70,7 @@ public class UnBoxingEvaluator implements Evaluator {
return primitiveValue;
}
Method method = ((ClassType)value.referenceType()).concreteMethodByName(conversionMethodName, conversionMethodSignature);
Method method = DebuggerUtils.findMethod(value.referenceType(), conversionMethodName, conversionMethodSignature);
if (method == null) {
throw new EvaluateException("Cannot convert to primitive value of type " + value.type() + ": Unable to find method " +
conversionMethodName + conversionMethodSignature);
@@ -1,4 +1,4 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.debugger.impl;
import com.intellij.debugger.engine.DebugProcess;
@@ -31,7 +31,7 @@ public class ClassLoadingUtils {
ArrayType arrayType = (ArrayType)context.getDebugProcess().findClass(context, "java.net.URL[]", context.getClassLoader());
ArrayReference emptyUrlArray = DebuggerUtilsEx.mirrorOfArray(arrayType, 0, context);
ClassType loaderClass = (ClassType)process.findClass(context, "java.net.URLClassLoader", context.getClassLoader());
Method ctorMethod = loaderClass.concreteMethodByName(JVMNameUtil.CONSTRUCTOR_NAME, "([Ljava/net/URL;Ljava/lang/ClassLoader;)V");
Method ctorMethod = DebuggerUtils.findMethod(loaderClass, JVMNameUtil.CONSTRUCTOR_NAME, "([Ljava/net/URL;Ljava/lang/ClassLoader;)V");
return context.computeAndKeep(() -> (ClassLoaderReference)process
.newInstance(context, loaderClass, ctorMethod, Arrays.asList(emptyUrlArray, context.getClassLoader())));
}
@@ -48,7 +48,7 @@ public class ClassLoadingUtils {
try {
VirtualMachineProxyImpl proxy = (VirtualMachineProxyImpl)process.getVirtualMachineProxy();
Method defineMethod =
((ClassType)classLoader.referenceType()).concreteMethodByName("defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
DebuggerUtils.findMethod(classLoader.referenceType(), "defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
process.invokeMethod(context, classLoader, defineMethod,
Arrays.asList(DebuggerUtilsEx.mirrorOfString(name, proxy, context),
mirrorOf(bytes, context, process),
@@ -1084,18 +1084,4 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils {
}
});
}
@Nullable
public static Method concreteMethodByName(@NotNull ClassType type, String name, String signature) {
//TODO: currently does not include interface default methods
while (type != null) {
for (Method candidate : type.methods()) {
if (candidate.name().equals(name) && candidate.signature().equals(signature) && !candidate.isAbstract()) {
return candidate;
}
}
type = type.superclass();
}
return null;
}
}
@@ -229,7 +229,7 @@ public abstract class ValueDescriptorImpl extends NodeDescriptorImpl implements
@Nullable
protected static Value invokeExceptionGetStackTrace(ObjectReference exceptionObj, EvaluationContextImpl evaluationContext)
throws EvaluateException {
Method method = ((ClassType)exceptionObj.referenceType()).concreteMethodByName("getStackTrace", "()[Ljava/lang/StackTraceElement;");
Method method = DebuggerUtils.findMethod(exceptionObj.referenceType(), "getStackTrace", "()[Ljava/lang/StackTraceElement;");
if (method != null) {
return evaluationContext.getDebugProcess().invokeMethod(evaluationContext, exceptionObj, method, Collections.emptyList());
}
@@ -1,11 +1,8 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.debugger.ui.tree.render;
import com.intellij.debugger.DebuggerManager;
import com.intellij.debugger.engine.DebugProcess;
import com.intellij.debugger.engine.DebugProcessListener;
import com.intellij.debugger.engine.JVMNameUtil;
import com.intellij.debugger.engine.SuspendContext;
import com.intellij.debugger.engine.*;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
import com.intellij.debugger.engine.evaluation.EvaluationContext;
@@ -76,7 +73,7 @@ public class BatchEvaluator {
}
if (batchEvaluatorClass != null) {
Method constructor = batchEvaluatorClass.concreteMethodByName(JVMNameUtil.CONSTRUCTOR_NAME, "()V");
Method constructor = DebuggerUtils.findMethod(batchEvaluatorClass, JVMNameUtil.CONSTRUCTOR_NAME, "()V");
if(constructor != null){
ObjectReference evaluator = null;
try {
@@ -88,7 +85,7 @@ public class BatchEvaluator {
myBatchEvaluatorObject = evaluator;
if(myBatchEvaluatorObject != null) {
myBatchEvaluatorMethod = batchEvaluatorClass.concreteMethodByName("evaluate", "([Ljava/lang/Object;)[Ljava/lang/Object;");
myBatchEvaluatorMethod = DebuggerUtils.findMethod(batchEvaluatorClass, "evaluate", "([Ljava/lang/Object;)[Ljava/lang/Object;");
}
}
}
@@ -41,7 +41,7 @@ class ClassObjectRenderer extends CompoundReferenceRenderer implements FullValue
public void evaluate(@NotNull XFullValueEvaluationCallback callback) {
Value value = valueDescriptor.getValue();
ClassType type = ((ClassType)value.type());
Method nameMethod = type.concreteMethodByName("getName", "()Ljava/lang/String;");
Method nameMethod = DebuggerUtils.findMethod(type, "getName", "()Ljava/lang/String;");
if (nameMethod != null) {
try {
final DebugProcessImpl process = evaluationContext.getDebugProcess();
@@ -2,6 +2,7 @@
package com.intellij.debugger.ui.tree.render;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContext;
@@ -33,7 +34,7 @@ class ColorObjectRenderer extends CompoundReferenceRenderer {
ReferenceType refType = objRef.referenceType();
if (refType instanceof ClassType) {
Value rgbValue = null;
Method getRGBMethod = ((ClassType)refType).concreteMethodByName("getRGB", "()I");
Method getRGBMethod = DebuggerUtils.findMethod(refType, "getRGB", "()I");
if (getRGBMethod != null) {
ReferenceType rgbMethodDeclaringType = getRGBMethod.declaringType();
if (rgbMethodDeclaringType.name().equals(getClassName())) { // getRGB is not overridden
@@ -2,6 +2,7 @@
package com.intellij.debugger.ui.tree.render;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.FullValueEvaluatorProvider;
import com.intellij.debugger.engine.JavaValue;
import com.intellij.debugger.engine.evaluation.EvaluateException;
@@ -40,7 +41,7 @@ class StackTraceElementObjectRenderer extends CompoundReferenceRenderer implemen
public void evaluate(@NotNull XFullValueEvaluationCallback callback) {
Value value = valueDescriptor.getValue();
ClassType type = ((ClassType)value.type());
Method toString = type.concreteMethodByName("toString", "()Ljava/lang/String;");
Method toString = DebuggerUtils.findMethod(type, "toString", "()Ljava/lang/String;");
if (toString != null) {
try {
Value res =
@@ -127,10 +127,9 @@ public class ToStringRenderer extends NodeRendererImpl implements OnDemandRender
return overridesToString(type);
}
@SuppressWarnings({"HardCodedStringLiteral"})
private static boolean overridesToString(Type type) {
if (type instanceof ClassType) {
Method toStringMethod = DebuggerUtilsEx.concreteMethodByName((ClassType)type, "toString", "()Ljava/lang/String;");
Method toStringMethod = DebuggerUtils.findMethod((ReferenceType)type, "toString", "()Ljava/lang/String;");
return toStringMethod != null && !CommonClassNames.JAVA_LANG_OBJECT.equals(toStringMethod.declaringType().name());
}
return false;
@@ -162,7 +162,7 @@ public abstract class DebuggerUtils {
Method method = null;
if (methodSignature != null) {
if (refType instanceof ClassType) {
method = ((ClassType)refType).concreteMethodByName(methodName, methodSignature);
method = concreteMethodByName((ClassType)refType, methodName, methodSignature);
}
if (method == null) {
method = ContainerUtil.getFirstItem(refType.methodsByName(methodName, methodSignature));
@@ -174,6 +174,39 @@ public abstract class DebuggerUtils {
return method;
}
/**
* Optimized version of {@link com.sun.jdi.ClassType#concreteMethodByName(java.lang.String, java.lang.String)}.
* It does not gather all visible methods before checking so can return early
*/
@Nullable
private static Method concreteMethodByName(@NotNull ClassType type, @NotNull String name, @NotNull String signature) {
LinkedList<InterfaceType> interfaces = new LinkedList<>();
// first check classes
while (type != null) {
for (Method candidate : type.methods()) {
if (candidate.name().equals(name) && candidate.signature().equals(signature)) {
return !candidate.isAbstract() ? candidate : null;
}
}
interfaces.addAll(type.interfaces());
type = type.superclass();
}
// then interfaces
Set<InterfaceType> checkedInterfaces = new HashSet<>();
InterfaceType iface;
while ((iface = interfaces.poll()) != null) {
if (checkedInterfaces.add(iface)) {
for (Method candidate : iface.methods()) {
if (candidate.name().equals(name) && candidate.signature().equals(signature) && !candidate.isAbstract()) {
return candidate;
}
}
interfaces.addAll(0, iface.superinterfaces());
}
}
return null;
}
public static boolean isNumeric(Value value) {
return value != null &&
(isInteger(value) ||