/**
   * Performs a search for a member with the given name using the given id as the parent variable.
   *
   * <p>If a match is found then, we return the parent variable of the member that matched. The
   * proto chain is optionally traversed.
   *
   * <p>No exceptions are thrown
   */
  Value locateParentForNamed(long id, String name, boolean traverseProto)
      throws PlayerDebugException {
    StringBuilder sb = new StringBuilder();

    Variable var = null;
    Value val = null;
    try {
      var = memberNamed(id, name);

      // see if we need to traverse the proto chain
      while (var == null && traverseProto) {
        // first attempt to get __proto__, then resolve name
        Variable proto = memberNamed(id, "__proto__"); // $NON-NLS-1$
        sb.append("__proto__"); // $NON-NLS-1$
        if (proto == null) traverseProto = false;
        else {
          id = proto.getValue().getId();
          var = memberNamed(id, name);
          if (var == null) sb.append('.');
        }
      }
    } catch (NoSuchVariableException nsv) {
      // don't worry about this one, it means variable with id couldn't be found
    } catch (NullPointerException npe) {
      // probably no session
    }

    // what we really want is the parent not the child variable
    if (var != null) {
      pushName(sb.toString());
      val = getSession().getWorkerSession(m_isolateId).getValue(id);
    }

    return val;
  }
  // assign the object o, the value v
  public void assign(Object o, Value v) throws NoSuchVariableException, PlayerFaultException {
    try {
      // first see if it is an internal property (avoids player calls)
      InternalProperty prop = resolveToInternalProperty(o);

      // we expect that o is a variable that can be resolved or is a specially marked internal
      // variable
      if (prop != null) {
        assignInternal(prop, v);
      } else {
        boolean wasCreateIfMissing = m_createIfMissing;
        createPseudoVariables(true);
        Variable var = null;
        try {
          var = resolveToVariable(o);
        } finally {
          createPseudoVariables(wasCreateIfMissing);
        }

        if (var == null) throw new NoSuchVariableException(m_current);

        // set the value, for the case of a variable that does not exist it will not have a type
        // so we try to glean one from v.
        FaultEvent faultEvent = var.setValue(getSession(), v.getType(), v.getValueAsString());
        if (faultEvent != null) throw new PlayerFaultException(faultEvent);
      }
    } catch (PlayerDebugException pde) {
      throw new ExpressionEvaluatorException(pde);
    }
  }
  /* returns a string consisting of formatted member names and values */
  public Object lookupMembers(Object o) throws NoSuchVariableException {
    Variable var = null;
    Value val = null;
    Variable[] mems = null;
    try {
      var = resolveToVariable(o);
      if (var != null) val = var.getValue();
      else val = resolveToValue(o);
      mems = val.getMembers(getSession());
    } catch (NullPointerException npe) {
      throw new NoSuchVariableException(o);
    } catch (PlayerDebugException pde) {
      throw new NoSuchVariableException(o); // not quite right...
    }

    StringBuilder sb = new StringBuilder();

    if (var != null) m_cache.appendVariable(sb, var, m_isolateId);
    else m_cache.appendVariableValue(sb, val, m_isolateId);

    boolean attrs = m_cache.propertyEnabled(DebugCLI.DISPLAY_ATTRIBUTES);
    if (attrs && var != null) ExpressionCache.appendVariableAttributes(sb, var);

    // [mmorearty] experimenting with hierarchical display of members
    String[] classHierarchy = val.getClassHierarchy(false);
    if (classHierarchy != null
        && getSession().getPreference(SessionManager.PREF_HIERARCHICAL_VARIABLES) != 0) {
      for (int c = 0; c < classHierarchy.length; ++c) {
        String classname = classHierarchy[c];
        sb.append(m_newline + "(Members of " + classname + ")"); // $NON-NLS-1$ //$NON-NLS-2$
        for (int i = 0; i < mems.length; ++i) {
          if (classname.equals(mems[i].getDefiningClass())) {
            sb.append(m_newline + " "); // $NON-NLS-1$
            m_cache.appendVariable(sb, mems[i], m_isolateId);
            if (attrs) ExpressionCache.appendVariableAttributes(sb, mems[i]);
          }
        }
      }
    } else {
      for (int i = 0; i < mems.length; i++) {
        sb.append(m_newline + " "); // $NON-NLS-1$
        m_cache.appendVariable(sb, mems[i], m_isolateId);
        if (attrs) ExpressionCache.appendVariableAttributes(sb, mems[i]);
      }
    }

    return sb.toString();
  }
  /**
   * All the really good stuff about finding where name exists goes here!
   *
   * <p>If name is not null, then it implies that we use the existing m_current to find a member of
   * m_current. If m_current is null Then we need to probe variable context points attempting to
   * locate name. When we find a match we set the m_current to this context
   *
   * <p>If name is null then we simply return the current context.
   */
  long determineContext(String name) throws PlayerDebugException {
    long id = Value.UNKNOWN_ID;

    // have we already resolved our context...
    if (m_current != null) {
      id = toValue().getId();
    }

    // nothing to go on, so we're done
    else if (name == null) ;

    // use the name and try and resolve where we are...
    else {
      // Each stack frame has a root variable under (BASE_ID-depth)
      // where depth is the depth of the stack.
      // So we query for our current stack depth and use that
      // as the context for our base computation
      long baseId = Value.BASE_ID;
      int depth = ((Integer) m_cache.get(DebugCLI.DISPLAY_FRAME_NUMBER)).intValue();
      baseId -= depth;

      // obtain data about our current state
      Variable contextVar = null;
      Value contextVal = null;
      Value val = null;

      // look for 'name' starting from local scope
      if ((val = locateParentForNamed(baseId, name, false)) != null) ;

      // get the this pointer, then look for 'name' starting from that point
      else if (((contextVar = locateForNamed(baseId, "this", false)) != null)
          && //$NON-NLS-1$
          (setName("this")
              && (val = locateParentForNamed(contextVar.getValue().getId(), name, true))
                  != null)) //$NON-NLS-1$
      ;

      // now try to see if 'name' exists off of _root
      else if (setName("_root")
          && (val = locateParentForNamed(Value.ROOT_ID, name, true)) != null) // $NON-NLS-1$
      ;

      // now try to see if 'name' exists off of _global
      else if (setName("_global")
          && (val = locateParentForNamed(Value.GLOBAL_ID, name, true)) != null) // $NON-NLS-1$
      ;

      // now try off of class level, if such a thing can be found
      else if (((contextVal = locate(Value.GLOBAL_ID, getCurrentPackageName(), false)) != null)
          && (setName("_global." + getCurrentPackageName())
              && (val = locateParentForNamed(contextVal.getId(), name, true))
                  != null)) //$NON-NLS-1$
      ;

      // if we found it then stake this as our context!
      if (val != null) {
        id = val.getId();
        pushName(name);
        lockName();
      }
    }

    return id;
  }