Skip to content

Commit 4adbd87

Browse files
authored
Merge branch 'master' into andy_user_error
2 parents 90eec07 + 172dd9f commit 4adbd87

File tree

4 files changed

+329
-1
lines changed

4 files changed

+329
-1
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2017 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Microsoft Corporation - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.microsoft.java.debug.core.adapter;
13+
14+
import java.util.concurrent.CompletableFuture;
15+
16+
import com.sun.jdi.StackFrame;
17+
import com.sun.jdi.ThreadReference;
18+
import com.sun.jdi.Value;
19+
20+
/**
21+
* An evaluation engine performs an evaluation of a code snippet or expression
22+
* in a specified thread of a debug target. An evaluation engine is associated
23+
* with a specific debug target and Java project on creation.
24+
*/
25+
public interface IEvaluationProvider extends IProvider {
26+
/**
27+
* This method provides the event hub the ability to exclude the breakpoint event raised during evaluation.
28+
* @param thread the thread to be checked against evaluation work.
29+
* @return whether or not the thread is performing evaluation
30+
*/
31+
boolean isInEvaluation(ThreadReference thread);
32+
33+
/**
34+
* Evaluate the expression at the given project and thread and stack frame depth, the promise is to be resolved/rejected when
35+
* the evaluation finishes.
36+
*
37+
* @param projectName The java project which provides resolve class used in the expression
38+
* @param expression The expression to be evaluated
39+
* @param sf The stack frame of the evaluation task
40+
* @return the evaluation result
41+
*/
42+
CompletableFuture<Value> evaluate(String projectName, String expression, StackFrame sf);
43+
44+
45+
/**
46+
* Cancel ongoing evaluation tasks on specified thread.
47+
* @param thread the JDI thread reference where the evaluation task is executing at
48+
*/
49+
void cancelEvaluation(ThreadReference thread);
50+
51+
}

com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ Require-Bundle: org.eclipse.core.runtime,
1313
org.eclipse.debug.core,
1414
org.eclipse.jdt.debug,
1515
org.eclipse.jdt.core,
16-
org.eclipse.jdt.ls.core
16+
org.eclipse.jdt.ls.core,
17+
org.eclipse.jdt.launching
1718
Bundle-ClassPath: lib/gson-2.7.jar,
1819
.,
1920
lib/rxjava-2.1.1.jar,

com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/JdtProviderContextFactory.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111

1212
package com.microsoft.java.debug.plugin.internal;
1313

14+
import com.microsoft.java.debug.core.adapter.IEvaluationProvider;
1415
import com.microsoft.java.debug.core.adapter.IProviderContext;
1516
import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider;
1617
import com.microsoft.java.debug.core.adapter.IVirtualMachineManagerProvider;
1718
import com.microsoft.java.debug.core.adapter.ProviderContext;
19+
import com.microsoft.java.debug.plugin.internal.eval.JdtEvaluationProvider;
1820

1921
/**
2022
* <code>IProviderContext</code> creator using language server.
@@ -28,6 +30,8 @@ public static IProviderContext createProviderContext() {
2830
IProviderContext context = new ProviderContext();
2931
context.registerProvider(ISourceLookUpProvider.class, new JdtSourceLookUpProvider());
3032
context.registerProvider(IVirtualMachineManagerProvider.class, new JdtVirtualMachineManagerProvider());
33+
context.registerProvider(IEvaluationProvider.class, new JdtEvaluationProvider());
34+
3135
return context;
3236
}
3337
}
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2017 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Microsoft Corporation - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.microsoft.java.debug.plugin.internal.eval;
13+
14+
import java.util.HashMap;
15+
import java.util.Map;
16+
import java.util.concurrent.CompletableFuture;
17+
import java.util.logging.Logger;
18+
19+
import org.apache.commons.lang3.StringUtils;
20+
import org.apache.commons.lang3.reflect.FieldUtils;
21+
import org.eclipse.core.resources.IProject;
22+
import org.eclipse.core.resources.ResourcesPlugin;
23+
import org.eclipse.core.runtime.CoreException;
24+
import org.eclipse.debug.core.DebugException;
25+
import org.eclipse.debug.core.ILaunch;
26+
import org.eclipse.debug.core.ILaunchConfiguration;
27+
import org.eclipse.debug.core.model.IDebugTarget;
28+
import org.eclipse.debug.core.model.IProcess;
29+
import org.eclipse.debug.core.model.ISourceLocator;
30+
import org.eclipse.debug.core.sourcelookup.AbstractSourceLookupDirector;
31+
import org.eclipse.debug.core.sourcelookup.containers.ProjectSourceContainer;
32+
import org.eclipse.jdt.core.IJavaProject;
33+
import org.eclipse.jdt.core.JavaCore;
34+
import org.eclipse.jdt.debug.eval.ICompiledExpression;
35+
import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
36+
import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame;
37+
import org.eclipse.jdt.internal.debug.core.model.JDIThread;
38+
import org.eclipse.jdt.internal.debug.eval.ast.engine.ASTEvaluationEngine;
39+
import org.eclipse.jdt.internal.launching.JavaSourceLookupDirector;
40+
41+
import com.microsoft.java.debug.core.Configuration;
42+
import com.microsoft.java.debug.core.adapter.IEvaluationProvider;
43+
import com.sun.jdi.StackFrame;
44+
import com.sun.jdi.ThreadReference;
45+
import com.sun.jdi.Value;
46+
47+
public class JdtEvaluationProvider implements IEvaluationProvider {
48+
private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME);
49+
private IJavaProject project;
50+
private ILaunch launch;
51+
private JDIDebugTarget debugTarget;
52+
private Map<ThreadReference, JDIThread> threadMap = new HashMap<>();
53+
54+
@Override
55+
public CompletableFuture<Value> evaluate(String projectName, String code, StackFrame sf) {
56+
CompletableFuture<Value> completableFuture = new CompletableFuture<>();
57+
if (debugTarget == null) {
58+
if (project == null) {
59+
if (StringUtils.isBlank(projectName)) {
60+
// TODO: get project from stackframe
61+
logger.severe("Cannot evaluate when project is not specified.");
62+
completableFuture.completeExceptionally(new IllegalStateException("Please specify projectName in launch.json."));
63+
return completableFuture;
64+
}
65+
for (IProject proj : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
66+
try {
67+
if (proj.getName().equals(projectName)) {
68+
if (!proj.isNatureEnabled("org.eclipse.jdt.core.javanature")) {
69+
completableFuture.completeExceptionally(
70+
new IllegalStateException(String.format("Project %s is not a java project.", projectName)));
71+
return completableFuture;
72+
}
73+
project = JavaCore.create(proj);
74+
break;
75+
}
76+
} catch (CoreException e) {
77+
logger.severe(String.format("Cannot initialize project: %s", e.toString()));
78+
}
79+
}
80+
}
81+
82+
if (project == null) {
83+
completableFuture.completeExceptionally(new IllegalStateException(String.format("Project %s cannot be found.", projectName)));
84+
return completableFuture;
85+
}
86+
if (launch == null) {
87+
launch = createILaunchMock(project);
88+
}
89+
}
90+
91+
ThreadReference thread = sf.thread();
92+
if (debugTarget == null) {
93+
debugTarget = new JDIDebugTarget(launch, thread.virtualMachine(), "", false, false, null, false) {
94+
@Override
95+
protected synchronized void initialize() {
96+
// use empty initialize intentionally to avoid to register jdi event listener
97+
}
98+
};
99+
}
100+
try {
101+
JDIThread jdiThread = getJDIThread(thread);
102+
103+
synchronized (jdiThread) {
104+
if (jdiThread.isPerformingEvaluation()) {
105+
jdiThread.wait();
106+
}
107+
108+
ASTEvaluationEngine engine = new ASTEvaluationEngine(project, debugTarget);
109+
JDIStackFrame stackframe = createStackFrame(sf);
110+
111+
ICompiledExpression ie = engine.getCompiledExpression(code, stackframe);
112+
engine.evaluateExpression(ie, stackframe, evaluateResult -> {
113+
synchronized (jdiThread) {
114+
jdiThread.notify();
115+
}
116+
if (evaluateResult == null || evaluateResult.hasErrors()) {
117+
Exception ex = evaluateResult.getException() != null ? evaluateResult.getException()
118+
: new RuntimeException(StringUtils.join(evaluateResult.getErrorMessages()));
119+
completableFuture.completeExceptionally(ex);
120+
return;
121+
}
122+
try {
123+
// we need to read fValue from the result Value instance implements by JDT
124+
Value value = (Value) FieldUtils.readField(evaluateResult.getValue(), "fValue", true);
125+
completableFuture.complete(value);
126+
} catch (IllegalArgumentException | IllegalAccessException ex) {
127+
completableFuture.completeExceptionally(ex);
128+
}
129+
}, 0, false);
130+
}
131+
132+
} catch (Exception ex) {
133+
completableFuture.completeExceptionally(ex);
134+
}
135+
return completableFuture;
136+
}
137+
138+
private JDIStackFrame createStackFrame(StackFrame sf) {
139+
return new JDIStackFrame(getJDIThread(sf.thread()), sf, 0);
140+
}
141+
142+
private JDIThread getJDIThread(ThreadReference thread) {
143+
synchronized (threadMap) {
144+
return threadMap.computeIfAbsent(thread, threadKey -> new JDIThread(debugTarget, thread));
145+
}
146+
147+
}
148+
149+
@Override
150+
public boolean isInEvaluation(ThreadReference thread) {
151+
return debugTarget != null && getJDIThread(thread).isPerformingEvaluation();
152+
}
153+
154+
@Override
155+
public void cancelEvaluation(ThreadReference thread) {
156+
if (debugTarget != null) {
157+
JDIThread jdiThread = getJDIThread(thread);
158+
if (jdiThread != null) {
159+
try {
160+
jdiThread.terminateEvaluation();
161+
} catch (DebugException e) {
162+
logger.warning(String.format("Error stopping evalutoin on thread %d: %s", thread.uniqueID(), e.toString()));
163+
}
164+
}
165+
}
166+
}
167+
168+
private static ILaunch createILaunchMock(IJavaProject project) {
169+
return new ILaunch() {
170+
private AbstractSourceLookupDirector locator;
171+
172+
@Override
173+
public boolean canTerminate() {
174+
return false;
175+
}
176+
177+
@Override
178+
public boolean isTerminated() {
179+
return false;
180+
}
181+
182+
@Override
183+
public void terminate() throws DebugException {
184+
}
185+
186+
@Override
187+
public <T> T getAdapter(Class<T> arg0) {
188+
return null;
189+
}
190+
191+
@Override
192+
public void addDebugTarget(IDebugTarget arg0) {
193+
}
194+
195+
@Override
196+
public void addProcess(IProcess arg0) {
197+
}
198+
199+
@Override
200+
public String getAttribute(String arg0) {
201+
return null;
202+
}
203+
204+
@Override
205+
public Object[] getChildren() {
206+
return null;
207+
}
208+
209+
@Override
210+
public IDebugTarget getDebugTarget() {
211+
return null;
212+
}
213+
214+
@Override
215+
public IDebugTarget[] getDebugTargets() {
216+
return null;
217+
}
218+
219+
@Override
220+
public ILaunchConfiguration getLaunchConfiguration() {
221+
return null;
222+
}
223+
224+
@Override
225+
public String getLaunchMode() {
226+
return null;
227+
}
228+
229+
@Override
230+
public IProcess[] getProcesses() {
231+
return null;
232+
}
233+
234+
@Override
235+
public ISourceLocator getSourceLocator() {
236+
if (locator != null) {
237+
return locator;
238+
}
239+
locator = new JavaSourceLookupDirector();
240+
241+
try {
242+
locator.setSourceContainers(new ProjectSourceContainer(project.getProject(), true).getSourceContainers());
243+
} catch (CoreException e) {
244+
logger.severe(String.format("Cannot initialize JavaSourceLookupDirector: %s", e.toString()));
245+
}
246+
locator.initializeParticipants();
247+
return locator;
248+
}
249+
250+
@Override
251+
public boolean hasChildren() {
252+
return false;
253+
}
254+
255+
@Override
256+
public void removeDebugTarget(IDebugTarget arg0) {
257+
}
258+
259+
@Override
260+
public void removeProcess(IProcess arg0) {
261+
}
262+
263+
@Override
264+
public void setAttribute(String arg0, String arg1) {
265+
}
266+
267+
@Override
268+
public void setSourceLocator(ISourceLocator arg0) {
269+
}
270+
};
271+
}
272+
}

0 commit comments

Comments
 (0)