| /* |
| *(c) Copyright QNX Software Systems Ltd. 2002. |
| * All Rights Reserved. |
| * |
| */ |
| |
| package org.eclipse.cdt.debug.internal.core.model; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.cdt.debug.core.CDebugCorePlugin; |
| import org.eclipse.cdt.debug.core.cdi.CDIException; |
| import org.eclipse.cdt.debug.core.cdi.ICDILocation; |
| import org.eclipse.cdt.debug.core.cdi.event.ICDIEvent; |
| import org.eclipse.cdt.debug.core.cdi.event.ICDIEventListener; |
| import org.eclipse.cdt.debug.core.cdi.model.ICDIStackFrame; |
| import org.eclipse.cdt.debug.core.cdi.model.ICDIVariableObject; |
| import org.eclipse.cdt.debug.core.model.IRestart; |
| import org.eclipse.cdt.debug.core.model.IResumeWithoutSignal; |
| import org.eclipse.cdt.debug.core.model.IStackFrameInfo; |
| import org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocator; |
| import org.eclipse.core.runtime.IAdaptable; |
| import org.eclipse.debug.core.DebugException; |
| import org.eclipse.debug.core.model.IRegisterGroup; |
| import org.eclipse.debug.core.model.ISourceLocator; |
| import org.eclipse.debug.core.model.IStackFrame; |
| import org.eclipse.debug.core.model.IThread; |
| import org.eclipse.debug.core.model.IVariable; |
| |
| /** |
| * |
| * Proxy to a stack frame on the target. |
| * |
| * @since Aug 7, 2002 |
| */ |
| public class CStackFrame extends CDebugElement |
| implements IStackFrame, |
| IStackFrameInfo, |
| IRestart, |
| IResumeWithoutSignal, |
| ICDIEventListener |
| { |
| /** |
| * Underlying CDI stack frame. |
| */ |
| private ICDIStackFrame fCDIStackFrame; |
| |
| /** |
| * The last (previous) CDI stack frame. |
| */ |
| private ICDIStackFrame fLastCDIStackFrame; |
| |
| /** |
| * Containing thread. |
| */ |
| private CThread fThread; |
| |
| /** |
| * List of visible variable (includes arguments). |
| */ |
| private List fVariables; |
| |
| /** |
| * Whether the variables need refreshing |
| */ |
| private boolean fRefreshVariables = true; |
| |
| /** |
| * Constructor for CStackFrame. |
| * @param target |
| */ |
| public CStackFrame( CThread thread, ICDIStackFrame cdiFrame ) |
| { |
| super( (CDebugTarget)thread.getDebugTarget() ); |
| setCDIStackFrame( cdiFrame ); |
| setThread( thread ); |
| getCDISession().getEventManager().addEventListener( this ); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStackFrame#getThread() |
| */ |
| public IThread getThread() |
| { |
| return fThread; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStackFrame#getVariables() |
| */ |
| public IVariable[] getVariables() throws DebugException |
| { |
| List list = getVariables0(); |
| return (IVariable[])list.toArray( new IVariable[list.size()] ); |
| } |
| |
| protected synchronized List getVariables0() throws DebugException |
| { |
| if ( fVariables == null ) |
| { |
| List vars = getAllCDIVariableObjects(); |
| fVariables = new ArrayList( vars.size() ); |
| Iterator it = vars.iterator(); |
| while( it.hasNext() ) |
| { |
| fVariables.add( new CModificationVariable( this, (ICDIVariableObject)it.next() ) ); |
| } |
| } |
| else if ( refreshVariables() ) |
| { |
| updateVariables(); |
| } |
| setRefreshVariables( false ); |
| return fVariables; |
| } |
| |
| /** |
| * Incrementally updates this stack frame's variables. |
| * |
| */ |
| protected void updateVariables() throws DebugException |
| { |
| List locals = getAllCDIVariableObjects(); |
| int index = 0; |
| while( index < fVariables.size() ) |
| { |
| ICDIVariableObject varObject = findVariable( locals, (CVariable)fVariables.get( index ) ); |
| if ( varObject != null ) |
| { |
| locals.remove( varObject ); |
| index++; |
| } |
| else |
| { |
| // remove variable |
| fVariables.remove( index ); |
| } |
| } |
| |
| // add any new locals |
| Iterator newOnes = locals.iterator(); |
| while( newOnes.hasNext() ) |
| { |
| fVariables.add( new CModificationVariable( this, (ICDIVariableObject)newOnes.next() ) ); |
| } |
| } |
| |
| /** |
| * Sets the containing thread. |
| * |
| * @param thread the containing thread |
| */ |
| protected void setThread( CThread thread ) |
| { |
| fThread = thread; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStackFrame#hasVariables() |
| */ |
| public boolean hasVariables() throws DebugException |
| { |
| return getVariables0().size() > 0; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStackFrame#getLineNumber() |
| */ |
| public int getLineNumber() throws DebugException |
| { |
| if ( isSuspended() ) |
| { |
| ISourceLocator locator = ((CDebugTarget)getDebugTarget()).getSourceLocator(); |
| if ( locator != null && locator instanceof IAdaptable && |
| ((IAdaptable)locator).getAdapter( ICSourceLocator.class ) != null ) |
| return ((ICSourceLocator)((IAdaptable)locator).getAdapter( ICSourceLocator.class )).getLineNumber( this ); |
| if ( getCDIStackFrame() != null && getCDIStackFrame().getLocation() != null ) |
| return getCDIStackFrame().getLocation().getLineNumber(); |
| } |
| return -1; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStackFrame#getCharStart() |
| */ |
| public int getCharStart() throws DebugException |
| { |
| return -1; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStackFrame#getCharEnd() |
| */ |
| public int getCharEnd() throws DebugException |
| { |
| return -1; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStackFrame#getName() |
| */ |
| public String getName() throws DebugException |
| { |
| ICDILocation location = getCDIStackFrame().getLocation(); |
| String name = new String(); |
| if ( location.getFunction() != null && location.getFunction().trim().length() > 0 ) |
| name += location.getFunction() + "() "; |
| if ( location.getFile() != null && location.getFile().trim().length() > 0 ) |
| { |
| name += "at " + location.getFile() + ":" ; |
| if ( location.getLineNumber() != 0 ) |
| name += location.getLineNumber(); |
| } |
| return name.toString(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStackFrame#getRegisterGroups() |
| */ |
| public IRegisterGroup[] getRegisterGroups() throws DebugException |
| { |
| return ((CDebugTarget)getDebugTarget()).getRegisterGroups(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStackFrame#hasRegisterGroups() |
| */ |
| public boolean hasRegisterGroups() throws DebugException |
| { |
| return ((CDebugTarget)getDebugTarget()).getRegisterGroups().length > 0; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.core.cdi.event.ICDIEventListener#handleDebugEvent(ICDIEvent) |
| */ |
| public void handleDebugEvent( ICDIEvent event ) |
| { |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStep#canStepInto() |
| */ |
| public boolean canStepInto() |
| { |
| try |
| { |
| return exists() && isTopStackFrame() && getThread().canStepInto(); |
| } |
| catch( DebugException e ) |
| { |
| logError( e ); |
| return false; |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStep#canStepOver() |
| */ |
| public boolean canStepOver() |
| { |
| try |
| { |
| return exists() && getThread().canStepOver(); |
| } |
| catch( DebugException e ) |
| { |
| logError( e ); |
| } |
| return false; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStep#canStepReturn() |
| */ |
| public boolean canStepReturn() |
| { |
| try |
| { |
| if ( !exists() ) |
| { |
| return false; |
| } |
| List frames = ((CThread)getThread()).computeStackFrames(); |
| if ( frames != null && !frames.isEmpty() ) |
| { |
| boolean bottomFrame = this.equals( frames.get( frames.size() - 1 ) ); |
| return !bottomFrame && getThread().canStepReturn(); |
| } |
| } |
| catch( DebugException e ) |
| { |
| logError( e ); |
| } |
| return false; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStep#isStepping() |
| */ |
| public boolean isStepping() |
| { |
| return getThread().isStepping(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStep#stepInto() |
| */ |
| public void stepInto() throws DebugException |
| { |
| if ( canStepInto() ) |
| { |
| getThread().stepInto(); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStep#stepOver() |
| */ |
| public void stepOver() throws DebugException |
| { |
| if ( !canStepOver() ) |
| { |
| return; |
| } |
| if ( isTopStackFrame() ) |
| { |
| getThread().stepOver(); |
| } |
| else |
| { |
| // ((CThread)getThread()).stepToFrame( this ); |
| getThread().stepOver(); // for now |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.IStep#stepReturn() |
| */ |
| public void stepReturn() throws DebugException |
| { |
| if ( !canStepReturn() ) |
| { |
| return; |
| } |
| if ( isTopStackFrame() ) |
| { |
| getThread().stepReturn(); |
| } |
| else |
| { |
| /* |
| List frames = ((CThread)getThread()).computeStackFrames(); |
| int index = frames.indexOf( this ); |
| if ( index >= 0 && index < frames.size() - 1 ) |
| { |
| IStackFrame nextFrame = (IStackFrame)frames.get( index + 1 ); |
| ((CThread)getThread()).stepToFrame( nextFrame ); |
| } |
| */ |
| getThread().stepReturn(); // for now |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.ISuspendResume#canResume() |
| */ |
| public boolean canResume() |
| { |
| return getThread().canResume(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend() |
| */ |
| public boolean canSuspend() |
| { |
| return getThread().canSuspend(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended() |
| */ |
| public boolean isSuspended() |
| { |
| return getThread().isSuspended(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.ISuspendResume#resume() |
| */ |
| public void resume() throws DebugException |
| { |
| getThread().resume(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.ISuspendResume#suspend() |
| */ |
| public void suspend() throws DebugException |
| { |
| getThread().suspend(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.ITerminate#canTerminate() |
| */ |
| public boolean canTerminate() |
| { |
| boolean exists = false; |
| try |
| { |
| exists = exists(); |
| } |
| catch( DebugException e ) |
| { |
| logError( e ); |
| } |
| return exists && getThread().canTerminate() || getDebugTarget().canTerminate(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.ITerminate#isTerminated() |
| */ |
| public boolean isTerminated() |
| { |
| return getThread().isTerminated(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.debug.core.model.ITerminate#terminate() |
| */ |
| public void terminate() throws DebugException |
| { |
| if ( getThread().canTerminate() ) |
| { |
| getThread().terminate(); |
| } |
| else |
| { |
| getDebugTarget().terminate(); |
| } |
| } |
| |
| /** |
| * Returns the underlying CDI stack frame that this model object is |
| * a proxy to. |
| * |
| * @return the underlying CDI stack frame |
| */ |
| protected ICDIStackFrame getCDIStackFrame() |
| { |
| return fCDIStackFrame; |
| } |
| |
| /** |
| * Sets the underlying CDI stack frame. Called by a thread |
| * when incrementally updating after a step has completed. |
| * |
| * @param frame the underlying stack frame |
| */ |
| protected void setCDIStackFrame( ICDIStackFrame frame ) |
| { |
| if ( frame != null ) |
| { |
| fLastCDIStackFrame = frame; |
| } |
| else |
| { |
| fLastCDIStackFrame = fCDIStackFrame; |
| } |
| fCDIStackFrame = frame; |
| setRefreshVariables( true ); |
| } |
| |
| /** |
| * The underlying stack frame that existed before the current underlying |
| * stack frame. Used only so that equality can be checked on stack frame |
| * after the new one has been set. |
| */ |
| protected ICDIStackFrame getLastCDIStackFrame() |
| { |
| return fLastCDIStackFrame; |
| } |
| |
| /** |
| * Helper method for computeStackFrames(). For the purposes of detecting if |
| * an underlying stack frame needs to be disposed, stack frames are equal if |
| * the frames are equal and the locations are equal. |
| */ |
| protected static boolean equalFrame( ICDIStackFrame frameOne, ICDIStackFrame frameTwo ) |
| { |
| if ( frameOne == null || frameTwo == null ) |
| return false; |
| ICDILocation loc1 = frameOne.getLocation(); |
| ICDILocation loc2 = frameTwo.getLocation(); |
| if ( loc1 == null || loc2 == null ) |
| return false; |
| if ( loc1.getFile() != null && loc1.getFile().length() > 0 && |
| loc2.getFile() != null && loc2.getFile().length() > 0 && |
| loc1.getFile().equals( loc2.getFile() ) ) |
| |
| { |
| if ( loc1.getFunction() != null && loc1.getFunction().length() > 0 && |
| loc2.getFunction() != null && loc2.getFunction().length() > 0 && |
| loc1.getFunction().equals( loc2.getFunction() ) ) |
| return true; |
| } |
| if ( ( loc1.getFile() == null || loc1.getFile().length() < 1 ) && |
| ( loc2.getFile() == null || loc2.getFile().length() < 1 ) ) |
| |
| { |
| if ( loc1.getFunction() != null && loc1.getFunction().length() > 0 && |
| loc2.getFunction() != null && loc2.getFunction().length() > 0 && |
| loc1.getFunction().equals( loc2.getFunction() ) ) |
| return true; |
| } |
| if ( ( loc1.getFile() == null || loc1.getFile().length() < 1 ) && |
| ( loc2.getFile() == null || loc2.getFile().length() < 1 ) && |
| ( loc1.getFunction() == null || loc1.getFunction().length() < 1 ) && |
| ( loc2.getFunction() == null || loc2.getFunction().length() < 1 ) ) |
| { |
| if ( loc1.getAddress() == loc2.getAddress() ) |
| return true; |
| } |
| return false; |
| } |
| |
| protected boolean exists() throws DebugException |
| { |
| return ((CThread)getThread()).computeStackFrames().indexOf( this ) != -1; |
| } |
| |
| /** |
| * @see IAdaptable#getAdapter(Class) |
| */ |
| public Object getAdapter( Class adapter ) |
| { |
| if ( adapter == CStackFrame.class ) |
| { |
| return this; |
| } |
| if ( adapter == IStackFrame.class ) |
| { |
| return this; |
| } |
| if ( adapter == ICDIStackFrame.class ) |
| { |
| return getCDIStackFrame(); |
| } |
| if ( adapter == IStackFrameInfo.class ) |
| { |
| return this; |
| } |
| return super.getAdapter( adapter ); |
| } |
| |
| protected void dispose() |
| { |
| getCDISession().getEventManager().removeEventListener( this ); |
| disposeAllVariables(); |
| } |
| |
| /** |
| * Retrieves local variables in this stack frame. Returns an empty |
| * list if there are no local variables. |
| * |
| */ |
| protected List getCDILocalVariableObjects() throws DebugException |
| { |
| List list = new ArrayList(); |
| try |
| { |
| list.addAll( Arrays.asList( getCDISession().getVariableManager().getLocalVariableObjects( getCDIStackFrame() ) ) ); |
| } |
| catch( CDIException e ) |
| { |
| targetRequestFailed( e.getMessage(), null ); |
| } |
| return list; |
| } |
| |
| /** |
| * Retrieves arguments in this stack frame. Returns an empty list |
| * if there are no arguments. |
| * |
| */ |
| protected List getCDIArgumentObjects() throws DebugException |
| { |
| List list = new ArrayList(); |
| try |
| { |
| list.addAll( Arrays.asList( getCDISession().getVariableManager().getArgumentObjects( getCDIStackFrame() ) ) ); |
| } |
| catch( CDIException e ) |
| { |
| targetRequestFailed( e.getMessage(), null ); |
| } |
| return list; |
| } |
| /* |
| protected List getAllCDIVariables() throws DebugException |
| { |
| List list = new ArrayList(); |
| list.addAll( getCDIArguments() ); |
| list.addAll( getCDILocalVariables() ); |
| return list; |
| } |
| */ |
| protected List getAllCDIVariableObjects() throws DebugException |
| { |
| List list = new ArrayList(); |
| list.addAll( getCDIArgumentObjects() ); |
| list.addAll( getCDILocalVariableObjects() ); |
| return list; |
| } |
| |
| protected boolean isTopStackFrame() throws DebugException |
| { |
| IStackFrame tos = getThread().getTopStackFrame(); |
| return tos != null && tos.equals( this ); |
| } |
| |
| protected void disposeAllVariables() |
| { |
| if ( fVariables == null ) |
| return; |
| Iterator it = fVariables.iterator(); |
| while( it.hasNext() ) |
| { |
| ((CVariable)it.next()).dispose(); |
| } |
| fVariables = null; |
| } |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.core.IStackFrameInfo#getAddress() |
| */ |
| public long getAddress() |
| { |
| return getCDIStackFrame().getLocation().getAddress(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.core.IStackFrameInfo#getFile() |
| */ |
| public String getFile() |
| { |
| return getCDIStackFrame().getLocation().getFile(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.core.IStackFrameInfo#getFunction() |
| */ |
| public String getFunction() |
| { |
| return getCDIStackFrame().getLocation().getFunction(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.core.IStackFrameInfo#getLevel() |
| */ |
| public int getLevel() |
| { |
| return getCDIStackFrame().getLevel(); |
| } |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.core.IStackFrameInfo#getFrameLineNumber() |
| */ |
| public int getFrameLineNumber() |
| { |
| return getCDIStackFrame().getLocation().getLineNumber(); |
| } |
| |
| /* |
| * @see org.eclipse.cdt.debug.core.IStackFrameInfo#getArguments() |
| */ |
| public IVariable[] getArguments() |
| { |
| ArrayList list = new ArrayList(); |
| IVariable[] vars = new IVariable[0]; |
| try |
| { |
| vars = getVariables(); |
| } |
| catch( DebugException e ) |
| { |
| CDebugCorePlugin.log( e ); |
| } |
| for ( int i = 0; i < vars.length; ++i ) |
| { |
| if ( vars[i] instanceof CVariable && ((CVariable)vars[i]).isArgument() ) |
| { |
| list.add( vars[i] ); |
| } |
| } |
| return (IVariable[])list.toArray( new IVariable[list.size()] ); |
| } |
| |
| protected synchronized void preserve() |
| { |
| preserveVariables(); |
| } |
| |
| private void preserveVariables() |
| { |
| if ( fVariables == null ) |
| return; |
| try |
| { |
| Iterator it = fVariables.iterator(); |
| while( it.hasNext() ) |
| { |
| ((CVariable)it.next()).setChanged( false ); |
| } |
| } |
| catch( DebugException e ) |
| { |
| CDebugCorePlugin.log( e ); |
| } |
| } |
| |
| protected ICDIVariableObject findVariable( List list, CVariable var ) |
| { |
| Iterator it = list.iterator(); |
| while( it.hasNext() ) |
| { |
| ICDIVariableObject newVarObject = (ICDIVariableObject)it.next(); |
| if ( var.sameVariableObject( newVarObject ) ) |
| return newVarObject; |
| } |
| return null; |
| } |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.core.IRestart#canRestart() |
| */ |
| public boolean canRestart() |
| { |
| return getDebugTarget() instanceof IRestart && ((IRestart)getDebugTarget()).canRestart(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.core.IRestart#restart() |
| */ |
| public void restart() throws DebugException |
| { |
| if ( canRestart() ) |
| { |
| ((IRestart)getDebugTarget()).restart(); |
| } |
| } |
| |
| private void setRefreshVariables( boolean refresh ) |
| { |
| fRefreshVariables = refresh; |
| } |
| |
| private boolean refreshVariables() |
| { |
| return fRefreshVariables; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.core.model.IResumeWithoutSignal#canResumeWithoutSignal() |
| */ |
| public boolean canResumeWithoutSignal() |
| { |
| return ( getDebugTarget() instanceof IResumeWithoutSignal && |
| ((IResumeWithoutSignal)getDebugTarget()).canResumeWithoutSignal() ); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.debug.core.model.IResumeWithoutSignal#resumeWithoutSignal() |
| */ |
| public void resumeWithoutSignal() throws DebugException |
| { |
| if ( canResumeWithoutSignal() ) |
| { |
| ((IResumeWithoutSignal)getDebugTarget()).resumeWithoutSignal(); |
| } |
| } |
| } |