/*******************************************************************************
* Copyright (c) 2000, 2010 QNX Software Systems 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:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.mi.core.cdi.model;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.debug.core.cdi.CDIException;
import org.eclipse.cdt.debug.core.cdi.ICDIFormat;
import org.eclipse.cdt.debug.core.cdi.model.ICDITarget;
import org.eclipse.cdt.debug.core.cdi.model.ICDIValue;
import org.eclipse.cdt.debug.core.cdi.model.ICDIVariable;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDIArrayType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDIBoolType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDICharType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDIDoubleType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDIEnumType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDIFloatType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDIFunctionType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDIIntType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDILongLongType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDILongType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDIPointerType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDIReferenceType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDIShortType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDIStructType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDIType;
import org.eclipse.cdt.debug.core.cdi.model.type.ICDIWCharType;
import org.eclipse.cdt.debug.mi.core.MIException;
import org.eclipse.cdt.debug.mi.core.MIPlugin;
import org.eclipse.cdt.debug.mi.core.MISession;
import org.eclipse.cdt.debug.mi.core.cdi.CdiResources;
import org.eclipse.cdt.debug.mi.core.cdi.ExpressionManager;
import org.eclipse.cdt.debug.mi.core.cdi.Format;
import org.eclipse.cdt.debug.mi.core.cdi.MI2CDIException;
import org.eclipse.cdt.debug.mi.core.cdi.MemoryManager;
import org.eclipse.cdt.debug.mi.core.cdi.RegisterManager;
import org.eclipse.cdt.debug.mi.core.cdi.Session;
import org.eclipse.cdt.debug.mi.core.cdi.VariableManager;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.ArrayValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.BoolValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.CharValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.DoubleValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.EnumValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.FloatValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.FunctionValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.IncompleteType;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.IntValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.LongLongValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.LongValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.PointerValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.ReferenceValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.ShortValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.StructValue;
import org.eclipse.cdt.debug.mi.core.cdi.model.type.WCharValue;
import org.eclipse.cdt.debug.mi.core.command.CommandFactory;
import org.eclipse.cdt.debug.mi.core.command.MIVarAssign;
import org.eclipse.cdt.debug.mi.core.command.MIVarCreate;
import org.eclipse.cdt.debug.mi.core.command.MIVarInfoExpression;
import org.eclipse.cdt.debug.mi.core.command.MIVarInfoType;
import org.eclipse.cdt.debug.mi.core.command.MIVarListChildren;
import org.eclipse.cdt.debug.mi.core.command.MIVarSetFormat;
import org.eclipse.cdt.debug.mi.core.command.MIVarShowAttributes;
import org.eclipse.cdt.debug.mi.core.event.MIVarChangedEvent;
import org.eclipse.cdt.debug.mi.core.output.MIInfo;
import org.eclipse.cdt.debug.mi.core.output.MIVar;
import org.eclipse.cdt.debug.mi.core.output.MIVarCreateInfo;
import org.eclipse.cdt.debug.mi.core.output.MIVarInfoExpressionInfo;
import org.eclipse.cdt.debug.mi.core.output.MIVarInfoTypeInfo;
import org.eclipse.cdt.debug.mi.core.output.MIVarListChildrenInfo;
import org.eclipse.cdt.debug.mi.core.output.MIVarShowAttributesInfo;
/**
*/
public abstract class Variable extends VariableDescriptor implements ICDIVariable {
private static final ICDIVariable[] NO_CHILDREN = new ICDIVariable[0];
protected MIVarCreate fVarCreateCMD;
protected MIVar fMIVar;
Value value;
public ICDIVariable[] children = NO_CHILDREN;
String editable = null;
String language;
boolean isFake = false;
boolean isUpdated = true;
private String hexAddress;
public Variable(VariableDescriptor obj, MIVarCreate var) {
super(obj);
fVarCreateCMD = var;
}
public Variable(Target target, Thread thread, StackFrame frame, String n, String q, int pos, int depth, MIVar miVar) {
super(target, thread, frame, n, q, pos, depth);
fMIVar = miVar;
}
public void setUpdated(boolean update) {
isUpdated = update;
}
public boolean isUpdated() {
return isUpdated;
}
public void update() throws CDIException {
Session session = (Session)getTarget().getSession();
VariableManager mgr = session.getVariableManager();
mgr.update(this);
}
public MIVar getMIVar() throws CDIException {
if (fMIVar == null) {
// Oops! what's up here, we should use Assert
if (fVarCreateCMD == null) {
throw new CDIException("Incomplete initialization of variable"); //$NON-NLS-1$
}
try {
MISession mi = ((Target)getTarget()).getMISession();
MIVarCreateInfo info = null;
// Wait for the response or timedout
synchronized (fVarCreateCMD) {
// RxThread will set the MIOutput on the cmd
// when the response arrive.
while ((info = fVarCreateCMD.getMIVarCreateInfo()) == null) {
try {
fVarCreateCMD.wait(mi.getCommandTimeout());
info = fVarCreateCMD.getMIVarCreateInfo();
if (info == null) {
throw new MIException(MIPlugin.getResourceString("src.MISession.Target_not_responding")); //$NON-NLS-1$
}
} catch (InterruptedException e) {
}
}
}
if (info == null) {
throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$
}
fMIVar = info.getMIVar();
} catch (MIException e) {
throw new MI2CDIException(e);
}
}
return fMIVar;
}
/**
* @return The address of this variable as hex string if available, otherwise an empty string.
* @noreference This method is not intended to be referenced by clients outside CDT.
* @since 7.1
*/
public String getHexAddress() throws CDIException {
if (hexAddress != null) {
return hexAddress;
}
VariableManager vm = ((Session)((Target)getTarget()).getSession()).getVariableManager();
String qualName = "&(" + getQualifiedName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
VariableDescriptor desc = createDescriptor((Target)getTarget(), (Thread)getThread(), (StackFrame)getStackFrame(), getName(), qualName, getPosition(), getStackDepth());
Variable v = vm.createVariable( desc );
// make sure to avoid infinite recursion. see bug 323630
if (v != this) {
v.setFormat(ICDIFormat.HEXADECIMAL);
hexAddress = v.getValue().getValueString();
} else {
hexAddress = ""; //$NON-NLS-1$
}
return hexAddress;
}
public Variable getChild(String name) {
for (int i = 0; i < children.length; i++) {
Variable variable = (Variable) children[i];
try {
if (name.equals(variable.getMIVar().getVarName())) {
return variable;
}
// Look also in the grandchildren.
Variable grandChild = variable.getChild(name);
if (grandChild != null) {
return grandChild;
}
} catch (CDIException e) {
// ignore;
}
}
return null;
}
String getLanguage() throws CDIException {
if (language == null) {
MISession mi = ((Target)getTarget()).getMISession();
CommandFactory factory = mi.getCommandFactory();
MIVarInfoExpression var = factory.createMIVarInfoExpression(getMIVar().getVarName());
try {
mi.postCommand(var);
MIVarInfoExpressionInfo info = var.getMIVarInfoExpressionInfo();
if (info == null) {
throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$
}
language = info.getLanguage();
} catch (MIException e) {
throw new MI2CDIException(e);
}
}
return (language == null) ? "" : language; //$NON-NLS-1$
}
boolean isCPPLanguage() throws CDIException {
return getLanguage().equalsIgnoreCase("C++"); //$NON-NLS-1$
}
void setIsFake(boolean f) {
isFake = f;
}
boolean isFake() {
return isFake;
}
public ICDIVariable[] getChildren() throws CDIException {
// Use the default timeout.
return getChildren(-1);
}
/**
* This can be a potentially long operation for GDB.
* allow the override of the timeout.
*/
public ICDIVariable[] getChildren(int timeout) throws CDIException {
MISession mi = ((Target)getTarget()).getMISession();
CommandFactory factory = mi.getCommandFactory();
MIVarListChildren var = factory.createMIVarListChildren(getMIVar().getVarName());
try {
if (timeout >= 0) {
mi.postCommand(var, timeout);
} else {
mi.postCommand(var);
}
MIVarListChildrenInfo info = var.getMIVarListChildrenInfo();
if (info == null) {
throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$
}
final MIVar[] vars = info.getMIVars();
final List childrenList = new ArrayList(vars.length);
final ICDIType t = getType();
final boolean cpp = isCPPLanguage();
for (int i = 0; i < vars.length; i++) {
String fn = getQualifiedName();
String childName = vars[i].getExp();
boolean childFake = false;
if (cpp && isAccessQualifier(childName)) {
// since access qualifier is keyword this only possible when gdb returns this as fake fields
// so it is pretty safe without to do without any other type checks
childFake = true;
// fn remains unchanged otherwise it would be like x->public
} else if (cpp && childName.equals(vars[i].getType())) {
// it is a base class (which is returned by GDB as a field)
// (type of a child is the name of a child)
String childNameForCast = childName.contains("::") ? "'" + childName + "'" : childName; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
if (t instanceof ICDIPointerType) {
// fn -> casting to pointer base class
fn = "(struct " + childNameForCast + ")(*" + fn + ")";//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} else {
// fn -> casting to base class
fn = "(struct " + childNameForCast + ")" + fn;//$NON-NLS-1$ //$NON-NLS-2$
}
} else if (t instanceof ICDIArrayType) {
// For Array gdb varobj only return the index, override here.
int index = castingIndex + i;
fn = "(" + fn + ")[" + i + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
childName = getName() + "[" + index + "]"; //$NON-NLS-1$ //$NON-NLS-2$
} else if (t instanceof ICDIPointerType) {
ICDIType subType = ((ICDIPointerType) t).getComponentType();
if (subType instanceof ICDIStructType || subType instanceof IncompleteType) {
fn = "(" + fn + ")->" + childName; //$NON-NLS-1$ //$NON-NLS-2$
} else {
fn = "*(" + fn + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
} else if (t instanceof ICDIReferenceType) {
ICDIType subType = ((ICDIReferenceType) t).getComponentType();
if (subType instanceof ICDIStructType || subType instanceof IncompleteType) {
fn = "(" + fn + ")." + childName; //$NON-NLS-1$ //$NON-NLS-2$
} else if (subType instanceof ICDIPointerType) {
fn = "(" + fn + ")->" + childName; //$NON-NLS-1$ //$NON-NLS-2$
} else if (subType instanceof ICDIArrayType) {
int index = castingIndex + i;
fn = "(" + fn + ")[" + index + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
// set this to look pretty
childName = getName() + "[" + index + "]"; //$NON-NLS-1$ //$NON-NLS-2$
} else {
fn = "*(" + fn + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
} else if (t instanceof ICDIStructType || t instanceof IncompleteType) {
if (childName.length()>0) {
fn = "(" + fn + ")." + childName; //$NON-NLS-1$ //$NON-NLS-2$
}
}
Variable v = createVariable((Target)getTarget(), (Thread)getThread(), (StackFrame)getStackFrame(),
childName, fn, getPosition(), getStackDepth(), vars[i]);
if (childFake) {
v.setIsFake(childFake);
// Hack to reset the typename to a known value
v.fType = t;
// don't add these, add their kids
ICDIVariable[] grandchildren = v.getChildren();
for (int j = 0; j < grandchildren.length; ++j)
childrenList.add(grandchildren[j]);
} else
childrenList.add(v);
}
children = (ICDIVariable[])childrenList.toArray(new ICDIVariable[childrenList.size()]);
} catch (MIException e) {
throw new MI2CDIException(e);
}
return children;
}
boolean isAccessQualifier(String foo) {
if (foo==null) return false;
return foo.equals("private") || foo.equals("public") || foo.equals("protected"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
protected abstract Variable createVariable(Target target, Thread thread, StackFrame frame,
String name, String fullName, int pos, int depth, MIVar miVar);
public int getChildrenNumber() throws CDIException {
return getMIVar().getNumChild();
}
/**
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIVariable#getValue()
*/
public ICDIValue getValue() throws CDIException {
if (value == null) {
ICDIType t = getType();
if (t instanceof ICDIBoolType) {
value = new BoolValue(this);
} else if (t instanceof ICDICharType) {
value = new CharValue(this);
} else if (t instanceof ICDIWCharType) {
value = new WCharValue(this);
} else if (t instanceof ICDIShortType) {
value = new ShortValue(this);
} else if (t instanceof ICDIIntType) {
value = new IntValue(this);
} else if (t instanceof ICDILongType) {
value = new LongValue(this);
} else if (t instanceof ICDILongLongType) {
value = new LongLongValue(this);
} else if (t instanceof ICDIEnumType) {
value = new EnumValue(this);
} else if (t instanceof ICDIFloatType) {
value = new FloatValue(this);
} else if (t instanceof ICDIDoubleType) {
value = new DoubleValue(this);
} else if (t instanceof ICDIFunctionType) {
value = new FunctionValue(this);
} else if (t instanceof ICDIPointerType) {
value = new PointerValue(this);
} else if (t instanceof ICDIReferenceType) {
value = new ReferenceValue(this);
} else if (t instanceof ICDIArrayType) {
value = new ArrayValue(this);
} else if (t instanceof ICDIStructType) {
value = new StructValue(this);
} else {
value = new Value(this);
}
}
return value;
}
/**
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIVariable#setValue(ICDIValue)
*/
public void setValue(ICDIValue value) throws CDIException {
setValue(value.getValueString());
}
/**
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIVariable#setValue(String)
*/
public void setValue(String expression) throws CDIException {
Target target = (Target)getTarget();
MISession miSession = target.getMISession();
CommandFactory factory = miSession.getCommandFactory();
MIVarAssign var = factory.createMIVarAssign(getMIVar().getVarName(), expression);
try {
miSession.postCommand(var);
MIInfo info = var.getMIInfo();
if (info == null) {
throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$
}
} catch (MIException e) {
throw new MI2CDIException(e);
}
// If the assign was succesfull fire a MIVarChangedEvent() for the variable
// Note GDB will not fire an event for the changed variable we have to do it manually.
MIVarChangedEvent change = new MIVarChangedEvent(miSession, var.getToken(), getMIVar().getVarName());
miSession.fireEvent(change);
// Changing values may have side effects i.e. affecting other variables
// if the manager is on autoupdate check all the other variables.
// Note: This maybe very costly.
// assigning may have side effects i.e. affecting other registers.
// If register was on autoupdate, update all the other registers
RegisterManager regMgr = ((Session)target.getSession()).getRegisterManager();
if (regMgr.isAutoUpdate()) {
regMgr.update(target);
}
// If expression manager is on autoupdate, update all expressions
ExpressionManager expMgr = ((Session)target.getSession()).getExpressionManager();
if (expMgr.isAutoUpdate()) {
expMgr.update(target);
}
// If variable manager is on autoupdate, update all variables
VariableManager varMgr = ((Session)target.getSession()).getVariableManager();
if (varMgr.isAutoUpdate()) {
varMgr.update(target);
}
// If memory manager is on autoupdate, update all memory blocks
MemoryManager memMgr = ((Session)target.getSession()).getMemoryManager();
if (memMgr.isAutoUpdate()) {
memMgr.update(target);
}
}
/**
* Overload the implementation of VariableDescriptor and let gdb
* handle it.
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIVariable#isEditable()
*/
public boolean isEditable() throws CDIException {
if (editable == null) {
MISession mi = ((Target) getTarget()).getMISession();
CommandFactory factory = mi.getCommandFactory();
MIVarShowAttributes var = factory.createMIVarShowAttributes(getMIVar().getVarName());
try {
mi.postCommand(var);
MIVarShowAttributesInfo info = var.getMIVarShowAttributesInfo();
if (info == null) {
throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$
}
editable = String.valueOf(info.isEditable());
} catch (MIException e) {
throw new MI2CDIException(e);
}
}
return (editable == null) ? false : editable.equalsIgnoreCase("true"); //$NON-NLS-1$
}
/**
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIVariable#setFormat()
*/
public void setFormat(int format) throws CDIException {
int fmt = Format.toMIFormat(format);
MISession mi = ((Target) getTarget()).getMISession();
CommandFactory factory = mi.getCommandFactory();
MIVarSetFormat var = factory.createMIVarSetFormat(getMIVar().getVarName(), fmt);
try {
mi.postCommand(var);
MIInfo info = var.getMIInfo();
if (info == null) {
throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$
}
} catch (MIException e) {
throw new MI2CDIException(e);
}
}
/**
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIVariable#equals()
*/
public boolean equals(ICDIVariable var) {
if (var instanceof Variable) {
Variable variable = (Variable) var;
return equals(variable);
}
return super.equals(var);
}
/**
* @param variable
* @return
*/
public boolean equals(Variable variable) {
try {
return getMIVar().getVarName().equals(variable.getMIVar().getVarName());
} catch (CDIException e) {
// ignore.
}
return super.equals(variable);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIVariable#dispose()
*/
public void dispose() throws CDIException {
ICDITarget target = getTarget();
VariableManager varMgr = ((Session)target.getSession()).getVariableManager();
varMgr.destroyVariable(this);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.core.cdi.model.ICDIVariableDescriptor#getTypeName()
*/
@Override
public String getTypeName() throws CDIException {
if (fTypename == null) {
fTypename = getMIVar().getType();
if (fTypename == null || fTypename.length() == 0) {
MISession mi = ((Target) getTarget()).getMISession();
CommandFactory factory = mi.getCommandFactory();
MIVarInfoType infoType = factory.createMIVarInfoType(getMIVar().getVarName());
try {
mi.postCommand(infoType);
MIVarInfoTypeInfo info = infoType.getMIVarInfoTypeInfo();
if (info == null) {
throw new CDIException(CdiResources.getString("cdi.Common.No_answer")); //$NON-NLS-1$
}
fTypename = info.getType();
} catch (MIException e) {
throw new MI2CDIException(e);
}
}
}
return fTypename;
}
public void setMIVarCreate(MIVarCreate miVar) {
fVarCreateCMD = miVar;
}
abstract protected VariableDescriptor createDescriptor(Target target, Thread thread, StackFrame frame, String n, String fn, int pos, int depth);
}