Module: internal Synopsis: Stub interface to dynamic definition used by incremental Author: Keith Playford Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND define function %define-method (gf :: , md :: , lib :: ) => () %add-a-method(gf, md, lib, #t, #t, #f) end function; define function %define-sealed-method (gf :: , md :: , lib :: ) => () %add-a-method(gf, md, lib, #t, #t, #t) end function; define variable *incomplete-generic-function-methods* :: = make(
); define variable *incomplete-generic-function-domains* ::
= make(
); define function note-incomplete-method-handler (g :: , m :: , lib :: ) => () g.incremental-gf-method-complete? := #f; *incomplete-generic-function-methods*[g] := pair(list(m, lib), element(*incomplete-generic-function-methods*, g, default: #())); with-lock ($class-bashing-lock) map-congruency-classes(method (c :: ) => () let ic :: = class-implementation-class(c); iclass-dependent-generics(ic) := add-new!(iclass-dependent-generics(ic), g) end method, m); end with-lock; end function; define function remove-incomplete-method-handler (g :: , frob, lib :: , test :: ) => (m :: false-or()) let old :: = element(*incomplete-generic-function-methods*, g, default: #()); unless (empty?(old)) block (return) for (e :: in old) let m :: = first(e); let l :: = second(e); if (test(m, frob)) *incomplete-generic-function-methods*[g] := remove!(old, e); return(m) end if end for end block end unless; end function; define inline-only function generic-function-incomplete-methods (g :: ) => (stuff :: ) element(*incomplete-generic-function-methods*, g, default: #()) end function; define function note-incomplete-domain-handler (g :: , d :: ) => () g.incremental-gf-method-complete? := #f; *incomplete-generic-function-domains*[g] := pair(d, element(*incomplete-generic-function-domains*, g, default: #())); with-lock ($class-bashing-lock) map-congruency-classes(method (c :: ) => () let ic :: = class-implementation-class(c); iclass-dependent-generics(ic) := add-new!(iclass-dependent-generics(ic), g) end method, d); end with-lock end function; define inline-only function generic-function-incomplete-domains (g :: ) => (stuff :: ) element(*incomplete-generic-function-domains*, g, default: #()) end function; define function remove-incomplete-domain-handler (g :: , frob, lib :: ) => (m :: false-or()) let old :: = element(*incomplete-generic-function-domains*, g, default: #()); unless (empty?(old)) block (return) for (d :: in old) if (domain-match?(d, frob)) *incomplete-generic-function-methods*[g] := remove!(old, d); return(d) end if end for end block end unless; end function; define inline-only function do-incomplete-frobs (f :: , g :: , t ::
) => (); let lossage :: = (with-object-lock (g) let subl :: = element(t, g, default: #()); local method inner-loop (subl :: , lossage :: , hdr :: false-or(), prev :: ) if (subl == #()) if (hdr) t[g] := hdr else remove-key!(t, g) end; lossage else let elt = head(subl); let nxt :: = tail(subl); let (win?, nlossage) = f(elt); let (hdr :: false-or(), prev :: ) = if (~win?) let cell :: = list(elt); values((if (hdr) tail(prev) := cell; hdr else cell end), cell) else values(hdr, prev) end if; let lossage = if (nlossage & nlossage ~== #()) concatenate(lossage, nlossage) else lossage end; inner-loop(nxt, lossage, hdr, prev) end if end method; inner-loop(subl, #(), #f, subl) end with-object-lock); bletch-stack(lossage); end function; begin note-generic-function-incomplete-method := note-incomplete-method-handler; note-generic-function-incomplete-domain := note-incomplete-domain-handler; remove-generic-function-incomplete-method := remove-incomplete-method-handler; remove-generic-function-incomplete-domain := remove-incomplete-domain-handler; end; //// COMPLETENESS DEFERRED ADDITIONS define thread variable *generics-being-finished* = #f; // define macro with-batch-deferred-generic-handling // { with-batch-deferred-generic-handling () ?body:body end } // => // { (begin // let %old-value% = *generics-being-finished*; // block () // unless (%old-value%) *generics-being-finished* := #t end; // ?body // cleanup // unless (%old-value%) // let z = *generics-being-finished*; // *generics-being-finished* := #f; // finish-the-generics(z) // end // end // end) // } // end macro; define method note-type-completeness-change (g :: ) => () if (instance?(g, )) let z = *generics-being-finished*; if (~z) handle-generic-completeness-change(g) else let z ::
= (if (z == #t) *generics-being-finished* := make(
) else z end); z[g] := #t end if end if end method; // define function finish-the-generics (z) => () // if (z ~== #t) // let z ::
= z; // for (ignore keyed-by g :: in z) // unless (type-complete?(g)) recompute-type-complete!(g) end // end for; // for (ignore keyed-by g :: in z) // // @@@@ Insert a restart here to handle aborting deferred additions to this gf // handle-generic-completeness-change(g) // end for // end if // end function; define method handle-generic-completeness-change (g :: ) if (instance?(g, ) & recompute-type-complete!(g)) let t = *incomplete-generic-function-domains*; let g :: = g; if (t) let t ::
= t; do-incomplete-frobs (method (d :: ) if (recompute-type-complete!(d)) values(#t, %add-domains-internal(g, vector(d))) else values(#f, #()) end if end method, g, t) end if; let t = *incomplete-generic-function-methods*; if (t) let t ::
= t; do-incomplete-frobs (method (e :: ) let m :: = first(e); if (recompute-type-complete!(m)) let lib :: = second(e); values(#t, add-method-internal(g, m, lib, #t, #t)) else values(#f, #()) end if end method, g, t) end if end if end method; define method complete-dependent-generic-function (g :: , c :: , u :: ) => () /* if (type-complete?(g) & ~incremental-gf-method-complete?(g)) (with-object-lock (g) block () for (d :: in generic-function-incomplete-domains(g)) if (type-complete?(d)) generic-function-incomplete-domains(g) := remove(generic-function-incomplete-domains(g), d); block let urk = %add-domain-internal(g, d, #t); if (urk) bletch(urk) end; exception(, init-arguments: vector(format-string: "Skip adding domain %= to %=.", format-arguments: vector(d, g))) end block end if end for; for (e :: in generic-function-incomplete-methods(g)) let m :: = first(e); let lib :: = second(e); if (type-complete?(m)) generic-function-incomplete-methods(g) := remove(generic-function-incomplete-methods(g), e); block let urk = add-method-internal(g, m, lib, #t, #t); if (urk) bletch(urk) end; exception(, init-arguments: vector(format-string: "Skip adding method %= to %=.", format-arguments: vector(m, g))) end block end if end for end block end with-object-lock) end if */ note-type-completeness-change(g) end method; //// Generic function definition/redefinition define inline function %%define-generic (gf :: , debug-name, module, sealed?, complain-about-congruency?, signature) => (gf :: ); let lossage :: = #(); (with-object-lock (gf) (begin let methods = generic-function-methods(gf); let (mlibs :: , glib :: ) = if (instance?(gf, )) let gf :: = gf; values(incremental-gf-method-libraries(gf), incremental-gf-library(gf)) else values(#[], $runtime-library) end if; let nlibs :: = size(mlibs); reinitialize(gf, debug-name: debug-name, signature: signature, sealed?: sealed?, module: module); for (m :: in methods, i :: from 0) let (well?, reason) = congruent?(gf, m); if (well?) let (ans, barfo) = add-method-internal(gf, m, if (i < nlibs) mlibs[i] else glib end, #f, #f); if (barfo) bletch(barfo) end; elseif (complain-about-congruency?) lossage := pair(list(m, reason), lossage) end if end for end) end with-object-lock); if (lossage ~== #()) error(make(, format-string: "Generic function definition for %= was not congruent with some existing methods %=.", format-arguments: list(gf, lossage))) end if; handle-generic-completeness-change(gf); gf end function; define function %define-generic (gf :: , signature) => (gf :: ); %%define-generic (gf, debug-name(gf), incremental-gf-module(gf), incremental-gf-sealed?(gf), #t, signature) end function; define function %redefine-generic (gf :: , debug-name, module, signature, sealed?) => (gf :: ); %%define-generic (gf, debug-name, module, sealed?, #f, signature) end function; //// Class definition/redefinition define function %define-class (class :: , superclasses, slots) => (class :: ); reinitialize (class, debug-name: debug-name(class), module: class-module(class), superclasses: superclasses, abstract?: class-abstract?(class), primary?: class-primary?(class), sealed?: class-sealed?(class), slots: slots, inherited-slots: #[], keywords: #[], dependent-generics: iclass-dependent-generics(class-implementation-class(class))); class end function; define function %define-complex-class (class :: , superclasses, slots, inherited-slots, keywords) => (class :: ); reinitialize (class, debug-name: debug-name(class), module: class-module(class), superclasses: superclasses, abstract?: class-abstract?(class), primary?: class-primary?(class), sealed?: class-sealed?(class), slots: slots, inherited-slots: inherited-slots, keywords: keywords, dependent-generics: iclass-dependent-generics(class-implementation-class(class))); class end function; define function dependent-subclasses (classes :: , ans :: ) => (ans :: ) iterate loop (classes :: = classes, ans :: = ans) if (empty?(classes)) ans else let the-head :: = head(classes); let the-tail :: = tail(classes); if (the-head == ) ans elseif (member?(the-head, ans)) // If a class has already been done, then its subclasses have been or // are in the process of being done. loop(the-tail, ans) else loop(the-tail, dependent-subclasses(direct-subclasses(the-head), pair(the-head, ans))) end if end if end iterate end function; define function %%redefine-complex-class (class :: , debug-name, module, abstract?, primary?, sealed?, superclasses, slots, inherited-slots, keywords) => (class :: ); let superclasses :: = if (instance?(superclasses, )) superclasses else vector(superclasses) end if; let slots :: = map-as(, method (stuff) apply(create-slot-descriptor, class, stuff) end, slots); let ans = with-lock ($class-bashing-lock) let dependents :: = dependent-subclasses(direct-subclasses(class), list(class)); if (nonstructural-redefinition?(class, superclasses, slots)) %redefine-class-attributes-only (class, dependents, superclasses: superclasses, slots: slots, inherited-slots: inherited-slots, keywords: keywords); else %redefine-class-of-new-structure (class, dependents, debug-name: debug-name, superclasses: superclasses, abstract?: abstract?, primary?: primary?, sealed?: sealed?, module: module, slots: slots, inherited-slots: inherited-slots, keywords: keywords); end if end with-lock; if (instance?(ans, )) bletch(ans) end; class end function; define function %redefine-class (class :: , debug-name, module, abstract?, primary?, sealed?, superclasses, slots) => (class :: ); %%redefine-complex-class (class, debug-name, module, abstract?, primary?, sealed?, superclasses, slots, #[], #[]) end function; define function %redefine-complex-class (class :: , debug-name, module, abstract?, primary?, sealed?, superclasses, slots, inherited-slots, keywords) => (class :: ); %%redefine-complex-class (class, debug-name, module, abstract?, primary?, sealed?, superclasses, slots, inherited-slots, keywords) end function; define method nonstructural-redefinition? (class :: , superclasses :: , slots :: ) superclasses = direct-superclasses(class) // @@@@@@ and they are the same metaclasses - done at compiletime? & (begin let oslots :: = direct-slot-descriptors(class); let noslots :: = size(oslots); let nslots :: = size(slots); nslots == noslots & (every?(method (sd :: ) let old = find-old-slot(sd, oslots); old & (begin let old :: = old; slot-allocation(old) == slot-allocation(sd) & same-specializer?(slot-type(old), slot-type(sd)) & slot-setter(old) == slot-setter(sd) end) end method, slots)) end) end method; define method find-old-slot (sd :: , slotvec :: ) => (ans :: false-or()) block (gotcha) for (nsd :: in slotvec) if (getter=(sd, nsd)) gotcha(nsd) end if end; #f end end method; define method %redefine-class-of-new-structure (class :: , dependents :: , #rest initargs) => (v :: false-or()) let u :: = make-empty-subjunctive-class-universe(); let thisiclass :: = apply(make, , class: class, defer-cross-class-computations?: #t, subjunctive-class-universe: u, initargs); // This is the redefining-one-class optimization. Just go straight through the // dependents making new implementation classes, although we do defer cross class // computations until we have our full SCU built up. for (c :: in dependents) if (c ~== class) make(, class: c, subjunctive-class-universe: u, defer-cross-class-computations?: #t, superclasses: direct-superclasses(c), slots: direct-slot-descriptors(c), inherited-slots: direct-inherited-slot-descriptors(c), keywords: direct-initialization-argument-descriptors(c)) end if; end for; // Now, we've computed all new implementation classes; do dependency-directed (by // direct superclasses) initialization of them. This also does error checking. scu-initialize-all(do-implementation-class-cross-class-initializations, u); // Figure out the new known-joint info. For classes with new implementation classes // we put it into the new implementation class, otherwise we remember an alist for // later updating. let joint-updates ::
= compute-known-joint(thisiclass, dependents, u); // Now we've got new implementation classes for all the classes that need to be redefined. // Most of the error checking has been performed, and we haven't yet trashed the existing // world. Now nuke the existing classes, and install the new ones. // -- There is one screw area here, and that has to do with adding slot methods. We do // -- not have a way to check for congruency of about-to-be-added slot methods with any // -- already defined generic functions in the "correct" environment (one in which the // -- type system reflects the redefinition and not the previous state). So for now we'll // -- do slot method addition as we install the new class definitions. Probably we should // -- install the new class definitions, then do all the slot method additions, and allow // -- individual bad ones to be skipped through judicious application of restarts. scu-do(invalidate-previous-implementation-class, u); scu-do(install-implementation-class, u); for (joint :: keyed-by c in joint-updates) let c :: = c; class-known-joint(c) := as(, joint) end for; scu-do(add-slot-methods, u); #f end method; define function compute-known-joint (iclass :: , dependents :: , u :: ) => (t ::
) let t ::
= make(
); let ndependents :: = dependent-subclasses(tail(all-superclasses(iclass)), dependent-subclasses (tail(all-superclasses(class-implementation-class (iclass-class(iclass)))), dependents)); for (c :: in ndependents) for (subl :: = tail(all-superclasses(c)) then tail(subl), until: empty?(subl) | head(subl) == ) let c1 :: = head(subl); let ic1 :: = scu-entry(c1, u); let j0 :: = element(t, c1, default: #()); local method loop (subl2 :: , j1 :: ) if (empty?(subl2) | head(subl2) == ) unless (j0 == j1) element(t, c1) := j1 end else let c2 :: = head(subl2); let ic2 :: = scu-entry(c2, u); if (subiclass?(ic1, c1, ic2, c2) | member?(c2, j1)) loop(tail(subl2), j1) else loop(tail(subl2), pair(c2, j1)) end if end if end method; loop(tail(subl), j0) end for end for; t end function; define function invalidate-previous-implementation-class (ic :: , scu :: ) => () let old = class-implementation-class(iclass-class(ic)); debug-assert(old ~== ic, "attempting to invalidate %= without a new one", ic); if (instance?(old, )) // @@@@ I'm not sure it's possible for this to not be true. I don't remember // the reason for this check - should review whether there's any circumstance // at all where it might be necessary. (Result of a failed make?) let old :: = old; invalidate-implementation-class(old) end if end function; define variable *obsolete-instance-class* :: = ; define method invalidate-implementation-class (ic :: ) => () let newclass :: = *obsolete-instance-class*; remove-implementation-class-slot-methods(ic); invalidate-class-instance?-iep(iclass-class(ic)); iclass-class(ic) := newclass; let mic :: = class-implementation-class(newclass); all-superclasses(ic) := all-superclasses(mic); class-known-joint(ic) := #[]; direct-superclasses(ic) := direct-superclasses(mic); // class-subtype-bit(iclass-class(ic)) := class-subtype-bit(iclass-class(mic)); mm-wrapper-subtype-mask(class-mm-wrapper(ic)) := mm-wrapper-subtype-mask(class-mm-wrapper(mic)); class-rcpl-vector(ic) := class-rcpl-vector(mic); class-rcpl-position(ic) := class-rcpl-position(mic); class-rcpl-other-positions(ic) := class-rcpl-other-positions(mic); direct-subclasses(ic) := #(); iclass-dispatch-key(ic) := iclass-dispatch-key(mic); end method; define method remove-implementation-class-slot-methods (ic :: ) => () local method find-and-remove-getter-method (g :: , sd :: ) => () let spec :: = slot-owner(sd); block (done) for (m :: in generic-function-methods(g)) if (spec == %method-specializer(m, 0)) %remove-method(g, m); done() end if end for end end method; local method find-and-remove-setter-method (g :: , sd :: ) => () let spec0 :: = slot-type(sd); let spec1 :: = slot-owner(sd); block (done) for (m :: in generic-function-methods(g)) if (same-specializer?(spec0, %method-specializer(m, 0)) & spec1 == %method-specializer(m, 1)) %remove-method(g, m); done() end if end for end end method; for (sd :: in direct-slot-descriptors(ic)) // @@@@@ ????? What to do with virtual slots? if (~instance?(sd, )) let g = slot-getter(sd); let s = slot-setter(sd); if (g) let g :: = g; find-and-remove-getter-method(g, sd); end if; if (s) let s :: = s; find-and-remove-setter-method(s, sd); end if; end if; end for; end method; define method overwrite-slot-descriptor (osd :: , nsd :: ) => () (with-lock ($slot-initial-data-lock) begin init-data-slot(osd) := init-data-slot(nsd); init-keyword(osd) := init-keyword(nsd); // These should be properties slots copied as a block. init-supplied?(osd) := init-supplied?(nsd); init-evaluated?(osd) := init-evaluated?(nsd); init-value?(osd) := init-value?(nsd); init-keyword-required?(osd) := init-keyword-required?(nsd); end end) end method; define method %redefine-class-attributes-only (class :: , dependents :: , #key slots :: = #[], inherited-slots :: = #[], keywords :: = #[], superclasses :: = #[]) => (v :: false-or()) for (osd :: in direct-slot-descriptors(class)) let nsd :: = find-old-slot(osd, slots) | error("Bug! Class %= is a structural redefinition?", class); overwrite-slot-descriptor(osd, nsd) end for; for (dep :: in dependents) compute-defaulted-initialization-arguments(class-implementation-class(dep), map-as(, method(x) class-implementation-class(x) end, all-superclasses(dep)), $empty-subjunctive-class-universe) end for; #f end method;