blob: 2b030fa15497ee4c3a95b043936d355771dd6575 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2010, 2011 Nokia and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nokia - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.edc.internal.symbols;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.RangeList;
import org.eclipse.cdt.debug.edc.symbols.ICompileUnitScope;
import org.eclipse.cdt.debug.edc.symbols.IEnumerator;
import org.eclipse.cdt.debug.edc.symbols.IModuleScope;
import org.eclipse.cdt.debug.edc.symbols.IRangeList;
import org.eclipse.cdt.debug.edc.symbols.IScope;
import org.eclipse.cdt.debug.edc.symbols.IVariable;
import org.eclipse.cdt.debug.edc.symbols.IRangeList.Entry;
import org.eclipse.cdt.utils.Addr32;
import org.eclipse.cdt.utils.Addr64;
/**
* abstract implementation of {@link IScope},
* optionally containing a name, low & high addresses, IScope parent,
* children, variables, enumerators, et al
*
*/
public abstract class Scope implements IScope {
protected String name;
protected IAddress lowAddress;
protected IAddress highAddress;
protected IScope parent;
protected List<IScope> children = new ArrayList<IScope>();
protected List<IVariable> variables = new ArrayList<IVariable>();
protected List<IEnumerator> enumerators = new ArrayList<IEnumerator>();
private TreeMap<IRangeList.Entry, IScope> addressToScopeMap;
protected IRangeList rangeList;
public Scope(String name, IAddress lowAddress, IAddress highAddress, IScope parent) {
this.name = name;
this.lowAddress = lowAddress;
this.highAddress = highAddress;
this.parent = parent;
}
public String getName() {
return name;
}
public IAddress getLowAddress() {
return lowAddress;
}
public IAddress getHighAddress() {
return highAddress;
}
/**
* Tell whether the address range for the scope is empty.
* @return flag
*/
public boolean hasEmptyRange() {
return (lowAddress == null || highAddress == null)
|| (lowAddress.isZero() && highAddress.isZero())
|| (lowAddress.getValue().longValue() == -1 && highAddress.isZero()) // TODO: remove this case
|| lowAddress.equals(highAddress);
}
/**
* Return the list of non-contiguous ranges for this scope.
* @return list or <code>null</code>
*/
public IRangeList getRangeList() {
return rangeList;
}
public void setLowAddress(IAddress lowAddress) {
this.lowAddress = lowAddress;
}
public void setHighAddress(IAddress highAddress) {
this.highAddress = highAddress;
}
public void setRangeList(IRangeList ranges) {
this.rangeList = ranges;
setLowAddress(new Addr32(rangeList.getLowAddress()));
setHighAddress(new Addr32(rangeList.getHighAddress()));
}
public IScope getParent() {
return parent;
}
public Collection<IScope> getChildren() {
return Collections.unmodifiableCollection(children);
}
public Collection<IVariable> getVariables() {
return Collections.unmodifiableCollection(variables);
}
public Collection<IEnumerator> getEnumerators() {
return Collections.unmodifiableCollection(enumerators);
}
public IScope getScopeAtAddress(IAddress linkAddress) {
// see if it's in this scope
if (linkAddress.compareTo(lowAddress) >= 0 && linkAddress.compareTo(highAddress) < 0) {
ensureScopeRangeLookup();
long addr = linkAddress.getValue().longValue();
IRangeList.Entry addressEntry = new IRangeList.Entry(addr, addr);
SortedMap<Entry,IScope> tailMap = addressToScopeMap.tailMap(addressEntry);
if (tailMap.isEmpty())
return this;
IScope child = tailMap.values().iterator().next();
if (linkAddress.compareTo(child.getLowAddress()) >= 0
&& linkAddress.compareTo(child.getHighAddress()) < 0) {
return child.getScopeAtAddress(linkAddress);
}
return this;
}
return null;
}
/**
* Make sure our mapping of address range to scope is valid.
*/
private void ensureScopeRangeLookup() {
if (addressToScopeMap == null) {
addressToScopeMap = new TreeMap<Entry, IScope>();
for (IScope scope : children) {
addScopeRange(scope);
}
//System.out.println("Mapping for " + getName()+ ": "+ addressToScopeMap.size() + " entries");
}
}
/**
* @param scope
*/
private void addScopeRange(IScope scope) {
IRangeList ranges = scope.getRangeList();
if (ranges != null) {
for (IRangeList.Entry entry : ranges) {
addressToScopeMap.put(entry, scope);
}
} else {
addressToScopeMap.put(new IRangeList.Entry(
scope.getLowAddress().getValue().longValue(),
scope.getHighAddress().getValue().longValue()),
scope);
}
}
/**
* Adds the given scope as a child of this scope
*
* @param scope
*/
public void addChild(IScope scope) {
children.add(scope);
if (addressToScopeMap == null)
addressToScopeMap = new TreeMap<Entry, IScope>();
addScopeRange(scope);
}
/**
* Adds the given variable to this scope
*
* @param variable
*/
public void addVariable(IVariable variable) {
variables.add(variable);
}
/**
* Adds the given variable to this scope
*
* @param variable
*/
public void addEnumerator(IEnumerator enumerator) {
enumerators.add(enumerator);
}
/** Scope specific compareTo() based upon<br>
* 1) if object to compare is IScope, comparison of lowAddress, then highAddress<br>
* 2) if object to compare is IAddress, compare address to this lowAddress<br>
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(Object o) {
if (o instanceof IScope) {
IScope io = (IScope) o;
int comparison = lowAddress.compareTo(io.getLowAddress());
if (comparison == 0) {
comparison = highAddress.compareTo(io.getHighAddress());
}
return comparison;
} else if (o instanceof IAddress) {
return lowAddress.compareTo(o);
}
return 0;
}
/**
* Scope specific version of toString():<br>
* <code>
* [ranges=, lowAddress=, highAddress=, name=]
* </code>
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Scope ["); //$NON-NLS-1$
if (rangeList != null) {
builder.append("ranges="); //$NON-NLS-1$
builder.append(rangeList);
} else {
builder.append("lowAddress="); //$NON-NLS-1$
builder.append(lowAddress != null ? lowAddress.toHexAddressString() : "null");
builder.append(", highAddress="); //$NON-NLS-1$
builder.append(highAddress != null ? highAddress.toHexAddressString() : "null");
}
builder.append(", "); //$NON-NLS-1$
if (name != null) {
builder.append("name="); //$NON-NLS-1$
builder.append(name);
builder.append(", "); //$NON-NLS-1$
}
builder.append("]"); //$NON-NLS-1$
return builder.toString();
}
/**
* deal with incorrectly generated ranges by the compiler
* that may not work together properly with ranges of children
* @param baseAddress
*/
public void fixupRanges(IAddress baseAddress) {
// compile unit scopes not generated by the compiler so
// figure it out from the functions
IAddress newLowAddress = Addr64.MAX;
IAddress newHighAddress = Addr64.ZERO;
IRangeList newRangeList = new RangeList();
boolean any = false;
for (IScope kid : getChildren()) {
// the compiler may generate (bad) low/high pc's which are not
// in the actual module space for some functions. to work
// around this, only honor addresses that are above the
// actual link address
if (kid.hasEmptyRange()) {
continue;
}
if (kid.getLowAddress().compareTo(baseAddress) > 0) {
IRangeList kidRanges = kid.getRangeList();
if (kidRanges == null){// If it didn't have ranges it still has one range
kidRanges = new RangeList();
((RangeList)kidRanges).addRange(kid.getLowAddress().getValue().longValue(),kid.getHighAddress().getValue().longValue());
}
newRangeList = RangeList.mergeRangeLists(newRangeList,kidRanges);
if (kid.getLowAddress().compareTo(newLowAddress) < 0) {
newLowAddress = kid.getLowAddress();
any = true;
}
if (kid.getHighAddress().compareTo(newHighAddress) > 0) {
newHighAddress = kid.getHighAddress();
any = true;
}
}
}
if (any) {
//System.out.println("Needed to fix up ranges for " + getName());
lowAddress = newLowAddress;
highAddress = newHighAddress;
int entryCount = 0;
for (@SuppressWarnings("unused") IRangeList.Entry entry : newRangeList){
++entryCount;
}
rangeList = (entryCount > 1) ? newRangeList : null;
} else {
if (lowAddress == null) {
lowAddress = highAddress = Addr32.ZERO;
}
}
}
/**
* Merge the code range(s) from the given scope into this one.
* @param scope
*/
protected void mergeScopeRange(IScope scope) {
if (hasEmptyRange()) {
// copy range
if (scope.getRangeList() != null) {
setRangeList(scope.getRangeList());
} else {
setLowAddress(scope.getLowAddress());
setHighAddress(scope.getHighAddress());
}
} else {
if (scope.getLowAddress() != null && scope.getLowAddress().compareTo(lowAddress) < 0
&& !scope.getLowAddress().isZero()) // ignore random 0 entries
{
if (rangeList != null) {
if (scope.getRangeList() != null) {
rangeList = RangeList.mergeRangeLists(rangeList,scope.getRangeList());
} else {
((RangeList)rangeList).addLowRange(scope.getLowAddress().getValue().longValue());
}
}
lowAddress = scope.getLowAddress();
}
if (scope.getHighAddress() != null && scope.getHighAddress().compareTo(highAddress) > 0) {
if (rangeList != null) {
if (scope.getRangeList() != null) {
rangeList = RangeList.mergeRangeLists(rangeList,scope.getRangeList());
} else {
((RangeList)rangeList).addHighRange(scope.getHighAddress().getValue().longValue());
}
}
highAddress = scope.getHighAddress();
}
}
}
/**
* @param scope
*/
protected void addLineInfoToParent(IScope scope) {
IScope cu = parent;
while (cu != null) {
if (cu instanceof ICompileUnitScope && cu.getParent() instanceof IModuleScope) {
IModuleScope module = (IModuleScope) cu.getParent();
ModuleLineEntryProvider provider = (ModuleLineEntryProvider) module.getModuleLineEntryProvider();
provider.addCompileUnitChild((ICompileUnitScope) cu, scope);
break;
}
cu = cu.getParent();
}
}
/**
*
*/
public void dispose() {
for (IScope scope : children)
scope.dispose();
children.clear();
for (IVariable var : variables)
var.dispose();
variables.clear();
enumerators.clear();
if (addressToScopeMap != null)
addressToScopeMap.clear();
rangeList = null;
}
/**
* there are not enough non-mutable members with which
* to provide a proper hashCode() for Scope, and we
* cannot provide equals() if we cannot guarantee
* hashCode() ... but at least we can make comparison
* of different Scope objects a little easier for
* situations where we want to claim to Scope objects
* have the same contents.
* <br>
* in this case, content equality is based upon the
* following criteria<br>
* 1) same children<br>
* 2) same highAddress<br>
* 3) same lowAddress<br>
* 4) same name<br>
* 5) same rangeList<br>
*/
public boolean contentsEquals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Scope other = (Scope) obj;
if (children == null) {
if (other.children != null)
return false;
} else if (!children.equals(other.children))
return false;
if (highAddress == null) {
if (other.highAddress != null)
return false;
} else if (!highAddress.equals(other.highAddress))
return false;
if (lowAddress == null) {
if (other.lowAddress != null)
return false;
} else if (!lowAddress.equals(other.lowAddress))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (rangeList == null) {
if (other.rangeList != null)
return false;
} else if (!rangeList.equals(other.rangeList))
return false;
return true;
}
}