;;; -*- Mode: LISP; Package: USER; Base: 10 -*- ;;; $Header: /ct/debug/point.l,v 1.12 84/11/20 11:00:57 bill Exp $ (putprop 'point "$Revision: 1.12 $" 'rcs_revision) ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; ;;; point.l ;;; ;;; ;;; ;;; William Brew 3-28-83 ;;; ;;; ;;; ;;; Utilities for "pointing" in debugger windows. ;;; ;;; ;;; ;;; This file is part of a proprietary software project. Source ;;; ;;; code and documentation describing implementation details are ;;; ;;; available on a confidential, non-disclosure basis only. These ;;; ;;; materials, including this file in particular, are trade secrets ;;; ;;; of Computer * Thought Corporation. ;;; ;;; ;;; ;;; (c) Copyright 1982 and 1983, Computer * Thought Corporation. ;;; ;;; All Rights Reserved. ;;; ;;; ;;; ;;; Reference materials: ;;; ;;; Foderaro and Sklower, The FRANZ LISP Manual, September 1981. ;;; ;;; Weinreb and Moon, LISP MACHINE MANUAL, Symbolics, July 1981. ;;; ;;; Charniak et al., 1980. Artificial Intelligence Programming. ;;; ;;; Miller, 1982. The C*T Ada Tutor: Guide to the Implementation. ;;; ;;; ;;; ;;; The following code assumes familiarity with these materials. ;;; ;;; ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Ensure presence of needed files. (comment Assumes ct_load and some suitable file_map are present) (eval-when (compile load eval) (ct_load 'aip)) ; AIP macros pkg. (eval-when (compile load eval) (ct_load 'compat)) ; Franz/LM compat pkg. (eval-when (compile load eval) (ct_load 'ctstrl)) ;New strings (eval-when (compile load eval) (ct_load 'ctio)) ;Compatable io (eval-when (compile load eval) (ct_load 'dbutils)) ;Debugger utilities #+franz (eval-when (compile load eval) (ct_load 'screens)) ;Window flavors #+lispm (eval-when (compile load eval) (ct_load 'lmscreens));Window flavors (eval-when (compile load eval) (ct_load 'ferec)) ;Interpretter records (eval-when (compile load eval) (ct_load 'diana)) ;Diana node utiliies (eval-when (load eval) (ct_load 'dfind)) ;Diana finding ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Compiler Declarations and Global Variables -- #+franz (declare (macros t)) #+franz (setq *flavor-expand-macros* t) (declare (special *db%code_window* *db%diana* *db%source_files* *inbuilt_diana_trees*)) (defvar *db%choosable_files* nil "A list of files which the user may choose to view. Consists of the files the user inputs and any files from the envirnment") (defvar *db%source_roots* nil "A list of source roots.") (defvar *db%inbuilt_source_roots* nil "An alist which associates a source path with an inbuilt diana subtree.") (defvar *db%hidden_files* '("wazzoo" "library") "Files (or pseudo files) which we do not want the user to see.") #+franz (declare (localf db%start_trees db%init_trees db%process_comp_units db%title_and_point db%path_to_sroot ; needs work db%add_generic_source db%root db%get_definition_source_range db%init_window db%start_window db%instantiation_depth db%same_nodetypep)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Macro definitions -- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Flavor definitions -- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Externally Callable Functions/Methods -- ; ; Initialize the debugger pointing module ; (defun db%init_point () (db%init_trees) (db%init_window)) ; ; Startup the debugger pointing module. ; (defun db%start_point () (db%start_trees) (db%start_window)) ; ; Give a menu from which the user can choose the file that he wants ; to see displayed. The files available are those that were originally ; input when the session was begun and are found in *db%choosable_files* ; (defun db%choose_file () (let ((sroot (db%ask_literal "Select a file: " *db%choosable_files*))) (db%source_to_window *db%code_window* sroot))) ;Lets turn this off for now until we can work out some of the problems. #| ; ; Select a generic instance. Find out what the user is pointing at. If it is ; a generic instance looking thing, then find the corresponding source root ; and show the user the code. ; ; --NBMay want to tag the site of the instantiation so the user can find it easily. (defun db%select_generic () (let* ((node (db%get_best_node '(dn_proc_id dn_function_id dn_package_id))) (body (and (diana_nodep node) (diana_get node 'sm_body)))) (cond ((not (and (diana_nodep node) (db%generic_rootp node) (eq (db%classify_node node) 'generic_copy) body)) (db%message "What you are pointing at is not a generic instance.")) ((member (source_region%path (diana_get body 'lx_srcpos)) *db%hidden_files*) (db%message "The generic instance you are pointing at is in an unviewable file.")) (t (db%source_to_window *db%code_window* (db%node_to_record body)))))) |# ; ; Show the source which corresponds to the source to tree root in sroot ; in the indicated window. First we check that everything looks good. Also ; we try to avoid the overhead of getting the file again if we already have ; it hooked up to the window. Also remember the current position in ; the file in case we decide to look at it again. ; (defun db%source_to_window (window sroot &optional (ochar (diana_late_get sroot 'position)) (origin ':center)) (let ((path (diana_late_get sroot 'path)) (current_sroot (get-iv db%debug_window window 'name)) (cursor (db%ask_cursor window)) (xy)) (cond ((member path *db%hidden_files*) (db%message "The file ~a is unviewable." path)) ((not (db%probef path)) (db%message "The file ~a was not found." path)) ((and (eq sroot current_sroot) (ct_string_equal path (get-iv db%debug_window window 'filename))) (ct_csend db%debug_window window ':center-around-char ochar origin) (setq xy (ct_csend db%debug_window window ':translate-document-to-screen ochar)) (db%point_in_window window (first xy) (second xy))) ((ct_string_equal path (get-iv db%debug_window window 'filename)) (and (diana_nodep current_sroot) (diana_late_put current_sroot (ct_csend db%debug_window window ':translate-screen-to-document (first cursor) (second cursor)) 'position)) (ct_csend db%debug_window window ':restrict-range (diana_late_get sroot 'top) (diana_late_get sroot 'bottom) nil) (ct_csend db%debug_window window ':center-around-char ochar origin) (setq xy (ct_csend db%debug_window window ':translate-document-to-screen ochar)) (db%title_and_point window sroot xy)) (t (and (diana_nodep current_sroot) (diana_late_put current_sroot (ct_csend db%debug_window window ':translate-screen-to-document (first cursor) (second cursor)) 'position)) (ct_csend db%debug_window window ':display-file path (diana_late_get sroot 'top) (diana_late_get sroot 'bottom) ochar origin) (setq xy (ct_csend db%debug_window window ':translate-document-to-screen ochar)) (db%title_and_point window sroot xy))))) ; ; Find the source root which corresponds to a diana node. First we look for ; a "root" for the subtree containing the node. Then we look to see if we ; have a source root for this root. If we do then return it. If not, then ; we assume we have found a generic instance and so create a new source root ; for it. ; #| (defun db%node_to_record (node) (loop with root = (db%root node) with key = (ct_if (db%generic_rootp root) (diana_get root 'sm_body) root) while root for record in *db%source_roots* for node_list = (diana_get (db%source_record%tree record) 'as_list) if (memq key node_list) collect record into possibles finally (cond ((eq (length possibles) 1) (return (first possibles))) ((and (null possibles) (db%generic_rootp root)) (return (db%add_generic_source root))) (t (lose 'pnt_no_tree 'db%node_to_tree '("I can't find a tree to match a node.")))))) |# ; Lets turn off the generic stuff for now. Also, this time lets use a different ; scheme to find the appropriate tree. We call tree-memberp. It can look up ; to see if a node is a member of a tree. (defun db%node_to_sroot (node) (loop for candidate in (db%path_to_sroots (source_region%path (diana_get node 'lx_srcpos))) if (or (tree-memberp node candidate) (and (memq (diana_nodetype_get node) '(dn_ct_task_handler dn_ct_exception_handler)) (tree-memberp (first (diana_get node 'ct_threadp)) candidate))) collect candidate into possibles finally (cond ((eq (length possibles) 1) (return (first possibles))) ((null possibles) (lose 'pnt_no_tree 'db%node_to_sroot '("I can't find a tree to match a node."))) (t (lose 'pnt_many_tree 'db%node_to_sroot '("I found several possible trees for a node.")))))) ; ; Get a list of nodes of a type in node_set which best correspond to the ; current cursor position in the code window. Get the cursor position. ; Translate it. Look for a diana subtree which corresponds to the file ; and then call the diana look up routine. ; (defun db%get_best_nodes (node_set) (let ((cursor_pos (db%ask_cursor *db%code_window*)) (tree (get-iv db%debug_window *db%code_window* 'name)) nodes) (cond ((not (and (diana_nodep tree) (eq (diana_nodetype_get tree) 'dn_compilation))) (lose 'pnt_no_tree 'db%get_best_node '("I dont have a tree for that file."))) (t (setq nodes (get-best-nodes tree (ct_csend db%debug_window *db%code_window* ':translate-screen-to-document (first cursor_pos) (second cursor_pos)) node_set)) (or (apply #'db%same_nodetypep nodes) (lose 'pnt_diff_nodes 'db%get_best_node '("The nodes found were of different types."))) nodes)))) ; ; Classify a node into one of three classes; normal nodes, generic copy nodes or ; generic definition nodes. Generic copies belong to an instantiation of a generic ; unit. Generic definitions belong to the definition of a generic unit. Normals ; are everything that is left over. ; ;;out for now until we work out some of the problems with generics. #| (defun db%classify_node (node) (let ((root_self (ct_if (db%generic_rootp node) node (db%root node)))) (cond ((not (or (and (diana_node_accepts_attributep node 'ct_generic_membership) (diana_get node 'ct_generic_membership)) (db%generic_rootp node))) 'normal) ((not (db%generic_rootp root_self)) 'generic_definition) ((memq (db%classify_node (first (diana_get (first (diana_get root_self 'ct_threadp)) 'ct_threadp))) `(normal generic_copy)) ; ((eq (db%instantiation_depth root) ; (length (diana_get (diana_get root 'sm_body) 'ct_generic_membership))) 'generic_copy) (t 'generic_definition)))) |# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Internal Use Only Functions/Methods -- ; Out for now until we work out some of the problems #| ; ; Find the generic instantiation depth of a node. We loop looking for successive ; ancestral roots of the node until we find one that is not a generic root. This ; should be the top of the tree. Depth counts the number of generic roots which ; we found. ; (defun db%instantiation_depth (node) (loop for root = (db%root node) then (db%root (first (diana_get root 'ct_threadp))) for depth from 0 while (db%generic_rootp root) finally (return depth))) |# ; ; A predicate to determine if a list of diana nodes are all of the same type. ; (defun db%same_nodetypep (&rest nodes) (loop with firsttype = (and (diana_nodep (first nodes)) (diana_nodetype_get (first nodes))) for node in (cdr nodes) always (and (diana_nodep node) (eq firsttype (diana_nodetype_get node))))) ; ; Initialize the inbuilt trees by flattening them for diana finding later on. ; Remember the inbuilt map. ; (defun db%init_trees () (setq *db%source_roots* nil) (and (boundp '*inbuilt_diana_trees*) (db%process_comp_units *inbuilt_diana_trees*)) (setq *db%inbuilt_source_roots* *db%source_roots*)) ; ; Initialize the pointing window. For now we just clear it out. ; (defun db%init_window () (ct_csend db%debug_window *db%code_window* ':display-string "")) ; ; Get all the diana trees set up. Initilize the map from the inbuilts. ; Flatten the users trees. ; Gather up the files names and set the source file list. Put up the initial ; display in the code window. ; (defun db%start_trees () (db%message "Preparing program for debugging ...") (setq *db%source_roots* *db%inbuilt_source_roots*) (db%process_comp_units (diana_get *db%diana* 'as_list)) (setq *db%choosable_files* (loop for sroot in *db%source_roots* if (eq (diana_late_get sroot 'type) 'source) collect (list (diana_late_get sroot 'path_string) sroot) into sources if (eq (diana_late_get sroot 'type) 'library) collect (list (diana_late_get sroot 'path_string) sroot) into libraries if (eq (diana_late_get sroot 'type) 'environment) collect (list (diana_late_get sroot 'path_string) sroot) into environments finally (return (nconc (nreverse sources) libraries environments))))) ; ; Start up the pointing window. Clear it out first so that it forgets the out file. ; Then show the user the first choosable file. ; (defun db%start_window () (ct_csend db%debug_window *db%code_window* ':display-string "") (set-iv db%debug_window *db%code_window* 'name "The window with no name") (db%source_to_window *db%code_window* (second (first *db%choosable_files*)) 0 ':top)) ; ; Put the title on the window and position the cursor appropriately. ; (defun db%title_and_point (window sroot xy) (db%point_in_window window (first xy) (second xy)) (set-iv db%debug_window window 'name sroot) (db%change_file_name (diana_late_get sroot 'path_string))) ; ; Find the sroots associated with a path. ; (defun db%path_to_sroots (path) (loop for sroot in *db%source_roots* if (ct_string_equal path (diana_late_get sroot 'path)) collect sroot)) ; ; Find the "root" for the diana tree containing node. We define the root ; as the comp unit if this is a normal node or a generic definition. If ; it is a generic instance, then we look for the id node which corresponds ; to the copy. Note that if we seach from below and find a generic root we ; call it the copy. If we start with the generic root then we search upward. ; ;Not needed while we have all the generic stuff commented out #| (defun db%root (node) (loop for the_node = node then (first (diana_get the_node 'ct_threadp)) if (not (diana_nodep the_node)) do (lose 'pnt_no_root 'db%root '("Can't find the root for a node.")) if (or (eq (diana_nodetype_get the_node) 'dn_comp_unit) (and (neq the_node node) (db%generic_rootp the_node))) return the_node)) |# ; ; Add a generic instantiation to the list of source trees. First look to see ; if this tree is already in the list. If not then we add it by cons'ing up ; a new dn_compilation. We make this dn_compilation look acceptable. ; We form a special source_record by looking around for the strings to use ; for its title line. Also we set its top and bottom so that only the ; body of the generic will be viewable. Finally, we flatten the tree so that ; we can search it later. ; ;;out for now while we work on a better scheme for generics. #| (defun db%add_generic_source (root) (let* ((body (diana_get root 'sm_body)) (spec (diana_get root 'sm_spec)) (path (source_region%path (diana_get body 'lx_srcpos))) (new_record (loop for record in *db%source_roots* if (memq body (diana_get (db%source_record%tree record) 'as_list)) return record)) new_node new_srcpos) (cond (new_record) ((member path *db%hidden_files*) nil) (t (setq new_node (diana_cons 'dn_compilation)) (diana_put new_node (list body spec) 'as_list) (diana_put new_node (source_region (- *plus-infinity* -1) (1- *plus-infinity*) 0 0 path 0 0) 'lx_srcpos) (diana_put new_node nil 'ct_threadp) (setq new_srcpos (db%get_definition_source_range root)) (setq new_record (db%source_record 'generic path (ct_format nil "<~a>~13tInstantiation of ~a ~a" 'generic (ct_selectq (diana_nodetype_get root) (dn_proc_id "procedure") (dn_function_id "function") (dn_package_id "package")) (apply #'ct_string_append (second (diana_get root 'lx_symrep)))) (source_region%startchar new_srcpos) (source_region%endchar new_srcpos) new_node (source_region%startchar new_srcpos))) (setq *db%source_roots* (cons new_record *db%source_roots*)) (get-best-nodes new_node path nil nil))) new_record)) |# ; ; Get the source position for the declaration of the body of a generic unit. ; First walk up the tree and over to the instantiation to find out what we are ; instantiating. Then find its defining occurence. Look for the body and finally, ; go up one node to get the declaration. When looking for the body declaration, ; we look for a subprogram_decl for generic subprograms. This is a ct departure ; from standard diana. ; ;out for now while we work on a better scheme for generics #| (defun db%get_definition_source_range (root) (let* ((decl (first (diana_get root 'ct_threadp))) (instant (ct_selectq (diana_nodetype_get decl) (dn_subprogram_decl (diana_get decl 'as_subprogram_def)) (dn_package_decl (diana_get decl 'as_package_def)) (otherwise (lose 'pnt_gen_def 'db%get_definition_source_range '("Cannot find generic definition"))))) (body (diana_get (db%diana_defineself (diana_get instant 'as_name)) 'sm_body))) (loop for daddy in (diana_get body 'ct_threadp) if (memq (diana_nodetype_get daddy) '(dn_subprogram_decl dn_package_body)) return (diana_get daddy 'lx_srcpos) finally (lose 'pnt_gen_body 'db%get_definition_source_range '("Cannot find the body declaration of a generic unit"))))) |# ; ; Process diana compilation units for diana finding. Loop over the list of ; compilation units. If the comp unit is for a hidden file do nothing. If this is ; the first time for this file then build a fake dn_compilation and add to the ; source to tree map. If we have seen the file already, then just put another ; comp unit in the fake dn_compilation. Finally call get best node to flatten ; things. Note, get best node will remember which subtrees it has flattened ; already and hence may be called multiple times with the same subtree. Note, ; that when this processing takes place, there is a one to one correspondance ; between paths and trees. Later, when we start discovering generic subtrees, ; this will no longer hold. ; (defun db%process_comp_units (comps) (loop with (nodes type) for comp_unit in comps for path = (source_region%path (diana_get comp_unit 'lx_srcpos)) for sroot = (first (db%path_to_sroots path)) do (cond ((member path *db%hidden_files*) nil) ((null sroot) (setq sroot (diana_cons 'dn_compilation)) (diana_put sroot (list comp_unit) 'as_list) (diana_put sroot (source_region nil nil 0 0 path 0 0) 'lx_srcpos) (diana_put sroot nil 'ct_threadp) (setq type (cond ((memq comp_unit *inbuilt_diana_trees*) 'environment) ((member path *db%source_files*) 'source) (t 'library))) (diana_late_put sroot type 'type) (diana_late_put sroot path 'path) (diana_late_put sroot (ct_format nil "<~a>~13t~a" type path) 'path_string) (diana_late_put sroot 0 'position) (setq *db%source_roots* (cons sroot *db%source_roots*))) ((and (setq nodes (diana_get sroot 'as_list)) (not (memq comp_unit nodes))) (diana_put sroot (cons comp_unit nodes) 'as_list)) (t (lose 'pnt_bad_tree 'db%process_comp_units '("An error has occurred trying to collect trees"))))) (flatten-diana-trees *db%source_roots*)) ;;; eof ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;