summaryrefslogtreecommitdiff
path: root/module/system
diff options
context:
space:
mode:
Diffstat (limited to 'module/system')
-rw-r--r--module/system/base/compile.scm258
-rw-r--r--module/system/base/language.scm99
-rw-r--r--module/system/base/message.scm102
-rw-r--r--module/system/base/pmatch.scm41
-rw-r--r--module/system/base/syntax.scm327
-rw-r--r--module/system/repl/command.scm502
-rw-r--r--module/system/repl/common.scm112
-rw-r--r--module/system/repl/describe.scm360
-rw-r--r--module/system/repl/repl.scm150
-rw-r--r--module/system/vm/debug.scm62
-rw-r--r--module/system/vm/frame.scm209
-rw-r--r--module/system/vm/instruction.scm27
-rw-r--r--module/system/vm/objcode.scm27
-rw-r--r--module/system/vm/profile.scm64
-rw-r--r--module/system/vm/program.scm100
-rw-r--r--module/system/vm/trace.scm76
-rw-r--r--module/system/vm/vm.scm41
-rw-r--r--module/system/xref.scm182
18 files changed, 2739 insertions, 0 deletions
diff --git a/module/system/base/compile.scm b/module/system/base/compile.scm
new file mode 100644
index 000000000..26dd29e20
--- /dev/null
+++ b/module/system/base/compile.scm
@@ -0,0 +1,258 @@
+;;; High-level compiler interface
+
+;; Copyright (C) 2001, 2009 Free Software Foundation, Inc.
+
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public
+;;; License as published by the Free Software Foundation; either
+;;; version 3 of the License, or (at your option) any later version.
+;;;
+;;; This library 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
+;;; Lesser General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+;;; Code:
+
+(define-module (system base compile)
+ #:use-module (system base syntax)
+ #:use-module (system base language)
+ #:use-module (system base message)
+ #:use-module (system vm vm) ;; FIXME: there's a reason for this, can't remember why tho
+ #:use-module (ice-9 regex)
+ #:use-module (ice-9 optargs)
+ #:use-module (ice-9 receive)
+ #:export (syntax-error
+ *current-language*
+ compiled-file-name compile-file compile-and-load
+ compile
+ decompile)
+ #:export-syntax (call-with-compile-error-catch))
+
+;;;
+;;; Compiler environment
+;;;
+
+(define (syntax-error loc msg exp)
+ (throw 'syntax-error-compile-time loc msg exp))
+
+(define-macro (call-with-compile-error-catch thunk)
+ `(catch 'syntax-error-compile-time
+ ,thunk
+ (lambda (key loc msg exp)
+ (if (pair? loc)
+ (let ((file (or (assq-ref loc 'filename) "unknown file"))
+ (line (assq-ref loc 'line))
+ (col (assq-ref loc 'column)))
+ (format (current-error-port)
+ "~A:~A:~A: ~A: ~A~%" file line col msg exp))
+ (format (current-error-port)
+ "unknown location: ~A: ~S~%" msg exp)))))
+
+
+;;;
+;;; Compiler
+;;;
+
+(define *current-language* (make-fluid))
+(fluid-set! *current-language* 'scheme)
+(define (current-language)
+ (fluid-ref *current-language*))
+
+(define (call-once thunk)
+ (let ((entered #f))
+ (dynamic-wind
+ (lambda ()
+ (if entered
+ (error "thunk may only be entered once: ~a" thunk))
+ (set! entered #t))
+ thunk
+ (lambda () #t))))
+
+(define* (call-with-output-file/atomic filename proc #:optional reference)
+ (let* ((template (string-append filename ".XXXXXX"))
+ (tmp (mkstemp! template)))
+ (call-once
+ (lambda ()
+ (with-throw-handler #t
+ (lambda ()
+ (proc tmp)
+ (chmod tmp (logand #o0666 (lognot (umask))))
+ (close-port tmp)
+ (if reference
+ (let ((st (stat reference)))
+ (utime template (stat:atime st) (stat:mtime st))))
+ (rename-file template filename))
+ (lambda args
+ (delete-file template)))))))
+
+(define (ensure-language x)
+ (if (language? x)
+ x
+ (lookup-language x)))
+
+;; Throws an exception if `dir' is not writable. The double-stat is OK,
+;; as this is only used during compilation.
+(define (ensure-writable-dir dir)
+ (if (file-exists? dir)
+ (if (access? dir W_OK)
+ #t
+ (error "directory not writable" dir))
+ (begin
+ (ensure-writable-dir (dirname dir))
+ (mkdir dir))))
+
+(define (dsu-sort list key less)
+ (map cdr
+ (stable-sort (map (lambda (x) (cons (key x) x)) list)
+ (lambda (x y) (less (car x) (car y))))))
+
+;;; This function is among the trickiest I've ever written. I tried many
+;;; variants. In the end, simple is best, of course.
+;;;
+;;; After turning this around a number of times, it seems that the the
+;;; desired behavior is that .go files should exist in a path, for
+;;; searching. That is orthogonal to this function. For writing .go
+;;; files, either you know where they should go, in which case you tell
+;;; compile-file explicitly, as in the srcdir != builddir case; or you
+;;; don't know, in which case this function is called, and we just put
+;;; them in your own ccache dir in ~/.guile-ccache.
+(define (compiled-file-name file)
+ (define (compiled-extension)
+ (cond ((or (null? %load-compiled-extensions)
+ (string-null? (car %load-compiled-extensions)))
+ (warn "invalid %load-compiled-extensions"
+ %load-compiled-extensions)
+ ".go")
+ (else (car %load-compiled-extensions))))
+ (and %compile-fallback-path
+ (let ((f (string-append
+ %compile-fallback-path
+ ;; no need for '/' separator here, canonicalize-path
+ ;; will give us an absolute path
+ (canonicalize-path file)
+ (compiled-extension))))
+ (and (false-if-exception (ensure-writable-dir (dirname f)))
+ f))))
+
+(define* (compile-file file #:key
+ (output-file #f)
+ (env #f)
+ (from (current-language))
+ (to 'objcode)
+ (opts '()))
+ (let* ((comp (or output-file (compiled-file-name file)))
+ (in (open-input-file file))
+ (enc (file-encoding in)))
+ (if enc
+ (set-port-encoding! in enc))
+ (ensure-writable-dir (dirname comp))
+ (call-with-output-file/atomic comp
+ (lambda (port)
+ ((language-printer (ensure-language to))
+ (read-and-compile in #:env env #:from from #:to to #:opts opts)
+ port))
+ file)
+ comp))
+
+(define* (compile-and-load file #:key (from 'scheme) (to 'value) (opts '()))
+ (read-and-compile (open-input-file file)
+ #:from from #:to to #:opts opts))
+
+
+;;;
+;;; Compiler interface
+;;;
+
+(define (compile-passes from to opts)
+ (map cdr
+ (or (lookup-compilation-order from to)
+ (error "no way to compile" from "to" to))))
+
+(define (compile-fold passes exp env opts)
+ (let lp ((passes passes) (x exp) (e env) (cenv env) (first? #t))
+ (if (null? passes)
+ (values x e cenv)
+ (receive (x e new-cenv) ((car passes) x e opts)
+ (lp (cdr passes) x e (if first? new-cenv cenv) #f)))))
+
+(define (find-language-joint from to)
+ (let lp ((in (reverse (or (lookup-compilation-order from to)
+ (error "no way to compile" from "to" to))))
+ (lang to))
+ (cond ((null? in)
+ (error "don't know how to join expressions" from to))
+ ((language-joiner lang) lang)
+ (else
+ (lp (cdr in) (caar in))))))
+
+(define* (read-and-compile port #:key
+ (env #f)
+ (from (current-language))
+ (to 'objcode)
+ (opts '()))
+ (let ((from (ensure-language from))
+ (to (ensure-language to)))
+ (let ((joint (find-language-joint from to)))
+ (with-fluids ((*current-language* from))
+ (let lp ((exps '()) (env #f) (cenv env))
+ (let ((x ((language-reader (current-language)) port)))
+ (cond
+ ((eof-object? x)
+ (compile ((language-joiner joint) (reverse exps) env)
+ #:from joint #:to to #:env env #:opts opts))
+ (else
+ ;; compile-fold instead of compile so we get the env too
+ (receive (jexp jenv jcenv)
+ (compile-fold (compile-passes (current-language) joint opts)
+ x cenv opts)
+ (lp (cons jexp exps) jenv jcenv))))))))))
+
+(define* (compile x #:key
+ (env #f)
+ (from (current-language))
+ (to 'value)
+ (opts '()))
+
+ (let ((warnings (memq #:warnings opts)))
+ (if (pair? warnings)
+ (let ((warnings (cadr warnings)))
+ ;; Sanity-check the requested warnings.
+ (for-each (lambda (w)
+ (or (lookup-warning-type w)
+ (warning 'unsupported-warning #f w)))
+ warnings))))
+
+ (receive (exp env cenv)
+ (compile-fold (compile-passes from to opts) x env opts)
+ exp))
+
+
+;;;
+;;; Decompiler interface
+;;;
+
+(define (decompile-passes from to opts)
+ (map cdr
+ (or (lookup-decompilation-order from to)
+ (error "no way to decompile" from "to" to))))
+
+(define (decompile-fold passes exp env opts)
+ (if (null? passes)
+ (values exp env)
+ (receive (exp env) ((car passes) exp env opts)
+ (decompile-fold (cdr passes) exp env opts))))
+
+(define* (decompile x #:key
+ (env #f)
+ (from 'value)
+ (to 'assembly)
+ (opts '()))
+ (decompile-fold (decompile-passes from to opts)
+ x
+ env
+ opts))
diff --git a/module/system/base/language.scm b/module/system/base/language.scm
new file mode 100644
index 000000000..3670c53d9
--- /dev/null
+++ b/module/system/base/language.scm
@@ -0,0 +1,99 @@
+;;; Multi-language support
+
+;; Copyright (C) 2001, 2009 Free Software Foundation, Inc.
+
+;; This library is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU Lesser General Public
+;; License as published by the Free Software Foundation; either
+;; version 3 of the License, or (at your option) any later version.
+;;
+;; This library 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
+;; Lesser General Public License for more details.
+;;
+;; You should have received a copy of the GNU Lesser General Public
+;; License along with this library; if not, write to the Free Software
+;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+;; 02110-1301 USA
+
+;;; Code:
+
+(define-module (system base language)
+ #:use-module (system base syntax)
+ #:export (define-language language? lookup-language make-language
+ language-name language-title language-version language-reader
+ language-printer language-parser
+ language-compilers language-decompilers language-evaluator
+ language-joiner
+
+ lookup-compilation-order lookup-decompilation-order
+ invalidate-compilation-cache!))
+
+
+;;;
+;;; Language class
+;;;
+
+(define-record/keywords <language>
+ name
+ title
+ version
+ reader
+ printer
+ (parser #f)
+ (compilers '())
+ (decompilers '())
+ (evaluator #f)
+ (joiner #f))
+
+(define-macro (define-language name . spec)
+ `(begin
+ (invalidate-compilation-cache!)
+ (define ,name (make-language #:name ',name ,@spec))))
+
+(define (lookup-language name)
+ (let ((m (resolve-module `(language ,name spec))))
+ (if (module-bound? m name)
+ (module-ref m name)
+ (error "no such language" name))))
+
+(define *compilation-cache* '())
+(define *decompilation-cache* '())
+
+(define (invalidate-compilation-cache!)
+ (set! *decompilation-cache* '())
+ (set! *compilation-cache* '()))
+
+(define (compute-translation-order from to language-translators)
+ (cond
+ ((not (language? to))
+ (compute-translation-order from (lookup-language to) language-translators))
+ (else
+ (let lp ((from from) (seen '()))
+ (cond
+ ((not (language? from))
+ (lp (lookup-language from) seen))
+ ((eq? from to) (reverse! seen))
+ ((memq from seen) #f)
+ (else (or-map (lambda (pair)
+ (lp (car pair) (acons from (cdr pair) seen)))
+ (language-translators from))))))))
+
+(define (lookup-compilation-order from to)
+ (let ((key (cons from to)))
+ (or (assoc-ref *compilation-cache* key)
+ (let ((order (compute-translation-order from to language-compilers)))
+ (set! *compilation-cache*
+ (acons key order *compilation-cache*))
+ order))))
+
+(define (lookup-decompilation-order from to)
+ (let ((key (cons from to)))
+ (or (assoc-ref *decompilation-cache* key)
+ ;; trickery!
+ (let ((order (and=>
+ (compute-translation-order to from language-decompilers)
+ reverse!)))
+ (set! *decompilation-cache* (acons key order *decompilation-cache*))
+ order))))
diff --git a/module/system/base/message.scm b/module/system/base/message.scm
new file mode 100644
index 000000000..6b68c5639
--- /dev/null
+++ b/module/system/base/message.scm
@@ -0,0 +1,102 @@
+;;; User interface messages
+
+;; Copyright (C) 2009 Free Software Foundation, Inc.
+
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public
+;;; License as published by the Free Software Foundation; either
+;;; version 3 of the License, or (at your option) any later version.
+;;;
+;;; This library 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
+;;; Lesser General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+;;; Commentary:
+;;;
+;;; This module provide a simple interface to send messages to the user.
+;;; TODO: Internationalize messages.
+;;;
+;;; Code:
+
+(define-module (system base message)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-9)
+ #:export (*current-warning-port* warning
+
+ warning-type? warning-type-name warning-type-description
+ warning-type-printer lookup-warning-type
+
+ %warning-types))
+
+
+;;;
+;;; Source location
+;;;
+
+(define (location-string loc)
+ (if (pair? loc)
+ (format #f "~a:~a:~a"
+ (or (assoc-ref loc 'filename) "<stdin>")
+ (1+ (assoc-ref loc 'line))
+ (assoc-ref loc 'column))
+ "<unknown-location>"))
+
+
+;;;
+;;; Warnings
+;;;
+
+(define *current-warning-port*
+ ;; The port where warnings are sent.
+ (make-fluid))
+
+(fluid-set! *current-warning-port* (current-error-port))
+
+(define-record-type <warning-type>
+ (make-warning-type name description printer)
+ warning-type?
+ (name warning-type-name)
+ (description warning-type-description)
+ (printer warning-type-printer))
+
+(define %warning-types
+ ;; List of know warning types.
+ (map (lambda (args)
+ (apply make-warning-type args))
+
+ `((unsupported-warning ;; a "meta warning"
+ "warn about unknown warning types"
+ ,(lambda (port unused name)
+ (format port "warning: unknown warning type `~A'~%"
+ name)))
+
+ (unused-variable
+ "report unused variables"
+ ,(lambda (port loc name)
+ (format port "~A: warning: unused variable `~A'~%"
+ loc name))))))
+
+(define (lookup-warning-type name)
+ "Return the warning type NAME or `#f' if not found."
+ (find (lambda (wt)
+ (eq? name (warning-type-name wt)))
+ %warning-types))
+
+(define (warning type location . args)
+ "Emit a warning of type TYPE for source location LOCATION (a source
+property alist) using the data in ARGS."
+ (let ((wt (lookup-warning-type type))
+ (port (fluid-ref *current-warning-port*)))
+ (if (warning-type? wt)
+ (apply (warning-type-printer wt)
+ port (location-string location)
+ args)
+ (format port "~A: unknown warning type `~A': ~A~%"
+ (location-string location) type args))))
+
+;;; message.scm ends here
diff --git a/module/system/base/pmatch.scm b/module/system/base/pmatch.scm
new file mode 100644
index 000000000..4777431e5
--- /dev/null
+++ b/module/system/base/pmatch.scm
@@ -0,0 +1,41 @@
+(define-module (system base pmatch)
+ #:export (pmatch))
+;; FIXME: shouldn't have to export ppat...
+
+;; Originally written by Oleg Kiselyov. Taken from:
+;; αKanren: A Fresh Name in Nominal Logic Programming
+;; by William E. Byrd and Daniel P. Friedman
+;; Proceedings of the 2007 Workshop on Scheme and Functional Programming
+;; Université Laval Technical Report DIUL-RT-0701
+
+;; Licensing unclear. Probably need to ask Oleg for a disclaimer.
+
+(define-syntax pmatch
+ (syntax-rules (else guard)
+ ((_ (op arg ...) cs ...)
+ (let ((v (op arg ...)))
+ (pmatch v cs ...)))
+ ((_ v) (if #f #f))
+ ((_ v (else e0 e ...)) (let () e0 e ...))
+ ((_ v (pat (guard g ...) e0 e ...) cs ...)
+ (let ((fk (lambda () (pmatch v cs ...))))
+ (ppat v pat
+ (if (and g ...) (let () e0 e ...) (fk))
+ (fk))))
+ ((_ v (pat e0 e ...) cs ...)
+ (let ((fk (lambda () (pmatch v cs ...))))
+ (ppat v pat (let () e0 e ...) (fk))))))
+
+(define-syntax ppat
+ (syntax-rules (_ quote unquote)
+ ((_ v _ kt kf) kt)
+ ((_ v () kt kf) (if (null? v) kt kf))
+ ((_ v (quote lit) kt kf)
+ (if (equal? v (quote lit)) kt kf))
+ ((_ v (unquote var) kt kf) (let ((var v)) kt))
+ ((_ v (x . y) kt kf)
+ (if (pair? v)
+ (let ((vx (car v)) (vy (cdr v)))
+ (ppat vx x (ppat vy y kt kf) kf))
+ kf))
+ ((_ v lit kt kf) (if (eq? v (quote lit)) kt kf))))
diff --git a/module/system/base/syntax.scm b/module/system/base/syntax.scm
new file mode 100644
index 000000000..249961d79
--- /dev/null
+++ b/module/system/base/syntax.scm
@@ -0,0 +1,327 @@
+;;; Guile VM specific syntaxes and utilities
+
+;; Copyright (C) 2001, 2009 Free Software Foundation, Inc
+
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public
+;;; License as published by the Free Software Foundation; either
+;;; version 3 of the License, or (at your option) any later version.
+;;;
+;;; This library 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
+;;; Lesser General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+;;; Code:
+
+(define-module (system base syntax)
+ #:export (%compute-initargs)
+ #:export-syntax (define-type define-record define-record/keywords
+ record-case transform-record))
+
+(define (symbol-trim-both sym pred)
+ (string->symbol (string-trim-both (symbol->string sym) pred)))
+(define (trim-brackets sym)
+ (symbol-trim-both sym (list->char-set '(#\< #\>))))
+
+
+;;;
+;;; Type
+;;;
+
+(define-macro (define-type name . rest)
+ (let ((name (if (pair? name) (car name) name))
+ (opts (if (pair? name) (cdr name) '())))
+ (let ((printer (kw-arg-ref opts #:printer))
+ (common-slots (or (kw-arg-ref opts #:common-slots) '())))
+ `(begin ,@(map (lambda (def)
+ `(define-record ,(if printer
+ `(,(car def) ,printer)
+ (car def))
+ ,@common-slots
+ ,@(cdr def)))
+ rest)
+ ,@(map (lambda (common-slot i)
+ `(define ,(symbol-append (trim-brackets name)
+ '- common-slot)
+ (make-procedure-with-setter
+ (lambda (x) (struct-ref x ,i))
+ (lambda (x v) (struct-set! x ,i v)))))
+ common-slots (iota (length common-slots)))))))
+
+
+;;;
+;;; Record
+;;;
+
+(define-macro (define-record name-form . slots)
+ (let* ((name (if (pair? name-form) (car name-form) name-form))
+ (printer (and (pair? name-form) (cadr name-form)))
+ (slot-names (map (lambda (slot) (if (pair? slot) (car slot) slot))
+ slots))
+ (stem (trim-brackets name)))
+ `(begin
+ (define ,name (make-record-type ,(symbol->string name) ',slot-names
+ ,@(if printer (list printer) '())))
+ ,(let* ((reqs (let lp ((slots slots))
+ (if (or (null? slots) (not (symbol? (car slots))))
+ '()
+ (cons (car slots) (lp (cdr slots))))))
+ (opts (list-tail slots (length reqs)))
+ (tail (gensym)))
+ `(define (,(symbol-append 'make- stem) ,@reqs . ,tail)
+ (let ,(map (lambda (o)
+ `(,(car o) (cond ((null? ,tail) ,(cadr o))
+ (else (let ((_x (car ,tail)))
+ (set! ,tail (cdr ,tail))
+ _x)))))
+ opts)
+ (make-struct ,name 0 ,@slot-names))))
+ (define ,(symbol-append stem '?) (record-predicate ,name))
+ ,@(map (lambda (sname)
+ `(define ,(symbol-append stem '- sname)
+ (make-procedure-with-setter
+ (record-accessor ,name ',sname)
+ (record-modifier ,name ',sname))))
+ slot-names))))
+
+;; like the former, but accepting keyword arguments in addition to
+;; optional arguments
+(define-macro (define-record/keywords name-form . slots)
+ (let* ((name (if (pair? name-form) (car name-form) name-form))
+ (printer (and (pair? name-form) (cadr name-form)))
+ (slot-names (map (lambda (slot) (if (pair? slot) (car slot) slot))
+ slots))
+ (stem (trim-brackets name)))
+ `(begin
+ (define ,name (make-record-type ,(symbol->string name) ',slot-names
+ ,@(if printer (list printer) '())))
+ (define ,(symbol-append 'make- stem)
+ (let ((slots (list ,@(map (lambda (slot)
+ (if (pair? slot)
+ `(cons ',(car slot) ,(cadr slot))
+ `',slot))
+ slots)))
+ (constructor (record-constructor ,name)))
+ (lambda args
+ (apply constructor (%compute-initargs args slots)))))
+ (define ,(symbol-append stem '?) (record-predicate ,name))
+ ,@(map (lambda (sname)
+ `(define ,(symbol-append stem '- sname)
+ (make-procedure-with-setter
+ (record-accessor ,name ',sname)
+ (record-modifier ,name ',sname))))
+ slot-names))))
+
+(define (%compute-initargs args slots)
+ (define (finish out)
+ (map (lambda (slot)
+ (let ((name (if (pair? slot) (car slot) slot)))
+ (cond ((assq name out) => cdr)
+ ((pair? slot) (cdr slot))
+ (else (error "unbound slot" args slots name)))))
+ slots))
+ (let lp ((in args) (positional slots) (out '()))
+ (cond
+ ((null? in)
+ (finish out))
+ ((keyword? (car in))
+ (let ((sym (keyword->symbol (car in))))
+ (cond
+ ((and (not (memq sym slots))
+ (not (assq sym (filter pair? slots))))
+ (error "unknown slot" sym))
+ ((assq sym out) (error "slot already set" sym out))
+ (else (lp (cddr in) '() (acons sym (cadr in) out))))))
+ ((null? positional)
+ (error "too many initargs" args slots))
+ (else
+ (lp (cdr in) (cdr positional)
+ (let ((slot (car positional)))
+ (acons (if (pair? slot) (car slot) slot)
+ (car in)
+ out)))))))
+
+;; So, dear reader. It is pleasant indeed around this fire or at this
+;; cafe or in this room, is it not? I think so too.
+;;
+;; This macro used to generate code that looked like this:
+;;
+;; `(((record-predicate ,record-type) ,r)
+;; (let ,(map (lambda (slot)
+;; (if (pair? slot)
+;; `(,(car slot) ((record-accessor ,record-type ',(cadr slot)) ,r))
+;; `(,slot ((record-accessor ,record-type ',slot) ,r))))
+;; slots)
+;; ,@body)))))
+;;
+;; But this was a hot spot, so computing all those predicates and
+;; accessors all the time was getting expensive, so we did a terrible
+;; thing: we decided that since above we're already defining accessors
+;; and predicates with computed names, we might as well just rely on that fact here.
+;;
+;; It's a bit nasty, I agree. But it is fast.
+;;
+;;scheme@(guile-user)> (with-statprof #:hz 1000 #:full-stacks? #t (resolve-module '(oop goops)))% cumulative self
+;; time seconds seconds name
+;; 8.82 0.03 0.01 glil->assembly
+;; 8.82 0.01 0.01 record-type-fields
+;; 5.88 0.01 0.01 %compute-initargs
+;; 5.88 0.01 0.01 list-index
+
+
+;;; So ugly... but I am too ignorant to know how to make it better.
+(define-syntax record-case
+ (lambda (x)
+ (syntax-case x ()
+ ((_ record clause ...)
+ (let ((r (syntax r))
+ (rtd (syntax rtd)))
+ (define (process-clause tag fields exprs)
+ (let ((infix (trim-brackets (syntax->datum tag))))
+ (with-syntax ((tag tag)
+ (((f . accessor) ...)
+ (let lp ((fields fields))
+ (syntax-case fields ()
+ (() (syntax ()))
+ (((v0 f0) f1 ...)
+ (acons (syntax v0)
+ (datum->syntax x
+ (symbol-append infix '- (syntax->datum
+ (syntax f0))))
+ (lp (syntax (f1 ...)))))
+ ((f0 f1 ...)
+ (acons (syntax f0)
+ (datum->syntax x
+ (symbol-append infix '- (syntax->datum
+ (syntax f0))))
+ (lp (syntax (f1 ...))))))))
+ ((e0 e1 ...)
+ (syntax-case exprs ()
+ (() (syntax (#t)))
+ ((e0 e1 ...) (syntax (e0 e1 ...))))))
+ (syntax
+ ((eq? rtd tag)
+ (let ((f (accessor r))
+ ...)
+ e0 e1 ...))))))
+ (with-syntax
+ ((r r)
+ (rtd rtd)
+ ((processed ...)
+ (let lp ((clauses (syntax (clause ...)))
+ (out '()))
+ (syntax-case clauses (else)
+ (()
+ (reverse! (cons (syntax
+ (else (error "unhandled record" r)))
+ out)))
+ (((else e0 e1 ...))
+ (reverse! (cons (syntax (else e0 e1 ...)) out)))
+ (((else e0 e1 ...) . rest)
+ (syntax-violation 'record-case
+ "bad else clause placement"
+ (syntax x)
+ (syntax (else e0 e1 ...))))
+ ((((<foo> f0 ...) e0 ...) . rest)
+ (lp (syntax rest)
+ (cons (process-clause (syntax <foo>)
+ (syntax (f0 ...))
+ (syntax (e0 ...)))
+ out)))))))
+ (syntax
+ (let* ((r record)
+ (rtd (struct-vtable r)))
+ (cond processed ...)))))))))
+
+
+;; Here we take the terrorism to another level. Nasty, but the client
+;; code looks good.
+
+(define-macro (transform-record type-and-common record . clauses)
+ (let ((r (gensym))
+ (rtd (gensym))
+ (type-stem (trim-brackets (car type-and-common))))
+ (define (make-stem s)
+ (symbol-append type-stem '- s))
+ (define (further-predicates x record-stem slots)
+ (define (access slot)
+ `(,(symbol-append (make-stem record-stem) '- slot) ,x))
+ (let lp ((in slots) (out '()))
+ (cond ((null? in) out)
+ ((pair? (car in))
+ (let ((slot (caar in))
+ (arg (cadar in)))
+ (cond ((symbol? arg)
+ (lp (cdr in) out))
+ ((pair? arg)
+ (lp (cdr in)
+ (append (further-predicates (access slot)
+ (car arg)
+ (cdr arg))
+ out)))
+ (else (lp (cdr in) (cons `(eq? ,(access slot) ',arg)
+ out))))))
+ (else (lp (cdr in) out)))))
+ (define (let-clauses x record-stem slots)
+ (define (access slot)
+ `(,(symbol-append (make-stem record-stem) '- slot) ,x))
+ (let lp ((in slots) (out '()))
+ (cond ((null? in) out)
+ ((pair? (car in))
+ (let ((slot (caar in))
+ (arg (cadar in)))
+ (cond ((symbol? arg)
+ (lp (cdr in)
+ (cons `(,arg ,(access slot)) out)))
+ ((pair? arg)
+ (lp (cdr in)
+ (append (let-clauses (access slot)
+ (car arg)
+ (cdr arg))
+ out)))
+ (else
+ (lp (cdr in) out)))))
+ (else
+ (lp (cdr in)
+ (cons `(,(car in) ,(access (car in))) out))))))
+ (define (transform-expr x)
+ (cond ((not (pair? x)) x)
+ ((eq? (car x) '->)
+ (if (= (length x) 2)
+ (let ((form (cadr x)))
+ `(,(symbol-append 'make- (make-stem (car form)))
+ ,@(cdr type-and-common)
+ ,@(map (lambda (y)
+ (if (and (pair? y) (eq? (car y) 'unquote))
+ (transform-expr (cadr y))
+ y))
+ (cdr form))))
+ (error "bad -> form" x)))
+ (else (cons (car x) (map transform-expr (cdr x))))))
+ (define (process-clause clause)
+ (if (eq? (car clause) 'else)
+ clause
+ (let ((stem (caar clause))
+ (slots (cdar clause))
+ (body (cdr clause)))
+ (let ((record-type (symbol-append '< (make-stem stem) '>)))
+ `((and (eq? ,rtd ,record-type)
+ ,@(reverse (further-predicates r stem slots)))
+ (let ,(reverse (let-clauses r stem slots))
+ ,@(if (pair? body)
+ (map transform-expr body)
+ '((if #f #f)))))))))
+ `(let* ((,r ,record)
+ (,rtd (struct-vtable ,r))
+ ,@(map (lambda (slot)
+ `(,slot (,(make-stem slot) ,r)))
+ (cdr type-and-common)))
+ (cond ,@(let ((clauses (map process-clause clauses)))
+ (if (assq 'else clauses)
+ clauses
+ (append clauses `((else (error "unhandled record" ,r))))))))))
diff --git a/module/system/repl/command.scm b/module/system/repl/command.scm
new file mode 100644
index 000000000..a99e1bae9
--- /dev/null
+++ b/module/system/repl/command.scm
@@ -0,0 +1,502 @@
+;;; Repl commands
+
+;; Copyright (C) 2001, 2009 Free Software Foundation, Inc.
+
+;; This library is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU Lesser General Public
+;; License as published by the Free Software Foundation; either
+;; version 3 of the License, or (at your option) any later version.
+;;
+;; This library 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
+;; Lesser General Public License for more details.
+;;
+;; You should have received a copy of the GNU Lesser General Public
+;; License along with this library; if not, write to the Free Software
+;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+;; 02110-1301 USA
+
+;;; Code:
+
+(define-module (system repl command)
+ #:use-module (system base syntax)
+ #:use-module (system base pmatch)
+ #:use-module (system base compile)
+ #:use-module (system repl common)
+ #:use-module (system vm objcode)
+ #:use-module (system vm program)
+ #:use-module (system vm vm)
+ #:autoload (system base language) (lookup-language language-reader)
+ #:autoload (system vm debug) (vm-debugger vm-backtrace)
+ #:autoload (system vm trace) (vm-trace vm-trace-on vm-trace-off)
+ #:autoload (system vm profile) (vm-profile)
+ #:use-module (ice-9 format)
+ #:use-module (ice-9 session)
+ #:use-module (ice-9 documentation)
+ #:use-module (ice-9 and-let-star)
+ #:use-module (ice-9 rdelim)
+ #:export (meta-command))
+
+
+;;;
+;;; Meta command interface
+;;;
+
+(define *command-table*
+ '((help (help h) (apropos a) (describe d) (option o) (quit q))
+ (module (module m) (import i) (load l) (binding b))
+ (language (language L))
+ (compile (compile c) (compile-file cc)
+ (disassemble x) (disassemble-file xx))
+ (profile (time t) (profile pr))
+ (debug (backtrace bt) (debugger db) (trace tr) (step st))
+ (system (gc) (statistics stat))))
+
+(define (group-name g) (car g))
+(define (group-commands g) (cdr g))
+
+;; Hack, until core can be extended.
+(define procedure-documentation
+ (let ((old-definition procedure-documentation))
+ (lambda (p)
+ (if (program? p)
+ (program-documentation p)
+ (old-definition p)))))
+
+(define *command-module* (current-module))
+(define (command-name c) (car c))
+(define (command-abbrev c) (if (null? (cdr c)) #f (cadr c)))
+(define (command-procedure c) (module-ref *command-module* (command-name c)))
+(define (command-doc c) (procedure-documentation (command-procedure c)))
+
+(define (command-usage c)
+ (let ((doc (command-doc c)))
+ (substring doc 0 (string-index doc #\newline))))
+
+(define (command-summary c)
+ (let* ((doc (command-doc c))
+ (start (1+ (string-index doc #\newline))))
+ (cond ((string-index doc #\newline start)
+ => (lambda (end) (substring doc start end)))
+ (else (substring doc start)))))
+
+(define (lookup-group name)
+ (assq name *command-table*))
+
+(define (lookup-command key)
+ (let loop ((groups *command-table*) (commands '()))
+ (cond ((and (null? groups) (null? commands)) #f)
+ ((null? commands)
+ (loop (cdr groups) (cdar groups)))
+ ((memq key (car commands)) (car commands))
+ (else (loop groups (cdr commands))))))
+
+(define (display-group group . opts)
+ (format #t "~:(~A~) Commands [abbrev]:~2%" (group-name group))
+ (for-each (lambda (c)
+ (display-summary (command-usage c)
+ (command-abbrev c)
+ (command-summary c)))
+ (group-commands group))
+ (newline))
+
+(define (display-command command)
+ (display "Usage: ")
+ (display (command-doc command))
+ (newline))
+
+(define (display-summary usage abbrev summary)
+ (let ((abbrev (if abbrev (format #f "[,~A]" abbrev) "")))
+ (format #t " ,~24A ~8@A - ~A\n" usage abbrev summary)))
+
+(define (read-datum repl)
+ (read))
+
+(define read-line
+ (let ((orig-read-line read-line))
+ (lambda (repl)
+ (orig-read-line))))
+
+(define (meta-command repl)
+ (let ((command (read-datum repl)))
+ (if (not (symbol? command))
+ (user-error "Meta-command not a symbol: ~s" command))
+ (let ((c (lookup-command command)))
+ (if c
+ ((command-procedure c) repl)
+ (user-error "Unknown meta command: ~A" command)))))
+
+(define-syntax define-meta-command
+ (syntax-rules ()
+ ((_ (name repl (expression0 ...) . datums) docstring b0 b1 ...)
+ (define (name repl)
+ docstring
+ (let* ((expression0
+ (with-fluid* current-reader
+ (language-reader (repl-language repl))
+ (lambda () (repl-reader ""))))
+ ...)
+ (apply (lambda datums b0 b1 ...)
+ (let ((port (open-input-string (read-line repl))))
+ (let lp ((out '()))
+ (let ((x (read port)))
+ (if (eof-object? x)
+ (reverse out)
+ (lp (cons x out))))))))))
+ ((_ (name repl . datums) docstring b0 b1 ...)
+ (define-meta-command (name repl () . datums)
+ docstring b0 b1 ...))))
+
+
+
+;;;
+;;; Help commands
+;;;
+
+(define-meta-command (help repl . args)
+ "help
+help GROUP
+help [-c] COMMAND
+
+Gives help on the meta-commands available at the REPL.
+
+With one argument, tries to look up the argument as a group name, giving
+help on that group if successful. Otherwise tries to look up the
+argument as a command, giving help on the command.
+
+If there is a command whose name is also a group name, use the ,help
+-c COMMAND form to give help on the command instead of the group.
+
+Without any argument, a list of help commands and command groups
+are displayed."
+ (pmatch args
+ (()
+ (display-group (lookup-group 'help))
+ (display "Command Groups:\n\n")
+ (display-summary "help all" #f "List all commands")
+ (for-each (lambda (g)
+ (let* ((name (symbol->string (group-name g)))
+ (usage (string-append "help " name))
+ (header (string-append "List " name " commands")))
+ (display-summary usage #f header)))
+ (cdr *command-table*))
+ (newline)
+ (display "Type `,COMMAND -h' to show documentation of each command.")
+ (newline))
+ ((all)
+ (for-each display-group *command-table*))
+ ((,group) (guard (lookup-group group))
+ (display-group (lookup-group group)))
+ ((,command) (guard (lookup-command command))
+ (display-command (lookup-command command)))
+ ((-c ,command) (guard (lookup-command command))
+ (display-command (lookup-command command)))
+ ((,command)
+ (user-error "Unknown command or group: ~A" command))
+ ((-c ,command)
+ (user-error "Unknown command: ~A" command))
+ (else
+ (user-error "Bad arguments: ~A" args))))
+
+(define guile:apropos apropos)
+(define-meta-command (apropos repl regexp)
+ "apropos REGEXP
+Find bindings/modules/packages."
+ (guile:apropos (->string regexp)))
+
+(define-meta-command (describe repl (form))
+ "describe OBJ
+Show description/documentation."
+ (display (object-documentation (repl-eval repl (repl-parse repl form))))
+ (newline))
+
+(define-meta-command (option repl . args)
+ "option [KEY VALUE]
+List/show/set options."
+ (pmatch args
+ (()
+ (for-each (lambda (key+val)
+ (format #t "~A\t~A\n" (car key+val) (cdr key+val)))
+ (repl-options repl)))
+ ((,key)
+ (display (repl-option-ref repl key))
+ (newline))
+ ((,key ,val)
+ (repl-option-set! repl key val)
+ (case key
+ ((trace)
+ (let ((vm (repl-vm repl)))
+ (if val
+ (apply vm-trace-on vm val)
+ (vm-trace-off vm))))))))
+
+(define-meta-command (quit repl)
+ "quit
+Quit this session."
+ (throw 'quit))
+
+
+;;;
+;;; Module commands
+;;;
+
+(define-meta-command (module repl . args)
+ "module [MODULE]
+Change modules / Show current module."
+ (pmatch args
+ (() (puts (module-name (current-module))))
+ ((,mod-name) (guard (list? mod-name))
+ (set-current-module (resolve-module mod-name)))
+ (,mod-name (set-current-module (resolve-module mod-name)))))
+
+(define-meta-command (import repl . args)
+ "import [MODULE ...]
+Import modules / List those imported."
+ (let ()
+ (define (use name)
+ (let ((mod (resolve-interface name)))
+ (if mod
+ (module-use! (current-module) mod)
+ (user-error "No such module: ~A" name))))
+ (if (null? args)
+ (for-each puts (map module-name (module-uses (current-module))))
+ (for-each use args))))
+
+(define-meta-command (load repl file . opts)
+ "load FILE
+Load a file in the current module.
+
+ -f Load source file (see `compile')"
+ (let* ((file (->string file))
+ (objcode (if (memq #:f opts)
+ (apply load-source-file file opts)
+ (apply load-file file opts))))
+ (vm-load (repl-vm repl) objcode)))
+
+(define-meta-command (binding repl)
+ "binding
+List current bindings."
+ (module-for-each (lambda (k v) (format #t "~23A ~A\n" k v))
+ (current-module)))
+
+
+;;;
+;;; Language commands
+;;;
+
+(define-meta-command (language repl name)
+ "language LANGUAGE
+Change languages."
+ (set! (repl-language repl) (lookup-language name))
+ (repl-welcome repl))
+
+
+;;;
+;;; Compile commands
+;;;
+
+(define-meta-command (compile repl (form) . opts)
+ "compile FORM
+Generate compiled code.
+
+ -e Stop after expanding syntax/macro
+ -t Stop after translating into GHIL
+ -c Stop after generating GLIL
+
+ -O Enable optimization
+ -D Add debug information"
+ (let ((x (apply repl-compile repl (repl-parse repl form) opts)))
+ (cond ((objcode? x) (guile:disassemble x))
+ (else (repl-print repl x)))))
+
+(define guile:compile-file compile-file)
+(define-meta-command (compile-file repl file . opts)
+ "compile-file FILE
+Compile a file."
+ (guile:compile-file (->string file) #:opts opts))
+
+(define (guile:disassemble x)
+ ((@ (language assembly disassemble) disassemble) x))
+
+(define-meta-command (disassemble repl (form))
+ "disassemble PROGRAM
+Disassemble a program."
+ (guile:disassemble (repl-eval repl (repl-parse repl form))))
+
+(define-meta-command (disassemble-file repl file)
+ "disassemble-file FILE
+Disassemble a file."
+ (guile:disassemble (load-objcode (->string file))))
+
+
+;;;
+;;; Profile commands
+;;;
+
+(define-meta-command (time repl (form))
+ "time FORM
+Time execution."
+ (let* ((vms-start (vm-stats (repl-vm repl)))
+ (gc-start (gc-run-time))
+ (tms-start (times))
+ (result (repl-eval repl (repl-parse repl form)))
+ (tms-end (times))
+ (gc-end (gc-run-time))
+ (vms-end (vm-stats (repl-vm repl))))
+ (define (get proc start end)
+ (exact->inexact (/ (- (proc end) (proc start)) internal-time-units-per-second)))
+ (repl-print repl result)
+ (display "clock utime stime cutime cstime gctime\n")
+ (format #t "~5,2F ~5,2F ~5,2F ~6,2F ~6,2F ~6,2F\n"
+ (get tms:clock tms-start tms-end)
+ (get tms:utime tms-start tms-end)
+ (get tms:stime tms-start tms-end)
+ (get tms:cutime tms-start tms-end)
+ (get tms:cstime tms-start tms-end)
+ (get identity gc-start gc-end))
+ result))
+
+(define-meta-command (profile repl form . opts)
+ "profile FORM
+Profile execution."
+ (apply vm-profile
+ (repl-vm repl)
+ (repl-compile repl (repl-parse repl form))
+ opts))
+
+
+;;;
+;;; Debug commands
+;;;
+
+(define-meta-command (backtrace repl)
+ "backtrace
+Display backtrace."
+ (vm-backtrace (repl-vm repl)))
+
+(define-meta-command (debugger repl)
+ "debugger
+Start debugger."
+ (vm-debugger (repl-vm repl)))
+
+(define-meta-command (trace repl form . opts)
+ "trace FORM
+Trace execution.
+
+ -s Display stack
+ -l Display local variables
+ -b Bytecode level trace"
+ (apply vm-trace (repl-vm repl)
+ (repl-compile repl (repl-parse repl form))
+ opts))
+
+(define-meta-command (step repl)
+ "step FORM
+Step execution."
+ (display "Not implemented yet\n"))
+
+
+;;;
+;;; System commands
+;;;
+
+(define guile:gc gc)
+(define-meta-command (gc repl)
+ "gc
+Garbage collection."
+ (guile:gc))
+
+(define-meta-command (statistics repl)
+ "statistics
+Display statistics."
+ (let ((this-tms (times))
+ (this-vms (vm-stats (repl-vm repl)))
+ (this-gcs (gc-stats))
+ (last-tms (repl-tm-stats repl))
+ (last-vms (repl-vm-stats repl))
+ (last-gcs (repl-gc-stats repl)))
+ ;; GC times
+ (let ((this-times (assq-ref this-gcs 'gc-times))
+ (last-times (assq-ref last-gcs 'gc-times)))
+ (display-diff-stat "GC times:" #t this-times last-times "times")
+ (newline))
+ ;; Memory size
+ (let ((this-cells (assq-ref this-gcs 'cells-allocated))
+ (this-heap (assq-ref this-gcs 'cell-heap-size))
+ (this-bytes (assq-ref this-gcs 'bytes-malloced))
+ (this-malloc (assq-ref this-gcs 'gc-malloc-threshold)))
+ (display-stat-title "Memory size:" "current" "limit")
+ (display-stat "heap" #f this-cells this-heap "cells")
+ (display-stat "malloc" #f this-bytes this-malloc "bytes")
+ (newline))
+ ;; Cells collected
+ (let ((this-marked (assq-ref this-gcs 'cells-marked))
+ (last-marked (assq-ref last-gcs 'cells-marked))
+ (this-swept (assq-ref this-gcs 'cells-swept))
+ (last-swept (assq-ref last-gcs 'cells-swept)))
+ (display-stat-title "Cells collected:" "diff" "total")
+ (display-diff-stat "marked" #f this-marked last-marked "cells")
+ (display-diff-stat "swept" #f this-swept last-swept "cells")
+ (newline))
+ ;; GC time taken
+ (let ((this-mark (assq-ref this-gcs 'gc-mark-time-taken))
+ (last-mark (assq-ref last-gcs 'gc-mark-time-taken))
+ (this-total (assq-ref this-gcs 'gc-time-taken))
+ (last-total (assq-ref last-gcs 'gc-time-taken)))
+ (display-stat-title "GC time taken:" "diff" "total")
+ (display-time-stat "mark" this-mark last-mark)
+ (display-time-stat "total" this-total last-total)
+ (newline))
+ ;; Process time spent
+ (let ((this-utime (tms:utime this-tms))
+ (last-utime (tms:utime last-tms))
+ (this-stime (tms:stime this-tms))
+ (last-stime (tms:stime last-tms))
+ (this-cutime (tms:cutime this-tms))
+ (last-cutime (tms:cutime last-tms))
+ (this-cstime (tms:cstime this-tms))
+ (last-cstime (tms:cstime last-tms)))
+ (display-stat-title "Process time spent:" "diff" "total")
+ (display-time-stat "user" this-utime last-utime)
+ (display-time-stat "system" this-stime last-stime)
+ (display-time-stat "child user" this-cutime last-cutime)
+ (display-time-stat "child system" this-cstime last-cstime)
+ (newline))
+ ;; VM statistics
+ (let ((this-time (vms:time this-vms))
+ (last-time (vms:time last-vms))
+ (this-clock (vms:clock this-vms))
+ (last-clock (vms:clock last-vms)))
+ (display-stat-title "VM statistics:" "diff" "total")
+ (display-time-stat "time spent" this-time last-time)
+ (display-diff-stat "bogoclock" #f this-clock last-clock "clock")
+ (display-mips-stat "bogomips" this-time this-clock last-time last-clock)
+ (newline))
+ ;; Save statistics
+ ;; Save statistics
+ (set! (repl-tm-stats repl) this-tms)
+ (set! (repl-vm-stats repl) this-vms)
+ (set! (repl-gc-stats repl) this-gcs)))
+
+(define (display-stat title flag field1 field2 unit)
+ (let ((str (format #f "~~20~AA ~~10@A /~~10@A ~~A~~%" (if flag "" "@"))))
+ (format #t str title field1 field2 unit)))
+
+(define (display-stat-title title field1 field2)
+ (display-stat title #t field1 field2 ""))
+
+(define (display-diff-stat title flag this last unit)
+ (display-stat title flag (- this last) this unit))
+
+(define (display-time-stat title this last)
+ (define (conv num)
+ (format #f "~10,2F" (exact->inexact (/ num internal-time-units-per-second))))
+ (display-stat title #f (conv (- this last)) (conv this) "s"))
+
+(define (display-mips-stat title this-time this-clock last-time last-clock)
+ (define (mips time clock)
+ (if (= time 0) "----" (format #f "~10,2F" (/ clock time 1000000.0))))
+ (display-stat title #f
+ (mips (- this-time last-time) (- this-clock last-clock))
+ (mips this-time this-clock) "mips"))
diff --git a/module/system/repl/common.scm b/module/system/repl/common.scm
new file mode 100644
index 000000000..2db4518ad
--- /dev/null
+++ b/module/system/repl/common.scm
@@ -0,0 +1,112 @@
+;;; Repl common routines
+
+;; Copyright (C) 2001 Free Software Foundation, Inc.
+
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public
+;;; License as published by the Free Software Foundation; either
+;;; version 3 of the License, or (at your option) any later version.
+;;;
+;;; This library 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
+;;; Lesser General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+;;; Code:
+
+(define-module (system repl common)
+ #:use-module (system base syntax)
+ #:use-module (system base compile)
+ #:use-module (system base language)
+ #:use-module (system vm vm)
+ #:export (<repl> make-repl repl-vm repl-language repl-options
+ repl-tm-stats repl-gc-stats repl-vm-stats
+ repl-welcome repl-prompt repl-read repl-compile repl-eval
+ repl-parse repl-print repl-option-ref repl-option-set!
+ puts ->string user-error))
+
+
+;;;
+;;; Repl type
+;;;
+
+(define-record/keywords <repl> vm language options tm-stats gc-stats vm-stats)
+
+(define repl-default-options
+ '((trace . #f)
+ (interp . #f)))
+
+(define %make-repl make-repl)
+(define (make-repl lang)
+ (%make-repl #:vm (the-vm)
+ #:language (lookup-language lang)
+ #:options repl-default-options
+ #:tm-stats (times)
+ #:gc-stats (gc-stats)
+ #:vm-stats (vm-stats (the-vm))))
+
+(define (repl-welcome repl)
+ (let ((language (repl-language repl)))
+ (format #t "~A interpreter ~A on Guile ~A\n"
+ (language-title language) (language-version language) (version)))
+ (display "Copyright (C) 2001-2008 Free Software Foundation, Inc.\n\n")
+ (display "Enter `,help' for help.\n"))
+
+(define (repl-prompt repl)
+ (format #f "~A@~A> " (language-name (repl-language repl))
+ (module-name (current-module))))
+
+(define (repl-read repl)
+ ((language-reader (repl-language repl))))
+
+(define (repl-compile repl form . opts)
+ (let ((to (lookup-language (cond ((memq #:e opts) 'scheme)
+ ((memq #:t opts) 'ghil)
+ ((memq #:c opts) 'glil)
+ (else 'objcode)))))
+ (compile form #:from (repl-language repl) #:to to #:opts opts)))
+
+(define (repl-parse repl form)
+ (let ((parser (language-parser (repl-language repl))))
+ (if parser (parser form) form)))
+
+(define (repl-eval repl form)
+ (let ((eval (language-evaluator (repl-language repl))))
+ (if (and eval
+ (or (null? (language-compilers (repl-language repl)))
+ (assq-ref (repl-options repl) 'interp)))
+ (eval form (current-module))
+ (vm-load (repl-vm repl) (repl-compile repl form '())))))
+
+(define (repl-print repl val)
+ (if (not (eq? val *unspecified*))
+ (begin
+ ;; The result of an evaluation is representable in scheme, and
+ ;; should be printed with the generic printer, `write'. The
+ ;; language-printer is something else: it prints expressions of
+ ;; a given language, not the result of evaluation.
+ (write val)
+ (newline))))
+
+(define (repl-option-ref repl key)
+ (assq-ref (repl-options repl) key))
+
+(define (repl-option-set! repl key val)
+ (set! (repl-options repl) (assq-set! (repl-options repl) key val)))
+
+
+;;;
+;;; Utilities
+;;;
+
+(define (puts x) (display x) (newline))
+
+(define (->string x)
+ (object->string x display))
+
+(define (user-error msg . args)
+ (throw 'user-error #f msg args #f))
diff --git a/module/system/repl/describe.scm b/module/system/repl/describe.scm
new file mode 100644
index 000000000..590d2235a
--- /dev/null
+++ b/module/system/repl/describe.scm
@@ -0,0 +1,360 @@
+;;; Describe objects
+
+;; Copyright (C) 2001 Free Software Foundation, Inc.
+
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public
+;;; License as published by the Free Software Foundation; either
+;;; version 3 of the License, or (at your option) any later version.
+;;;
+;;; This library 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
+;;; Lesser General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+;;; Code:
+
+(define-module (system repl describe)
+ #:use-module (oop goops)
+ #:use-module (ice-9 regex)
+ #:use-module (ice-9 format)
+ #:use-module (ice-9 and-let-star)
+ #:export (describe))
+
+(define-method (describe (symbol <symbol>))
+ (format #t "`~s' is " symbol)
+ (if (not (defined? symbol))
+ (display "not defined in the current module.\n")
+ (describe-object (module-ref (current-module) symbol))))
+
+
+;;;
+;;; Display functions
+;;;
+
+(define (safe-class-name class)
+ (if (slot-bound? class 'name)
+ (class-name class)
+ class))
+
+(define-method (display-class class . args)
+ (let* ((name (safe-class-name class))
+ (desc (if (pair? args) (car args) name)))
+ (if (eq? *describe-format* 'tag)
+ (format #t "@class{~a}{~a}" name desc)
+ (format #t "~a" desc))))
+
+(define (display-list title list)
+ (if title (begin (display title) (display ":\n\n")))
+ (if (null? list)
+ (display "(not defined)\n")
+ (for-each display-summary list)))
+
+(define (display-slot-list title instance list)
+ (if title (begin (display title) (display ":\n\n")))
+ (if (null? list)
+ (display "(not defined)\n")
+ (for-each (lambda (slot)
+ (let ((name (slot-definition-name slot)))
+ (display "Slot: ")
+ (display name)
+ (if (and instance (slot-bound? instance name))
+ (begin
+ (display " = ")
+ (display (slot-ref instance name))))
+ (newline)))
+ list)))
+
+(define (display-file location)
+ (display "Defined in ")
+ (if (eq? *describe-format* 'tag)
+ (format #t "@location{~a}.\n" location)
+ (format #t "`~a'.\n" location)))
+
+(define (format-documentation doc)
+ (with-current-buffer (make-buffer #:text doc)
+ (lambda ()
+ (let ((regexp (make-regexp "@([a-z]*)(\\{([^}]*)\\})?")))
+ (do-while (match (re-search-forward regexp))
+ (let ((key (string->symbol (match:substring match 1)))
+ (value (match:substring match 3)))
+ (case key
+ ((deffnx)
+ (delete-region! (match:start match)
+ (begin (forward-line) (point))))
+ ((var)
+ (replace-match! match 0 (string-upcase value)))
+ ((code)
+ (replace-match! match 0 (string-append "`" value "'")))))))
+ (display (string (current-buffer)))
+ (newline))))
+
+
+;;;
+;;; Top
+;;;
+
+(define description-table
+ (list
+ (cons <boolean> "a boolean")
+ (cons <null> "an empty list")
+ (cons <integer> "an integer")
+ (cons <real> "a real number")
+ (cons <complex> "a complex number")
+ (cons <char> "a character")
+ (cons <symbol> "a symbol")
+ (cons <keyword> "a keyword")
+ (cons <promise> "a promise")
+ (cons <hook> "a hook")
+ (cons <fluid> "a fluid")
+ (cons <stack> "a stack")
+ (cons <variable> "a variable")
+ (cons <regexp> "a regexp object")
+ (cons <module> "a module object")
+ (cons <unknown> "an unknown object")))
+
+(define-generic describe-object)
+(export describe-object)
+
+(define-method (describe-object (obj <top>))
+ (display-type obj)
+ (display-location obj)
+ (newline)
+ (display-value obj)
+ (newline)
+ (display-documentation obj))
+
+(define-generic display-object)
+(define-generic display-summary)
+(define-generic display-type)
+(define-generic display-value)
+(define-generic display-location)
+(define-generic display-description)
+(define-generic display-documentation)
+(export display-object display-summary display-type display-value
+ display-location display-description display-documentation)
+
+(define-method (display-object (obj <top>))
+ (write obj))
+
+(define-method (display-summary (obj <top>))
+ (display "Value: ")
+ (display-object obj)
+ (newline))
+
+(define-method (display-type (obj <top>))
+ (cond
+ ((eof-object? obj) (display "the end-of-file object"))
+ ((unspecified? obj) (display "unspecified"))
+ (else (let ((class (class-of obj)))
+ (display-class class (or (assq-ref description-table class)
+ (safe-class-name class))))))
+ (display ".\n"))
+
+(define-method (display-value (obj <top>))
+ (if (not (unspecified? obj))
+ (begin (display-object obj) (newline))))
+
+(define-method (display-location (obj <top>))
+ *unspecified*)
+
+(define-method (display-description (obj <top>))
+ (let* ((doc (with-output-to-string (lambda () (display-documentation obj))))
+ (index (string-index doc #\newline)))
+ (display (make-shared-substring doc 0 (1+ index)))))
+
+(define-method (display-documentation (obj <top>))
+ (display "Not documented.\n"))
+
+
+;;;
+;;; Pairs
+;;;
+
+(define-method (display-type (obj <pair>))
+ (cond
+ ((list? obj) (display-class <list> "a list"))
+ ((pair? (cdr obj)) (display "an improper list"))
+ (else (display-class <pair> "a pair")))
+ (display ".\n"))
+
+
+;;;
+;;; Strings
+;;;
+
+(define-method (display-type (obj <string>))
+ (if (read-only-string? 'obj)
+ (display "a read-only string")
+ (display-class <string> "a string"))
+ (display ".\n"))
+
+
+;;;
+;;; Procedures
+;;;
+
+(define-method (display-object (obj <procedure>))
+ (cond
+ ((closure? obj)
+ ;; Construct output from the source.
+ (display "(")
+ (display (procedure-name obj))
+ (let ((args (cadr (procedure-source obj))))
+ (cond ((null? args) (display ")"))
+ ((pair? args)
+ (let ((str (with-output-to-string (lambda () (display args)))))
+ (format #t " ~a" (string-upcase! (substring str 1)))))
+ (else
+ (format #t " . ~a)" (string-upcase! (symbol->string args)))))))
+ (else
+ ;; Primitive procedure. Let's lookup the dictionary.
+ (and-let* ((entry (lookup-procedure obj)))
+ (let ((name (entry-property entry 'name))
+ (print-arg (lambda (arg)
+ (display " ")
+ (display (string-upcase (symbol->string arg))))))
+ (display "(")
+ (display name)
+ (and-let* ((args (entry-property entry 'args)))
+ (for-each print-arg args))
+ (and-let* ((opts (entry-property entry 'opts)))
+ (display " &optional")
+ (for-each print-arg opts))
+ (and-let* ((rest (entry-property entry 'rest)))
+ (display " &rest")
+ (print-arg rest))
+ (display ")"))))))
+
+(define-method (display-summary (obj <procedure>))
+ (display "Procedure: ")
+ (display-object obj)
+ (newline)
+ (display " ")
+ (display-description obj))
+
+(define-method (display-type (obj <procedure>))
+ (cond
+ ((and (thunk? obj) (not (procedure-name obj))) (display "a thunk"))
+ ((closure? obj) (display-class <procedure> "a procedure"))
+ ((procedure-with-setter? obj)
+ (display-class <procedure-with-setter> "a procedure with setter"))
+ ((not (struct? obj)) (display "a primitive procedure"))
+ (else (display-class <procedure> "a procedure")))
+ (display ".\n"))
+
+(define-method (display-location (obj <procedure>))
+ (and-let* ((entry (lookup-procedure obj)))
+ (display-file (entry-file entry))))
+
+(define-method (display-documentation (obj <procedure>))
+ (cond ((cond ((closure? obj) (procedure-documentation obj))
+ ((lookup-procedure obj) => entry-text)
+ (else #f))
+ => format-documentation)
+ (else (next-method))))
+
+
+;;;
+;;; Classes
+;;;
+
+(define-method (describe-object (obj <class>))
+ (display-type obj)
+ (display-location obj)
+ (newline)
+ (display-documentation obj)
+ (newline)
+ (display-value obj))
+
+(define-method (display-summary (obj <class>))
+ (display "Class: ")
+ (display-class obj)
+ (newline)
+ (display " ")
+ (display-description obj))
+
+(define-method (display-type (obj <class>))
+ (display-class <class> "a class")
+ (if (not (eq? (class-of obj) <class>))
+ (begin (display " of ") (display-class (class-of obj))))
+ (display ".\n"))
+
+(define-method (display-value (obj <class>))
+ (display-list "Class precedence list" (class-precedence-list obj))
+ (newline)
+ (display-list "Direct superclasses" (class-direct-supers obj))
+ (newline)
+ (display-list "Direct subclasses" (class-direct-subclasses obj))
+ (newline)
+ (display-slot-list "Direct slots" #f (class-direct-slots obj))
+ (newline)
+ (display-list "Direct methods" (class-direct-methods obj)))
+
+
+;;;
+;;; Instances
+;;;
+
+(define-method (display-type (obj <object>))
+ (display-class <object> "an instance")
+ (display " of class ")
+ (display-class (class-of obj))
+ (display ".\n"))
+
+(define-method (display-value (obj <object>))
+ (display-slot-list #f obj (class-slots (class-of obj))))
+
+
+;;;
+;;; Generic functions
+;;;
+
+(define-method (display-type (obj <generic>))
+ (display-class <generic> "a generic function")
+ (display " of class ")
+ (display-class (class-of obj))
+ (display ".\n"))
+
+(define-method (display-value (obj <generic>))
+ (display-list #f (generic-function-methods obj)))
+
+
+;;;
+;;; Methods
+;;;
+
+(define-method (display-object (obj <method>))
+ (display "(")
+ (let ((gf (method-generic-function obj)))
+ (display (if gf (generic-function-name gf) "#<anonymous>")))
+ (let loop ((args (method-specializers obj)))
+ (cond
+ ((null? args))
+ ((pair? args)
+ (display " ")
+ (display-class (car args))
+ (loop (cdr args)))
+ (else (display " . ") (display-class args))))
+ (display ")"))
+
+(define-method (display-summary (obj <method>))
+ (display "Method: ")
+ (display-object obj)
+ (newline)
+ (display " ")
+ (display-description obj))
+
+(define-method (display-type (obj <method>))
+ (display-class <method> "a method")
+ (display " of class ")
+ (display-class (class-of obj))
+ (display ".\n"))
+
+(define-method (display-documentation (obj <method>))
+ (let ((doc (procedure-documentation (method-procedure obj))))
+ (if doc (format-documentation doc) (next-method))))
diff --git a/module/system/repl/repl.scm b/module/system/repl/repl.scm
new file mode 100644
index 000000000..2f4a3783a
--- /dev/null
+++ b/module/system/repl/repl.scm
@@ -0,0 +1,150 @@
+;;; Read-Eval-Print Loop
+
+;; Copyright (C) 2001, 2009 Free Software Foundation, Inc.
+
+;; This library is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU Lesser General Public
+;; License as published by the Free Software Foundation; either
+;; version 3 of the License, or (at your option) any later version.
+;;
+;; This library 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
+;; Lesser General Public License for more details.
+;;
+;; You should have received a copy of the GNU Lesser General Public
+;; License along with this library; if not, write to the Free Software
+;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+;; 02110-1301 USA
+
+;;; Code:
+
+(define-module (system repl repl)
+ #:use-module (system base syntax)
+ #:use-module (system base pmatch)
+ #:use-module (system base compile)
+ #:use-module (system base language)
+ #:use-module (system repl common)
+ #:use-module (system repl command)
+ #:use-module (system vm vm)
+ #:use-module (system vm debug)
+ #:export (start-repl call-with-backtrace))
+
+(define meta-command-token (cons 'meta 'command))
+
+(define (meta-reader read)
+ (lambda read-args
+ (with-input-from-port
+ (if (pair? read-args) (car read-args) (current-input-port))
+ (lambda ()
+ (let ((ch (next-char #t)))
+ (cond ((eof-object? ch)
+ ;; apparently sometimes even if this is eof, read will
+ ;; wait on somethingorother. strange.
+ ch)
+ ((eqv? ch #\,)
+ (read-char)
+ meta-command-token)
+ (else (read))))))))
+
+;; repl-reader is a function defined in boot-9.scm, and is replaced by
+;; something else if readline has been activated. much of this hoopla is
+;; to be able to re-use the existing readline machinery.
+(define (prompting-meta-read repl)
+ (let ((prompt (lambda () (repl-prompt repl)))
+ (lread (language-reader (repl-language repl))))
+ (with-fluid* current-reader (meta-reader lread)
+ (lambda () (repl-reader (lambda () (repl-prompt repl)))))))
+
+(define (default-catch-handler . args)
+ (pmatch args
+ ((quit . _)
+ (apply throw args))
+ ((,key ,subr ,msg ,args . ,rest)
+ (let ((cep (current-error-port)))
+ (cond ((not (stack? (fluid-ref the-last-stack))))
+ ((memq 'backtrace (debug-options-interface))
+ (let ((highlights (if (or (eq? key 'wrong-type-arg)
+ (eq? key 'out-of-range))
+ (car rest)
+ '())))
+ (run-hook before-backtrace-hook)
+ (newline cep)
+ (display "Backtrace:\n")
+ (display-backtrace (fluid-ref the-last-stack) cep
+ #f #f highlights)
+ (newline cep)
+ (run-hook after-backtrace-hook))))
+ (run-hook before-error-hook)
+ (display-error (fluid-ref the-last-stack) cep subr msg args rest)
+ (run-hook after-error-hook)
+ (set! stack-saved? #f)
+ (force-output cep)))
+ (else
+ (format (current-error-port) "\nERROR: uncaught throw to `~a', args: ~a\n"
+ (car args) (cdr args)))))
+
+(define (call-with-backtrace thunk)
+ (catch #t
+ (lambda () (%start-stack #t thunk))
+ default-catch-handler
+ default-pre-unwind-handler))
+
+(define-macro (with-backtrace form)
+ `(call-with-backtrace (lambda () ,form)))
+
+(define (start-repl lang)
+ (let ((repl (make-repl lang))
+ (status #f))
+ (repl-welcome repl)
+ (let prompt-loop ()
+ (let ((exp (with-backtrace (prompting-meta-read repl))))
+ (cond
+ ((eqv? exp (if #f #f))) ; read error, pass
+ ((eq? exp meta-command-token)
+ (with-backtrace (meta-command repl)))
+ ((eof-object? exp)
+ (newline)
+ (set! status '()))
+ (else
+ ;; since the input port is line-buffered, consume up to the
+ ;; newline
+ (flush-to-newline)
+ (with-backtrace
+ (catch 'quit
+ (lambda ()
+ (call-with-values
+ (lambda ()
+ (run-hook before-eval-hook exp)
+ (start-stack #t
+ (repl-eval repl (repl-parse repl exp))))
+ (lambda l
+ (for-each (lambda (v)
+ (run-hook before-print-hook v)
+ (repl-print repl v))
+ l))))
+ (lambda (k . args)
+ (set! status args))))))
+ (or status
+ (begin
+ (next-char #f) ;; consume trailing whitespace
+ (prompt-loop)))))))
+
+(define (next-char wait)
+ (if (or wait (char-ready?))
+ (let ((ch (peek-char)))
+ (cond ((eof-object? ch) ch)
+ ((char-whitespace? ch) (read-char) (next-char wait))
+ (else ch)))
+ #f))
+
+(define (flush-to-newline)
+ (if (char-ready?)
+ (let ((ch (peek-char)))
+ (if (and (not (eof-object? ch)) (char-whitespace? ch))
+ (begin
+ (read-char)
+ (if (not (char=? ch #\newline))
+ (flush-to-newline)))))))
+
+ \ No newline at end of file
diff --git a/module/system/vm/debug.scm b/module/system/vm/debug.scm
new file mode 100644
index 000000000..740111257
--- /dev/null
+++ b/module/system/vm/debug.scm
@@ -0,0 +1,62 @@
+;;; Guile VM debugging facilities
+
+;; Copyright (C) 2001 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 program; see the file COPYING. If not, write to
+;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Code:
+
+(define-module (system vm debug)
+ #:use-module (system base syntax)
+ #:use-module (system vm vm)
+ #:use-module (system vm frame)
+ #:use-module (ice-9 format)
+ #:export (vm-debugger vm-backtrace))
+
+
+;;;
+;;; Debugger
+;;;
+
+(define-record/keywords <debugger> vm chain index)
+
+(define (vm-debugger vm)
+ (let ((chain (vm-last-frame-chain vm)))
+ (if (null? chain)
+ (display "Nothing to debug\n")
+ (debugger-repl (make-debugger
+ #:vm vm #:chain chain #:index (length chain))))))
+
+(define (debugger-repl db)
+ (let loop ()
+ (display "debug> ")
+ (let ((cmd (read)))
+ (case cmd
+ ((bt) (vm-backtrace (debugger-vm db)))
+ ((stack)
+ (write (vm-fetch-stack (debugger-vm db)))
+ (newline))
+ (else
+ (format #t "Unknown command: ~A" cmd))))))
+
+
+;;;
+;;; Backtrace
+;;;
+
+(define (vm-backtrace vm)
+ (print-frame-chain-as-backtrace
+ (reverse (vm-last-frame-chain vm))))
diff --git a/module/system/vm/frame.scm b/module/system/vm/frame.scm
new file mode 100644
index 000000000..332cd6172
--- /dev/null
+++ b/module/system/vm/frame.scm
@@ -0,0 +1,209 @@
+;;; Guile VM frame functions
+
+;;; Copyright (C) 2001, 2009 Free Software Foundation, Inc.
+;;; Copyright (C) 2005 Ludovic Courtès <ludovic.courtes@laas.fr>
+;;;
+;;; 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 of the License, 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 program; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+;;; Code:
+
+(define-module (system vm frame)
+ #:use-module (system vm program)
+ #:use-module (system vm instruction)
+ #:use-module ((srfi srfi-1) #:select (fold))
+ #:export (vm-frame?
+ vm-frame-program
+ vm-frame-local-ref vm-frame-local-set!
+ vm-frame-return-address vm-frame-mv-return-address
+ vm-frame-dynamic-link
+ vm-frame-stack
+
+
+ vm-frame-number vm-frame-address
+ make-frame-chain
+ print-frame print-frame-chain-as-backtrace
+ frame-arguments frame-local-variables
+ frame-environment
+ frame-variable-exists? frame-variable-ref frame-variable-set!
+ frame-object-name
+ frame-local-ref frame-local-set!
+ frame-return-address frame-program
+ frame-dynamic-link heap-frame?))
+
+(load-extension "libguile" "scm_init_frames")
+
+;;;
+;;; Frame chain
+;;;
+
+(define vm-frame-number (make-object-property))
+(define vm-frame-address (make-object-property))
+
+;; FIXME: the header.
+(define (bootstrap-frame? frame)
+ (let ((code (objcode->bytecode (program-objcode (frame-program frame)))))
+ (and (= (uniform-vector-ref code (1- (uniform-vector-length code)))
+ (instruction->opcode 'halt)))))
+
+(define (make-frame-chain frame addr)
+ (define (make-rest)
+ (make-frame-chain (frame-dynamic-link frame)
+ (frame-return-address frame)))
+ (cond
+ ((or (eq? frame #t) (eq? frame #f))
+ ;; handle #f or #t dynamic links
+ '())
+ ((bootstrap-frame? frame)
+ (make-rest))
+ (else
+ (let ((chain (make-rest)))
+ (set! (frame-number frame) (length chain))
+ (set! (frame-address frame)
+ (- addr (program-base (frame-program frame))))
+ (cons frame chain)))))
+
+
+;;;
+;;; Pretty printing
+;;;
+
+(define (frame-line-number frame)
+ (let ((addr (frame-address frame)))
+ (cond ((assv addr (program-sources (frame-program frame)))
+ => source:line)
+ (else (format #f "@~a" addr)))))
+
+(define (frame-file frame prev)
+ (let ((sources (program-sources (frame-program frame))))
+ (if (null? sources)
+ prev
+ (or (source:file (car sources))
+ "current input"))))
+
+(define (print-frame frame)
+ (format #t "~4@a: ~a ~s\n" (frame-line-number frame) (frame-number frame)
+ (frame-call-representation frame)))
+
+
+(define (frame-call-representation frame)
+ (define (abbrev x)
+ (cond ((list? x)
+ (if (> (length x) 4)
+ (list (abbrev (car x)) (abbrev (cadr x)) '...)
+ (map abbrev x)))
+ ((pair? x)
+ (cons (abbrev (car x)) (abbrev (cdr x))))
+ ((vector? x)
+ (case (vector-length x)
+ ((0) x)
+ ((1) (vector (abbrev (vector-ref x 0))))
+ (else (vector (abbrev (vector-ref x 0)) '...))))
+ (else x)))
+ (abbrev (cons (frame-program-name frame) (frame-arguments frame))))
+
+(define (print-frame-chain-as-backtrace frames)
+ (if (null? frames)
+ (format #t "No backtrace available.\n")
+ (begin
+ (format #t "VM backtrace:\n")
+ (fold (lambda (frame file)
+ (let ((new-file (frame-file frame file)))
+ (if (not (equal? new-file file))
+ (format #t "In ~a:\n" new-file))
+ (print-frame frame)
+ new-file))
+ 'no-file
+ frames))))
+
+(define (frame-program-name frame)
+ (let ((prog (frame-program frame))
+ (link (frame-dynamic-link frame)))
+ (or (program-name prog)
+ (object-property prog 'name)
+ (and (heap-frame? link) (frame-address link)
+ (frame-object-name link (1- (frame-address link)) prog))
+ (hash-fold (lambda (s v d) (if (and (variable-bound? v)
+ (eq? prog (variable-ref v)))
+ s d))
+ prog (module-obarray (current-module))))))
+
+
+;;;
+;;; Frames
+;;;
+
+(define (frame-arguments frame)
+ (let* ((prog (frame-program frame))
+ (arity (program-arity prog)))
+ (do ((n (+ (arity:nargs arity) -1) (1- n))
+ (l '() (cons (frame-local-ref frame n) l)))
+ ((< n 0) l))))
+
+(define (frame-local-variables frame)
+ (let* ((prog (frame-program frame))
+ (arity (program-arity prog)))
+ (do ((n (+ (arity:nargs arity) (arity:nlocs arity) -1) (1- n))
+ (l '() (cons (frame-local-ref frame n) l)))
+ ((< n 0) l))))
+
+(define (frame-binding-ref frame binding)
+ (let ((x (frame-local-ref frame (binding:index binding))))
+ (if (and (binding:boxed? binding) (variable? x))
+ (variable-ref x)
+ x)))
+
+(define (frame-binding-set! frame binding val)
+ (if (binding:boxed? binding)
+ (let ((v (frame-local-ref frame binding)))
+ (if (variable? v)
+ (variable-set! v val)
+ (frame-local-set! frame binding (make-variable val))))
+ (frame-local-set! frame binding val)))
+
+;; FIXME handle #f program-bindings return
+(define (frame-bindings frame addr)
+ (filter (lambda (b) (and (>= addr (binding:start b))
+ (<= addr (binding:end b))))
+ (program-bindings (frame-program frame))))
+
+(define (frame-lookup-binding frame addr sym)
+ (assq sym (reverse (frame-bindings frame addr))))
+
+(define (frame-object-binding frame addr obj)
+ (do ((bs (frame-bindings frame addr) (cdr bs)))
+ ((or (null? bs) (eq? obj (frame-binding-ref frame (car bs))))
+ (and (pair? bs) (car bs)))))
+
+(define (frame-environment frame addr)
+ (map (lambda (binding)
+ (cons (binding:name binding) (frame-binding-ref frame binding)))
+ (frame-bindings frame addr)))
+
+(define (frame-variable-exists? frame addr sym)
+ (if (frame-lookup-binding frame addr sym) #t #f))
+
+(define (frame-variable-ref frame addr sym)
+ (cond ((frame-lookup-binding frame addr sym) =>
+ (lambda (binding) (frame-binding-ref frame binding)))
+ (else (error "Unknown variable:" sym))))
+
+(define (frame-variable-set! frame addr sym val)
+ (cond ((frame-lookup-binding frame addr sym) =>
+ (lambda (binding) (frame-binding-set! frame binding val)))
+ (else (error "Unknown variable:" sym))))
+
+(define (frame-object-name frame addr obj)
+ (cond ((frame-object-binding frame addr obj) => binding:name)
+ (else #f)))
diff --git a/module/system/vm/instruction.scm b/module/system/vm/instruction.scm
new file mode 100644
index 000000000..403e9cdc7
--- /dev/null
+++ b/module/system/vm/instruction.scm
@@ -0,0 +1,27 @@
+;;; Guile VM instructions
+
+;; Copyright (C) 2001 Free Software Foundation, Inc.
+
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public
+;;; License as published by the Free Software Foundation; either
+;;; version 3 of the License, or (at your option) any later version.
+;;;
+;;; This library 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
+;;; Lesser General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+;;; Code:
+
+(define-module (system vm instruction)
+ #:export (instruction-list
+ instruction? instruction-length
+ instruction-pops instruction-pushes
+ instruction->opcode opcode->instruction))
+
+(load-extension "libguile" "scm_init_instructions")
diff --git a/module/system/vm/objcode.scm b/module/system/vm/objcode.scm
new file mode 100644
index 000000000..7c0490da6
--- /dev/null
+++ b/module/system/vm/objcode.scm
@@ -0,0 +1,27 @@
+;;; Guile VM object code
+
+;; Copyright (C) 2001 Free Software Foundation, Inc.
+
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public
+;;; License as published by the Free Software Foundation; either
+;;; version 3 of the License, or (at your option) any later version.
+;;;
+;;; This library 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
+;;; Lesser General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+;;; Code:
+
+(define-module (system vm objcode)
+ #:export (objcode? objcode-meta
+ bytecode->objcode objcode->bytecode
+ load-objcode write-objcode
+ word-size byte-order))
+
+(load-extension "libguile" "scm_init_objcodes")
diff --git a/module/system/vm/profile.scm b/module/system/vm/profile.scm
new file mode 100644
index 000000000..6ab418ac3
--- /dev/null
+++ b/module/system/vm/profile.scm
@@ -0,0 +1,64 @@
+;;; Guile VM profiler
+
+;; Copyright (C) 2001 Free Software Foundation, Inc.
+
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public
+;;; License as published by the Free Software Foundation; either
+;;; version 3 of the License, or (at your option) any later version.
+;;;
+;;; This library 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
+;;; Lesser General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+;;; Code:
+
+(define-module (system vm profile)
+ #:use-module (system vm vm)
+ #:use-module (ice-9 format)
+ #:export (vm-profile))
+
+(define (vm-profile vm objcode . opts)
+ (let ((flag (vm-option vm 'debug)))
+ (dynamic-wind
+ (lambda ()
+ (set-vm-option! vm 'debug #t)
+ (set-vm-option! vm 'profile-data '())
+ (add-hook! (vm-next-hook vm) profile-next)
+ (add-hook! (vm-enter-hook vm) profile-enter)
+ (add-hook! (vm-exit-hook vm) profile-exit))
+ (lambda ()
+ (vm-load vm objcode)
+ (print-result vm))
+ (lambda ()
+ (set-vm-option! vm 'debug flag)
+ (remove-hook! (vm-next-hook vm) profile-next)
+ (remove-hook! (vm-enter-hook vm) profile-enter)
+ (remove-hook! (vm-exit-hook vm) profile-exit)))))
+
+(define (profile-next vm)
+ (set-vm-option! vm 'profile-data
+ (cons (vm-fetch-code vm) (vm-option vm 'profile-data))))
+
+(define (profile-enter vm)
+ #f)
+
+(define (profile-exit vm)
+ #f)
+
+(define (print-result vm . opts)
+ (do ((data (vm-option vm 'profile-data) (cdr data))
+ (summary '() (let ((inst (caar data)))
+ (assq-set! summary inst
+ (1+ (or (assq-ref summary inst) 0))))))
+ ((null? data)
+ (display "Count Instruction\n")
+ (display "----- -----------\n")
+ (for-each (lambda (entry)
+ (format #t "~5@A ~A\n" (cdr entry) (car entry)))
+ (sort summary (lambda (e1 e2) (> (cdr e1) (cdr e2))))))))
diff --git a/module/system/vm/program.scm b/module/system/vm/program.scm
new file mode 100644
index 000000000..755c606e2
--- /dev/null
+++ b/module/system/vm/program.scm
@@ -0,0 +1,100 @@
+;;; Guile VM program functions
+
+;;; Copyright (C) 2001, 2009 Free Software Foundation, Inc.
+;;;
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public
+;;; License as published by the Free Software Foundation; either
+;;; version 3 of the License, or (at your option) any later version.
+;;;
+;;; This library 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
+;;; Lesser General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+;;; Code:
+
+(define-module (system vm program)
+ #:export (make-program
+
+ arity:nargs arity:nrest arity:nlocs
+
+ make-binding binding:name binding:boxed? binding:index
+ binding:start binding:end
+
+ source:addr source:line source:column source:file
+ program-bindings program-sources program-source
+ program-properties program-property program-documentation
+ program-name program-arguments
+
+ program-arity program-meta
+ program-objcode program? program-objects
+ program-module program-base program-free-variables))
+
+(load-extension "libguile" "scm_init_programs")
+
+(define arity:nargs car)
+(define arity:nrest cadr)
+(define arity:nlocs caddr)
+
+(define (make-binding name boxed? index start end)
+ (list name boxed? index start end))
+(define (binding:name b) (list-ref b 0))
+(define (binding:boxed? b) (list-ref b 1))
+(define (binding:index b) (list-ref b 2))
+(define (binding:start b) (list-ref b 3))
+(define (binding:end b) (list-ref b 4))
+
+(define (source:addr source)
+ (car source))
+(define (source:file source)
+ (cadr source))
+(define (source:line source)
+ (caddr source))
+(define (source:column source)
+ (cdddr source))
+
+(define (program-property prog prop)
+ (assq-ref (program-properties proc) prop))
+
+(define (program-documentation prog)
+ (assq-ref (program-properties prog) 'documentation))
+
+(define (program-arguments prog)
+ (let ((bindings (program-bindings prog))
+ (nargs (arity:nargs (program-arity prog)))
+ (rest? (not (zero? (arity:nrest (program-arity prog))))))
+ (if bindings
+ (let ((args (map binding:name (list-head bindings nargs))))
+ (if rest?
+ `((required . ,(list-head args (1- (length args))))
+ (rest . ,(car (last-pair args))))
+ `((required . ,args))))
+ #f)))
+
+(define (program-bindings-as-lambda-list prog)
+ (let ((bindings (program-bindings prog))
+ (nargs (arity:nargs (program-arity prog)))
+ (rest? (not (zero? (arity:nrest (program-arity prog))))))
+ (if (not bindings)
+ (if rest? (cons (1- nargs) 1) (list nargs))
+ (let ((args (map binding:name (list-head bindings nargs))))
+ (if rest?
+ (apply cons* args)
+ args)))))
+
+(define (write-program prog port)
+ (format port "#<program ~a ~a>"
+ (or (program-name prog)
+ (and=> (program-source prog 0)
+ (lambda (s)
+ (format #f "~a at ~a:~a:~a"
+ (number->string (object-address prog) 16)
+ (or (source:file s) "<unknown port>")
+ (source:line s) (source:column s))))
+ (number->string (object-address prog) 16))
+ (program-bindings-as-lambda-list prog)))
diff --git a/module/system/vm/trace.scm b/module/system/vm/trace.scm
new file mode 100644
index 000000000..d8165f202
--- /dev/null
+++ b/module/system/vm/trace.scm
@@ -0,0 +1,76 @@
+;;; Guile VM tracer
+
+;; Copyright (C) 2001, 2009 Free Software Foundation, Inc.
+
+;;; This library is free software; you can redistribute it and/or
+;;; modify it under the terms of the GNU Lesser General Public
+;;; License as published by the Free Software Foundation; either
+;;; version 3 of the License, or (at your option) any later version.
+;;;
+;;; This library 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
+;;; Lesser General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Lesser General Public
+;;; License along with this library; if not, write to the Free Software
+;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+;;; Code:
+
+(define-module (system vm trace)
+ #:use-module (system base syntax)
+ #:use-module (system vm vm)
+ #:use-module (system vm frame)
+ #:use-module (ice-9 format)
+ #:export (vm-trace vm-trace-on vm-trace-off))
+
+(define (vm-trace vm objcode . opts)
+ (dynamic-wind
+ (lambda () (apply vm-trace-on vm opts))
+ (lambda () (vm-load vm objcode))
+ (lambda () (apply vm-trace-off vm opts))))
+
+(define (vm-trace-on vm . opts)
+ (set-vm-option! vm 'trace-first #t)
+ (if (memq #:b opts) (add-hook! (vm-next-hook vm) trace-next))
+ (set-vm-option! vm 'trace-options opts)
+ (add-hook! (vm-apply-hook vm) trace-apply)
+ (add-hook! (vm-return-hook vm) trace-return))
+
+(define (vm-trace-off vm . opts)
+ (if (memq #:b opts) (remove-hook! (vm-next-hook vm) trace-next))
+ (remove-hook! (vm-apply-hook vm) trace-apply)
+ (remove-hook! (vm-return-hook vm) trace-return))
+
+(define (trace-next vm)
+ (define (puts x) (display #\tab) (write x))
+ (define (truncate! x n)
+ (if (> (length x) n)
+ (list-cdr-set! x (1- n) '(...))) x)
+ ;; main
+ (format #t "0x~8X ~16S" (vm:ip vm) (vm-fetch-code vm))
+ (do ((opts (vm-option vm 'trace-options) (cdr opts)))
+ ((null? opts) (newline))
+ (case (car opts)
+ ((:s) (puts (truncate! (vm-fetch-stack vm) 3)))
+ ((:l) (puts (vm-fetch-locals vm))))))
+
+(define (trace-apply vm)
+ (if (vm-option vm 'trace-first)
+ (set-vm-option! vm 'trace-first #f)
+ (let ((chain (vm-current-frame-chain vm)))
+ (print-indent chain)
+ (print-frame-call (car chain))
+ (newline))))
+
+(define (trace-return vm)
+ (let ((chain (vm-current-frame-chain vm)))
+ (print-indent chain)
+ (write (vm-return-value vm))
+ (newline)))
+
+(define (print-indent chain)
+ (cond ((pair? (cdr chain))
+ (display "| ")
+ (print-indent (cdr chain)))))
diff --git a/module/system/vm/vm.scm b/module/system/vm/vm.scm
new file mode 100644
index 000000000..48dc4f2b8
--- /dev/null
+++ b/module/system/vm/vm.scm
@@ -0,0 +1,41 @@
+;;; Guile VM core
+
+;; Copyright (C) 2001 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 program; see the file COPYING. If not, write to
+;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Code:
+
+(define-module (system vm vm)
+ #:use-module (system vm frame)
+ #:use-module (system vm program)
+ #:export (vm? the-vm make-vm vm-version
+ vm:ip vm:sp vm:fp vm:last-ip
+
+ vm-load vm-option set-vm-option! vm-version vm-stats
+ vms:time vms:clock
+
+ vm-trace-frame
+ vm-next-hook vm-apply-hook vm-boot-hook vm-return-hook
+ vm-break-hook vm-exit-hook vm-halt-hook vm-enter-hook))
+
+(load-extension "libguile" "scm_init_vm")
+
+(define (vms:time stat) (vector-ref stat 0))
+(define (vms:clock stat) (vector-ref stat 1))
+
+(define (vm-load vm objcode)
+ (vm (make-program objcode)))
diff --git a/module/system/xref.scm b/module/system/xref.scm
new file mode 100644
index 000000000..906ec8e4a
--- /dev/null
+++ b/module/system/xref.scm
@@ -0,0 +1,182 @@
+;;;; Copyright (C) 2009 Free Software Foundation, Inc.
+;;;;
+;;;; This library is free software; you can redistribute it and/or
+;;;; modify it under the terms of the GNU Lesser General Public
+;;;; License as published by the Free Software Foundation; either
+;;;; version 2.1 of the License, or (at your option) any later version.
+;;;;
+;;;; This library 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
+;;;; Lesser General Public License for more details.
+;;;;
+;;;; You should have received a copy of the GNU Lesser General Public
+;;;; License along with this library; if not, write to the Free Software
+;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+;;;;
+
+
+(define-module (system xref)
+ #:use-module (system base pmatch)
+ #:use-module (system base compile)
+ #:use-module (system vm program)
+ #:use-module (srfi srfi-1)
+ #:export (*xref-ignored-modules*
+ procedure-callees
+ procedure-callers))
+
+(define (program-callee-rev-vars prog)
+ (define (cons-uniq x y)
+ (if (memq x y) y (cons x y)))
+ (cond
+ ((program-objects prog)
+ => (lambda (objects)
+ (let ((n (vector-length objects))
+ (progv (make-vector (vector-length objects) #f))
+ (asm (decompile (program-objcode prog) #:to 'assembly)))
+ (pmatch asm
+ ((load-program ,nargs ,nrest ,nlocs ,labels ,len . ,body)
+ (for-each
+ (lambda (x)
+ (pmatch x
+ ((toplevel-ref ,n) (vector-set! progv n #t))
+ ((toplevel-set ,n) (vector-set! progv n #t))))
+ body)))
+ (let lp ((i 0) (out '()))
+ (cond
+ ((= i n) out)
+ ((program? (vector-ref objects i))
+ (lp (1+ i)
+ (fold cons-uniq out
+ (program-callee-rev-vars (vector-ref objects i)))))
+ ((vector-ref progv i)
+ (let ((obj (vector-ref objects i)))
+ (if (variable? obj)
+ (lp (1+ i) (cons-uniq obj out))
+ ;; otherwise it's an unmemoized binding
+ (pmatch obj
+ (,sym (guard (symbol? sym))
+ (let ((v (module-variable (or (program-module prog)
+ the-root-module)
+ sym)))
+ (lp (1+ i) (if v (cons-uniq v out) out))))
+ ((,mod ,sym ,public?)
+ ;; hm, hacky.
+ (let* ((m (nested-ref the-root-module
+ (append '(%app modules) mod)))
+ (v (and m
+ (module-variable
+ (if public?
+ (module-public-interface m)
+ m)
+ sym))))
+ (lp (1+ i)
+ (if v (cons-uniq v out) out))))))))
+ (else (lp (1+ i) out)))))))
+ (else '())))
+
+(define (procedure-callee-rev-vars proc)
+ (cond
+ ((program? proc) (program-callee-rev-vars proc))
+ (else '())))
+
+(define (procedure-callees prog)
+ "Evaluates to a list of the given program callees."
+ (let lp ((in (procedure-callee-rev-vars prog)) (out '()))
+ (cond ((null? in) out)
+ ((variable-bound? (car in))
+ (lp (cdr in) (cons (variable-ref (car in)) out)))
+ (else (lp (cdr in) out)))))
+
+;; var -> ((module-name caller ...) ...)
+(define *callers-db* #f)
+;; module-name -> (callee ...)
+(define *module-callees-db* (make-hash-table))
+;; (module-name ...)
+(define *tainted-modules* '())
+
+(define *xref-ignored-modules* '((value-history)))
+(define (on-module-modified m)
+ (let ((name (module-name m)))
+ (if (and (not (member name *xref-ignored-modules*))
+ (not (member name *tainted-modules*))
+ (pair? name))
+ (set! *tainted-modules* (cons name *tainted-modules*)))))
+
+(define (add-caller callee caller mod-name)
+ (let ((all-callers (hashq-ref *callers-db* callee)))
+ (if (not all-callers)
+ (hashq-set! *callers-db* callee `((,mod-name ,caller)))
+ (let ((callers (assoc mod-name all-callers)))
+ (if callers
+ (if (not (member caller callers))
+ (set-cdr! callers (cons caller (cdr callers))))
+ (hashq-set! *callers-db* callee
+ (cons `(,mod-name ,caller) all-callers)))))))
+
+(define (forget-callers callee mod-name)
+ (hashq-set! *callers-db* callee
+ (assoc-remove! (hashq-ref *callers-db* callee '()) mod-name)))
+
+(define (add-callees callees mod-name)
+ (hash-set! *module-callees-db* mod-name
+ (append callees (hash-ref *module-callees-db* mod-name '()))))
+
+(define (untaint-modules)
+ (define (untaint m)
+ (for-each (lambda (callee) (forget-callers callee m))
+ (hash-ref *module-callees-db* m '()))
+ (ensure-callers-db m))
+ (ensure-callers-db #f)
+ (for-each untaint *tainted-modules*)
+ (set! *tainted-modules* '()))
+
+(define (ensure-callers-db mod-name)
+ (let ((mod (and mod-name (resolve-module mod-name)))
+ (visited #f))
+ (define (visit-variable var recurse mod-name)
+ (if (variable-bound? var)
+ (let ((x (variable-ref var)))
+ (cond
+ ((and visited (hashq-ref visited x)))
+ ((procedure? x)
+ (if visited (hashq-set! visited x #t))
+ (let ((callees (filter variable-bound?
+ (procedure-callee-rev-vars x))))
+ (for-each (lambda (callee)
+ (add-caller callee x mod-name))
+ callees)
+ (add-callees callees mod-name)))
+ ((and recurse (module? x))
+ (visit-module x #t))))))
+
+ (define (visit-module mod recurse)
+ (if visited (hashq-set! visited mod #t))
+ (if (not (memq on-module-modified (module-observers mod)))
+ (module-observe mod on-module-modified))
+ (let ((name (module-name mod)))
+ (module-for-each (lambda (sym var)
+ (visit-variable var recurse name))
+ mod)))
+
+ (cond ((and (not mod-name) (not *callers-db*))
+ (set! *callers-db* (make-hash-table 1000))
+ (set! visited (make-hash-table 1000))
+ (visit-module the-root-module #t))
+ (mod-name (visit-module mod #f)))))
+
+(define (procedure-callers var)
+ "Returns an association list, keyed by module name, of known callers
+of the given procedure. The latter can specified directly as a
+variable, a symbol (which gets resolved in the current module) or a
+pair of the form (module-name . variable-name), "
+ (let ((v (cond ((variable? var) var)
+ ((symbol? var) (module-variable (current-module) var))
+ (else
+ (pmatch var
+ ((,modname . ,sym)
+ (module-variable (resolve-module modname) sym))
+ (else
+ (error "expected a variable, symbol, or (modname . sym)" var)))))))
+ (untaint-modules)
+ (hashq-ref *callers-db* v '())))