program.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. ;;; Guile VM program functions
  2. ;;; Copyright (C) 2001, 2009, 2010, 2013, 2014 Free Software Foundation, Inc.
  3. ;;;
  4. ;;; This library is free software; you can redistribute it and/or
  5. ;;; modify it under the terms of the GNU Lesser General Public
  6. ;;; License as published by the Free Software Foundation; either
  7. ;;; version 3 of the License, or (at your option) any later version.
  8. ;;;
  9. ;;; This library is distributed in the hope that it will be useful,
  10. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;;; Lesser General Public License for more details.
  13. ;;;
  14. ;;; You should have received a copy of the GNU Lesser General Public
  15. ;;; License along with this library; if not, write to the Free Software
  16. ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. ;;; Code:
  18. (define-module (system vm program)
  19. #:use-module (ice-9 match)
  20. #:use-module (system vm debug)
  21. #:use-module (rnrs bytevectors)
  22. #:use-module (srfi srfi-1)
  23. #:use-module (srfi srfi-26)
  24. #:export (source:addr source:line source:column source:file
  25. source:line-for-user
  26. program-sources program-sources-pre-retire program-source
  27. program-address-range
  28. program-arities program-arity arity:start arity:end
  29. arity:nreq arity:nopt arity:rest? arity:kw arity:allow-other-keys?
  30. program-arguments-alist program-arguments-alists
  31. program-lambda-list
  32. program? program-code
  33. program-free-variables
  34. program-num-free-variables
  35. program-free-variable-ref program-free-variable-set!
  36. print-program
  37. primitive?))
  38. (load-extension (string-append "libguile-" (effective-version))
  39. "scm_init_programs")
  40. ;; These procedures are called by programs.c.
  41. (define (program-name program)
  42. (and=> (find-program-debug-info (program-code program))
  43. program-debug-info-name))
  44. (define (program-documentation program)
  45. (find-program-docstring (program-code program)))
  46. (define (program-minimum-arity program)
  47. (find-program-minimum-arity (program-code program)))
  48. (define (program-properties program)
  49. (find-program-properties (program-code program)))
  50. (define (source:addr source)
  51. (car source))
  52. (define (source:file source)
  53. (cadr source))
  54. (define (source:line source)
  55. (caddr source))
  56. (define (source:column source)
  57. (cdddr source))
  58. ;; Lines are zero-indexed inside Guile, but users expect them to be
  59. ;; one-indexed. Columns, on the other hand, are zero-indexed to both. Go
  60. ;; figure.
  61. (define (source:line-for-user source)
  62. (1+ (source:line source)))
  63. (define (source-for-addr addr)
  64. (and=> (find-source-for-addr addr)
  65. (lambda (source)
  66. ;; FIXME: absolute or relative address?
  67. (cons* 0
  68. (source-file source)
  69. (source-line source)
  70. (source-column source)))))
  71. (define (program-sources proc)
  72. (map (lambda (source)
  73. (cons* (- (source-post-pc source) (program-code proc))
  74. (source-file source)
  75. (source-line source)
  76. (source-column source)))
  77. (find-program-sources (program-code proc))))
  78. (define* (program-source proc ip #:optional (sources (program-sources proc)))
  79. (let lp ((source #f) (sources sources))
  80. (match sources
  81. (() source)
  82. (((and s (pc . _)) . sources)
  83. (if (<= pc ip)
  84. (lp s sources)
  85. source)))))
  86. (define (program-address-range program)
  87. "Return the start and end addresses of @var{program}'s code, as a pair
  88. of integers."
  89. (let ((pdi (find-program-debug-info (program-code program))))
  90. (and pdi
  91. (cons (program-debug-info-addr pdi)
  92. (+ (program-debug-info-addr pdi)
  93. (program-debug-info-size pdi))))))
  94. ;; Source information could in theory be correlated with the ip of the
  95. ;; instruction, or the ip just after the instruction is retired. Guile
  96. ;; does the latter, to make backtraces easy -- an error produced while
  97. ;; running an opcode always happens after it has retired its arguments.
  98. ;;
  99. ;; But for breakpoints and such, we need the ip before the instruction
  100. ;; is retired -- before it has had a chance to do anything. So here we
  101. ;; change from the post-retire addresses given by program-sources to
  102. ;; pre-retire addresses.
  103. ;;
  104. (define (program-sources-pre-retire proc)
  105. (map (lambda (source)
  106. (cons* (- (source-pre-pc source) (program-code proc))
  107. (source-file source)
  108. (source-line source)
  109. (source-column source)))
  110. (find-program-sources (program-code proc))))
  111. (define (arity:start a)
  112. (match a ((start end . _) start) (_ (error "bad arity" a))))
  113. (define (arity:end a)
  114. (match a ((start end . _) end) (_ (error "bad arity" a))))
  115. (define (arity:nreq a)
  116. (match a ((_ _ nreq . _) nreq) (_ 0)))
  117. (define (arity:nopt a)
  118. (match a ((_ _ nreq nopt . _) nopt) (_ 0)))
  119. (define (arity:rest? a)
  120. (match a ((_ _ nreq nopt rest? . _) rest?) (_ #f)))
  121. (define (arity:kw a)
  122. (match a ((_ _ nreq nopt rest? (_ . kw)) kw) (_ '())))
  123. (define (arity:allow-other-keys? a)
  124. (match a ((_ _ nreq nopt rest? (aok . kw)) aok) (_ #f)))
  125. (define (program-arity prog ip)
  126. (let ((arities (program-arities prog)))
  127. (and arities
  128. (let lp ((arities arities))
  129. (cond ((null? arities) #f)
  130. ((not ip) (car arities)) ; take the first one
  131. ((and (< (arity:start (car arities)) ip)
  132. (<= ip (arity:end (car arities))))
  133. (car arities))
  134. (else (lp (cdr arities))))))))
  135. (define (arglist->arguments-alist arglist)
  136. (match arglist
  137. ((req opt keyword allow-other-keys? rest . extents)
  138. `((required . ,req)
  139. (optional . ,opt)
  140. (keyword . ,keyword)
  141. (allow-other-keys? . ,allow-other-keys?)
  142. (rest . ,rest)
  143. (extents . ,extents)))
  144. (_ #f)))
  145. (define* (arity->arguments-alist prog arity
  146. #:optional
  147. (make-placeholder
  148. (lambda (i) (string->symbol "_"))))
  149. (let lp ((nreq (arity:nreq arity)) (req '())
  150. (nopt (arity:nopt arity)) (opt '())
  151. (rest? (arity:rest? arity)) (rest #f)
  152. (n 0))
  153. (cond
  154. ((< 0 nreq)
  155. (lp (1- nreq) (cons (make-placeholder n) req)
  156. nopt opt rest? rest (1+ n)))
  157. ((< 0 nopt)
  158. (lp nreq req
  159. (1- nopt) (cons (make-placeholder n) opt)
  160. rest? rest (1+ n)))
  161. (rest?
  162. (lp nreq req nopt opt
  163. #f (make-placeholder (+ n (length (arity:kw arity))))
  164. (1+ n)))
  165. (else
  166. `((required . ,(reverse req))
  167. (optional . ,(reverse opt))
  168. (keyword . ,(arity:kw arity))
  169. (allow-other-keys? . ,(arity:allow-other-keys? arity))
  170. (rest . ,rest))))))
  171. ;; the name "program-arguments" is taken by features.c...
  172. (define* (program-arguments-alist prog #:optional ip)
  173. "Returns the signature of the given procedure in the form of an association list."
  174. (cond
  175. ((primitive? prog)
  176. (match (procedure-minimum-arity prog)
  177. (#f #f)
  178. ((nreq nopt rest?)
  179. (let ((start (primitive-call-ip prog)))
  180. ;; Assume that there is only one IP for the call.
  181. (and (or (not ip) (= start ip))
  182. (arity->arguments-alist
  183. prog
  184. (list 0 0 nreq nopt rest? '(#f . ()))))))))
  185. ((program? prog)
  186. (or-map (lambda (arity)
  187. (and (or (not ip)
  188. (and (<= (arity-low-pc arity) ip)
  189. (< ip (arity-high-pc arity))))
  190. (arity-arguments-alist arity)))
  191. (or (find-program-arities (program-code prog)) '())))
  192. (else
  193. (let ((arity (program-arity prog ip)))
  194. (and arity
  195. (arity->arguments-alist prog arity))))))
  196. (define* (program-lambda-list prog #:optional ip)
  197. "Returns the signature of the given procedure in the form of an argument list."
  198. (and=> (program-arguments-alist prog ip) arguments-alist->lambda-list))
  199. (define (arguments-alist->lambda-list arguments-alist)
  200. (let ((req (or (assq-ref arguments-alist 'required) '()))
  201. (opt (or (assq-ref arguments-alist 'optional) '()))
  202. (key (map keyword->symbol
  203. (map car (or (assq-ref arguments-alist 'keyword) '()))))
  204. (rest (or (assq-ref arguments-alist 'rest) '())))
  205. `(,@req
  206. ,@(if (pair? opt) (cons #:optional opt) '())
  207. ,@(if (pair? key) (cons #:key key) '())
  208. . ,rest)))
  209. (define (program-free-variables prog)
  210. "Return the list of free variables of PROG."
  211. (let ((count (program-num-free-variables prog)))
  212. (unfold (lambda (i) (>= i count))
  213. (cut program-free-variable-ref prog <>)
  214. 1+
  215. 0)))
  216. (define (program-arguments-alists prog)
  217. "Returns all arities of the given procedure, as a list of association
  218. lists."
  219. (define (fallback)
  220. (match (procedure-minimum-arity prog)
  221. (#f '())
  222. ((nreq nopt rest?)
  223. (list
  224. (arity->arguments-alist
  225. prog
  226. (list 0 0 nreq nopt rest? '(#f . ())))))))
  227. (cond
  228. ((primitive? prog) (fallback))
  229. ((program? prog)
  230. (let ((arities (find-program-arities (program-code prog))))
  231. (if arities
  232. (map arity-arguments-alist arities)
  233. (fallback))))
  234. (else (error "expected a program" prog))))
  235. (define* (print-program #:optional program (port (current-output-port))
  236. #:key (addr (program-code program))
  237. (always-print-addr? #f) (never-print-addr? #f)
  238. (always-print-source? #f) (never-print-source? #f)
  239. (name-only? #f) (print-formals? #t))
  240. (let* ((pdi (find-program-debug-info addr))
  241. ;; It could be the procedure had its name property set via the
  242. ;; procedure property interface.
  243. (name (or (and program (procedure-name program))
  244. (program-debug-info-name pdi)))
  245. (source (match (find-program-sources addr)
  246. (() #f)
  247. ((source . _) source)))
  248. (formals (if program
  249. (program-arguments-alists program)
  250. (let ((arities (find-program-arities addr)))
  251. (if arities
  252. (map arity-arguments-alist arities)
  253. '())))))
  254. (define (hex n)
  255. (number->string n 16))
  256. (cond
  257. ((and name-only? name)
  258. (format port "~a" name))
  259. (else
  260. (format port "#<procedure")
  261. (format port " ~a"
  262. (or name
  263. (and program (hex (object-address program)))
  264. (if never-print-addr?
  265. ""
  266. (string-append "@" (hex addr)))))
  267. (when (and always-print-addr? (not never-print-addr?))
  268. (unless (and (not name) (not program))
  269. (format port " @~a" (hex addr))))
  270. (when (and source (not never-print-source?)
  271. (or always-print-source? (not name)))
  272. (format port " at ~a:~a:~a"
  273. (or (source-file source) "<unknown port>")
  274. (source-line-for-user source)
  275. (source-column source)))
  276. (unless (or (null? formals) (not print-formals?))
  277. (format port "~a"
  278. (string-append
  279. " " (string-join (map (lambda (a)
  280. (object->string
  281. (arguments-alist->lambda-list a)))
  282. formals)
  283. " | "))))
  284. (format port ">")))))
  285. (define (write-program prog port)
  286. (print-program prog port))