Bugs caused by internally constructed objects

Abierto
#51 17 comentarios 1 reacción 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
25/100
Tipo de issue
Error
Claridad
Necesita aclaración
Estado de actividad
Estancado

Línea de trabajo

Revisa los contratos de las clases de racket/draw y los puntos de entrada de los ejemplos: region%, record-dc%, canvas%, bitmap y snip%/copy. Reproduce primero los tres casos; se consideraría terminado cuando exista un comportamiento contractual decidido y coherente para los objetos construidos internamente, pero el issue no especifica el diseño previsto.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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.

Lenguaje dominante
Racket
Estrellas
19
Forks
24
Métricas de merge de PR
Sin PR fusionados en 30 d

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de racket/draw

Todos los issues de racket/draw

Issues similares

Más issues de Backend & API Design

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.