/*******************************************************************************
 * Copyright (c) 2006, 2016 Wind River Systems and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *     Ericsson 		  - Modified for handling of multiple execution contexts
 *     Axel Mueller       - Bug 306555 - Add support for cast to type / view as array (IExpressions2)
 *     Jens Elmenthaler (Verigy) - Added Full GDB pretty-printing support (bug 302121)
 *     Marc Khouzam (Ericsson) - Added support for expression aliases for return values of functions (bug 341731)
 *     Abeer Bagul (Tensilica) - Extra partition created for arrays of length 20000 or greater (Bug 443687)
 *******************************************************************************/
package org.eclipse.cdt.dsf.mi.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
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.ICachingService;
import org.eclipse.cdt.dsf.debug.service.IExpressions;
import org.eclipse.cdt.dsf.debug.service.IExpressions2;
import org.eclipse.cdt.dsf.debug.service.IExpressions3;
import org.eclipse.cdt.dsf.debug.service.IFormattedValues;
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent;
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext;
import org.eclipse.cdt.dsf.debug.service.IMemorySpaces;
import org.eclipse.cdt.dsf.debug.service.IMemorySpaces.DecodeResult;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExecutionDMContext;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason;
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
import org.eclipse.cdt.dsf.debug.service.command.CommandCache;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
import org.eclipse.cdt.dsf.gdb.GDBTypeParser.GDBType;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceRecordSelectedChangedDMEvent;
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetAttributes;
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildCount;
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetChildren;
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetValue;
import org.eclipse.cdt.dsf.mi.service.command.commands.ExprMetaGetVar;
import org.eclipse.cdt.dsf.mi.service.command.events.IMIDMEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIFunctionFinishedEvent;
import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent;
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetAttributesInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildCountInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetChildrenInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetValueInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.ExprMetaGetVarInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIDataEvaluateExpressionInfo;
import org.eclipse.cdt.dsf.mi.service.command.output.MIFrame;
import org.eclipse.cdt.dsf.service.AbstractDsfService;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.utils.Addr32;
import org.eclipse.cdt.utils.Addr64;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.osgi.framework.BundleContext;

/**
 * This class implements a debugger expression evaluator as a DSF service. The
 * primary interface that clients of this class should use is IExpressions.
 *
 * This class used to be name ExpressionService in the 1.1 release.
 *
 * @since 2.0
 */
public class MIExpressions extends AbstractDsfService implements IMIExpressions, ICachingService {

	private static final int PARTITION_LENGTH = 100;

	/**
	 * A format that gives more details about an expression and supports pretty-printing
	 * provided by the backend.
	 *
	 * @since 3.0
	 */
	public static final String DETAILS_FORMAT = "Details"; //$NON-NLS-1$

	/* The order given here is the order that will be used by DSF in the Details Pane */
	private static final String[] FORMATS_SUPPORTED = new String[] { DETAILS_FORMAT, IFormattedValues.NATURAL_FORMAT,
			IFormattedValues.DECIMAL_FORMAT, IFormattedValues.HEX_FORMAT, IFormattedValues.BINARY_FORMAT,
			IFormattedValues.OCTAL_FORMAT };

	/**
	 * This class represents the two expressions that characterize an Expression Context.
	 */
	public static class ExpressionInfo {
		private final String fullExpression;
		private final String relativeExpression;
		private boolean isDynamic = false;
		private ExpressionInfo parent;
		private int indexInParent = -1;
		private int childCountLimit = IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED;

		public ExpressionInfo(String full, String relative) {
			fullExpression = full;
			relativeExpression = relative;
		}

		/**
		 * @since 4.0
		 */
		public ExpressionInfo(String full, String relative, boolean isDynamic, ExpressionInfo parent,
				int indexInParent) {
			fullExpression = full;
			relativeExpression = relative;
			this.isDynamic = isDynamic;
			this.parent = parent;
			this.indexInParent = indexInParent;
		}

		public String getFullExpr() {
			return fullExpression;
		}

		public String getRelExpr() {
			return relativeExpression;
		}

		@Override
		public boolean equals(Object other) {
			if (other instanceof ExpressionInfo) {
				if (fullExpression == null ? ((ExpressionInfo) other).fullExpression == null
						: fullExpression.equals(((ExpressionInfo) other).fullExpression)) {
					if (relativeExpression == null ? ((ExpressionInfo) other).relativeExpression == null
							: relativeExpression.equals(((ExpressionInfo) other).relativeExpression)) {
						// The other members don't play any role for equality.
						return true;
					}
				}
			}
			return false;
		}

		@Override
		public int hashCode() {
			return (fullExpression == null ? 0 : fullExpression.hashCode())
					^ (relativeExpression == null ? 0 : relativeExpression.hashCode());
			// The other members don't play any role for equality.
		}

		@Override
		public String toString() {
			return "[" + fullExpression + ", " + relativeExpression + ", isDynamic=" + isDynamic + "]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$
		}

		/**
		 * @return The parent expression info, if existing.
		 * @since 4.0
		 */
		public ExpressionInfo getParent() {
			return parent;
		}

		/**
		 * @return The index in the child array of the parent. Only valid if
		 *         {@link #getParent()} returns not null.
		 * @since 4.0
		 */
		public int getIndexInParentExpression() {
			return indexInParent;
		}

		/**
		 * @return Whether the corresponding variable object is dynamic,
		 *         i.e. it's value and children are provided by a pretty printer.
		 * @since 4.0
		 */
		public boolean isDynamic() {
			return isDynamic;
		}

		/**
		 * @return Whether the expression info has any ancestor that is dynamic.
		 * @since 4.0
		 */
		public boolean hasDynamicAncestor() {
			for (ExpressionInfo parent = getParent(); parent != null; parent = parent.getParent()) {
				if (parent.isDynamic()) {
					return true;
				}
			}

			return false;
		}

		/**
		 * @param isDynamic
		 *            Whether the value and children of this expression is
		 *            currently provided by a pretty printer or not.
		 * @since 4.0
		 */
		public void setDynamic(boolean isDynamic) {
			this.isDynamic = isDynamic;
		}

		/**
		 * @param parent The new parent expression info.
		 * @since 4.0
		 */
		public void setParent(ExpressionInfo parent) {
			this.parent = parent;
		}

		/**
		 * @param index The index in the children array of the parent.
		 * @since 4.0
		 */
		public void setIndexInParent(int index) {
			this.indexInParent = index;
		}

		/**
		 * @return The current limit on the number of children to be fetched.
		 * @since 4.0
		 */
		public int getChildCountLimit() {
			return childCountLimit;
		}

		/**
		 * @param newLimit
		 *            The new limit on the number of children to be fetched.
		 * @since 4.0
		 */
		public void setChildCountLimit(int newLimit) {
			this.childCountLimit = newLimit;
		}

		/**
		 * @return if this expression is part of the memory space or not.
		 *         If it not part of the memory space, it won't have an address.
		 * @since 4.3
		 */
		public boolean inMemory() {
			// Registers and convenience variables which both start with $
			// are not part of memory.  We care about the top-most parent
			// as it is the only one that can be a register or convenience var.
			if (getParent() == null) {
				if (getRelExpr().startsWith("$")) { //$NON-NLS-1$
					return false;
				}
				return true;
			}

			return getParent().inMemory();
		}
	}

	/**
	 * This class represents an expression.
	 * @noextend This class is not intended to be subclassed by clients.
	 * @since 4.3
	 */
	public static class MIExpressionDMC extends AbstractDMContext implements IExpressionDMContext {
		/**
		 * This field holds an expression to be evaluated.
		 */
		private ExpressionInfo exprInfo;

		/**
		 * ExpressionDMC Constructor for expression to be evaluated in context of
		 * a stack frame.
		 *
		 * @param sessionId
		 *            The session ID in which this context is created.
		 * @param expression
		 *            The expression to be described by this ExpressionDMC
		 * @param relExpr
		 *            The relative expression if this expression was created as a child
		 * @param frameCtx
		 *            The parent stack frame context for this ExpressionDMC.
		 */
		public MIExpressionDMC(String sessionId, String expression, String relExpr, IFrameDMContext frameCtx) {
			this(sessionId, expression, relExpr, (IDMContext) frameCtx);
		}

		/**
		 * ExpressionDMC Constructor for expression to be evaluated in context of
		 * an thread.
		 *
		 * @param sessionId
		 *            The session ID in which this context is created.
		 * @param expression
		 *            The expression to be described by this ExpressionDMC
		 * @param relExpr
		 *            The relative expression if this expression was created as a child
		 * @param execCtx
		 *            The parent thread context for this ExpressionDMC.
		 */
		public MIExpressionDMC(String sessionId, String expression, String relExpr, IMIExecutionDMContext execCtx) {
			this(sessionId, expression, relExpr, (IDMContext) execCtx);
		}

		/**
		 * ExpressionDMC Constructor for expression to be evaluated in context of
		 * a memory space.
		 *
		 * @param sessionId
		 *            The session ID in which this context is created.
		 * @param expression
		 *            The expression to be described by this ExpressionDMC
		 * @param relExpr
		 *            The relative expression if this expression was created as a child
		 * @param memoryCtx
		 *            The parent memory space context for this ExpressionDMC.
		 */
		public MIExpressionDMC(String sessionId, String expression, String relExpr, IMemoryDMContext memoryCtx) {
			this(sessionId, expression, relExpr, (IDMContext) memoryCtx);
		}

		private MIExpressionDMC(String sessionId, String expr, String relExpr, IDMContext parent) {
			this(sessionId, new ExpressionInfo(expr, relExpr), parent);
		}

		/**
		 * ExpressionDMC Constructor for expression to be evaluated in context
		 * of a stack frame.
		 *
		 * @param sessionId
		 *            The session ID in which this context is created.
		 * @param info
		 *            The expression info that this expression is to use.
		 * @param frameCtx
		 *            The parent stack frame context for this ExpressionDMC.
		 *
		 * @since 4.0
		 */
		public MIExpressionDMC(String sessionId, ExpressionInfo info, IFrameDMContext frameCtx) {
			this(sessionId, info, (IDMContext) frameCtx);
		}

		/**
		 * @since 4.3
		 */
		public MIExpressionDMC(String sessionId, ExpressionInfo info, IDMContext parent) {
			super(sessionId, new IDMContext[] { parent });
			exprInfo = info;
		}

		/**
		 * @return True if the two objects are equal, false otherwise.
		 */
		@Override
		public boolean equals(Object other) {
			return super.baseEquals(other) && exprInfo.equals(((MIExpressionDMC) other).exprInfo);
		}

		/**
		 *
		 * @return The hash code of this ExpressionDMC object.
		 */
		@Override
		public int hashCode() {
			return super.baseHashCode() + exprInfo.hashCode();
		}

		/**
		 *
		 * @return A string representation of this ExpressionDMC (including the
		 *         expression to which it is bound).
		 */
		@Override
		public String toString() {
			return baseToString() + ".expr" + exprInfo.toString(); //$NON-NLS-1$
		}

		/**
		 * @return The full expression string represented by this ExpressionDMC
		 */
		@Override
		public String getExpression() {
			return exprInfo.getFullExpr();
		}

		/**
		 * @return The relative expression string represented by this ExpressionDMC
		 */
		public String getRelativeExpression() {
			return exprInfo.getRelExpr();
		}

		/**
		 * @return Get the expression info for this context.
		 * @since 4.0
		 */
		public ExpressionInfo getExpressionInfo() {
			return exprInfo;
		}

		/**
		 * @param info
		 *
		 * @since 4.0
		 */
		public void setExpressionInfo(ExpressionInfo info) {
			assert (this.exprInfo.getFullExpr().equals(info.getFullExpr()));
			assert (this.exprInfo.getRelExpr().equals(info.getRelExpr()));

			this.exprInfo = info;
		}
	}

	protected static class InvalidContextExpressionDMC extends AbstractDMContext implements IExpressionDMContext {
		private final String expression;

		public InvalidContextExpressionDMC(String sessionId, String expr, IDMContext parent) {
			super(sessionId, new IDMContext[] { parent });
			expression = expr;
		}

		@Override
		public boolean equals(Object other) {
			return super.baseEquals(other)
					&& (expression == null ? ((InvalidContextExpressionDMC) other).getExpression() == null
							: expression.equals(((InvalidContextExpressionDMC) other).getExpression()));
		}

		@Override
		public int hashCode() {
			return expression == null ? super.baseHashCode() : super.baseHashCode() ^ expression.hashCode();
		}

		@Override
		public String toString() {
			return baseToString() + ".invalid_expr[" + expression + "]"; //$NON-NLS-1$ //$NON-NLS-2$
		}

		@Override
		public String getExpression() {
			return expression;
		}
	}

	/**
	 * @since 4.1
	 */
	protected static class IndexedPartitionDMC extends MIExpressionDMC implements IIndexedPartitionDMContext {

		final private MIExpressionDMC fParentExpression;
		private final int fIndex;
		private final int fLength;

		/**
		 * @deprecated This method does not keep track of casted expressions.
		 * It has been replaced by the constructor that takes an MIExpressionDMC
		 * as a parameter.
		 */
		@Deprecated
		public IndexedPartitionDMC(String sessionId, ExpressionInfo parentInfo, IFrameDMContext frameCtx, int index,
				int length) {
			this(new MIExpressionDMC(sessionId, parentInfo, frameCtx), frameCtx, index, length);
		}

		/**
		 * @since 4.2
		 */
		public IndexedPartitionDMC(MIExpressionDMC parentExpr, int index, int length) {
			this(parentExpr, getParentDmc(parentExpr), index, length);
		}

		/**
		 * @param parentExpr The expression of the array.  This can be a casted expression.
		 *                   This is not the parent that will be used in the context hierarchy, as we chose
		 *                   not to stack up partitions.
		 * @param parentDmc The frame or thread context that will be used as a parent in the context hierarchy.
		 */
		private IndexedPartitionDMC(MIExpressionDMC parentExpr, IDMContext parentDmc, int index, int length) {
			super(parentExpr.getSessionId(), createExpressionInfo(parentExpr.getExpressionInfo(), index, length),
					parentDmc);
			fIndex = index;
			fLength = length;
			fParentExpression = parentExpr;
		}

		/**
		 * Find the frame context that will be the parent of this partition in the context hierarchy.
		 * Not to be confused with the original parent array that contains the partition.  That parent
		 * can be obtained using getParentExpressionContext()
		 */
		private static IDMContext getParentDmc(MIExpressionDMC parentExpr) {
			IFrameDMContext frameDmc = DMContexts.getAncestorOfType(parentExpr, IFrameDMContext.class);
			if (frameDmc != null) {
				return frameDmc;
			}

			IMIExecutionDMContext execCtx = DMContexts.getAncestorOfType(parentExpr, IMIExecutionDMContext.class);
			if (execCtx != null) {
				// If we have a thread context but not a frame context, we give the user
				// the expression as per the top-most frame of the specified thread.
				// To do this, we create our own frame context.
				DsfServicesTracker tracker = new DsfServicesTracker(GdbPlugin.getBundleContext(),
						parentExpr.getSessionId());
				MIStack stackService = tracker.getService(MIStack.class);
				tracker.dispose();

				if (stackService != null) {
					return stackService.createFrameDMContext(execCtx, 0);
				}
			}

			return parentExpr;
		}

		public ExpressionInfo getParentInfo() {
			return fParentExpression.getExpressionInfo();
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.dsf.debug.service.IExpressions4.IIndexedPartitionDMContext#getParentExpression()
		 */
		@Override
		public String getParentExpression() {
			return getParentExpressionContext().getExpression();
		}

		/**
		 * Get the context of the parent array.  This can be used to know if the
		 * parent array is a casted expression.
		 * @since 4.2
		 */
		public MIExpressionDMC getParentExpressionContext() {
			return fParentExpression;
		}

		@Override
		public int getIndex() {
			return fIndex;
		}

		@Override
		public int getLength() {
			return fLength;
		}

		@Override
		public boolean equals(Object other) {
			return super.baseEquals(other)
					&& ((IndexedPartitionDMC) other).getParentExpressionContext().equals(getParentExpressionContext())
					&& ((IndexedPartitionDMC) other).getIndex() == getIndex()
					&& ((IndexedPartitionDMC) other).getLength() == getLength();
		}

		@Override
		public int hashCode() {
			return super.baseHashCode() + 17 * getIndex() + 31 * getLength();
		}

		@Override
		public String toString() {
			return String.format("%s.expr[%s][%d-%d]", baseToString(), getParentExpression(), getIndex(), //$NON-NLS-1$
					getIndex() + getLength() - 1);
		}

		private static ExpressionInfo createExpressionInfo(ExpressionInfo parentInfo, int index, int length) {
			String expression = String.format("*((%s)+%d)@%d", //$NON-NLS-1$
					parentInfo.getFullExpr(), Integer.valueOf(index), Integer.valueOf(length));
			return new ExpressionInfo(expression, expression);
		}
	}

	/**
	 * Contains the address of an expression as well as the size of its type.
	 */
	protected static class ExpressionDMAddress implements IExpressionDMAddress {
		IAddress fAddr;
		int fSize;
		String fMemSpace = ""; //$NON-NLS-1$

		public ExpressionDMAddress(IAddress addr, int size) {
			fAddr = addr;
			fSize = size;
		}

		public ExpressionDMAddress(String addrStr, int size) {
			fSize = size;
			// We must count the "0x" and that
			// is why we compare with 10 characters
			// instead of 8
			if (addrStr.length() <= 10) {
				fAddr = new Addr32(addrStr);
			} else {
				fAddr = new Addr64(addrStr);
			}
		}

		/**
		 * @since 5.0
		 */
		public ExpressionDMAddress(String addrStr, int size, String memSpace) {
			this(addrStr, size);
			fMemSpace = memSpace;
		}

		@Override
		public IAddress getAddress() {
			return fAddr;
		}

		@Override
		public int getSize() {
			return fSize;
		}

		/**
		 * @since 5.0
		 */
		@Override
		public String getMemorySpaceID() {
			return fMemSpace;
		}

		@Override
		public boolean equals(Object other) {
			if (other instanceof ExpressionDMAddress) {
				ExpressionDMAddress otherAddr = (ExpressionDMAddress) other;
				boolean sameAddr = fAddr == null ? otherAddr.getAddress() == null
						: fAddr.equals(otherAddr.getAddress());
				boolean sameMemSpace = fMemSpace == null ? otherAddr.getMemorySpaceID() == null
						: fMemSpace.equals(otherAddr.getMemorySpaceID());
				return (fSize == otherAddr.getSize()) && sameAddr && sameMemSpace;
			}
			return false;
		}

		@Override
		public int hashCode() {
			return (fAddr == null ? 0 : fAddr.hashCode()) + fSize;
		}

		@Override
		public String toString() {
			return (fAddr == null ? "null" : "(" + fAddr.toHexAddressString()) + ", " + fSize + ")"; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$
		}
	}

	/**
	 * If an expressions doesn't have an address, or it cannot be determined,
	 * use this class.
	 * @since 4.0
	 */
	protected class InvalidDMAddress implements IExpressionDMLocation {

		@Override
		public IAddress getAddress() {
			return IExpressions.IExpressionDMLocation.INVALID_ADDRESS;
		}

		@Override
		public int getSize() {
			return 0;
		}

		@Override
		public String getLocation() {
			return ""; //$NON-NLS-1$
		}
	}

	/**
	 * This class represents the static data referenced by an instance of ExpressionDMC,
	 * such as its type and number of children; it does not contain the value or format
	 * of the expression.
	 */
	protected static class ExpressionDMData implements IExpressionDMDataExtension {
		// This is the relative expression, such as the name of a field within a structure,
		// in contrast to the fully-qualified expression contained in the ExpressionDMC,
		// which refers to the full name, including parent structure.
		private final String relativeExpression;
		private final String exprType;

		/**
		 * A hint at the number of children.
		 * In the case of C++ complex structures, this number will not be the
		 * actual number of children.  This is because GDB considers
		 * 'private/protected/public' as an actual level of children, but
		 * we do not.  This number is meant to be used to know if the expression
		 * has children at all.
		 */
		private final int numChildrenHint;

		private final boolean editable;
		private final BasicType fBasicType;

		/**
		 * ExpressionDMData constructor.
		 */
		public ExpressionDMData(String expr, String type, int num, boolean edit) {
			this(expr, type, num, edit, null);
		}

		/**
		 * ExpressionDMData constructor.
		 * @since 3.0
		 */
		public ExpressionDMData(String expr, String type, int num, boolean edit, BasicType basicType) {
			relativeExpression = expr;
			exprType = type;
			numChildrenHint = num;
			editable = edit;
			fBasicType = basicType;
		}

		@Override
		public BasicType getBasicType() {
			return fBasicType;
		}

		@Override
		public String getEncoding() {
			return null;
		}

		@Override
		public Map<String, Integer> getEnumerations() {
			return new HashMap<>();
		}

		@Override
		public String getName() {
			return relativeExpression;
		}

		@Override
		public IRegisterDMContext getRegister() {
			return null;
		}

		// See class VariableVMNode for an example of usage of this method
		public String getStringValue() {
			return null;
		}

		@Override
		public String getTypeId() {
			return null;
		}

		@Override
		public String getTypeName() {
			return exprType;
		}

		/**
		 * This method only returns a 'hint' to the number of children.
		 * In the case of C++ complex structures, this number will not be the
		 * actual number of children.  This is because GDB considers
		 * 'private/protected/public' as an actual level of children, but
		 * we do not.
		 *
		 * This method can be used reliably to know if the expression
		 * does have children or not.  However, for this particular use,
		 * the new {@link IExpressionDMDataExtension#hasChildren()} method should be used instead.
		 *
		 * To get the correct number of children of an expression, a call
		 * to {@link IExpressions#getSubExpressionCount} should be used.
		 *
		 * @deprecated
		 */
		@Deprecated
		public int getNumChildren() {
			return numChildrenHint;
		}

		public boolean isEditable() {
			return editable;
		}

		/**
		 * @since 4.0
		 */
		@Override
		public boolean hasChildren() {
			return numChildrenHint > 0;
		}

		@Override
		public boolean equals(Object other) {
			if (other instanceof ExpressionDMData) {
				ExpressionDMData otherData = (ExpressionDMData) other;
				return (numChildrenHint == otherData.numChildrenHint)
						&& (getTypeName() == null ? otherData.getTypeName() == null
								: getTypeName().equals(otherData.getTypeName()))
						&& (getName() == null ? otherData.getName() == null : getName().equals(otherData.getName()));
			}
			return false;
		}

		@Override
		public int hashCode() {
			return relativeExpression == null ? 0
					: relativeExpression.hashCode() + exprType == null ? 0 : exprType.hashCode() + numChildrenHint;
		}

		@Override
		public String toString() {
			return "relExpr=" + relativeExpression + ", type=" + exprType + ", numchildren=" + numChildrenHint; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$
		}
	}

	/**
	 * Event generated every time an expression is changed by the ExpressionService.
	 *
	 * A client wishing to receive such events has to register as a service
	 * event listener and implement the corresponding eventDispatched method.
	 *
	 * E.g.:
	 *
	 *    getSession().addServiceEventListener(listenerObject, null);
	 *
	 *    @DsfServiceEventHandler
	 *    public void eventDispatched(ExpressionChangedEvent e) {
	 *       IExpressionDMContext context = e.getDMContext();
	 *       // do something...
	 *    }
	 */
	protected static class ExpressionChangedEvent extends AbstractDMEvent<IExpressionDMContext>
			implements IExpressionChangedDMEvent {

		public ExpressionChangedEvent(IExpressionDMContext context) {
			super(context);
		}
	}

	/**
	 * Keeps track of aliases for return values of methods.
	 */
	private class ReturnValueAliasing {
		/**
		 *  Map of expression to alias.  The expression is the name of the convenience variable
		 *  storing the return value, e.g., $1 -> "foo() returned"
		 *  This map allows to quickly find the alias to be used for return value variables.
		 */
		private Map<String, String> fExpressionAliasesMap = new HashMap<>();
		/**
		 * Map of thread to aliases expression list.  This map allows to know which aliases are related
		 * to a thread of execution.  This is important to allow us to delete aliases when a
		 * thread exits.  Note that we need a list because we keep all previous aliases until
		 * the thread exits.
		 */
		private Map<IMIExecutionDMContext, List<String>> fThreadToAliasedExpressionsMap = new HashMap<>();
		/**
		 * Map of thread to the name of the method the thread last stopped in.
		 * This allows us to create the alias based on the method the thread was in
		 * before it returned out of the method.
		 */
		private Map<IMIExecutionDMContext, String> fThreadToTopMethodName = new HashMap<>();

		/**
		 * Create an alias for expr with respect to threadDmc.
		 * The alias is created based on where threadDmc was previously stopped.
		 */
		public void createAlias(IMIExecutionDMContext threadDmc, String expr) {
			String alias = expr;
			String methodName = fThreadToTopMethodName.get(threadDmc);
			if (methodName != null) {
				alias = String.format(Messages.MIExpressions_ReturnValueAlias, methodName + "()"); //$NON-NLS-1$
			}

			fExpressionAliasesMap.put(expr, alias);

			List<String> aliasedExprList = fThreadToAliasedExpressionsMap.get(threadDmc);
			if (aliasedExprList == null) {
				aliasedExprList = new ArrayList<>();
				fThreadToAliasedExpressionsMap.put(threadDmc, aliasedExprList);
			}
			aliasedExprList.add(expr);
		}

		/**
		 * Clear all information related to a particular thread of execution.
		 */
		public void clearThread(IMIExecutionDMContext threadDmc) {
			fThreadToTopMethodName.remove(threadDmc);
			clearAliases(threadDmc);
		}

		/**
		 * Clear all aliased expressions related to a particular thread of execution.
		 * It is good to keep the aliases around as long as the thread is alive;
		 * even if we won't show the return value automatically, the user
		 * could add the expression in the expression view, and the alias
		 * would then be used.
		 */
		public void clearAliases(IMIExecutionDMContext threadDmc) {
			List<String> aliasedExprList = fThreadToAliasedExpressionsMap.remove(threadDmc);
			if (aliasedExprList != null) {
				for (String expr : aliasedExprList) {
					fExpressionAliasesMap.remove(expr);
				}
			}
		}

		/**
		 * Update the method name of the last location where threadDmc was stopped.
		 */
		public void updateStoppedLocation(IMIExecutionDMContext threadDmc, String methodName) {
			fThreadToTopMethodName.put(threadDmc, methodName);
		}

		/**
		 * @return The alias for 'expr' if there is one.  null if there
		 *         is no alias for that expression.
		 */
		public String getAlias(String expr) {
			String alias = fExpressionAliasesMap.get(expr);
			if (alias == null) {
				// Check if the expression contains the string that must be aliased.
				// E.g., $1[0], *$2
				// If it does, just replace that string within the expression to
				// create the full alias
				for (Entry<String, String> entry : fExpressionAliasesMap.entrySet()) {
					int index = expr.indexOf(entry.getKey());
					if (index != -1) {
						// Found the string! Now replace it with our alias.
						// We put it between () to make things clearer to the user.
						// Note that there can only be one string contained
						// in the expression, so once we found it, we are done.
						alias = expr.substring(0, index) + "(" + entry.getValue() + ")" + //$NON-NLS-1$ //$NON-NLS-2$
								expr.substring(index + entry.getKey().length());
						break;
					}
				}
			}
			return alias;
		}
	}

	/** Structure to keep track of aliases for method return values. */
	private ReturnValueAliasing fReturnValueAliases = new ReturnValueAliasing();

	/**
	 * @since 4.3
	 */
	protected CommandCache fExpressionCache;

	private CommandFactory fCommandFactory;
	private MIVariableManager varManager;

	/**
	 * Indicates that we are currently visualizing trace data.
	 * In this case, some errors should not be reported.
	 */
	private boolean fTraceVisualization;
	private IMemorySpaces fMemorySpaceService;

	public MIExpressions(DsfSession session) {
		super(session);
	}

	/**
	 * This method initializes this service.
	 *
	 * @param requestMonitor
	 *            The request monitor indicating the operation is finished
	 */
	@Override
	public void initialize(final RequestMonitor requestMonitor) {
		super.initialize(new ImmediateRequestMonitor(requestMonitor) {
			@Override
			protected void handleSuccess() {
				doInitialize(requestMonitor);
			}
		});
	}

	/**
	 * This method initializes this service after our superclass's initialize()
	 * method succeeds.
	 *
	 * @param requestMonitor
	 *            The call-back object to notify when this service's
	 *            initialization is done.
	 */
	private void doInitialize(RequestMonitor requestMonitor) {

		// Register to receive service events for this session.
		getSession().addServiceEventListener(this, null);

		// Register this service, but only if we don't already have an
		// IExpression service present.  This allows another expression
		// service to be used, while delegating calls to this service.
		if (getServicesTracker().getService(IExpressions.class) == null) {
			register(new String[] { IExpressions.class.getName(), IExpressions2.class.getName(),
					IExpressions3.class.getName(), IMIExpressions.class.getName(), MIExpressions.class.getName() },
					new Hashtable<String, String>());
		}

		// Create the expressionService-specific CommandControl which is our
		// variable object manager.
		// It will deal with the meta-commands, before sending real MI commands
		// to the back-end, through the MICommandControl service
		// It must be created after the ExpressionService is registered
		// since it will need to find it.
		varManager = createMIVariableManager();

		// Create the meta command cache which will use the variable manager
		// to actually send MI commands to the back-end
		fExpressionCache = new CommandCache(getSession(), varManager);
		ICommandControlService commandControl = getServicesTracker().getService(ICommandControlService.class);
		fExpressionCache.setContextAvailable(commandControl.getContext(), true);

		fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory();

		fMemorySpaceService = getServicesTracker().getService(IMemorySpaces.class);

		requestMonitor.done();
	}

	/**
	 * Creates the MI variable manager to be used by this expression service.
	 * Overriding classes may override to provide a custom services tracker.
	 *
	 * @since 3.0
	 */
	protected MIVariableManager createMIVariableManager() {
		return new MIVariableManager(getSession(), getServicesTracker());
	}

	/**
	 * This method shuts down this service. It unregisters the service, stops
	 * receiving service events, and calls the superclass shutdown() method to
	 * finish the shutdown process.
	 */
	@Override
	public void shutdown(RequestMonitor requestMonitor) {
		unregister();
		varManager.dispose();
		getSession().removeServiceEventListener(this);
		super.shutdown(requestMonitor);
	}

	/**
	 * @return The bundle context of the plug-in to which this service belongs.
	 */
	@Override
	protected BundleContext getBundleContext() {
		return GdbPlugin.getBundleContext();
	}

	/**
	 * Create an expression context with the same full and relative expression
	 */
	@Override
	public IExpressionDMContext createExpression(IDMContext ctx, String expression) {
		return createExpression(ctx, expression, expression);
	}

	/**
	 * Create an expression context.
	 */
	public IExpressionDMContext createExpression(IDMContext ctx, String expression, String relExpr) {
		return createExpression(ctx, new ExpressionInfo(expression, relExpr));
	}

	/**
	 * Create an expression context from a given expression info.
	 * @since 4.0
	 */
	private IExpressionDMContext createExpression(IDMContext ctx, ExpressionInfo info) {
		String expression = info.getFullExpr();
		IFrameDMContext frameDmc = DMContexts.getAncestorOfType(ctx, IFrameDMContext.class);
		if (frameDmc != null) {
			return new MIExpressionDMC(getSession().getId(), info, frameDmc);
		}

		IMIExecutionDMContext execCtx = DMContexts.getAncestorOfType(ctx, IMIExecutionDMContext.class);
		if (execCtx != null) {
			// If we have a thread context but not a frame context, we give the user
			// the expression as per the top-most frame of the specified thread.
			// To do this, we create our own frame context.
			MIStack stackService = getServicesTracker().getService(MIStack.class);
			if (stackService != null) {
				frameDmc = stackService.createFrameDMContext(execCtx, 0);
				return new MIExpressionDMC(getSession().getId(), info, frameDmc);
			}

			return new InvalidContextExpressionDMC(getSession().getId(), expression, execCtx);
		}

		IMemoryDMContext memoryCtx = DMContexts.getAncestorOfType(ctx, IMemoryDMContext.class);
		if (memoryCtx != null) {
			return new MIExpressionDMC(getSession().getId(), info, memoryCtx);
		}

		// Don't care about the relative expression at this point
		return new InvalidContextExpressionDMC(getSession().getId(), expression, ctx);
	}

	/**
	 * @see IFormattedValues.getFormattedValueContext(IFormattedDataDMContext, String)
	 *
	 * @param dmc
	 *            The context describing the data for which we want to create
	 *            a Formatted context.
	 * @param formatId
	 *            The format that will be used to create the Formatted context
	 *
	 * @return A FormattedValueDMContext that can be used to obtain the value
	 *         of an expression in a specific format.
	 */

	@Override
	public FormattedValueDMContext getFormattedValueContext(IFormattedDataDMContext dmc, String formatId) {
		return new FormattedValueDMContext(this, dmc, formatId);
	}

	/**
	 * @see IFormattedValues.getAvailableFormats(IFormattedDataDMContext, DataRequestMonitor)
	 *
	 * @param dmc
	 *            The context describing the data for which we want to know
	 *            which formats are available.
	 * @param rm
	 *            The data request monitor for this asynchronous operation.
	 *
	 */

	@Override
	public void getAvailableFormats(IFormattedDataDMContext dmc, final DataRequestMonitor<String[]> rm) {
		rm.setData(FORMATS_SUPPORTED);
		rm.done();
	}

	/**
	 * Obtains the static data of an expression represented
	 * by an ExpressionDMC object (<tt>dmc</tt>).
	 *
	 * @param dmc
	 *            The ExpressionDMC for the expression to be evaluated.
	 * @param rm
	 *            The data request monitor that will contain the requested data
	 */
	@Override
	public void getExpressionData(final IExpressionDMContext dmc, final DataRequestMonitor<IExpressionDMData> rm) {
		if (dmc instanceof MIExpressionDMC) {
			fExpressionCache.execute(new ExprMetaGetVar(dmc),
					new DataRequestMonitor<ExprMetaGetVarInfo>(getExecutor(), rm) {

						@Override
						protected void handleSuccess() {
							IExpressionDMData.BasicType basicType = getBasicType(getData());

							String relativeExpr = getData().getExpr();
							String alias = fReturnValueAliases.getAlias(relativeExpr);
							if (alias != null) {
								relativeExpr = alias;
							}
							rm.setData(new ExpressionDMData(relativeExpr, getData().getType(),
									getData().getNumChildren(), getData().getEditable(), basicType));
							rm.done();
						}
					});
		} else if (dmc instanceof InvalidContextExpressionDMC) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
					"Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
			rm.done();
		} else {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", //$NON-NLS-1$
					null));
			rm.done();
		}
	}

	/**
	 * @since 4.7
	 */
	protected IExpressionDMData.BasicType getBasicType(ExprMetaGetVarInfo varInfo) {
		IExpressionDMData.BasicType basicType = null;

		GDBType gdbType = varInfo.getGDBType();

		if (gdbType != null) {
			switch (gdbType.getType()) {
			case GDBType.ARRAY:
				basicType = IExpressionDMData.BasicType.array;
				break;
			case GDBType.FUNCTION:
				basicType = IExpressionDMData.BasicType.function;
				break;
			case GDBType.POINTER:
			case GDBType.REFERENCE:
				basicType = IExpressionDMData.BasicType.pointer;
				break;
			case GDBType.GENERIC:
			default:
				// The interesting question is not hasChildren,
				// but canHaveChildren. E.g. an empty
				// collection still is a composite.
				if (varInfo.hasChildren() || varInfo.getCollectionHint()) {
					basicType = IExpressionDMData.BasicType.composite;
				} else {
					basicType = IExpressionDMData.BasicType.basic;
				}
				break;
			}
		}
		return basicType;
	}

	/**
	 * Obtains the address of an expression and the size of its type.
	 *
	 * @param dmc
	 *            The ExpressionDMC for the expression.
	 * @param rm
	 *            The data request monitor that will contain the requested data
	 */
	@Override
	public void getExpressionAddressData(final IExpressionDMContext dmc,
			final DataRequestMonitor<IExpressionDMAddress> rm) {

		if (dmc instanceof MIExpressionDMC) {
			MIExpressionDMC miDMC = (MIExpressionDMC) dmc;
			if (miDMC.getExpressionInfo().hasDynamicAncestor() || !miDMC.getExpressionInfo().inMemory()) {
				// For children of dynamic varobjs, there is no full expression that gdb
				// could evaluate in order to provide address and size.
				// Also, if an expression is not in memory, such as a register
				// or a GDB convenience variable, there is no address to return
				rm.setData(new InvalidDMAddress());
				rm.done();
				return;
			}
		}

		// First create an address expression and a size expression
		// to be used in back-end calls
		final IExpressionDMContext addressDmc = createExpression(dmc, "&(" + dmc.getExpression() + ")");//$NON-NLS-1$//$NON-NLS-2$
		final IExpressionDMContext sizeDmc = createExpression(dmc, "sizeof(" + dmc.getExpression() + ")"); //$NON-NLS-1$//$NON-NLS-2$

		if (addressDmc instanceof InvalidContextExpressionDMC || sizeDmc instanceof InvalidContextExpressionDMC) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
					"Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
			rm.done();
		} else {
			fExpressionCache.execute(fCommandFactory.createMIDataEvaluateExpression(addressDmc),
					new DataRequestMonitor<MIDataEvaluateExpressionInfo>(getExecutor(), rm) {
						@Override
						protected void handleSuccess() {
							String tmpAddrStr = getData().getValue();

							DecodeResult memSpaceParsed = null;
							if (fMemorySpaceService != null) {
								try {
									memSpaceParsed = fMemorySpaceService.decodeAddress(tmpAddrStr);
								} catch (CoreException e1) {
									// No memory space id found
								}
							}

							String tMemSpace = ""; //$NON-NLS-1$
							if (memSpaceParsed != null) {
								tmpAddrStr = memSpaceParsed.getExpression();
								tMemSpace = memSpaceParsed.getMemorySpaceId();
							}

							final String memSpaceId = tMemSpace;

							// Deal with addresses of contents of a char* which is in
							// the form of "0x12345678 \"This is a string\""
							int split = tmpAddrStr.indexOf(' ');
							if (split != -1)
								tmpAddrStr = tmpAddrStr.substring(0, split);
							final String addrStr = tmpAddrStr;

							fExpressionCache.execute(fCommandFactory.createMIDataEvaluateExpression(sizeDmc),
									new DataRequestMonitor<MIDataEvaluateExpressionInfo>(getExecutor(), rm) {
										@Override
										protected void handleSuccess() {
											try {
												int size = Integer.parseInt(getData().getValue());
												rm.setData(new ExpressionDMAddress(addrStr, size, memSpaceId));
											} catch (NumberFormatException e) {
												rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID,
														INVALID_HANDLE, "Unexpected size format from backend: " //$NON-NLS-1$
																+ getData().getValue(),
														null));
											}
											rm.done();
										}
									});
						}
					});
		}
	}

	/**
	 * Obtains the value of an expression in a specific format.
	 *
	 * @param dmc
	 *            The context for the format of the value requested and
	 *            for the expression to be evaluated.  The expression context
	 *            should be a parent of the FormattedValueDMContext.
	 * @param rm
	 *            The data request monitor that will contain the requested data
	 */
	@Override
	public void getFormattedExpressionValue(final FormattedValueDMContext dmc,
			final DataRequestMonitor<FormattedValueDMData> rm) {
		// We need to make sure the FormattedValueDMContext also holds an ExpressionContext,
		// or else this method cannot do its work.
		// Note that we look for MIExpressionDMC and not IExpressionDMC, because
		// looking for IExpressionDMC could yield InvalidContextExpressionDMC which is still
		// not what we need.
		MIExpressionDMC exprDmc = DMContexts.getAncestorOfType(dmc, MIExpressionDMC.class);
		if (exprDmc == null) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
					"Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
			rm.done();
		} else {
			if (DETAILS_FORMAT.equals(dmc.getFormatID())) {
				if (exprDmc.getExpressionInfo().hasDynamicAncestor()) {
					// -data-evaluate-expression does not work for children of
					// dynamic varobjs, since there is no full expression
					// that gdb could evaluate.
					rm.setData(
							new FormattedValueDMData(Messages.MIExpressions_NotAvailableBecauseChildOfDynamicVarobj));
					rm.done();
				} else {
					// This format is obtained through a different GDB command.
					// It yields more details than the variableObject output.
					// Starting with GDB 7.0, this format automatically supports pretty-printing, as long as
					// GDB has been configured to support it.
					fExpressionCache.execute(fCommandFactory.createMIDataEvaluateExpression(exprDmc),
							new DataRequestMonitor<MIDataEvaluateExpressionInfo>(getExecutor(), rm) {
								@Override
								protected void handleSuccess() {
									rm.setData(new FormattedValueDMData(getData().getValue()));
									rm.done();
								}

								@Override
								protected void handleError() {
									if (fTraceVisualization) {
										rm.setData(new FormattedValueDMData("")); //$NON-NLS-1$
										rm.done();
									} else {
										super.handleError();
									}
								}
							});
				}
			} else {
				fExpressionCache.execute(new ExprMetaGetValue(dmc),
						new DataRequestMonitor<ExprMetaGetValueInfo>(getExecutor(), rm) {
							@Override
							protected void handleSuccess() {
								rm.setData(new FormattedValueDMData(getData().getValue()));
								rm.done();
							}
						});
			}
		}
	}

	/* Not implemented
	 *
	 * (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.service.IExpressions#getBaseExpressions(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
	 */
	@Override
	public void getBaseExpressions(IExpressionDMContext exprContext, DataRequestMonitor<IExpressionDMContext[]> rm) {
		rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, NOT_SUPPORTED, "Not supported", null)); //$NON-NLS-1$
		rm.done();
	}

	/**
	 * Retrieves the children expressions of the specified expression
	 *
	 * @param dmc
	 *            The context for the expression for which the children
	 *            should be retrieved.
	 * @param rm
	 *            The data request monitor that will contain the requested data
	 */
	@Override
	public void getSubExpressions(IExpressionDMContext dmc, DataRequestMonitor<IExpressionDMContext[]> rm) {
		getSubExpressions(dmc, -1, -1, rm);
	}

	/**
	 * Retrieves a range of children expressions of the specified expression
	 *
	 * @param exprCtx
	 *            The context for the expression for which the children
	 *            should be retrieved.
	 * @param startIndex
	 *            The starting index within the list of all children of the parent
	 *            expression.  Must be a positive integer.
	 * @param length
	 *            The length or number of elements of the range requested.
	 *            Must be a positive integer.
	 * @param rm
	 *            The data request monitor that will contain the requested data
	 */
	@Override
	public void getSubExpressions(final IExpressionDMContext exprCtx, final int startIndex, final int length,
			final DataRequestMonitor<IExpressionDMContext[]> rm) {

		if (exprCtx instanceof IndexedPartitionDMC) {
			getIndexedPartitionChildren((IndexedPartitionDMC) exprCtx, startIndex, length, rm);
		} else if (exprCtx instanceof MIExpressionDMC) {
			getRealSubExpressionCount(exprCtx, IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED,
					new DataRequestMonitor<Integer>(getExecutor(), rm) {
						/* (non-Javadoc)
						 * @see org.eclipse.cdt.dsf.concurrent.RequestMonitor#handleSuccess()
						 */
						@Override
						protected void handleSuccess() {
							final int realNumChildren = getData().intValue();
							if (realNumChildren == 0) {
								rm.setData(new IExpressionDMContext[0]);
								rm.done();
								return;
							}

							if (realNumChildren <= getArrayPartitionLength()) {
								getRealSubExpressions(exprCtx, startIndex, length, rm);
							} else {
								getExpressionData(exprCtx,
										new DataRequestMonitor<IExpressionDMData>(ImmediateExecutor.getInstance(), rm) {

											@Override
											protected void handleSuccess() {
												if (IExpressionDMData.BasicType.array
														.equals(getData().getBasicType())) {
													rm.setData(getTopLevelIndexedPartitions((MIExpressionDMC) exprCtx,
															realNumChildren, startIndex, length));
													rm.done();
												} else {
													getRealSubExpressions(exprCtx, startIndex, length, rm);
												}
											}
										});
							}
						}
					});
		} else if (exprCtx instanceof InvalidContextExpressionDMC) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
					"Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
			rm.done();
		} else {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", //$NON-NLS-1$
					null));
			rm.done();
		}
	}

	/**
	 * @since 4.0
	 */
	@Override
	public void safeToAskForAllSubExpressions(IExpressionDMContext dmc, final DataRequestMonitor<Boolean> rm) {
		if (dmc instanceof MIExpressionDMC) {
			fExpressionCache.execute(new ExprMetaGetVar(dmc),
					new DataRequestMonitor<ExprMetaGetVarInfo>(getExecutor(), rm) {
						@Override
						protected void handleSuccess() {
							boolean safe = getData().isSafeToAskForAllChildren();

							rm.setData(safe);
							rm.done();
						}
					});
		} else if (dmc instanceof InvalidContextExpressionDMC) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
					"Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
			rm.done();
		} else {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", //$NON-NLS-1$
					null));
			rm.done();
		}
	}

	/**
	 * @since 4.0
	 */
	@Override
	public void getSubExpressionCount(final IExpressionDMContext dmc, final int numChildLimit,
			final DataRequestMonitor<Integer> rm) {

		if (dmc instanceof MIExpressionDMC) {
			if (dmc instanceof IndexedPartitionDMC) {
				int length = ((IndexedPartitionDMC) dmc).getLength();
				rm.setData(computeNumberOfChildren(length));
				rm.done();
			} else {
				getRealSubExpressionCount(dmc, numChildLimit, new DataRequestMonitor<Integer>(getExecutor(), rm) {

					@Override
					protected void handleSuccess() {
						final int realNum = getData().intValue();
						if (realNum <= getArrayPartitionLength()) {
							rm.setData(Integer.valueOf(realNum));
							rm.done();
						} else {
							getExpressionData(dmc,
									new DataRequestMonitor<IExpressionDMData>(ImmediateExecutor.getInstance(), rm) {

										@Override
										protected void handleSuccess() {
											if (IExpressionDMData.BasicType.array.equals(getData().getBasicType())) {
												rm.setData(computeNumberOfChildren(realNum));
											} else {
												rm.setData(Integer.valueOf(realNum));
											}
											rm.done();
										}
									});
						}
					}
				});
			}
		} else if (dmc instanceof InvalidContextExpressionDMC) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
					"Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
			rm.done();
		} else {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", //$NON-NLS-1$
					null));
			rm.done();
		}
	}

	/**
	 * Retrieves the count of children expressions of the specified expression
	 *
	 * @param dmc
	 *            The context for the expression for which the children count
	 *            should be retrieved.
	 * @param rm
	 *            The data request monitor that will contain the requested data
	 */
	@Override
	public void getSubExpressionCount(IExpressionDMContext dmc, final DataRequestMonitor<Integer> rm) {
		getSubExpressionCount(dmc, IMIExpressions.CHILD_COUNT_LIMIT_UNSPECIFIED, rm);
	}

	/**
	 * This method indicates if an expression can be written to.
	 *
	 * @param dmc The data model context representing an expression.
	 *
	 * @param rm Data Request monitor containing True if this expression's value can be edited.  False otherwise.
	 */

	@Override
	public void canWriteExpression(IExpressionDMContext dmc, final DataRequestMonitor<Boolean> rm) {
		if (dmc instanceof MIExpressionDMC) {
			fExpressionCache.execute(new ExprMetaGetAttributes(dmc),
					new DataRequestMonitor<ExprMetaGetAttributesInfo>(getExecutor(), rm) {
						@Override
						protected void handleSuccess() {
							rm.setData(getData().getEditable());
							rm.done();
						}
					});
		} else if (dmc instanceof InvalidContextExpressionDMC) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
					"Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
			rm.done();
		} else {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", //$NON-NLS-1$
					null));
			rm.done();
		}
	}

	/**
	 * Changes the value of the specified expression based on the new value and format.
	 *
	 * @param dmc
	 *            The context for the expression for which the value
	 *            should be changed.
	 * @param expressionValue
	 *            The new value for the specified expression
	 * @param formatId
	 *            The format in which the value is specified
	 * @param rm
	 *            The request monitor that will indicate the completion of the operation
	 */
	@Override
	public void writeExpression(final IExpressionDMContext dmc, String expressionValue, String formatId,
			final RequestMonitor rm) {

		if (dmc instanceof MIExpressionDMC) {
			// This command must not be cached, since it changes the state of the back-end.
			// We must send it directly to the variable manager
			varManager.writeValue(dmc, expressionValue, formatId, new RequestMonitor(getExecutor(), rm) {
				@Override
				protected void handleSuccess() {
					// A value has changed, we should remove any references to that
					// value in our cache.  Since we don't have such granularity,
					// we must clear the entire cache.
					// We cannot use the context to do a more-specific reset, because
					// the same global variable can be set with different contexts
					fExpressionCache.reset();

					// Issue event that the expression has changed
					getSession().dispatchEvent(new ExpressionChangedEvent(dmc), getProperties());

					rm.done();
				}
			});
		} else if (dmc instanceof InvalidContextExpressionDMC) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
					"Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
			rm.done();
		} else {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", //$NON-NLS-1$
					null));
			rm.done();
		}
	}

	@DsfServiceEventHandler
	public void eventDispatched(IResumedDMEvent e) {
		fExpressionCache.setContextAvailable(e.getDMContext(), false);
		if (e.getReason() != StateChangeReason.STEP) {
			fExpressionCache.reset();
		}
	}

	@DsfServiceEventHandler
	public void eventDispatched(ISuspendedDMEvent e) {
		fExpressionCache.setContextAvailable(e.getDMContext(), true);
		fExpressionCache.reset();

		handleReturnValueAliasing(e);
	}

	private void handleReturnValueAliasing(ISuspendedDMEvent e) {
		// Process MIStoppedEvent from within the ISuspendedDMEvent
		// to avoid any race conditions where the actual MIStoppedEvent
		// can arrive faster that a preceding IResumedDMEvent
		if (e instanceof IMIDMEvent) {
			Object miEvent = ((IMIDMEvent) e).getMIEvent();
			if (miEvent instanceof MIStoppedEvent) {
				IMIExecutionDMContext stoppedEventThread = null;
				if (e instanceof IContainerSuspendedDMEvent) {
					// All-stop mode
					IExecutionDMContext[] triggerContexts = ((IContainerSuspendedDMEvent) e).getTriggeringContexts();
					if (triggerContexts.length != 0 && triggerContexts[0] instanceof IMIExecutionDMContext) {
						stoppedEventThread = (IMIExecutionDMContext) triggerContexts[0];
					}
				} else {
					// Non-stop mode
					IDMContext dmc = e.getDMContext();
					if (dmc instanceof IMIExecutionDMContext) {
						stoppedEventThread = (IMIExecutionDMContext) dmc;
					}
				}

				if (stoppedEventThread != null) {
					if (miEvent instanceof MIFunctionFinishedEvent) {
						// When getting an MIFunctionFinishedEvent we must set
						// a proper alias for the convenience variable
						String resultVar = ((MIFunctionFinishedEvent) miEvent).getGDBResultVar();
						if (resultVar != null && !resultVar.isEmpty()) {
							fReturnValueAliases.createAlias(stoppedEventThread, resultVar);
						}
					}

					// Keep track of the latest method the thread is stopped in.
					// Must do this after creating any alias, or else we will overwrite
					// the previous function name, which we need for the alias
					MIFrame frame = ((MIStoppedEvent) miEvent).getFrame();
					if (frame != null) {
						fReturnValueAliases.updateStoppedLocation(stoppedEventThread, frame.getFunction());
					}
				}
			}
		}
	}

	@DsfServiceEventHandler
	public void eventDispatched(IMemoryChangedEvent e) {
		fExpressionCache.reset();
		// MIVariableManager separately traps this event
	}

	/** @since 3.0 */
	@DsfServiceEventHandler
	public void eventDispatched(ITraceRecordSelectedChangedDMEvent e) {
		if (e.isVisualizationModeEnabled()) {
			fTraceVisualization = true;
		} else {
			fTraceVisualization = false;
		}
	}

	/**
	 * @nooverride This method is not intended to be re-implemented or extended by clients.
	 * @noreference This method is not intended to be referenced by clients.
	 */
	@DsfServiceEventHandler
	public void eventDispatched(IExitedDMEvent e) {
		IDMContext ctx = e.getDMContext();
		if (ctx instanceof IMIExecutionDMContext) {
			// When a thread exits, clear the alias structure for that
			// thread to avoid leaks
			fReturnValueAliases.clearThread((IMIExecutionDMContext) ctx);
		}
	}

	/**
	 * {@inheritDoc}
	 * @since 1.1
	 */
	@Override
	public void flushCache(IDMContext context) {
		fExpressionCache.reset(context);
		// We must also mark all variable objects as out-of-date
		// to refresh them as well
		varManager.markAllOutOfDate();
	}

	/**
	 * A casted or array-displayed expression.
	 * @since 3.0
	 */
	protected class CastedExpressionDMC extends MIExpressionDMC implements ICastedExpressionDMContext {

		private final CastInfo fCastInfo;

		public CastedExpressionDMC(MIExpressionDMC exprDMC, String castExpression, CastInfo castInfo) {
			super(getSession().getId(), castExpression, exprDMC.getRelativeExpression(), exprDMC);
			fCastInfo = castInfo;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.dsf.debug.service.IExpressions2.ICastedExpressionDMContext#getCastInfo()
		 */
		@Override
		public CastInfo getCastInfo() {
			return fCastInfo;
		}

		/**
		 * @return True if the two objects are equal, false otherwise.
		 */
		@Override
		public boolean equals(Object other) {
			return super.equals(other) && fCastInfo.equals(((CastedExpressionDMC) other).fCastInfo);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.service.IExpressions2#createCastedExpression(org.eclipse.cdt.dsf.datamodel.IDMContext, java.lang.String, org.eclipse.cdt.dsf.debug.service.IExpressions2.ICastedExpressionDMContext)
	 */
	/** @since 3.0 */
	@Override
	public ICastedExpressionDMContext createCastedExpression(IExpressionDMContext exprDMC, CastInfo castInfo) {
		if (exprDMC instanceof MIExpressionDMC && castInfo != null) {
			String castType = castInfo.getTypeString();
			String castExpression = exprDMC.getExpression();
			int castingLength = castInfo.getArrayCount();
			int castingIndex = castInfo.getArrayStartIndex();

			// cast to type
			if (castType != null && !castType.isEmpty()) {
				StringBuilder buffer = new StringBuilder();
				buffer.append('(').append(castType).append(')');
				buffer.append('(').append(castExpression).append(')');
				castExpression = buffer.toString();
			}

			// cast to array (can be in addition to cast to type)
			if (castingLength > 0) {
				StringBuilder buffer = new StringBuilder();
				buffer.append("*("); //$NON-NLS-1$
				buffer.append('(').append(castExpression).append(')');
				buffer.append('+').append(castingIndex).append(')');
				buffer.append('@').append(castingLength);
				castExpression = buffer.toString();
			}

			// Surround the entire casted expression with parenthesis in case we are
			// dealing with an array.  Arrays must be parenthesized before they are
			// subscripted.  Note that we can be casting to an array or displaying
			// as an array, so we must do this all the time.
			castExpression = String.format("(%s)", castExpression); //$NON-NLS-1$

			return new CastedExpressionDMC((MIExpressionDMC) exprDMC, castExpression, castInfo);
		} else {
			assert false;
			return null;
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.service.IExpressions3#getExpressionDataExtension(org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
	 */
	/** @since 4.0 */
	@Override
	public void getExpressionDataExtension(IExpressionDMContext dmc,
			final DataRequestMonitor<IExpressionDMDataExtension> rm) {
		getExpressionData(dmc, new DataRequestMonitor<IExpressionDMData>(getExecutor(), rm) {
			@Override
			protected void handleSuccess() {
				rm.setData((IExpressionDMDataExtension) getData());
				super.handleSuccess();
			}
		});
	}

	private IndexedPartitionDMC[] getTopLevelIndexedPartitions(MIExpressionDMC exprCtx, int realNumChildren,
			int startIndex, int length) {

		int numChildren = computeNumberOfChildren(realNumChildren);
		if (startIndex >= numChildren)
			return new IndexedPartitionDMC[0];
		int startIndex1 = (startIndex < 0) ? 0 : startIndex;
		int length1 = (length < 0) ? numChildren - startIndex1 : Math.min(length, numChildren - startIndex1);

		IndexedPartitionDMC[] children = new IndexedPartitionDMC[numChildren];
		int index = 0;
		// If the parent array is a casted expression it could have a different
		// start index.  We want the partition to start at the right index, not always 0
		//		if (exprCtx instanceof ICastedExpressionDMContext) {
		//			index = ((ICastedExpressionDMContext)exprCtx).getCastInfo().getArrayStartIndex();
		//		}
		for (int i = 0; i < children.length; ++i) {
			int partLength = computePartitionLength(realNumChildren, i);
			children[i] = createIndexedPartition(exprCtx, index, partLength);
			index += partLength;
		}
		return Arrays.copyOfRange(children, startIndex1, startIndex1 + length1);
	}

	private void getIndexedPartitionChildren(final IndexedPartitionDMC partDmc, final int startIndex, final int length,
			final DataRequestMonitor<IExpressionDMContext[]> rm) {

		final int startIndex1 = (startIndex < 0) ? 0 : startIndex;
		final int length1 = (length < 0) ? Integer.MAX_VALUE : length;

		final int partStartIndex = partDmc.getIndex();
		final int partLength = partDmc.getLength();
		if (partLength > getArrayPartitionLength()) {
			// create subpartitions
			int numChildren = computeNumberOfChildren(partLength);

			if (startIndex1 >= numChildren) {
				rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED,
						"Invalid range for evaluating sub expressions.", null)); //$NON-NLS-1$
				rm.done();
				return;
			}

			int numPart = Math.min(numChildren, length1);
			IndexedPartitionDMC[] children = new IndexedPartitionDMC[numPart];
			int index = partStartIndex;
			for (int i = 0; i < startIndex1; ++i)
				index += computePartitionLength(partLength, i);
			for (int i = 0; i < children.length; ++i) {
				int childPartLength = computePartitionLength(partLength, i + startIndex1);
				children[i] = createIndexedPartition(partDmc.getParentExpressionContext(), index, childPartLength);
				index += childPartLength;
			}
			rm.setData(children);
			rm.done();
		} else {
			// this is the last partition level, create "real" children
			if (startIndex1 > partLength) {
				rm.setData(new IExpressionDMContext[0]);
				rm.done();
			} else {
				getRealSubExpressions(partDmc.getParentExpressionContext(), partStartIndex + startIndex1,
						Math.min(length1, partLength - startIndex1), rm);
			}
		}
	}

	void getRealSubExpressions(final IExpressionDMContext exprCtx, int startIndex, int length,
			final DataRequestMonitor<IExpressionDMContext[]> rm) {

		ExprMetaGetChildren getChildren = (startIndex < 0 || length < 0) ? new ExprMetaGetChildren(exprCtx)
				: new ExprMetaGetChildren(exprCtx, startIndex + length);
		final int startIndex1 = (startIndex < 0) ? 0 : startIndex;
		final int length1 = (length < 0) ? Integer.MAX_VALUE : length;
		fExpressionCache.execute(getChildren, new DataRequestMonitor<ExprMetaGetChildrenInfo>(getExecutor(), rm) {
			@Override
			protected void handleSuccess() {
				ExpressionInfo[] childrenExpr = getData().getChildrenExpressions();

				if (startIndex1 >= childrenExpr.length) {
					rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, REQUEST_FAILED,
							"Invalid range for evaluating sub expressions.", null)); //$NON-NLS-1$
					rm.done();
					return;
				}

				int numChildren = childrenExpr.length - startIndex1;
				numChildren = Math.min(length1, numChildren);
				IExpressionDMContext[] childrenArray = new IExpressionDMContext[numChildren];
				for (int i = 0; i < numChildren; i++) {
					childrenArray[i] = createExpression(exprCtx.getParents()[0], childrenExpr[startIndex1 + i]);
				}
				rm.setData(childrenArray);
				rm.done();
			}
		});
	}

	/**
	 * Returns the number of "real" children if it is less or equal to the partition size,
	 * otherwise returns the number of partitions.
	 */
	private int computeNumberOfChildren(int realNumberOfChildren) {
		int childNum = realNumberOfChildren;
		int partLength = getArrayPartitionLength();
		int maxPartitionLength = 1;
		while (childNum > partLength) {
			childNum /= partLength;
			maxPartitionLength *= partLength;
		}
		if (childNum * maxPartitionLength < realNumberOfChildren)
			++childNum;
		return childNum;
	}

	private int computePartitionLength(int realNumberOfChildren, int index) {
		int childNum = realNumberOfChildren;
		int depth = 0;
		int partLength = getArrayPartitionLength();
		int length = partLength;
		while (childNum > partLength) {
			childNum /= partLength;
			if (depth > 0)
				length *= partLength;
			++depth;
		}
		int diff = realNumberOfChildren - length * index;
		return (diff > length) ? length : diff;
	}

	private IndexedPartitionDMC createIndexedPartition(MIExpressionDMC parentExpr, int index, int length) {
		return new IndexedPartitionDMC(parentExpr, index, length);
	}

	private void getRealSubExpressionCount(IExpressionDMContext dmc, int numChildLimit,
			final DataRequestMonitor<Integer> rm) {
		if (dmc instanceof MIExpressionDMC) {
			fExpressionCache.execute(new ExprMetaGetChildCount(dmc, numChildLimit),
					new DataRequestMonitor<ExprMetaGetChildCountInfo>(getExecutor(), rm) {
						@Override
						protected void handleSuccess() {
							rm.setData(getData().getChildNum());
							rm.done();
						}
					});
		} else if (dmc instanceof InvalidContextExpressionDMC) {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE,
					"Invalid context for evaluating expressions.", null)); //$NON-NLS-1$
			rm.done();
		} else {
			rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid expression context.", //$NON-NLS-1$
					null));
			rm.done();
		}
	}

	private int getArrayPartitionLength() {
		// Replace this in case we or the platform decide to add a user preference.
		// See org.eclipse.debug.internal.ui.model.elements.VariableContentProvider.
		return PARTITION_LENGTH;
	}
}
