/
CompilingEvaluator.java
144 lines (123 loc) · 5.98 KB
/
CompilingEvaluator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// 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.
package com.intellij.debugger.ui.impl.watch;
import com.intellij.debugger.DebuggerInvocationUtil;
import com.intellij.debugger.EvaluatingComputable;
import com.intellij.debugger.SourcePosition;
import com.intellij.debugger.engine.DebugProcess;
import com.intellij.debugger.engine.JVMNameUtil;
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.ReadAction;
import com.intellij.openapi.compiler.ClassObject;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler;
import com.sun.jdi.ClassLoaderReference;
import com.sun.jdi.Value;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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 java.util.Collection;
/**
* @author egor
*/
public abstract class CompilingEvaluator implements ExpressionEvaluator {
@NotNull protected final Project myProject;
@NotNull protected final PsiElement myPsiContext;
@NotNull protected final ExtractLightMethodObjectHandler.ExtractedData myData;
public CompilingEvaluator(@NotNull Project project, @NotNull PsiElement context, @NotNull ExtractLightMethodObjectHandler.ExtractedData data) {
myProject = project;
myPsiContext = context;
myData = data;
}
@Override
public Modifier getModifier() {
return null;
}
private TextWithImports getCallCode() {
return new TextWithImportsImpl(CodeFragmentKind.CODE_BLOCK, myData.getGeneratedCallText());
}
@Override
public Value evaluate(final EvaluationContext evaluationContext) throws EvaluateException {
DebugProcess process = evaluationContext.getDebugProcess();
EvaluationContextImpl autoLoadContext = ((EvaluationContextImpl)evaluationContext).withAutoLoadClasses(true);
ClassLoaderReference classLoader = ClassLoadingUtils.getClassLoader(autoLoadContext, process);
autoLoadContext.setClassLoader(classLoader);
JavaSdkVersion version = JavaSdkVersion.fromVersionString(((VirtualMachineProxyImpl)process.getVirtualMachineProxy()).version());
Collection<ClassObject> classes = compile(version);
defineClasses(classes, autoLoadContext, process, classLoader);
try {
// invoke base evaluator on call code
ExpressionEvaluator evaluator =
DebuggerInvocationUtil.commitAndRunReadAction(myProject, new EvaluatingComputable<ExpressionEvaluator>() {
@Override
public ExpressionEvaluator compute() throws EvaluateException {
TextWithImports callCode = getCallCode();
PsiElement copyContext = myData.getAnchor();
CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(callCode, copyContext);
return factory.getEvaluatorBuilder().build(factory.createCodeFragment(callCode, copyContext, myProject),
// can not use evaluation position here, it does not match classes then
SourcePosition.createFromElement(copyContext));
}
});
return evaluator.evaluate(autoLoadContext);
}
catch (Exception e) {
throw new EvaluateException("Error during generated code invocation " + e, e);
}
}
private void defineClasses(Collection<ClassObject> classes,
EvaluationContext context,
DebugProcess process,
ClassLoaderReference classLoader) throws EvaluateException {
boolean useMagicAccessorImpl = myData.useMagicAccessor();
for (ClassObject cls : classes) {
if (cls.getPath().contains(GEN_CLASS_NAME)) {
byte[] bytes = cls.getContent();
if (bytes != null) {
if (useMagicAccessorImpl) {
bytes = changeSuperToMagicAccessor(bytes);
}
ClassLoadingUtils.defineClass(cls.getClassName(), bytes, context, process, classLoader);
}
}
}
process.findClass(context, getGenClassQName(), classLoader);
}
private static byte[] changeSuperToMagicAccessor(byte[] bytes) {
ClassWriter classWriter = new ClassWriter(0);
ClassVisitor classVisitor = new ClassVisitor(Opcodes.API_VERSION, 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();
}
@NotNull
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 = "invoke";
protected String getGenClassQName() {
return ReadAction.compute(() -> JVMNameUtil.getNonAnonymousClassName(myData.getGeneratedInnerClass()));
}
///////////////// Compiler stuff
@NotNull
protected abstract Collection<ClassObject> compile(@Nullable JavaSdkVersion debuggeeVersion) throws EvaluateException;
}