1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-04 14:20:26 +02:00
guile/ice-9/debugger/breakpoints/source.scm
Neil Jerram 3f619266b1 Fix error in recent define-option-interface "simplification".
Fix source breakpoints to cope with port whose filename is #f.
Ensure that position recording is enabled when source breakpoint module loaded.
2002-11-06 00:29:24 +00:00

287 lines
9.9 KiB
Scheme

;;;; (ice-9 debugger breakpoints source) -- source location breakpoints
;;; Copyright (C) 2002 Free Software Foundation, Inc.
;;;
;;; This program is free software; you can redistribute it and/or
;;; modify it under the terms of the GNU General Public License as
;;; published by the Free Software Foundation; either version 2, or
;;; (at your option) any later version.
;;;
;;; This program is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;;; General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this software; see the file COPYING. If not, write to
;;; the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
;;; Boston, MA 02111-1307 USA
;;;
;;; As a special exception, the Free Software Foundation gives permission
;;; for additional uses of the text contained in its release of GUILE.
;;;
;;; The exception is that, if you link the GUILE library with other files
;;; to produce an executable, this does not by itself cause the
;;; resulting executable to be covered by the GNU General Public License.
;;; Your use of that executable is in no way restricted on account of
;;; linking the GUILE library code into it.
;;;
;;; This exception does not however invalidate any other reasons why
;;; the executable file might be covered by the GNU General Public License.
;;;
;;; This exception applies only to the code released by the
;;; Free Software Foundation under the name GUILE. If you copy
;;; code from other Free Software Foundation releases into a copy of
;;; GUILE, as the General Public License permits, the exception does
;;; not apply to the code that you add in this way. To avoid misleading
;;; anyone as to the status of such modified files, you must delete
;;; this exception notice from them.
;;;
;;; If you write modifications of your own for GUILE, it is your choice
;;; whether to permit this exception to apply to your modifications.
;;; If you do not wish that, delete this exception notice.
(define-module (ice-9 debugger breakpoints source)
#:use-module (ice-9 format)
#:use-module (ice-9 debugger breakpoints)
#:use-module (ice-9 debugger trap-hooks)
#:use-module (ice-9 debugger trc)
#:use-module (ice-9 debugger utils)
#:use-module (oop goops)
#:export (<source-breakpoint>
bp-location
bp-expression))
;;; {Source Breakpoints}
;;;
;;; Breakpoints that activate upon reaching a particular source
;;; location or range of source locations.
(define-generic bp-location)
(define-generic bp-expression)
(define-class <source-breakpoint> (<breakpoint>)
;; The location of this breakpoint.
(location #:accessor bp-location
#:init-keyword #:location)
;; The source expression at this breakpoint.
(expression #:accessor bp-expression
#:init-keyword #:expression)
;; Counter: incremented when the breakpoint is set, decremented when
;; a source expression using this breakpoint applied has been GC'd.
(use-count #:accessor bp-use-count
#:init-value 0))
(define (location->string filename line column)
(format #f "~A:~A:~A" filename (+ line 1) (+ column 1)))
(define-method (bp-message (bp <source-breakpoint>) message port)
(format port
"~A ~A: ~A: ~S\n"
message
(bp-number bp)
(apply location->string (bp-location bp))
(bp-expression bp)))
(define-method (bp-describe (bp <source-breakpoint>) port)
(next-method)
(if (zero? (bp-use-count bp))
(format port "\t(this breakpoint is a zombie)\n"))
*unspecified*)
;;; Alist of all source breakpoints:
;;; ((LOCATION . BREAKPOINT) ...)
;;; where LOCATION is
;;; (FILE-NAME LINE COLUMN)
;;; Keys are unique according to `equal?'.
(define source-breakpoints '())
(define-method (get-breakpoint (filename <string>)
(line <integer>)
(column <integer>))
(assoc-ref source-breakpoints (list filename line column)))
;;; When the source expression that a breakpoint is attached to is
;;; GC'd, typically because the variable that included it in its value
;;; has been redefined, we'd like to mark the breakpoint as no longer
;;; relevant. We do this by using a property guardian ...
(define (make-property-guardian)
;; Return a new property guardian. A property guardian is a
;; combination of a guardian and an object property that accepts KEY
;; -> VALUE associations and gives you back the VALUE when its KEY
;; has been garbage collected.
;;
;; To store an association, call it in the same way as you would an
;; object property: (set! (PROPERTY-GUARDIAN KEY) VALUE).
;;
;; To retrieve the VALUE for a KEY that has been GC'd, call the
;; property guardian in the same way as you would a guardian, with
;; no args: (PROPERTY-GUARDIAN).
(let ((p (make-object-property))
(g (make-guardian)))
(make-procedure-with-setter
(lambda ()
(let ((collected (g)))
(and collected (car collected))))
(lambda (key value)
(let ((collectible (list value)))
;; Store the collectible value both as an object property,
;; and in the guardian.
(set! (p key) collectible)
(g collectible))))))
(define source-breakpoint-guardian (make-property-guardian))
(add-hook! after-gc-hook
(lambda ()
(let loop ((bp (source-breakpoint-guardian)))
(if bp
(let ((new-use-count (- (bp-use-count bp) 1)))
(set! (bp-use-count bp) new-use-count)
(if (zero? new-use-count)
(bp-message bp "Zombified breakpoint" #t))
(loop (source-breakpoint-guardian)))))))
(define (add-breakpoint filename line column expression)
(let* ((location (list filename line column))
(bp (make <source-breakpoint>
#:location location
#:expression (if (pair? expression)
;; The point of this strange looking
;; copy is to copy the expression
;; without its source properties.
;; This is necessary to allow the
;; source properties to be GC'd when
;; the source expression becomes
;; obsolete. (Note that `copy-tree'
;; copies source properties as well!)
(cons (car expression) (cdr expression))
expression))))
(set! source-breakpoints (assoc-set! source-breakpoints location bp))
bp))
(define-method (set-breakpoint! behaviour
x-as-read
(x-pairified <pair>))
(let ((filename (source-property x-pairified 'filename))
(line (source-property x-pairified 'line))
(column (source-property x-pairified 'column)))
(let ((bp (or (get-breakpoint filename line column)
(add-breakpoint filename line column x-as-read))))
(set! (bp-behaviour bp) behaviour)
(install-breakpoint x-pairified bp)
(bp-message bp "Set breakpoint" #t)
bp)))
(define (install-breakpoint x bp)
;; Make the necessary connections with the specified expression and
;; its breakpoint.
(set-source-property! x 'breakpoint #t)
(set! (source-breakpoint-guardian x) bp)
(set! (bp-use-count bp) (+ (bp-use-count bp) 1))
(remove/install-source-breakpoint-hooks))
(define remove/install-source-breakpoint-hooks
(let ((hooks-installed? #f))
(lambda ()
(cond ((and hooks-installed?
(null? source-breakpoints))
(remove-hook! before-enter-frame-hook
source-before-enter-frame-hook)
(remove-breakpoint-hook! source-breakpoint-hook)
(set! hooks-installed? #f))
((and (not hooks-installed?)
(not (null? source-breakpoints)))
(add-hook! before-enter-frame-hook
source-before-enter-frame-hook)
(add-breakpoint-hook! source-breakpoint-hook)
(set! hooks-installed? #t))))))
(define *cont* #f)
(define (source-before-enter-frame-hook cont . ignored)
(trc 'source-before-enter-frame-hook)
(set! *cont* cont))
(define (source-breakpoint-hook)
(trc 'source-breakpoint-hook)
(let* ((frame (last-stack-frame *cont*))
(source (frame-source frame))
(position (and source (source-position source)))
(bp (and position (apply get-breakpoint position))))
(if bp
(bp-run bp))))
(define-method (bp-delete! (bp <source-breakpoint>))
(set! source-breakpoints (assoc-remove! source-breakpoints (bp-location bp)))
(remove/install-source-breakpoint-hooks)
(bp-message bp "Deleted breakpoint" #t)
*unspecified*)
(register-breakpoint-subclass <source-breakpoint>
(lambda ()
(map cdr source-breakpoints)))
(read-hash-extend #\#
(lambda (c port)
(let (;; Save off port coordinates before reading
;; the following expression, as we'll need
;; to install source coordinates by hand if
;; the expression turns out not to be a
;; pair.
(filename (port-filename port))
(line (port-line port))
(column (port-column port)))
;; Now read the marked expression.
(let* ((x (read port))
(x' (if (pair? x)
x
;; The marked expression isn't a
;; pair, so it can't carry source
;; properties by itself.
;; Therefore we pretend instead
;; to have read `(begin X)', and
;; attach coordinate and
;; breakpoint information to the
;; begin expression.
(let ((x' (list begin x)))
(set-source-property! x' 'filename
filename)
(set-source-property! x' 'line
line)
(set-source-property! x' 'column
column)
x'))))
;; Don't allow breakpointed expression to have
;; a filename property that isn't a string.
(or (string? filename)
(set-source-property! x' 'filename "<unnamed port>"))
(break! x x')
x'))))
(read-enable 'positions)
; (define g (make-guardian))
; (read-hash-extend #\g
; (lambda (c port)
; (let ((x (read port))
; (l (list 3)))
; (set-source-property! x 'test l)
; (g l)
; x)))
; (add-hook! after-gc-hook
; (lambda ()
; (let loop ((obj (g)))
; (if obj
; (begin
; (display "Guardian: ")
; (write obj)
; (newline)
; (loop (g)))))))
;;; (ice-9 debugger breakpoints source) ends here.