define helper classes if not available in classpath (remote session for example)

This commit is contained in:
Egor.Ushakov
2015-02-27 21:12:32 +03:00
parent 05f6c60dd8
commit b0345f803b
4 changed files with 169 additions and 87 deletions
@@ -26,6 +26,7 @@ import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.DebuggerUtils;
import com.intellij.debugger.engine.JVMName;
import com.intellij.debugger.engine.evaluation.*;
import com.intellij.debugger.impl.ClassLoadingUtils;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
import com.intellij.openapi.diagnostic.Logger;
@@ -180,11 +181,13 @@ public class MethodEvaluator implements Evaluator {
}
// fix for default methods in interfaces, see IDEA-124066
if (myCheckDefaultInterfaceMethod && jdiMethod.declaringType() instanceof InterfaceType) {
return invokeDefaultMethod(debugProcess, context, objRef, myMethodName);
}
else {
return debugProcess.invokeMethod(context, objRef, jdiMethod, args);
try {
return invokeDefaultMethod(debugProcess, context, objRef, myMethodName);
} catch (EvaluateException e) {
LOG.info(e);
}
}
return debugProcess.invokeMethod(context, objRef, jdiMethod, args);
}
catch (Exception e) {
if (LOG.isDebugEnabled()) {
@@ -197,10 +200,8 @@ public class MethodEvaluator implements Evaluator {
// only methods without arguments for now
private static Value invokeDefaultMethod(DebugProcess debugProcess, EvaluationContext evaluationContext,
Value obj, String name)
throws EvaluateException, ClassNotLoadedException, InvalidTypeException {
ClassType invokerClass = (ClassType)debugProcess.findClass(
evaluationContext, DefaultMethodInvoker.class.getName(),
evaluationContext.getClassLoader());
throws EvaluateException {
ClassType invokerClass = ClassLoadingUtils.getHelperClass(DefaultMethodInvoker.class.getName(), evaluationContext, debugProcess);
if (invokerClass != null) {
List<Method> methods = invokerClass.methodsByName("invoke");
@@ -0,0 +1,143 @@
/*
* Copyright 2000-2015 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.debugger.impl;
import com.intellij.debugger.engine.DebugProcess;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContext;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
import com.intellij.openapi.util.io.StreamUtil;
import com.intellij.rt.debugger.ImageSerializer;
import com.sun.jdi.*;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
/**
* @author egor
*/
public class ClassLoadingUtils {
private ClassLoadingUtils() {}
public static ClassLoaderReference getClassLoader(EvaluationContext context, DebugProcess process) throws EvaluateException {
try {
// TODO: cache?
ClassType loaderClass = (ClassType)process.findClass(context, "java.net.URLClassLoader", context.getClassLoader());
Method ctorMethod = loaderClass.concreteMethodByName("<init>", "([Ljava/net/URL;Ljava/lang/ClassLoader;)V");
ClassLoaderReference reference = (ClassLoaderReference)process.newInstance(context, loaderClass, ctorMethod, Arrays
.asList(createURLArray(context), context.getClassLoader()));
keep(reference, context);
return reference;
}
catch (Exception e) {
throw new EvaluateException("Error creating evaluation class loader: " + e, e);
}
}
public static void defineClass(String name,
byte[] bytes,
EvaluationContext context,
DebugProcess process,
ClassLoaderReference classLoader) throws EvaluateException {
try {
VirtualMachineProxyImpl proxy = (VirtualMachineProxyImpl)process.getVirtualMachineProxy();
Method defineMethod =
((ClassType)classLoader.referenceType()).concreteMethodByName("defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
StringReference nameObj = proxy.mirrorOf(name);
keep(nameObj, context);
process.invokeMethod(context, classLoader, defineMethod,
Arrays.asList(nameObj,
mirrorOf(bytes, context, process),
proxy.mirrorOf(0),
proxy.mirrorOf(bytes.length)));
}
catch (Exception e) {
throw new EvaluateException("Error during class " + name + " definition: " + e, e);
}
}
@Nullable
public static ClassType getHelperClass(String name, EvaluationContext evaluationContext, DebugProcess process) throws EvaluateException {
try {
ClassLoaderReference classLoader = evaluationContext.getClassLoader();
return (ClassType)process.findClass(evaluationContext, name, classLoader);
} catch (EvaluateException e) {
Throwable cause = e.getCause();
if (cause instanceof InvocationException) {
if ("java.lang.ClassNotFoundException".equals(((InvocationException)cause).exception().type().name())) {
// need to define
ClassLoaderReference classLoader = getClassLoader(evaluationContext, process);
InputStream stream = ImageSerializer.class.getResourceAsStream("/" + name.replaceAll("[.]", "/") + ".class");
try {
if (stream == null) return null;
defineClass(name, StreamUtil.loadFromStream(stream), evaluationContext, process, classLoader);
((EvaluationContextImpl)evaluationContext).setClassLoader(classLoader);
return (ClassType)process.findClass(evaluationContext, name, classLoader);
}
catch (IOException ioe) {
throw new EvaluateException("Unable to read " + name + " class bytes", ioe);
}
finally {
try {
if (stream != null) {
stream.close();
}
}
catch (IOException ignored) {}
}
}
}
throw e;
}
}
private static ArrayReference createURLArray(EvaluationContext context)
throws EvaluateException, InvocationException, InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException {
DebugProcess process = context.getDebugProcess();
ArrayType arrayType = (ArrayType)process.findClass(context, "java.net.URL[]", context.getClassLoader());
ArrayReference arrayRef = arrayType.newInstance(1);
keep(arrayRef, context);
ClassType classType = (ClassType)process.findClass(context, "java.net.URL", context.getClassLoader());
VirtualMachineProxyImpl proxy = (VirtualMachineProxyImpl)process.getVirtualMachineProxy();
StringReference url = proxy.mirrorOf("file:a");
keep(url, context);
ObjectReference reference = process.newInstance(context, classType, classType.concreteMethodByName("<init>", "(Ljava/lang/String;)V"),
Collections.singletonList(url));
keep(reference, context);
arrayRef.setValues(Collections.singletonList(reference));
return arrayRef;
}
private static ArrayReference mirrorOf(byte[] bytes, EvaluationContext context, DebugProcess process)
throws EvaluateException, InvalidTypeException, ClassNotLoadedException {
ArrayType arrayClass = (ArrayType)process.findClass(context, "byte[]", context.getClassLoader());
ArrayReference reference = process.newInstance(arrayClass, bytes.length);
keep(reference, context);
for (int i = 0; i < bytes.length; i++) {
reference.setValue(i, ((VirtualMachineProxyImpl)process.getVirtualMachineProxy()).mirrorOf(bytes[i]));
}
return reference;
}
public static void keep(ObjectReference reference, EvaluationContext context) {
((SuspendContextImpl)context.getSuspendContext()).keep(reference);
}
}
@@ -21,6 +21,7 @@ import com.intellij.debugger.engine.FullValueEvaluatorProvider;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContext;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.impl.ClassLoadingUtils;
import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.rt.debugger.ImageSerializer;
@@ -88,15 +89,14 @@ class ImageObjectRenderer extends ToStringBasedRenderer implements FullValueEval
}
private static Value getImageBytes(EvaluationContext evaluationContext, Value obj, String methodName)
throws EvaluateException, ClassNotLoadedException, InvalidTypeException {
throws EvaluateException {
DebugProcess process = evaluationContext.getDebugProcess();
ClassType cls = (ClassType)process.findClass(evaluationContext, ImageSerializer.class.getName(),
evaluationContext.getClassLoader());
ClassType helperClass = ClassLoadingUtils.getHelperClass(ImageSerializer.class.getName(), evaluationContext, process);
if (cls != null) {
List<Method> methods = cls.methodsByName(methodName);
if (helperClass != null) {
List<Method> methods = helperClass.methodsByName(methodName);
if (!methods.isEmpty()) {
return process.invokeMethod(evaluationContext, cls, methods.get(0), Collections.singletonList(obj));
return process.invokeMethod(evaluationContext, helperClass, methods.get(0), Collections.singletonList(obj));
}
}
return null;
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2015 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.
@@ -20,10 +20,10 @@ import com.intellij.debugger.EvaluatingComputable;
import com.intellij.debugger.engine.ContextUtil;
import com.intellij.debugger.engine.DebugProcess;
import com.intellij.debugger.engine.JVMNameUtil;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.evaluation.*;
import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
import com.intellij.debugger.engine.evaluation.expression.Modifier;
import com.intellij.debugger.impl.ClassLoadingUtils;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
import com.intellij.openapi.application.ApplicationManager;
@@ -33,7 +33,9 @@ import com.intellij.openapi.projectRoots.JdkVersionUtil;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler;
import com.sun.jdi.*;
import com.sun.jdi.ClassLoaderReference;
import com.sun.jdi.ClassType;
import com.sun.jdi.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.org.objectweb.asm.ClassReader;
import org.jetbrains.org.objectweb.asm.ClassVisitor;
@@ -43,7 +45,8 @@ import org.jetbrains.org.objectweb.asm.Opcodes;
import javax.tools.*;
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author egor
@@ -75,24 +78,13 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator {
public Value evaluate(final EvaluationContext evaluationContext) throws EvaluateException {
DebugProcess process = evaluationContext.getDebugProcess();
ClassLoaderReference classLoader;
try {
classLoader = getClassLoader(evaluationContext, process);
}
catch (Exception e) {
throw new EvaluateException("Error creating evaluation class loader: " + e, e);
}
ClassLoaderReference classLoader = ClassLoadingUtils.getClassLoader(evaluationContext, process);
String version = ((VirtualMachineProxyImpl)process.getVirtualMachineProxy()).version();
JavaSdkVersion sdkVersion = JdkVersionUtil.getVersion(version);
Collection<OutputFileObject> classes = compile(sdkVersion != null ? sdkVersion.getDescription() : null);
try {
defineClasses(classes, evaluationContext, process, classLoader);
}
catch (Exception e) {
throw new EvaluateException("Error during classes definition " + e, e);
}
defineClasses(classes, evaluationContext, process, classLoader);
try {
// invoke base evaluator on call code
@@ -122,40 +114,14 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator {
}
}
private static ClassLoaderReference getClassLoader(EvaluationContext context, DebugProcess process)
throws EvaluateException, InvocationException, InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException {
// TODO: cache
ClassType loaderClass = (ClassType)process.findClass(context, "java.net.URLClassLoader", context.getClassLoader());
Method ctorMethod = loaderClass.concreteMethodByName("<init>", "([Ljava/net/URL;Ljava/lang/ClassLoader;)V");
ClassLoaderReference reference = (ClassLoaderReference)process.newInstance(context, loaderClass, ctorMethod, Arrays.asList(createURLArray(context), context.getClassLoader()));
keep(reference, context);
return reference;
}
private static void keep(ObjectReference reference, EvaluationContext context) {
((SuspendContextImpl)context.getSuspendContext()).keep(reference);
}
private ClassType defineClasses(Collection<OutputFileObject> classes,
EvaluationContext context,
DebugProcess process,
ClassLoaderReference classLoader)
throws EvaluateException, InvalidTypeException, ClassNotLoadedException {
VirtualMachineProxyImpl proxy = (VirtualMachineProxyImpl)process.getVirtualMachineProxy();
ClassLoaderReference classLoader) throws EvaluateException {
for (OutputFileObject cls : classes) {
if (cls.getName().contains(GEN_CLASS_NAME)) {
Method defineMethod =
((ClassType)classLoader.referenceType()).concreteMethodByName("defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
byte[] bytes = changeSuperToMagicAccessor(cls.toByteArray());
ArrayList<Value> args = new ArrayList<Value>();
StringReference name = proxy.mirrorOf(cls.myOrigName);
keep(name, context);
args.add(name);
args.add(mirrorOf(bytes, context, process));
args.add(proxy.mirrorOf(0));
args.add(proxy.mirrorOf(bytes.length));
process.invokeMethod(context, classLoader, defineMethod, args);
ClassLoadingUtils.defineClass(cls.myOrigName, bytes, context, process, classLoader);
}
}
return (ClassType)process.findClass(context, getGenClassQName(), classLoader);
@@ -176,17 +142,6 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator {
return classWriter.toByteArray();
}
private static ArrayReference mirrorOf(byte[] bytes, EvaluationContext context, DebugProcess process)
throws EvaluateException, InvalidTypeException, ClassNotLoadedException {
ArrayType arrayClass = (ArrayType)process.findClass(context, "byte[]", context.getClassLoader());
ArrayReference reference = process.newInstance(arrayClass, bytes.length);
keep(reference, context);
for (int i = 0; i < bytes.length; i++) {
reference.setValue(i, ((VirtualMachineProxyImpl)process.getVirtualMachineProxy()).mirrorOf(bytes[i]));
}
return reference;
}
public static String getGeneratedClassName() {
return GEN_CLASS_NAME;
}
@@ -206,23 +161,6 @@ public abstract class CompilingEvaluator implements ExpressionEvaluator {
});
}
private static ArrayReference createURLArray(EvaluationContext context)
throws EvaluateException, InvocationException, InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException {
DebugProcess process = context.getDebugProcess();
ArrayType arrayType = (ArrayType)process.findClass(context, "java.net.URL[]", context.getClassLoader());
ArrayReference arrayRef = arrayType.newInstance(1);
keep(arrayRef, context);
ClassType classType = (ClassType)process.findClass(context, "java.net.URL", context.getClassLoader());
VirtualMachineProxyImpl proxy = (VirtualMachineProxyImpl)process.getVirtualMachineProxy();
StringReference url = proxy.mirrorOf("file:a");
keep(url, context);
ObjectReference reference = process.newInstance(context, classType, classType.concreteMethodByName("<init>", "(Ljava/lang/String;)V"),
Collections.singletonList(url));
keep(reference, context);
arrayRef.setValues(Collections.singletonList(reference));
return arrayRef;
}
///////////////// Compiler stuff
@NotNull