program.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. ;;; Guile VM program functions
  2. ;;; Copyright (C) 2001, 2009, 2010, 2013 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 (system base pmatch)
  20. #:use-module (system vm instruction)
  21. #:use-module (system vm objcode)
  22. #:use-module (rnrs bytevectors)
  23. #:use-module (srfi srfi-1)
  24. #:use-module (srfi srfi-26)
  25. #:export (make-program
  26. make-binding binding:name binding:boxed? binding:index
  27. binding:start binding:end
  28. source:addr source:line source:column source:file
  29. source:line-for-user
  30. program-sources program-sources-pre-retire program-source
  31. program-bindings program-bindings-by-index program-bindings-for-ip
  32. program-arities program-arity arity:start arity:end
  33. arity:nreq arity:nopt arity:rest? arity:kw arity:allow-other-keys?
  34. program-arguments-alist program-lambda-list
  35. program-meta
  36. program-objcode program? program-objects
  37. program-module program-base
  38. program-free-variables
  39. program-num-free-variables
  40. program-free-variable-ref program-free-variable-set!))
  41. (load-extension (string-append "libguile-" (effective-version))
  42. "scm_init_programs")
  43. (define (make-binding name boxed? index start end)
  44. (list name boxed? index start end))
  45. (define (binding:name b) (list-ref b 0))
  46. (define (binding:boxed? b) (list-ref b 1))
  47. (define (binding:index b) (list-ref b 2))
  48. (define (binding:start b) (list-ref b 3))
  49. (define (binding:end b) (list-ref b 4))
  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. ;; FIXME: pull this definition from elsewhere.
  64. (define *bytecode-header-len* 8)
  65. ;; We could decompile the program to get this, but that seems like a
  66. ;; waste.
  67. (define (bytecode-instruction-length bytecode ip)
  68. (let* ((idx (+ ip *bytecode-header-len*))
  69. (inst (opcode->instruction (bytevector-u8-ref bytecode idx))))
  70. ;; 1+ for the instruction itself.
  71. (1+ (cond
  72. ((eq? inst 'load-program)
  73. (+ (bytevector-u32-native-ref bytecode (+ idx 1))
  74. (bytevector-u32-native-ref bytecode (+ idx 5))))
  75. ((< (instruction-length inst) 0)
  76. ;; variable length instruction -- the length is encoded in the
  77. ;; instruction stream.
  78. (+ (ash (bytevector-u8-ref bytecode (+ idx 1)) 16)
  79. (ash (bytevector-u8-ref bytecode (+ idx 2)) 8)
  80. (bytevector-u8-ref bytecode (+ idx 3))))
  81. (else
  82. ;; fixed length
  83. (instruction-length inst))))))
  84. ;; Source information could in theory be correlated with the ip of the
  85. ;; instruction, or the ip just after the instruction is retired. Guile
  86. ;; does the latter, to make backtraces easy -- an error produced while
  87. ;; running an opcode always happens after it has retired its arguments.
  88. ;;
  89. ;; But for breakpoints and such, we need the ip before the instruction
  90. ;; is retired -- before it has had a chance to do anything. So here we
  91. ;; change from the post-retire addresses given by program-sources to
  92. ;; pre-retire addresses.
  93. ;;
  94. (define (program-sources-pre-retire proc)
  95. (let ((bv (objcode->bytecode (program-objcode proc))))
  96. (let lp ((in (program-sources proc))
  97. (out '())
  98. (ip 0))
  99. (cond
  100. ((null? in)
  101. (reverse out))
  102. (else
  103. (pmatch (car in)
  104. ((,post-ip . ,source)
  105. (let lp2 ((ip ip)
  106. (next ip))
  107. (if (< next post-ip)
  108. (lp2 next (+ next (bytecode-instruction-length bv next)))
  109. (lp (cdr in)
  110. (acons ip source out)
  111. next))))
  112. (else
  113. (error "unexpected"))))))))
  114. (define (collapse-locals locs)
  115. (let lp ((ret '()) (locs locs))
  116. (if (null? locs)
  117. (map cdr (sort! ret
  118. (lambda (x y) (< (car x) (car y)))))
  119. (let ((b (car locs)))
  120. (cond
  121. ((assv-ref ret (binding:index b))
  122. => (lambda (bindings)
  123. (append! bindings (list b))
  124. (lp ret (cdr locs))))
  125. (else
  126. (lp (acons (binding:index b) (list b) ret)
  127. (cdr locs))))))))
  128. ;; returns list of list of bindings
  129. ;; (list-ref ret N) == bindings bound to the Nth local slot
  130. (define (program-bindings-by-index prog)
  131. (cond ((program-bindings prog) => collapse-locals)
  132. (else '())))
  133. (define (program-bindings-for-ip prog ip)
  134. (let lp ((in (program-bindings-by-index prog)) (out '()))
  135. (if (null? in)
  136. (reverse out)
  137. (lp (cdr in)
  138. (let inner ((binds (car in)))
  139. (cond ((null? binds) out)
  140. ((<= (binding:start (car binds))
  141. ip
  142. (binding:end (car binds)))
  143. (cons (car binds) out))
  144. (else (inner (cdr binds)))))))))
  145. (define (arity:start a)
  146. (pmatch a ((,start ,end . _) start) (else (error "bad arity" a))))
  147. (define (arity:end a)
  148. (pmatch a ((,start ,end . _) end) (else (error "bad arity" a))))
  149. (define (arity:nreq a)
  150. (pmatch a ((_ _ ,nreq . _) nreq) (else 0)))
  151. (define (arity:nopt a)
  152. (pmatch a ((_ _ ,nreq ,nopt . _) nopt) (else 0)))
  153. (define (arity:rest? a)
  154. (pmatch a ((_ _ ,nreq ,nopt ,rest? . _) rest?) (else #f)))
  155. (define (arity:kw a)
  156. (pmatch a ((_ _ ,nreq ,nopt ,rest? (_ . ,kw)) kw) (else '())))
  157. (define (arity:allow-other-keys? a)
  158. (pmatch a ((_ _ ,nreq ,nopt ,rest? (,aok . ,kw)) aok) (else #f)))
  159. (define (program-arity prog ip)
  160. (let ((arities (program-arities prog)))
  161. (and arities
  162. (let lp ((arities arities))
  163. (cond ((null? arities) #f)
  164. ((not ip) (car arities)) ; take the first one
  165. ((and (< (arity:start (car arities)) ip)
  166. (<= ip (arity:end (car arities))))
  167. (car arities))
  168. (else (lp (cdr arities))))))))
  169. (define (arglist->arguments-alist arglist)
  170. (pmatch arglist
  171. ((,req ,opt ,keyword ,allow-other-keys? ,rest . ,extents)
  172. `((required . ,req)
  173. (optional . ,opt)
  174. (keyword . ,keyword)
  175. (allow-other-keys? . ,allow-other-keys?)
  176. (rest . ,rest)
  177. (extents . ,extents)))
  178. (else #f)))
  179. (define* (arity->arguments-alist prog arity
  180. #:optional
  181. (make-placeholder
  182. (lambda (i) (string->symbol "_"))))
  183. (define var-by-index
  184. (let ((rbinds (map (lambda (x)
  185. (cons (binding:index x) (binding:name x)))
  186. (program-bindings-for-ip prog
  187. (arity:start arity)))))
  188. (lambda (i)
  189. (or (assv-ref rbinds i)
  190. ;; if we don't know the name, return a placeholder
  191. (make-placeholder i)))))
  192. (let lp ((nreq (arity:nreq arity)) (req '())
  193. (nopt (arity:nopt arity)) (opt '())
  194. (rest? (arity:rest? arity)) (rest #f)
  195. (n 0))
  196. (cond
  197. ((< 0 nreq)
  198. (lp (1- nreq) (cons (var-by-index n) req)
  199. nopt opt rest? rest (1+ n)))
  200. ((< 0 nopt)
  201. (lp nreq req
  202. (1- nopt) (cons (var-by-index n) opt)
  203. rest? rest (1+ n)))
  204. (rest?
  205. (lp nreq req nopt opt
  206. #f (var-by-index (+ n (length (arity:kw arity))))
  207. (1+ n)))
  208. (else
  209. `((required . ,(reverse req))
  210. (optional . ,(reverse opt))
  211. (keyword . ,(arity:kw arity))
  212. (allow-other-keys? . ,(arity:allow-other-keys? arity))
  213. (rest . ,rest))))))
  214. ;; the name "program-arguments" is taken by features.c...
  215. (define* (program-arguments-alist prog #:optional ip)
  216. "Returns the signature of the given procedure in the form of an association list."
  217. (let ((arity (program-arity prog ip)))
  218. (and arity
  219. (arity->arguments-alist prog arity))))
  220. (define* (program-lambda-list prog #:optional ip)
  221. "Returns the signature of the given procedure in the form of an argument list."
  222. (and=> (program-arguments-alist prog ip) arguments-alist->lambda-list))
  223. (define (arguments-alist->lambda-list arguments-alist)
  224. (let ((req (or (assq-ref arguments-alist 'required) '()))
  225. (opt (or (assq-ref arguments-alist 'optional) '()))
  226. (key (map keyword->symbol
  227. (map car (or (assq-ref arguments-alist 'keyword) '()))))
  228. (rest (or (assq-ref arguments-alist 'rest) '())))
  229. `(,@req
  230. ,@(if (pair? opt) (cons #:optional opt) '())
  231. ,@(if (pair? key) (cons #:key key) '())
  232. . ,rest)))
  233. (define (program-free-variables prog)
  234. "Return the list of free variables of PROG."
  235. (let ((count (program-num-free-variables prog)))
  236. (unfold (lambda (i) (>= i count))
  237. (cut program-free-variable-ref prog <>)
  238. 1+
  239. 0)))
  240. (define (write-program prog port)
  241. (format port "#<procedure ~a~a>"
  242. (or (procedure-name prog)
  243. (and=> (program-source prog 0)
  244. (lambda (s)
  245. (format #f "~a at ~a:~a:~a"
  246. (number->string (object-address prog) 16)
  247. (or (source:file s)
  248. (if s "<current input>" "<unknown port>"))
  249. (source:line-for-user s) (source:column s))))
  250. (number->string (object-address prog) 16))
  251. (let ((arities (program-arities prog)))
  252. (if (or (not arities) (null? arities))
  253. ""
  254. (string-append
  255. " " (string-join (map (lambda (a)
  256. (object->string
  257. (arguments-alist->lambda-list
  258. (arity->arguments-alist prog a))))
  259. arities)
  260. " | "))))))