/* * #%~ * org.overture.ide.debug * %% * Copyright (C) 2008 - 2014 Overture * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #~% */ package org.overture.ide.debug.core.model.internal; import java.io.File; import java.net.URI; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IRegisterGroup; import org.eclipse.debug.core.model.IThread; import org.eclipse.debug.core.model.IVariable; import org.eclipse.osgi.util.NLS; import org.overture.ide.debug.core.VdmDebugManager; import org.overture.ide.debug.core.VdmDebugPlugin; import org.overture.ide.debug.core.dbgp.IDbgpProperty; import org.overture.ide.debug.core.dbgp.IDbgpStackLevel; import org.overture.ide.debug.core.dbgp.commands.IDbgpContextCommands; import org.overture.ide.debug.core.dbgp.exceptions.DbgpDebuggingEngineException; import org.overture.ide.debug.core.dbgp.exceptions.DbgpException; import org.overture.ide.debug.core.model.IRefreshableVdmVariable; import org.overture.ide.debug.core.model.ISourceOffsetLookup; import org.overture.ide.debug.core.model.IVdmStack; import org.overture.ide.debug.core.model.IVdmStackFrame; import org.overture.ide.debug.core.model.IVdmThread; import org.overture.ide.debug.core.model.IVdmVariable; import org.overture.ide.debug.logging.LogItem; public class VdmStackFrame extends VdmDebugElement implements IVdmStackFrame { private final IVdmThread thread; private IDbgpStackLevel level; private final IVdmStack stack; private VdmVariableContainer variables = null; private boolean needRefreshVariables = false; protected static IVdmVariable[] readVariables(VdmStackFrame parentFrame, int contextId, IDbgpContextCommands commands) throws DbgpException { try { IDbgpProperty[] properties = commands.getContextProperties(parentFrame.getLevel(), contextId); IVdmVariable[] variables = new IVdmVariable[properties.length]; // Workaround for bug 215215 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=215215 // Remove this code when Tcl active state debugger fixed Set<String> duplicates = findDuplicateNames(properties); for (int i = 0; i < properties.length; ++i) { IDbgpProperty property = properties[i]; String name = property.getName(); if (duplicates.contains(name)) { name = property.getEvalName(); } variables[i] = new VdmVariable(parentFrame, name, property); } return variables; } catch (DbgpDebuggingEngineException e) { if (VdmDebugPlugin.DEBUG) { e.printStackTrace(); } return new IVdmVariable[0]; } } private static Set<String> findDuplicateNames(IDbgpProperty[] properties) { final Set<String> duplicates = new HashSet<String>(); final Set<String> alreadyExsisting = new HashSet<String>(); for (int i = 0; i < properties.length; ++i) { final IDbgpProperty property = properties[i]; final String name = property.getName(); if (!alreadyExsisting.add(name)) { duplicates.add(name); } } return duplicates; } protected VdmVariableContainer readAllVariables() throws DbgpException { final IDbgpContextCommands commands = thread.getDbgpSession().getCoreCommands(); ((VdmThread) this.thread).getVdmDebugTarget().printLog(new LogItem(((VdmThread) this.thread).getDbgpSession().getInfo(), "REQUEST", true, "getContextNames")); final Map<Integer, String> names = commands.getContextNames(getLevel()); ((VdmThread) this.thread).getVdmDebugTarget().printLog(new LogItem(((VdmThread) this.thread).getDbgpSession().getInfo(), "RESPONSE", false, "getContextNames")); final VdmVariableContainer result = new VdmVariableContainer(); if (thread.retrieveLocalVariables() && names.containsKey(new Integer(IDbgpContextCommands.LOCAL_CONTEXT_ID))) { result.locals = readVariables(this, IDbgpContextCommands.LOCAL_CONTEXT_ID, commands); ((VdmThread) this.thread).getVdmDebugTarget().printLog(new LogItem(((VdmThread) this.thread).getDbgpSession().getInfo(), "RESPONSE", false, "Read local variables")); } if (thread.retrieveGlobalVariables() && names.containsKey(new Integer(IDbgpContextCommands.GLOBAL_CONTEXT_ID))) { result.globals = readVariables(this, IDbgpContextCommands.GLOBAL_CONTEXT_ID, commands); ((VdmThread) this.thread).getVdmDebugTarget().printLog(new LogItem(((VdmThread) this.thread).getDbgpSession().getInfo(), "RESPONSE", false, "Read Global variables")); } if (thread.retrieveClassVariables() && names.containsKey(new Integer(IDbgpContextCommands.CLASS_CONTEXT_ID))) { result.classes = readVariables(this, IDbgpContextCommands.CLASS_CONTEXT_ID, commands); ((VdmThread) this.thread).getVdmDebugTarget().printLog(new LogItem(((VdmThread) this.thread).getDbgpSession().getInfo(), "RESPONSE", false, "Read Class variables")); } return result; } private static class VdmVariableContainer { IVariable[] locals = null; IVariable[] globals = null; IVariable[] classes = null; VdmVariableWrapper globalsWrapper = null; VdmVariableWrapper classesWrapper = null; VdmVariableContainer sort(IDebugTarget target) { final Comparator<Object> variableComparator = VdmDebugManager.getInstance().getVariableNameComparator(); if (locals != null) { Arrays.sort(locals, variableComparator); } if (globals != null) { Arrays.sort(globals, variableComparator); } if (classes != null) { Arrays.sort(classes, variableComparator); } return this; } private int size() { int size = 0; if (locals != null) { size += locals.length; } if (globals != null && globals.length > 0) { ++size; } if (classes != null && classes.length > 0) { ++size; } return size; } IVdmVariable[] toArray(IDebugTarget target) { final int size = size(); final IVdmVariable[] result = new IVdmVariable[size]; if (size != 0) { int index = 0; if (globals != null && globals.length > 0) { if (globalsWrapper == null) { globalsWrapper = new VdmVariableWrapper(target, "Global Variables", globals); } else { globalsWrapper.refreshValue(globals); } result[index++] = globalsWrapper; } if (classes != null && classes.length > 0) { if (classesWrapper == null) { classesWrapper = new VdmVariableWrapper(target, "Instance Variables", classes); } else { classesWrapper.refreshValue(classes); } result[index++] = classesWrapper; } if (locals != null) { System.arraycopy(locals, 0, result, index, locals.length); index += locals.length; } } return result; } /** * @return */ public boolean hasVariables() { return locals != null && locals.length != 0 || classes != null || globals != null; } /** * @param varName * @return * @throws DebugException */ public IVariable findVariable(String varName) throws DebugException { if (locals != null) { final IVariable variable = findVariable(varName, locals); if (variable != null) { return variable; } } if (globals != null) { final IVariable variable = findVariable(varName, globals); if (variable != null) { return variable; } } return null; } private static IVariable findVariable(String varName, IVariable[] vars) throws DebugException { for (int i = 0; i < vars.length; i++) { final IVariable var = vars[i]; if (var.getName().equals(varName)) { return var; } } return null; } } public VdmStackFrame(IVdmStack stack, IDbgpStackLevel stackLevel) { this.stack = stack; this.thread = stack.getThread(); this.level = stackLevel; } public synchronized void updateVariables() { this.variables = null; try { checkVariablesAvailable(); } catch (DebugException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public IVdmStack getStack() { return stack; } /** * @return * @deprecated use #getSourceURI() */ public URI getFileName() { return level.getFileURI(); } private static final int MULTI_LINE_COUNT = 2; public int getCharStart() throws DebugException { final int beginLine = level.getBeginLine(); if (beginLine > 0) { final int endLine = level.getEndLine(); if (endLine > 0 && endLine >= beginLine) { final ISourceOffsetLookup offsetLookup = VdmDebugPlugin.getSourceOffsetLookup(); if (offsetLookup != null) { return offsetLookup.calculateOffset(this, beginLine, level.getBeginColumn(), false); } } } return -1; } public int getCharEnd() throws DebugException { final int endLine = level.getEndLine(); if (endLine > 0) { final int beginLine = level.getBeginLine(); if (beginLine > 0 && endLine >= beginLine) { final ISourceOffsetLookup offsetLookup = VdmDebugPlugin.getSourceOffsetLookup(); if (offsetLookup != null) { if (endLine < beginLine + MULTI_LINE_COUNT) { final int offset = offsetLookup.calculateOffset(this, endLine, level.getEndColumn(), true); if (offset >= 0) { return offset + 1; } } else { final int offset = offsetLookup.calculateOffset(this, beginLine, -1, true); if (offset >= 0) { return offset + 1; } } } } } return -1; } public int getLineNumber() throws DebugException { return level.getLineNumber(); } public int getBeginLine() { return level.getBeginLine(); } public int getBeginColumn() { return level.getBeginColumn(); } public int getEndLine() { return level.getEndLine(); } public int getEndColumn() { return level.getEndColumn(); } public String getWhere() { return level.getWhere().trim(); } public String getName() throws DebugException { // String name = level.getWhere().trim(); // // if (name == null || name.length() == 0) { // name = toString(); // } // // name += " (" + level.getFileURI().getPath() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ return getOnlyFileName() + " line: " + getLineNumber(); } public String getOnlyFileName() { URI uri = level.getFileURI(); String res = new File(uri).getName();// .toASCIIString(); return res;// .substring(res.lastIndexOf('/') + 1); } public boolean hasRegisterGroups() throws DebugException { return false; } public IRegisterGroup[] getRegisterGroups() throws DebugException { return new IRegisterGroup[0]; } public IThread getThread() { return thread; } public synchronized boolean hasVariables() throws DebugException { checkVariablesAvailable(); return variables.hasVariables(); } private synchronized void checkVariablesAvailable() throws DebugException { try { if (variables == null) { variables = readAllVariables(); variables.sort(getDebugTarget()); } else if (needRefreshVariables) { try { refreshVariables(); } finally { needRefreshVariables = false; } } } catch (DbgpException e) { variables = new VdmVariableContainer(); final Status status = new Status(IStatus.ERROR, VdmDebugPlugin.PLUGIN_ID, "Unable To Load Variables", e); VdmDebugPlugin.log(status); throw new DebugException(status); } } /** * @throws DebugException * @throws DbgpException */ private void refreshVariables() throws DebugException, DbgpException { final VdmVariableContainer newVars = readAllVariables(); newVars.sort(getDebugTarget()); variables.locals = refreshVariables(newVars.locals, variables.locals); variables.globals = refreshVariables(newVars.globals, variables.globals); variables.classes = refreshVariables(newVars.classes, variables.classes); } /** * @param newVars * @param oldVars * @return * @throws DebugException */ static IVariable[] refreshVariables(IVariable[] newVars, IVariable[] oldVars) throws DebugException { if (oldVars != null) { final Map<String, IVariable> map = new HashMap<String, IVariable>(); for (int i = 0; i < oldVars.length; ++i) { final IVariable variable = oldVars[i]; if (variable instanceof IRefreshableVdmVariable) { map.put(variable.getName(), variable); } } for (int i = 0; i < newVars.length; ++i) { final IVariable variable = newVars[i]; final IRefreshableVdmVariable old; old = (IRefreshableVdmVariable) map.get(variable.getName()); if (old != null) { newVars[i] = old.refreshVariable(variable); } } } return newVars; } public synchronized IVariable[] getVariables() throws DebugException { checkVariablesAvailable(); return variables.toArray(getDebugTarget()); } // IStep public boolean canStepInto() { return thread.canStepInto(); } public boolean canStepOver() { return thread.canStepOver(); } public boolean canStepReturn() { return thread.canStepReturn(); } public boolean isStepping() { return thread.isStepping(); } public void stepInto() throws DebugException { thread.stepInto(); } public void stepOver() throws DebugException { thread.stepOver(); } public void stepReturn() throws DebugException { thread.stepReturn(); } // ISuspenResume public boolean canResume() { return thread.canResume(); } public boolean canSuspend() { return thread.canSuspend(); } public boolean isSuspended() { return thread.isSuspended(); } public void resume() throws DebugException { thread.resume(); } public void suspend() throws DebugException { thread.suspend(); } // ITerminate public boolean canTerminate() { return thread.canTerminate(); } public boolean isTerminated() { return thread.isTerminated(); } public void terminate() throws DebugException { thread.terminate(); } // IDebugElement public IDebugTarget getDebugTarget() { return thread.getDebugTarget(); } public synchronized IVdmVariable findVariable(String varName) throws DebugException { checkVariablesAvailable(); return (IVdmVariable) variables.findVariable(varName); } public int getLevel() { return level.getLevel(); } public String toString() { return NLS.bind("stackFrame", new Integer(level.getLevel())); } public String getSourceLine() { return level.getWhere(); } public URI getSourceURI() { return level.getFileURI(); } public IVdmThread getVdmThread() { return (IVdmThread) getThread(); } /** * @param frame * @param depth * @return */ public VdmStackFrame bind(IDbgpStackLevel newLevel) { if (level.isSameMethod(newLevel)) { level = newLevel; needRefreshVariables = true; return this; } return new VdmStackFrame(stack, newLevel); } }