mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-17 17:20:29 +02:00
Merge branch 'master' into boehm-demers-weiser-gc
Conflicts: libguile/continuations.c libguile/gc-freelist.c libguile/gc-mark.c libguile/symbols.c libguile/threads.c module/ice-9/boot-9.scm
This commit is contained in:
commit
d9e59f894e
293 changed files with 49645 additions and 1718 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -37,7 +37,7 @@ autom4te.cache
|
||||||
benchmark-guile
|
benchmark-guile
|
||||||
check-guile
|
check-guile
|
||||||
check-guile.log
|
check-guile.log
|
||||||
compile
|
build-aux/compile
|
||||||
confdefs.h
|
confdefs.h
|
||||||
config.build-subdirs
|
config.build-subdirs
|
||||||
config.cache
|
config.cache
|
||||||
|
@ -68,8 +68,10 @@ guile-procedures.txt
|
||||||
guile-config/guile-config
|
guile-config/guile-config
|
||||||
guile-readline/guile-readline-config.h
|
guile-readline/guile-readline-config.h
|
||||||
guile-readline/guile-readline-config.h.in
|
guile-readline/guile-readline-config.h.in
|
||||||
|
*.go
|
||||||
TAGS
|
TAGS
|
||||||
guile-1.8.pc
|
guile-1.8.pc
|
||||||
|
gdb-pre-inst-guile
|
||||||
libguile/stack-limit-calibration.scm
|
libguile/stack-limit-calibration.scm
|
||||||
cscope.out
|
cscope.out
|
||||||
cscope.files
|
cscope.files
|
||||||
|
|
|
@ -24,8 +24,9 @@
|
||||||
#
|
#
|
||||||
AUTOMAKE_OPTIONS = 1.10
|
AUTOMAKE_OPTIONS = 1.10
|
||||||
|
|
||||||
SUBDIRS = lib oop libguile ice-9 guile-config guile-readline emacs \
|
SUBDIRS = lib libguile guile-config guile-readline emacs \
|
||||||
scripts srfi doc examples test-suite benchmark-suite lang am
|
scripts srfi doc examples test-suite benchmark-suite lang am \
|
||||||
|
module testsuite
|
||||||
|
|
||||||
bin_SCRIPTS = guile-tools
|
bin_SCRIPTS = guile-tools
|
||||||
|
|
||||||
|
|
6
NEWS
6
NEWS
|
@ -43,6 +43,10 @@ indicating length of the `scm_t_option' array.
|
||||||
** Primitive procedures (aka. "subrs") are now stored in double cells
|
** Primitive procedures (aka. "subrs") are now stored in double cells
|
||||||
This removes the subr table and simplifies the code.
|
This removes the subr table and simplifies the code.
|
||||||
|
|
||||||
|
** Primitive procedures with more than 3 arguments (aka. "gsubrs") are
|
||||||
|
no longer implemented using the "compiled closure" mechanism. This
|
||||||
|
simplifies code and reduces both the storage and run-time overhead.
|
||||||
|
|
||||||
|
|
||||||
Changes in 1.8.7 (since 1.8.6)
|
Changes in 1.8.7 (since 1.8.6)
|
||||||
|
|
||||||
|
@ -60,6 +64,8 @@ transformed by (ice-9 syncase) would cause an "Invalid syntax" error.
|
||||||
Now it works as you would expect (giving the value of the specified
|
Now it works as you would expect (giving the value of the specified
|
||||||
module binding).
|
module binding).
|
||||||
|
|
||||||
|
** Have `scm_take_locale_symbol ()' return an interned symbol (bug #25865)
|
||||||
|
|
||||||
|
|
||||||
Changes in 1.8.6 (since 1.8.5)
|
Changes in 1.8.6 (since 1.8.5)
|
||||||
|
|
||||||
|
|
57
NEWS.guile-vm
Normal file
57
NEWS.guile-vm
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
Guile-VM NEWS
|
||||||
|
|
||||||
|
|
||||||
|
Guile-VM is a bytecode compiler and virtual machine for Guile.
|
||||||
|
|
||||||
|
|
||||||
|
guile-vm 0.7 -- 2008-05-20
|
||||||
|
==========================
|
||||||
|
|
||||||
|
* Initial release with NEWS.
|
||||||
|
|
||||||
|
* Revived from Keisuke Nishida's Guile-VM project from 2000-2001, with
|
||||||
|
the help of Ludovic Courtès.
|
||||||
|
|
||||||
|
* Meta-level changes
|
||||||
|
** Updated to compile with Guile 1.8.
|
||||||
|
** Documentation updated, including documentation on the instructions.
|
||||||
|
** Added benchmarking and a test harness.
|
||||||
|
|
||||||
|
* Changes to the inventory
|
||||||
|
** Renamed the library from libguilevm to libguile-vm.
|
||||||
|
** Added new executable script, guile-disasm.
|
||||||
|
|
||||||
|
* New features
|
||||||
|
** Add support for compiling macros, both defmacros and syncase macros.
|
||||||
|
Primitive macros produced with the procedure->macro family of procedures
|
||||||
|
are not supported, however.
|
||||||
|
** Improvements to the REPL
|
||||||
|
Multiple values support, readline integration, ice-9 history integration
|
||||||
|
** Add support for eval-case
|
||||||
|
The compiler recognizes compile-toplevel in addition to load-toplevel
|
||||||
|
** Completely self-compiling
|
||||||
|
Almost, anyway: not (system repl describe), because it uses GOOPS
|
||||||
|
|
||||||
|
* Internal cleanups
|
||||||
|
** Internal objects are now based on Guile records.
|
||||||
|
** Guile-VM's code doesn't use the dot-syntax any more.
|
||||||
|
** Changed (ice-9 match) for Kiselyov's pmatch.scm
|
||||||
|
** New instructions: define, link-later, link-now, late-variable-{ref,set}
|
||||||
|
** Object code now represented as u8vectors instead of strings.
|
||||||
|
** Remove local import of an old version of slib
|
||||||
|
|
||||||
|
* Bugfixes
|
||||||
|
** The `optimize' procedure is coming out of bitrot
|
||||||
|
** The Scheme compiler is now more strict about placement of internal
|
||||||
|
defines
|
||||||
|
** set! is now compiled differently from define
|
||||||
|
** Module-level variables are now bound at first use instead of in the
|
||||||
|
program prolog
|
||||||
|
** Bugfix to load-program (stack misinterpretation)
|
||||||
|
|
||||||
|
|
||||||
|
Copyright (C) 2008 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Copying and distribution of this file, with or without modification, are
|
||||||
|
permitted in any medium without royalty provided the copyright notice
|
||||||
|
and this notice are preserved.
|
117
README.guile-vm
Normal file
117
README.guile-vm
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
This is an attempt to revive the Guile-VM project by Keisuke Nishida
|
||||||
|
written back in the years 2000 and 2001. Below are a few pointers to
|
||||||
|
relevant threads on Guile's development mailing list.
|
||||||
|
|
||||||
|
Enjoy!
|
||||||
|
|
||||||
|
Ludovic Courtès <ludovic.courtes@laas.fr>, Apr. 2005.
|
||||||
|
|
||||||
|
|
||||||
|
Pointers
|
||||||
|
--------
|
||||||
|
|
||||||
|
Status of the last release, 0.5
|
||||||
|
http://lists.gnu.org/archive/html/guile-devel/2001-04/msg00266.html
|
||||||
|
|
||||||
|
The very first release, 0.0
|
||||||
|
http://sources.redhat.com/ml/guile/2000-07/msg00418.html
|
||||||
|
|
||||||
|
Simple benchmark
|
||||||
|
http://sources.redhat.com/ml/guile/2000-07/msg00425.html
|
||||||
|
|
||||||
|
Performance, portability, GNU Lightning
|
||||||
|
http://lists.gnu.org/archive/html/guile-devel/2001-03/msg00132.html
|
||||||
|
|
||||||
|
Playing with GNU Lightning
|
||||||
|
http://lists.gnu.org/archive/html/guile-devel/2001-03/msg00185.html
|
||||||
|
|
||||||
|
On things left to be done
|
||||||
|
http://lists.gnu.org/archive/html/guile-devel/2001-03/msg00146.html
|
||||||
|
|
||||||
|
|
||||||
|
---8<--- Original README below. -----------------------------------------
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
1. Install the latest Guile from CVS.
|
||||||
|
|
||||||
|
2. Install Guile VM:
|
||||||
|
|
||||||
|
% configure
|
||||||
|
% make install
|
||||||
|
% ln -s module/{guile,system,language} /usr/local/share/guile/
|
||||||
|
|
||||||
|
3. Add the following lines to your ~/.guile:
|
||||||
|
|
||||||
|
(use-modules (system vm core)
|
||||||
|
|
||||||
|
(cond ((string=? (car (command-line)) "guile-vm")
|
||||||
|
(use-modules (system repl repl))
|
||||||
|
(start-repl 'scheme)
|
||||||
|
(quit)))
|
||||||
|
|
||||||
|
Example Session
|
||||||
|
---------------
|
||||||
|
|
||||||
|
% guile-vm
|
||||||
|
Guile Scheme interpreter 0.5 on Guile 1.4.1
|
||||||
|
Copyright (C) 2001 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Enter `,help' for help.
|
||||||
|
scheme@guile-user> (+ 1 2)
|
||||||
|
3
|
||||||
|
scheme@guile-user> ,c -c (+ 1 2) ;; Compile into GLIL
|
||||||
|
(@asm (0 1 0 0)
|
||||||
|
(module-ref #f +)
|
||||||
|
(const 1)
|
||||||
|
(const 2)
|
||||||
|
(tail-call 2))
|
||||||
|
scheme@guile-user> ,c (+ 1 2) ;; Compile into object code
|
||||||
|
Disassembly of #<objcode 403c5fb0>:
|
||||||
|
|
||||||
|
nlocs = 0 nexts = 0
|
||||||
|
|
||||||
|
0 link "+" ;; (+ . ???)
|
||||||
|
3 variable-ref
|
||||||
|
4 make-int8:1 ;; 1
|
||||||
|
5 make-int8 2 ;; 2
|
||||||
|
7 tail-call 2
|
||||||
|
|
||||||
|
scheme@guile-user> (define (add x y) (+ x y))
|
||||||
|
scheme@guile-user> (add 1 2)
|
||||||
|
3
|
||||||
|
scheme@guile-user> ,x add ;; Disassemble
|
||||||
|
Disassembly of #<program add>:
|
||||||
|
|
||||||
|
nargs = 2 nrest = 0 nlocs = 0 nexts = 0
|
||||||
|
|
||||||
|
Bytecode:
|
||||||
|
|
||||||
|
0 object-ref 0 ;; (+ . #<primitive-procedure +>)
|
||||||
|
2 variable-ref
|
||||||
|
3 local-ref 0
|
||||||
|
5 local-ref 1
|
||||||
|
7 tail-call 2
|
||||||
|
|
||||||
|
Objects:
|
||||||
|
|
||||||
|
0 (+ . #<primitive-procedure +>)
|
||||||
|
|
||||||
|
scheme@guile-user>
|
||||||
|
|
||||||
|
Compile Modules
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Use `guilec' to compile your modules:
|
||||||
|
|
||||||
|
% cat fib.scm
|
||||||
|
(define-module (fib) :export (fib))
|
||||||
|
(define (fib n) (if (< n 2) 1 (+ (fib (- n 1)) (fib (- n 2)))))
|
||||||
|
|
||||||
|
% guilec fib.scm
|
||||||
|
Wrote fib.go
|
||||||
|
% guile
|
||||||
|
guile> (use-modules (fib))
|
||||||
|
guile> (fib 8)
|
||||||
|
34
|
1
THANKS.guile-vm
Normal file
1
THANKS.guile-vm
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Guile VM was inspired by QScheme, librep, and Objective Caml.
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
AUTOMAKE_OPTIONS = gnu
|
AUTOMAKE_OPTIONS = gnu
|
||||||
|
|
||||||
am_frags = pre-inst-guile maintainer-dirs
|
am_frags = pre-inst-guile maintainer-dirs guilec
|
||||||
|
|
||||||
EXTRA_DIST = $(am_frags) ChangeLog-2008
|
EXTRA_DIST = $(am_frags) ChangeLog-2008
|
||||||
|
|
||||||
|
|
13
am/guilec
Normal file
13
am/guilec
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# -*- makefile -*-
|
||||||
|
GOBJECTS = $(SOURCES:%.scm=%.go)
|
||||||
|
|
||||||
|
moddir = $(pkgdatadir)/$(GUILE_EFFECTIVE_VERSION)/$(modpath)
|
||||||
|
nobase_mod_DATA = $(SOURCES) $(NOCOMP_SOURCES) $(GOBJECTS)
|
||||||
|
EXTRA_DIST = $(SOURCES) $(NOCOMP_SOURCES)
|
||||||
|
|
||||||
|
CLEANFILES = $(GOBJECTS)
|
||||||
|
|
||||||
|
SUFFIXES = .scm .go
|
||||||
|
.scm.go:
|
||||||
|
$(MKDIR_P) `dirname $@`
|
||||||
|
$(top_builddir)/pre-inst-guile-env $(top_builddir)/guile-tools compile -o "$@" "$<"
|
111
benchmark/lib.scm
Normal file
111
benchmark/lib.scm
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
;; -*- Scheme -*-
|
||||||
|
;;
|
||||||
|
;; A library of dumb functions that may be used to benchmark Guile-VM.
|
||||||
|
|
||||||
|
|
||||||
|
;; The comments are from Ludovic, a while ago. The speedups now are much
|
||||||
|
;; more significant (all over 2x, sometimes 8x).
|
||||||
|
|
||||||
|
(define (fibo x)
|
||||||
|
(if (or (= x 1) (= x 2))
|
||||||
|
1
|
||||||
|
(+ (fibo (- x 1))
|
||||||
|
(fibo (- x 2)))))
|
||||||
|
|
||||||
|
(define (g-c-d x y)
|
||||||
|
(if (= x y)
|
||||||
|
x
|
||||||
|
(if (< x y)
|
||||||
|
(g-c-d x (- y x))
|
||||||
|
(g-c-d (- x y) y))))
|
||||||
|
|
||||||
|
(define (loop n)
|
||||||
|
;; This one shows that procedure calls are no faster than within the
|
||||||
|
;; interpreter: the VM yields no performance improvement.
|
||||||
|
(if (= 0 n)
|
||||||
|
0
|
||||||
|
(loop (1- n))))
|
||||||
|
|
||||||
|
;; Disassembly of `loop'
|
||||||
|
;;
|
||||||
|
;; Disassembly of #<objcode b79bdf28>:
|
||||||
|
|
||||||
|
;; nlocs = 0 nexts = 0
|
||||||
|
|
||||||
|
;; 0 (make-int8 64) ;; 64
|
||||||
|
;; 2 (load-symbol "guile-user") ;; guile-user
|
||||||
|
;; 14 (list 0 1) ;; 1 element
|
||||||
|
;; 17 (load-symbol "loop") ;; loop
|
||||||
|
;; 23 (link-later)
|
||||||
|
;; 24 (vector 0 1) ;; 1 element
|
||||||
|
;; 27 (make-int8 0) ;; 0
|
||||||
|
;; 29 (load-symbol "n") ;; n
|
||||||
|
;; 32 (make-false) ;; #f
|
||||||
|
;; 33 (make-int8 0) ;; 0
|
||||||
|
;; 35 (list 0 3) ;; 3 elements
|
||||||
|
;; 38 (list 0 2) ;; 2 elements
|
||||||
|
;; 41 (list 0 1) ;; 1 element
|
||||||
|
;; 44 (make-int8 5) ;; 5
|
||||||
|
;; 46 (make-false) ;; #f
|
||||||
|
;; 47 (cons)
|
||||||
|
;; 48 (make-int8 18) ;; 18
|
||||||
|
;; 50 (make-false) ;; #f
|
||||||
|
;; 51 (cons)
|
||||||
|
;; 52 (make-int8 20) ;; 20
|
||||||
|
;; 54 (make-false) ;; #f
|
||||||
|
;; 55 (cons)
|
||||||
|
;; 56 (list 0 4) ;; 4 elements
|
||||||
|
;; 59 (load-program ##{66}#)
|
||||||
|
;; 81 (define "loop")
|
||||||
|
;; 87 (variable-set)
|
||||||
|
;; 88 (void)
|
||||||
|
;; 89 (return)
|
||||||
|
|
||||||
|
;; Bytecode ##{66}#:
|
||||||
|
|
||||||
|
;; 0 (make-int8 0) ;; 0
|
||||||
|
;; 2 (local-ref 0)
|
||||||
|
;; 4 (ee?)
|
||||||
|
;; 5 (br-if-not 0 3) ;; -> 11
|
||||||
|
;; 8 (make-int8 0) ;; 0
|
||||||
|
;; 10 (return)
|
||||||
|
;; 11 (toplevel-ref 0)
|
||||||
|
;; 13 (local-ref 0)
|
||||||
|
;; 15 (make-int8 1) ;; 1
|
||||||
|
;; 17 (sub)
|
||||||
|
;; 18 (tail-call 1)
|
||||||
|
|
||||||
|
(define (loopi n)
|
||||||
|
;; Same as `loop'.
|
||||||
|
(let loopi ((n n))
|
||||||
|
(if (= 0 n)
|
||||||
|
0
|
||||||
|
(loopi (1- n)))))
|
||||||
|
|
||||||
|
(define (do-loop n)
|
||||||
|
;; Same as `loop' using `do'.
|
||||||
|
(do ((i n (1- i)))
|
||||||
|
((= 0 i))
|
||||||
|
;; do nothing
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
(define (do-cons x)
|
||||||
|
;; This one shows that the built-in `cons' instruction yields a significant
|
||||||
|
;; improvement (speedup: 1.5).
|
||||||
|
(let loop ((x x)
|
||||||
|
(result '()))
|
||||||
|
(if (<= x 0)
|
||||||
|
result
|
||||||
|
(loop (1- x) (cons x result)))))
|
||||||
|
|
||||||
|
(define big-list (iota 500000))
|
||||||
|
|
||||||
|
(define (copy-list lst)
|
||||||
|
;; Speedup: 5.9.
|
||||||
|
(let loop ((lst lst)
|
||||||
|
(result '()))
|
||||||
|
(if (null? lst)
|
||||||
|
result
|
||||||
|
(loop (cdr lst)
|
||||||
|
(cons (car lst) result)))))
|
64
benchmark/measure.scm
Executable file
64
benchmark/measure.scm
Executable file
|
@ -0,0 +1,64 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# aside from this initial boilerplate, this is actually -*- scheme -*- code
|
||||||
|
main='(module-ref (resolve-module '\''(measure)) '\'main')'
|
||||||
|
exec ${GUILE-guile} -l $0 -c "(apply $main (cdr (command-line)))" "$@"
|
||||||
|
!#
|
||||||
|
|
||||||
|
;; A simple interpreter vs. VM performance comparison tool
|
||||||
|
;;
|
||||||
|
|
||||||
|
(define-module (measure)
|
||||||
|
:export (measure)
|
||||||
|
:use-module (system vm vm)
|
||||||
|
:use-module (system base compile)
|
||||||
|
:use-module (system base language))
|
||||||
|
|
||||||
|
|
||||||
|
(define (time-for-eval sexp eval)
|
||||||
|
(let ((before (tms:utime (times))))
|
||||||
|
(eval sexp)
|
||||||
|
(let ((elapsed (- (tms:utime (times)) before)))
|
||||||
|
(format #t "elapsed time: ~a~%" elapsed)
|
||||||
|
elapsed)))
|
||||||
|
|
||||||
|
(define *scheme* (lookup-language 'scheme))
|
||||||
|
|
||||||
|
|
||||||
|
(define (measure . args)
|
||||||
|
(if (< (length args) 2)
|
||||||
|
(begin
|
||||||
|
(format #t "Usage: measure SEXP FILE-TO-LOAD...~%")
|
||||||
|
(format #t "~%")
|
||||||
|
(format #t "Example: measure '(loop 23424)' lib.scm~%~%")
|
||||||
|
(exit 1)))
|
||||||
|
(for-each load (cdr args))
|
||||||
|
(let* ((sexp (with-input-from-string (car args)
|
||||||
|
(lambda ()
|
||||||
|
(read))))
|
||||||
|
(eval-here (lambda (sexp) (eval sexp (current-module))))
|
||||||
|
(proc-name (car sexp))
|
||||||
|
(proc-source (procedure-source (eval proc-name (current-module))))
|
||||||
|
(% (format #t "proc: ~a~%source: ~a~%" proc-name proc-source))
|
||||||
|
(time-interpreted (time-for-eval sexp eval-here))
|
||||||
|
(& (if (defined? proc-name)
|
||||||
|
(eval `(set! ,proc-name #f) (current-module))
|
||||||
|
(format #t "unbound~%")))
|
||||||
|
(the-program (compile proc-source))
|
||||||
|
|
||||||
|
(time-compiled (time-for-eval `(,proc-name ,@(cdr sexp))
|
||||||
|
(lambda (sexp)
|
||||||
|
(eval `(begin
|
||||||
|
(define ,proc-name
|
||||||
|
,the-program)
|
||||||
|
,sexp)
|
||||||
|
(current-module))))))
|
||||||
|
|
||||||
|
(format #t "proc: ~a => ~a~%"
|
||||||
|
proc-name (eval proc-name (current-module)))
|
||||||
|
(format #t "interpreted: ~a~%" time-interpreted)
|
||||||
|
(format #t "compiled: ~a~%" time-compiled)
|
||||||
|
(format #t "speedup: ~a~%"
|
||||||
|
(exact->inexact (/ time-interpreted time-compiled)))
|
||||||
|
0))
|
||||||
|
|
||||||
|
(define main measure)
|
16
configure.in
16
configure.in
|
@ -288,6 +288,8 @@ AC_CHECK_LIB(uca, __uc_get_ar_bsp)
|
||||||
|
|
||||||
AC_C_BIGENDIAN
|
AC_C_BIGENDIAN
|
||||||
|
|
||||||
|
AC_C_LABELS_AS_VALUES
|
||||||
|
|
||||||
AC_CHECK_SIZEOF(char)
|
AC_CHECK_SIZEOF(char)
|
||||||
AC_CHECK_SIZEOF(unsigned char)
|
AC_CHECK_SIZEOF(unsigned char)
|
||||||
AC_CHECK_SIZEOF(short)
|
AC_CHECK_SIZEOF(short)
|
||||||
|
@ -1557,17 +1559,20 @@ AC_CONFIG_FILES([
|
||||||
examples/safe/Makefile
|
examples/safe/Makefile
|
||||||
examples/scripts/Makefile
|
examples/scripts/Makefile
|
||||||
guile-config/Makefile
|
guile-config/Makefile
|
||||||
ice-9/Makefile
|
|
||||||
ice-9/debugger/Makefile
|
|
||||||
ice-9/debugging/Makefile
|
|
||||||
lang/Makefile
|
lang/Makefile
|
||||||
libguile/Makefile
|
libguile/Makefile
|
||||||
oop/Makefile
|
|
||||||
oop/goops/Makefile
|
|
||||||
scripts/Makefile
|
scripts/Makefile
|
||||||
srfi/Makefile
|
srfi/Makefile
|
||||||
test-suite/Makefile
|
test-suite/Makefile
|
||||||
test-suite/standalone/Makefile
|
test-suite/standalone/Makefile
|
||||||
|
module/Makefile
|
||||||
|
module/ice-9/Makefile
|
||||||
|
module/ice-9/debugger/Makefile
|
||||||
|
module/ice-9/debugging/Makefile
|
||||||
|
module/srfi/Makefile
|
||||||
|
module/oop/Makefile
|
||||||
|
module/oop/goops/Makefile
|
||||||
|
testsuite/Makefile
|
||||||
])
|
])
|
||||||
|
|
||||||
AC_CONFIG_FILES([guile-1.8.pc])
|
AC_CONFIG_FILES([guile-1.8.pc])
|
||||||
|
@ -1576,6 +1581,7 @@ AC_CONFIG_FILES([benchmark-guile], [chmod +x benchmark-guile])
|
||||||
AC_CONFIG_FILES([guile-tools], [chmod +x guile-tools])
|
AC_CONFIG_FILES([guile-tools], [chmod +x guile-tools])
|
||||||
AC_CONFIG_FILES([pre-inst-guile], [chmod +x pre-inst-guile])
|
AC_CONFIG_FILES([pre-inst-guile], [chmod +x pre-inst-guile])
|
||||||
AC_CONFIG_FILES([pre-inst-guile-env], [chmod +x pre-inst-guile-env])
|
AC_CONFIG_FILES([pre-inst-guile-env], [chmod +x pre-inst-guile-env])
|
||||||
|
AC_CONFIG_FILES([gdb-pre-inst-guile], [chmod +x gdb-pre-inst-guile])
|
||||||
AC_CONFIG_FILES([libguile/guile-snarf],
|
AC_CONFIG_FILES([libguile/guile-snarf],
|
||||||
[chmod +x libguile/guile-snarf])
|
[chmod +x libguile/guile-snarf])
|
||||||
AC_CONFIG_FILES([libguile/guile-doc-snarf],
|
AC_CONFIG_FILES([libguile/guile-doc-snarf],
|
||||||
|
|
|
@ -44,3 +44,4 @@ guile-api.alist: guile-api.alist-FORCE
|
||||||
( cd $(top_builddir) ; $(mscripts)/update-guile-api.alist )
|
( cd $(top_builddir) ; $(mscripts)/update-guile-api.alist )
|
||||||
guile-api.alist-FORCE:
|
guile-api.alist-FORCE:
|
||||||
|
|
||||||
|
info_TEXINFOS = guile-vm.texi
|
||||||
|
|
78
doc/goops.mail
Normal file
78
doc/goops.mail
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
From: Mikael Djurfeldt <mdj@mdj.nada.kth.se>
|
||||||
|
Subject: Re: After GOOPS integration: Computation with native types!
|
||||||
|
To: Keisuke Nishida <kxn30@po.cwru.edu>
|
||||||
|
Cc: djurfeldt@nada.kth.se, guile@sourceware.cygnus.com
|
||||||
|
Cc: djurfeldt@nada.kth.se
|
||||||
|
Date: 17 Aug 2000 03:01:13 +0200
|
||||||
|
|
||||||
|
Keisuke Nishida <kxn30@po.cwru.edu> writes:
|
||||||
|
|
||||||
|
> Do I need to include some special feature in my VM? Hmm, but maybe
|
||||||
|
> I shouldn't do that now...
|
||||||
|
|
||||||
|
Probably not, so I probably shouldn't answer, but... :)
|
||||||
|
|
||||||
|
You'll need to include some extremely efficient mechanism to do
|
||||||
|
multi-method dispatch. The SCM_IM_DISPATCH form, with its
|
||||||
|
implementation at line 2250 in eval.c, is the current basis for
|
||||||
|
efficient dispatch in GOOPS.
|
||||||
|
|
||||||
|
I think we should develop a new instruction for the VM which
|
||||||
|
corresponds to the SCM_IM_DISPATCH form.
|
||||||
|
|
||||||
|
This form serves both the purpose to map argument types to the correct
|
||||||
|
code, and as a cache of compiled methods.
|
||||||
|
|
||||||
|
Notice that I talk about cmethods below, not methods. In GOOPS, the
|
||||||
|
GF has a set of methods, but each method has a "code-table" mapping
|
||||||
|
argument types to code compiled for those particular concrete types.
|
||||||
|
(So, in essence, GOOPS methods abstractly do a deeper level of type
|
||||||
|
dispatch.)
|
||||||
|
|
||||||
|
The SCM_IM_DISPATCH form has two shapes, depending on whether we use
|
||||||
|
sequential search (few cmethods) or hashed lookup (many cmethods).
|
||||||
|
|
||||||
|
Shape 1:
|
||||||
|
|
||||||
|
(#@dispatch args N-SPECIALIZED #((TYPE1 ... ENV FORMALS FORM1 ...) ...) GF)
|
||||||
|
|
||||||
|
Shape 2:
|
||||||
|
|
||||||
|
(#@dispatch args N-SPECIALIZED HASHSET MASK
|
||||||
|
#((TYPE1 ... ENV FORMALS FORM1 ...) ...)
|
||||||
|
GF)
|
||||||
|
|
||||||
|
`args' is (I hope!) a now historic obscure optimization.
|
||||||
|
|
||||||
|
N-SPECIALIZED is the maximum number of arguments t do type checking
|
||||||
|
on. This is used early termination of argument checking where the
|
||||||
|
already checked arguments are enough to pick out the cmethod.
|
||||||
|
|
||||||
|
The vector is the cache proper.
|
||||||
|
|
||||||
|
During sequential search the argument types are simply checked against
|
||||||
|
each entry.
|
||||||
|
|
||||||
|
The method for hashed dispatch is described in:
|
||||||
|
|
||||||
|
http://www.parc.xerox.com/csl/groups/sda/publications/papers/Kiczales-Andreas-PCL
|
||||||
|
|
||||||
|
In this method, each class has a hash code. Dispatch means summing
|
||||||
|
the hash codes for all arguments (up til N-SPECIALIZED) and using the
|
||||||
|
sum to pick a location in the cache. The cache is sequentially
|
||||||
|
searched for an argument type match from that point.
|
||||||
|
|
||||||
|
Kiczales introduced a clever method to maximize the probability of a
|
||||||
|
direct cache hit. We actually have 8 separate sets of hash codes for
|
||||||
|
all types. The hash set to use is selected specifically per GF and is
|
||||||
|
optimized to give fastest average hit.
|
||||||
|
|
||||||
|
|
||||||
|
What we could try to do as soon as the VM is complete enough is to
|
||||||
|
represent the cmethods as chunks of byte code. In the current GOOPS
|
||||||
|
code, the compilation step (which is currently empty) is situated in
|
||||||
|
`compile-cmethod' in guile-oops/compile.scm. [Apologies for the
|
||||||
|
terrible code. That particular part was written at Arlanda airport
|
||||||
|
after a sleepless night (packing luggage, not coding), on my way to
|
||||||
|
visit Marius (who, BTW, didn't take GOOPS seriously. ;-)]
|
||||||
|
|
1042
doc/guile-vm.texi
Normal file
1042
doc/guile-vm.texi
Normal file
File diff suppressed because it is too large
Load diff
|
@ -68,6 +68,9 @@ guile_TEXINFOS = preface.texi \
|
||||||
autoconf.texi \
|
autoconf.texi \
|
||||||
autoconf-macros.texi \
|
autoconf-macros.texi \
|
||||||
tools.texi \
|
tools.texi \
|
||||||
|
history.texi \
|
||||||
|
vm.texi \
|
||||||
|
compiler.texi \
|
||||||
fdl.texi \
|
fdl.texi \
|
||||||
libguile-concepts.texi \
|
libguile-concepts.texi \
|
||||||
libguile-smobs.texi \
|
libguile-smobs.texi \
|
||||||
|
|
|
@ -2797,11 +2797,11 @@ structure.
|
||||||
@example
|
@example
|
||||||
(make-vtable "prpw"
|
(make-vtable "prpw"
|
||||||
(lambda (struct port)
|
(lambda (struct port)
|
||||||
(display "#<")
|
(display "#<" port)
|
||||||
(display (struct-ref 0))
|
(display (struct-ref struct 0) port)
|
||||||
(display " and ")
|
(display " and " port)
|
||||||
(display (struct-ref 1))
|
(display (struct-ref struct 1) port)
|
||||||
(display ">")))
|
(display ">" port)))
|
||||||
@end example
|
@end example
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
|
|
@ -1889,6 +1889,8 @@ this-is-a-matric
|
||||||
guile>
|
guile>
|
||||||
@end lisp
|
@end lisp
|
||||||
|
|
||||||
|
@anchor{Memoization}
|
||||||
|
@cindex Memoization
|
||||||
(For anyone wondering why the first @code{(do-main 4)} call above
|
(For anyone wondering why the first @code{(do-main 4)} call above
|
||||||
generates lots more trace lines than the subsequent calls: these
|
generates lots more trace lines than the subsequent calls: these
|
||||||
examples also demonstrate how the Guile evaluator ``memoizes'' code.
|
examples also demonstrate how the Guile evaluator ``memoizes'' code.
|
||||||
|
|
|
@ -5,20 +5,22 @@
|
||||||
@c See the file guile.texi for copying conditions.
|
@c See the file guile.texi for copying conditions.
|
||||||
|
|
||||||
@page
|
@page
|
||||||
@node Read/Load/Eval
|
@node Read/Load/Eval/Compile
|
||||||
@section Reading and Evaluating Scheme Code
|
@section Reading and Evaluating Scheme Code
|
||||||
|
|
||||||
This chapter describes Guile functions that are concerned with reading,
|
This chapter describes Guile functions that are concerned with reading,
|
||||||
loading and evaluating Scheme code at run time.
|
loading, evaluating, and compiling Scheme code at run time.
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
* Scheme Syntax:: Standard and extended Scheme syntax.
|
* Scheme Syntax:: Standard and extended Scheme syntax.
|
||||||
* Scheme Read:: Reading Scheme code.
|
* Scheme Read:: Reading Scheme code.
|
||||||
* Fly Evaluation:: Procedures for on the fly evaluation.
|
* Fly Evaluation:: Procedures for on the fly evaluation.
|
||||||
|
* Compilation:: How to compile Scheme files and procedures.
|
||||||
* Loading:: Loading Scheme code from file.
|
* Loading:: Loading Scheme code from file.
|
||||||
* Delayed Evaluation:: Postponing evaluation until it is needed.
|
* Delayed Evaluation:: Postponing evaluation until it is needed.
|
||||||
* Local Evaluation:: Evaluation in a local environment.
|
* Local Evaluation:: Evaluation in a local environment.
|
||||||
* Evaluator Behaviour:: Modifying Guile's evaluator.
|
* Evaluator Behaviour:: Modifying Guile's evaluator.
|
||||||
|
* VM Behaviour:: Modifying Guile's virtual machine.
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
|
|
||||||
|
@ -411,6 +413,69 @@ the current module.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
|
||||||
|
@node Compilation
|
||||||
|
@subsection Compiling Scheme Code
|
||||||
|
|
||||||
|
The @code{eval} procedure directly interprets the S-expression
|
||||||
|
representation of Scheme. An alternate strategy for evaluation is to
|
||||||
|
determine ahead of time what computations will be necessary to
|
||||||
|
evaluate the expression, and then use that recipe to produce the
|
||||||
|
desired results. This is known as @dfn{compilation}.
|
||||||
|
|
||||||
|
While it is possible to compile simple Scheme expressions such as
|
||||||
|
@code{(+ 2 2)} or even @code{"Hello world!"}, compilation is most
|
||||||
|
interesting in the context of procedures. Compiling a lambda expression
|
||||||
|
produces a compiled procedure, which is just like a normal procedure
|
||||||
|
except typically much faster, because it can bypass the generic
|
||||||
|
interpreter.
|
||||||
|
|
||||||
|
Functions from system modules in a Guile installation are normally
|
||||||
|
compiled already, so they load and run quickly.
|
||||||
|
|
||||||
|
Note that well-written Scheme programs will not typically call the
|
||||||
|
procedures in this section, for the same reason that it is often bad
|
||||||
|
taste to use @code{eval}. The normal interface to the compiler is the
|
||||||
|
command-line file compiler, which can be invoked from the shell as
|
||||||
|
@code{guile-tools compile @var{foo.scm}}. This interface needs more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
(Why are calls to @code{eval} and @code{compile} usually in bad taste?
|
||||||
|
Because they are limited, in that they can only really make sense for
|
||||||
|
top-level expressions. Also, most needs for ``compile-time''
|
||||||
|
computation are fulfilled by macros and closures. Of course one good
|
||||||
|
counterexample is the REPL itself, or any code that reads expressions
|
||||||
|
from a port.)
|
||||||
|
|
||||||
|
For more information on the compiler itself, see @ref{Compiling to the
|
||||||
|
Virtual Machine}. For information on the virtual machine, see @ref{A
|
||||||
|
Virtual Machine for Guile}.
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} compile exp [env=#f] [from=(current-language)] [to=value] [opts=()]
|
||||||
|
Compile the expression @var{exp} in the environment @var{env}. If
|
||||||
|
@var{exp} is a procedure, the result will be a compiled procedure;
|
||||||
|
otherwise @code{compile} is mostly equivalent to @code{eval}.
|
||||||
|
|
||||||
|
For a discussion of languages and compiler options, @xref{Compiling to
|
||||||
|
the Virtual Machine}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} compile-file file [to=objcode] [opts='()]
|
||||||
|
Compile the file named @var{file}.
|
||||||
|
|
||||||
|
Output will be written to a file in the current directory whose name
|
||||||
|
is computed as @code{(compiled-file-name @var{file})}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} compiled-file-name file
|
||||||
|
Compute an appropriate name for a compiled version of a Scheme file
|
||||||
|
named @var{file}.
|
||||||
|
|
||||||
|
Usually, the result will be the original file name with the
|
||||||
|
@code{.scm} suffix replaced with @code{.go}, but the exact behavior
|
||||||
|
depends on the contents of the @code{%load-extensions} and
|
||||||
|
@code{%load-compiled-extensions} lists.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
@node Loading
|
@node Loading
|
||||||
@subsection Loading Scheme Code from File
|
@subsection Loading Scheme Code from File
|
||||||
|
|
||||||
|
@ -435,9 +500,19 @@ procedure that will be called before any code is loaded. See
|
||||||
documentation for @code{%load-hook} later in this section.
|
documentation for @code{%load-hook} later in this section.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} load-compiled filename
|
||||||
|
Load the compiled file named @var{filename}. The load paths are not
|
||||||
|
searched.
|
||||||
|
|
||||||
|
Compiling a source file (@pxref{Read/Load/Eval/Compile}) and then
|
||||||
|
calling @code{load-compiled} on the resulting file is equivalent to
|
||||||
|
calling @code{load} on the source file.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
@deffn {Scheme Procedure} load-from-path filename
|
@deffn {Scheme Procedure} load-from-path filename
|
||||||
Similar to @code{load}, but searches for @var{filename} in the load
|
Similar to @code{load}, but searches for @var{filename} in the load
|
||||||
paths.
|
paths. Preferentially loads a compiled version of the file, if it is
|
||||||
|
available and up-to-date.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@deffn {Scheme Procedure} primitive-load filename
|
@deffn {Scheme Procedure} primitive-load filename
|
||||||
|
@ -461,7 +536,8 @@ documentation for @code{%load-hook} later in this section.
|
||||||
Search @code{%load-path} for the file named @var{filename} and
|
Search @code{%load-path} for the file named @var{filename} and
|
||||||
load it into the top-level environment. If @var{filename} is a
|
load it into the top-level environment. If @var{filename} is a
|
||||||
relative pathname and is not found in the list of search paths,
|
relative pathname and is not found in the list of search paths,
|
||||||
an error is signalled.
|
an error is signalled. Preferentially loads a compiled version of the
|
||||||
|
file, if it is available and up-to-date.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@deffn {Scheme Procedure} %search-load-path filename
|
@deffn {Scheme Procedure} %search-load-path filename
|
||||||
|
@ -639,6 +715,30 @@ trap handlers.
|
||||||
Option interface for the evaluator trap options.
|
Option interface for the evaluator trap options.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
@node VM Behaviour
|
||||||
|
@subsection VM Behaviour
|
||||||
|
|
||||||
|
Like the procedures from the previous section that operate on the
|
||||||
|
evaluator, there are also procedures to modify the behavior of a
|
||||||
|
virtual machine.
|
||||||
|
|
||||||
|
The most useful thing that a user can do is to add to one of the
|
||||||
|
virtual machine's predefined hooks:
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} vm-next-hook vm
|
||||||
|
@deffnx {Scheme Procedure} vm-apply-hook vm
|
||||||
|
@deffnx {Scheme Procedure} vm-boot-hook vm
|
||||||
|
@deffnx {Scheme Procedure} vm-return-hook vm
|
||||||
|
@deffnx {Scheme Procedure} vm-break-hook vm
|
||||||
|
@deffnx {Scheme Procedure} vm-exit-hook vm
|
||||||
|
@deffnx {Scheme Procedure} vm-halt-hook vm
|
||||||
|
@deffnx {Scheme Procedure} vm-enter-hook vm
|
||||||
|
Accessors to a virtual machine's hooks. Usually you pass
|
||||||
|
@code{(the-vm)} as the @var{vm}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@xref{A Virtual Machine for Guile}, for more information on Guile's
|
||||||
|
virtual machine.
|
||||||
|
|
||||||
@c Local Variables:
|
@c Local Variables:
|
||||||
@c TeX-master: "guile.texi"
|
@c TeX-master: "guile.texi"
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
@menu
|
@menu
|
||||||
* Lambda:: Basic procedure creation using lambda.
|
* Lambda:: Basic procedure creation using lambda.
|
||||||
* Primitive Procedures:: Procedures defined in C.
|
* Primitive Procedures:: Procedures defined in C.
|
||||||
|
* Compiled Procedures:: Scheme procedures can be compiled.
|
||||||
* Optional Arguments:: Handling keyword, optional and rest arguments.
|
* Optional Arguments:: Handling keyword, optional and rest arguments.
|
||||||
* Procedure Properties:: Procedure properties and meta-information.
|
* Procedure Properties:: Procedure properties and meta-information.
|
||||||
* Procedures with Setters:: Procedures with setters.
|
* Procedures with Setters:: Procedures with setters.
|
||||||
|
@ -131,6 +132,164 @@ use @code{scm_c_make_subr} and also @code{scm_makcclo} if necessary.
|
||||||
It is advisable to use the gsubr variants since they provide a
|
It is advisable to use the gsubr variants since they provide a
|
||||||
slightly higher-level abstraction of the Guile implementation.
|
slightly higher-level abstraction of the Guile implementation.
|
||||||
|
|
||||||
|
@node Compiled Procedures
|
||||||
|
@subsection Compiled Procedures
|
||||||
|
|
||||||
|
Procedures that were created when loading a compiled file are
|
||||||
|
themselves compiled. (In contrast, procedures that are defined by
|
||||||
|
loading a Scheme source file are interpreted, and often not as fast as
|
||||||
|
compiled procedures.)
|
||||||
|
|
||||||
|
Loading compiled files is the normal way that compiled procedures come
|
||||||
|
to being, though procedures can be compiled at runtime as well.
|
||||||
|
@xref{Read/Load/Eval/Compile}, for more information on runtime
|
||||||
|
compilation.
|
||||||
|
|
||||||
|
Compiled procedures, also known as @dfn{programs}, respond all
|
||||||
|
procedures that operate on procedures. In addition, there are a few
|
||||||
|
more accessors for low-level details on programs.
|
||||||
|
|
||||||
|
Most people won't need to use the routines described in this section,
|
||||||
|
but it's good to have them documented. You'll have to include the
|
||||||
|
appropriate module first, though:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(use-modules (system vm program))
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program? obj
|
||||||
|
@deffnx {C Function} scm_program_p (obj)
|
||||||
|
Returns @code{#t} iff @var{obj} is a compiled procedure.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program-bytecode program
|
||||||
|
@deffnx {C Function} scm_program_bytecode (program)
|
||||||
|
Returns the object code associated with this program, as a
|
||||||
|
@code{u8vector}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program-base program
|
||||||
|
@deffnx {C Function} scm_program_base (program)
|
||||||
|
Returns the address in memory corresponding to the start of
|
||||||
|
@var{program}'s object code, as an integer. This is useful mostly when
|
||||||
|
you map the value of an instruction pointer from the VM to actual
|
||||||
|
instructions.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program-objects program
|
||||||
|
@deffnx {C Function} scm_program_objects (program)
|
||||||
|
Returns the ``object table'' associated with this program, as a
|
||||||
|
vector. @xref{VM Programs}, for more information.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program-module program
|
||||||
|
@deffnx {C Function} scm_program_module (program)
|
||||||
|
Returns the module that was current when this program was created.
|
||||||
|
Free variables in this program are looked up with respect to this
|
||||||
|
module.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program-external program
|
||||||
|
@deffnx {C Function} scm_program_external (program)
|
||||||
|
Returns the set of heap-allocated variables that this program captures
|
||||||
|
in its closure, as a list. If a closure is code with data, you can get
|
||||||
|
the code from @code{program-bytecode}, and the data via
|
||||||
|
@code{program-external}.
|
||||||
|
|
||||||
|
Users must not modify the returned value unless they think they're
|
||||||
|
really clever.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program-external-set! program external
|
||||||
|
@deffnx {C Function} scm_program_external_set_x (program, external)
|
||||||
|
Set @var{external} as the set of closure variables on @var{program}.
|
||||||
|
|
||||||
|
The Guile maintainers will not be held responsible for side effects of
|
||||||
|
calling this function, including but not limited to replacement of
|
||||||
|
shampoo with hair dye, and a slight salty taste in tomorrow's dinner.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program-arity program
|
||||||
|
@deffnx {C Function} scm_program_arity (program)
|
||||||
|
@deffnx {Scheme Procedure} arity:nargs arity
|
||||||
|
@deffnx {Scheme Procedure} arity:nrest arity
|
||||||
|
@deffnx {Scheme Procedure} arity:nlocs arity
|
||||||
|
@deffnx {Scheme Procedure} arity:nexts arity
|
||||||
|
Accessors for a representation of the ``arity'' of a program.
|
||||||
|
|
||||||
|
@code{nargs} is the number of arguments to the procedure, and
|
||||||
|
@code{nrest} will be non-zero if the last argument is a rest argument.
|
||||||
|
|
||||||
|
The other two accessors determine the number of local and external
|
||||||
|
(heap-allocated) variables that this procedure will need to have
|
||||||
|
allocated.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program-meta program
|
||||||
|
@deffnx scm_program_meta (program)
|
||||||
|
Return the metadata thunk of @var{program}, or @code{#f} if it has no
|
||||||
|
metadata.
|
||||||
|
|
||||||
|
When called, a metadata thunk returns a list of the following form:
|
||||||
|
@code{(@var{bindings} @var{sources} . @var{properties})}. The format
|
||||||
|
of each of these elements is discussed below.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program-bindings program
|
||||||
|
@deffnx {Scheme Procedure} make-binding name extp index start end
|
||||||
|
@deffnx {Scheme Procedure} binding:name binding
|
||||||
|
@deffnx {Scheme Procedure} binding:extp binding
|
||||||
|
@deffnx {Scheme Procedure} binding:index binding
|
||||||
|
@deffnx {Scheme Procedure} binding:start binding
|
||||||
|
@deffnx {Scheme Procedure} binding:end binding
|
||||||
|
Bindings annotations for programs, along with their accessors.
|
||||||
|
|
||||||
|
Bindings declare names and liveness extents for block-local variables.
|
||||||
|
The best way to see what these are is to play around with them at a
|
||||||
|
REPL. The only tricky bit is that @var{extp} is a boolean, declaring
|
||||||
|
whether the binding is heap-allocated or not. @xref{VM Concepts}, for
|
||||||
|
more information.
|
||||||
|
|
||||||
|
Note that bindings information are stored in a program as part of its
|
||||||
|
metadata thunk, so including them in the generated object code does
|
||||||
|
not impose a runtime performance penalty.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program-sources program
|
||||||
|
@deffnx {Scheme Procedure} source:addr source
|
||||||
|
@deffnx {Scheme Procedure} source:line source
|
||||||
|
@deffnx {Scheme Procedure} source:column source
|
||||||
|
@deffnx {Scheme Procedure} source:file source
|
||||||
|
Source location annotations for programs, along with their accessors.
|
||||||
|
|
||||||
|
Source location information propagates through the compiler and ends
|
||||||
|
up being serialized to the program's metadata. This information is
|
||||||
|
keyed by the offset of the instruction pointer within the object code
|
||||||
|
of the program. Specifically, it is keyed on the @code{ip} @emph{just
|
||||||
|
following} an instruction, so that backtraces can find the source
|
||||||
|
location of a call that is in progress.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program-properties program
|
||||||
|
Return the properties of a @code{program} as an association list,
|
||||||
|
keyed by property name (a symbol).
|
||||||
|
|
||||||
|
Some interesting properties include:
|
||||||
|
@itemize
|
||||||
|
@item @code{name}, the name of the procedure
|
||||||
|
@item @code{documentation}, the procedure's docstring
|
||||||
|
@end itemize
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program-property program name
|
||||||
|
Access a program's property by name, returning @code{#f} if not found.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} program-documentation program
|
||||||
|
@deffnx {Scheme Procedure} program-name program
|
||||||
|
Accessors for specific properties.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
@node Optional Arguments
|
@node Optional Arguments
|
||||||
@subsection Optional Arguments
|
@subsection Optional Arguments
|
||||||
|
|
||||||
|
|
698
doc/ref/compiler.texi
Normal file
698
doc/ref/compiler.texi
Normal file
|
@ -0,0 +1,698 @@
|
||||||
|
@c -*-texinfo-*-
|
||||||
|
@c This is part of the GNU Guile Reference Manual.
|
||||||
|
@c Copyright (C) 2008
|
||||||
|
@c Free Software Foundation, Inc.
|
||||||
|
@c See the file guile.texi for copying conditions.
|
||||||
|
|
||||||
|
@node Compiling to the Virtual Machine
|
||||||
|
@section Compiling to the Virtual Machine
|
||||||
|
|
||||||
|
Compilers have a mystique about them that is attractive and
|
||||||
|
off-putting at the same time. They are attractive because they are
|
||||||
|
magical -- they transform inert text into live results, like throwing
|
||||||
|
the switch on Frankenstein's monster. However, this magic is perceived
|
||||||
|
by many to be impenetrable.
|
||||||
|
|
||||||
|
This section aims to pay attention to the small man behind the
|
||||||
|
curtain.
|
||||||
|
|
||||||
|
@xref{Read/Load/Eval/Compile}, if you're lost and you just wanted to
|
||||||
|
know how to compile your .scm file.
|
||||||
|
|
||||||
|
@menu
|
||||||
|
* Compiler Tower::
|
||||||
|
* The Scheme Compiler::
|
||||||
|
* GHIL::
|
||||||
|
* GLIL::
|
||||||
|
* Object Code::
|
||||||
|
* Extending the Compiler::
|
||||||
|
@end menu
|
||||||
|
|
||||||
|
@node Compiler Tower
|
||||||
|
@subsection Compiler Tower
|
||||||
|
|
||||||
|
Guile's compiler is quite simple, actually -- its @emph{compilers}, to
|
||||||
|
put it more accurately. Guile defines a tower of languages, starting
|
||||||
|
at Scheme and progressively simplifying down to languages that
|
||||||
|
resemble the VM instruction set (@pxref{Instruction Set}).
|
||||||
|
|
||||||
|
Each language knows how to compile to the next, so each step is simple
|
||||||
|
and understandable. Furthermore, this set of languages is not
|
||||||
|
hardcoded into Guile, so it is possible for the user to add new
|
||||||
|
high-level languages, new passes, or even different compilation
|
||||||
|
targets.
|
||||||
|
|
||||||
|
Languages are registered in the module, @code{(system base language)}:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(use-modules (system base language))
|
||||||
|
@end example
|
||||||
|
|
||||||
|
They are registered with the @code{define-language} form.
|
||||||
|
|
||||||
|
@deffn {Scheme Syntax} define-language @
|
||||||
|
name title version reader printer @
|
||||||
|
[parser=#f] [read-file=#f] [compilers='()] [evaluator=#f]
|
||||||
|
Define a language.
|
||||||
|
|
||||||
|
This syntax defines a @code{#<language>} object, bound to @var{name}
|
||||||
|
in the current environment. In addition, the language will be added to
|
||||||
|
the global language set. For example, this is the language definition
|
||||||
|
for Scheme:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(define-language scheme
|
||||||
|
#:title "Guile Scheme"
|
||||||
|
#:version "0.5"
|
||||||
|
#:reader read
|
||||||
|
#:read-file read-file
|
||||||
|
#:compilers `((,ghil . ,compile-ghil))
|
||||||
|
#:evaluator (lambda (x module) (primitive-eval x))
|
||||||
|
#:printer write)
|
||||||
|
@end example
|
||||||
|
|
||||||
|
In this example, from @code{(language scheme spec)}, @code{read-file}
|
||||||
|
reads expressions from a port and wraps them in a @code{begin} block.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
The interesting thing about having languages defined this way is that
|
||||||
|
they present a uniform interface to the read-eval-print loop. This
|
||||||
|
allows the user to change the current language of the REPL:
|
||||||
|
|
||||||
|
@example
|
||||||
|
$ guile
|
||||||
|
Guile Scheme interpreter 0.5 on Guile 1.9.0
|
||||||
|
Copyright (C) 2001-2008 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Enter `,help' for help.
|
||||||
|
scheme@@(guile-user)> ,language ghil
|
||||||
|
Guile High Intermediate Language (GHIL) interpreter 0.3 on Guile 1.9.0
|
||||||
|
Copyright (C) 2001-2008 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Enter `,help' for help.
|
||||||
|
ghil@@(guile-user)>
|
||||||
|
@end example
|
||||||
|
|
||||||
|
Languages can be looked up by name, as they were above.
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} lookup-language name
|
||||||
|
Looks up a language named @var{name}, autoloading it if necessary.
|
||||||
|
|
||||||
|
Languages are autoloaded by looking for a variable named @var{name} in
|
||||||
|
a module named @code{(language @var{name} spec)}.
|
||||||
|
|
||||||
|
The language object will be returned, or @code{#f} if there does not
|
||||||
|
exist a language with that name.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
Defining languages this way allows us to programmatically determine
|
||||||
|
the necessary steps for compiling code from one language to another.
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} lookup-compilation-order from to
|
||||||
|
Recursively traverses the set of languages to which @var{from} can
|
||||||
|
compile, depth-first, and return the first path that can transform
|
||||||
|
@var{from} to @var{to}. Returns @code{#f} if no path is found.
|
||||||
|
|
||||||
|
This function memoizes its results in a cache that is invalidated by
|
||||||
|
subsequent calls to @code{define-language}, so it should be quite
|
||||||
|
fast.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
There is a notion of a ``current language'', which is maintained in
|
||||||
|
the @code{*current-language*} fluid. This language is normally Scheme,
|
||||||
|
and may be rebound by the user. The run-time compilation interfaces
|
||||||
|
(@pxref{Read/Load/Eval/Compile}) also allow you to choose other source
|
||||||
|
and target languages.
|
||||||
|
|
||||||
|
The normal tower of languages when compiling Scheme goes like this:
|
||||||
|
|
||||||
|
@itemize
|
||||||
|
@item Scheme, which we know and love
|
||||||
|
@item Guile High Intermediate Language (GHIL)
|
||||||
|
@item Guile Low Intermediate Language (GLIL)
|
||||||
|
@item Object code
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
Object code may be serialized to disk directly, though it has a cookie
|
||||||
|
and version prepended to the front. But when compiling Scheme at
|
||||||
|
run time, you want a Scheme value, e.g. a compiled procedure. For this
|
||||||
|
reason, so as not to break the abstraction, Guile defines a fake
|
||||||
|
language, @code{value}. Compiling to @code{value} loads the object
|
||||||
|
code into a procedure, and wakes the sleeping giant.
|
||||||
|
|
||||||
|
Perhaps this strangeness can be explained by example:
|
||||||
|
@code{compile-file} defaults to compiling to object code, because it
|
||||||
|
produces object code that has to live in the barren world outside the
|
||||||
|
Guile runtime; but @code{compile} defaults to compiling to
|
||||||
|
@code{value}, as its product re-enters the Guile world.
|
||||||
|
|
||||||
|
Indeed, the process of compilation can circulate through these
|
||||||
|
different worlds indefinitely, as shown by the following quine:
|
||||||
|
|
||||||
|
@example
|
||||||
|
((lambda (x) ((compile x) x)) '(lambda (x) ((compile x) x)))
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@node The Scheme Compiler
|
||||||
|
@subsection The Scheme Compiler
|
||||||
|
|
||||||
|
The job of the Scheme compiler is to expand all macros and to resolve
|
||||||
|
all symbols to lexical variables. Its target language, GHIL, is fairly
|
||||||
|
close to Scheme itself, so this process is not very complicated.
|
||||||
|
|
||||||
|
The Scheme compiler is driven by a table of @dfn{translators},
|
||||||
|
declared with the @code{define-scheme-translator} form, defined in the
|
||||||
|
module, @code{(language scheme compile-ghil)}.
|
||||||
|
|
||||||
|
@deffn {Scheme Syntax} define-scheme-translator head clause1 clause2...
|
||||||
|
The best documentation of this form is probably an example. Here is
|
||||||
|
the translator for @code{if}:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(define-scheme-translator if
|
||||||
|
;; (if TEST THEN [ELSE])
|
||||||
|
((,test ,then)
|
||||||
|
(make-ghil-if e l (retrans test) (retrans then) (retrans '(begin))))
|
||||||
|
((,test ,then ,else)
|
||||||
|
(make-ghil-if e l (retrans test) (retrans then) (retrans else))))
|
||||||
|
@end example
|
||||||
|
|
||||||
|
The match syntax is from the @code{pmatch} macro, defined in
|
||||||
|
@code{(system base pmatch)}. The result of a clause should be a valid
|
||||||
|
GHIL value. If no clause matches, a syntax error is signalled.
|
||||||
|
|
||||||
|
In the body of the clauses, the following bindings are introduced:
|
||||||
|
@itemize
|
||||||
|
@item @code{e}, the current environment
|
||||||
|
@item @code{l}, the current source location (or @code{#f})
|
||||||
|
@item @code{retrans}, a procedure that may be called to compile
|
||||||
|
subexpressions
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
Note that translators are looked up by @emph{value}, not by name. That
|
||||||
|
is to say, the translator is keyed under the @emph{value} of
|
||||||
|
@code{if}, which normally prints as @code{#<primitive-builtin-macro!
|
||||||
|
if>}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
Users can extend the compiler by defining new translators.
|
||||||
|
Additionally, some forms can be inlined directly to
|
||||||
|
instructions -- @xref{Inlined Scheme Instructions}, for a list. The
|
||||||
|
actual inliners are defined in @code{(language scheme inline)}:
|
||||||
|
|
||||||
|
@deffn {Scheme Syntax} define-inline head arity1 result1 arity2 result2...
|
||||||
|
Defines an inliner for @code{head}. As in
|
||||||
|
@code{define-scheme-translator}, inliners are keyed by value and not
|
||||||
|
by name.
|
||||||
|
|
||||||
|
Expressions are matched on their arities. For example:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(define-inline eq?
|
||||||
|
(x y) (eq? x y))
|
||||||
|
@end example
|
||||||
|
|
||||||
|
This inlines calls to the Scheme procedure, @code{eq?}, to the
|
||||||
|
instruction @code{eq?}.
|
||||||
|
|
||||||
|
A more complicated example would be:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(define-inline +
|
||||||
|
() 0
|
||||||
|
(x) x
|
||||||
|
(x y) (add x y)
|
||||||
|
(x y . rest) (add x (+ y . rest)))
|
||||||
|
@end example
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
Compilers take two arguments, an expression and an environment, and
|
||||||
|
return two values as well: an expression in the target language, and
|
||||||
|
an environment suitable for the target language. The format of the
|
||||||
|
environment is language-dependent.
|
||||||
|
|
||||||
|
For Scheme, an environment may be one of three things:
|
||||||
|
@itemize
|
||||||
|
@item @code{#f}, in which case compilation is performed in the context
|
||||||
|
of the current module;
|
||||||
|
@item a module, which specifies the context of the compilation; or
|
||||||
|
@item a @dfn{compile environment}, which specifies lexical variables
|
||||||
|
as well.
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
The format of a compile environment for scheme is @code{(@var{module}
|
||||||
|
@var{lexicals} . @var{externals})}, though users are strongly
|
||||||
|
discouraged from constructing these environments themselves. Instead,
|
||||||
|
if you need this functionality -- as in GOOPS' dynamic method compiler
|
||||||
|
-- capture an environment with @code{compile-time-environment}, then
|
||||||
|
pass that environment to @code{compile}.
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} compile-time-environment
|
||||||
|
A special function known to the compiler that, when compiled, will
|
||||||
|
return a representation of the lexical environment in place at compile
|
||||||
|
time. Useful for supporting some forms of dynamic compilation. Returns
|
||||||
|
@code{#f} if called from the interpreter.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@node GHIL
|
||||||
|
@subsection GHIL
|
||||||
|
|
||||||
|
Guile High Intermediate Language (GHIL) is a structured intermediate
|
||||||
|
language that is close in expressive power to Scheme. It is an
|
||||||
|
expanded, pre-analyzed Scheme.
|
||||||
|
|
||||||
|
GHIL is ``structured'' in the sense that its representation is based
|
||||||
|
on records, not S-expressions. This gives a rigidity to the language
|
||||||
|
that ensures that compiling to a lower-level language only requires a
|
||||||
|
limited set of transformations. Practically speaking, consider the
|
||||||
|
GHIL type, @code{<ghil-quote>}, which has fields named @code{env},
|
||||||
|
@code{loc}, and @code{exp}. Instances of this type are records created
|
||||||
|
via @code{make-ghil-quote}, and whose fields are accessed as
|
||||||
|
@code{ghil-quote-env}, @code{ghil-quote-loc}, and
|
||||||
|
@code{ghil-quote-exp}. There is also a predicate, @code{ghil-quote?}.
|
||||||
|
@xref{Records}, for more information on records.
|
||||||
|
|
||||||
|
Expressions of GHIL name their environments explicitly, and all
|
||||||
|
variables are referenced by identity in addition to by name.
|
||||||
|
@code{(language ghil)} defines a number of routines to deal explicitly
|
||||||
|
with variables and environments:
|
||||||
|
|
||||||
|
@deftp {Scheme Variable} <ghil-toplevel-env> [table='()]
|
||||||
|
A toplevel environment. The @var{table} holds all toplevel variables
|
||||||
|
that have been resolved in this environment.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-env> parent [table='()] [variables='()]
|
||||||
|
A lexical environment. @var{parent} will be the enclosing lexical
|
||||||
|
environment, or a toplevel environment. @var{table} holds an alist
|
||||||
|
mapping symbols to variables bound in this environment, while
|
||||||
|
@var{variables} holds a cumulative list of all variables ever defined
|
||||||
|
in this environment.
|
||||||
|
|
||||||
|
Lexical environments correspond to procedures. Bindings introduced
|
||||||
|
e.g. by Scheme's @code{let} add to the bindings in a lexical
|
||||||
|
environment. An example of a case in which a variable might be in
|
||||||
|
@var{variables} but not in @var{table} would be a variable that is in
|
||||||
|
the same procedure, but is out of scope.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-var> env name kind [index=#f]
|
||||||
|
A variable. @var{kind} is one of @code{argument}, @code{local},
|
||||||
|
@code{external}, @code{toplevel}, @code{public}, or @code{private};
|
||||||
|
see the procedures below for more information. @var{index} is used in
|
||||||
|
compilation.
|
||||||
|
@end deftp
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} ghil-var-is-bound? env sym
|
||||||
|
Recursively look up a variable named @var{sym} in @var{env}, and
|
||||||
|
return it or @code{#f} if none is found.
|
||||||
|
@end deffn
|
||||||
|
@deffn {Scheme Procedure} ghil-var-for-ref! env sym
|
||||||
|
Recursively look up a variable named @var{sym} in @var{env}, and
|
||||||
|
return it. If the symbol was not bound, return a new toplevel
|
||||||
|
variable.
|
||||||
|
@end deffn
|
||||||
|
@deffn {Scheme Procedure} ghil-var-for-set! env sym
|
||||||
|
Like @code{ghil-var-for-ref!}, except that the returned variable will
|
||||||
|
be marked as @code{external}. @xref{Variables and the VM}.
|
||||||
|
@end deffn
|
||||||
|
@deffn {Scheme Procedure} ghil-var-define! toplevel-env sym
|
||||||
|
Return an existing or new toplevel variable named @var{sym}.
|
||||||
|
@var{toplevel-env} must be a toplevel environment.
|
||||||
|
@end deffn
|
||||||
|
@deffn {Scheme Procedure} ghil-var-at-module! env modname sym interface?
|
||||||
|
Return a variable that will be resolved at run-time with respect to a
|
||||||
|
specific module named @var{modname}. If @var{interface?} is true, the
|
||||||
|
variable will be of type @code{public}, otherwise @code{private}.
|
||||||
|
@end deffn
|
||||||
|
@deffn {Scheme Procedure} call-with-ghil-environment env syms func
|
||||||
|
Bind @var{syms} to fresh variables within a new lexical environment
|
||||||
|
whose parent is @var{env}, and call @var{func} as @code{(@var{func}
|
||||||
|
@var{new-env} @var{new-vars})}.
|
||||||
|
@end deffn
|
||||||
|
@deffn {Scheme Procedure} call-with-ghil-bindings env syms func
|
||||||
|
Like @code{call-with-ghil-environment}, except the existing
|
||||||
|
environment @var{env} is re-used. For that reason, @var{func} is
|
||||||
|
invoked as @code{(@var{func} @var{new-vars})}
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
In the aforementioned @code{<ghil-quote>} type, the @var{env} slot
|
||||||
|
holds a pointer to the environment in which the expression occurs. The
|
||||||
|
@var{loc} slot holds source location information, so that errors
|
||||||
|
corresponding to this expression can be mapped back to the initial
|
||||||
|
expression in the higher-level language, e.g. Scheme. @xref{Compiled
|
||||||
|
Procedures}, for more information on source location objects.
|
||||||
|
|
||||||
|
GHIL also has a declarative serialization format, which makes writing
|
||||||
|
and reading it a tractable problem for the human mind. Since all GHIL
|
||||||
|
language constructs contain @code{env} and @code{loc} pointers, they
|
||||||
|
are left out of the serialization. (Serializing @code{env} structures
|
||||||
|
would be difficult, as they are often circular.) What is left is the
|
||||||
|
type of expression, and the remaining slots defined in the expression
|
||||||
|
type.
|
||||||
|
|
||||||
|
For example, an S-expression representation of the @code{<ghil-quote>}
|
||||||
|
expression would be:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(quote 3)
|
||||||
|
@end example
|
||||||
|
|
||||||
|
It's deceptively like Scheme. The general rule is, for a type defined
|
||||||
|
as @code{<ghil-@var{foo}> env loc @var{slot1} @var{slot2}...}, the
|
||||||
|
S-expression representation will be @code{(@var{foo} @var{slot1}
|
||||||
|
@var{slot2}...)}. Users may program with this format directly at the
|
||||||
|
REPL:
|
||||||
|
|
||||||
|
@example
|
||||||
|
scheme@@(guile-user)> ,language ghil
|
||||||
|
Guile High Intermediate Language (GHIL) interpreter 0.3 on Guile 1.9.0
|
||||||
|
Copyright (C) 2001-2008 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Enter `,help' for help.
|
||||||
|
ghil@@(guile-user)> (call (ref +) (quote 32) (quote 10))
|
||||||
|
@result{} 42
|
||||||
|
@end example
|
||||||
|
|
||||||
|
For convenience, some slots are serialized as rest arguments; those
|
||||||
|
are noted below. The other caveat is that variables are serialized as
|
||||||
|
their names only, and not their identities.
|
||||||
|
|
||||||
|
@deftp {Scheme Variable} <ghil-void> env loc
|
||||||
|
The unspecified value.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-quote> env loc exp
|
||||||
|
A quoted expression.
|
||||||
|
|
||||||
|
Note that unlike in Scheme, there are no self-quoting expressions; all
|
||||||
|
constants must come from @code{quote} expressions.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-quasiquote> env loc exp
|
||||||
|
A quasiquoted expression. The expression is treated as a constant,
|
||||||
|
except for embedded @code{unquote} and @code{unquote-splicing} forms.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-unquote> env loc exp
|
||||||
|
Like Scheme's @code{unquote}; only valid within a quasiquote.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-unquote-splicing> env loc exp
|
||||||
|
Like Scheme's @code{unquote-splicing}; only valid within a quasiquote.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-ref> env loc var
|
||||||
|
A variable reference. Note that for purposes of serialization,
|
||||||
|
@var{var} is serialized as its name, as a symbol.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-set> env loc var val
|
||||||
|
A variable mutation. @var{var} is serialized as a symbol.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-define> env loc var val
|
||||||
|
A toplevel variable definition. See @code{ghil-var-define!}.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-if> env loc test then else
|
||||||
|
A conditional. Note that @var{else} is not optional.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-and> env loc . exps
|
||||||
|
Like Scheme's @code{and}.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-or> env loc . exps
|
||||||
|
Like Scheme's @code{or}.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-begin> env loc . body
|
||||||
|
Like Scheme's @code{begin}.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-bind> env loc vars exprs . body
|
||||||
|
Like a deconstructed @code{let}: each element of @var{vars} will be
|
||||||
|
bound to the corresponding GHIL expression in @var{exprs}.
|
||||||
|
|
||||||
|
Note that for purposes of the serialization format, @var{exprs} are
|
||||||
|
evaluated before the new bindings are added to the environment. For
|
||||||
|
@code{letrec} semantics, there also exists a @code{bindrec} parse
|
||||||
|
flavor. This is useful for writing GHIL at the REPL, but the
|
||||||
|
serializer does not currently have the cleverness needed to determine
|
||||||
|
whether a @code{<ghil-bind>} has @code{let} or @code{letrec}
|
||||||
|
semantics, and thus only serializes @code{<ghil-bind>} as @code{bind}.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-mv-bind> env loc vars rest producer . body
|
||||||
|
Like Scheme's @code{receive} -- binds the values returned by
|
||||||
|
applying @code{producer}, which should be a thunk, to the
|
||||||
|
@code{lambda}-like bindings described by @var{vars} and @var{rest}.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-lambda> env loc vars rest meta . body
|
||||||
|
A closure. @var{vars} is the argument list, serialized as a list of
|
||||||
|
symbols. @var{rest} is a boolean, which is @code{#t} iff the last
|
||||||
|
argument is a rest argument. @var{meta} is an association list of
|
||||||
|
properties. The actual @var{body} should be a list of GHIL
|
||||||
|
expressions.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-call> env loc proc . args
|
||||||
|
A procedure call.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-mv-call> env loc producer consumer
|
||||||
|
Like Scheme's @code{call-with-values}.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-inline> env loc op . args
|
||||||
|
An inlined VM instruction. @var{op} should be the instruction name as
|
||||||
|
a symbol, and @var{args} should be its arguments, as GHIL expressions.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-values> env loc . values
|
||||||
|
Like Scheme's @code{values}.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-values*> env loc . values
|
||||||
|
@var{values} are as in the Scheme expression, @code{(apply values .
|
||||||
|
@var{vals})}.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <ghil-reified-env> env loc
|
||||||
|
Produces, at run-time, a reification of the environment at compile
|
||||||
|
time. Used in the implementation of Scheme's
|
||||||
|
@code{compile-time-environment}.
|
||||||
|
@end deftp
|
||||||
|
|
||||||
|
GHIL implements a compiler to GLIL that recursively traverses GHIL
|
||||||
|
expressions, writing out GLIL expressions into a linear list. The
|
||||||
|
compiler also keeps some state as to whether the current expression is
|
||||||
|
in tail context, and whether its value will be used in future
|
||||||
|
computations. This state allows the compiler not to emit code for
|
||||||
|
constant expressions that will not be used (e.g. docstrings), and to
|
||||||
|
perform tail calls when in tail position.
|
||||||
|
|
||||||
|
Just as the Scheme to GHIL compiler introduced new hidden state---the
|
||||||
|
environment---the GHIL to GLIL compiler introduces more state, the
|
||||||
|
stack. While not represented explicitly, the stack is present in the
|
||||||
|
compilation of each GHIL expression: compiling a GHIL expression
|
||||||
|
should leave the run-time value stack in the same state. For example,
|
||||||
|
if the intermediate value stack has two elements before evaluating an
|
||||||
|
@code{if} expression, it should have two elements after that
|
||||||
|
expression.
|
||||||
|
|
||||||
|
Interested readers are encouraged to read the implementation in
|
||||||
|
@code{(language ghil compile-glil)} for more details.
|
||||||
|
|
||||||
|
@node GLIL
|
||||||
|
@subsection GLIL
|
||||||
|
|
||||||
|
Guile Low Intermediate Language (GLIL) is a structured intermediate
|
||||||
|
language whose expressions closely mirror the functionality of Guile's
|
||||||
|
VM instruction set.
|
||||||
|
|
||||||
|
Its expression types are defined in @code{(language glil)}, and as
|
||||||
|
with GHIL, some of its fields parse as rest arguments.
|
||||||
|
|
||||||
|
@deftp {Scheme Variable} <glil-program> nargs nrest nlocs nexts meta . body
|
||||||
|
A unit of code that at run-time will correspond to a compiled
|
||||||
|
procedure. @var{nargs} @var{nrest} @var{nlocs}, and @var{nexts}
|
||||||
|
collectively define the program's arity; see @ref{Compiled
|
||||||
|
Procedures}, for more information. @var{meta} should be an alist of
|
||||||
|
properties, as in @code{<ghil-lambda>}. @var{body} is a list of GLIL
|
||||||
|
expressions.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-bind> . vars
|
||||||
|
An advisory expression that notes a liveness extent for a set of
|
||||||
|
variables. @var{vars} is a list of @code{(@var{name} @var{type}
|
||||||
|
@var{index})}, where @var{type} should be either @code{argument},
|
||||||
|
@code{local}, or @code{external}.
|
||||||
|
|
||||||
|
@code{<glil-bind>} expressions end up being serialized as part of a
|
||||||
|
program's metadata and do not form part of a program's code path.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-mv-bind> vars rest
|
||||||
|
A multiple-value binding of the values on the stack to @var{vars}. Iff
|
||||||
|
@var{rest} is true, the last element of @var{vars} will be treated as
|
||||||
|
a rest argument.
|
||||||
|
|
||||||
|
In addition to pushing a binding annotation on the stack, like
|
||||||
|
@code{<glil-bind>}, an expression is emitted at compilation time to
|
||||||
|
make sure that there are enough values available to bind. See the
|
||||||
|
notes on @code{truncate-values} in @ref{Procedural Instructions}, for
|
||||||
|
more information.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-unbind>
|
||||||
|
Closes the liveness extent of the most recently encountered
|
||||||
|
@code{<glil-bind>} or @code{<glil-mv-bind>} expression. As GLIL
|
||||||
|
expressions are compiled, a parallel stack of live bindings is
|
||||||
|
maintained; this expression pops off the top element from that stack.
|
||||||
|
|
||||||
|
Bindings are written into the program's metadata so that debuggers and
|
||||||
|
other tools can determine the set of live local variables at a given
|
||||||
|
offset within a VM program.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-source> loc
|
||||||
|
Records source information for the preceding expression. @var{loc}
|
||||||
|
should be a vector, @code{#(@var{line} @var{column} @var{filename})}.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-void>
|
||||||
|
Pushes the unspecified value on the stack.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-const> obj
|
||||||
|
Pushes a constant value onto the stack. @var{obj} must be a number,
|
||||||
|
string, symbol, keyword, boolean, character, or a pair or vector or
|
||||||
|
list thereof, or the empty list.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-argument> op index
|
||||||
|
Accesses an argument on the stack. If @var{op} is @code{ref}, the
|
||||||
|
argument is pushed onto the stack; if it is @code{set}, the argument
|
||||||
|
is set from the top value on the stack, which is popped off.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-local> op index
|
||||||
|
Like @code{<glil-argument>}, but for local variables. @xref{Stack
|
||||||
|
Layout}, for more information.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-external> op depth index
|
||||||
|
Accesses a heap-allocated variable, addressed by @var{depth}, the nth
|
||||||
|
enclosing environment, and @var{index}, the variable's position within
|
||||||
|
the environment. @var{op} is @code{ref} or @code{set}.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-toplevel> op name
|
||||||
|
Accesses a toplevel variable. @var{op} may be @code{ref}, @code{set},
|
||||||
|
or @code{define}.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-module> op mod name public?
|
||||||
|
Accesses a variable within a specific module. See
|
||||||
|
@code{ghil-var-at-module!}, for more information.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-label> label
|
||||||
|
Creates a new label. @var{label} can be any Scheme value, and should
|
||||||
|
be unique.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-branch> inst label
|
||||||
|
Branch to a label. @var{label} should be a @code{<ghil-label>}.
|
||||||
|
@code{inst} is a branching instruction: @code{br-if}, @code{br}, etc.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-call> inst nargs
|
||||||
|
This expression is probably misnamed, as it does not correspond to
|
||||||
|
function calls. @code{<glil-call>} invokes the VM instruction named
|
||||||
|
@var{inst}, noting that it is called with @var{nargs} stack arguments.
|
||||||
|
The arguments should be pushed on the stack already. What happens to
|
||||||
|
the stack afterwards depends on the instruction.
|
||||||
|
@end deftp
|
||||||
|
@deftp {Scheme Variable} <glil-mv-call> nargs ra
|
||||||
|
Performs a multiple-value call. @var{ra} is a @code{<glil-label>}
|
||||||
|
corresponding to the multiple-value return address for the call. See
|
||||||
|
the notes on @code{mv-call} in @ref{Procedural Instructions}, for more
|
||||||
|
information.
|
||||||
|
@end deftp
|
||||||
|
|
||||||
|
Users may enter in GLIL at the REPL as well, though there is a bit
|
||||||
|
more bookkeeping to do. Since GLIL needs the set of variables to be
|
||||||
|
declared explicitly in a @code{<glil-program>}, GLIL expressions must
|
||||||
|
be wrapped in a thunk that declares the arity of the expression:
|
||||||
|
|
||||||
|
@example
|
||||||
|
scheme@@(guile-user)> ,language glil
|
||||||
|
Guile Lowlevel Intermediate Language (GLIL) interpreter 0.3 on Guile 1.9.0
|
||||||
|
Copyright (C) 2001-2008 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Enter `,help' for help.
|
||||||
|
glil@@(guile-user)> (program 0 0 0 0 () (const 3) (call return 0))
|
||||||
|
@result{} 3
|
||||||
|
@end example
|
||||||
|
|
||||||
|
Just as in all of Guile's compilers, an environment is passed to the
|
||||||
|
GLIL-to-object code compiler, and one is returned as well, along with
|
||||||
|
the object code.
|
||||||
|
|
||||||
|
@node Object Code
|
||||||
|
@subsection Object Code
|
||||||
|
|
||||||
|
Object code is the serialization of the raw instruction stream of a
|
||||||
|
program, ready for interpretation by the VM. Procedures related to
|
||||||
|
object code are defined in the @code{(system vm objcode)} module.
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} objcode? obj
|
||||||
|
@deffnx {C Function} scm_objcode_p (obj)
|
||||||
|
Returns @code{#f} iff @var{obj} is object code, @code{#f} otherwise.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} bytecode->objcode bytecode nlocs nexts
|
||||||
|
@deffnx {C Function} scm_bytecode_to_objcode (bytecode, nlocs, nexts)
|
||||||
|
Makes a bytecode object from @var{bytecode}, which should be a
|
||||||
|
@code{u8vector}. @var{nlocs} and @var{nexts} denote the number of
|
||||||
|
stack and heap variables to reserve when this objcode is executed.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Variable} load-objcode file
|
||||||
|
@deffnx {C Function} scm_load_objcode (file)
|
||||||
|
Load object code from a file named @var{file}. The file will be mapped
|
||||||
|
into memory via @code{mmap}, so this is a very fast operation.
|
||||||
|
|
||||||
|
On disk, object code has an eight-byte cookie prepended to it, so that
|
||||||
|
we will not execute arbitrary garbage. In addition, two more bytes are
|
||||||
|
reserved for @var{nlocs} and @var{nexts}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Variable} objcode->u8vector objcode
|
||||||
|
@deffnx {C Function} scm_objcode_to_u8vector (objcode)
|
||||||
|
Copy object code out to a @code{u8vector} for analysis by Scheme. The
|
||||||
|
ten-byte header is included.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Variable} objcode->program objcode [external='()]
|
||||||
|
@deffnx {C Function} scm_objcode_to_program (objcode, external)
|
||||||
|
Load up object code into a Scheme program. The resulting program will
|
||||||
|
be a thunk that captures closure variables from @var{external}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
Object code from a file may be disassembled at the REPL via the
|
||||||
|
meta-command @code{,disassemble-file}, abbreviated as @code{,xx}.
|
||||||
|
Programs may be disassembled via @code{,disassemble}, abbreviated as
|
||||||
|
@code{,x}.
|
||||||
|
|
||||||
|
Compiling object code to the fake language, @code{value}, is performed
|
||||||
|
via loading objcode into a program, then executing that thunk with
|
||||||
|
respect to the compilation environment. Normally the environment
|
||||||
|
propagates through the compiler transparently, but users may specify
|
||||||
|
the compilation environment manually as well:
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} make-objcode-env module externals
|
||||||
|
Make an object code environment. @var{module} should be a Scheme
|
||||||
|
module, and @var{externals} should be a list of external variables.
|
||||||
|
@code{#f} is also a valid object code environment.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@node Extending the Compiler
|
||||||
|
@subsection Extending the Compiler
|
||||||
|
|
||||||
|
At this point, we break with the impersonal tone of the rest of the
|
||||||
|
manual, and make an intervention. Admit it: if you've read this far
|
||||||
|
into the compiler internals manual, you are a junkie. Perhaps a course
|
||||||
|
at your university left you unsated, or perhaps you've always harbored
|
||||||
|
a sublimated desire to hack the holy of computer science holies: a
|
||||||
|
compiler. Well you're in good company, and in a good position. Guile's
|
||||||
|
compiler needs your help.
|
||||||
|
|
||||||
|
There are many possible avenues for improving Guile's compiler.
|
||||||
|
Probably the most important improvement, speed-wise, will be some form
|
||||||
|
of native compilation, both just-in-time and ahead-of-time. This could
|
||||||
|
be done in many ways. Probably the easiest strategy would be to extend
|
||||||
|
the compiled procedure structure to include a pointer to a native code
|
||||||
|
vector, and compile from bytecode to native code at run-time after a
|
||||||
|
procedure is called a certain number of times.
|
||||||
|
|
||||||
|
The name of the game is a profiling-based harvest of the low-hanging
|
||||||
|
fruit, running programs of interest under a system-level profiler and
|
||||||
|
determining which improvements would give the most bang for the buck.
|
||||||
|
There are many well-known efficiency hacks in the literature: Dybvig's
|
||||||
|
letrec optimization, individual boxing of heap-allocated values (and
|
||||||
|
then store the boxes on the stack directory), optimized case-lambda
|
||||||
|
expressions, stack underflow and overflow handlers, etc. Highly
|
||||||
|
recommended papers: Dybvig's HOCS, Ghuloum's compiler paper.
|
||||||
|
|
||||||
|
The compiler also needs help at the top end, enhancing the Scheme that
|
||||||
|
it knows to also understand R6RS, and adding new high-level compilers:
|
||||||
|
Emacs Lisp, Lua, JavaScript...
|
|
@ -4,135 +4,6 @@
|
||||||
@c Free Software Foundation, Inc.
|
@c Free Software Foundation, Inc.
|
||||||
@c See the file guile.texi for copying conditions.
|
@c See the file guile.texi for copying conditions.
|
||||||
|
|
||||||
@c essay \input texinfo
|
|
||||||
@c essay @c -*-texinfo-*-
|
|
||||||
@c essay @c %**start of header
|
|
||||||
@c essay @setfilename data-rep.info
|
|
||||||
@c essay @settitle Data Representation in Guile
|
|
||||||
@c essay @c %**end of header
|
|
||||||
|
|
||||||
@c essay @include version.texi
|
|
||||||
|
|
||||||
@c essay @dircategory The Algorithmic Language Scheme
|
|
||||||
@c essay @direntry
|
|
||||||
@c essay * data-rep: (data-rep). Data Representation in Guile --- how to use
|
|
||||||
@c essay Guile objects in your C code.
|
|
||||||
@c essay @end direntry
|
|
||||||
|
|
||||||
@c essay @setchapternewpage off
|
|
||||||
|
|
||||||
@c essay @ifinfo
|
|
||||||
@c essay Data Representation in Guile
|
|
||||||
|
|
||||||
@c essay Copyright (C) 1998, 1999, 2000, 2003, 2006 Free Software Foundation
|
|
||||||
|
|
||||||
@c essay Permission is granted to make and distribute verbatim copies of
|
|
||||||
@c essay this manual provided the copyright notice and this permission notice
|
|
||||||
@c essay are preserved on all copies.
|
|
||||||
|
|
||||||
@c essay @ignore
|
|
||||||
@c essay Permission is granted to process this file through TeX and print the
|
|
||||||
@c essay results, provided the printed document carries copying permission
|
|
||||||
@c essay notice identical to this one except for the removal of this paragraph
|
|
||||||
@c essay (this paragraph not being relevant to the printed manual).
|
|
||||||
@c essay @end ignore
|
|
||||||
|
|
||||||
@c essay Permission is granted to copy and distribute modified versions of this
|
|
||||||
@c essay manual under the conditions for verbatim copying, provided that the entire
|
|
||||||
@c essay resulting derived work is distributed under the terms of a permission
|
|
||||||
@c essay notice identical to this one.
|
|
||||||
|
|
||||||
@c essay Permission is granted to copy and distribute translations of this manual
|
|
||||||
@c essay into another language, under the above conditions for modified versions,
|
|
||||||
@c essay except that this permission notice may be stated in a translation approved
|
|
||||||
@c essay by the Free Software Foundation.
|
|
||||||
@c essay @end ifinfo
|
|
||||||
|
|
||||||
@c essay @titlepage
|
|
||||||
@c essay @sp 10
|
|
||||||
@c essay @comment The title is printed in a large font.
|
|
||||||
@c essay @title Data Representation in Guile
|
|
||||||
@c essay @subtitle $Id: data-rep.texi,v 1.20 2006-04-16 23:11:15 kryde Exp $
|
|
||||||
@c essay @subtitle For use with Guile @value{VERSION}
|
|
||||||
@c essay @author Jim Blandy
|
|
||||||
@c essay @author Free Software Foundation
|
|
||||||
@c essay @author @email{jimb@@red-bean.com}
|
|
||||||
@c essay @c The following two commands start the copyright page.
|
|
||||||
@c essay @page
|
|
||||||
@c essay @vskip 0pt plus 1filll
|
|
||||||
@c essay @vskip 0pt plus 1filll
|
|
||||||
@c essay Copyright @copyright{} 1998, 2006 Free Software Foundation
|
|
||||||
|
|
||||||
@c essay Permission is granted to make and distribute verbatim copies of
|
|
||||||
@c essay this manual provided the copyright notice and this permission notice
|
|
||||||
@c essay are preserved on all copies.
|
|
||||||
|
|
||||||
@c essay Permission is granted to copy and distribute modified versions of this
|
|
||||||
@c essay manual under the conditions for verbatim copying, provided that the entire
|
|
||||||
@c essay resulting derived work is distributed under the terms of a permission
|
|
||||||
@c essay notice identical to this one.
|
|
||||||
|
|
||||||
@c essay Permission is granted to copy and distribute translations of this manual
|
|
||||||
@c essay into another language, under the above conditions for modified versions,
|
|
||||||
@c essay except that this permission notice may be stated in a translation approved
|
|
||||||
@c essay by Free Software Foundation.
|
|
||||||
@c essay @end titlepage
|
|
||||||
|
|
||||||
@c essay @c @smallbook
|
|
||||||
@c essay @c @finalout
|
|
||||||
@c essay @headings double
|
|
||||||
|
|
||||||
|
|
||||||
@c essay @node Top, Data Representation in Scheme, (dir), (dir)
|
|
||||||
@c essay @top Data Representation in Guile
|
|
||||||
|
|
||||||
@c essay @ifinfo
|
|
||||||
@c essay This essay is meant to provide the background necessary to read and
|
|
||||||
@c essay write C code that manipulates Scheme values in a way that conforms to
|
|
||||||
@c essay libguile's interface. If you would like to write or maintain a
|
|
||||||
@c essay Guile-based application in C or C++, this is the first information you
|
|
||||||
@c essay need.
|
|
||||||
|
|
||||||
@c essay In order to make sense of Guile's @code{SCM_} functions, or read
|
|
||||||
@c essay libguile's source code, it's essential to have a good grasp of how Guile
|
|
||||||
@c essay actually represents Scheme values. Otherwise, a lot of the code, and
|
|
||||||
@c essay the conventions it follows, won't make very much sense.
|
|
||||||
|
|
||||||
@c essay We assume you know both C and Scheme, but we do not assume you are
|
|
||||||
@c essay familiar with Guile's C interface.
|
|
||||||
@c essay @end ifinfo
|
|
||||||
|
|
||||||
|
|
||||||
@node Data Representation
|
|
||||||
@appendix Data Representation in Guile
|
|
||||||
|
|
||||||
@strong{by Jim Blandy}
|
|
||||||
|
|
||||||
[Due to the rather non-orthogonal and performance-oriented nature of the
|
|
||||||
SCM interface, you need to understand SCM internals *before* you can use
|
|
||||||
the SCM API. That's why this chapter comes first.]
|
|
||||||
|
|
||||||
[NOTE: this is Jim Blandy's essay almost entirely unmodified. It has to
|
|
||||||
be adapted to fit this manual smoothly.]
|
|
||||||
|
|
||||||
In order to make sense of Guile's SCM_ functions, or read libguile's
|
|
||||||
source code, it's essential to have a good grasp of how Guile actually
|
|
||||||
represents Scheme values. Otherwise, a lot of the code, and the
|
|
||||||
conventions it follows, won't make very much sense. This essay is meant
|
|
||||||
to provide the background necessary to read and write C code that
|
|
||||||
manipulates Scheme values in a way that is compatible with libguile.
|
|
||||||
|
|
||||||
We assume you know both C and Scheme, but we do not assume you are
|
|
||||||
familiar with Guile's implementation.
|
|
||||||
|
|
||||||
@menu
|
|
||||||
* Data Representation in Scheme:: Why things aren't just totally
|
|
||||||
straightforward, in general terms.
|
|
||||||
* How Guile does it:: How to write C code that manipulates
|
|
||||||
Guile values, with an explanation
|
|
||||||
of Guile's garbage collector.
|
|
||||||
@end menu
|
|
||||||
|
|
||||||
@node Data Representation in Scheme
|
@node Data Representation in Scheme
|
||||||
@section Data Representation in Scheme
|
@section Data Representation in Scheme
|
||||||
|
|
||||||
|
@ -159,8 +30,8 @@ The following sections will present a simple typing system, and then
|
||||||
make some refinements to correct its major weaknesses. However, this is
|
make some refinements to correct its major weaknesses. However, this is
|
||||||
not a description of the system Guile actually uses. It is only an
|
not a description of the system Guile actually uses. It is only an
|
||||||
illustration of the issues Guile's system must address. We provide all
|
illustration of the issues Guile's system must address. We provide all
|
||||||
the information one needs to work with Guile's data in @ref{How Guile
|
the information one needs to work with Guile's data in @ref{The
|
||||||
does it}.
|
Libguile Runtime Environment}.
|
||||||
|
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
|
@ -423,22 +294,21 @@ significant loss of efficiency, but the simplified system would still be
|
||||||
more complex than what we've presented above.
|
more complex than what we've presented above.
|
||||||
|
|
||||||
|
|
||||||
@node How Guile does it
|
@node The Libguile Runtime Environment
|
||||||
@section How Guile does it
|
@section The Libguile Runtime Environment
|
||||||
|
|
||||||
Here we present the specifics of how Guile represents its data. We
|
Here we present the specifics of how Guile represents its data. We
|
||||||
don't go into complete detail; an exhaustive description of Guile's
|
don't go into complete detail; an exhaustive description of Guile's
|
||||||
system would be boring, and we do not wish to encourage people to write
|
system would be boring, and we do not wish to encourage people to write
|
||||||
code which depends on its details anyway. We do, however, present
|
code which depends on its details anyway. We do, however, present
|
||||||
everything one need know to use Guile's data.
|
everything one need know to use Guile's data. It is assumed that the
|
||||||
|
reader understands the concepts laid out in @ref{Data Representation
|
||||||
|
in Scheme}.
|
||||||
|
|
||||||
This section is in limbo. It used to document the 'low-level' C API
|
FIXME: much of this is outdated as of 1.8, we don't provide many of
|
||||||
of Guile that was used both by clients of libguile and by libguile
|
these macros any more. Also here we're missing sections about the
|
||||||
itself.
|
evaluator implementation, which is interesting, and notes about tail
|
||||||
|
recursion between scheme and c.
|
||||||
In the future, clients should only need to look into the sections
|
|
||||||
@ref{Programming in C} and @ref{API Reference}. This section will in
|
|
||||||
the end only contain stuff about the internals of Guile.
|
|
||||||
|
|
||||||
@menu
|
@menu
|
||||||
* General Rules::
|
* General Rules::
|
||||||
|
@ -1127,7 +997,7 @@ This reference can be decoded to a C pointer to a heap cell using the
|
||||||
@code{SCM} value is done using the @code{PTR2SCM} macro.
|
@code{SCM} value is done using the @code{PTR2SCM} macro.
|
||||||
|
|
||||||
@c (FIXME:: this name should be changed)
|
@c (FIXME:: this name should be changed)
|
||||||
@deftypefn Macro (scm_t_cell *) SCM2PTR (SCM @var{x})
|
@deftypefn Macro {scm_t_cell *} SCM2PTR (SCM @var{x})
|
||||||
Extract and return the heap cell pointer from a non-immediate @code{SCM}
|
Extract and return the heap cell pointer from a non-immediate @code{SCM}
|
||||||
object @var{x}.
|
object @var{x}.
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
|
@ -177,11 +177,12 @@ x
|
||||||
|
|
||||||
* Guile Modules::
|
* Guile Modules::
|
||||||
|
|
||||||
|
* Guile Implementation::
|
||||||
|
|
||||||
* Autoconf Support::
|
* Autoconf Support::
|
||||||
|
|
||||||
Appendices
|
Appendices
|
||||||
|
|
||||||
* Data Representation:: All the details.
|
|
||||||
* GNU Free Documentation License:: The license of this manual.
|
* GNU Free Documentation License:: The license of this manual.
|
||||||
|
|
||||||
Indices
|
Indices
|
||||||
|
@ -252,7 +253,9 @@ different ways to design a program around Guile, or how to embed Guile
|
||||||
into existing programs.
|
into existing programs.
|
||||||
|
|
||||||
There is also a pedagogical yet detailed explanation of how the data
|
There is also a pedagogical yet detailed explanation of how the data
|
||||||
representation of Guile is implemented, @xref{Data Representation}.
|
representation of Guile is implemented, see @ref{Data Representation in
|
||||||
|
Scheme} and @ref{The Libguile Runtime Environment}.
|
||||||
|
|
||||||
You don't need to know the details given there to use Guile from C,
|
You don't need to know the details given there to use Guile from C,
|
||||||
but they are useful when you want to modify Guile itself or when you
|
but they are useful when you want to modify Guile itself or when you
|
||||||
are just curious about how it is all done.
|
are just curious about how it is all done.
|
||||||
|
@ -298,7 +301,7 @@ available through both Scheme and C interfaces.
|
||||||
* Binding Constructs:: Definitions and variable bindings.
|
* Binding Constructs:: Definitions and variable bindings.
|
||||||
* Control Mechanisms:: Controlling the flow of program execution.
|
* Control Mechanisms:: Controlling the flow of program execution.
|
||||||
* Input and Output:: Ports, reading and writing.
|
* Input and Output:: Ports, reading and writing.
|
||||||
* Read/Load/Eval:: Reading and evaluating Scheme code.
|
* Read/Load/Eval/Compile:: Reading and evaluating Scheme code.
|
||||||
* Memory Management:: Memory management and garbage collection.
|
* Memory Management:: Memory management and garbage collection.
|
||||||
* Objects:: Low level object orientation support.
|
* Objects:: Low level object orientation support.
|
||||||
* Modules:: Designing reusable code libraries.
|
* Modules:: Designing reusable code libraries.
|
||||||
|
@ -362,9 +365,45 @@ available through both Scheme and C interfaces.
|
||||||
@include scsh.texi
|
@include scsh.texi
|
||||||
@include scheme-debugging.texi
|
@include scheme-debugging.texi
|
||||||
|
|
||||||
|
@node Guile Implementation
|
||||||
|
@chapter Guile Implementation
|
||||||
|
|
||||||
|
At some point, after one has been programming in Scheme for some time,
|
||||||
|
another level of Scheme comes into view: its implementation. Knowledge
|
||||||
|
of how Scheme can be implemented turns out to be necessary to become
|
||||||
|
an expert hacker. As Peter Norvig notes in his retrospective on
|
||||||
|
PAIP@footnote{PAIP is the common abbreviation for @cite{Paradigms of
|
||||||
|
Artificial Intelligence Programming}, an old but still useful text on
|
||||||
|
Lisp. Norvig's retrospective sums up the lessons of PAIP, and can be
|
||||||
|
found at @uref{http://norvig.com/Lisp-retro.html}.}, ``The expert Lisp
|
||||||
|
programmer eventually develops a good `efficiency model'.''
|
||||||
|
|
||||||
|
By this Norvig means that over time, the Lisp hacker eventually
|
||||||
|
develops an understanding of how much her code ``costs'' in terms of
|
||||||
|
space and time.
|
||||||
|
|
||||||
|
This chapter describes Guile as an implementation of Scheme: its
|
||||||
|
history, how it represents and evaluates its data, and its compiler.
|
||||||
|
This knowledge can help you to make that step from being one who is
|
||||||
|
merely familiar with Scheme to being a real hacker.
|
||||||
|
|
||||||
|
@menu
|
||||||
|
* History:: A brief history of Guile.
|
||||||
|
* Data Representation in Scheme:: Why things aren't just totally
|
||||||
|
straightforward, in general terms.
|
||||||
|
* The Libguile Runtime Environment:: Low-level details on Guile's C
|
||||||
|
runtime library.
|
||||||
|
* A Virtual Machine for Guile:: How compiled procedures work.
|
||||||
|
* Compiling to the Virtual Machine:: Not as hard as you might think.
|
||||||
|
@end menu
|
||||||
|
|
||||||
|
@include history.texi
|
||||||
|
@include data-rep.texi
|
||||||
|
@include vm.texi
|
||||||
|
@include compiler.texi
|
||||||
|
|
||||||
@include autoconf.texi
|
@include autoconf.texi
|
||||||
|
|
||||||
@include data-rep.texi
|
|
||||||
@include fdl.texi
|
@include fdl.texi
|
||||||
|
|
||||||
@iftex
|
@iftex
|
||||||
|
|
285
doc/ref/history.texi
Normal file
285
doc/ref/history.texi
Normal file
|
@ -0,0 +1,285 @@
|
||||||
|
@c -*-texinfo-*-
|
||||||
|
@c This is part of the GNU Guile Reference Manual.
|
||||||
|
@c Copyright (C) 2008
|
||||||
|
@c Free Software Foundation, Inc.
|
||||||
|
@c See the file guile.texi for copying conditions.
|
||||||
|
|
||||||
|
@node History
|
||||||
|
@section A Brief History of Guile
|
||||||
|
|
||||||
|
Guile is an artifact of historical processes, both as code and as a
|
||||||
|
community of hackers. It is sometimes useful to know this history when
|
||||||
|
hacking the source code, to know about past decisions and future
|
||||||
|
directions.
|
||||||
|
|
||||||
|
Of course, the real history of Guile is written by the hackers hacking
|
||||||
|
and not the writers writing, so we round up the section with a note on
|
||||||
|
current status and future directions.
|
||||||
|
|
||||||
|
@menu
|
||||||
|
* The Emacs Thesis::
|
||||||
|
* Early Days::
|
||||||
|
* A Scheme of Many Maintainers::
|
||||||
|
* A Timeline of Selected Guile Releases::
|
||||||
|
* Status::
|
||||||
|
@end menu
|
||||||
|
|
||||||
|
@node The Emacs Thesis
|
||||||
|
@subsection The Emacs Thesis
|
||||||
|
|
||||||
|
The story of Guile is the story of bringing the development experience
|
||||||
|
of Emacs to the mass of programs on a GNU system.
|
||||||
|
|
||||||
|
Emacs, when it was first created in its GNU form in 1984, was a new
|
||||||
|
take on the problem of ``how to make a program''. The Emacs thesis is
|
||||||
|
that it is delightful to create composite programs based on an
|
||||||
|
orthogonal kernel written in a low-level language together with a
|
||||||
|
powerful, high-level extension language.
|
||||||
|
|
||||||
|
Extension languages foster extensible programs, programs which adapt
|
||||||
|
readily to different users and to changing times. Proof of this can be
|
||||||
|
seen in Emacs' current and continued existence, spanning more than a
|
||||||
|
quarter-century.
|
||||||
|
|
||||||
|
Besides providing for modification of a program by others, extension
|
||||||
|
languages are good for @emph{intension} as well. Programs built in
|
||||||
|
``the Emacs way'' are pleasurable and easy for their authors to flesh
|
||||||
|
out with the features that they need.
|
||||||
|
|
||||||
|
After the Emacs experience was appreciated more widely, a number of
|
||||||
|
hackers started to consider how to spread this experience to the rest
|
||||||
|
of the GNU system. It was clear that the easiest way to Emacsify a
|
||||||
|
program would be to embed a shared language implementation into it.
|
||||||
|
|
||||||
|
@node Early Days
|
||||||
|
@subsection Early Days
|
||||||
|
|
||||||
|
Tom Lord was the first to fully concentrate his efforts on an
|
||||||
|
embeddable language runtime, which he named ``GEL'', the GNU Extension
|
||||||
|
Language.
|
||||||
|
|
||||||
|
GEL was the product of converting SCM, Aubrey Jaffer's implementation
|
||||||
|
of Scheme, into something more appropriate to embedding as a library.
|
||||||
|
(SCM was itself based on an implementation by George Carrette, SIOD.)
|
||||||
|
|
||||||
|
Lord managed to convince Richard Stallman to dub GEL the official
|
||||||
|
extension language for the GNU project. It was a natural fit, given
|
||||||
|
that Scheme was a cleaner, more modern Lisp than Emacs Lisp. Part of
|
||||||
|
the argument was that eventually when GEL became more capable, it
|
||||||
|
could gain the ability to execute other languages, especially Emacs
|
||||||
|
Lisp.
|
||||||
|
|
||||||
|
Due to a naming conflict with another programming language, Jim Blandy
|
||||||
|
suggested a new name for GEL: ``Guile''. Besides being a recursive
|
||||||
|
acroymn, ``Guile'' craftily follows the naming of its ancestors,
|
||||||
|
``Planner'', ``Conniver'', and ``Schemer''. (The latter was truncated
|
||||||
|
to ``Scheme'' due to a 6-character file name limit on an old operating
|
||||||
|
system.) Finally, ``Guile'' suggests ``guy-ell'', or ``Guy L.
|
||||||
|
Steele'', who, together with Gerald Sussman, originally discovered
|
||||||
|
Scheme.
|
||||||
|
|
||||||
|
Around the same time that Guile (then GEL) was readying itself for
|
||||||
|
public release, another extension language was gaining in popularity,
|
||||||
|
Tcl. Many developers found advantages in Tcl because of its shell-like
|
||||||
|
syntax and its well-developed graphical widgets library, Tk. Also, at
|
||||||
|
the time there was a large marketing push promoting Tcl as a
|
||||||
|
``universal extension language''.
|
||||||
|
|
||||||
|
Richard Stallman, as the primary author of GNU Emacs, had a particular
|
||||||
|
vision of what extension languages should be, and Tcl did not seem to
|
||||||
|
him to be as capable as Emacs Lisp. He posted a criticism to the
|
||||||
|
comp.lang.tcl newsgroup, sparking one of the internet's legendary
|
||||||
|
flamewars. As part of these discussions, retrospectively dubbed the
|
||||||
|
``Tcl Wars'', he announced the Free Software Foundation's intent to
|
||||||
|
promote Guile as the extension language for the GNU project.
|
||||||
|
|
||||||
|
It is a common misconception that Guile was created as a reaction to
|
||||||
|
Tcl. While it is true that the public announcement of Guile happened
|
||||||
|
at the same time as the ``Tcl wars'', Guile was created out of a
|
||||||
|
condition that existed outside the polemic. Indeed, the need for a
|
||||||
|
powerful language to bridge the gap between extension of existing
|
||||||
|
applications and a more fully dynamic programming environment is still
|
||||||
|
with us today.
|
||||||
|
|
||||||
|
@node A Scheme of Many Maintainers
|
||||||
|
@subsection A Scheme of Many Mantainers
|
||||||
|
|
||||||
|
Surveying the field, it seems that Scheme implementations correspond
|
||||||
|
with their maintainers on an N-to-1 relationship. That is to say, that
|
||||||
|
those people that implement Schemes might do so on a number of
|
||||||
|
occasions, but that the lifetime of a given Scheme is tied to the
|
||||||
|
maintainership of one individual.
|
||||||
|
|
||||||
|
Guile is atypical in this regard.
|
||||||
|
|
||||||
|
Tom Lord maintaned Guile for its first year and a half or so,
|
||||||
|
corresponding to the end of 1994 through the middle of 1996. The
|
||||||
|
releases made in this time constitute an arc from SCM as a standalone
|
||||||
|
program to Guile as a reusable, embeddable library, but passing
|
||||||
|
through a explosion of features: embedded Tcl and Tk, a toolchain for
|
||||||
|
compiling and disassembling Java, addition of a C-like syntax,
|
||||||
|
creation of a module system, and a start at a rich POSIX interface.
|
||||||
|
|
||||||
|
Only some of those features remain in Guile. There were ongoing
|
||||||
|
tensions between providing a small, embeddable language, and one which
|
||||||
|
had all of the features (e.g. a graphical toolkit) that a modern Emacs
|
||||||
|
might need. In the end, as Guile gained in uptake, the development
|
||||||
|
team decided to focus on depth, documentation and orthogonality rather
|
||||||
|
than on breadth. This has been the focus of Guile ever since, although
|
||||||
|
there is a wide range of third-party libraries for Guile.
|
||||||
|
|
||||||
|
Jim Blandy presided over that period of stabilization, in the three
|
||||||
|
years until the end of 1999, when he too moved on to other projects.
|
||||||
|
Since then, Guile has had a group maintainership. The first group was
|
||||||
|
Maciej Stachowiak, Mikael Djurfeldt, and Marius Vollmer, with Vollmer
|
||||||
|
staying on the longest. By late 2007, Vollmer had mostly moved on to
|
||||||
|
other things, so Neil Jerram and Ludovic Courtès stepped up to take on
|
||||||
|
the primary maintenance responsibility.
|
||||||
|
|
||||||
|
Of course, a large part of the actual work on Guile has come from
|
||||||
|
other contributors too numerous to mention, but without whom the world
|
||||||
|
would be a poorer place.
|
||||||
|
|
||||||
|
@node A Timeline of Selected Guile Releases
|
||||||
|
@subsection A Timeline of Selected Guile Releases
|
||||||
|
|
||||||
|
@table @asis
|
||||||
|
@item guile-i --- 4 February 1995
|
||||||
|
SCM, turned into a library.
|
||||||
|
|
||||||
|
@item guile-ii --- 6 April 1995
|
||||||
|
A low-level module system was added. Tcl/Tk support was added,
|
||||||
|
allowing extension of Scheme by Tcl or vice versa. POSIX support was
|
||||||
|
improved, and there was an experimental stab at Java integration.
|
||||||
|
|
||||||
|
@item guile-iii --- 18 August 1995
|
||||||
|
The C-like syntax, ctax, was improved, but mostly this release
|
||||||
|
featured a start at the task of breaking Guile into pieces.
|
||||||
|
|
||||||
|
@item 1.0 --- 5 January 1997
|
||||||
|
@code{#f} was distinguished from @code{'()}. User-level, cooperative
|
||||||
|
multi-threading was added. Source-level debugging became more useful,
|
||||||
|
and programmer's and user's manuals were begun. The module system
|
||||||
|
gained a high-level interface, which is still used today in more or
|
||||||
|
less the same form.
|
||||||
|
|
||||||
|
@item 1.1 --- 16 May 1997
|
||||||
|
@itemx 1.2 --- 24 June 1997
|
||||||
|
Support for Tcl/Tk and ctax were split off as separate packages, and
|
||||||
|
have remained there since. Guile became more compatible with SCSH, and
|
||||||
|
more useful as a UNIX scripting language. Libguile can now be built as
|
||||||
|
a shared library, and third-party extensions written in C became
|
||||||
|
loadable via dynamic linking.
|
||||||
|
|
||||||
|
@item 1.3.0 --- 19 October 1998
|
||||||
|
Command-line editing became much more pleasant through the use of the
|
||||||
|
readline library. The initial support for internationalization via
|
||||||
|
multi-byte strings was removed, and has yet to be added back, though
|
||||||
|
UTF-8 hacks are common. Modules gained the ability to have custom
|
||||||
|
expanders, which is still used for syntax-case macros. Initial Emacs
|
||||||
|
Lisp support landed, ports gained better support for file descriptors,
|
||||||
|
and fluids were added.
|
||||||
|
|
||||||
|
@item 1.3.2 --- 20 August 1999
|
||||||
|
@itemx 1.3.4 --- 25 September 1999
|
||||||
|
@itemx 1.4 --- 21 June 2000
|
||||||
|
A long list of lispy features were added: hooks, Common Lisp's
|
||||||
|
@code{format}, optional and keyword procedure arguments,
|
||||||
|
@code{getopt-long}, sorting, random numbers, and many other fixes and
|
||||||
|
enhancements. Guile now has an interactive debugger, interactive help,
|
||||||
|
and gives better backtraces.
|
||||||
|
|
||||||
|
@item 1.6 --- 6 September 2002
|
||||||
|
Guile gained support for the R5RS standard, and added a number of SRFI
|
||||||
|
modules. The module system was expanded with programmatic support for
|
||||||
|
identifier selection and renaming. The GOOPS object system was merged
|
||||||
|
into Guile core.
|
||||||
|
|
||||||
|
@item 1.8 --- 20 February 2006
|
||||||
|
Guile's arbitrary-precision arithmetic switched to use the GMP
|
||||||
|
library, and added support for exact rationals. Guile's embedded
|
||||||
|
user-space threading was removed in favor of POSIX pre-emptive
|
||||||
|
threads, providing true multiprocessing. Gettext support was added,
|
||||||
|
and Guile's C API was cleaned up and orthogonalized in a massive way.
|
||||||
|
|
||||||
|
@item 2.0 --- thus far, only unstable snapshots available
|
||||||
|
A virtual machine was added to Guile, along with the associated
|
||||||
|
compiler and toolchain. Support for internationalization was added.
|
||||||
|
Running Guile instances became controllable and debuggable from within
|
||||||
|
Emacs, via GDS, which was also backported to 1.8.5. An SRFI-18
|
||||||
|
interface to multithreading was added, including thread cancellation.
|
||||||
|
@end table
|
||||||
|
|
||||||
|
@node Status
|
||||||
|
@subsection Status, or: Your Help Needed
|
||||||
|
|
||||||
|
Guile has achieved much of what it set out to achieve, but there is
|
||||||
|
much remaining to do.
|
||||||
|
|
||||||
|
There is still the old problem of bringing existing applications into
|
||||||
|
a more Emacs-like experience. Guile has had some successes in this
|
||||||
|
respect, but still most applications in the GNU system are without
|
||||||
|
Guile integration.
|
||||||
|
|
||||||
|
Getting Guile to those applications takes an investment, the
|
||||||
|
``hacktivation energy'' needed to wire Guile into a program that only
|
||||||
|
pays off once it is good enough to enable new kinds of behavior. This
|
||||||
|
would be a great way for new hackers to contribute: take an
|
||||||
|
application that you use and that you know well, think of something
|
||||||
|
that it can't yet do, and figure out a way to integrate Guile and
|
||||||
|
implement that task in Guile.
|
||||||
|
|
||||||
|
With time, perhaps this exposure can reverse itself, whereby programs
|
||||||
|
can run under Guile instead of vice versa, eventually resulting in the
|
||||||
|
Emacsification of the entire GNU system. Indeed, this is the reason
|
||||||
|
for the naming of the many Guile modules that live in the @code{ice-9}
|
||||||
|
namespace, a nod to the fictional substance in Kurt Vonnegut's
|
||||||
|
novel, Cat's Cradle, capable of acting as a seed crystal to
|
||||||
|
crystallize the mass of software.
|
||||||
|
|
||||||
|
Implicit to this whole discussion is the idea that dynamic languages
|
||||||
|
are somehow better than languages like C. While languages like C have
|
||||||
|
their place, Guile's take on this question is that yes, Scheme is more
|
||||||
|
expressive than C, and more fun to write. This realization carries an
|
||||||
|
imperative with it to write as much code in Scheme as possible rather
|
||||||
|
than in other languages.
|
||||||
|
|
||||||
|
These days it is possible to write extensible applications almost
|
||||||
|
entirely from high-level languages, through byte-code and native
|
||||||
|
compilation, speed gains in the underlying hardware, and foreign call
|
||||||
|
interfaces in the high-level language. Smalltalk systems are like
|
||||||
|
this, as are Common Lisp-based systems. While there already are a
|
||||||
|
number of pure-Guile applications out there, users still need to drop
|
||||||
|
down to C for some tasks: interfacing to system libraries that don't
|
||||||
|
have prebuilt Guile interfaces, and for some tasks requiring high
|
||||||
|
performance.
|
||||||
|
|
||||||
|
The addition of the virtual machine in Guile 2.0, together with the
|
||||||
|
compiler infrastructure, should go a long way to addressing the speed
|
||||||
|
issues. But there is much optimization to be done. Interested
|
||||||
|
contributors will find lots of delightful low-hanging fruit, from
|
||||||
|
simple profile-driven optimization to hacking a just-in-time compiler
|
||||||
|
from VM bytecode to native code.
|
||||||
|
|
||||||
|
Still, even with an all-Guile application, sometimes you want to
|
||||||
|
provide an opportunity for users to extend your program from a
|
||||||
|
language with a syntax that is closer to C, or to Python. Another
|
||||||
|
interesting idea to consider is compiling e.g. Python to Guile. It's
|
||||||
|
not that far-fetched of an idea: see for example IronPython or JRuby.
|
||||||
|
|
||||||
|
And then there's Emacs itself. Though there is a somewhat-working
|
||||||
|
Emacs Lisp translator for Guile, it cannot yet execute all of Emacs
|
||||||
|
Lisp. A serious integration of Guile with Emacs would replace the
|
||||||
|
Elisp virtual machine with Guile, and provide the necessary C shims so
|
||||||
|
that Guile could emulate Emacs' C API. This would give lots of
|
||||||
|
exciting things to Emacs: native threads, a real object system, more
|
||||||
|
sophisticated types, cleaner syntax, and access to all of the Guile
|
||||||
|
extensions.
|
||||||
|
|
||||||
|
Finally, there is another axis of crystallization, the axis between
|
||||||
|
different Scheme implementations. Guile does not yet support the
|
||||||
|
latest Scheme standard, R6RS, and should do so. Like all standards,
|
||||||
|
R6RS is imperfect, but supporting it will allow more code to run on
|
||||||
|
Guile without modification, and will allow Guile hackers to produce
|
||||||
|
code compatible with other schemes. Help in this regard would be much
|
||||||
|
appreciated.
|
|
@ -153,8 +153,8 @@ that have been added to Guile by third-party libraries.
|
||||||
|
|
||||||
Also, computing with @code{SCM} is not necessarily inefficient. Small
|
Also, computing with @code{SCM} is not necessarily inefficient. Small
|
||||||
integers will be encoded directly in the @code{SCM} value, for example,
|
integers will be encoded directly in the @code{SCM} value, for example,
|
||||||
and do not need any additional memory on the heap. See @ref{Data
|
and do not need any additional memory on the heap. See @ref{The
|
||||||
Representation} to find out the details.
|
Libguile Runtime Environment} to find out the details.
|
||||||
|
|
||||||
Some special @code{SCM} values are available to C code without needing
|
Some special @code{SCM} values are available to C code without needing
|
||||||
to convert them from C values:
|
to convert them from C values:
|
||||||
|
@ -170,8 +170,8 @@ In addition to @code{SCM}, Guile also defines the related type
|
||||||
@code{scm_t_bits}. This is an unsigned integral type of sufficient
|
@code{scm_t_bits}. This is an unsigned integral type of sufficient
|
||||||
size to hold all information that is directly contained in a
|
size to hold all information that is directly contained in a
|
||||||
@code{SCM} value. The @code{scm_t_bits} type is used internally by
|
@code{SCM} value. The @code{scm_t_bits} type is used internally by
|
||||||
Guile to do all the bit twiddling explained in @ref{Data
|
Guile to do all the bit twiddling explained in @ref{The Libguile
|
||||||
Representation}, but you will encounter it occasionally in low-level
|
Runtime Environment}, but you will encounter it occasionally in low-level
|
||||||
user code as well.
|
user code as well.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -517,10 +517,10 @@ Smobs are called smob because they are small: they normally have only
|
||||||
room for one @code{void*} or @code{SCM} value plus 16 bits. The
|
room for one @code{void*} or @code{SCM} value plus 16 bits. The
|
||||||
reason for this is that smobs are directly implemented by using the
|
reason for this is that smobs are directly implemented by using the
|
||||||
low-level, two-word cells of Guile that are also used to implement
|
low-level, two-word cells of Guile that are also used to implement
|
||||||
pairs, for example. (@pxref{Data Representation} for the details.)
|
pairs, for example. (@pxref{The Libguile Runtime Environment} for the
|
||||||
One word of the two-word cells is used for @code{SCM_SMOB_DATA} (or
|
details.) One word of the two-word cells is used for
|
||||||
@code{SCM_SMOB_OBJECT}), the other contains the 16-bit type tag and
|
@code{SCM_SMOB_DATA} (or @code{SCM_SMOB_OBJECT}), the other contains
|
||||||
the 16 extra bits.
|
the 16-bit type tag and the 16 extra bits.
|
||||||
|
|
||||||
In addition to the fundamental two-word cells, Guile also has
|
In addition to the fundamental two-word cells, Guile also has
|
||||||
four-word cells, which are appropriately called @dfn{double cells}.
|
four-word cells, which are appropriately called @dfn{double cells}.
|
||||||
|
|
919
doc/ref/vm.texi
Normal file
919
doc/ref/vm.texi
Normal file
|
@ -0,0 +1,919 @@
|
||||||
|
@c -*-texinfo-*-
|
||||||
|
@c This is part of the GNU Guile Reference Manual.
|
||||||
|
@c Copyright (C) 2008,2009
|
||||||
|
@c Free Software Foundation, Inc.
|
||||||
|
@c See the file guile.texi for copying conditions.
|
||||||
|
|
||||||
|
@node A Virtual Machine for Guile
|
||||||
|
@section A Virtual Machine for Guile
|
||||||
|
|
||||||
|
Guile has both an interpreter and a compiler. To a user, the
|
||||||
|
difference is largely transparent---interpreted and compiled
|
||||||
|
procedures can call each other as they please.
|
||||||
|
|
||||||
|
The difference is that the compiler creates and interprets bytecode
|
||||||
|
for a custom virtual machine, instead of interpreting the
|
||||||
|
S-expressions directly. Running compiled code is faster than running
|
||||||
|
interpreted code.
|
||||||
|
|
||||||
|
The virtual machine that does the bytecode interpretation is a part of
|
||||||
|
Guile itself. This section describes the nature of Guile's virtual
|
||||||
|
machine.
|
||||||
|
|
||||||
|
@menu
|
||||||
|
* Why a VM?::
|
||||||
|
* VM Concepts::
|
||||||
|
* Stack Layout::
|
||||||
|
* Variables and the VM::
|
||||||
|
* VM Programs::
|
||||||
|
* Instruction Set::
|
||||||
|
@end menu
|
||||||
|
|
||||||
|
@node Why a VM?
|
||||||
|
@subsection Why a VM?
|
||||||
|
|
||||||
|
@cindex interpreter
|
||||||
|
@cindex evaluator
|
||||||
|
For a long time, Guile only had an interpreter, called the
|
||||||
|
@dfn{evaluator}. Guile's evaluator operates directly on the
|
||||||
|
S-expression representation of Scheme source code.
|
||||||
|
|
||||||
|
But while the evaluator is highly optimized and hand-tuned, and
|
||||||
|
contains some extensive speed trickery (@pxref{Memoization}), it still
|
||||||
|
performs many needless computations during the course of evaluating an
|
||||||
|
expression. For example, application of a function to arguments
|
||||||
|
needlessly conses up the arguments in a list. Evaluation of an
|
||||||
|
expression always has to figure out what the car of the expression is
|
||||||
|
-- a procedure, a memoized form, or something else. All values have to
|
||||||
|
be allocated on the heap. Et cetera.
|
||||||
|
|
||||||
|
The solution to this problem is to compile the higher-level language,
|
||||||
|
Scheme, into a lower-level language for which all of the checks and
|
||||||
|
dispatching have already been done---the code is instead stripped to
|
||||||
|
the bare minimum needed to ``do the job''.
|
||||||
|
|
||||||
|
The question becomes then, what low-level language to choose? There
|
||||||
|
are many options. We could compile to native code directly, but that
|
||||||
|
poses portability problems for Guile, as it is a highly cross-platform
|
||||||
|
project.
|
||||||
|
|
||||||
|
So we want the performance gains that compilation provides, but we
|
||||||
|
also want to maintain the portability benefits of a single code path.
|
||||||
|
The obvious solution is to compile to a virtual machine that is
|
||||||
|
present on all Guile installations.
|
||||||
|
|
||||||
|
The easiest (and most fun) way to depend on a virtual machine is to
|
||||||
|
implement the virtual machine within Guile itself. This way the
|
||||||
|
virtual machine provides what Scheme needs (tail calls, multiple
|
||||||
|
values, @code{call/cc}) and can provide optimized inline instructions
|
||||||
|
for Guile (@code{cons}, @code{struct-ref}, etc.).
|
||||||
|
|
||||||
|
So this is what Guile does. The rest of this section describes that VM
|
||||||
|
that Guile implements, and the compiled procedures that run on it.
|
||||||
|
|
||||||
|
Note that this decision to implement a bytecode compiler does not
|
||||||
|
preclude native compilation. We can compile from bytecode to native
|
||||||
|
code at runtime, or even do ahead of time compilation. More
|
||||||
|
possibilities are discussed in @ref{Extending the Compiler}.
|
||||||
|
|
||||||
|
@node VM Concepts
|
||||||
|
@subsection VM Concepts
|
||||||
|
|
||||||
|
A virtual machine (VM) is a Scheme object. Users may create virtual
|
||||||
|
machines using the standard procedures described later in this manual,
|
||||||
|
but that is usually unnecessary, as Guile ensures that there is one
|
||||||
|
virtual machine per thread. When a VM-compiled procedure is run, Guile
|
||||||
|
looks up the virtual machine for the current thread and executes the
|
||||||
|
procedure using that VM.
|
||||||
|
|
||||||
|
Guile's virtual machine is a stack machine---that is, it has few
|
||||||
|
registers, and the instructions defined in the VM operate by pushing
|
||||||
|
and popping values from a stack.
|
||||||
|
|
||||||
|
Stack memory is exclusive to the virtual machine that owns it. In
|
||||||
|
addition to their stacks, virtual machines also have access to the
|
||||||
|
global memory (modules, global bindings, etc) that is shared among
|
||||||
|
other parts of Guile, including other VMs.
|
||||||
|
|
||||||
|
A VM has generic instructions, such as those to reference local
|
||||||
|
variables, and instructions designed to support Guile's languages --
|
||||||
|
mathematical instructions that support the entire numerical tower, an
|
||||||
|
inlined implementation of @code{cons}, etc.
|
||||||
|
|
||||||
|
The registers that a VM has are as follows:
|
||||||
|
|
||||||
|
@itemize
|
||||||
|
@item ip - Instruction pointer
|
||||||
|
@item sp - Stack pointer
|
||||||
|
@item fp - Frame pointer
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
In other architectures, the instruction pointer is sometimes called
|
||||||
|
the ``program counter'' (pc). This set of registers is pretty typical
|
||||||
|
for stack machines; their exact meanings in the context of Guile's VM
|
||||||
|
is described in the next section.
|
||||||
|
|
||||||
|
A virtual machine executes by loading a compiled procedure, and
|
||||||
|
executing the object code associated with that procedure. Of course,
|
||||||
|
that procedure may call other procedures, tail-call others, ad
|
||||||
|
infinitum---indeed, within a guile whose modules have all been
|
||||||
|
compiled to object code, one might never leave the virtual machine.
|
||||||
|
|
||||||
|
@c wingo: I wish the following were true, but currently we just use
|
||||||
|
@c the one engine. This kind of thing is possible tho.
|
||||||
|
|
||||||
|
@c A VM may have one of three engines: reckless, regular, or debugging.
|
||||||
|
@c Reckless engine is fastest but dangerous. Regular engine is normally
|
||||||
|
@c fail-safe and reasonably fast. Debugging engine is safest and
|
||||||
|
@c functional but very slow.
|
||||||
|
|
||||||
|
@node Stack Layout
|
||||||
|
@subsection Stack Layout
|
||||||
|
|
||||||
|
While not strictly necessary to understand how to work with the VM, it
|
||||||
|
is instructive and sometimes entertaining to consider the struture of
|
||||||
|
the VM stack.
|
||||||
|
|
||||||
|
Logically speaking, a VM stack is composed of ``frames''. Each frame
|
||||||
|
corresponds to the application of one compiled procedure, and contains
|
||||||
|
storage space for arguments, local variables, intermediate values, and
|
||||||
|
some bookkeeping information (such as what to do after the frame
|
||||||
|
computes its value).
|
||||||
|
|
||||||
|
While the compiler is free to do whatever it wants to, as long as the
|
||||||
|
semantics of a computation are preserved, in practice every time you
|
||||||
|
call a function, a new frame is created. (The notable exception of
|
||||||
|
course is the tail call case, @pxref{Tail Calls}.)
|
||||||
|
|
||||||
|
Within a frame, you have the data associated with the function
|
||||||
|
application itself, which is of a fixed size, and the stack space for
|
||||||
|
intermediate values. Sometimes only the former is referred to as the
|
||||||
|
``frame'', and the latter is the ``stack'', although all pending
|
||||||
|
application frames can have some intermediate computations interleaved
|
||||||
|
on the stack.
|
||||||
|
|
||||||
|
The structure of the fixed part of an application frame is as follows:
|
||||||
|
|
||||||
|
@example
|
||||||
|
Stack
|
||||||
|
| | <- fp + bp->nargs + bp->nlocs + 4
|
||||||
|
+------------------+ = SCM_FRAME_UPPER_ADDRESS (fp)
|
||||||
|
| Return address |
|
||||||
|
| MV return address|
|
||||||
|
| Dynamic link |
|
||||||
|
| External link | <- fp + bp->nargs + bp->nlocs
|
||||||
|
| Local variable 1 | = SCM_FRAME_DATA_ADDRESS (fp)
|
||||||
|
| Local variable 0 | <- fp + bp->nargs
|
||||||
|
| Argument 1 |
|
||||||
|
| Argument 0 | <- fp
|
||||||
|
| Program | <- fp - 1
|
||||||
|
+------------------+ = SCM_FRAME_LOWER_ADDRESS (fp)
|
||||||
|
| |
|
||||||
|
@end example
|
||||||
|
|
||||||
|
In the above drawing, the stack grows upward. The intermediate values
|
||||||
|
stored in the application of this frame are stored above
|
||||||
|
@code{SCM_FRAME_UPPER_ADDRESS (fp)}. @code{bp} refers to the
|
||||||
|
@code{struct scm_program*} data associated with the program at
|
||||||
|
@code{fp - 1}. @code{nargs} and @code{nlocs} are properties of the
|
||||||
|
compiled procedure, which will be discussed later.
|
||||||
|
|
||||||
|
The individual fields of the frame are as follows:
|
||||||
|
|
||||||
|
@table @asis
|
||||||
|
@item Return address
|
||||||
|
The @code{ip} that was in effect before this program was applied. When
|
||||||
|
we return from this activation frame, we will jump back to this
|
||||||
|
@code{ip}.
|
||||||
|
|
||||||
|
@item MV return address
|
||||||
|
The @code{ip} to return to if this application returns multiple
|
||||||
|
values. For continuations that only accept one value, this value will
|
||||||
|
be @code{NULL}; for others, it will be an @code{ip} that points to a
|
||||||
|
multiple-value return address in the calling code. That code will
|
||||||
|
expect the top value on the stack to be an integer---the number of
|
||||||
|
values being returned---and that below that integer there are the
|
||||||
|
values being returned.
|
||||||
|
|
||||||
|
@item Dynamic link
|
||||||
|
This is the @code{fp} in effect before this program was applied. In
|
||||||
|
effect, this and the return address are the registers that are always
|
||||||
|
``saved''.
|
||||||
|
|
||||||
|
@item External link
|
||||||
|
This field is a reference to the list of heap-allocated variables
|
||||||
|
associated with this frame. For a discussion of heap versus stack
|
||||||
|
allocation, @xref{Variables and the VM}.
|
||||||
|
|
||||||
|
@item Local variable @var{n}
|
||||||
|
Lambda-local variables that are allocated on the stack are all
|
||||||
|
allocated as part of the frame. This makes access to non-captured,
|
||||||
|
non-mutated variables very cheap.
|
||||||
|
|
||||||
|
@item Argument @var{n}
|
||||||
|
The calling convention of the VM requires arguments of a function
|
||||||
|
application to be pushed on the stack, and here they are. Normally
|
||||||
|
references to arguments dispatch to these locations on the stack.
|
||||||
|
However if an argument has to be stored on the heap, it will be copied
|
||||||
|
from its initial value here onto a location in the heap, and
|
||||||
|
thereafter only referenced on the heap.
|
||||||
|
|
||||||
|
@item Program
|
||||||
|
This is the program being applied. For more information on how
|
||||||
|
programs are implemented, @xref{VM Programs}.
|
||||||
|
@end table
|
||||||
|
|
||||||
|
@node Variables and the VM
|
||||||
|
@subsection Variables and the VM
|
||||||
|
|
||||||
|
Let's think about the following Scheme code as an example:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(define (foo a)
|
||||||
|
(lambda (b) (list foo a b)))
|
||||||
|
@end example
|
||||||
|
|
||||||
|
Within the lambda expression, "foo" is a top-level variable, "a" is a
|
||||||
|
lexically captured variable, and "b" is a local variable.
|
||||||
|
|
||||||
|
That is to say: @code{b} may safely be allocated on the stack, as
|
||||||
|
there is no enclosed procedure that references it, nor is it ever
|
||||||
|
mutated.
|
||||||
|
|
||||||
|
@code{a}, on the other hand, is referenced by an enclosed procedure,
|
||||||
|
that of the lambda. Thus it must be allocated on the heap, as it may
|
||||||
|
(and will) outlive the dynamic extent of the invocation of @code{foo}.
|
||||||
|
|
||||||
|
@code{foo} is a toplevel variable, as mandated by Scheme's semantics:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(define proc (foo 'bar)) ; assuming prev. definition of @code{foo}
|
||||||
|
(define foo 42) ; redefinition
|
||||||
|
(proc 'baz)
|
||||||
|
@result{} (42 bar baz)
|
||||||
|
@end example
|
||||||
|
|
||||||
|
Note that variables that are mutated (via @code{set!}) must be
|
||||||
|
allocated on the heap, even if they are local variables. This is
|
||||||
|
because any called subprocedure might capture the continuation, which
|
||||||
|
would need to capture locations instead of values. Thus perhaps
|
||||||
|
counterintuitively, what would seem ``closer to the metal'', viz
|
||||||
|
@code{set!}, actually forces heap allocation instead of stack
|
||||||
|
allocation.
|
||||||
|
|
||||||
|
@node VM Programs
|
||||||
|
@subsection Compiled Procedures are VM Programs
|
||||||
|
|
||||||
|
By default, when you enter in expressions at Guile's REPL, they are
|
||||||
|
first compiled to VM object code, then that VM object code is executed
|
||||||
|
to produce a value. If the expression evaluates to a procedure, the
|
||||||
|
result of this process is a compiled procedure.
|
||||||
|
|
||||||
|
A compiled procedure is a compound object, consisting of its bytecode,
|
||||||
|
a reference to any captured lexical variables, an object array, and
|
||||||
|
some metadata such as the procedure's arity, name, and documentation.
|
||||||
|
You can pick apart these pieces with the accessors in @code{(system vm
|
||||||
|
program)}. @xref{Compiled Procedures}, for a full API reference.
|
||||||
|
|
||||||
|
@cindex object table
|
||||||
|
The object array of a compiled procedure, also known as the
|
||||||
|
@dfn{object table}, holds all Scheme objects whose values are known
|
||||||
|
not to change across invocations of the procedure: constant strings,
|
||||||
|
symbols, etc. The object table of a program is initialized right
|
||||||
|
before a program is loaded with @code{load-program}.
|
||||||
|
@xref{Loading Instructions}, for more information.
|
||||||
|
|
||||||
|
Variable objects are one such type of constant object: when a global
|
||||||
|
binding is defined, a variable object is associated to it and that
|
||||||
|
object will remain constant over time, even if the value bound to it
|
||||||
|
changes. Therefore, toplevel bindings only need to be looked up once.
|
||||||
|
Thereafter, references to the corresponding toplevel variables from
|
||||||
|
within the program are then performed via the @code{toplevel-ref}
|
||||||
|
instruction, which uses the object vector, and are almost as fast as
|
||||||
|
local variable references.
|
||||||
|
|
||||||
|
We can see how these concepts tie together by disassembling the
|
||||||
|
@code{foo} function to see what is going on:
|
||||||
|
|
||||||
|
@smallexample
|
||||||
|
scheme@@(guile-user)> (define (foo a) (lambda (b) (list foo a b)))
|
||||||
|
scheme@@(guile-user)> ,x foo
|
||||||
|
Disassembly of #<program foo (a)>:
|
||||||
|
|
||||||
|
Bytecode:
|
||||||
|
|
||||||
|
0 (local-ref 0) ;; `a' (arg)
|
||||||
|
2 (external-set 0) ;; `a' (arg)
|
||||||
|
4 (object-ref 0) ;; #<program #(0 28 #f) (b)>
|
||||||
|
6 (make-closure) at (unknown file):0:16
|
||||||
|
7 (return)
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
Disassembly of #<program #(0 28 #f) (b)>:
|
||||||
|
|
||||||
|
Bytecode:
|
||||||
|
|
||||||
|
0 (toplevel-ref 0) ;; `list'
|
||||||
|
2 (toplevel-ref 1) ;; `foo'
|
||||||
|
4 (external-ref 0) ;; (closure variable)
|
||||||
|
6 (local-ref 0) ;; `b' (arg)
|
||||||
|
8 (goto/args 3) at (unknown file):0:28
|
||||||
|
@end smallexample
|
||||||
|
|
||||||
|
At @code{ip} 0 and 2, we do the copy from argument to heap for
|
||||||
|
@code{a}. @code{Ip} 4 loads up the compiled lambda, and then at
|
||||||
|
@code{ip} 6 we make a closure---binding code (from the compiled
|
||||||
|
lambda) with data (the heap-allocated variables). Finally we return
|
||||||
|
the closure.
|
||||||
|
|
||||||
|
The second stanza disassembles the compiled lambda. Toplevel variables
|
||||||
|
are resolved relative to the module that was current when the
|
||||||
|
procedure was created. This lookup occurs lazily, at the first time
|
||||||
|
the variable is actually referenced, and the location of the lookup is
|
||||||
|
cached so that future references are very cheap. @xref{Environment
|
||||||
|
Control Instructions}, for more details.
|
||||||
|
|
||||||
|
Then we see a reference to an external variable, corresponding to
|
||||||
|
@code{a}. The disassembler doesn't have enough information to give a
|
||||||
|
name to that variable, so it just marks it as being a ``closure
|
||||||
|
variable''. Finally we see the reference to @code{b}, then a tail call
|
||||||
|
(@code{goto/args}) with three arguments.
|
||||||
|
|
||||||
|
@node Instruction Set
|
||||||
|
@subsection Instruction Set
|
||||||
|
|
||||||
|
There are about 100 instructions in Guile's virtual machine. These
|
||||||
|
instructions represent atomic units of a program's execution. Ideally,
|
||||||
|
they perform one task without conditional branches, then dispatch to
|
||||||
|
the next instruction in the stream.
|
||||||
|
|
||||||
|
Instructions themselves are one byte long. Some instructions take
|
||||||
|
parameters, which follow the instruction byte in the instruction
|
||||||
|
stream.
|
||||||
|
|
||||||
|
Sometimes the compiler can figure out that it is compiling a special
|
||||||
|
case that can be run more efficiently. So, for example, while Guile
|
||||||
|
offers a generic test-and-branch instruction, it also offers specific
|
||||||
|
instructions for special cases, so that the following cases all have
|
||||||
|
their own test-and-branch instructions:
|
||||||
|
|
||||||
|
@example
|
||||||
|
(if pred then else)
|
||||||
|
(if (not pred) then else)
|
||||||
|
(if (null? l) then else)
|
||||||
|
(if (not (null? l)) then else)
|
||||||
|
@end example
|
||||||
|
|
||||||
|
In addition, some Scheme primitives have their own inline
|
||||||
|
implementations, e.g. @code{cons}.
|
||||||
|
|
||||||
|
So Guile's instruction set is a @emph{complete} instruction set, in
|
||||||
|
that it provides the instructions that are suited to the problem, and
|
||||||
|
is not concerned with making a minimal, orthogonal set of
|
||||||
|
instructions. More instructions may be added over time.
|
||||||
|
|
||||||
|
@menu
|
||||||
|
* Environment Control Instructions::
|
||||||
|
* Branch Instructions::
|
||||||
|
* Loading Instructions::
|
||||||
|
* Procedural Instructions::
|
||||||
|
* Data Control Instructions::
|
||||||
|
* Miscellaneous Instructions::
|
||||||
|
* Inlined Scheme Instructions::
|
||||||
|
* Inlined Mathematical Instructions::
|
||||||
|
@end menu
|
||||||
|
|
||||||
|
@node Environment Control Instructions
|
||||||
|
@subsubsection Environment Control Instructions
|
||||||
|
|
||||||
|
These instructions access and mutate the environment of a compiled
|
||||||
|
procedure---the local bindings, the ``external'' bindings, and the
|
||||||
|
toplevel bindings.
|
||||||
|
|
||||||
|
@deffn Instruction local-ref index
|
||||||
|
Push onto the stack the value of the local variable located at
|
||||||
|
@var{index} within the current stack frame.
|
||||||
|
|
||||||
|
Note that arguments and local variables are all in one block. Thus the
|
||||||
|
first argument, if any, is at index 0, and local bindings follow the
|
||||||
|
arguments.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction local-set index
|
||||||
|
Pop the Scheme object located on top of the stack and make it the new
|
||||||
|
value of the local variable located at @var{index} within the current
|
||||||
|
stack frame.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction external-ref index
|
||||||
|
Push the value of the closure variable located at position
|
||||||
|
@var{index} within the program's list of external variables.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction external-set index
|
||||||
|
Pop the Scheme object located on top of the stack and make it the new
|
||||||
|
value of the closure variable located at @var{index} within the
|
||||||
|
program's list of external variables.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
The external variable lookup algorithm should probably be made more
|
||||||
|
efficient in the future via addressing by frame and index. Currently,
|
||||||
|
external variables are all consed onto a list, which results in O(N)
|
||||||
|
lookup time.
|
||||||
|
|
||||||
|
@deffn Instruction externals
|
||||||
|
Pushes the current list of external variables onto the stack. This
|
||||||
|
instruction is used in the implementation of
|
||||||
|
@code{compile-time-environment}. @xref{The Scheme Compiler}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction toplevel-ref index
|
||||||
|
Push the value of the toplevel binding whose location is stored in at
|
||||||
|
position @var{index} in the object table.
|
||||||
|
|
||||||
|
Initially, a cell in the object table that is used by
|
||||||
|
@code{toplevel-ref} is initialized to one of two forms. The normal
|
||||||
|
case is that the cell holds a symbol, whose binding will be looked up
|
||||||
|
relative to the module that was current when the current program was
|
||||||
|
created.
|
||||||
|
|
||||||
|
Alternately, the lookup may be performed relative to a particular
|
||||||
|
module, determined at compile-time (e.g. via @code{@@} or
|
||||||
|
@code{@@@@}). In that case, the cell in the object table holds a list:
|
||||||
|
@code{(@var{modname} @var{sym} @var{interface?})}. The symbol
|
||||||
|
@var{sym} will be looked up in the module named @var{modname} (a list
|
||||||
|
of symbols). The lookup will be performed against the module's public
|
||||||
|
interface, unless @var{interface?} is @code{#f}, which it is for
|
||||||
|
example when compiling @code{@@@@}.
|
||||||
|
|
||||||
|
In any case, if the symbol is unbound, an error is signalled.
|
||||||
|
Otherwise the initial form is replaced with the looked-up variable, an
|
||||||
|
in-place mutation of the object table. This mechanism provides for
|
||||||
|
lazy variable resolution, and an important cached fast-path once the
|
||||||
|
variable has been successfully resolved.
|
||||||
|
|
||||||
|
This instruction pushes the value of the variable onto the stack.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction toplevel-ref index
|
||||||
|
Pop a value off the stack, and set it as the value of the toplevel
|
||||||
|
variable stored at @var{index} in the object table. If the variable
|
||||||
|
has not yet been looked up, we do the lookup as in
|
||||||
|
@code{toplevel-ref}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction link-now
|
||||||
|
Pop a value, @var{x}, from the stack. Look up the binding for @var{x},
|
||||||
|
according to the rules for @code{toplevel-ref}, and push that variable
|
||||||
|
on the stack. If the lookup fails, an error will be signalled.
|
||||||
|
|
||||||
|
This instruction is mostly used when loading programs, because it can
|
||||||
|
do toplevel variable lookups without an object vector.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction variable-ref
|
||||||
|
Dereference the variable object which is on top of the stack and
|
||||||
|
replace it by the value of the variable it represents.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction variable-set
|
||||||
|
Pop off two objects from the stack, a variable and a value, and set
|
||||||
|
the variable to the value.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction object-ref n
|
||||||
|
Push @var{n}th value from the current program's object vector.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@node Branch Instructions
|
||||||
|
@subsubsection Branch Instructions
|
||||||
|
|
||||||
|
All the conditional branch instructions described below work in the
|
||||||
|
same way:
|
||||||
|
|
||||||
|
@itemize
|
||||||
|
@item They pop off the Scheme object located on the stack and use it as
|
||||||
|
the branch condition;
|
||||||
|
@item If the condition is true, then the instruction pointer is
|
||||||
|
increased by the offset passed as an argument to the branch
|
||||||
|
instruction;
|
||||||
|
@item Program execution proceeds with the next instruction (that is,
|
||||||
|
the one to which the instruction pointer points).
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
Note that the offset passed to the instruction is encoded on two 8-bit
|
||||||
|
integers which are then combined by the VM as one 16-bit integer.
|
||||||
|
|
||||||
|
@deffn Instruction br offset
|
||||||
|
Jump to @var{offset}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction br-if offset
|
||||||
|
Jump to @var{offset} if the condition on the stack is not false.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction br-if-not offset
|
||||||
|
Jump to @var{offset} if the condition on the stack is false.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction br-if-eq offset
|
||||||
|
Jump to @var{offset} if the two objects located on the stack are
|
||||||
|
equal in the sense of @var{eq?}. Note that, for this instruction, the
|
||||||
|
stack pointer is decremented by two Scheme objects instead of only
|
||||||
|
one.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction br-if-not-eq offset
|
||||||
|
Same as @var{br-if-eq} for non-@code{eq?} objects.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction br-if-null offset
|
||||||
|
Jump to @var{offset} if the object on the stack is @code{'()}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction br-if-not-null offset
|
||||||
|
Jump to @var{offset} if the object on the stack is not @code{'()}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
|
||||||
|
@node Loading Instructions
|
||||||
|
@subsubsection Loading Instructions
|
||||||
|
|
||||||
|
In addition to VM instructions, an instruction stream may contain
|
||||||
|
variable-length data embedded within it. This data is always preceded
|
||||||
|
by special loading instructions, which interpret the data and advance
|
||||||
|
the instruction pointer to the next VM instruction.
|
||||||
|
|
||||||
|
All of these loading instructions have a @code{length} parameter,
|
||||||
|
indicating the size of the embedded data, in bytes. The length itself
|
||||||
|
may be encoded in 1, 2, or 4 bytes.
|
||||||
|
|
||||||
|
@deffn Instruction load-integer length
|
||||||
|
@deffnx Instruction load-unsigned-integer length
|
||||||
|
Load a 32-bit integer (respectively unsigned integer) from the
|
||||||
|
instruction stream.
|
||||||
|
@end deffn
|
||||||
|
@deffn Instruction load-number length
|
||||||
|
Load an arbitrary number from the instruction stream. The number is
|
||||||
|
embedded in the stream as a string.
|
||||||
|
@end deffn
|
||||||
|
@deffn Instruction load-string length
|
||||||
|
Load a string from the instruction stream.
|
||||||
|
@end deffn
|
||||||
|
@deffn Instruction load-symbol length
|
||||||
|
Load a symbol from the instruction stream.
|
||||||
|
@end deffn
|
||||||
|
@deffn Instruction load-keyword length
|
||||||
|
Load a keyword from the instruction stream.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction define length
|
||||||
|
Load a symbol from the instruction stream, and look up its binding in
|
||||||
|
the current toplevel environment, creating the binding if necessary.
|
||||||
|
Push the variable corresponding to the binding.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction load-program length
|
||||||
|
Load bytecode from the instruction stream, and push a compiled
|
||||||
|
procedure. This instruction pops the following values from the stack:
|
||||||
|
|
||||||
|
@itemize
|
||||||
|
@item Optionally, a thunk, which when called should return metadata
|
||||||
|
associated with this program---for example its name, the names of its
|
||||||
|
arguments, its documentation string, debugging information, etc.
|
||||||
|
|
||||||
|
Normally, this thunk its itself a compiled procedure (with no
|
||||||
|
metadata). Metadata is represented this way so that the initial load
|
||||||
|
of a procedure is fast: the VM just mmap's the thunk and goes. The
|
||||||
|
symbols and pairs associated with the metadata are only created if the
|
||||||
|
user asks for them.
|
||||||
|
|
||||||
|
For information on the format of the thunk's return value,
|
||||||
|
@xref{Compiled Procedures}.
|
||||||
|
@item Optionally, the program's object table, as a vector.
|
||||||
|
|
||||||
|
A program that does not reference toplevel bindings and does not use
|
||||||
|
@code{object-ref} does not need an object table.
|
||||||
|
@item Finally, either one immediate integer or four immediate integers
|
||||||
|
representing the arity of the program.
|
||||||
|
|
||||||
|
In the four-fixnum case, the values are respectively the number of
|
||||||
|
arguments taken by the function (@var{nargs}), the number of @dfn{rest
|
||||||
|
arguments} (@var{nrest}, 0 or 1), the number of local variables
|
||||||
|
(@var{nlocs}) and the number of external variables (@var{nexts})
|
||||||
|
(@pxref{Environment Control Instructions}).
|
||||||
|
|
||||||
|
The common single-fixnum case represents all of these values within a
|
||||||
|
16-bit bitmask.
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
The resulting compiled procedure will not have any ``external''
|
||||||
|
variables captured, so it will be loaded only once but may be used
|
||||||
|
many times to create closures.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
Finally, while this instruction is not strictly a ``loading''
|
||||||
|
instruction, it's useful to wind up the @code{load-program} discussion
|
||||||
|
here:
|
||||||
|
|
||||||
|
@deffn Instruction make-closure
|
||||||
|
Pop the program object from the stack, capture the current set of
|
||||||
|
``external'' variables, and assign those external variables to a copy
|
||||||
|
of the program. Push the new program object, which shares state with
|
||||||
|
the original program. Also captures the current module.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@node Procedural Instructions
|
||||||
|
@subsubsection Procedural Instructions
|
||||||
|
|
||||||
|
@deffn Instruction return
|
||||||
|
Free the program's frame, returning the top value from the stack to
|
||||||
|
the current continuation. (The stack should have exactly one value on
|
||||||
|
it.)
|
||||||
|
|
||||||
|
Specifically, the @code{sp} is decremented to one below the current
|
||||||
|
@code{fp}, the @code{ip} is reset to the current return address, the
|
||||||
|
@code{fp} is reset to the value of the current dynamic link, and then
|
||||||
|
the top item on the stack (formerly the procedure being applied) is
|
||||||
|
set to the returned value.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction call nargs
|
||||||
|
Call the procedure located at @code{sp[-nargs]} with the @var{nargs}
|
||||||
|
arguments located from @code{sp[0]} to @code{sp[-nargs + 1]}.
|
||||||
|
|
||||||
|
For non-compiled procedures (continuations, primitives, and
|
||||||
|
interpreted procedures), @code{call} will pop the procedure and
|
||||||
|
arguments off the stack, and push the result of calling
|
||||||
|
@code{scm_apply}.
|
||||||
|
|
||||||
|
For compiled procedures, this instruction sets up a new stack frame,
|
||||||
|
as described in @ref{Stack Layout}, and then dispatches to the first
|
||||||
|
instruction in the called procedure, relying on the called procedure
|
||||||
|
to return one value to the newly-created continuation.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction goto/args nargs
|
||||||
|
Like @code{call}, but reusing the current continuation. This
|
||||||
|
instruction implements tail calling as required by RnRS.
|
||||||
|
|
||||||
|
For compiled procedures, that means that @code{goto/args} reuses the
|
||||||
|
current frame instead of building a new one. The @code{goto/*}
|
||||||
|
instruction family is named as it is because tail calls are equivalent
|
||||||
|
to @code{goto}, along with relabeled variables.
|
||||||
|
|
||||||
|
For non-VM procedures, the result is the same, but the current VM
|
||||||
|
invocation remains on the C stack. True tail calls are not currently
|
||||||
|
possible between compiled and non-compiled procedures.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction apply nargs
|
||||||
|
@deffnx Instruction goto/apply nargs
|
||||||
|
Like @code{call} and @code{goto/args}, except that the top item on the
|
||||||
|
stack must be a list. The elements of that list are then pushed on the
|
||||||
|
stack and treated as additional arguments, replacing the list itself,
|
||||||
|
then the procedure is invoked as usual.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction call/nargs
|
||||||
|
@deffnx Instruction goto/nargs
|
||||||
|
These are like @code{call} and @code{goto/args}, except they take the
|
||||||
|
number of arguments from the stack instead of the instruction stream.
|
||||||
|
These instructions are used in the implementation of multiple value
|
||||||
|
returns, where the actual number of values is pushed on the stack.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction call/cc
|
||||||
|
@deffnx Instruction goto/cc
|
||||||
|
Capture the current continuation, and then call (or tail-call) the
|
||||||
|
procedure on the top of the stack, with the continuation as the
|
||||||
|
argument.
|
||||||
|
|
||||||
|
Both the VM continuation and the C continuation are captured.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction mv-call nargs offset
|
||||||
|
Like @code{call}, except that a multiple-value continuation is created
|
||||||
|
in addition to a single-value continuation.
|
||||||
|
|
||||||
|
The offset (a two-byte value) is an offset within the instruction
|
||||||
|
stream; the multiple-value return address in the new frame
|
||||||
|
(@pxref{Stack Layout}) will be set to the normal return address plus
|
||||||
|
this offset. Instructions at that offset will expect the top value of
|
||||||
|
the stack to be the number of values, and below that values
|
||||||
|
themselves, pushed separately.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction return/values nvalues
|
||||||
|
Return the top @var{nvalues} to the current continuation.
|
||||||
|
|
||||||
|
If the current continuation is a multiple-value continuation,
|
||||||
|
@code{return/values} pushes the number of values on the stack, then
|
||||||
|
returns as in @code{return}, but to the multiple-value return address.
|
||||||
|
|
||||||
|
Otherwise if the current continuation accepts only one value, i.e. the
|
||||||
|
multiple-value return address is @code{NULL}, then we assume the user
|
||||||
|
only wants one value, and we give them the first one. If there are no
|
||||||
|
values, an error is signaled.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction return/values* nvalues
|
||||||
|
Like a combination of @code{apply} and @code{return/values}, in which
|
||||||
|
the top value on the stack is interpreted as a list of additional
|
||||||
|
values. This is an optimization for the common @code{(apply values
|
||||||
|
...)} case.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction truncate-values nbinds nrest
|
||||||
|
Used in multiple-value continuations, this instruction takes the
|
||||||
|
values that are on the stack (including the number-of-value marker)
|
||||||
|
and truncates them for a binding construct.
|
||||||
|
|
||||||
|
For example, a call to @code{(receive (x y . z) (foo) ...)} would,
|
||||||
|
logically speaking, pop off the values returned from @code{(foo)} and
|
||||||
|
push them as three values, corresponding to @code{x}, @code{y}, and
|
||||||
|
@code{z}. In that case, @var{nbinds} would be 3, and @var{nrest} would
|
||||||
|
be 1 (to indicate that one of the bindings was a rest arguments).
|
||||||
|
|
||||||
|
Signals an error if there is an insufficient number of values.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@node Data Control Instructions
|
||||||
|
@subsubsection Data Control Instructions
|
||||||
|
|
||||||
|
These instructions push simple immediate values onto the stack, or
|
||||||
|
manipulate lists and vectors on the stack.
|
||||||
|
|
||||||
|
@deffn Instruction make-int8 value
|
||||||
|
Push @var{value}, an 8-bit integer, onto the stack.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction make-int8:0
|
||||||
|
Push the immediate value @code{0} onto the stack.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction make-int8:1
|
||||||
|
Push the immediate value @code{1} onto the stack.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction make-int16 value
|
||||||
|
Push @var{value}, a 16-bit integer, onto the stack.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction make-false
|
||||||
|
Push @code{#f} onto the stack.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction make-true
|
||||||
|
Push @code{#t} onto the stack.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction make-eol
|
||||||
|
Push @code{'()} onto the stack.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction make-char8 value
|
||||||
|
Push @var{value}, an 8-bit character, onto the stack.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction list n
|
||||||
|
Pops off the top @var{n} values off of the stack, consing them up into
|
||||||
|
a list, then pushes that list on the stack. What was the topmost value
|
||||||
|
will be the last element in the list.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction vector n
|
||||||
|
Create and fill a vector with the top @var{n} values from the stack,
|
||||||
|
popping off those values and pushing on the resulting vector.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction mark
|
||||||
|
Pushes a special value onto the stack that other stack instructions
|
||||||
|
like @code{list-mark} can use.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction list-mark
|
||||||
|
Create a list from values from the stack, as in @code{list}, but
|
||||||
|
instead of knowing beforehand how many there will be, keep going until
|
||||||
|
we see a @code{mark} value.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction cons-mark
|
||||||
|
As the scheme procedure @code{cons*} is to the scheme procedure
|
||||||
|
@code{list}, so the instruction @code{cons-mark} is to the instruction
|
||||||
|
@code{list-mark}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction vector-mark
|
||||||
|
Like @code{list-mark}, but makes a vector instead of a list.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction list-break
|
||||||
|
The opposite of @code{list}: pops a value, which should be a list, and
|
||||||
|
pushes its elements on the stack.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@node Miscellaneous Instructions
|
||||||
|
@subsubsection Miscellaneous Instructions
|
||||||
|
|
||||||
|
@deffn Instruction nop
|
||||||
|
Does nothing!
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction halt
|
||||||
|
Exits the VM, returning a SCM value. Normally, this instruction is
|
||||||
|
only part of the ``bootstrap program'', a program run when a virtual
|
||||||
|
machine is first entered; compiled Scheme procedures will not contain
|
||||||
|
this instruction.
|
||||||
|
|
||||||
|
If multiple values have been returned, the SCM value will be a
|
||||||
|
multiple-values object (@pxref{Multiple Values}).
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction break
|
||||||
|
Does nothing, but invokes the break hook.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction drop
|
||||||
|
Pops off the top value from the stack, throwing it away.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction dup
|
||||||
|
Re-pushes the top value onto the stack.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn Instruction void
|
||||||
|
Pushes ``the unspecified value'' onto the stack.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@node Inlined Scheme Instructions
|
||||||
|
@subsubsection Inlined Scheme Instructions
|
||||||
|
|
||||||
|
The Scheme compiler can recognize the application of standard Scheme
|
||||||
|
procedures, or unbound variables that look like they are bound to
|
||||||
|
standard Scheme procedures. It tries to inline these small operations
|
||||||
|
to avoid the overhead of creating new stack frames.
|
||||||
|
|
||||||
|
Since most of these operations are historically implemented as C
|
||||||
|
primitives, not inlining them would entail constantly calling out from
|
||||||
|
the VM to the interpreter, which has some costs---registers must be
|
||||||
|
saved, the interpreter has to dispatch, called procedures have to do
|
||||||
|
much typechecking, etc. It's much more efficient to inline these
|
||||||
|
operations in the virtual machine itself.
|
||||||
|
|
||||||
|
All of these instructions pop their arguments from the stack and push
|
||||||
|
their results, and take no parameters from the instruction stream.
|
||||||
|
Thus, unlike in the previous sections, these instruction definitions
|
||||||
|
show stack parameters instead of parameters from the instruction
|
||||||
|
stream.
|
||||||
|
|
||||||
|
@deffn Instruction not x
|
||||||
|
@deffnx Instruction not-not x
|
||||||
|
@deffnx Instruction eq? x y
|
||||||
|
@deffnx Instruction not-eq? x y
|
||||||
|
@deffnx Instruction null?
|
||||||
|
@deffnx Instruction not-null?
|
||||||
|
@deffnx Instruction eqv? x y
|
||||||
|
@deffnx Instruction equal? x y
|
||||||
|
@deffnx Instruction pair? x y
|
||||||
|
@deffnx Instruction list? x y
|
||||||
|
@deffnx Instruction set-car! pair x
|
||||||
|
@deffnx Instruction set-cdr! pair x
|
||||||
|
@deffnx Instruction slot-ref struct n
|
||||||
|
@deffnx Instruction slot-set struct n x
|
||||||
|
@deffnx Instruction cons x
|
||||||
|
@deffnx Instruction car x
|
||||||
|
@deffnx Instruction cdr x
|
||||||
|
Inlined implementations of their Scheme equivalents.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
Note that @code{caddr} and friends compile to a series of @code{car}
|
||||||
|
and @code{cdr} instructions.
|
||||||
|
|
||||||
|
@node Inlined Mathematical Instructions
|
||||||
|
@subsubsection Inlined Mathematical Instructions
|
||||||
|
|
||||||
|
Inlining mathematical operations has the obvious advantage of handling
|
||||||
|
fixnums without function calls or allocations. The trick, of course,
|
||||||
|
is knowing when the result of an operation will be a fixnum, and there
|
||||||
|
might be a couple bugs here.
|
||||||
|
|
||||||
|
More instructions could be added here over time.
|
||||||
|
|
||||||
|
As in the previous section, the definitions below show stack
|
||||||
|
parameters instead of instruction stream parameters.
|
||||||
|
|
||||||
|
@deffn Instruction add x y
|
||||||
|
@deffnx Instruction sub x y
|
||||||
|
@deffnx Instruction mul x y
|
||||||
|
@deffnx Instruction div x y
|
||||||
|
@deffnx Instruction quo x y
|
||||||
|
@deffnx Instruction rem x y
|
||||||
|
@deffnx Instruction mod x y
|
||||||
|
@deffnx Instruction ee? x y
|
||||||
|
@deffnx Instruction lt? x y
|
||||||
|
@deffnx Instruction gt? x y
|
||||||
|
@deffnx Instruction le? x y
|
||||||
|
@deffnx Instruction ge? x y
|
||||||
|
Inlined implementations of the corresponding mathematical operations.
|
||||||
|
@end deffn
|
8962
doc/texinfo.tex
Normal file
8962
doc/texinfo.tex
Normal file
File diff suppressed because it is too large
Load diff
38
gdb-pre-inst-guile.in
Normal file
38
gdb-pre-inst-guile.in
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Copyright (C) 2002, 2006, 2008 Free Software Foundation
|
||||||
|
#
|
||||||
|
# This file is part of GUILE.
|
||||||
|
#
|
||||||
|
# GUILE 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.
|
||||||
|
#
|
||||||
|
# GUILE 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 GUILE; see the file COPYING. If not, write
|
||||||
|
# to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
||||||
|
# Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
# Commentary:
|
||||||
|
|
||||||
|
# Usage: gdb-pre-inst-guile [ARGS]
|
||||||
|
#
|
||||||
|
# This script runs Guile from the build tree under GDB. See
|
||||||
|
# ./pre-inst-guile for more information.
|
||||||
|
#
|
||||||
|
# In addition to running ./gdb-pre-inst-guile, sometimes it's useful to
|
||||||
|
# run e.g. ./check-guile -i ./gdb-pre-inst-guile foo.test.
|
||||||
|
|
||||||
|
# Code:
|
||||||
|
|
||||||
|
set -e
|
||||||
|
# env (set by configure)
|
||||||
|
top_builddir="@top_builddir_absolute@"
|
||||||
|
exec ${top_builddir}/pre-inst-guile-env libtool --mode=execute \
|
||||||
|
gdb --args ${top_builddir}/libguile/guile "$@"
|
202
gdbinit
Normal file
202
gdbinit
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
# -*- GDB-Script -*-
|
||||||
|
|
||||||
|
define newline
|
||||||
|
call (void)scm_newline (scm_current_error_port ())
|
||||||
|
end
|
||||||
|
|
||||||
|
define pp
|
||||||
|
call (void)scm_call_1 (scm_variable_ref (scm_c_module_lookup (scm_c_resolve_module ("ice-9 pretty-print"), "pretty-print")), $arg0)
|
||||||
|
end
|
||||||
|
|
||||||
|
define gdisplay
|
||||||
|
call (void)scm_display ($arg0, scm_current_error_port ())
|
||||||
|
newline
|
||||||
|
end
|
||||||
|
|
||||||
|
define gwrite
|
||||||
|
call (void)scm_write ($arg0, scm_current_error_port ())
|
||||||
|
newline
|
||||||
|
end
|
||||||
|
|
||||||
|
define sputs
|
||||||
|
call (void)scm_puts ($arg0, scm_current_error_port ())
|
||||||
|
end
|
||||||
|
|
||||||
|
define gslot
|
||||||
|
print ((SCM**)$arg0)[1][$arg1]
|
||||||
|
end
|
||||||
|
|
||||||
|
define pslot
|
||||||
|
gslot $arg0 $arg1
|
||||||
|
gwrite $
|
||||||
|
end
|
||||||
|
|
||||||
|
define lforeach
|
||||||
|
set $l=$arg0
|
||||||
|
while $l != 0x404
|
||||||
|
set $x=scm_car($l)
|
||||||
|
$arg1 $x
|
||||||
|
set $l = scm_cdr($l)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define modsum
|
||||||
|
modname $arg0
|
||||||
|
gslot $arg0 1
|
||||||
|
set $uses=$
|
||||||
|
output "uses:\n"
|
||||||
|
lforeach $uses modname
|
||||||
|
end
|
||||||
|
|
||||||
|
define moduses
|
||||||
|
pslot $arg0 1
|
||||||
|
end
|
||||||
|
|
||||||
|
define modname
|
||||||
|
pslot $arg0 5
|
||||||
|
end
|
||||||
|
|
||||||
|
define modkind
|
||||||
|
pslot $arg0 6
|
||||||
|
end
|
||||||
|
|
||||||
|
define car
|
||||||
|
call scm_car ($arg0)
|
||||||
|
end
|
||||||
|
|
||||||
|
define cdr
|
||||||
|
call scm_cdr ($arg0)
|
||||||
|
end
|
||||||
|
|
||||||
|
define smobwordtox
|
||||||
|
set $x=((SCM*)$arg0)[$arg1]
|
||||||
|
end
|
||||||
|
|
||||||
|
define smobdatatox
|
||||||
|
smobwordtox $arg0 1
|
||||||
|
end
|
||||||
|
|
||||||
|
define program_objcode
|
||||||
|
smobdatatox $arg0
|
||||||
|
set $objcode=$x
|
||||||
|
smobdatatox $objcode
|
||||||
|
p *(struct scm_objcode*)$x
|
||||||
|
end
|
||||||
|
|
||||||
|
define proglocals
|
||||||
|
set $i=bp->nlocs
|
||||||
|
while $i > 0
|
||||||
|
set $i=$i-1
|
||||||
|
gwrite fp[bp->nargs+$i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define progstack
|
||||||
|
set $x=sp
|
||||||
|
while $x > stack_base
|
||||||
|
gwrite *$x
|
||||||
|
set $x=$x-1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define tc16
|
||||||
|
p ((scm_t_bits)$arg0) & 0xffff
|
||||||
|
end
|
||||||
|
|
||||||
|
define smobdescriptor
|
||||||
|
p scm_smobs[0xff & (((scm_t_bits)$arg0) >> 8)]
|
||||||
|
end
|
||||||
|
|
||||||
|
define vmstackinit
|
||||||
|
set $vmsp=sp
|
||||||
|
set $vmstack_base=stack_base
|
||||||
|
set $vmfp=fp
|
||||||
|
set $vmbp=bp
|
||||||
|
set $vmframe=0
|
||||||
|
end
|
||||||
|
|
||||||
|
define nextframe
|
||||||
|
set $orig_vmsp=$vmsp
|
||||||
|
while $vmsp > $vmstack_base
|
||||||
|
output $orig_vmsp - $vmsp
|
||||||
|
sputs "\t"
|
||||||
|
output $vmsp
|
||||||
|
sputs "\t"
|
||||||
|
gwrite *$vmsp
|
||||||
|
set $vmsp=$vmsp-1
|
||||||
|
end
|
||||||
|
newline
|
||||||
|
sputs "Frame "
|
||||||
|
output $vmframe
|
||||||
|
newline
|
||||||
|
sputs "ra:\t"
|
||||||
|
output $vmsp
|
||||||
|
sputs "\t"
|
||||||
|
output (SCM*)*$vmsp
|
||||||
|
set $vmsp=$vmsp-1
|
||||||
|
newline
|
||||||
|
sputs "mvra:\t"
|
||||||
|
output $vmsp
|
||||||
|
sputs "\t"
|
||||||
|
output (SCM*)*$vmsp
|
||||||
|
set $vmsp=$vmsp-1
|
||||||
|
newline
|
||||||
|
sputs "dl:\t"
|
||||||
|
output $vmsp
|
||||||
|
sputs "\t"
|
||||||
|
set $vmdl=(SCM*)(*$vmsp)
|
||||||
|
output $vmdl
|
||||||
|
newline
|
||||||
|
set $vmsp=$vmsp-1
|
||||||
|
sputs "el:\t"
|
||||||
|
output $vmsp
|
||||||
|
sputs "\t"
|
||||||
|
gwrite *$vmsp
|
||||||
|
set $vmsp=$vmsp-1
|
||||||
|
set $vmnlocs=(int)$vmbp->nlocs
|
||||||
|
while $vmnlocs > 0
|
||||||
|
sputs "loc #"
|
||||||
|
output $vmnlocs
|
||||||
|
sputs ":\t"
|
||||||
|
output $vmsp
|
||||||
|
sputs "\t"
|
||||||
|
gwrite *$vmsp
|
||||||
|
set $vmsp=$vmsp-1
|
||||||
|
set $vmnlocs=$vmnlocs-1
|
||||||
|
end
|
||||||
|
set $vmnargs=(int)$vmbp->nargs
|
||||||
|
while $vmnargs > 0
|
||||||
|
sputs "arg #"
|
||||||
|
output $vmnargs
|
||||||
|
sputs ":\t"
|
||||||
|
output $vmsp
|
||||||
|
sputs "\t"
|
||||||
|
gwrite *$vmsp
|
||||||
|
set $vmsp=$vmsp-1
|
||||||
|
set $vmnargs=$vmnargs-1
|
||||||
|
end
|
||||||
|
sputs "prog:\t"
|
||||||
|
output $vmsp
|
||||||
|
sputs "\t"
|
||||||
|
gwrite *$vmsp
|
||||||
|
set $vmsp=$vmsp-1
|
||||||
|
newline
|
||||||
|
if $vmdl
|
||||||
|
set $vmfp=$vmdl
|
||||||
|
set $vmbp=(struct scm_objcode*)((SCM*)(((SCM*)($vmfp[-1]))[1])[1])
|
||||||
|
set $vmstack_base=$vmfp+$vmbp->nargs+$vmbp->nlocs+4
|
||||||
|
set $vmframe=$vmframe+1
|
||||||
|
newline
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define vmstack
|
||||||
|
vmstackinit
|
||||||
|
while $vmsp > vp->stack_base
|
||||||
|
nextframe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
define inst
|
||||||
|
p scm_instruction_table[$arg0]
|
||||||
|
end
|
|
@ -215,7 +215,7 @@
|
||||||
(set-buffered-input-continuation?! (readline-port) #f)
|
(set-buffered-input-continuation?! (readline-port) #f)
|
||||||
(set-readline-prompt! repl-prompt "... ")
|
(set-readline-prompt! repl-prompt "... ")
|
||||||
(set-readline-read-hook! repl-read-hook))
|
(set-readline-read-hook! repl-read-hook))
|
||||||
(lambda () (read))
|
(lambda () ((or (fluid-ref current-reader) read)))
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(set-readline-prompt! outer-new-input-prompt outer-continuation-prompt)
|
(set-readline-prompt! outer-new-input-prompt outer-continuation-prompt)
|
||||||
(set-readline-read-hook! outer-read-hook))))))
|
(set-readline-read-hook! outer-read-hook))))))
|
||||||
|
|
|
@ -48,10 +48,13 @@ pkgdatadir="@datadir@/@PACKAGE@"
|
||||||
guileversion="@GUILE_EFFECTIVE_VERSION@"
|
guileversion="@GUILE_EFFECTIVE_VERSION@"
|
||||||
default_scriptsdir=$pkgdatadir/$guileversion/scripts
|
default_scriptsdir=$pkgdatadir/$guileversion/scripts
|
||||||
|
|
||||||
|
top_srcdir="@top_srcdir_absolute@"
|
||||||
|
top_builddir="@top_builddir_absolute@"
|
||||||
|
|
||||||
# pre-install invocation frob
|
# pre-install invocation frob
|
||||||
mydir=`dirname $0`
|
mydir=$(cd $(dirname $0) && pwd)
|
||||||
if [ -d "$mydir/scripts" -a -f "$mydir/scripts/Makefile.am" ] ; then
|
if [ "$mydir" = "$top_builddir" ] ; then
|
||||||
default_scriptsdir=`(cd $mydir/scripts ; pwd)`
|
default_scriptsdir=$top_srcdir/scripts
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# option processing -- basically, you can override either the script dir
|
# option processing -- basically, you can override either the script dir
|
||||||
|
|
File diff suppressed because one or more lines are too long
1
libguile/.gitignore
vendored
1
libguile/.gitignore
vendored
|
@ -13,3 +13,4 @@ guile_filter_doc_snarfage
|
||||||
libpath.h
|
libpath.h
|
||||||
scmconfig.h
|
scmconfig.h
|
||||||
version.h
|
version.h
|
||||||
|
vm-i-*.i
|
||||||
|
|
|
@ -85,7 +85,7 @@ c-tokenize.$(OBJEXT): c-tokenize.c
|
||||||
if [ "$(cross_compiling)" = "yes" ]; then \
|
if [ "$(cross_compiling)" = "yes" ]; then \
|
||||||
$(CC_FOR_BUILD) $(DEFS) $(AM_CPPFLAGS) -c -o $@ $<; \
|
$(CC_FOR_BUILD) $(DEFS) $(AM_CPPFLAGS) -c -o $@ $<; \
|
||||||
else \
|
else \
|
||||||
$(COMPILE) -c -o $@ $<; \
|
$(filter-out -Werror,$(COMPILE)) -c -o $@ $<; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
## Override default rule; this should run on BUILD host.
|
## Override default rule; this should run on BUILD host.
|
||||||
|
@ -121,6 +121,9 @@ libguile_la_SOURCES = alist.c arbiters.c async.c backtrace.c boolean.c \
|
||||||
throw.c values.c variable.c vectors.c version.c vports.c weaks.c \
|
throw.c values.c variable.c vectors.c version.c vports.c weaks.c \
|
||||||
ramap.c unif.c
|
ramap.c unif.c
|
||||||
|
|
||||||
|
# vm-related sources
|
||||||
|
libguile_la_SOURCES += frames.c instructions.c objcodes.c programs.c vm.c
|
||||||
|
|
||||||
libguile_i18n_v_@LIBGUILE_I18N_MAJOR@_la_SOURCES = i18n.c
|
libguile_i18n_v_@LIBGUILE_I18N_MAJOR@_la_SOURCES = i18n.c
|
||||||
libguile_i18n_v_@LIBGUILE_I18N_MAJOR@_la_CFLAGS = \
|
libguile_i18n_v_@LIBGUILE_I18N_MAJOR@_la_CFLAGS = \
|
||||||
$(libguile_la_CFLAGS)
|
$(libguile_la_CFLAGS)
|
||||||
|
@ -144,6 +147,9 @@ DOT_X_FILES = alist.x arbiters.x async.x backtrace.x boolean.x chars.x \
|
||||||
strports.x struct.x symbols.x threads.x throw.x values.x \
|
strports.x struct.x symbols.x threads.x throw.x values.x \
|
||||||
variable.x vectors.x version.x vports.x weaks.x ramap.x unif.x
|
variable.x vectors.x version.x vports.x weaks.x ramap.x unif.x
|
||||||
|
|
||||||
|
# vm-related snarfs
|
||||||
|
DOT_X_FILES += frames.x instructions.x objcodes.x programs.x vm.x
|
||||||
|
|
||||||
EXTRA_DOT_X_FILES = @EXTRA_DOT_X_FILES@
|
EXTRA_DOT_X_FILES = @EXTRA_DOT_X_FILES@
|
||||||
|
|
||||||
DOT_DOC_FILES = alist.doc arbiters.doc async.doc backtrace.doc \
|
DOT_DOC_FILES = alist.doc arbiters.doc async.doc backtrace.doc \
|
||||||
|
@ -166,9 +172,14 @@ DOT_DOC_FILES = alist.doc arbiters.doc async.doc backtrace.doc \
|
||||||
|
|
||||||
EXTRA_DOT_DOC_FILES = @EXTRA_DOT_DOC_FILES@
|
EXTRA_DOT_DOC_FILES = @EXTRA_DOT_DOC_FILES@
|
||||||
|
|
||||||
|
DOT_I_FILES = vm-i-system.i vm-i-scheme.i vm-i-loader.i
|
||||||
|
|
||||||
|
.c.i:
|
||||||
|
grep '^VM_DEFINE' $< > $@
|
||||||
|
|
||||||
BUILT_SOURCES = cpp_err_symbols.c cpp_sig_symbols.c libpath.h \
|
BUILT_SOURCES = cpp_err_symbols.c cpp_sig_symbols.c libpath.h \
|
||||||
version.h scmconfig.h \
|
version.h scmconfig.h \
|
||||||
$(DOT_X_FILES) $(EXTRA_DOT_X_FILES)
|
$(DOT_I_FILES) $(DOT_X_FILES) $(EXTRA_DOT_X_FILES)
|
||||||
|
|
||||||
EXTRA_libguile_la_SOURCES = _scm.h \
|
EXTRA_libguile_la_SOURCES = _scm.h \
|
||||||
inet_aton.c memmove.c putenv.c strerror.c \
|
inet_aton.c memmove.c putenv.c strerror.c \
|
||||||
|
@ -196,6 +207,9 @@ noinst_HEADERS = convert.i.c \
|
||||||
win32-uname.h win32-dirent.h win32-socket.h \
|
win32-uname.h win32-dirent.h win32-socket.h \
|
||||||
private-gc.h private-options.h
|
private-gc.h private-options.h
|
||||||
|
|
||||||
|
# vm instructions
|
||||||
|
noinst_HEADERS += vm-engine.c vm-i-system.c vm-i-scheme.c vm-i-loader.c
|
||||||
|
|
||||||
libguile_la_DEPENDENCIES = @LIBLOBJS@
|
libguile_la_DEPENDENCIES = @LIBLOBJS@
|
||||||
libguile_la_LIBADD = @LIBLOBJS@ $(gnulib_library)
|
libguile_la_LIBADD = @LIBLOBJS@ $(gnulib_library)
|
||||||
libguile_la_LDFLAGS = @LTLIBINTL@ -version-info @LIBGUILE_INTERFACE_CURRENT@:@LIBGUILE_INTERFACE_REVISION@:@LIBGUILE_INTERFACE_AGE@ -export-dynamic -no-undefined
|
libguile_la_LDFLAGS = @LTLIBINTL@ -version-info @LIBGUILE_INTERFACE_CURRENT@:@LIBGUILE_INTERFACE_REVISION@:@LIBGUILE_INTERFACE_AGE@ -export-dynamic -no-undefined
|
||||||
|
@ -224,6 +238,9 @@ modinclude_HEADERS = __scm.h alist.h arbiters.h async.h backtrace.h \
|
||||||
pthread-threads.h null-threads.h throw.h unif.h values.h \
|
pthread-threads.h null-threads.h throw.h unif.h values.h \
|
||||||
variable.h vectors.h vports.h weaks.h
|
variable.h vectors.h vports.h weaks.h
|
||||||
|
|
||||||
|
modinclude_HEADERS += vm-bootstrap.h frames.h instructions.h objcodes.h \
|
||||||
|
programs.h vm.h vm-engine.h vm-expand.h
|
||||||
|
|
||||||
nodist_modinclude_HEADERS = version.h scmconfig.h
|
nodist_modinclude_HEADERS = version.h scmconfig.h
|
||||||
|
|
||||||
bin_SCRIPTS = guile-snarf
|
bin_SCRIPTS = guile-snarf
|
||||||
|
|
|
@ -467,8 +467,21 @@ static void
|
||||||
display_backtrace_get_file_line (SCM frame, SCM *file, SCM *line)
|
display_backtrace_get_file_line (SCM frame, SCM *file, SCM *line)
|
||||||
{
|
{
|
||||||
SCM source = SCM_FRAME_SOURCE (frame);
|
SCM source = SCM_FRAME_SOURCE (frame);
|
||||||
*file = SCM_MEMOIZEDP (source) ? scm_source_property (source, scm_sym_filename) : SCM_BOOL_F;
|
*file = *line = SCM_BOOL_F;
|
||||||
*line = (SCM_MEMOIZEDP (source)) ? scm_source_property (source, scm_sym_line) : SCM_BOOL_F;
|
if (SCM_MEMOIZEDP (source))
|
||||||
|
{
|
||||||
|
*file = scm_source_property (source, scm_sym_filename);
|
||||||
|
*line = scm_source_property (source, scm_sym_line);
|
||||||
|
}
|
||||||
|
else if (scm_is_pair (source)
|
||||||
|
&& scm_is_pair (scm_cdr (source))
|
||||||
|
&& scm_is_pair (scm_cddr (source))
|
||||||
|
&& !scm_is_pair (scm_cdddr (source)))
|
||||||
|
{
|
||||||
|
/* (addr . (filename . (line . column))), from vm compilation */
|
||||||
|
*file = scm_cadr (source);
|
||||||
|
*line = scm_caddr (source);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "libguile/dynwind.h"
|
#include "libguile/dynwind.h"
|
||||||
#include "libguile/values.h"
|
#include "libguile/values.h"
|
||||||
#include "libguile/eval.h"
|
#include "libguile/eval.h"
|
||||||
|
#include "libguile/vm.h"
|
||||||
|
|
||||||
#include "libguile/validate.h"
|
#include "libguile/validate.h"
|
||||||
#include "libguile/continuations.h"
|
#include "libguile/continuations.h"
|
||||||
|
@ -91,6 +92,7 @@ scm_make_continuation (int *first)
|
||||||
#endif
|
#endif
|
||||||
continuation->offset = continuation->stack - src;
|
continuation->offset = continuation->stack - src;
|
||||||
memcpy (continuation->stack, src, sizeof (SCM_STACKITEM) * stack_size);
|
memcpy (continuation->stack, src, sizeof (SCM_STACKITEM) * stack_size);
|
||||||
|
continuation->vm_conts = scm_vm_capture_continuations ();
|
||||||
|
|
||||||
*first = !setjmp (continuation->jmpbuf);
|
*first = !setjmp (continuation->jmpbuf);
|
||||||
if (*first)
|
if (*first)
|
||||||
|
@ -169,6 +171,7 @@ copy_stack (void *data)
|
||||||
copy_stack_data *d = (copy_stack_data *)data;
|
copy_stack_data *d = (copy_stack_data *)data;
|
||||||
memcpy (d->dst, d->continuation->stack,
|
memcpy (d->dst, d->continuation->stack,
|
||||||
sizeof (SCM_STACKITEM) * d->continuation->num_stack_items);
|
sizeof (SCM_STACKITEM) * d->continuation->num_stack_items);
|
||||||
|
scm_vm_reinstate_continuations (d->continuation->vm_conts);
|
||||||
#ifdef __ia64__
|
#ifdef __ia64__
|
||||||
SCM_I_CURRENT_THREAD->pending_rbs_continuation = d->continuation;
|
SCM_I_CURRENT_THREAD->pending_rbs_continuation = d->continuation;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -51,6 +51,7 @@ typedef struct
|
||||||
#endif /* __ia64__ */
|
#endif /* __ia64__ */
|
||||||
size_t num_stack_items; /* size of the saved stack. */
|
size_t num_stack_items; /* size of the saved stack. */
|
||||||
SCM root; /* continuation root identifier. */
|
SCM root; /* continuation root identifier. */
|
||||||
|
SCM vm_conts; /* vm continuations (they use separate stacks) */
|
||||||
|
|
||||||
/* The offset from the live stack location to this copy. This is
|
/* The offset from the live stack location to this copy. This is
|
||||||
used to adjust pointers from within the copied stack to the stack
|
used to adjust pointers from within the copied stack to the stack
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include "libguile/root.h"
|
#include "libguile/root.h"
|
||||||
#include "libguile/fluids.h"
|
#include "libguile/fluids.h"
|
||||||
#include "libguile/objects.h"
|
#include "libguile/objects.h"
|
||||||
|
#include "libguile/programs.h"
|
||||||
|
|
||||||
#include "libguile/validate.h"
|
#include "libguile/validate.h"
|
||||||
#include "libguile/debug.h"
|
#include "libguile/debug.h"
|
||||||
|
@ -72,7 +73,9 @@ SCM_DEFINE (scm_debug_options, "debug-options-interface", 0, 1, 0,
|
||||||
SCM_OUT_OF_RANGE (1, setting);
|
SCM_OUT_OF_RANGE (1, setting);
|
||||||
}
|
}
|
||||||
SCM_RESET_DEBUG_MODE;
|
SCM_RESET_DEBUG_MODE;
|
||||||
|
#ifdef STACK_CHECKING
|
||||||
scm_stack_checking_enabled_p = SCM_STACK_CHECKING_P;
|
scm_stack_checking_enabled_p = SCM_STACK_CHECKING_P;
|
||||||
|
#endif
|
||||||
scm_debug_eframe_size = 2 * SCM_N_FRAMES;
|
scm_debug_eframe_size = 2 * SCM_N_FRAMES;
|
||||||
|
|
||||||
scm_dynwind_end ();
|
scm_dynwind_end ();
|
||||||
|
@ -312,6 +315,8 @@ SCM_DEFINE (scm_procedure_name, "procedure-name", 1, 0, 0,
|
||||||
#endif
|
#endif
|
||||||
if (scm_is_false (name) && SCM_CLOSUREP (proc))
|
if (scm_is_false (name) && SCM_CLOSUREP (proc))
|
||||||
name = scm_reverse_lookup (SCM_ENV (proc), proc);
|
name = scm_reverse_lookup (SCM_ENV (proc), proc);
|
||||||
|
if (scm_is_false (name) && SCM_PROGRAM_P (proc))
|
||||||
|
name = scm_program_name (proc);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -440,8 +445,10 @@ scm_reverse_lookup (SCM env, SCM data)
|
||||||
return SCM_BOOL_F;
|
return SCM_BOOL_F;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCM
|
SCM_DEFINE (scm_sys_start_stack, "%start-stack", 2, 0, 0,
|
||||||
scm_start_stack (SCM id, SCM exp, SCM env)
|
(SCM id, SCM thunk),
|
||||||
|
"Call @var{thunk} on an evaluator stack tagged with @var{id}.")
|
||||||
|
#define FUNC_NAME s_scm_sys_start_stack
|
||||||
{
|
{
|
||||||
SCM answer;
|
SCM answer;
|
||||||
scm_t_debug_frame vframe;
|
scm_t_debug_frame vframe;
|
||||||
|
@ -451,27 +458,12 @@ scm_start_stack (SCM id, SCM exp, SCM env)
|
||||||
vframe.vect = &vframe_vect_body;
|
vframe.vect = &vframe_vect_body;
|
||||||
vframe.vect[0].id = id;
|
vframe.vect[0].id = id;
|
||||||
scm_i_set_last_debug_frame (&vframe);
|
scm_i_set_last_debug_frame (&vframe);
|
||||||
answer = scm_i_eval (exp, env);
|
answer = scm_call_0 (thunk);
|
||||||
scm_i_set_last_debug_frame (vframe.prev);
|
scm_i_set_last_debug_frame (vframe.prev);
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCM_SYNTAX(s_start_stack, "start-stack", scm_makacro, scm_m_start_stack);
|
|
||||||
|
|
||||||
static SCM
|
|
||||||
scm_m_start_stack (SCM exp, SCM env)
|
|
||||||
#define FUNC_NAME s_start_stack
|
|
||||||
{
|
|
||||||
exp = SCM_CDR (exp);
|
|
||||||
if (!scm_is_pair (exp)
|
|
||||||
|| !scm_is_pair (SCM_CDR (exp))
|
|
||||||
|| !scm_is_null (SCM_CDDR (exp)))
|
|
||||||
SCM_WRONG_NUM_ARGS ();
|
|
||||||
return scm_start_stack (scm_eval_car (exp, env), SCM_CADR (exp), env);
|
|
||||||
}
|
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
/* {Debug Objects}
|
/* {Debug Objects}
|
||||||
*
|
*
|
||||||
* The debugging evaluator throws these on frame traps.
|
* The debugging evaluator throws these on frame traps.
|
||||||
|
|
|
@ -138,7 +138,7 @@ SCM_API scm_t_bits scm_tc16_memoized;
|
||||||
SCM_API SCM scm_debug_object_p (SCM obj);
|
SCM_API SCM scm_debug_object_p (SCM obj);
|
||||||
SCM_API SCM scm_local_eval (SCM exp, SCM env);
|
SCM_API SCM scm_local_eval (SCM exp, SCM env);
|
||||||
SCM_API SCM scm_reverse_lookup (SCM env, SCM data);
|
SCM_API SCM scm_reverse_lookup (SCM env, SCM data);
|
||||||
SCM_API SCM scm_start_stack (SCM info_id, SCM exp, SCM env);
|
SCM_API SCM scm_sys_start_stack (SCM info_id, SCM thunk);
|
||||||
SCM_API SCM scm_procedure_environment (SCM proc);
|
SCM_API SCM scm_procedure_environment (SCM proc);
|
||||||
SCM_API SCM scm_procedure_source (SCM proc);
|
SCM_API SCM scm_procedure_source (SCM proc);
|
||||||
SCM_API SCM scm_procedure_name (SCM proc);
|
SCM_API SCM scm_procedure_name (SCM proc);
|
||||||
|
|
|
@ -315,7 +315,7 @@ scm_i_dowinds (SCM to, long delta, void (*turn_func) (void *), void *data)
|
||||||
if (SCM_VARIABLEP (SCM_CAR (wind_key)))
|
if (SCM_VARIABLEP (SCM_CAR (wind_key)))
|
||||||
scm_swap_bindings (wind_key, SCM_CDR (wind_elt));
|
scm_swap_bindings (wind_key, SCM_CDR (wind_elt));
|
||||||
}
|
}
|
||||||
else if (SCM_TYP3 (wind_key) == scm_tc3_closure)
|
else if (scm_is_true (scm_thunk_p (wind_key)))
|
||||||
scm_call_0 (wind_key);
|
scm_call_0 (wind_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -351,7 +351,7 @@ scm_i_dowinds (SCM to, long delta, void (*turn_func) (void *), void *data)
|
||||||
if (SCM_VARIABLEP (SCM_CAR (wind_key)))
|
if (SCM_VARIABLEP (SCM_CAR (wind_key)))
|
||||||
scm_swap_bindings (wind_key, SCM_CDR (wind_elt));
|
scm_swap_bindings (wind_key, SCM_CDR (wind_elt));
|
||||||
}
|
}
|
||||||
else if (SCM_TYP3 (wind_key) == scm_tc3_closure)
|
else if (scm_is_true (scm_thunk_p (wind_key)))
|
||||||
scm_call_0 (SCM_CDR (wind_elt));
|
scm_call_0 (SCM_CDR (wind_elt));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
#include "libguile/ports.h"
|
#include "libguile/ports.h"
|
||||||
#include "libguile/print.h"
|
#include "libguile/print.h"
|
||||||
#include "libguile/procprop.h"
|
#include "libguile/procprop.h"
|
||||||
|
#include "libguile/programs.h"
|
||||||
#include "libguile/root.h"
|
#include "libguile/root.h"
|
||||||
#include "libguile/smob.h"
|
#include "libguile/smob.h"
|
||||||
#include "libguile/srcprop.h"
|
#include "libguile/srcprop.h"
|
||||||
|
@ -62,6 +63,7 @@
|
||||||
#include "libguile/validate.h"
|
#include "libguile/validate.h"
|
||||||
#include "libguile/values.h"
|
#include "libguile/values.h"
|
||||||
#include "libguile/vectors.h"
|
#include "libguile/vectors.h"
|
||||||
|
#include "libguile/vm.h"
|
||||||
|
|
||||||
#include "libguile/eval.h"
|
#include "libguile/eval.h"
|
||||||
#include "libguile/private-options.h"
|
#include "libguile/private-options.h"
|
||||||
|
@ -2966,7 +2968,7 @@ scm_t_option scm_debug_opts[] = {
|
||||||
{ SCM_OPTION_BOOLEAN, "backtrace", 0, "Show backtrace on error." },
|
{ SCM_OPTION_BOOLEAN, "backtrace", 0, "Show backtrace on error." },
|
||||||
{ SCM_OPTION_BOOLEAN, "debug", 0, "Use the debugging evaluator." },
|
{ SCM_OPTION_BOOLEAN, "debug", 0, "Use the debugging evaluator." },
|
||||||
|
|
||||||
{ SCM_OPTION_INTEGER, "stack", 20000, "Stack size limit (measured in words; 0 = no check)." },
|
{ SCM_OPTION_INTEGER, "stack", 40000, "Stack size limit (measured in words; 0 = no check)." },
|
||||||
{ SCM_OPTION_SCM, "show-file-name", (unsigned long)SCM_BOOL_T,
|
{ SCM_OPTION_SCM, "show-file-name", (unsigned long)SCM_BOOL_T,
|
||||||
"Show file names and line numbers "
|
"Show file names and line numbers "
|
||||||
"in backtraces when not `#f'. A value of `base' "
|
"in backtraces when not `#f'. A value of `base' "
|
||||||
|
@ -3050,30 +3052,54 @@ SCM_DEFINE (scm_evaluator_traps, "evaluator-traps-interface", 0, 1, 0,
|
||||||
SCM
|
SCM
|
||||||
scm_call_0 (SCM proc)
|
scm_call_0 (SCM proc)
|
||||||
{
|
{
|
||||||
|
if (SCM_PROGRAM_P (proc))
|
||||||
|
return scm_c_vm_run (scm_the_vm (), proc, NULL, 0);
|
||||||
|
else
|
||||||
return scm_apply (proc, SCM_EOL, SCM_EOL);
|
return scm_apply (proc, SCM_EOL, SCM_EOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
SCM
|
SCM
|
||||||
scm_call_1 (SCM proc, SCM arg1)
|
scm_call_1 (SCM proc, SCM arg1)
|
||||||
{
|
{
|
||||||
|
if (SCM_PROGRAM_P (proc))
|
||||||
|
return scm_c_vm_run (scm_the_vm (), proc, &arg1, 1);
|
||||||
|
else
|
||||||
return scm_apply (proc, arg1, scm_listofnull);
|
return scm_apply (proc, arg1, scm_listofnull);
|
||||||
}
|
}
|
||||||
|
|
||||||
SCM
|
SCM
|
||||||
scm_call_2 (SCM proc, SCM arg1, SCM arg2)
|
scm_call_2 (SCM proc, SCM arg1, SCM arg2)
|
||||||
{
|
{
|
||||||
|
if (SCM_PROGRAM_P (proc))
|
||||||
|
{
|
||||||
|
SCM args[] = { arg1, arg2 };
|
||||||
|
return scm_c_vm_run (scm_the_vm (), proc, args, 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
return scm_apply (proc, arg1, scm_cons (arg2, scm_listofnull));
|
return scm_apply (proc, arg1, scm_cons (arg2, scm_listofnull));
|
||||||
}
|
}
|
||||||
|
|
||||||
SCM
|
SCM
|
||||||
scm_call_3 (SCM proc, SCM arg1, SCM arg2, SCM arg3)
|
scm_call_3 (SCM proc, SCM arg1, SCM arg2, SCM arg3)
|
||||||
{
|
{
|
||||||
|
if (SCM_PROGRAM_P (proc))
|
||||||
|
{
|
||||||
|
SCM args[] = { arg1, arg2, arg3 };
|
||||||
|
return scm_c_vm_run (scm_the_vm (), proc, args, 3);
|
||||||
|
}
|
||||||
|
else
|
||||||
return scm_apply (proc, arg1, scm_cons2 (arg2, arg3, scm_listofnull));
|
return scm_apply (proc, arg1, scm_cons2 (arg2, arg3, scm_listofnull));
|
||||||
}
|
}
|
||||||
|
|
||||||
SCM
|
SCM
|
||||||
scm_call_4 (SCM proc, SCM arg1, SCM arg2, SCM arg3, SCM arg4)
|
scm_call_4 (SCM proc, SCM arg1, SCM arg2, SCM arg3, SCM arg4)
|
||||||
{
|
{
|
||||||
|
if (SCM_PROGRAM_P (proc))
|
||||||
|
{
|
||||||
|
SCM args[] = { arg1, arg2, arg3, arg4 };
|
||||||
|
return scm_c_vm_run (scm_the_vm (), proc, args, 4);
|
||||||
|
}
|
||||||
|
else
|
||||||
return scm_apply (proc, arg1, scm_cons2 (arg2, arg3,
|
return scm_apply (proc, arg1, scm_cons2 (arg2, arg3,
|
||||||
scm_cons (arg4, scm_listofnull)));
|
scm_cons (arg4, scm_listofnull)));
|
||||||
}
|
}
|
||||||
|
@ -3663,13 +3689,23 @@ scm_closure (SCM code, SCM env)
|
||||||
|
|
||||||
scm_t_bits scm_tc16_promise;
|
scm_t_bits scm_tc16_promise;
|
||||||
|
|
||||||
SCM
|
SCM_DEFINE (scm_make_promise, "make-promise", 1, 0, 0,
|
||||||
scm_makprom (SCM code)
|
(SCM thunk),
|
||||||
|
"Create a new promise object.\n\n"
|
||||||
|
"@code{make-promise} is a procedural form of @code{delay}.\n"
|
||||||
|
"These two expressions are equivalent:\n"
|
||||||
|
"@lisp\n"
|
||||||
|
"(delay @var{exp})\n"
|
||||||
|
"(make-promise (lambda () @var{exp}))\n"
|
||||||
|
"@end lisp\n")
|
||||||
|
#define FUNC_NAME s_scm_make_promise
|
||||||
{
|
{
|
||||||
|
SCM_VALIDATE_THUNK (1, thunk);
|
||||||
SCM_RETURN_NEWSMOB2 (scm_tc16_promise,
|
SCM_RETURN_NEWSMOB2 (scm_tc16_promise,
|
||||||
SCM_UNPACK (code),
|
SCM_UNPACK (thunk),
|
||||||
scm_make_recursive_mutex ());
|
scm_make_recursive_mutex ());
|
||||||
}
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
|
@ -162,7 +162,7 @@ SCM_API SCM scm_dapply (SCM proc, SCM arg1, SCM args);
|
||||||
SCM_API SCM scm_map (SCM proc, SCM arg1, SCM args);
|
SCM_API SCM scm_map (SCM proc, SCM arg1, SCM args);
|
||||||
SCM_API SCM scm_for_each (SCM proc, SCM arg1, SCM args);
|
SCM_API SCM scm_for_each (SCM proc, SCM arg1, SCM args);
|
||||||
SCM_API SCM scm_closure (SCM code, SCM env);
|
SCM_API SCM scm_closure (SCM code, SCM env);
|
||||||
SCM_API SCM scm_makprom (SCM code);
|
SCM_API SCM scm_make_promise (SCM thunk);
|
||||||
SCM_API SCM scm_force (SCM x);
|
SCM_API SCM scm_force (SCM x);
|
||||||
SCM_API SCM scm_promise_p (SCM x);
|
SCM_API SCM scm_promise_p (SCM x);
|
||||||
SCM_API SCM scm_cons_source (SCM xorig, SCM x, SCM y);
|
SCM_API SCM scm_cons_source (SCM xorig, SCM x, SCM y);
|
||||||
|
|
|
@ -732,7 +732,7 @@ dispatch:
|
||||||
|
|
||||||
|
|
||||||
case (ISYMNUM (SCM_IM_DELAY)):
|
case (ISYMNUM (SCM_IM_DELAY)):
|
||||||
RETURN (scm_makprom (scm_closure (SCM_CDR (x), env)));
|
RETURN (scm_make_promise (scm_closure (SCM_CDR (x), env)));
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
/* See futures.h for a comment why futures are not enabled.
|
/* See futures.h for a comment why futures are not enabled.
|
||||||
|
@ -855,9 +855,12 @@ dispatch:
|
||||||
args = SCM_CDR (args);
|
args = SCM_CDR (args);
|
||||||
z = SCM_CDR (z);
|
z = SCM_CDR (z);
|
||||||
}
|
}
|
||||||
/* Fewer arguments than specifiers => CAR != ENV */
|
/* Fewer arguments than specifiers => CAR != CLASS */
|
||||||
if (scm_is_null (SCM_CAR (z)) || scm_is_pair (SCM_CAR (z)))
|
if (!scm_is_pair (z))
|
||||||
goto apply_cmethod;
|
goto apply_vm_cmethod;
|
||||||
|
else if (!SCM_CLASSP (SCM_CAR (z))
|
||||||
|
&& !scm_is_symbol (SCM_CAR (z)))
|
||||||
|
goto apply_memoized_cmethod;
|
||||||
next_method:
|
next_method:
|
||||||
hash_value = (hash_value + 1) & mask;
|
hash_value = (hash_value + 1) & mask;
|
||||||
} while (hash_value != cache_end_pos);
|
} while (hash_value != cache_end_pos);
|
||||||
|
@ -865,7 +868,15 @@ dispatch:
|
||||||
/* No appropriate method was found in the cache. */
|
/* No appropriate method was found in the cache. */
|
||||||
z = scm_memoize_method (x, arg1);
|
z = scm_memoize_method (x, arg1);
|
||||||
|
|
||||||
apply_cmethod: /* inputs: z, arg1 */
|
if (scm_is_pair (z))
|
||||||
|
goto apply_memoized_cmethod;
|
||||||
|
|
||||||
|
apply_vm_cmethod:
|
||||||
|
proc = z;
|
||||||
|
PREP_APPLY (proc, arg1);
|
||||||
|
goto apply_proc;
|
||||||
|
|
||||||
|
apply_memoized_cmethod: /* inputs: z, arg1 */
|
||||||
{
|
{
|
||||||
SCM formals = SCM_CMETHOD_FORMALS (z);
|
SCM formals = SCM_CMETHOD_FORMALS (z);
|
||||||
env = SCM_EXTEND_ENV (formals, arg1, SCM_CMETHOD_ENV (z));
|
env = SCM_EXTEND_ENV (formals, arg1, SCM_CMETHOD_ENV (z));
|
||||||
|
|
316
libguile/frames.c
Normal file
316
libguile/frames.c
Normal file
|
@ -0,0 +1,316 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
#if HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "vm-bootstrap.h"
|
||||||
|
#include "frames.h"
|
||||||
|
|
||||||
|
|
||||||
|
scm_t_bits scm_tc16_vm_frame;
|
||||||
|
|
||||||
|
#define RELOC(frame, val) (val + SCM_VM_FRAME_OFFSET (frame))
|
||||||
|
|
||||||
|
SCM
|
||||||
|
scm_c_make_vm_frame (SCM stack_holder, SCM *fp, SCM *sp,
|
||||||
|
scm_byte_t *ip, scm_t_ptrdiff offset)
|
||||||
|
{
|
||||||
|
struct scm_vm_frame *p = scm_gc_malloc (sizeof (struct scm_vm_frame),
|
||||||
|
"vmframe");
|
||||||
|
p->stack_holder = stack_holder;
|
||||||
|
p->fp = fp;
|
||||||
|
p->sp = sp;
|
||||||
|
p->ip = ip;
|
||||||
|
p->offset = offset;
|
||||||
|
SCM_RETURN_NEWSMOB (scm_tc16_vm_frame, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vm_frame_print (SCM frame, SCM port, scm_print_state *pstate)
|
||||||
|
{
|
||||||
|
scm_puts ("#<vm-frame ", port);
|
||||||
|
scm_uintprint (SCM_UNPACK (frame), 16, port);
|
||||||
|
scm_putc (' ', port);
|
||||||
|
scm_write (scm_vm_frame_program (frame), port);
|
||||||
|
/* don't write args, they can get us into trouble. */
|
||||||
|
scm_puts (">", port);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
vm_frame_mark (SCM obj)
|
||||||
|
{
|
||||||
|
return SCM_VM_FRAME_STACK_HOLDER (obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static scm_sizet
|
||||||
|
vm_frame_free (SCM obj)
|
||||||
|
{
|
||||||
|
struct scm_vm_frame *p = SCM_VM_FRAME_DATA (obj);
|
||||||
|
scm_gc_free (p, sizeof(struct scm_vm_frame), "vmframe");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scheme interface */
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_frame_p, "vm-frame?", 1, 0, 0,
|
||||||
|
(SCM obj),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_frame_p
|
||||||
|
{
|
||||||
|
return SCM_BOOL (SCM_VM_FRAME_P (obj));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_frame_program, "vm-frame-program", 1, 0, 0,
|
||||||
|
(SCM frame),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_frame_program
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_VM_FRAME (1, frame);
|
||||||
|
return SCM_FRAME_PROGRAM (SCM_VM_FRAME_FP (frame));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_frame_arguments, "vm-frame-arguments", 1, 0, 0,
|
||||||
|
(SCM frame),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_frame_arguments
|
||||||
|
{
|
||||||
|
SCM *fp;
|
||||||
|
int i;
|
||||||
|
struct scm_objcode *bp;
|
||||||
|
SCM ret;
|
||||||
|
|
||||||
|
SCM_VALIDATE_VM_FRAME (1, frame);
|
||||||
|
|
||||||
|
fp = SCM_VM_FRAME_FP (frame);
|
||||||
|
bp = SCM_PROGRAM_DATA (SCM_FRAME_PROGRAM (fp));
|
||||||
|
|
||||||
|
if (!bp->nargs)
|
||||||
|
return SCM_EOL;
|
||||||
|
else if (bp->nrest)
|
||||||
|
ret = fp[bp->nargs - 1];
|
||||||
|
else
|
||||||
|
ret = scm_cons (fp[bp->nargs - 1], SCM_EOL);
|
||||||
|
|
||||||
|
for (i = bp->nargs - 2; i >= 0; i--)
|
||||||
|
ret = scm_cons (fp[i], ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_frame_source, "vm-frame-source", 1, 0, 0,
|
||||||
|
(SCM frame),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_frame_source
|
||||||
|
{
|
||||||
|
SCM *fp;
|
||||||
|
struct scm_objcode *bp;
|
||||||
|
|
||||||
|
SCM_VALIDATE_VM_FRAME (1, frame);
|
||||||
|
|
||||||
|
fp = SCM_VM_FRAME_FP (frame);
|
||||||
|
bp = SCM_PROGRAM_DATA (SCM_FRAME_PROGRAM (fp));
|
||||||
|
|
||||||
|
return scm_c_program_source (SCM_FRAME_PROGRAM (fp),
|
||||||
|
SCM_VM_FRAME_IP (frame) - bp->base);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_frame_local_ref, "vm-frame-local-ref", 2, 0, 0,
|
||||||
|
(SCM frame, SCM index),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_frame_local_ref
|
||||||
|
{
|
||||||
|
SCM *fp;
|
||||||
|
unsigned int i;
|
||||||
|
struct scm_objcode *bp;
|
||||||
|
|
||||||
|
SCM_VALIDATE_VM_FRAME (1, frame);
|
||||||
|
|
||||||
|
fp = SCM_VM_FRAME_FP (frame);
|
||||||
|
bp = SCM_PROGRAM_DATA (SCM_FRAME_PROGRAM (fp));
|
||||||
|
|
||||||
|
SCM_VALIDATE_UINT_COPY (2, index, i);
|
||||||
|
SCM_ASSERT_RANGE (2, index, i < bp->nargs + bp->nlocs);
|
||||||
|
|
||||||
|
return SCM_FRAME_VARIABLE (fp, i);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_frame_local_set_x, "vm-frame-local-set!", 3, 0, 0,
|
||||||
|
(SCM frame, SCM index, SCM val),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_frame_local_set_x
|
||||||
|
{
|
||||||
|
SCM *fp;
|
||||||
|
unsigned int i;
|
||||||
|
struct scm_objcode *bp;
|
||||||
|
|
||||||
|
SCM_VALIDATE_VM_FRAME (1, frame);
|
||||||
|
|
||||||
|
fp = SCM_VM_FRAME_FP (frame);
|
||||||
|
bp = SCM_PROGRAM_DATA (SCM_FRAME_PROGRAM (fp));
|
||||||
|
|
||||||
|
SCM_VALIDATE_UINT_COPY (2, index, i);
|
||||||
|
SCM_ASSERT_RANGE (2, index, i < bp->nargs + bp->nlocs);
|
||||||
|
|
||||||
|
SCM_FRAME_VARIABLE (fp, i) = val;
|
||||||
|
|
||||||
|
return SCM_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_frame_return_address, "vm-frame-return-address", 1, 0, 0,
|
||||||
|
(SCM frame),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_frame_return_address
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_VM_FRAME (1, frame);
|
||||||
|
return scm_from_ulong ((unsigned long)
|
||||||
|
(SCM_FRAME_RETURN_ADDRESS
|
||||||
|
(SCM_VM_FRAME_FP (frame))));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_frame_mv_return_address, "vm-frame-mv-return-address", 1, 0, 0,
|
||||||
|
(SCM frame),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_frame_mv_return_address
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_VM_FRAME (1, frame);
|
||||||
|
return scm_from_ulong ((unsigned long)
|
||||||
|
(SCM_FRAME_MV_RETURN_ADDRESS
|
||||||
|
(SCM_VM_FRAME_FP (frame))));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_frame_dynamic_link, "vm-frame-dynamic-link", 1, 0, 0,
|
||||||
|
(SCM frame),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_frame_dynamic_link
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_VM_FRAME (1, frame);
|
||||||
|
/* fixme: munge fp if holder is a continuation */
|
||||||
|
return scm_from_ulong
|
||||||
|
((unsigned long)
|
||||||
|
RELOC (frame,
|
||||||
|
SCM_FRAME_DYNAMIC_LINK (SCM_VM_FRAME_FP (frame))));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_frame_external_link, "vm-frame-external-link", 1, 0, 0,
|
||||||
|
(SCM frame),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_frame_external_link
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_VM_FRAME (1, frame);
|
||||||
|
return SCM_FRAME_EXTERNAL_LINK (SCM_VM_FRAME_FP (frame));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_frame_stack, "vm-frame-stack", 1, 0, 0,
|
||||||
|
(SCM frame),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_frame_stack
|
||||||
|
{
|
||||||
|
SCM *top, *bottom, ret = SCM_EOL;
|
||||||
|
|
||||||
|
SCM_VALIDATE_VM_FRAME (1, frame);
|
||||||
|
|
||||||
|
top = SCM_VM_FRAME_SP (frame);
|
||||||
|
bottom = SCM_FRAME_UPPER_ADDRESS (SCM_VM_FRAME_FP (frame));
|
||||||
|
while (bottom <= top)
|
||||||
|
ret = scm_cons (*bottom++, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
extern SCM
|
||||||
|
scm_c_vm_frame_prev (SCM frame)
|
||||||
|
{
|
||||||
|
SCM *this_fp, *new_fp, *new_sp;
|
||||||
|
this_fp = SCM_VM_FRAME_FP (frame);
|
||||||
|
new_fp = SCM_FRAME_DYNAMIC_LINK (this_fp);
|
||||||
|
if (new_fp)
|
||||||
|
{ new_fp = RELOC (frame, new_fp);
|
||||||
|
new_sp = SCM_FRAME_LOWER_ADDRESS (this_fp) - 1;
|
||||||
|
return scm_c_make_vm_frame (SCM_VM_FRAME_STACK_HOLDER (frame),
|
||||||
|
new_fp, new_sp,
|
||||||
|
SCM_FRAME_RETURN_ADDRESS (this_fp),
|
||||||
|
SCM_VM_FRAME_OFFSET (frame));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return SCM_BOOL_F;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
scm_bootstrap_frames (void)
|
||||||
|
{
|
||||||
|
scm_tc16_vm_frame = scm_make_smob_type ("vm-frame", 0);
|
||||||
|
scm_set_smob_mark (scm_tc16_vm_frame, vm_frame_mark);
|
||||||
|
scm_set_smob_free (scm_tc16_vm_frame, vm_frame_free);
|
||||||
|
scm_set_smob_print (scm_tc16_vm_frame, vm_frame_print);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scm_init_frames (void)
|
||||||
|
{
|
||||||
|
scm_bootstrap_vm ();
|
||||||
|
|
||||||
|
#ifndef SCM_MAGIC_SNARFER
|
||||||
|
#include "libguile/frames.x"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
146
libguile/frames.h
Normal file
146
libguile/frames.h
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
#ifndef _SCM_FRAMES_H_
|
||||||
|
#define _SCM_FRAMES_H_
|
||||||
|
|
||||||
|
#include <libguile.h>
|
||||||
|
#include "programs.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VM frames
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* VM Frame Layout
|
||||||
|
---------------
|
||||||
|
|
||||||
|
| | <- fp + bp->nargs + bp->nlocs + 4
|
||||||
|
+------------------+ = SCM_FRAME_UPPER_ADDRESS (fp)
|
||||||
|
| Return address |
|
||||||
|
| MV return address|
|
||||||
|
| Dynamic link |
|
||||||
|
| External link | <- fp + bp->nargs + bp->nlocs
|
||||||
|
| Local variable 1 | = SCM_FRAME_DATA_ADDRESS (fp)
|
||||||
|
| Local variable 0 | <- fp + bp->nargs
|
||||||
|
| Argument 1 |
|
||||||
|
| Argument 0 | <- fp
|
||||||
|
| Program | <- fp - 1
|
||||||
|
+------------------+ = SCM_FRAME_LOWER_ADDRESS (fp)
|
||||||
|
| |
|
||||||
|
|
||||||
|
As can be inferred from this drawing, it is assumed that
|
||||||
|
`sizeof (SCM *) == sizeof (SCM)', since pointers (the `link' parts) are
|
||||||
|
assumed to be as long as SCM objects. */
|
||||||
|
|
||||||
|
#define SCM_FRAME_DATA_ADDRESS(fp) \
|
||||||
|
(fp + SCM_PROGRAM_DATA (SCM_FRAME_PROGRAM (fp))->nargs \
|
||||||
|
+ SCM_PROGRAM_DATA (SCM_FRAME_PROGRAM (fp))->nlocs)
|
||||||
|
#define SCM_FRAME_UPPER_ADDRESS(fp) (SCM_FRAME_DATA_ADDRESS (fp) + 4)
|
||||||
|
#define SCM_FRAME_LOWER_ADDRESS(fp) (fp - 1)
|
||||||
|
|
||||||
|
#define SCM_FRAME_BYTE_CAST(x) ((scm_byte_t *) SCM_UNPACK (x))
|
||||||
|
#define SCM_FRAME_STACK_CAST(x) ((SCM *) SCM_UNPACK (x))
|
||||||
|
|
||||||
|
#define SCM_FRAME_RETURN_ADDRESS(fp) \
|
||||||
|
(SCM_FRAME_BYTE_CAST (SCM_FRAME_DATA_ADDRESS (fp)[3]))
|
||||||
|
#define SCM_FRAME_MV_RETURN_ADDRESS(fp) \
|
||||||
|
(SCM_FRAME_BYTE_CAST (SCM_FRAME_DATA_ADDRESS (fp)[2]))
|
||||||
|
#define SCM_FRAME_DYNAMIC_LINK(fp) \
|
||||||
|
(SCM_FRAME_STACK_CAST (SCM_FRAME_DATA_ADDRESS (fp)[1]))
|
||||||
|
#define SCM_FRAME_SET_DYNAMIC_LINK(fp, dl) \
|
||||||
|
((SCM_FRAME_DATA_ADDRESS (fp)[1])) = (SCM)(dl);
|
||||||
|
#define SCM_FRAME_EXTERNAL_LINK(fp) (SCM_FRAME_DATA_ADDRESS (fp)[0])
|
||||||
|
#define SCM_FRAME_VARIABLE(fp,i) fp[i]
|
||||||
|
#define SCM_FRAME_PROGRAM(fp) fp[-1]
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Heap frames
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern scm_t_bits scm_tc16_vm_frame;
|
||||||
|
|
||||||
|
struct scm_vm_frame
|
||||||
|
{
|
||||||
|
SCM stack_holder;
|
||||||
|
SCM *fp;
|
||||||
|
SCM *sp;
|
||||||
|
scm_byte_t *ip;
|
||||||
|
scm_t_ptrdiff offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SCM_VM_FRAME_P(x) SCM_SMOB_PREDICATE (scm_tc16_vm_frame, x)
|
||||||
|
#define SCM_VM_FRAME_DATA(x) ((struct scm_vm_frame*)SCM_SMOB_DATA (x))
|
||||||
|
#define SCM_VM_FRAME_STACK_HOLDER(f) SCM_VM_FRAME_DATA(f)->stack_holder
|
||||||
|
#define SCM_VM_FRAME_FP(f) SCM_VM_FRAME_DATA(f)->fp
|
||||||
|
#define SCM_VM_FRAME_SP(f) SCM_VM_FRAME_DATA(f)->sp
|
||||||
|
#define SCM_VM_FRAME_IP(f) SCM_VM_FRAME_DATA(f)->ip
|
||||||
|
#define SCM_VM_FRAME_OFFSET(f) SCM_VM_FRAME_DATA(f)->offset
|
||||||
|
#define SCM_VALIDATE_VM_FRAME(p,x) SCM_MAKE_VALIDATE (p, x, VM_FRAME_P)
|
||||||
|
|
||||||
|
/* FIXME rename scm_byte_t */
|
||||||
|
extern SCM scm_c_make_vm_frame (SCM stack_holder, SCM *fp, SCM *sp,
|
||||||
|
scm_byte_t *ip, scm_t_ptrdiff offset);
|
||||||
|
extern SCM scm_vm_frame_p (SCM obj);
|
||||||
|
extern SCM scm_vm_frame_program (SCM frame);
|
||||||
|
extern SCM scm_vm_frame_arguments (SCM frame);
|
||||||
|
extern SCM scm_vm_frame_source (SCM frame);
|
||||||
|
extern SCM scm_vm_frame_local_ref (SCM frame, SCM index);
|
||||||
|
extern SCM scm_vm_frame_local_set_x (SCM frame, SCM index, SCM val);
|
||||||
|
extern SCM scm_vm_frame_return_address (SCM frame);
|
||||||
|
extern SCM scm_vm_frame_mv_return_address (SCM frame);
|
||||||
|
extern SCM scm_vm_frame_dynamic_link (SCM frame);
|
||||||
|
extern SCM scm_vm_frame_external_link (SCM frame);
|
||||||
|
extern SCM scm_vm_frame_stack (SCM frame);
|
||||||
|
|
||||||
|
extern SCM scm_c_vm_frame_prev (SCM frame);
|
||||||
|
|
||||||
|
extern void scm_bootstrap_frames (void);
|
||||||
|
extern void scm_init_frames (void);
|
||||||
|
|
||||||
|
#endif /* _SCM_FRAMES_H_ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
132
libguile/goops.c
132
libguile/goops.c
|
@ -59,24 +59,32 @@
|
||||||
|
|
||||||
#define SPEC_OF(x) SCM_SLOT (x, scm_si_specializers)
|
#define SPEC_OF(x) SCM_SLOT (x, scm_si_specializers)
|
||||||
|
|
||||||
|
/* this file is a mess. in theory, though, we shouldn't have many SCM references
|
||||||
|
-- most of the references should be to vars. */
|
||||||
|
|
||||||
|
static SCM var_slot_unbound = SCM_BOOL_F;
|
||||||
|
static SCM var_slot_missing = SCM_BOOL_F;
|
||||||
|
static SCM var_compute_cpl = SCM_BOOL_F;
|
||||||
|
static SCM var_no_applicable_method = SCM_BOOL_F;
|
||||||
|
static SCM var_memoize_method_x = SCM_BOOL_F;
|
||||||
|
static SCM var_change_class = SCM_BOOL_F;
|
||||||
|
|
||||||
|
SCM_SYMBOL (sym_slot_unbound, "slot-unbound");
|
||||||
|
SCM_SYMBOL (sym_slot_missing, "slot-missing");
|
||||||
|
SCM_SYMBOL (sym_compute_cpl, "compute-cpl");
|
||||||
|
SCM_SYMBOL (sym_no_applicable_method, "no-applicable-method");
|
||||||
|
SCM_SYMBOL (sym_memoize_method_x, "memoize-method!");
|
||||||
|
SCM_SYMBOL (sym_change_class, "change-class");
|
||||||
|
|
||||||
|
SCM_VARIABLE (scm_var_make_extended_generic, "make-extended-generic");
|
||||||
|
|
||||||
|
|
||||||
|
/* FIXME, exports should come from the scm file only */
|
||||||
#define DEFVAR(v, val) \
|
#define DEFVAR(v, val) \
|
||||||
{ scm_eval (scm_list_3 (scm_sym_define_public, (v), (val)), \
|
{ scm_module_define (scm_module_goops, (v), (val)); \
|
||||||
scm_module_goops); }
|
scm_module_export (scm_module_goops, scm_list_1 ((v))); \
|
||||||
/* Temporary hack until we get the new module system */
|
}
|
||||||
/*fixme* Should optimize by keeping track of the variable object itself */
|
|
||||||
#define GETVAR(v) (SCM_VARIABLE_REF (scm_call_2 (scm_goops_lookup_closure, \
|
|
||||||
(v), SCM_BOOL_F)))
|
|
||||||
|
|
||||||
/* Fixme: Should use already interned symbols */
|
|
||||||
|
|
||||||
#define CALL_GF1(name, a) (scm_call_1 (GETVAR (scm_from_locale_symbol (name)), \
|
|
||||||
a))
|
|
||||||
#define CALL_GF2(name, a, b) (scm_call_2 (GETVAR (scm_from_locale_symbol (name)), \
|
|
||||||
a, b))
|
|
||||||
#define CALL_GF3(name, a, b, c) (scm_call_3 (GETVAR (scm_from_locale_symbol (name)), \
|
|
||||||
a, b, c))
|
|
||||||
#define CALL_GF4(name, a, b, c, d) (scm_call_4 (GETVAR (scm_from_locale_symbol (name)), \
|
|
||||||
a, b, c, d))
|
|
||||||
|
|
||||||
/* Class redefinition protocol:
|
/* Class redefinition protocol:
|
||||||
|
|
||||||
|
@ -119,8 +127,6 @@
|
||||||
static int goops_loaded_p = 0;
|
static int goops_loaded_p = 0;
|
||||||
static scm_t_rstate *goops_rstate;
|
static scm_t_rstate *goops_rstate;
|
||||||
|
|
||||||
static SCM scm_goops_lookup_closure;
|
|
||||||
|
|
||||||
/* These variables are filled in by the object system when loaded. */
|
/* These variables are filled in by the object system when loaded. */
|
||||||
SCM scm_class_boolean, scm_class_char, scm_class_pair;
|
SCM scm_class_boolean, scm_class_char, scm_class_pair;
|
||||||
SCM scm_class_procedure, scm_class_string, scm_class_symbol;
|
SCM scm_class_procedure, scm_class_string, scm_class_symbol;
|
||||||
|
@ -346,7 +352,7 @@ static SCM
|
||||||
compute_cpl (SCM class)
|
compute_cpl (SCM class)
|
||||||
{
|
{
|
||||||
if (goops_loaded_p)
|
if (goops_loaded_p)
|
||||||
return CALL_GF1 ("compute-cpl", class);
|
return scm_call_1 (SCM_VARIABLE_REF (var_compute_cpl), class);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SCM supers = SCM_SLOT (class, scm_si_direct_supers);
|
SCM supers = SCM_SLOT (class, scm_si_direct_supers);
|
||||||
|
@ -588,13 +594,10 @@ SCM_DEFINE (scm_sys_initialize_object, "%initialize-object", 2, 0, 0,
|
||||||
{
|
{
|
||||||
slot_value = get_slot_value (class, obj, SCM_CAR (get_n_set));
|
slot_value = get_slot_value (class, obj, SCM_CAR (get_n_set));
|
||||||
if (SCM_GOOPS_UNBOUNDP (slot_value))
|
if (SCM_GOOPS_UNBOUNDP (slot_value))
|
||||||
{
|
|
||||||
SCM env = SCM_EXTEND_ENV (SCM_EOL, SCM_EOL, SCM_ENV (tmp));
|
|
||||||
set_slot_value (class,
|
set_slot_value (class,
|
||||||
obj,
|
obj,
|
||||||
SCM_CAR (get_n_set),
|
SCM_CAR (get_n_set),
|
||||||
scm_eval_body (SCM_CLOSURE_BODY (tmp), env));
|
scm_call_0 (tmp));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1195,7 +1198,7 @@ SCM_DEFINE (scm_assert_bound, "assert-bound", 2, 0, 0,
|
||||||
#define FUNC_NAME s_scm_assert_bound
|
#define FUNC_NAME s_scm_assert_bound
|
||||||
{
|
{
|
||||||
if (SCM_GOOPS_UNBOUNDP (value))
|
if (SCM_GOOPS_UNBOUNDP (value))
|
||||||
return CALL_GF1 ("slot-unbound", obj);
|
return scm_call_1 (SCM_VARIABLE_REF (var_slot_unbound), obj);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
@ -1208,7 +1211,7 @@ SCM_DEFINE (scm_at_assert_bound_ref, "@assert-bound-ref", 2, 0, 0,
|
||||||
{
|
{
|
||||||
SCM value = SCM_SLOT (obj, scm_to_int (index));
|
SCM value = SCM_SLOT (obj, scm_to_int (index));
|
||||||
if (SCM_GOOPS_UNBOUNDP (value))
|
if (SCM_GOOPS_UNBOUNDP (value))
|
||||||
return CALL_GF1 ("slot-unbound", obj);
|
return scm_call_1 (SCM_VARIABLE_REF (var_slot_unbound), obj);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
@ -1296,7 +1299,7 @@ get_slot_value (SCM class SCM_UNUSED, SCM obj, SCM slotdef)
|
||||||
|
|
||||||
code = SCM_CAR (access);
|
code = SCM_CAR (access);
|
||||||
if (!SCM_CLOSUREP (code))
|
if (!SCM_CLOSUREP (code))
|
||||||
return SCM_SUBRF (code) (obj);
|
return scm_call_1 (code, obj);
|
||||||
env = SCM_EXTEND_ENV (SCM_CLOSURE_FORMALS (code),
|
env = SCM_EXTEND_ENV (SCM_CLOSURE_FORMALS (code),
|
||||||
scm_list_1 (obj),
|
scm_list_1 (obj),
|
||||||
SCM_ENV (code));
|
SCM_ENV (code));
|
||||||
|
@ -1313,7 +1316,7 @@ get_slot_value_using_name (SCM class, SCM obj, SCM slot_name)
|
||||||
if (scm_is_true (slotdef))
|
if (scm_is_true (slotdef))
|
||||||
return get_slot_value (class, obj, slotdef);
|
return get_slot_value (class, obj, slotdef);
|
||||||
else
|
else
|
||||||
return CALL_GF3 ("slot-missing", class, obj, slot_name);
|
return scm_call_3 (SCM_VARIABLE_REF (var_slot_missing), class, obj, slot_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SCM
|
static SCM
|
||||||
|
@ -1339,7 +1342,7 @@ set_slot_value (SCM class SCM_UNUSED, SCM obj, SCM slotdef, SCM value)
|
||||||
|
|
||||||
code = SCM_CADR (access);
|
code = SCM_CADR (access);
|
||||||
if (!SCM_CLOSUREP (code))
|
if (!SCM_CLOSUREP (code))
|
||||||
SCM_SUBRF (code) (obj, value);
|
scm_call_2 (code, obj, value);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
env = SCM_EXTEND_ENV (SCM_CLOSURE_FORMALS (code),
|
env = SCM_EXTEND_ENV (SCM_CLOSURE_FORMALS (code),
|
||||||
|
@ -1360,7 +1363,7 @@ set_slot_value_using_name (SCM class, SCM obj, SCM slot_name, SCM value)
|
||||||
if (scm_is_true (slotdef))
|
if (scm_is_true (slotdef))
|
||||||
return set_slot_value (class, obj, slotdef, value);
|
return set_slot_value (class, obj, slotdef, value);
|
||||||
else
|
else
|
||||||
return CALL_GF4 ("slot-missing", class, obj, slot_name, value);
|
return scm_call_4 (SCM_VARIABLE_REF (var_slot_missing), class, obj, slot_name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SCM
|
static SCM
|
||||||
|
@ -1390,7 +1393,7 @@ SCM_DEFINE (scm_slot_ref_using_class, "slot-ref-using-class", 3, 0, 0,
|
||||||
|
|
||||||
res = get_slot_value_using_name (class, obj, slot_name);
|
res = get_slot_value_using_name (class, obj, slot_name);
|
||||||
if (SCM_GOOPS_UNBOUNDP (res))
|
if (SCM_GOOPS_UNBOUNDP (res))
|
||||||
return CALL_GF3 ("slot-unbound", class, obj, slot_name);
|
return scm_call_3 (SCM_VARIABLE_REF (var_slot_unbound), class, obj, slot_name);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
@ -1453,7 +1456,7 @@ SCM_DEFINE (scm_slot_ref, "slot-ref", 2, 0, 0,
|
||||||
|
|
||||||
res = get_slot_value_using_name (class, obj, slot_name);
|
res = get_slot_value_using_name (class, obj, slot_name);
|
||||||
if (SCM_GOOPS_UNBOUNDP (res))
|
if (SCM_GOOPS_UNBOUNDP (res))
|
||||||
return CALL_GF3 ("slot-unbound", class, obj, slot_name);
|
return scm_call_3 (SCM_VARIABLE_REF (var_slot_unbound), class, obj, slot_name);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
@ -1742,7 +1745,7 @@ SCM_SYMBOL (scm_sym_change_class, "change-class");
|
||||||
static SCM
|
static SCM
|
||||||
purgatory (void *args)
|
purgatory (void *args)
|
||||||
{
|
{
|
||||||
return scm_apply_0 (GETVAR (scm_sym_change_class),
|
return scm_apply_0 (SCM_VARIABLE_REF (var_change_class),
|
||||||
SCM_PACK ((scm_t_bits) args));
|
SCM_PACK ((scm_t_bits) args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2143,7 +2146,7 @@ scm_compute_applicable_methods (SCM gf, SCM args, long len, int find_method_p)
|
||||||
{
|
{
|
||||||
if (find_method_p)
|
if (find_method_p)
|
||||||
return SCM_BOOL_F;
|
return SCM_BOOL_F;
|
||||||
CALL_GF2 ("no-applicable-method", gf, save);
|
scm_call_2 (SCM_VARIABLE_REF (var_no_applicable_method), gf, save);
|
||||||
/* if we are here, it's because no-applicable-method hasn't signaled an error */
|
/* if we are here, it's because no-applicable-method hasn't signaled an error */
|
||||||
return SCM_BOOL_F;
|
return SCM_BOOL_F;
|
||||||
}
|
}
|
||||||
|
@ -2200,8 +2203,13 @@ call_memoize_method (void *a)
|
||||||
SCM cmethod = scm_mcache_lookup_cmethod (x, SCM_CDDR (args));
|
SCM cmethod = scm_mcache_lookup_cmethod (x, SCM_CDDR (args));
|
||||||
if (scm_is_true (cmethod))
|
if (scm_is_true (cmethod))
|
||||||
return cmethod;
|
return cmethod;
|
||||||
/*fixme* Use scm_apply */
|
|
||||||
return CALL_GF3 ("memoize-method!", gf, SCM_CDDR (args), x);
|
if (SCM_UNLIKELY (scm_is_false (var_memoize_method_x)))
|
||||||
|
var_memoize_method_x =
|
||||||
|
scm_permanent_object
|
||||||
|
(scm_module_variable (scm_module_goops, sym_memoize_method_x));
|
||||||
|
|
||||||
|
return scm_call_3 (SCM_VARIABLE_REF (var_memoize_method_x), gf, SCM_CDDR (args), x);
|
||||||
}
|
}
|
||||||
|
|
||||||
SCM
|
SCM
|
||||||
|
@ -2229,6 +2237,9 @@ scm_memoize_method (SCM x, SCM args)
|
||||||
SCM_KEYWORD (k_setter, "setter");
|
SCM_KEYWORD (k_setter, "setter");
|
||||||
SCM_KEYWORD (k_specializers, "specializers");
|
SCM_KEYWORD (k_specializers, "specializers");
|
||||||
SCM_KEYWORD (k_procedure, "procedure");
|
SCM_KEYWORD (k_procedure, "procedure");
|
||||||
|
SCM_KEYWORD (k_formals, "formals");
|
||||||
|
SCM_KEYWORD (k_body, "body");
|
||||||
|
SCM_KEYWORD (k_make_procedure, "make-procedure");
|
||||||
SCM_KEYWORD (k_dsupers, "dsupers");
|
SCM_KEYWORD (k_dsupers, "dsupers");
|
||||||
SCM_KEYWORD (k_slots, "slots");
|
SCM_KEYWORD (k_slots, "slots");
|
||||||
SCM_KEYWORD (k_gf, "generic-function");
|
SCM_KEYWORD (k_gf, "generic-function");
|
||||||
|
@ -2292,9 +2303,27 @@ SCM_DEFINE (scm_make, "make", 0, 0, 1,
|
||||||
scm_i_get_keyword (k_procedure,
|
scm_i_get_keyword (k_procedure,
|
||||||
args,
|
args,
|
||||||
len - 1,
|
len - 1,
|
||||||
SCM_EOL,
|
SCM_BOOL_F,
|
||||||
FUNC_NAME));
|
FUNC_NAME));
|
||||||
SCM_SET_SLOT (z, scm_si_code_table, SCM_EOL);
|
SCM_SET_SLOT (z, scm_si_code_table, SCM_EOL);
|
||||||
|
SCM_SET_SLOT (z, scm_si_formals,
|
||||||
|
scm_i_get_keyword (k_formals,
|
||||||
|
args,
|
||||||
|
len - 1,
|
||||||
|
SCM_EOL,
|
||||||
|
FUNC_NAME));
|
||||||
|
SCM_SET_SLOT (z, scm_si_body,
|
||||||
|
scm_i_get_keyword (k_body,
|
||||||
|
args,
|
||||||
|
len - 1,
|
||||||
|
SCM_EOL,
|
||||||
|
FUNC_NAME));
|
||||||
|
SCM_SET_SLOT (z, scm_si_make_procedure,
|
||||||
|
scm_i_get_keyword (k_make_procedure,
|
||||||
|
args,
|
||||||
|
len - 1,
|
||||||
|
SCM_BOOL_F,
|
||||||
|
FUNC_NAME));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2434,10 +2463,14 @@ static void
|
||||||
create_standard_classes (void)
|
create_standard_classes (void)
|
||||||
{
|
{
|
||||||
SCM slots;
|
SCM slots;
|
||||||
SCM method_slots = scm_list_4 (scm_from_locale_symbol ("generic-function"),
|
SCM method_slots = scm_list_n (scm_from_locale_symbol ("generic-function"),
|
||||||
scm_from_locale_symbol ("specializers"),
|
scm_from_locale_symbol ("specializers"),
|
||||||
sym_procedure,
|
sym_procedure,
|
||||||
scm_from_locale_symbol ("code-table"));
|
scm_from_locale_symbol ("code-table"),
|
||||||
|
scm_from_locale_symbol ("formals"),
|
||||||
|
scm_from_locale_symbol ("body"),
|
||||||
|
scm_from_locale_symbol ("make-procedure"),
|
||||||
|
SCM_UNDEFINED);
|
||||||
SCM amethod_slots = scm_list_1 (scm_list_3 (scm_from_locale_symbol ("slot-definition"),
|
SCM amethod_slots = scm_list_1 (scm_list_3 (scm_from_locale_symbol ("slot-definition"),
|
||||||
k_init_keyword,
|
k_init_keyword,
|
||||||
k_slot_definition));
|
k_slot_definition));
|
||||||
|
@ -2646,7 +2679,7 @@ make_class_from_template (char const *template, char const *type_name, SCM super
|
||||||
|
|
||||||
/* Only define name if doesn't already exist. */
|
/* Only define name if doesn't already exist. */
|
||||||
if (!SCM_GOOPS_UNBOUNDP (name)
|
if (!SCM_GOOPS_UNBOUNDP (name)
|
||||||
&& scm_is_false (scm_call_2 (scm_goops_lookup_closure, name, SCM_BOOL_F)))
|
&& scm_is_false (scm_module_variable (scm_module_goops, name)))
|
||||||
DEFVAR (name, class);
|
DEFVAR (name, class);
|
||||||
return class;
|
return class;
|
||||||
}
|
}
|
||||||
|
@ -2978,8 +3011,23 @@ SCM_DEFINE (scm_sys_goops_loaded, "%goops-loaded", 0, 0, 0,
|
||||||
{
|
{
|
||||||
goops_loaded_p = 1;
|
goops_loaded_p = 1;
|
||||||
var_compute_applicable_methods =
|
var_compute_applicable_methods =
|
||||||
scm_sym2var (sym_compute_applicable_methods, scm_goops_lookup_closure,
|
scm_permanent_object
|
||||||
SCM_BOOL_F);
|
(scm_module_variable (scm_module_goops, sym_compute_applicable_methods));
|
||||||
|
var_slot_unbound =
|
||||||
|
scm_permanent_object
|
||||||
|
(scm_module_variable (scm_module_goops, sym_slot_unbound));
|
||||||
|
var_slot_missing =
|
||||||
|
scm_permanent_object
|
||||||
|
(scm_module_variable (scm_module_goops, sym_slot_missing));
|
||||||
|
var_compute_cpl =
|
||||||
|
scm_permanent_object
|
||||||
|
(scm_module_variable (scm_module_goops, sym_compute_cpl));
|
||||||
|
var_no_applicable_method =
|
||||||
|
scm_permanent_object
|
||||||
|
(scm_module_variable (scm_module_goops, sym_no_applicable_method));
|
||||||
|
var_change_class =
|
||||||
|
scm_permanent_object
|
||||||
|
(scm_module_variable (scm_module_goops, sym_change_class));
|
||||||
setup_extended_primitive_generics ();
|
setup_extended_primitive_generics ();
|
||||||
return SCM_UNSPECIFIED;
|
return SCM_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
|
@ -2991,12 +3039,10 @@ SCM
|
||||||
scm_init_goops_builtins (void)
|
scm_init_goops_builtins (void)
|
||||||
{
|
{
|
||||||
scm_module_goops = scm_current_module ();
|
scm_module_goops = scm_current_module ();
|
||||||
scm_goops_lookup_closure = scm_module_lookup_closure (scm_module_goops);
|
|
||||||
|
|
||||||
/* Not really necessary right now, but who knows...
|
/* Not really necessary right now, but who knows...
|
||||||
*/
|
*/
|
||||||
scm_permanent_object (scm_module_goops);
|
scm_permanent_object (scm_module_goops);
|
||||||
scm_permanent_object (scm_goops_lookup_closure);
|
|
||||||
|
|
||||||
scm_components = scm_permanent_object (scm_make_weak_key_hash_table
|
scm_components = scm_permanent_object (scm_make_weak_key_hash_table
|
||||||
(scm_from_int (37)));
|
(scm_from_int (37)));
|
||||||
|
|
|
@ -149,9 +149,11 @@ typedef struct scm_t_method {
|
||||||
|
|
||||||
#define scm_si_generic_function 0 /* offset of gf slot in a <method> */
|
#define scm_si_generic_function 0 /* offset of gf slot in a <method> */
|
||||||
#define scm_si_specializers 1 /* offset of spec. slot in a <method> */
|
#define scm_si_specializers 1 /* offset of spec. slot in a <method> */
|
||||||
|
|
||||||
#define scm_si_procedure 2 /* offset of proc. slot in a <method> */
|
#define scm_si_procedure 2 /* offset of proc. slot in a <method> */
|
||||||
#define scm_si_code_table 3 /* offset of code. slot in a <method> */
|
#define scm_si_code_table 3 /* offset of code. slot in a <method> */
|
||||||
|
#define scm_si_formals 4 /* offset of form. slot in a <method> */
|
||||||
|
#define scm_si_body 5 /* offset of body slot in a <method> */
|
||||||
|
#define scm_si_make_procedure 6 /* offset of makep.slot in a <method> */
|
||||||
|
|
||||||
/* C interface */
|
/* C interface */
|
||||||
SCM_API SCM scm_class_boolean;
|
SCM_API SCM scm_class_boolean;
|
||||||
|
|
|
@ -118,6 +118,7 @@
|
||||||
#include "libguile/variable.h"
|
#include "libguile/variable.h"
|
||||||
#include "libguile/vectors.h"
|
#include "libguile/vectors.h"
|
||||||
#include "libguile/version.h"
|
#include "libguile/version.h"
|
||||||
|
#include "libguile/vm-bootstrap.h"
|
||||||
#include "libguile/vports.h"
|
#include "libguile/vports.h"
|
||||||
#include "libguile/weaks.h"
|
#include "libguile/weaks.h"
|
||||||
#include "libguile/guardians.h"
|
#include "libguile/guardians.h"
|
||||||
|
@ -281,7 +282,7 @@ scm_load_startup_files ()
|
||||||
/* Load Ice-9. */
|
/* Load Ice-9. */
|
||||||
if (!scm_ice_9_already_loaded)
|
if (!scm_ice_9_already_loaded)
|
||||||
{
|
{
|
||||||
scm_primitive_load_path (scm_from_locale_string ("ice-9/boot-9.scm"));
|
scm_primitive_load_path (scm_from_locale_string ("ice-9/boot-9"));
|
||||||
|
|
||||||
/* Load the init.scm file. */
|
/* Load the init.scm file. */
|
||||||
if (scm_is_true (init_path))
|
if (scm_is_true (init_path))
|
||||||
|
@ -573,6 +574,8 @@ scm_i_init_guile (SCM_STACKITEM *base)
|
||||||
scm_init_rw ();
|
scm_init_rw ();
|
||||||
scm_init_extensions ();
|
scm_init_extensions ();
|
||||||
|
|
||||||
|
scm_bootstrap_vm ();
|
||||||
|
|
||||||
atexit (cleanup_for_exit);
|
atexit (cleanup_for_exit);
|
||||||
scm_load_startup_files ();
|
scm_load_startup_files ();
|
||||||
}
|
}
|
||||||
|
|
234
libguile/instructions.c
Normal file
234
libguile/instructions.c
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
#if HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "vm-bootstrap.h"
|
||||||
|
#include "instructions.h"
|
||||||
|
|
||||||
|
struct scm_instruction {
|
||||||
|
enum scm_opcode opcode; /* opcode */
|
||||||
|
const char *name; /* instruction name */
|
||||||
|
signed char len; /* Instruction length. This may be -1 for
|
||||||
|
the loader (see the `VM_LOADER'
|
||||||
|
macro). */
|
||||||
|
signed char npop; /* The number of values popped. This may be
|
||||||
|
-1 for insns like `call' which can take
|
||||||
|
any number of arguments. */
|
||||||
|
char npush; /* the number of values pushed */
|
||||||
|
SCM symname; /* filled in later */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SCM_VALIDATE_LOOKUP_INSTRUCTION(pos, var, cvar) \
|
||||||
|
do { \
|
||||||
|
cvar = scm_lookup_instruction_by_name (var); \
|
||||||
|
SCM_ASSERT_TYPE (cvar, var, pos, FUNC_NAME, "INSTRUCTION_P"); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
static struct scm_instruction*
|
||||||
|
fetch_instruction_table ()
|
||||||
|
{
|
||||||
|
static struct scm_instruction *table = NULL;
|
||||||
|
|
||||||
|
if (SCM_UNLIKELY (!table))
|
||||||
|
{
|
||||||
|
size_t bytes = scm_op_last * sizeof(struct scm_instruction);
|
||||||
|
int i;
|
||||||
|
table = malloc (bytes);
|
||||||
|
memset (table, 0, bytes);
|
||||||
|
#define VM_INSTRUCTION_TO_TABLE 1
|
||||||
|
#include <libguile/vm-expand.h>
|
||||||
|
#include <libguile/vm-i-system.i>
|
||||||
|
#include <libguile/vm-i-scheme.i>
|
||||||
|
#include <libguile/vm-i-loader.i>
|
||||||
|
#undef VM_INSTRUCTION_TO_TABLE
|
||||||
|
for (i = 0; i < scm_op_last; i++)
|
||||||
|
{
|
||||||
|
table[i].opcode = i;
|
||||||
|
if (table[i].name)
|
||||||
|
table[i].symname = scm_from_locale_symbol (table[i].name);
|
||||||
|
else
|
||||||
|
table[i].symname = SCM_BOOL_F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct scm_instruction *
|
||||||
|
scm_lookup_instruction_by_name (SCM name)
|
||||||
|
{
|
||||||
|
static SCM instructions_by_name = SCM_BOOL_F;
|
||||||
|
struct scm_instruction *table = fetch_instruction_table ();
|
||||||
|
SCM op;
|
||||||
|
|
||||||
|
if (SCM_UNLIKELY (SCM_FALSEP (instructions_by_name)))
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
instructions_by_name = scm_make_hash_table (SCM_I_MAKINUM (scm_op_last));
|
||||||
|
for (i = 0; i < scm_op_last; i++)
|
||||||
|
if (scm_is_true (table[i].symname))
|
||||||
|
scm_hashq_set_x (instructions_by_name, table[i].symname,
|
||||||
|
SCM_I_MAKINUM (i));
|
||||||
|
instructions_by_name = scm_permanent_object (instructions_by_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
op = scm_hashq_ref (instructions_by_name, name, SCM_UNDEFINED);
|
||||||
|
if (SCM_I_INUMP (op))
|
||||||
|
return &table[SCM_I_INUM (op)];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Scheme interface */
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_instruction_list, "instruction-list", 0, 0, 0,
|
||||||
|
(void),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_instruction_list
|
||||||
|
{
|
||||||
|
SCM list = SCM_EOL;
|
||||||
|
struct scm_instruction *ip;
|
||||||
|
for (ip = fetch_instruction_table (); ip->opcode != scm_op_last; ip++)
|
||||||
|
if (ip->name)
|
||||||
|
list = scm_cons (ip->symname, list);
|
||||||
|
return scm_reverse_x (list, SCM_EOL);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_instruction_p, "instruction?", 1, 0, 0,
|
||||||
|
(SCM obj),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_instruction_p
|
||||||
|
{
|
||||||
|
return SCM_BOOL (scm_lookup_instruction_by_name (obj));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_instruction_length, "instruction-length", 1, 0, 0,
|
||||||
|
(SCM inst),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_instruction_length
|
||||||
|
{
|
||||||
|
struct scm_instruction *ip;
|
||||||
|
SCM_VALIDATE_LOOKUP_INSTRUCTION (1, inst, ip);
|
||||||
|
return SCM_I_MAKINUM (ip->len);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_instruction_pops, "instruction-pops", 1, 0, 0,
|
||||||
|
(SCM inst),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_instruction_pops
|
||||||
|
{
|
||||||
|
struct scm_instruction *ip;
|
||||||
|
SCM_VALIDATE_LOOKUP_INSTRUCTION (1, inst, ip);
|
||||||
|
return SCM_I_MAKINUM (ip->npop);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_instruction_pushes, "instruction-pushes", 1, 0, 0,
|
||||||
|
(SCM inst),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_instruction_pushes
|
||||||
|
{
|
||||||
|
struct scm_instruction *ip;
|
||||||
|
SCM_VALIDATE_LOOKUP_INSTRUCTION (1, inst, ip);
|
||||||
|
return SCM_I_MAKINUM (ip->npush);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_instruction_to_opcode, "instruction->opcode", 1, 0, 0,
|
||||||
|
(SCM inst),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_instruction_to_opcode
|
||||||
|
{
|
||||||
|
struct scm_instruction *ip;
|
||||||
|
SCM_VALIDATE_LOOKUP_INSTRUCTION (1, inst, ip);
|
||||||
|
return SCM_I_MAKINUM (ip->opcode);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_opcode_to_instruction, "opcode->instruction", 1, 0, 0,
|
||||||
|
(SCM op),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_opcode_to_instruction
|
||||||
|
{
|
||||||
|
int opcode;
|
||||||
|
SCM ret = SCM_BOOL_F;
|
||||||
|
|
||||||
|
SCM_MAKE_VALIDATE (1, op, I_INUMP);
|
||||||
|
opcode = SCM_I_INUM (op);
|
||||||
|
|
||||||
|
if (opcode < scm_op_last)
|
||||||
|
ret = fetch_instruction_table ()[opcode].symname;
|
||||||
|
|
||||||
|
if (scm_is_false (ret))
|
||||||
|
scm_wrong_type_arg_msg (FUNC_NAME, 1, op, "INSTRUCTION_P");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
void
|
||||||
|
scm_bootstrap_instructions (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scm_init_instructions (void)
|
||||||
|
{
|
||||||
|
scm_bootstrap_vm ();
|
||||||
|
|
||||||
|
#ifndef SCM_MAGIC_SNARFER
|
||||||
|
#include "libguile/instructions.x"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
77
libguile/instructions.h
Normal file
77
libguile/instructions.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
#ifndef _SCM_INSTRUCTIONS_H_
|
||||||
|
#define _SCM_INSTRUCTIONS_H_
|
||||||
|
|
||||||
|
#include <libguile.h>
|
||||||
|
|
||||||
|
#define SCM_VM_NUM_INSTRUCTIONS (1<<7)
|
||||||
|
#define SCM_VM_INSTRUCTION_MASK (SCM_VM_NUM_INSTRUCTIONS-1)
|
||||||
|
|
||||||
|
enum scm_opcode {
|
||||||
|
#define VM_INSTRUCTION_TO_OPCODE 1
|
||||||
|
#include <libguile/vm-expand.h>
|
||||||
|
#include <libguile/vm-i-system.i>
|
||||||
|
#include <libguile/vm-i-scheme.i>
|
||||||
|
#include <libguile/vm-i-loader.i>
|
||||||
|
#undef VM_INSTRUCTION_TO_OPCODE
|
||||||
|
scm_op_last = SCM_VM_NUM_INSTRUCTIONS
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SCM scm_instruction_list (void);
|
||||||
|
extern SCM scm_instruction_p (SCM obj);
|
||||||
|
extern SCM scm_instruction_length (SCM inst);
|
||||||
|
extern SCM scm_instruction_pops (SCM inst);
|
||||||
|
extern SCM scm_instruction_pushes (SCM inst);
|
||||||
|
extern SCM scm_instruction_to_opcode (SCM inst);
|
||||||
|
extern SCM scm_opcode_to_instruction (SCM op);
|
||||||
|
|
||||||
|
extern void scm_bootstrap_instructions (void);
|
||||||
|
extern void scm_init_instructions (void);
|
||||||
|
|
||||||
|
#endif /* _SCM_INSTRUCTIONS_H_ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
101
libguile/load.c
101
libguile/load.c
|
@ -44,6 +44,8 @@
|
||||||
#include "libguile/load.h"
|
#include "libguile/load.h"
|
||||||
#include "libguile/fluids.h"
|
#include "libguile/fluids.h"
|
||||||
|
|
||||||
|
#include "libguile/vm.h" /* for load-compiled/vm */
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
@ -172,6 +174,9 @@ static SCM *scm_loc_load_path;
|
||||||
/* List of extensions we try adding to the filenames. */
|
/* List of extensions we try adding to the filenames. */
|
||||||
static SCM *scm_loc_load_extensions;
|
static SCM *scm_loc_load_extensions;
|
||||||
|
|
||||||
|
/* Like %load-extensions, but for compiled files. */
|
||||||
|
static SCM *scm_loc_load_compiled_extensions;
|
||||||
|
|
||||||
|
|
||||||
SCM_DEFINE (scm_parse_path, "parse-path", 1, 1, 0,
|
SCM_DEFINE (scm_parse_path, "parse-path", 1, 1, 0,
|
||||||
(SCM path, SCM tail),
|
(SCM path, SCM tail),
|
||||||
|
@ -206,6 +211,14 @@ scm_init_load_path ()
|
||||||
SCM path = SCM_EOL;
|
SCM path = SCM_EOL;
|
||||||
|
|
||||||
#ifdef SCM_LIBRARY_DIR
|
#ifdef SCM_LIBRARY_DIR
|
||||||
|
env = getenv ("GUILE_SYSTEM_PATH");
|
||||||
|
if (env && strcmp (env, "") == 0)
|
||||||
|
/* special-case interpret system-path=="" as meaning no system path instead
|
||||||
|
of '("") */
|
||||||
|
;
|
||||||
|
else if (env)
|
||||||
|
path = scm_parse_path (scm_from_locale_string (env), path);
|
||||||
|
else
|
||||||
path = scm_list_3 (scm_from_locale_string (SCM_SITE_DIR),
|
path = scm_list_3 (scm_from_locale_string (SCM_SITE_DIR),
|
||||||
scm_from_locale_string (SCM_LIBRARY_DIR),
|
scm_from_locale_string (SCM_LIBRARY_DIR),
|
||||||
scm_from_locale_string (SCM_PKGDATA_DIR));
|
scm_from_locale_string (SCM_PKGDATA_DIR));
|
||||||
|
@ -291,14 +304,33 @@ stringbuf_cat (struct stringbuf *buf, char *str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
scm_c_string_has_an_ext (char *str, size_t len, SCM extensions)
|
||||||
|
{
|
||||||
|
for (; !scm_is_null (extensions); extensions = SCM_CDR (extensions))
|
||||||
|
{
|
||||||
|
char *ext;
|
||||||
|
size_t extlen;
|
||||||
|
int match;
|
||||||
|
ext = scm_to_locale_string (SCM_CAR (extensions));
|
||||||
|
extlen = strlen (ext);
|
||||||
|
match = (len > extlen && str[len - extlen - 1] == '.'
|
||||||
|
&& strncmp (str + (len - extlen), ext, extlen) == 0);
|
||||||
|
free (ext);
|
||||||
|
if (match)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Search PATH for a directory containing a file named FILENAME.
|
/* Search PATH for a directory containing a file named FILENAME.
|
||||||
The file must be readable, and not a directory.
|
The file must be readable, and not a directory.
|
||||||
If we find one, return its full filename; otherwise, return #f.
|
If we find one, return its full filename; otherwise, return #f.
|
||||||
If FILENAME is absolute, return it unchanged.
|
If FILENAME is absolute, return it unchanged.
|
||||||
If given, EXTENSIONS is a list of strings; for each directory
|
If given, EXTENSIONS is a list of strings; for each directory
|
||||||
in PATH, we search for FILENAME concatenated with each EXTENSION. */
|
in PATH, we search for FILENAME concatenated with each EXTENSION. */
|
||||||
SCM_DEFINE (scm_search_path, "search-path", 2, 1, 0,
|
SCM_DEFINE (scm_search_path, "search-path", 2, 2, 0,
|
||||||
(SCM path, SCM filename, SCM extensions),
|
(SCM path, SCM filename, SCM extensions, SCM require_exts),
|
||||||
"Search @var{path} for a directory containing a file named\n"
|
"Search @var{path} for a directory containing a file named\n"
|
||||||
"@var{filename}. The file must be readable, and not a directory.\n"
|
"@var{filename}. The file must be readable, and not a directory.\n"
|
||||||
"If we find one, return its full filename; otherwise, return\n"
|
"If we find one, return its full filename; otherwise, return\n"
|
||||||
|
@ -316,6 +348,9 @@ SCM_DEFINE (scm_search_path, "search-path", 2, 1, 0,
|
||||||
if (SCM_UNBNDP (extensions))
|
if (SCM_UNBNDP (extensions))
|
||||||
extensions = SCM_EOL;
|
extensions = SCM_EOL;
|
||||||
|
|
||||||
|
if (SCM_UNBNDP (require_exts))
|
||||||
|
require_exts = SCM_BOOL_F;
|
||||||
|
|
||||||
scm_dynwind_begin (0);
|
scm_dynwind_begin (0);
|
||||||
|
|
||||||
filename_chars = scm_to_locale_string (filename);
|
filename_chars = scm_to_locale_string (filename);
|
||||||
|
@ -334,8 +369,14 @@ SCM_DEFINE (scm_search_path, "search-path", 2, 1, 0,
|
||||||
if (filename_len >= 1 && filename_chars[0] == '/')
|
if (filename_len >= 1 && filename_chars[0] == '/')
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
SCM res = filename;
|
||||||
|
if (scm_is_true (require_exts) &&
|
||||||
|
!scm_c_string_has_an_ext (filename_chars, filename_len,
|
||||||
|
extensions))
|
||||||
|
res = SCM_BOOL_F;
|
||||||
|
|
||||||
scm_dynwind_end ();
|
scm_dynwind_end ();
|
||||||
return filename;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If FILENAME has an extension, don't try to add EXTENSIONS to it. */
|
/* If FILENAME has an extension, don't try to add EXTENSIONS to it. */
|
||||||
|
@ -348,6 +389,15 @@ SCM_DEFINE (scm_search_path, "search-path", 2, 1, 0,
|
||||||
{
|
{
|
||||||
if (*endp == '.')
|
if (*endp == '.')
|
||||||
{
|
{
|
||||||
|
if (scm_is_true (require_exts) &&
|
||||||
|
!scm_c_string_has_an_ext (filename_chars, filename_len,
|
||||||
|
extensions))
|
||||||
|
{
|
||||||
|
/* This filename has an extension, but not one of the right
|
||||||
|
ones... */
|
||||||
|
scm_dynwind_end ();
|
||||||
|
return SCM_BOOL_F;
|
||||||
|
}
|
||||||
/* This filename already has an extension, so cancel the
|
/* This filename already has an extension, so cancel the
|
||||||
list of extensions. */
|
list of extensions. */
|
||||||
extensions = SCM_EOL;
|
extensions = SCM_EOL;
|
||||||
|
@ -453,7 +503,7 @@ SCM_DEFINE (scm_sys_search_load_path, "%search-load-path", 1, 0, 0,
|
||||||
SCM_MISC_ERROR ("%load-path is not a proper list", SCM_EOL);
|
SCM_MISC_ERROR ("%load-path is not a proper list", SCM_EOL);
|
||||||
if (scm_ilength (exts) < 0)
|
if (scm_ilength (exts) < 0)
|
||||||
SCM_MISC_ERROR ("%load-extension list is not a proper list", SCM_EOL);
|
SCM_MISC_ERROR ("%load-extension list is not a proper list", SCM_EOL);
|
||||||
return scm_search_path (path, filename, exts);
|
return scm_search_path (path, filename, exts, SCM_UNDEFINED);
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
@ -466,15 +516,51 @@ SCM_DEFINE (scm_primitive_load_path, "primitive-load-path", 1, 0, 0,
|
||||||
"an error is signalled.")
|
"an error is signalled.")
|
||||||
#define FUNC_NAME s_scm_primitive_load_path
|
#define FUNC_NAME s_scm_primitive_load_path
|
||||||
{
|
{
|
||||||
SCM full_filename;
|
SCM full_filename, compiled_filename;
|
||||||
|
|
||||||
full_filename = scm_sys_search_load_path (filename);
|
full_filename = scm_sys_search_load_path (filename);
|
||||||
|
compiled_filename = scm_search_path (*scm_loc_load_path,
|
||||||
|
filename,
|
||||||
|
*scm_loc_load_compiled_extensions,
|
||||||
|
SCM_BOOL_T);
|
||||||
|
|
||||||
if (scm_is_false (full_filename))
|
if (scm_is_false (full_filename) && scm_is_false (compiled_filename))
|
||||||
SCM_MISC_ERROR ("Unable to find file ~S in load path",
|
SCM_MISC_ERROR ("Unable to find file ~S in load path",
|
||||||
scm_list_1 (filename));
|
scm_list_1 (filename));
|
||||||
|
|
||||||
|
if (scm_is_false (compiled_filename))
|
||||||
return scm_primitive_load (full_filename);
|
return scm_primitive_load (full_filename);
|
||||||
|
|
||||||
|
if (scm_is_false (full_filename))
|
||||||
|
return scm_load_compiled_with_vm (compiled_filename);
|
||||||
|
|
||||||
|
{
|
||||||
|
char *source, *compiled;
|
||||||
|
struct stat stat_source, stat_compiled;
|
||||||
|
|
||||||
|
source = scm_to_locale_string (full_filename);
|
||||||
|
compiled = scm_to_locale_string (compiled_filename);
|
||||||
|
|
||||||
|
if (stat (source, &stat_source) == 0
|
||||||
|
&& stat (compiled, &stat_compiled) == 0
|
||||||
|
&& stat_source.st_mtime <= stat_compiled.st_mtime)
|
||||||
|
{
|
||||||
|
free (source);
|
||||||
|
free (compiled);
|
||||||
|
return scm_load_compiled_with_vm (compiled_filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scm_puts (";;; note: source file ", scm_current_error_port ());
|
||||||
|
scm_puts (source, scm_current_error_port ());
|
||||||
|
scm_puts (" newer than compiled ", scm_current_error_port ());
|
||||||
|
scm_puts (compiled, scm_current_error_port ());
|
||||||
|
scm_puts ("\n", scm_current_error_port ());
|
||||||
|
free (source);
|
||||||
|
free (compiled);
|
||||||
|
return scm_primitive_load (full_filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
@ -514,6 +600,9 @@ scm_init_load ()
|
||||||
= SCM_VARIABLE_LOC (scm_c_define ("%load-extensions",
|
= SCM_VARIABLE_LOC (scm_c_define ("%load-extensions",
|
||||||
scm_list_2 (scm_from_locale_string (".scm"),
|
scm_list_2 (scm_from_locale_string (".scm"),
|
||||||
scm_nullstr)));
|
scm_nullstr)));
|
||||||
|
scm_loc_load_compiled_extensions
|
||||||
|
= SCM_VARIABLE_LOC (scm_c_define ("%load-compiled-extensions",
|
||||||
|
scm_list_1 (scm_from_locale_string (".go"))));
|
||||||
scm_loc_load_hook = SCM_VARIABLE_LOC (scm_c_define ("%load-hook", SCM_BOOL_F));
|
scm_loc_load_hook = SCM_VARIABLE_LOC (scm_c_define ("%load-hook", SCM_BOOL_F));
|
||||||
|
|
||||||
the_reader = scm_make_fluid ();
|
the_reader = scm_make_fluid ();
|
||||||
|
|
|
@ -31,7 +31,7 @@ SCM_API SCM scm_c_primitive_load (const char *filename);
|
||||||
SCM_API SCM scm_sys_package_data_dir (void);
|
SCM_API SCM scm_sys_package_data_dir (void);
|
||||||
SCM_API SCM scm_sys_library_dir (void);
|
SCM_API SCM scm_sys_library_dir (void);
|
||||||
SCM_API SCM scm_sys_site_dir (void);
|
SCM_API SCM scm_sys_site_dir (void);
|
||||||
SCM_API SCM scm_search_path (SCM path, SCM filename, SCM exts);
|
SCM_API SCM scm_search_path (SCM path, SCM filename, SCM exts, SCM require_exts);
|
||||||
SCM_API SCM scm_sys_search_load_path (SCM filename);
|
SCM_API SCM scm_sys_search_load_path (SCM filename);
|
||||||
SCM_API SCM scm_primitive_load_path (SCM filename);
|
SCM_API SCM scm_primitive_load_path (SCM filename);
|
||||||
SCM_API SCM scm_c_primitive_load_path (const char *filename);
|
SCM_API SCM scm_c_primitive_load_path (const char *filename);
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "libguile/deprecation.h"
|
#include "libguile/deprecation.h"
|
||||||
|
|
||||||
#include "libguile/validate.h"
|
#include "libguile/validate.h"
|
||||||
|
#include "libguile/programs.h"
|
||||||
#include "libguile/macros.h"
|
#include "libguile/macros.h"
|
||||||
|
|
||||||
#include "libguile/private-options.h"
|
#include "libguile/private-options.h"
|
||||||
|
@ -47,7 +48,7 @@ macro_print (SCM macro, SCM port, scm_print_state *pstate)
|
||||||
|| scm_is_false (scm_printer_apply (SCM_PRINT_CLOSURE,
|
|| scm_is_false (scm_printer_apply (SCM_PRINT_CLOSURE,
|
||||||
macro, port, pstate)))
|
macro, port, pstate)))
|
||||||
{
|
{
|
||||||
if (!SCM_CLOSUREP (code))
|
if (!SCM_CLOSUREP (code) && !SCM_PROGRAM_P (code))
|
||||||
scm_puts ("#<primitive-", port);
|
scm_puts ("#<primitive-", port);
|
||||||
else
|
else
|
||||||
scm_puts ("#<", port);
|
scm_puts ("#<", port);
|
||||||
|
@ -223,9 +224,15 @@ SCM_DEFINE (scm_macro_transformer, "macro-transformer", 1, 0, 0,
|
||||||
"Return the transformer of the macro @var{m}.")
|
"Return the transformer of the macro @var{m}.")
|
||||||
#define FUNC_NAME s_scm_macro_transformer
|
#define FUNC_NAME s_scm_macro_transformer
|
||||||
{
|
{
|
||||||
|
SCM data;
|
||||||
|
|
||||||
SCM_VALIDATE_SMOB (1, m, macro);
|
SCM_VALIDATE_SMOB (1, m, macro);
|
||||||
return ((SCM_CLOSUREP (SCM_PACK (SCM_SMOB_DATA (m)))) ?
|
data = SCM_PACK (SCM_SMOB_DATA (m));
|
||||||
SCM_PACK(SCM_SMOB_DATA (m)) : SCM_BOOL_F);
|
|
||||||
|
if (SCM_CLOSUREP (data) || SCM_PROGRAM_P (data))
|
||||||
|
return data;
|
||||||
|
else
|
||||||
|
return SCM_BOOL_F;
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
|
@ -345,6 +345,8 @@ resolve_duplicate_binding (SCM module, SCM sym,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SCM scm_pre_modules_obarray;
|
||||||
|
|
||||||
/* Lookup SYM as an imported variable of MODULE. */
|
/* Lookup SYM as an imported variable of MODULE. */
|
||||||
static inline SCM
|
static inline SCM
|
||||||
module_imported_variable (SCM module, SCM sym)
|
module_imported_variable (SCM module, SCM sym)
|
||||||
|
@ -471,6 +473,9 @@ SCM_DEFINE (scm_module_variable, "module-variable", 2, 0, 0,
|
||||||
|
|
||||||
SCM_VALIDATE_SYMBOL (2, sym);
|
SCM_VALIDATE_SYMBOL (2, sym);
|
||||||
|
|
||||||
|
if (scm_is_false (module))
|
||||||
|
return scm_hashq_ref (scm_pre_modules_obarray, sym, SCM_UNDEFINED);
|
||||||
|
|
||||||
/* 1. Check module obarray */
|
/* 1. Check module obarray */
|
||||||
var = scm_hashq_ref (SCM_MODULE_OBARRAY (module), sym, SCM_UNDEFINED);
|
var = scm_hashq_ref (SCM_MODULE_OBARRAY (module), sym, SCM_UNDEFINED);
|
||||||
if (SCM_BOUND_THING_P (var))
|
if (SCM_BOUND_THING_P (var))
|
||||||
|
@ -624,6 +629,25 @@ SCM_DEFINE (scm_module_import_interface, "module-import-interface", 2, 0, 0,
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_SYMBOL (sym_sys_module_public_interface, "%module-public-interface");
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_module_public_interface, "module-public-interface", 1, 0, 0,
|
||||||
|
(SCM module),
|
||||||
|
"Return the public interface of @var{module}.\n\n"
|
||||||
|
"If @var{module} has no public interface, @code{#f} is returned.")
|
||||||
|
#define FUNC_NAME s_scm_module_public_interface
|
||||||
|
{
|
||||||
|
SCM var;
|
||||||
|
|
||||||
|
SCM_VALIDATE_MODULE (1, module);
|
||||||
|
var = scm_module_local_variable (module, sym_sys_module_public_interface);
|
||||||
|
if (scm_is_true (var))
|
||||||
|
return SCM_VARIABLE_REF (var);
|
||||||
|
else
|
||||||
|
return SCM_BOOL_F;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
/* scm_sym2var
|
/* scm_sym2var
|
||||||
*
|
*
|
||||||
* looks up the variable bound to SYM according to PROC. PROC should be
|
* looks up the variable bound to SYM according to PROC. PROC should be
|
||||||
|
@ -637,8 +661,6 @@ SCM_DEFINE (scm_module_import_interface, "module-import-interface", 2, 0, 0,
|
||||||
* the scm_pre_modules_obarray (a `eq' hash table).
|
* the scm_pre_modules_obarray (a `eq' hash table).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SCM scm_pre_modules_obarray;
|
|
||||||
|
|
||||||
SCM
|
SCM
|
||||||
scm_sym2var (SCM sym, SCM proc, SCM definep)
|
scm_sym2var (SCM sym, SCM proc, SCM definep)
|
||||||
#define FUNC_NAME "scm_sym2var"
|
#define FUNC_NAME "scm_sym2var"
|
||||||
|
|
|
@ -100,6 +100,7 @@ SCM_API void scm_c_export (const char *name, ...);
|
||||||
|
|
||||||
SCM_API SCM scm_sym2var (SCM sym, SCM thunk, SCM definep);
|
SCM_API SCM scm_sym2var (SCM sym, SCM thunk, SCM definep);
|
||||||
|
|
||||||
|
SCM_API SCM scm_module_public_interface (SCM module);
|
||||||
SCM_API SCM scm_module_import_interface (SCM module, SCM sym);
|
SCM_API SCM scm_module_import_interface (SCM module, SCM sym);
|
||||||
SCM_API SCM scm_module_lookup_closure (SCM module);
|
SCM_API SCM scm_module_lookup_closure (SCM module);
|
||||||
SCM_API SCM scm_module_transformer (SCM module);
|
SCM_API SCM scm_module_transformer (SCM module);
|
||||||
|
|
296
libguile/objcodes.c
Normal file
296
libguile/objcodes.c
Normal file
|
@ -0,0 +1,296 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
#if HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "vm-bootstrap.h"
|
||||||
|
#include "programs.h"
|
||||||
|
#include "objcodes.h"
|
||||||
|
|
||||||
|
/* nb, the length of the header should be a multiple of 8 bytes */
|
||||||
|
#define OBJCODE_COOKIE "GOOF-0.5"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Objcode type
|
||||||
|
*/
|
||||||
|
|
||||||
|
scm_t_bits scm_tc16_objcode;
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
make_objcode_by_mmap (int fd)
|
||||||
|
#define FUNC_NAME "make_objcode_by_mmap"
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
char *addr;
|
||||||
|
struct stat st;
|
||||||
|
SCM sret = SCM_BOOL_F;
|
||||||
|
struct scm_objcode *data;
|
||||||
|
|
||||||
|
ret = fstat (fd, &st);
|
||||||
|
if (ret < 0)
|
||||||
|
SCM_SYSERROR;
|
||||||
|
|
||||||
|
if (st.st_size <= sizeof (struct scm_objcode) + strlen (OBJCODE_COOKIE))
|
||||||
|
scm_misc_error (FUNC_NAME, "object file too small (~a bytes)",
|
||||||
|
SCM_LIST1 (SCM_I_MAKINUM (st.st_size)));
|
||||||
|
|
||||||
|
addr = mmap (0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
|
if (addr == MAP_FAILED)
|
||||||
|
SCM_SYSERROR;
|
||||||
|
|
||||||
|
if (memcmp (addr, OBJCODE_COOKIE, strlen (OBJCODE_COOKIE)))
|
||||||
|
SCM_SYSERROR;
|
||||||
|
|
||||||
|
data = (struct scm_objcode*)(addr + strlen (OBJCODE_COOKIE));
|
||||||
|
|
||||||
|
if (data->len + data->metalen != (st.st_size - sizeof (*data) - strlen (OBJCODE_COOKIE)))
|
||||||
|
scm_misc_error (FUNC_NAME, "bad length header (~a, ~a)",
|
||||||
|
SCM_LIST2 (scm_from_size_t (st.st_size),
|
||||||
|
scm_from_uint32 (sizeof (*data) + data->len + data->metalen)));
|
||||||
|
|
||||||
|
SCM_NEWSMOB3 (sret, scm_tc16_objcode, addr + strlen (OBJCODE_COOKIE),
|
||||||
|
SCM_PACK (SCM_BOOL_F), fd);
|
||||||
|
SCM_SET_SMOB_FLAGS (sret, SCM_F_OBJCODE_IS_MMAP);
|
||||||
|
|
||||||
|
/* FIXME: we leak ourselves and the file descriptor. but then again so does
|
||||||
|
dlopen(). */
|
||||||
|
return scm_permanent_object (sret);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM
|
||||||
|
scm_c_make_objcode_slice (SCM parent, scm_t_uint8 *ptr)
|
||||||
|
#define FUNC_NAME "make-objcode-slice"
|
||||||
|
{
|
||||||
|
struct scm_objcode *data, *parent_data;
|
||||||
|
SCM ret;
|
||||||
|
|
||||||
|
SCM_VALIDATE_OBJCODE (1, parent);
|
||||||
|
parent_data = SCM_OBJCODE_DATA (parent);
|
||||||
|
|
||||||
|
if (ptr < parent_data->base
|
||||||
|
|| ptr >= (parent_data->base + parent_data->len + parent_data->metalen
|
||||||
|
- sizeof (struct scm_objcode)))
|
||||||
|
scm_misc_error (FUNC_NAME, "offset out of bounds (~a vs ~a + ~a + ~a)",
|
||||||
|
SCM_LIST4 (scm_from_ulong ((unsigned long)ptr),
|
||||||
|
scm_from_ulong ((unsigned long)parent_data->base),
|
||||||
|
scm_from_uint32 (parent_data->len),
|
||||||
|
scm_from_uint32 (parent_data->metalen)));
|
||||||
|
|
||||||
|
data = (struct scm_objcode*)ptr;
|
||||||
|
if (data->base + data->len + data->metalen > parent_data->base + parent_data->len + parent_data->metalen)
|
||||||
|
abort ();
|
||||||
|
|
||||||
|
SCM_NEWSMOB2 (ret, scm_tc16_objcode, data, parent);
|
||||||
|
SCM_SET_SMOB_FLAGS (ret, SCM_F_OBJCODE_IS_SLICE);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
objcode_mark (SCM obj)
|
||||||
|
{
|
||||||
|
return SCM_SMOB_OBJECT_2 (obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scheme interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_objcode_p, "objcode?", 1, 0, 0,
|
||||||
|
(SCM obj),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_objcode_p
|
||||||
|
{
|
||||||
|
return SCM_BOOL (SCM_OBJCODE_P (obj));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_objcode_meta, "objcode-meta", 1, 0, 0,
|
||||||
|
(SCM objcode),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_objcode_meta
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_OBJCODE (1, objcode);
|
||||||
|
|
||||||
|
if (SCM_OBJCODE_META_LEN (objcode) == 0)
|
||||||
|
return SCM_BOOL_F;
|
||||||
|
else
|
||||||
|
return scm_c_make_objcode_slice (objcode, (SCM_OBJCODE_BASE (objcode)
|
||||||
|
+ SCM_OBJCODE_LEN (objcode)));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_bytecode_to_objcode, "bytecode->objcode", 1, 0, 0,
|
||||||
|
(SCM bytecode),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_bytecode_to_objcode
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
ssize_t increment;
|
||||||
|
scm_t_array_handle handle;
|
||||||
|
const scm_t_uint8 *c_bytecode;
|
||||||
|
struct scm_objcode *data;
|
||||||
|
SCM objcode;
|
||||||
|
|
||||||
|
if (scm_is_false (scm_u8vector_p (bytecode)))
|
||||||
|
scm_wrong_type_arg (FUNC_NAME, 1, bytecode);
|
||||||
|
|
||||||
|
c_bytecode = scm_u8vector_elements (bytecode, &handle, &size, &increment);
|
||||||
|
data = (struct scm_objcode*)c_bytecode;
|
||||||
|
SCM_NEWSMOB2 (objcode, scm_tc16_objcode, data, bytecode);
|
||||||
|
scm_array_handle_release (&handle);
|
||||||
|
|
||||||
|
SCM_ASSERT_RANGE (0, bytecode, size >= sizeof(struct scm_objcode));
|
||||||
|
if (data->len + data->metalen != (size - sizeof (*data)))
|
||||||
|
scm_misc_error (FUNC_NAME, "bad u8vector size (~a != ~a)",
|
||||||
|
SCM_LIST2 (scm_from_size_t (size),
|
||||||
|
scm_from_uint32 (sizeof (*data) + data->len + data->metalen)));
|
||||||
|
assert (increment == 1);
|
||||||
|
SCM_SET_SMOB_FLAGS (objcode, SCM_F_OBJCODE_IS_U8VECTOR);
|
||||||
|
|
||||||
|
/* foolishly, we assume that as long as bytecode is around, that c_bytecode
|
||||||
|
will be of the same length; perhaps a bad assumption? */
|
||||||
|
|
||||||
|
return objcode;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_load_objcode, "load-objcode", 1, 0, 0,
|
||||||
|
(SCM file),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_load_objcode
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
char *c_file;
|
||||||
|
|
||||||
|
SCM_VALIDATE_STRING (1, file);
|
||||||
|
|
||||||
|
c_file = scm_to_locale_string (file);
|
||||||
|
fd = open (c_file, O_RDONLY);
|
||||||
|
free (c_file);
|
||||||
|
if (fd < 0) SCM_SYSERROR;
|
||||||
|
|
||||||
|
return make_objcode_by_mmap (fd);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_objcode_to_bytecode, "objcode->bytecode", 1, 0, 0,
|
||||||
|
(SCM objcode),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_objcode_to_bytecode
|
||||||
|
{
|
||||||
|
scm_t_uint8 *u8vector;
|
||||||
|
scm_t_uint32 len;
|
||||||
|
|
||||||
|
SCM_VALIDATE_OBJCODE (1, objcode);
|
||||||
|
|
||||||
|
len = sizeof(struct scm_objcode) + SCM_OBJCODE_TOTAL_LEN (objcode);
|
||||||
|
/* FIXME: Is `gc_malloc' ok here? */
|
||||||
|
u8vector = scm_gc_malloc (len, "objcode-u8vector");
|
||||||
|
memcpy (u8vector, SCM_OBJCODE_DATA (objcode), len);
|
||||||
|
|
||||||
|
return scm_take_u8vector (u8vector, len);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_write_objcode, "write-objcode", 2, 0, 0,
|
||||||
|
(SCM objcode, SCM port),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_write_objcode
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_OBJCODE (1, objcode);
|
||||||
|
SCM_VALIDATE_OUTPUT_PORT (2, port);
|
||||||
|
|
||||||
|
scm_c_write (port, OBJCODE_COOKIE, strlen (OBJCODE_COOKIE));
|
||||||
|
scm_c_write (port, SCM_OBJCODE_DATA (objcode),
|
||||||
|
sizeof (struct scm_objcode) + SCM_OBJCODE_TOTAL_LEN (objcode));
|
||||||
|
|
||||||
|
return SCM_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
scm_bootstrap_objcodes (void)
|
||||||
|
{
|
||||||
|
scm_tc16_objcode = scm_make_smob_type ("objcode", 0);
|
||||||
|
scm_set_smob_mark (scm_tc16_objcode, objcode_mark);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Before, we used __BYTE_ORDER, but that is not defined on all
|
||||||
|
systems. So punt and use automake, PDP endianness be damned. */
|
||||||
|
#ifdef WORDS_BIGENDIAN
|
||||||
|
#define SCM_BYTE_ORDER 4321
|
||||||
|
#else
|
||||||
|
#define SCM_BYTE_ORDER 1234
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
scm_init_objcodes (void)
|
||||||
|
{
|
||||||
|
scm_bootstrap_vm ();
|
||||||
|
|
||||||
|
#ifndef SCM_MAGIC_SNARFER
|
||||||
|
#include "libguile/objcodes.x"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
scm_c_define ("word-size", scm_from_size_t (sizeof(SCM)));
|
||||||
|
scm_c_define ("byte-order", scm_from_uint16 (SCM_BYTE_ORDER));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
99
libguile/objcodes.h
Normal file
99
libguile/objcodes.h
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
#ifndef _SCM_OBJCODES_H_
|
||||||
|
#define _SCM_OBJCODES_H_
|
||||||
|
|
||||||
|
#include <libguile.h>
|
||||||
|
|
||||||
|
/* objcode data should be directly mappable to this C structure. */
|
||||||
|
struct scm_objcode {
|
||||||
|
scm_t_uint8 nargs;
|
||||||
|
scm_t_uint8 nrest;
|
||||||
|
scm_t_uint8 nlocs;
|
||||||
|
scm_t_uint8 nexts;
|
||||||
|
scm_t_uint32 len; /* the maximum index of base[] */
|
||||||
|
scm_t_uint32 metalen; /* well, i lie. this many bytes at the end of
|
||||||
|
base[] for metadata */
|
||||||
|
scm_t_uint8 base[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SCM_F_OBJCODE_IS_MMAP (1<<0)
|
||||||
|
#define SCM_F_OBJCODE_IS_U8VECTOR (1<<1)
|
||||||
|
#define SCM_F_OBJCODE_IS_SLICE (1<<2)
|
||||||
|
|
||||||
|
extern scm_t_bits scm_tc16_objcode;
|
||||||
|
|
||||||
|
#define SCM_OBJCODE_P(x) (SCM_SMOB_PREDICATE (scm_tc16_objcode, x))
|
||||||
|
#define SCM_OBJCODE_DATA(x) ((struct scm_objcode *) SCM_SMOB_DATA (x))
|
||||||
|
#define SCM_VALIDATE_OBJCODE(p,x) SCM_MAKE_VALIDATE (p, x, OBJCODE_P)
|
||||||
|
|
||||||
|
#define SCM_OBJCODE_LEN(x) (SCM_OBJCODE_DATA (x)->len)
|
||||||
|
#define SCM_OBJCODE_META_LEN(x) (SCM_OBJCODE_DATA (x)->metalen)
|
||||||
|
#define SCM_OBJCODE_TOTAL_LEN(x) (SCM_OBJCODE_LEN (x) + SCM_OBJCODE_META_LEN (x))
|
||||||
|
#define SCM_OBJCODE_NARGS(x) (SCM_OBJCODE_DATA (x)->nargs)
|
||||||
|
#define SCM_OBJCODE_NREST(x) (SCM_OBJCODE_DATA (x)->nrest)
|
||||||
|
#define SCM_OBJCODE_NLOCS(x) (SCM_OBJCODE_DATA (x)->nlocs)
|
||||||
|
#define SCM_OBJCODE_NEXTS(x) (SCM_OBJCODE_DATA (x)->nexts)
|
||||||
|
#define SCM_OBJCODE_BASE(x) (SCM_OBJCODE_DATA (x)->base)
|
||||||
|
|
||||||
|
#define SCM_OBJCODE_IS_MMAP(x) (SCM_SMOB_FLAGS (x) & SCM_F_OBJCODE_IS_MMAP)
|
||||||
|
#define SCM_OBJCODE_IS_U8VECTOR(x) (SCM_SMOB_FLAGS (x) & SCM_F_OBJCODE_IS_U8VECTOR)
|
||||||
|
#define SCM_OBJCODE_IS_SLICE(x) (SCM_SMOB_FLAGS (x) & SCM_F_OBJCODE_IS_SLICE)
|
||||||
|
|
||||||
|
SCM scm_c_make_objcode_slice (SCM parent, scm_t_uint8 *ptr);
|
||||||
|
extern SCM scm_load_objcode (SCM file);
|
||||||
|
extern SCM scm_objcode_p (SCM obj);
|
||||||
|
extern SCM scm_objcode_meta (SCM objcode);
|
||||||
|
extern SCM scm_bytecode_to_objcode (SCM bytecode);
|
||||||
|
extern SCM scm_objcode_to_bytecode (SCM objcode);
|
||||||
|
extern SCM scm_write_objcode (SCM objcode, SCM port);
|
||||||
|
|
||||||
|
extern void scm_bootstrap_objcodes (void);
|
||||||
|
extern void scm_init_objcodes (void);
|
||||||
|
|
||||||
|
#endif /* _SCM_OBJCODES_H_ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
|
@ -39,6 +39,8 @@
|
||||||
#include "libguile/ports.h"
|
#include "libguile/ports.h"
|
||||||
#include "libguile/strings.h"
|
#include "libguile/strings.h"
|
||||||
#include "libguile/vectors.h"
|
#include "libguile/vectors.h"
|
||||||
|
#include "libguile/programs.h"
|
||||||
|
#include "libguile/vm.h"
|
||||||
|
|
||||||
#include "libguile/validate.h"
|
#include "libguile/validate.h"
|
||||||
#include "libguile/objects.h"
|
#include "libguile/objects.h"
|
||||||
|
@ -138,8 +140,9 @@ scm_mcache_lookup_cmethod (SCM cache, SCM args)
|
||||||
z = SCM_CDR (z);
|
z = SCM_CDR (z);
|
||||||
}
|
}
|
||||||
while (j-- && !scm_is_null (ls));
|
while (j-- && !scm_is_null (ls));
|
||||||
/* Fewer arguments than specifiers => CAR != ENV */
|
/* Fewer arguments than specifiers => CAR != CLASS or `no-method' */
|
||||||
if (scm_is_null (SCM_CAR (z)) || scm_is_pair (SCM_CAR (z)))
|
if (!scm_is_pair (z)
|
||||||
|
|| (!SCM_CLASSP (SCM_CAR (z)) && !scm_is_symbol (SCM_CAR (z))))
|
||||||
return z;
|
return z;
|
||||||
next_method:
|
next_method:
|
||||||
i = (i + 1) & mask;
|
i = (i + 1) & mask;
|
||||||
|
@ -161,10 +164,15 @@ SCM
|
||||||
scm_apply_generic (SCM gf, SCM args)
|
scm_apply_generic (SCM gf, SCM args)
|
||||||
{
|
{
|
||||||
SCM cmethod = scm_mcache_compute_cmethod (SCM_ENTITY_PROCEDURE (gf), args);
|
SCM cmethod = scm_mcache_compute_cmethod (SCM_ENTITY_PROCEDURE (gf), args);
|
||||||
|
if (SCM_PROGRAM_P (cmethod))
|
||||||
|
return scm_vm_apply (scm_the_vm (), cmethod, args);
|
||||||
|
else if (scm_is_pair (cmethod))
|
||||||
return scm_eval_body (SCM_CDR (SCM_CMETHOD_CODE (cmethod)),
|
return scm_eval_body (SCM_CDR (SCM_CMETHOD_CODE (cmethod)),
|
||||||
SCM_EXTEND_ENV (SCM_CAR (SCM_CMETHOD_CODE (cmethod)),
|
SCM_EXTEND_ENV (SCM_CAR (SCM_CMETHOD_CODE (cmethod)),
|
||||||
args,
|
args,
|
||||||
SCM_CMETHOD_ENV (cmethod)));
|
SCM_CMETHOD_ENV (cmethod)));
|
||||||
|
else
|
||||||
|
return scm_apply (cmethod, args, SCM_EOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
SCM
|
SCM
|
||||||
|
|
|
@ -31,6 +31,9 @@
|
||||||
|
|
||||||
#include "libguile/validate.h"
|
#include "libguile/validate.h"
|
||||||
#include "libguile/procs.h"
|
#include "libguile/procs.h"
|
||||||
|
#include "libguile/procprop.h"
|
||||||
|
#include "libguile/objcodes.h"
|
||||||
|
#include "libguile/programs.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -138,7 +141,9 @@ SCM_DEFINE (scm_thunk_p, "thunk?", 1, 0, 0,
|
||||||
obj = SCM_PROCEDURE (obj);
|
obj = SCM_PROCEDURE (obj);
|
||||||
goto again;
|
goto again;
|
||||||
default:
|
default:
|
||||||
;
|
if (SCM_PROGRAM_P (obj) && SCM_PROGRAM_DATA (obj)->nargs == 0)
|
||||||
|
return SCM_BOOL_T;
|
||||||
|
/* otherwise fall through */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return SCM_BOOL_F;
|
return SCM_BOOL_F;
|
||||||
|
@ -208,11 +213,25 @@ SCM_DEFINE (scm_make_procedure_with_setter, "make-procedure-with-setter", 2, 0,
|
||||||
"with the associated setter @var{setter}.")
|
"with the associated setter @var{setter}.")
|
||||||
#define FUNC_NAME s_scm_make_procedure_with_setter
|
#define FUNC_NAME s_scm_make_procedure_with_setter
|
||||||
{
|
{
|
||||||
|
SCM name, ret;
|
||||||
SCM_VALIDATE_PROC (1, procedure);
|
SCM_VALIDATE_PROC (1, procedure);
|
||||||
SCM_VALIDATE_PROC (2, setter);
|
SCM_VALIDATE_PROC (2, setter);
|
||||||
return scm_double_cell (scm_tc7_pws,
|
ret = scm_double_cell (scm_tc7_pws,
|
||||||
SCM_UNPACK (procedure),
|
SCM_UNPACK (procedure),
|
||||||
SCM_UNPACK (setter), 0);
|
SCM_UNPACK (setter), 0);
|
||||||
|
/* don't use procedure_name, because don't care enough to do a reverse
|
||||||
|
lookup */
|
||||||
|
switch (SCM_TYP7 (procedure)) {
|
||||||
|
case scm_tcs_subrs:
|
||||||
|
name = SCM_SNAME (procedure);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
name = scm_procedure_property (procedure, scm_sym_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (scm_is_true (name))
|
||||||
|
scm_set_procedure_property_x (ret, scm_sym_name, name);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
387
libguile/programs.c
Normal file
387
libguile/programs.c
Normal file
|
@ -0,0 +1,387 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
#if HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "vm-bootstrap.h"
|
||||||
|
#include "instructions.h"
|
||||||
|
#include "modules.h"
|
||||||
|
#include "programs.h"
|
||||||
|
#include "procprop.h" // scm_sym_name
|
||||||
|
#include "srcprop.h" // scm_sym_filename
|
||||||
|
#include "vm.h"
|
||||||
|
|
||||||
|
|
||||||
|
scm_t_bits scm_tc16_program;
|
||||||
|
|
||||||
|
static SCM write_program = SCM_BOOL_F;
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_make_program, "make-program", 1, 2, 0,
|
||||||
|
(SCM objcode, SCM objtable, SCM external),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_make_program
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_OBJCODE (1, objcode);
|
||||||
|
if (SCM_UNLIKELY (SCM_UNBNDP (objtable)))
|
||||||
|
objtable = SCM_BOOL_F;
|
||||||
|
else if (scm_is_true (objtable))
|
||||||
|
SCM_VALIDATE_VECTOR (2, objtable);
|
||||||
|
if (SCM_UNLIKELY (SCM_UNBNDP (external)))
|
||||||
|
external = SCM_EOL;
|
||||||
|
else
|
||||||
|
/* FIXME: currently this test is quite expensive (can be 2-3% of total
|
||||||
|
execution time in programs that make many closures). We could remove it,
|
||||||
|
yes, but we'd get much better gains if we used some other method, like
|
||||||
|
just capturing the variables that we need instead of all heap-allocated
|
||||||
|
variables. Dunno. Keeping the check for now, as it's a user-callable
|
||||||
|
function, and inlining the op in the vm's make-closure operation. */
|
||||||
|
SCM_VALIDATE_LIST (3, external);
|
||||||
|
|
||||||
|
SCM_RETURN_NEWSMOB3 (scm_tc16_program, objcode, objtable, external);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
program_mark (SCM obj)
|
||||||
|
{
|
||||||
|
if (scm_is_true (SCM_PROGRAM_OBJTABLE (obj)))
|
||||||
|
scm_gc_mark (SCM_PROGRAM_OBJTABLE (obj));
|
||||||
|
if (!scm_is_null (SCM_PROGRAM_EXTERNALS (obj)))
|
||||||
|
scm_gc_mark (SCM_PROGRAM_EXTERNALS (obj));
|
||||||
|
return SCM_PROGRAM_OBJCODE (obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
program_apply (SCM program, SCM args)
|
||||||
|
{
|
||||||
|
return scm_vm_apply (scm_the_vm (), program, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
program_apply_0 (SCM program)
|
||||||
|
{
|
||||||
|
return scm_c_vm_run (scm_the_vm (), program, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
program_apply_1 (SCM program, SCM a)
|
||||||
|
{
|
||||||
|
return scm_c_vm_run (scm_the_vm (), program, &a, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
program_apply_2 (SCM program, SCM a, SCM b)
|
||||||
|
{
|
||||||
|
SCM args[2];
|
||||||
|
args[0] = a;
|
||||||
|
args[1] = b;
|
||||||
|
return scm_c_vm_run (scm_the_vm (), program, args, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
program_print (SCM program, SCM port, scm_print_state *pstate)
|
||||||
|
{
|
||||||
|
static int print_error = 0;
|
||||||
|
|
||||||
|
if (SCM_FALSEP (write_program) && scm_module_system_booted_p)
|
||||||
|
write_program = scm_module_local_variable
|
||||||
|
(scm_c_resolve_module ("system vm program"),
|
||||||
|
scm_from_locale_symbol ("write-program"));
|
||||||
|
|
||||||
|
if (SCM_FALSEP (write_program) || print_error)
|
||||||
|
return scm_smob_print (program, port, pstate);
|
||||||
|
|
||||||
|
print_error = 1;
|
||||||
|
scm_call_2 (SCM_VARIABLE_REF (write_program), program, port);
|
||||||
|
print_error = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scheme interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_p, "program?", 1, 0, 0,
|
||||||
|
(SCM obj),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_program_p
|
||||||
|
{
|
||||||
|
return SCM_BOOL (SCM_PROGRAM_P (obj));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_base, "program-base", 1, 0, 0,
|
||||||
|
(SCM program),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_program_base
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
|
||||||
|
return scm_from_ulong ((unsigned long) SCM_PROGRAM_DATA (program)->base);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_arity, "program-arity", 1, 0, 0,
|
||||||
|
(SCM program),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_program_arity
|
||||||
|
{
|
||||||
|
struct scm_objcode *p;
|
||||||
|
|
||||||
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
|
||||||
|
p = SCM_PROGRAM_DATA (program);
|
||||||
|
return SCM_LIST4 (SCM_I_MAKINUM (p->nargs),
|
||||||
|
SCM_I_MAKINUM (p->nrest),
|
||||||
|
SCM_I_MAKINUM (p->nlocs),
|
||||||
|
SCM_I_MAKINUM (p->nexts));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_objects, "program-objects", 1, 0, 0,
|
||||||
|
(SCM program),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_program_objects
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
return SCM_PROGRAM_OBJTABLE (program);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_module, "program-module", 1, 0, 0,
|
||||||
|
(SCM program),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_program_module
|
||||||
|
{
|
||||||
|
SCM objs;
|
||||||
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
objs = SCM_PROGRAM_OBJTABLE (program);
|
||||||
|
return scm_is_true (objs) ? scm_c_vector_ref (objs, 0) : SCM_BOOL_F;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_meta, "program-meta", 1, 0, 0,
|
||||||
|
(SCM program),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_program_meta
|
||||||
|
{
|
||||||
|
SCM metaobj;
|
||||||
|
|
||||||
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
|
||||||
|
metaobj = scm_objcode_meta (SCM_PROGRAM_OBJCODE (program));
|
||||||
|
if (scm_is_true (metaobj))
|
||||||
|
return scm_make_program (metaobj, SCM_BOOL_F, SCM_EOL);
|
||||||
|
else
|
||||||
|
return SCM_BOOL_F;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_bindings, "program-bindings", 1, 0, 0,
|
||||||
|
(SCM program),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_program_bindings
|
||||||
|
{
|
||||||
|
SCM meta;
|
||||||
|
|
||||||
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
|
||||||
|
meta = scm_program_meta (program);
|
||||||
|
if (scm_is_false (meta))
|
||||||
|
return SCM_BOOL_F;
|
||||||
|
|
||||||
|
return scm_car (scm_call_0 (meta));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_sources, "program-sources", 1, 0, 0,
|
||||||
|
(SCM program),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_program_sources
|
||||||
|
{
|
||||||
|
SCM meta, sources, ret, filename;
|
||||||
|
|
||||||
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
|
||||||
|
meta = scm_program_meta (program);
|
||||||
|
if (scm_is_false (meta))
|
||||||
|
return SCM_EOL;
|
||||||
|
|
||||||
|
filename = SCM_BOOL_F;
|
||||||
|
ret = SCM_EOL;
|
||||||
|
for (sources = scm_cadr (scm_call_0 (meta)); !scm_is_null (sources);
|
||||||
|
sources = scm_cdr (sources))
|
||||||
|
{
|
||||||
|
SCM x = scm_car (sources);
|
||||||
|
if (scm_is_pair (x))
|
||||||
|
{
|
||||||
|
if (scm_is_number (scm_car (x)))
|
||||||
|
{
|
||||||
|
SCM addr = scm_car (x);
|
||||||
|
ret = scm_acons (addr, scm_cons (filename, scm_cdr (x)),
|
||||||
|
ret);
|
||||||
|
}
|
||||||
|
else if (scm_is_eq (scm_car (x), scm_sym_filename))
|
||||||
|
filename = scm_cdr (x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scm_reverse_x (ret, SCM_UNDEFINED);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_properties, "program-properties", 1, 0, 0,
|
||||||
|
(SCM program),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_program_properties
|
||||||
|
{
|
||||||
|
SCM meta;
|
||||||
|
|
||||||
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
|
||||||
|
meta = scm_program_meta (program);
|
||||||
|
if (scm_is_false (meta))
|
||||||
|
return SCM_EOL;
|
||||||
|
|
||||||
|
return scm_cddr (scm_call_0 (meta));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_name, "program-name", 1, 0, 0,
|
||||||
|
(SCM program),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_program_name
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
return scm_assq_ref (scm_program_properties (program), scm_sym_name);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_source, "program-source", 2, 0, 0,
|
||||||
|
(SCM program, SCM ip),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_program_source
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
return scm_c_program_source (program, scm_to_size_t (ip));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
extern SCM
|
||||||
|
scm_c_program_source (SCM program, size_t ip)
|
||||||
|
{
|
||||||
|
SCM sources, source = SCM_BOOL_F;
|
||||||
|
|
||||||
|
for (sources = scm_program_sources (program);
|
||||||
|
!scm_is_null (sources)
|
||||||
|
&& scm_to_size_t (scm_caar (sources)) <= ip;
|
||||||
|
sources = scm_cdr (sources))
|
||||||
|
source = scm_car (sources);
|
||||||
|
|
||||||
|
return source; /* (addr . (filename . (line . column))) */
|
||||||
|
}
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_external, "program-external", 1, 0, 0,
|
||||||
|
(SCM program),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_program_external
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
return SCM_PROGRAM_EXTERNALS (program);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_external_set_x, "program-external-set!", 2, 0, 0,
|
||||||
|
(SCM program, SCM external),
|
||||||
|
"Modify the list of closure variables of @var{program} (for "
|
||||||
|
"debugging purposes).")
|
||||||
|
#define FUNC_NAME s_scm_program_external_set_x
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
SCM_VALIDATE_LIST (2, external);
|
||||||
|
SCM_PROGRAM_EXTERNALS (program) = external;
|
||||||
|
return SCM_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_program_objcode, "program-objcode", 1, 0, 0,
|
||||||
|
(SCM program),
|
||||||
|
"Return a @var{program}'s object code.")
|
||||||
|
#define FUNC_NAME s_scm_program_objcode
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_PROGRAM (1, program);
|
||||||
|
|
||||||
|
return SCM_PROGRAM_OBJCODE (program);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
scm_bootstrap_programs (void)
|
||||||
|
{
|
||||||
|
scm_tc16_program = scm_make_smob_type ("program", 0);
|
||||||
|
scm_set_smob_mark (scm_tc16_program, program_mark);
|
||||||
|
scm_set_smob_apply (scm_tc16_program, program_apply, 0, 0, 1);
|
||||||
|
scm_smobs[SCM_TC2SMOBNUM (scm_tc16_program)].apply_0 = program_apply_0;
|
||||||
|
scm_smobs[SCM_TC2SMOBNUM (scm_tc16_program)].apply_1 = program_apply_1;
|
||||||
|
scm_smobs[SCM_TC2SMOBNUM (scm_tc16_program)].apply_2 = program_apply_2;
|
||||||
|
scm_set_smob_print (scm_tc16_program, program_print);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scm_init_programs (void)
|
||||||
|
{
|
||||||
|
scm_bootstrap_vm ();
|
||||||
|
|
||||||
|
#ifndef SCM_MAGIC_SNARFER
|
||||||
|
#include "libguile/programs.x"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
94
libguile/programs.h
Normal file
94
libguile/programs.h
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
#ifndef _SCM_PROGRAMS_H_
|
||||||
|
#define _SCM_PROGRAMS_H_
|
||||||
|
|
||||||
|
#include <libguile.h>
|
||||||
|
#include <libguile/objcodes.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Programs
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef unsigned char scm_byte_t;
|
||||||
|
|
||||||
|
extern scm_t_bits scm_tc16_program;
|
||||||
|
|
||||||
|
#define SCM_F_PROGRAM_IS_BOOT (1<<0)
|
||||||
|
|
||||||
|
#define SCM_PROGRAM_P(x) (SCM_SMOB_PREDICATE (scm_tc16_program, x))
|
||||||
|
#define SCM_PROGRAM_OBJCODE(x) (SCM_SMOB_OBJECT (x))
|
||||||
|
#define SCM_PROGRAM_OBJTABLE(x) (SCM_SMOB_OBJECT_2 (x))
|
||||||
|
#define SCM_PROGRAM_EXTERNALS(x) (SCM_SMOB_OBJECT_3 (x))
|
||||||
|
#define SCM_PROGRAM_DATA(x) (SCM_OBJCODE_DATA (SCM_PROGRAM_OBJCODE (x)))
|
||||||
|
#define SCM_VALIDATE_PROGRAM(p,x) SCM_MAKE_VALIDATE (p, x, PROGRAM_P)
|
||||||
|
#define SCM_PROGRAM_IS_BOOT(x) (SCM_SMOB_FLAGS (x) & SCM_F_PROGRAM_IS_BOOT)
|
||||||
|
|
||||||
|
extern SCM scm_make_program (SCM objcode, SCM objtable, SCM externals);
|
||||||
|
|
||||||
|
extern SCM scm_program_p (SCM obj);
|
||||||
|
extern SCM scm_program_base (SCM program);
|
||||||
|
extern SCM scm_program_arity (SCM program);
|
||||||
|
extern SCM scm_program_meta (SCM program);
|
||||||
|
extern SCM scm_program_bindings (SCM program);
|
||||||
|
extern SCM scm_program_sources (SCM program);
|
||||||
|
extern SCM scm_program_source (SCM program, SCM ip);
|
||||||
|
extern SCM scm_program_properties (SCM program);
|
||||||
|
extern SCM scm_program_name (SCM program);
|
||||||
|
extern SCM scm_program_objects (SCM program);
|
||||||
|
extern SCM scm_program_module (SCM program);
|
||||||
|
extern SCM scm_program_external (SCM program);
|
||||||
|
extern SCM scm_program_external_set_x (SCM program, SCM external);
|
||||||
|
extern SCM scm_program_objcode (SCM program);
|
||||||
|
|
||||||
|
extern SCM scm_c_program_source (SCM program, size_t ip);
|
||||||
|
|
||||||
|
extern void scm_bootstrap_programs (void);
|
||||||
|
extern void scm_init_programs (void);
|
||||||
|
|
||||||
|
#endif /* _SCM_PROGRAMS_H_ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
|
@ -32,6 +32,8 @@
|
||||||
#include "libguile/modules.h"
|
#include "libguile/modules.h"
|
||||||
#include "libguile/root.h"
|
#include "libguile/root.h"
|
||||||
#include "libguile/strings.h"
|
#include "libguile/strings.h"
|
||||||
|
#include "libguile/vm.h" /* to capture vm stacks */
|
||||||
|
#include "libguile/frames.h" /* vm frames */
|
||||||
|
|
||||||
#include "libguile/validate.h"
|
#include "libguile/validate.h"
|
||||||
#include "libguile/stacks.h"
|
#include "libguile/stacks.h"
|
||||||
|
@ -123,19 +125,17 @@
|
||||||
#define RELOC_FRAME(ptr, offset) \
|
#define RELOC_FRAME(ptr, offset) \
|
||||||
((scm_t_debug_frame *) ((SCM_STACKITEM *) (ptr) + (offset)))
|
((scm_t_debug_frame *) ((SCM_STACKITEM *) (ptr) + (offset)))
|
||||||
|
|
||||||
|
|
||||||
/* Count number of debug info frames on a stack, beginning with
|
/* Count number of debug info frames on a stack, beginning with
|
||||||
* DFRAME. OFFSET is used for relocation of pointers when the stack
|
* DFRAME. OFFSET is used for relocation of pointers when the stack
|
||||||
* is read from a continuation.
|
* is read from a continuation.
|
||||||
*/
|
*/
|
||||||
static scm_t_bits
|
static long
|
||||||
stack_depth (scm_t_debug_frame *dframe, scm_t_ptrdiff offset,
|
stack_depth (scm_t_debug_frame *dframe, scm_t_ptrdiff offset, SCM vmframe,
|
||||||
SCM *id, int *maxp)
|
SCM *id)
|
||||||
{
|
{
|
||||||
long n;
|
long n;
|
||||||
long max_depth = SCM_BACKTRACE_MAXDEPTH;
|
|
||||||
for (n = 0;
|
for (n = 0;
|
||||||
dframe && !SCM_VOIDFRAMEP (*dframe) && n < max_depth;
|
dframe && !SCM_VOIDFRAMEP (*dframe);
|
||||||
dframe = RELOC_FRAME (dframe->prev, offset))
|
dframe = RELOC_FRAME (dframe->prev, offset))
|
||||||
{
|
{
|
||||||
if (SCM_EVALFRAMEP (*dframe))
|
if (SCM_EVALFRAMEP (*dframe))
|
||||||
|
@ -150,13 +150,37 @@ stack_depth (scm_t_debug_frame *dframe, scm_t_ptrdiff offset,
|
||||||
&& !SCM_UNBNDP (info[1].a.proc))
|
&& !SCM_UNBNDP (info[1].a.proc))
|
||||||
++n;
|
++n;
|
||||||
}
|
}
|
||||||
|
else if (SCM_APPLYFRAMEP (*dframe))
|
||||||
|
{
|
||||||
|
scm_t_debug_info *vect = RELOC_INFO (dframe->vect, offset);
|
||||||
|
if (SCM_PROGRAM_P (vect[0].a.proc))
|
||||||
|
{
|
||||||
|
if (!SCM_PROGRAM_IS_BOOT (vect[0].a.proc))
|
||||||
|
/* Programs can end up in the debug stack via deval; but we just
|
||||||
|
ignore those, because we know that the debugging VM engine
|
||||||
|
pushes one dframe per invocation, with the boot program as
|
||||||
|
the proc, so we only count those. */
|
||||||
|
continue;
|
||||||
|
/* count vmframe back to previous boot frame */
|
||||||
|
for (; scm_is_true (vmframe); vmframe = scm_c_vm_frame_prev (vmframe))
|
||||||
|
{
|
||||||
|
if (!SCM_PROGRAM_IS_BOOT (scm_vm_frame_program (vmframe)))
|
||||||
|
++n;
|
||||||
|
else
|
||||||
|
{ /* skip boot frame, cut out of the vm backtrace */
|
||||||
|
vmframe = scm_c_vm_frame_prev (vmframe);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++n; /* increment for non-program apply frame */
|
||||||
|
}
|
||||||
else
|
else
|
||||||
++n;
|
++n;
|
||||||
}
|
}
|
||||||
if (dframe && SCM_VOIDFRAMEP (*dframe))
|
if (dframe && SCM_VOIDFRAMEP (*dframe))
|
||||||
*id = RELOC_INFO(dframe->vect, offset)[0].id;
|
*id = RELOC_INFO(dframe->vect, offset)[0].id;
|
||||||
else if (dframe)
|
|
||||||
*maxp = 1;
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +258,7 @@ do { \
|
||||||
|
|
||||||
static scm_t_bits
|
static scm_t_bits
|
||||||
read_frames (scm_t_debug_frame *dframe, scm_t_ptrdiff offset,
|
read_frames (scm_t_debug_frame *dframe, scm_t_ptrdiff offset,
|
||||||
long n, scm_t_info_frame *iframes)
|
SCM vmframe, long n, scm_t_info_frame *iframes)
|
||||||
{
|
{
|
||||||
scm_t_info_frame *iframe = iframes;
|
scm_t_info_frame *iframe = iframes;
|
||||||
scm_t_debug_info *info, *vect;
|
scm_t_debug_info *info, *vect;
|
||||||
|
@ -293,6 +317,35 @@ read_frames (scm_t_debug_frame *dframe, scm_t_ptrdiff offset,
|
||||||
NEXT_FRAME (iframe, n, quit);
|
NEXT_FRAME (iframe, n, quit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (SCM_PROGRAM_P (iframe->proc))
|
||||||
|
{
|
||||||
|
if (!SCM_PROGRAM_IS_BOOT (iframe->proc))
|
||||||
|
/* Programs can end up in the debug stack via deval; but we just
|
||||||
|
ignore those, because we know that the debugging VM engine
|
||||||
|
pushes one dframe per invocation, with the boot program as
|
||||||
|
the proc, so we only count those. */
|
||||||
|
continue;
|
||||||
|
for (; scm_is_true (vmframe);
|
||||||
|
vmframe = scm_c_vm_frame_prev (vmframe))
|
||||||
|
{
|
||||||
|
if (SCM_PROGRAM_IS_BOOT (scm_vm_frame_program (vmframe)))
|
||||||
|
{ /* skip boot frame, back to interpreted frames */
|
||||||
|
vmframe = scm_c_vm_frame_prev (vmframe);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Oh dear, oh dear, oh dear. */
|
||||||
|
iframe->flags = SCM_UNPACK (SCM_INUM0) | SCM_FRAMEF_PROC;
|
||||||
|
iframe->source = scm_vm_frame_source (vmframe);
|
||||||
|
iframe->proc = scm_vm_frame_program (vmframe);
|
||||||
|
iframe->args = scm_vm_frame_arguments (vmframe);
|
||||||
|
++iframe;
|
||||||
|
if (--n == 0)
|
||||||
|
goto quit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NEXT_FRAME (iframe, n, quit);
|
NEXT_FRAME (iframe, n, quit);
|
||||||
|
@ -428,6 +481,7 @@ SCM_DEFINE (scm_make_stack, "make-stack", 1, 0, 1,
|
||||||
int maxp;
|
int maxp;
|
||||||
scm_t_debug_frame *dframe;
|
scm_t_debug_frame *dframe;
|
||||||
scm_t_info_frame *iframe;
|
scm_t_info_frame *iframe;
|
||||||
|
SCM vmframe;
|
||||||
long offset = 0;
|
long offset = 0;
|
||||||
SCM stack, id;
|
SCM stack, id;
|
||||||
SCM inner_cut, outer_cut;
|
SCM inner_cut, outer_cut;
|
||||||
|
@ -436,17 +490,37 @@ SCM_DEFINE (scm_make_stack, "make-stack", 1, 0, 1,
|
||||||
scm_make_stack was given. */
|
scm_make_stack was given. */
|
||||||
if (scm_is_eq (obj, SCM_BOOL_T))
|
if (scm_is_eq (obj, SCM_BOOL_T))
|
||||||
{
|
{
|
||||||
|
struct scm_vm *vp = SCM_VM_DATA (scm_the_vm ());
|
||||||
dframe = scm_i_last_debug_frame ();
|
dframe = scm_i_last_debug_frame ();
|
||||||
|
vmframe = scm_c_make_vm_frame (scm_the_vm (), vp->fp, vp->sp, vp->ip, 0);
|
||||||
}
|
}
|
||||||
else if (SCM_DEBUGOBJP (obj))
|
else if (SCM_DEBUGOBJP (obj))
|
||||||
{
|
{
|
||||||
dframe = SCM_DEBUGOBJ_FRAME (obj);
|
dframe = SCM_DEBUGOBJ_FRAME (obj);
|
||||||
|
vmframe = SCM_BOOL_F;
|
||||||
|
}
|
||||||
|
else if (SCM_VM_FRAME_P (obj))
|
||||||
|
{
|
||||||
|
dframe = NULL;
|
||||||
|
vmframe = obj;
|
||||||
}
|
}
|
||||||
else if (SCM_CONTINUATIONP (obj))
|
else if (SCM_CONTINUATIONP (obj))
|
||||||
{
|
{
|
||||||
scm_t_contregs *cont = SCM_CONTREGS (obj);
|
scm_t_contregs *cont = SCM_CONTREGS (obj);
|
||||||
offset = cont->offset;
|
offset = cont->offset;
|
||||||
dframe = RELOC_FRAME (cont->dframe, offset);
|
dframe = RELOC_FRAME (cont->dframe, offset);
|
||||||
|
if (!scm_is_null (cont->vm_conts))
|
||||||
|
{ SCM vm_cont;
|
||||||
|
struct scm_vm_cont *data;
|
||||||
|
vm_cont = scm_cdr (scm_car (cont->vm_conts));
|
||||||
|
data = SCM_VM_CONT_DATA (vm_cont);
|
||||||
|
vmframe = scm_c_make_vm_frame (vm_cont,
|
||||||
|
data->fp + data->reloc,
|
||||||
|
data->sp + data->reloc,
|
||||||
|
data->ip,
|
||||||
|
data->reloc);
|
||||||
|
} else
|
||||||
|
vmframe = SCM_BOOL_F;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -459,7 +533,8 @@ SCM_DEFINE (scm_make_stack, "make-stack", 1, 0, 1,
|
||||||
(SCM_BACKTRACE_MAXDEPTH). */
|
(SCM_BACKTRACE_MAXDEPTH). */
|
||||||
id = SCM_BOOL_F;
|
id = SCM_BOOL_F;
|
||||||
maxp = 0;
|
maxp = 0;
|
||||||
n = stack_depth (dframe, offset, &id, &maxp);
|
n = stack_depth (dframe, offset, vmframe, &id);
|
||||||
|
/* FIXME: redo maxp? */
|
||||||
size = n * SCM_FRAME_N_SLOTS;
|
size = n * SCM_FRAME_N_SLOTS;
|
||||||
|
|
||||||
/* Make the stack object. */
|
/* Make the stack object. */
|
||||||
|
@ -467,10 +542,15 @@ SCM_DEFINE (scm_make_stack, "make-stack", 1, 0, 1,
|
||||||
SCM_STACK (stack) -> id = id;
|
SCM_STACK (stack) -> id = id;
|
||||||
iframe = &SCM_STACK (stack) -> tail[0];
|
iframe = &SCM_STACK (stack) -> tail[0];
|
||||||
SCM_STACK (stack) -> frames = iframe;
|
SCM_STACK (stack) -> frames = iframe;
|
||||||
|
SCM_STACK (stack) -> length = n;
|
||||||
|
|
||||||
/* Translate the current chain of stack frames into debugging information. */
|
/* Translate the current chain of stack frames into debugging information. */
|
||||||
n = read_frames (dframe, offset, n, iframe);
|
n = read_frames (dframe, offset, vmframe, n, iframe);
|
||||||
SCM_STACK (stack) -> length = n;
|
if (n != SCM_STACK (stack)->length)
|
||||||
|
{
|
||||||
|
scm_puts ("warning: stack count incorrect!\n", scm_current_error_port ());
|
||||||
|
SCM_STACK (stack)->length = n;
|
||||||
|
}
|
||||||
|
|
||||||
/* Narrow the stack according to the arguments given to scm_make_stack. */
|
/* Narrow the stack according to the arguments given to scm_make_stack. */
|
||||||
SCM_VALIDATE_REST_ARGUMENT (args);
|
SCM_VALIDATE_REST_ARGUMENT (args);
|
||||||
|
@ -497,12 +577,11 @@ SCM_DEFINE (scm_make_stack, "make-stack", 1, 0, 1,
|
||||||
n = SCM_STACK (stack) -> length;
|
n = SCM_STACK (stack) -> length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n > 0)
|
if (n > 0 && maxp)
|
||||||
{
|
|
||||||
if (maxp)
|
|
||||||
iframe[n - 1].flags |= SCM_FRAMEF_OVERFLOW;
|
iframe[n - 1].flags |= SCM_FRAMEF_OVERFLOW;
|
||||||
|
|
||||||
|
if (n > 0)
|
||||||
return stack;
|
return stack;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return SCM_BOOL_F;
|
return SCM_BOOL_F;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (C) 1995,1996,1997,1998,2000,2001, 2003, 2004, 2006 Free Software Foundation, Inc.
|
/* Copyright (C) 1995,1996,1997,1998,2000,2001, 2003, 2004, 2006, 2009 Free Software Foundation, Inc.
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -154,31 +154,40 @@ lookup_interned_symbol (const char *name, size_t len,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Intern SYMBOL, an uninterned symbol. */
|
||||||
|
static void
|
||||||
|
intern_symbol (SCM symbol)
|
||||||
|
{
|
||||||
|
SCM slot, cell;
|
||||||
|
unsigned long hash;
|
||||||
|
|
||||||
|
hash = scm_i_symbol_hash (symbol) % SCM_HASHTABLE_N_BUCKETS (symbols);
|
||||||
|
slot = SCM_HASHTABLE_BUCKET (symbols, hash);
|
||||||
|
cell = scm_cons (symbol, SCM_UNDEFINED);
|
||||||
|
|
||||||
|
SCM_SET_HASHTABLE_BUCKET (symbols, hash, scm_cons (cell, slot));
|
||||||
|
SCM_HASHTABLE_INCREMENT (symbols);
|
||||||
|
|
||||||
|
if (SCM_HASHTABLE_N_ITEMS (symbols) > SCM_HASHTABLE_UPPER (symbols))
|
||||||
|
scm_i_rehash (symbols, scm_i_hash_symbol, 0, "intern_symbol");
|
||||||
|
}
|
||||||
|
|
||||||
static SCM
|
static SCM
|
||||||
scm_i_c_mem2symbol (const char *name, size_t len)
|
scm_i_c_mem2symbol (const char *name, size_t len)
|
||||||
{
|
{
|
||||||
SCM symbol;
|
SCM symbol;
|
||||||
size_t raw_hash = scm_string_hash ((const unsigned char *) name, len);
|
size_t raw_hash = scm_string_hash ((const unsigned char *) name, len);
|
||||||
size_t hash = raw_hash % SCM_HASHTABLE_N_BUCKETS (symbols);
|
|
||||||
|
|
||||||
symbol = lookup_interned_symbol (name, len, raw_hash);
|
symbol = lookup_interned_symbol (name, len, raw_hash);
|
||||||
if (symbol != SCM_BOOL_F)
|
if (scm_is_false (symbol))
|
||||||
return symbol;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
/* The symbol was not found - create it. */
|
/* The symbol was not found, create it. */
|
||||||
SCM symbol = scm_i_c_make_symbol (name, len, 0, raw_hash,
|
symbol = scm_i_c_make_symbol (name, len, 0, raw_hash,
|
||||||
scm_cons (SCM_BOOL_F, SCM_EOL));
|
scm_cons (SCM_BOOL_F, SCM_EOL));
|
||||||
|
intern_symbol (symbol);
|
||||||
SCM slot = SCM_HASHTABLE_BUCKET (symbols, hash);
|
}
|
||||||
SCM cell = scm_weak_car_pair (symbol, SCM_UNDEFINED);
|
|
||||||
SCM_SET_HASHTABLE_BUCKET (symbols, hash, scm_cons (cell, slot));
|
|
||||||
SCM_HASHTABLE_INCREMENT (symbols);
|
|
||||||
if (SCM_HASHTABLE_N_ITEMS (symbols) > SCM_HASHTABLE_UPPER (symbols))
|
|
||||||
scm_i_rehash (symbols, scm_i_hash_symbol, 0, "scm_mem2symbol");
|
|
||||||
|
|
||||||
return symbol;
|
return symbol;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static SCM
|
static SCM
|
||||||
|
@ -188,26 +197,17 @@ scm_i_mem2symbol (SCM str)
|
||||||
const char *name = scm_i_string_chars (str);
|
const char *name = scm_i_string_chars (str);
|
||||||
size_t len = scm_i_string_length (str);
|
size_t len = scm_i_string_length (str);
|
||||||
size_t raw_hash = scm_string_hash ((const unsigned char *) name, len);
|
size_t raw_hash = scm_string_hash ((const unsigned char *) name, len);
|
||||||
size_t hash = raw_hash % SCM_HASHTABLE_N_BUCKETS (symbols);
|
|
||||||
|
|
||||||
symbol = lookup_interned_symbol (name, len, raw_hash);
|
symbol = lookup_interned_symbol (name, len, raw_hash);
|
||||||
if (symbol != SCM_BOOL_F)
|
if (scm_is_false (symbol))
|
||||||
return symbol;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
/* The symbol was not found - create it. */
|
/* The symbol was not found, create it. */
|
||||||
SCM symbol = scm_i_make_symbol (str, 0, raw_hash,
|
symbol = scm_i_make_symbol (str, 0, raw_hash,
|
||||||
scm_cons (SCM_BOOL_F, SCM_EOL));
|
scm_cons (SCM_BOOL_F, SCM_EOL));
|
||||||
|
intern_symbol (symbol);
|
||||||
SCM slot = SCM_HASHTABLE_BUCKET (symbols, hash);
|
}
|
||||||
SCM cell = scm_weak_car_pair (symbol, SCM_UNDEFINED);
|
|
||||||
SCM_SET_HASHTABLE_BUCKET (symbols, hash, scm_cons (cell, slot));
|
|
||||||
SCM_HASHTABLE_INCREMENT (symbols);
|
|
||||||
if (SCM_HASHTABLE_N_ITEMS (symbols) > SCM_HASHTABLE_UPPER (symbols))
|
|
||||||
scm_i_rehash (symbols, scm_i_hash_symbol, 0, "scm_mem2symbol");
|
|
||||||
|
|
||||||
return symbol;
|
return symbol;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -448,14 +448,14 @@ scm_take_locale_symboln (char *sym, size_t len)
|
||||||
|
|
||||||
raw_hash = scm_string_hash ((unsigned char *)sym, len);
|
raw_hash = scm_string_hash ((unsigned char *)sym, len);
|
||||||
res = lookup_interned_symbol (sym, len, raw_hash);
|
res = lookup_interned_symbol (sym, len, raw_hash);
|
||||||
if (res != SCM_BOOL_F)
|
if (scm_is_false (res))
|
||||||
{
|
{
|
||||||
free (sym);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = scm_i_c_take_symbol (sym, len, 0, raw_hash,
|
res = scm_i_c_take_symbol (sym, len, 0, raw_hash,
|
||||||
scm_cons (SCM_BOOL_F, SCM_EOL));
|
scm_cons (SCM_BOOL_F, SCM_EOL));
|
||||||
|
intern_symbol (res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
free (sym);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -499,6 +499,7 @@ guilify_self_2 (SCM parent)
|
||||||
|
|
||||||
t->continuation_root = scm_cons (t->handle, SCM_EOL);
|
t->continuation_root = scm_cons (t->handle, SCM_EOL);
|
||||||
t->continuation_base = t->base;
|
t->continuation_base = t->base;
|
||||||
|
t->vm = SCM_BOOL_F;
|
||||||
|
|
||||||
if (scm_is_true (parent))
|
if (scm_is_true (parent))
|
||||||
t->dynamic_state = scm_make_dynamic_state (parent);
|
t->dynamic_state = scm_make_dynamic_state (parent);
|
||||||
|
|
|
@ -107,6 +107,7 @@ typedef struct scm_i_thread {
|
||||||
SCM_STACKITEM *continuation_base;
|
SCM_STACKITEM *continuation_base;
|
||||||
|
|
||||||
/* For keeping track of the stack and registers. */
|
/* For keeping track of the stack and registers. */
|
||||||
|
SCM vm;
|
||||||
SCM_STACKITEM *base;
|
SCM_STACKITEM *base;
|
||||||
SCM_STACKITEM *top;
|
SCM_STACKITEM *top;
|
||||||
jmp_buf regs;
|
jmp_buf regs;
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "libguile/throw.h"
|
#include "libguile/throw.h"
|
||||||
#include "libguile/init.h"
|
#include "libguile/init.h"
|
||||||
#include "libguile/strings.h"
|
#include "libguile/strings.h"
|
||||||
|
#include "libguile/vm.h"
|
||||||
|
|
||||||
#include "libguile/private-options.h"
|
#include "libguile/private-options.h"
|
||||||
|
|
||||||
|
@ -169,8 +170,17 @@ scm_c_catch (SCM tag,
|
||||||
struct jmp_buf_and_retval jbr;
|
struct jmp_buf_and_retval jbr;
|
||||||
SCM jmpbuf;
|
SCM jmpbuf;
|
||||||
SCM answer;
|
SCM answer;
|
||||||
|
SCM vm;
|
||||||
|
SCM *sp = NULL, *fp = NULL; /* to reset the vm */
|
||||||
struct pre_unwind_data pre_unwind;
|
struct pre_unwind_data pre_unwind;
|
||||||
|
|
||||||
|
vm = scm_the_vm ();
|
||||||
|
if (SCM_NFALSEP (vm))
|
||||||
|
{
|
||||||
|
sp = SCM_VM_DATA (vm)->sp;
|
||||||
|
fp = SCM_VM_DATA (vm)->fp;
|
||||||
|
}
|
||||||
|
|
||||||
jmpbuf = make_jmpbuf ();
|
jmpbuf = make_jmpbuf ();
|
||||||
answer = SCM_EOL;
|
answer = SCM_EOL;
|
||||||
scm_i_set_dynwinds (scm_acons (tag, jmpbuf, scm_i_dynwinds ()));
|
scm_i_set_dynwinds (scm_acons (tag, jmpbuf, scm_i_dynwinds ()));
|
||||||
|
@ -199,6 +209,30 @@ scm_c_catch (SCM tag,
|
||||||
throw_tag = jbr.throw_tag;
|
throw_tag = jbr.throw_tag;
|
||||||
jbr.throw_tag = SCM_EOL;
|
jbr.throw_tag = SCM_EOL;
|
||||||
jbr.retval = SCM_EOL;
|
jbr.retval = SCM_EOL;
|
||||||
|
if (SCM_NFALSEP (vm))
|
||||||
|
{
|
||||||
|
SCM_VM_DATA (vm)->sp = sp;
|
||||||
|
SCM_VM_DATA (vm)->fp = fp;
|
||||||
|
#ifdef VM_ENABLE_STACK_NULLING
|
||||||
|
/* see vm.c -- you'll have to enable this manually */
|
||||||
|
memset (sp + 1, 0,
|
||||||
|
(SCM_VM_DATA (vm)->stack_size
|
||||||
|
- (sp + 1 - SCM_VM_DATA (vm)->stack_base)) * sizeof(SCM));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (SCM_NFALSEP ((vm = scm_the_vm ())))
|
||||||
|
{
|
||||||
|
/* oof, it's possible this catch was called before the vm was
|
||||||
|
booted... yick. anyway, try to reset the vm stack. */
|
||||||
|
SCM_VM_DATA (vm)->sp = SCM_VM_DATA (vm)->stack_base - 1;
|
||||||
|
SCM_VM_DATA (vm)->fp = NULL;
|
||||||
|
#ifdef VM_ENABLE_STACK_NULLING
|
||||||
|
/* see vm.c -- you'll have to enable this manually */
|
||||||
|
memset (SCM_VM_DATA (vm)->stack_base, 0,
|
||||||
|
SCM_VM_DATA (vm)->stack_size * sizeof(SCM));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
answer = handler (handler_data, throw_tag, throw_args);
|
answer = handler (handler_data, throw_tag, throw_args);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -378,7 +378,7 @@
|
||||||
|
|
||||||
#define SCM_VALIDATE_VECTOR_LEN(pos, v, len) \
|
#define SCM_VALIDATE_VECTOR_LEN(pos, v, len) \
|
||||||
do { \
|
do { \
|
||||||
SCM_ASSERT (SCM_VECTORP (v) && len == SCM_VECTOR_LENGTH (v), v, pos, FUNC_NAME); \
|
SCM_ASSERT (scm_is_vector (v) && len == scm_c_vector_length (v), v, pos, FUNC_NAME); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
|
53
libguile/vm-bootstrap.h
Normal file
53
libguile/vm-bootstrap.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
#ifndef _SCM_VM_BOOTSTRAP_H_
|
||||||
|
#define _SCM_VM_BOOTSTRAP_H_
|
||||||
|
|
||||||
|
extern void scm_bootstrap_vm (void);
|
||||||
|
|
||||||
|
#endif /* _SCM_VM_BOOTSTRAP_H_ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
283
libguile/vm-engine.c
Normal file
283
libguile/vm-engine.c
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
/* This file is included in vm.c multiple times */
|
||||||
|
|
||||||
|
#if (VM_ENGINE == SCM_VM_REGULAR_ENGINE)
|
||||||
|
#define VM_USE_HOOKS 0 /* Various hooks */
|
||||||
|
#define VM_USE_CLOCK 0 /* Bogoclock */
|
||||||
|
#define VM_CHECK_EXTERNAL 1 /* Check external link */
|
||||||
|
#define VM_CHECK_OBJECT 1 /* Check object table */
|
||||||
|
#define VM_PUSH_DEBUG_FRAMES 0 /* Push frames onto the evaluator debug stack */
|
||||||
|
#elif (VM_ENGINE == SCM_VM_DEBUG_ENGINE)
|
||||||
|
#define VM_USE_HOOKS 1
|
||||||
|
#define VM_USE_CLOCK 1
|
||||||
|
#define VM_CHECK_EXTERNAL 1
|
||||||
|
#define VM_CHECK_OBJECT 1
|
||||||
|
#define VM_PUSH_DEBUG_FRAMES 1
|
||||||
|
#else
|
||||||
|
#error unknown debug engine VM_ENGINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "vm-engine.h"
|
||||||
|
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
VM_NAME (struct scm_vm *vp, SCM program, SCM *argv, int nargs)
|
||||||
|
{
|
||||||
|
/* VM registers */
|
||||||
|
register scm_byte_t *ip IP_REG; /* instruction pointer */
|
||||||
|
register SCM *sp SP_REG; /* stack pointer */
|
||||||
|
register SCM *fp FP_REG; /* frame pointer */
|
||||||
|
|
||||||
|
/* Cache variables */
|
||||||
|
struct scm_objcode *bp = NULL; /* program base pointer */
|
||||||
|
SCM external = SCM_EOL; /* external environment */
|
||||||
|
SCM *objects = NULL; /* constant objects */
|
||||||
|
size_t object_count = 0; /* length of OBJECTS */
|
||||||
|
SCM *stack_base = vp->stack_base; /* stack base address */
|
||||||
|
SCM *stack_limit = vp->stack_limit; /* stack limit address */
|
||||||
|
|
||||||
|
/* Internal variables */
|
||||||
|
int nvalues = 0;
|
||||||
|
long start_time = scm_c_get_internal_run_time ();
|
||||||
|
SCM finish_args; /* used both for returns: both in error
|
||||||
|
and normal situations */
|
||||||
|
#if VM_USE_HOOKS
|
||||||
|
SCM hook_args = SCM_EOL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_LABELS_AS_VALUES
|
||||||
|
static void **jump_table = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if VM_PUSH_DEBUG_FRAMES
|
||||||
|
scm_t_debug_frame debug;
|
||||||
|
scm_t_debug_info debug_vect_body;
|
||||||
|
debug.status = SCM_VOIDFRAME;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_LABELS_AS_VALUES
|
||||||
|
if (SCM_UNLIKELY (!jump_table))
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
jump_table = malloc (SCM_VM_NUM_INSTRUCTIONS * sizeof(void*));
|
||||||
|
for (i = 0; i < SCM_VM_NUM_INSTRUCTIONS; i++)
|
||||||
|
jump_table[i] = &&vm_error_bad_instruction;
|
||||||
|
#define VM_INSTRUCTION_TO_LABEL 1
|
||||||
|
#include <libguile/vm-expand.h>
|
||||||
|
#include <libguile/vm-i-system.i>
|
||||||
|
#include <libguile/vm-i-scheme.i>
|
||||||
|
#include <libguile/vm-i-loader.i>
|
||||||
|
#undef VM_INSTRUCTION_TO_LABEL
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Initialization */
|
||||||
|
{
|
||||||
|
SCM prog = program;
|
||||||
|
|
||||||
|
/* Boot program */
|
||||||
|
program = vm_make_boot_program (nargs);
|
||||||
|
|
||||||
|
#if VM_PUSH_DEBUG_FRAMES
|
||||||
|
debug.prev = scm_i_last_debug_frame ();
|
||||||
|
debug.status = SCM_APPLYFRAME;
|
||||||
|
debug.vect = &debug_vect_body;
|
||||||
|
debug.vect[0].a.proc = program; /* the boot program */
|
||||||
|
debug.vect[0].a.args = SCM_EOL;
|
||||||
|
scm_i_set_last_debug_frame (&debug);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Initial frame */
|
||||||
|
CACHE_REGISTER ();
|
||||||
|
CACHE_PROGRAM ();
|
||||||
|
PUSH (program);
|
||||||
|
NEW_FRAME ();
|
||||||
|
|
||||||
|
/* Initial arguments */
|
||||||
|
PUSH (prog);
|
||||||
|
if (SCM_UNLIKELY (sp + nargs >= stack_limit))
|
||||||
|
goto vm_error_too_many_args;
|
||||||
|
while (nargs--)
|
||||||
|
PUSH (*argv++);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Let's go! */
|
||||||
|
BOOT_HOOK ();
|
||||||
|
NEXT;
|
||||||
|
|
||||||
|
#ifndef HAVE_LABELS_AS_VALUES
|
||||||
|
vm_start:
|
||||||
|
switch ((*ip++) & SCM_VM_INSTRUCTION_MASK) {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "vm-expand.h"
|
||||||
|
#include "vm-i-system.c"
|
||||||
|
#include "vm-i-scheme.c"
|
||||||
|
#include "vm-i-loader.c"
|
||||||
|
|
||||||
|
#ifndef HAVE_LABELS_AS_VALUES
|
||||||
|
default:
|
||||||
|
goto vm_error_bad_instruction;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
vm_done:
|
||||||
|
SYNC_ALL ();
|
||||||
|
#if VM_PUSH_DEBUG_FRAMES
|
||||||
|
scm_i_set_last_debug_frame (debug.prev);
|
||||||
|
#endif
|
||||||
|
return finish_args;
|
||||||
|
|
||||||
|
/* Errors */
|
||||||
|
{
|
||||||
|
SCM err_msg;
|
||||||
|
|
||||||
|
vm_error_bad_instruction:
|
||||||
|
err_msg = scm_from_locale_string ("VM: Bad instruction: ~A");
|
||||||
|
finish_args = SCM_LIST1 (scm_from_uchar (ip[-1]));
|
||||||
|
goto vm_error;
|
||||||
|
|
||||||
|
vm_error_unbound:
|
||||||
|
err_msg = scm_from_locale_string ("VM: Unbound variable: ~A");
|
||||||
|
goto vm_error;
|
||||||
|
|
||||||
|
vm_error_wrong_type_arg:
|
||||||
|
err_msg = scm_from_locale_string ("VM: Wrong type argument");
|
||||||
|
finish_args = SCM_EOL;
|
||||||
|
goto vm_error;
|
||||||
|
|
||||||
|
vm_error_too_many_args:
|
||||||
|
err_msg = scm_from_locale_string ("VM: Too many arguments");
|
||||||
|
finish_args = SCM_LIST1 (scm_from_int (nargs));
|
||||||
|
goto vm_error;
|
||||||
|
|
||||||
|
vm_error_wrong_num_args:
|
||||||
|
/* nargs and program are valid */
|
||||||
|
SYNC_ALL ();
|
||||||
|
scm_wrong_num_args (program);
|
||||||
|
/* shouldn't get here */
|
||||||
|
goto vm_error;
|
||||||
|
|
||||||
|
vm_error_wrong_type_apply:
|
||||||
|
err_msg = scm_from_locale_string ("VM: Wrong type to apply: ~S "
|
||||||
|
"[IP offset: ~a]");
|
||||||
|
finish_args = SCM_LIST2 (program,
|
||||||
|
SCM_I_MAKINUM (ip - bp->base));
|
||||||
|
goto vm_error;
|
||||||
|
|
||||||
|
vm_error_stack_overflow:
|
||||||
|
err_msg = scm_from_locale_string ("VM: Stack overflow");
|
||||||
|
finish_args = SCM_EOL;
|
||||||
|
goto vm_error;
|
||||||
|
|
||||||
|
vm_error_stack_underflow:
|
||||||
|
err_msg = scm_from_locale_string ("VM: Stack underflow");
|
||||||
|
finish_args = SCM_EOL;
|
||||||
|
goto vm_error;
|
||||||
|
|
||||||
|
vm_error_improper_list:
|
||||||
|
err_msg = scm_from_locale_string ("VM: Attempt to unroll an improper list: tail is ~A");
|
||||||
|
goto vm_error;
|
||||||
|
|
||||||
|
vm_error_not_a_pair:
|
||||||
|
SYNC_ALL ();
|
||||||
|
scm_wrong_type_arg_msg (FUNC_NAME, 1, finish_args, "pair");
|
||||||
|
/* shouldn't get here */
|
||||||
|
goto vm_error;
|
||||||
|
|
||||||
|
vm_error_no_values:
|
||||||
|
err_msg = scm_from_locale_string ("VM: 0-valued return");
|
||||||
|
finish_args = SCM_EOL;
|
||||||
|
goto vm_error;
|
||||||
|
|
||||||
|
vm_error_not_enough_values:
|
||||||
|
err_msg = scm_from_locale_string ("VM: Not enough values for mv-bind");
|
||||||
|
finish_args = SCM_EOL;
|
||||||
|
goto vm_error;
|
||||||
|
|
||||||
|
vm_error_no_such_module:
|
||||||
|
err_msg = scm_from_locale_string ("VM: No such module: ~A");
|
||||||
|
goto vm_error;
|
||||||
|
|
||||||
|
#if VM_CHECK_IP
|
||||||
|
vm_error_invalid_address:
|
||||||
|
err_msg = scm_from_locale_string ("VM: Invalid program address");
|
||||||
|
finish_args = SCM_EOL;
|
||||||
|
goto vm_error;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if VM_CHECK_EXTERNAL
|
||||||
|
vm_error_external:
|
||||||
|
err_msg = scm_from_locale_string ("VM: Invalid external access");
|
||||||
|
finish_args = SCM_EOL;
|
||||||
|
goto vm_error;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if VM_CHECK_OBJECT
|
||||||
|
vm_error_object:
|
||||||
|
err_msg = scm_from_locale_string ("VM: Invalid object table access");
|
||||||
|
finish_args = SCM_EOL;
|
||||||
|
goto vm_error;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vm_error:
|
||||||
|
SYNC_ALL ();
|
||||||
|
|
||||||
|
scm_ithrow (sym_vm_error, SCM_LIST3 (sym_vm_run, err_msg, finish_args), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
abort (); /* never reached */
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef VM_USE_HOOKS
|
||||||
|
#undef VM_USE_CLOCK
|
||||||
|
#undef VM_CHECK_EXTERNAL
|
||||||
|
#undef VM_CHECK_OBJECT
|
||||||
|
#undef VM_PUSH_DEBUG_FRAMES
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
433
libguile/vm-engine.h
Normal file
433
libguile/vm-engine.h
Normal file
|
@ -0,0 +1,433 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
/* This file is included in vm_engine.c */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Registers
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Register optimization. [ stolen from librep/src/lispmach.h,v 1.3 ]
|
||||||
|
|
||||||
|
Some compilers underestimate the use of the local variables representing
|
||||||
|
the abstract machine registers, and don't put them in hardware registers,
|
||||||
|
which slows down the interpreter considerably.
|
||||||
|
For GCC, I have hand-assigned hardware registers for several architectures.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#ifdef __mips__
|
||||||
|
#define IP_REG asm("$16")
|
||||||
|
#define SP_REG asm("$17")
|
||||||
|
#define FP_REG asm("$18")
|
||||||
|
#endif
|
||||||
|
#ifdef __sparc__
|
||||||
|
#define IP_REG asm("%l0")
|
||||||
|
#define SP_REG asm("%l1")
|
||||||
|
#define FP_REG asm("%l2")
|
||||||
|
#endif
|
||||||
|
#ifdef __alpha__
|
||||||
|
#ifdef __CRAY__
|
||||||
|
#define IP_REG asm("r9")
|
||||||
|
#define SP_REG asm("r10")
|
||||||
|
#define FP_REG asm("r11")
|
||||||
|
#else
|
||||||
|
#define IP_REG asm("$9")
|
||||||
|
#define SP_REG asm("$10")
|
||||||
|
#define FP_REG asm("$11")
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#ifdef __i386__
|
||||||
|
/* gcc on lenny actually crashes if we allocate these variables in registers.
|
||||||
|
hopefully this is the only one of these. */
|
||||||
|
#if !(__GNUC__==4 && __GNUC_MINOR__==1 && __GNUC_PATCHLEVEL__==2)
|
||||||
|
#define IP_REG asm("%esi")
|
||||||
|
#define SP_REG asm("%edi")
|
||||||
|
#define FP_REG
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if defined(PPC) || defined(_POWER) || defined(_IBMR2)
|
||||||
|
#define IP_REG asm("26")
|
||||||
|
#define SP_REG asm("27")
|
||||||
|
#define FP_REG asm("28")
|
||||||
|
#endif
|
||||||
|
#ifdef __hppa__
|
||||||
|
#define IP_REG asm("%r18")
|
||||||
|
#define SP_REG asm("%r17")
|
||||||
|
#define FP_REG asm("%r16")
|
||||||
|
#endif
|
||||||
|
#ifdef __mc68000__
|
||||||
|
#define IP_REG asm("a5")
|
||||||
|
#define SP_REG asm("a4")
|
||||||
|
#define FP_REG
|
||||||
|
#endif
|
||||||
|
#ifdef __arm__
|
||||||
|
#define IP_REG asm("r9")
|
||||||
|
#define SP_REG asm("r8")
|
||||||
|
#define FP_REG asm("r7")
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IP_REG
|
||||||
|
#define IP_REG
|
||||||
|
#endif
|
||||||
|
#ifndef SP_REG
|
||||||
|
#define SP_REG
|
||||||
|
#endif
|
||||||
|
#ifndef FP_REG
|
||||||
|
#define FP_REG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cache/Sync
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef VM_ENABLE_ASSERTIONS
|
||||||
|
# define ASSERT(condition) if (SCM_UNLIKELY (!(condition))) abort()
|
||||||
|
#else
|
||||||
|
# define ASSERT(condition)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define CACHE_REGISTER() \
|
||||||
|
{ \
|
||||||
|
ip = vp->ip; \
|
||||||
|
sp = vp->sp; \
|
||||||
|
fp = vp->fp; \
|
||||||
|
stack_base = fp ? SCM_FRAME_UPPER_ADDRESS (fp) - 1 : vp->stack_base; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SYNC_REGISTER() \
|
||||||
|
{ \
|
||||||
|
vp->ip = ip; \
|
||||||
|
vp->sp = sp; \
|
||||||
|
vp->fp = fp; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef VM_ENABLE_PARANOID_ASSERTIONS
|
||||||
|
#define CHECK_IP() \
|
||||||
|
do { if (ip < bp->base || ip - bp->base > bp->len) abort (); } while (0)
|
||||||
|
#else
|
||||||
|
#define CHECK_IP()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Get a local copy of the program's "object table" (i.e. the vector of
|
||||||
|
external bindings that are referenced by the program), initialized by
|
||||||
|
`load-program'. */
|
||||||
|
/* XXX: We could instead use the "simple vector macros", thus not having to
|
||||||
|
call `scm_vector_writable_elements ()' and the likes. */
|
||||||
|
#define CACHE_PROGRAM() \
|
||||||
|
{ \
|
||||||
|
if (bp != SCM_PROGRAM_DATA (program)) { \
|
||||||
|
bp = SCM_PROGRAM_DATA (program); \
|
||||||
|
if (SCM_I_IS_VECTOR (SCM_PROGRAM_OBJTABLE (program))) { \
|
||||||
|
objects = SCM_I_VECTOR_WELTS (SCM_PROGRAM_OBJTABLE (program)); \
|
||||||
|
object_count = SCM_I_VECTOR_LENGTH (SCM_PROGRAM_OBJTABLE (program)); \
|
||||||
|
} else { \
|
||||||
|
objects = NULL; \
|
||||||
|
object_count = 0; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SYNC_BEFORE_GC() \
|
||||||
|
{ \
|
||||||
|
SYNC_REGISTER (); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SYNC_ALL() \
|
||||||
|
{ \
|
||||||
|
SYNC_REGISTER (); \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Error check
|
||||||
|
*/
|
||||||
|
|
||||||
|
#undef CHECK_EXTERNAL
|
||||||
|
#if VM_CHECK_EXTERNAL
|
||||||
|
#define CHECK_EXTERNAL(e) \
|
||||||
|
do { if (SCM_UNLIKELY (!SCM_CONSP (e))) goto vm_error_external; } while (0)
|
||||||
|
#else
|
||||||
|
#define CHECK_EXTERNAL(e)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Accesses to a program's object table. */
|
||||||
|
#if VM_CHECK_OBJECT
|
||||||
|
#define CHECK_OBJECT(_num) \
|
||||||
|
do { if (SCM_UNLIKELY ((_num) >= object_count)) goto vm_error_object; } while (0)
|
||||||
|
#else
|
||||||
|
#define CHECK_OBJECT(_num)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
|
||||||
|
#undef RUN_HOOK
|
||||||
|
#if VM_USE_HOOKS
|
||||||
|
#define RUN_HOOK(h) \
|
||||||
|
{ \
|
||||||
|
if (SCM_UNLIKELY (!SCM_FALSEP (vp->hooks[h])))\
|
||||||
|
{ \
|
||||||
|
SYNC_REGISTER (); \
|
||||||
|
vm_dispatch_hook (vp, vp->hooks[h], hook_args); \
|
||||||
|
CACHE_REGISTER (); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define RUN_HOOK(h)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BOOT_HOOK() RUN_HOOK (SCM_VM_BOOT_HOOK)
|
||||||
|
#define HALT_HOOK() RUN_HOOK (SCM_VM_HALT_HOOK)
|
||||||
|
#define NEXT_HOOK() RUN_HOOK (SCM_VM_NEXT_HOOK)
|
||||||
|
#define BREAK_HOOK() RUN_HOOK (SCM_VM_BREAK_HOOK)
|
||||||
|
#define ENTER_HOOK() RUN_HOOK (SCM_VM_ENTER_HOOK)
|
||||||
|
#define APPLY_HOOK() RUN_HOOK (SCM_VM_APPLY_HOOK)
|
||||||
|
#define EXIT_HOOK() RUN_HOOK (SCM_VM_EXIT_HOOK)
|
||||||
|
#define RETURN_HOOK() RUN_HOOK (SCM_VM_RETURN_HOOK)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stack operation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef VM_ENABLE_STACK_NULLING
|
||||||
|
# define CHECK_STACK_LEAKN(_n) ASSERT (!sp[_n]);
|
||||||
|
# define CHECK_STACK_LEAK() CHECK_STACK_LEAKN(1)
|
||||||
|
# define NULLSTACK(_n) { int __x = _n; CHECK_STACK_LEAKN (_n+1); while (__x > 0) sp[__x--] = NULL; }
|
||||||
|
/* If you have a nonlocal exit in a pre-wind proc while invoking a continuation
|
||||||
|
inside a dynwind (phew!), the stack is fully rewound but vm_reset_stack for
|
||||||
|
that continuation doesn't have a chance to run. It's not important on a
|
||||||
|
semantic level, but it does mess up our stack nulling -- so this macro is to
|
||||||
|
fix that. */
|
||||||
|
# define NULLSTACK_FOR_NONLOCAL_EXIT() if (vp->sp > sp) NULLSTACK (vp->sp - sp);
|
||||||
|
#else
|
||||||
|
# define CHECK_STACK_LEAKN(_n)
|
||||||
|
# define CHECK_STACK_LEAK()
|
||||||
|
# define NULLSTACK(_n)
|
||||||
|
# define NULLSTACK_FOR_NONLOCAL_EXIT()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CHECK_OVERFLOW() \
|
||||||
|
if (sp > stack_limit) \
|
||||||
|
goto vm_error_stack_overflow
|
||||||
|
|
||||||
|
#define CHECK_UNDERFLOW() \
|
||||||
|
if (sp < stack_base) \
|
||||||
|
goto vm_error_stack_underflow;
|
||||||
|
|
||||||
|
#define PUSH(x) do { sp++; CHECK_OVERFLOW (); *sp = x; } while (0)
|
||||||
|
#define DROP() do { sp--; CHECK_UNDERFLOW (); NULLSTACK (1); } while (0)
|
||||||
|
#define DROPN(_n) do { sp -= (_n); CHECK_UNDERFLOW (); NULLSTACK (_n); } while (0)
|
||||||
|
#define POP(x) do { x = *sp; DROP (); } while (0)
|
||||||
|
|
||||||
|
/* A fast CONS. This has to be fast since its used, for instance, by
|
||||||
|
POP_LIST when fetching a function's argument list. Note: `scm_cell' is an
|
||||||
|
inlined function in Guile 1.7. Unfortunately, it calls
|
||||||
|
`scm_gc_for_newcell ()' which is _not_ inlined and allocated cells on the
|
||||||
|
heap. XXX */
|
||||||
|
#define CONS(x,y,z) \
|
||||||
|
{ \
|
||||||
|
SYNC_BEFORE_GC (); \
|
||||||
|
x = scm_cell (SCM_UNPACK (y), SCM_UNPACK (z)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pop the N objects on top of the stack and push a list that contains
|
||||||
|
them. */
|
||||||
|
#define POP_LIST(n) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
int i; \
|
||||||
|
SCM l = SCM_EOL, x; \
|
||||||
|
for (i = n; i; i--) \
|
||||||
|
{ \
|
||||||
|
POP (x); \
|
||||||
|
CONS (l, x, l); \
|
||||||
|
} \
|
||||||
|
PUSH (l); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* The opposite: push all of the elements in L onto the list. */
|
||||||
|
#define PUSH_LIST(l, NILP) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
for (; scm_is_pair (l); l = SCM_CDR (l)) \
|
||||||
|
PUSH (SCM_CAR (l)); \
|
||||||
|
if (SCM_UNLIKELY (!NILP (l))) { \
|
||||||
|
finish_args = scm_list_1 (l); \
|
||||||
|
goto vm_error_improper_list; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
#define POP_LIST_MARK() \
|
||||||
|
do { \
|
||||||
|
SCM o; \
|
||||||
|
SCM l = SCM_EOL; \
|
||||||
|
POP (o); \
|
||||||
|
while (!SCM_UNBNDP (o)) \
|
||||||
|
{ \
|
||||||
|
CONS (l, o, l); \
|
||||||
|
POP (o); \
|
||||||
|
} \
|
||||||
|
PUSH (l); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define POP_CONS_MARK() \
|
||||||
|
do { \
|
||||||
|
SCM o, l; \
|
||||||
|
POP (l); \
|
||||||
|
POP (o); \
|
||||||
|
while (!SCM_UNBNDP (o)) \
|
||||||
|
{ \
|
||||||
|
CONS (l, o, l); \
|
||||||
|
POP (o); \
|
||||||
|
} \
|
||||||
|
PUSH (l); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Instruction operation
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define FETCH() (*ip++)
|
||||||
|
#define FETCH_LENGTH(len) do { len=*ip++; len<<=8; len+=*ip++; len<<=8; len+=*ip++; } while (0)
|
||||||
|
|
||||||
|
#undef CLOCK
|
||||||
|
#if VM_USE_CLOCK
|
||||||
|
#define CLOCK(n) vp->clock += n
|
||||||
|
#else
|
||||||
|
#define CLOCK(n)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef NEXT_JUMP
|
||||||
|
#ifdef HAVE_LABELS_AS_VALUES
|
||||||
|
#define NEXT_JUMP() goto *jump_table[FETCH () & SCM_VM_INSTRUCTION_MASK]
|
||||||
|
#else
|
||||||
|
#define NEXT_JUMP() goto vm_start
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NEXT \
|
||||||
|
{ \
|
||||||
|
CLOCK (1); \
|
||||||
|
NEXT_HOOK (); \
|
||||||
|
CHECK_STACK_LEAK (); \
|
||||||
|
NEXT_JUMP (); \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stack frame
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define INIT_ARGS() \
|
||||||
|
{ \
|
||||||
|
if (SCM_UNLIKELY (bp->nrest)) \
|
||||||
|
{ \
|
||||||
|
int n = nargs - (bp->nargs - 1); \
|
||||||
|
if (n < 0) \
|
||||||
|
goto vm_error_wrong_num_args; \
|
||||||
|
/* NB, can cause GC while setting up the \
|
||||||
|
stack frame */ \
|
||||||
|
POP_LIST (n); \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
if (SCM_UNLIKELY (nargs != bp->nargs)) \
|
||||||
|
goto vm_error_wrong_num_args; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See frames.h for the layout of stack frames */
|
||||||
|
/* When this is called, bp points to the new program data,
|
||||||
|
and the arguments are already on the stack */
|
||||||
|
#define NEW_FRAME() \
|
||||||
|
{ \
|
||||||
|
int i; \
|
||||||
|
SCM *dl, *data; \
|
||||||
|
scm_byte_t *ra = ip; \
|
||||||
|
\
|
||||||
|
/* Save old registers */ \
|
||||||
|
ra = ip; \
|
||||||
|
dl = fp; \
|
||||||
|
\
|
||||||
|
/* New registers */ \
|
||||||
|
fp = sp - bp->nargs + 1; \
|
||||||
|
data = SCM_FRAME_DATA_ADDRESS (fp); \
|
||||||
|
sp = data + 3; \
|
||||||
|
CHECK_OVERFLOW (); \
|
||||||
|
stack_base = sp; \
|
||||||
|
ip = bp->base; \
|
||||||
|
\
|
||||||
|
/* Init local variables */ \
|
||||||
|
for (i=bp->nlocs; i; i--) \
|
||||||
|
data[-i] = SCM_UNDEFINED; \
|
||||||
|
\
|
||||||
|
/* Set frame data */ \
|
||||||
|
data[3] = (SCM)ra; \
|
||||||
|
data[2] = 0x0; \
|
||||||
|
data[1] = (SCM)dl; \
|
||||||
|
\
|
||||||
|
/* Postpone initializing external vars, \
|
||||||
|
because if the CONS causes a GC, we \
|
||||||
|
want the stack marker to see the data \
|
||||||
|
array formatted as expected. */ \
|
||||||
|
data[0] = SCM_UNDEFINED; \
|
||||||
|
external = SCM_PROGRAM_EXTERNALS (fp[-1]); \
|
||||||
|
for (i = 0; i < bp->nexts; i++) \
|
||||||
|
CONS (external, SCM_UNDEFINED, external); \
|
||||||
|
data[0] = external; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CACHE_EXTERNAL() external = fp[bp->nargs + bp->nlocs]
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
102
libguile/vm-expand.h
Normal file
102
libguile/vm-expand.h
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
#ifndef VM_LABEL
|
||||||
|
#define VM_LABEL(tag) l_##tag
|
||||||
|
#define VM_OPCODE(tag) scm_op_##tag
|
||||||
|
|
||||||
|
#ifdef HAVE_LABELS_AS_VALUES
|
||||||
|
#define VM_TAG(tag) VM_LABEL(tag):
|
||||||
|
#define VM_ADDR(tag) &&VM_LABEL(tag)
|
||||||
|
#else /* not HAVE_LABELS_AS_VALUES */
|
||||||
|
#define VM_TAG(tag) case VM_OPCODE(tag):
|
||||||
|
#define VM_ADDR(tag) NULL
|
||||||
|
#endif /* not HAVE_LABELS_AS_VALUES */
|
||||||
|
#endif /* VM_LABEL */
|
||||||
|
|
||||||
|
#undef VM_DEFINE_FUNCTION
|
||||||
|
#undef VM_DEFINE_LOADER
|
||||||
|
#define VM_DEFINE_FUNCTION(code,tag,name,nargs) \
|
||||||
|
VM_DEFINE_INSTRUCTION(code,tag,name,0,nargs,1)
|
||||||
|
#define VM_DEFINE_LOADER(code,tag,name) \
|
||||||
|
VM_DEFINE_INSTRUCTION(code,tag,name,-1,0,1)
|
||||||
|
|
||||||
|
#undef VM_DEFINE_INSTRUCTION
|
||||||
|
/*
|
||||||
|
* These will go to scm_instruction_table in instructions.c
|
||||||
|
*/
|
||||||
|
#ifdef VM_INSTRUCTION_TO_TABLE
|
||||||
|
#define VM_DEFINE_INSTRUCTION(code_,tag_,name_,len_,npop_,npush_) \
|
||||||
|
table[VM_OPCODE (tag_)].opcode = code_; \
|
||||||
|
table[VM_OPCODE (tag_)].name = name_; \
|
||||||
|
table[VM_OPCODE (tag_)].len = len_; \
|
||||||
|
table[VM_OPCODE (tag_)].npop = npop_; \
|
||||||
|
table[VM_OPCODE (tag_)].npush = npush_;
|
||||||
|
|
||||||
|
#else
|
||||||
|
#ifdef VM_INSTRUCTION_TO_LABEL
|
||||||
|
/*
|
||||||
|
* These will go to jump_table in vm_engine.c
|
||||||
|
*/
|
||||||
|
#define VM_DEFINE_INSTRUCTION(code,tag,name,len,npop,npush) jump_table[code] = VM_ADDR (tag);
|
||||||
|
|
||||||
|
#else
|
||||||
|
#ifdef VM_INSTRUCTION_TO_OPCODE
|
||||||
|
/*
|
||||||
|
* These will go to scm_opcode in instructions.h
|
||||||
|
*/
|
||||||
|
#define VM_DEFINE_INSTRUCTION(code,tag,name,len,npop,npush) VM_OPCODE (tag) = code,
|
||||||
|
|
||||||
|
#else /* Otherwise */
|
||||||
|
/*
|
||||||
|
* These are directly included in vm_engine.c
|
||||||
|
*/
|
||||||
|
#define VM_DEFINE_INSTRUCTION(code,tag,name,len,npop,npush) VM_TAG (tag)
|
||||||
|
|
||||||
|
#endif /* VM_INSTRUCTION_TO_OPCODE */
|
||||||
|
#endif /* VM_INSTRUCTION_TO_LABEL */
|
||||||
|
#endif /* VM_INSTRUCTION_TO_TABLE */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
184
libguile/vm-i-loader.c
Normal file
184
libguile/vm-i-loader.c
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
/* Copyright (C) 2001,2008,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
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* This file is included in vm_engine.c */
|
||||||
|
|
||||||
|
VM_DEFINE_LOADER (59, load_unsigned_integer, "load-unsigned-integer")
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
FETCH_LENGTH (len);
|
||||||
|
if (SCM_LIKELY (len <= 4))
|
||||||
|
{
|
||||||
|
unsigned int val = 0;
|
||||||
|
while (len-- > 0)
|
||||||
|
val = (val << 8U) + FETCH ();
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
PUSH (scm_from_uint (val));
|
||||||
|
NEXT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SCM_MISC_ERROR ("load-unsigned-integer: not implemented yet", SCM_EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_LOADER (60, load_integer, "load-integer")
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
FETCH_LENGTH (len);
|
||||||
|
if (SCM_LIKELY (len <= 4))
|
||||||
|
{
|
||||||
|
int val = 0;
|
||||||
|
while (len-- > 0)
|
||||||
|
val = (val << 8) + FETCH ();
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
PUSH (scm_from_int (val));
|
||||||
|
NEXT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SCM_MISC_ERROR ("load-integer: not implemented yet", SCM_EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_LOADER (61, load_number, "load-number")
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
FETCH_LENGTH (len);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
PUSH (scm_string_to_number (scm_from_locale_stringn ((char *)ip, len),
|
||||||
|
SCM_UNDEFINED /* radix = 10 */));
|
||||||
|
/* Was: scm_istring2number (ip, len, 10)); */
|
||||||
|
ip += len;
|
||||||
|
NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_LOADER (62, load_string, "load-string")
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
FETCH_LENGTH (len);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
PUSH (scm_from_locale_stringn ((char *)ip, len));
|
||||||
|
/* Was: scm_makfromstr (ip, len, 0) */
|
||||||
|
ip += len;
|
||||||
|
NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_LOADER (63, load_symbol, "load-symbol")
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
FETCH_LENGTH (len);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
PUSH (scm_from_locale_symboln ((char *)ip, len));
|
||||||
|
ip += len;
|
||||||
|
NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_LOADER (64, load_keyword, "load-keyword")
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
FETCH_LENGTH (len);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
PUSH (scm_from_locale_keywordn ((char *)ip, len));
|
||||||
|
ip += len;
|
||||||
|
NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_LOADER (65, load_program, "load-program")
|
||||||
|
{
|
||||||
|
scm_t_uint32 len;
|
||||||
|
SCM objs, objcode;
|
||||||
|
|
||||||
|
POP (objs);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
|
||||||
|
if (scm_is_vector (objs) && scm_is_false (scm_c_vector_ref (objs, 0)))
|
||||||
|
scm_c_vector_set_x (objs, 0, scm_current_module ());
|
||||||
|
|
||||||
|
objcode = scm_c_make_objcode_slice (SCM_PROGRAM_OBJCODE (fp[-1]), ip);
|
||||||
|
len = sizeof (struct scm_objcode) + SCM_OBJCODE_TOTAL_LEN (objcode);
|
||||||
|
|
||||||
|
PUSH (scm_make_program (objcode, objs, SCM_EOL));
|
||||||
|
|
||||||
|
ip += len;
|
||||||
|
|
||||||
|
NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_INSTRUCTION (66, link_now, "link-now", 0, 1, 1)
|
||||||
|
{
|
||||||
|
SCM what;
|
||||||
|
POP (what);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
if (SCM_LIKELY (SCM_SYMBOLP (what)))
|
||||||
|
{
|
||||||
|
PUSH (scm_lookup (what)); /* might longjmp */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SCM mod;
|
||||||
|
/* compilation of @ or @@
|
||||||
|
`what' is a three-element list: (MODNAME SYM INTERFACE?)
|
||||||
|
INTERFACE? is #t if we compiled @ or #f if we compiled @@
|
||||||
|
*/
|
||||||
|
mod = scm_resolve_module (SCM_CAR (what));
|
||||||
|
if (scm_is_true (SCM_CADDR (what)))
|
||||||
|
mod = scm_module_public_interface (mod);
|
||||||
|
if (SCM_FALSEP (mod))
|
||||||
|
{
|
||||||
|
finish_args = SCM_LIST1 (SCM_CAR (what));
|
||||||
|
goto vm_error_no_such_module;
|
||||||
|
}
|
||||||
|
/* might longjmp */
|
||||||
|
PUSH (scm_module_lookup (mod, SCM_CADR (what)));
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_LOADER (67, define, "define")
|
||||||
|
{
|
||||||
|
SCM sym;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
FETCH_LENGTH (len);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
sym = scm_from_locale_symboln ((char *)ip, len);
|
||||||
|
ip += len;
|
||||||
|
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
PUSH (scm_sym2var (sym, scm_current_module_lookup_closure (), SCM_BOOL_T));
|
||||||
|
NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
(defun renumber-ops ()
|
||||||
|
"start from top of buffer and renumber 'VM_DEFINE_FOO (\n' sequences"
|
||||||
|
(interactive "")
|
||||||
|
(save-excursion
|
||||||
|
(let ((counter 59)) (goto-char (point-min))
|
||||||
|
(while (re-search-forward "^VM_DEFINE_[^ ]+ (\\([^,]+\\)," (point-max) t)
|
||||||
|
(replace-match
|
||||||
|
(number-to-string (setq counter (1+ counter)))
|
||||||
|
t t nil 1)))))
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
314
libguile/vm-i-scheme.c
Normal file
314
libguile/vm-i-scheme.c
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
/* This file is included in vm_engine.c */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Predicates
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ARGS1(a1) SCM a1 = sp[0];
|
||||||
|
#define ARGS2(a1,a2) SCM a1 = sp[-1], a2 = sp[0]; sp--; NULLSTACK (1);
|
||||||
|
#define ARGS3(a1,a2,a3) SCM a1 = sp[-2], a2 = sp[-1], a3 = sp[0]; sp -= 2; NULLSTACK (2);
|
||||||
|
|
||||||
|
#define RETURN(x) do { *sp = x; NEXT; } while (0)
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (80, not, "not", 1)
|
||||||
|
{
|
||||||
|
ARGS1 (x);
|
||||||
|
RETURN (SCM_BOOL (SCM_FALSEP (x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (81, not_not, "not-not", 1)
|
||||||
|
{
|
||||||
|
ARGS1 (x);
|
||||||
|
RETURN (SCM_BOOL (!SCM_FALSEP (x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (82, eq, "eq?", 2)
|
||||||
|
{
|
||||||
|
ARGS2 (x, y);
|
||||||
|
RETURN (SCM_BOOL (SCM_EQ_P (x, y)));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (83, not_eq, "not-eq?", 2)
|
||||||
|
{
|
||||||
|
ARGS2 (x, y);
|
||||||
|
RETURN (SCM_BOOL (!SCM_EQ_P (x, y)));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (84, nullp, "null?", 1)
|
||||||
|
{
|
||||||
|
ARGS1 (x);
|
||||||
|
RETURN (SCM_BOOL (SCM_NULLP (x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (85, not_nullp, "not-null?", 1)
|
||||||
|
{
|
||||||
|
ARGS1 (x);
|
||||||
|
RETURN (SCM_BOOL (!SCM_NULLP (x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (86, eqv, "eqv?", 2)
|
||||||
|
{
|
||||||
|
ARGS2 (x, y);
|
||||||
|
if (SCM_EQ_P (x, y))
|
||||||
|
RETURN (SCM_BOOL_T);
|
||||||
|
if (SCM_IMP (x) || SCM_IMP (y))
|
||||||
|
RETURN (SCM_BOOL_F);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
RETURN (scm_eqv_p (x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (87, equal, "equal?", 2)
|
||||||
|
{
|
||||||
|
ARGS2 (x, y);
|
||||||
|
if (SCM_EQ_P (x, y))
|
||||||
|
RETURN (SCM_BOOL_T);
|
||||||
|
if (SCM_IMP (x) || SCM_IMP (y))
|
||||||
|
RETURN (SCM_BOOL_F);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
RETURN (scm_equal_p (x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (88, pairp, "pair?", 1)
|
||||||
|
{
|
||||||
|
ARGS1 (x);
|
||||||
|
RETURN (SCM_BOOL (SCM_CONSP (x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (89, listp, "list?", 1)
|
||||||
|
{
|
||||||
|
ARGS1 (x);
|
||||||
|
RETURN (SCM_BOOL (scm_ilength (x) >= 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Basic data
|
||||||
|
*/
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (90, cons, "cons", 2)
|
||||||
|
{
|
||||||
|
ARGS2 (x, y);
|
||||||
|
CONS (x, x, y);
|
||||||
|
RETURN (x);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VM_VALIDATE_CONS(x) \
|
||||||
|
if (SCM_UNLIKELY (!scm_is_pair (x))) \
|
||||||
|
{ finish_args = x; \
|
||||||
|
goto vm_error_not_a_pair; \
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (91, car, "car", 1)
|
||||||
|
{
|
||||||
|
ARGS1 (x);
|
||||||
|
VM_VALIDATE_CONS (x);
|
||||||
|
RETURN (SCM_CAR (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (92, cdr, "cdr", 1)
|
||||||
|
{
|
||||||
|
ARGS1 (x);
|
||||||
|
VM_VALIDATE_CONS (x);
|
||||||
|
RETURN (SCM_CDR (x));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (93, set_car, "set-car!", 2)
|
||||||
|
{
|
||||||
|
ARGS2 (x, y);
|
||||||
|
VM_VALIDATE_CONS (x);
|
||||||
|
SCM_SETCAR (x, y);
|
||||||
|
RETURN (SCM_UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (94, set_cdr, "set-cdr!", 2)
|
||||||
|
{
|
||||||
|
ARGS2 (x, y);
|
||||||
|
VM_VALIDATE_CONS (x);
|
||||||
|
SCM_SETCDR (x, y);
|
||||||
|
RETURN (SCM_UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Numeric relational tests
|
||||||
|
*/
|
||||||
|
|
||||||
|
#undef REL
|
||||||
|
#define REL(crel,srel) \
|
||||||
|
{ \
|
||||||
|
ARGS2 (x, y); \
|
||||||
|
if (SCM_I_INUMP (x) && SCM_I_INUMP (y)) \
|
||||||
|
RETURN (SCM_BOOL (SCM_I_INUM (x) crel SCM_I_INUM (y))); \
|
||||||
|
SYNC_REGISTER (); \
|
||||||
|
RETURN (srel (x, y)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (95, ee, "ee?", 2)
|
||||||
|
{
|
||||||
|
REL (==, scm_num_eq_p);
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (96, lt, "lt?", 2)
|
||||||
|
{
|
||||||
|
REL (<, scm_less_p);
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (97, le, "le?", 2)
|
||||||
|
{
|
||||||
|
REL (<=, scm_leq_p);
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (98, gt, "gt?", 2)
|
||||||
|
{
|
||||||
|
REL (>, scm_gr_p);
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (99, ge, "ge?", 2)
|
||||||
|
{
|
||||||
|
REL (>=, scm_geq_p);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Numeric functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
#undef FUNC2
|
||||||
|
#define FUNC2(CFUNC,SFUNC) \
|
||||||
|
{ \
|
||||||
|
ARGS2 (x, y); \
|
||||||
|
if (SCM_I_INUMP (x) && SCM_I_INUMP (y)) \
|
||||||
|
{ \
|
||||||
|
scm_t_bits n = SCM_I_INUM (x) CFUNC SCM_I_INUM (y);\
|
||||||
|
if (SCM_FIXABLE (n)) \
|
||||||
|
RETURN (SCM_I_MAKINUM (n)); \
|
||||||
|
} \
|
||||||
|
SYNC_REGISTER (); \
|
||||||
|
RETURN (SFUNC (x, y)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (100, add, "add", 2)
|
||||||
|
{
|
||||||
|
FUNC2 (+, scm_sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (101, sub, "sub", 2)
|
||||||
|
{
|
||||||
|
FUNC2 (-, scm_difference);
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (102, mul, "mul", 2)
|
||||||
|
{
|
||||||
|
ARGS2 (x, y);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
RETURN (scm_product (x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (103, div, "div", 2)
|
||||||
|
{
|
||||||
|
ARGS2 (x, y);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
RETURN (scm_divide (x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (104, quo, "quo", 2)
|
||||||
|
{
|
||||||
|
ARGS2 (x, y);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
RETURN (scm_quotient (x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (105, rem, "rem", 2)
|
||||||
|
{
|
||||||
|
ARGS2 (x, y);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
RETURN (scm_remainder (x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (106, mod, "mod", 2)
|
||||||
|
{
|
||||||
|
ARGS2 (x, y);
|
||||||
|
SYNC_REGISTER ();
|
||||||
|
RETURN (scm_modulo (x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GOOPS support
|
||||||
|
*/
|
||||||
|
VM_DEFINE_FUNCTION (107, slot_ref, "slot-ref", 2)
|
||||||
|
{
|
||||||
|
size_t slot;
|
||||||
|
ARGS2 (instance, idx);
|
||||||
|
slot = SCM_I_INUM (idx);
|
||||||
|
RETURN (SCM_PACK (SCM_STRUCT_DATA (instance) [slot]));
|
||||||
|
}
|
||||||
|
|
||||||
|
VM_DEFINE_FUNCTION (108, slot_set, "slot-set", 3)
|
||||||
|
{
|
||||||
|
size_t slot;
|
||||||
|
ARGS3 (instance, idx, val);
|
||||||
|
slot = SCM_I_INUM (idx);
|
||||||
|
SCM_STRUCT_DATA (instance) [slot] = SCM_UNPACK (val);
|
||||||
|
RETURN (SCM_UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
(defun renumber-ops ()
|
||||||
|
"start from top of buffer and renumber 'VM_DEFINE_FOO (\n' sequences"
|
||||||
|
(interactive "")
|
||||||
|
(save-excursion
|
||||||
|
(let ((counter 79)) (goto-char (point-min))
|
||||||
|
(while (re-search-forward "^VM_DEFINE_[^ ]+ (\\([^,]+\\)," (point-max) t)
|
||||||
|
(replace-match
|
||||||
|
(number-to-string (setq counter (1+ counter)))
|
||||||
|
t t nil 1)))))
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
1139
libguile/vm-i-system.c
Normal file
1139
libguile/vm-i-system.c
Normal file
File diff suppressed because it is too large
Load diff
682
libguile/vm.c
Normal file
682
libguile/vm.c
Normal file
|
@ -0,0 +1,682 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
#if HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <alloca.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "vm-bootstrap.h"
|
||||||
|
#include "frames.h"
|
||||||
|
#include "instructions.h"
|
||||||
|
#include "objcodes.h"
|
||||||
|
#include "programs.h"
|
||||||
|
#include "lang.h" /* NULL_OR_NIL_P */
|
||||||
|
#include "vm.h"
|
||||||
|
|
||||||
|
/* I sometimes use this for debugging. */
|
||||||
|
#define vm_puts(OBJ) \
|
||||||
|
{ \
|
||||||
|
scm_display (OBJ, scm_current_error_port ()); \
|
||||||
|
scm_newline (scm_current_error_port ()); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The VM has a number of internal assertions that shouldn't normally be
|
||||||
|
necessary, but might be if you think you found a bug in the VM. */
|
||||||
|
#define VM_ENABLE_ASSERTIONS
|
||||||
|
|
||||||
|
/* We can add a mode that ensures that all stack items above the stack pointer
|
||||||
|
are NULL. This is useful for checking the internal consistency of the VM's
|
||||||
|
assumptions and its operators, but isn't necessary for normal operation. It
|
||||||
|
will ensure that assertions are enabled. Slows down the VM by about 30%. */
|
||||||
|
/* NB! If you enable this, search for NULLING in throw.c */
|
||||||
|
/* #define VM_ENABLE_STACK_NULLING */
|
||||||
|
|
||||||
|
/* #define VM_ENABLE_PARANOID_ASSERTIONS */
|
||||||
|
|
||||||
|
#if defined (VM_ENABLE_STACK_NULLING) && !defined (VM_ENABLE_ASSERTIONS)
|
||||||
|
#define VM_ENABLE_ASSERTIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VM Continuation
|
||||||
|
*/
|
||||||
|
|
||||||
|
scm_t_bits scm_tc16_vm_cont;
|
||||||
|
|
||||||
|
static void
|
||||||
|
vm_mark_stack (SCM *base, scm_t_ptrdiff size, SCM *fp, scm_t_ptrdiff reloc)
|
||||||
|
{
|
||||||
|
SCM *sp, *upper, *lower;
|
||||||
|
sp = base + size - 1;
|
||||||
|
|
||||||
|
while (sp > base && fp)
|
||||||
|
{
|
||||||
|
upper = SCM_FRAME_UPPER_ADDRESS (fp);
|
||||||
|
lower = SCM_FRAME_LOWER_ADDRESS (fp);
|
||||||
|
|
||||||
|
for (; sp >= upper; sp--)
|
||||||
|
if (SCM_NIMP (*sp))
|
||||||
|
{
|
||||||
|
if (scm_in_heap_p (*sp))
|
||||||
|
scm_gc_mark (*sp);
|
||||||
|
else
|
||||||
|
fprintf (stderr, "BADNESS: crap on the stack: %p\n", *sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* skip ra, mvra */
|
||||||
|
sp -= 2;
|
||||||
|
|
||||||
|
/* update fp from the dynamic link */
|
||||||
|
fp = (SCM*)*sp-- + reloc;
|
||||||
|
|
||||||
|
/* mark from the el down to the lower address */
|
||||||
|
for (; sp >= lower; sp--)
|
||||||
|
if (*sp && SCM_NIMP (*sp))
|
||||||
|
scm_gc_mark (*sp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
vm_cont_mark (SCM obj)
|
||||||
|
{
|
||||||
|
struct scm_vm_cont *p = SCM_VM_CONT_DATA (obj);
|
||||||
|
|
||||||
|
if (p->stack_size)
|
||||||
|
vm_mark_stack (p->stack_base, p->stack_size, p->fp + p->reloc, p->reloc);
|
||||||
|
|
||||||
|
return SCM_BOOL_F;
|
||||||
|
}
|
||||||
|
|
||||||
|
static scm_sizet
|
||||||
|
vm_cont_free (SCM obj)
|
||||||
|
{
|
||||||
|
struct scm_vm_cont *p = SCM_VM_CONT_DATA (obj);
|
||||||
|
|
||||||
|
scm_gc_free (p->stack_base, p->stack_size * sizeof (SCM), "stack-base");
|
||||||
|
scm_gc_free (p, sizeof (struct scm_vm), "vm");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
capture_vm_cont (struct scm_vm *vp)
|
||||||
|
{
|
||||||
|
struct scm_vm_cont *p = scm_gc_malloc (sizeof (*p), "capture_vm_cont");
|
||||||
|
p->stack_size = vp->sp - vp->stack_base + 1;
|
||||||
|
p->stack_base = scm_gc_malloc (p->stack_size * sizeof (SCM),
|
||||||
|
"capture_vm_cont");
|
||||||
|
#ifdef VM_ENABLE_STACK_NULLING
|
||||||
|
if (vp->sp >= vp->stack_base)
|
||||||
|
if (!vp->sp[0] || vp->sp[1])
|
||||||
|
abort ();
|
||||||
|
memset (p->stack_base, 0, p->stack_size * sizeof (SCM));
|
||||||
|
#endif
|
||||||
|
p->ip = vp->ip;
|
||||||
|
p->sp = vp->sp;
|
||||||
|
p->fp = vp->fp;
|
||||||
|
memcpy (p->stack_base, vp->stack_base, p->stack_size * sizeof (SCM));
|
||||||
|
p->reloc = p->stack_base - vp->stack_base;
|
||||||
|
SCM_RETURN_NEWSMOB (scm_tc16_vm_cont, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
reinstate_vm_cont (struct scm_vm *vp, SCM cont)
|
||||||
|
{
|
||||||
|
struct scm_vm_cont *p = SCM_VM_CONT_DATA (cont);
|
||||||
|
if (vp->stack_size < p->stack_size)
|
||||||
|
{
|
||||||
|
/* puts ("FIXME: Need to expand"); */
|
||||||
|
abort ();
|
||||||
|
}
|
||||||
|
#ifdef VM_ENABLE_STACK_NULLING
|
||||||
|
{
|
||||||
|
scm_t_ptrdiff nzero = (vp->sp - p->sp);
|
||||||
|
if (nzero > 0)
|
||||||
|
memset (vp->stack_base + p->stack_size, 0, nzero * sizeof (SCM));
|
||||||
|
/* actually nzero should always be negative, because vm_reset_stack will
|
||||||
|
unwind the stack to some point *below* this continuation */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
vp->ip = p->ip;
|
||||||
|
vp->sp = p->sp;
|
||||||
|
vp->fp = p->fp;
|
||||||
|
memcpy (vp->stack_base, p->stack_base, p->stack_size * sizeof (SCM));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* In theory, a number of vm instances can be active in the call trace, and we
|
||||||
|
only want to reify the continuations of those in the current continuation
|
||||||
|
root. I don't see a nice way to do this -- ideally it would involve dynwinds,
|
||||||
|
and previous values of the *the-vm* fluid within the current continuation
|
||||||
|
root. But we don't have access to continuation roots in the dynwind stack.
|
||||||
|
So, just punt for now -- take the current value of *the-vm*.
|
||||||
|
|
||||||
|
While I'm on the topic, ideally we could avoid copying the C stack if the
|
||||||
|
continuation root is inside VM code, and call/cc was invoked within that same
|
||||||
|
call to vm_run; but that's currently not implemented.
|
||||||
|
*/
|
||||||
|
SCM
|
||||||
|
scm_vm_capture_continuations (void)
|
||||||
|
{
|
||||||
|
SCM vm = scm_the_vm ();
|
||||||
|
return scm_acons (vm, capture_vm_cont (SCM_VM_DATA (vm)), SCM_EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scm_vm_reinstate_continuations (SCM conts)
|
||||||
|
{
|
||||||
|
for (; conts != SCM_EOL; conts = SCM_CDR (conts))
|
||||||
|
reinstate_vm_cont (SCM_VM_DATA (SCM_CAAR (conts)), SCM_CDAR (conts));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void enfalsen_frame (void *p)
|
||||||
|
{
|
||||||
|
struct scm_vm *vp = p;
|
||||||
|
vp->trace_frame = SCM_BOOL_F;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vm_dispatch_hook (struct scm_vm *vp, SCM hook, SCM hook_args)
|
||||||
|
{
|
||||||
|
if (!SCM_FALSEP (vp->trace_frame))
|
||||||
|
return;
|
||||||
|
|
||||||
|
scm_dynwind_begin (0);
|
||||||
|
// FIXME, stack holder should be the vm
|
||||||
|
vp->trace_frame = scm_c_make_vm_frame (SCM_BOOL_F, vp->fp, vp->sp, vp->ip, 0);
|
||||||
|
scm_dynwind_unwind_handler (enfalsen_frame, vp, SCM_F_WIND_EXPLICITLY);
|
||||||
|
|
||||||
|
scm_c_run_hook (hook, hook_args);
|
||||||
|
|
||||||
|
scm_dynwind_end ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VM Internal functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
static SCM sym_vm_run;
|
||||||
|
static SCM sym_vm_error;
|
||||||
|
static SCM sym_debug;
|
||||||
|
|
||||||
|
static SCM make_u8vector (const scm_t_uint8 *bytes, size_t len)
|
||||||
|
{
|
||||||
|
scm_t_uint8 *new_bytes = scm_gc_malloc (len, "make-u8vector");
|
||||||
|
memcpy (new_bytes, bytes, len);
|
||||||
|
return scm_take_u8vector (new_bytes, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
really_make_boot_program (long nargs)
|
||||||
|
{
|
||||||
|
scm_byte_t bytes[] = {0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
scm_op_mv_call, 0, 0, 1, scm_op_make_int8_1, scm_op_halt};
|
||||||
|
SCM ret;
|
||||||
|
((scm_t_uint32*)bytes)[1] = 6; /* set len in current endianness, no meta */
|
||||||
|
if (SCM_UNLIKELY (nargs > 255 || nargs < 0))
|
||||||
|
abort ();
|
||||||
|
bytes[13] = (scm_byte_t)nargs;
|
||||||
|
ret = scm_make_program (scm_bytecode_to_objcode (make_u8vector (bytes, sizeof(bytes))),
|
||||||
|
SCM_BOOL_F, SCM_EOL);
|
||||||
|
SCM_SET_SMOB_FLAGS (ret, SCM_F_PROGRAM_IS_BOOT);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#define NUM_BOOT_PROGS 8
|
||||||
|
static SCM
|
||||||
|
vm_make_boot_program (long nargs)
|
||||||
|
{
|
||||||
|
static SCM programs[NUM_BOOT_PROGS] = { 0, };
|
||||||
|
|
||||||
|
if (SCM_UNLIKELY (!programs[0]))
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < NUM_BOOT_PROGS; i++)
|
||||||
|
programs[i] = scm_permanent_object (really_make_boot_program (i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SCM_LIKELY (nargs < NUM_BOOT_PROGS))
|
||||||
|
return programs[nargs];
|
||||||
|
else
|
||||||
|
return really_make_boot_program (nargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VM
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define VM_DEFAULT_STACK_SIZE (16 * 1024)
|
||||||
|
|
||||||
|
#define VM_NAME vm_regular_engine
|
||||||
|
#define FUNC_NAME "vm-regular-engine"
|
||||||
|
#define VM_ENGINE SCM_VM_REGULAR_ENGINE
|
||||||
|
#include "vm-engine.c"
|
||||||
|
#undef VM_NAME
|
||||||
|
#undef FUNC_NAME
|
||||||
|
#undef VM_ENGINE
|
||||||
|
|
||||||
|
#define VM_NAME vm_debug_engine
|
||||||
|
#define FUNC_NAME "vm-debug-engine"
|
||||||
|
#define VM_ENGINE SCM_VM_DEBUG_ENGINE
|
||||||
|
#include "vm-engine.c"
|
||||||
|
#undef VM_NAME
|
||||||
|
#undef FUNC_NAME
|
||||||
|
#undef VM_ENGINE
|
||||||
|
|
||||||
|
static const scm_t_vm_engine vm_engines[] =
|
||||||
|
{ vm_regular_engine, vm_debug_engine };
|
||||||
|
|
||||||
|
scm_t_bits scm_tc16_vm;
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
make_vm (void)
|
||||||
|
#define FUNC_NAME "make_vm"
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!scm_tc16_vm)
|
||||||
|
return SCM_BOOL_F; /* not booted yet */
|
||||||
|
|
||||||
|
struct scm_vm *vp = scm_gc_malloc (sizeof (struct scm_vm), "vm");
|
||||||
|
|
||||||
|
vp->stack_size = VM_DEFAULT_STACK_SIZE;
|
||||||
|
vp->stack_base = scm_gc_malloc (vp->stack_size * sizeof (SCM),
|
||||||
|
"stack-base");
|
||||||
|
#ifdef VM_ENABLE_STACK_NULLING
|
||||||
|
memset (vp->stack_base, 0, vp->stack_size * sizeof (SCM));
|
||||||
|
#endif
|
||||||
|
vp->stack_limit = vp->stack_base + vp->stack_size - 3;
|
||||||
|
vp->ip = NULL;
|
||||||
|
vp->sp = vp->stack_base - 1;
|
||||||
|
vp->fp = NULL;
|
||||||
|
vp->engine = SCM_VM_DEBUG_ENGINE;
|
||||||
|
vp->time = 0;
|
||||||
|
vp->clock = 0;
|
||||||
|
vp->options = SCM_EOL;
|
||||||
|
for (i = 0; i < SCM_VM_NUM_HOOKS; i++)
|
||||||
|
vp->hooks[i] = SCM_BOOL_F;
|
||||||
|
vp->trace_frame = SCM_BOOL_F;
|
||||||
|
SCM_RETURN_NEWSMOB (scm_tc16_vm, vp);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
vm_mark (SCM obj)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct scm_vm *vp = SCM_VM_DATA (obj);
|
||||||
|
|
||||||
|
#ifdef VM_ENABLE_STACK_NULLING
|
||||||
|
if (vp->sp >= vp->stack_base)
|
||||||
|
if (!vp->sp[0] || vp->sp[1])
|
||||||
|
abort ();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* mark the stack, precisely */
|
||||||
|
vm_mark_stack (vp->stack_base, vp->sp + 1 - vp->stack_base, vp->fp, 0);
|
||||||
|
|
||||||
|
/* mark other objects */
|
||||||
|
for (i = 0; i < SCM_VM_NUM_HOOKS; i++)
|
||||||
|
scm_gc_mark (vp->hooks[i]);
|
||||||
|
|
||||||
|
scm_gc_mark (vp->trace_frame);
|
||||||
|
|
||||||
|
return vp->options;
|
||||||
|
}
|
||||||
|
|
||||||
|
static scm_sizet
|
||||||
|
vm_free (SCM obj)
|
||||||
|
{
|
||||||
|
struct scm_vm *vp = SCM_VM_DATA (obj);
|
||||||
|
|
||||||
|
scm_gc_free (vp->stack_base, vp->stack_size * sizeof (SCM),
|
||||||
|
"stack-base");
|
||||||
|
scm_gc_free (vp, sizeof (struct scm_vm), "vm");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SCM
|
||||||
|
scm_c_vm_run (SCM vm, SCM program, SCM *argv, int nargs)
|
||||||
|
{
|
||||||
|
struct scm_vm *vp = SCM_VM_DATA (vm);
|
||||||
|
return vm_engines[vp->engine](vp, program, argv, nargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCM
|
||||||
|
scm_vm_apply (SCM vm, SCM program, SCM args)
|
||||||
|
#define FUNC_NAME "scm_vm_apply"
|
||||||
|
{
|
||||||
|
SCM *argv;
|
||||||
|
int i, nargs;
|
||||||
|
|
||||||
|
SCM_VALIDATE_VM (1, vm);
|
||||||
|
SCM_VALIDATE_PROGRAM (2, program);
|
||||||
|
|
||||||
|
nargs = scm_ilength (args);
|
||||||
|
if (SCM_UNLIKELY (nargs < 0))
|
||||||
|
scm_wrong_type_arg_msg (FUNC_NAME, 3, args, "list");
|
||||||
|
|
||||||
|
argv = alloca(nargs * sizeof(SCM));
|
||||||
|
for (i = 0; i < nargs; i++)
|
||||||
|
{
|
||||||
|
argv[i] = SCM_CAR (args);
|
||||||
|
args = SCM_CDR (args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return scm_c_vm_run (vm, program, argv, nargs);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
/* Scheme interface */
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_version, "vm-version", 0, 0, 0,
|
||||||
|
(void),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_version
|
||||||
|
{
|
||||||
|
return scm_from_locale_string (PACKAGE_VERSION);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_the_vm, "the-vm", 0, 0, 0,
|
||||||
|
(void),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_the_vm
|
||||||
|
{
|
||||||
|
scm_i_thread *t = SCM_I_CURRENT_THREAD;
|
||||||
|
|
||||||
|
if (SCM_UNLIKELY (SCM_FALSEP ((t->vm))))
|
||||||
|
t->vm = make_vm ();
|
||||||
|
|
||||||
|
return t->vm;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_p, "vm?", 1, 0, 0,
|
||||||
|
(SCM obj),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_p
|
||||||
|
{
|
||||||
|
return SCM_BOOL (SCM_VM_P (obj));
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_make_vm, "make-vm", 0, 0, 0,
|
||||||
|
(void),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_make_vm,
|
||||||
|
{
|
||||||
|
return make_vm ();
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_ip, "vm:ip", 1, 0, 0,
|
||||||
|
(SCM vm),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_ip
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_VM (1, vm);
|
||||||
|
return scm_from_ulong ((unsigned long) SCM_VM_DATA (vm)->ip);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_sp, "vm:sp", 1, 0, 0,
|
||||||
|
(SCM vm),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_sp
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_VM (1, vm);
|
||||||
|
return scm_from_ulong ((unsigned long) SCM_VM_DATA (vm)->sp);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_fp, "vm:fp", 1, 0, 0,
|
||||||
|
(SCM vm),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_fp
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_VM (1, vm);
|
||||||
|
return scm_from_ulong ((unsigned long) SCM_VM_DATA (vm)->fp);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
#define VM_DEFINE_HOOK(n) \
|
||||||
|
{ \
|
||||||
|
struct scm_vm *vp; \
|
||||||
|
SCM_VALIDATE_VM (1, vm); \
|
||||||
|
vp = SCM_VM_DATA (vm); \
|
||||||
|
if (SCM_FALSEP (vp->hooks[n])) \
|
||||||
|
vp->hooks[n] = scm_make_hook (SCM_I_MAKINUM (1)); \
|
||||||
|
return vp->hooks[n]; \
|
||||||
|
}
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_boot_hook, "vm-boot-hook", 1, 0, 0,
|
||||||
|
(SCM vm),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_boot_hook
|
||||||
|
{
|
||||||
|
VM_DEFINE_HOOK (SCM_VM_BOOT_HOOK);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_halt_hook, "vm-halt-hook", 1, 0, 0,
|
||||||
|
(SCM vm),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_halt_hook
|
||||||
|
{
|
||||||
|
VM_DEFINE_HOOK (SCM_VM_HALT_HOOK);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_next_hook, "vm-next-hook", 1, 0, 0,
|
||||||
|
(SCM vm),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_next_hook
|
||||||
|
{
|
||||||
|
VM_DEFINE_HOOK (SCM_VM_NEXT_HOOK);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_break_hook, "vm-break-hook", 1, 0, 0,
|
||||||
|
(SCM vm),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_break_hook
|
||||||
|
{
|
||||||
|
VM_DEFINE_HOOK (SCM_VM_BREAK_HOOK);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_enter_hook, "vm-enter-hook", 1, 0, 0,
|
||||||
|
(SCM vm),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_enter_hook
|
||||||
|
{
|
||||||
|
VM_DEFINE_HOOK (SCM_VM_ENTER_HOOK);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_apply_hook, "vm-apply-hook", 1, 0, 0,
|
||||||
|
(SCM vm),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_apply_hook
|
||||||
|
{
|
||||||
|
VM_DEFINE_HOOK (SCM_VM_APPLY_HOOK);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_exit_hook, "vm-exit-hook", 1, 0, 0,
|
||||||
|
(SCM vm),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_exit_hook
|
||||||
|
{
|
||||||
|
VM_DEFINE_HOOK (SCM_VM_EXIT_HOOK);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_return_hook, "vm-return-hook", 1, 0, 0,
|
||||||
|
(SCM vm),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_return_hook
|
||||||
|
{
|
||||||
|
VM_DEFINE_HOOK (SCM_VM_RETURN_HOOK);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_option, "vm-option", 2, 0, 0,
|
||||||
|
(SCM vm, SCM key),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_option
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_VM (1, vm);
|
||||||
|
return scm_assq_ref (SCM_VM_DATA (vm)->options, key);
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_set_vm_option_x, "set-vm-option!", 3, 0, 0,
|
||||||
|
(SCM vm, SCM key, SCM val),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_set_vm_option_x
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_VM (1, vm);
|
||||||
|
SCM_VM_DATA (vm)->options
|
||||||
|
= scm_assq_set_x (SCM_VM_DATA (vm)->options, key, val);
|
||||||
|
return SCM_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_stats, "vm-stats", 1, 0, 0,
|
||||||
|
(SCM vm),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_stats
|
||||||
|
{
|
||||||
|
SCM stats;
|
||||||
|
|
||||||
|
SCM_VALIDATE_VM (1, vm);
|
||||||
|
|
||||||
|
stats = scm_make_vector (SCM_I_MAKINUM (2), SCM_UNSPECIFIED);
|
||||||
|
scm_vector_set_x (stats, SCM_I_MAKINUM (0),
|
||||||
|
scm_from_ulong (SCM_VM_DATA (vm)->time));
|
||||||
|
scm_vector_set_x (stats, SCM_I_MAKINUM (1),
|
||||||
|
scm_from_ulong (SCM_VM_DATA (vm)->clock));
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_vm_trace_frame, "vm-trace-frame", 1, 0, 0,
|
||||||
|
(SCM vm),
|
||||||
|
"")
|
||||||
|
#define FUNC_NAME s_scm_vm_trace_frame
|
||||||
|
{
|
||||||
|
SCM_VALIDATE_VM (1, vm);
|
||||||
|
return SCM_VM_DATA (vm)->trace_frame;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize
|
||||||
|
*/
|
||||||
|
|
||||||
|
SCM scm_load_compiled_with_vm (SCM file)
|
||||||
|
{
|
||||||
|
SCM program = scm_make_program (scm_load_objcode (file),
|
||||||
|
SCM_BOOL_F, SCM_EOL);
|
||||||
|
|
||||||
|
return scm_c_vm_run (scm_the_vm (), program, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scm_bootstrap_vm (void)
|
||||||
|
{
|
||||||
|
static int strappage = 0;
|
||||||
|
|
||||||
|
if (strappage)
|
||||||
|
return;
|
||||||
|
|
||||||
|
scm_bootstrap_frames ();
|
||||||
|
scm_bootstrap_instructions ();
|
||||||
|
scm_bootstrap_objcodes ();
|
||||||
|
scm_bootstrap_programs ();
|
||||||
|
|
||||||
|
scm_tc16_vm_cont = scm_make_smob_type ("vm-cont", 0);
|
||||||
|
scm_set_smob_mark (scm_tc16_vm_cont, vm_cont_mark);
|
||||||
|
scm_set_smob_free (scm_tc16_vm_cont, vm_cont_free);
|
||||||
|
|
||||||
|
scm_tc16_vm = scm_make_smob_type ("vm", 0);
|
||||||
|
scm_set_smob_mark (scm_tc16_vm, vm_mark);
|
||||||
|
scm_set_smob_free (scm_tc16_vm, vm_free);
|
||||||
|
scm_set_smob_apply (scm_tc16_vm, scm_vm_apply, 1, 0, 1);
|
||||||
|
|
||||||
|
scm_c_define ("load-compiled",
|
||||||
|
scm_c_make_gsubr ("load-compiled/vm", 1, 0, 0,
|
||||||
|
scm_load_compiled_with_vm));
|
||||||
|
|
||||||
|
sym_vm_run = scm_permanent_object (scm_from_locale_symbol ("vm-run"));
|
||||||
|
sym_vm_error = scm_permanent_object (scm_from_locale_symbol ("vm-error"));
|
||||||
|
sym_debug = scm_permanent_object (scm_from_locale_symbol ("debug"));
|
||||||
|
|
||||||
|
strappage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scm_init_vm (void)
|
||||||
|
{
|
||||||
|
scm_bootstrap_vm ();
|
||||||
|
|
||||||
|
#ifndef SCM_MAGIC_SNARFER
|
||||||
|
#include "libguile/vm.x"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
139
libguile/vm.h
Normal file
139
libguile/vm.h
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
/* 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 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. */
|
||||||
|
|
||||||
|
#ifndef _SCM_VM_H_
|
||||||
|
#define _SCM_VM_H_
|
||||||
|
|
||||||
|
#include <libguile.h>
|
||||||
|
#include <libguile/programs.h>
|
||||||
|
|
||||||
|
#define SCM_VM_BOOT_HOOK 0
|
||||||
|
#define SCM_VM_HALT_HOOK 1
|
||||||
|
#define SCM_VM_NEXT_HOOK 2
|
||||||
|
#define SCM_VM_BREAK_HOOK 3
|
||||||
|
#define SCM_VM_ENTER_HOOK 4
|
||||||
|
#define SCM_VM_APPLY_HOOK 5
|
||||||
|
#define SCM_VM_EXIT_HOOK 6
|
||||||
|
#define SCM_VM_RETURN_HOOK 7
|
||||||
|
#define SCM_VM_NUM_HOOKS 8
|
||||||
|
|
||||||
|
struct scm_vm;
|
||||||
|
|
||||||
|
typedef SCM (*scm_t_vm_engine) (struct scm_vm *vp, SCM program, SCM *argv, int nargs);
|
||||||
|
|
||||||
|
#define SCM_VM_REGULAR_ENGINE 0
|
||||||
|
#define SCM_VM_DEBUG_ENGINE 1
|
||||||
|
#define SCM_VM_NUM_ENGINES 2
|
||||||
|
|
||||||
|
struct scm_vm {
|
||||||
|
scm_byte_t *ip; /* instruction pointer */
|
||||||
|
SCM *sp; /* stack pointer */
|
||||||
|
SCM *fp; /* frame pointer */
|
||||||
|
size_t stack_size; /* stack size */
|
||||||
|
SCM *stack_base; /* stack base address */
|
||||||
|
SCM *stack_limit; /* stack limit address */
|
||||||
|
int engine; /* which vm engine we're using */
|
||||||
|
SCM hooks[SCM_VM_NUM_HOOKS]; /* hooks */
|
||||||
|
SCM options; /* options */
|
||||||
|
unsigned long time; /* time spent */
|
||||||
|
unsigned long clock; /* bogos clock */
|
||||||
|
SCM trace_frame; /* a frame being traced */
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SCM scm_the_vm_fluid;
|
||||||
|
|
||||||
|
#define SCM_VM_P(x) SCM_SMOB_PREDICATE (scm_tc16_vm, x)
|
||||||
|
#define SCM_VM_DATA(vm) ((struct scm_vm *) SCM_SMOB_DATA (vm))
|
||||||
|
#define SCM_VALIDATE_VM(pos,x) SCM_MAKE_VALIDATE (pos, x, VM_P)
|
||||||
|
|
||||||
|
extern SCM scm_the_vm ();
|
||||||
|
extern SCM scm_make_vm (void);
|
||||||
|
extern SCM scm_vm_apply (SCM vm, SCM program, SCM args);
|
||||||
|
extern SCM scm_c_vm_run (SCM vm, SCM program, SCM *argv, int nargs);
|
||||||
|
extern SCM scm_vm_option_ref (SCM vm, SCM key);
|
||||||
|
extern SCM scm_vm_option_set_x (SCM vm, SCM key, SCM val);
|
||||||
|
|
||||||
|
extern SCM scm_vm_version (void);
|
||||||
|
extern SCM scm_the_vm (void);
|
||||||
|
extern SCM scm_vm_p (SCM obj);
|
||||||
|
extern SCM scm_vm_ip (SCM vm);
|
||||||
|
extern SCM scm_vm_sp (SCM vm);
|
||||||
|
extern SCM scm_vm_fp (SCM vm);
|
||||||
|
extern SCM scm_vm_boot_hook (SCM vm);
|
||||||
|
extern SCM scm_vm_halt_hook (SCM vm);
|
||||||
|
extern SCM scm_vm_next_hook (SCM vm);
|
||||||
|
extern SCM scm_vm_break_hook (SCM vm);
|
||||||
|
extern SCM scm_vm_enter_hook (SCM vm);
|
||||||
|
extern SCM scm_vm_apply_hook (SCM vm);
|
||||||
|
extern SCM scm_vm_exit_hook (SCM vm);
|
||||||
|
extern SCM scm_vm_return_hook (SCM vm);
|
||||||
|
extern SCM scm_vm_option (SCM vm, SCM key);
|
||||||
|
extern SCM scm_set_vm_option_x (SCM vm, SCM key, SCM val);
|
||||||
|
extern SCM scm_vm_stats (SCM vm);
|
||||||
|
extern SCM scm_vm_trace_frame (SCM vm);
|
||||||
|
|
||||||
|
struct scm_vm_cont {
|
||||||
|
scm_byte_t *ip;
|
||||||
|
SCM *sp;
|
||||||
|
SCM *fp;
|
||||||
|
scm_t_ptrdiff stack_size;
|
||||||
|
SCM *stack_base;
|
||||||
|
scm_t_ptrdiff reloc;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern scm_t_bits scm_tc16_vm_cont;
|
||||||
|
#define SCM_VM_CONT_P(OBJ) SCM_SMOB_PREDICATE (scm_tc16_vm_cont, OBJ)
|
||||||
|
#define SCM_VM_CONT_DATA(CONT) ((struct scm_vm_cont *) SCM_CELL_WORD_1 (CONT))
|
||||||
|
|
||||||
|
extern SCM scm_vm_capture_continuations (void);
|
||||||
|
extern void scm_vm_reinstate_continuations (SCM conts);
|
||||||
|
|
||||||
|
extern SCM scm_load_compiled_with_vm (SCM file);
|
||||||
|
|
||||||
|
extern void scm_init_vm (void);
|
||||||
|
|
||||||
|
#endif /* _SCM_VM_H_ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Local Variables:
|
||||||
|
c-file-style: "gnu"
|
||||||
|
End:
|
||||||
|
*/
|
22
m4/labels-as-values.m4
Normal file
22
m4/labels-as-values.m4
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
dnl check for gcc's "labels as values" feature
|
||||||
|
AC_DEFUN([AC_C_LABELS_AS_VALUES],
|
||||||
|
[AC_CACHE_CHECK([labels as values], ac_cv_labels_as_values,
|
||||||
|
[AC_TRY_COMPILE([
|
||||||
|
int foo(int);
|
||||||
|
int foo(i)
|
||||||
|
int i; {
|
||||||
|
static void *label[] = { &&l1, &&l2 };
|
||||||
|
goto *label[i];
|
||||||
|
l1: return 1;
|
||||||
|
l2: return 2;
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[int i;],
|
||||||
|
ac_cv_labels_as_values=yes,
|
||||||
|
ac_cv_labels_as_values=no)])
|
||||||
|
if test "$ac_cv_labels_as_values" = yes; then
|
||||||
|
AC_DEFINE([HAVE_LABELS_AS_VALUES], [],
|
||||||
|
[Define if compiler supports gcc's "labels as values" (aka computed goto)
|
||||||
|
feature, used to speed up instruction dispatch in the interpreter.])
|
||||||
|
fi
|
||||||
|
])
|
90
module/Makefile.am
Normal file
90
module/Makefile.am
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
## Process this file with automake to produce Makefile.in.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2009 Free Software Foundation, Inc.
|
||||||
|
##
|
||||||
|
## This file is part of GUILE.
|
||||||
|
##
|
||||||
|
## GUILE 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.
|
||||||
|
##
|
||||||
|
## GUILE 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 GUILE; see the file COPYING. If not, write
|
||||||
|
## to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
||||||
|
## Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
# Build the compiler and VM support first to avoid stack overflows
|
||||||
|
# when building the rest.
|
||||||
|
SUBDIRS = . ice-9 srfi oop
|
||||||
|
|
||||||
|
# We're at the root of the module hierarchy.
|
||||||
|
modpath =
|
||||||
|
|
||||||
|
SOURCES = \
|
||||||
|
system/base/pmatch.scm system/base/syntax.scm \
|
||||||
|
system/base/compile.scm system/base/language.scm \
|
||||||
|
\
|
||||||
|
system/vm/debug.scm system/vm/frame.scm system/vm/instruction.scm \
|
||||||
|
system/vm/objcode.scm system/vm/profile.scm system/vm/program.scm \
|
||||||
|
system/vm/trace.scm system/vm/vm.scm \
|
||||||
|
\
|
||||||
|
system/xref.scm \
|
||||||
|
\
|
||||||
|
system/repl/repl.scm system/repl/common.scm \
|
||||||
|
system/repl/command.scm \
|
||||||
|
\
|
||||||
|
language/ghil.scm language/glil.scm language/assembly.scm \
|
||||||
|
\
|
||||||
|
$(SCHEME_LANG_SOURCES) $(ECMASCRIPT_LANG_SOURCES) \
|
||||||
|
$(GHIL_LANG_SOURCES) $(GLIL_LANG_SOURCES) \
|
||||||
|
$(ASSEMBLY_LANG_SOURCES) $(BYTECODE_LANG_SOURCES) \
|
||||||
|
$(OBJCODE_LANG_SOURCES) $(VALUE_LANG_SOURCES)
|
||||||
|
|
||||||
|
SCHEME_LANG_SOURCES = \
|
||||||
|
language/scheme/amatch.scm language/scheme/expand.scm \
|
||||||
|
language/scheme/compile-ghil.scm language/scheme/spec.scm \
|
||||||
|
language/scheme/inline.scm
|
||||||
|
|
||||||
|
GHIL_LANG_SOURCES = \
|
||||||
|
language/ghil/spec.scm language/ghil/compile-glil.scm
|
||||||
|
|
||||||
|
GLIL_LANG_SOURCES = \
|
||||||
|
language/glil/spec.scm language/glil/compile-assembly.scm \
|
||||||
|
language/glil/decompile-assembly.scm
|
||||||
|
|
||||||
|
ASSEMBLY_LANG_SOURCES = \
|
||||||
|
language/assembly/spec.scm \
|
||||||
|
language/assembly/compile-bytecode.scm \
|
||||||
|
language/assembly/decompile-bytecode.scm \
|
||||||
|
language/assembly/disassemble.scm
|
||||||
|
|
||||||
|
BYTECODE_LANG_SOURCES = \
|
||||||
|
language/bytecode/spec.scm
|
||||||
|
|
||||||
|
OBJCODE_LANG_SOURCES = \
|
||||||
|
language/objcode/spec.scm
|
||||||
|
|
||||||
|
VALUE_LANG_SOURCES = \
|
||||||
|
language/value/spec.scm
|
||||||
|
|
||||||
|
ECMASCRIPT_LANG_SOURCES = \
|
||||||
|
language/ecmascript/parse-lalr.scm \
|
||||||
|
language/ecmascript/tokenize.scm \
|
||||||
|
language/ecmascript/parse.scm \
|
||||||
|
language/ecmascript/spec.scm \
|
||||||
|
language/ecmascript/impl.scm \
|
||||||
|
language/ecmascript/base.scm \
|
||||||
|
language/ecmascript/function.scm \
|
||||||
|
language/ecmascript/array.scm \
|
||||||
|
language/ecmascript/compile-ghil.scm
|
||||||
|
|
||||||
|
NOCOMP_SOURCES = \
|
||||||
|
system/repl/describe.scm
|
||||||
|
|
||||||
|
include $(top_srcdir)/am/guilec
|
|
@ -24,35 +24,47 @@ AUTOMAKE_OPTIONS = gnu
|
||||||
SUBDIRS = debugger debugging
|
SUBDIRS = debugger debugging
|
||||||
|
|
||||||
# These should be installed and distributed.
|
# These should be installed and distributed.
|
||||||
ice9_sources = \
|
modpath = ice-9
|
||||||
and-let-star.scm boot-9.scm calling.scm common-list.scm \
|
# Compile psyntax and boot-9 first, so that we get the speed benefit in
|
||||||
|
# the rest of the compilation. Also, if there is too much switching back
|
||||||
|
# and forth between interpreted and compiled code, we end up using more
|
||||||
|
# of the C stack than the interpreter would have; so avoid that by
|
||||||
|
# putting these core modules first.
|
||||||
|
SOURCES = psyntax-pp.scm annotate.scm boot-9.scm \
|
||||||
|
and-let-star.scm calling.scm common-list.scm \
|
||||||
debug.scm debugger.scm documentation.scm emacs.scm expect.scm \
|
debug.scm debugger.scm documentation.scm emacs.scm expect.scm \
|
||||||
format.scm getopt-long.scm hcons.scm i18n.scm \
|
format.scm getopt-long.scm hcons.scm i18n.scm \
|
||||||
lineio.scm ls.scm mapping.scm \
|
lineio.scm ls.scm mapping.scm match.scm \
|
||||||
match.scm networking.scm null.scm optargs.scm poe.scm popen.scm \
|
networking.scm null.scm occam-channel.scm optargs.scm poe.scm \
|
||||||
posix.scm psyntax.pp psyntax.ss q.scm r4rs.scm r5rs.scm \
|
popen.scm posix.scm q.scm r4rs.scm r5rs.scm \
|
||||||
rdelim.scm receive.scm regex.scm runq.scm rw.scm \
|
rdelim.scm receive.scm regex.scm runq.scm rw.scm \
|
||||||
safe-r5rs.scm safe.scm session.scm slib.scm stack-catch.scm \
|
safe-r5rs.scm safe.scm session.scm slib.scm stack-catch.scm \
|
||||||
streams.scm string-fun.scm syncase.scm threads.scm \
|
streams.scm string-fun.scm syncase.scm threads.scm \
|
||||||
buffered-input.scm time.scm history.scm channel.scm \
|
buffered-input.scm time.scm history.scm channel.scm \
|
||||||
pretty-print.scm ftw.scm gap-buffer.scm occam-channel.scm \
|
pretty-print.scm ftw.scm gap-buffer.scm \
|
||||||
weak-vector.scm deprecated.scm list.scm serialize.scm \
|
weak-vector.scm deprecated.scm list.scm serialize.scm \
|
||||||
gds-client.scm gds-server.scm
|
gds-server.scm
|
||||||
|
|
||||||
subpkgdatadir = $(pkgdatadir)/${GUILE_EFFECTIVE_VERSION}/ice-9
|
# gds-client is tight with the memoizer, so punt on it until it can be
|
||||||
subpkgdata_DATA = $(ice9_sources)
|
# made portable.
|
||||||
TAGS_FILES = $(subpkgdata_DATA)
|
#
|
||||||
|
# psyntax.scm needs help. fortunately it's only needed when recompiling
|
||||||
|
# psyntax-pp.scm.
|
||||||
|
NOCOMP_SOURCES = gds-client.scm psyntax.scm
|
||||||
|
|
||||||
|
include $(top_srcdir)/am/guilec
|
||||||
|
|
||||||
## test.scm is not currently installed.
|
## test.scm is not currently installed.
|
||||||
EXTRA_DIST = $(ice9_sources) test.scm compile-psyntax.scm ChangeLog-2008
|
EXTRA_DIST += test.scm compile-psyntax.scm ChangeLog-2008
|
||||||
|
|
||||||
|
TAGS_FILES = $(SOURCES)
|
||||||
|
|
||||||
# We expect this to never be invoked when there is not already
|
# We expect this to never be invoked when there is not already
|
||||||
# ice-9/psyntax.pp in %load-path, since compile-psyntax.scm depends
|
# ice-9/psyntax-pp.scm in %load-path, since compile-psyntax.scm depends
|
||||||
# on ice-9/syncase.scm, which does `(load-from-path "ice-9/psyntax.pp")'.
|
# on ice-9/syncase.scm, which does `(load-from-path "ice-9/psyntax-pp.scm")'.
|
||||||
# In other words, to bootstrap this file, you need to do something like:
|
# In other words, to bootstrap this file, you need to do something like:
|
||||||
# GUILE_LOAD_PATH=/usr/local/share/guile/1.5.4 make psyntax.pp
|
# GUILE_LOAD_PATH=/usr/local/share/guile/1.5.4 make psyntax-pp.scm
|
||||||
include $(top_srcdir)/am/pre-inst-guile
|
include $(top_srcdir)/am/pre-inst-guile
|
||||||
psyntax.pp: psyntax.ss
|
psyntax-pp.scm: psyntax.scm
|
||||||
$(preinstguile) -s $(srcdir)/compile-psyntax.scm \
|
$(preinstguile) -s $(srcdir)/compile-psyntax.scm \
|
||||||
$(srcdir)/psyntax.ss $(srcdir)/psyntax.pp
|
$(srcdir)/psyntax.scm $(srcdir)/psyntax-pp.scm
|
||||||
|
|
80
module/ice-9/annotate.scm
Normal file
80
module/ice-9/annotate.scm
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
;;;; 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 (ice-9 annotate)
|
||||||
|
:export (<annotation> annotation? annotate deannotate make-annotation
|
||||||
|
annotation-expression annotation-source annotation-stripped
|
||||||
|
set-annotation-stripped!
|
||||||
|
deannotate/source-properties))
|
||||||
|
|
||||||
|
(define <annotation>
|
||||||
|
(make-vtable "prprpw"
|
||||||
|
(lambda (struct port)
|
||||||
|
(display "#<annotated " port)
|
||||||
|
(display (struct-ref struct 0) port)
|
||||||
|
(display ">" port))))
|
||||||
|
|
||||||
|
(define (annotation? x)
|
||||||
|
(and (struct? x) (eq? (struct-vtable x) <annotation>)))
|
||||||
|
|
||||||
|
(define (make-annotation e s . stripped?)
|
||||||
|
(if (null? stripped?)
|
||||||
|
(make-struct <annotation> 0 e s #f)
|
||||||
|
(apply make-struct <annotation> 0 e s stripped?)))
|
||||||
|
|
||||||
|
(define (annotation-expression a)
|
||||||
|
(struct-ref a 0))
|
||||||
|
(define (annotation-source a)
|
||||||
|
(struct-ref a 1))
|
||||||
|
(define (annotation-stripped a)
|
||||||
|
(struct-ref a 2))
|
||||||
|
(define (set-annotation-stripped! a stripped?)
|
||||||
|
(struct-set! a 2 stripped?))
|
||||||
|
|
||||||
|
(define (annotate e)
|
||||||
|
(let ((p (if (pair? e) (source-properties e) #f))
|
||||||
|
(out (cond ((and (list? e) (not (null? e)))
|
||||||
|
(map annotate e))
|
||||||
|
((pair? e)
|
||||||
|
(cons (annotate (car e)) (annotate (cdr e))))
|
||||||
|
(else e))))
|
||||||
|
(if (pair? p)
|
||||||
|
(make-annotation out p #f)
|
||||||
|
out)))
|
||||||
|
|
||||||
|
(define (deannotate e)
|
||||||
|
(cond ((list? e)
|
||||||
|
(map deannotate e))
|
||||||
|
((pair? e)
|
||||||
|
(cons (deannotate (car e)) (deannotate (cdr e))))
|
||||||
|
((annotation? e) (deannotate (annotation-expression e)))
|
||||||
|
(else e)))
|
||||||
|
|
||||||
|
(define (deannotate/source-properties e)
|
||||||
|
(cond ((list? e)
|
||||||
|
(map deannotate/source-properties e))
|
||||||
|
((pair? e)
|
||||||
|
(cons (deannotate/source-properties (car e))
|
||||||
|
(deannotate/source-properties (cdr e))))
|
||||||
|
((annotation? e)
|
||||||
|
(let ((e (deannotate/source-properties (annotation-expression e)))
|
||||||
|
(source (annotation-source e)))
|
||||||
|
(if (pair? e)
|
||||||
|
(set-source-properties! e source))
|
||||||
|
e))
|
||||||
|
(else e)))
|
|
@ -1,6 +1,6 @@
|
||||||
;;; installed-scm-file
|
;;; installed-scm-file
|
||||||
|
|
||||||
;;;; Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008
|
;;;; Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009
|
||||||
;;;; Free Software Foundation, Inc.
|
;;;; Free Software Foundation, Inc.
|
||||||
;;;;
|
;;;;
|
||||||
;;;; This library is free software; you can redistribute it and/or
|
;;;; This library is free software; you can redistribute it and/or
|
||||||
|
@ -86,43 +86,39 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;; {EVAL-CASE}
|
;; (eval-when (situation...) form...)
|
||||||
;;;
|
|
||||||
|
|
||||||
;; (eval-case ((situation*) forms)* (else forms)?)
|
|
||||||
;;
|
;;
|
||||||
;; Evaluate certain code based on the situation that eval-case is used
|
;; Evaluate certain code based on the situation that eval-when is used
|
||||||
;; in. The only defined situation right now is `load-toplevel' which
|
;; in. There are three situations defined.
|
||||||
;; triggers for code evaluated at the top-level, for example from the
|
;;
|
||||||
;; REPL or when loading a file.
|
;; `load' triggers when a file is loaded via `load', or when a compiled
|
||||||
|
;; file is loaded.
|
||||||
|
;;
|
||||||
|
;; `compile' triggers when an expression is compiled.
|
||||||
|
;;
|
||||||
|
;; `eval' triggers when code is evaluated interactively, as at the REPL
|
||||||
|
;; or via the `compile' or `eval' procedures.
|
||||||
|
|
||||||
(define eval-case
|
;; NB: this macro is only ever expanded by the interpreter. The compiler
|
||||||
|
;; notices it and interprets the situations differently.
|
||||||
|
(define eval-when
|
||||||
(procedure->memoizing-macro
|
(procedure->memoizing-macro
|
||||||
(lambda (exp env)
|
(lambda (exp env)
|
||||||
(define (toplevel-env? env)
|
(let ((situations (cadr exp))
|
||||||
(or (not (pair? env)) (not (pair? (car env)))))
|
(body (cddr exp)))
|
||||||
(define (syntax)
|
(if (or (memq 'load situations)
|
||||||
(error "syntax error in eval-case"))
|
(memq 'eval situations))
|
||||||
(let loop ((clauses (cdr exp)))
|
`(begin . ,body))))))
|
||||||
(cond
|
|
||||||
((null? clauses)
|
|
||||||
#f)
|
|
||||||
((not (list? (car clauses)))
|
|
||||||
(syntax))
|
|
||||||
((eq? 'else (caar clauses))
|
|
||||||
(or (null? (cdr clauses))
|
|
||||||
(syntax))
|
|
||||||
(cons 'begin (cdar clauses)))
|
|
||||||
((not (list? (caar clauses)))
|
|
||||||
(syntax))
|
|
||||||
((and (toplevel-env? env)
|
|
||||||
(memq 'load-toplevel (caar clauses)))
|
|
||||||
(cons 'begin (cdar clauses)))
|
|
||||||
(else
|
|
||||||
(loop (cdr clauses))))))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;; Before compiling, make sure any symbols are resolved in the (guile)
|
||||||
|
;; module, the primary location of those symbols, rather than in
|
||||||
|
;; (guile-user), the default module that we compile in.
|
||||||
|
|
||||||
|
(eval-when (compile)
|
||||||
|
(set-current-module (resolve-module '(guile))))
|
||||||
|
|
||||||
;;; {Defmacros}
|
;;; {Defmacros}
|
||||||
;;;
|
;;;
|
||||||
;;; Depends on: features, eval-case
|
;;; Depends on: features, eval-case
|
||||||
|
@ -150,19 +146,11 @@
|
||||||
(let ((defmacro-transformer
|
(let ((defmacro-transformer
|
||||||
(lambda (name parms . body)
|
(lambda (name parms . body)
|
||||||
(let ((transformer `(lambda ,parms ,@body)))
|
(let ((transformer `(lambda ,parms ,@body)))
|
||||||
`(eval-case
|
`(eval-when
|
||||||
((load-toplevel)
|
(eval load compile)
|
||||||
(define ,name (defmacro:transformer ,transformer)))
|
(define ,name (defmacro:transformer ,transformer)))))))
|
||||||
(else
|
|
||||||
(error "defmacro can only be used at the top level")))))))
|
|
||||||
(defmacro:transformer defmacro-transformer)))
|
(defmacro:transformer defmacro-transformer)))
|
||||||
|
|
||||||
(define defmacro:syntax-transformer
|
|
||||||
(lambda (f)
|
|
||||||
(procedure->syntax
|
|
||||||
(lambda (exp env)
|
|
||||||
(copy-tree (apply f (cdr exp)))))))
|
|
||||||
|
|
||||||
|
|
||||||
;; XXX - should the definition of the car really be looked up in the
|
;; XXX - should the definition of the car really be looked up in the
|
||||||
;; current module?
|
;; current module?
|
||||||
|
@ -196,15 +184,15 @@
|
||||||
|
|
||||||
(defmacro begin-deprecated forms
|
(defmacro begin-deprecated forms
|
||||||
(if (include-deprecated-features)
|
(if (include-deprecated-features)
|
||||||
(cons begin forms)
|
`(begin ,@forms)
|
||||||
#f))
|
(begin)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;; {R4RS compliance}
|
;;; {R4RS compliance}
|
||||||
;;;
|
;;;
|
||||||
|
|
||||||
(primitive-load-path "ice-9/r4rs.scm")
|
(primitive-load-path "ice-9/r4rs")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -327,22 +315,6 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;; {Environments}
|
|
||||||
;;;
|
|
||||||
|
|
||||||
(define the-environment
|
|
||||||
(procedure->syntax
|
|
||||||
(lambda (x e)
|
|
||||||
e)))
|
|
||||||
|
|
||||||
(define the-root-environment (the-environment))
|
|
||||||
|
|
||||||
(define (environment-module env)
|
|
||||||
(let ((closure (and (pair? env) (car (last-pair env)))))
|
|
||||||
(and closure (procedure-property closure 'module))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;; {Records}
|
;;; {Records}
|
||||||
;;;
|
;;;
|
||||||
|
|
||||||
|
@ -418,13 +390,13 @@
|
||||||
|
|
||||||
(define (record-constructor rtd . opt)
|
(define (record-constructor rtd . opt)
|
||||||
(let ((field-names (if (pair? opt) (car opt) (record-type-fields rtd))))
|
(let ((field-names (if (pair? opt) (car opt) (record-type-fields rtd))))
|
||||||
(local-eval `(lambda ,field-names
|
(primitive-eval
|
||||||
|
`(lambda ,field-names
|
||||||
(make-struct ',rtd 0 ,@(map (lambda (f)
|
(make-struct ',rtd 0 ,@(map (lambda (f)
|
||||||
(if (memq f field-names)
|
(if (memq f field-names)
|
||||||
f
|
f
|
||||||
#f))
|
#f))
|
||||||
(record-type-fields rtd))))
|
(record-type-fields rtd)))))))
|
||||||
the-root-environment)))
|
|
||||||
|
|
||||||
(define (record-predicate rtd)
|
(define (record-predicate rtd)
|
||||||
(lambda (obj) (and (struct? obj) (eq? rtd (struct-vtable obj)))))
|
(lambda (obj) (and (struct? obj) (eq? rtd (struct-vtable obj)))))
|
||||||
|
@ -437,25 +409,22 @@
|
||||||
#f)))
|
#f)))
|
||||||
|
|
||||||
(define (record-accessor rtd field-name)
|
(define (record-accessor rtd field-name)
|
||||||
(let* ((pos (list-index (record-type-fields rtd) field-name)))
|
(let ((pos (list-index (record-type-fields rtd) field-name)))
|
||||||
(if (not pos)
|
(if (not pos)
|
||||||
(error 'no-such-field field-name))
|
(error 'no-such-field field-name))
|
||||||
(local-eval `(lambda (obj)
|
(lambda (obj)
|
||||||
(if (eq? (struct-vtable obj) ,rtd)
|
(if (eq? (struct-vtable obj) rtd)
|
||||||
(struct-ref obj ,pos)
|
(struct-ref obj pos)
|
||||||
(%record-type-error ,rtd obj)))
|
(%record-type-error rtd obj)))))
|
||||||
the-root-environment)))
|
|
||||||
|
|
||||||
(define (record-modifier rtd field-name)
|
(define (record-modifier rtd field-name)
|
||||||
(let* ((pos (list-index (record-type-fields rtd) field-name)))
|
(let ((pos (list-index (record-type-fields rtd) field-name)))
|
||||||
(if (not pos)
|
(if (not pos)
|
||||||
(error 'no-such-field field-name))
|
(error 'no-such-field field-name))
|
||||||
(local-eval `(lambda (obj val)
|
(lambda (obj val)
|
||||||
(if (eq? (struct-vtable obj) ,rtd)
|
(if (eq? (struct-vtable obj) rtd)
|
||||||
(struct-set! obj ,pos val)
|
(struct-set! obj pos val)
|
||||||
(%record-type-error ,rtd obj)))
|
(%record-type-error rtd obj)))))
|
||||||
the-root-environment)))
|
|
||||||
|
|
||||||
|
|
||||||
(define (record? obj)
|
(define (record? obj)
|
||||||
(and (struct? obj) (record-type? (struct-vtable obj))))
|
(and (struct? obj) (record-type? (struct-vtable obj))))
|
||||||
|
@ -538,10 +507,10 @@
|
||||||
|
|
||||||
|
|
||||||
(if (provided? 'posix)
|
(if (provided? 'posix)
|
||||||
(primitive-load-path "ice-9/posix.scm"))
|
(primitive-load-path "ice-9/posix"))
|
||||||
|
|
||||||
(if (provided? 'socket)
|
(if (provided? 'socket)
|
||||||
(primitive-load-path "ice-9/networking.scm"))
|
(primitive-load-path "ice-9/networking"))
|
||||||
|
|
||||||
;; For reference, Emacs file-exists-p uses stat in this same way.
|
;; For reference, Emacs file-exists-p uses stat in this same way.
|
||||||
;; ENHANCE-ME: Catching an exception from stat is a bit wasteful, do this in
|
;; ENHANCE-ME: Catching an exception from stat is a bit wasteful, do this in
|
||||||
|
@ -569,10 +538,7 @@
|
||||||
#f)))))
|
#f)))))
|
||||||
|
|
||||||
(define (has-suffix? str suffix)
|
(define (has-suffix? str suffix)
|
||||||
(let ((sufl (string-length suffix))
|
(string-suffix? suffix str))
|
||||||
(sl (string-length str)))
|
|
||||||
(and (> sl sufl)
|
|
||||||
(string=? (substring str (- sl sufl) sl) suffix))))
|
|
||||||
|
|
||||||
(define (system-error-errno args)
|
(define (system-error-errno args)
|
||||||
(if (eq? (car args) 'system-error)
|
(if (eq? (car args) 'system-error)
|
||||||
|
@ -768,6 +734,14 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;; {The interpreter stack}
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(defmacro start-stack (tag exp)
|
||||||
|
`(%start-stack ,tag (lambda () ,exp)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;; {Loading by paths}
|
;;; {Loading by paths}
|
||||||
;;;
|
;;;
|
||||||
|
|
||||||
|
@ -1306,7 +1280,7 @@
|
||||||
*unspecified*)
|
*unspecified*)
|
||||||
|
|
||||||
(define module-defer-observers #f)
|
(define module-defer-observers #f)
|
||||||
(define module-defer-observers-mutex (make-mutex))
|
(define module-defer-observers-mutex (make-mutex 'recursive))
|
||||||
(define module-defer-observers-table (make-hash-table))
|
(define module-defer-observers-table (make-hash-table))
|
||||||
|
|
||||||
(define (module-modified m)
|
(define (module-modified m)
|
||||||
|
@ -1695,7 +1669,8 @@
|
||||||
;; Add INTERFACE to the list of interfaces used by MODULE.
|
;; Add INTERFACE to the list of interfaces used by MODULE.
|
||||||
;;
|
;;
|
||||||
(define (module-use! module interface)
|
(define (module-use! module interface)
|
||||||
(if (not (eq? module interface))
|
(if (not (or (eq? module interface)
|
||||||
|
(memq interface (module-uses module))))
|
||||||
(begin
|
(begin
|
||||||
;; Newly used modules must be appended rather than consed, so that
|
;; Newly used modules must be appended rather than consed, so that
|
||||||
;; `module-variable' traverses the use list starting from the first
|
;; `module-variable' traverses the use list starting from the first
|
||||||
|
@ -1803,8 +1778,7 @@
|
||||||
;;; The directory of all modules and the standard root module.
|
;;; The directory of all modules and the standard root module.
|
||||||
;;;
|
;;;
|
||||||
|
|
||||||
(define (module-public-interface m)
|
;; module-public-interface is defined in C.
|
||||||
(module-ref m '%module-public-interface #f))
|
|
||||||
(define (set-module-public-interface! m i)
|
(define (set-module-public-interface! m i)
|
||||||
(module-define! m '%module-public-interface i))
|
(module-define! m '%module-public-interface i))
|
||||||
(define (set-system-module! m s)
|
(define (set-system-module! m s)
|
||||||
|
@ -1815,23 +1789,26 @@
|
||||||
(set-module-name! the-root-module '(guile))
|
(set-module-name! the-root-module '(guile))
|
||||||
(set-module-name! the-scm-module '(guile))
|
(set-module-name! the-scm-module '(guile))
|
||||||
(set-module-kind! the-scm-module 'interface)
|
(set-module-kind! the-scm-module 'interface)
|
||||||
(for-each set-system-module! (list the-root-module the-scm-module) '(#t #t))
|
(set-system-module! the-root-module #t)
|
||||||
|
(set-system-module! the-scm-module #t)
|
||||||
|
|
||||||
;; NOTE: This binding is used in libguile/modules.c.
|
;; NOTE: This binding is used in libguile/modules.c.
|
||||||
;;
|
;;
|
||||||
(define (make-modules-in module name)
|
(define (make-modules-in module name)
|
||||||
(if (null? name)
|
(if (null? name)
|
||||||
module
|
module
|
||||||
(cond
|
(make-modules-in
|
||||||
((module-ref module (car name) #f)
|
(let* ((var (module-local-variable module (car name)))
|
||||||
=> (lambda (m) (make-modules-in m (cdr name))))
|
(val (and var (variable-bound? var) (variable-ref var))))
|
||||||
(else (let ((m (make-module 31)))
|
(if (module? val)
|
||||||
|
val
|
||||||
|
(let ((m (make-module 31)))
|
||||||
(set-module-kind! m 'directory)
|
(set-module-kind! m 'directory)
|
||||||
(set-module-name! m (append (or (module-name module)
|
(set-module-name! m (append (or (module-name module) '())
|
||||||
'())
|
|
||||||
(list (car name))))
|
(list (car name))))
|
||||||
(module-define! module (car name) m)
|
(module-define! module (car name) m)
|
||||||
(make-modules-in m (cdr name)))))))
|
m)))
|
||||||
|
(cdr name))))
|
||||||
|
|
||||||
(define (beautify-user-module! module)
|
(define (beautify-user-module! module)
|
||||||
(let ((interface (module-public-interface module)))
|
(let ((interface (module-public-interface module)))
|
||||||
|
@ -1848,25 +1825,28 @@
|
||||||
|
|
||||||
;; NOTE: This binding is used in libguile/modules.c.
|
;; NOTE: This binding is used in libguile/modules.c.
|
||||||
;;
|
;;
|
||||||
(define (resolve-module name . maybe-autoload)
|
(define resolve-module
|
||||||
|
(let ((the-root-module the-root-module))
|
||||||
|
(lambda (name . maybe-autoload)
|
||||||
|
(if (equal? name '(guile))
|
||||||
|
the-root-module
|
||||||
(let ((full-name (append '(%app modules) name)))
|
(let ((full-name (append '(%app modules) name)))
|
||||||
(let ((already (nested-ref the-root-module full-name)))
|
(let ((already (nested-ref the-root-module full-name))
|
||||||
(if already
|
(autoload (or (null? maybe-autoload) (car maybe-autoload))))
|
||||||
;; The module already exists...
|
(cond
|
||||||
(if (and (or (null? maybe-autoload) (car maybe-autoload))
|
((and already (module? already)
|
||||||
(not (module-public-interface already)))
|
(or (not autoload) (module-public-interface already)))
|
||||||
;; ...but we are told to load and it doesn't contain source, so
|
;; A hit, a palpable hit.
|
||||||
(begin
|
already)
|
||||||
|
(autoload
|
||||||
|
;; Try to autoload the module, and recurse.
|
||||||
(try-load-module name)
|
(try-load-module name)
|
||||||
already)
|
(resolve-module name #f))
|
||||||
;; simply return it.
|
(else
|
||||||
already)
|
;; A module is not bound (but maybe something else is),
|
||||||
(begin
|
;; we're not autoloading -- here's the weird semantics,
|
||||||
;; Try to autoload it if we are told so
|
;; we create an empty module.
|
||||||
(if (or (null? maybe-autoload) (car maybe-autoload))
|
(make-modules-in the-root-module full-name)))))))))
|
||||||
(try-load-module name))
|
|
||||||
;; Get/create it.
|
|
||||||
(make-modules-in (current-module) full-name))))))
|
|
||||||
|
|
||||||
;; Cheat. These bindings are needed by modules.c, but we don't want
|
;; Cheat. These bindings are needed by modules.c, but we don't want
|
||||||
;; to move their real definition here because that would be unnatural.
|
;; to move their real definition here because that would be unnatural.
|
||||||
|
@ -1877,16 +1857,17 @@
|
||||||
(define module-export! #f)
|
(define module-export! #f)
|
||||||
(define default-duplicate-binding-procedures #f)
|
(define default-duplicate-binding-procedures #f)
|
||||||
|
|
||||||
|
(define %app (make-module 31))
|
||||||
|
(define app %app) ;; for backwards compatability
|
||||||
|
|
||||||
|
(local-define '(%app modules) (make-module 31))
|
||||||
|
(local-define '(%app modules guile) the-root-module)
|
||||||
|
|
||||||
;; This boots the module system. All bindings needed by modules.c
|
;; This boots the module system. All bindings needed by modules.c
|
||||||
;; must have been defined by now.
|
;; must have been defined by now.
|
||||||
;;
|
;;
|
||||||
(set-current-module the-root-module)
|
(set-current-module the-root-module)
|
||||||
|
|
||||||
(define %app (make-module 31))
|
|
||||||
(define app %app) ;; for backwards compatability
|
|
||||||
(local-define '(%app modules) (make-module 31))
|
|
||||||
(local-define '(%app modules guile) the-root-module)
|
|
||||||
|
|
||||||
;; (define-special-value '(%app modules new-ws) (lambda () (make-scm-module)))
|
;; (define-special-value '(%app modules new-ws) (lambda () (make-scm-module)))
|
||||||
|
|
||||||
(define (try-load-module name)
|
(define (try-load-module name)
|
||||||
|
@ -2147,7 +2128,8 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
|
|
||||||
;;; {Compiled module}
|
;;; {Compiled module}
|
||||||
|
|
||||||
(define load-compiled #f)
|
(if (not (defined? 'load-compiled))
|
||||||
|
(define load-compiled #f))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2177,14 +2159,20 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
(lambda () (autoload-in-progress! dir-hint name))
|
(lambda () (autoload-in-progress! dir-hint name))
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(let ((file (in-vicinity dir-hint name)))
|
(let ((file (in-vicinity dir-hint name)))
|
||||||
(cond ((and load-compiled
|
(let ((compiled (and load-compiled
|
||||||
(%search-load-path (string-append file ".go")))
|
(%search-load-path
|
||||||
=> (lambda (full)
|
(string-append file ".go"))))
|
||||||
(load-file load-compiled full)))
|
(source (%search-load-path file)))
|
||||||
((%search-load-path file)
|
(cond ((and source
|
||||||
=> (lambda (full)
|
(or (not compiled)
|
||||||
|
(< (stat:mtime (stat compiled))
|
||||||
|
(stat:mtime (stat source)))))
|
||||||
|
(if compiled
|
||||||
|
(warn "source file" source "newer than" compiled))
|
||||||
(with-fluids ((current-reader #f))
|
(with-fluids ((current-reader #f))
|
||||||
(load-file primitive-load full)))))))
|
(load-file primitive-load source)))
|
||||||
|
(compiled
|
||||||
|
(load-file load-compiled compiled))))))
|
||||||
(lambda () (set-autoloaded! dir-hint name didit)))
|
(lambda () (set-autoloaded! dir-hint name didit)))
|
||||||
didit))))
|
didit))))
|
||||||
|
|
||||||
|
@ -2225,12 +2213,20 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
;;; {Run-time options}
|
;;; {Run-time options}
|
||||||
;;;
|
;;;
|
||||||
|
|
||||||
(define define-option-interface
|
(defmacro define-option-interface (option-group)
|
||||||
(let* ((option-name car)
|
(let* ((option-name car)
|
||||||
(option-value cadr)
|
(option-value cadr)
|
||||||
(option-documentation caddr)
|
(option-documentation caddr)
|
||||||
|
|
||||||
(print-option (lambda (option)
|
;; Below follow the macros defining the run-time option interfaces.
|
||||||
|
|
||||||
|
(make-options (lambda (interface)
|
||||||
|
`(lambda args
|
||||||
|
(cond ((null? args) (,interface))
|
||||||
|
((list? (car args))
|
||||||
|
(,interface (car args)) (,interface))
|
||||||
|
(else (for-each
|
||||||
|
(lambda (option)
|
||||||
(display (option-name option))
|
(display (option-name option))
|
||||||
(if (< (string-length
|
(if (< (string-length
|
||||||
(symbol->string (option-name option)))
|
(symbol->string (option-name option)))
|
||||||
|
@ -2240,16 +2236,7 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
(display (option-value option))
|
(display (option-value option))
|
||||||
(display #\tab)
|
(display #\tab)
|
||||||
(display (option-documentation option))
|
(display (option-documentation option))
|
||||||
(newline)))
|
(newline))
|
||||||
|
|
||||||
;; Below follow the macros defining the run-time option interfaces.
|
|
||||||
|
|
||||||
(make-options (lambda (interface)
|
|
||||||
`(lambda args
|
|
||||||
(cond ((null? args) (,interface))
|
|
||||||
((list? (car args))
|
|
||||||
(,interface (car args)) (,interface))
|
|
||||||
(else (for-each ,print-option
|
|
||||||
(,interface #t)))))))
|
(,interface #t)))))))
|
||||||
|
|
||||||
(make-enable (lambda (interface)
|
(make-enable (lambda (interface)
|
||||||
|
@ -2265,10 +2252,7 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
flags)
|
flags)
|
||||||
(,interface options)
|
(,interface options)
|
||||||
(,interface))))))
|
(,interface))))))
|
||||||
(procedure->memoizing-macro
|
(let* ((interface (car option-group))
|
||||||
(lambda (exp env)
|
|
||||||
(let* ((option-group (cadr exp))
|
|
||||||
(interface (car option-group))
|
|
||||||
(options/enable/disable (cadr option-group)))
|
(options/enable/disable (cadr option-group)))
|
||||||
`(begin
|
`(begin
|
||||||
(define ,(car options/enable/disable)
|
(define ,(car options/enable/disable)
|
||||||
|
@ -2278,9 +2262,9 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
(define ,(caddr options/enable/disable)
|
(define ,(caddr options/enable/disable)
|
||||||
,(make-disable interface))
|
,(make-disable interface))
|
||||||
(defmacro ,(caaddr option-group) (opt val)
|
(defmacro ,(caaddr option-group) (opt val)
|
||||||
`(,,(car options/enable/disable)
|
`(,',(car options/enable/disable)
|
||||||
(append (,,(car options/enable/disable))
|
(append (,',(car options/enable/disable))
|
||||||
(list ',opt ,val))))))))))
|
(list ',opt ,val))))))))
|
||||||
|
|
||||||
(define-option-interface
|
(define-option-interface
|
||||||
(eval-options-interface
|
(eval-options-interface
|
||||||
|
@ -2335,12 +2319,12 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
|
|
||||||
(define (set-repl-prompt! v) (set! scm-repl-prompt v))
|
(define (set-repl-prompt! v) (set! scm-repl-prompt v))
|
||||||
|
|
||||||
(define (default-lazy-handler key . args)
|
(define (default-pre-unwind-handler key . args)
|
||||||
(save-stack lazy-handler-dispatch)
|
(save-stack pre-unwind-handler-dispatch)
|
||||||
(apply throw key args))
|
(apply throw key args))
|
||||||
|
|
||||||
(define (lazy-handler-dispatch key . args)
|
(define (pre-unwind-handler-dispatch key . args)
|
||||||
(apply default-lazy-handler key args))
|
(apply default-pre-unwind-handler key args))
|
||||||
|
|
||||||
(define abort-hook (make-hook))
|
(define abort-hook (make-hook))
|
||||||
|
|
||||||
|
@ -2417,15 +2401,15 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
(else
|
(else
|
||||||
(apply bad-throw key args)))))))
|
(apply bad-throw key args)))))))
|
||||||
|
|
||||||
;; Note that having just `lazy-handler-dispatch'
|
;; Note that having just `pre-unwind-handler-dispatch'
|
||||||
;; here is connected with the mechanism that
|
;; here is connected with the mechanism that
|
||||||
;; produces a nice backtrace upon error. If, for
|
;; produces a nice backtrace upon error. If, for
|
||||||
;; example, this is replaced with (lambda args
|
;; example, this is replaced with (lambda args
|
||||||
;; (apply lazy-handler-dispatch args)), the stack
|
;; (apply pre-unwind-handler-dispatch args)), the stack
|
||||||
;; cutting (in save-stack) goes wrong and ends up
|
;; cutting (in save-stack) goes wrong and ends up
|
||||||
;; saving no stack at all, so there is no
|
;; saving no stack at all, so there is no
|
||||||
;; backtrace.
|
;; backtrace.
|
||||||
lazy-handler-dispatch)))
|
pre-unwind-handler-dispatch)))
|
||||||
|
|
||||||
(if next (loop next) status)))
|
(if next (loop next) status)))
|
||||||
(set! set-batch-mode?! (lambda (arg)
|
(set! set-batch-mode?! (lambda (arg)
|
||||||
|
@ -2536,7 +2520,7 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
;;; the readline library.
|
;;; the readline library.
|
||||||
(define repl-reader
|
(define repl-reader
|
||||||
(lambda (prompt)
|
(lambda (prompt)
|
||||||
(display prompt)
|
(display (if (string? prompt) prompt (prompt)))
|
||||||
(force-output)
|
(force-output)
|
||||||
(run-hook before-read-hook)
|
(run-hook before-read-hook)
|
||||||
((or (fluid-ref current-reader) read) (current-input-port))))
|
((or (fluid-ref current-reader) read) (current-input-port))))
|
||||||
|
@ -2719,25 +2703,11 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
(if (symbol? first)
|
(if (symbol? first)
|
||||||
(car rest)
|
(car rest)
|
||||||
`(lambda ,(cdr first) ,@rest))))
|
`(lambda ,(cdr first) ,@rest))))
|
||||||
`(eval-case
|
`(eval-when
|
||||||
((load-toplevel)
|
(eval load compile)
|
||||||
(define ,name (defmacro:transformer ,transformer)))
|
(define ,name (defmacro:transformer ,transformer)))))
|
||||||
(else
|
|
||||||
(error "define-macro can only be used at the top level")))))
|
|
||||||
|
|
||||||
|
|
||||||
(defmacro define-syntax-macro (first . rest)
|
|
||||||
(let ((name (if (symbol? first) first (car first)))
|
|
||||||
(transformer
|
|
||||||
(if (symbol? first)
|
|
||||||
(car rest)
|
|
||||||
`(lambda ,(cdr first) ,@rest))))
|
|
||||||
`(eval-case
|
|
||||||
((load-toplevel)
|
|
||||||
(define ,name (defmacro:syntax-transformer ,transformer)))
|
|
||||||
(else
|
|
||||||
(error "define-syntax-macro can only be used at the top level")))))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;; {While}
|
;;; {While}
|
||||||
|
@ -2748,32 +2718,25 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
;; The inner `do' loop avoids re-establishing a catch every iteration,
|
;; The inner `do' loop avoids re-establishing a catch every iteration,
|
||||||
;; that's only necessary if continue is actually used. A new key is
|
;; that's only necessary if continue is actually used. A new key is
|
||||||
;; generated every time, so break and continue apply to their originating
|
;; generated every time, so break and continue apply to their originating
|
||||||
;; `while' even when recursing. `while-helper' is an easy way to keep the
|
;; `while' even when recursing.
|
||||||
;; `key' binding away from the cond and body code.
|
|
||||||
;;
|
;;
|
||||||
;; FIXME: This is supposed to have an `unquote' on the `do' the same used
|
;; FIXME: This macro is unintentionally unhygienic with respect to let,
|
||||||
;; for lambda and not, so as to protect against any user rebinding of that
|
;; make-symbol, do, throw, catch, lambda, and not.
|
||||||
;; symbol, but unfortunately an unquote breaks with ice-9 syncase, eg.
|
|
||||||
;;
|
|
||||||
;; (use-modules (ice-9 syncase))
|
|
||||||
;; (while #f)
|
|
||||||
;; => ERROR: invalid syntax ()
|
|
||||||
;;
|
|
||||||
;; This is probably a bug in syncase.
|
|
||||||
;;
|
;;
|
||||||
(define-macro (while cond . body)
|
(define-macro (while cond . body)
|
||||||
(define (while-helper proc)
|
(let ((keyvar (make-symbol "while-keyvar")))
|
||||||
(do ((key (make-symbol "while-key")))
|
`(let ((,keyvar (make-symbol "while-key")))
|
||||||
((catch key
|
|
||||||
(lambda ()
|
|
||||||
(proc (lambda () (throw key #t))
|
|
||||||
(lambda () (throw key #f))))
|
|
||||||
(lambda (key arg) arg)))))
|
|
||||||
`(,while-helper (,lambda (break continue)
|
|
||||||
(do ()
|
(do ()
|
||||||
((,not ,cond))
|
((catch ,keyvar
|
||||||
|
(lambda ()
|
||||||
|
(let ((break (lambda () (throw ,keyvar #t)))
|
||||||
|
(continue (lambda () (throw ,keyvar #f))))
|
||||||
|
(do ()
|
||||||
|
((not ,cond))
|
||||||
,@body)
|
,@body)
|
||||||
#t)))
|
#t))
|
||||||
|
(lambda (key arg)
|
||||||
|
arg)))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2784,6 +2747,11 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
;; Return a list of expressions that evaluate to the appropriate
|
;; Return a list of expressions that evaluate to the appropriate
|
||||||
;; arguments for resolve-interface according to SPEC.
|
;; arguments for resolve-interface according to SPEC.
|
||||||
|
|
||||||
|
(eval-when
|
||||||
|
(compile)
|
||||||
|
(if (memq 'prefix (read-options))
|
||||||
|
(error "boot-9 must be compiled with #:kw, not :kw")))
|
||||||
|
|
||||||
(define (compile-interface-spec spec)
|
(define (compile-interface-spec spec)
|
||||||
(define (make-keyarg sym key quote?)
|
(define (make-keyarg sym key quote?)
|
||||||
(cond ((or (memq sym spec)
|
(cond ((or (memq sym spec)
|
||||||
|
@ -2847,14 +2815,12 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
(cddr args))))))
|
(cddr args))))))
|
||||||
|
|
||||||
(defmacro define-module args
|
(defmacro define-module args
|
||||||
`(eval-case
|
`(eval-when
|
||||||
((load-toplevel)
|
(eval load compile)
|
||||||
(let ((m (process-define-module
|
(let ((m (process-define-module
|
||||||
(list ,@(compile-define-module-args args)))))
|
(list ,@(compile-define-module-args args)))))
|
||||||
(set-current-module m)
|
(set-current-module m)
|
||||||
m))
|
m)))
|
||||||
(else
|
|
||||||
(error "define-module can only be used at the top level"))))
|
|
||||||
|
|
||||||
;; The guts of the use-modules macro. Add the interfaces of the named
|
;; The guts of the use-modules macro. Add the interfaces of the named
|
||||||
;; modules to the use-list of the current module, in order.
|
;; modules to the use-list of the current module, in order.
|
||||||
|
@ -2872,28 +2838,24 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
(module-use-interfaces! (current-module) interfaces)))))
|
(module-use-interfaces! (current-module) interfaces)))))
|
||||||
|
|
||||||
(defmacro use-modules modules
|
(defmacro use-modules modules
|
||||||
`(eval-case
|
`(eval-when
|
||||||
((load-toplevel)
|
(eval load compile)
|
||||||
(process-use-modules
|
(process-use-modules
|
||||||
(list ,@(map (lambda (m)
|
(list ,@(map (lambda (m)
|
||||||
`(list ,@(compile-interface-spec m)))
|
`(list ,@(compile-interface-spec m)))
|
||||||
modules)))
|
modules)))
|
||||||
*unspecified*)
|
*unspecified*))
|
||||||
(else
|
|
||||||
(error "use-modules can only be used at the top level"))))
|
|
||||||
|
|
||||||
(defmacro use-syntax (spec)
|
(defmacro use-syntax (spec)
|
||||||
`(eval-case
|
`(eval-when
|
||||||
((load-toplevel)
|
(eval load compile)
|
||||||
,@(if (pair? spec)
|
,@(if (pair? spec)
|
||||||
`((process-use-modules (list
|
`((process-use-modules (list
|
||||||
(list ,@(compile-interface-spec spec))))
|
(list ,@(compile-interface-spec spec))))
|
||||||
(set-module-transformer! (current-module)
|
(set-module-transformer! (current-module)
|
||||||
,(car (last-pair spec))))
|
,(car (last-pair spec))))
|
||||||
`((set-module-transformer! (current-module) ,spec)))
|
`((set-module-transformer! (current-module) ,spec)))
|
||||||
*unspecified*)
|
*unspecified*))
|
||||||
(else
|
|
||||||
(error "use-syntax can only be used at the top level"))))
|
|
||||||
|
|
||||||
;; Dirk:FIXME:: This incorrect (according to R5RS) syntax needs to be changed
|
;; Dirk:FIXME:: This incorrect (according to R5RS) syntax needs to be changed
|
||||||
;; as soon as guile supports hygienic macros.
|
;; as soon as guile supports hygienic macros.
|
||||||
|
@ -2914,7 +2876,7 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
(let ((name (defined-name (car args))))
|
(let ((name (defined-name (car args))))
|
||||||
`(begin
|
`(begin
|
||||||
(define-private ,@args)
|
(define-private ,@args)
|
||||||
(eval-case ((load-toplevel) (export ,name))))))))
|
(export ,name))))))
|
||||||
|
|
||||||
(defmacro defmacro-public args
|
(defmacro defmacro-public args
|
||||||
(define (syntax)
|
(define (syntax)
|
||||||
|
@ -2929,7 +2891,7 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
(#t
|
(#t
|
||||||
(let ((name (defined-name (car args))))
|
(let ((name (defined-name (car args))))
|
||||||
`(begin
|
`(begin
|
||||||
(eval-case ((load-toplevel) (export-syntax ,name)))
|
(export-syntax ,name)
|
||||||
(defmacro ,@args))))))
|
(defmacro ,@args))))))
|
||||||
|
|
||||||
;; Export a local variable
|
;; Export a local variable
|
||||||
|
@ -2967,22 +2929,14 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
names)))
|
names)))
|
||||||
|
|
||||||
(defmacro export names
|
(defmacro export names
|
||||||
`(eval-case
|
`(call-with-deferred-observers
|
||||||
((load-toplevel)
|
|
||||||
(call-with-deferred-observers
|
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(module-export! (current-module) ',names))))
|
(module-export! (current-module) ',names))))
|
||||||
(else
|
|
||||||
(error "export can only be used at the top level"))))
|
|
||||||
|
|
||||||
(defmacro re-export names
|
(defmacro re-export names
|
||||||
`(eval-case
|
`(call-with-deferred-observers
|
||||||
((load-toplevel)
|
|
||||||
(call-with-deferred-observers
|
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(module-re-export! (current-module) ',names))))
|
(module-re-export! (current-module) ',names))))
|
||||||
(else
|
|
||||||
(error "re-export can only be used at the top level"))))
|
|
||||||
|
|
||||||
(defmacro export-syntax names
|
(defmacro export-syntax names
|
||||||
`(export ,@names))
|
`(export ,@names))
|
||||||
|
@ -3019,6 +2973,19 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
;;; {Compiler interface}
|
||||||
|
;;;
|
||||||
|
;;; The full compiler interface can be found in (system). Here we put a
|
||||||
|
;;; few useful procedures into the global namespace.
|
||||||
|
|
||||||
|
(module-autoload! the-scm-module
|
||||||
|
'(system base compile)
|
||||||
|
'(compile
|
||||||
|
compile-time-environment))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;; {Parameters}
|
;;; {Parameters}
|
||||||
;;;
|
;;;
|
||||||
|
|
||||||
|
@ -3364,6 +3331,8 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
;; scmsigs.c scm_sigaction_for_thread), so the handlers setup here have
|
;; scmsigs.c scm_sigaction_for_thread), so the handlers setup here have
|
||||||
;; no effect.
|
;; no effect.
|
||||||
(let ((old-handlers #f)
|
(let ((old-handlers #f)
|
||||||
|
(start-repl (module-ref (resolve-interface '(system repl repl))
|
||||||
|
'start-repl))
|
||||||
(signals (if (provided? 'posix)
|
(signals (if (provided? 'posix)
|
||||||
`((,SIGINT . "User interrupt")
|
`((,SIGINT . "User interrupt")
|
||||||
(,SIGFPE . "Arithmetic error")
|
(,SIGFPE . "Arithmetic error")
|
||||||
|
@ -3398,7 +3367,7 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
|
|
||||||
;; the protected thunk.
|
;; the protected thunk.
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(let ((status (scm-style-repl)))
|
(let ((status (start-repl 'scheme)))
|
||||||
(run-hook exit-hook)
|
(run-hook exit-hook)
|
||||||
status))
|
status))
|
||||||
|
|
||||||
|
@ -3430,7 +3399,7 @@ module '(ice-9 q) '(make-q q-length))}."
|
||||||
(provided? sym)))
|
(provided? sym)))
|
||||||
|
|
||||||
(begin-deprecated
|
(begin-deprecated
|
||||||
(primitive-load-path "ice-9/deprecated.scm"))
|
(primitive-load-path "ice-9/deprecated"))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -131,16 +131,16 @@ Indicates that the debugger should display an introductory message.
|
||||||
|
|
||||||
(define (debug-on-error syms)
|
(define (debug-on-error syms)
|
||||||
"Enable or disable debug on error."
|
"Enable or disable debug on error."
|
||||||
(set! lazy-handler-dispatch
|
(set! pre-unwind-handler-dispatch
|
||||||
(if syms
|
(if syms
|
||||||
(lambda (key . args)
|
(lambda (key . args)
|
||||||
(if (memq key syms)
|
(if (memq key syms)
|
||||||
(begin
|
(begin
|
||||||
(debug-stack (make-stack #t lazy-handler-dispatch)
|
(debug-stack (make-stack #t pre-unwind-handler-dispatch)
|
||||||
#:with-introduction
|
#:with-introduction
|
||||||
#:continuable)
|
#:continuable)
|
||||||
(throw 'abort key)))
|
(throw 'abort key)))
|
||||||
(apply default-lazy-handler key args))
|
(apply default-pre-unwind-handler key args))
|
||||||
default-lazy-handler)))
|
default-pre-unwind-handler)))
|
||||||
|
|
||||||
;;; (ice-9 debugger) ends here.
|
;;; (ice-9 debugger) ends here.
|
415
module/ice-9/debugging/breakpoints.scm
Normal file
415
module/ice-9/debugging/breakpoints.scm
Normal file
|
@ -0,0 +1,415 @@
|
||||||
|
;;;; (ice-9 debugging breakpoints) -- practical breakpoints
|
||||||
|
|
||||||
|
;;; Copyright (C) 2005 Neil Jerram
|
||||||
|
;;;
|
||||||
|
;; 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
|
||||||
|
;;; This module provides a practical interface for setting and
|
||||||
|
;;; manipulating breakpoints.
|
||||||
|
|
||||||
|
(define-module (ice-9 debugging breakpoints)
|
||||||
|
#:use-module (ice-9 debugger)
|
||||||
|
#:use-module (ice-9 ls)
|
||||||
|
#:use-module (ice-9 optargs)
|
||||||
|
#:use-module (ice-9 regex)
|
||||||
|
#:use-module (oop goops)
|
||||||
|
#:use-module (ice-9 debugging ice-9-debugger-extensions)
|
||||||
|
#:use-module (ice-9 debugging traps)
|
||||||
|
#:use-module (ice-9 debugging trc)
|
||||||
|
#:use-module (srfi srfi-1)
|
||||||
|
#:use-module (srfi srfi-13)
|
||||||
|
#:export (break-in
|
||||||
|
break-at
|
||||||
|
default-breakpoint-behaviour
|
||||||
|
delete-breakpoint
|
||||||
|
for-each-breakpoint
|
||||||
|
setup-before-load
|
||||||
|
setup-after-load
|
||||||
|
setup-after-read
|
||||||
|
setup-after-eval))
|
||||||
|
|
||||||
|
;; If the running Guile does not provide before- and after- load hooks
|
||||||
|
;; itself, install them using the (ice-9 debugging load-hooks) module.
|
||||||
|
(or (defined? 'after-load-hook)
|
||||||
|
(begin
|
||||||
|
(use-modules (ice-9 debugging load-hooks))
|
||||||
|
(install-load-hooks)))
|
||||||
|
|
||||||
|
;; Getter/setter for default breakpoint behaviour.
|
||||||
|
(define default-breakpoint-behaviour
|
||||||
|
(let ((behaviour debug-trap))
|
||||||
|
(make-procedure-with-setter
|
||||||
|
;; Getter: return current default behaviour.
|
||||||
|
(lambda ()
|
||||||
|
behaviour)
|
||||||
|
;; Setter: set default behaviour to given procedure.
|
||||||
|
(lambda (new-behaviour)
|
||||||
|
(set! behaviour new-behaviour)))))
|
||||||
|
|
||||||
|
;; Base class for breakpoints. (We don't need to use GOOPS to
|
||||||
|
;; represent breakpoints, but it's a nice way to describe a composite
|
||||||
|
;; object.)
|
||||||
|
(define-class <breakpoint> ()
|
||||||
|
;; This breakpoint's trap options, which include its behaviour.
|
||||||
|
(trap-options #:init-keyword #:trap-options)
|
||||||
|
;; All the traps relating to this breakpoint.
|
||||||
|
(traps #:init-value '())
|
||||||
|
;; Observer. This is a procedure that is called when the breakpoint
|
||||||
|
;; trap list changes.
|
||||||
|
(observer #:init-value #f))
|
||||||
|
|
||||||
|
;; Noop base class definitions of all the possible setup methods.
|
||||||
|
(define-method (setup-before-load (bp <breakpoint>) filename)
|
||||||
|
*unspecified*)
|
||||||
|
(define-method (setup-after-load (bp <breakpoint>) filename)
|
||||||
|
*unspecified*)
|
||||||
|
(define-method (setup-after-read (bp <breakpoint>) x)
|
||||||
|
*unspecified*)
|
||||||
|
(define-method (setup-after-eval (bp <breakpoint>) filename)
|
||||||
|
*unspecified*)
|
||||||
|
|
||||||
|
;; Call the breakpoint's observer, if it has one.
|
||||||
|
(define-method (call-observer (bp <breakpoint>))
|
||||||
|
(cond ((slot-ref bp 'observer)
|
||||||
|
=>
|
||||||
|
(lambda (proc)
|
||||||
|
(proc)))))
|
||||||
|
|
||||||
|
;; Delete a breakpoint.
|
||||||
|
(define (delete-breakpoint bp)
|
||||||
|
;; Remove this breakpoint from the global list.
|
||||||
|
(set! breakpoints (delq! bp breakpoints))
|
||||||
|
;; Uninstall and discard all its traps.
|
||||||
|
(for-each uninstall-trap (slot-ref bp 'traps))
|
||||||
|
(slot-set! bp 'traps '()))
|
||||||
|
|
||||||
|
;; Class for `break-in' breakpoints.
|
||||||
|
(define-class <break-in> (<breakpoint>)
|
||||||
|
;; The name of the procedure to break in.
|
||||||
|
(procedure-name #:init-keyword #:procedure-name)
|
||||||
|
;; The name of the module or file that the procedure is defined in.
|
||||||
|
;; A module name is a list of symbols that exactly names the
|
||||||
|
;; relevant module. A file name is a string, which can in fact be
|
||||||
|
;; any substring of the relevant full file name.
|
||||||
|
(module-or-file-name #:init-keyword #:module-or-file-name))
|
||||||
|
|
||||||
|
;; Class for `break-at' breakpoints.
|
||||||
|
(define-class <break-at> (<breakpoint>)
|
||||||
|
;; The name of the file to break in. This is a string, which can in
|
||||||
|
;; fact be any substring of the relevant full file name.
|
||||||
|
(file-name #:init-keyword #:file-name)
|
||||||
|
;; Line and column number to break at.
|
||||||
|
(line #:init-keyword #:line)
|
||||||
|
(column #:init-keyword #:column))
|
||||||
|
|
||||||
|
;; Global list of non-deleted breakpoints.
|
||||||
|
(define breakpoints '())
|
||||||
|
|
||||||
|
;; Add to the above list.
|
||||||
|
(define-method (add-to-global-breakpoint-list (bp <breakpoint>))
|
||||||
|
(set! breakpoints (append! breakpoints (list bp))))
|
||||||
|
|
||||||
|
;; break-in: create a `break-in' breakpoint.
|
||||||
|
(define (break-in procedure-name . options)
|
||||||
|
;; Sort out the optional args.
|
||||||
|
(let* ((module-or-file-name+options
|
||||||
|
(cond ((and (not (null? options))
|
||||||
|
(or (string? (car options))
|
||||||
|
(list? (car options))))
|
||||||
|
options)
|
||||||
|
(else
|
||||||
|
(cons (module-name (current-module)) options))))
|
||||||
|
(module-or-file-name (car module-or-file-name+options))
|
||||||
|
(trap-options (cdr module-or-file-name+options))
|
||||||
|
;; Create the new breakpoint object.
|
||||||
|
(bp (make <break-in>
|
||||||
|
#:procedure-name procedure-name
|
||||||
|
#:module-or-file-name module-or-file-name
|
||||||
|
#:trap-options (if (memq #:behaviour trap-options)
|
||||||
|
trap-options
|
||||||
|
(cons* #:behaviour
|
||||||
|
(default-breakpoint-behaviour)
|
||||||
|
trap-options)))))
|
||||||
|
;; Add it to the global breakpoint list.
|
||||||
|
(add-to-global-breakpoint-list bp)
|
||||||
|
;; Set the new breakpoint, if possible, in already loaded code.
|
||||||
|
(set-in-existing-code bp)
|
||||||
|
;; Return the breakpoint object to our caller.
|
||||||
|
bp))
|
||||||
|
|
||||||
|
;; break-at: create a `break-at' breakpoint.
|
||||||
|
(define (break-at file-name line column . trap-options)
|
||||||
|
;; Create the new breakpoint object.
|
||||||
|
(let* ((bp (make <break-at>
|
||||||
|
#:file-name file-name
|
||||||
|
#:line line
|
||||||
|
#:column column
|
||||||
|
#:trap-options (if (memq #:behaviour trap-options)
|
||||||
|
trap-options
|
||||||
|
(cons* #:behaviour
|
||||||
|
(default-breakpoint-behaviour)
|
||||||
|
trap-options)))))
|
||||||
|
;; Add it to the global breakpoint list.
|
||||||
|
(add-to-global-breakpoint-list bp)
|
||||||
|
;; Set the new breakpoint, if possible, in already loaded code.
|
||||||
|
(set-in-existing-code bp)
|
||||||
|
;; Return the breakpoint object to our caller.
|
||||||
|
bp))
|
||||||
|
|
||||||
|
;; Set a `break-in' breakpoint in already loaded code, if possible.
|
||||||
|
(define-method (set-in-existing-code (bp <break-in>))
|
||||||
|
;; Get the module or file name that was specified for this
|
||||||
|
;; breakpoint.
|
||||||
|
(let ((module-or-file-name (slot-ref bp 'module-or-file-name)))
|
||||||
|
;; Handling is simpler for a module name.
|
||||||
|
(cond ((list? module-or-file-name)
|
||||||
|
;; See if the named module exists yet.
|
||||||
|
(let ((m (module-if-already-loaded module-or-file-name)))
|
||||||
|
(maybe-break-in-module-proc m bp)))
|
||||||
|
((string? module-or-file-name)
|
||||||
|
;; Try all loaded modules.
|
||||||
|
(or-map (lambda (m)
|
||||||
|
(maybe-break-in-module-proc m bp))
|
||||||
|
(all-loaded-modules)))
|
||||||
|
(else
|
||||||
|
(error "Bad module-or-file-name:" module-or-file-name)))))
|
||||||
|
|
||||||
|
(define (make-observer bp trap)
|
||||||
|
(lambda (event)
|
||||||
|
(trap-target-gone bp trap)))
|
||||||
|
|
||||||
|
;; Set a `break-at' breakpoint in already loaded code, if possible.
|
||||||
|
(define-method (set-in-existing-code (bp <break-at>) . code)
|
||||||
|
;; Procedure to install a source trap on each expression that we
|
||||||
|
;; find matching this breakpoint.
|
||||||
|
(define (install-source-trap x)
|
||||||
|
(or (or-map (lambda (trap)
|
||||||
|
(and (is-a? trap <source-trap>)
|
||||||
|
(eq? (slot-ref trap 'expression) x)))
|
||||||
|
(slot-ref bp 'traps))
|
||||||
|
(let ((trap (apply make <source-trap>
|
||||||
|
#:expression x
|
||||||
|
(slot-ref bp 'trap-options))))
|
||||||
|
(slot-set! trap 'observer (make-observer bp trap))
|
||||||
|
(install-trap trap)
|
||||||
|
(trc 'install-source-trap (object-address trap) (object-address x))
|
||||||
|
(trap-installed bp trap #t))))
|
||||||
|
;; Scan the source whash, and install a trap on all code matching
|
||||||
|
;; this breakpoint.
|
||||||
|
(trc 'set-in-existing-code (length code))
|
||||||
|
(if (null? code)
|
||||||
|
(scan-source-whash (slot-ref bp 'file-name)
|
||||||
|
(slot-ref bp 'line)
|
||||||
|
(slot-ref bp 'column)
|
||||||
|
install-source-trap)
|
||||||
|
(scan-code (car code)
|
||||||
|
(slot-ref bp 'file-name)
|
||||||
|
(slot-ref bp 'line)
|
||||||
|
(slot-ref bp 'column)
|
||||||
|
install-source-trap)))
|
||||||
|
|
||||||
|
;; Temporary implementation of scan-source-whash - this _really_ needs
|
||||||
|
;; to be implemented in C.
|
||||||
|
(define (scan-source-whash file-name line column proc)
|
||||||
|
;; Procedure to call for each source expression in the whash.
|
||||||
|
(define (folder x props acc)
|
||||||
|
(if (and (= line (source-property x 'line))
|
||||||
|
(= column (source-property x 'column))
|
||||||
|
(let ((fn (source-property x 'filename)))
|
||||||
|
(trc 'scan-source-whash fn)
|
||||||
|
(and (string? fn)
|
||||||
|
(string-contains fn file-name))))
|
||||||
|
(proc x)))
|
||||||
|
;; Tracing.
|
||||||
|
(trc 'scan-source-whash file-name line column)
|
||||||
|
;; Apply this procedure to the whash.
|
||||||
|
(hash-fold folder 0 source-whash))
|
||||||
|
|
||||||
|
(define (scan-code x file-name line column proc)
|
||||||
|
(trc 'scan-code file-name line column)
|
||||||
|
(if (pair? x)
|
||||||
|
(begin
|
||||||
|
(if (and (eq? line (source-property x 'line))
|
||||||
|
(eq? column (source-property x 'column))
|
||||||
|
(let ((fn (source-property x 'filename)))
|
||||||
|
(trc 'scan-code fn)
|
||||||
|
(and (string? fn)
|
||||||
|
(string-contains fn file-name))))
|
||||||
|
(proc x))
|
||||||
|
(scan-code (car x) file-name line column proc)
|
||||||
|
(scan-code (cdr x) file-name line column proc))))
|
||||||
|
|
||||||
|
;; If a module named MODULE-NAME has been loaded, return its module
|
||||||
|
;; object; otherwise return #f.
|
||||||
|
(define (module-if-already-loaded module-name)
|
||||||
|
(nested-ref the-root-module (append '(app modules) module-name)))
|
||||||
|
|
||||||
|
;; Construct and return a list of all loaded modules.
|
||||||
|
(define (all-loaded-modules)
|
||||||
|
;; This is the list that accumulates known modules. It has to be
|
||||||
|
;; defined outside the following functions, and accumulated using
|
||||||
|
;; set!, so as to avoid infinite loops - because of the fact that
|
||||||
|
;; all non-pure modules have a variable `app'.
|
||||||
|
(define known-modules '())
|
||||||
|
;; Return an alist of submodules of the given PARENT-MODULE-NAME.
|
||||||
|
;; Each element of the alist is (NAME . MODULE), where NAME is the
|
||||||
|
;; module's leaf name (i.e. relative to PARENT-MODULE-NAME) and
|
||||||
|
;; MODULE is the module object. By a "submodule of a parent
|
||||||
|
;; module", we mean any module value that is bound to a symbol in
|
||||||
|
;; the parent module, and which is not an interface module.
|
||||||
|
(define (direct-submodules parent-module-name)
|
||||||
|
(filter (lambda (name+value)
|
||||||
|
(and (module? (cdr name+value))
|
||||||
|
(not (eq? (module-kind (cdr name+value)) 'interface))))
|
||||||
|
(map (lambda (name)
|
||||||
|
(cons name (local-ref (append parent-module-name
|
||||||
|
(list name)))))
|
||||||
|
(cdar (lls parent-module-name)))))
|
||||||
|
;; Add all submodules (direct and indirect) of the module named
|
||||||
|
;; PARENT-MODULE-NAME to `known-modules', if not already there.
|
||||||
|
(define (add-submodules-of parent-module-name)
|
||||||
|
(let ((ds (direct-submodules parent-module-name)))
|
||||||
|
(for-each
|
||||||
|
(lambda (name+module)
|
||||||
|
(or (memq (cdr name+module) known-modules)
|
||||||
|
(begin
|
||||||
|
(set! known-modules (cons (cdr name+module) known-modules))
|
||||||
|
(add-submodules-of (append parent-module-name
|
||||||
|
(list (car name+module)))))))
|
||||||
|
ds)))
|
||||||
|
;; Add submodules recursively, starting from the root of all
|
||||||
|
;; modules.
|
||||||
|
(add-submodules-of '(app modules))
|
||||||
|
;; Return the result.
|
||||||
|
known-modules)
|
||||||
|
|
||||||
|
;; Before-load setup for `break-at' breakpoints.
|
||||||
|
(define-method (setup-before-load (bp <break-at>) filename)
|
||||||
|
(let ((trap (apply make <location-trap>
|
||||||
|
#:file-regexp (regexp-quote (slot-ref bp 'file-name))
|
||||||
|
#:line (slot-ref bp 'line)
|
||||||
|
#:column (slot-ref bp 'column)
|
||||||
|
(slot-ref bp 'trap-options))))
|
||||||
|
(install-trap trap)
|
||||||
|
(trap-installed bp trap #f)
|
||||||
|
(letrec ((uninstaller
|
||||||
|
(lambda (file-name)
|
||||||
|
(uninstall-trap trap)
|
||||||
|
(remove-hook! after-load-hook uninstaller))))
|
||||||
|
(add-hook! after-load-hook uninstaller))))
|
||||||
|
|
||||||
|
;; After-load setup for `break-in' breakpoints.
|
||||||
|
(define-method (setup-after-load (bp <break-in>) filename)
|
||||||
|
;; Get the module that the loaded file created or was loaded into,
|
||||||
|
;; and the module or file name that were specified for this
|
||||||
|
;; breakpoint.
|
||||||
|
(let ((m (current-module))
|
||||||
|
(module-or-file-name (slot-ref bp 'module-or-file-name)))
|
||||||
|
;; Decide whether the breakpoint spec matches this load.
|
||||||
|
(if (or (and (string? module-or-file-name)
|
||||||
|
(string-contains filename module-or-file-name))
|
||||||
|
(and (list? module-or-file-name)
|
||||||
|
(equal? (module-name (current-module)) module-or-file-name)))
|
||||||
|
;; It does, so try to install the breakpoint.
|
||||||
|
(maybe-break-in-module-proc m bp))))
|
||||||
|
|
||||||
|
;; After-load setup for `break-at' breakpoints.
|
||||||
|
(define-method (setup-after-load (bp <break-at>) filename)
|
||||||
|
(if (string-contains filename (slot-ref bp 'file-name))
|
||||||
|
(set-in-existing-code bp)))
|
||||||
|
|
||||||
|
(define (maybe-break-in-module-proc m bp)
|
||||||
|
"If module M defines a procedure matching the specification of
|
||||||
|
breakpoint BP, install a trap on it."
|
||||||
|
(let ((proc (module-ref m (slot-ref bp 'procedure-name) #f)))
|
||||||
|
(if (and proc
|
||||||
|
(procedure? proc)
|
||||||
|
(let ((module-or-file-name (slot-ref bp 'module-or-file-name)))
|
||||||
|
(if (string? module-or-file-name)
|
||||||
|
(source-file-matches (procedure-source proc)
|
||||||
|
module-or-file-name)
|
||||||
|
#t))
|
||||||
|
(not (or-map (lambda (trap)
|
||||||
|
(and (is-a? trap <procedure-trap>)
|
||||||
|
(eq? (slot-ref trap 'procedure) proc)))
|
||||||
|
(slot-ref bp 'traps))))
|
||||||
|
;; There is, so install a <procedure-trap> on it.
|
||||||
|
(letrec ((trap (apply make <procedure-trap>
|
||||||
|
#:procedure proc
|
||||||
|
(slot-ref bp 'trap-options))))
|
||||||
|
(slot-set! trap 'observer (make-observer bp trap))
|
||||||
|
(install-trap trap)
|
||||||
|
(trap-installed bp trap #t)
|
||||||
|
;; Tell caller that we installed a trap.
|
||||||
|
#t)
|
||||||
|
;; Tell caller that we did not install a trap.
|
||||||
|
#f)))
|
||||||
|
|
||||||
|
;; After-read setup for `break-at' breakpoints.
|
||||||
|
(define-method (setup-after-read (bp <break-at>) x)
|
||||||
|
(set-in-existing-code bp x))
|
||||||
|
|
||||||
|
;; Common code for associating a newly created and installed trap with
|
||||||
|
;; a breakpoint object.
|
||||||
|
(define (trap-installed bp trap record?)
|
||||||
|
(if record?
|
||||||
|
;; Remember this trap in the breakpoint object.
|
||||||
|
(slot-set! bp 'traps (append! (slot-ref bp 'traps) (list trap))))
|
||||||
|
;; Update the breakpoint status.
|
||||||
|
(call-observer bp))
|
||||||
|
|
||||||
|
;; Common code for handling when the target of one of a breakpoint's
|
||||||
|
;; traps is being GC'd.
|
||||||
|
(define (trap-target-gone bp trap)
|
||||||
|
(trc 'trap-target-gone (object-address trap))
|
||||||
|
;; Remove this trap from the breakpoint's list.
|
||||||
|
(slot-set! bp 'traps (delq! trap (slot-ref bp 'traps)))
|
||||||
|
;; Update the breakpoint status.
|
||||||
|
(call-observer bp))
|
||||||
|
|
||||||
|
(define (source-file-matches source file-name)
|
||||||
|
"Return #t if any of the expressions in SOURCE have a 'filename
|
||||||
|
source property that includes FILE-NAME; otherwise return #f."
|
||||||
|
(and (pair? source)
|
||||||
|
(or (let ((source-file-name (source-property source 'filename)))
|
||||||
|
(and source-file-name
|
||||||
|
(string? source-file-name)
|
||||||
|
(string-contains source-file-name file-name)))
|
||||||
|
(let loop ((source source))
|
||||||
|
(and (pair? source)
|
||||||
|
(or (source-file-matches (car source) file-name)
|
||||||
|
(loop (cdr source))))))))
|
||||||
|
|
||||||
|
;; Install load hook functions.
|
||||||
|
(add-hook! before-load-hook
|
||||||
|
(lambda (fn)
|
||||||
|
(for-each-breakpoint setup-before-load fn)))
|
||||||
|
|
||||||
|
(add-hook! after-load-hook
|
||||||
|
(lambda (fn)
|
||||||
|
(for-each-breakpoint setup-after-load fn)))
|
||||||
|
|
||||||
|
;;; Apply generic function GF to each breakpoint, passing the
|
||||||
|
;;; breakpoint object and ARGS as args on each call.
|
||||||
|
(define (for-each-breakpoint gf . args)
|
||||||
|
(for-each (lambda (bp)
|
||||||
|
(apply gf bp args))
|
||||||
|
breakpoints))
|
||||||
|
|
||||||
|
;; Make sure that recording of source positions is enabled. Without
|
||||||
|
;; this break-at breakpoints will obviously not work.
|
||||||
|
(read-enable 'positions)
|
||||||
|
|
||||||
|
;;; (ice-9 debugging breakpoints) ends here.
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue