diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/MethodEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/MethodEvaluator.java index 3aff8225622f..bb36ecf04672 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/MethodEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/MethodEvaluator.java @@ -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 methods = invokerClass.methodsByName("invoke"); diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/ClassLoadingUtils.java b/java/debugger/impl/src/com/intellij/debugger/impl/ClassLoadingUtils.java new file mode 100644 index 000000000000..22998e41c52b --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/impl/ClassLoadingUtils.java @@ -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("", "([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("", "(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); + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/ImageObjectRenderer.java b/java/debugger/impl/src/com/intellij/debugger/settings/ImageObjectRenderer.java index 926543fe5b10..ff4cb6bc3f08 100644 --- a/java/debugger/impl/src/com/intellij/debugger/settings/ImageObjectRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/settings/ImageObjectRenderer.java @@ -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 methods = cls.methodsByName(methodName); + if (helperClass != null) { + List 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; diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java index 93e1ff136ad1..5d4aaed71106 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java @@ -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 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("", "([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 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 args = new ArrayList(); - 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("", "(Ljava/lang/String;)V"), - Collections.singletonList(url)); - keep(reference, context); - arrayRef.setValues(Collections.singletonList(reference)); - return arrayRef; - } - ///////////////// Compiler stuff @NotNull