diff options
Diffstat (limited to 'java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java')
-rw-r--r-- | java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java | 210 |
1 files changed, 179 insertions, 31 deletions
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 042216c40f05..467cf985f04c 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 @@ -15,28 +15,57 @@ */ package com.intellij.debugger.ui.impl.watch; +import com.intellij.debugger.DebuggerInvocationUtil; +import com.intellij.debugger.EvaluatingComputable; +import com.intellij.debugger.engine.ContextUtil; import com.intellij.debugger.engine.DebugProcess; -import com.intellij.debugger.engine.evaluation.EvaluateException; -import com.intellij.debugger.engine.evaluation.EvaluationContext; -import com.intellij.debugger.engine.evaluation.TextWithImports; +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.DebuggerUtilsEx; import com.intellij.debugger.jdi.VirtualMachineProxyImpl; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiCodeFragment; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiJavaFile; +import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler; import com.sun.jdi.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.org.objectweb.asm.ClassReader; +import org.jetbrains.org.objectweb.asm.ClassVisitor; +import org.jetbrains.org.objectweb.asm.ClassWriter; +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.Arrays; +import java.util.Collection; /** * @author egor */ -class CompilingEvaluator implements ExpressionEvaluator { +public class CompilingEvaluator implements ExpressionEvaluator { private final TextWithImports myText; - - public CompilingEvaluator(TextWithImports text) { + private final PsiCodeFragment myCodeFragment; + private final PsiElement myPsiContext; + @NotNull private final ExtractLightMethodObjectHandler.ExtractedData myData; + private final EvaluationDescriptor myDescriptor; + + public CompilingEvaluator(TextWithImports text, + PsiCodeFragment codeFragment, + PsiElement context, + @NotNull ExtractLightMethodObjectHandler.ExtractedData data, + EvaluationDescriptor descriptor) { myText = text; + myCodeFragment = codeFragment; + myPsiContext = context; + myData = data; + myDescriptor = descriptor; } @Override @@ -49,20 +78,41 @@ class CompilingEvaluator implements ExpressionEvaluator { return null; } + private TextWithImports getCallCode() { + return new TextWithImportsImpl(CodeFragmentKind.CODE_BLOCK, myData.getGeneratedCallText()); + } + @Override - public Value evaluate(EvaluationContext context) throws EvaluateException { + public Value evaluate(final EvaluationContext evaluationContext) throws EvaluateException { try { - DebugProcess process = context.getDebugProcess(); - ThreadReference threadReference = context.getSuspendContext().getThread().getThreadReference(); + DebugProcess process = evaluationContext.getDebugProcess(); + ThreadReference threadReference = evaluationContext.getSuspendContext().getThread().getThreadReference(); - ClassLoaderReference classLoader = getClassLoader(context); + ClassLoaderReference classLoader = getClassLoader(evaluationContext); Collection<OutputFileObject> classes = compile(); - ClassType mainClass = defineClasses(classes, context, process, threadReference, classLoader); - - Method foo = mainClass.methodsByName(GEN_METHOD_NAME).get(0); - return mainClass.invokeMethod(threadReference, foo, Collections.<Value>emptyList() ,ClassType.INVOKE_SINGLE_THREADED); + ClassType mainClass = defineClasses(classes, evaluationContext, process, threadReference, classLoader); + + //Method foo = mainClass.methodsByName(GEN_METHOD_NAME).get(0); + //return mainClass.invokeMethod(threadReference, foo, Collections.<Value>emptyList() ,ClassType.INVOKE_SINGLE_THREADED); + + // invoke base evaluator on call code + final Project project = myPsiContext.getProject(); + ExpressionEvaluator evaluator = + DebuggerInvocationUtil.commitAndRunReadAction(project, new EvaluatingComputable<ExpressionEvaluator>() { + @Override + public ExpressionEvaluator compute() throws EvaluateException { + final TextWithImports callCode = getCallCode(); + PsiElement copyContext = myData.getAnchor(); + final CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(callCode, copyContext); + return factory.getEvaluatorBuilder(). + build(factory.createCodeFragment(callCode, copyContext, project), + ContextUtil.getSourcePosition(evaluationContext)); + } + }); + ((EvaluationContextImpl)evaluationContext).setClassLoader(classLoader); + return evaluator.evaluate(evaluationContext); } catch (Exception e) { throw new EvaluateException(e.getMessage()); @@ -74,13 +124,13 @@ class CompilingEvaluator implements ExpressionEvaluator { // TODO: cache DebugProcess process = context.getDebugProcess(); ClassType loaderClass = (ClassType)process.findClass(context, "java.net.URLClassLoader", context.getClassLoader()); - Method ctorMethod = loaderClass.concreteMethodByName("<init>", "([Ljava/net/URL;)V"); + Method ctorMethod = loaderClass.concreteMethodByName("<init>", "([Ljava/net/URL;Ljava/lang/ClassLoader;)V"); ThreadReference threadReference = context.getSuspendContext().getThread().getThreadReference(); return (ClassLoaderReference)loaderClass.newInstance(threadReference, ctorMethod, - Arrays.asList(createURLArray(context)), ClassType.INVOKE_SINGLE_THREADED); + Arrays.asList(createURLArray(context), context.getClassLoader()), ClassType.INVOKE_SINGLE_THREADED); } - private static ClassType defineClasses(Collection<OutputFileObject> classes, + private ClassType defineClasses(Collection<OutputFileObject> classes, EvaluationContext context, DebugProcess process, ThreadReference threadReference, @@ -89,16 +139,34 @@ class CompilingEvaluator implements ExpressionEvaluator { VirtualMachineProxyImpl proxy = (VirtualMachineProxyImpl)process.getVirtualMachineProxy(); for (OutputFileObject cls : classes) { - Method defineMethod = ((ClassType)classLoader.referenceType()).concreteMethodByName("defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;"); - byte[] bytes = cls.toByteArray(); - ArrayList<Value> args = new ArrayList<Value>(); - args.add(proxy.mirrorOf(cls.myOrigName)); - args.add(mirrorOf(bytes, context, process)); - args.add(proxy.mirrorOf(0)); - args.add(proxy.mirrorOf(bytes.length)); - classLoader.invokeMethod(threadReference, defineMethod, args, ClassType.INVOKE_SINGLE_THREADED); + if (cls.getName().contains(getGenClassName())) { + 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>(); + args.add(proxy.mirrorOf(cls.myOrigName)); + args.add(mirrorOf(bytes, context, process)); + args.add(proxy.mirrorOf(0)); + args.add(proxy.mirrorOf(bytes.length)); + classLoader.invokeMethod(threadReference, defineMethod, args, ClassType.INVOKE_SINGLE_THREADED); + } } - return (ClassType)process.findClass(context, GEN_CLASS_FULL_NAME, classLoader); + return (ClassType)process.findClass(context, getGenClassFullName(), classLoader); + } + + private static byte[] changeSuperToMagicAccessor(byte[] bytes) { + ClassWriter classWriter = new ClassWriter(0); + ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM5, classWriter) { + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + if ("java/lang/Object".equals(superName)) { + superName = "sun/reflect/MagicAccessorImpl"; + } + super.visit(version, access, name, signature, superName, interfaces); + } + }; + new ClassReader(bytes).accept(classVisitor, 0); + return classWriter.toByteArray(); } private static ArrayReference mirrorOf(byte[] bytes, EvaluationContext context, DebugProcess process) @@ -112,10 +180,90 @@ class CompilingEvaluator implements ExpressionEvaluator { return reference; } - private static final String GEN_CLASS_NAME = "Evaluator"; + public static String getGeneratedClassName() { + return GEN_CLASS_NAME; + } + + private static final String GEN_CLASS_NAME = "GeneratedEvaluationClass"; private static final String GEN_CLASS_PACKAGE = "dummy"; private static final String GEN_CLASS_FULL_NAME = GEN_CLASS_PACKAGE + '.' + GEN_CLASS_NAME; - private static final String GEN_METHOD_NAME = "eval"; + private static final String GEN_METHOD_NAME = "invoke"; + + private String getClassCode() { + if (myData != null) { + return ApplicationManager.getApplication().runReadAction(new Computable<String>() { + @Override + public String compute() { + //String text = myData.getGeneratedInnerClass().getText(); + ////TODO: remove + //String prefix = "public static"; + //if (text.startsWith(prefix)) { + // text = "public" + text.substring(prefix.length()); + //} + //PsiElement[] children = ((PsiJavaFile)myPsiContext.getContainingFile()).getImportList().getChildren(); + //StringBuilder imports = new StringBuilder(); + //for (PsiElement child : children) { + // if (child instanceof PsiImportStatement) { + // String name = ((PsiImportStatement)child).getImportReference().getQualifiedName(); + // imports.append("import ").append(name).append(";"); + // } + //} + //text = text.replace("class " + GEN_CLASS_NAME, "class " + getGenClassName()); + //text = text.replace(GEN_CLASS_NAME + "(", getGenClassName() + "("); + //text = text.replace(((PsiClass)myData.getGeneratedInnerClass().getParent()).getName() + "." + GEN_CLASS_NAME, getGenClassName()); + //return "package " + getGenPackageName() + "; " + imports.toString() + text; + return myData.getGeneratedInnerClass().getContainingFile().getText(); + } + }); + } + return null; + } + + private String getGenPackageName() { + return ApplicationManager.getApplication().runReadAction(new Computable<String>() { + @Override + public String compute() { + return ((PsiJavaFile)myData.getGeneratedInnerClass().getContainingFile()).getPackageName(); + } + }); + } + + private String getMainClassName() { + return ApplicationManager.getApplication().runReadAction(new Computable<String>() { + @Override + public String compute() { + return ((PsiClass)myData.getGeneratedInnerClass().getParent()).getName(); + } + }); + } + + private String getGenClassName() { + return getMainClassName() + '$' + GEN_CLASS_NAME; + } + + private String getGenClassFullName() { + String packageName = getGenPackageName(); + if (packageName.isEmpty()) { + return getGenClassName(); + } + return packageName + '.' + getGenClassName(); + } + + //private String createClassCode() { + // return ApplicationManager.getApplication().runReadAction(new Computable<String>() { + // @Override + // public String compute() { + // try { + // myExtractedData = + // ExtractLightMethodObjectHandler.extractLightMethodObject(myCodeFragment.getProject(), myFile , myCodeFragment, "test"); + // } + // catch (PrepareFailedException e) { + // e.printStackTrace(); + // } + // return null; + // } + // }); + //} private static String createClassCode(TextWithImports body) { StringBuilder text = new StringBuilder(); @@ -155,9 +303,9 @@ class CompilingEvaluator implements ExpressionEvaluator { MemoryFileManager manager = new MemoryFileManager(compiler); DiagnosticCollector<JavaFileObject> diagnostic = new DiagnosticCollector<JavaFileObject>(); if (!compiler.getTask(null, manager, diagnostic, null, null, Arrays - .asList(new SourceFileObject(GEN_CLASS_NAME, JavaFileObject.Kind.SOURCE, createClassCode(myText)))).call()) { + .asList(new SourceFileObject(getMainClassName(), JavaFileObject.Kind.SOURCE, getClassCode()))).call()) { // TODO: show only errors - throw new EvaluateException(diagnostic.getDiagnostics().get(0).getMessage(Locale.getDefault())); + throw new EvaluateException(diagnostic.getDiagnostics().get(0).toString()); } return manager.classes; } |