|
27 | 27 | ;; blame parties (e.g., a particular module). |
28 | 28 |
|
29 | 29 | (require racket/class |
30 | | - (only-in racket/private/class-c-old |
31 | | - base-object/c? build-object/c-type-name object/c-width-subtype? |
32 | | - object/c-common-methods-stronger? object/c-common-fields-stronger?) |
33 | 30 | racket/match |
34 | 31 | racket/contract/base |
35 | 32 | racket/contract/combinator |
|
39 | 36 |
|
40 | 37 | (provide object/c-opaque) |
41 | 38 |
|
42 | | -(module+ for-testing |
43 | | - (provide restrict-typed->/c |
44 | | - restrict-typed-field/c)) |
45 | | - |
46 | | -;; projection for base-object/c-opaque |
47 | | -(define ((object/c-opaque-late-neg-proj ctc) blame) |
48 | | - (match-define (base-object/c-opaque |
49 | | - base-ctc |
50 | | - methods method-ctcs |
51 | | - fields field-ctcs) |
52 | | - ctc) |
53 | | - (define guard/c (dynamic-object/c methods method-ctcs fields field-ctcs)) |
54 | | - (define guard/c-proj ((contract-late-neg-projection guard/c) blame)) |
55 | | - (λ (obj neg-party) |
56 | | - (unless (object? obj) |
57 | | - (raise-blame-error blame #:missing-party neg-party obj "expected an object got ~a" obj)) |
58 | | - (define actual-fields (field-names obj)) |
59 | | - (define actual-methods |
60 | | - (interface->method-names (object-interface obj))) |
61 | | - (define remaining-fields |
62 | | - (remove* fields actual-fields)) |
63 | | - (define remaining-methods |
64 | | - (remove* methods actual-methods)) |
65 | | - (cond |
66 | | - [(and (null? remaining-methods) (null? remaining-fields)) |
67 | | - (guard/c-proj obj neg-party)] |
68 | | - [else |
69 | | - (define restrict-guard/c |
70 | | - (dynamic-object/c remaining-methods |
71 | | - (for/list ([m (in-list remaining-methods)]) |
72 | | - (restrict-typed->/c m)) |
73 | | - remaining-fields |
74 | | - (for/list ([m (in-list remaining-fields)]) |
75 | | - (restrict-typed-field/c m)))) |
76 | | - ;; FIXME: this is a bit sketchy because we have to construct |
77 | | - ;; a contract that depends on the actual object that we got |
78 | | - ;; since we don't know its methods beforehand |
79 | | - (((contract-late-neg-projection restrict-guard/c) blame) |
80 | | - (guard/c-proj obj neg-party) |
81 | | - neg-party)]))) |
82 | | - |
83 | | -(define (object/c-opaque-name ctc) |
84 | | - (build-object/c-type-name 'object/c-opaque |
85 | | - (base-object/c-opaque-method-names ctc) |
86 | | - (base-object/c-opaque-method-ctcs ctc) |
87 | | - (base-object/c-opaque-field-names ctc) |
88 | | - (base-object/c-opaque-field-ctcs ctc))) |
89 | | - |
90 | | -;; Similar to object/c-stronger, but without width subtyping. |
91 | | -;; (Intuition: unspecified fields are guarded by the strongest possible contract) |
92 | | -;; An opaque object contract `this` is stronger than `that` when: |
93 | | -;; - `that` is an opaque contract |
94 | | -;; and `this` specifies at most the same members as `that` |
95 | | -;; and `this` has stronger contracts on all members |
96 | | -;; - `that` is an object/c contract |
97 | | -;; and `this` has stronger contracts on their common members |
98 | | -(define (object/c-opaque-stronger? this that) |
99 | | - (define that-opaque? (base-object/c-opaque? that)) |
100 | | - (cond |
101 | | - [(or that-opaque? |
102 | | - (base-object/c? that)) |
103 | | - (define this-ctc (base-object/c-opaque-obj/c this)) |
104 | | - (define that-ctc (if that-opaque? (base-object/c-opaque-obj/c that) that)) |
105 | | - (and |
106 | | - (if that-opaque? |
107 | | - ;; then members of `this` should be a SUBSET of members of `that` |
108 | | - (object/c-width-subtype? that-ctc this-ctc) |
109 | | - #t) |
110 | | - (object/c-common-fields-stronger? this-ctc that-ctc) |
111 | | - (object/c-common-methods-stronger? this-ctc that-ctc) |
112 | | - #t)] |
113 | | - [else #f])) |
114 | | - |
115 | | -;; An `object/c-opaque` contract is equivalent to another `object/c-opaque` |
116 | | -;; contract that has the same fields+methods and the same contracts on them. |
117 | | -(define (object/c-opaque-equivalent? this that) |
118 | | - (and (base-object/c-opaque? that) |
119 | | - (contract-equivalent? (base-object/c-opaque-obj/c this) |
120 | | - (base-object/c-opaque-obj/c that)))) |
121 | | - |
122 | | -(struct base-object/c-opaque |
123 | | - (obj/c ; keep a copy of the normal object/c for first-order and stronger checks |
124 | | - method-names method-ctcs field-names field-ctcs) |
125 | | - #:property prop:contract |
126 | | - (build-contract-property |
127 | | - #:stronger object/c-opaque-stronger? |
128 | | - #:equivalent object/c-opaque-equivalent? |
129 | | - #:name object/c-opaque-name |
130 | | - #:first-order (λ (ctc) |
131 | | - (define obj/c (base-object/c-opaque-obj/c ctc)) |
132 | | - (λ (val) |
133 | | - (contract-first-order-passes? obj/c val))) |
134 | | - #:late-neg-projection object/c-opaque-late-neg-proj)) |
135 | | - |
136 | 39 | (begin-for-syntax |
137 | 40 | (define-syntax-class object/c-clause |
138 | 41 | #:attributes (method-names method-ctcs field-names field-ctcs) |
|
151 | 54 | (syntax-parse stx |
152 | 55 | [(_ ?clause:object/c-clause ...) |
153 | 56 | (syntax/loc stx |
154 | | - (let ([names (append ?clause.method-names ...)] |
155 | | - [ctcs (append ?clause.method-ctcs ...)] |
156 | | - [fnames (append ?clause.field-names ...)] |
157 | | - [fctcs (append ?clause.field-ctcs ...)]) |
158 | | - (base-object/c-opaque |
159 | | - (dynamic-object/c names ctcs fnames fctcs) |
160 | | - names ctcs fnames fctcs)))])) |
161 | | - |
162 | | -;; This contract combinator prevents the method call if the target |
163 | | -;; method is typed (assuming that the caller is untyped or the receiving |
164 | | -;; object went through untyped code) |
165 | | -(define (((restrict-typed->-late-neg-projection ctc) blame) val neg-party) |
166 | | - (cond |
167 | | - [(typed-method? val) |
168 | | - (chaperone-procedure val |
169 | | - (make-keyword-procedure |
170 | | - (λ (_ kw-args . rst) |
171 | | - (raise-blame-error (blame-swap blame) val #:missing-party neg-party |
172 | | - "cannot call uncontracted typed method")) |
173 | | - (λ args |
174 | | - (raise-blame-error (blame-swap blame) val #:missing-party neg-party |
175 | | - "cannot call uncontracted typed method"))))] |
176 | | - [else val])) |
177 | | - |
178 | | -;; Returns original method name |
179 | | -(define (restrict-typed->-name ctc) |
180 | | - (define name (restrict-typed->/c-name ctc)) |
181 | | - (build-compound-type-name 'restrict-typed->/c name)) |
182 | | - |
183 | | -(define (restrict-typed->/c-equivalent? this that) |
184 | | - (and (restrict-typed->/c? that) |
185 | | - (eq? (restrict-typed->/c-name this) |
186 | | - (restrict-typed->/c-name that)))) |
187 | | - |
188 | | -(struct restrict-typed->/c (name) |
189 | | - #:property prop:chaperone-contract |
190 | | - (build-chaperone-contract-property |
191 | | - #:name restrict-typed->-name |
192 | | - #:stronger restrict-typed->/c-equivalent? |
193 | | - #:equivalent restrict-typed->/c-equivalent? |
194 | | - #:late-neg-projection restrict-typed->-late-neg-projection)) |
195 | | - |
196 | | -(define (restrict-typed-field-late-neg-proj ctc) |
197 | | - (define name (restrict-typed-field/c-name ctc)) |
198 | | - (λ (*blame) |
199 | | - (define blame |
200 | | - ;; Blame has been swapped if this is for a set-field!, in which case |
201 | | - ;; the blame matches the original negative party. Otherwise we want |
202 | | - ;; to swap to blame negative. |
203 | | - (if (blame-swapped? *blame) |
204 | | - *blame |
205 | | - (blame-swap *blame))) |
206 | | - (λ (val neg-party) |
207 | | - (raise-blame-error |
208 | | - blame val #:missing-party neg-party |
209 | | - "cannot read or write field hidden by Typed Racket")))) |
210 | | - |
211 | | -(define (restrict-typed-field-name ctc) |
212 | | - (define name (restrict-typed-field/c-name ctc)) |
213 | | - (build-compound-type-name 'restrict-typed-field/c name)) |
214 | | - |
215 | | -(define (restrict-typed-field-equivalent? this that) |
216 | | - (and (restrict-typed-field/c? that) |
217 | | - (equal? (restrict-typed-field/c-name this) |
218 | | - (restrict-typed-field/c-name that)))) |
219 | | - |
220 | | -(struct restrict-typed-field/c (name) |
221 | | - #:property prop:flat-contract |
222 | | - (build-flat-contract-property |
223 | | - #:name restrict-typed-field-name |
224 | | - #:stronger restrict-typed-field-equivalent? |
225 | | - #:equivalent restrict-typed-field-equivalent? |
226 | | - #:late-neg-projection restrict-typed-field-late-neg-proj)) |
| 57 | + (object/c ?clause ... |
| 58 | + #:opaque-except typed-method? |
| 59 | + #:opaque-fields #t |
| 60 | + #:do-not-check-class-field-accessor-or-mutator-access))])) |
0 commit comments