org.chromium.debug.core/src/org/chromium/debug/core/model/StackFrame.java
author TasneemS@US-TASNEEMS
Wed, 23 Dec 2009 17:13:18 -0800
changeset 2 e4420d2515f1
child 52 f577ea64429e
permissions -rw-r--r--
Initial version of WRT Debugger.

// 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();
  }

}