blob: 04442422e18a5c903903ef61043c4e7fff8449db [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.modules.debugger.jpda;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.Bootstrap;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.ClassObjectReference;
import com.sun.jdi.ClassType;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.IntegerValue;
import com.sun.jdi.InterfaceType;
import com.sun.jdi.InternalException;
import com.sun.jdi.InvalidStackFrameException;
import com.sun.jdi.InvalidTypeException;
import com.sun.jdi.LocalVariable;
import com.sun.jdi.Method;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StackFrame;
import com.sun.jdi.StringReference;
import com.sun.jdi.ThreadGroupReference;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.TypeComponent;
import com.sun.jdi.Value;
import com.sun.jdi.VirtualMachine;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyVetoException;
import java.io.InvalidObjectException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.auth.Refreshable;
import org.netbeans.api.debugger.DebuggerEngine;
import org.netbeans.api.debugger.DebuggerInfo;
import org.netbeans.api.debugger.DebuggerManager;
import org.netbeans.api.debugger.LazyActionsManagerListener;
import org.netbeans.api.debugger.Properties;
import org.netbeans.api.debugger.Session;
import org.netbeans.api.debugger.jpda.AbstractDICookie;
import org.netbeans.api.debugger.jpda.AttachingDICookie;
import org.netbeans.api.debugger.jpda.CallStackFrame;
import org.netbeans.api.debugger.jpda.DeadlockDetector;
import org.netbeans.api.debugger.jpda.DebuggerStartException;
import org.netbeans.api.debugger.jpda.InvalidExpressionException;
import org.netbeans.api.debugger.jpda.JPDABreakpoint;
import org.netbeans.api.debugger.jpda.JPDAClassType;
import org.netbeans.api.debugger.jpda.JPDADebugger;
import org.netbeans.api.debugger.jpda.JPDAStep;
import org.netbeans.api.debugger.jpda.JPDAThread;
import org.netbeans.api.debugger.jpda.JPDAThreadGroup;
import org.netbeans.api.debugger.jpda.ListeningDICookie;
import org.netbeans.api.debugger.jpda.ObjectVariable;
import org.netbeans.api.debugger.jpda.SmartSteppingFilter;
import org.netbeans.api.debugger.jpda.Variable;
import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent;
import org.netbeans.api.io.InputOutput;
import org.netbeans.modules.debugger.jpda.actions.ActionErrorMessageCallback;
import org.netbeans.modules.debugger.jpda.actions.ActionMessageCallback;
import org.netbeans.modules.debugger.jpda.actions.ActionStatusDisplayCallback;
import org.netbeans.modules.debugger.jpda.actions.CompoundSmartSteppingListener;
import org.netbeans.modules.debugger.jpda.breakpoints.BreakpointsEngineListener;
import org.netbeans.modules.debugger.jpda.expr.EvaluatorExpression;
import org.netbeans.modules.debugger.jpda.expr.InvocationExceptionTranslated;
import org.netbeans.modules.debugger.jpda.expr.JDIVariable;
import org.netbeans.modules.debugger.jpda.expr.formatters.Formatters;
import org.netbeans.modules.debugger.jpda.expr.formatters.FormattersLoopControl;
import org.netbeans.modules.debugger.jpda.expr.formatters.VariablesFormatter;
import org.netbeans.modules.debugger.jpda.jdi.ClassNotPreparedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ClassTypeWrapper;
import org.netbeans.modules.debugger.jpda.jdi.IllegalArgumentExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.IllegalThreadStateExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.IntegerValueWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InternalExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.InvalidStackFrameExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.MirrorWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ObjectCollectedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.StackFrameWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ThreadReferenceWrapper;
import org.netbeans.modules.debugger.jpda.jdi.VMDisconnectedExceptionWrapper;
import org.netbeans.modules.debugger.jpda.jdi.ValueWrapper;
import org.netbeans.modules.debugger.jpda.jdi.VirtualMachineWrapper;
import org.netbeans.modules.debugger.jpda.models.AbstractObjectVariable;
import org.netbeans.modules.debugger.jpda.models.AbstractVariable;
import org.netbeans.modules.debugger.jpda.models.CallStackFrameImpl;
import org.netbeans.modules.debugger.jpda.models.ClassVariableImpl;
import org.netbeans.modules.debugger.jpda.models.JPDAClassTypeImpl;
import org.netbeans.modules.debugger.jpda.models.JPDAThreadImpl;
import org.netbeans.modules.debugger.jpda.models.ObjectTranslation;
import org.netbeans.modules.debugger.jpda.models.ThreadsCache;
import org.netbeans.modules.debugger.jpda.models.VariableMirrorTranslator;
import org.netbeans.modules.debugger.jpda.util.JPDAUtils;
import org.netbeans.modules.debugger.jpda.util.Operator;
import org.netbeans.spi.debugger.ContextProvider;
import org.netbeans.spi.debugger.DebuggerEngineProvider;
import org.netbeans.spi.debugger.DelegatingSessionProvider;
import org.netbeans.spi.debugger.jpda.Evaluator;
import org.netbeans.spi.debugger.jpda.SmartSteppingCallback.StopOrStep;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.Mutex;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;
import org.openide.util.WeakSet;
import org.openide.util.lookup.Lookups;
/**
* Representation of a debugging session.
*
* @author Jan Jancura
*/
@JPDADebugger.Registration(path="netbeans-JPDASession")
public class JPDADebuggerImpl extends JPDADebugger {
private static final Logger logger = Logger.getLogger(JPDADebuggerImpl.class.getName());
private static final boolean SINGLE_THREAD_STEPPING = !Boolean.getBoolean("netbeans.debugger.multiThreadStepping");
// variables ...............................................................
//private DebuggerEngine debuggerEngine;
private VirtualMachine virtualMachine = null;
private final Object virtualMachineLock = new Object();
private Throwable throwable;
private int state = 0;
private final Object stateLock = new Object();
private Operator operator;
private PropertyChangeSupport pcs;
public PropertyChangeSupport varChangeSupport = new PropertyChangeSupport(this);
private JPDAThreadImpl currentThread;
private CallStackFrame currentCallStackFrame;
private final Object currentThreadAndFrameLock = new Object();
private volatile JPDAThreadImpl currentSuspendedNoFireThread; // Used during initial event processing only
private int suspend = (SINGLE_THREAD_STEPPING) ? SUSPEND_EVENT_THREAD : SUSPEND_ALL;
public final ReentrantReadWriteLock accessLock = new DebuggerReentrantReadWriteLock(true);
private final Object LOCK2 = new Object ();
private boolean starting;
private AbstractDICookie attachingCookie;
private JavaEngineProvider javaEngineProvider;
private Set<String> languages;
private String lastStratumn;
private ContextProvider lookupProvider;
private ObjectTranslation threadsTranslation;
private ObjectTranslation localsTranslation;
private ExpressionPool expressionPool;
private ThreadsCache threadsCache;
private DeadlockDetector deadlockDetector;
private ThreadsCollectorImpl threadsCollector;
private final Object threadsCollectorLock = new Object();
private final Map<Long, String> markedObjects = new LinkedHashMap<Long, String>();
private final Map<String, ObjectVariable> markedObjectLabels = new LinkedHashMap<String, ObjectVariable>();
private final Map<ClassType, List> allInterfacesMap = new WeakHashMap<ClassType, List>();
private StackFrame altCSF = null; //PATCH 48174
private boolean doContinue = true; // Whether resume() will actually resume
private Boolean singleThreadStepResumeDecision = null;
private Boolean stepInterruptByBptResumeDecision = null;
private boolean breakpointsActive = true;
private final DebuggerConsoleIO io;
private PeriodicThreadsDump ptd;
private boolean vmSuspended = false; // true after VM.suspend() was called.
// init ....................................................................
public JPDADebuggerImpl (ContextProvider lookupProvider) {
this.lookupProvider = lookupProvider;
Properties p = Properties.getDefault().getProperties("debugger.options.JPDA");
int stepResume = p.getInt("StepResume", (SINGLE_THREAD_STEPPING) ? 1 : 0);
suspend = (stepResume == 1) ? SUSPEND_EVENT_THREAD : SUSPEND_ALL;
pcs = new PropertyChangeSupport (this);
List l = lookupProvider.lookup (null, DebuggerEngineProvider.class);
int i, k = l.size ();
for (i = 0; i < k; i++) {
if (l.get (i) instanceof JavaEngineProvider) {
javaEngineProvider = (JavaEngineProvider) l.get (i);
}
}
if (javaEngineProvider == null) {
throw new IllegalArgumentException
("JavaEngineProvider have to be used to start JPDADebugger!");
}
languages = new HashSet<String>();
languages.add ("Java");
threadsTranslation = ObjectTranslation.createThreadTranslation(this);
localsTranslation = ObjectTranslation.createLocalsTranslation(this);
this.expressionPool = new ExpressionPool();
io = new DebuggerConsoleIO(this, lookupProvider);
}
// JPDADebugger methods ....................................................
/**
* Returns current state of JPDA debugger.
*
* @return current state of JPDA debugger
* @see #STATE_STARTING
* @see #STATE_RUNNING
* @see #STATE_STOPPED
* @see #STATE_DISCONNECTED
*/
@Override
public int getState () {
synchronized (stateLock) {
return state;
}
}
/**
* Gets value of suspend property.
*
* @return value of suspend property
*/
@Override
public int getSuspend () {
synchronized (stateLock) {
return suspend;
}
}
/**
* Sets value of suspend property.
*
* @param s a new value of suspend property
*/
@Override
public void setSuspend (int s) {
int old;
synchronized (stateLock) {
if (s == suspend) {
return;
}
old = suspend;
suspend = s;
}
firePropertyChange (PROP_SUSPEND, Integer.valueOf(old), Integer.valueOf(s));
}
/**
* Returns current thread or null.
*
* @return current thread or null
*/
@Override
public JPDAThread getCurrentThread () {
synchronized (currentThreadAndFrameLock) {
return currentThread;
}
}
/**
* Returns current stack frame or null.
*
* @return current stack frame or null
*/
@Override
public CallStackFrame getCurrentCallStackFrame () {
synchronized (currentThreadAndFrameLock) {
return currentCallStackFrame;
}
}
/**
* Evaluates given expression in the current context.
*
* @param expression a expression to be evaluated
*
* @return current value of given expression
*/
@Override
public Variable evaluate (String expression) throws InvalidExpressionException {
return evaluate(expression, null, null);
}
/**
* Evaluates given expression in the context of the variable.
*
* @param expression a expression to be evaluated
*
* @return current value of given expression
*/
public Variable evaluate (String expression, ObjectVariable var)
throws InvalidExpressionException {
return evaluate(expression, null, var);
}
/**
* Evaluates given expression in the current context.
*
* @param expression a expression to be evaluated
*
* @return current value of given expression
*/
public Variable evaluate (String expression, CallStackFrame csf)
throws InvalidExpressionException {
return evaluate(expression, csf, null);
}
/**
* Evaluates given expression in the current context.
*
* @param expression a expression to be evaluated
*
* @return current value of given expression
*/
public Variable evaluate (String expression, CallStackFrame csf, ObjectVariable var)
throws InvalidExpressionException {
return evaluateGeneric(new EvaluatorExpression(expression), csf, var);
}
/**
* Waits till the Virtual Machine is started and returns
* {@link DebuggerStartException} if any.
*
* @throws DebuggerStartException if some problems occurs during debugger
* start
*
* @see AbstractDICookie#getVirtualMachine()
*/
@Override
public void waitRunning () throws DebuggerStartException {
waitRunning(0);
}
/**
* Waits till the Virtual Machine is started.
*
* @return <code>true</code> when debugger started or finished in the mean time,
* <code>false</code> when the debugger is still starting
* @throws DebuggerStartException if some problems occurs during debugger
* start
*
* @see AbstractDICookie#getVirtualMachine()
*/
private boolean waitRunning(long timeout) throws DebuggerStartException {
synchronized (LOCK2) {
int state = getState();
if (state == STATE_DISCONNECTED) {
if (throwable != null) {
throw new DebuggerStartException (throwable);
} else {
return true;
}
}
if (!starting && state > STATE_STARTING || throwable != null) {
return true; // We're already running
}
try {
logger.log(Level.FINE, "JPDADebuggerImpl.waitRunning(): starting = {0}, state = {1}, exception = {2}", new Object[]{starting, state, throwable});
LOCK2.wait (timeout);
if ((starting || state == STATE_STARTING) && throwable == null) {
return false; // We're still not running and the time expired
}
} catch (InterruptedException e) {
throw new DebuggerStartException (e);
}
if (throwable != null) {
throw new DebuggerStartException (throwable);
} else {
return true;
}
}
}
public DebuggerConsoleIO getConsoleIO() {
return io;
}
/**
* Returns <code>true</code> if this debugger supports Pop action.
*
* @return <code>true</code> if this debugger supports Pop action
*/
@Override
public boolean canPopFrames () {
VirtualMachine vm = getVirtualMachine ();
if (vm == null) {
return false;
}
return VirtualMachineWrapper.canPopFrames0(vm);
}
/**
* Returns <code>true</code> if this debugger supports fix & continue
* (HotSwap).
*
* @return <code>true</code> if this debugger supports fix & continue
*/
@Override
public boolean canFixClasses () {
VirtualMachine vm = getVirtualMachine ();
if (vm == null) {
return false;
}
return VirtualMachineWrapper.canRedefineClasses0(vm);
}
/**
* Implements fix & continue (HotSwap). Map should contain class names
* as a keys, and byte[] arrays as a values.
*
* @param classes a map from class names to be fixed to byte[]
*/
@Override
public void fixClasses (Map<String, byte[]> classes) {
PropertyChangeEvent evt = null;
accessLock.writeLock().lock();
try {
// 1) redefine classes
Map<ReferenceType, byte[]> map = new HashMap<ReferenceType, byte[]>();
Iterator<Map.Entry<String, byte[]>> e = classes.entrySet ().iterator ();
VirtualMachine vm = getVirtualMachine();
if (vm == null) {
return ; // The session has finished
}
try {
while (e.hasNext ()) {
Map.Entry<String, byte[]> classEntry = e.next();
String className = classEntry.getKey();
byte[] bytes = classEntry.getValue();
List<ReferenceType> classRefs = VirtualMachineWrapper.classesByName (vm, className);
int j, jj = classRefs.size ();
for (j = 0; j < jj; j++) {
map.put (
classRefs.get (j),
bytes
);
}
}
VirtualMachineWrapper.redefineClasses (vm, map);
} catch (InternalExceptionWrapper ex) {
} catch (VMDisconnectedExceptionWrapper ex) {
}
// update breakpoints
fixBreakpoints();
firePropertyChange(JPDADebugger.PROP_CLASSES_FIXED, null, null);
// 2) pop obsoleted frames
JPDAThread t = getCurrentThread ();
if (t != null && t.isSuspended()) {
try {
if (t.getStackDepth () < 2) {
return;
}
CallStackFrame frame;
try {
CallStackFrame[] frames = t.getCallStack(0, 1);
if (frames.length == 0) {
return ;
}
frame = frames[0]; // Retrieve the new, possibly obsoleted frame and check it.
} catch (AbsentInformationException ex) {
return;
}
//PATCH #52209
if (frame.isObsolete () && ((CallStackFrameImpl) frame).canPop()) {
frame.popFrame ();
setState (STATE_RUNNING);
evt = updateCurrentCallStackFrameNoFire (t);
setState (STATE_STOPPED);
}
} catch (InvalidStackFrameException ex) {
}
}
} finally {
accessLock.writeLock().unlock();
}
if (evt != null) {
firePropertyChange(evt);
}
}
public void fixBreakpoints() {
// Just reset the time stamp so that new line numbers are taken.
EditorContextBridge.getContext().disposeTimeStamp(this);
EditorContextBridge.getContext().createTimeStamp(this);
Session s = getSession();
DebuggerEngine de = s.getEngineForLanguage ("Java");
if (de == null) {
de = s.getCurrentEngine();
}
if (de != null) {
BreakpointsEngineListener bel = null;
List lazyListeners = de.lookup(null, LazyActionsManagerListener.class);
for (int li = 0; li < lazyListeners.size(); li++) {
Object service = lazyListeners.get(li);
if (service instanceof BreakpointsEngineListener) {
bel = (BreakpointsEngineListener) service;
break;
}
}
if (bel != null) {
bel.fixBreakpointImpls ();
}
}
expressionPool.clear();
}
@Override
public boolean getBreakpointsActive() {
return breakpointsActive;
}
@Override
public void setBreakpointsActive(boolean active) {
synchronized (this) {
if (breakpointsActive == active) {
return ;
}
breakpointsActive = active;
}
firePropertyChange(PROP_BREAKPOINTS_ACTIVE, !active, active);
}
public Session getSession() {
return lookupProvider.lookupFirst(null, Session.class);
}
public RequestProcessor getRequestProcessor() {
return javaEngineProvider.getRequestProcessor();
}
private Boolean canBeModified;
private final Object canBeModifiedLock = new Object();
@Override
public boolean canBeModified() {
VirtualMachine vm = getVirtualMachine ();
if (vm == null) {
return false;
}
synchronized (canBeModifiedLock) {
if (canBeModified == null) {
canBeModified = vm.canBeModified();
}
return canBeModified.booleanValue();
}
}
private SmartSteppingFilter smartSteppingFilter;
/**
* Returns instance of SmartSteppingFilter.
*
* @return instance of SmartSteppingFilter
*/
@Override
public SmartSteppingFilter getSmartSteppingFilter () {
if (smartSteppingFilter == null) {
smartSteppingFilter = lookupProvider.lookupFirst(null, SmartSteppingFilter.class);
}
return smartSteppingFilter;
}
CompoundSmartSteppingListener compoundSmartSteppingListener;
private CompoundSmartSteppingListener getCompoundSmartSteppingListener () {
if (compoundSmartSteppingListener == null) {
compoundSmartSteppingListener = lookupProvider.lookupFirst(null, CompoundSmartSteppingListener.class);
}
return compoundSmartSteppingListener;
}
/**
* Test whether we should stop here according to the smart-stepping rules.
*/
StopOrStep stopHere(JPDAThread t) {
CallStackFrame topFrame = null;
try {
CallStackFrame[] topFrameArr = t.getCallStack(0, 1);
if (topFrameArr.length > 0) {
topFrame = topFrameArr[0];
}
} catch (AbsentInformationException aiex) {}
if (topFrame != null) {
return getCompoundSmartSteppingListener().stopAt
(lookupProvider, topFrame, getSmartSteppingFilter());
} else {
return getCompoundSmartSteppingListener().stopHere
(lookupProvider, t, getSmartSteppingFilter()) ?
StopOrStep.stop() : StopOrStep.skip();
}
}
/**
* Helper method that fires JPDABreakpointEvent on JPDABreakpoints.
*
* @param breakpoint a breakpoint to be changed
* @param event a event to be fired
*/
@Override
public void fireBreakpointEvent (
JPDABreakpoint breakpoint,
JPDABreakpointEvent event
) {
super.fireBreakpointEvent (breakpoint, event);
}
/**
* Adds property change listener.
*
* @param l new listener.
*/
@Override
public void addPropertyChangeListener (PropertyChangeListener l) {
pcs.addPropertyChangeListener (l);
}
/**
* Removes property change listener.
*
* @param l removed listener.
*/
@Override
public void removePropertyChangeListener (PropertyChangeListener l) {
pcs.removePropertyChangeListener (l);
}
/**
* Adds property change listener.
*
* @param l new listener.
*/
@Override
public void addPropertyChangeListener (String propertyName, PropertyChangeListener l) {
pcs.addPropertyChangeListener (propertyName, l);
}
/**
* Removes property change listener.
*
* @param l removed listener.
*/
@Override
public void removePropertyChangeListener (String propertyName, PropertyChangeListener l) {
pcs.removePropertyChangeListener (propertyName, l);
}
// internal interface ......................................................
public void popFrames (ThreadReference thread, StackFrame frame) {
PropertyChangeEvent evt = null;
accessLock.readLock().lock();
try {
JPDAThreadImpl threadImpl = getThread(thread);
setState (STATE_RUNNING);
try {
threadImpl.popFrames(frame);
evt = updateCurrentCallStackFrameNoFire(threadImpl);
} catch (IncompatibleThreadStateException ex) {
Exceptions.printStackTrace(ex);
} finally {
setState (STATE_STOPPED);
}
} finally {
accessLock.readLock().unlock();
}
if (evt != null) {
firePropertyChange(evt);
}
}
public void setException (Throwable t) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("JPDADebuggerImpl.setException("+t+")");
}
synchronized (LOCK2) {
throwable = t;
starting = false;
LOCK2.notifyAll ();
}
}
public void setCurrentThread (JPDAThread thread) {
Object oldT;
PropertyChangeEvent event;
CallStackFrame topFrame = getTopFrame(thread);
synchronized (currentThreadAndFrameLock) {
oldT = currentThread;
currentThread = (JPDAThreadImpl) thread;
event = updateCurrentCallStackFrameNoFire(topFrame);
}
checkJSR45Languages(thread);
if (thread != oldT) {
firePropertyChange (PROP_CURRENT_THREAD, oldT, thread);
}
if (event != null) {
firePropertyChange(event);
}
setState(thread.isSuspended() ? STATE_STOPPED : STATE_RUNNING);
}
/**
* Set the current thread and call stack, but do not fire changes.
* @return The PropertyChangeEvent associated with this change, it can have
* attached other PropertyChangeEvents as a propagation ID.
*/
private PropertyChangeEvent setCurrentThreadNoFire(JPDAThread thread) {
Object oldT;
PropertyChangeEvent evt = null;
PropertyChangeEvent evt2;
CallStackFrame topFrame = getTopFrame(thread);
synchronized (currentThreadAndFrameLock) {
oldT = currentThread;
currentThread = (JPDAThreadImpl) thread;
evt2 = updateCurrentCallStackFrameNoFire(topFrame);
}
if (thread != oldT) {
evt = new PropertyChangeEvent(this, PROP_CURRENT_THREAD, oldT, thread);
}
if (evt == null) {
evt = evt2;
} else if (evt2 != null) {
evt.setPropagationId(evt2);
}
return evt;
}
public void setCurrentCallStackFrame (CallStackFrame callStackFrame) {
CallStackFrame old = setCurrentCallStackFrameNoFire(callStackFrame);
if (old == callStackFrame) {
return ;
}
if (callStackFrame != null) {
checkJSR45Languages(callStackFrame);
}
firePropertyChange (
PROP_CURRENT_CALL_STACK_FRAME,
old,
callStackFrame
);
}
private CallStackFrame setCurrentCallStackFrameNoFire (CallStackFrame callStackFrame) {
CallStackFrame old;
synchronized (currentThreadAndFrameLock) {
if (callStackFrame == currentCallStackFrame) {
return callStackFrame;
}
old = currentCallStackFrame;
currentCallStackFrame = callStackFrame;
}
return old;
}
/**
* Used by AbstractVariable and watches.
*/
// * Might be changed to return a variable with disabled collection. When not used any more,
// * it's collection must be enabled again.
public Value evaluateIn (String expression) throws InvalidExpressionException {
return evaluateIn(new EvaluatorExpression(expression), null);
}
/**
* Used by BreakpointImpl.
*/
// * Might be changed to return a variable with disabled collection. When not used any more,
// * it's collection must be enabled again.
public Value evaluateIn (EvaluatorExpression expression, CallStackFrame csf) throws InvalidExpressionException {
return evaluateIn(expression, csf, null);
}
// * Might be changed to return a variable with disabled collection. When not used any more,
// * it's collection must be enabled again.
public Value evaluateIn (EvaluatorExpression expression, CallStackFrame csf, ObjectVariable var) throws InvalidExpressionException {
Variable variable = evaluateGeneric(expression, csf, var);
if (variable instanceof JDIVariable) {
return ((JDIVariable) variable).getJDIValue();
} else {
return null;
}
}
//PATCH 48174
public void setAltCSF(StackFrame sf) {
altCSF = sf;
}
public StackFrame getAltCSF() {
return altCSF;
}
/**
* Used by WatchesModel & BreakpointImpl.
*/
// * Might be changed to return a variable with disabled collection. When not used any more,
// * it's collection must be enabled again.
public Value evaluateIn (EvaluatorExpression expression) throws InvalidExpressionException {
return evaluateIn(expression, null, null);
}
InvalidExpressionException methodCallsUnsupportedExc;
/**
* Evaluates given expression in the current context.
*
* @param expression a expression to be evaluated
*
* @return current value of given expression
*/
private Variable evaluateGeneric(EvaluatorExpression expression, CallStackFrame c, ObjectVariable var)
throws InvalidExpressionException {
Session s = getSession();
Evaluator e = s.lookupFirst(s.getCurrentLanguage(), Evaluator.class);
logger.log(Level.FINE, "Have evaluator {0} for language {1}", new Object[]{e, s.getCurrentLanguage()}); // NOI18N
if (e == null) {
e = s.lookupFirst("Java", Evaluator.class);
if (e == null) {
Exceptions.printStackTrace(new IllegalStateException("No evaluator registered for Java language!"));
e = new JavaEvaluator(s);
}
}
CallStackFrameImpl csf;
if (c instanceof CallStackFrameImpl) {
csf = (CallStackFrameImpl) c;
} else {
csf = (CallStackFrameImpl) getCurrentCallStackFrame ();
}
if (csf == null) {
StackFrame sf = getAltCSF();
if (sf != null) {
try {
ThreadReference t = StackFrameWrapper.thread(sf);
JPDAThread tr = getThread(t);
CallStackFrame[] stackFrames = tr.getCallStack(0, 1);
if (stackFrames.length > 0) {
c = stackFrames[0];
csf = (CallStackFrameImpl) c;
}
} catch (InternalExceptionWrapper ex) {
} catch (InvalidStackFrameExceptionWrapper ex) {
} catch (VMDisconnectedExceptionWrapper ex) {
} catch (AbsentInformationException aiex) {
}
}
}
if (csf == null) {
throw new InvalidExpressionException
(NbBundle.getMessage(JPDADebuggerImpl.class, "MSG_NoCurrentContextStackFrame"));
}
JPDAThread frameThread = csf.getThread();
JPDAThreadImpl frameThreadImpl = (JPDAThreadImpl) frameThread;
//Object pendingAction = frameThreadImpl.getPendingAction();
//if (pendingAction != null) { For the case that evaluation should be blocked by pending action
// //return frameThreadImpl.getPendingVariable(pendingActions);
// throw new InvalidExpressionException(frameThreadImpl.getPendingString(pendingAction));
//}
Lock lock = frameThreadImpl.accessLock.writeLock();
lock.lock();
Variable vr;
try {
if (!frameThread.isSuspended() && !((JPDAThreadImpl) frameThread).isSuspendedNoFire()) {
// Thread not suspended => Can not start evaluation
throw new InvalidExpressionException
(NbBundle.getMessage(JPDADebuggerImpl.class, "MSG_NoCurrentContextStackFrame"));
}
ObjectReference v = null;
if (var instanceof JDIVariable) {
v = (ObjectReference) ((JDIVariable) var).getJDIValue();
if (v == null) {
throw new InvalidExpressionException
(NbBundle.getMessage(JPDADebuggerImpl.class,
"MSG_CanNotEvaluateInContextOfNull",
expression.getExpression()));
}
}
Evaluator.Result result;
final JPDAThreadImpl[] resumedThread = new JPDAThreadImpl[] { null };
try {
StackFrame sf = csf.getStackFrame();
int stackDepth = csf.getFrameDepth();
final ThreadReference tr = frameThreadImpl.getThreadReference();
Runnable methodToBeInvokedNotifier = new Runnable() {
@Override
public void run() {
if (resumedThread[0] == null) {
JPDAThreadImpl theResumedThread = getThread(tr);
try {
theResumedThread.notifyMethodInvoking();
} catch (PropertyVetoException pvex) {
throw new RuntimeException(
new InvalidExpressionException (pvex.getMessage()));
} catch (ThreadDeath td) {
throw td;
} catch (Throwable t) {
Exceptions.printStackTrace(t);
}
resumedThread[0] = theResumedThread;
}
}
};
Lookup context;
if (var != null) {
if (v != null) {
context = Lookups.fixed(csf, var, sf, stackDepth, v, methodToBeInvokedNotifier);
} else {
context = Lookups.fixed(csf, var, sf, stackDepth, methodToBeInvokedNotifier);
}
} else {
context = Lookups.fixed(csf, sf, stackDepth, methodToBeInvokedNotifier);
}
result = expression.evaluate(e, new Evaluator.Context(context));
} catch (InternalExceptionWrapper ex) {
return null;
} catch (InvalidStackFrameExceptionWrapper ex) {
throw new InvalidExpressionException
(NbBundle.getMessage(JPDADebuggerImpl.class, "MSG_NoCurrentContextStackFrame"));
} catch (VMDisconnectedExceptionWrapper ex) {
// Causes kill action when something is being evaluated.
return null;
} catch (InternalException ex) {
InvalidExpressionException isex = new InvalidExpressionException(ex.getLocalizedMessage());
isex.initCause(ex);
Exceptions.attachMessage(isex, "Expression = '"+expression+"'");
throw isex;
} catch (RuntimeException rex) {
Throwable cause = rex.getCause();
if (cause instanceof InvalidExpressionException) {
Exceptions.attachMessage(cause, "Expression = '"+expression+"'");
throw (InvalidExpressionException) cause;
} else {
throw rex;
}
} finally {
if (resumedThread[0] != null) {
resumedThread[0].notifyMethodInvokeDone();
}
}
vr = result.getVariable();
if (vr == null) {
vr = createVariable(result.getValue());
}
} finally {
lock.unlock();
}
return vr;
}
/**
* Used by AbstractVariable.
*/
public Value invokeMethod (
ObjectReference reference,
Method method,
Value[] arguments
) throws InvalidExpressionException {
return invokeMethod(null, reference, null, method, arguments, 0, null);
}
public Value invokeMethod (
ObjectReference reference,
Method method,
Value[] arguments,
int maxLength
) throws InvalidExpressionException {
return invokeMethod(null, reference, null, method, arguments, maxLength, null);
}
/**
* Used by AbstractVariable.
*/
public Value invokeMethod (
JPDAThreadImpl thread,
ObjectReference reference,
Method method,
Value[] arguments
) throws InvalidExpressionException {
return invokeMethod(thread, reference, method, arguments, null);
}
public Value invokeMethod (
JPDAThreadImpl thread,
ObjectReference reference,
Method method,
Value[] arguments,
InvocationExceptionTranslated existingInvocationException
) throws InvalidExpressionException {
return invokeMethod(thread, reference, null, method, arguments, 0, existingInvocationException);
}
/**
* Used by JPDAClassTypeImpl.
*/
public Value invokeMethod (
JPDAThreadImpl thread,
ClassType classType,
Method method,
Value[] arguments
) throws InvalidExpressionException {
return invokeMethod(thread, null, classType, method, arguments, 0, null);
}
private Value invokeMethod (
JPDAThreadImpl thread,
ObjectReference reference,
ClassType classType,
Method method,
Value[] arguments,
int maxLength,
InvocationExceptionTranslated existingInvocationException
) throws InvalidExpressionException {
synchronized (currentThreadAndFrameLock) {
if (thread == null && currentThread == null) {
thread = currentSuspendedNoFireThread;
if (thread == null) {
throw new InvalidExpressionException
(NbBundle.getMessage(JPDADebuggerImpl.class, "MSG_NoCurrentContext"));
}
}
if (thread == null) {
thread = currentThread;
}
}
thread.accessLock.writeLock().lock();
try {
if (methodCallsUnsupportedExc != null) {
throw methodCallsUnsupportedExc;
}
boolean threadSuspended = false;
JPDAThread frameThread = null;
CallStackFrameImpl csf = null;
try {
// Remember the current stack frame, it might be necessary to re-set.
csf = (CallStackFrameImpl) getCurrentCallStackFrame ();
if (csf != null) {
try {
frameThread = csf.getThread();
} catch (InvalidStackFrameException isfex) {}
}
ThreadReference tr = thread.getThreadReference();
try {
thread.notifyMethodInvoking();
threadSuspended = true;
} catch (PropertyVetoException pvex) {
throw new InvalidExpressionException (pvex.getMessage());
} catch (RuntimeException rex) {
// Give up
thread.notifyMethodInvokeDone();
throw rex;
} catch (ThreadDeath td) {
throw td;
} catch (Error e) {
// Give up
thread.notifyMethodInvokeDone();
throw e;
}
try {
Value v;
if (reference != null) {
v = org.netbeans.modules.debugger.jpda.expr.TreeEvaluator.
invokeVirtual (
reference,
method,
tr,
Arrays.asList (arguments),
this,
existingInvocationException
);
} else {
v = org.netbeans.modules.debugger.jpda.expr.TreeEvaluator.
invokeVirtual (
classType,
method,
tr,
Arrays.asList (arguments),
this,
existingInvocationException
);
}
if (maxLength > 0 && maxLength < Integer.MAX_VALUE && (v instanceof StringReference)) {
v = cutLength((StringReference) v, maxLength, tr);
}
return v;
} catch (InternalException e) {
throw new InvalidExpressionException(e);
}
} catch (InvalidExpressionException ieex) {
if (ieex.getTargetException() instanceof UnsupportedOperationException) {
methodCallsUnsupportedExc = ieex;
}
throw ieex;
} finally {
if (threadSuspended) {
thread.notifyMethodInvokeDone();
}
if (frameThread != null && csf != null) {
try {
csf.getThread();
} catch (InvalidStackFrameException isfex) {
// The current frame is invalidated, set the new current...
int depth = csf.getFrameDepth();
try {
CallStackFrame csf2 = frameThread.getCallStack(depth, depth + 1)[0];
setCurrentCallStackFrameNoFire(csf2);
} catch (AbsentInformationException aiex) {
setCurrentCallStackFrame(null);
}
}
}
}
} finally {
thread.accessLock.writeLock().unlock();
}
}
private Value cutLength(StringReference sr, int maxLength, ThreadReference tr) throws InvalidExpressionException {
try {
Method stringLengthMethod;
stringLengthMethod = ClassTypeWrapper.concreteMethodByName(
(ClassType) ValueWrapper.type (sr), "length", "()I"); // NOI18N
List<Value> emptyArgs = Collections.emptyList();
IntegerValue lengthValue = (IntegerValue) org.netbeans.modules.debugger.jpda.expr.TreeEvaluator.
invokeVirtual (
sr,
stringLengthMethod,
tr,
emptyArgs,
this
);
if (IntegerValueWrapper.value(lengthValue) > maxLength) {
Method subStringMethod = ClassTypeWrapper.concreteMethodByName(
(ClassType) ValueWrapper.type (sr), "substring", "(II)Ljava/lang/String;"); // NOI18N
if (subStringMethod != null) {
sr = (StringReference) org.netbeans.modules.debugger.jpda.expr.TreeEvaluator.
invokeVirtual (
sr,
subStringMethod,
tr,
Arrays.asList(new Value [] { VirtualMachineWrapper.mirrorOf(MirrorWrapper.virtualMachine(sr), 0),
VirtualMachineWrapper.mirrorOf(MirrorWrapper.virtualMachine(sr), maxLength) }),
this
);
}
}
} catch (InternalExceptionWrapper ex) {
} catch (ObjectCollectedExceptionWrapper ex) {
} catch (VMDisconnectedExceptionWrapper ex) {
} catch (ClassNotPreparedExceptionWrapper ex) {
}
return sr;
}
public static String getGenericSignature (TypeComponent component) {
if (tcGenericSignatureMethod == null) {
return null;
}
try {
return (String) tcGenericSignatureMethod.invoke
(component, new Object[0]);
} catch (IllegalAccessException e) {
Exceptions.printStackTrace(e);
return null; // should not happen
} catch (InvocationTargetException e) {
Exceptions.printStackTrace(e);
return null; // should not happen
}
}
public static String getGenericSignature (LocalVariable component) {
if (lvGenericSignatureMethod == null) {
return null;
}
try {
return (String) lvGenericSignatureMethod.invoke(component, new Object[0]);
} catch (IllegalAccessException e) {
Exceptions.printStackTrace(e);
return null; // should not happen
} catch (InvocationTargetException e) {
Exceptions.printStackTrace(e);
return null; // should not happen
}
}
public VirtualMachine getVirtualMachine () {
synchronized (virtualMachineLock) {
return virtualMachine;
}
}
public Operator getOperator () {
synchronized (virtualMachineLock) {
return operator;
}
}
public void setStarting () {
setState (STATE_STARTING);
}
public synchronized void setAttaching(AbstractDICookie cookie) {
this.attachingCookie = cookie;
}
public void setRunning (VirtualMachine vm, Operator o) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Start - JPDADebuggerImpl.setRunning ()");
JPDAUtils.printFeatures (logger, vm);
}
synchronized (LOCK2) {
starting = true;
}
synchronized (virtualMachineLock) {
virtualMachine = vm;
operator = o;
}
if (logger.isLoggable(Level.FINEST)) {
ptd = new PeriodicThreadsDump(vm);
}
synchronized (canBeModifiedLock) {
canBeModified = null; // Reset the can be modified flag
}
initGenericsSupport ();
EditorContextBridge.getContext().createTimeStamp(this);
// Iterator i = getVirtualMachine ().allThreads ().iterator ();
// while (i.hasNext ()) {
// ThreadReference tr = (ThreadReference) i.next ();
// if (tr.isSuspended ()) {
// if (startVerbose)
// System.out.println("\nS JPDADebuggerImpl.setRunning () - " +
// "thread supended"
// );
// setState (STATE_RUNNING);
// synchronized (LOCK) {
// virtualMachine.resume ();
// }
// if (startVerbose)
// System.out.println("\nS JPDADebuggerImpl.setRunning () - " +
// "thread supended - VM resumed - end"
// );
// synchronized (LOCK2) {
// LOCK2.notify ();
// }
// return;
// }
// }
ThreadsCache tc;
synchronized (threadsCollectorLock) {
tc = threadsCache;
}
if (tc != null) {
tc.setVirtualMachine(vm);
}
setState (STATE_RUNNING);
synchronized (virtualMachineLock) {
vm = virtualMachine; // re-take the VM, it can be nulled by finish()
}
if (vm != null) {
notifyToBeResumedAll();
accessLock.writeLock().lock();
try {
VirtualMachineWrapper.resume(vm);
} catch (VMDisconnectedExceptionWrapper e) {
} catch (InternalExceptionWrapper e) {
} finally {
accessLock.writeLock().unlock();
}
}
logger.fine(" JPDADebuggerImpl.setRunning () finished, VM resumed.");
synchronized (LOCK2) {
starting = false;
LOCK2.notifyAll ();
}
}
/**
* Performs stop action.
*/
public void setStoppedState (ThreadReference thread, boolean stoppedAll) {
setStoppedState(thread, true, false);
}
public void setStoppedState (ThreadReference thread, boolean stoppedAll, boolean forceThreadSwitch) {
PropertyChangeEvent evt = null;
accessLock.readLock().lock();
if (logger.isLoggable(Level.FINE)) {
logger.fine("setStoppedState("+thread+", "+stoppedAll+", "+forceThreadSwitch+")");
}
try {
// this method can be called in stopped state to switch
// the current thread only
JPDAThread t = getThread (thread);
if (!forceThreadSwitch) {
JPDAThread c = getCurrentThread();
if (logger.isLoggable(Level.FINER)) {
logger.finer("Have c = "+((c == null) ? null : ((JPDAThreadImpl) c).getThreadStateLog()));
logger.finer("Have t = "+((t == null) ? null : ((JPDAThreadImpl) t).getThreadStateLog()));
}
if (c != null && c != t
&& (!stoppedAll || ((JPDAThreadImpl) c).isSuspendedOnAnEvent())
&& c.isSuspended()) {
// We already have a suspended current thread, do not switch in that case.
// But update it's frames:
evt = updateCurrentCallStackFrameNoFire(c);
return ;
}
}
logger.fine(" changing the current thread.");
checkJSR45Languages (t);
evt = setCurrentThreadNoFire(t);
PropertyChangeEvent evt2 = setStateNoFire(STATE_STOPPED);
if (evt == null) {
evt = evt2;
} else if (evt2 != null) {
PropertyChangeEvent evt3 = evt;
while(evt3.getPropagationId() != null) {
evt3 = (PropertyChangeEvent) evt3.getPropagationId();
}
evt3.setPropagationId(evt2);
}
} finally {
accessLock.readLock().unlock();
if (evt != null) {
do {
firePropertyChange(evt);
evt = (PropertyChangeEvent) evt.getPropagationId();
} while (evt != null);
}
}
}
/**
* Can be called if the current thread is resumed after stop.
*/
public void setRunningState() {
setState(STATE_RUNNING);
}
/**
* Performs stop action and disable a next call to resume()
*/
public void setStoppedStateNoContinue (ThreadReference thread) {
PropertyChangeEvent evt;
accessLock.readLock().lock();
try {
// this method can be called in stopped state to switch
// the current thread only
evt = setStateNoFire(STATE_RUNNING);
JPDAThread t = getThread (thread);
checkJSR45Languages (t);
PropertyChangeEvent evt2 = setCurrentThreadNoFire(t);
if (evt == null) {
evt = evt2;
} else if (evt2 != null) {
evt.setPropagationId(evt2);
}
evt2 = setStateNoFire(STATE_STOPPED);
if (evt == null) {
evt = evt2;
} else if (evt2 != null) {
PropertyChangeEvent evt3 = evt;
while(evt3.getPropagationId() != null) {
evt3 = (PropertyChangeEvent) evt3.getPropagationId();
}
evt3.setPropagationId(evt2);
}
doContinue = false;
} finally {
accessLock.readLock().unlock();
}
if (evt != null) {
do {
firePropertyChange(evt);
evt = (PropertyChangeEvent) evt.getPropagationId();
} while (evt != null);
}
}
private boolean finishing;
/**
* Used by KillActionProvider.
*/
public void finish () {
try {
synchronized (this) {
if (finishing) {
// Can easily be called twice - from the operator termination
return ;
}
finishing = true;
}
logger.fine("StartActionProvider.finish ()");
if (getState () == STATE_DISCONNECTED) {
return;
}
AbstractDICookie di = lookupProvider.lookupFirst(null, AbstractDICookie.class);
Operator o = getOperator();
if (o != null) {
o.stop();
}
stopAttachListening();
try {
boolean startedUp;
do {
startedUp = waitRunning(500); // First wait till the debugger comes up
if (!startedUp) {
stopAttachListening();
continue;
}
} while (false);
} catch (DebuggerStartException dsex) {
// We do not want to start it anyway when we're finishing - do not bother
}
VirtualMachine vm;
synchronized (virtualMachineLock) {
vm = virtualMachine;
virtualMachine = null;
}
if (ptd != null) {
ptd.finish();
}
setState (STATE_DISCONNECTED);
if (jsr45EngineProviders != null) {
for (Iterator<JSR45DebuggerEngineProvider> i = jsr45EngineProviders.iterator(); i.hasNext();) {
JSR45DebuggerEngineProvider provider = i.next();
DebuggerEngine.Destructor d = provider.getDesctuctor();
if (d != null) {
d.killEngine();
}
}
jsr45EngineProviders = null;
}
DebuggerEngine.Destructor d = javaEngineProvider.getDestructor();
if (d != null) {
d.killEngine ();
}
if (vm != null) {
try {
if (di instanceof AttachingDICookie) {
JPDAThreadImpl t;
synchronized (currentThreadAndFrameLock) {
t = currentThread;
}
if (t != null && t.isMethodInvoking()) {
try {
t.waitUntilMethodInvokeDone(5000); // Wait 5 seconds at most
} catch (InterruptedException ex) {}
}
logger.fine(" StartActionProvider.finish() VM dispose");
VirtualMachineWrapper.dispose (vm);
} else {
logger.fine(" StartActionProvider.finish() VM exit");
VirtualMachineWrapper.exit (vm, 0);
}
} catch (InternalExceptionWrapper e) {
logger.log(Level.FINE, " StartActionProvider.finish() VM exception {0}", e);
} catch (VMDisconnectedExceptionWrapper e) {
logger.log(Level.FINE, " StartActionProvider.finish() VM exception {0}", e);
// debugee VM is already disconnected (it finished normally)
}
}
logger.fine (" StartActionProvider.finish() end.");
//Notify LOCK2 so that no one is waiting forever
synchronized (LOCK2) {
starting = false;
LOCK2.notifyAll ();
}
EditorContextBridge.getContext().disposeTimeStamp(this);
} finally {
finishing = false; // for safety reasons
}
}
private synchronized void stopAttachListening() {
if (attachingCookie != null) {
if (attachingCookie instanceof ListeningDICookie) {
ListeningDICookie listeningCookie = (ListeningDICookie) attachingCookie;
try {
listeningCookie.getListeningConnector().stopListening(listeningCookie.getArgs());
} catch (java.io.IOException ioex) {
} catch (com.sun.jdi.connect.IllegalConnectorArgumentsException icaex) {
} catch (IllegalArgumentException iaex) {
}
}
}
}
public boolean isVMSuspended() {
return vmSuspended;
}
/**
* Suspends the target virtual machine (if any).
* Used by PauseActionProvider.
*
* @see com.sun.jdi.ThreadReference#suspend
*/
public void suspend () {
VirtualMachine vm;
synchronized (virtualMachineLock) {
vm = virtualMachine;
}
accessLock.writeLock().lock();
try {
if (vm != null) {
logger.fine("VM suspend");
try {
VirtualMachineWrapper.suspend (vm);
vmSuspended = true;
// Check the suspended count
List<ThreadReference> threads = VirtualMachineWrapper.allThreads(vm);
for (ThreadReference t : threads) {
try {
while (ThreadReferenceWrapper.suspendCount(t) > 1) {
ThreadReferenceWrapper.resume(t);
}
if (ThreadReferenceWrapper.name(t).contains(ThreadsCache.THREAD_NAME_FILTER_PATTERN)) {
// Do not suspend filtered threads, they might not be resumed.
ThreadReferenceWrapper.resume(t);
}
} catch (IllegalThreadStateExceptionWrapper e) {
} catch (ObjectCollectedExceptionWrapper e) {
} catch (InternalExceptionWrapper e) {
}
}
} catch (VMDisconnectedExceptionWrapper e) {
return ;
} catch (InternalExceptionWrapper e) {
return ;
}
}
setState (STATE_STOPPED);
} finally {
accessLock.writeLock().unlock();
}
notifySuspendAll(true, true);
}
public List<PropertyChangeEvent> notifySuspendAll(boolean doFire, boolean explicitelyPaused) {
return notifySuspendAll(doFire, explicitelyPaused, null);
}
public List<PropertyChangeEvent> notifySuspendAll(boolean doFire, boolean explicitelyPaused,
Set<ThreadReference> ignoredThreads) {
Collection threads = threadsTranslation.getTranslated();
List<PropertyChangeEvent> events = new ArrayList<PropertyChangeEvent>(threads.size());
for (Iterator it = threads.iterator(); it.hasNext(); ) {
Object threadOrGroup = it.next();
if (threadOrGroup instanceof JPDAThreadImpl &&
(ignoredThreads == null || !ignoredThreads.contains(((JPDAThreadImpl) threadOrGroup).getThreadReference()))) {
int status = ((JPDAThreadImpl) threadOrGroup).getState();
boolean invalid = (status == JPDAThread.STATE_NOT_STARTED ||
status == JPDAThread.STATE_UNKNOWN ||
status == JPDAThread.STATE_ZOMBIE);
if (!invalid) {
try {
PropertyChangeEvent event = ((JPDAThreadImpl) threadOrGroup).notifySuspended(doFire, explicitelyPaused);
if (event != null) {
events.add(event);
}
} catch (ObjectCollectedException ocex) {
invalid = true;
}
} else if (status == JPDAThread.STATE_UNKNOWN || status == JPDAThread.STATE_ZOMBIE) {
threadsTranslation.remove(((JPDAThreadImpl) threadOrGroup).getThreadReference());
}
}
}
return events;
}
public void notifySuspendAllNoFire() {
notifySuspendAllNoFire(null, null);
}
public void notifySuspendAllNoFire(Set<ThreadReference> ignoredThreads, ThreadReference eventThread) {
Collection threads = threadsTranslation.getTranslated();
for (Iterator it = threads.iterator(); it.hasNext(); ) {
Object threadOrGroup = it.next();
if (threadOrGroup instanceof JPDAThreadImpl) {
JPDAThreadImpl thread = (JPDAThreadImpl) threadOrGroup;
ThreadReference tr = thread.getThreadReference();
if (ignoredThreads == null || !ignoredThreads.contains(tr)) {
int status = thread.getState();
boolean invalid = (status == JPDAThread.STATE_NOT_STARTED ||
status == JPDAThread.STATE_UNKNOWN ||
status == JPDAThread.STATE_ZOMBIE);
if (!invalid) {
try {
thread.notifySuspendedNoFire(tr == eventThread, false);
} catch (ObjectCollectedException ocex) {
invalid = true;
}
} else if (status == JPDAThread.STATE_UNKNOWN || status == JPDAThread.STATE_ZOMBIE) {
threadsTranslation.remove(tr);
}
}
}
}
}
/**
* Used by ContinueActionProvider & StepActionProvider.
*/
public void resume () {
try {
getOperator().waitForParallelEventsToProcess();
} catch (InterruptedException iex) {
return;
}
accessLock.readLock().lock();
try {
if (!doContinue) {
doContinue = true;
// Continue the next time and do nothing now.
return ;
}
} finally {
accessLock.readLock().unlock();
}
PropertyChangeEvent stateChangeEvent;
//notifyToBeResumedAll();
VirtualMachine vm;
synchronized (virtualMachineLock) {
vm = virtualMachine;
}
if (vm != null) {
logger.fine("VM resume");
List<JPDAThread> allThreads = getAllThreads(true);
accessLock.writeLock().lock();
logger.finer("Debugger WRITE lock taken.");
stateChangeEvent = setStateNoFire(STATE_RUNNING);
// We must resume only threads which are regularly suspended.
// Otherwise we may unexpectedly resume threads which just hit an event!
// However, this can not be done right without an atomic resume of a set of threads.
// Since this functionality is not available in the backend, we will
// call VirtualMachine.resume() if all threads are suspended
// (so that the model of of suspend all/resume all works correctly)
// and call ThreadReference.resume() on individual suspended threads
// (so that suspend of event thread together with continue works correctly).
// Other cases (some threads suspended and some running with events suspending all threads)
// will NOT WORK CORRECTLY. Because while resuming threads one at a time,
// if the first one hits an event which suspends all, other resumes will resume the
// suspended threads.
// But this looks like a reasonable trade-off considering the available functionality.
List<JPDAThreadImpl> threadsToResume = null;
try {
// Deal only with threads that are living
{
boolean modifiableAllThreads = false;
int n = allThreads.size();
for (int i = 0; i < n; i++) {
JPDAThread t = allThreads.get(i);
int status = t.getState();
if (status == JPDAThread.STATE_ZOMBIE || status == JPDAThread.STATE_UNKNOWN ||
t.getName().contains(ThreadsCache.THREAD_NAME_FILTER_PATTERN) && !t.isSuspended()) {
if (!modifiableAllThreads) {
allThreads = new ArrayList<JPDAThread>(allThreads);
modifiableAllThreads = true;
}
allThreads.remove(i);
n--;
i--;
}
}
}
threadsToResume = new ArrayList<JPDAThreadImpl>();
for (JPDAThread t : allThreads) {
if (t.isSuspended() || ((JPDAThreadImpl) t).getThreadReference().suspendCount() > 0) {
// There can be threads that have running state, are not suspended, but their suspend count is > 0. These need to be resumed I guess...
threadsToResume.add((JPDAThreadImpl) t);
}
}
for (int i = 0; i < threadsToResume.size(); i++) {
JPDAThreadImpl t = threadsToResume.get(i);
boolean can = t.cleanBeforeResume();
if (!can) {
threadsToResume.remove(i);
i--;
}
}
if (vmSuspended || allThreads.size() == threadsToResume.size()) {
// Resuming all
for (JPDAThreadImpl t : threadsToResume) {
t.setAsResumed(false);
t.reduceThreadSuspendCount();
}
// We also need to check for newly-born threads,
// that we do not know about yet,
// and that might be suspended multiple-times.
reduceThreadSuspendCountOfAllBut(vm, threadsToResume);
//logger.severe("Before VM.resume():");
//Operator.dumpThreadsStatus(vm, Level.SEVERE);
VirtualMachineWrapper.resume(vm);
vmSuspended = false;
logger.finer("All VM threads resumed.");
//logger.severe("After VM.resume():");
//Operator.dumpThreadsStatus(vm, Level.SEVERE);
} else {
logger.finer("Resuming selected suspended threads.");
for (JPDAThreadImpl t : threadsToResume) {
t.setAsResumed(false);
t.resumeAfterClean();
}
}
} catch (VMDisconnectedExceptionWrapper e) {
} catch (InternalExceptionWrapper e) {
} finally {
accessLock.writeLock().unlock();
logger.finer("Debugger WRITE lock released.");
if (stateChangeEvent != null) {
firePropertyChange(stateChangeEvent);
}
if (threadsToResume != null) {
for (JPDAThreadImpl t : threadsToResume) {
try {
t.fireAfterResume();
} catch (ThreadDeath td) {
throw td;
} catch (Throwable th) {
Exceptions.printStackTrace(th);
}
}
}
}
}
}
private void reduceThreadSuspendCountOfAllBut(VirtualMachine vm, List<JPDAThreadImpl> threadsToIgnore) {
List<ThreadReference> allThreads = VirtualMachineWrapper.allThreads0(vm);
if (allThreads.size() == threadsToIgnore.size()) {
return; // Everything is under control.
}
Set<Long> ignoredIDs = new HashSet<Long>(threadsToIgnore.size());
for (JPDAThreadImpl t : threadsToIgnore) {
ignoredIDs.add(t.getThreadReference().uniqueID());
}
for (ThreadReference t : allThreads) {
if (ignoredIDs.contains(t.uniqueID())) {
continue;
}
try {
int count = ThreadReferenceWrapper.suspendCount (t);
//logger.severe("Reducing suspend count of "+JPDAThreadImpl.getThreadStateLog(t));
while (count > 1) {
ThreadReferenceWrapper.resume (t); count--;
}
} catch (IllegalThreadStateExceptionWrapper ex) {
// Thrown when thread has exited
} catch (ObjectCollectedExceptionWrapper ex) {
} catch (InternalExceptionWrapper iex) {
} catch (VMDisconnectedExceptionWrapper vmdex) {
}
}
}
/** DO NOT CALL FROM ANYWHERE BUT JPDAThreadImpl.resume(). */
public boolean currentThreadToBeResumed() {
accessLock.readLock().lock();
try {
if (!doContinue) {
doContinue = true;
// Continue the next time and do nothing now.
return false;
}
} finally {
accessLock.readLock().unlock();
}
setState (STATE_RUNNING);
return true;
}
public void resumeCurrentThread() {
accessLock.readLock().lock();
try {
if (!doContinue) {
doContinue = true;
// Continue the next time and do nothing now.
return ;
}
} finally {
accessLock.readLock().unlock();
}
setState (STATE_RUNNING);
currentThread.resume();
}
public void notifyToBeResumedAll() {
Collection threads = threadsTranslation.getTranslated();
for (Iterator it = threads.iterator(); it.hasNext(); ) {
Object threadOrGroup = it.next();
if (threadOrGroup instanceof JPDAThreadImpl) {
int status = ((JPDAThreadImpl) threadOrGroup).getState();
boolean invalid = (status == JPDAThread.STATE_NOT_STARTED ||
status == JPDAThread.STATE_UNKNOWN ||
status == JPDAThread.STATE_ZOMBIE);
if (!invalid) {
((JPDAThreadImpl) threadOrGroup).notifyToBeResumed();
} else if (status == JPDAThread.STATE_UNKNOWN || status == JPDAThread.STATE_ZOMBIE) {
threadsTranslation.remove(((JPDAThreadImpl) threadOrGroup).getThreadReference());
}
}
}
}
public void notifyToBeResumedAllNoFire() {
notifyToBeResumedAllNoFire(null);
}
public void notifyToBeResumedAllNoFire(Set<ThreadReference> ignoredThreads) {
Collection threads = threadsTranslation.getTranslated();
for (Iterator it = threads.iterator(); it.hasNext(); ) {
Object threadOrGroup = it.next();
if (threadOrGroup instanceof JPDAThreadImpl &&
(ignoredThreads == null || !ignoredThreads.contains(threadOrGroup))) {
int status = ((JPDAThreadImpl) threadOrGroup).getState();
boolean invalid = (status == JPDAThread.STATE_NOT_STARTED ||
status == JPDAThread.STATE_UNKNOWN ||
status == JPDAThread.STATE_ZOMBIE);
if (!invalid) {
((JPDAThreadImpl) threadOrGroup).notifyToBeResumedNoFire();
} else if (status == JPDAThread.STATE_UNKNOWN || status == JPDAThread.STATE_ZOMBIE) {
threadsTranslation.remove(((JPDAThreadImpl) threadOrGroup).getThreadReference());
}
}
}
}
public void setCurrentSuspendedNoFireThread(JPDAThreadImpl thread) {
this.currentSuspendedNoFireThread = thread;
}
private Set<JPDAThreadGroup> interestedThreadGroups = new WeakSet<JPDAThreadGroup>();
public void interestedInThreadGroup(JPDAThreadGroup tg) {
interestedThreadGroups.add(tg);
}
public boolean isInterestedInThreadGroups() {
return !interestedThreadGroups.isEmpty();
}
public ThreadsCache getThreadsCache() {
synchronized (threadsCollectorLock) {
if (threadsCache == null) {
threadsCache = new ThreadsCache(this);
threadsCache.addPropertyChangeListener(new PropertyChangeListener() {
// Re-fire the changes
@Override
public void propertyChange(PropertyChangeEvent evt) {
String propertyName = evt.getPropertyName();
if (ThreadsCache.PROP_THREAD_STARTED.equals(propertyName)) {
firePropertyChange(PROP_THREAD_STARTED, null, getThread((ThreadReference) evt.getNewValue()));
}
if (ThreadsCache.PROP_THREAD_DIED.equals(propertyName)) {
firePropertyChange(PROP_THREAD_DIED, getThread((ThreadReference) evt.getOldValue()), null);
}
if (ThreadsCache.PROP_GROUP_ADDED.equals(propertyName)) {
firePropertyChange(PROP_THREAD_GROUP_ADDED, null, getThreadGroup((ThreadGroupReference) evt.getNewValue()));
}
}
});
}
return threadsCache;
}
}
List<JPDAThread> getAllThreads() {
return getAllThreads(false);
}
List<JPDAThread> getAllThreads(boolean findNewBornThreads) {
ThreadsCache tc = getThreadsCache();
if (tc == null) {
return Collections.emptyList();
}
List<ThreadReference> threadList = tc.getAllThreads();
if (findNewBornThreads) {
Set<Long> threadIDs = new HashSet<Long>(threadList.size());
for (ThreadReference t : threadList) {
threadIDs.add(t.uniqueID());
}
List<ThreadReference> allThreads = VirtualMachineWrapper.allThreads0(virtualMachine);
boolean added = false;
for (ThreadReference t : allThreads) {
if (!threadIDs.contains(t.uniqueID())) {
if (!added) {
threadList = new ArrayList<ThreadReference>(threadList);
added = true;
}
threadList.add(t);
}
}
}
int n = threadList.size();
List<JPDAThread> threads = new ArrayList<JPDAThread>(n);
for (int i = 0; i < n; i++) {
threads.add(getThread(threadList.get(i)));
}
return Collections.unmodifiableList(threads);
}
public JPDAThreadGroup[] getTopLevelThreadGroups() {
ThreadsCache tc = getThreadsCache();
if (tc == null) {
return new JPDAThreadGroup[0];
}
List<ThreadGroupReference> groupList = tc.getTopLevelThreadGroups();
JPDAThreadGroup[] groups = new JPDAThreadGroup[groupList.size()];
for (int i = 0; i < groups.length; i++) {
groups[i] = getThreadGroup((ThreadGroupReference) groupList.get(i));
}
return groups;
}
public JPDAThreadImpl getThread (ThreadReference tr) {
return (JPDAThreadImpl) threadsTranslation.translate (tr);
}
public JPDAThreadImpl getExistingThread (ThreadReference tr) {
return (JPDAThreadImpl) threadsTranslation.translateExisting(tr);
}
public JPDAThreadGroup getThreadGroup (ThreadGroupReference tgr) {
return (JPDAThreadGroup) threadsTranslation.translate (tgr);
}
public Variable getLocalVariable(JPDAThread thread, LocalVariable lv, Value v) {
return (Variable) localsTranslation.translateOnThread(thread, lv, v);
}
public JPDAClassType getClassType(ReferenceType cr) {
return (JPDAClassType) localsTranslation.translate (cr);
}
public Variable getVariable (Value value) {
if (value instanceof ClassObjectReference) {
return new ClassVariableImpl(this, (ClassObjectReference) value, null);
}
return createVariable(value);
}
private Variable createVariable(Value v) {
if (v instanceof ObjectReference || v == null) {
return new AbstractObjectVariable (
this,
(ObjectReference) v,
null
);
} else {
return new AbstractVariable (this, v, null);
}
}
@Override
public Variable createMirrorVar(Object obj, boolean isPrimitive) throws InvalidObjectException {
if (obj == null) {
return null;
}
Variable v;
try {
v = getVariable(VariableMirrorTranslator.createValueFromMirror(obj, !isPrimitive, this));
} catch (IllegalArgumentExceptionWrapper ex) {
InvalidObjectException ioex = new InvalidObjectException(ex.getLocalizedMessage());
ioex.initCause(ex);
throw ioex;
} catch (InternalExceptionWrapper ex) {
InvalidObjectException ioex = new InvalidObjectException(ex.getLocalizedMessage());
ioex.initCause(ex);
throw ioex;
} catch (VMDisconnectedExceptionWrapper ex) {
InvalidObjectException ioex = new InvalidObjectException(ex.getLocalizedMessage());
ioex.initCause(ex);
throw ioex;
} catch (ObjectCollectedExceptionWrapper ex) {
InvalidObjectException ioex = new InvalidObjectException(ex.getLocalizedMessage());
ioex.initCause(ex);
throw ioex;
} catch (InvalidTypeException ex) {
InvalidObjectException ioex = new InvalidObjectException(ex.getLocalizedMessage());
ioex.initCause(ex);
throw ioex;
} catch (ClassNotLoadedException ex) {
InvalidObjectException ioex = new InvalidObjectException(ex.getLocalizedMessage());
ioex.initCause(ex);
throw ioex;
} catch (ClassNotPreparedExceptionWrapper ex) {
InvalidObjectException ioex = new InvalidObjectException(ex.getLocalizedMessage());
ioex.initCause(ex);
throw ioex;
}
if (v == null) {
throw new InvalidObjectException("No target value from "+obj);
}
return v;
}
public void markObject(ObjectVariable var, String label) {
synchronized (markedObjects) {
long uid = var.getUniqueID();
String oldLabel = markedObjects.remove(uid);
if (oldLabel != null) {
markedObjectLabels.remove(oldLabel);
}
if (label != null) {
markedObjects.put(uid, label);
ObjectVariable markedVar = markedObjectLabels.get(label);
if (markedVar != null) {
markedObjects.remove(markedVar.getUniqueID());
}
markedObjectLabels.put(label, var);
}
}
}
/*public Map<ObjectVariable, String> getMarkedObjects() {
Map<ObjectVariable, String> map;
synchronized (markedObjects) {
map = new LinkedHashMap<ObjectVariable, String>(markedObjects);
}
return map;
}*/
public String getLabel(ObjectVariable var) {
if (var instanceof Refreshable) {
if (!((Refreshable) var).isCurrent()) {
return null;
}
}
synchronized (markedObjects) {
return markedObjects.get(var.getUniqueID());
}
}
public ObjectVariable getLabeledVariable(String label) {
synchronized (markedObjects) {
return markedObjectLabels.get(label);
}
}
public Map<String, ObjectVariable> getAllLabels() {
synchronized (markedObjects) {
return new HashMap<String, ObjectVariable>(markedObjectLabels);
}
}
public ExpressionPool getExpressionPool() {
return expressionPool;
}
synchronized void setSingleThreadStepResumeDecision(Boolean decision) {
singleThreadStepResumeDecision = decision;
}
synchronized Boolean getSingleThreadStepResumeDecision() {
return singleThreadStepResumeDecision;
}
public synchronized void setStepInterruptByBptResumeDecision(Boolean decision) {
stepInterruptByBptResumeDecision = decision;
}
public synchronized Boolean getStepInterruptByBptResumeDecision() {
return stepInterruptByBptResumeDecision;
}
// private helper methods ..................................................
private static final java.util.regex.Pattern jvmVersionPattern =
java.util.regex.Pattern.compile ("(\\d+)\\.(\\d+)\\.(\\d+)(_\\d+)?(-\\w+)?");
private static java.lang.reflect.Method tcGenericSignatureMethod;
private static java.lang.reflect.Method lvGenericSignatureMethod;
private void initGenericsSupport () {
tcGenericSignatureMethod = null;
if (Bootstrap.virtualMachineManager ().minorInterfaceVersion () >= 5) {
VirtualMachine vm;
synchronized (virtualMachineLock) {
vm = virtualMachine;
}
if (vm == null) {
return ;
}
try {
java.util.regex.Matcher m = jvmVersionPattern.matcher(VirtualMachineWrapper.version(vm));
if (m.matches ()) {
int minor = Integer.parseInt (m.group (2));
if (minor >= 5) {
try {
tcGenericSignatureMethod = TypeComponent.class.
getMethod ("genericSignature", new Class [0]);
lvGenericSignatureMethod = LocalVariable.class.
getMethod ("genericSignature", new Class [0]);
} catch (NoSuchMethodException e) {
// the method is not available, ignore generics
}
}
}
} catch (InternalExceptionWrapper e) {
} catch (VMDisconnectedExceptionWrapper e) {
}
}
}
private PropertyChangeEvent setStateNoFire (int state) {
int o;
synchronized (stateLock) {
if (state == this.state) {
return null;
}
o = this.state;
this.state = state;
}
//PENDING HACK see issue 46287
System.setProperty(
"org.openide.awt.SwingBrowserImpl.do-not-block-awt",
String.valueOf (state != STATE_DISCONNECTED)
);
return new PropertyChangeEvent(this, PROP_STATE, new Integer (o), new Integer (state));
}
private void setState (int state) {
if (state == STATE_RUNNING) {
CallStackFrame old;
synchronized (currentThreadAndFrameLock) {
old = currentCallStackFrame;
currentCallStackFrame = null;
}
firePropertyChange (
PROP_CURRENT_CALL_STACK_FRAME,
old,
null
);
}
PropertyChangeEvent evt = setStateNoFire(state);
if (evt != null) {
firePropertyChange(evt);
}
}
/**
* Fires property change.
*/
private void firePropertyChange (String name, Object o, Object n) {
pcs.firePropertyChange (name, o, n);
//System.err.println("ALL Change listeners count = "+pcs.getPropertyChangeListeners().length);
}
/**
* Fires property change.
*/
private void firePropertyChange (PropertyChangeEvent evt) {
pcs.firePropertyChange (evt);
//System.err.println("ALL Change listeners count = "+pcs.getPropertyChangeListeners().length);
}
private SourcePath engineContext;
public synchronized SourcePath getEngineContext () {
if (engineContext == null) {
engineContext = lookupProvider.lookupFirst(null, SourcePath.class);
}
return engineContext;
}
private ThreadReference getEvaluationThread () {
synchronized (currentThreadAndFrameLock) {
if (currentThread != null) {
return currentThread.getThreadReference ();
}
}
VirtualMachine vm;
synchronized (virtualMachineLock) {
vm = virtualMachine;
}
if (vm == null) {
return null;
}
List l;
try {
l = VirtualMachineWrapper.allThreads(vm);
} catch (InternalExceptionWrapper ex) {
return null;
} catch (VMDisconnectedExceptionWrapper ex) {
return null;
}
if (l.size () < 1) {
return null;
}
int i, k = l.size ();
ThreadReference thread = null;
for (i = 0; i < k; i++) {
ThreadReference t = (ThreadReference) l.get (i);
try {
if (ThreadReferenceWrapper.isSuspended (t)) {
thread = t;
if (ThreadReferenceWrapper.name (t).equals ("Finalizer")) {
return t;
}
}
} catch (InternalExceptionWrapper ex) {
} catch (IllegalThreadStateExceptionWrapper ex) {
} catch (ObjectCollectedExceptionWrapper ex) {
} catch (VMDisconnectedExceptionWrapper ex) {
}
}
return thread;
}
private PropertyChangeEvent updateCurrentCallStackFrameNoFire (JPDAThread thread) {
CallStackFrame oldSF;
CallStackFrame newSF;
if ((thread == null) || (thread.getStackDepth () < 1)) {
newSF = null;
} else {
try {
CallStackFrame[] csfs = thread.getCallStack(0, 1);
if (csfs.length > 0) {
newSF = csfs[0];
} else {
newSF = null;
}
} catch (AbsentInformationException e) {
newSF = null;
}
}
oldSF = setCurrentCallStackFrameNoFire(newSF);
if (oldSF == newSF) {
return null;
} else {
return new PropertyChangeEvent(this, PROP_CURRENT_CALL_STACK_FRAME,
oldSF, newSF);
}
}
private CallStackFrame preferredTopFrame;
public void setPreferredTopFrame(CallStackFrame preferredTopFrame) {
this.preferredTopFrame = preferredTopFrame;
}
private CallStackFrame getTopFrame(JPDAThread thread) {
CallStackFrame callStackFrame;
if (preferredTopFrame != null) {
callStackFrame = preferredTopFrame;
preferredTopFrame = null;
return callStackFrame;
}
if ((thread == null) || (thread.getStackDepth () < 1)) {
callStackFrame = null;
} else {
try {
CallStackFrame[] csfs = thread.getCallStack(0, 1);
if (csfs.length > 0) {
callStackFrame = csfs[0];
} else {
callStackFrame = null;
}
} catch (AbsentInformationException e) {
callStackFrame = null;
}
}
return callStackFrame;
}
/**
* @param thread The thread to take the top frame from
* @return A PropertyChangeEvent or <code>null</code>.
*/
private PropertyChangeEvent updateCurrentCallStackFrameNoFire(CallStackFrame callStackFrame) {
CallStackFrame old;
old = setCurrentCallStackFrameNoFire(callStackFrame);
if (old == callStackFrame) {
return null;
} else {
return new PropertyChangeEvent(this, PROP_CURRENT_CALL_STACK_FRAME,
old, callStackFrame);
}
}
private void checkJSR45Languages (JPDAThread t) {
if (t.getStackDepth () > 0) {
try {
CallStackFrame[] frames = t.getCallStack (0, 1);
if (frames.length < 1) {
return ; // Internal error or disconnected
}
checkJSR45Languages(frames[0]);
} catch (AbsentInformationException e) {
}
}
}
private void checkJSR45Languages(CallStackFrame f) {
List<String> l = f.getAvailableStrata ();
String stratum = f.getDefaultStratum ();
//String sourceDebugExtension;
//sourceDebugExtension = (String) f.getClass().getMethod("getSourceDebugExtension").invoke(f);
/* This was moved to CallStackFrameImpl.
if (l.size() == 1 && "Java".equals(l.get(0))) { // NOI18N
// Hack for non-Java languages that do not define stratum:
String sourceName = f.getSourceName(null);
int ext = sourceName.lastIndexOf('.');
if (ext > 0) {
String extension = sourceName.substring(++ext);
extension = extension.toUpperCase();
if (!"JAVA".equals(extension)) { // NOI18N
l = Collections.singletonList(extension);
stratum = extension;
}
} else if ("<eval>".equals(sourceName)) {
// Check Nashorn:
if ("jdk/nashorn/internal/scripts/<eval>".equals(f.getSourcePath(null))) {
l = Collections.singletonList("JS");
stratum = "JS";
}
}
}*/
int i, k = l.size ();
for (i = 0; i < k; i++) {
if (!languages.contains (l.get (i))) {
String language = l.get (i);
DebuggerManager.getDebuggerManager ().startDebugging (
createJSR45DI (language)
);
languages.add (language);
}
} // for
if ( (stratum != null) &&
(!stratum.equals (lastStratumn))
) {
javaEngineProvider.getSession ().setCurrentLanguage (stratum);
}
lastStratumn = stratum;
}
private Set<JSR45DebuggerEngineProvider> jsr45EngineProviders;
private DebuggerInfo createJSR45DI (final String language) {
if (jsr45EngineProviders == null) {
jsr45EngineProviders = new HashSet<JSR45DebuggerEngineProvider>(1);
}
JSR45DebuggerEngineProvider provider = new JSR45DebuggerEngineProvider(language, getRequestProcessor());
jsr45EngineProviders.add(provider);
return DebuggerInfo.create (
"netbeans-jpda-JSR45DICookie-" + language,
new Object[] {
new DelegatingSessionProvider () {
@Override
public Session getSession (
DebuggerInfo debuggerInfo
) {
return javaEngineProvider.getSession ();
}
},
provider
}
);
}
@Override
public JPDAStep createJPDAStep(int size, int depth) {
Session session = lookupProvider.lookupFirst(null, Session.class);
return new JPDAStepImpl(this, session, size, depth);
}
/*public synchronized Heap getHeap() {
if (virtualMachine != null && canGetInstanceInfo(virtualMachine)) {
return new HeapImpl(virtualMachine);
} else {
return null;
}
}*/
@Override
public List<JPDAClassType> getAllClasses() {
//assert !java.awt.EventQueue.isDispatchThread() : "All classes retrieving in AWT Event Queue!";
List<ReferenceType> classes;
synchronized (virtualMachineLock) {
if (virtualMachine == null) {
classes = Collections.emptyList();
} else {
classes = VirtualMachineWrapper.allClasses0(virtualMachine);
}
}
return new ClassTypeList(this, classes);
}
@Override
public List<JPDAClassType> getClassesByName(String name) {
List<ReferenceType> classes;
synchronized (virtualMachineLock) {
if (virtualMachine == null) {
classes = Collections.emptyList();
} else {
classes = VirtualMachineWrapper.classesByName0(virtualMachine, name);
}
}
return new ClassTypeList(this, classes);
}
@Override
public long[] getInstanceCounts(List<JPDAClassType> classTypes) throws UnsupportedOperationException {
//assert !java.awt.EventQueue.isDispatchThread() : "Instance counts retrieving in AWT Event Queue!";
VirtualMachine vm;
synchronized (virtualMachineLock) {
vm = virtualMachine;
}
if (vm == null) {
return new long[classTypes.size()];
}
List<ReferenceType> types;
if (classTypes instanceof ClassTypeList) {
ClassTypeList cl = (ClassTypeList) classTypes;
types = cl.getTypes();
} else {
types = new ArrayList<ReferenceType>(classTypes.size());
for (JPDAClassType clazz : classTypes) {
types.add(((JPDAClassTypeImpl) clazz).getType());
}
}
try {
return VirtualMachineWrapper.instanceCounts(vm, types);
} catch (InternalExceptionWrapper e) {
return new long[classTypes.size()];
} catch (VMDisconnectedExceptionWrapper e) {
return new long[classTypes.size()];
}
}
@Override
public boolean canGetInstanceInfo() {
synchronized (virtualMachineLock) {
return virtualMachine != null && virtualMachine.canGetInstanceInfo();
}
}
@Override
public ThreadsCollectorImpl getThreadsCollector() {
synchronized (threadsCollectorLock) {
if (threadsCollector == null) {
threadsCollector = new ThreadsCollectorImpl(this);
}
return threadsCollector;
}
}
DeadlockDetector getDeadlockDetector() {
synchronized (threadsCollectorLock) {
if (deadlockDetector == null) {
deadlockDetector = new DeadlockDetectorImpl(this);
}
return deadlockDetector;
}
}
private static final String COMPUTING_INTERFACES = "computing"; // NOI18N
public List<JPDAClassType> getAllInterfaces(ClassType ct) {
try {
List allInterfaces;
boolean toCompute = false;
synchronized (allInterfacesMap) {
allInterfaces = allInterfacesMap.get(ct);
if (allInterfaces == null) {
allInterfaces = new ArrayList();
allInterfaces.add(COMPUTING_INTERFACES);
allInterfacesMap.put(ct, allInterfaces);
toCompute = true;
}
}
if (toCompute) {
List<InterfaceType> interfaces = null;
try {
//assert !javax.swing.SwingUtilities.isEventDispatchThread();
interfaces = ClassTypeWrapper.allInterfaces0(ct);
} finally {
if (interfaces == null) {
synchronized (allInterfacesMap) {
allInterfacesMap.remove(ct);
}
}
synchronized (allInterfaces) {
allInterfaces.clear();
if (interfaces != null) {
for (InterfaceType it : interfaces) {
allInterfaces.add(getClassType(it));
}
}
allInterfaces.notifyAll();
}
}
} else {
synchronized (allInterfaces) {
if (allInterfaces.contains(COMPUTING_INTERFACES)) {
try {
allInterfaces.wait();
} catch (InterruptedException ex) {
return null;
}
}
}
if (allInterfaces.contains(COMPUTING_INTERFACES)) {
// some race-condition? (http://netbeans.org/bugzilla/show_bug.cgi?id=219823)
assert !allInterfaces.contains(COMPUTING_INTERFACES);
return null;
}
}
return Collections.unmodifiableList(allInterfaces);
} catch (ClassNotPreparedExceptionWrapper cnpex) {
return null;
}
}
public boolean hasAllInterfaces(ClassType ct) {
List allInterfaces;
synchronized (allInterfacesMap) {
allInterfaces = allInterfacesMap.get(ct);
}
if (allInterfaces == null) {
return false;
} else {
synchronized (allInterfaces) {
if (allInterfaces.contains("computing")) { // NOI18N
return false;
}
}
}
return true;
}
public Variable getFormattedValue(ObjectVariable ov) {
return getFormattedValue(ov, new FormattersLoopControl());
}
private Variable getFormattedValue(ObjectVariable ov, FormattersLoopControl formattersLoopControl) {
JPDAClassType ct = ov.getClassType();
if (ct == null) {
return ov;
}
VariablesFormatter f = Formatters.getFormatterForType(ct, formattersLoopControl.getFormatters());
if (f != null && formattersLoopControl.canUse(f, ct.getName(), null)) {
String code = f.getValueFormatCode();
if (code != null && code.length() > 0) {
try {
Variable ret = ((AbstractObjectVariable) ov).evaluate(code);
if (ret == null) {
return null;
}
if (ret instanceof ObjectVariable) {
return getFormattedValue((ObjectVariable) ret, formattersLoopControl);
} else {
return ret;
}
} catch (InvalidExpressionException iex) {
//return VariablesTableModel.getMessage((InvalidExpressionException) t);
}
}
}
return ov;
}
public void actionMessageCallback(Object action, String message) {
List<? extends ActionMessageCallback> amcList = lookupProvider.lookup(null, ActionMessageCallback.class);
for (ActionMessageCallback amc : amcList) {
amc.messageCallback(action, message);
}
}
public void actionErrorMessageCallback(Object action, String message) {
List<? extends ActionErrorMessageCallback> amcList = lookupProvider.lookup(null, ActionErrorMessageCallback.class);
for (ActionErrorMessageCallback amc : amcList) {
amc.errorMessageCallback(action, message);
}
}
public void actionStatusDisplayCallback(Object action, String status) {
List<? extends ActionStatusDisplayCallback> asdcList = lookupProvider.lookup(null, ActionStatusDisplayCallback.class);
for (ActionStatusDisplayCallback asdc : asdcList) {
asdc.statusDisplayCallback(action, status);
}
}
private static class DebuggerReentrantReadWriteLock extends ReentrantReadWriteLock {
private ReadLock readerLock;
private WriteLock writerLock;
public DebuggerReentrantReadWriteLock(boolean fair) {
super(fair);
readerLock = new DebuggerReadLock(this);
writerLock = new DebuggerWriteLock(this);
}
@Override
public ReadLock readLock() {
return readerLock;
}
@Override
public WriteLock writeLock() {
return writerLock;
}
private static class DebuggerReadLock extends ReentrantReadWriteLock.ReadLock {
protected DebuggerReadLock(ReentrantReadWriteLock lock) {
super(lock);
}
@Override
public void lock() {
assert !Mutex.EVENT.isReadAccess() : "Debugger lock taken in AWT Event Queue!";
super.lock();
}
}
private static class DebuggerWriteLock extends ReentrantReadWriteLock.WriteLock {
protected DebuggerWriteLock(ReentrantReadWriteLock lock) {
super(lock);
}
@Override
public void lock() {
assert !Mutex.EVENT.isReadAccess() : "Debugger lock taken in AWT Event Queue!";
super.lock();
}
}
}
private static class PeriodicThreadsDump implements Runnable {
private static final int INTERVAL = 5000;
private RequestProcessor rp = new RequestProcessor(PeriodicThreadsDump.class.getName());
private VirtualMachine vm;
private volatile boolean finish = false;
public PeriodicThreadsDump(VirtualMachine vm) {
this.vm = vm;
rp.post(this, INTERVAL);
}
public void finish() {
finish = true;
}
@Override
public void run() {
List<ThreadReference> allThreads = vm.allThreads();
System.err.println("All Threads:");
for (ThreadReference tr : allThreads) {
String name = tr.name();
boolean suspended = tr.isSuspended();
int suspendCount = tr.suspendCount();
int status = tr.status();
System.err.println(name+"\t SUSP = "+suspended+", COUNT = "+suspendCount+", STATUS = "+status);
}
System.err.println("");
if (!finish) {
rp.post(this, INTERVAL);
}
}
}
}