| /******************************************************************************* |
| * 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; |
| } |
| } |