racket / racket/draw

Bugs caused by internally constructed objects

Open
#51 17 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Racket
Stars
19
Forks
24
PR merge metrics
No merged PRs in 30d

Description

Some time ago I did a deep dive into the racket/draw contracts. Here is an observation that led me to some bugs: class contracts do not provide a guarantee on objects that are created from within the contract boundary.

Example 1

The numbers given to in-region? are expected to be real?. If you give imaginary numbers

(define reg (new region%))
(send reg in-region? 0+i 0+i)

the correct error from the region% class contract is raised:

; in-region?: contract violation
;   expected: real?
;   given: 0+1i
;   in: the 1st argument of
;       the in-region? method in
;       region%/c

However, regions can be created indirectly via the clipping region

(define r-dc (new record-dc%))
(send r-dc set-clipping-rect 0 0 550 400)
(define reg (send r-dc get-clipping-region))
(send reg in-region? 0+i 0+i)

and such will give an internal error since the region was not created through the protected region% class:

; cairo_in_fill: given value does not fit primitive C type
;   C type: _double*
;   value: 0.0+1.0i

Example 2

The fifth argument to get-argb-pixels is expected to be bytes?

(define bmp (make-bitmap 550 400))
(send bmp get-argb-pixels 0 0 0 0 #f)

and gives an error as such

; get-argb-pixels: contract violation
;   expected: bytes?
;   given: #f
;   in: an and/c case of
;       the 5th argument of
;       the get-argb-pixels method in
;       the range of ...

But bitmaps can be created through the make-bitmap method of canvases

(define frame (new frame% [label "Example"]))
(define canvas (new canvas% [parent frame]))
(define bmp (send canvas make-bitmap 550 400))
(send bmp get-argb-pixels 0 0 0 0 #f)

and in such a case an internal error is raised because it's unprotected

; bytes-length: contract violation
;   expected: bytes?
;   given: #f

Example 3

The blink-caret method of snips expects a dc<%>

(define sn (new snip%))
(send sn blink-caret #f #f #f)

and the contract gives this error

; blink-caret: contract violation
;   expected: (is-a?/c dc<%>)
;   given: #f
;   in: the 1st argument of
;       the blink-caret method in
;       the 2nd conjunct of ...

However, cloning the snip constructs a new object that is not protected

(define sn (new snip%))
(define sn2 (send sn copy))
(send sn2 blink-caret #f #f #f)

and gives this different error

; blink-caret method of snip%: expected argument of type <dc<%> instance>; given: #f; other arguments: #f #f

which doesn't come from a contract check, but is a (usually redundant) defensive check that is present in the blink-caret method.

Summary

These are just a few instances I found in racket/draw—there are probably quite a few more. I'm not sure what the takeaway is. My thought is that using (is-a?/c my-class%) and then subsequently assuming that objects satisfy the class contract on my-class% is dangerous since it assumes that my-class% objects are only constructed through the protected constructor. Using object/c contracts everywhere instead of is-a?/c isn't a panacea either because, as far as I know, they will not get collapsed and wrappers might quickly accumulate. So while these bugs that I mentioned can be fixed individually, I feel as though there is a deeper concern here with respect to class contracts in general.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Review the racket/draw class contracts and the entry points in the examples: region%, record-dc%, canvas%, bitmap, and snip%/copy. Reproduce the three cases first; done would require a decided, consistent contract behavior for internally constructed objects, but the issue does not specify the intended design.

Written by the indexing model from the issue text.

Assessment

Domain
backend-api-design
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.