~ chicken-core (chicken-5) 529032c1d98cf5eda3caeb80bcf46dfaa7fff28f


commit 529032c1d98cf5eda3caeb80bcf46dfaa7fff28f
Author:     felix <felix@call-with-current-continuation.org>
AuthorDate: Wed Nov 25 10:10:35 2009 +0100
Commit:     felix <felix@call-with-current-continuation.org>
CommitDate: Wed Nov 25 10:10:35 2009 +0100

    reverted incorrect merge

diff --git a/README b/README
index 66b5517c..eb9baf56 100644
--- a/README
+++ b/README
@@ -124,6 +124,13 @@
           option you can still enable symbol GC at runtime by passing 
           the `-:w' runtime option when running the program.
 
+        NOAPPLYHOOK=1
+          For maximum performance this will disable support for 
+          breakpoints, but speed up procedure invocation in safe 
+          code. Smaller binaries can be obtained by also giving 
+          "NOPTABLES=1", but that means serialization (available 
+          as a separate package) of procedures will not be available.
+
         OPTIMIZE_FOR_SPEED=1
           Use C optimization options that prefer speed over size. For
 	  the GNU C compiler this will currently select "-O3" (the
diff --git a/csi.scm b/csi.scm
index c5184965..02eea351 100644
--- a/csi.scm
+++ b/csi.scm
@@ -56,7 +56,7 @@ EOF
     ##sys#windows-platform)
   (hide parse-option-string bytevector-data member* canonicalize-args do-trace do-untrace
 	traced-procedures describer-table dirseparator? resolve-var
-	findall trace-indent command-table) )
+	findall trace-indent command-table do-break do-unbreak broken-procedures) )
 
 
 ;;; Parameters:
@@ -254,6 +254,7 @@ EOF
   (let ((eval eval)
 	(load-noisily load-noisily)
 	(read read)
+	(singlestep singlestep)
 	(read-line read-line)
 	(length length)
 	(display display)
@@ -316,13 +317,37 @@ EOF
 			     (apply values rs) ) ) )
 			((tr) (do-trace (map resolve-var (string-split (read-line)))))
 			((utr) (do-untrace (map resolve-var (string-split (read-line)))))
+			((br) (do-break (map resolve-var (string-split (read-line)))))
+			((ubr) (do-unbreak (map resolve-var (string-split (read-line)))))
+			((uba) (do-unbreak-all))
+			((breakall) 
+			 (set! ##sys#break-in-thread #f) ) 
+			((breakonly)
+			 (set! ##sys#break-in-thread (eval (read))) )
 			((info)
 			 (when (pair? traced-procedures)
-			   (printf "Traced: ~s~%" (map car traced-procedures)) ) )
+			   (printf "Traced: ~s~%" (map car traced-procedures)) )
+			 (when (pair? broken-procedures)
+			   (printf "Breakpoints: ~s~%" (map car broken-procedures)) ) )
+			((c)
+			 (cond (##sys#last-breakpoint
+				(let ((exn ##sys#last-breakpoint))
+				  (set! ##sys#last-breakpoint #f)
+				  (##sys#break-resume exn) ) )
+			       (else (display "no breakpoint pending\n") ) ) )
 			((exn) 
 			 (when ##sys#last-exception
 			   (history-add (list ##sys#last-exception))
 			   (describe ##sys#last-exception) ) )
+			((step)
+			 (let ((x (read)))
+			   (read-line)
+			   (singlestep (eval `(lambda () ,x))) ) )
+			((s)
+			 (let* ((str (read-line))
+				(r (system str)) )
+			   (history-add (list r))
+			   r) )
 			((?)
 			 (display 
 			  "Toplevel commands:
@@ -339,7 +364,14 @@ EOF
  ,s TEXT ...       Execute shell-command
  ,tr NAME ...      Trace procedures
  ,utr NAME ...     Untrace procedures
- ,info             List traced procedures
+ ,br NAME ...      Set breakpoints
+ ,ubr NAME ...     Remove breakpoints
+ ,uba              Remove all breakpoints
+ ,breakall         Break in all threads (default)
+ ,breakonly THREAD Break only in specified thread
+ ,c                Continue from breakpoint
+ ,info             List traced procedures and breakpoints
+ ,step EXPR        Execute EXPR in single-stepping mode
  ,exn              Describe last exception
  ,t EXP            Evaluate form and print elapsed time
  ,x EXP            Pretty print expanded expression EXP\n")
@@ -416,6 +448,8 @@ EOF
 	   (let ((s (expand s)))
 	     (cond ((assq s traced-procedures)
 		    (##sys#warn "procedure already traced" s) )
+		   ((assq s broken-procedures)
+		    (##sys#warn "procedure already has breakpoint") )
 		   (else
 		    (let ((old (##sys#slot s 0)))
 		      (cond ((not (procedure? old)) (##sys#error "cannot trace non-procedure" s))
@@ -443,6 +477,49 @@ EOF
 		(set! traced-procedures (del p traced-procedures eq?) ) ) ) ) )
      names) ) )
 
+(define do-break
+  (lambda (names)
+    (if (null? names)
+	(for-each (lambda (b) (print (car a))) broken-procedures) 
+	(for-each
+	 (lambda (s)
+	   (let* ((s (expand s))
+		  (a (assq s traced-procedures)))
+	     (when a
+	       (##sys#warn "un-tracing procedure" s)
+	       (##sys#setslot s 0 (cdr a))
+	       (set! traced-procedures (del a traced-procedures eq?)) )
+	     (let ((old (##sys#slot s 0)))
+	       (cond ((not (procedure? old)) (##sys#error "cannot set breakpoint on non-procedure" s))
+		     (else
+		      (set! broken-procedures (cons (cons s old) broken-procedures))
+		      (##sys#setslot
+		       s 0
+		       (lambda args
+			 (##sys#break-entry s args)
+			 (##sys#apply old args) ) ) ) ) ) ) )
+	 names) ) ) )
+
+(define do-unbreak
+  (lambda (names)
+    (for-each
+     (lambda (s)
+       (let* ((s (expand s))
+	      (p (assq s broken-procedures)) )
+	 (cond ((not p) (##sys#warn "procedure has no breakpoint" s))
+	       (else
+		(##sys#setslot s 0 (cdr p))
+		(set! broken-procedures (del p broken-procedures eq?) ) ) ) ) )
+     names) ) )
+
+(define do-unbreak-all
+  (lambda ()
+    (for-each (lambda (bp)
+                (##sys#setslot (car bp) 0 (cdr bp)))
+              broken-procedures)
+    (set! broken-procedures '())
+    (##sys#void)))
+
 ;;; Parse options from string:
 
 (define (parse-option-string str)
diff --git a/library.scm b/library.scm
index 3da58977..54aae68c 100644
--- a/library.scm
+++ b/library.scm
@@ -159,7 +159,7 @@ EOF
      ##sys#memory-info ##sys#make-c-string ##sys#find-symbol-table display
      newline string-append ##sys#with-print-length-limit write print vector-fill! ##sys#context-switch
      ##sys#set-finalizer! open-output-string get-output-string read ##sys#make-pointer
-     ##sys#pointer->address number->string ##sys#flush-output
+     ##sys#pointer->address number->string ##sys#flush-output ##sys#break-entry ##sys#step
      ##sys#apply-values ##sys#get-call-chain ##sys#really-print-call-chain
      string->keyword keyword? string->keyword get-environment-variable ##sys#number->string ##sys#copy-bytes
      call-with-current-continuation ##sys#string->number ##sys#inexact->exact ##sys#exact->inexact
@@ -3302,6 +3302,7 @@ EOF
 		   (if (##sys#fudge 24) " dload" "") 
 		   (if (##sys#fudge 28) " ptables" "")
 		   (if (##sys#fudge 32) " gchooks" "") 
+		   (if (##sys#fudge 35) " applyhook" "")
 		   (if (##sys#fudge 39) " cross" "") ) ) )
 	(string-append
 	 "Version " +build-version+ "\n"
@@ -3351,6 +3352,7 @@ EOF
 (when (##sys#fudge 40) (set! ##sys#features (cons #:manyargs ##sys#features)))
 (when (##sys#fudge 24) (set! ##sys#features (cons #:dload ##sys#features)))
 (when (##sys#fudge 28) (set! ##sys#features (cons #:ptables ##sys#features)))
+(when (##sys#fudge 35) (set! ##sys#features (cons #:applyhook ##sys#features)))
 (when (##sys#fudge 39) (set! ##sys#features (cons #:cross-chicken ##sys#features)))
 
 (define (register-feature! . fs)
@@ -3448,6 +3450,105 @@ EOF
       (##sys#setslot ##sys#primordial-thread 1 break) ) )
 
 
+;;; Breakpoints
+
+(define ##sys#last-breakpoint #f)
+(define ##sys#break-in-thread #f)
+
+(define (##sys#break-entry name args)
+  ;; Does _not_ unwind!
+  (##sys#call-with-current-continuation
+   (lambda (c)
+     (let ((exn (##sys#make-structure
+		 'condition
+		 '(exn breakpoint)
+		 (list '(exn . message) "*** breakpoint ***"
+		       '(exn . arguments) (list (cons name args))
+		       '(exn . location) name
+		       '(exn . continuation) c) ) ) )
+       (set! ##sys#last-breakpoint exn)
+       (##sys#signal exn) ) ) ) )
+
+(define (##sys#break-resume exn)
+  (let ((a (member '(exn . continuation) (##sys#slot exn 2))))
+    (if a
+	((cadr a) (##core#undefined))
+	(##sys#signal-hook #:type-error "condition has no continuation" exn) ) ) )
+
+(define (breakpoint #!optional name)
+  (##sys#break-entry (or name 'breakpoint) '()) )
+
+
+;;; Single stepping
+
+(define ##sys#stepped-thread #f)
+(define ##sys#step-ports (cons ##sys#standard-input ##sys#standard-output))
+
+(define (##sys#step thunk)
+  (when (eq? ##sys#stepped-thread ##sys#current-thread)
+    (##sys#call-with-values
+     (lambda () 
+       (set! ##sys#apply-hook ##sys#step-hook)
+       (##core#app thunk) )
+     (lambda vals
+       (set! ##sys#apply-hook #f)
+       (set! ##sys#stepped-thread #f)
+       (##sys#apply-values vals) ) ) ) )
+
+(define (singlestep thunk)
+  (unless (##sys#fudge 35)
+    (##sys#signal-hook #:runtime-error 'singlestep "apply-hook not available") )
+  (##sys#check-closure thunk 'singlestep)
+  (set! ##sys#stepped-thread ##sys#current-thread)
+  (##sys#step thunk) )
+
+(define (##sys#step-hook . args)
+  (set! ##sys#apply-hook #f)
+  (let ((o (##sys#slot ##sys#step-ports 1))
+	(i (##sys#slot ##sys#step-ports 0))
+	(p ##sys#last-applied-procedure))
+    (define (skip-to-nl)
+      (let ((c (##sys#read-char-0 i)))
+	(unless (or (eof-object? c) (char=? #\newline c))
+	  (sip-to-nl) ) ) )
+    (define (cont)
+      (set! ##sys#stepped-thread #f)
+      (##sys#apply p args) )
+    (##sys#print "\n " #f o)
+    (##sys#with-print-length-limit 
+     1024
+     (lambda () (##sys#print (cons p args) #t o)) )
+    (flush-output o)
+    (let loop ()
+      (##sys#print "\n	      step (RETURN), (s)kip, (c)ontinue or (b)reak ? " #f o)
+      (let ((c (##sys#read-char-0 i)))
+	(if (eof-object? c)
+	    (cont)
+	    (case c
+	      ((#\newline) 
+	       (set! ##sys#apply-hook ##sys#step-hook)
+	       (##core#app ##sys#apply p args))
+	      ((#\return #\tab #\space) (loop))
+	      ((#\c) (skip-to-nl) (cont))
+	      ((#\s) 
+	       (skip-to-nl)
+	       (##sys#call-with-values 
+		(lambda () (##core#app ##sys#apply p args))
+		(lambda results
+		  (set! ##sys#apply-hook ##sys#step-hook)
+		  (##core#app ##sys#apply-values results) ) ) )
+	      ((#\b) 
+	       (skip-to-nl)
+	       (set! ##sys#stepped-thread #f)
+	       (##sys#break-entry '<step> '())
+	       (##sys#apply p args) ) 
+	      (else
+	       (cond ((eof-object? c) (cont))
+		     (else 
+		      (skip-to-nl) 
+		      (loop))))))))))
+
+
 ;;; Default handlers
 
 (define ##sys#break-on-error (##sys#fudge 25))
diff --git a/lolevel.import.scm b/lolevel.import.scm
index 77aa16aa..58ea8f22 100644
--- a/lolevel.import.scm
+++ b/lolevel.import.scm
@@ -31,6 +31,7 @@
    allocate
    block-ref
    block-set!
+   clear-unbound-variable-value!
    extend-procedure
    extended-procedure?
    free
@@ -38,6 +39,7 @@
    global-make-unbound!
    global-ref
    global-set!
+   invalid-procedure-call-handler
    locative->object
    locative-ref
    locative-set!
@@ -90,6 +92,12 @@
    record-instance-slot
    record-instance-slot-set!
    record-instance-type
+   set-invalid-procedure-call-handler!
    set-procedure-data!
+   set-unbound-variable-value!
    tag-pointer
-   tagged-pointer?))
+   tagged-pointer?
+   unbound-variable-value
+   unbound-variable-given-value
+   unbound-variable-signals-error?
+   vector-like?))
diff --git a/lolevel.scm b/lolevel.scm
index 32628bfa..b27036a2 100644
--- a/lolevel.scm
+++ b/lolevel.scm
@@ -440,6 +440,9 @@ EOF
 (define block-set! ##sys#block-set!)
 (define block-ref (getter-with-setter ##sys#block-ref ##sys#block-set!))
 
+(define (vector-like? x)
+  (%generic-vector? x) )
+
 (define (number-of-slots x)
   (##sys#check-generic-vector x 'number-of-slots)
   (##sys#size x) )
@@ -644,6 +647,38 @@ EOF
     new ) )
 
 
+;;; Hooks:
+
+; we need this because `##sys#invalid-procedure-call-hook' cannot
+; have free variables.
+(define ipc-hook-0 #f)
+
+(define (invalid-procedure-call-handler) ipc-hook-0)
+
+(define (set-invalid-procedure-call-handler! proc)
+  (##sys#check-closure proc 'set-invalid-procedure-call-handler!)
+  (set! ipc-hook-0 proc)
+  (set! ##sys#invalid-procedure-call-hook
+        (lambda args (ipc-hook-0 ##sys#last-invalid-procedure args))) )
+
+(define (unbound-variable-signals-error?) (not ##sys#unbound-variable-value-hook))
+
+; result only trusted when (unbound-variable-signals-error?) is #f
+(define (unbound-variable-given-value)
+  (and ##sys#unbound-variable-value-hook
+       (vector-ref ##sys#unbound-variable-value-hook 0)) )
+
+(define (set-unbound-variable-value! val) (set! ##sys#unbound-variable-value-hook (vector val)))
+
+(define (clear-unbound-variable-value!) (set! ##sys#unbound-variable-value-hook #f))
+
+; this should be the current value procedure
+(define (unbound-variable-value . val)
+  (set! ##sys#unbound-variable-value-hook 
+    (and (pair? val)
+	 (vector (car val)))) )
+
+
 ;;; Access computed globals:
 
 (define (global-ref sym)
diff --git a/manual/Unit library b/manual/Unit library
index bf1638eb..e6c2ad40 100644
--- a/manual/Unit library	
+++ b/manual/Unit library	
@@ -420,6 +420,14 @@ context as the {{condition-case}} form.
 </enscript>
 
 
+==== breakpoint
+
+<procedure>(breakpoint [NAME])</procedure>
+
+Programmatically triggers a breakpoint (similar to the {{,br}} top-level csi
+command).
+
+
 ==== get-condition-property
 
 <procedure>(get-condition-property CONDITION KIND PROPERTY [DEFAULT])</procedure>
@@ -481,6 +489,10 @@ Signaled on errors raised by failed matches (see the section on {{match}}).
 
 Signaled on syntax errors.
 
+</td></tr><tr><td> (exn breakpoint)
+
+Signaled when a breakpoint is reached.
+
 </td></tr>
 
 </table>
diff --git a/manual/Unit lolevel b/manual/Unit lolevel
index d145c033..ff57013d 100644
--- a/manual/Unit lolevel	
+++ b/manual/Unit lolevel	
@@ -434,6 +434,14 @@ These procedures operate with what are known as {{vector-like objects}}. A
 Note that strings and blobs are not considered vector-like.
 
 
+==== vector-like?
+
+ [procedure] (vector-like? X)
+
+Returns {{#t}} when {{X}} is a vector-like object, returns {{#f}}
+otherwise.
+
+
 ==== block-ref
 
  [procedure] (block-ref VECTOR* INDEX)
@@ -695,6 +703,47 @@ Returns a new vector with the type and the elements of the record structure
 {{RECORD}}. Signals an error if {{RECORD}} is not a record structure.
 
 
+
+=== Procedure-call- and variable reference hooks
+
+
+==== set-invalid-procedure-call-handler!
+
+ [procedure] (set-invalid-procedure-call-handler! PROC)
+
+Sets an internal hook that is invoked when a call to an object other than a
+procedure is executed at runtime. The procedure {{PROC}} will in that case be
+called with two arguments: the object being called and a list of the passed
+arguments.
+
+<enscript highlight=scheme>
+;;; Access sequence-elements as in ARC:
+
+(set-invalid-procedure-call-handler!
+  (lambda (proc args)
+    (cond [(string? proc) (apply string-ref proc args)]
+          [(vector? proc) (apply vector-ref proc args)]
+          [else (error "call of non-procedure" proc)] ) ) )
+
+("hello" 4)    ==>  #\o
+</enscript>
+
+This facility does not work in code compiled with the ''unsafe'' setting.
+
+
+==== unbound-variable-value
+
+ [procedure] (unbound-variable-value [X])
+
+Defines the value that is returned for unbound variables. Normally an error is
+signalled, use this procedure to override the check and return {{X}} instead.
+To set the default behavior (of signalling an error), call
+{{unbound-variable-value}} with no arguments.
+
+This facility does not work in code compiled with the ''unsafe'' setting.
+
+
+
 === Magic
 
 
diff --git a/manual/Using the interpreter b/manual/Using the interpreter
index 701a9c06..e9f878c3 100644
--- a/manual/Using the interpreter	
+++ b/manual/Using the interpreter	
@@ -172,7 +172,19 @@ k
 
 ; ,utr SYMBOL ... : Disables tracing of the given toplevel procedures.
 
-; ,info : Lists traced procedures.
+; ,br SYMBOL ... : Sets a breakpoint at the procedures named {{SYMBOL ...}}. Breakpoint can also be trigged using the {{breakpoint}} procedure.
+
+; ,ubr SYMBOL ... : Removes breakpoints.
+
+; ,c : Continues execution from the last invoked breakpoint.
+
+; ,breakall : Enable breakpoints for all threads (this is the default).
+
+; ,breakonly THREAD : Enable breakpoints only for the thread returned by the expression {{THREAD}}.
+
+; ,info : Lists traced procedures and breakpoints.
+
+; ,step EXPR : Evaluates {{EXPR}} in single-stepping mode. On each procedure call you will be presented with a menu that allows stepping to the next call, leaving single-stepping mode or triggering a breakpoint. Note that you will see some internal calls, and unsafe or heavily optimized compiled code might not be stepped at all. Single-stepping mode is also possible by invoking the {{singlestep}} procedure.
 
 You can define your own toplevel commands using the {{toplevel-command}}
 procedure:
diff --git a/runtime.c b/runtime.c
index a67db235..3e8d5a43 100644
--- a/runtime.c
+++ b/runtime.c
@@ -334,6 +334,7 @@ C_TLS void *C_restart_address;
 C_TLS int C_entry_point_status;
 C_TLS int (*C_gc_mutation_hook)(C_word *slot, C_word val);
 C_TLS void (*C_gc_trace_hook)(C_word *var, int mode);
+C_TLS C_word(*C_get_unbound_variable_value_hook)(C_word sym);
 C_TLS void (*C_panic_hook)(C_char *msg) = NULL;
 C_TLS void (*C_pre_gc_hook)(int mode) = NULL;
 C_TLS void (*C_post_gc_hook)(int mode, long ms) = NULL;
@@ -404,6 +405,12 @@ static C_TLS C_word
   interrupt_hook_symbol,
   current_thread_symbol,
   error_hook_symbol,
+  invalid_procedure_call_hook_symbol,
+  unbound_variable_value_hook_symbol,
+  last_invalid_procedure_symbol,
+  identity_unbound_value_symbol,
+  apply_hook_symbol,
+  last_applied_procedure_symbol,
   pending_finalizers_symbol,
   callback_continuation_stack_symbol,
   *forwarding_table;
@@ -489,6 +496,7 @@ static void C_fcall remark_system_globals(void) C_regparm;
 static void C_fcall remark(C_word *x) C_regparm;
 static C_word C_fcall intern0(C_char *name) C_regparm;
 static void C_fcall update_locative_table(int mode) C_regparm;
+static C_word get_unbound_variable_value(C_word sym);
 static LF_LIST *find_module_handle(C_char *name);
 
 static C_ccall void call_cc_wrapper(C_word c, C_word closure, C_word k, C_word result) C_noret;
@@ -625,6 +633,7 @@ int CHICKEN_initialize(int heap, int stack, int symbols, void *toplevel)
   mutation_stack_limit = mutation_stack_bottom + DEFAULT_MUTATION_STACK_SIZE;
   C_gc_mutation_hook = NULL;
   C_gc_trace_hook = NULL;
+  C_get_unbound_variable_value_hook = get_unbound_variable_value;
 
   /* Allocate weak item table: */
   if(C_enable_gcweak) {
@@ -972,7 +981,13 @@ void initialize_symbol_table(void)
   error_hook_symbol = C_intern2(C_heaptop, C_text("\003syserror-hook"));
   callback_continuation_stack_symbol = C_intern3(C_heaptop, C_text("\003syscallback-continuation-stack"), C_SCHEME_END_OF_LIST);
   pending_finalizers_symbol = C_intern2(C_heaptop, C_text("\003syspending-finalizers"));
+  invalid_procedure_call_hook_symbol = C_intern3(C_heaptop, C_text("\003sysinvalid-procedure-call-hook"), C_SCHEME_FALSE);
+  unbound_variable_value_hook_symbol = C_intern3(C_heaptop, C_text("\003sysunbound-variable-value-hook"), C_SCHEME_FALSE);
+  last_invalid_procedure_symbol = C_intern3(C_heaptop, C_text("\003syslast-invalid-procedure"), C_SCHEME_FALSE);
+  identity_unbound_value_symbol = C_intern3(C_heaptop, C_text("\003sysidentity-unbound-value"), C_SCHEME_FALSE);
   current_thread_symbol = C_intern3(C_heaptop, C_text("\003syscurrent-thread"), C_SCHEME_FALSE);
+  apply_hook_symbol = C_intern3(C_heaptop, C_text("\003sysapply-hook"), C_SCHEME_FALSE);
+  last_applied_procedure_symbol = C_intern2(C_heaptop, C_text("\003syslast-applied-procedure"));
 }
 
 
@@ -2958,7 +2973,13 @@ C_regparm void C_fcall mark_system_globals(void)
   mark(&error_hook_symbol);
   mark(&callback_continuation_stack_symbol);
   mark(&pending_finalizers_symbol);
+  mark(&invalid_procedure_call_hook_symbol);
+  mark(&unbound_variable_value_hook_symbol);
+  mark(&last_invalid_procedure_symbol);
+  mark(&identity_unbound_value_symbol);
   mark(&current_thread_symbol);
+  mark(&apply_hook_symbol);
+  mark(&last_applied_procedure_symbol);
 }
 
 
@@ -3263,7 +3284,13 @@ C_regparm void C_fcall remark_system_globals(void)
   remark(&error_hook_symbol);
   remark(&callback_continuation_stack_symbol);
   remark(&pending_finalizers_symbol);
+  remark(&invalid_procedure_call_hook_symbol);
+  remark(&unbound_variable_value_hook_symbol);
+  remark(&last_invalid_procedure_symbol);
+  remark(&identity_unbound_value_symbol);
   remark(&current_thread_symbol);
+  remark(&apply_hook_symbol);
+  remark(&last_applied_procedure_symbol);
 }
 
 
@@ -3537,12 +3564,24 @@ C_regparm C_word C_fcall C_retrieve(C_word sym)
   C_word val = C_block_item(sym, 0);
 
   if(val == C_SCHEME_UNBOUND)
-    barf(C_UNBOUND_VARIABLE_ERROR, NULL, sym);
+    return C_get_unbound_variable_value_hook(sym);
 
   return val;
 }
 
 
+C_word get_unbound_variable_value(C_word sym)
+{
+  C_word x = C_block_item(unbound_variable_value_hook_symbol, 0);
+
+  if(x == identity_unbound_value_symbol) return sym;
+  else if(x == C_SCHEME_FALSE)
+    barf(C_UNBOUND_VARIABLE_ERROR, NULL, sym);
+
+  return C_block_item(x, 0);
+}
+
+
 C_regparm C_word C_fcall C_retrieve2(C_word val, char *name)
 {
   C_word *p;
@@ -3553,7 +3592,7 @@ C_regparm C_word C_fcall C_retrieve2(C_word val, char *name)
     /* this is ok: we won't return from `C_retrieve2'
      * (or the value isn't needed). */
     p = C_alloc(C_SIZEOF_STRING(len));
-    barf(C_UNBOUND_VARIABLE_ERROR, NULL, C_string2(&p, name));
+    return get_unbound_variable_value(C_string2(&p, name));
   }
 
   return val;
@@ -3566,7 +3605,13 @@ static C_word resolve_procedure(C_word closure, C_char *where)
   C_word s;
 
   if(C_immediatep(closure) || C_header_bits(closure) != C_CLOSURE_TYPE) {
-    barf(C_NOT_A_CLOSURE_ERROR, where, closure);
+    s = C_block_item(invalid_procedure_call_hook_symbol, 0);
+
+    if(s == C_SCHEME_FALSE)
+      barf(C_NOT_A_CLOSURE_ERROR, where, closure);
+
+    C_mutate(&C_block_item(last_invalid_procedure_symbol, 0), closure);
+    closure = s;
   }
 
   return closure;
@@ -3577,6 +3622,14 @@ static C_word resolve_procedure(C_word closure, C_char *where)
 C_regparm void *C_fcall C_retrieve_proc(C_word closure)
 {
   closure = resolve_procedure(closure, NULL);
+
+#ifndef C_NO_APPLY_HOOK
+  if(C_block_item(apply_hook_symbol, 0) != C_SCHEME_FALSE) {
+    C_mutate(&C_block_item(last_applied_procedure_symbol, 0), closure);
+    return (void *)C_block_item(C_block_item(apply_hook_symbol, 0), 0);
+  }
+#endif
+
   return (void *)C_block_item(closure, 0);
 }
 
@@ -3587,9 +3640,17 @@ C_regparm void *C_fcall C_retrieve_symbol_proc(C_word sym)
   C_word closure;
 
   if(val == C_SCHEME_UNBOUND)
-    barf(C_UNBOUND_VARIABLE_ERROR, NULL, sym);
+    val = C_get_unbound_variable_value_hook(sym);
 
   closure = resolve_procedure(val, NULL);
+
+#ifndef C_NO_APPLY_HOOK
+  if(C_block_item(apply_hook_symbol, 0) != C_SCHEME_FALSE) {
+    C_mutate(&C_block_item(last_applied_procedure_symbol, 0), closure);
+    return (void *)C_block_item(C_block_item(apply_hook_symbol, 0), 0);
+  }
+#endif
+
   return (void *)C_block_item(closure, 0);
 }
 
@@ -3604,10 +3665,18 @@ C_regparm void *C_fcall C_retrieve2_symbol_proc(C_word val, char *name)
     len = C_strlen(name);
     /* this is ok: we won't return from `C_retrieve2' (or the value isn't needed). */
     p = C_alloc(C_SIZEOF_STRING(len));
-    barf(C_UNBOUND_VARIABLE_ERROR, NULL, C_string2(&p, name));
+    val = get_unbound_variable_value(C_string2(&p, name));
   }
 
   closure = resolve_procedure(val, NULL);
+
+#ifndef C_NO_APPLY_HOOK
+  if(C_block_item(apply_hook_symbol, 0) != C_SCHEME_FALSE) {
+    C_mutate(&C_block_item(last_applied_procedure_symbol, 0), closure);
+    return (void *)C_block_item(C_block_item(apply_hook_symbol, 0), 0);
+  }
+#endif
+
   return (void *)C_block_item(closure, 0);
 }
 
@@ -4191,8 +4260,11 @@ C_regparm C_word C_fcall C_fudge(C_word fudge_factor)
 #endif
 
   case C_fix(35):
-    /* used to be apply-hook indicator */
+#ifndef C_NO_APPLY_HOOK
+    return C_SCHEME_TRUE;
+#else
     return C_SCHEME_FALSE;
+#endif
     
   case C_fix(36):
     debug_mode = !debug_mode;
diff --git a/tests/lolevel-tests.scm b/tests/lolevel-tests.scm
index 94040a5c..96d93574 100644
--- a/tests/lolevel-tests.scm
+++ b/tests/lolevel-tests.scm
@@ -265,3 +265,25 @@
 (assert (not (eq? foo new-foo)))
 
 (assert (equal? '(hello 1 2) (foo 1 2)))
+
+; set-invalid-procedure-call-handler!
+
+(set-invalid-procedure-call-handler!
+ (lambda (proc args)
+   (cond [(string? proc) (apply string-ref proc args)]
+         [(vector? proc) (apply vector-ref proc args)]
+         [else
+          (error 'lolevel-test:invalid-procedure-call-handler
+                 "bad argument type - not a procedure" proc args)])))
+
+(assert (char=? #\b ("abc" 1)))
+
+(assert (char=? #\b ('#(#\a #\b #\c) 1)))
+
+; unbound-variable-value
+
+(unbound-variable-value '23skidoo)
+
+(assert (eq? '23skidoo skidoo))
+
+(unbound-variable-value)
Trap