~ubuntu-branches/ubuntu/trusty/mit-scheme/trusty-proposed

« back to all changes in this revision

Viewing changes to src/runtime/ffi.scm

  • Committer: Package Import Robot
  • Author(s): Chris Hanson
  • Date: 2011-10-15 03:08:33 UTC
  • mfrom: (1.1.8) (3.1.7 sid)
  • Revision ID: package-import@ubuntu.com-20111015030833-x7qc6yxuulvxbafv
Tags: 9.1-1
* New upstream.
* debian/control, debian/copyright, debian/mit-scheme-doc.*,
  debian/mit-scheme.install, debian/rules, Upstream has removed cover
  texts from documentation licenses, so merge packages mit-scheme and
  mit-scheme-doc back together.
* debian/compat: Bump to current version.
* debian/control: Bump standards-version to current and make
  necessary changes.
* debian/rules: Fix lintian warnings.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#| -*-Scheme-*-
 
2
 
 
3
Copyright (C) 2006, 2007, 2008, 2009, 2010 Matthew Birkholz
 
4
 
 
5
This file is part of MIT/GNU Scheme.
 
6
 
 
7
MIT/GNU Scheme is free software; you can redistribute it and/or modify
 
8
it under the terms of the GNU General Public License as published by
 
9
the Free Software Foundation; either version 2 of the License, or (at
 
10
your option) any later version.
 
11
 
 
12
MIT/GNU Scheme is distributed in the hope that it will be useful, but
 
13
WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
15
General Public License for more details.
 
16
 
 
17
You should have received a copy of the GNU General Public License
 
18
along with MIT/GNU Scheme; if not, write to the Free Software
 
19
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
 
20
USA.
 
21
 
 
22
|#
 
23
 
 
24
;;;; Aliens and Alien Functions
 
25
;;; package: (runtime ffi)
 
26
 
 
27
(declare (usual-integrations))
 
28
 
 
29
 
 
30
;;; Aliens
 
31
 
 
32
(define-structure (alien (constructor %make-alien)
 
33
                         (conc-name %alien/)
 
34
                         (copier copy-alien)
 
35
                         (predicate alien?))
 
36
  ;; Two fixnums.
 
37
  (high-bits 0) (low-bits 0)
 
38
  ;; A symbol or list.
 
39
  ctype)
 
40
 
 
41
;; Breaking a word in two produces high and low fixnums.  If they are
 
42
;; two digits representing a larger number, then RADIX is their base.
 
43
;; For a 32 bit word, (radix) is #x10000.
 
44
;;
 
45
;; This substitutes a constant when there is a compiler, per its
 
46
;; target.  Else this is a reference to %radix.
 
47
(define-syntax radix
 
48
  (er-macro-transformer
 
49
   (lambda (form rename compare)
 
50
     (declare (ignore rename compare))
 
51
     (if (not (null? (cdr form)))
 
52
         (syntax-error "No sub-forms allowed:" form))
 
53
     (cond ((get-subsystem-version "LIAR/i386") #x10000)
 
54
           ((get-subsystem-version "LIAR/x86-64") #x100000000)
 
55
           (else
 
56
            '%RADIX)))))
 
57
 
 
58
;; This is only needed when the target machine's word size is unknown
 
59
;; (e.g. when compiling to C, or when there is no compiler).
 
60
(define %radix)
 
61
 
 
62
(set-record-type-unparser-method! rtd:alien
 
63
  (standard-unparser-method
 
64
   'alien
 
65
   (lambda (alien port)
 
66
     (write-char #\space port)
 
67
     (write (%alien/ctype alien) port)
 
68
     (write-string " 0x" port)
 
69
     (write-string (alien/address-string alien) port))))
 
70
 
 
71
(define-integrable alien/ctype %alien/ctype)
 
72
 
 
73
(define-integrable set-alien/ctype! set-%alien/ctype!)
 
74
 
 
75
(declare (integrate-operator c-cast))
 
76
(define (c-cast alien ctype)
 
77
  (set-%alien/ctype! alien ctype)
 
78
  alien)
 
79
 
 
80
(define (alien/address-string alien)
 
81
  ;; Returns a string, e.g. "081adc60".
 
82
  (let ((high (%alien/high-bits alien))
 
83
        (low (%alien/low-bits alien))
 
84
        (hex (lambda (n)
 
85
               (string-pad-left (number->string n 16)
 
86
                                (if (fix:= (radix) #x10000) 4 8)
 
87
                                #\0))))
 
88
    (string-append (hex high) (hex low))))
 
89
 
 
90
(define (make-alien #!optional ctype)
 
91
  (let ((ctype (if (default-object? ctype) #f ctype)))
 
92
    (%make-alien 0 0 ctype)))
 
93
 
 
94
(declare (integrate-operator alien/address))
 
95
(define (alien/address alien)
 
96
  (+ (* (%alien/high-bits alien) (radix))
 
97
     (%alien/low-bits alien)))
 
98
 
 
99
(declare (integrate-operator copy-alien-address!))
 
100
(define (copy-alien-address! alien source)
 
101
  (if (not (eq? alien source))
 
102
      (begin
 
103
        (set-%alien/high-bits! alien (%alien/high-bits source))
 
104
        (set-%alien/low-bits! alien (%alien/low-bits source)))))
 
105
 
 
106
(declare (integrate-operator alien-null?))
 
107
(define (alien-null? alien)
 
108
  (and (fix:zero? (%alien/high-bits alien))
 
109
       (fix:zero? (%alien/low-bits alien))))
 
110
 
 
111
(declare (integrate-operator alien-null!))
 
112
(define (alien-null! alien)
 
113
  (set-%alien/high-bits! alien 0)
 
114
  (set-%alien/low-bits! alien 0))
 
115
 
 
116
(define null-alien (make-alien '|void|))
 
117
 
 
118
(declare (integrate-operator alien=?))
 
119
(define (alien=? alien1 alien2)
 
120
  (and (fix:= (%alien/high-bits alien1) (%alien/high-bits alien2))
 
121
       (fix:= (%alien/low-bits alien1) (%alien/low-bits alien2))))
 
122
 
 
123
(define (alien-hash alien modulus)
 
124
  ;; Appropriate for hash table construction (as is alien=?).
 
125
  (remainder (fix:xor (%alien/high-bits alien)
 
126
                      (%alien/low-bits alien)) modulus))
 
127
 
 
128
(define (alien-byte-increment alien offset #!optional ctype)
 
129
  ;; Returns a new alien - a copy of ALIEN - whose address is OFFSET
 
130
  ;; bytes from ALIEN's.  If CTYPE is specified, the type slot of the
 
131
  ;; new alien is set.
 
132
  (let ((new (copy-alien alien)))
 
133
    (alien-byte-increment! new offset)
 
134
    (if (not (default-object? ctype))
 
135
        (set-%alien/ctype! new ctype))
 
136
    new))
 
137
 
 
138
(define (alien-byte-increment! alien increment #!optional ctype)
 
139
  ;; This procedure returns ALIEN after modifying it to have an
 
140
  ;; address INCREMENT bytes away from its previous address.  If CTYPE
 
141
  ;; is specified, the type slot of ALIEN is set.
 
142
  (let ((quotient.remainder (fix:divide increment (radix))))
 
143
    (let ((new-high (fix:+ (%alien/high-bits alien)
 
144
                           (integer-divide-quotient quotient.remainder)))
 
145
          (new-low (fix:+ (%alien/low-bits alien)
 
146
                          (integer-divide-remainder quotient.remainder))))
 
147
      (cond ((fix:negative? new-high)
 
148
             (error:bad-range-argument increment 'alien-byte-increment!))
 
149
            ((fix:negative? new-low)
 
150
             (if (fix:zero? new-high)
 
151
                 (error:bad-range-argument increment 'alien-byte-increment!)
 
152
                 (begin
 
153
                   (set-%alien/low-bits! alien (fix:+ new-low (radix)))
 
154
                   (set-%alien/high-bits! alien (fix:-1+ new-high)))))
 
155
            ((fix:>= new-low (radix))
 
156
             (set-%alien/low-bits! alien (fix:- new-low (radix)))
 
157
             (set-%alien/high-bits! alien (fix:1+ new-high)))
 
158
            (else
 
159
             (set-%alien/low-bits! alien new-low)
 
160
             (set-%alien/high-bits! alien new-high)))))
 
161
  (if (not (default-object? ctype))
 
162
      (set-%alien/ctype! alien ctype))
 
163
  alien)
 
164
 
 
165
(declare (integrate-operator guarantee-alien))
 
166
(define (guarantee-alien object operator)
 
167
  (if (not (alien? object))
 
168
      (error:not-alien object operator)))
 
169
 
 
170
(define (error:not-alien object operator)
 
171
  (call-with-current-continuation
 
172
   (lambda (continuation)
 
173
     (with-restart
 
174
      'USE-VALUE                        ;name
 
175
      "Continue with an alien."         ;reporter
 
176
      continuation                      ;effector
 
177
      (lambda ()                        ;interactor
 
178
        (values
 
179
         (prompt-for-evaluated-expression
 
180
          "New alien (an expression to be evaluated)")))
 
181
      (lambda ()                        ;thunk
 
182
        (error:wrong-type-argument object "an alien" operator))))))
 
183
 
 
184
 
 
185
;;; Alien Functions
 
186
 
 
187
(define-structure (alien-function
 
188
                   (constructor %make-alien-function)
 
189
                   (conc-name %alien-function/)
 
190
                   (predicate alien-function?)
 
191
                   ;; To be fasdump/loadable.
 
192
                   (type vector) (named 'alien-function)
 
193
                   (print-procedure
 
194
                    (standard-unparser-method 'ALIEN-FUNCTION
 
195
                     (lambda (alienf port)
 
196
                       (write-char #\space port)
 
197
                       (write-string (%alien-function/name alienf)
 
198
                                     port)))))
 
199
 
 
200
  ;; C function entry address as two fixnums.
 
201
  high-bits low-bits
 
202
 
 
203
  ;; String: name of trampoline.  (Starts with "Scm_".)
 
204
  name
 
205
 
 
206
  ;; String: name of shim.  (WithOUT "-shim.so" on the end.)
 
207
  library
 
208
 
 
209
  ;; Caseful symbol or list, e.g. (* |GtkWidget|).
 
210
  return-type
 
211
 
 
212
  ;; Alist of parameter names * types, e.g. ((widget (* |GtkWidget|))...)
 
213
  parameters
 
214
 
 
215
  ;; Filename from which the EXTERN declaration was read.
 
216
  filename
 
217
 
 
218
  ;; Band ID
 
219
  band-id)
 
220
 
 
221
(declare (integrate-operator guarantee-alien-function))
 
222
(define (guarantee-alien-function object operator)
 
223
  (if (not (alien-function? object))
 
224
      (error:not-alien-function object operator)))
 
225
 
 
226
(define (error:not-alien-function object operator)
 
227
  (error:wrong-type-argument object "an alien function" operator))
 
228
 
 
229
(define (make-alien-function name library return-type params filename)
 
230
  (%make-alien-function 0 0 (string-append "Scm_" name)
 
231
                        library return-type params filename #f))
 
232
 
 
233
(define-integrable alien-function/return-type %alien-function/return-type)
 
234
 
 
235
(define-integrable alien-function/parameters %alien-function/parameters)
 
236
 
 
237
(define-integrable alien-function/filename %alien-function/filename)
 
238
 
 
239
(define-integrable (alien-function/name alienf)
 
240
  (string-tail (%alien-function/name alienf) 4)) 
 
241
 
 
242
(define (%set-alien-function/address! alienf address)
 
243
  (let ((qr (integer-divide address (radix))))
 
244
    (set-%alien-function/high-bits! alienf (integer-divide-quotient qr))
 
245
    (set-%alien-function/low-bits! alienf (integer-divide-remainder qr))))
 
246
 
 
247
(define band-id)
 
248
 
 
249
(define (reset-alien-functions!)
 
250
  (set! band-id (list (get-universal-time))))
 
251
 
 
252
(define (alien-function-cache! afunc)
 
253
  (if (eq? band-id (%alien-function/band-id afunc))
 
254
      unspecific
 
255
      (let* ((library (%alien-function/library afunc))
 
256
             (name (%alien-function/name afunc))
 
257
             (pathname (merge-pathnames
 
258
                        (pathname-new-type (string-append library "-shim") "so")
 
259
                        (system-library-directory-pathname)))
 
260
             (handle (or (find-dld-handle
 
261
                          (lambda (h)
 
262
                            (pathname=? pathname (dld-handle-pathname h))))
 
263
                         (dld-load-file pathname)))
 
264
             (address (dld-lookup-symbol handle name)))
 
265
        (if address
 
266
            (%set-alien-function/address! afunc address)
 
267
            (error:bad-range-argument afunc 'alien-function-cache!))
 
268
        (set-%alien-function/band-id! afunc band-id))))
 
269
 
 
270
(define (c-peek-cstring alien)
 
271
  ((ucode-primitive c-peek-cstring 2) alien 0))
 
272
 
 
273
(define (c-peek-cstring! alien)
 
274
  ((ucode-primitive c-peek-cstring! 2) alien 0))
 
275
 
 
276
(define (c-peek-cstringp alien)
 
277
  ((ucode-primitive c-peek-cstringp 2) alien 0))
 
278
 
 
279
(define (c-peek-cstringp! alien)
 
280
  ((ucode-primitive c-peek-cstringp! 2) alien 0))
 
281
 
 
282
(define (c-poke-pointer dest alien)
 
283
  ;; Sets the pointer at the alien DEST to point to the ALIEN.
 
284
  ((ucode-primitive c-poke-pointer 3) dest 0 alien))
 
285
 
 
286
(define (c-poke-pointer! dest alien)
 
287
  ;; Like c-poke-pointer, but increments DEST by a pointer width.
 
288
  ((ucode-primitive c-poke-pointer! 3) dest 0 alien))
 
289
 
 
290
(define (c-poke-string alien string)
 
291
  ;; Copy STRING to the bytes at the ALIEN address.
 
292
  (guarantee-string string 'C-POKE-STRING)
 
293
  ((ucode-primitive c-poke-string 3) alien 0 string))
 
294
 
 
295
(define (c-poke-string! alien string)
 
296
  ;; Like c-poke-string, but increments ALIEN by the null-terminated
 
297
  ;; STRING length.
 
298
  (guarantee-string string 'C-POKE-STRING)
 
299
  ((ucode-primitive c-poke-string! 3) alien 0 string))
 
300
 
 
301
(define (c-enum-name value enum-name constants)
 
302
  enum-name
 
303
  (let loop ((consts constants))
 
304
    (if (null? consts)
 
305
        (error:bad-range-argument value 'c-enum-name)
 
306
        (let ((name.value (car consts)))
 
307
          (if (= value (cdr name.value))
 
308
              (car name.value)
 
309
              (loop (cdr consts)))))))
 
310
 
 
311
(define (call-alien alien-function . args)
 
312
  (guarantee-alien-function alien-function 'call-alien)
 
313
  (alien-function-cache! alien-function)
 
314
  (for-each
 
315
   (lambda (arg)
 
316
     (if (alien-function? arg)
 
317
         (alien-function-cache! arg)))
 
318
   args)
 
319
  (without-interrupts
 
320
   (lambda ()
 
321
     (call-alien* alien-function args))))
 
322
 
 
323
(define (call-alien* alien-function args)
 
324
  (let ((old-top calloutback-stack))
 
325
    (if-tracing
 
326
     (outf-console ";"(tindent)"=> "alien-function" "args"\n")
 
327
     (set! calloutback-stack (cons (cons* alien-function args) old-top)))
 
328
    (let ((value (apply (ucode-primitive c-call -1) alien-function args)))
 
329
      (if-tracing
 
330
       (assert (eq? old-top (cdr calloutback-stack))
 
331
               "call-alien: freak stack "calloutback-stack"\n")
 
332
       (set! calloutback-stack old-top)
 
333
       (outf-console ";"(tindent)"<= "value"\n"))
 
334
      value)))
 
335
 
 
336
 
 
337
;;; Malloc/Free
 
338
 
 
339
;; Weak alist of: ( malloc alien X copy for c-free )...
 
340
(define malloced-aliens '())
 
341
 
 
342
(define (free-malloced-aliens)
 
343
  (let loop ((aliens malloced-aliens)
 
344
             (prev #f))
 
345
    (if (pair? aliens)
 
346
        (if (weak-pair/car? (car aliens))
 
347
            (loop (cdr aliens) aliens)
 
348
            (let ((copy (weak-cdr (car aliens)))
 
349
                  (next (cdr aliens)))
 
350
              (if prev
 
351
                  (set-cdr! prev next)
 
352
                  (set! malloced-aliens next))
 
353
              (if (not (alien-null? copy))
 
354
                  (begin
 
355
                    ((ucode-primitive c-free 1) copy)
 
356
                    (alien-null! copy)))
 
357
              (loop next prev))))))
 
358
 
 
359
(define (reset-malloced-aliens!)
 
360
  (let loop ((aliens malloced-aliens))
 
361
    (if (pair? aliens)
 
362
        (let ((alien (weak-car (car aliens)))
 
363
              (copy (weak-cdr (car aliens))))
 
364
          (if alien (alien-null! alien))
 
365
          (alien-null! copy)
 
366
          (loop (cdr aliens)))))
 
367
  (set! malloced-aliens '()))
 
368
 
 
369
(define (make-alien-to-free ctype init)
 
370
  ;; Register BEFORE initializing (allocating).
 
371
  (let ((alien (make-alien ctype)))
 
372
    (let ((copy (make-alien ctype)))
 
373
      (let ((entry (weak-cons alien copy)))
 
374
        (without-interrupts
 
375
         (lambda ()
 
376
           (set! malloced-aliens (cons entry malloced-aliens)))))
 
377
      (init copy)
 
378
      ;; Even an abort here will not leak a byte.
 
379
      (copy-alien-address! alien copy))
 
380
    alien))
 
381
 
 
382
(define (malloc size ctype)
 
383
  (make-alien-to-free ctype
 
384
                      (lambda (alien)
 
385
                        ((ucode-primitive c-malloc 2) alien size))))
 
386
 
 
387
(define (free alien)
 
388
  (if (not (alien? alien))
 
389
      (warn "Cannot free a non-alien:" alien)
 
390
      (let ((weak (weak-assq alien malloced-aliens)))
 
391
        (if (not weak)
 
392
            (warn "Cannot free an alien that was not malloced:" alien)
 
393
            (let ((copy (weak-cdr weak)))
 
394
              (without-interrupts
 
395
               (lambda ()
 
396
                 (if (not (alien-null? alien))
 
397
                     (begin
 
398
                       (alien-null! alien)
 
399
                       ((ucode-primitive c-free 1) copy)
 
400
                       (alien-null! copy))))))))))
 
401
 
 
402
(define (weak-assq obj alist)
 
403
  (let loop ((alist alist))
 
404
    (if (null? alist) #f
 
405
        (let* ((entry (car alist))
 
406
               (key (weak-car entry)))
 
407
          (if (eq? obj key) entry
 
408
              (loop (cdr alist)))))))
 
409
 
 
410
 
 
411
;;; Callback support
 
412
 
 
413
(define registered-callbacks)
 
414
(define first-free-id)
 
415
 
 
416
(define (reset-callbacks!)
 
417
  (set! registered-callbacks (make-vector 100 #f))
 
418
  (set! first-free-id 1))
 
419
 
 
420
(define (register-c-callback procedure)
 
421
  (if (not (procedure? procedure))
 
422
      (error:wrong-type-argument procedure "a procedure" 'register-c-callback))
 
423
  (without-interrupts
 
424
   (lambda ()
 
425
     (let ((id first-free-id))
 
426
       (set! first-free-id (next-free-id (1+ id)))
 
427
       (vector-set! registered-callbacks id procedure)
 
428
       id))))
 
429
 
 
430
(define (next-free-id id)
 
431
  (let ((len (vector-length registered-callbacks)))
 
432
    (let next-id ((id id))
 
433
      (cond ((= id len)
 
434
             (set! registered-callbacks
 
435
                   (vector-grow registered-callbacks (* 2 len)))
 
436
             (next-free-id id))
 
437
            ((not (vector-ref registered-callbacks id)) id)
 
438
            ;; When not recycling ids, the above is always true.
 
439
            ;; There is no need for the next-id loop.
 
440
            (else (next-id (1+ id)))))))
 
441
 
 
442
(define (de-register-c-callback id)
 
443
  (vector-set! registered-callbacks id #f)
 
444
  ;; Uncomment to recycle ids.
 
445
  ;;(if (< id first-free-id)
 
446
  ;;    (set! first-free-id id))
 
447
  )
 
448
 
 
449
(define (normalize-aliens! args)
 
450
  ;; Any vectors among ARGS are assumed to be freshly-consed aliens
 
451
  ;; without their record-type.  Fix them.
 
452
  (let ((tag (record-type-dispatch-tag rtd:alien)))
 
453
    (let loop ((args args))
 
454
      (if (null? args)
 
455
          unspecific
 
456
          (let ((arg (car args)))
 
457
            (if (%record? arg) (%record-set! arg 0 tag))
 
458
            (loop (cdr args)))))))
 
459
 
 
460
(define (callback-handler id args)
 
461
  ;; Installed in the fixed-objects-vector, this procedure is called
 
462
  ;; by a callback trampoline.  The callout should have already masked
 
463
  ;; all but the GC interrupts.
 
464
 
 
465
  (if (not (< id (vector-length registered-callbacks)))
 
466
      (error:bad-range-argument id 'apply-callback))
 
467
  (let ((procedure (vector-ref registered-callbacks id)))
 
468
    (if (not procedure)
 
469
        (error:bad-range-argument id 'apply-callback))
 
470
    (normalize-aliens! args)
 
471
    (let ((old-top calloutback-stack))
 
472
      (if-tracing
 
473
       (outf-console ";"(tindent)"=>> "procedure" "args"\n")
 
474
       (set! calloutback-stack (cons (cons procedure args) old-top)))
 
475
      (let ((value (apply-callback-proc procedure args)))
 
476
        (if-tracing
 
477
         (assert (and (pair? calloutback-stack)
 
478
                      (eq? old-top (cdr calloutback-stack)))
 
479
                 "callback-handler: freak stack "calloutback-stack"\n")
 
480
         (set! calloutback-stack old-top)
 
481
         (outf-console ";"(tindent)"<<= "value"\n"))
 
482
        value))))
 
483
 
 
484
(define (apply-callback-proc procedure args)
 
485
  (call-with-current-continuation
 
486
   (lambda (return)
 
487
     (with-restart
 
488
      'USE-VALUE                        ;name
 
489
      "Return a value from the callback." ;reporter
 
490
      return                            ;effector
 
491
      (lambda ()                        ;interactor
 
492
        (values (prompt-for-evaluated-expression
 
493
                 "Value to return from callback")))
 
494
      (lambda ()                        ;thunk
 
495
        (let ((done? #f))
 
496
          (if (not done?)
 
497
              (begin
 
498
                (set! done? #t)
 
499
                (apply procedure args))
 
500
              (let loop ()
 
501
                (error "Cannot return from a callback more than once.")
 
502
                (loop)))))))))
 
503
 
 
504
;; For callback debugging...
 
505
(define (outf-console . objects)
 
506
  ((ucode-primitive outf-console 1)
 
507
   (apply string-append
 
508
          (map (lambda (o) (if (string? o) o (write-to-string o)))
 
509
               objects))))
 
510
 
 
511
(define (initialize-callbacks!)
 
512
  (vector-set! (get-fixed-objects-vector) #x41 callback-handler))
 
513
 
 
514
 
 
515
(define calloutback-stack '())
 
516
 
 
517
(define trace? #f)
 
518
 
 
519
(define (reset-package!)
 
520
  (reset-alien-functions!)
 
521
  (reset-malloced-aliens!)
 
522
  (reset-callbacks!)
 
523
  (set! %radix (if (fix:fixnum? #x100000000) #x100000000 #x10000))
 
524
  (set! trace? #f)
 
525
  (set! calloutback-stack '()))
 
526
 
 
527
(define (initialize-package!)
 
528
  (reset-package!)
 
529
  (initialize-callbacks!)
 
530
  (add-event-receiver! event:after-restore reset-package!)
 
531
  (add-gc-daemon! free-malloced-aliens)
 
532
  unspecific)
 
533
 
 
534
(define-syntax if-tracing
 
535
  (syntax-rules ()
 
536
    ((_ . BODY)
 
537
     (if trace? ((lambda () . BODY))))))
 
538
 
 
539
(define-syntax assert
 
540
  (syntax-rules ()
 
541
    ((_ TEST . MSG)
 
542
     (if (not TEST) (error "Failed assert:" . MSG)))))
 
543
 
 
544
(define-syntax trace
 
545
  (syntax-rules ()
 
546
    ((_ . MSG)
 
547
     (if trace? ((lambda () (outf-console . MSG)))))))
 
548
 
 
549
(define (tindent)
 
550
  (make-string (* 2 (length calloutback-stack)) #\space))
 
 
b'\\ No newline at end of file'