mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +07:00
[debugger] made invocation helper use method handles
GitOrigin-RevId: 340efd6c3cfacd29266b704ef80f0f9f3e2f3a11
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1d58994a43
commit
e8576838b4
@@ -1506,9 +1506,9 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
|
|||||||
|
|
||||||
private static boolean shouldInvokeWithHandler(@NotNull Method method, int invocationOptions) {
|
private static boolean shouldInvokeWithHandler(@NotNull Method method, int invocationOptions) {
|
||||||
return Registry.is("debugger.evaluate.method.helper") &&
|
return Registry.is("debugger.evaluate.method.helper") &&
|
||||||
!BitUtil.isSet(invocationOptions, ObjectReference.INVOKE_NONVIRTUAL) &&
|
!BitUtil.isSet(invocationOptions, ObjectReference.INVOKE_NONVIRTUAL) && // TODO: support
|
||||||
!DebuggerUtils.isPrimitiveType(method.returnTypeName()) &&
|
!DebuggerUtils.isPrimitiveType(method.returnTypeName()) &&
|
||||||
!DebuggerUtilsEx.isVoid(method) &&
|
(!DebuggerUtilsEx.isVoid(method) || method.isConstructor()) &&
|
||||||
!"clone".equals(method.name());
|
!"clone".equals(method.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1517,38 +1517,34 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
|
|||||||
@NotNull Method method,
|
@NotNull Method method,
|
||||||
@NotNull List<? extends Value> args,
|
@NotNull List<? extends Value> args,
|
||||||
@NotNull EvaluationContextImpl evaluationContext) throws EvaluateException {
|
@NotNull EvaluationContextImpl evaluationContext) throws EvaluateException {
|
||||||
try {
|
DebugProcessImpl debugProcess = evaluationContext.getDebugProcess();
|
||||||
DebugProcessImpl debugProcess = evaluationContext.getDebugProcess();
|
VirtualMachineProxyImpl virtualMachineProxy = debugProcess.getVirtualMachineProxy();
|
||||||
VirtualMachineProxyImpl virtualMachineProxy = debugProcess.getVirtualMachineProxy();
|
ArrayList<Value> invokerArgs = new ArrayList<>();
|
||||||
|
|
||||||
ArrayList<Value> invokerArgs = new ArrayList<>();
|
ReferenceType lookupClass =
|
||||||
invokerArgs.add(type.classObject()); // class
|
debugProcess.findClass(evaluationContext, "java.lang.invoke.MethodHandles$Lookup", evaluationContext.getClassLoader());
|
||||||
invokerArgs.add(objRef); // object
|
ObjectReference implLookup = (ObjectReference)lookupClass.getValue(lookupClass.fieldByName("IMPL_LOOKUP"));
|
||||||
invokerArgs.add(DebuggerUtilsEx.mirrorOfString(method.name(), virtualMachineProxy, evaluationContext)); // method name
|
|
||||||
// argument types
|
|
||||||
List<ClassObjectReference> types = mapArgumentTypes(method, evaluationContext);
|
|
||||||
ArrayType objectArrayClass = (ArrayType)debugProcess.findClass(
|
|
||||||
evaluationContext,
|
|
||||||
CommonClassNames.JAVA_LANG_OBJECT + "[]",
|
|
||||||
evaluationContext.getClassLoader());
|
|
||||||
ArrayReference arrayTypes = DebuggerUtilsEx.mirrorOfArray(objectArrayClass, types, evaluationContext);
|
|
||||||
invokerArgs.add(arrayTypes);
|
|
||||||
// argument values
|
|
||||||
List<Value> boxedArgs = new ArrayList<>(args.size());
|
|
||||||
for (Value arg : args) {
|
|
||||||
boxedArgs.add((Value)BoxingEvaluator.box(arg, evaluationContext));
|
|
||||||
}
|
|
||||||
if (method.isVarArgs() /*|| (method.isBridge() && ContainerUtil.getLastItem(method.argumentTypes()) instanceof ArrayType)*/) {
|
|
||||||
MethodImpl.handleVarArgs(method, boxedArgs);
|
|
||||||
}
|
|
||||||
ArrayReference arrayArgs = DebuggerUtilsEx.mirrorOfArray(objectArrayClass, boxedArgs, evaluationContext);
|
|
||||||
invokerArgs.add(arrayArgs);
|
|
||||||
|
|
||||||
return DebuggerUtilsImpl.invokeHelperMethod(evaluationContext, MethodInvoker.class, "invoke", invokerArgs, false);
|
invokerArgs.add(implLookup); // lookup
|
||||||
}
|
invokerArgs.add(type.classObject()); // class
|
||||||
catch (ClassNotLoadedException | InvalidTypeException e) {
|
invokerArgs.add(objRef); // object
|
||||||
throw new EvaluateException(e.getMessage(), e);
|
invokerArgs.add(DebuggerUtilsEx.mirrorOfString(method.name(), virtualMachineProxy, evaluationContext)); // method name
|
||||||
|
invokerArgs.add(DebuggerUtilsEx.mirrorOfString(method.signature(), virtualMachineProxy, evaluationContext)); // method descriptor
|
||||||
|
|
||||||
|
// argument values
|
||||||
|
List<Value> boxedArgs = new ArrayList<>(args.size());
|
||||||
|
for (Value arg : args) {
|
||||||
|
boxedArgs.add((Value)BoxingEvaluator.box(arg, evaluationContext));
|
||||||
}
|
}
|
||||||
|
ArrayType objectArrayClass = (ArrayType)debugProcess.findClass(
|
||||||
|
evaluationContext,
|
||||||
|
CommonClassNames.JAVA_LANG_OBJECT + "[]",
|
||||||
|
evaluationContext.getClassLoader());
|
||||||
|
|
||||||
|
ArrayReference arrayArgs = DebuggerUtilsEx.mirrorOfArray(objectArrayClass, boxedArgs, evaluationContext);
|
||||||
|
invokerArgs.add(arrayArgs);
|
||||||
|
invokerArgs.add(evaluationContext.getClassLoader()); // class laoder
|
||||||
|
return DebuggerUtilsImpl.invokeHelperMethod(evaluationContext, MethodInvoker.class, "invoke", invokerArgs, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<ClassObjectReference> mapArgumentTypes(@NotNull Method method, @NotNull EvaluationContextImpl evaluationContext)
|
private static List<ClassObjectReference> mapArgumentTypes(@NotNull Method method, @NotNull EvaluationContextImpl evaluationContext)
|
||||||
@@ -1679,17 +1675,22 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb
|
|||||||
@NotNull List<? extends Value> args,
|
@NotNull List<? extends Value> args,
|
||||||
final int invocationOptions,
|
final int invocationOptions,
|
||||||
boolean internalEvaluate) throws EvaluateException {
|
boolean internalEvaluate) throws EvaluateException {
|
||||||
InvokeCommand<ObjectReference> invokeCommand = new InvokeCommand<>(method, args, (EvaluationContextImpl)evaluationContext) {
|
if (!internalEvaluate && shouldInvokeWithHandler(method, invocationOptions)) {
|
||||||
@Override
|
return (ObjectReference)invokeWithHelper(classType, null, method, args, (EvaluationContextImpl)evaluationContext);
|
||||||
protected ObjectReference invokeMethod(ThreadReference thread, int invokePolicy, Method method, List<? extends Value> args)
|
}
|
||||||
throws InvocationException, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException {
|
else {
|
||||||
if (LOG.isDebugEnabled()) {
|
InvokeCommand<ObjectReference> invokeCommand = new InvokeCommand<>(method, args, (EvaluationContextImpl)evaluationContext) {
|
||||||
LOG.debug("New instance " + classType.name() + "." + method.name());
|
@Override
|
||||||
|
protected ObjectReference invokeMethod(ThreadReference thread, int invokePolicy, Method method, List<? extends Value> args)
|
||||||
|
throws InvocationException, ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException {
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug("New instance " + classType.name() + "." + method.name());
|
||||||
|
}
|
||||||
|
return classType.newInstance(thread, method, args, invokePolicy | invocationOptions);
|
||||||
}
|
}
|
||||||
return classType.newInstance(thread, method, args, invokePolicy | invocationOptions);
|
};
|
||||||
}
|
return invokeCommand.start(internalEvaluate);
|
||||||
};
|
}
|
||||||
return invokeCommand.start(internalEvaluate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearCashes(@MagicConstant(flagsFromClass = EventRequest.class) int suspendPolicy) {
|
public void clearCashes(@MagicConstant(flagsFromClass = EventRequest.class) int suspendPolicy) {
|
||||||
|
|||||||
@@ -1,57 +1,52 @@
|
|||||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||||
package com.intellij.rt.debugger;
|
package com.intellij.rt.debugger;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.ArrayList;
|
import java.lang.invoke.MethodType;
|
||||||
import java.util.List;
|
import java.lang.invoke.WrongMethodTypeException;
|
||||||
|
|
||||||
public final class MethodInvoker {
|
public final class MethodInvoker {
|
||||||
// TODO: may leak objects here
|
// TODO: may leak objects here
|
||||||
static ThreadLocal<Object> returnValue = new ThreadLocal<>();
|
static ThreadLocal<Object> returnValue = new ThreadLocal<>();
|
||||||
|
|
||||||
public static Object invoke(Class<?> cls, Object obj, String name, Class<?>[] parameterTypes, Object[] args)
|
public static Object invoke(MethodHandles.Lookup lookup,
|
||||||
|
Class<?> cls,
|
||||||
|
Object obj,
|
||||||
|
String name,
|
||||||
|
String descriptor,
|
||||||
|
Object[] args,
|
||||||
|
ClassLoader loader)
|
||||||
throws Throwable {
|
throws Throwable {
|
||||||
ArrayList<Method> methods = new ArrayList<>();
|
|
||||||
// TODO: better collect methods lazily
|
|
||||||
addMatchingMethods(methods, cls, name, parameterTypes);
|
|
||||||
for (Method method : methods) {
|
|
||||||
try {
|
|
||||||
method.setAccessible(true);
|
|
||||||
Object res = method.invoke(obj, args);
|
|
||||||
returnValue.set(res); // to avoid gc for the result object
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
catch (InvocationTargetException e) {
|
|
||||||
throw e.getCause();
|
|
||||||
}
|
|
||||||
catch (IllegalAccessException | SecurityException e) {
|
|
||||||
// try the next method
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
// InaccessibleObjectException appeared in jdk 9
|
|
||||||
if (!"java.lang.reflect.InaccessibleObjectException".equals(e.getClass().getName())) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
// try the next method
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new RuntimeException("Unable to execute the method " + name);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: avoid recursion
|
|
||||||
private static void addMatchingMethods(List<Method> methods, Class<?> cls, String name, Class<?>[] parameterTypes) {
|
|
||||||
try {
|
try {
|
||||||
methods.add(cls.getDeclaredMethod(name, parameterTypes));
|
MethodType mt = MethodType.fromMethodDescriptorString(descriptor, loader);
|
||||||
|
MethodHandle method;
|
||||||
|
if ("<init>".equals(name)) {
|
||||||
|
method = lookup.findConstructor(cls, mt);
|
||||||
|
}
|
||||||
|
else if (obj != null) {
|
||||||
|
method = lookup.findVirtual(cls, name, mt).bindTo(obj);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
method = lookup.findStatic(cls, name, mt);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object result;
|
||||||
|
|
||||||
|
// handle the case where null is passed as the vararg array
|
||||||
|
// TODO: handle when vararg is not the only parameter
|
||||||
|
if (mt.parameterCount() == 1 && args.length == 1 && args[0] == null && mt.parameterType(0).isArray()) {
|
||||||
|
result = method.invoke((Object[])null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = method.invokeWithArguments(args);
|
||||||
|
}
|
||||||
|
returnValue.set(result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
catch (NoSuchMethodException ignored) {
|
catch (WrongMethodTypeException | ClassCastException e) {
|
||||||
}
|
e.printStackTrace();
|
||||||
Class<?> superclass = cls.getSuperclass();
|
throw e;
|
||||||
if (superclass != null) {
|
|
||||||
addMatchingMethods(methods, superclass, name, parameterTypes);
|
|
||||||
}
|
|
||||||
for (Class<?> anInterface : cls.getInterfaces()) {
|
|
||||||
addMatchingMethods(methods, anInterface, name, parameterTypes);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user