First cut of converting object-contract to share a common base that

object/c will also use.

svn: r18274
This commit is contained in:
Stevie Strickland 2010-02-22 21:55:32 +00:00
parent 815dd80923
commit d820493feb
3 changed files with 170 additions and 258 deletions

View File

@ -13,27 +13,6 @@
implementation?/c
object-contract)
;; example of how one contract is constructed
#;
(let* ([cm (syntax-parameterize ((making-a-method #t)) (-> any/c integer? integer?))]
[cf (-> integer? integer?)]
[m-proj ((contract-projection cm)
(make-blame #'here #f "whatever" 'pos 'neg #t))]
[f-proj ((contract-projection cf)
(make-blame #'here #f "whatever" 'pos 'neg #t))]
[cls (make-wrapper-class 'wrapper-class
'(m)
(list
(m-proj (λ (this x) (send (wrapper-object-wrapped this) m x))))
'(f)
#f)]
[o (new (class object%
(field [f (λ (x) x)])
(define/public (m x) x)
(super-new)))]
[wo (make-object cls o (f-proj (get-field/proc 'f o)))])
((get-field/proc 'f wo) #f))
(define-for-syntax (parse-object-contract stx args)
(let loop ([args (syntax->list args)]
[mtds '()]
@ -52,55 +31,17 @@
[_
(raise-syntax-error #f "malformed object-contract clause" stx (car args))])])))
(define (o-c-first-order ctc val blame meth-projs)
(let/ec return
(define (failed str . args)
(if blame
(apply raise-blame-error blame val str args)
(return #f)))
(unless (object? val)
(failed "expected an object, got ~e" val))
(let ([meth-names (object-contract-methods ctc)])
(for-each (λ (m proj)
(let-values ([(method unwrapper)
(find-method/who 'object-contract val m #:error? #f)])
(unless method
(failed "expected an object with method ~s" m))
;; verify the first-order properties by apply the projection and
;; throwing the result away. Without this, the contract wrappers
;; just check the first-order properties of the wrappers, which is
;; the wrong thing.
(proj method)))
meth-names
meth-projs))
(let ([ctc-field-names (object-contract-fields ctc)]
[fields (field-names val)])
(for-each (λ (f)
(unless (memq f fields)
(failed "expected an object with field ~s" f)))
ctc-field-names))
#t))
(define-struct object-contract (methods method-ctcs method-wrappers fields field-ctcs)
(define-struct object-contract (methods method-ctcs fields field-ctcs)
#:omit-define-syntaxes
#:property prop:contract
(build-contract-property
#:projection
(λ (ctc)
(let ([meth-names (object-contract-methods ctc)]
[meth-param-projs (map contract-projection (object-contract-method-ctcs ctc))]
[ctc-field-names (object-contract-fields ctc)]
[field-param-projs (map contract-projection (object-contract-field-ctcs ctc))])
(λ (blame)
(let* ([meth-projs (map (λ (x) (x blame)) meth-param-projs)]
[meths (map (λ (p x) (p x)) meth-projs (object-contract-method-wrappers ctc))]
[cls (make-wrapper-class 'wrapper-class meth-names meths ctc-field-names #f)]
[field-projs (map (λ (x) (x blame)) field-param-projs)])
(λ (val)
(o-c-first-order ctc val blame meth-projs)
(apply make-object cls val
(map (λ (field proj) (proj (get-field/proc field val)))
ctc-field-names field-projs)))))))
(λ (blame)
(λ (val)
(make-wrapper-object val blame
(object-contract-methods ctc) (object-contract-method-ctcs ctc)
(object-contract-fields ctc) (object-contract-field-ctcs ctc)))))
#:name
(λ (ctc) `(object-contract ,@(map (λ (fld ctc) (build-compound-type-name 'field fld ctc))
(object-contract-fields ctc)
@ -112,7 +53,7 @@
#:first-order
(λ (ctc)
(λ (val)
(o-c-first-order ctc val #f (map (λ (x) values) (object-contract-method-ctcs ctc)))))))
(check-object-contract val #f (object-contract-methods ctc) (object-contract-fields ctc))))))
(define-syntax (object-contract stx)
(syntax-case stx ()
@ -124,14 +65,12 @@
(syntax->list #'(method-id ...)))])
#'(build-object-contract '(method-id ...)
(syntax-parameterize ((making-a-method #t)) (list (let ([method-name method-ctc]) method-name) ...))
(list (λ (this . x) (send (wrapper-object-wrapped this) method-id . x)) ...)
'(field-id ...)
(list field-ctc ...))))]))
(define (build-object-contract methods method-ctcs wrappers fields field-ctcs)
(define (build-object-contract methods method-ctcs fields field-ctcs)
(make-object-contract methods
(map (λ (x) (coerce-contract 'object-contract x)) method-ctcs)
wrappers
fields
(map (λ (x) (coerce-contract 'object-contract x)) field-ctcs)))

View File

@ -2994,7 +2994,7 @@
augments augment-ctcs
augrides augride-ctcs))))]))
(define (object/c-check-first-order ctc obj blame)
(define (check-object-contract obj blame methods fields)
(let/ec return
(define (failed str . args)
(if blame
@ -3004,19 +3004,24 @@
(failed "not a object"))
(let ([cls (object-ref obj)])
(let ([method-ht (class-method-ht cls)])
(for ([m (object/c-methods ctc)])
(for ([m methods])
(unless (hash-ref method-ht m #f)
(failed "no public method ~a" m))))
(let ([field-ht (class-field-ht cls)])
(for ([m (object/c-fields ctc)])
(for ([m fields])
(unless (hash-ref field-ht m #f)
(failed "no public field ~a" m)))))))
(define (object/c-check-first-order ctc obj blame)
(check-object-contract obj blame (object/c-methods) (object/c-fields)))
(define (object/c-proj ctc)
(λ (blame)
(λ (obj)
(object/c-check-first-order ctc obj blame)
obj)))
(make-wrapper-object obj blame
(object/c-methods ctc) (object/c-method-contracts ctc)
(object/c-fields ctc) (object/c-field-contracts ctc)))))
(define-struct object/c (methods method-contracts fields field-contracts)
#:omit-define-syntaxes
@ -3835,8 +3840,6 @@
(cond
[index
((vector-ref (class-ext-field-sets cls) index) obj val)]
[(wrapper-object? obj)
(loop (wrapper-object-wrapped obj))]
[else
(raise-mismatch-error
'get-field
@ -3876,8 +3879,6 @@
(cond
[index
((vector-ref (class-ext-field-refs cls) index) obj)]
[(wrapper-object? obj)
(loop (wrapper-object-wrapped obj))]
[else
(raise-mismatch-error
'get-field
@ -3913,10 +3914,8 @@
(let loop ([obj obj])
(let* ([cls (object-ref obj)]
[field-ht (class-field-ht cls)])
(or (and (hash-ref field-ht id #f)
#t) ;; ensure that only #t and #f leak out, not bindings in ht
(and (wrapper-object? obj)
(loop (wrapper-object-wrapped obj))))))))
(and (hash-ref field-ht id #f)
#t))))) ;; ensure that only #t and #f leak out, not bindings in ht
(define-traced (field-names obj)
(unless (object? obj)
@ -3930,9 +3929,7 @@
(let* ([cls (object-ref obj)]
[field-ht (class-field-ht cls)]
[flds (filter interned? (hash-map field-ht (lambda (x y) x)))])
(if (wrapper-object? obj)
(append flds (loop (wrapper-object-wrapped obj)))
flds)))))
flds))))
(define-syntaxes (with-method with-method-traced)
(let ()
@ -4059,7 +4056,6 @@
(cond
[pos (procedure-arity-includes? (vector-ref (class-methods c) pos)
(add1 cnt))]
[(wrapper-object? o) (loop (wrapper-object-wrapped o))]
[else #f])))))
(define (implementation? v i)
@ -4262,15 +4258,12 @@
;; wrapper for contracts
;;--------------------------------------------------------------------
(define-struct wrapper-field (name ctc-stx))
(define-struct wrapper-method (name mth-stx))
(define-values (wrapper-object? wrapper-object-wrapped set-wrapper-object-wrapped! struct:wrapper-object)
(let-values ([(struct:wrapper-object make-wrapper-object wrapper-object? ref set!)
(make-struct-type 'raw-wrapper-object
#f
0
1)])
1
0)])
(values wrapper-object?
(lambda (v) (ref v 0))
(lambda (o v) (set! o 0 v))
@ -4283,161 +4276,137 @@
(loop (wrapper-object-wrapped o))
o)))
;; make-wrapper-class : symbol
;; (listof symbol)
;; method-spec [depends on the boolean what it is]
;; (listof symbol)
;; boolean
;; -> class
;; the resulting class is the "proxy" class for the contracted version of an
;; object with contracts on the method-ids.
;; Overall, objects of this class have one field for the original object,
;; one field per method in the contract and one field per field in the contract.
;; Each of the methods (passed in) just accesses the initial (method) fields
;; (which contain procedures) and calls them and returns their results.
;; Those fields do not show up from outside of this file, via the usual
;; field accessors. In addition, the class has one field per field that
;; will contain the contracted versions of the input fields.
;; The class accepts one initialization argument per method and
;; one init arg per field (in that order) using the make-object style
;; initialization.
(define (make-wrapper-class class-name method-ids methods field-ids old-style?)
(let* ([supers (vector object% #f)]
[method-ht (make-hasheq)]
[method-count (length method-ids)]
[methods-vec (make-vector method-count #f)]
[int-methods-vec (make-vector method-count)]
[dynamic-idxs (make-vector method-count 0)]
[dynamic-projs (make-vector method-count (vector values))]
[field-ht (make-hasheq)]
[field-count (length field-ids)]
[int-field-refs (make-vector field-count)]
[int-field-sets (make-vector field-count)]
[ext-field-refs (make-vector field-count)]
[ext-field-sets (make-vector field-count)]
[cls
(make-class class-name
1
supers
'bogus-self-interface
void ; nothing can be inspected
method-count
method-ht
(reverse method-ids)
methods-vec
methods-vec
int-methods-vec
(list->vector (map (lambda (x) 'final) method-ids))
'dont-use-me!
(make-vector method-count values)
dynamic-idxs
dynamic-projs
(if old-style?
(+ field-count method-count 1)
field-count)
field-count
field-ht
field-ids
int-field-refs
int-field-sets
ext-field-refs
ext-field-sets
#f; struct:object
#f; object?
#f; make-object ;; -> void
#f; field-ref
#f; field-set!
#f ;; only by position arguments
'normal ; init-mode - ??
#f ; init
#f ; orig-cls
#f #f ; not serializable
#f)])
(let-values ([(struct:object make-object object? field-ref field-set!)
(make-struct-type 'wrapper-object
(define (make-wrapper-class obj cls blame methods method-contracts fields field-contracts)
(let* ([name (class-name cls)]
[method-width (class-method-width cls)]
[method-ht (class-method-ht cls)]
[methods (if (null? methods)
(class-methods cls)
(make-vector method-width))]
[field-pub-width (class-field-pub-width cls)]
[field-ht (class-field-ht cls)]
[int-field-refs (make-vector field-pub-width)]
[int-field-sets (make-vector field-pub-width)]
[ext-field-refs (make-vector field-pub-width)]
[ext-field-sets (make-vector field-pub-width)]
[class-make (if name
(make-naming-constructor
struct:class
(string->symbol (format "class:~a" name)))
make-class)]
[c (class-make name
(class-pos cls)
(list->vector (vector->list (class-supers cls)))
(class-self-interface cls)
void ;; No inspecting
method-width
method-ht
(class-method-ids cls)
methods
(class-super-methods cls)
(class-int-methods cls)
(class-beta-methods cls)
(class-meth-flags cls)
(class-inner-projs cls)
(class-dynamic-idxs cls)
(class-dynamic-projs cls)
(class-field-width cls)
field-pub-width
field-ht
(class-field-ids cls)
int-field-refs
int-field-sets
ext-field-refs
ext-field-sets
'struct:object 'object? 'make-object
'field-ref 'field-set!
(class-init-args cls)
(class-init-mode cls)
(class-init cls)
(class-orig-cls cls)
#f #f ; serializer is never set
#f)]
[obj-name (if name
(string->symbol (format "wrapper-object:~a" name))
'object)])
(vector-set! (class-supers c) (class-pos c) c)
;; --- Make the new object struct ---
(let-values ([(struct:object object-make object? object-field-ref object-field-set!)
(make-struct-type obj-name
struct:wrapper-object
0
(if old-style?
(+ (length field-ids) (length method-ids))
(length field-ids))
0 ;; No init fields
0 ;; No new fields in this wrapped object
undefined
(list (cons prop:object cls))
insp)])
(set-class-struct:object! cls struct:object)
(set-class-object?! cls object?)
(set-class-make-object! cls make-object)
(set-class-field-ref! cls field-ref)
(set-class-field-set!! cls field-set!)
(set-class-orig-cls! cls cls)
(let ([init
(lambda (o continue-make-super c inited? named-args leftover-args)
;; leftover args will contain the original object and new field values
;; fill the original object in and then fill in the fields.
(set-wrapper-object-wrapped! o (car leftover-args))
(let loop ([leftover-args (cdr leftover-args)]
[i 0])
(unless (null? leftover-args)
(field-set! o i (car leftover-args))
(loop (cdr leftover-args)
(+ i 1))))
(continue-make-super o c inited? '() '() '()))])
(set-class-init! cls init))
;; fill in the methods vector & methods-ht
(let loop ([i 0]
[methods methods]
[method-ids method-ids])
(when (< i method-count)
(vector-set! methods-vec i (if old-style?
((car methods) field-ref)
(car methods)))
(vector-set! int-methods-vec i
(vector (if old-style?
((car methods) field-ref)
(car methods))))
(hash-set! method-ht (car method-ids) i)
(loop (+ i 1)
(cdr methods)
(cdr method-ids))))
;; fill in the fields-ht
(let loop ([i 0]
[field-ids field-ids])
(when (< i field-count)
(hash-set! field-ht (car field-ids) i)
(vector-set! int-field-refs i
(make-struct-field-accessor field-ref i #f))
(vector-set! int-field-sets i
(make-struct-field-mutator field-set! i #f))
(vector-set! ext-field-refs i
(make-struct-field-accessor field-ref i (car field-ids)))
(vector-set! ext-field-sets i
(make-struct-field-mutator field-set! i (car field-ids)))
(loop (+ i 1)
(cdr field-ids))))
;; fill in the supers vector
(vector-set! supers 1 cls)
cls)))
;; Map object property to class:
(list (cons prop:object c)))])
(set-class-struct:object! c struct:object)
(set-class-object?! c object?)
(set-class-make-object! c object-make)
(set-class-field-ref! c object-field-ref)
(set-class-field-set!! c object-field-set!))
;; Handle public method contracts
(unless (null? methods)
;; First, fill in from old methods
(vector-copy! methods 0 (class-methods cls))
;; Now apply projections
(for ([m (in-list methods)]
[c (in-list method-contracts)])
(when c
(let ([i (hash-ref method-ht m)]
[p ((contract-projection c) blame)])
(vector-set! methods i (p (vector-ref methods i)))))))
;; Redirect internal/external field accessors/mutators
(let ([old-int-refs (class-int-field-refs cls)]
[old-int-sets (class-int-field-sets cls)]
[old-ext-refs (class-ext-field-refs cls)]
[old-ext-sets (class-ext-field-sets cls)])
(for ([i (in-range field-pub-width)])
(let ([old-int-ref (vector-ref old-int-refs i)]
[old-int-set (vector-ref old-int-sets i)]
[old-ext-ref (vector-ref old-ext-refs i)]
[old-ext-set (vector-ref old-ext-sets i)])
;; Take in the object, then completely ignore it.
(vector-set! int-field-refs i (λ (o) (old-int-ref obj)))
(vector-set! int-field-sets i (λ (o) (old-int-set obj)))
(vector-set! ext-field-refs i (λ (o) (old-ext-ref obj)))
(vector-set! ext-field-sets i (λ (o) (old-ext-set obj))))))
;; Handle external field contracts
(unless (null? fields)
(let ([bset (blame-swap blame)])
(for ([f (in-list fields)]
[c (in-list field-contracts)])
(when c
(let* ([i (hash-ref field-ht f)]
[pre-p (contract-projection c)]
[old-ref (vector-ref ext-field-refs i)]
[old-set (vector-ref ext-field-sets i)])
(vector-set! ext-field-refs i
(λ (o)
((pre-p blame) (old-ref o))))
(vector-set! ext-field-sets i
(λ (o v)
(old-set o ((pre-p bset) v)))))))))
c))
; extract-vtable : object -> (vectorof method-proc[this args ... -> res])
(define (extract-vtable o) (class-methods (object-ref o)))
; extract-method-ht : object -> hash-table[sym -> number]
(define (extract-method-ht o) (class-method-ht (object-ref o)))
;; make-wrapper-object: object (listof symbol) (listof contract?) (listof symbol) (listof contract?)
(define (make-wrapper-object obj blame methods method-contracts fields field-contracts)
(check-object-contract obj blame methods fields)
(let ([new-cls (make-wrapper-class obj (object-ref obj) blame methods method-contracts fields field-contracts)])
((class-make-object new-cls) obj)))
;;--------------------------------------------------------------------
;; misc utils
@ -4626,10 +4595,8 @@
)
;; Providing normal functionality:
(provide (protect-out make-wrapper-class
wrapper-object-wrapped
extract-vtable
extract-method-ht
(provide (protect-out make-wrapper-object
check-object-contract
get-field/proc)
(rename-out [_class class]) class* class/derived

View File

@ -3043,10 +3043,12 @@
(test/pos-blame
'object-contract/field2
'(contract (object-contract (field x integer?))
(new (class object% (field [x #t]) (super-new)))
'pos
'neg))
'(get-field
x
(contract (object-contract (field x integer?))
(new (class object% (field [x #t]) (super-new)))
'pos
'neg)))
(test/spec-passed/result
'object-contract/field3
@ -3060,17 +3062,21 @@
(test/pos-blame
'object-contract/field4
'(contract (object-contract (field x boolean?) (field y boolean?))
(new (class object% (field [x #t] [y 'x]) (super-new)))
'pos
'neg))
'(get-field
y
(contract (object-contract (field x boolean?) (field y boolean?))
(new (class object% (field [x #t] [y 'x]) (super-new)))
'pos
'neg)))
(test/pos-blame
'object-contract/field5
'(contract (object-contract (field x symbol?) (field y symbol?))
(new (class object% (field [x #t] [y 'x]) (super-new)))
'pos
'neg))
'(get-field
x
(contract (object-contract (field x symbol?) (field y symbol?))
(new (class object% (field [x #t] [y 'x]) (super-new)))
'pos
'neg)))
(test/spec-passed/result
'object-contract/field6