/******************************************************************************* * Copyright (c) 2010 Wind River Systems, Inc. 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: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tm.internal.tcf.cdt.ui.hover; import java.math.BigInteger; import java.util.Map; import org.eclipse.cdt.debug.ui.editors.AbstractDebugTextHover; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.jface.text.DefaultInformationControl; import org.eclipse.jface.text.IInformationControl; import org.eclipse.jface.text.IInformationControlCreator; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextHoverExtension2; import org.eclipse.jface.text.ITextViewer; import org.eclipse.swt.widgets.Shell; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFChildren; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFChildrenStackTrace; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNode; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNodeExecContext; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNodeExpression; import org.eclipse.tm.internal.tcf.debug.ui.model.TCFNodeStackFrame; import org.eclipse.tm.tcf.protocol.IChannel; import org.eclipse.tm.tcf.protocol.IToken; import org.eclipse.tm.tcf.services.IExpressions; import org.eclipse.tm.tcf.services.IExpressions.DoneCreate; import org.eclipse.tm.tcf.services.IExpressions.DoneDispose; import org.eclipse.tm.tcf.services.IExpressions.DoneEvaluate; import org.eclipse.tm.tcf.services.IExpressions.Expression; import org.eclipse.tm.tcf.services.IExpressions.Value; import org.eclipse.tm.tcf.util.TCFDataCache; import org.eclipse.tm.tcf.util.TCFTask; /** * TCF implementation of debug expression hover for the C/C++ Editor. */ public class TCFDebugTextHover extends AbstractDebugTextHover implements ITextHoverExtension2 { @Override public IInformationControlCreator getHoverControlCreator() { if (useExpressionExplorer()) { return createExpressionInformationControlCreator(); } else { return new IInformationControlCreator() { public IInformationControl createInformationControl(Shell parent) { return new DefaultInformationControl(parent, false); } }; } } private IInformationControlCreator createExpressionInformationControlCreator() { return new ExpressionInformationControlCreator(); } protected boolean useExpressionExplorer() { return true; } public Object getHoverInfo2(ITextViewer textViewer, IRegion hoverRegion) { if (!useExpressionExplorer()) return getHoverInfo(textViewer, hoverRegion); final TCFNodeStackFrame activeFrame = getActiveFrame(); if (activeFrame == null) return null; final String text = getExpressionText(textViewer, hoverRegion); if (text == null || text.length() == 0) return null; try { return new TCFTask<TCFNode>(activeFrame.getChannel()) { public void run() { TCFNode evalContext = activeFrame.isEmulated() ? activeFrame.getParent() : activeFrame; TCFChildren cache = evalContext.getModel().getHoverExpressionCache(evalContext, text); if (!cache.validate(this)) return; Map<String,TCFNode> nodes = cache.getData(); if (nodes != null) { for (TCFNode node : nodes.values()) { TCFDataCache<IExpressions.Value> value = ((TCFNodeExpression)node).getValue(); if (!value.validate(this)) return; if (value.getData() != null) { done(node.getParent()); return; } } } done(null); } }.get(); } catch (Exception x) { // Problem in Eclipse 3.7: // TextViewerHoverManager calls Thread.interrupt(), // but it fails to handle InterruptedException. // We have to catch and ignore the exception. return null; } } @Override protected boolean canEvaluate() { return getActiveFrame() != null; } private TCFNodeStackFrame getActiveFrame() { IAdaptable context = getSelectionAdaptable(); if (context instanceof TCFNodeStackFrame) return (TCFNodeStackFrame) context; if (context instanceof TCFNodeExecContext) { try { final TCFNodeExecContext exe = (TCFNodeExecContext) context; return new TCFTask<TCFNodeStackFrame>(exe.getChannel()) { public void run() { TCFChildrenStackTrace stack = exe.getStackTrace(); if (!stack.validate(this)) return; done(stack.getTopFrame()); } }.get(); } catch (Exception x) { // Problem in Eclipse 3.7: // TextViewerHoverManager calls Thread.interrupt(), // but it fails to handle InterruptedException. // We have to catch and ignore the exception. return null; } } return null; } @Override protected String evaluateExpression(final String expression) { final TCFNodeStackFrame activeFrame = getActiveFrame(); if (activeFrame == null) return null; final IChannel channel = activeFrame.getChannel(); return new TCFTask<String>(channel) { public void run() { final IExpressions exprSvc = channel.getRemoteService(IExpressions.class); if (exprSvc != null) { TCFNode evalContext = activeFrame.isEmulated() ? activeFrame.getParent() : activeFrame; exprSvc.create(evalContext.getID(), null, expression, new DoneCreate() { public void doneCreate(IToken token, Exception error, final Expression context) { if (error == null) { exprSvc.evaluate(context.getID(), new DoneEvaluate() { public void doneEvaluate(IToken token, Exception error, Value value) { if (error == null) { done(getValueText(value)); } else { done(null); } exprSvc.dispose(context.getID(), new DoneDispose() { public void doneDispose(IToken token, Exception error) { // no-op } }); } }); } else { done(null); } } }); } else { done(null); } } }.getE(); } private static String getValueText(Value value) { BigInteger bigInteger = toBigInteger(value.getValue(), value.isBigEndian(), true); switch(value.getTypeClass()) { case integer: return bigInteger.toString(); case real: if (value.getValue().length <= 4) { return String.valueOf(Float.intBitsToFloat(bigInteger.intValue())); } else if (value.getValue().length <= 8) { return String.valueOf(Double.longBitsToDouble(bigInteger.longValue())); } else { return "N/A"; } default: return "0x"+bigInteger.toString(16); } } private static BigInteger toBigInteger(byte[] data, boolean big_endian, boolean sign_extension) { byte[] temp = null; if (sign_extension) { temp = new byte[data.length]; } else { temp = new byte[data.length + 1]; temp[0] = 0; // Extra byte to avoid sign extension by BigInteger } if (big_endian) { System.arraycopy(data, 0, temp, sign_extension ? 0 : 1, data.length); } else { for (int i = 0; i < data.length; i++) { temp[temp.length - i - 1] = data[i]; } } return new BigInteger(temp); } }