/* * Copyright (c) 2012, the Dart project authors. * * Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.dart.tools.debug.core.server; import com.google.dart.tools.debug.core.DartDebugCorePlugin; import com.google.dart.tools.debug.core.expr.IExpressionEvaluator; import com.google.dart.tools.debug.core.expr.WatchExpressionResult; import com.google.dart.tools.debug.core.util.DebuggerUtils; import com.google.dart.tools.debug.core.util.IDartStackFrame; import com.google.dart.tools.debug.core.util.IExceptionStackFrame; import com.google.dart.tools.debug.core.util.IVariableResolver; import org.eclipse.core.resources.IFile; 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.IStackFrame; import org.eclipse.debug.core.model.IThread; import org.eclipse.debug.core.model.IValue; import org.eclipse.debug.core.model.IVariable; import org.eclipse.debug.core.model.IWatchExpressionListener; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * The IStackFrame implementation for the VM debug elements. This stack frame element represents a * Dart frame. */ public class ServerDebugStackFrame extends ServerDebugElement implements IStackFrame, IDartStackFrame, IExceptionStackFrame, IVariableResolver, IExpressionEvaluator { private IThread thread; private VmCallFrame vmFrame; private boolean isExceptionStackFrame; private List<ServerDebugVariable> locals; private IValue classValue; private IVariable globalVariable; public ServerDebugStackFrame(IDebugTarget target, IThread thread, VmCallFrame vmFrame) { super(target); this.thread = thread; this.vmFrame = vmFrame; this.locals = createFrom(vmFrame); } @Override public boolean canResume() { return getThread().canResume(); } @Override public boolean canStepInto() { return !hasException() && getThread().canStepInto() && !isOnAwaitKeyword(); } @Override public boolean canStepOver() { return !hasException() && getThread().canStepOver() && !isOnAwaitKeyword(); } @Override public boolean canStepReturn() { return !hasException() && getThread().canStepReturn(); } @Override public boolean canSuspend() { return getThread().canSuspend(); } @Override public boolean canTerminate() { return getThread().canTerminate(); } @Override public void evaluateExpression(final String expression, final IWatchExpressionListener listener) { try { getConnection().evaluateOnCallFrame( vmFrame.getIsolate(), vmFrame, expression, new VmCallback<VmValue>() { @Override public void handleResult(VmResult<VmValue> result) { if (result.isError()) { listener.watchEvaluationFinished(WatchExpressionResult.error( expression, result.getError())); } else { listener.watchEvaluationFinished(WatchExpressionResult.value( expression, ServerDebugValue.createValue(getTarget(), result.getResult()))); } } }); } catch (IOException e) { DebugException exception = createDebugException(e); listener.watchEvaluationFinished(WatchExpressionResult.exception(expression, exception)); } } @Override public IVariable findVariable(String varName) throws DebugException { // search in locals for (IVariable var : getVariables()) { if (var.getName().equals(varName)) { return var; } } // search in instance variables IVariable thisVar = getThisVariable(); if (thisVar != null) { IValue thisValue = thisVar.getValue(); for (IVariable var : thisValue.getVariables()) { if (var.getName().equals(varName)) { return var; } } } // search in statics IValue classValue = getClassValue(); if (classValue != null) { for (IVariable var : classValue.getVariables()) { if (var.getName().equals(varName)) { return var; } } } // search globals IVariable globalVar = getGlobalVariable(); if (globalVar != null) { for (IVariable var : globalVar.getValue().getVariables()) { if (var.getName().equals(varName)) { return var; } } } return null; } @SuppressWarnings("rawtypes") @Override public Object getAdapter(Class adapterClass) { if (adapterClass == IThread.class) { return getThread(); } else { return super.getAdapter(adapterClass); } } @Override public int getCharEnd() throws DebugException { return -1; } @Override public int getCharStart() throws DebugException { return -1; } @Override public String getExceptionDisplayText() throws DebugException { ServerDebugValue exceptionValue = (ServerDebugValue) locals.get(0).getValue(); return "Exception: " + exceptionValue.getDisplayString(); } public IValue getLibraryValue() { VmLibrary vmLibrary = getConnection().getLibraryPropertiesSync( vmFrame.getIsolate(), vmFrame.getLibraryId()); if (vmLibrary == null) { return null; } else { return new ServerDebugValueLibrary(getTarget(), vmLibrary); } } @Override public int getLineNumber() throws DebugException { VmLocation location = vmFrame.getLocation(); if (location != null) { return location.getLineNumber(getConnection()); } else { return 0; } } @Override public String getLongName() { String file = getFileOrLibraryName(); return getShortName() + (file == null ? "" : " - " + file); } @Override public String getName() throws DebugException { if (DebuggerUtils.areSiblingNamesUnique(this)) { return getShortName(); } else { return getLongName(); } } @Override public IRegisterGroup[] getRegisterGroups() throws DebugException { return new IRegisterGroup[0]; } @Override public String getShortName() { return DebuggerUtils.demangleVmName(vmFrame.getFunctionName()) + "()"; } @Override public String getSourceLocationPath() { VmLocation location = vmFrame.getLocation(); if (location == null) { return null; } String url = location.getUrl(); String filePath = getTarget().getUriToFileResolver().getFileForUri(url); if (filePath != null) { return filePath; } return "builtin:" + vmFrame.getLibraryId() + ":" + url; } @Override public IThread getThread() { return thread; } @Override public IVariable[] getVariables() throws DebugException { return locals.toArray(new IVariable[locals.size()]); } /** * Public for testing. */ public VmCallFrame getVmFrame() { return vmFrame; } @Override public boolean hasException() { return isExceptionStackFrame; } @Override public boolean hasRegisterGroups() throws DebugException { return false; } @Override public boolean hasVariables() throws DebugException { return getVariables().length > 0; } @Override public boolean isPrivate() { return DebuggerUtils.isPrivateName(vmFrame.getFunctionName()); } @Override public boolean isStepping() { return getThread().isStepping(); } @Override public boolean isSuspended() { return getThread().isSuspended(); } @Override public boolean isTerminated() { return getThread().isTerminated(); } @Override public boolean isUsingSourceMaps() { return false; } @Override public void resume() throws DebugException { getThread().resume(); } @Override public void stepInto() throws DebugException { getThread().stepInto(); } @Override public void stepOver() throws DebugException { getThread().stepOver(); } @Override public void stepReturn() throws DebugException { getThread().stepReturn(); } @Override public void suspend() throws DebugException { getThread().suspend(); } @Override public void terminate() throws DebugException { getThread().terminate(); } @Override public String toString() { return getShortName(); } protected void addException(VmValue exception) { isExceptionStackFrame = true; List<ServerDebugVariable> newLocals = new ArrayList<ServerDebugVariable>(); VmVariable exceptionVariable = VmVariable.createFromException(exception); // Create a copy of the list, add the exception variable to the beginning. newLocals.add(new ServerDebugVariable(getTarget(), exceptionVariable)); newLocals.addAll(locals); locals = newLocals; } protected IValue getClassValue() { if (classValue == null && vmFrame.hasClassId()) { VmClass vmClass = getConnection().getClassInfoSync(vmFrame.getIsolate(), vmFrame.getClassId()); classValue = new ServerDebugValueClass(getTarget(), vmClass); } return classValue; } protected IVariable getGlobalVariable() throws DebugException { if (globalVariable == null) { globalVariable = ServerDebugVariable.createLibraryVariable( getTarget(), vmFrame.getIsolate(), vmFrame.getLibraryId()); } return globalVariable; } protected IVariable getThisVariable() throws DebugException { for (IVariable var : getVariables()) { if (var instanceof ServerDebugVariable) { if (((ServerDebugVariable) var).isThisObject()) { return var; } } } return null; } /** * @return whether we are on a line with the 'await' keyword. */ protected boolean isOnAwaitKeyword() { final String await = "await"; if (!DartDebugCorePlugin.DISABLE_STEPPING_ON_AWAIT) { return false; } try { String line = getCurrentSourceLine(); if (line == null) { return false; } // Find occurrences of `await`. int index = line.indexOf(await); while (index != -1) { // Check to make sure the the beginning and end of the 'await' string are not valid // identifier parts. if (index == 0 || !Character.isJavaIdentifierPart(line.charAt(index - 1))) { if (index + await.length() >= line.length() || !Character.isJavaIdentifierPart(line.charAt(index + await.length()))) { return true; } } index = line.indexOf(await, index + 1); } } catch (DebugException e) { } return false; } private List<ServerDebugVariable> createFrom(VmCallFrame frame) { if (frame.getLocals() == null) { return Collections.emptyList(); } else { List<ServerDebugVariable> variables = new ArrayList<ServerDebugVariable>(); // // create a synthetic globals variable // variables.add(ServerDebugVariable.createLibraryVariable( // getTarget(), // frame.getIsolate(), // vmFrame.getLibraryId())); for (VmVariable var : frame.getLocals()) { if (var.isHidden()) { continue; } ServerDebugVariable serverVariable = new ServerDebugVariable(getTarget(), var); variables.add(serverVariable); } return variables; } } private String getCurrentSourceLine() throws DebugException { int line = getLineNumber(); if (line == 0) { return null; } Object sourceElement = getLaunch().getSourceLocator().getSourceElement(this); if (sourceElement instanceof IFile) { IFile file = (IFile) sourceElement; return DebuggerUtils.extractFileLine(file, line - 1); } return null; } private String getFileOrLibraryName() { VmLocation location = vmFrame.getLocation(); if (location != null) { String url = location.getUrl(); int index = url.lastIndexOf('/'); if (index != -1) { return url.substring(index + 1); } else { return url; } } return null; } }