3794 lines
143 KiB
Scheme
3794 lines
143 KiB
Scheme
|
|
(module class-internal scheme/base
|
|
(require (for-syntax scheme/base)
|
|
mzlib/list
|
|
mzlib/etc
|
|
mzlib/stxparam
|
|
"class-events.ss"
|
|
"serialize-structs.ss"
|
|
(for-syntax syntax/kerncase
|
|
syntax/stx
|
|
syntax/name
|
|
syntax/context
|
|
syntax/define
|
|
syntax/private/boundmap
|
|
mzlib/stxparam
|
|
"classidmap.ss"))
|
|
|
|
(define insp (current-inspector)) ; for all opaque structures
|
|
|
|
;;--------------------------------------------------------------------
|
|
;; spec for external interface
|
|
;;--------------------------------------------------------------------
|
|
|
|
(provide provide-public-names
|
|
;; needed for Typed Scheme
|
|
(protect-out do-make-object find-method/who))
|
|
(define-syntax-rule (provide-public-names)
|
|
(provide class class* class/derived
|
|
define-serializable-class define-serializable-class*
|
|
class?
|
|
mixin
|
|
interface interface?
|
|
object% object? externalizable<%> printable<%>
|
|
object=?
|
|
new make-object instantiate
|
|
send send/apply send* class-field-accessor class-field-mutator with-method
|
|
get-field field-bound? field-names
|
|
private* public* pubment*
|
|
override* overment*
|
|
augride* augment*
|
|
public-final* override-final* augment-final*
|
|
define/private define/public define/pubment
|
|
define/override define/overment
|
|
define/augride define/augment
|
|
define/public-final define/override-final define/augment-final
|
|
define-local-member-name define-member-name
|
|
member-name-key generate-member-key
|
|
member-name-key? member-name-key=? member-name-key-hash-code
|
|
generic make-generic send-generic
|
|
is-a? subclass? implementation? interface-extension?
|
|
object-interface object-info object->vector
|
|
object-method-arity-includes?
|
|
method-in-interface? interface->method-names class->interface class-info
|
|
(struct-out exn:fail:object)
|
|
make-primitive-class
|
|
|
|
;; "keywords":
|
|
private public override augment
|
|
pubment overment augride
|
|
public-final override-final augment-final
|
|
field init init-field init-rest
|
|
rename-super rename-inner inherit inherit/super inherit/inner inherit-field
|
|
this super inner
|
|
super-make-object super-instantiate super-new
|
|
inspect))
|
|
|
|
|
|
;;--------------------------------------------------------------------
|
|
;; keyword setup
|
|
;;--------------------------------------------------------------------
|
|
|
|
(define-for-syntax (do-class-keyword stx)
|
|
(if (identifier? stx)
|
|
(raise-syntax-error
|
|
#f
|
|
"illegal (unparenthesized) use of a class keyword"
|
|
stx)
|
|
(raise-syntax-error
|
|
#f
|
|
"use of a class keyword is not in a class top-level"
|
|
stx)))
|
|
|
|
(define-syntax provide-class-keyword
|
|
(syntax-rules ()
|
|
[(_ id ...)
|
|
(begin
|
|
(define-syntax (id stx) (do-class-keyword stx))
|
|
...
|
|
(provide id ...))]))
|
|
|
|
(provide-class-keyword private public override augride
|
|
pubment overment augment
|
|
public-final override-final augment-final
|
|
rename-super rename-inner inherit inherit-field
|
|
inherit/super inherit/inner
|
|
inspect
|
|
init-rest)
|
|
|
|
(define-for-syntax (do-define-like-internal stx)
|
|
(syntax-case stx ()
|
|
[(_ orig . __)
|
|
(raise-syntax-error
|
|
#f
|
|
"use of a class keyword is not in a class top-level"
|
|
#'orig)]))
|
|
|
|
(define-for-syntax (do-define-like stx internal-id)
|
|
(syntax-case stx ()
|
|
[(_ elem ...)
|
|
(syntax-property
|
|
#`(#,internal-id #,stx
|
|
#,@(map (lambda (e)
|
|
(if (identifier? e)
|
|
e
|
|
(syntax-property
|
|
(syntax-case e ()
|
|
[((n1 n2) . expr)
|
|
(quasisyntax/loc e
|
|
(#,(syntax-property
|
|
#'(n1 n2)
|
|
'certify-mode 'transparent)
|
|
. expr))]
|
|
[_else e])
|
|
'certify-mode 'transparent)))
|
|
(syntax-e #'(elem ...))))
|
|
'certify-mode
|
|
'transparent)]
|
|
[(_ . elems)
|
|
#`(#,internal-id #,stx . elems)]
|
|
[_else
|
|
(raise-syntax-error #f "illegal (unparenthesized) use of class keyword" stx)]))
|
|
|
|
(define-syntax provide-class-define-like-keyword
|
|
(syntax-rules ()
|
|
[(_ [internal-id id] ...)
|
|
(begin
|
|
(define-syntax (internal-id stx) (do-define-like-internal stx))
|
|
...
|
|
(define-syntax (id stx) (do-define-like stx #'internal-id))
|
|
...
|
|
(provide id ...))]))
|
|
|
|
(provide-class-define-like-keyword
|
|
[-field field]
|
|
[-init init]
|
|
[-init-field init-field])
|
|
|
|
|
|
(define-for-syntax not-in-a-class
|
|
(lambda (stx)
|
|
(raise-syntax-error
|
|
#f
|
|
"use of a class keyword is not in a class"
|
|
stx)))
|
|
|
|
(define-syntax define/provide-context-keyword
|
|
(syntax-rules ()
|
|
[(_ (id param-id) ...)
|
|
(begin
|
|
(begin
|
|
(provide id)
|
|
(define-syntax-parameter param-id
|
|
(make-set!-transformer not-in-a-class))
|
|
(define-syntax id
|
|
(make-parameter-rename-transformer #'param-id)))
|
|
...)]))
|
|
|
|
(define/provide-context-keyword
|
|
[this this-param]
|
|
[super super-param]
|
|
[inner inner-param]
|
|
[super-make-object super-make-object-param]
|
|
[super-instantiate super-instantiate-param]
|
|
[super-new super-new-param])
|
|
|
|
;;--------------------------------------------------------------------
|
|
;; local member name lookup
|
|
;;--------------------------------------------------------------------
|
|
|
|
(define-for-syntax (localize orig-id)
|
|
(do-localize orig-id #'validate-local-member))
|
|
|
|
(define (validate-local-member orig s)
|
|
(if (symbol? s)
|
|
s
|
|
(error 'local-member-name
|
|
"used before its definition: ~a"
|
|
orig)))
|
|
|
|
;;--------------------------------------------------------------------
|
|
;; class macros
|
|
;;--------------------------------------------------------------------
|
|
|
|
(define-syntaxes (class* _class class/derived
|
|
class*-traced class-traced class/derived-traced)
|
|
(let ()
|
|
;; Start with Helper functions
|
|
|
|
(define (expand-all-forms stx defn-and-exprs def-ctx bind-local-id)
|
|
(let* ([stop-forms
|
|
(append
|
|
(kernel-form-identifier-list)
|
|
(list
|
|
(quote-syntax #%app) ; scheme/base app, as opposed to #%plain-app
|
|
(quote-syntax lambda) ; scheme/base lambda, as opposed to #%plain-lambda
|
|
(quote-syntax -init)
|
|
(quote-syntax init-rest)
|
|
(quote-syntax -field)
|
|
(quote-syntax -init-field)
|
|
(quote-syntax inherit-field)
|
|
(quote-syntax private)
|
|
(quote-syntax public)
|
|
(quote-syntax override)
|
|
(quote-syntax augride)
|
|
(quote-syntax public-final)
|
|
(quote-syntax override-final)
|
|
(quote-syntax augment-final)
|
|
(quote-syntax pubment)
|
|
(quote-syntax overment)
|
|
(quote-syntax augment)
|
|
(quote-syntax rename-super)
|
|
(quote-syntax inherit)
|
|
(quote-syntax inherit/super)
|
|
(quote-syntax inherit/inner)
|
|
(quote-syntax rename-inner)
|
|
(quote-syntax super)
|
|
(quote-syntax inner)
|
|
(quote-syntax this)
|
|
(quote-syntax super-instantiate)
|
|
(quote-syntax super-make-object)
|
|
(quote-syntax super-new)
|
|
(quote-syntax inspect)))]
|
|
[expand-context (generate-class-expand-context)]
|
|
[expand
|
|
(lambda (defn-or-expr)
|
|
(local-expand
|
|
defn-or-expr
|
|
expand-context
|
|
stop-forms
|
|
def-ctx))])
|
|
(let loop ([l defn-and-exprs])
|
|
(if (null? l)
|
|
null
|
|
(let ([e (expand (car l))])
|
|
(syntax-case e (begin define-syntaxes define-values)
|
|
[(begin expr ...)
|
|
(loop (append
|
|
(syntax->list (syntax (expr ...)))
|
|
(cdr l)))]
|
|
[(define-syntaxes (id ...) rhs)
|
|
(andmap identifier? (syntax->list #'(id ...)))
|
|
(begin
|
|
(with-syntax ([rhs (local-transformer-expand
|
|
#'rhs
|
|
'expression
|
|
null)])
|
|
(syntax-local-bind-syntaxes (syntax->list #'(id ...)) #'rhs def-ctx)
|
|
(cons #'(define-syntaxes (id ...) rhs) (loop (cdr l)))))]
|
|
[(define-values (id ...) rhs)
|
|
(andmap identifier? (syntax->list #'(id ...)))
|
|
(begin
|
|
(map bind-local-id (syntax->list #'(id ...)))
|
|
(cons e (loop (cdr l))))]
|
|
[(begin . _)
|
|
(raise-syntax-error
|
|
#f
|
|
"ill-formed begin expression"
|
|
e)]
|
|
[_else (cons e (loop (cdr l)))]))))))
|
|
|
|
;; returns two lists: expressions that start with an identifier in
|
|
;; `kws', and expressions that don't
|
|
(define (extract kws l out-cons)
|
|
(let loop ([l l])
|
|
(if (null? l)
|
|
(values null null)
|
|
(let-values ([(in out) (loop (cdr l))])
|
|
(cond
|
|
[(and (stx-pair? (car l))
|
|
(let ([id (stx-car (car l))])
|
|
(and (identifier? id)
|
|
(ormap (lambda (k) (free-identifier=? k id)) kws))))
|
|
(values (cons (car l) in) out)]
|
|
[else
|
|
(values in (out-cons (car l) out))])))))
|
|
|
|
(define (extract* kws l)
|
|
(let-values ([(in out) (extract kws l void)])
|
|
in))
|
|
|
|
(define (flatten alone l)
|
|
(apply append
|
|
(map (lambda (i)
|
|
(let ([l (let ([l (syntax->list i)])
|
|
(if (ormap (lambda (i)
|
|
(free-identifier=? (car l) i))
|
|
(syntax-e (quote-syntax (-init -init-field -field))))
|
|
(cddr l)
|
|
(cdr l)))])
|
|
(if alone
|
|
(map (lambda (i)
|
|
(if (identifier? i)
|
|
(alone i)
|
|
(cons (stx-car i)
|
|
(stx-car (stx-cdr i)))))
|
|
l)
|
|
l)))
|
|
l)))
|
|
|
|
;; Used with flatten:
|
|
(define (pair i) (cons i i))
|
|
|
|
(define (normalize-init/field i)
|
|
;; Put i in ((iid eid) optional-expr) form
|
|
(cond
|
|
[(identifier? i) (list (list i i))]
|
|
[else (let ([a (stx-car i)])
|
|
(if (identifier? a)
|
|
(cons (list a a) (stx-cdr i))
|
|
i))]))
|
|
|
|
(define (norm-init/field-iid norm) (stx-car (stx-car norm)))
|
|
(define (norm-init/field-eid norm) (stx-car (stx-cdr (stx-car norm))))
|
|
|
|
;; expands an expression enough that we can check whether it has
|
|
;; the right form for a method; must use local syntax definitions
|
|
(define (proc-shape name orig-stx xform?
|
|
the-obj the-finder
|
|
bad class-name expand-stop-names
|
|
def-ctx lookup-localize)
|
|
(define (expand expr locals)
|
|
(local-expand
|
|
expr
|
|
'expression
|
|
(append locals expand-stop-names)
|
|
def-ctx))
|
|
;; Checks whether the vars sequence is well-formed
|
|
(define (vars-ok? vars)
|
|
(or (identifier? vars)
|
|
(stx-null? vars)
|
|
(and (stx-pair? vars)
|
|
(identifier? (stx-car vars))
|
|
(vars-ok? (stx-cdr vars)))))
|
|
(define (kw-vars-ok? vars)
|
|
(or (identifier? vars)
|
|
(stx-null? vars)
|
|
(and (stx-pair? vars)
|
|
(let ([a (stx-car vars)]
|
|
[opt-arg-ok?
|
|
(lambda (a)
|
|
(or (identifier? a)
|
|
(and (stx-pair? a)
|
|
(identifier? (stx-car a))
|
|
(stx-pair? (stx-cdr a))
|
|
(stx-null? (stx-cdr (stx-cdr a))))))])
|
|
(or (and (opt-arg-ok? a)
|
|
(kw-vars-ok? (stx-cdr vars)))
|
|
(and (keyword? (syntax-e a))
|
|
(stx-pair? (stx-cdr vars))
|
|
(opt-arg-ok? (stx-car (stx-cdr vars)))
|
|
(kw-vars-ok? (stx-cdr (stx-cdr vars)))))))))
|
|
;; mk-name: constructs a method name
|
|
;; for error reporting, etc.
|
|
(define (mk-name name)
|
|
(datum->syntax
|
|
#f
|
|
(string->symbol (format "~a method~a~a"
|
|
(syntax-e name)
|
|
(if class-name
|
|
" in "
|
|
"")
|
|
(or class-name
|
|
"")))
|
|
#f))
|
|
;; -- tranform loop starts here --
|
|
(let loop ([stx orig-stx][can-expand? #t][name name][locals null])
|
|
(syntax-case stx (#%plain-lambda lambda case-lambda letrec-values let-values)
|
|
[(lam vars body1 body ...)
|
|
(or (and (free-identifier=? #'lam #'#%plain-lambda)
|
|
(vars-ok? (syntax vars)))
|
|
(and (free-identifier=? #'lam #'lambda)
|
|
(kw-vars-ok? (syntax vars))))
|
|
(if xform?
|
|
(with-syntax ([the-obj the-obj]
|
|
[the-finder the-finder]
|
|
[name (mk-name name)])
|
|
(with-syntax ([vars (if (free-identifier=? #'lam #'lambda)
|
|
(let loop ([vars #'vars])
|
|
(cond
|
|
[(identifier? vars) vars]
|
|
[(syntax? vars)
|
|
(datum->syntax vars
|
|
(loop (syntax-e vars))
|
|
vars
|
|
vars)]
|
|
[(pair? vars)
|
|
(syntax-case (car vars) ()
|
|
[(id expr)
|
|
(identifier? #'id)
|
|
;; optional argument; need to wrap arg expression
|
|
(cons
|
|
(with-syntax ([expr (syntax/loc #'expr
|
|
(let-syntax ([the-finder (quote-syntax the-obj)])
|
|
(#%expression expr)))])
|
|
(syntax/loc (car vars)
|
|
(id expr)))
|
|
(loop (cdr vars)))]
|
|
[_ (cons (car vars) (loop (cdr vars)))])]
|
|
[else vars]))
|
|
#'vars)])
|
|
(let ([l (syntax/loc stx
|
|
(lambda (the-obj . vars)
|
|
(let-syntax ([the-finder (quote-syntax the-obj)])
|
|
body1 body ...)))])
|
|
(with-syntax ([l (recertify (add-method-property l) stx)])
|
|
(syntax/loc stx
|
|
(let ([name l]) name))))))
|
|
stx)]
|
|
[(#%plain-lambda . _)
|
|
(bad "ill-formed lambda expression for method" stx)]
|
|
[(lambda . _)
|
|
(bad "ill-formed lambda expression for method" stx)]
|
|
[(case-lambda [vars body1 body ...] ...)
|
|
(andmap vars-ok? (syntax->list (syntax (vars ...))))
|
|
(if xform?
|
|
(with-syntax ([the-obj the-obj]
|
|
[the-finder the-finder]
|
|
[name (mk-name name)])
|
|
(let ([cl (syntax/loc stx
|
|
(case-lambda [(the-obj . vars)
|
|
(let-syntax ([the-finder (quote-syntax the-obj)])
|
|
body1 body ...)] ...))])
|
|
(with-syntax ([cl (recertify (add-method-property cl) stx)])
|
|
(syntax/loc stx
|
|
(let ([name cl]) name)))))
|
|
stx)]
|
|
[(case-lambda . _)
|
|
(bad "ill-formed case-lambda expression for method" stx)]
|
|
[(let- ([(id) expr] ...) let-body)
|
|
(and (or (free-identifier=? (syntax let-)
|
|
(quote-syntax let-values))
|
|
(free-identifier=? (syntax let-)
|
|
(quote-syntax letrec-values)))
|
|
(andmap identifier? (syntax->list (syntax (id ...)))))
|
|
(let* ([letrec? (free-identifier=? (syntax let-)
|
|
(quote-syntax letrec-values))]
|
|
[ids (syntax->list (syntax (id ...)))]
|
|
[new-ids (if xform?
|
|
(map
|
|
(lambda (id)
|
|
(datum->syntax
|
|
#f
|
|
(gensym (syntax-e id))))
|
|
ids)
|
|
ids)]
|
|
[body-locals (append ids locals)]
|
|
[exprs (map (lambda (expr id)
|
|
(loop expr #t id (if letrec?
|
|
body-locals
|
|
locals)))
|
|
(syntax->list (syntax (expr ...)))
|
|
ids)]
|
|
[body (let ([body (syntax let-body)])
|
|
(if (identifier? body)
|
|
(ormap (lambda (id new-id)
|
|
(and (bound-identifier=? body id)
|
|
new-id))
|
|
ids new-ids)
|
|
(loop body #t name body-locals)))])
|
|
(unless body
|
|
(bad "bad form for method definition" orig-stx))
|
|
(with-syntax ([(proc ...) exprs]
|
|
[(new-id ...) new-ids]
|
|
[mappings
|
|
(if xform?
|
|
(map
|
|
(lambda (old-id new-id)
|
|
(with-syntax ([old-id old-id]
|
|
[old-id-localized (lookup-localize (localize old-id))]
|
|
[new-id new-id]
|
|
[the-obj the-obj]
|
|
[the-finder the-finder])
|
|
(syntax (old-id (make-direct-method-map
|
|
(quote-syntax the-finder)
|
|
(quote the-obj)
|
|
(quote-syntax old-id)
|
|
(quote-syntax old-id-localized)
|
|
(quote new-id))))))
|
|
ids new-ids)
|
|
null)]
|
|
[body body])
|
|
(recertify
|
|
(if xform?
|
|
(if letrec?
|
|
(syntax/loc stx (letrec-syntax mappings
|
|
(let- ([(new-id) proc] ...)
|
|
body)))
|
|
(syntax/loc stx (let- ([(new-id) proc] ...)
|
|
(letrec-syntax mappings
|
|
body))))
|
|
(syntax/loc stx (let- ([(new-id) proc] ...)
|
|
body)))
|
|
stx)))]
|
|
[_else
|
|
(if can-expand?
|
|
(loop (expand stx locals) #f name locals)
|
|
(bad "bad form for method definition" orig-stx))])))
|
|
|
|
(define (add-method-property l)
|
|
(syntax-property l 'method-arity-error #t))
|
|
|
|
(define method-insp (current-code-inspector))
|
|
|
|
(define (recertify new old)
|
|
(syntax-recertify new old method-insp #f))
|
|
|
|
;; --------------------------------------------------------------------------------
|
|
;; Start here:
|
|
|
|
(define (main stx trace-flag super-expr deserialize-id-expr name-id interface-exprs defn-and-exprs)
|
|
(let-values ([(this-id) #'this-id]
|
|
[(the-obj) (datum->syntax (quote-syntax here) (gensym 'self))]
|
|
[(the-finder) (datum->syntax (quote-syntax here) (gensym 'find-self))])
|
|
|
|
(let* ([def-ctx (syntax-local-make-definition-context)]
|
|
[localized-map (make-bound-identifier-mapping)]
|
|
[any-localized? #f]
|
|
[localize/set-flag (lambda (id)
|
|
(let ([id2 (localize id)])
|
|
(unless (eq? id id2)
|
|
(set! any-localized? #t))
|
|
id2))]
|
|
[bind-local-id (lambda (id)
|
|
(let ([l (localize/set-flag id)])
|
|
(syntax-local-bind-syntaxes (list id) #f def-ctx)
|
|
(bound-identifier-mapping-put!
|
|
localized-map
|
|
id
|
|
l)))]
|
|
[lookup-localize (lambda (id)
|
|
(bound-identifier-mapping-get
|
|
localized-map
|
|
id
|
|
(lambda ()
|
|
;; If internal & external names are distinguished,
|
|
;; we need to fall back to localize:
|
|
(localize id))))])
|
|
|
|
;; ----- Expand definitions -----
|
|
(let ([defn-and-exprs (expand-all-forms stx defn-and-exprs def-ctx bind-local-id)]
|
|
[bad (lambda (msg expr)
|
|
(raise-syntax-error #f msg stx expr))]
|
|
[class-name (if name-id
|
|
(syntax-e name-id)
|
|
(let ([s (syntax-local-infer-name stx)])
|
|
(if (syntax? s)
|
|
(syntax-e s)
|
|
s)))])
|
|
|
|
;; ------ Basic syntax checks -----
|
|
(for-each (lambda (stx)
|
|
(syntax-case stx (-init init-rest -field -init-field inherit-field
|
|
private public override augride
|
|
public-final override-final augment-final
|
|
pubment overment augment
|
|
rename-super inherit inherit/super inherit/inner rename-inner
|
|
inspect)
|
|
[(form orig idp ...)
|
|
(and (identifier? (syntax form))
|
|
(or (free-identifier=? (syntax form) (quote-syntax -init))
|
|
(free-identifier=? (syntax form) (quote-syntax -init-field))))
|
|
|
|
(let ([form (syntax-e (stx-car (syntax orig)))])
|
|
(for-each
|
|
(lambda (idp)
|
|
(syntax-case idp ()
|
|
[id (identifier? (syntax id)) 'ok]
|
|
[((iid eid)) (and (identifier? (syntax iid))
|
|
(identifier? (syntax eid))) 'ok]
|
|
[(id expr) (identifier? (syntax id)) 'ok]
|
|
[((iid eid) expr) (and (identifier? (syntax iid))
|
|
(identifier? (syntax eid))) 'ok]
|
|
[else
|
|
(bad
|
|
(format
|
|
"~a element is not an optionally renamed identifier or identifier-expression pair"
|
|
form)
|
|
idp)]))
|
|
(syntax->list (syntax (idp ...)))))]
|
|
[(inspect expr)
|
|
'ok]
|
|
[(inspect . rest)
|
|
(bad "ill-formed inspect clause" stx)]
|
|
[(-init orig . rest)
|
|
(bad "ill-formed init clause" #'orig)]
|
|
[(init-rest)
|
|
'ok]
|
|
[(init-rest rest)
|
|
(identifier? (syntax rest))
|
|
'ok]
|
|
[(init-rest . rest)
|
|
(bad "ill-formed init-rest clause" stx)]
|
|
[(-init-field orig . rest)
|
|
(bad "ill-formed init-field clause" #'orig)]
|
|
[(-field orig idp ...)
|
|
(for-each (lambda (idp)
|
|
(syntax-case idp ()
|
|
[(id expr) (identifier? (syntax id)) 'ok]
|
|
[((iid eid) expr) (and (identifier? (syntax iid))
|
|
(identifier? (syntax eid)))
|
|
'ok]
|
|
[else
|
|
(bad
|
|
"field element is not an optionally renamed identifier-expression pair"
|
|
idp)]))
|
|
(syntax->list (syntax (idp ...))))]
|
|
[(-field orig . rest)
|
|
(bad "ill-formed field clause" #'orig)]
|
|
[(private id ...)
|
|
(for-each
|
|
(lambda (id)
|
|
(unless (identifier? id)
|
|
(bad "private element is not an identifier" id)))
|
|
(syntax->list (syntax (id ...))))]
|
|
[(private . rest)
|
|
(bad "ill-formed private clause" stx)]
|
|
[(form idp ...)
|
|
(and (identifier? (syntax form))
|
|
(ormap (lambda (f) (free-identifier=? (syntax form) f))
|
|
(syntax-e (quote-syntax (public
|
|
override
|
|
augride
|
|
public-final
|
|
override-final
|
|
augment-final
|
|
pubment
|
|
overment
|
|
augment
|
|
inherit
|
|
inherit/super
|
|
inherit/inner
|
|
inherit-field)))))
|
|
(let ([form (syntax-e (syntax form))])
|
|
(for-each
|
|
(lambda (idp)
|
|
(syntax-case idp ()
|
|
[id (identifier? (syntax id)) 'ok]
|
|
[(iid eid) (and (identifier? (syntax iid)) (identifier? (syntax eid))) 'ok]
|
|
[else
|
|
(bad
|
|
(format
|
|
"~a element is not an identifier or pair of identifiers"
|
|
form)
|
|
idp)]))
|
|
(syntax->list (syntax (idp ...)))))]
|
|
[(public . rest)
|
|
(bad "ill-formed public clause" stx)]
|
|
[(override . rest)
|
|
(bad "ill-formed override clause" stx)]
|
|
[(augride . rest)
|
|
(bad "ill-formed augride clause" stx)]
|
|
[(public-final . rest)
|
|
(bad "ill-formed public-final clause" stx)]
|
|
[(override-final . rest)
|
|
(bad "ill-formed override-final clause" stx)]
|
|
[(augment-final . rest)
|
|
(bad "ill-formed augment-final clause" stx)]
|
|
[(pubment . rest)
|
|
(bad "ill-formed pubment clause" stx)]
|
|
[(overment . rest)
|
|
(bad "ill-formed overment clause" stx)]
|
|
[(augment . rest)
|
|
(bad "ill-formed augment clause" stx)]
|
|
[(inherit . rest)
|
|
(bad "ill-formed inherit clause" stx)]
|
|
[(inherit/super . rest)
|
|
(bad "ill-formed inherit/super clause" stx)]
|
|
[(inherit/inner . rest)
|
|
(bad "ill-formed inherit/inner clause" stx)]
|
|
[(inherit-field . rest)
|
|
(bad "ill-formed inherit-field clause" stx)]
|
|
[(kw idp ...)
|
|
(and (identifier? #'kw)
|
|
(or (free-identifier=? #'rename-super #'kw)
|
|
(free-identifier=? #'rename-inner #'kw)))
|
|
(for-each
|
|
(lambda (idp)
|
|
(syntax-case idp ()
|
|
[(iid eid) (and (identifier? (syntax iid)) (identifier? (syntax eid))) 'ok]
|
|
[else
|
|
(bad
|
|
(format "~a element is not a pair of identifiers" (syntax-e #'kw))
|
|
idp)]))
|
|
(syntax->list (syntax (idp ...))))]
|
|
[(rename-super . rest)
|
|
(bad "ill-formed rename-super clause" stx)]
|
|
[(rename-inner . rest)
|
|
(bad "ill-formed rename-inner clause" stx)]
|
|
[_ 'ok]))
|
|
defn-and-exprs)
|
|
|
|
;; ----- Sort body into different categories -----
|
|
(let*-values ([(decls exprs)
|
|
(extract (syntax-e (quote-syntax (inherit-field
|
|
private
|
|
public
|
|
override
|
|
augride
|
|
public-final
|
|
override-final
|
|
augment-final
|
|
pubment
|
|
overment
|
|
augment
|
|
rename-super
|
|
inherit
|
|
inherit/super
|
|
inherit/inner
|
|
rename-inner)))
|
|
defn-and-exprs
|
|
cons)]
|
|
[(inspect-decls exprs)
|
|
(extract (list (quote-syntax inspect))
|
|
exprs
|
|
cons)]
|
|
[(plain-inits)
|
|
;; Normalize after, but keep un-normal for error reporting
|
|
(flatten #f (extract* (syntax-e
|
|
(quote-syntax (-init init-rest)))
|
|
exprs))]
|
|
[(normal-plain-inits) (map normalize-init/field plain-inits)]
|
|
[(init-rest-decls _)
|
|
(extract (list (quote-syntax init-rest))
|
|
exprs
|
|
void)]
|
|
[(inits)
|
|
(flatten #f (extract* (syntax-e
|
|
(quote-syntax (-init -init-field)))
|
|
exprs))]
|
|
[(normal-inits)
|
|
(map normalize-init/field inits)]
|
|
[(plain-fields)
|
|
(flatten #f (extract* (list (quote-syntax -field)) exprs))]
|
|
[(normal-plain-fields)
|
|
(map normalize-init/field plain-fields)]
|
|
[(plain-init-fields)
|
|
(flatten #f (extract* (list (quote-syntax -init-field)) exprs))]
|
|
[(normal-plain-init-fields)
|
|
(map normalize-init/field plain-init-fields)]
|
|
[(inherit-fields)
|
|
(flatten pair (extract* (list (quote-syntax inherit-field)) decls))]
|
|
[(privates)
|
|
(flatten pair (extract* (list (quote-syntax private)) decls))]
|
|
[(publics)
|
|
(flatten pair (extract* (list (quote-syntax public)) decls))]
|
|
[(overrides)
|
|
(flatten pair (extract* (list (quote-syntax override)) decls))]
|
|
[(augrides)
|
|
(flatten pair (extract* (list (quote-syntax augride)) decls))]
|
|
[(public-finals)
|
|
(flatten pair (extract* (list (quote-syntax public-final)) decls))]
|
|
[(override-finals)
|
|
(flatten pair (extract* (list (quote-syntax override-final)) decls))]
|
|
[(pubments)
|
|
(flatten pair (extract* (list (quote-syntax pubment)) decls))]
|
|
[(overments)
|
|
(flatten pair (extract* (list (quote-syntax overment)) decls))]
|
|
[(augments)
|
|
(flatten pair (extract* (list (quote-syntax augment)) decls))]
|
|
[(augment-finals)
|
|
(flatten pair (extract* (list (quote-syntax augment-final)) decls))]
|
|
[(rename-supers)
|
|
(flatten pair (extract* (list (quote-syntax rename-super)) decls))]
|
|
[(inherits)
|
|
(flatten pair (extract* (list (quote-syntax inherit)) decls))]
|
|
[(inherit/supers)
|
|
(flatten pair (extract* (list (quote-syntax inherit/super)) decls))]
|
|
[(inherit/inners)
|
|
(flatten pair (extract* (list (quote-syntax inherit/inner)) decls))]
|
|
[(rename-inners)
|
|
(flatten pair (extract* (list (quote-syntax rename-inner)) decls))])
|
|
|
|
;; At most one inspect:
|
|
(unless (or (null? inspect-decls)
|
|
(null? (cdr inspect-decls)))
|
|
(bad "multiple inspect clauses" (cadr inspect-decls)))
|
|
|
|
;; At most one init-rest:
|
|
(unless (or (null? init-rest-decls)
|
|
(null? (cdr init-rest-decls)))
|
|
(bad "multiple init-rest clauses" (cadr init-rest-decls)))
|
|
|
|
;; Make sure init-rest is last
|
|
(unless (null? init-rest-decls)
|
|
(let loop ([l exprs] [saw-rest? #f])
|
|
(unless (null? l)
|
|
(cond
|
|
[(and (stx-pair? (car l))
|
|
(identifier? (stx-car (car l))))
|
|
(let ([form (stx-car (car l))])
|
|
(cond
|
|
[(free-identifier=? #'init-rest form)
|
|
(loop (cdr l) #t)]
|
|
[(not saw-rest?) (loop (cdr l) #f)]
|
|
[(free-identifier=? #'-init form)
|
|
(bad "init clause follows init-rest clause" (stx-car (stx-cdr (car l))))]
|
|
[(free-identifier=? #'-init-field form)
|
|
(bad "init-field clause follows init-rest clause" (stx-car (stx-cdr (car l))))]
|
|
[else (loop (cdr l) #t)]))]
|
|
[else (loop (cdr l) saw-rest?)]))))
|
|
|
|
;; --- Check initialization on inits: ---
|
|
(let loop ([inits inits] [normal-inits normal-inits])
|
|
(unless (null? normal-inits)
|
|
(if (stx-null? (stx-cdr (car normal-inits)))
|
|
(loop (cdr inits)(cdr normal-inits))
|
|
(let loop ([inits (cdr inits)] [normal-inits (cdr normal-inits)])
|
|
(unless (null? inits)
|
|
(if (stx-null? (stx-cdr (car normal-inits)))
|
|
(bad "initializer without default follows an initializer with default"
|
|
(car inits))
|
|
(loop (cdr inits) (cdr normal-inits))))))))
|
|
|
|
;; ----- Extract method definitions; check that they look like procs -----
|
|
;; Optionally transform them, can expand even if not transforming.
|
|
(let* ([field-names (map norm-init/field-iid
|
|
(append normal-plain-fields normal-plain-init-fields))]
|
|
[inherit-field-names (map car inherit-fields)]
|
|
[plain-init-names (map norm-init/field-iid normal-plain-inits)]
|
|
[inherit-names (map car inherits)]
|
|
[inherit/super-names (map car inherit/supers)]
|
|
[inherit/inner-names (map car inherit/inners)]
|
|
[rename-super-names (map car rename-supers)]
|
|
[rename-inner-names (map car rename-inners)]
|
|
[local-public-dynamic-names (map car (append publics overrides augrides
|
|
overments augments
|
|
override-finals augment-finals))]
|
|
[local-public-names (append (map car (append pubments public-finals))
|
|
local-public-dynamic-names)]
|
|
[local-method-names (append (map car privates) local-public-names)]
|
|
[expand-stop-names (append
|
|
local-method-names
|
|
field-names
|
|
inherit-field-names
|
|
plain-init-names
|
|
inherit-names
|
|
inherit/super-names
|
|
inherit/inner-names
|
|
rename-super-names
|
|
rename-inner-names
|
|
(kernel-form-identifier-list))])
|
|
;; Do the extraction:
|
|
(let-values ([(methods ; (listof (cons id stx))
|
|
private-methods ; (listof (cons id stx))
|
|
exprs ; (listof stx)
|
|
stx-defines) ; (listof (cons (listof id) stx))
|
|
(let loop ([exprs exprs][ms null][pms null][es null][sd null])
|
|
(if (null? exprs)
|
|
(values (reverse ms) (reverse pms) (reverse es) (reverse sd))
|
|
(syntax-case (car exprs) (define-values define-syntaxes)
|
|
[(define-values (id ...) expr)
|
|
(let ([ids (syntax->list (syntax (id ...)))])
|
|
;; Check form:
|
|
(for-each (lambda (id)
|
|
(unless (identifier? id)
|
|
(bad "not an identifier for definition" id)))
|
|
ids)
|
|
;; method defn? (id in the list of privates/publics/overrides/augrides?)
|
|
(if (ormap (lambda (id)
|
|
(ormap (lambda (i) (bound-identifier=? i id))
|
|
local-method-names))
|
|
ids)
|
|
;; Yes, it's a method:
|
|
(begin
|
|
(unless (null? (cdr ids))
|
|
(bad "each method variable needs its own definition"
|
|
(car exprs)))
|
|
(let ([expr (proc-shape #f (syntax expr) #f
|
|
the-obj the-finder
|
|
bad class-name expand-stop-names
|
|
def-ctx lookup-localize)]
|
|
[public? (ormap (lambda (i)
|
|
(bound-identifier=? i (car ids)))
|
|
local-public-names)])
|
|
(loop (cdr exprs)
|
|
(if public?
|
|
(cons (cons (car ids) expr) ms)
|
|
ms)
|
|
(if public?
|
|
pms
|
|
(cons (cons (car ids) expr) pms))
|
|
es
|
|
sd)))
|
|
;; Non-method defn:
|
|
(loop (cdr exprs) ms pms (cons (car exprs) es) sd)))]
|
|
[(define-values . _)
|
|
(bad "ill-formed definition" (car exprs))]
|
|
[(define-syntaxes (id ...) expr)
|
|
(let ([ids (syntax->list (syntax (id ...)))])
|
|
(for-each (lambda (id) (unless (identifier? id)
|
|
(bad "syntax name is not an identifier" id)))
|
|
ids)
|
|
(loop (cdr exprs) ms pms es (cons (cons ids (car exprs)) sd)))]
|
|
[(define-syntaxes . _)
|
|
(bad "ill-formed syntax definition" (car exprs))]
|
|
[_else
|
|
(loop (cdr exprs) ms pms (cons (car exprs) es) sd)])))])
|
|
|
|
;; ---- Extract all defined names, including field accessors and mutators ---
|
|
(let ([defined-syntax-names (apply append (map car stx-defines))]
|
|
[defined-method-names (append (map car methods)
|
|
(map car private-methods))]
|
|
[private-field-names (let loop ([l exprs])
|
|
(if (null? l)
|
|
null
|
|
(syntax-case (car l) (define-values)
|
|
[(define-values (id ...) expr)
|
|
(append (syntax->list (syntax (id ...)))
|
|
(loop (cdr l)))]
|
|
[_else (loop (cdr l))])))]
|
|
[init-mode (cond
|
|
[(null? init-rest-decls) 'normal]
|
|
[(stx-null? (stx-cdr (car init-rest-decls))) 'stop]
|
|
[else 'list])])
|
|
|
|
;; -- Look for duplicates --
|
|
(let ([dup (check-duplicate-identifier
|
|
(append defined-syntax-names
|
|
defined-method-names
|
|
private-field-names
|
|
field-names
|
|
inherit-field-names
|
|
plain-init-names
|
|
inherit-names
|
|
inherit/super-names
|
|
inherit/inner-names
|
|
rename-super-names
|
|
rename-inner-names))])
|
|
(when dup
|
|
(bad "duplicate declared identifier" dup)))
|
|
|
|
;; -- Could still have duplicates within private/public/override/augride --
|
|
(let ([dup (check-duplicate-identifier local-method-names)])
|
|
(when dup
|
|
(bad "duplicate declared identifier" dup)))
|
|
|
|
;; -- Check for duplicate external method names, init names, or field names
|
|
(let ([check-dup
|
|
(lambda (what l)
|
|
(let ([ht (make-hasheq)])
|
|
(for-each (lambda (id)
|
|
(when (hash-ref ht (syntax-e id) #f)
|
|
(bad (format "duplicate declared external ~a name" what) id))
|
|
(hash-set! ht (syntax-e id) #t))
|
|
l)))])
|
|
;; method names
|
|
(check-dup "method" (map cdr (append publics overrides augrides
|
|
pubments overments augments
|
|
public-finals override-finals augment-finals)))
|
|
;; inits
|
|
(check-dup "init" (map norm-init/field-eid (append normal-inits)))
|
|
;; fields
|
|
(check-dup "field" (map norm-init/field-eid (append normal-plain-fields normal-plain-init-fields))))
|
|
|
|
;; -- Check that private/public/override/augride are defined --
|
|
(let ([ht (make-hasheq)]
|
|
[stx-ht (make-hasheq)])
|
|
(for-each
|
|
(lambda (defined-name)
|
|
(let ([l (hash-ref ht (syntax-e defined-name) null)])
|
|
(hash-set! ht (syntax-e defined-name) (cons defined-name l))))
|
|
defined-method-names)
|
|
(for-each
|
|
(lambda (defined-name)
|
|
(let ([l (hash-ref stx-ht (syntax-e defined-name) null)])
|
|
(hash-set! stx-ht (syntax-e defined-name) (cons defined-name l))))
|
|
defined-syntax-names)
|
|
(for-each
|
|
(lambda (pubovr-name)
|
|
(let ([l (hash-ref ht (syntax-e pubovr-name) null)])
|
|
(unless (ormap (lambda (i) (bound-identifier=? i pubovr-name)) l)
|
|
;; Either undefined or defined as syntax:
|
|
(let ([stx-l (hash-ref stx-ht (syntax-e pubovr-name) null)])
|
|
(if (ormap (lambda (i) (bound-identifier=? i pubovr-name)) stx-l)
|
|
(bad
|
|
"method declared but defined as syntax"
|
|
pubovr-name)
|
|
(bad
|
|
"method declared but not defined"
|
|
pubovr-name))))))
|
|
local-method-names))
|
|
|
|
;; ---- Check that rename-inner doesn't have a non-final decl ---
|
|
(unless (null? rename-inners)
|
|
(let ([ht (make-hasheq)])
|
|
(for-each (lambda (pub)
|
|
(hash-set! ht (syntax-e (cdr pub)) #t))
|
|
(append publics public-finals overrides override-finals augrides))
|
|
(for-each (lambda (inn)
|
|
(when (hash-ref ht (syntax-e (cdr inn)) #f)
|
|
(bad
|
|
"inner method is locally declared as public, override, public-final, override-final, or augride"
|
|
(cdr inn))))
|
|
rename-inners)))
|
|
|
|
;; ---- Convert expressions ----
|
|
;; Non-method definitions to set!
|
|
;; Initializations args access/set!
|
|
(let ([exprs (map (lambda (e)
|
|
(syntax-case e (define-values -field init-rest)
|
|
[(define-values (id ...) expr)
|
|
(syntax/loc e (set!-values (id ...) expr))]
|
|
[(_init orig idp ...)
|
|
(and (identifier? (syntax _init))
|
|
(ormap (lambda (it)
|
|
(free-identifier=? it (syntax _init)))
|
|
(syntax-e (quote-syntax (-init
|
|
-init-field)))))
|
|
(let* ([norms (map normalize-init/field
|
|
(syntax->list (syntax (idp ...))))]
|
|
[iids (map norm-init/field-iid norms)]
|
|
[exids (map norm-init/field-eid norms)])
|
|
(with-syntax ([(id ...) iids]
|
|
[(idpos ...) (map localize/set-flag exids)]
|
|
[(defval ...)
|
|
(map (lambda (norm)
|
|
(if (stx-null? (stx-cdr norm))
|
|
(syntax #f)
|
|
(with-syntax ([defexp (stx-car (stx-cdr norm))])
|
|
(syntax (lambda () defexp)))))
|
|
norms)]
|
|
[class-name class-name])
|
|
(syntax/loc e
|
|
(begin
|
|
1 ; to ensure a non-empty body
|
|
(set! id (extract-arg 'class-name `idpos init-args defval))
|
|
...))))]
|
|
[(-field orig idp ...)
|
|
(with-syntax ([(((iid eid) expr) ...)
|
|
(map normalize-init/field (syntax->list #'(idp ...)))])
|
|
(syntax/loc e (begin
|
|
1 ; to ensure a non-empty body
|
|
(set! iid expr)
|
|
...)))]
|
|
[(init-rest id/rename)
|
|
(with-syntax ([n (+ (length plain-inits)
|
|
(length plain-init-fields)
|
|
-1)]
|
|
[id (if (identifier? #'id/rename)
|
|
#'id/rename
|
|
(stx-car #'id/rename))])
|
|
(syntax/loc e (set! id (extract-rest-args n init-args))))]
|
|
[(init-rest)
|
|
(syntax (void))]
|
|
[_else e]))
|
|
exprs)]
|
|
[mk-method-temp
|
|
(lambda (id-stx)
|
|
(datum->syntax (quote-syntax here)
|
|
(gensym (syntax-e id-stx))))]
|
|
[rename-super-extras (append overments overrides override-finals inherit/supers)]
|
|
[rename-inner-extras (append pubments overments augments inherit/inners)]
|
|
[all-rename-inners (append (map car rename-inners)
|
|
(generate-temporaries (map car pubments))
|
|
(generate-temporaries (map car overments))
|
|
(generate-temporaries (map car augments))
|
|
(generate-temporaries (map car inherit/inners)))]
|
|
[all-inherits (append inherits inherit/supers inherit/inners)]
|
|
[definify (lambda (l)
|
|
(map bind-local-id l)
|
|
l)])
|
|
|
|
;; ---- set up field and method mappings ----
|
|
(with-syntax ([(rename-super-orig ...) (definify (map car rename-supers))]
|
|
[(rename-super-orig-localized ...) (map lookup-localize (map car rename-supers))]
|
|
[(rename-super-extra-orig ...) (map car rename-super-extras)]
|
|
[(rename-super-temp ...) (definify (generate-temporaries (map car rename-supers)))]
|
|
[(rename-super-extra-temp ...) (generate-temporaries (map car rename-super-extras))]
|
|
[(rename-inner-orig ...) (definify (map car rename-inners))]
|
|
[(rename-inner-orig-localized ...) (map lookup-localize (map car rename-inners))]
|
|
[(rename-inner-extra-orig ...) (map car rename-inner-extras)]
|
|
[(rename-inner-temp ...) (generate-temporaries (map car rename-inners))]
|
|
[(rename-inner-extra-temp ...) (generate-temporaries (map car rename-inner-extras))]
|
|
[(private-name ...) (map car privates)]
|
|
[(private-name-localized ...) (map lookup-localize (map car privates))]
|
|
[(private-temp ...) (map mk-method-temp (map car privates))]
|
|
[(pubment-name ...) (map car pubments)]
|
|
[(pubment-name-localized ...) (map lookup-localize (map car pubments))]
|
|
[(pubment-temp ...) (map
|
|
mk-method-temp
|
|
(map car pubments))]
|
|
[(public-final-name ...) (map car public-finals)]
|
|
[(public-final-name-localized ...) (map lookup-localize (map car public-finals))]
|
|
[(public-final-temp ...) (map
|
|
mk-method-temp
|
|
(map car public-finals))]
|
|
[(method-name ...) (append local-public-dynamic-names
|
|
(map car all-inherits))]
|
|
[(method-name-localized ...) (map lookup-localize
|
|
(append local-public-dynamic-names
|
|
(map car all-inherits)))]
|
|
[(method-accessor ...) (generate-temporaries
|
|
(map car
|
|
(append publics overrides augrides
|
|
overments augments
|
|
override-finals augment-finals
|
|
all-inherits)))]
|
|
[(inherit-field-accessor ...) (generate-temporaries
|
|
(map (lambda (id)
|
|
(format "get-~a"
|
|
(syntax-e id)))
|
|
inherit-field-names))]
|
|
[(inherit-field-mutator ...) (generate-temporaries
|
|
(map (lambda (id)
|
|
(format "set-~a!"
|
|
(syntax-e id)))
|
|
inherit-field-names))]
|
|
[(inherit-name ...) (definify (map car all-inherits))]
|
|
[(inherit-field-name ...) (definify inherit-field-names)]
|
|
[(inherit-field-name-localized ...) (map lookup-localize inherit-field-names)]
|
|
[(local-field ...) (definify
|
|
(append field-names
|
|
private-field-names))]
|
|
[(local-field-localized ...) (map lookup-localize
|
|
(append field-names
|
|
private-field-names))]
|
|
[(local-field-pos ...) (let loop ([pos 0][l (append field-names
|
|
private-field-names)])
|
|
(if (null? l)
|
|
null
|
|
(cons pos (loop (add1 pos) (cdr l)))))]
|
|
[(plain-init-name ...) (definify plain-init-names)]
|
|
[(plain-init-name-localized ...) (map lookup-localize plain-init-names)]
|
|
[(local-plain-init-name ...) (generate-temporaries plain-init-names)])
|
|
(let ([mappings
|
|
;; make-XXX-map is supplied by private/classidmap.ss
|
|
(with-syntax ([the-obj the-obj]
|
|
[the-finder the-finder]
|
|
[this-id this-id]
|
|
[trace-flag (if trace-flag (syntax #t) (syntax #f))])
|
|
(syntax
|
|
([(inherit-field-name ...
|
|
local-field ...
|
|
rename-super-orig ...
|
|
rename-inner-orig ...
|
|
method-name ...
|
|
private-name ...
|
|
public-final-name ...
|
|
pubment-name ...)
|
|
(values
|
|
(make-field-map trace-flag
|
|
(quote-syntax the-finder)
|
|
(quote the-obj)
|
|
(quote-syntax inherit-field-name)
|
|
(quote-syntax inherit-field-name-localized)
|
|
(quote-syntax inherit-field-accessor)
|
|
(quote-syntax inherit-field-mutator)
|
|
'())
|
|
...
|
|
(make-field-map trace-flag
|
|
(quote-syntax the-finder)
|
|
(quote the-obj)
|
|
(quote-syntax local-field)
|
|
(quote-syntax local-field-localized)
|
|
(quote-syntax local-accessor)
|
|
(quote-syntax local-mutator)
|
|
'(local-field-pos))
|
|
...
|
|
(make-rename-super-map (quote-syntax the-finder)
|
|
(quote the-obj)
|
|
(quote-syntax rename-super-orig)
|
|
(quote-syntax rename-super-orig-localized)
|
|
(quote-syntax rename-super-temp))
|
|
...
|
|
(make-rename-inner-map (quote-syntax the-finder)
|
|
(quote the-obj)
|
|
(quote-syntax rename-inner-orig)
|
|
(quote-syntax rename-inner-orig-localized)
|
|
(quote-syntax rename-inner-temp))
|
|
...
|
|
(make-method-map (quote-syntax the-finder)
|
|
(quote the-obj)
|
|
(quote-syntax method-name)
|
|
(quote-syntax method-name-localized)
|
|
(quote-syntax method-accessor))
|
|
...
|
|
(make-direct-method-map (quote-syntax the-finder)
|
|
(quote the-obj)
|
|
(quote-syntax private-name)
|
|
(quote-syntax private-name-localized)
|
|
(quote private-temp))
|
|
...
|
|
(make-direct-method-map (quote-syntax the-finder)
|
|
(quote the-obj)
|
|
(quote-syntax public-final-name)
|
|
(quote-syntax public-final-name-localized)
|
|
(quote public-final-temp))
|
|
...
|
|
(make-direct-method-map (quote-syntax the-finder)
|
|
(quote the-obj)
|
|
(quote-syntax pubment-name)
|
|
(quote-syntax pubment-name-localized)
|
|
(quote pubment-temp))
|
|
...)])))]
|
|
[extra-init-mappings (syntax
|
|
([(plain-init-name ...)
|
|
(values
|
|
(make-init-error-map (quote-syntax plain-init-name-localized))
|
|
...)]))])
|
|
|
|
(let ([find-method
|
|
(lambda (methods)
|
|
(lambda (name)
|
|
(ormap
|
|
(lambda (m)
|
|
(and (bound-identifier=? (car m) name)
|
|
(with-syntax ([proc (proc-shape (car m) (cdr m) #t
|
|
the-obj the-finder
|
|
bad class-name expand-stop-names
|
|
def-ctx lookup-localize)]
|
|
[extra-init-mappings extra-init-mappings])
|
|
(syntax
|
|
(syntax-parameterize
|
|
([super-instantiate-param super-error-map]
|
|
[super-make-object-param super-error-map]
|
|
[super-new-param super-error-map])
|
|
(letrec-syntaxes+values extra-init-mappings ()
|
|
proc))))))
|
|
methods)))]
|
|
[lookup-localize-cdr (lambda (p) (lookup-localize (cdr p)))])
|
|
|
|
(internal-definition-context-seal def-ctx)
|
|
|
|
;; ---- build final result ----
|
|
(with-syntax ([public-names (map lookup-localize-cdr publics)]
|
|
[public-final-names (map lookup-localize-cdr public-finals)]
|
|
[override-names (map lookup-localize-cdr overrides)]
|
|
[override-final-names (map lookup-localize-cdr override-finals)]
|
|
[augride-names (map lookup-localize-cdr augrides)]
|
|
[pubment-names (map lookup-localize-cdr pubments)]
|
|
[overment-names (map lookup-localize-cdr overments)]
|
|
[augment-names (map lookup-localize-cdr augments)]
|
|
[augment-final-names (map lookup-localize-cdr augment-finals)]
|
|
[(rename-super-name ...) (map lookup-localize-cdr rename-supers)]
|
|
[(rename-super-extra-name ...) (map lookup-localize-cdr rename-super-extras)]
|
|
[(rename-inner-name ...) (map lookup-localize-cdr rename-inners)]
|
|
[(rename-inner-extra-name ...) (map lookup-localize-cdr rename-inner-extras)]
|
|
[inherit-names (map lookup-localize-cdr all-inherits)]
|
|
[num-fields (datum->syntax
|
|
(quote-syntax here)
|
|
(+ (length private-field-names)
|
|
(length plain-init-fields)
|
|
(length plain-fields)))]
|
|
[field-names (map (lambda (norm)
|
|
(lookup-localize (norm-init/field-eid norm)))
|
|
(append
|
|
normal-plain-fields
|
|
normal-plain-init-fields))]
|
|
[inherit-field-names (map lookup-localize (map cdr inherit-fields))]
|
|
[init-names (map (lambda (norm)
|
|
(lookup-localize
|
|
(norm-init/field-eid norm)))
|
|
normal-inits)]
|
|
[init-mode init-mode]
|
|
[(private-method ...) (map (find-method private-methods) (map car privates))]
|
|
[public-methods (map (find-method methods) (map car publics))]
|
|
[override-methods (map (find-method methods) (map car (append overments
|
|
override-finals
|
|
overrides)))]
|
|
[augride-methods (map (find-method methods) (map car (append augments
|
|
augment-finals
|
|
augrides)))]
|
|
[(pubment-method ...) (map (find-method methods) (map car pubments))]
|
|
[(public-final-method ...) (map (find-method methods) (map car public-finals))]
|
|
[mappings mappings]
|
|
|
|
[exprs exprs]
|
|
[the-obj the-obj]
|
|
[the-finder the-finder]
|
|
[name class-name]
|
|
[(stx-def ...) (map cdr stx-defines)]
|
|
[super-expression super-expr]
|
|
[(interface-expression ...) interface-exprs]
|
|
[inspector (if (pair? inspect-decls)
|
|
(stx-car (stx-cdr (car inspect-decls)))
|
|
#'(current-inspector))]
|
|
[deserialize-id-expr deserialize-id-expr])
|
|
|
|
(quasisyntax/loc stx
|
|
(let ([superclass super-expression]
|
|
[interfaces (list interface-expression ...)])
|
|
(compose-class
|
|
'name superclass interfaces inspector deserialize-id-expr #,any-localized?
|
|
;; Field count:
|
|
num-fields
|
|
;; Field names:
|
|
`field-names
|
|
`inherit-field-names
|
|
;; Method names:
|
|
`(rename-super-name ... rename-super-extra-name ...)
|
|
`(rename-inner-name ... rename-inner-extra-name ...)
|
|
`pubment-names
|
|
`public-final-names
|
|
`public-names
|
|
`overment-names
|
|
`override-final-names
|
|
`override-names
|
|
`augment-names
|
|
`augment-final-names
|
|
`augride-names
|
|
`inherit-names
|
|
;; Init arg names (in order)
|
|
`init-names
|
|
(quote init-mode)
|
|
;; Methods (when given needed super-methods, etc.):
|
|
#, ;; Attach srcloc (useful for profiling)
|
|
(quasisyntax/loc stx
|
|
(lambda (local-accessor
|
|
local-mutator
|
|
inherit-field-accessor ... ; inherit
|
|
inherit-field-mutator ...
|
|
rename-super-temp ... rename-super-extra-temp ...
|
|
rename-inner-temp ... rename-inner-extra-temp ...
|
|
method-accessor ...) ; for a local call that needs a dynamic lookup
|
|
(syntax-parameterize
|
|
([this-param (make-this-map (quote-syntax this-id)
|
|
(quote-syntax the-finder)
|
|
(quote the-obj))])
|
|
(let-syntaxes
|
|
mappings
|
|
(syntax-parameterize
|
|
([super-param
|
|
(lambda (stx)
|
|
(syntax-case stx (rename-super-extra-orig ...)
|
|
[(_ rename-super-extra-orig . args)
|
|
(generate-super-call
|
|
stx
|
|
(quote-syntax the-finder)
|
|
(quote the-obj)
|
|
(quote-syntax rename-super-extra-temp)
|
|
(syntax args))]
|
|
...
|
|
[(_ id . args)
|
|
(identifier? #'id)
|
|
(raise-syntax-error
|
|
#f
|
|
(string-append
|
|
"identifier for super call does not have an override, "
|
|
"override-final, overment, or inherit/super declaration")
|
|
stx
|
|
#'id)]
|
|
[_else
|
|
(raise-syntax-error
|
|
#f
|
|
"expected an identifier after the keyword"
|
|
stx)]))]
|
|
[inner-param
|
|
(lambda (stx)
|
|
(syntax-case stx (rename-inner-extra-orig ...)
|
|
[(_ default-expr rename-inner-extra-orig . args)
|
|
(generate-inner-call
|
|
stx
|
|
(quote-syntax the-finder)
|
|
(quote the-obj)
|
|
(syntax default-expr)
|
|
(quote-syntax rename-inner-extra-temp)
|
|
(syntax args))]
|
|
...
|
|
[(_ default-expr id . args)
|
|
(identifier? #'id)
|
|
(raise-syntax-error
|
|
#f
|
|
(string-append
|
|
"identifier for inner call does not have a pubment, augment, "
|
|
"overment, or inherit/inner declaration")
|
|
stx
|
|
#'id)]
|
|
[(_)
|
|
(raise-syntax-error
|
|
#f
|
|
"expected a default-value expression after the keyword"
|
|
stx
|
|
#'id)]
|
|
[_else
|
|
(raise-syntax-error
|
|
#f
|
|
"expected an identifier after the keyword and default-value expression"
|
|
stx)]))])
|
|
stx-def ...
|
|
(letrec ([private-temp private-method]
|
|
...
|
|
[pubment-temp pubment-method]
|
|
...
|
|
[public-final-temp public-final-method]
|
|
...)
|
|
(values
|
|
(list pubment-temp ... public-final-temp ... . public-methods)
|
|
(list . override-methods)
|
|
(list . augride-methods)
|
|
;; Initialization
|
|
#, ;; Attach srcloc (useful for profiling)
|
|
(quasisyntax/loc stx
|
|
(lambda (the-obj super-go si_c si_inited? si_leftovers init-args)
|
|
(let-syntax ([the-finder (quote-syntax the-obj)])
|
|
(syntax-parameterize
|
|
([super-instantiate-param
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ (arg (... ...)) (kw kwarg) (... ...))
|
|
(with-syntax ([stx stx])
|
|
(syntax (-instantiate super-go stx (the-obj si_c si_inited?
|
|
si_leftovers)
|
|
(list arg (... ...))
|
|
(kw kwarg) (... ...))))]))]
|
|
[super-new-param
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ (kw kwarg) (... ...))
|
|
(with-syntax ([stx stx])
|
|
(syntax (-instantiate super-go stx (the-obj si_c si_inited?
|
|
si_leftovers)
|
|
null
|
|
(kw kwarg) (... ...))))]))]
|
|
[super-make-object-param
|
|
(lambda (stx)
|
|
(let ([code
|
|
(quote-syntax
|
|
(lambda args
|
|
(super-go the-obj si_c si_inited? si_leftovers args null)))])
|
|
(if (identifier? stx)
|
|
code
|
|
(datum->syntax
|
|
code
|
|
(cons code
|
|
(cdr (syntax-e stx)))))))])
|
|
(letrec-syntaxes+values
|
|
([(plain-init-name) (make-init-redirect
|
|
(quote-syntax set!)
|
|
(quote-syntax #%plain-app)
|
|
(quote-syntax local-plain-init-name)
|
|
(quote-syntax plain-init-name-localized))] ...)
|
|
([(local-plain-init-name) undefined] ...)
|
|
(void) ; in case the body is empty
|
|
. exprs))))))))))))
|
|
;; Not primitive:
|
|
#f))))))))))))))))
|
|
|
|
(define (core-class* trace-flag)
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ super-expression (interface-expr ...)
|
|
defn-or-expr
|
|
...)
|
|
(main stx trace-flag
|
|
#'super-expression
|
|
#f #f
|
|
(syntax->list #'(interface-expr ...))
|
|
(syntax->list #'(defn-or-expr ...)))])))
|
|
|
|
(define (core-class trace-flag)
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ super-expression
|
|
defn-or-expr
|
|
...)
|
|
(main stx trace-flag
|
|
#'super-expression
|
|
#f #f
|
|
null
|
|
(syntax->list #'(defn-or-expr ...)))])))
|
|
|
|
(define (core-class/derived trace-flag)
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ orig-stx
|
|
[name-id super-expression (interface-expr ...) deserialize-id-expr]
|
|
defn-or-expr
|
|
...)
|
|
(main #'orig-stx trace-flag
|
|
#'super-expression
|
|
#'deserialize-id-expr
|
|
(and (syntax-e #'name-id) #'name-id)
|
|
(syntax->list #'(interface-expr ...))
|
|
(syntax->list #'(defn-or-expr ...)))])))
|
|
|
|
;; The class* and class entry points:
|
|
(values
|
|
;; class*
|
|
(core-class* #f)
|
|
;; class
|
|
(core-class #f)
|
|
;; class/derived
|
|
(core-class/derived #f)
|
|
;; class*-traced
|
|
(core-class* #t)
|
|
;; class-traced
|
|
(core-class #t)
|
|
;; class/derived-traced
|
|
(core-class/derived #t)
|
|
)))
|
|
|
|
(define-syntax (-define-serializable-class stx)
|
|
(syntax-case stx ()
|
|
[(_ orig-stx name super-expression (interface-expr ...)
|
|
defn-or-expr ...)
|
|
(let ([deserialize-name-info (datum->syntax
|
|
#'name
|
|
(string->symbol
|
|
(format "deserialize-info:~a" (syntax-e #'name)))
|
|
#'name)])
|
|
(unless (memq (syntax-local-context) '(top-level module))
|
|
(raise-syntax-error
|
|
#f
|
|
"allowed only at the top level or within a module top level"
|
|
#'orig-stx))
|
|
(with-syntax ([deserialize-name-info deserialize-name-info]
|
|
[(provision ...) (if (eq? (syntax-local-context) 'module)
|
|
#`((provide #,deserialize-name-info))
|
|
#'())])
|
|
#'(begin
|
|
(define-values (name deserialize-name-info)
|
|
(class/derived orig-stx [name
|
|
super-expression
|
|
(interface-expr ...)
|
|
#'deserialize-name-info]
|
|
defn-or-expr ...))
|
|
provision ...)))]))
|
|
|
|
(define-syntax (define-serializable-class* stx)
|
|
(syntax-case stx ()
|
|
[(_ name super-expression (interface-expr ...)
|
|
defn-or-expr ...)
|
|
(with-syntax ([orig-stx stx])
|
|
#'(-define-serializable-class orig-stx
|
|
name
|
|
super-expression
|
|
(interface-expr ...)
|
|
defn-or-expr ...))]))
|
|
|
|
(define-syntax (define-serializable-class stx)
|
|
(syntax-case stx ()
|
|
[(_ name super-expression
|
|
defn-or-expr ...)
|
|
(with-syntax ([orig-stx stx])
|
|
#'(-define-serializable-class orig-stx
|
|
name
|
|
super-expression
|
|
()
|
|
defn-or-expr ...))]))
|
|
|
|
|
|
(define-syntaxes (private* public* pubment* override* overment* augride* augment*
|
|
public-final* override-final* augment-final*)
|
|
(let ([mk
|
|
(lambda (who decl-form)
|
|
(lambda (stx)
|
|
(unless (class-top-level-context? (syntax-local-context))
|
|
(raise-syntax-error
|
|
#f
|
|
"use of a class keyword is not in a class top-level"
|
|
stx))
|
|
(syntax-case stx ()
|
|
[(_ binding ...)
|
|
(let ([bindings (syntax->list (syntax (binding ...)))])
|
|
(let ([name-exprs
|
|
(map (lambda (binding)
|
|
(syntax-case binding ()
|
|
[(name expr)
|
|
(identifier? (syntax name))
|
|
(cons (syntax name) (syntax expr))]
|
|
[_else
|
|
(identifier? (syntax name))
|
|
(raise-syntax-error
|
|
#f
|
|
"expected an identifier and expression"
|
|
stx
|
|
binding)]))
|
|
bindings)])
|
|
(with-syntax ([(name ...) (map car name-exprs)]
|
|
[(expr ...) (map cdr name-exprs)]
|
|
[decl-form decl-form])
|
|
(syntax
|
|
(begin
|
|
(decl-form name ...)
|
|
(define name expr)
|
|
...)))))])))])
|
|
(values
|
|
(mk 'private* (syntax private))
|
|
(mk 'public* (syntax public))
|
|
(mk 'pubment* (syntax pubment))
|
|
(mk 'override* (syntax override))
|
|
(mk 'overment* (syntax overment))
|
|
(mk 'augride* (syntax augride))
|
|
(mk 'augment* (syntax augment))
|
|
(mk 'public-final* (syntax public-final))
|
|
(mk 'override-final* (syntax override-final))
|
|
(mk 'augment-final* (syntax augment)))))
|
|
|
|
(define-syntaxes (define/private define/public define/pubment
|
|
define/override define/overment
|
|
define/augride define/augment
|
|
define/public-final define/override-final define/augment-final)
|
|
(let ([mk
|
|
(lambda (decl-form)
|
|
(lambda (stx)
|
|
(unless (class-top-level-context? (syntax-local-context))
|
|
(raise-syntax-error
|
|
#f
|
|
"use of a class keyword is not in a class top-level"
|
|
stx))
|
|
(let-values ([(id rhs) (normalize-definition stx #'lambda #f #t)])
|
|
(quasisyntax/loc stx
|
|
(begin
|
|
(#,decl-form #,id)
|
|
(define #,id #,rhs))))))])
|
|
(values
|
|
(mk #'private)
|
|
(mk #'public)
|
|
(mk #'pubment)
|
|
(mk #'override)
|
|
(mk #'overment)
|
|
(mk #'augride)
|
|
(mk #'augment)
|
|
(mk #'public-final)
|
|
(mk #'override-final)
|
|
(mk #'augment-final))))
|
|
|
|
(define-syntax (define-local-member-name stx)
|
|
(syntax-case stx ()
|
|
[(_ id ...)
|
|
(let ([ids (syntax->list (syntax (id ...)))])
|
|
(for-each (lambda (id)
|
|
(unless (identifier? id)
|
|
(raise-syntax-error
|
|
#f
|
|
"expected an identifier"
|
|
stx
|
|
id)))
|
|
ids)
|
|
(let ([dup (check-duplicate-identifier ids)])
|
|
(when dup
|
|
(raise-syntax-error
|
|
#f
|
|
"duplicate identifier"
|
|
stx
|
|
dup)))
|
|
(if (eq? (syntax-local-context) 'top-level)
|
|
;; Does nothing in particular at the top level:
|
|
(syntax/loc stx (define-syntaxes (id ...) (values 'id ...)))
|
|
;; Map names to private indicators, which are made private
|
|
;; simply by introduction:
|
|
(with-syntax ([(gen-id ...) (generate-temporaries ids)])
|
|
(with-syntax ([stx-defs
|
|
;; Need to attach srcloc to this definition:
|
|
(syntax/loc stx
|
|
(define-syntaxes (id ...)
|
|
(values (make-private-name (quote-syntax id) (quote-syntax gen-id))
|
|
...)))])
|
|
(syntax/loc stx
|
|
(begin
|
|
(define-values (gen-id ...)
|
|
(values (generate-local-member-name 'id) ...))
|
|
stx-defs))))))]))
|
|
|
|
(define-syntax (define-member-name stx)
|
|
(syntax-case stx ()
|
|
[(_ id expr)
|
|
(let ([name #'id])
|
|
(unless (identifier? name)
|
|
(raise-syntax-error
|
|
#f
|
|
"expected an identifier for definition"
|
|
stx
|
|
name))
|
|
(with-syntax ([stx-def
|
|
;; Need to attach srcloc to this definition:
|
|
(syntax/loc stx
|
|
(define-syntax id
|
|
(make-private-name (quote-syntax id)
|
|
((syntax-local-certifier) (quote-syntax member-name)))))])
|
|
#'(begin
|
|
(define member-name (check-member-key 'id expr))
|
|
stx-def)))]))
|
|
|
|
(define (generate-local-member-name id)
|
|
(string->uninterned-symbol
|
|
(symbol->string id)))
|
|
|
|
|
|
(define-values (struct:member-key make-member-key member-name-key? member-key-ref member-key-set!)
|
|
(make-struct-type 'member-name-key
|
|
#f
|
|
1 0 #f
|
|
(list
|
|
(cons prop:custom-write
|
|
(lambda (v p write?)
|
|
(fprintf p "#<member-key:~a>" (member-key-id v)))))))
|
|
|
|
(define member-key-id (make-struct-field-accessor member-key-ref 0))
|
|
|
|
(define (check-member-key id v)
|
|
(unless (member-name-key? v)
|
|
(error 'define-local-member-name "not a member key for ~a: ~e" id v))
|
|
(member-key-id v))
|
|
|
|
(define-syntax (member-name-key stx)
|
|
(syntax-case stx ()
|
|
[(_ id)
|
|
(identifier? #'id)
|
|
(with-syntax ([id (localize #'id)])
|
|
(syntax/loc stx (make-member-key `id)))]
|
|
[(_ x)
|
|
(raise-syntax-error
|
|
#f
|
|
"not an identifier"
|
|
stx
|
|
#'x)]))
|
|
|
|
(define (generate-member-key)
|
|
(make-member-key (generate-local-member-name (gensym 'member))))
|
|
|
|
(define (member-name-key=? a b)
|
|
(if (and (member-name-key? a)
|
|
(member-name-key? b))
|
|
(eq? (member-key-id a) (member-key-id b))
|
|
(eq? a b)))
|
|
|
|
(define (member-name-key-hash-code a)
|
|
(unless (member-name-key? a)
|
|
(raise-type-error
|
|
'member-name-key-hash-code
|
|
"member name key"
|
|
a))
|
|
(eq-hash-code (member-key-id a)))
|
|
|
|
;;--------------------------------------------------------------------
|
|
;; class implementation
|
|
;;--------------------------------------------------------------------
|
|
|
|
(define-struct class (name
|
|
pos supers ; pos is subclass depth, supers is vector
|
|
self-interface ; self interface
|
|
insp-mk ; dummy struct maker to control inspection access
|
|
|
|
method-width ; total number of methods
|
|
method-ht ; maps public names to vector positions
|
|
method-ids ; reverse-ordered list of public method names
|
|
|
|
methods ; vector of methods
|
|
beta-methods ; vector of vector of methods
|
|
meth-flags ; vector: #f => primitive-implemented
|
|
; 'final => final
|
|
; 'augmentable => can augment
|
|
|
|
field-width ; total number of fields
|
|
field-ht ; maps public field names to (cons class pos)
|
|
field-ids ; list of public field names
|
|
|
|
[struct:object ; structure type for instances
|
|
#:mutable]
|
|
[object? ; predicate
|
|
#:mutable]
|
|
[make-object ; : (-> object), constructor that creates an uninitialized object
|
|
#:mutable]
|
|
[field-ref ; accessor
|
|
#:mutable]
|
|
[field-set! ; mutator
|
|
#:mutable]
|
|
|
|
init-args ; list of symbols in order; #f => only by position
|
|
init-mode ; 'normal, 'stop (don't accept by-pos for super), or 'list
|
|
|
|
[init ; initializer
|
|
#:mutable] ; : object
|
|
; (object class (box boolean) leftover-args new-by-pos-args new-named-args
|
|
; -> void) // always continue-make-super?
|
|
; class
|
|
; (box boolean)
|
|
; leftover-args
|
|
; named-args
|
|
; -> void
|
|
|
|
[serializer ; proc => serializer, #f => not serializable
|
|
#:mutable]
|
|
[fixup ; for deserialization
|
|
#:mutable]
|
|
|
|
no-super-init?); #t => no super-init needed
|
|
#:inspector insp)
|
|
|
|
;; compose-class: produces one result if `deserialize-id' is #f, two
|
|
;; results if `deserialize-id' is not #f
|
|
(define (compose-class name ; symbol
|
|
super ; class
|
|
interfaces ; list of interfaces
|
|
inspector ; inspector or #f
|
|
deserialize-id ; identifier or #f
|
|
any-localized? ; #t => need to double-check distinct external names
|
|
|
|
num-fields ; total fields (public & private)
|
|
public-field-names ; list of symbols (shorter than num-fields)
|
|
inherit-field-names ; list of symbols (not included in num-fields)
|
|
|
|
rename-super-names ; list of symbols
|
|
rename-inner-names
|
|
pubment-names
|
|
public-final-names
|
|
public-normal-names
|
|
overment-names
|
|
override-final-names
|
|
override-normal-names
|
|
augment-names
|
|
augment-final-names
|
|
augride-normal-names
|
|
inherit-names
|
|
|
|
init-args ; list of symbols in order, or #f
|
|
init-mode ; 'normal, 'stop, or 'list
|
|
|
|
make-methods ; takes field and method accessors
|
|
make-struct:prim) ; see "primitive classes", below
|
|
|
|
;; -- Check superclass --
|
|
(unless (class? super)
|
|
(obj-error 'class* "superclass expression returned a non-class: ~a~a"
|
|
super
|
|
(for-class name)))
|
|
|
|
(when any-localized?
|
|
(check-still-unique name
|
|
init-args
|
|
"initialization argument names")
|
|
;; We intentionally leave inherited names out of the lists below,
|
|
;; on the threory that it's ok to decide to inherit from
|
|
;; yourself:
|
|
(check-still-unique name
|
|
(append public-field-names)
|
|
"field names")
|
|
(check-still-unique name
|
|
(append pubment-names public-final-names public-normal-names
|
|
overment-names override-final-names override-normal-names
|
|
augment-names augment-final-names augride-normal-names)
|
|
"method names"))
|
|
|
|
;; -- Create new class's name --
|
|
(let* ([name (or name
|
|
(let ([s (class-name super)])
|
|
(and s
|
|
(not (eq? super object%))
|
|
(if (symbol? s)
|
|
(format "derived-from-~a" s)
|
|
s))))]
|
|
;; Combine method lists
|
|
[public-names (append pubment-names public-final-names public-normal-names)]
|
|
[override-names (append overment-names override-final-names override-normal-names)]
|
|
[augride-names (append augment-names augment-final-names augride-normal-names)]
|
|
[final-names (append public-final-names override-final-names augment-final-names)]
|
|
[augonly-names (append pubment-names overment-names augment-names)]
|
|
;; Mis utilities
|
|
[no-new-methods? (null? public-names)]
|
|
[no-method-changes? (and (null? public-names)
|
|
(null? override-names)
|
|
(null? augride-names)
|
|
(null? final-names))]
|
|
[no-new-fields? (null? public-field-names)]
|
|
[xappend (lambda (a b) (if (null? b) a (append a b)))])
|
|
|
|
;; -- Check interfaces ---
|
|
(for-each
|
|
(lambda (intf)
|
|
(unless (interface? intf)
|
|
(obj-error 'class* "interface expression returned a non-interface: ~a~a"
|
|
intf
|
|
(for-class name))))
|
|
interfaces)
|
|
|
|
;; -- Check inspectors ---
|
|
(when inspector
|
|
(unless (inspector? inspector)
|
|
(obj-error 'class* "inspect class result is not an inspector or #f: ~a~a"
|
|
inspector
|
|
(for-class name))))
|
|
|
|
;; -- Match method and field names to indices --
|
|
(let ([method-ht (if no-new-methods?
|
|
(class-method-ht super)
|
|
(make-hasheq))]
|
|
[field-ht (if no-new-fields?
|
|
(class-field-ht super)
|
|
(make-hasheq))]
|
|
[super-method-ht (class-method-ht super)]
|
|
[super-method-ids (class-method-ids super)]
|
|
[super-field-ids (class-field-ids super)]
|
|
[super-field-ht (class-field-ht super)])
|
|
|
|
;; Put superclass ids in tables, with pos
|
|
(unless no-new-methods?
|
|
(let loop ([ids super-method-ids][p (sub1 (class-method-width super))])
|
|
(unless (null? ids)
|
|
(hash-set! method-ht (car ids) p)
|
|
(loop (cdr ids) (sub1 p)))))
|
|
(unless no-new-fields?
|
|
(let loop ([ids super-field-ids])
|
|
(unless (null? ids)
|
|
(hash-set! field-ht (car ids) (hash-ref super-field-ht (car ids)))
|
|
(loop (cdr ids)))))
|
|
|
|
;; Put new ids in table, with pos (replace field pos with accessor info later)
|
|
(unless no-new-methods?
|
|
(let loop ([ids public-names][p (class-method-width super)])
|
|
(unless (null? ids)
|
|
(when (hash-ref method-ht (car ids) #f)
|
|
(obj-error 'class* "superclass already contains method: ~a~a"
|
|
(car ids)
|
|
(for-class name)))
|
|
(hash-set! method-ht (car ids) p)
|
|
(loop (cdr ids) (add1 p)))))
|
|
(unless no-new-fields?
|
|
(let loop ([ids public-field-names][p (class-field-width super)])
|
|
(unless (null? ids)
|
|
(when (hash-ref field-ht (car ids) #f)
|
|
(obj-error 'class* "superclass already contains field: ~a~a"
|
|
(car ids)
|
|
(for-class name)))
|
|
(hash-set! field-ht (car ids) p)
|
|
(loop (cdr ids) (add1 p)))))
|
|
|
|
;; Check that superclass has expected fields
|
|
(for-each (lambda (id)
|
|
(unless (hash-ref field-ht id #f)
|
|
(obj-error 'class* "superclass does not provide field: ~a~a"
|
|
id
|
|
(for-class name))))
|
|
inherit-field-names)
|
|
|
|
;; Check that superclass has expected methods, and get indices
|
|
(let ([get-indices
|
|
(lambda (method-ht what ids)
|
|
(map
|
|
(lambda (id)
|
|
(hash-ref
|
|
method-ht id
|
|
(lambda ()
|
|
(obj-error 'class*
|
|
"~a does not provide an expected method for ~a: ~a~a"
|
|
(if (eq? method-ht super-method-ht) "superclass" "class")
|
|
what
|
|
id
|
|
(for-class name)))))
|
|
ids))]
|
|
[method-width (+ (class-method-width super) (length public-names))]
|
|
[field-width (+ (class-field-width super) num-fields)])
|
|
(let ([inherit-indices (get-indices super-method-ht "inherit" inherit-names)]
|
|
[replace-augonly-indices (get-indices super-method-ht "overment" overment-names)]
|
|
[replace-final-indices (get-indices super-method-ht "override-final" override-final-names)]
|
|
[replace-normal-indices (get-indices super-method-ht "override" override-normal-names)]
|
|
[refine-augonly-indices (get-indices super-method-ht "augment" augment-names)]
|
|
[refine-final-indices (get-indices super-method-ht "augment-final" augment-final-names)]
|
|
[refine-normal-indices (get-indices super-method-ht "augride" augride-normal-names)]
|
|
[rename-super-indices (get-indices super-method-ht "rename-super" rename-super-names)]
|
|
[rename-inner-indices (get-indices method-ht "rename-inner" rename-inner-names)]
|
|
[new-augonly-indices (get-indices method-ht "pubment" pubment-names)]
|
|
[new-final-indices (get-indices method-ht "public-final" public-final-names)]
|
|
[new-normal-indices (get-indices method-ht "public" public-normal-names)])
|
|
|
|
;; -- Check that all interfaces are satisfied --
|
|
(for-each
|
|
(lambda (intf)
|
|
(for-each
|
|
(lambda (var)
|
|
(unless (hash-ref method-ht var #f)
|
|
(obj-error 'class*
|
|
"interface-required method missing: ~a~a~a"
|
|
var
|
|
(for-class name)
|
|
(for-intf (interface-name intf)))))
|
|
(interface-public-ids intf)))
|
|
interfaces)
|
|
(let ([c (get-implement-requirement interfaces 'class* (for-class name))])
|
|
(when (and c (not (subclass? super c)))
|
|
(obj-error 'class*
|
|
"interface-required implementation not satisfied~a~a"
|
|
(for-class name)
|
|
(let ([r (class-name c)])
|
|
(if r
|
|
(format " required class: ~a" r)
|
|
"")))))
|
|
|
|
;; -- For serialization, check that the superclass is compatible --
|
|
(when deserialize-id
|
|
(unless (class-serializer super)
|
|
(obj-error 'class*
|
|
"superclass is not serialiazable, not transparent, and does not implement externalizable<%>: ~e~a"
|
|
super
|
|
(for-class name))))
|
|
|
|
;; ---- Make the class and its interface ----
|
|
(let* ([class-make (if name
|
|
(make-naming-constructor
|
|
struct:class
|
|
(string->symbol (format "class:~a" name)))
|
|
make-class)]
|
|
[interface-make (if name
|
|
(make-naming-constructor
|
|
struct:interface
|
|
(string->symbol (format "interface:~a" name)))
|
|
make-interface)]
|
|
[method-names (append (reverse public-names) super-method-ids)]
|
|
[field-names (append public-field-names super-field-ids)]
|
|
[super-interfaces (cons (class-self-interface super) interfaces)]
|
|
[i (interface-make name super-interfaces #f method-names #f)]
|
|
[methods (if no-method-changes?
|
|
(class-methods super)
|
|
(make-vector method-width))]
|
|
[beta-methods (if no-method-changes?
|
|
(class-beta-methods super)
|
|
(make-vector method-width))]
|
|
[meth-flags (if no-method-changes?
|
|
(class-meth-flags super)
|
|
(make-vector method-width))]
|
|
[c (class-make name
|
|
(add1 (class-pos super))
|
|
(list->vector (append (vector->list (class-supers super)) (list #f)))
|
|
i
|
|
(let-values ([(struct: make- ? -ref -set) (make-struct-type 'insp #f 0 0 #f null inspector)])
|
|
make-)
|
|
method-width method-ht method-names
|
|
methods beta-methods meth-flags
|
|
field-width field-ht field-names
|
|
'struct:object 'object? 'make-object 'field-ref 'field-set!
|
|
init-args
|
|
init-mode
|
|
'init
|
|
#f #f ; serializer is set later
|
|
(and make-struct:prim #t))]
|
|
[obj-name (if name
|
|
(string->symbol (format "object:~a" name))
|
|
'object)]
|
|
;; Used only for prim classes
|
|
[preparer (lambda (name)
|
|
;; Map symbol to number:
|
|
(hash-ref method-ht name))]
|
|
[dispatcher (lambda (obj n)
|
|
;; Extract method:
|
|
(vector-ref (class-methods (object-ref obj)) n))])
|
|
|
|
(setup-all-implemented! i)
|
|
(vector-set! (class-supers c) (add1 (class-pos super)) c)
|
|
|
|
;; --- Make the new object struct ---
|
|
(let*-values ([(prim-object-make prim-object? struct:prim-object)
|
|
(if make-struct:prim
|
|
(make-struct:prim c prop:object preparer dispatcher)
|
|
(values #f #f #f))]
|
|
[(struct:object object-make object? object-field-ref object-field-set!)
|
|
(if make-struct:prim
|
|
;; Use prim struct:
|
|
(values struct:prim-object prim-object-make prim-object? #f #f)
|
|
;; Normal struct creation:
|
|
(make-struct-type obj-name
|
|
(class-struct:object super)
|
|
0 ;; No init fields
|
|
;; Fields for new slots:
|
|
num-fields undefined
|
|
;; Map object property to class:
|
|
(append
|
|
(list (cons prop:object c))
|
|
(if (interface-extension? i printable<%>)
|
|
(list (cons prop:custom-write
|
|
(lambda (obj port write?)
|
|
(if write?
|
|
(send obj custom-write port)
|
|
(send obj custom-display port)))))
|
|
null)
|
|
(if deserialize-id
|
|
(list
|
|
(cons prop:serializable
|
|
;; Serialization:
|
|
(make-serialize-info
|
|
(lambda (obj)
|
|
((class-serializer c) obj))
|
|
deserialize-id
|
|
(and (not inspector)
|
|
(not (interface-extension? i externalizable<%>))
|
|
(eq? #t (class-serializer super)))
|
|
(or (current-load-relative-directory)
|
|
(current-directory)))))
|
|
null))
|
|
inspector))])
|
|
(set-class-struct:object! c struct:object)
|
|
(set-class-object?! c object?)
|
|
(set-class-make-object! c object-make)
|
|
(unless (zero? num-fields)
|
|
;; We need these only if there are fields, used for for public-field
|
|
;; access or for inspection:
|
|
(set-class-field-ref! c object-field-ref)
|
|
(set-class-field-set!! c object-field-set!))
|
|
|
|
;; --- Build field accessors and mutators ---
|
|
;; Use public field names to name the accessors and mutators
|
|
(let-values ([(inh-accessors inh-mutators)
|
|
(values
|
|
(map (lambda (id) (make-class-field-accessor super id))
|
|
inherit-field-names)
|
|
(map (lambda (id) (make-class-field-mutator super id))
|
|
inherit-field-names))])
|
|
;; -- Reset field table to register accessor and mutator info --
|
|
;; There are more accessors and mutators than public fields...
|
|
(let loop ([ids public-field-names][pos 0])
|
|
(unless (null? ids)
|
|
(hash-set! field-ht (car ids) (cons c pos))
|
|
(loop (cdr ids) (add1 pos))))
|
|
|
|
;; -- Extract superclass methods and make rename-inners ---
|
|
(let ([rename-supers (map (lambda (index mname)
|
|
(let ([vec (vector-ref (class-beta-methods super) index)])
|
|
(if (positive? (vector-length vec))
|
|
(or (vector-ref vec (sub1 (vector-length vec)))
|
|
(obj-error 'class*
|
|
(string-append
|
|
"superclass method for override, overment, inherit/super, "
|
|
"or rename-super is not overrideable: ~a~a")
|
|
mname
|
|
(for-class name)))
|
|
(vector-ref (class-methods super) index))))
|
|
rename-super-indices
|
|
rename-super-names)]
|
|
[rename-inners (let ([new-augonly (make-vector method-width #f)])
|
|
(define (get-depth index)
|
|
(+ (if (index . < . (class-method-width super))
|
|
(vector-length (vector-ref (class-beta-methods super)
|
|
index))
|
|
0)
|
|
(if (vector-ref new-augonly index) 0 -1)))
|
|
;; To compute `rename-inner' indices, we need to know which methods
|
|
;; are augonly in this new class.
|
|
(for-each (lambda (id)
|
|
(vector-set! new-augonly (hash-ref method-ht id) #t))
|
|
(append pubment-names overment-names))
|
|
(let ([check-aug
|
|
(lambda (maybe-here?)
|
|
(lambda (mname index)
|
|
(let ([aug-ok?
|
|
(or (if (index . < . (class-method-width super))
|
|
(eq? (vector-ref (class-meth-flags super) index) 'augmentable)
|
|
#f)
|
|
(and maybe-here?
|
|
(or (memq mname pubment-names)
|
|
(memq mname overment-names))))])
|
|
(unless aug-ok?
|
|
(obj-error 'class*
|
|
(string-append
|
|
"superclass method for augride, augment, inherit/inner, "
|
|
"or rename-inner method is not augmentable: ~a~a")
|
|
mname
|
|
(for-class name))))))])
|
|
(for-each (check-aug #f)
|
|
augride-normal-names
|
|
(get-indices method-ht "augride" augride-normal-names))
|
|
(for-each (check-aug #f)
|
|
augment-final-names
|
|
refine-final-indices)
|
|
(for-each (check-aug #t)
|
|
rename-inner-names
|
|
rename-inner-indices))
|
|
;; Now that checking is done, add `augment':
|
|
(for-each (lambda (id)
|
|
(vector-set! new-augonly (hash-ref method-ht id) #t))
|
|
augment-names)
|
|
(map (lambda (mname index)
|
|
(let ([depth (get-depth index)])
|
|
(lambda (obj)
|
|
(vector-ref (vector-ref (class-beta-methods (object-ref obj))
|
|
index)
|
|
depth))))
|
|
rename-inner-names
|
|
rename-inner-indices))])
|
|
;; -- Create method accessors --
|
|
(let ([method-accessors (map (lambda (index)
|
|
(lambda (obj)
|
|
(vector-ref (class-methods (object-ref obj)) index)))
|
|
(append new-normal-indices replace-normal-indices refine-normal-indices
|
|
replace-augonly-indices refine-augonly-indices
|
|
replace-final-indices refine-final-indices
|
|
inherit-indices))])
|
|
|
|
;; -- Get new methods and initializers --
|
|
(let-values ([(new-methods override-methods augride-methods init)
|
|
(apply make-methods
|
|
object-field-ref
|
|
object-field-set!
|
|
(append inh-accessors
|
|
inh-mutators
|
|
rename-supers
|
|
rename-inners
|
|
method-accessors))])
|
|
;; -- Fill in method tables --
|
|
;; First copy old methods
|
|
(unless no-method-changes?
|
|
(hash-for-each
|
|
super-method-ht
|
|
(lambda (name index)
|
|
(vector-set! methods index (vector-ref (class-methods super) index))
|
|
(vector-set! beta-methods index (vector-ref (class-beta-methods super) index))
|
|
(vector-set! meth-flags index (vector-ref (class-meth-flags super) index)))))
|
|
;; Add new methods:
|
|
(for-each (lambda (index method)
|
|
(vector-set! methods index method)
|
|
(vector-set! beta-methods index (vector)))
|
|
(append new-augonly-indices new-final-indices new-normal-indices)
|
|
new-methods)
|
|
;; Override old methods:
|
|
(for-each (lambda (index method id)
|
|
(when (eq? 'final (vector-ref meth-flags index))
|
|
(obj-error 'class*
|
|
"cannot override or augment final method: ~a~a"
|
|
id
|
|
(for-class name)))
|
|
(let ([v (vector-ref beta-methods index)])
|
|
(if (zero? (vector-length v))
|
|
;; Normal mode - set vtable entry
|
|
(vector-set! methods index method)
|
|
;; Under final mode - set extended vtable entry
|
|
(let ([v (list->vector (vector->list v))])
|
|
(vector-set! v (sub1 (vector-length v)) method)
|
|
(vector-set! beta-methods index v))))
|
|
(when (not (vector-ref meth-flags index))
|
|
(vector-set! meth-flags index (not make-struct:prim))))
|
|
(append replace-augonly-indices replace-final-indices replace-normal-indices
|
|
refine-augonly-indices refine-final-indices refine-normal-indices)
|
|
(append override-methods augride-methods)
|
|
(append override-names augride-names))
|
|
;; Update 'augmentable flags:
|
|
(unless no-method-changes?
|
|
(for-each (lambda (id)
|
|
(vector-set! meth-flags (hash-ref method-ht id) 'augmentable))
|
|
(append overment-names pubment-names))
|
|
(for-each (lambda (id)
|
|
(vector-set! meth-flags (hash-ref method-ht id) #t))
|
|
augride-normal-names))
|
|
;; Expand `rename-inner' vector, adding a #f to indicate that
|
|
;; no rename-inner function is available, so far
|
|
(for-each (lambda (id)
|
|
(let ([index (hash-ref method-ht id)])
|
|
(let ([v (list->vector (append (vector->list (vector-ref beta-methods index))
|
|
(list #f)))])
|
|
(vector-set! beta-methods index v))))
|
|
augonly-names)
|
|
;; Mark final methods:
|
|
(for-each (lambda (id)
|
|
(let ([index (hash-ref method-ht id)])
|
|
(vector-set! meth-flags index 'final)))
|
|
final-names)
|
|
|
|
;; --- Install serialize info into class --
|
|
(set-class-serializer!
|
|
c
|
|
(cond
|
|
[(interface-extension? i externalizable<%>)
|
|
(let ([index (car (get-indices method-ht "???" '(externalize)))])
|
|
(lambda (obj)
|
|
(vector ((vector-ref methods index) obj))))]
|
|
[(and (or deserialize-id
|
|
(not inspector))
|
|
(class-serializer super))
|
|
=> (lambda (ss)
|
|
(lambda (obj)
|
|
(vector (cons (ss obj)
|
|
(let loop ([i 0])
|
|
(if (= i num-fields)
|
|
null
|
|
(cons (object-field-ref obj i)
|
|
(loop (add1 i)))))))))]
|
|
[else #f]))
|
|
|
|
(set-class-fixup!
|
|
c
|
|
;; Used only for non-externalizable:
|
|
(lambda (o args)
|
|
(if (pair? args)
|
|
(begin
|
|
((class-fixup super) o (vector-ref (car args) 0))
|
|
(let loop ([i 0][args (cdr args)])
|
|
(unless (= i num-fields)
|
|
(object-field-set! o i (car args))
|
|
(loop (add1 i) (cdr args)))))
|
|
(begin
|
|
((class-fixup super) o args)
|
|
(let loop ([i 0])
|
|
(unless (= i num-fields)
|
|
(object-field-set! o i (object-field-ref args i))
|
|
(loop (add1 i))))))))
|
|
|
|
;; --- Install initializer into class ---
|
|
(set-class-init! c init)
|
|
|
|
;; -- result is the class, and maybe deserialize-info ---
|
|
(if deserialize-id
|
|
(values c (make-deserialize-info
|
|
(if (interface-extension? i externalizable<%>)
|
|
(lambda (args)
|
|
(let ([o (make-object c)])
|
|
(send o internalize args)
|
|
o))
|
|
(lambda (args)
|
|
(let ([o (object-make)])
|
|
((class-fixup c) o args)
|
|
o)))
|
|
(if (interface-extension? i externalizable<%>)
|
|
(lambda ()
|
|
(error 'deserialize "cannot deserialize instance with cycles~a"
|
|
(for-class name)))
|
|
(lambda ()
|
|
(let ([o (object-make)])
|
|
(values o
|
|
(lambda (o2)
|
|
((class-fixup c) o o2))))))))
|
|
c))))))))))))
|
|
|
|
(define (check-still-unique name syms what)
|
|
(let ([ht (make-hasheq)])
|
|
(for-each (lambda (s)
|
|
(when (hash-ref ht s
|
|
(lambda ()
|
|
(hash-set! ht s #t)
|
|
#f))
|
|
(obj-error 'class* "external ~a mapped to overlapping keys~a"
|
|
what
|
|
(for-class name))))
|
|
syms)))
|
|
|
|
(define-values (prop:object object? object-ref) (make-struct-type-property 'object))
|
|
|
|
;;--------------------------------------------------------------------
|
|
;; interfaces
|
|
;;--------------------------------------------------------------------
|
|
|
|
;; >> Simplistic implementation for now <<
|
|
|
|
(define-syntax _interface
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ (interface-expr ...) var ...)
|
|
(let ([vars (syntax->list (syntax (var ...)))]
|
|
[name (syntax-local-infer-name stx)])
|
|
(for-each
|
|
(lambda (v)
|
|
(unless (identifier? v)
|
|
(raise-syntax-error #f
|
|
"not an identifier"
|
|
stx
|
|
v)))
|
|
vars)
|
|
(let ([dup (check-duplicate-identifier vars)])
|
|
(when dup
|
|
(raise-syntax-error #f
|
|
"duplicate name"
|
|
stx
|
|
dup)))
|
|
(with-syntax ([name (datum->syntax #f name #f)]
|
|
[(var ...) (map localize vars)])
|
|
(syntax/loc
|
|
stx
|
|
(compose-interface
|
|
'name
|
|
(list interface-expr ...)
|
|
`(var ...)))))])))
|
|
|
|
(define-struct interface
|
|
(name ; symbol
|
|
supers ; (listof interface)
|
|
[all-implemented ; hash-table: interface -> #t
|
|
#:mutable]
|
|
public-ids ; (listof symbol) (in any order?!?)
|
|
[class ; (union #f class) -- means that anything implementing
|
|
#:mutable]) ; this interface must be derived from this class
|
|
#:inspector insp)
|
|
|
|
(define (compose-interface name supers vars)
|
|
(for-each
|
|
(lambda (intf)
|
|
(unless (interface? intf)
|
|
(obj-error 'interface
|
|
"superinterface expression returned a non-interface: ~a~a"
|
|
intf
|
|
(for-intf name))))
|
|
supers)
|
|
(let ([ht (make-hasheq)])
|
|
(for-each
|
|
(lambda (var)
|
|
(hash-set! ht var #t))
|
|
vars)
|
|
;; Check that vars don't already exist in supers:
|
|
(for-each
|
|
(lambda (super)
|
|
(for-each
|
|
(lambda (var)
|
|
(when (hash-ref ht var #f)
|
|
(obj-error 'interface "variable already in superinterface: ~a~a~a"
|
|
var
|
|
(for-intf name)
|
|
(let ([r (interface-name super)])
|
|
(if r
|
|
(format " already in: ~a" r)
|
|
"")))))
|
|
(interface-public-ids super)))
|
|
supers)
|
|
;; Check for [conflicting] implementation requirements
|
|
(let ([class (get-implement-requirement supers 'interface (for-intf name))]
|
|
[interface-make (if name
|
|
(make-naming-constructor
|
|
struct:interface
|
|
(string->symbol (format "interface:~a" name)))
|
|
make-interface)])
|
|
;; Add supervars to table:
|
|
(for-each
|
|
(lambda (super)
|
|
(for-each
|
|
(lambda (var) (hash-set! ht var #t))
|
|
(interface-public-ids super)))
|
|
supers)
|
|
;; Done
|
|
(let ([i (interface-make name supers #f (hash-map ht (lambda (k v) k)) class)])
|
|
(setup-all-implemented! i)
|
|
i))))
|
|
|
|
;; setup-all-implemented! : interface -> void
|
|
;; Creates the hash table for all implemented interfaces
|
|
(define (setup-all-implemented! i)
|
|
(let ([ht (make-hasheq)])
|
|
(hash-set! ht i #t)
|
|
(for-each (lambda (si)
|
|
(hash-for-each
|
|
(interface-all-implemented si)
|
|
(lambda (k v)
|
|
(hash-set! ht k #t))))
|
|
(interface-supers i))
|
|
(set-interface-all-implemented! i ht)))
|
|
|
|
(define (get-implement-requirement interfaces where for)
|
|
(let loop ([class #f]
|
|
[supers interfaces])
|
|
(if (null? supers)
|
|
class
|
|
(let ([c (interface-class (car supers))])
|
|
(loop
|
|
(cond
|
|
[(not c) class]
|
|
[(not class) c]
|
|
[(subclass? c class) class]
|
|
[(subclass? class c) c]
|
|
[else
|
|
(obj-error
|
|
where
|
|
"conflicting class implementation requirements in superinterfaces~a"
|
|
for)])
|
|
(cdr supers))))))
|
|
|
|
;;--------------------------------------------------------------------
|
|
;; object%
|
|
;;--------------------------------------------------------------------
|
|
|
|
(define (make-naming-constructor type name)
|
|
(let-values ([(struct: make- ? -accessor -mutator)
|
|
(make-struct-type name type 0 0 #f null insp)])
|
|
make-))
|
|
|
|
(define object<%> ((make-naming-constructor struct:interface 'interface:object%)
|
|
'object% null #f null #f))
|
|
(setup-all-implemented! object<%>)
|
|
(define object% ((make-naming-constructor struct:class 'class:object%)
|
|
'object%
|
|
0 (vector #f)
|
|
object<%>
|
|
void ; never inspectable
|
|
|
|
0 (make-hasheq) null
|
|
(vector) (vector) (vector)
|
|
|
|
0 (make-hasheq) null
|
|
|
|
'struct:object object? 'make-object
|
|
'field-ref-not-needed 'field-set!-not-needed
|
|
|
|
null
|
|
'normal
|
|
|
|
(lambda (this super-init si_c si_inited? si_leftovers args)
|
|
(unless (null? args)
|
|
(unused-args-error this args))
|
|
(void))
|
|
|
|
(lambda (obj) #(())) ; serialize
|
|
(lambda (obj args) (void)) ; deserialize-fixup
|
|
|
|
#t)) ; no super-init
|
|
|
|
(vector-set! (class-supers object%) 0 object%)
|
|
(let*-values ([(struct:obj make-obj obj? -get -set!)
|
|
(make-struct-type 'object #f 0 0 #f (list (cons prop:object object%)) #f)])
|
|
(set-class-struct:object! object% struct:obj)
|
|
(set-class-make-object! object% make-obj))
|
|
(set-class-object?! object% object?) ; don't use struct pred; it wouldn't work with prim classes
|
|
|
|
(set-interface-class! object<%> object%)
|
|
|
|
;;--------------------------------------------------------------------
|
|
;; instantiation
|
|
;;--------------------------------------------------------------------
|
|
|
|
(define-syntaxes (new new-traced)
|
|
|
|
(let* ([core-new
|
|
(lambda (instantiate-stx stx)
|
|
(syntax-case stx ()
|
|
[(_ cls (id arg) ...)
|
|
(andmap identifier? (syntax->list (syntax (id ...))))
|
|
(quasisyntax/loc stx
|
|
((unsyntax instantiate-stx) cls () (id arg) ...))]
|
|
[(_ cls (id arg) ...)
|
|
(for-each (lambda (id)
|
|
(unless (identifier? id)
|
|
(raise-syntax-error 'new "expected identifier" stx id)))
|
|
(syntax->list (syntax (id ...))))]
|
|
[(_ cls pr ...)
|
|
(for-each
|
|
(lambda (pr)
|
|
(syntax-case pr ()
|
|
[(x y) (void)]
|
|
[else (raise-syntax-error 'new "expected name and value binding" stx pr)]))
|
|
(syntax->list (syntax (pr ...))))]))])
|
|
|
|
(values
|
|
(lambda (stx) (core-new (syntax/loc stx instantiate) stx))
|
|
(lambda (stx) (core-new (syntax/loc stx instantiate-traced) stx)))))
|
|
|
|
(define make-object
|
|
(lambda (class . args)
|
|
(do-make-object class args null)))
|
|
|
|
(define make-object-traced
|
|
(lambda (class . args)
|
|
(do-make-object-traced class args null)))
|
|
|
|
(define-syntaxes (instantiate instantiate-traced)
|
|
|
|
(let* ([core-instantiate
|
|
(lambda (do-make-object-stx stx)
|
|
(syntax-case stx ()
|
|
[(form class (arg ...) . x)
|
|
(with-syntax ([orig-stx stx])
|
|
(quasisyntax/loc stx
|
|
(-instantiate (unsyntax do-make-object-stx)
|
|
orig-stx (class) (list arg ...) . x)))]))])
|
|
|
|
(values
|
|
(lambda (stx)
|
|
(core-instantiate (syntax/loc stx do-make-object) stx))
|
|
(lambda (stx)
|
|
(core-instantiate (syntax/loc stx do-make-object-traced) stx)))))
|
|
|
|
;; Helper; used by instantiate and super-instantiate
|
|
(define-syntax -instantiate
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ do-make-object orig-stx (maker-arg ...) args (kw arg) ...)
|
|
(andmap identifier? (syntax->list (syntax (kw ...))))
|
|
(with-syntax ([(kw ...) (map localize (syntax->list (syntax (kw ...))))])
|
|
(syntax/loc stx
|
|
(do-make-object maker-arg ...
|
|
args
|
|
(list (cons `kw arg)
|
|
...))))]
|
|
[(_ super-make-object orig-stx (make-arg ...) args kwarg ...)
|
|
;; some kwarg must be bad:
|
|
(for-each (lambda (kwarg)
|
|
(syntax-case kwarg ()
|
|
[(kw arg)
|
|
(identifier? (syntax kw))
|
|
'ok]
|
|
[(kw arg)
|
|
(raise-syntax-error
|
|
#f
|
|
"by-name argument does not start with an identifier"
|
|
(syntax orig-stx)
|
|
kwarg)]
|
|
[_else
|
|
(raise-syntax-error
|
|
#f
|
|
"ill-formed by-name argument"
|
|
(syntax orig-stx)
|
|
kwarg)]))
|
|
(syntax->list (syntax (kwarg ...))))])))
|
|
|
|
(define (alist->sexp alist)
|
|
(map (lambda (pair) (list (car pair) (cdr pair))) alist))
|
|
|
|
(define-traced (do-make-object class by-pos-args named-args)
|
|
(unless (class? class)
|
|
(raise-type-error 'instantiate "class" class))
|
|
(let ([o ((class-make-object class))])
|
|
(trace-begin
|
|
;; Initialize it:
|
|
(trace (new-event class o (alist->sexp (get-field-alist o))))
|
|
(trace (initialize-call-event
|
|
o (string->symbol "(constructor)")
|
|
(cons (alist->sexp named-args) by-pos-args)))
|
|
(continue-make-object o class by-pos-args named-args #t)
|
|
(trace (finalize-call-event o))
|
|
o)))
|
|
|
|
(define (get-field-alist obj)
|
|
(map (lambda (id) (cons id (get-field/proc id obj)))
|
|
(field-names obj)))
|
|
|
|
(define (continue-make-object o c by-pos-args named-args explict-named-args?)
|
|
(let ([by-pos-only? (not (class-init-args c))])
|
|
;; When a superclass has #f for init-args (meaning "by-pos args with no names"),
|
|
;; some propagated named args may have #f keys; move them to by-position args.
|
|
(let-values ([(by-pos-args named-args)
|
|
(if by-pos-only?
|
|
(let ([l (filter (lambda (x) (not (car x))) named-args)])
|
|
(if (pair? l)
|
|
(values (append by-pos-args (map cdr l))
|
|
(filter car named-args))
|
|
(values by-pos-args named-args)))
|
|
(values by-pos-args named-args))])
|
|
;; Primitive class with by-pos arguments?
|
|
(when by-pos-only?
|
|
(unless (null? named-args)
|
|
(if explict-named-args?
|
|
(obj-error
|
|
'instantiate
|
|
"class has only by-position initializers, but given by-name arguments:~a~a"
|
|
(make-named-arg-string named-args)
|
|
(for-class (class-name c)))
|
|
;; If args were implicit from subclass, should report as unused:
|
|
(unused-args-error o named-args))))
|
|
;; Merge by-pos into named args:
|
|
(let* ([named-args (if (not by-pos-only?)
|
|
;; Normal merge
|
|
(do-merge by-pos-args (class-init-args c) c named-args by-pos-args c)
|
|
;; Non-merge for by-position initializers
|
|
by-pos-args)]
|
|
[leftovers (if (not by-pos-only?)
|
|
(get-leftovers named-args (class-init-args c))
|
|
null)])
|
|
;; In 'list mode, make sure no by-name arguments are left over
|
|
(when (eq? 'list (class-init-mode c))
|
|
(unless (or (null? leftovers)
|
|
(not (ormap car leftovers)))
|
|
(unused-args-error o (filter car leftovers))))
|
|
(unless (and (eq? c object%)
|
|
(null? named-args))
|
|
(let ([inited? (box (class-no-super-init? c))])
|
|
;; ----- Execute the class body -----
|
|
((class-init c)
|
|
o
|
|
continue-make-super
|
|
c inited? leftovers ; merely passed through to continue-make-super
|
|
named-args)
|
|
(unless (unbox inited?)
|
|
(obj-error 'instantiate "superclass initialization not invoked by initialization~a"
|
|
(for-class (class-name c))))))))))
|
|
|
|
(define (continue-make-super o c inited? leftovers by-pos-args new-named-args)
|
|
(when (unbox inited?)
|
|
(obj-error 'instantiate "superclass already initialized by class initialization~a"
|
|
(for-class (class-name c))))
|
|
(set-box! inited? #t)
|
|
(let ([named-args (if (eq? 'list (class-init-mode c))
|
|
;; all old args must have been used up
|
|
new-named-args
|
|
;; Normal mode: merge leftover keyword-based args with new ones
|
|
(append
|
|
new-named-args
|
|
leftovers))])
|
|
(continue-make-object o
|
|
(vector-ref (class-supers c) (sub1 (class-pos c)))
|
|
by-pos-args
|
|
named-args
|
|
(pair? new-named-args))))
|
|
|
|
(define (do-merge al nl ic named-args by-pos-args c)
|
|
(cond
|
|
[(null? al) named-args]
|
|
[(null? nl)
|
|
;; continue mapping with superclass init args, if allowed
|
|
(let ([super (and (eq? 'normal (class-init-mode ic))
|
|
(positive? (class-pos ic))
|
|
(vector-ref (class-supers ic) (sub1 (class-pos ic))))])
|
|
(cond
|
|
[super
|
|
(if (class-init-args super)
|
|
(do-merge al (class-init-args super) super named-args by-pos-args c)
|
|
;; Like 'list mode:
|
|
(append (map (lambda (x) (cons #f x)) al)
|
|
named-args))]
|
|
[(eq? 'list (class-init-mode ic))
|
|
;; All unconsumed named-args must have #f
|
|
;; "name"s, otherwise an error is raised in
|
|
;; the leftovers checking.
|
|
(append (map (lambda (x) (cons #f x)) al)
|
|
named-args)]
|
|
[else
|
|
(obj-error 'instantiate
|
|
"too many initialization arguments:~a~a"
|
|
(make-pos-arg-string by-pos-args)
|
|
(for-class (class-name c)))]))]
|
|
[else (cons (cons (car nl) (car al))
|
|
(do-merge (cdr al) (cdr nl) ic named-args by-pos-args c))]))
|
|
|
|
(define (get-leftovers l names)
|
|
(cond
|
|
[(null? l) null]
|
|
[(memq (caar l) names)
|
|
(get-leftovers (cdr l) (remq (caar l) names))]
|
|
[else (cons (car l) (get-leftovers (cdr l) names))]))
|
|
|
|
(define (extract-arg class-name name arguments default)
|
|
(if (symbol? name)
|
|
;; Normal mode
|
|
(let ([a (assq name arguments)])
|
|
(cond
|
|
[a (cdr a)]
|
|
[default (default)]
|
|
[else (missing-argument-error class-name name)]))
|
|
;; By-position mode
|
|
(cond
|
|
[(< name (length arguments))
|
|
(cdr (list-ref arguments name))]
|
|
[default (default)]
|
|
[else (obj-error 'instantiate "too few initialization arguments")])))
|
|
|
|
(define (extract-rest-args skip arguments)
|
|
(if (< skip (length arguments))
|
|
(map cdr (list-tail arguments skip))
|
|
null))
|
|
|
|
(define (make-pos-arg-string args)
|
|
(let ([len (length args)])
|
|
(apply string-append
|
|
(map (lambda (a)
|
|
(format " ~e" a))
|
|
args))))
|
|
|
|
(define (make-named-arg-string args)
|
|
(let loop ([args args][count 0])
|
|
(cond
|
|
[(null? args) ""]
|
|
[(= count 3) " ..."]
|
|
[else (let ([rest (loop (cdr args) (add1 count))])
|
|
(format " (~a ~e)~a"
|
|
(caar args)
|
|
(cdar args)
|
|
rest))])))
|
|
|
|
(define (unused-args-error this args)
|
|
(let ([arg-string (make-named-arg-string args)])
|
|
(obj-error 'instantiate "unused initialization arguments:~a~a"
|
|
arg-string
|
|
(for-class/which "instantiated" (class-name (object-ref this))))))
|
|
|
|
(define (missing-argument-error class-name name)
|
|
(obj-error 'instantiate "no argument for required init variable: ~a~a"
|
|
name
|
|
(if class-name (format " in class: ~a" class-name) "")))
|
|
|
|
;;--------------------------------------------------------------------
|
|
;; methods and fields
|
|
;;--------------------------------------------------------------------
|
|
|
|
(define-syntaxes (send send/apply send-traced send/apply-traced)
|
|
(let ()
|
|
|
|
(define (do-method traced? stx form obj name args rest-arg?)
|
|
(with-syntax ([(sym method receiver)
|
|
(generate-temporaries (syntax (1 2 3)))])
|
|
(quasisyntax/loc stx
|
|
(let*-values ([(sym) (quasiquote (unsyntax (localize name)))]
|
|
[(method receiver)
|
|
(find-method/who '(unsyntax form)
|
|
(unsyntax obj)
|
|
sym)])
|
|
(unsyntax
|
|
(make-method-call
|
|
traced?
|
|
stx
|
|
(syntax/loc stx receiver)
|
|
(syntax/loc stx unwrap-object)
|
|
(syntax/loc stx method)
|
|
(syntax/loc stx sym)
|
|
args
|
|
rest-arg?))))))
|
|
|
|
(define (core-send traced? apply?)
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(form obj name . args)
|
|
(identifier? (syntax name))
|
|
(if (stx-list? (syntax args))
|
|
;; (send obj name arg ...) or (send/apply obj name arg ...)
|
|
(do-method traced? stx #'form #'obj #'name #'args apply?)
|
|
(if apply?
|
|
;; (send/apply obj name arg ... . rest)
|
|
(raise-syntax-error
|
|
#f "bad syntax (illegal use of `.')" stx)
|
|
;; (send obj name arg ... . rest)
|
|
(do-method traced? stx #'form #'obj #'name
|
|
(flatten-args #'args) #t)))]
|
|
[(form obj name . args)
|
|
(raise-syntax-error
|
|
#f "method name is not an identifier" stx #'name)])))
|
|
|
|
(values
|
|
;; send
|
|
(core-send #f #f)
|
|
;; send/apply
|
|
(core-send #f #t)
|
|
;; send-traced
|
|
(core-send #t #f)
|
|
;; send/apply-traced
|
|
(core-send #t #t))))
|
|
|
|
(define-syntaxes (send* send*-traced)
|
|
(let* ([core-send*
|
|
(lambda (traced?)
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(form obj clause ...)
|
|
(quasisyntax/loc stx
|
|
(let* ([o obj])
|
|
(unsyntax-splicing
|
|
(map
|
|
(lambda (clause-stx)
|
|
(syntax-case clause-stx ()
|
|
[(meth . args)
|
|
(quasisyntax/loc stx
|
|
((unsyntax (if traced?
|
|
(syntax/loc stx send-traced)
|
|
(syntax/loc stx send)))
|
|
o meth . args))]
|
|
[_ (raise-syntax-error
|
|
#f "bad method call" stx clause-stx)]))
|
|
(syntax->list (syntax (clause ...)))))))])))])
|
|
(values (core-send* #f) (core-send* #t))))
|
|
|
|
;; find-method/who : symbol[top-level-form/proc-name]
|
|
;; any[object]
|
|
;; symbol[method-name]
|
|
;; -> (values method-proc object)
|
|
;; returns the method's procedure and a function to unwrap `this' in the case
|
|
;; that this is a wrapper object that is just "falling thru".
|
|
(define (find-method/who who in-object name)
|
|
(unless (object? in-object)
|
|
(obj-error who "target is not an object: ~e for method: ~a"
|
|
in-object name))
|
|
|
|
(let-syntax ([loop-body
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ abs-object wrapper-case)
|
|
(identifier? (syntax abs-object))
|
|
(syntax
|
|
(let* ([c (object-ref abs-object)]
|
|
[pos (hash-ref (class-method-ht c) name #f)])
|
|
(cond
|
|
[pos (values (vector-ref (class-methods c) pos) abs-object)]
|
|
[(wrapper-object? abs-object) wrapper-case]
|
|
[else
|
|
(obj-error who "no such method: ~a~a"
|
|
name
|
|
(for-class (class-name c)))])))]))])
|
|
(loop-body
|
|
in-object
|
|
(let loop ([loop-object in-object])
|
|
(loop-body
|
|
loop-object
|
|
(loop (wrapper-object-wrapped loop-object)))))))
|
|
|
|
|
|
(define (class-field-X who which cwhich class name)
|
|
(unless (class? class)
|
|
(raise-type-error who "class" class))
|
|
(unless (symbol? name)
|
|
(raise-type-error who "symbol" name))
|
|
(let ([p (hash-ref (class-field-ht class) name
|
|
(lambda ()
|
|
(obj-error who "no such field: ~a~a"
|
|
name
|
|
(for-class (class-name class)))))])
|
|
(which (cwhich (car p)) (cdr p))))
|
|
|
|
(define (make-class-field-accessor class name)
|
|
(class-field-X 'class-field-accessor
|
|
make-struct-field-accessor class-field-ref
|
|
class name))
|
|
|
|
(define (make-class-field-mutator class name)
|
|
(class-field-X 'class-field-mutator
|
|
make-struct-field-mutator class-field-set!
|
|
class name))
|
|
|
|
(define-struct generic (name applicable))
|
|
|
|
;; Internally, make-generic comes from the struct def.
|
|
;; Externally, make-generic is the following procedure.
|
|
;; The extra `let' gives it the right name.
|
|
(define make-generic/proc
|
|
(let ([make-generic
|
|
(lambda (class name)
|
|
(unless (or (class? class) (interface? class))
|
|
(raise-type-error 'make-generic "class or interface" class))
|
|
(unless (symbol? name)
|
|
(raise-type-error 'make-generic "symbol" name))
|
|
(make-generic
|
|
name
|
|
(if (interface? class)
|
|
(let ([intf class])
|
|
(unless (method-in-interface? name intf)
|
|
(obj-error 'make-generic "no such method: ~a~a"
|
|
name
|
|
(for-intf (interface-name intf))))
|
|
(lambda (obj)
|
|
(unless (is-a? obj intf)
|
|
(raise-type-error
|
|
(string->symbol (format "generic:~a~a" name (for-intf (interface-name intf))))
|
|
(format "instance~a" (for-intf (interface-name intf)))
|
|
obj))
|
|
(let-values ([(mth ths) (find-method/who 'make-generic obj name)])
|
|
mth)))
|
|
(let* ([pos (hash-ref (class-method-ht class) name
|
|
(lambda ()
|
|
(obj-error 'make-generic "no such method: ~a~a"
|
|
name
|
|
(for-class (class-name class)))))]
|
|
[instance? (class-object? class)]
|
|
[dynamic-generic
|
|
(lambda (obj)
|
|
(unless (instance? obj)
|
|
(raise-type-error
|
|
(string->symbol (format "generic:~a~a" name (for-class (class-name class))))
|
|
(format "instance~a" (for-class (class-name class)))
|
|
obj))
|
|
(vector-ref (class-methods (object-ref obj)) pos))])
|
|
(if (eq? 'final (vector-ref (class-meth-flags class) pos))
|
|
(let ([method (vector-ref (class-methods class) pos)])
|
|
(lambda (obj)
|
|
(unless (instance? obj)
|
|
(dynamic-generic obj))
|
|
method))
|
|
dynamic-generic)))))])
|
|
make-generic))
|
|
|
|
(define-syntaxes (send-generic send-generic-traced)
|
|
(let ()
|
|
(define (core-send-generic traced?)
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ object generic . args)
|
|
(let* ([args-stx (syntax args)]
|
|
[proper? (stx-list? args-stx)]
|
|
[flat-stx (if proper? args-stx (flatten-args args-stx))])
|
|
(with-syntax ([(gen obj)
|
|
(generate-temporaries (syntax (generic object)))])
|
|
(quasisyntax/loc stx
|
|
(let* ([obj object]
|
|
[gen generic])
|
|
(unsyntax
|
|
(make-method-call
|
|
traced?
|
|
stx
|
|
(syntax obj)
|
|
(syntax/loc stx unwrap-object)
|
|
(syntax/loc stx ((generic-applicable gen) obj))
|
|
(syntax/loc stx (generic-name gen))
|
|
flat-stx
|
|
(not proper?)))))))])))
|
|
(values (core-send-generic #f) (core-send-generic #t))))
|
|
|
|
(define-syntaxes (class-field-accessor class-field-mutator generic/form)
|
|
(let ([mk
|
|
(lambda (make targets)
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ class-expr name)
|
|
(let ([name (syntax name)])
|
|
(unless (identifier? name)
|
|
(raise-syntax-error
|
|
#f
|
|
"expected an indentifier"
|
|
stx
|
|
name))
|
|
(with-syntax ([name (localize name)]
|
|
[make make])
|
|
(syntax/loc stx (make class-expr `name))))]
|
|
[(_ class-expr)
|
|
(raise-syntax-error
|
|
#f
|
|
(format "expected a field name after the ~a expression"
|
|
targets)
|
|
stx)])))])
|
|
(values
|
|
(mk (quote-syntax make-class-field-accessor) "class")
|
|
(mk (quote-syntax make-class-field-mutator) "class")
|
|
(mk (quote-syntax make-generic/proc) "class or interface"))))
|
|
|
|
(define-syntax (class-field-accessor-traced stx)
|
|
(syntax-case stx ()
|
|
[(form class name)
|
|
(syntax/loc stx
|
|
(let* ([accessor (class-field-accessor class name)])
|
|
(lambda (obj)
|
|
(begin0 (accessor obj)
|
|
(get-event obj 'name)))))]))
|
|
|
|
(define-syntax (class-field-mutator-traced stx)
|
|
(syntax-case stx ()
|
|
[(form class name)
|
|
(syntax/loc stx
|
|
(let* ([mutator (class-field-mutator class name)])
|
|
(lambda (obj value)
|
|
(begin0 (mutator obj value)
|
|
(set-event obj 'name value)))))]))
|
|
|
|
(define-syntaxes (get-field get-field-traced)
|
|
(let ()
|
|
(define (core-get-field traced?)
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ name obj)
|
|
(identifier? (syntax name))
|
|
(with-syntax ([get (if traced?
|
|
(syntax get-field/proc-traced)
|
|
(syntax get-field/proc))]
|
|
[localized (localize (syntax name))])
|
|
(syntax (get `localized obj)))]
|
|
[(_ name obj)
|
|
(raise-syntax-error
|
|
'get-field "expected a field name as first argument"
|
|
stx (syntax name))])))
|
|
(values (core-get-field #f) (core-get-field #t))))
|
|
|
|
(define-traced (get-field/proc id obj)
|
|
(unless (object? obj)
|
|
(raise-mismatch-error
|
|
'get-field
|
|
"expected an object, got "
|
|
obj))
|
|
(trace-begin
|
|
(trace (get-event obj id))
|
|
(let loop ([obj obj])
|
|
(let* ([cls (object-ref obj)]
|
|
[field-ht (class-field-ht cls)]
|
|
[index (hash-ref
|
|
field-ht
|
|
id
|
|
#f)])
|
|
(cond
|
|
[index
|
|
((class-field-ref (car index)) obj (cdr index))]
|
|
[(wrapper-object? obj)
|
|
(loop (wrapper-object-wrapped obj))]
|
|
[else
|
|
(raise-mismatch-error
|
|
'get-field
|
|
(format "expected an object that has a field named ~s, got " id)
|
|
obj)])))))
|
|
|
|
(define-syntaxes (field-bound? field-bound?-traced)
|
|
(let ()
|
|
(define (core-field-bound? traced?)
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ name obj)
|
|
(identifier? (syntax name))
|
|
(with-syntax ([localized (localize (syntax name))]
|
|
[bound? (if traced?
|
|
(syntax field-bound?/proc-traced)
|
|
(syntax field-bound?/proc))])
|
|
(syntax (bound? `localized obj)))]
|
|
[(_ name obj)
|
|
(raise-syntax-error
|
|
'field-bound? "expected a field name as first argument"
|
|
stx (syntax name))])))
|
|
(values (core-field-bound? #f) (core-field-bound? #t))))
|
|
|
|
(define-traced (field-bound?/proc id obj)
|
|
(unless (object? obj)
|
|
(raise-mismatch-error
|
|
'field-bound?
|
|
"expected an object, got "
|
|
obj))
|
|
(trace-begin
|
|
(trace (inspect-event obj))
|
|
(let loop ([obj obj])
|
|
(let* ([cls (object-ref obj)]
|
|
[field-ht (class-field-ht cls)])
|
|
(or (and (hash-ref field-ht id #f)
|
|
#t) ;; ensure that only #t and #f leak out, not bindings in ht
|
|
(and (wrapper-object? obj)
|
|
(loop (wrapper-object-wrapped obj))))))))
|
|
|
|
(define-traced (field-names obj)
|
|
(unless (object? obj)
|
|
(raise-mismatch-error
|
|
'field-names
|
|
"expected an object, got "
|
|
obj))
|
|
(trace-begin
|
|
(trace (inspect-event obj))
|
|
(let loop ([obj obj])
|
|
(let* ([cls (object-ref obj)]
|
|
[field-ht (class-field-ht cls)]
|
|
[flds (filter interned? (hash-map field-ht (lambda (x y) x)))])
|
|
(if (wrapper-object? obj)
|
|
(append flds (loop (wrapper-object-wrapped obj)))
|
|
flds)))))
|
|
|
|
(define-syntaxes (with-method with-method-traced)
|
|
(let ()
|
|
(define (core-with-method traced?)
|
|
(lambda (stx)
|
|
(syntax-case stx ()
|
|
[(_ ([id (obj-expr name)] ...) body0 body1 ...)
|
|
(let ([ids (syntax->list (syntax (id ...)))]
|
|
[names (syntax->list (syntax (name ...)))])
|
|
(for-each (lambda (id name)
|
|
(unless (identifier? id)
|
|
(raise-syntax-error #f
|
|
"not an identifier for binding"
|
|
stx
|
|
id))
|
|
(unless (identifier? name)
|
|
(raise-syntax-error #f
|
|
"not an identifier for method name"
|
|
stx
|
|
name)))
|
|
ids names)
|
|
(with-syntax ([(method ...) (generate-temporaries ids)]
|
|
[(method-obj ...) (generate-temporaries ids)]
|
|
[(name ...) (map localize names)]
|
|
[trace-flag (if traced? (syntax/loc stx #t) (syntax/loc stx #f))])
|
|
(syntax/loc stx (let-values ([(method method-obj)
|
|
(let ([obj obj-expr])
|
|
(find-method/who 'with-method obj `name))]
|
|
...)
|
|
(letrec-syntaxes+values ([(id) (make-with-method-map
|
|
trace-flag
|
|
(quote-syntax set!)
|
|
(quote-syntax id)
|
|
(quote-syntax method)
|
|
(quote-syntax method-obj)
|
|
(syntax unwrap-object))]
|
|
...)
|
|
()
|
|
body0 body1 ...)))))]
|
|
;; Error cases:
|
|
[(_ (clause ...) . body)
|
|
(begin
|
|
(for-each (lambda (clause)
|
|
(syntax-case clause ()
|
|
[(id (obj-expr name))
|
|
(and (identifier? (syntax id))
|
|
(identifier? (syntax name)))
|
|
'ok]
|
|
[_else
|
|
(raise-syntax-error
|
|
#f
|
|
"binding clause is not of the form (identifier (object-expr method-identifier))"
|
|
stx
|
|
clause)]))
|
|
(syntax->list (syntax (clause ...))))
|
|
;; If we get here, the body must be bad
|
|
(if (stx-null? (syntax body))
|
|
(raise-syntax-error
|
|
#f
|
|
"empty body"
|
|
stx)
|
|
(raise-syntax-error
|
|
#f
|
|
"bad syntax (illegal use of `.')"
|
|
stx)))]
|
|
[(_ x . rest)
|
|
(raise-syntax-error
|
|
#f
|
|
"not a binding sequence"
|
|
stx
|
|
(syntax x))])))
|
|
|
|
(values
|
|
;; with-method
|
|
(core-with-method #f)
|
|
;; with-method-traced
|
|
(core-with-method #t))))
|
|
|
|
|
|
;;--------------------------------------------------------------------
|
|
;; class, interface, and object properties
|
|
;;--------------------------------------------------------------------
|
|
|
|
(define-traced (is-a? v c)
|
|
(trace-begin
|
|
(trace (when (object? v)
|
|
(inspect-event v)))
|
|
(cond
|
|
[(class? c) ((class-object? c) (unwrap-object v))]
|
|
[(interface? c)
|
|
(and (object? v)
|
|
(implementation? (object-ref (unwrap-object v)) c))]
|
|
[else (raise-type-error 'is-a? "class or interface" 1 v c)])))
|
|
|
|
(define (subclass? v c)
|
|
(unless (class? c)
|
|
(raise-type-error 'subclass? "class" 1 v c))
|
|
(and (class? v)
|
|
(let ([p (class-pos c)])
|
|
(and (<= p (class-pos v))
|
|
(eq? c (vector-ref (class-supers v) p))))))
|
|
|
|
(define-traced (object-interface o)
|
|
(unless (object? o)
|
|
(raise-type-error 'object-interface "object" o))
|
|
(trace-begin
|
|
(trace (inspect-event o))
|
|
(class-self-interface (object-ref (unwrap-object o)))))
|
|
|
|
(define-traced (object-method-arity-includes? o name cnt)
|
|
(unless (object? o)
|
|
(raise-type-error 'object-method-arity-includes? "object" o))
|
|
(unless (symbol? name)
|
|
(raise-type-error 'object-method-arity-includes? "symbol" name))
|
|
(unless (and (integer? cnt)
|
|
(exact? cnt)
|
|
(not (negative? cnt)))
|
|
(raise-type-error 'object-method-arity-includes? "non-negative exact integer" cnt))
|
|
(trace-begin
|
|
(trace (inspect-event o))
|
|
(let loop ([o o])
|
|
(let* ([c (object-ref o)]
|
|
[pos (hash-ref (class-method-ht c) name #f)])
|
|
(cond
|
|
[pos (procedure-arity-includes? (vector-ref (class-methods c) pos)
|
|
(add1 cnt))]
|
|
[(wrapper-object? o) (loop (wrapper-object-wrapped o))]
|
|
[else #f])))))
|
|
|
|
(define (implementation? v i)
|
|
(unless (interface? i)
|
|
(raise-type-error 'implementation? "interface" 1 v i))
|
|
(and (class? v)
|
|
(interface-extension? (class-self-interface v) i)))
|
|
|
|
(define (interface-extension? v i)
|
|
(unless (interface? i)
|
|
(raise-type-error 'interface-extension? "interface" 1 v i))
|
|
(and (interface? i)
|
|
(hash-ref (interface-all-implemented v) i #f)))
|
|
|
|
(define (method-in-interface? s i)
|
|
(unless (symbol? s)
|
|
(raise-type-error 'method-in-interface? "symbol" 0 s i))
|
|
(unless (interface? i)
|
|
(raise-type-error 'method-in-interface? "interface" 1 s i))
|
|
(and (memq s (interface-public-ids i)) #t))
|
|
|
|
(define (class->interface c)
|
|
(unless (class? c)
|
|
(raise-type-error 'class->interface "class" c))
|
|
(class-self-interface c))
|
|
|
|
(define (interned? sym)
|
|
(eq? sym (string->symbol (symbol->string sym))))
|
|
|
|
(define (interface->method-names i)
|
|
(unless (interface? i)
|
|
(raise-type-error 'interface->method-names "interface" i))
|
|
(filter interned? (interface-public-ids i)))
|
|
|
|
|
|
(define-traced (object-info o)
|
|
(unless (object? o)
|
|
(raise-type-error 'object-info "object" o))
|
|
(trace-begin
|
|
(trace (inspect-event o))
|
|
(let loop ([c (object-ref (unwrap-object o))]
|
|
[skipped? #f])
|
|
(if (struct? ((class-insp-mk c)))
|
|
;; current inspector can inspect this object
|
|
(values c skipped?)
|
|
(if (zero? (class-pos c))
|
|
(values #f #t)
|
|
(loop (vector-ref (class-supers c) (sub1 (class-pos c))) #t))))))
|
|
|
|
(define (to-sym s)
|
|
(if (string? s)
|
|
(string->symbol s)
|
|
s))
|
|
|
|
(define (class-info c)
|
|
(unless (class? c)
|
|
(raise-type-error 'class-info "class" c))
|
|
(if (struct? ((class-insp-mk c)))
|
|
(let ([super (vector-ref (class-supers c) (sub1 (class-pos c)))])
|
|
(let loop ([next super][skipped? #f])
|
|
(if (or (not next)
|
|
(struct? ((class-insp-mk next))))
|
|
(values (to-sym (class-name c))
|
|
(- (class-field-width c) (class-field-width super))
|
|
(filter interned? (class-field-ids c))
|
|
(class-field-ref c)
|
|
(class-field-set! c)
|
|
next
|
|
skipped?)
|
|
(if (zero? (class-pos next))
|
|
(loop #f #t)
|
|
(loop (vector-ref (class-supers next) (sub1 (class-pos next))) #t)))))
|
|
(raise-mismatch-error 'class-info "current inspector cannot inspect class: " c)))
|
|
|
|
(define-traced object->vector
|
|
(opt-lambda (in-o [opaque-v '...])
|
|
(unless (object? in-o)
|
|
(raise-type-error 'object->vector "object" in-o))
|
|
(trace-begin
|
|
(trace (inspect-event in-o))
|
|
(let ([o (unwrap-object in-o)])
|
|
(list->vector
|
|
(cons
|
|
(string->symbol (format "object:~a" (class-name (object-ref o))))
|
|
(reverse
|
|
(let-values ([(c skipped?) (object-info o)])
|
|
(let loop ([c c][skipped? skipped?])
|
|
(cond
|
|
[(not c) (if skipped? (list opaque-v) null)]
|
|
[else (let-values ([(name num-fields field-ids field-ref
|
|
field-set next next-skipped?)
|
|
(class-info c)])
|
|
(let ([rest (loop next next-skipped?)]
|
|
[here (let loop ([n num-fields])
|
|
(if (zero? n)
|
|
null
|
|
(cons (field-ref o (sub1 n))
|
|
(loop (sub1 n)))))])
|
|
(append (if skipped? (list opaque-v) null)
|
|
here
|
|
rest)))]))))))))))
|
|
|
|
(define (object=? o1 o2)
|
|
(unless (object? o1)
|
|
(raise-type-error 'object=? "object" o1))
|
|
(unless (object? o2)
|
|
(raise-type-error 'object=? "object" o2))
|
|
(eq? (unwrap-object o1)
|
|
(unwrap-object o2)))
|
|
|
|
;;--------------------------------------------------------------------
|
|
;; primitive classes
|
|
;;--------------------------------------------------------------------
|
|
|
|
(define (make-primitive-class
|
|
make-struct:prim ; see below
|
|
prim-init ; primitive initializer: takes obj and list of name-arg pairs
|
|
name ; symbol
|
|
super ; superclass
|
|
init-arg-names ; #f or list of syms and sym--value lists
|
|
override-names ; overridden method names
|
|
new-names ; new (public) method names
|
|
override-methods ; list of methods
|
|
new-methods) ; list of methods
|
|
|
|
; The `make-struct:prim' function takes prop:object, a
|
|
; class, a preparer, and a dispatcher function, and produces:
|
|
; * a struct constructor (must have prop:object)
|
|
; * a struct predicate
|
|
; * a struct type for derived classes (mustn't have prop:object)
|
|
;
|
|
; The supplied preparer takes a symbol and returns a num.
|
|
;
|
|
; The supplied dispatcher takes an object and a num and returns a method.
|
|
;
|
|
; When a primitive class has a superclass, the struct:prim maker
|
|
; is responsible for ensuring that the returned struct items match
|
|
; the supertype predicate.
|
|
|
|
(compose-class name
|
|
(or super object%)
|
|
null
|
|
#f
|
|
#f
|
|
#f
|
|
|
|
0 null null ; no fields
|
|
|
|
null ; no rename-supers
|
|
null ; no rename-inners
|
|
null null new-names
|
|
null null override-names
|
|
null null null ; no augrides
|
|
null ; no inherits
|
|
|
|
; #f => init args by position only
|
|
; sym => required arg
|
|
; sym--value list => optional arg
|
|
(and init-arg-names
|
|
(map (lambda (s)
|
|
(if (symbol? s) s (car s)))
|
|
init-arg-names))
|
|
'stop
|
|
|
|
(lambda ignored
|
|
(values
|
|
new-methods
|
|
override-methods
|
|
null ; no augride-methods
|
|
(lambda (this super-go/ignored si_c/ignored si_inited?/ignored si_leftovers/ignored init-args)
|
|
(apply prim-init this
|
|
(if init-arg-names
|
|
(extract-primitive-args this name init-arg-names init-args)
|
|
init-args)))))
|
|
|
|
make-struct:prim))
|
|
|
|
(define (extract-primitive-args this class-name init-arg-names init-args)
|
|
(let loop ([names init-arg-names][args init-args])
|
|
(cond
|
|
[(null? names)
|
|
(unless (null? args)
|
|
(unused-args-error this args))
|
|
null]
|
|
[else (let* ([name (car names)]
|
|
[id (if (symbol? name)
|
|
name
|
|
(car name))])
|
|
(let ([arg (assq id args)])
|
|
(cond
|
|
[arg
|
|
(cons (cdr arg) (loop (cdr names) (remq arg args)))]
|
|
[(symbol? name)
|
|
(missing-argument-error class-name name)]
|
|
[else
|
|
(cons (cadr name) (loop (cdr names) args))])))])))
|
|
|
|
;;--------------------------------------------------------------------
|
|
;; wrapper for contracts
|
|
;;--------------------------------------------------------------------
|
|
|
|
(define-struct wrapper-field (name ctc-stx))
|
|
(define-struct wrapper-method (name mth-stx))
|
|
|
|
(define-values (wrapper-object? wrapper-object-wrapped set-wrapper-object-wrapped! struct:wrapper-object)
|
|
(let-values ([(struct:wrapper-object make-wrapper-object wrapper-object? ref set!)
|
|
(make-struct-type 'raw-wrapper-object
|
|
#f
|
|
0
|
|
1)])
|
|
(values wrapper-object?
|
|
(lambda (v) (ref v 0))
|
|
(lambda (o v) (set! o 0 v))
|
|
struct:wrapper-object)))
|
|
|
|
;; unwrap-object : (union wrapper-object object) -> object
|
|
(define (unwrap-object o)
|
|
(let loop ([o o])
|
|
(if (wrapper-object? o)
|
|
(loop (wrapper-object-wrapped o))
|
|
o)))
|
|
|
|
;; make-wrapper-class : symbol
|
|
;; (listof symbol)
|
|
;; method-spec [depends on the boolean what it is]
|
|
;; (listof symbol)
|
|
;; boolean
|
|
;; -> class
|
|
;; the resulting class is the "proxy" class for the contracted version of an
|
|
;; object with contracts on the method-ids.
|
|
|
|
;; Overall, objects of this class have one field for the original object,
|
|
;; one field per method in the contract and one field per field in the contract.
|
|
;; Each of the methods (passed in) just accesses the initial (method) fields
|
|
;; (which contain procedures) and calls them and returns their results.
|
|
;; Those fields do not show up from outside of this file, via the usual
|
|
;; field accessors. In addition, the class has one field per field that
|
|
;; will contain the contracted versions of the input fields.
|
|
;; The class accepts one initialization argument per method and
|
|
;; one init arg per field (in that order) using the make-object style
|
|
;; initialization.
|
|
(define (make-wrapper-class class-name method-ids methods field-ids old-style?)
|
|
(let* ([supers (vector object% #f)]
|
|
[method-ht (make-hasheq)]
|
|
[method-count (length method-ids)]
|
|
[methods-vec (make-vector method-count #f)]
|
|
|
|
[field-ht (make-hasheq)]
|
|
[field-count (length field-ids)]
|
|
|
|
[cls
|
|
(make-class class-name
|
|
1
|
|
supers
|
|
'bogus-self-interface
|
|
void ; nothing can be inspected
|
|
|
|
method-count
|
|
method-ht
|
|
(reverse method-ids)
|
|
|
|
methods-vec
|
|
(list->vector (map (lambda (x) 'final) method-ids))
|
|
'dont-use-me!
|
|
|
|
(if old-style?
|
|
(+ field-count method-count 1)
|
|
field-count)
|
|
field-ht
|
|
field-ids
|
|
|
|
#f; struct:object
|
|
#f; object?
|
|
#f; make-object ;; -> void
|
|
#f; field-ref
|
|
#f; field-set!
|
|
|
|
#f ;; only by position arguments
|
|
'normal ; init-mode - ??
|
|
|
|
#f ; init
|
|
#f #f ; not serializable
|
|
#f)])
|
|
(let-values ([(struct:object make-object object? field-ref field-set!)
|
|
(make-struct-type 'wrapper-object
|
|
struct:wrapper-object
|
|
0
|
|
(if old-style?
|
|
(+ (length field-ids) (length method-ids))
|
|
(length field-ids))
|
|
undefined
|
|
(list (cons prop:object cls))
|
|
insp)])
|
|
(set-class-struct:object! cls struct:object)
|
|
(set-class-object?! cls object?)
|
|
(set-class-make-object! cls make-object)
|
|
(set-class-field-ref! cls field-ref)
|
|
(set-class-field-set!! cls field-set!)
|
|
|
|
(let ([init
|
|
(lambda (o continue-make-super c inited? named-args leftover-args)
|
|
;; leftover args will contain the original object and new field values
|
|
;; fill the original object in and then fill in the fields.
|
|
(set-wrapper-object-wrapped! o (car leftover-args))
|
|
(let loop ([leftover-args (cdr leftover-args)]
|
|
[i 0])
|
|
(unless (null? leftover-args)
|
|
(field-set! o i (car leftover-args))
|
|
(loop (cdr leftover-args)
|
|
(+ i 1))))
|
|
(continue-make-super o c inited? '() '() '()))])
|
|
(set-class-init! cls init))
|
|
|
|
;; fill in the methods vector & methods-ht
|
|
(let loop ([i 0]
|
|
[methods methods]
|
|
[method-ids method-ids])
|
|
(when (< i method-count)
|
|
(vector-set! methods-vec i (if old-style?
|
|
((car methods) field-ref)
|
|
(car methods)))
|
|
(hash-set! method-ht (car method-ids) i)
|
|
(loop (+ i 1)
|
|
(cdr methods)
|
|
(cdr method-ids))))
|
|
|
|
;; fill in the fields-ht
|
|
(let loop ([i 0]
|
|
[field-ids field-ids])
|
|
(when (< i field-count)
|
|
(hash-set! field-ht (car field-ids) (cons cls i))
|
|
(loop (+ i 1)
|
|
(cdr field-ids))))
|
|
|
|
;; fill in the supers vector
|
|
(vector-set! supers 1 cls)
|
|
|
|
cls)))
|
|
|
|
; extract-vtable : object -> (vectorof method-proc[this args ... -> res])
|
|
(define (extract-vtable o) (class-methods (object-ref o)))
|
|
|
|
; extract-method-ht : object -> hash-table[sym -> number]
|
|
(define (extract-method-ht o) (class-method-ht (object-ref o)))
|
|
|
|
;;--------------------------------------------------------------------
|
|
;; misc utils
|
|
;;--------------------------------------------------------------------
|
|
|
|
(define undefined (letrec ([x x]) x))
|
|
|
|
(define-struct (exn:fail:object exn:fail) () #:inspector insp)
|
|
|
|
(define (obj-error where . msg)
|
|
(raise (make-exn:fail:object
|
|
(string-append (format "~a: " where) (apply format msg))
|
|
(current-continuation-marks))))
|
|
|
|
(define (for-class name)
|
|
(if name (format " for class: ~a" name) ""))
|
|
(define (for-class/which which name)
|
|
(if name (format " for ~a class: ~a" which name) ""))
|
|
(define (for-intf name)
|
|
(if name (format " for interface: ~a" name) ""))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;;
|
|
;; mixin
|
|
;;
|
|
|
|
(define (check-mixin-super mixin-name super% from-ids)
|
|
(let ([mixin-name (or mixin-name 'mixin)])
|
|
(unless (class? super%)
|
|
(error mixin-name "argument is not a class: ~e" super%))
|
|
(for-each (lambda (from-id)
|
|
(unless (implementation? super% from-id)
|
|
(error mixin-name "argument does not implement ~e: ~e" from-id super%)))
|
|
from-ids)))
|
|
|
|
(define (check-mixin-from-interfaces all-from)
|
|
(for-each (lambda (from-id)
|
|
(unless (interface? from-id)
|
|
(error 'mixin
|
|
"expected from-interface, got: ~e; others ~e"
|
|
from-id
|
|
all-from)))
|
|
all-from))
|
|
|
|
(define (check-mixin-to-interfaces all-to)
|
|
(for-each (lambda (to-id)
|
|
(unless (interface? to-id)
|
|
(error 'mixin
|
|
"expected to-interface, got: ~e; others ~e"
|
|
to-id
|
|
all-to)))
|
|
all-to))
|
|
|
|
|
|
(define (check-interface-includes xs from-ids)
|
|
(for-each
|
|
(lambda (x)
|
|
(unless (ormap (lambda (i) (method-in-interface? x i)) from-ids)
|
|
(error 'mixin
|
|
"method `~a' was referenced in definition, but is not in any of the from-interfaces: ~e"
|
|
x from-ids)))
|
|
xs))
|
|
|
|
(define-syntax (mixin stx)
|
|
(syntax-case stx ()
|
|
[(_ (from ...) (to ...) clauses ...)
|
|
(let ([extract-renamed-names
|
|
(λ (x)
|
|
(map (λ (x) (syntax-case x ()
|
|
[(internal-name external-name) (syntax external-name)]
|
|
[else x]))
|
|
(syntax->list x)))])
|
|
(define (get-super-names stx)
|
|
(syntax-case stx (inherit rename
|
|
override overment override-final
|
|
define/override define/overment define/override-final
|
|
augment augride augment-final
|
|
define/augment define/augride define/augment-final)
|
|
[(inherit names ...) (extract-renamed-names (syntax (names ...)))]
|
|
[(rename [x names] ...) (syntax->list (syntax (names ...)))]
|
|
[(override names ...) (extract-renamed-names (syntax (names ...)))]
|
|
[(overment names ...) (extract-renamed-names (syntax (names ...)))]
|
|
[(override-final names ...) (extract-renamed-names (syntax (names ...)))]
|
|
[(augment names ...) (extract-renamed-names (syntax (names ...)))]
|
|
[(augride names ...) (extract-renamed-names (syntax (names ...)))]
|
|
[(augment-final names ...) (extract-renamed-names (syntax (names ...)))]
|
|
|
|
[(define/augment (name . names) . rest) (extract-renamed-names (syntax (name)))]
|
|
[(define/augment name . rest) (identifier? (syntax name)) (extract-renamed-names (syntax (name)))]
|
|
[(define/augride (name . names) . rest) (extract-renamed-names (syntax (name)))]
|
|
[(define/augride name . rest) (identifier? (syntax name)) (extract-renamed-names (syntax (name)))]
|
|
[(define/augment-final (name . names) . rest) (extract-renamed-names (syntax (name)))]
|
|
[(define/augment-final name . rest) (identifier? (syntax name)) (extract-renamed-names (syntax (name)))]
|
|
[(define/override (name . names) . rest) (extract-renamed-names (syntax (name)))]
|
|
[(define/override name . rest) (identifier? (syntax name)) (extract-renamed-names (syntax (name)))]
|
|
[(define/overment (name . names) . rest) (extract-renamed-names (syntax (name)))]
|
|
[(define/overment name . rest) (identifier? (syntax name)) (extract-renamed-names (syntax (name)))]
|
|
[(define/override-final (name . names) . rest) (extract-renamed-names (syntax (name)))]
|
|
[(define/override-final name . rest) (identifier? (syntax name)) (extract-renamed-names (syntax (name)))]
|
|
[else null]))
|
|
(with-syntax ([(from-ids ...) (generate-temporaries (syntax (from ...)))]
|
|
[(to-ids ...) (generate-temporaries (syntax (to ...)))]
|
|
[(super-vars ...)
|
|
(apply
|
|
append
|
|
(map get-super-names
|
|
(syntax->list (syntax (clauses ...)))))]
|
|
[mixin-name (or (with-syntax ([tmp (syntax-local-name)])
|
|
(syntax (quote tmp)))
|
|
(syntax (quote mixin)))])
|
|
|
|
;; Build the class expression first, to give it a good src location:
|
|
(with-syntax ([class-expr
|
|
(with-syntax ([orig-stx stx])
|
|
(syntax/loc stx
|
|
(class/derived orig-stx [#f super% (to-ids ...) #f]
|
|
clauses ...)))])
|
|
|
|
;; Now build mixin proc, again to give it a good src location:
|
|
(with-syntax ([mixin-expr
|
|
(syntax/loc stx
|
|
(λ (super%)
|
|
(check-mixin-super mixin-name super% (list from-ids ...))
|
|
class-expr))])
|
|
|
|
;; Finally, build the complete mixin expression:
|
|
(syntax/loc stx
|
|
(let ([from-ids from] ...)
|
|
(let ([to-ids to] ...)
|
|
(check-mixin-from-interfaces (list from-ids ...))
|
|
(check-mixin-to-interfaces (list to-ids ...))
|
|
(check-interface-includes (list (quote super-vars) ...)
|
|
(list from-ids ...))
|
|
mixin-expr)))))))]))
|
|
|
|
(define externalizable<%>
|
|
(_interface () externalize internalize))
|
|
|
|
(define printable<%>
|
|
(_interface () custom-write custom-display))
|
|
|
|
;; Providing traced versions:
|
|
(provide class-traced
|
|
class*-traced
|
|
class/derived-traced
|
|
(rename-out [define-serializable-class define-serializable-class-traced]
|
|
[define-serializable-class* define-serializable-class*-traced]
|
|
[mixin mixin-traced])
|
|
new-traced
|
|
make-object-traced
|
|
instantiate-traced
|
|
send-traced
|
|
send/apply-traced
|
|
send*-traced
|
|
class-field-accessor-traced
|
|
class-field-mutator-traced
|
|
with-method-traced
|
|
get-field-traced
|
|
field-bound?-traced
|
|
field-names-traced
|
|
(rename-out [generic/form generic-traced]
|
|
[make-generic/proc make-generic-traced])
|
|
send-generic-traced
|
|
is-a?-traced
|
|
object-interface-traced
|
|
object-info-traced
|
|
object->vector-traced
|
|
object-method-arity-includes?-traced
|
|
)
|
|
|
|
;; Providing normal functionality:
|
|
(provide (protect-out make-wrapper-class
|
|
wrapper-object-wrapped
|
|
extract-vtable
|
|
extract-method-ht
|
|
get-field/proc)
|
|
|
|
(rename-out [_class class]) class* class/derived
|
|
define-serializable-class define-serializable-class*
|
|
class?
|
|
mixin
|
|
(rename-out [_interface interface]) interface?
|
|
object% object? object=? externalizable<%> printable<%>
|
|
new make-object instantiate
|
|
get-field field-bound? field-names
|
|
send send/apply send* class-field-accessor class-field-mutator with-method
|
|
private* public* pubment*
|
|
override* overment*
|
|
augride* augment*
|
|
public-final* override-final* augment-final*
|
|
define/private define/public define/pubment
|
|
define/override define/overment
|
|
define/augride define/augment
|
|
define/public-final define/override-final define/augment-final
|
|
define-local-member-name define-member-name
|
|
member-name-key generate-member-key member-name-key? member-name-key=? member-name-key-hash-code
|
|
(rename-out [generic/form generic]) (rename-out [make-generic/proc make-generic]) send-generic generic?
|
|
is-a? subclass? implementation? interface-extension?
|
|
object-interface object-info object->vector
|
|
object-method-arity-includes?
|
|
method-in-interface? interface->method-names class->interface class-info
|
|
(struct-out exn:fail:object)
|
|
make-primitive-class))
|
|
|