Package flash.tools.debugger.concrete

Source Code of flash.tools.debugger.concrete.PlayerSession

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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 flash.tools.debugger.concrete;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import flash.tools.debugger.AIRLaunchInfo;
import flash.tools.debugger.Frame;
import flash.tools.debugger.IDebuggerCallbacks;
import flash.tools.debugger.ILauncher;
import flash.tools.debugger.InProgressException;
import flash.tools.debugger.Isolate;
import flash.tools.debugger.IsolateController;
import flash.tools.debugger.IsolateSession;
import flash.tools.debugger.Location;
import flash.tools.debugger.NoResponseException;
import flash.tools.debugger.NotConnectedException;
import flash.tools.debugger.NotSupportedException;
import flash.tools.debugger.NotSuspendedException;
import flash.tools.debugger.PlayerDebugException;
import flash.tools.debugger.Session;
import flash.tools.debugger.SessionManager;
import flash.tools.debugger.SourceFile;
import flash.tools.debugger.SourceLocator;
import flash.tools.debugger.SuspendedException;
import flash.tools.debugger.SwfInfo;
import flash.tools.debugger.Value;
import flash.tools.debugger.ValueAttribute;
import flash.tools.debugger.Variable;
import flash.tools.debugger.VariableAttribute;
import flash.tools.debugger.VariableType;
import flash.tools.debugger.VersionException;
import flash.tools.debugger.Watch;
import flash.tools.debugger.concrete.DProtocol.ListenerIndex;
import flash.tools.debugger.events.DebugEvent;
import flash.tools.debugger.events.ExceptionFault;
import flash.tools.debugger.events.FaultEvent;
import flash.tools.debugger.expression.ECMA;
import flash.tools.debugger.expression.PlayerFaultException;
import flash.util.Trace;


public class PlayerSession implements Session, DProtocolNotifierIF, Runnable, IsolateController
{
  public static final int MAX_STACK_DEPTH = 256;
  public static final long MAX_TERMINATE_WAIT_MILLIS = 10000;

  private Socket        m_socket;
  private DProtocol      m_protocol;
  private DManager      m_manager;
  private IDebuggerCallbacks  m_debuggerCallbacks;
  private Process        m_process;
  private Map<String, Object> m_prefs; // WARNING -- accessed from multiple threads
  private static final String  s_newline = System.getProperty("line.separator"); //$NON-NLS-1$

  private volatile boolean m_isConnected; // WARNING -- accessed from multiple threads
  private volatile boolean m_isHalted; // WARNING -- accessed from multiple threads
  private volatile boolean m_incoming; // WARNING -- accessed from multiple threads
  private volatile boolean m_lastResponse;  // whether there was a reponse from the last message to the Player
  private volatile HashMap<Integer, PlayerSessionIsolateStatus> m_isolateStatus = new HashMap<Integer, PlayerSessionIsolateStatus>();
 
  private int        m_watchTransactionTag;
  private Boolean      m_playerCanCallFunctions;
  private Boolean      m_playerSupportsWatchpoints;
  private Boolean      m_playerCanBreakOnAllExceptions;
  private Boolean      m_playerSupportsConcurrency;
  private Boolean      m_playerSupportsWideLine;
 
  private ILauncher launcher;

  /**
   * The URL that was launched, or <code>null</code> if not known.  Note:
   * This is NOT the value returned by getURI().  getURI() returns the
   * URL that came from the Player, and is therefore probably the URI of
   * the SWF; but m_launchedUrl contains the URL that we tried to launch,
   * which might be an HTML wrapper, e.g. http://localhost/myapp.html
   */
  private String        m_launchUrl;

  private AIRLaunchInfo  m_airLaunchInfo; // null if this is not an AIR app

  static volatile boolean  m_debugMsgOn;    // debug ONLY; turned on with "set $debug_messages = 1"
  volatile int      m_debugMsgSize;    // debug ONLY; controlled with "set $debug_message_size = NNN"
  static volatile boolean  m_debugMsgFileOn;  // debug ONLY for file dump; turned on with "set $debug_message_file = 1"
  volatile int      m_debugMsgFileSize;  // debug ONLY for file dump; controlled with "set $debug_message_file_size = NNN"

  //FIXME: Make this concurrency aware
  /**
   * A simple cache of previous "is" and "instanceof" queries, in order to
   * avoid having to send redundant messages to the player.
   */
  private Map<String, Boolean> m_evalIsAndInstanceofCache = new HashMap<String, Boolean>();
 
  private volatile int m_lastPreIsolate = Isolate.DEFAULT_ID;
 
  private final Map<Integer, IsolateSession> m_isolateSessions;

  private static final String DEBUG_MESSAGES = "$debug_messages"; //$NON-NLS-1$
  private static final String DEBUG_MESSAGE_SIZE = "$debug_message_size"; //$NON-NLS-1$
  private static final String DEBUG_MESSAGE_FILE = "$debug_message_file"; //$NON-NLS-1$
  private static final String DEBUG_MESSAGE_FILE_SIZE = "$debug_message_file_size"; //$NON-NLS-1$

  private static final String CONSOLE_ERRORS = "$console_errors"; //$NON-NLS-1$

  private static final String FLASH_PREFIX = "$flash_"; //$NON-NLS-1$
 
  PlayerSession(Socket s, DProtocol proto, DManager manager, IDebuggerCallbacks debuggerCallbacks)
  {
    m_isConnected = false;
    m_isHalted = false;
    m_socket = s;
    m_protocol = proto;
    m_manager = manager;
    m_prefs = Collections.synchronizedMap(new HashMap<String, Object>());
    m_incoming = false;
    m_debugMsgOn = false;
    m_debugMsgSize = 16;
    m_debugMsgFileOn = false;
    m_debugMsgFileSize = 128;
    m_watchTransactionTag = 1// number that is sent for each watch transaction that occurs
    m_playerCanCallFunctions = null;
    m_debuggerCallbacks = debuggerCallbacks;
    m_isolateSessions = Collections.synchronizedMap(new HashMap<Integer, IsolateSession>());
  }
 
  private static PlayerSession createFromSocketHelper(Socket s, IDebuggerCallbacks debuggerCallbacks, DProtocol proto) throws IOException
  {
    // let the manager hear incoming messages
    DManager manager = new DManager();

    PlayerSession session = new PlayerSession(s, proto, manager, debuggerCallbacks);
    return session;
  }

  /**
   * @deprecated Use createFromSocketWithOptions
   * @param s
   * @param debuggerCallbacks
   * @return
   * @throws java.io.IOException
   */
  public static PlayerSession createFromSocket(Socket s, IDebuggerCallbacks debuggerCallbacks) throws IOException
  {
    DProtocol proto = DProtocol.createFromSocket(s);

    return createFromSocketHelper(s, debuggerCallbacks, proto);
  }
 
  /**
   * Creates a session from the socket. Sets session specific
   * socket settings and stores the callback object.
   * @param s
   * @param debuggerCallbacks
   * @param sessionManager
   * @return
   * @throws java.io.IOException
   */
  public static PlayerSession createFromSocketWithOptions(Socket s, IDebuggerCallbacks debuggerCallbacks, SessionManager sessionManager) throws IOException
  {
    DProtocol proto = DProtocol.createFromSocket(s, sessionManager);

    return createFromSocketHelper(s, debuggerCallbacks, proto);
  }

  /* getter */
  public DMessageCounter    getMessageCounter()    { return m_protocol.getMessageCounter(); }
  public String        getURI()        { return m_manager.getURI(); }
  public boolean        playerSupportsGet()    { return m_manager.isGetSupported(); }
    public int                  playerVersion()         { return m_manager.getVersion(); }
    public SourceLocator        getSourceLocator()      { return m_manager.getSourceLocator(); }

  /*
   * @see flash.tools.debugger.Session#setSourceLocator(flash.tools.debugger.SourceLocator)
   */
  public void setSourceLocator(SourceLocator sourceLocator)
  {
    m_manager.setSourceLocator(sourceLocator);
  }

  /**
   * If the manager started the process for us, then note it here. We will attempt to kill
   * it when we go down
   */
  void setProcess(Process proc)
  {
    m_process = proc;
  }

  /*
   * @see flash.tools.debugger.Session#getLaunchProcess()
   */
  public Process getLaunchProcess()
  {
    return m_process;
  }

  /**
   * Set preference
   * If an invalid preference is passed, it will be silently ignored.
   */
  public void      setPreferences(Map<String, ? extends Object> map)  { m_prefs.putAll(map); mapBack(); }
  public Set<String>  keySet()                { return m_prefs.keySet(); }
  public Object    getPreferenceAsObject(String pref)    { return m_prefs.get(pref); }

  /**
   * Set a property. Special logic for debug message boolean
   */
  public void setPreference(String pref, int value)
  {
    m_prefs.put(pref, new Integer(value));
    mapBack();

    // change in console messages?
    if (pref.equals(CONSOLE_ERRORS))
      sendConsoleErrorsAsTrace(value == 1);

    // generic message for flash player wherein "$flash_xxx" causes "xxx" to be sent
    if (pref.startsWith(FLASH_PREFIX))
      sendOptionMessage(pref.substring(FLASH_PREFIX.length()), Integer.toString(value));
  }

  // helper for mapBack()
  private int mapBackOnePreference(String preferenceName, int defaultValue)
  {
    Object prefValue = getPreferenceAsObject(preferenceName);
    if (prefValue != null)
      return ((Integer)prefValue).intValue();
    else
      return defaultValue;
  }

  // helper for mapBack()
  private boolean mapBackOnePreference(String preferenceName, boolean defaultValue)
  {
    Object prefValue = getPreferenceAsObject(preferenceName);
    if (prefValue != null)
      return ((Integer)prefValue).intValue() != 0 ? true : false;
    else
      return defaultValue;
  }

  // look for preferences, that map back to variables
  private void mapBack()
  {
    m_debugMsgOn = mapBackOnePreference(DEBUG_MESSAGES, m_debugMsgOn);
    m_debugMsgSize = mapBackOnePreference(DEBUG_MESSAGE_SIZE, m_debugMsgSize);

    m_debugMsgFileOn = mapBackOnePreference(DEBUG_MESSAGE_FILE, m_debugMsgFileOn);
    m_debugMsgFileSize = mapBackOnePreference(DEBUG_MESSAGE_FILE_SIZE, m_debugMsgFileSize);
  }

  public int getPreference(String pref)
  {
    int val = 0;
    Integer i = (Integer)m_prefs.get(pref);
    if (i == null)
      throw new NullPointerException();
    else
      val = i.intValue();
    return val;
  }


  /*
   * @see flash.tools.debugger.Session#isConnected()
   */
  public boolean isConnected()
  {
    return m_isConnected;
  }

  /*
   * @see flash.tools.debugger.Session#isSuspended()
   */
  public boolean isSuspended() throws NotConnectedException
  {
    if (!isConnected())
      throw new NotConnectedException();

    return m_isHalted;
  }
 
  /*
   * @see flash.tools.debugger.Session#isIsolateSuspended()
   */
  public boolean isWorkerSuspended(int isolateId) throws NotConnectedException
  {
    if (isolateId == Isolate.DEFAULT_ID)
      return isSuspended();
   
    if (!isConnected())
      throw new NotConnectedException();
   
    if (m_isolateStatus.containsKey(isolateId)) {
      return m_isolateStatus.get(isolateId).m_isHalted;
    }
   
    return false;
  }

  /**
   * Start up the session listening for incoming messages on the socket
   */
  public boolean bind() throws VersionException
  {
    boolean bound = false;

    if (m_isConnected)
      return false;
   
    // mark that we are connected
    m_isConnected = true;

    // attach us to the pipe (we are last to ensure that DManager and msg counter
    // get updated first
    m_protocol.addListener(ListenerIndex.PlayerSession, this);

    // start up the receiving thread
    bound = m_protocol.bind();

    // transmit our first few adjustment messages
    sendStopWarning();
    sendStopOnFault();
    sendEnumerateOverride();
    sendFailureNotify();
    sendInvokeSetters();
    sendSwfloadNotify();
    sendGetterTimeout();
    sendSetterTimeout();
    boolean responded = sendSquelch(true, Isolate.DEFAULT_ID);

    // now note in our preferences whether get is working or not.
    setPreference(SessionManager.PLAYER_SUPPORTS_GET, playerSupportsGet() ? 1 : 0);
    if (supportsConcurrency()) {
      sendConcurrentDebugger();
    }
   
    if (supportsWideLineNumbers()) {
      sendWideLineDebugger();
    }

    // Spawn a background thread which fetches the SWF and SWD
    // from the Player and uses them to build function name tables
    // for each source file
    Thread t = new Thread(this, "SWF/SWD reader"); //$NON-NLS-1$
    t.setDaemon(true);
    t.start();

    // we're probably using a bad version
    if (!responded)
      throw new VersionException();

    return bound;
  }

  /**
   * Permanently stops the debugging session and breaks the
   * connection to the Player
   */
  public void unbind()
  {
    unbind(false);
  }

  /**
   * @param requestTerminate
   *            if true, and if the player to which we are attached is capable
   *            of terminating itself (e.g. Adobe AIR), then the player will
   *            be told to terminate.
   * @return true if the player is capable of terminating itself and has been
   *         told to do so
   */
  private boolean unbind(boolean requestTerminate)
  {
    // If the caller asked us to terminate the player, then we first check
    // whether the player to which we are connected is capable of that.
    // (The web-based players are not; Adobe AIR is.)
    requestTerminate = requestTerminate && playerCanTerminate();
    DMessage dm = DMessageCache.alloc(1);
    dm.setType(DMessage.OutExit);
    dm.putByte((byte)(requestTerminate ? 1 : 0));
    sendMessage(dm);

    // unbind from the socket, so that we don't receive any more messages
    m_protocol.unbind();

    // kill the socket
    try { m_socket.close(); } catch(IOException io) {}

    m_isConnected = false;
    m_isHalted = false;

    return requestTerminate; // true if player was told to terminate
  }

  /**
   * Execute the specified AppleScript by passing it to /usr/bin/osascript.
   *
   * @param appleScript
   *            the AppleScript to execute, as a series of lines
   * @param argv
   *            any arguments; these can be accessed from within your
   *            AppleScript via "item 1 or argv", "item 2 of argv", etc.
   * @return any text which was sent to stdout by /usr/bin/osascript, with the
   *         trailing \n already removed
   */
  private String executeAppleScript(String[] appleScript, String[] argv)
  {
    StringBuilder retval = new StringBuilder();
    try
    {
      List<String> execArgs = new LinkedList<String>();
      // "osascript" is the command-line way of executing AppleScript.
      execArgs.add("/usr/bin/osascript"); //$NON-NLS-1$
      execArgs.add("-"); //$NON-NLS-1$
      if (argv != null)
      {
        for (int i=0; i<argv.length; ++i)
          execArgs.add(argv[i]);
      }
      Process osascript = Runtime.getRuntime().exec(execArgs.toArray(new String[execArgs.size()]));
      // feed our AppleScript code to osascript's stdin
      OutputStream outputStream = osascript.getOutputStream();
      PrintWriter writer = new PrintWriter(outputStream, true);
      writer.println("on run argv"); //$NON-NLS-1$ // this gives the name "argv" to the command-line args
      for (int i=0; i<appleScript.length; ++i)
        writer.println(appleScript[i]);
      writer.println("end run"); //$NON-NLS-1$
      writer.close();
      InputStreamReader reader = new InputStreamReader(osascript.getInputStream());
      int ch;
      while ( (ch=reader.read()) != -1 )
        retval.append((char)ch);
    }
    catch (IOException e)
    {
      // ignore
    }
    return retval.toString().replaceAll("\n$", ""); //$NON-NLS-1$ //$NON-NLS-2$
  }

  /**
   * Execute the specified AppleScript by passing it to /usr/bin/osascript.
   *
   * @param appleScriptFilename
   *            The name of the file containing AppleScript to execute.  This
   *            must be relative to PlayerSession.java.
   * @param argv
   *            any arguments; these can be accessed from within your
   *            AppleScript via "item 1 or argv", "item 2 of argv", etc.
   * @return any text which was sent to stdout by /usr/bin/osascript, with the
   *         trailing \n already removed
   * @throws java.io.IOException
   */
  private String executeAppleScript(String appleScriptFilename, String[] argv) throws IOException
  {
    InputStream stm = null;
    try {
      stm = PlayerSession.class.getResourceAsStream(appleScriptFilename);
      BufferedReader reader = new BufferedReader(new InputStreamReader(stm));
      String line;
      List<String> appleScriptLines = new ArrayList<String>();
      while ( (line=reader.readLine()) != null )
        appleScriptLines.add(line);
      String[] lines = appleScriptLines.toArray(new String[appleScriptLines.size()]);
      return executeAppleScript(lines, argv);
    } finally {
      if (stm != null) {
        stm.close();
      }
    }
  }

  /**
   * Checks whether the specified Macintosh web browser is currently
   * running.  You should only call this function if you have already
   * checked that you are running on a Mac.
   *
   * @param browserName a name, e.g. "Safari", "Firefox", "Camino"
   * @return true if currently running
   */
  private Set<String> runningApplications()
  {
    String running = executeAppleScript(
      new String[]
          {
        "tell application \"System Events\"", //$NON-NLS-1$
        "  name of processes", //$NON-NLS-1$
        "end tell" //$NON-NLS-1$
          },
          null
    );
    String[] apps = running.split(", "); //$NON-NLS-1$
    Set<String> retval = new HashSet<String>();
    for (int i=0; i<apps.length; ++i)
      retval.add(apps[i]);
    return retval;
  }

  /**
   * Destroys all objects related to the connection
   * including the process that was tied to this
   * session via SessionManager.launch(), if it
   * exists.
   */
  public void terminate()
  {
    boolean playerWillTerminateItself = false;

    // unbind first
    try
    {
      // Tell player to end session.  Note that this is just a hint, and will often
      // do nothing.  For example, the Flash player running in a browser will
      // currently never terminate when you tell it to, but the AIR player will
      // terminate.
      playerWillTerminateItself = unbind(true);
    } catch(Exception e)
    {
    }

    if (!playerWillTerminateItself)
    {
      if (System.getProperty("os.name").toLowerCase().startsWith("mac os x")) //$NON-NLS-1$ //$NON-NLS-2$
      {
        if (m_airLaunchInfo != null)
        {
          // nothing we need to do -- Process.destroy() will kill the AIR app
        }
        else if (m_launchUrl != null && m_launchUrl.length() > 0)
        {
          boolean closedAnyWindows = false;
          Set<String> runningApps = runningApplications();

          if (!closedAnyWindows && runningApps.contains("Safari")) //$NON-NLS-1$
          {
            try {
              String url = m_launchUrl.replaceAll(" ", "%20"); //$NON-NLS-1$ //$NON-NLS-2$
              String safariClosedAnyWindows = executeAppleScript("appleScriptCloseSafariWindow.txt", new String[] { url }); //$NON-NLS-1$
              if ("true".equals(safariClosedAnyWindows)) { //$NON-NLS-1$                
                closedAnyWindows = true;               
              }
              else if ( "appquit".equals(safariClosedAnyWindows) ) { //$NON-NLS-1$
                closedAnyWindows = true;
                //we closed Safari, verify safari was closed
                runningApps = waitForMacAppQuit("Safari"); //$NON-NLS-1$
              }
            } catch (IOException e) {
              // ignore
            }
          }
                   
          if (!closedAnyWindows && runningApps.contains("Camino")) //$NON-NLS-1$
          {
            // For local file: URLs, Camino uses "file://localhost/..." instead of "file:///..."
            String url = m_launchUrl.replaceFirst("^file:///", "file://localhost/"); //$NON-NLS-1$ //$NON-NLS-2$
            try {
              String caminoClosedAnyWindows = executeAppleScript("appleScriptCloseCaminoWindow.txt", new String[] { url }); //$NON-NLS-1$
              if ("true".equals(caminoClosedAnyWindows)) { //$NON-NLS-1$               
                closedAnyWindows = true;
              }
              else if ( "appquit".equals(caminoClosedAnyWindows) ) { //$NON-NLS-1$
                closedAnyWindows = true;
                //we closed camino, verify camino was closed
                runningApps = waitForMacAppQuit("Camino"); //$NON-NLS-1$
              }
               
            } catch (IOException e) {
              // ignore
            }
          }

          // The standalone player on the Mac has gone through several name changes,
          // so we have to look for all of these.
          String[] macStandalonePlayerNames =
          {
            "Flash Player Debugger"// New name as of Player 10.1  //$NON-NLS-1$
            "Flash Player",        // New name as of 12/4/06    //$NON-NLS-1$
            "SAFlashPlayer",      // An older name        //$NON-NLS-1$
            "standalone"        // Another older name      //$NON-NLS-1$
          };

          for (int i=0; !closedAnyWindows && i<macStandalonePlayerNames.length; ++i)
          {
            if (runningApps.contains(macStandalonePlayerNames[i]))
            {
              executeAppleScript(new String[] { "tell application \"" + macStandalonePlayerNames[i] + "\" to quit" }, null); //$NON-NLS-1$ //$NON-NLS-2$
              waitForMacAppQuit(macStandalonePlayerNames[i]);
              closedAnyWindows = true;
            }
          }
        }
      }

      // if we have a process pop it
      if (m_process != null)
      {
        try
        {
          //if a launcher is set for handling the launcher operations then use it.
          if(null != launcher)
          {
            m_debuggerCallbacks.terminateDebugTarget(m_process,launcher);
          }
          else
          {
            m_debuggerCallbacks.terminateDebugTarget(m_process);
          }
        }
        catch (IOException e)
        {
          // ignore
        }
      }
    }
    else if (m_process != null) {
      try {
        m_process.waitFor();
      }
      catch (Exception e) {
      }
    }

    // now clear it all
    m_isConnected = false;
    m_isHalted = false;
  }

  /**
   * Utility function to wait for a mac application to quit.
   * This waits for a maximum of MAX_TERMINATE_WAIT_MILLIS.
   *
   * Waiting is important because applescript "quit" is not
   * synchronous and launching a URL while the browser is
   * quitting is not good. (See FB-21879)
   * @return Set<String> of running applications.
   */
  private Set<String> waitForMacAppQuit(String browser) {
    Set<String> runningApps;
    boolean appClosed = true;
    final long startMillis = System.currentTimeMillis();   
    final long waitMillis = 100;
    do {
      runningApps = runningApplications();
      if ( runningApps.contains(browser) ) {
        appClosed = false;
       
        try {
          Thread.sleep(waitMillis);         
        } catch (InterruptedException e) {
          return runningApps;
        }
       
        long currentMillis = System.currentTimeMillis();
       
        if ( currentMillis - startMillis >= MAX_TERMINATE_WAIT_MILLIS )
          break;
      }
      else {
        appClosed = true;
      }
    }
    while ( !appClosed );
    return runningApps;
  }

  /*
   * @see flash.tools.debugger.Session#resume()
   */
  public void resume() throws NotSuspendedException, NotConnectedException, NoResponseException
  {
    resumeWorker(Isolate.DEFAULT_ID);
  }

  /*
   * @see flash.tools.debugger.Session#suspend()
   */
  public void suspend() throws SuspendedException, NotConnectedException, NoResponseException
  {
    suspendWorker(Isolate.DEFAULT_ID);
  }

  /**
   * Obtain all the suspend information
   */
  public DSuspendInfo getSuspendInfo()
  {
    return getSuspendInfoIsolate(Isolate.DEFAULT_ID);
  }

  /**
   * Return the reason that the player has suspended itself.
   */
  public int suspendReason()
  {
    DSuspendInfo info = getSuspendInfo();
    return info.getReason();
  }

  /**
   * Return the offset in which the player has suspended itself.  The BreakReason
   * message contains both reason and offset.
   */
  public int getSuspendOffset()
  {
    DSuspendInfo info = getSuspendInfo();
    return info.getOffset();
  }

  /**
   * Return the offset in which the player has suspended itself.  The BreakReason
   * message contains both reason and offset.
   */
  public int getSuspendActionIndex()
  {
    DSuspendInfo info = getSuspendInfo();
    return info.getActionIndex();
  }

  /**
   * Obtain information about the various SWF(s) that have been
   * loaded into the Player, for this session.
   *
   * Note: As SWFs are loaded by the Player a SwfLoadedEvent is
   * fired.  At this point, a call to getSwfInfo() will provide
   * updated information.
   *
   * @return array of records describing the SWFs
   */
  public SwfInfo[] getSwfs() throws NoResponseException
  {
    return getSwfsWorker(Isolate.DEFAULT_ID);
  }

  /**
   * Request information on a particular swf, used by DSwfInfo
   * to fill itself correctly
   */
  public void requestSwfInfo(int at, int isolateId) throws NoResponseException
  {
    // nope don't have it...might as well go out and ask for all of them.
    DMessage dm = DMessageCache.alloc(4);
    dm.setType( DMessage.OutSwfInfo );
    dm.setTargetIsolate(isolateId);
    dm.putWord(at);
    dm.putWord(0)// rserved

    int to = getPreference(SessionManager.PREF_CONTEXT_RESPONSE_TIMEOUT);

    if (!simpleRequestResponseMessage(dm, DMessage.InSwfInfo, to))
      throw new NoResponseException(to);
  }

  /**
   * Request a set of actions from the player
   */
  public byte[] getActions(int which, int at, int len) throws NoResponseException
  {
    byte[] actions = null;

    // send a actions message
    DMessage dm = DMessageCache.alloc(12);
    dm.setType( DMessage.OutGetActions );
    dm.putWord(which);
    dm.putWord(0); // rsrvd
    dm.putDWord(at);
    dm.putDWord(len);

    // request action bytes
    int to = getPreference(SessionManager.PREF_CONTEXT_RESPONSE_TIMEOUT);
    if (simpleRequestResponseMessage(dm, DMessage.InGetActions, to))
      actions = m_manager.getActions();
    else
      throw new NoResponseException(to);

    return actions;
  }

  /*
   * @see flash.tools.debugger.Session#stepInto()
   */
  public void stepInto() throws NotSuspendedException, NoResponseException, NotConnectedException
  {
    stepIntoWorker(Isolate.DEFAULT_ID);
  }

  /*
   * @see flash.tools.debugger.Session#stepOut()
   */
  public void stepOut() throws NotSuspendedException, NoResponseException, NotConnectedException
  {
    stepOutWorker(Isolate.DEFAULT_ID);
  }

  /*
   * @see flash.tools.debugger.Session#stepOver()
   */
  public void stepOver() throws NotSuspendedException, NoResponseException, NotConnectedException
  {
    stepOverWorker(Isolate.DEFAULT_ID);
  }

  /*
   * @see flash.tools.debugger.Session#stepContinue()
   */
  public void stepContinue() throws NotSuspendedException, NoResponseException, NotConnectedException
  {
    if (!isSuspended())
      throw new NotSuspendedException();

    // send a step-continue message and then wait for the Flash player to tell us that is has
    // resumed execution
    if (!simpleRequestResponseMessage(DMessage.OutStepContinue, DMessage.InContinue))
      throw new NoResponseException(getPreference(SessionManager.PREF_RESPONSE_TIMEOUT));
  }

    /**
     * Sends a request to the player to obtain function names.
     * The resultant message end up populating the function name array
     * for the given DModule.
     *
     * @param moduleId
     * @param lineNbr
     * @return
     */
    public void requestFunctionNames(int moduleId, int lineNbr, int isolateId) throws VersionException, NoResponseException
    {
        // only player 9 supports this message
        if (m_manager.getVersion() >= 9)
        {
            DMessage dm = DMessageCache.alloc(8);
            dm.setType(DMessage.OutGetFncNames);
            dm.setTargetIsolate(isolateId);
            dm.putDWord(moduleId);
            dm.putDWord(lineNbr);

            if (!simpleRequestResponseMessage(dm, DMessage.InGetFncNames))
                throw new NoResponseException(0);
        }
        else
        {
            throw new VersionException();
        }
    }

  /**
   * From a given file identifier return a source file object
   */
  public SourceFile getFile(int fileId, int isolateId)
  {
    return m_manager.getSource(fileId, isolateId);
  }

  /**
   * Get a list of breakpoints
   */
  public Location[] getBreakpointList()
  {
    return m_manager.getBreakpoints(Isolate.DEFAULT_ID);
  }

  /*
   * @see flash.tools.debugger.Session#setBreakpoint(int, int)
   */
  public Location setBreakpoint(int fileId, int lineNum) throws NoResponseException, NotConnectedException
  {
    return setBreakpointWorker(fileId, lineNum, Isolate.DEFAULT_ID);
  }

  /*
   * @see flash.tools.debugger.Session#clearBreakpoint(flash.tools.debugger.Location)
   */
  public Location clearBreakpoint(Location local)
  {
    /* first find it */
    SourceFile source = local.getFile();
    int fileId = source.getId();
    int lineNum = local.getLine();
    int bp = DLocation.encodeId(fileId, lineNum);
    int isolateId = local.getIsolateId();
    Location l = null;
    l = m_manager.getBreakpoint(bp, isolateId);

    if (l != null)
    {
      /* send the message */
      int wideLineSize = 0;
      if (supportsWideLineNumbers())
        wideLineSize = 4;
      DMessage dm = DMessageCache.alloc(8 + wideLineSize);
      dm.setType(DMessage.OutRemoveBreakpoints);
      dm.setTargetIsolate(isolateId);
      dm.putDWord(1);
      if (!supportsWideLineNumbers())
        dm.putDWord(bp);
      else {
        dm.putDWord(fileId);
        dm.putDWord(lineNum);
      }
      sendMessage(dm);

      /* no callback from the player so we remove it ourselves */
      m_manager.removeBreakpoint(bp, isolateId);
    }
    return l;
  }

  /*
   * @see flash.tools.debugger.Session#getWatchList()
   */
  public Watch[] getWatchList() throws NoResponseException, NotConnectedException
  {
    return getWatchListWorker(Isolate.DEFAULT_ID);
  }
 
  /*
   * @see flash.tools.debugger.Session#getWatchList()
   */
  public Watch[] getWatchListWorker(int isolateId) throws NoResponseException, NotConnectedException
  {
      return m_manager.getWatchpoints(isolateId);
  }

  private Watch setWatch(long varId, String memberName, int kind, int isolateId) throws NoResponseException, NotConnectedException, NotSupportedException
  {
    // we really have two cases here, one where we add a completely new
    // watchpoint and the other where we modify an existing one.
    // In either case the DManager logic is such that the last watchpoint
    // in the list will contain our id if successful.
    Watch w = null;
    int tag = m_watchTransactionTag++;

    if (addWatch(varId, memberName, kind, tag, isolateId))
    {
      // good that we got a response now let's check that
      // it actually worked.
      int count = m_manager.getWatchpointCount(isolateId);
      if (count > 0)
      {
        DWatch lastWatch = m_manager.getWatchpoint(count-1, isolateId);
        if (lastWatch.getTag() == tag)
          w = lastWatch;
      }
    }
    return w;
  }
 
  /*
   * @see flash.tools.debugger.Session#setWatch(flash.tools.debugger.Variable, java.lang.String, int)
   */
  public Watch setWatch(Value v, String memberName, int kind) throws NoResponseException, NotConnectedException, NotSupportedException
  {
    return setWatch(v.getId(), memberName, kind, v.getIsolateId());
  }

  public Watch setWatch(Watch watch) throws NoResponseException, NotConnectedException, NotSupportedException
  {
    return setWatch(watch.getValueId(), watch.getMemberName(), watch.getKind(), watch.getIsolateId());
  }

  /*
   * @see flash.tools.debugger.Session#clearWatch(flash.tools.debugger.Watch)
   */
  public Watch clearWatch(Watch watch) throws NoResponseException, NotConnectedException
  {
    Watch[] list = getWatchListWorker(watch.getIsolateId());
    Watch w = null;
    if ( removeWatch(watch.getValueId(), watch.getMemberName(), watch.getIsolateId()) )
    {
      // now let's first check the size of the list, it
      // should now be one less
      if (m_manager.getWatchpointCount(watch.getIsolateId()) < list.length)
      {
        // ok we made a change. So let's compare list and see which
        // one went away
        Watch[] newList = getWatchListWorker(watch.getIsolateId());
        for(int i=0; i<newList.length; i++)
        {
          // where they differ is the missing one
          if (list[i] != newList[i])
          {
            w = list[i];
            break;
          }
        }

        // might be the last one...
        if (w == null)
          w = list[list.length-1];
      }
    }
    return w;
  }

  /*
   * @see flash.tools.debugger.Session#getVariableList()
   */
  public Variable[] getVariableList() throws NotSuspendedException, NoResponseException, NotConnectedException, VersionException
  {
    return getVariableListWorker(Isolate.DEFAULT_ID);
  }
 
  public Variable[] getVariableListWorker(int isolateId) throws NotSuspendedException, NoResponseException, NotConnectedException, VersionException
  {
    // make sure the player has stopped and send our message awaiting a response
    if (!isWorkerSuspended(isolateId))
      throw new NotSuspendedException();

    requestFrame(0, isolateId)// our 0th frame gets our local context

    // now let's request all of the special variables too
    getValueWorker(Value.GLOBAL_ID, isolateId);
    getValueWorker(Value.THIS_ID, isolateId);
    getValueWorker(Value.ROOT_ID, isolateId);

    // request as many levels as we can get
    int i = 0;
    Value v = null;
    do
    {
      v = getValueWorker(Value.LEVEL_ID-i, isolateId);
    }
    while( i++ < 128 && v != null);

    // now that we've primed the DManager we can request the base variable whose
    // children are the variables that are available
    v = m_manager.getValue(Value.BASE_ID, isolateId);
    if (v == null)
      throw new VersionException();
    return v.getMembers(this);
  }

  /*
   * @see flash.tools.debugger.Session#getFrames()
   */
  public Frame[] getFrames() throws NotConnectedException
  {
    return m_manager.getFrames(Isolate.DEFAULT_ID);
  }

  /**
   * Asks the player to return information regarding our current context which includes
   * this pointer, arguments for current frame, locals, etc.
   */
  public void requestFrame(int depth, int isolateId) throws NotSuspendedException, NoResponseException, NotConnectedException
  {
    if (playerSupportsGet())
    {
      if (!isWorkerSuspended(isolateId))
        throw new NotSuspendedException();

      int timeout = getPreference(SessionManager.PREF_CONTEXT_RESPONSE_TIMEOUT);

      DMessage dm = DMessageCache.alloc(4);
      dm.setType(DMessage.OutGetFrame);
      dm.setTargetIsolate(isolateId);
      dm.putDWord(depth)// depth of zero
      if (!simpleRequestResponseMessage(dm,  DMessage.InFrame, timeout)) {
        throw new NoResponseException(timeout);
      }

      pullUpActivationObjectVariables(depth, isolateId);
    }
  }
 
  /**
   * The compiler sometimes creates special local variables called
   * "activation objects."  When it decides to do this (e.g. if the
   * current function contains any anonymous functions, try/catch
   * blocks, complicated E4X expressions, or "with" clauses), then
   * all locals and arguments are actually stored as children of
   * this activation object, rather than the usual way.
   *
   * We need to hide this implementation detail from the user.  So,
   * if we find any activation objects among the locals of the current
   * function, then we will "pull up" its members, and represent them
   * as if they were actually args/locals of the function itself.
   *
   * @param depth the depth of the stackframe we are fixing; 0 is topmost
   */
  private void pullUpActivationObjectVariables(int depth, int isolateId) throws NotSuspendedException, NoResponseException, NotConnectedException
  {
    DValue frame = m_manager.getValue(Value.BASE_ID-depth, isolateId);
    if (frame == null)
      return;
    DStackContext context = m_manager.getFrame(depth, isolateId);   
    DVariable[] frameVars = (DVariable[]) frame.getMembers(this);
    Map<String, DVariable> varmap = new LinkedHashMap<String, DVariable>(frameVars.length); // preserves order
    List<DVariable> activationObjects = new ArrayList<DVariable>();
    Pattern activationObjectNamePattern = Pattern.compile("^.*\\$\\d+$"); //$NON-NLS-1$

    // loop through all frame variables, and separate them into two
    // groups: activation objects, and all others (locals and arguments)
    for (int i=0; i<frameVars.length; ++i)
    {
      DVariable member = frameVars[i];
      Matcher matcher = activationObjectNamePattern.matcher(member.getName());
      if (matcher.matches())
        activationObjects.add(member);
      else
        varmap.put(member.getName(), member);
    }

    // If there are no activation objects, then we don't need to do anything
    if (activationObjects.size() == 0)
      return;

    // overwrite existing args and locals with ones pulled from the activation objects
    for (int i=0; i<activationObjects.size(); ++i)
    {
      DVariable activationObject = activationObjects.get(i);
      DVariable[] activationMembers = (DVariable[]) activationObject.getValue().getMembers(this);
      for (int j=0; j<activationMembers.length; ++j)
      {
        DVariable member = activationMembers[j];
        int attributes = member.getAttributes();

        // For some odd reason, the activation object often contains a whole bunch of
        // other variables that we shouldn't be displaying.  I don't know what they
        // are, but I do know that they are all marked "static".
        if ((attributes & VariableAttribute.IS_STATIC) != 0)
          continue;

        // No matter what the activation object member's scope is, we want all locals
        // and arguments to be considered "public"
        attributes &= ~(VariableAttribute.PRIVATE_SCOPE | VariableAttribute.PROTECTED_SCOPE | VariableAttribute.NAMESPACE_SCOPE);
        attributes |= VariableAttribute.PUBLIC_SCOPE;
        member.setAttributes(attributes);

        String name = member.getName();
        DVariable oldvar = varmap.get(name);
        int vartype;
        if (oldvar != null)
          vartype = oldvar.getAttributes() & (VariableAttribute.IS_ARGUMENT | VariableAttribute.IS_LOCAL);
        else
          vartype = VariableAttribute.IS_LOCAL;
        member.setAttributes(member.getAttributes() | vartype);
        varmap.put(name, member);
      }

      context.convertLocalToActivationObject(activationObject);
    }

    for (DVariable var: varmap.values())
    {
      frame.addMember(var);
      if (var.isAttributeSet(VariableAttribute.IS_LOCAL))
      {
        context.addLocal(var);
      }
      else if (var.isAttributeSet(VariableAttribute.IS_ARGUMENT))
      {
        if (var.getName().equals("this")) //$NON-NLS-1$
          context.setThis(var);
        else
          context.addArgument(var);
      }
    }
  }

  /*
   * @see flash.tools.debugger.Session#getValue(int)
   */
  public Value getValue(long valueId) throws NotSuspendedException, NoResponseException, NotConnectedException
  {
    return getValueWorker(valueId, Isolate.DEFAULT_ID);
  }
 
  public Value getValueWorker(long valueId, int isolateId) throws NotSuspendedException, NoResponseException, NotConnectedException
  {
    DValue val = null;

    if (!isWorkerSuspended(isolateId))
      throw new NotSuspendedException();

    // get it from cache if we can
    val = m_manager.getValue(valueId, isolateId);

    if (val == null)
    {
      // if a special variable, then we need to trigger a local frame call, otherwise just use id to get it
      if (valueId < Value.UNKNOWN_ID)
      {
        requestFrame(0, isolateId); // force our current frame to get populated, BASE_ID will be available
      }
      else if (valueId > Value.UNKNOWN_ID)
      {
        requestVariable(valueId, null, isolateId);
      }

      // after all this we should have our variable cache'd so try again if it wasn't there the first time
      val = m_manager.getValue(valueId, isolateId);
    }

    return val;
  }

  /**
   * Returns the current value object for the given id; never requests it from the player.
   */
  public Value getRawValue(long valueId, int isolateId)
  {
    return m_manager.getValue(valueId, isolateId);
  }

  /**
   * Returns the previous value object for the given id -- that is, the value that that
   * object had the last time the player was suspended.  Never requests it from the
   * player (because it can't, of course).  Returns <code>null</code> if we don't have
   * a value for that id.
   */
  public Value getPreviousValue(long valueId, int isolateId)
  {
    return m_manager.getPreviousValue(valueId, isolateId);
  }

  /**
   * Launches a request to obtain all the members of the specified variable, and
   * store them in the variable which would be returned by
   * {@link flash.tools.debugger.concrete.DManager#getVariable(long)}.
   *
   * @param valueId id of variable whose members we want; underlying Variable must
   * already be known by the PlayerSessionManager.
   *
   * @throws flash.tools.debugger.NoResponseException
   * @throws flash.tools.debugger.NotConnectedException
   * @throws flash.tools.debugger.NotSuspendedException
   */
  void obtainMembers(long valueId, int isolateId) throws NoResponseException, NotConnectedException, NotSuspendedException
  {
    if (!isWorkerSuspended(isolateId))
      throw new NotSuspendedException();

    // Get it from cache.  Normally, this should never fail; however, in
    // the case of Flex Builder, which is multithreaded, it is possible
    // that a thread has called this even after a different thread has
    // single-stepped, so that the original variable is no longer valid.
    // So, we'll check for a null return value.
    DValue v = m_manager.getValue(valueId, isolateId);

    if (v != null && !v.membersObtained())
    {
      requestVariable(valueId, null, false, true, isolateId);
    }
  }

  public Value getGlobal(String name) throws NotSuspendedException, NoResponseException, NotConnectedException
  {
    return getGlobalWorker(name, Isolate.DEFAULT_ID);
  }
 
  public Value getGlobalWorker(String name, int isolateId) throws NotSuspendedException, NoResponseException, NotConnectedException
  {
    Value v = getValue(0, name, isolateId);

    if (v==null || v.getType() == VariableType.UNDEFINED)
      return null;
    else
      return v;
  }

  /**
   * Get the value of the variable named 'name' using varId
   * as the context id for the Variable.
   *
   * This call is used to fire getters, where the id must
   * be that of the original object and not the object id
   * of where the getter actually lives.  For example
   * a getter a() may live under o.__proto__.__proto__
   * but you must use the id of o and the name of 'a'
   * in order for the getter to fire correctly.  [Note: This
   * paragraph was written for AS2; __proto__ doesn't exist
   * in AS3.  TODO: revise this paragraph]
   */
  public Value getValue(long varId, String name, int isolateId) throws NotSuspendedException, NoResponseException, NotConnectedException
  {
    Value v = null;
    if (isWorkerSuspended(isolateId))
    {
      int fireGetter = getPreference(SessionManager.PREF_INVOKE_GETTERS);

      // disable children attaching to parent variables and clear our
      // most recently seen variable
      m_manager.clearLastVariable(isolateId);
      m_manager.enableChildAttach(false, isolateId);

      try
      {
        requestVariable(varId, name, (fireGetter != 0), false, isolateId);

        DVariable lastVariable = m_manager.lastVariable(isolateId);
        if (lastVariable != null)
          v = lastVariable.getValue();
        else
          v = DValue.forPrimitive(Value.UNDEFINED, isolateId);
      }
      catch (NoResponseException e)
      {
        if (fireGetter != 0)
        {
          // We fired a getter -- most likely, what happened is that that getter
          // (which is actual code in the user's movie) just took too long to
          // calculate its value.  So rather than throwing an exception, we store
          // some error text for the value of the variable itself.
          //
          // TODO [mmorearty 4/20/06] Even though I wrote the below code, I now
          // am wondering if it is incorrect that I am calling addVariableMember(),
          // because in every other case, this function does not add members to
          // existing objects.  Need to revisit this.
          v = new DValue(VariableType.STRING, "String", "String", ValueAttribute.IS_EXCEPTION, //$NON-NLS-1$ //$NON-NLS-2$
              e.getLocalizedMessage(), isolateId);
          if (varId != 0) {
            DVariable var = new DVariable(name, (DValue)v, isolateId);
            m_manager.enableChildAttach(true, isolateId);
            m_manager.addVariableMember(varId, var, isolateId);
          }
        }
        else
        {
          throw e; // re-throw
        }
      }
      finally
      {
        // reset our attach flag, so that children attach to parent variables.
        m_manager.enableChildAttach(true, isolateId);
      }
    }
    else
      throw new NotSuspendedException();

    return v;
  }

  private void requestVariable(long id, String name, int isolateId) throws NoResponseException, NotConnectedException, NotSuspendedException
  {
    requestVariable(id, name, false, false, isolateId);
  }

  /**
   * @param thisValue the value of the "this" pointer; meaningless if isConstructor is true
   * @param isConstructor whether we're calling a constructor as opposed to a regular function
   * @param funcname the name of the function to call (or class whose constructor we're calling)
   * @param args the args to the function
   * @return the return value of the function
   */
  private Value callFunction(Value thisValue, boolean isConstructor, String funcname, Value[] args, int isolateId) throws PlayerDebugException
  {
    if (!isWorkerSuspended(isolateId))
      throw new NotSuspendedException();

    if (!playerCanCallFunctions(isolateId))
      throw new NotSupportedException(PlayerSessionManager.getLocalizationManager().getLocalizedTextString("functionCallsNotSupported")); //$NON-NLS-1$

    // name = getRawMemberName(id, name);

    m_manager.clearLastFunctionCall(isolateId);

    DMessage dm = buildCallFunctionMessage(isConstructor, thisValue, funcname, args);

    dm.setTargetIsolate(isolateId);
   
    // make sure any exception during the setter gets held onto
    m_manager.beginPlayerCodeExecution(isolateId);

    // TODO wrong timeout
    int timeout = getPreference(SessionManager.PREF_GETVAR_RESPONSE_TIMEOUT);
    timeout += 500; // give the player enough time to raise its timeout exception

    boolean result = simpleRequestResponseMessage(dm, DMessage.InCallFunction, timeout);

    // tell manager we're done; ignore returned FaultEvent
    m_manager.endPlayerCodeExecution(isolateId);

    if (!result)
      throw new NoResponseException(timeout);

    DVariable lastFunctionCall = m_manager.lastFunctionCall(isolateId);
    if (lastFunctionCall != null)
      return lastFunctionCall.getValue();
    else
      return DValue.forPrimitive(Value.UNDEFINED, isolateId);
  }

  /*
   * @see flash.tools.debugger.Session#callFunction(flash.tools.debugger.Value, java.lang.String, flash.tools.debugger.Value[])
   */
  public Value callFunction(Value thisValue, String funcname, Value[] args) throws PlayerDebugException
  {
    return callFunctionWorker(thisValue, funcname, args, Isolate.DEFAULT_ID);
  }
 
  public Value callFunctionWorker(Value thisValue, String funcname, Value[] args, int isolateId) throws PlayerDebugException
  {
    Value retval = callPseudoFunction(thisValue, funcname, args, isolateId);
    if (retval != null) {
      return retval;
    }

    return callFunction(thisValue, false, funcname, args, isolateId);
  }

  /**
   * Checks to see if the function being called is a debugger pseudofunction such as
   * $obj(), and if so, handles that directly rather than calling the player.  Returns
   * null if the function being called is not a pseudofunction.
   */
  private Value callPseudoFunction(Value thisValue, String funcname, Value[] args, int isolateId) throws PlayerDebugException{
    if (thisValue.getType() == VariableType.UNDEFINED || thisValue.getType() == VariableType.NULL) {
      if ("$obj".equals(funcname)) { //$NON-NLS-1$
        return callObjPseudoFunction(args, isolateId);
      }
    }

    return null;
  }

  /**
   * Handles a call to the debugger pseudofunction $obj() -- e.g. $obj(1234) returns
   * a pointer to the object with id 1234.
   */
  private Value callObjPseudoFunction(Value[] args, int isolateId) throws PlayerDebugException {
    if (args.length != 1) {
      return DValue.forPrimitive(DValue.UNDEFINED, isolateId);
    }
    double arg = ECMA.toNumber(this, args[0]);
    long id = (long) arg;
    if (id != arg) {
      return DValue.forPrimitive(DValue.UNDEFINED, isolateId);
    }
    DValue value = m_manager.getValue(id, isolateId);
    if (value == null) {
      return DValue.forPrimitive(DValue.UNDEFINED, isolateId);
    }
    return value;
  }

  public Value callConstructor(String funcname, Value[] args) throws PlayerDebugException
  {
    return callConstructorWorker(funcname, args, Isolate.DEFAULT_ID);
  }
 
  public Value callConstructorWorker(String funcname, Value[] args, int isolateId) throws PlayerDebugException
  {
    return callFunction(DValue.forPrimitive(null, isolateId), true, funcname, args, isolateId);
  }

  private DMessage buildCallFunctionMessage(boolean isConstructor, Value thisValue, String funcname, Value[] args)
  {
    funcname = (funcname == null) ? "" : funcname; //$NON-NLS-1$

    int messageSize = 8; // DWORD representing flags + DWORD representing frame
    String thisType = DVariable.typeNameFor(thisValue.getType());
    String thisValueString = thisValue.getValueAsString();
    messageSize += DMessage.getStringLength(thisType)+1;
    messageSize += DMessage.getStringLength(thisValueString)+1;
    messageSize += DMessage.getStringLength(funcname)+1;
    messageSize += 4; // DWORD representing the number of args
    String[] argTypes = new String[args.length];
    String[] argValues = new String[args.length];
    for (int i=0; i<args.length; ++i)
    {
      argTypes[i] = DVariable.typeNameFor(args[i].getType());
      argValues[i] = args[i].getValueAsString();
      messageSize += DMessage.getStringLength(argValues[i])+1;
      messageSize += DMessage.getStringLength(argTypes[i])+1;
    }

    DMessage dm = DMessageCache.alloc(messageSize);
    dm.setType(DMessage.OutCallFunction);
    try
    {
      dm.putDWord(isConstructor ? 1 : 0);
      dm.putDWord(0); // TODO: the currently active frame number
      dm.putString(thisType);
      dm.putString(thisValueString);
      dm.putString(funcname);
      dm.putDWord(args.length);
      for (int i=0; i<args.length; ++i)
      {
        dm.putString(argTypes[i]);
        dm.putString(argValues[i]);
      }
    }
    catch(UnsupportedEncodingException uee)
    {
      // couldn't write out the string, so just terminate it and complete anyway
      dm.putByte((byte)'\0');
    }

    return dm;
  }

  private void requestVariable(long id, String name, boolean fireGetter, boolean alsoGetChildren, int isolateId) throws NoResponseException, NotConnectedException, NotSuspendedException
  {
    if (!isWorkerSuspended(isolateId))
      throw new NotSuspendedException();

    name = getRawMemberName(id, name, isolateId);

    DMessage dm = buildOutGetMessage(id, name, fireGetter, alsoGetChildren);

    dm.setTargetIsolate(isolateId);
   
    // make sure any exception during the setter gets held onto
    m_manager.beginPlayerCodeExecution(isolateId);

    int timeout = getPreference(SessionManager.PREF_GETVAR_RESPONSE_TIMEOUT);
    timeout += 500; // give the player enough time to raise its timeout exception

    boolean result = simpleRequestResponseMessage(dm, DMessage.InGetVariable, timeout);

    // tell manager we're done; ignore returned FaultEvent
    m_manager.endPlayerCodeExecution(isolateId);

    if (!result)
      throw new NoResponseException(timeout);
  }

  private DMessage buildOutGetMessage(long id, String name, boolean fireGetter, boolean alsoGetChildren)
  {
    final int FLAGS_SIZE = 4;
    name = (name == null) ? "" : name; //$NON-NLS-1$

    DMessage dm = DMessageCache.alloc(DMessage.getSizeofPtr() + DMessage.getStringLength(name)+1 + FLAGS_SIZE);
    dm.setType( (!fireGetter) ? DMessage.OutGetVariable : DMessage.OutGetVariableWhichInvokesGetter );
    dm.putPtr(id);
    try
    {
      dm.putString(name);
    }
    catch(UnsupportedEncodingException uee)
    {
      // couldn't write out the string, so just terminate it and complete anyway
      dm.putByte((byte)'\0');
    }

    // as an optimization, newer player builds allow us to tell them not to
    // send all the children of an object along with the object, because
    // frequently we don't care about the children
    int flags = GetVariableFlag.DONT_GET_FUNCTIONS; // we never want functions
    if (fireGetter)
      flags |= GetVariableFlag.INVOKE_GETTER;
    if (alsoGetChildren)
      flags |= GetVariableFlag.ALSO_GET_CHILDREN | GetVariableFlag.GET_CLASS_HIERARCHY;
    dm.putDWord(flags);

    return dm;
  }

  public FaultEvent setScalarMember(long varId, String memberName, int type, String value, int isolateId) throws NotSuspendedException, NoResponseException, NotConnectedException
  {
    if (!isWorkerSuspended(isolateId))
      throw new NotSuspendedException();

    // If the varId is that of a stack frame, then we need to check whether that
    // stack frame has an "activation object".  If it does, then all of the
    // arguments and locals are actually kept as members of that activation
    // object, and so we need to change varId to be the ID of that activation
    // object -- that way, the player will modify the member of the activation
    // object rather than modifying the "regular" argument or local.  See bug
    // 155031.
    if (varId <= Value.BASE_ID && varId > Value.LEVEL_ID)
    {
      int depth = (int) (Value.BASE_ID - varId);
      DStackContext context = m_manager.getFrame(depth,isolateId);
      DVariable activationObject = context.getActivationObject();
      if (activationObject != null)
        varId = activationObject.getValue().getId();
    }

    memberName = getRawMemberName(varId, memberName, isolateId);

    // see if it is our any of our special variables
    FaultEvent faultEvent = requestSetVariable( isPseudoVarId(varId) ? 0 : varId, memberName, type, value, isolateId);

    // now that we sent it out, we need to clear our variable cache
    // if it is our special context then mark the frame as stale.
    if (isPseudoVarId(varId) && m_manager.getFrameCount(isolateId) > 0)
    {
      m_manager.getFrame(0, isolateId).markStale();
    }
    else
    {
      DValue parent = m_manager.getValue(varId,isolateId);
      if (parent != null)
        parent.removeAllMembers();
    }

    return faultEvent;
  }

  /**
   * Returns whether a variable ID is "real" or not.  For example,
   * Value.THIS_ID is a "pseudo" varId, as are all the other special
   * hard-coded varIds in the Value class.
   */
  private boolean isPseudoVarId(long varId)
  {
    /*
     * Unfortunately, this is actually just taking a guess.  The old code
     * used "varId &lt; 0"; however, the Linux player sometimes has real
     * variable IDs which are less than zero.
     */
    return (varId < 0 && varId > -65535);
  }

  /**
   * <code>memberName</code> might be just <code>"varname"</code>, or it
   * might be <code>"namespace::varname"</code>, or it might be
   * <code>"namespace@hexaddr::varname"</code>.  In the third case, it is
   * fully resolved, and there is nothing we need to do.  But in the first
   * and second cases, we may need to fully resolve it so that the Player
   * will recognize it.
   */
  private String getRawMemberName(long parentValueId, String memberName, int isolateId)
  {
    if (memberName != null)
    {
      DValue parent = m_manager.getValue(parentValueId, isolateId);
      if (parent != null)
      {
        int doubleColon = memberName.indexOf("::"); //$NON-NLS-1$
        String shortName = (doubleColon==-1) ? memberName : memberName.substring(doubleColon+2);
        DVariable member = parent.findMember(shortName);
        if (member != null)
          memberName = member.getRawName();
      }
    }
    return memberName;
  }

  /**
   * @return null for success, or fault event if a setter in the player threw an exception
   */
  private FaultEvent requestSetVariable(long id, String name, int t, String value, int isolateId) throws NoResponseException
  {
    // convert type to typeName
    String type = DVariable.typeNameFor(t);
    DMessage dm = buildOutSetMessage(id, name, type, value);
    dm.setTargetIsolate(isolateId);
    FaultEvent faultEvent = null;
//    System.out.println("setmsg id="+id+",name="+name+",t="+type+",value="+value);

    // make sure any exception during the setter gets held onto
    m_manager.beginPlayerCodeExecution(isolateId);

    // turn off squelch so we can hear the response
    sendSquelch(false, isolateId);

    int timeout = getPreference(SessionManager.PREF_GETVAR_RESPONSE_TIMEOUT);

    if (!simpleRequestResponseMessage(dm, (t == VariableType.STRING) ? DMessage.InSetVariable : DMessage.InSetVariable2, timeout))
      throw new NoResponseException(getPreference(SessionManager.PREF_RESPONSE_TIMEOUT));

    // turn it back on
    sendSquelch(true, isolateId);

    // tell manager we're done, and get exception if any
    faultEvent = m_manager.endPlayerCodeExecution(isolateId);

    // hammer the variable cache and context array
    m_manager.freeValueCache(isolateId);
    return faultEvent;
  }

  private DMessage buildOutSetMessage(long id, String name, String type, String v)
  {
    DMessage dm = DMessageCache.alloc(DMessage.getSizeofPtr()+
        DMessage.getStringLength(name)+
        DMessage.getStringLength(type)+
        DMessage.getStringLength(v)+
        3);
    dm.setType(DMessage.OutSetVariable);
    dm.putPtr(id);
    try { dm.putString(name); } catch(UnsupportedEncodingException uee) { dm.putByte((byte)'\0'); }
    try { dm.putString(type); } catch(UnsupportedEncodingException uee) { dm.putByte((byte)'\0'); }
    try { dm.putString(v); } catch(UnsupportedEncodingException uee) { dm.putByte((byte)'\0'); }
    return dm;
  }

  /*
   * @see flash.tools.debugger.Session#waitForEvent()
   */
  public void waitForEvent() throws NotConnectedException, InterruptedException
  {
    Object eventNotifier = m_manager.getEventNotifier();
    synchronized (eventNotifier)
    {
      while (getEventCount() == 0 && isConnected())
      {
        eventNotifier.wait();
      }
    }

    // We should NOT call isConnected() to test for a broken connection!  That
    // is because we may have received one or more events AND lost the connection,
    // almost simultaneously.  If there are any messages available for the
    // caller to process, we should not throw an exception.
    if (getEventCount() == 0 && !isConnected())
      throw new NotConnectedException();
  }

  /*
   * @see flash.tools.debugger.Session#getEventCount()
   */
  public int getEventCount()
  {
    return m_manager.getEventCount();
  }

  /*
   * @see flash.tools.debugger.Session#nextEvent()
   */
  public DebugEvent nextEvent()
  {
    return m_manager.nextEvent();
  }

  /**
   * Adds a watchpoint on the given expression
   * @throws flash.tools.debugger.NotConnectedException
   * @throws flash.tools.debugger.NoResponseException
   * @throws flash.tools.debugger.NotSupportedException
   * @throws flash.tools.debugger.NotSuspendedException
   */
  public boolean addWatch(long varId, String varName, int type, int tag, int isolateId) throws NoResponseException, NotConnectedException, NotSupportedException
  {
    // TODO check for NoResponse, NotConnected

    if (!supportsWatchpoints(isolateId))
      throw new NotSupportedException(PlayerSessionManager.getLocalizationManager().getLocalizedTextString("watchpointsNotSupported")); //$NON-NLS-1$

    varName = getRawMemberName(varId, varName, isolateId);
    DMessage dm = DMessageCache.alloc(4+DMessage.getSizeofPtr()+DMessage.getStringLength(varName)+1);
    dm.setType(DMessage.OutAddWatch2);
    dm.setTargetIsolate(isolateId);
    dm.putPtr(varId);
    try { dm.putString(varName); } catch(UnsupportedEncodingException uee) { dm.putByte((byte)'\0'); }
    dm.putWord(type);
    dm.putWord(tag);

    int timeout = getPreference(SessionManager.PREF_GETVAR_RESPONSE_TIMEOUT);
    boolean result = simpleRequestResponseMessage(dm, DMessage.InWatch2, timeout);
    return result;
  }

  /**
   * Removes a watchpoint on the given expression
   * @throws flash.tools.debugger.NotConnectedException
   * @throws flash.tools.debugger.NoResponseException
   * @throws flash.tools.debugger.NotSuspendedException
   */
  public boolean removeWatch(long varId, String memberName, int isolateId) throws NoResponseException, NotConnectedException
  {
    memberName = getRawMemberName(varId, memberName, isolateId);
    DMessage dm = DMessageCache.alloc(DMessage.getSizeofPtr()+DMessage.getStringLength(memberName)+1);
    dm.setType(DMessage.OutRemoveWatch2);
    dm.putPtr(varId);
    try { dm.putString(memberName); } catch(UnsupportedEncodingException uee) { dm.putByte((byte)'\0'); }

    int timeout = getPreference(SessionManager.PREF_GETVAR_RESPONSE_TIMEOUT);
    boolean result = simpleRequestResponseMessage(dm, DMessage.InWatch2, timeout);
    return result;
  }

  /**
   * Send a message that contains no data
   */
  void sendMessage(int message)
  {
    DMessage dm = DMessageCache.alloc(0);
    dm.setType(message);
    sendMessage(dm);
  }
 
  /**
   * Send a message that contains no data
   */
  void sendMessageIsolate(int message, int isolateId)
  {
    DMessage dm = DMessageCache.alloc(0);
    dm.setTargetIsolate(isolateId);
    dm.setType(message);
    sendMessage(dm);
  }

  /**
   * Send a fully formed message and release it when done
   */
  synchronized void sendMessage(DMessage dm)
  {
    try
    {
      if (dm.getType() != DMessage.OutSetActiveIsolate) {
        int isolate = dm.getTargetIsolate();
        if (isolate != getActiveIsolate().getId()) {
          DMessage dm1 = DMessageCache.alloc(4);
          dm1.setTargetIsolate(isolate);
          dm1.setType(DMessage.OutSetActiveIsolate);
          dm1.putDWord(isolate);

          /* Use sendMessage here to avoid waiting for a response.
           * The assumption is that once the message is sent, subsequent
           * messages are for that isolate regardless of the player confirming
           * it. With this change, performance has improved considerably; player
           * debugger has not gone out of sync since the ProcessTag messages
           * flood issue was resolved. */
          sendMessage(dm1);

          m_manager.setActiveIsolate(m_manager.getIsolate(isolate));

        }
      }
      m_protocol.txMessage(dm);

      if (m_debugMsgOn || m_debugMsgFileOn)
        trace(dm, false);
    }
    catch(IOException io)
    {
      if (Trace.error)
      {
        Trace.trace("Attempt to send message "+dm.outToString()+" failed"); //$NON-NLS-1$ //$NON-NLS-2$
        io.printStackTrace();
      }
    }
    DMessageCache.free(dm);
  }

  /**
   * Tell the player to shut-up
   */
  boolean sendSquelch(boolean on, int isolateId)
  {
    boolean responded;
    DMessage dm = DMessageCache.alloc(4);
    dm.setType(DMessage.OutSetSquelch);
    dm.setTargetIsolate(isolateId);
    dm.putDWord( on ? 1 : 0);
    responded = simpleRequestResponseMessage(dm, DMessage.InSquelch);
    return responded;
  }

  void sendStopWarning()
  {
    // Currently, "disable_script_stuck_dialog" only works for AS2, not for AS3.
    String option = "disable_script_stuck_dialog"; //$NON-NLS-1$
    String value = "on"; //$NON-NLS-1$

    sendOptionMessage(option, value);

    // HACK: Completely disable the script-stuck notifications, so that we can
    // get AS3 debugging working.
    option = "disable_script_stuck"; //$NON-NLS-1$
    value = "on"; //$NON-NLS-1$

    sendOptionMessage(option, value);
  }

  void sendStopOnFault()
  {
    String option = "break_on_fault"; //$NON-NLS-1$
    String value = "on"; //$NON-NLS-1$

    sendOptionMessage(option, value);
  }

  void sendEnumerateOverride()
  {
    String option = "enumerate_override"; //$NON-NLS-1$
    String value = "on"; //$NON-NLS-1$

    sendOptionMessage(option, value);
  }

  void sendFailureNotify()
  {
    String option = "notify_on_failure"; //$NON-NLS-1$
    String value = "on"; //$NON-NLS-1$

    sendOptionMessage(option, value);
  }

  void sendInvokeSetters()
  {
    String option = "invoke_setters"; //$NON-NLS-1$
    String value = "on"; //$NON-NLS-1$

    sendOptionMessage(option, value);
  }

  void sendSwfloadNotify()
  {
    String option = "swf_load_messages"; //$NON-NLS-1$
    String value = "on"; //$NON-NLS-1$

    sendOptionMessage(option, value);
  }

  void sendConsoleErrorsAsTrace(boolean on)
  {
    String option = "console_errors"; //$NON-NLS-1$
    String value = (on) ? "on" : "off"; //$NON-NLS-1$ //$NON-NLS-2$

    sendOptionMessage(option, value);
  }

  void sendGetterTimeout()
  {
    String option = "getter_timeout"; //$NON-NLS-1$
    String value = "" + getPreference(SessionManager.PREF_GETVAR_RESPONSE_TIMEOUT); //$NON-NLS-1$

    sendOptionMessage(option, value);
  }

  void sendSetterTimeout()
  {
    String option = "setter_timeout"; //$NON-NLS-1$
    String value = "" + getPreference(SessionManager.PREF_SETVAR_RESPONSE_TIMEOUT); //$NON-NLS-1$

    sendOptionMessage(option, value);
  }
 
  void sendConcurrentDebugger()
  {
    String option = "concurrent_debugger"; //$NON-NLS-1$
    String value = "on"; //$NON-NLS-1$

    sendOptionMessage(option, value);
  }
 
  void sendWideLineDebugger()
  {
    String option = "wide_line_debugger"; //$NON-NLS-1$
    String value = "on"; //$NON-NLS-1$

    sendOptionMessage(option, value);
    m_manager.setWideLines(true);
  }

  void sendOptionMessage(String option, String value)
  {
    int msgSize = DMessage.getStringLength(option)+DMessage.getStringLength(value)+2// add 2 for trailing nulls of each string

    DMessage dm = DMessageCache.alloc(msgSize);
    dm.setType(DMessage.OutSetOption);
    try { dm.putString(option); } catch(UnsupportedEncodingException uee) { dm.putByte((byte)'\0'); }
    try { dm.putString(value); } catch(UnsupportedEncodingException uee) { dm.putByte((byte)'\0'); }
    simpleRequestResponseMessage(dm, DMessage.InOption);
  }

  public boolean supportsWatchpoints()
  {
    return supportsWatchpoints(Isolate.DEFAULT_ID);
  }
 
  public boolean supportsWatchpoints(int isolateId)
  {
    if (m_playerSupportsWatchpoints == null)
      m_playerSupportsWatchpoints = new Boolean(getOption("can_set_watchpoints", false, isolateId)); //$NON-NLS-1$
    return m_playerSupportsWatchpoints.booleanValue();
  }

  public boolean playerCanBreakOnAllExceptions()
  {
    return playerCanBreakOnAllExceptions(Isolate.DEFAULT_ID);
  }
 
  public boolean playerCanBreakOnAllExceptions(int isolateId)
  {
    if (m_playerCanBreakOnAllExceptions == null)
      m_playerCanBreakOnAllExceptions = new Boolean(getOption("can_break_on_all_exceptions", false, isolateId)); //$NON-NLS-1$
    return m_playerCanBreakOnAllExceptions.booleanValue();
  }
 
  public boolean supportsConcurrency(int isolateId)
  {
    if (m_playerSupportsConcurrency == null)
      m_playerSupportsConcurrency = new Boolean(getOption("concurrent_player", false, isolateId)); //$NON-NLS-1$
    return m_playerSupportsConcurrency.booleanValue();
  }
 
  public boolean supportsConcurrency()
  {
    return supportsConcurrency(Isolate.DEFAULT_ID);
  }
 
  public boolean supportsWideLineNumbers()
  {
    return supportsWideLineNumbers(Isolate.DEFAULT_ID);
  }
 
  public boolean supportsWideLineNumbers(int isolateId)
  {
    if (m_playerSupportsWideLine == null)
      m_playerSupportsWideLine = new Boolean(getOption("wide_line_player", false, isolateId)); //$NON-NLS-1$
    return m_playerSupportsWideLine.booleanValue();
  }

  public boolean playerCanTerminate()
  {
    return getOption("can_terminate", false, Isolate.DEFAULT_ID); //$NON-NLS-1$
  }

  public boolean playerCanCallFunctions()
  {
    return playerCanCallFunctions(Isolate.DEFAULT_ID);
  }
 
  public boolean playerCanCallFunctions(int isolateId)
  {
    if (m_playerCanCallFunctions == null)
      m_playerCanCallFunctions = new Boolean(getOption("can_call_functions", false, isolateId)); //$NON-NLS-1$
    return m_playerCanCallFunctions.booleanValue();
  }

  /**
   * Returns the value of a Flash Player boolean option that was requested by
   * OutGetOption and returned by InOption.
   *
   * @param optionName
   *            the name of the option
   * @return its value, or null
   */
  public boolean getOption(String optionName, boolean defaultValue, int isolateId)
  {
    boolean retval = defaultValue;
    String optionValue = getOption(optionName, null, isolateId);

    if (optionValue != null)
      retval = Boolean.valueOf(optionValue).booleanValue();

    return retval;
  }

  /**
   * Returns the value of a Flash Player string option that was requested by
   * OutGetOption and returned by InOption.
   *
   * @param optionName
   *            the name of the option
   * @return its value, or null
   */
  public String getOption(String optionName, String defaultValue, int isolateId)
  {
    String optionValue = defaultValue;

    int msgSize = DMessage.getStringLength(optionName)+1// add 1 for trailing null of string

    DMessage dm = DMessageCache.alloc(msgSize);
    dm.setTargetIsolate(isolateId);
    dm.setType(DMessage.OutGetOption);
    try { dm.putString(optionName); } catch(UnsupportedEncodingException uee) { dm.putByte((byte)'\0'); }
    if (simpleRequestResponseMessage(dm, DMessage.InOption))
      optionValue = m_manager.getOption(optionName);
    return optionValue;
  }
 
  long getMessageInCount(DMessageCounter counter, long isolate, int msgType) {
    if (isolate == Isolate.DEFAULT_ID) {
      return counter.getInCount(msgType);
    }
    else {
      return counter.getIsolateInCount(isolate, msgType);
    }
  }
 
  Object getMessageInLock(DMessageCounter counter, long isolate) {
    if (isolate == Isolate.DEFAULT_ID) {
      return counter.getInLock();
    }
    else {
      return counter.getIsolateInLock(isolate);
    }
  }

  /**
   * Send our message and assume that the next response that is received is
   * ours.  Primitive but there is no use in setting up a full request / response
   * pattern since the player doesn't follow it.
   *
   * @return false is no response.
   */
  boolean simpleRequestResponseMessage(DMessage msg, int msgType, int timeout)
  {
    boolean response = false;

    //FIXME: Check if timeout needs to adjust to the isolate switching
    // delay
    // use default or user supplied timeout
    timeout = (timeout > 0) ? timeout : getPreference(SessionManager.PREF_RESPONSE_TIMEOUT);

    // note the number of messages of this type before our send
    DMessageCounter msgCounter = getMessageCounter();
    int isolate = msg.getTargetIsolate();
    long num = getMessageInCount(msgCounter, isolate, msgType);
    long expect = num+1;

    // send the message
    sendMessage(msg);

    long startTime = System.currentTimeMillis();
//    System.out.println("sending- "+DMessage.outTypeName(msg.getType())+",timeout="+timeout+",start="+start);

    // now wait till we see a message come in
    m_incoming = false;
    synchronized (getMessageInLock(msgCounter, isolate))
    {
      while( (expect > getMessageInCount(msgCounter, isolate, msgType)) &&
          System.currentTimeMillis() < startTime + timeout &&
          isConnected())
      {
        // block until the message counter tells us that some message has been received
        try
        {
          getMessageInLock(msgCounter, isolate).wait(timeout);
        }
        catch (InterruptedException e)
        {
          // this should never happen
          e.printStackTrace();
          //FIXME: Will resetting the interrupted status here
          //cause any problems?
//                Thread.currentThread().interrupt();
        }

        // if we see incoming messages, then we should reset our timeout
        synchronized (this)
        {
          if (m_incoming)
          {
            startTime = System.currentTimeMillis();
            m_incoming = false;
          }
        }
      }
    }

    if (getMessageInCount(msgCounter, isolate, msgType) >= expect)
      response = true;
    else if (timeout <= 0 && Trace.error)
      Trace.trace("Timed-out waiting for "+DMessage.inTypeName(msgType)+" response to message "+msg.outToString()); //$NON-NLS-1$ //$NON-NLS-2$

//    long endTime = System.currentTimeMillis();
//    System.out.println("    response- "+response+",timeout="+timeout+",elapsed="+(endTime-startTime));
    m_lastResponse = response;
    return response;
  }

  // use default timeout
  boolean simpleRequestResponseMessage(DMessage msg, int msgType)   { return simpleRequestResponseMessage(msg, msgType, -1);   }
 
  boolean simpleRequestResponseMessageIsolate(DMessage msg, int msgType, int isolateId)   {
    return simpleRequestResponseMessageIsolate(msg, msgType, -1, isolateId);      
  }
 
  boolean simpleRequestResponseMessageIsolate(DMessage msg, int msgType, int timeout, int isolateId)
  {
    msg.setTargetIsolate(isolateId);
    return simpleRequestResponseMessage(msg, msgType, timeout);
  }
 
  boolean simpleRequestResponseMessage(int msg, int msgType)      { return simpleRequestResponseMessage(msg, msgType, -1);   }
 
  boolean simpleRequestResponseMessageIsolate(int msg, int msgType, int isolateId) {
    return simpleRequestResponseMessageIsolate(msg, msgType, -1, isolateId);
  }

  boolean simpleRequestResponseMessageIsolate(int msg, int msgType, int timeout, int isolateId)
  {
    DMessage dm = DMessageCache.alloc(0);
    dm.setType(msg);
    dm.setTargetIsolate(isolateId);
    return simpleRequestResponseMessage(dm, msgType, timeout);
  }
 
  // Convenience function
  boolean simpleRequestResponseMessage(int msg, int msgType, int timeout)
  {
    DMessage dm = DMessageCache.alloc(0);
    dm.setType(msg);
    return simpleRequestResponseMessage(dm, msgType, timeout);
  }

  /**
   * We register ourself as a listener to DMessages from the pipe for the
   * sole purpose of monitoring the state of the debugger.  All other
   * object management occurs with DManager
   */
  /**
   * Issued when the socket connection to the player is cut
   */
  public void disconnected()
  {
    m_isHalted = false;
    m_isConnected = false;
    m_manager.disconnected();
  }

  /**
   * This is the core routine for decoding incoming messages and deciding what should be
   * done with them.  We have registered ourself with DProtocol to be notified when any
   * incoming messages have been received.
   *
   * It is important to note that we should not rely on the contents of the message
   * since it may be reused after we exit this method.
   */
  public void messageArrived(DMessage msg, DProtocol which)
  {
    preMessageArrived(msg, which);
    msg.reset(); // allow the message to be re-parsed
    m_manager.messageArrived(msg, which);
    msg.reset(); // allow the message to be re-parsed
    postMessageArrived(msg, which);
  }

  /**
   * Processes the message before it is passed to the DManager.
   */
  private void preMessageArrived(DMessage msg, DProtocol which)
  {
    switch (msg.getType())
    {
    case DMessage.InIsolate:

        m_lastPreIsolate = (int)msg.getDWord();
     
      break;
   
      case DMessage.InAskBreakpoints:
      case DMessage.InBreakAt:
      case DMessage.InBreakAtExt:
      {
        // We need to set m_isHalted to true before the DManager processes
        // the message, because the DManager may add a BreakEvent to the
        // event queue, which the host debugger may immediately process;
        // if the debugger calls back to the Session, the Session must be
        // correctly marked as halted.
        if (m_lastPreIsolate == Isolate.DEFAULT_ID)
          m_isHalted = true;
        else
          updateHaltIsolateStatus(m_lastPreIsolate, true);
        break;
      }
    }
  }

  /**
   * Processes the message after it has been passed to the DManager.
   */
  private void postMessageArrived(DMessage msg, DProtocol which)
  {
    if (m_debugMsgOn || m_debugMsgFileOn)
      trace(msg, true);

    /* at this point we just open up a big switch statement and walk through all possible cases */
    int type = msg.getType();
    switch(type)
    {
      case DMessage.InExit:
      {
        m_isConnected = false;
        break;
      }

      case DMessage.InProcessTag:
      {
        // need to send a response to this message to keep the player going
        sendMessageIsolate(DMessage.OutProcessedTag, msg.getTargetIsolate());
        break;
      }

      case DMessage.InContinue:
      {
        if (msg.getTargetIsolate() == Isolate.DEFAULT_ID)
          m_isHalted = false;
        else {
          updateHaltIsolateStatus(msg.getTargetIsolate(), false);
        }
        break;
      }

      case DMessage.InOption:
      {
        //workers inherit options, so only store options
        //from main thread.
        if (msg.getTargetIsolate() == Isolate.DEFAULT_ID) {
          String s = msg.getString();
          String v = msg.getString();

          // add it to our properties, for DEBUG purposes only
          m_prefs.put(s, v);
        }
        break;
      }

      case DMessage.InSwfInfo:
      case DMessage.InScript:
      case DMessage.InRemoveScript:
      {
        //FIXME: Clear this cache only for affected
        //workers. Right now, the key contains worker
        //id, so we are safe. But we unnecessarily flush
        //the queue.
        m_evalIsAndInstanceofCache.clear();
       
        m_incoming = true;
        break;
      }

      default:
      {
        /**
         * Simple indicator that we have received a message.  We
         * put this indicator in default so that InProcessTag msgs
         * wouldn't generate false triggers.  Mainly, we want to
         * reset our timeout counter when we receive trace messages.
         */
        m_incoming = true;
        break;
      }
    }

    // something came in so assume that we can now talk
    // to the player
    m_lastResponse = true;
  }

    private void updateHaltIsolateStatus(int targetIsolate, boolean value) {
      if (!m_isolateStatus.containsKey(targetIsolate)) {
        PlayerSessionIsolateStatus status = new PlayerSessionIsolateStatus();
        status.m_isHalted = value;
        m_isolateStatus.put(targetIsolate, status);       
      }
      else {
        m_isolateStatus.get(targetIsolate).m_isHalted = value;
      }
  }

  /**
     * A background thread which wakes up periodically and fetches the SWF and SWD
     * from the Player for new movies that have loaded.  It then uses these to create
   * an instance of MovieMetaData (a class shared with the Profiler) from which
   * fdb can cull function names.
     * This work is done on a background thread because it can take several
     * seconds, and we want the fdb user to be able to execute other commands
     * while it is happening.
     */
    public void run()
    {
      long last = 0;
      while(isConnected())
      {
        // try every 250ms
        try { Thread.sleep(250); } catch(InterruptedException ie) {}

        try
        {
          // let's make sure that the traffic level is low before
          // we do our requests.
          long current = m_protocol.messagesReceived();
          long delta = last - current;
          last = current;

          // if the last message that went out was not responded to
          // or we are not suspended and have high traffic
          // then wait for later.
          if (!m_lastResponse || (!isSuspended() && delta > 5))
            throw new NotSuspendedException();

          // we are either suspended or low enough traffic

          // get the list of swfs we have
          for (Isolate isolate : m_manager.getIsolates()) {
            int isolateId = isolate.getId();
            if (isolateId != Isolate.DEFAULT_ID && !isWorkerSuspended(isolateId) && delta > 5) {
              throw new NotSuspendedException();
            }
            int count = m_manager.getSwfInfoCount(isolateId);
            for(int i=0; i<count; i++)
            {
              DSwfInfo info = m_manager.getSwfInfo(i, isolateId);

              // no need to process if it's been removed
              if (info == null || info.isUnloaded() || info.isPopulated() || (info.getVmVersion() > 0) )
                continue;

              // see if the swd has been loaded, throws exception if unable to load it.
              // Also triggers a callback into the info object to freshen its contents
              // if successful
              //FIXME: remove sysout
              info.getSwdSize(this);
              // check since our vm version info could get updated in between.
              if (info.getVmVersion() > 0)
              {
                // mark it populated if we haven't already done so
                info.setPopulated();
                continue;
              }

              // so by this point we know that we've got good swd data,
              // or we've made too many attempts and gave up.
              if (!info.isSwdLoading() && !info.isUnloaded())
              {
                // now load the swf, if we haven't already got it
                if (info.getSwf() == null && !info.isUnloaded())
                  info.setSwf(requestSwf(i));

                // only get the swd if we haven't got it
                if (info.getSwd() == null && !info.isUnloaded())
                  info.setSwd(requestSwd(i));

                try
                {
                  // now go populate the functions tables...
                  if (!info.isUnloaded())
                    info.parseSwfSwd(m_manager);
                }
                catch(Throwable e)
                {
                  // oh this is not good and means that we should probably
                  // give up.
                  if (Trace.error)
                  {
                    Trace.trace("Error while parsing swf/swd '"+info.getUrl()+"'. Giving up and marking it processed"); //$NON-NLS-1$ //$NON-NLS-2$
                    e.printStackTrace();
                  }

                  info.setPopulated();
                }
              }
            }
          }
        }
        catch(InProgressException ipe)
        {
          // swd is still loading so give us a bit of
          // time and then come back and try again
        }
        catch(NoResponseException nre)
        {
          // timed out on one of our requests so don't bother
          // continuing right now,  try again later
        }
        catch(NotSuspendedException nse)
        {
          // probably want to wait until we are halted before
          // doing this heavy action
        }
        catch(Exception e)
        {
          // maybe not good
          if (Trace.error)
          {
            Trace.trace("Exception in background swf/swd processing thread"); //$NON-NLS-1$
            e.printStackTrace();
          }
        }
      }
    }

  byte[] requestSwf(int index) throws NoResponseException
  {
    /* send the message */
    int to = getPreference(SessionManager.PREF_SWFSWD_LOAD_TIMEOUT);
    byte[] swf = null;

    // the query
    DMessage dm = DMessageCache.alloc(2);
    dm.setType(DMessage.OutGetSwf);
    dm.putWord(index);

    if (simpleRequestResponseMessage(dm, DMessage.InGetSwf, to))
      swf = m_manager.getSWF();
    else
      throw new NoResponseException(to);

    return swf;
  }

  byte[] requestSwd(int index) throws NoResponseException
  {
    /* send the message */
    int to = getPreference(SessionManager.PREF_SWFSWD_LOAD_TIMEOUT);
    byte[] swd = null;

    // the query
    DMessage dm = DMessageCache.alloc(2);
    dm.setType(DMessage.OutGetSwd);
    dm.putWord(index);

    if (simpleRequestResponseMessage(dm, DMessage.InGetSwd, to))
      swd = m_manager.getSWD();
    else
      throw new NoResponseException(to);

    return swd;
  }

  //
  // Debug purposes only.  Dump contents of our messages to the screen
  // and/or file.
  //
  synchronized void trace(DMessage dm, boolean in)
  {
    try
    {
      if (m_debugMsgOn) {
        System.out.println( (in) ? dm.inToString(m_debugMsgSize) : dm.outToString(m_debugMsgSize) );
      }

      if (m_debugMsgFileOn)
      {
        traceFile().write( (in) ? dm.inToString(m_debugMsgFileSize) : dm.outToString(m_debugMsgFileSize) );
        m_trace.write(s_newline);
        m_trace.flush();
      }
    }
    catch(Exception e) {}
  }

  // i/o for tracing
    java.io.Writer m_trace;
 

  java.io.Writer traceFile() throws IOException
  {
    if (m_trace == null)
    {
      m_trace = new java.io.FileWriter("mm_debug_api_trace.txt"); //$NON-NLS-1$
      try { m_trace.write(new java.util.Date().toString()); } catch(Exception e) { m_trace.write("Date unknown"); } //$NON-NLS-1$
      try
      {
        m_trace.write(s_newline);

        // java properties dump
        java.util.Properties props = System.getProperties();
        props.list(new PrintWriter(m_trace));

        m_trace.write(s_newline);

        // property dump
        for (String key: m_prefs.keySet())
        {
          Object value = m_prefs.get(key);
          m_trace.write(key);
          m_trace.write(" = "); //$NON-NLS-1$
          m_trace.write(value.toString());
          m_trace.write(s_newline);
        }
      }
      catch(Exception e) { if (Trace.error) e.printStackTrace(); }
      m_trace.write(s_newline);
    }
    return m_trace;
  }

  public void setLaunchUrl(String url)
  {
    if (url.startsWith("/")) { //$NON-NLS-1$
      url = "file://" + url; //$NON-NLS-1$
    }
    m_launchUrl = url;
  }

  public void setAIRLaunchInfo(AIRLaunchInfo airLaunchInfo)
  {
    m_airLaunchInfo = airLaunchInfo;
  }

  public void breakOnCaughtExceptions(boolean b) throws NotSupportedException, NoResponseException {
    breakOnCaughtExceptions(b, Isolate.DEFAULT_ID);
  }
 
  public void breakOnCaughtExceptions(boolean b, int isolateId) throws NotSupportedException, NoResponseException {
    if (!playerCanBreakOnAllExceptions(isolateId))
      throw new NotSupportedException(PlayerSessionManager.getLocalizationManager().getLocalizedTextString("exceptionBreakpointsNotSupported")); //$NON-NLS-1$

    DMessage dm = DMessageCache.alloc(1);
    dm.setType(DMessage.OutPassAllExceptionsToDebugger);
    dm.putByte((byte)(b ? 1 : 0));
    dm.setTargetIsolate(isolateId);
    /* TODO: Verify that sendMessage below is a bug */
//    sendMessage(dm);
    if (!simpleRequestResponseMessage(dm, DMessage.InPassAllExceptionsToDebugger))
      throw new NoResponseException(getPreference(SessionManager.PREF_RESPONSE_TIMEOUT));
  }


  public boolean evalIs(Value value, Value type) throws PlayerDebugException, PlayerFaultException
  {
    return evalIsOrInstanceof(BinaryOp.Is, value, type, Isolate.DEFAULT_ID);
  }

  public boolean evalIs(Value value, String type) throws PlayerDebugException, PlayerFaultException
  {
    return evalIsOrInstanceof(BinaryOp.Is, value, type, Isolate.DEFAULT_ID);
  }

  public boolean evalInstanceof(Value value, Value type) throws PlayerDebugException, PlayerFaultException
  {
    return evalIsOrInstanceof(BinaryOp.Instanceof, value, type, Isolate.DEFAULT_ID);
  }

  public boolean evalInstanceof(Value value, String type) throws PlayerDebugException, PlayerFaultException
  {
    return evalIsOrInstanceof(BinaryOp.Instanceof, value, type, Isolate.DEFAULT_ID);
  }
 
  // isolate version
 
  public boolean evalIsWorker(Value value, Value type, int isolateId) throws PlayerDebugException, PlayerFaultException
  {
    return evalIsOrInstanceof(BinaryOp.Is, value, type, isolateId);
  }

  public boolean evalIsWorker(Value value, String type, int isolateId) throws PlayerDebugException, PlayerFaultException
  {
    return evalIsOrInstanceof(BinaryOp.Is, value, type, isolateId);
  }

  public boolean evalInstanceofWorker(Value value, Value type, int isolateId) throws PlayerDebugException, PlayerFaultException
  {
    return evalIsOrInstanceof(BinaryOp.Instanceof, value, type, isolateId);
  }

  public boolean evalInstanceofWorker(Value value, String type, int isolateId) throws PlayerDebugException, PlayerFaultException
  {
    return evalIsOrInstanceof(BinaryOp.Instanceof, value, type, isolateId);
  }

  private boolean evalIsOrInstanceof(BinaryOp op, Value value, Value type, int isolateId) throws PlayerDebugException, PlayerFaultException
  {
    String key = value.getTypeName() + " " + op + " " + type.getTypeName() + " " + String.valueOf(isolateId); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    Boolean retval = m_evalIsAndInstanceofCache.get(key);
    if (retval == null)
    {
      retval = new Boolean(ECMA.toBoolean(evalBinaryOp(op, value, type, isolateId)));
      m_evalIsAndInstanceofCache.put(key, retval);
    }

    return retval.booleanValue();
  }

  private boolean evalIsOrInstanceof(BinaryOp op, Value value, String type, int isolateId) throws PlayerDebugException, PlayerFaultException
  {
    String key = value.getTypeName() + " " + op + " " + type + " " + String.valueOf(isolateId); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    Boolean retval = m_evalIsAndInstanceofCache.get(key);
    if (retval == null)
    {
      Value typeval = getGlobalWorker(type, isolateId);
      if (typeval == null)
        retval = Boolean.FALSE;
      else
        retval = new Boolean(ECMA.toBoolean(evalBinaryOp(op, value, typeval, isolateId)));
      m_evalIsAndInstanceofCache.put(key, retval);
    }

    return retval.booleanValue();
  }

  public boolean evalIn(Value property, Value object) throws PlayerDebugException, PlayerFaultException
  {
    return ECMA.toBoolean(evalBinaryOp(BinaryOp.In, property, object, Isolate.DEFAULT_ID));
  }

  public Value evalAs(Value value, Value type) throws PlayerDebugException, PlayerFaultException {
    return evalBinaryOp(BinaryOp.As, value, type, Isolate.DEFAULT_ID);
  }

  private Value evalBinaryOp(BinaryOp op, Value lhs, Value rhs, int isolateId) throws PlayerDebugException, PlayerFaultException
  {
    if (!isWorkerSuspended(isolateId))
      throw new NotSuspendedException();

    if (!playerCanCallFunctions(isolateId))
    {
      Map<String,String> parameters = new HashMap<String,String>();
      parameters.put("operator", op.getName()); //$NON-NLS-1$
      String message = PlayerSessionManager.getLocalizationManager().getLocalizedTextString("operatorNotSupported", parameters); //$NON-NLS-1$
      throw new NotSupportedException(message);
    }

    int id = (int) (Math.random() * 65536); // good 'nuff
    DMessage dm = buildBinaryOpMessage(id, op, lhs, rhs);

    dm.setTargetIsolate(isolateId);
    m_manager.clearLastBinaryOp(isolateId);

    // make sure any exception gets held onto
    m_manager.beginPlayerCodeExecution(isolateId);

    // TODO wrong timeout
    int timeout = getPreference(SessionManager.PREF_GETVAR_RESPONSE_TIMEOUT);
    timeout += 500; // give the player enough time to raise its timeout exception

    boolean result = simpleRequestResponseMessage(dm, DMessage.InBinaryOp, timeout);

    // tell manager we're done; ignore returned FaultEvent
    m_manager.endPlayerCodeExecution(isolateId);

    if (!result)
      throw new NoResponseException(timeout);

    DVariable lastBinaryOp = m_manager.lastBinaryOp(isolateId);
    Value v;
    if (lastBinaryOp != null)
      v = lastBinaryOp.getValue();
    else
      v = DValue.forPrimitive(Value.UNDEFINED, isolateId);

    if (v.isAttributeSet(ValueAttribute.IS_EXCEPTION))
      throw new PlayerFaultException(new ExceptionFault(v.getValueAsString(), false, v, isolateId));

    return v;
  }

  private DMessage buildBinaryOpMessage(int id, BinaryOp op, Value lhs, Value rhs) {
    int messageSize = 5; // DWORD representing id + byte representing op
    String lhsType = DVariable.typeNameFor(lhs.getType());
    String lhsValueString = lhs.getValueAsString();
    String rhsType = DVariable.typeNameFor(rhs.getType());
    String rhsValueString = rhs.getValueAsString();
    messageSize += DMessage.getStringLength(lhsType)+1;
    messageSize += DMessage.getStringLength(lhsValueString)+1;
    messageSize += DMessage.getStringLength(rhsType)+1;
    messageSize += DMessage.getStringLength(rhsValueString)+1;

    DMessage dm = DMessageCache.alloc(messageSize);
    dm.setType(DMessage.OutBinaryOp);
    try
    {
      dm.putDWord(id);
      dm.putByte((byte) op.getValue());
      dm.putString(lhsType);
      dm.putString(lhsValueString);
      dm.putString(rhsType);
      dm.putString(rhsValueString);
    }
    catch(UnsupportedEncodingException uee)
    {
      // couldn't write out the string, so just terminate it and complete anyway
      dm.putByte((byte)'\0');
    }

    return dm;
  }

  /* (non-Javadoc)
   * @see flash.tools.debugger.Session#getDisconnectCause()
   */
  public Exception getDisconnectCause() {
    if (m_protocol != null)
      return m_protocol.getDisconnectCause();
   
    return null;
  }

  /* (non-Javadoc)
   * @see flash.tools.debugger.Session#refreshIsolates()
   */
  public Isolate[] refreshWorkers() throws NotSupportedException,
      NotSuspendedException, NoResponseException, NotConnectedException {
    if (!supportsConcurrency()) {
      throw new NotSupportedException(PlayerSessionManager.getLocalizationManager().getLocalizedTextString("concurrencyNotSupported")); //$NON-NLS-1$
    }
    if (!isSuspended())
      throw new NotSuspendedException();
   
    boolean response = simpleRequestResponseMessage(DMessage.OutIsolateEnumerate, DMessage.InIsolateEnumerate);
   
    if (response) {
      return m_manager.getIsolates();
    }
    return null;
  }

  private Isolate getActiveIsolate() {
    return m_manager.getActiveIsolate();
  }

  @Override
  public Isolate[] getWorkers() {
    return m_manager.getIsolates();
  }

  @Override
  public void resumeWorker(int isolateId) throws NotSuspendedException,
      NotConnectedException, NoResponseException {
    if (!isWorkerSuspended(isolateId))
      throw new NotSuspendedException();

    if (!simpleRequestResponseMessageIsolate(DMessage.OutContinue, DMessage.InContinue, isolateId))
      throw new NoResponseException(getPreference(SessionManager.PREF_RESPONSE_TIMEOUT));
  }

  @Override
  public int suspendReasonWorker(int isolateId) throws NotConnectedException {
    DSuspendInfo info = getSuspendInfoIsolate(isolateId);
    return info.getReason();
  }
 
  public DSuspendInfo getSuspendInfoIsolate(int isolateId)
  {
    DSuspendInfo info = m_manager.getSuspendInfo(isolateId);
    if (info == null)
    {
      if (simpleRequestResponseMessageIsolate(DMessage.OutGetBreakReason, DMessage.InBreakReason, isolateId))
        info = m_manager.getSuspendInfo(isolateId);

      // if we still can't get any info from the manager...
      if (info == null)
        info = new DSuspendInfo()// empty unknown break information
    }
    return info;
  }

  @Override
  public void stepContinueWorker(int isolateId)
      throws NotSuspendedException, NoResponseException,
      NotConnectedException {
    if (!isWorkerSuspended(isolateId))
      throw new NotSuspendedException();

    // send a step-continue message and then wait for the Flash player to tell us that is has
    // resumed execution
    if (!simpleRequestResponseMessageIsolate(DMessage.OutStepContinue, DMessage.InContinue, isolateId))
      throw new NoResponseException(getPreference(SessionManager.PREF_RESPONSE_TIMEOUT));
  }

  @Override
  public Location setBreakpointWorker(int fileId, int lineNum, int isolateId)
      throws NoResponseException, NotConnectedException {
    /* send the message to the player and await a response */
    Location l = null;
    int bp = DLocation.encodeId(fileId, lineNum);
    int wideLineSize = 0;
    if (supportsWideLineNumbers())
      wideLineSize = 4;
    DMessage dm = DMessageCache.alloc(8 + wideLineSize);
    dm.setType(DMessage.OutSetBreakpoints);
    dm.putDWord(1);
    if (!supportsWideLineNumbers())
      dm.putDWord(bp);
    else {
      dm.putDWord(fileId);
      dm.putDWord(lineNum);
    }

    boolean gotResponse = simpleRequestResponseMessageIsolate(dm, DMessage.InSetBreakpoint, isolateId);
    if(gotResponse)
    {
      /* even though we think we got an answer check that the breakpoint was added */
      l = m_manager.getBreakpoint(bp, isolateId);
    }
    else
      throw new NoResponseException(getPreference(SessionManager.PREF_RESPONSE_TIMEOUT));

    return l;
  }

  @Override
  public Frame[] getFramesWorker(int isolateId) throws NotConnectedException {
    return m_manager.getFrames(isolateId);   
  }

  @Override
  public SwfInfo[] getSwfsWorker(int isolateId) throws NoResponseException {
    int swfCount = 0;
    swfCount = m_manager.getSwfInfoCount(isolateId);
    if (swfCount == 0)
    {
      // need to help out on the first one since the player
      // doesn't send it
      requestSwfInfo(0, isolateId);
    }
    //SwfInfo[] swfs = m_manager.getSwfInfos();
   
    ArrayList<SwfInfo> swfList = new ArrayList<SwfInfo>();

    for ( SwfInfo info : m_manager.getSwfInfos(isolateId) ) {
      swfList.add(info);
    }

    return swfList.toArray(new SwfInfo[0]);
  }

  @Override
  public void stepIntoWorker(int isolateId) throws NotSuspendedException,
      NoResponseException, NotConnectedException {
    if (isWorkerSuspended(isolateId))
    {
      // send a step-into message and then wait for the Flash player to tell us that is has
      // resumed execution
      if (!simpleRequestResponseMessageIsolate(DMessage.OutStepInto, DMessage.InContinue, isolateId))
        throw new NoResponseException(getPreference(SessionManager.PREF_RESPONSE_TIMEOUT));
    }
    else
      throw new NotSuspendedException();
   
  }

  @Override
  public void stepOutWorker(int isolateId) throws NotSuspendedException,
      NoResponseException, NotConnectedException {
    if (isWorkerSuspended(isolateId))
    {
      // send a step-out message and then wait for the Flash player to tell us that is has
      // resumed execution
      if (!simpleRequestResponseMessageIsolate(DMessage.OutStepOut, DMessage.InContinue, isolateId))
        throw new NoResponseException(getPreference(SessionManager.PREF_RESPONSE_TIMEOUT));
    }
    else
      throw new NotSuspendedException();
   
  }

  @Override
  public void stepOverWorker(int isolateId) throws NotSuspendedException,
      NoResponseException, NotConnectedException {
    if (isWorkerSuspended(isolateId))
    {
      // send a step-over message and then wait for the Flash player to tell us that is has
      // resumed execution
      if (!simpleRequestResponseMessageIsolate(DMessage.OutStepOver, DMessage.InContinue, isolateId))
        throw new NoResponseException(getPreference(SessionManager.PREF_RESPONSE_TIMEOUT));
    }
    else
      throw new NotSuspendedException();
   
  }

  public boolean evalInWorker(Value property, Value object, int isolateId) throws PlayerDebugException, PlayerFaultException
  {
    return ECMA.toBoolean(evalBinaryOp(BinaryOp.In, property, object, isolateId));
  }

  public Value evalAsWorker(Value value, Value type, int isolateId) throws PlayerDebugException, PlayerFaultException {
    return evalBinaryOp(BinaryOp.As, value, type, isolateId);
  }

  @Override
  public void suspendWorker(int isolateId) throws SuspendedException,
      NotConnectedException, NoResponseException {
    // send a halt message
    int wait = getPreference(SessionManager.PREF_SUSPEND_WAIT);
     int every = 50; // wait this long for a response.  The lower the number the more aggressive we are!

    if (isWorkerSuspended(isolateId))
      throw new SuspendedException();

    while (!isWorkerSuspended(isolateId) && wait > 0)
    {
      simpleRequestResponseMessageIsolate(DMessage.OutStopDebug, DMessage.InBreakAtExt, every, isolateId);
      wait -= every;
    }

    if (!isWorkerSuspended(isolateId))
      throw new NoResponseException(wait)
  }

  @Override
  public IsolateSession getWorkerSession(int isolateId) {
    if (m_isolateSessions.containsKey(isolateId)) {
      return m_isolateSessions.get(isolateId);
    }
    else {
      IsolateSession workerSession = new IsolatePlayerSession(isolateId, this);
      m_isolateSessions.put(isolateId, workerSession);
      return workerSession;
    }
   
  }
 
  public boolean setExceptionBreakpoint(String exceptionClass) throws NoResponseException, NotConnectedException {
    return setExceptionBreakpointWorker(exceptionClass, Isolate.DEFAULT_ID);
  }
 
  public boolean setExceptionBreakpointWorker(String exceptionClass, int isolateId) throws NoResponseException, NotConnectedException {
    int messageSize = DMessage.getStringLength(exceptionClass) + 1;
   
    DMessage dm = DMessageCache.alloc(messageSize);
    dm.setType(DMessage.OutSetExceptionBreakpoint);
    dm.setTargetIsolate(isolateId);
    try {
      dm.putString(exceptionClass);
    } catch (UnsupportedEncodingException e) {
      // couldn't write out the string, so just terminate it and complete anyway
      dm.putByte((byte)'\0');
    }

    boolean gotResponse = simpleRequestResponseMessageIsolate(dm, DMessage.InSetExceptionBreakpoint, isolateId);
    if(gotResponse)
    {
      return true;
    }
    else
      throw new NoResponseException(getPreference(SessionManager.PREF_RESPONSE_TIMEOUT));

  }
 
  @Override
  public boolean clearExceptionBreakpoint(String exceptionClass) throws NoResponseException, NotConnectedException {
    return clearExceptionBreakpointWorker(exceptionClass, Isolate.DEFAULT_ID);
  }

  @Override
  public boolean clearExceptionBreakpointWorker(String exceptionClass, int isolateId) throws NoResponseException, NotConnectedException {
    int messageSize = DMessage.getStringLength(exceptionClass) + 1;
    DMessage dm = DMessageCache.alloc(messageSize);
    dm.setType(DMessage.OutRemoveExceptionBreakpoint);
    dm.setTargetIsolate(isolateId);
    try {
      dm.putString(exceptionClass);
    } catch (UnsupportedEncodingException e) {
      // couldn't write out the string, so just terminate it and complete anyway
      dm.putByte((byte)'\0');
    }

    boolean gotResponse = simpleRequestResponseMessageIsolate(dm, DMessage.InRemoveExceptionBreakpoint, isolateId);
    if(gotResponse)
    {
      return true;
    }
    else
      throw new NoResponseException(getPreference(SessionManager.PREF_RESPONSE_TIMEOUT));

  }

  @Override
  public void setLauncher(ILauncher launcher) {
    this.launcher = launcher;
  }

}
TOP

Related Classes of flash.tools.debugger.concrete.PlayerSession

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.