/******************************************************************************* | |
* 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.LinkedHashMap; | |
import java.util.List; | |
import java.util.Map; | |
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.IRangeList.Entry; | |
import org.eclipse.cdt.debug.edc.symbols.IScope; | |
import org.eclipse.cdt.debug.edc.symbols.IVariable; | |
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 Map<String, IVariable> variables = new LinkedHashMap<String, 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.values()); | |
} | |
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.put(variable.getName(), 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.values()) | |
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; | |
} | |
} |