Next: , Previous: , Up: objects   [Index]


13.24 Closure objects

Closure objects are actual procedures: closure objects are the ones for which the procedure? predicate returns #t. A closure object is a fixed length memory block referenced by machine words tagged as closures; each closure object is associated to either a code object that implements the procedure or a routine directly coded in assembly. The memory layout of a closure object is as follows:

|------------------------|-------------| reference to closure
      heap pointer         closure tag

                       0   1   2   3   4   5
|--------------------|---|---|---|---|---|---| memory block
  raw memory pointer    one slot for every
  to binary code        free variable

the first word in the memory block holds a raw memory pointer referencing the first byte in the code object implementing the closure; the subsequent words (if any) are slots associated to the free variables referenced by the closure’s code.

Inspecting a closure’s free variables

We can take a look at the free variables referenced by a closure with the facilities of the library (vicare system $codes). In a normal application: we must not mess with the internals of closure and code objects.

We start by noticing that in the following example: the function f has no “true” free variables, because it accesses only global variables:

#!r6rs
(import (rnrs)
  (vicare system $codes))
(define a 123)
(define (f)
  a)
($code-freevars ($closure-code f))      ⇒ 0

In the following example: the function f is the only one referencing the free variable a, so such variable is stored directly in the closure’s slot:

#!r6rs
(import (rnrs)
  (vicare system $codes))
(define f
  (let ((a 123))
    (lambda () a)))
($code-freevars ($closure-code f))      ⇒ 1
($cpref f 0)                            ⇒ 123

when there are two such variables:

#!r6rs
(import (vicare)
  (vicare system $codes))

(define f
  (let ((a 123)
        (b 456))
    (lambda ()
      (list a b))))
($code-freevars ($closure-code f))      ⇒ 2
($cpref f 0)                            ⇒ 456
($cpref f 1)                            ⇒ 123

When more than one closure references the same free variable, the storage of the variable is inside a Scheme vector and such vector is referenced by the slots of the closures:

#!r6rs
(import (rnrs)
  (vicare system $codes))
(define f #f)
(define g #f)
(let ((a 123))
  (set! f (lambda () a))
  (set! g (lambda (x)
            (set! a x)
            a)))
($code-freevars ($closure-code f))      ⇒ 1
($cpref f 0)                            ⇒ #(123)
($code-freevars ($closure-code g))      ⇒ 1
($cpref g 0)                            ⇒ #(123)

Basic operations

Closure objects are allocated on the heap; to perform the allocation and initialisation for a closure object without free variables we do:

ikptr_t   p_closure;
ikptr_t   s_closure;
ikptr_t   s_code = ...;

p_closure = ik_safe_alloc(pcb, IK_ALIGN(disp_closure_data));
s_closure = p_closure | closure_tag;
IK_REF(s_closure, off_closure_code) = s_code + off_code_data;

ik_safe_alloc() returns an ikptr_t value representing the aligned pointer, having the 3 least significant bits set to zero; we add to it the closure tag (an integer value fitting in 3 bits) which allows to recognise closures among all the other built in objects.

Preprocessor Symbol: closure_tag

An integer used to tag ikptr_t references to closure memory blocks.

Preprocessor Symbol: closure_mask

An integer representing the bitmask used to extract (with a bitwise logic AND) the tag from ikptr_t references to closure objects.

Preprocessor Symbol: disp_closure_code

Displacement of code pointer. The number of bytes to add to an untagged pointer to closure to get a pointer to the word in the memory block holding the binary code entry point.

Preprocessor Symbol: disp_closure_data

Displacement of free variables. The number of bytes to add to an untagged pointer to closure to get a pointer to the first word in the data area of the memory block.

Preprocessor Symbol: off_closure_code

An integer to add to a tagged ikptr_t closure reference to get a pointer to the word in the memory block holding the binary code entry point.

Preprocessor Symbol: off_closure_data

An integer to add to a tagged ikptr_t closure reference to get a pointer to the first word in the data area of the memory block.

Convenience preprocessor macros

Preprocessor Macro: int IK_IS_CLOSURE (ikptr_t X)

Evaluate to true if X is a reference to closure object.

Preprocessor Macro: ikptr_t IK_CLOSURE_ENTRY_POINT (X)

Given a reference to closure object: return the address of the binary code entry point.

Preprocessor Macro: ikptr_t IK_CLOSURE_CODE_OBJECT (X)

Given a reference to closure object: return a reference to the associated code object. Only works for closures associated to code objects.

Preprocessor Macro: int IK_CLOSURE_NUMBER_OF_FREE_VARS (X)

Given a reference to closure object: return the number of free variables.

Preprocessor Macro: ikptr_t IK_CLOSURE_FREE_VAR (X, idx)

Given a reference to closure object: return a reference to value of the free variable at index idx in the data area. idx must be less than the number of free variables in this closure object.


Next: , Previous: , Up: objects   [Index]