struct-impersonator: make method-style prop:procedure receive impersonated
When a struct is called as a procedure and the struct is impersonators, make a method-style `prop:procedure` receive the impersonated structure as its argument. This change makes a method-style `prop:procedure` more consistent with a field-index `prop:procedure. The old behavior, meanwhile, seems to create an unsoundness in Typed Racket. Closes #2574
This commit is contained in:
parent
30426fff9d
commit
486ab09587
|
@ -3582,6 +3582,83 @@
|
|||
(test #t has-impersonator-prop:contracted? group-rows*)
|
||||
(test 1 'apply (group-rows* #:group 10)))
|
||||
|
||||
;; ----------------------------------------
|
||||
;; More checks on the interaction of procedure and struct impersonators
|
||||
|
||||
(let ()
|
||||
(struct foo ([val #:mutable])
|
||||
#:property prop:procedure
|
||||
(λ (self)
|
||||
(log-error "getting")
|
||||
(foo-val self)))
|
||||
|
||||
(define orig-foo (foo 'original))
|
||||
|
||||
(define the-foo
|
||||
(impersonate-struct
|
||||
orig-foo
|
||||
foo-val
|
||||
(λ (self val) 'impersonated)
|
||||
set-foo-val!
|
||||
(λ (self val) (error "cannot set!"))))
|
||||
|
||||
(test 'impersonated foo-val the-foo)
|
||||
(test 'impersonated the-foo))
|
||||
|
||||
(let ()
|
||||
(define chaperoned 0)
|
||||
|
||||
(struct bar (proc)
|
||||
#:property prop:procedure 0)
|
||||
|
||||
(define orig-bar (bar (lambda () 'ok)))
|
||||
|
||||
(define the-bar
|
||||
(chaperone-struct
|
||||
orig-bar
|
||||
bar-proc
|
||||
(lambda (self val) (set! chaperoned (add1 chaperoned)) val)))
|
||||
|
||||
(test 0 values chaperoned)
|
||||
(test 'ok the-bar)
|
||||
(test 1 values chaperoned)
|
||||
|
||||
(define proc-bar
|
||||
(chaperone-procedure orig-bar (lambda () (values))))
|
||||
|
||||
(define another-bar
|
||||
(chaperone-struct
|
||||
proc-bar
|
||||
bar-proc
|
||||
(lambda (self val) (set! chaperoned (add1 chaperoned)) val)))
|
||||
|
||||
(test 1 values chaperoned)
|
||||
(test 'ok another-bar)
|
||||
(test 2 values chaperoned)
|
||||
|
||||
(define-values (prop:tagged tagged? tagged-ref) (make-impersonator-property 'tagged))
|
||||
|
||||
(define was-tagged? #f)
|
||||
(define proc*-bar
|
||||
(chaperone-procedure* orig-bar (lambda (orig)
|
||||
(set! was-tagged? (tagged? orig))
|
||||
(values))))
|
||||
|
||||
(define struct*-bar
|
||||
(chaperone-struct
|
||||
proc-bar
|
||||
bar-proc
|
||||
(lambda (self val) (set! chaperoned (add1 chaperoned)) val)))
|
||||
|
||||
(define the*-bar
|
||||
(chaperone-procedure proc*-bar
|
||||
(lambda () (values))
|
||||
prop:tagged #t))
|
||||
|
||||
(test #f values was-tagged?)
|
||||
(test 'ok the*-bar)
|
||||
(test #t values was-tagged?))
|
||||
|
||||
;; ----------------------------------------
|
||||
;; Check that position-consuming accessor and mutators work with
|
||||
;; `impersonate-struct`.
|
||||
|
|
|
@ -2560,14 +2560,13 @@ scheme_do_eval(Scheme_Object *obj, int num_rands, Scheme_Object **rands,
|
|||
|
||||
UPDATE_THREAD_RSPTR_FOR_ERROR(); /* in case */
|
||||
|
||||
v = obj;
|
||||
obj = scheme_extract_struct_procedure(orig_obj, check_rands, rands, &is_method);
|
||||
if (is_method) {
|
||||
/* Have to add an extra argument to the front of rands */
|
||||
if ((rands == RUNSTACK) && (RUNSTACK != RUNSTACK_START)){
|
||||
/* Common case: we can just push self onto the front: */
|
||||
rands = PUSH_RUNSTACK(p, RUNSTACK, 1);
|
||||
rands[0] = v;
|
||||
rands[0] = orig_obj;
|
||||
} else {
|
||||
int i;
|
||||
Scheme_Object **a;
|
||||
|
@ -2585,7 +2584,7 @@ scheme_do_eval(Scheme_Object *obj, int num_rands, Scheme_Object **rands,
|
|||
for (i = num_rands; i--; ) {
|
||||
a[i + 1] = rands[i];
|
||||
}
|
||||
a[0] = v;
|
||||
a[0] = orig_obj;
|
||||
rands = a;
|
||||
}
|
||||
num_rands++;
|
||||
|
|
|
@ -661,7 +661,7 @@
|
|||
|
||||
(define (set-impersonator-applicables!)
|
||||
(let ([add (lambda (rtd)
|
||||
(struct-property-set! prop:procedure rtd impersonate-apply)
|
||||
(struct-property-set! prop:procedure rtd 'impersonate-apply)
|
||||
(struct-property-set! prop:procedure-arity rtd 3))])
|
||||
(add (record-type-descriptor props-procedure-impersonator))
|
||||
(add (record-type-descriptor props-procedure-chaperone))
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
(let dloop ([p (car args)] [v (cadr args)])
|
||||
(cond
|
||||
[(impersonator? p)
|
||||
(dloop (impersonator-val p) (impersonate-apply/parameter p #f (list v)))]
|
||||
(dloop (impersonator-val p) (impersonate-apply/parameter p p #f (list v)))]
|
||||
[(derived-parameter? p)
|
||||
(dloop (derived-parameter-next p) (|#%app| (parameter-guard p) v))]
|
||||
[else
|
||||
|
|
|
@ -93,12 +93,12 @@
|
|||
(#3%$app/no-inline slow-extract-procedure tmp n-args))))
|
||||
|
||||
(define (slow-extract-procedure f n-args)
|
||||
(do-extract-procedure f f n-args #f not-a-procedure))
|
||||
(do-extract-procedure f f f n-args #f not-a-procedure))
|
||||
|
||||
;; Returns a host-Scheme procedure, but first checks arity so that
|
||||
;; checking and reporting use the right top-level function, and
|
||||
;; the returned procedure may just report a not-a-procedure error
|
||||
(define (do-extract-procedure f orig-f n-args success-k fail-k)
|
||||
(define (do-extract-procedure f self-f orig-f n-args success-k fail-k)
|
||||
(cond
|
||||
[(#%procedure? f)
|
||||
(if (or (not n-args)
|
||||
|
@ -117,44 +117,56 @@
|
|||
(let* ([rtd (record-rtd f)]
|
||||
[v (struct-property-ref prop:procedure rtd none)])
|
||||
(cond
|
||||
[(eq? v none) (fail-k orig-f)]
|
||||
[(fixnum? v)
|
||||
(let ([a (struct-property-ref prop:procedure-arity rtd #f)])
|
||||
(cond
|
||||
[(and a n-args (not (bitwise-bit-set? (unsafe-struct*-ref f a) n-args)))
|
||||
(wrong-arity-wrapper orig-f)]
|
||||
[else
|
||||
(do-extract-procedure (unsafe-struct-ref f v) orig-f n-args success-k wrong-arity-wrapper)]))]
|
||||
[(eq? v 'unsafe)
|
||||
(do-extract-procedure
|
||||
(if (chaperone? f)
|
||||
(unsafe-procedure-chaperone-replace-proc f)
|
||||
(unsafe-procedure-impersonator-replace-proc f))
|
||||
orig-f
|
||||
n-args
|
||||
success-k
|
||||
wrong-arity-wrapper)]
|
||||
[else
|
||||
(let ([a (struct-property-ref prop:procedure-arity rtd #f)])
|
||||
(cond
|
||||
[(and a n-args (not (bitwise-bit-set? (unsafe-struct*-ref f a) n-args)))
|
||||
(wrong-arity-wrapper orig-f)]
|
||||
[else
|
||||
(do-extract-procedure
|
||||
v
|
||||
orig-f
|
||||
(and n-args (fx+ n-args 1))
|
||||
(lambda (v)
|
||||
(let ([proc (case-lambda
|
||||
[() (v f)]
|
||||
[(a) (v f a)]
|
||||
[(a b) (v f a b)]
|
||||
[(a b c) (v f a b c)]
|
||||
[args (chez:apply v f args)])])
|
||||
(if success-k
|
||||
(success-k proc)
|
||||
proc)))
|
||||
wrong-arity-wrapper)]))]))]
|
||||
[(eq? v none) (fail-k orig-f)]
|
||||
[(fixnum? v)
|
||||
(let ([a (struct-property-ref prop:procedure-arity rtd #f)])
|
||||
(cond
|
||||
[(and a n-args (not (bitwise-bit-set? (unsafe-struct*-ref f a) n-args)))
|
||||
(wrong-arity-wrapper orig-f)]
|
||||
[else
|
||||
(let ([new-f (unsafe-struct-ref self-f v)])
|
||||
(do-extract-procedure new-f new-f orig-f n-args success-k wrong-arity-wrapper))]))]
|
||||
[(eq? v 'unsafe)
|
||||
(let ([new-f (if (chaperone? f)
|
||||
(unsafe-procedure-chaperone-replace-proc f)
|
||||
(unsafe-procedure-impersonator-replace-proc f))])
|
||||
(do-extract-procedure
|
||||
new-f
|
||||
new-f
|
||||
orig-f
|
||||
n-args
|
||||
success-k
|
||||
wrong-arity-wrapper))]
|
||||
[(eq? v 'struct-impersonate-apply)
|
||||
(do-extract-procedure (impersonator-next f) self-f orig-f n-args success-k fail-k)]
|
||||
[else
|
||||
(let ([a (struct-property-ref prop:procedure-arity rtd #f)])
|
||||
(cond
|
||||
[(and a n-args (not (bitwise-bit-set? (unsafe-struct*-ref f a) n-args)))
|
||||
(wrong-arity-wrapper orig-f)]
|
||||
[(eq? v 'impersonate-apply)
|
||||
(let ([proc (lambda args
|
||||
(impersonate-apply/parameter f self-f #t args))])
|
||||
(if success-k
|
||||
(success-k proc)
|
||||
proc))]
|
||||
[else
|
||||
(do-extract-procedure
|
||||
v
|
||||
v
|
||||
orig-f
|
||||
(and n-args (fx+ n-args 1))
|
||||
(lambda (v)
|
||||
(let ([proc (case-lambda
|
||||
[() (v self-f)]
|
||||
[(a) (v self-f a)]
|
||||
[(a b) (v self-f a b)]
|
||||
[(a b c) (v self-f a b c)]
|
||||
[args (chez:apply v self-f args)])])
|
||||
(if success-k
|
||||
(success-k proc)
|
||||
proc)))
|
||||
wrong-arity-wrapper)]))]))]
|
||||
[else (fail-k orig-f)]))
|
||||
|
||||
(define (extract-procedure-name f)
|
||||
|
@ -680,21 +692,18 @@
|
|||
[else
|
||||
(k #f #f #f)])))
|
||||
|
||||
(define (impersonate-apply proc . args)
|
||||
(impersonate-apply/parameter proc #t args))
|
||||
|
||||
;; If `actually-call?` is #f, then don't call the procedure in `proc`,
|
||||
;; because we're trying to get an inpersonated-parameter value
|
||||
(define (impersonate-apply/parameter proc actually-call? args)
|
||||
(define (impersonate-apply/parameter p self-p actually-call? args)
|
||||
(let ([n (length args)])
|
||||
(cond
|
||||
[(not (procedure-arity-includes? (impersonator-val proc) n))
|
||||
[(not (procedure-arity-includes? (impersonator-val p) n))
|
||||
;; Let primitive application complain:
|
||||
(|#%app| (impersonator-val proc) args)]
|
||||
(|#%app| (impersonator-val p) args)]
|
||||
[else
|
||||
;; Loop through wrappers so that `{chaperone,impersonate}-procedure*`
|
||||
;; wrappers can receive the original `proc` argument
|
||||
(let loop ([p proc] [args args])
|
||||
(let loop ([p p] [args args])
|
||||
(cond
|
||||
[(or (procedure-impersonator? p)
|
||||
(procedure-chaperone? p))
|
||||
|
@ -723,7 +732,7 @@
|
|||
(if (if chaperone?
|
||||
(procedure*-chaperone? p)
|
||||
(procedure*-impersonator? p))
|
||||
(apply wrapper proc args)
|
||||
(apply wrapper self-p args)
|
||||
(apply wrapper args)))])
|
||||
;; Set mark, if any, while calling:
|
||||
(cond
|
||||
|
@ -783,7 +792,7 @@
|
|||
[(fx> nn n)
|
||||
(raise-wrapper-bad-extra-result-error chaperone? pos (car new-args) next-p wrapper)]
|
||||
[else
|
||||
(raise-wrapper-result-arity-error chaperone? proc wrapper n nn)])))))]
|
||||
(raise-wrapper-result-arity-error chaperone? self-p wrapper n nn)])))))]
|
||||
[(unsafe-procedure-impersonator? p)
|
||||
(apply p args)]
|
||||
[(unsafe-procedure-chaperone? p)
|
||||
|
@ -795,15 +804,16 @@
|
|||
[else
|
||||
;; If `p` is a structure whose `prop:procedure` value is an
|
||||
;; integer `i`, then we should extract the field at position
|
||||
;; `i` from `proc`, not from `p`, so that any interpositions
|
||||
;; `i` from `self-p`, not from `p`, so that any interpositions
|
||||
;; on that access are performed.
|
||||
(let ([v (and (record? p)
|
||||
(struct-property-ref prop:procedure (record-rtd p) #f))])
|
||||
(cond
|
||||
[(integer? v)
|
||||
(apply (unsafe-struct-ref proc v) args)]
|
||||
(apply (unsafe-struct-ref self-p v) args)]
|
||||
[else
|
||||
(apply p args)]))]))])))
|
||||
(#%apply (do-extract-procedure p self-p self-p (length args) #f not-a-procedure)
|
||||
args)]))]))])))
|
||||
|
||||
(define (set-procedure-impersonator-hash!)
|
||||
(record-type-hash-procedure (record-type-descriptor procedure-chaperone)
|
||||
|
@ -1061,22 +1071,24 @@
|
|||
#t)
|
||||
|
||||
(let ([register-procedure-impersonator-struct-type!
|
||||
(lambda (rtd)
|
||||
(struct-property-set! prop:procedure rtd impersonate-apply))])
|
||||
(lambda (rtd struct?)
|
||||
(struct-property-set! prop:procedure rtd (if struct?
|
||||
'struct-impersonate-apply
|
||||
'impersonate-apply)))])
|
||||
(let ([register-procedure-impersonator-struct-type!
|
||||
(lambda (rtd)
|
||||
(register-procedure-impersonator-struct-type! rtd)
|
||||
(lambda (rtd struct?)
|
||||
(register-procedure-impersonator-struct-type! rtd struct?)
|
||||
(struct-property-set! prop:procedure-arity rtd 4))])
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure-chaperone))
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure-impersonator))
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure*-chaperone))
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure*-impersonator))
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure-struct-chaperone))
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure-struct-impersonator))
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure~-struct-chaperone))
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure~-struct-impersonator)))
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure-struct-undefined-chaperone))
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure~-struct-undefined-chaperone)))
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure-chaperone) #f)
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure-impersonator) #f)
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure*-chaperone) #f)
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure*-impersonator) #f)
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure-struct-chaperone) #t)
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure-struct-impersonator) #t)
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure~-struct-chaperone) #t)
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure~-struct-impersonator) #t))
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure-struct-undefined-chaperone) #t)
|
||||
(register-procedure-impersonator-struct-type! (record-type-descriptor procedure~-struct-undefined-chaperone) #t))
|
||||
|
||||
(let ([register-procedure-incomplete-arity!
|
||||
(lambda (rtd)
|
||||
|
|
Loading…
Reference in New Issue
Block a user