Post by Per BothnerThis could be fixed if we implemented an optimization which sets the 'button'
variable before we set its fields. This has a big advantage in that it enables
building self-referential data structures more easily, as well as data structures that
mutually reference each other. It would have the side-effect of fixing this mini-bug.
I've thought into doing the above optimization, but I don't remember how far I got.
(There are lot of old corners in Kawa.)
Anyway, I'm looking into it.
It looks like more work than I want to spend right now, but I came up with
an approach that I think makes sense. I wrote it up in the "Ideas and tasks for
contributing to Kawa" section (in the git version of the manual but not yet online):
3.6.1 Recusively initialized data structures
--------------------------------------------
(GSoC)
Kawa has convenient syntax to *note allocate and initialize objects:
Allocating objects, but it gets messier it you want to initialize
multiple objects that reference each other. Likewise for a single
object “tree” which contains links to the root. In this example, we
will looks at two vectors, but the feature is more useful for tree
structures. Assume:
(define-constant list1 [1 2 list2])
(define-constant list2 ['a 'b list1])
The compiler translates this to:
(define-constant list1
(let ((t (object[] length: 3))) ;; allocate native Java array
(set! (t 0) 1)
(set! (t 1) 2)
(set! (t 2) list2)
(FVector:makeConstant t)))
(define-constant list2
(let ((t (object[] length: 3))) ;; allocate native Java array
(set! (t 0) 'a)
(set! (t 1) 'b)
(set! (t 2) list1)
(FVector:makeConstant t)))
The problem is that ‘list2’ has not been created when we evaluate the
initializing expression for ‘list’.
We can solve the problem by re-writing:
(define-private tmp1 (object[] length: 3))
(define-constant list1 (FVector:makeConstant tmp1)
(define-private tmp2 (object[] length: 3))
(define-constant list2 (FVector:makeConstant tmp2)
(set! (tmp1 0) 1)
(set! (tmp1 1) 2)
(set! (tmp1 2) list2)
(set! (tmp2 0) 1)
(set! (tmp2 1) 2)
(set! (tmp2 2) list1)
The complication is that the code for re-writing vector and object
constructors is spread out (depending on the result type), and not where
we deal with initializing the variables. One solution is to introduce
an inlineable helper function ‘$build$’ defined as:
(define ($build$ raw-value create init)
(let ((result (create raw-value))
(init raw-value result)
result))
Then we can re-write the above code to:
(define-constant list1
($build$
(object[] length: 3)
(lambda (raw) (FVector:makeConstant raw))
(lambda (raw result)
($init-raw-array$ raw 1 2 list2))))
(define-constant list2
($build$
(object[] length: 3)
(lambda (raw) (FVector:makeConstant raw))
(lambda (raw result)
($init-raw-array$ raw 'a 'b list1))))
Note that the call to ‘$build$’, as well as the generated ‘lambda’
expressions, are all easily inlineable.
Now assume if at the top-level BODY if there is a sequence of
‘define-constant’ definitions initialized with calls to ‘$build$’. Now
it is relatively easy to move all the ‘init’ calls after all ‘alloc’ and
‘create’ expressions. The ‘$init-raw-array$’ calls are expanded after
the code has been re-ordered.
The project includes both implementing the above framework, as well
as updating type-specific (and default) object creation to use the
framework. It would also be good to have compiler warnings if accessing
an uninitialized object.
--
--Per Bothner
***@bothner.com http://per.bothner.com/