### Eclipse Workspace Patch 1.0 #P org.eclipse.cdt.dsf Index: src/org/eclipse/cdt/dsf/debug/service/IExpressions.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.cdt/dsf/org.eclipse.cdt.dsf/src/org/eclipse/cdt/dsf/debug/service/IExpressions.java,v retrieving revision 1.5 diff -u -r1.5 IExpressions.java --- src/org/eclipse/cdt/dsf/debug/service/IExpressions.java 11 Jan 2010 22:08:24 -0000 1.5 +++ src/org/eclipse/cdt/dsf/debug/service/IExpressions.java 19 Mar 2010 17:00:56 -0000 @@ -143,6 +142,9 @@ * "int *", "mytypedef", "(int *)[]", "enum Bar". If the debugger backend cannot supply * this information, this method returns "" (the angle brackets are there just in * case there is a type named "UNKNOWN" in the application). + *

+ * If you implement {@link IExpressions2}, this should return the casted type name, + * if this expression was generated via {@link IExpressions2#createCastedExpression(IDMContext, String, IExpressions2.ICastedExpressionDMContext)} */ String getTypeName(); Index: src/org/eclipse/cdt/dsf/debug/service/IExpressions2.java =================================================================== RCS file: src/org/eclipse/cdt/dsf/debug/service/IExpressions2.java diff -N src/org/eclipse/cdt/dsf/debug/service/IExpressions2.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/cdt/dsf/debug/service/IExpressions2.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2010 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.dsf.debug.service; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; + +/** + * This interface extends the expressions service with support for casting to type or + * array. + * @since 2.1 + */ +public interface IExpressions2 extends IExpressions { + + /** + * This class specifies how an expression should be + * typecast to another type and/or displayed as an array. + */ + public static class CastInfo { + private final String typeString; + private final int arrayCount; + private final int arrayStart; + + /** + * Create an instance of casting information + * @param typeString if not null, the C/C++ type to which to cast the expression (e.g. "char**") + * @param arrayStart if arrayCount > 0, the start index for viewing contents of the expression as an array + * @param arrayCount if > 0, indicates to show [arrayStart ... arrayStart+arrayCount) as child expressions + */ + public CastInfo(String typeString, int arrayStart, int arrayCount) { + this.typeString = typeString; + this.arrayStart = arrayStart; + this.arrayCount = arrayCount; + } + + /** + * Create an instance of casting information with a different type + * @param typeString the C/C++ type to which to cast the expression (e.g. "char**") (must not be null) + */ + public CastInfo(CastInfo original, String typeString) { + if (typeString == null) + throw new IllegalArgumentException(); + this.typeString = typeString; + this.arrayStart = original.getArrayStartIndex(); + this.arrayCount = original.getArrayCount(); + } + + /** + * Create an instance of casting information with different array bounds + * @param arrayStart if arrayCount > 0, the start index for viewing contents of the expression as an array + * @param arrayCount if > 0, indicates to show [arrayStart ... arrayStart+arrayCount) as child expressions + */ + public CastInfo(CastInfo original, int arrayStart, int arrayCount) { + if (arrayCount <= 0) + throw new IllegalArgumentException(); + this.typeString = original.getTypeString(); + this.arrayStart = arrayStart; + this.arrayCount = arrayCount; + } + + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + arrayCount; + result = prime * result + arrayStart; + result = prime * result + + ((typeString == null) ? 0 : typeString.hashCode()); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CastInfo other = (CastInfo) obj; + if (arrayCount != other.arrayCount) + return false; + if (arrayStart != other.arrayStart) + return false; + if (typeString == null) { + if (other.typeString != null) + return false; + } else if (!typeString.equals(other.typeString)) + return false; + return true; + } + + /** + * Get the user-friendly type name. This may be a post-processed string + * but should be semantically equivalent to the type used to create the context. + * @return type string, or null if no type casting performed + */ + public String getTypeString() { + return typeString; + } + + /** + * Get the start index for viewing children as an array. (Only effective if #getCount() > 0) + * @return the index of the first element of the array. 0 means that + * the original element is the first member of the array. This may be negative, too. + */ + public int getArrayStartIndex(){ + return arrayStart; + } + + /** + * Get the number of elements to show when viewing children as an array. + * @return the array size, or <= 0 if not viewing as an array + */ + public int getArrayCount(){ + return arrayCount; + } + } + + /** + * This context identifies a casted expression. Its parent is the original + * {@link IExpressionDMContext}. + */ + public interface ICastedExpressionDMContext extends IExpressionDMContext { + CastInfo getCastInfo(); + + } + + /** + * Create a variant of the expression which is casted with the given casting info. + *

+ * If {@link ICastInfo#getTypeString()} is not null, such an expression should + * report the casted type via {@link IExpressionDMData} and generate subexpressions accordingly. + *

+ * Note that typically, a cast of a primitive type (int, double, etc.) to another + * primitive type is interpreted as "*(other_type*)&(expression)", not merely + * as casting the rvalue of "expression" to another type (which usually only + * truncates or extends the value without much benefit). + *

+ * If {@link ICastInfo#getArrayCount()} is greater than 0, the expression should + * yield that number of elements as subexpressions, as array elements, starting with index + * {@link ICastInfo#getArrayStartIndex()}. (This does not affect the address of the + * expression itself, only which children are returned.) + *

+ * The expected semantics of an array cast ranging from J to K are to take a + * pointer-valued expression whose base type is size N, evaluate its value + * to A, and yield array elements of the pointer base type at locations + * A + N*J, A + N*(J+1), ..., + * A + N*(J+K-1). But the address of the expression is not modified + * when an array cast is applied. + *

An implementation may provide its own semantics for viewing other data as arrays, if so desired. + * @param context an existing expression + * @param castInfo the casting information + * @param rm request monitor returning a casted expression data model context object that must be passed to the appropriate + * data retrieval routine to obtain the value of the expression. The object must + * report the casted type (if any) via {@link #getExpressionData(IExpressionDMContext, DataRequestMonitor)} + * and report alternate children according to the array casting context via + * {@link #getSubExpressionCount(IExpressionDMContext, DataRequestMonitor)} + * and {@link #getSubExpressions}. + */ + ICastedExpressionDMContext createCastedExpression(IExpressionDMContext context, + CastInfo castInfo); + + +} #P org.eclipse.cdt.dsf.ui Index: src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/MessagesForVariablesVM.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.cdt/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/MessagesForVariablesVM.java,v retrieving revision 1.6 diff -u -r1.6 MessagesForVariablesVM.java --- src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/MessagesForVariablesVM.java 11 Jan 2010 22:08:22 -0000 1.6 +++ src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/MessagesForVariablesVM.java 19 Mar 2010 17:00:57 -0000 @@ -24,7 +24,9 @@ public static String VariableColumnPresentation_value; public static String VariableColumnPresentation_location; - public static String VariableVMNode_Location_column__Error__text_format; + public static String VariableVMNode_CannotCastVariable; + + public static String VariableVMNode_Location_column__Error__text_format; public static String VariableVMNode_Location_column__text_format; public static String VariableVMNode_Description_column__text_format; public static String VariableVMNode_Expression_column__text_format; Index: src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMNode.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.cdt/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMNode.java,v retrieving revision 1.15 diff -u -r1.15 VariableVMNode.java --- src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMNode.java 9 Mar 2010 23:11:09 -0000 1.15 +++ src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMNode.java 19 Mar 2010 17:00:57 -0000 @@ -13,10 +13,12 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.RejectedExecutionException; +import org.eclipse.cdt.debug.core.model.ICastToArray; import org.eclipse.cdt.debug.internal.ui.CDebugImages; import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; @@ -26,9 +28,13 @@ import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IExpressions; +import org.eclipse.cdt.dsf.debug.service.IExpressions2; +import org.eclipse.cdt.dsf.debug.service.IExpressions2.CastInfo; +import org.eclipse.cdt.dsf.debug.service.IExpressions2.ICastedExpressionDMContext; import org.eclipse.cdt.dsf.debug.service.IFormattedValues; import org.eclipse.cdt.dsf.debug.service.IStack; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionChangedDMEvent; @@ -69,7 +75,9 @@ import org.eclipse.cdt.dsf.ui.viewmodel.update.StaleDataLabelForeground; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.PlatformObject; import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.model.IExpression; import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; @@ -137,19 +145,164 @@ */ private final IElementLabelProvider fLabelProvider; - public class VariableExpressionVMC extends DMVMContext implements IFormattedValueVMContext { + + + private class CastImplementation extends PlatformObject implements ICastToArray { + private final IExpressionDMContext exprDMC; + private String memento; + + public CastImplementation(IExpressionDMContext exprDMC) { + this.exprDMC = exprDMC; + this.memento = createCastedExpressionMemento(exprDMC, exprDMC.getExpression()); + } + + private boolean isValid() { + return (getServicesTracker().getService(IExpressions2.class) != null && exprDMC != null); + } + + private void throwIfNotValid() throws DebugException { + if (!isValid()) + throw new DebugException(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR, + MessagesForVariablesVM.VariableVMNode_CannotCastVariable, null)); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToType#canCast() + */ + public boolean canCast() { + return isValid(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToType#getCurrentType() + */ + public String getCurrentType() { + // get expected casted type first, if possible (if there's an error in the type, + // the expression might not evaluate successfully) + CastInfo castDMC = fCastedExpressionStorage.get(memento); + if (castDMC != null && castDMC.getTypeString() != null) + return castDMC.getTypeString(); + + // else, get the actual type + IExpressionDMData data = fSyncVariableDataAccess.readVariable(exprDMC); + if (data != null) + return data.getTypeName(); + + return ""; //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToType#cast(java.lang.String) + */ + public void cast(String type) throws DebugException { + throwIfNotValid(); + + CastInfo currentContext = fCastedExpressionStorage.get(memento); + + updateCastInformation(type, + currentContext != null ? currentContext.getArrayStartIndex() : 0, + currentContext != null ? currentContext.getArrayCount() : 0); + + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToType#restoreOriginal() + */ + public void restoreOriginal() throws DebugException { + throwIfNotValid(); + fCastedExpressionStorage.remove(memento); + fireExpressionChangedEvent(exprDMC); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToType#isCasted() + */ + public boolean isCasted() { + if (isValid()) + return fCastedExpressionStorage.containsKey(memento); + else + return false; + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToArray#canCastToArray() + */ + public boolean canCastToArray() { + return isValid(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToArray#castToArray(int, int) + */ + public void castToArray(int startIndex, int length) + throws DebugException { + throwIfNotValid(); + + CastInfo currentContext = fCastedExpressionStorage.get(memento); + + updateCastInformation(currentContext != null ? currentContext.getTypeString() : null, + startIndex, + length); + } + + private void updateCastInformation( + String type, int arrayStartIndex, + int arrayCount) { + final CastInfo info = new CastInfo(type, arrayStartIndex, arrayCount); + fCastedExpressionStorage.put(memento, info); + fireExpressionChangedEvent(exprDMC); + } + + private class ExpressionChangedEvent extends AbstractDMEvent implements IExpressionChangedDMEvent { + public ExpressionChangedEvent(IExpressionDMContext context) { + super(context); + } + } + + private void fireExpressionChangedEvent(IExpressionDMContext exprDMC) { + ExpressionChangedEvent event = new ExpressionChangedEvent(exprDMC); + getDMVMProvider().handleEvent(event); + //getDMVMProvider().refresh(); + } + } + + public class VariableExpressionVMC extends DMVMContext implements IFormattedValueVMContext, ICastToArray { private IExpression fExpression; + private CastImplementation castImpl; public VariableExpressionVMC(IDMContext dmc) { super(dmc); + + // Support for casting to type. The CDT UI for this support + // relies on objectContribution and instanceof, but we'd rather deal + // with this off to the side. + IExpressionDMContext exprDMC = null; + if (dmc instanceof ICastedExpressionDMContext) + exprDMC = DMContexts.getAncestorOfType(dmc.getParents()[0], IExpressionDMContext.class); + else if (dmc instanceof IExpressionDMContext) + exprDMC = (IExpressionDMContext) dmc; + + if (exprDMC != null) { + this.castImpl = new CastImplementation(exprDMC); + } else { + // the dmc should be an expression + assert false; + } } public void setExpression(IExpression expression) { fExpression = expression; } - @SuppressWarnings("rawtypes") + @SuppressWarnings({"unchecked", "rawtypes"}) @Override public Object getAdapter(Class adapter) { if (fExpression != null && adapter.isAssignableFrom(fExpression.getClass())) { @@ -175,6 +328,66 @@ public int hashCode() { return super.hashCode() + (fExpression != null ? fExpression.hashCode() : 0); } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToType#canCast() + */ + public boolean canCast() { + return castImpl != null && castImpl.canCast(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToType#getCurrentType() + */ + public String getCurrentType() { + return castImpl != null ? castImpl.getCurrentType() : ""; //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToType#cast(java.lang.String) + */ + public void cast(String type) throws DebugException { + if (castImpl != null) + castImpl.cast(type); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToType#restoreOriginal() + */ + public void restoreOriginal() throws DebugException { + if (castImpl != null) + castImpl.restoreOriginal(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToType#isCasted() + */ + public boolean isCasted() { + return castImpl != null && castImpl.isCasted(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToArray#canCastToArray() + */ + public boolean canCastToArray() { + return castImpl != null && castImpl.canCastToArray(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.debug.core.model.ICastToArray#castToArray(int, int) + */ + public void castToArray(int startIndex, int length) + throws DebugException { + if (castImpl != null) + castImpl.castToArray(startIndex, length); + } } protected class VariableExpressionFactory implements IWatchExpressionFactoryAdapter2 { @@ -218,6 +432,9 @@ private LabelBackground columnNoColumnsBackground; private IPropertyChangeListener fPreferenceChangeListener; + /** expression memento to casting context (TODO: persist these; bug 228301)*/ + private Map fCastedExpressionStorage = new HashMap(); + /* * (non-Javadoc) * @see org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode#dispose() @@ -821,9 +1038,10 @@ public void run() { final IExpressions expressionService = getServicesTracker().getService(IExpressions.class); if (expressionService != null) { - IExpressionDMContext expressionDMC = expressionService.createExpression( - createCompositeDMVMContext(update), - update.getExpression().getExpressionText()); + IExpressionDMContext expressionDMC = createExpression(expressionService, + createCompositeDMVMContext(update), + update.getExpression().getExpressionText()); + VariableExpressionVMC variableVmc = (VariableExpressionVMC)createVMContext(expressionDMC); variableVmc.setExpression(update.getExpression()); @@ -839,8 +1057,17 @@ } } - - @Override + /** + * Create a memento that records the expression value and context + * @param expressionDMC + * @return String + */ + private String createCastedExpressionMemento(IDMContext context, String expression) { + String memento = context.getSessionId() + "." + expression; //$NON-NLS-1$ + return memento; + } + + @Override protected void handleFailedUpdate(IViewerUpdate update) { if (update instanceof IExpressionUpdate) { update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE, "Update failed", null)); //$NON-NLS-1$ @@ -965,10 +1192,26 @@ handleFailedUpdate(update); return; } - if (update.getOffset() < 0) { - fillUpdateWithVMCs(update, getData()); + + IExpressionDMContext[] data = getData(); + + IExpressions2 expression2Service = getServicesTracker().getService(IExpressions2.class); + + // If any of these expressions use casts, replace them. + if (expression2Service != null && !fCastedExpressionStorage.isEmpty()) { + for (int i = 0; i < data.length; i++) { + String memento = createCastedExpressionMemento(data[i], data[i].getExpression()); + CastInfo castInfo = fCastedExpressionStorage.get(memento); + if (castInfo != null) { + data[i] = expression2Service.createCastedExpression(data[i], castInfo); + } + } + } + + if (update.getOffset() < 0) { + fillUpdateWithVMCs(update, data); } else { - fillUpdateWithVMCs(update, getData(), update.getOffset()); + fillUpdateWithVMCs(update, data, update.getOffset()); } update.done(); } @@ -976,13 +1219,12 @@ // Make the asynchronous call to IExpressions.getSubExpressions(). The results are processed in the // DataRequestMonitor.handleCompleted() above. - - if (update.getOffset() < 0 || update.getLength() < 0) { - // If the range is not specified, get all expressions. - expressionService.getSubExpressions(expressionDMC, rm); - } else { - expressionService.getSubExpressions(expressionDMC, update.getOffset(), update.getLength(), rm); - } + if (update.getOffset() < 0 || update.getLength() < 0) { + // If the range is not specified, get all expressions. + expressionService.getSubExpressions(expressionDMC, rm); + } else { + expressionService.getSubExpressions(expressionDMC, update.getOffset(), update.getLength(), rm); + } } else { handleFailedUpdate(update); } @@ -1058,7 +1300,11 @@ int i = 0; for (IVariableDMData localDMData : localsDMData) { - expressionDMCs[i++] = expressionService.createExpression(frameDmc, localDMData.getName()); + expressionDMCs[i++] = createExpression(expressionService, frameDmc, localDMData.getName()); + if (isCanceled()) { + handleFailedUpdate(update); + return; + } } // Lastly, we fill the update from the array of view model context objects @@ -1099,7 +1345,24 @@ stackFrameService.getLocals(frameDmc, rm); } - public int getDeltaFlags(Object e) { + + private IExpressionDMContext createExpression( + IExpressions expressionService, + final IDMContext dmc, final String expression) { + IExpressionDMContext exprDMC = expressionService.createExpression(dmc, expression); + + String memento = createCastedExpressionMemento(exprDMC, expression); + if (expressionService instanceof IExpressions2) { + // wrap expression with cast, if applied + final CastInfo castInfo = fCastedExpressionStorage.get(memento); + if (castInfo != null) { + exprDMC = ((IExpressions2) expressionService).createCastedExpression(exprDMC, castInfo); + } + } + return exprDMC; + } + + public int getDeltaFlags(Object e) { if ( e instanceof ISuspendedDMEvent || e instanceof IMemoryChangedEvent || e instanceof IExpressionChangedDMEvent || Index: src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/messages.properties =================================================================== RCS file: /cvsroot/tools/org.eclipse.cdt/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/messages.properties,v retrieving revision 1.5 diff -u -r1.5 messages.properties --- src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/messages.properties 11 Jan 2010 22:08:22 -0000 1.5 +++ src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/messages.properties 19 Mar 2010 17:00:57 -0000 @@ -16,6 +16,7 @@ VariableColumnPresentation_value=Value VariableColumnPresentation_location=Location +VariableVMNode_CannotCastVariable=Cannot cast this variable VariableVMNode_Location_column__Error__text_format= VariableVMNode_Location_column__text_format={0} VariableVMNode_Description_column__text_format=