program.scm 11 KB

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