module:     dm-internals
synopsis:   The runtime context. 
            This object is generated by the DM, given to the environment,
            passed through the interactive compiler (which just holds onto
            it), given to the interactive downloader, which can unpick it
            again.
author:     Paul Howard
Copyright:    Original Code is Copyright (c) 1995-2004 Functional Objects, Inc.
              All rights reserved.
License:      See License.txt in this distribution for details.
Warranty:     Distributed WITHOUT WARRANTY OF ANY KIND


///// <RUNTIME-CONTEXT>
//    The DM is capable of allocating this object for any specified thread
//    during a debugger transaction. Only the DM and the interactive 
//    downloader need to unpick this object.

define sealed abstract class <runtime-context> (<object>)

  constant slot runtime-context-debug-target :: <debug-target>,
    required-init-keyword: target:;

  constant slot runtime-context-thread :: <remote-thread>,
    required-init-keyword: thread:;

  constant slot runtime-context-frame :: false-or(<application-stack-frame>),
    init-value: #f,
    init-keyword: stack-frame:;

  constant slot runtime-context-scoped-variables :: <sequence>,
    init-value: #[],
    init-keyword: scoped-variables:;

end class;


// Ensure instantiability.

define sealed class <dm-runtime-context> (<runtime-context>)
end class;

define sealed method make 
    (cl == <runtime-context>, #rest keys, #key, #all-keys)
       => (inst :: <runtime-context>)
  apply(make, <dm-runtime-context>, keys)
end method;


///// <HISTORY-PLACE-HOLDER>
//    Since ACTIVE-LEXICAL-VARIABLES is a compiler callback, we cannot
//    be sure of the application state when it is called. Therefore, we
//    must not try to obtain the values of history variables. Instead
//    we create one of these objects as a place-holder.

define class <history-place-holder> (<object>)

  constant slot history-place-holder-thread :: <remote-thread>,
    required-init-keyword: thread:;

  constant slot history-place-holder-index :: <integer>,
    required-init-keyword: index:;

end class;


///// ACTIVE-LEXICAL-VARIABLES
//    A method on the open GF exported by DFMC-INTERACTIVE-EXECUTION in the
//    DFMC-BROWSER-SUPPORT library.

define method active-lexical-variables (context :: <runtime-context>)
 => (plist :: <sequence>)
  let vars = #();
  let thread = context.runtime-context-thread;
  let application = context.runtime-context-debug-target;
  let dm-thread = find-thread(application, thread);

  local method place-holder-for (hist-index :: <integer>)
                    => (place-holder :: <history-place-holder>)
          make(<history-place-holder>,
               thread: thread, index: hist-index)
        end method;

  for (entry in context.runtime-context-scoped-variables)
    vars := pair(as(<symbol>, entry.head), pair(entry.tail, vars))
  end for;
  for (i from 0 below dm-thread.thread-registered-history.size)
    let name = format-to-string("$%d", i);
    vars := pair(as(<symbol>, name), pair(place-holder-for(i), vars));
  end for;
  vars;
end method;


///// CURRENT-RUNTIME-CONTEXT
//    Creates and returns the runtime-context for a thread in an application.

define method current-runtime-context
    (application :: <debug-target>, thread :: <remote-thread>,
     #key stack-frame = #f)
       => (context :: <runtime-context>)

  let dm-thread = find-thread(application, thread);

  // If we've already computed the runtime context for the
  // global (stack-frame == #f) or frame-local case, then use it.

  let cached-context = 
    element(dm-thread.thread-runtime-context, stack-frame, default: #f);

  local method find-scoped-variables () => (vars :: <sequence>)
    let scoped-variables = #[];
    if (instance?(stack-frame, <call-frame>) & 
        dylan-call-frame?(application, stack-frame))
      let (names, types, models, vals) =
        active-dylan-lexical-variables(application, stack-frame);
      scoped-variables := make(<vector>, size: models.size);
      for (i from 0 below models.size)
        scoped-variables[i] := pair(names[i], vals[i]);
      end for;
    end if;
    scoped-variables
  end method;

  if (cached-context)
    cached-context
  else
    let context = make(<runtime-context>, 
                       target: application, 
                       thread: thread,
                       stack-frame: stack-frame,
                       scoped-variables: find-scoped-variables());
    dm-thread.thread-runtime-context[stack-frame] := context;
    context
  end if;
end method;


