org.chromium.debug.core/src/org/chromium/debug/core/model/StackFrame.java
changeset 2 e4420d2515f1
child 52 f577ea64429e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.debug.core/src/org/chromium/debug/core/model/StackFrame.java	Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,309 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.debug.core.model;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.chromium.debug.core.ChromiumDebugPlugin;
+import org.chromium.sdk.CallFrame;
+import org.chromium.sdk.JsArray;
+import org.chromium.sdk.JsFunction;
+import org.chromium.sdk.JsObject;
+import org.chromium.sdk.JsScope;
+import org.chromium.sdk.JsValue;
+import org.chromium.sdk.JsVariable;
+import org.chromium.sdk.Script;
+import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
+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.IVariable;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * An IStackFrame implementation over a JsStackFrame instance.
+ */
+public class StackFrame extends DebugElementImpl implements IStackFrame {
+
+  private final JavascriptThread thread;
+
+  private final CallFrame stackFrame;
+
+  private IVariable[] variables;
+
+  /**
+   * Constructs a stack frame for the given handler using the FrameMirror data
+   * from the remote V8 VM.
+   *
+   * @param debugTarget the global parent
+   * @param thread for which the stack frame is created
+   * @param stackFrame an underlying SDK stack frame
+   */
+  public StackFrame(IChromiumDebugTarget debugTarget, JavascriptThread thread, CallFrame stackFrame) {
+    super(debugTarget);
+    this.thread = thread;
+    this.stackFrame = stackFrame;
+  }
+
+  public CallFrame getCallFrame() {
+    return stackFrame;
+  }
+
+  public IThread getThread() {
+    return thread;
+  }
+
+  public IVariable[] getVariables() throws DebugException {
+    if (variables == null) {
+      try {
+        variables = wrapScopes(getDebugTarget(), stackFrame.getVariableScopes(),
+            stackFrame.getReceiverVariable());
+      } catch (RuntimeException e) {
+        // We shouldn't throw RuntimeException from here, because calling
+        // ElementContentProvider#update will forget to call update.done().
+        throw new DebugException(new Status(IStatus.ERROR, ChromiumDebugPlugin.PLUGIN_ID,
+            "Failed to read variables", e)); //$NON-NLS-1$
+      }
+    }
+    return variables;
+  }
+
+  static IVariable[] wrapVariables(
+      IChromiumDebugTarget debugTarget, Collection<? extends JsVariable> jsVars,
+      Collection <? extends JsVariable> jsInternalProperties) {
+    List<Variable> vars = new ArrayList<Variable>(jsVars.size());
+    for (JsVariable jsVar : jsVars) {
+      vars.add(new Variable(debugTarget, jsVar, false));
+    }
+    for (JsVariable jsMetaVar : jsInternalProperties) {
+      vars.add(new Variable(debugTarget, jsMetaVar, true));
+    }
+    return vars.toArray(new IVariable[vars.size()]);
+  }
+
+  static IVariable[] wrapScopes(IChromiumDebugTarget debugTarget, List<? extends JsScope> jsScopes,
+      JsVariable receiverVariable) {
+    List<Variable> vars = new ArrayList<Variable>();
+
+    for (JsScope scope : jsScopes) {
+      if (scope.getType() == JsScope.Type.GLOBAL) {
+        if (receiverVariable != null) {
+          vars.add(new Variable(debugTarget, receiverVariable, false));
+          receiverVariable = null;
+        }
+        vars.add(new Variable(debugTarget, wrapScopeAsVariable(scope), false));
+      } else {
+        for (JsVariable var : scope.getVariables()) {
+          vars.add(new Variable(debugTarget, var, false));
+        }
+      }
+    }
+    if (receiverVariable != null) {
+      vars.add(new Variable(debugTarget, receiverVariable, false));
+    }
+
+    IVariable[] result = new IVariable[vars.size()];
+    // Return in reverse order.
+    for (int i = 0; i < result.length; i++) {
+      result[result.length - i - 1] = vars.get(i);
+    }
+    return result;
+  }
+
+  private static JsVariable wrapScopeAsVariable(final JsScope jsScope) {
+    class ScopeObjectVariable implements JsVariable, JsObject {
+      public String getFullyQualifiedName() {
+        return getName();
+      }
+      public String getName() {
+        // TODO(peter.rybin): should we localize it?
+        return "<" + jsScope.getType() + ">";
+      }
+      public JsValue getValue() throws UnsupportedOperationException {
+        return this;
+      }
+      public boolean isMutable() {
+        return false;
+      }
+      public boolean isReadable() {
+        return true;
+      }
+      public void setValue(String newValue, SetValueCallback callback)
+          throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+      }
+      public JsArray asArray() {
+        return null;
+      }
+      public JsFunction asFunction() {
+        return null;
+      }
+      public String getClassName() {
+        // TODO(peter.rybin): should we localize it?
+        return "#Scope";
+      }
+      public Collection<? extends JsVariable> getProperties() throws MethodIsBlockingException {
+        return jsScope.getVariables();
+      }
+      public Collection<? extends JsVariable> getInternalProperties()
+          throws MethodIsBlockingException {
+        return Collections.emptyList();
+      }
+      public JsVariable getProperty(String name) {
+        for (JsVariable var : getProperties()) {
+          if (var.getName().equals(name)) {
+            return var;
+          }
+        }
+        return null;
+      }
+      public JsObject asObject() {
+        return this;
+      }
+      public Type getType() {
+        return Type.TYPE_OBJECT;
+      }
+      public String getValueString() {
+        return getClassName();
+      }
+      public String getRefId() {
+        return null;
+      }
+    }
+    return new ScopeObjectVariable();
+  }
+
+  public boolean hasVariables() throws DebugException {
+    return stackFrame.getReceiverVariable() != null || stackFrame.getVariableScopes().size() > 0;
+  }
+
+  public int getLineNumber() throws DebugException {
+    // convert 0-based to 1-based
+    return stackFrame.getLineNumber() + 1;
+  }
+
+  public int getCharStart() throws DebugException {
+    return stackFrame.getCharStart();
+  }
+
+  public int getCharEnd() throws DebugException {
+    return -1;
+  }
+
+  public String getName() throws DebugException {
+    String name = stackFrame.getFunctionName();
+    Script script = stackFrame.getScript();
+    if (script == null) {
+      return Messages.StackFrame_UnknownScriptName;
+    }
+    int line = script.getStartLine() + getLineNumber();
+    if (line != -1) {
+      name = NLS.bind(Messages.StackFrame_NameFormat, new Object[] {name, script.getName(), line});
+    }
+    return name;
+  }
+
+  public IRegisterGroup[] getRegisterGroups() throws DebugException {
+    return null;
+  }
+
+  public boolean hasRegisterGroups() throws DebugException {
+    return false;
+  }
+
+  public boolean canStepInto() {
+    return getThread().canStepInto();
+  }
+
+  public boolean canStepOver() {
+    return getThread().canStepOver();
+  }
+
+  public boolean canStepReturn() {
+    return getThread().canStepReturn();
+  }
+
+  public boolean isStepping() {
+    return getThread().isStepping();
+  }
+
+  public void stepInto() throws DebugException {
+    getThread().stepInto();
+  }
+
+  public void stepOver() throws DebugException {
+    getThread().stepOver();
+  }
+
+  public void stepReturn() throws DebugException {
+    getThread().stepReturn();
+  }
+
+  public boolean canResume() {
+    return getThread().canResume();
+  }
+
+  public boolean canSuspend() {
+    return getThread().canSuspend();
+  }
+
+  public boolean isSuspended() {
+    return getThread().isSuspended();
+  }
+
+  public void resume() throws DebugException {
+    getThread().resume();
+  }
+
+  public void suspend() throws DebugException {
+    getThread().suspend();
+  }
+
+  public boolean canTerminate() {
+    return getThread().canTerminate();
+  }
+
+  public boolean isTerminated() {
+    return getThread().isTerminated();
+  }
+
+  public void terminate() throws DebugException {
+    getThread().terminate();
+  }
+
+  /**
+   * Returns the name of the source file this stack frame is associated with.
+   *
+   * @return the name of the source file this stack frame is associated with
+   */
+  String getSourceName() {
+    Script script = stackFrame.getScript();
+    if (script == null) {
+      return Messages.StackFrame_UnknownScriptName;
+    }
+    return script.getName();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof StackFrame) {
+      StackFrame other = (StackFrame) obj;
+      return other.stackFrame.equals(this.stackFrame);
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    return stackFrame.hashCode();
+  }
+
+}