blob: 33c4534b9e2bf38e6aa938c631f7beef44becf9a [file] [log] [blame]
/*
*(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();
}
}
}