From 84e41c9f76745583679ee67f59ec08c59fd1aa0f Mon Sep 17 00:00:00 2001 From: Egor Ushakov Date: Fri, 26 Jul 2019 14:41:31 +0300 Subject: [PATCH] optimized version of concreteMethodByName which is much faster in most cases GitOrigin-RevId: cd49d6e8d5b38eeff1e81a7c8fec461078133d87 --- .../expression/AssertStatementEvaluator.java | 5 +-- .../expression/BoxingEvaluator.java | 7 ++-- .../expression/LiteralEvaluator.java | 5 +-- .../expression/UnBoxingEvaluator.java | 19 ++-------- .../debugger/impl/ClassLoadingUtils.java | 6 ++-- .../debugger/impl/DebuggerUtilsEx.java | 14 -------- .../ui/impl/watch/ValueDescriptorImpl.java | 2 +- .../ui/tree/render/BatchEvaluator.java | 11 +++--- .../ui/tree/render/ClassObjectRenderer.java | 2 +- .../ui/tree/render/ColorObjectRenderer.java | 3 +- .../StackTraceElementObjectRenderer.java | 3 +- .../ui/tree/render/ToStringRenderer.java | 3 +- .../debugger/engine/DebuggerUtils.java | 35 ++++++++++++++++++- 13 files changed, 61 insertions(+), 54 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/AssertStatementEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/AssertStatementEvaluator.java index fcc1b564b5ae..06bfe5de2505 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/AssertStatementEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/AssertStatementEvaluator.java @@ -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()) { diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BoxingEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BoxingEvaluator.java index 2bdfd4f5abad..b750befdd09b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BoxingEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/BoxingEvaluator.java @@ -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"); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/LiteralEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/LiteralEvaluator.java index 99377e847aa5..6389cb242b37 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/LiteralEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/LiteralEvaluator.java @@ -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()); } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnBoxingEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnBoxingEvaluator.java index 47c14dda3da7..40c468bd3d97 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnBoxingEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnBoxingEvaluator.java @@ -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); diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/ClassLoadingUtils.java b/java/debugger/impl/src/com/intellij/debugger/impl/ClassLoadingUtils.java index ab41ec185afa..545c3d30feb9 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/ClassLoadingUtils.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/ClassLoadingUtils.java @@ -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), diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java index 5923da05e776..e95e17d99bd5 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java @@ -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; - } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java index a4c5c92b9c58..f4b4ea721693 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ValueDescriptorImpl.java @@ -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()); } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BatchEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BatchEvaluator.java index b6877aa7808f..e9d9e53e9fb9 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BatchEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BatchEvaluator.java @@ -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;"); } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassObjectRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassObjectRenderer.java index b08cdc690725..5be8957659cd 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassObjectRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassObjectRenderer.java @@ -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(); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ColorObjectRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ColorObjectRenderer.java index afdd838d9e66..b62a4c82e599 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ColorObjectRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ColorObjectRenderer.java @@ -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 diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/StackTraceElementObjectRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/StackTraceElementObjectRenderer.java index efc0664b012a..4c29c1d2280f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/StackTraceElementObjectRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/StackTraceElementObjectRenderer.java @@ -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 = diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ToStringRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ToStringRenderer.java index 8644e9c3de3a..742cdb3d9711 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ToStringRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ToStringRenderer.java @@ -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; diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/DebuggerUtils.java b/java/debugger/openapi/src/com/intellij/debugger/engine/DebuggerUtils.java index 5c35c808f6ce..07ea00c7489e 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/engine/DebuggerUtils.java +++ b/java/debugger/openapi/src/com/intellij/debugger/engine/DebuggerUtils.java @@ -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 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 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) ||