package jetbrains.mps.debugger.java.runtime.engine.events;
/*Generated by MPS */
import jetbrains.mps.debugger.java.runtime.engine.concurrent.ManagerThread;
import com.sun.jdi.VirtualMachine;
import java.util.concurrent.atomic.AtomicInteger;
import jetbrains.mps.debugger.java.runtime.engine.RequestManager;
import jetbrains.mps.debugger.java.runtime.engine.DebugProcessMulticaster;
import jetbrains.mps.debugger.java.runtime.engine.SystemMessagesReporter;
import jetbrains.mps.debug.api.BreakpointManagerComponent;
import jetbrains.mps.debug.api.IDebuggableFramesSelector;
import com.intellij.openapi.project.Project;
import java.util.Map;
import com.sun.jdi.ThreadReference;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.debugger.java.runtime.engine.concurrent.Commands;
import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes;
import jetbrains.mps.debugger.java.runtime.engine.requests.StepRequestor;
import com.sun.jdi.event.ClassPrepareEvent;
import com.sun.jdi.event.StepEvent;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.request.StepRequest;
import com.sun.jdi.event.LocatableEvent;
import jetbrains.mps.debugger.java.runtime.engine.requests.LocatableEventRequestor;
import jetbrains.mps.debugger.java.runtime.breakpoints.JavaBreakpoint;
import com.sun.jdi.AbsentInformationException;
import com.intellij.openapi.application.ApplicationManager;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.atomic.AtomicReference;
import com.intellij.openapi.progress.util.ProgressWindowWithNotification;
import com.intellij.openapi.progress.util.ProgressIndicatorListenerAdapter;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import jetbrains.mps.debugger.java.runtime.engine.DebugProcessListener;
import com.sun.jdi.event.EventQueue;
import com.sun.jdi.event.EventSet;
import com.sun.jdi.event.Event;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import com.sun.jdi.event.VMDeathEvent;
import com.sun.jdi.event.VMDisconnectEvent;
public class EventsProcessor {
protected static final int STATE_INITIAL = 0;
protected static final int STATE_ATTACHED = 1;
protected static final int STATE_DETACHING = 2;
protected static final int STATE_DETACHED = 3;
private final ManagerThread myManagerThread = new ManagerThread();
private EventsProcessor.EventProcessorRunnable myRunnable = new EventsProcessor.EventProcessorRunnable();
private VirtualMachine myVirtualMachine;
protected final AtomicInteger myState = new AtomicInteger(STATE_INITIAL);
private RequestManager myRequestManager;
private final ContextManager myContextManager = new ContextManager();
private final DebugProcessMulticaster myMulticaster = new DebugProcessMulticaster();
private final SystemMessagesReporter myReporter = new SystemMessagesReporter(myMulticaster);
private final BreakpointManagerComponent myBreakpointManager;
private IDebuggableFramesSelector myFramesSelector;
private final Project myProject;
private final Map<ThreadReference, Integer> myEvaluatedThreads = MapSequence.fromMap(new HashMap<ThreadReference, Integer>());
public EventsProcessor(Project project, BreakpointManagerComponent breakpointsManagerComponent) {
myProject = project;
myBreakpointManager = breakpointsManagerComponent;
myRequestManager = new RequestManager(this);
// todo?
}
public void commitVm(@NotNull VirtualMachine vm) {
myVirtualMachine = vm;
if (myState.compareAndSet(STATE_INITIAL, STATE_ATTACHED)) {
myMulticaster.processAttached(this);
}
new Thread(myRunnable, "Debug Events Processor Thread").start();
}
public boolean isAttached() {
return myState.get() == STATE_ATTACHED;
}
private void closeProcess(boolean byUser) {
ManagerThread.assertIsMangerThread();
if (myState.compareAndSet(STATE_INITIAL, STATE_DETACHING) || myState.compareAndSet(STATE_ATTACHED, STATE_DETACHING)) {
myVirtualMachine = null;
try {
myManagerThread.close();
} finally {
myState.set(STATE_DETACHED);
myMulticaster.processDetached(this, byUser);
}
}
}
public void pause() {
myManagerThread.invoke(Commands.fromClosure(new _FunctionTypes._void_P0_E0() {
public void invoke() {
myVirtualMachine.suspend();
UserContext context = new UserContext(EventsProcessor.this);
myContextManager.pauseUserContext(context);
myMulticaster.paused(context);
}
}));
}
public void resume(@NotNull final Context context) {
myManagerThread.invoke(Commands.fromClosure(new _FunctionTypes._void_P0_E0() {
public void invoke() {
myContextManager.resume(context);
myMulticaster.resumed(context);
}
}));
}
public void step(@NotNull final EventsProcessor.StepKind kind, @NotNull final Context context) {
myManagerThread.invoke(Commands.fromClosure(new _FunctionTypes._void_P0_E0() {
public void invoke() {
int jdiType = kind.getJdiType();
addNewStepRequest(new StepRequestor(context.getThread(), jdiType, myFramesSelector), jdiType, context.getThread(), context.getSuspendPolicy());
resume(context);
}
}));
}
public void stop(final boolean terminate) {
myManagerThread.invoke(Commands.fromClosure(new _FunctionTypes._void_P0_E0() {
public void invoke() {
if (isAttached()) {
if (terminate) {
myVirtualMachine.exit(-1);
} else {
// some VM's (like IBM VM 1.4.2 bundled with WebSpere) does not
// resume threads on dispose() like it should
myVirtualMachine.resume();
myVirtualMachine.dispose();
}
} else {
// todo DebugProcessImpl.stopConnecting
closeProcess(true);
}
}
}));
}
private void processVmDeathEvent() {
ManagerThread.assertIsMangerThread();
if (myRunnable != null) {
myRunnable.stop();
myRunnable = null;
}
closeProcess(false);
}
private void processClassPrepareEvent(EventContext context, ClassPrepareEvent event) {
ManagerThread.assertIsMangerThread();
myRequestManager.processClassPrepared(event);
myContextManager.voteResume(context);
}
private void processStepEvent(EventContext context, StepEvent event) {
myRequestManager.deleteStepRequests();
EventRequest request = event.request();
if (request instanceof StepRequest) {
StepRequest stepRequest = (StepRequest) request;
StepRequestor requestor = (StepRequestor) myRequestManager.findRequestor(stepRequest);
int nextStep = requestor.nextStep(event);
if (nextStep == StepRequestor.STOP) {
boolean paused = myContextManager.votePause(context);
if (paused) {
myMulticaster.paused(context);
}
return;
} else {
addNewStepRequest(requestor, nextStep, event.thread(), context.getSuspendPolicy());
}
}
myContextManager.voteResume(context);
}
private void addNewStepRequest(StepRequestor stepRequestor, int stepType, ThreadReference threadReference, int suspendPolicy) {
ManagerThread.assertIsMangerThread();
StepRequest stepRequest = myRequestManager.createStepRequest(stepRequestor, stepType, threadReference, suspendPolicy);
// TODO request filters should be configured by user
// this particular list was taken from idea debugger settings in order to fix MPS-8725
stepRequest.addClassExclusionFilter("java.*");
stepRequest.addClassExclusionFilter("javax.*");
stepRequest.addClassExclusionFilter("org.omg.*");
stepRequest.addClassExclusionFilter("sun.*");
stepRequest.addClassExclusionFilter("junit.*");
stepRequest.addClassExclusionFilter("com.sun.*");
// TODO also might wanna let user to exclude constructors, classloaders, getters,
// synthetic methods (whatever synthetic methods are).
// see idea debugger settings for the full list
myRequestManager.enableRequest(stepRequest);
}
private void processLocatableEvent(final EventContext context, final LocatableEvent event) {
ManagerThread.assertIsMangerThread();
// if inside evaluation, resume
final ThreadReference thread = event.thread();
if (isEvaluated(thread)) {
myContextManager.voteResume(context);
return;
}
final LocatableEventRequestor requestor = (LocatableEventRequestor) myRequestManager.findRequestor(event.request());
// if no requestor or suspend none resume
if (requestor == null || EventRequest.SUSPEND_NONE == requestor.getSuspendPolicy()) {
myContextManager.voteResume(context);
}
// requestor may evaluate something inside, like a condition or an expression to print
scheduleEvaluation(new _FunctionTypes._void_P0_E0() {
public void invoke() {
boolean resume = true;
try {
resume = !(requestor.isRequestHitByEvent(context, event));
} finally {
if (resume) {
myContextManager.voteResume(context);
} else {
try {
if (requestor instanceof JavaBreakpoint && ((JavaBreakpoint) requestor).isLogMessage()) {
// todo move to java breakpoint?
myReporter.reportInformation("Breakpoint hit: " + ((JavaBreakpoint) requestor).getPresentation() + " " + event.location().sourceName() + ":" + event.location().lineNumber());
}
} catch (AbsentInformationException ignore) {
} finally {
boolean paused = myContextManager.votePause(context);
if (paused) {
myMulticaster.paused(context);
}
}
}
}
}
}, thread);
}
public void scheduleEvaluation(final _FunctionTypes._void_P0_E0 evaluationCommand, final ThreadReference threadToEvaluateIn) {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
public void run() {
startEvaluation(threadToEvaluateIn);
try {
evaluationCommand.invoke();
} finally {
finishEvaluation(threadToEvaluateIn);
}
}
});
}
@Nullable
public <R> R invokeEvaluationUnderProgress(final _FunctionTypes._return_P0_E0<? extends R> evaluationCommand, final ThreadReference threadToEvaluateIn) {
ApplicationManager.getApplication().assertIsDispatchThread();
final AtomicReference<R> resultReference = new AtomicReference();
final ProgressWindowWithNotification progress = new ProgressWindowWithNotification(true, false, myProject, null, null);
progress.setTitle("Evaluating");
progress.addListener(new ProgressIndicatorListenerAdapter() {
@Override
public void cancelled() {
progress.stop();
}
});
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
public void run() {
try {
ProgressManager.getInstance().runProcess(new Runnable() {
public void run() {
startEvaluation(threadToEvaluateIn);
try {
resultReference.set(evaluationCommand.invoke());
} finally {
finishEvaluation(threadToEvaluateIn);
}
}
}, progress);
} catch (ProcessCanceledException e) {
progress.cancel();
} catch (RuntimeException e) {
progress.cancel();
throw e;
}
}
});
progress.startBlocking();
return resultReference.get();
}
public void schedule(_FunctionTypes._void_P0_E0 command, _FunctionTypes._void_P0_E0 cancel) {
myManagerThread.schedule(Commands.fromClosure(command, cancel));
}
public void schedule(_FunctionTypes._void_P0_E0 command) {
myManagerThread.schedule(Commands.fromClosure(command));
}
public void invoke(_FunctionTypes._void_P0_E0 command) {
myManagerThread.invoke(Commands.fromClosure(command));
}
public RequestManager getRequestManager() {
return myRequestManager;
}
public ContextManager getContextManager() {
return myContextManager;
}
public BreakpointManagerComponent getBreakpointManager() {
return myBreakpointManager;
}
public VirtualMachine getVirtualMachine() {
return myVirtualMachine;
}
public void addDebugProcessListener(DebugProcessListener listener) {
myMulticaster.addListener(listener);
}
public void removeDebugProcessListener(DebugProcessListener listener) {
myMulticaster.removeListener(listener);
}
public void setDebuggableFramesSelector(IDebuggableFramesSelector framesSelector) {
myFramesSelector = framesSelector;
}
public SystemMessagesReporter getSystemMessagesReporter() {
return myReporter;
}
public DebugProcessMulticaster getMulticaster() {
// todo review all this getters, really
return myMulticaster;
}
private synchronized void startEvaluation(@NotNull ThreadReference threadReference) {
Integer evaluated = MapSequence.fromMap(myEvaluatedThreads).get(threadReference);
if (evaluated == null) {
evaluated = 0;
}
MapSequence.fromMap(myEvaluatedThreads).put(threadReference, evaluated + 1);
}
private synchronized void finishEvaluation(@NotNull ThreadReference threadReference) {
Integer evaluated = MapSequence.fromMap(myEvaluatedThreads).get(threadReference);
assert evaluated != null && evaluated > 0;
if (evaluated == 1) {
MapSequence.fromMap(myEvaluatedThreads).removeKey(threadReference);
} else {
MapSequence.fromMap(myEvaluatedThreads).put(threadReference, evaluated - 1);
}
}
private synchronized boolean isEvaluated(@NotNull ThreadReference threadReference) {
return MapSequence.fromMap(myEvaluatedThreads).containsKey(threadReference) && MapSequence.fromMap(myEvaluatedThreads).get(threadReference) > 0;
}
public static boolean isOnPooledThread() {
// it is sufficient to check for this two
return !(ManagerThread.isManagerThread()) && !(ApplicationManager.getApplication().isDispatchThread());
}
public class EventProcessorRunnable implements Runnable {
private volatile boolean myIsStopped = false;
public EventProcessorRunnable() {
}
@Override
public void run() {
try {
EventQueue eventQueue = myVirtualMachine.eventQueue();
while (!(myIsStopped)) {
final EventSet events = eventQueue.remove();
myManagerThread.invokeAndWait(Commands.fromClosure(new _FunctionTypes._void_P0_E0() {
public void invoke() {
EventContext context = new EventContext(EventsProcessor.this, events);
for (Event event : SetSequence.fromSet(events)) {
if (event instanceof VMDeathEvent) {
processVmDeathEvent();
} else
if (event instanceof VMDisconnectEvent) {
processVmDeathEvent();
} else
if (event instanceof ClassPrepareEvent) {
processClassPrepareEvent(context, (ClassPrepareEvent) event);
} else
if (event instanceof StepEvent) {
processStepEvent(context, (StepEvent) event);
} else
if (event instanceof LocatableEvent) {
processLocatableEvent(context, (LocatableEvent) event);
} else {
myContextManager.voteResume(context);
}
}
}
}));
}
} catch (InterruptedException e) {
myManagerThread.invokeAndWait(Commands.fromClosure(new _FunctionTypes._void_P0_E0() {
public void invoke() {
processVmDeathEvent();
}
}));
}
}
public void stop() {
myIsStopped = true;
}
public boolean isStopped() {
return myIsStopped;
}
}
public enum StepKind {
Over(StepRequest.STEP_OVER),
Into(StepRequest.STEP_INTO),
Out(StepRequest.STEP_OUT);
private final int myJdiType;
private StepKind(int jdiType) {
myJdiType = jdiType;
}
public int getJdiType() {
return myJdiType;
}
}
}