--- orig/ice-9/boot-9.scm +++ mod/ice-9/boot-9.scm @@ -1100,8 +1100,6 @@ ;;; ;;; - duplicates-handlers ;;; -;;; - duplicates-interface -;;; ;;; - observers ;;; ;;; - weak-observers @@ -1173,8 +1171,10 @@ (define module-type (make-record-type 'module '(obarray uses binder eval-closure transformer name kind - duplicates-handlers duplicates-interface - observers weak-observers observer-id) + duplicates-handlers + duplicates-interface ;; FIXME: to be removed + observers weak-observers observer-id + import-obarray) %print-module)) ;; make-module &opt size uses binder @@ -1190,6 +1190,10 @@ (list-ref args index) default)) + (define %default-import-size + ;; This should be the size of the pre-module obarray. + 500) + (if (> (length args) 3) (error "Too many args to make-module." args)) @@ -1209,8 +1213,9 @@ (let ((module (module-constructor (make-hash-table size) uses binder #f #f #f #f #f #f '() - (make-weak-value-hash-table 31) - 0))) + (make-weak-key-hash-table 31) + 0 + (make-hash-table %default-import-size)))) ;; We can't pass this as an argument to module-constructor, ;; because we need it to close over a pointer to the module @@ -1240,10 +1245,6 @@ (record-accessor module-type 'duplicates-handlers)) (define set-module-duplicates-handlers! (record-modifier module-type 'duplicates-handlers)) -(define module-duplicates-interface - (record-accessor module-type 'duplicates-interface)) -(define set-module-duplicates-interface! - (record-modifier module-type 'duplicates-interface)) (define module-observers (record-accessor module-type 'observers)) (define set-module-observers! (record-modifier module-type 'observers)) (define module-weak-observers (record-accessor module-type 'weak-observers)) @@ -1251,6 +1252,9 @@ (define set-module-observer-id! (record-modifier module-type 'observer-id)) (define module? (record-predicate module-type)) +(define module-import-obarray (record-accessor module-type 'import-obarray)) +(define set-module-import-obarray! (record-modifier module-type 'import-obarray)) + (define set-module-eval-closure! (let ((setter (record-modifier module-type 'eval-closure))) (lambda (module closure) @@ -1269,11 +1273,10 @@ (set-module-observers! module (cons proc (module-observers module))) (cons module proc)) -(define (module-observe-weak module proc) - (let ((id (module-observer-id module))) - (hash-set! (module-weak-observers module) id proc) - (set-module-observer-id! module (+ 1 id)) - (cons module id))) +(define (module-observe-weak module observer-id . proc) + (let ((id (if (null? proc) (gensym) observer-id)) + (proc (if (null? proc) observer-id (car proc)))) + (hashq-set! (module-weak-observers module) observer-id proc))) (define (module-unobserve token) (let ((module (car token)) @@ -1311,7 +1314,11 @@ (define (module-call-observers m) (for-each (lambda (proc) (proc m)) (module-observers m)) - (hash-fold (lambda (id proc res) (proc m)) #f (module-weak-observers m))) + + ;; We assume that weak observers don't (un)register themselves as they are + ;; called since this would preclude proper iteration over the hash table + ;; elements. + (hash-for-each (lambda (id proc) (proc m)) (module-weak-observers m))) @@ -1435,26 +1442,8 @@ ;;; ;;; If the symbol is not found at all, return #f. ;;; -(define (module-local-variable m v) -; (caddr -; (list m v - (let ((b (module-obarray-ref (module-obarray m) v))) - (or (and (variable? b) b) - (and (module-binder m) - ((module-binder m) m v #f))))) -;)) - -;; module-variable module symbol -;; -;; like module-local-variable, except search the uses in the -;; case V is not found in M. -;; -;; NOTE: This function is superseded with C code (see modules.c) -;;; when using the standard eval closure. -;; -(define (module-variable m v) - (module-search module-local-variable m v)) - +;;; (This is now written in C, see `modules.c'.) +;;; ;;; {Mapping modules x symbols --> bindings} ;;; @@ -1515,19 +1504,10 @@ (module-modified m) b))) - ;; No local variable yet, so we need to create a new one. That - ;; new variable is initialized with the old imported value of V, - ;; if there is one. - (let ((imported-var (module-variable m v)) - (local-var (or (and (module-binder m) - ((module-binder m) m v #t)) - (begin - (let ((answer (make-undefined-variable))) - (module-add! m v answer) - answer))))) - (if (and imported-var (not (variable-bound? local-var))) - (variable-set! local-var (variable-ref imported-var))) - local-var))) + ;; Create a new local variable. + (let ((local-var (make-undefined-variable))) + (module-add! m v local-var) + local-var))) ;; module-ensure-local-variable! module symbol ;; @@ -1696,46 +1676,41 @@ ;; Add INTERFACE to the list of interfaces used by MODULE. ;; (define (module-use! module interface) - (set-module-uses! module - (cons interface - (filter (lambda (m) - (not (equal? (module-name m) - (module-name interface)))) - (module-uses module)))) - (module-modified module)) + ;; Perform duplicate checking, thereby populating the `import-obarray' of + ;; MODULE. + (if (not (eq? module interface)) + (begin + (process-duplicates module interface) + + (set-module-uses! module + (cons interface + (filter (lambda (m) + (not + (equal? (module-name m) + (module-name interface)))) + (module-uses module)))) + + ;; Prepare to update MODULE's import obarray when INTERFACE changes. + ;; This can happen if dynamic module modification features like + ;; `module-define!' are used, but also, more commonly, in the case of + ;; mutually dependent modules (circular dependency). + (module-observe-weak interface module + (lambda (interface) + ;;(format (current-error-port) + ;; "iface ~a changed, updating module ~a~%" + ;; interface module) + (process-duplicates module interface))) + + (module-modified module)))) ;; MODULE-USE-INTERFACES! module interfaces ;; ;; Same as MODULE-USE! but add multiple interfaces and check for duplicates ;; (define (module-use-interfaces! module interfaces) - (let* ((duplicates-handlers? (or (module-duplicates-handlers module) - (default-duplicate-binding-procedures))) - (uses (module-uses module))) - ;; remove duplicates-interface - (set! uses (delq! (module-duplicates-interface module) uses)) - ;; remove interfaces to be added - (for-each (lambda (interface) - (set! uses - (filter (lambda (m) - (not (equal? (module-name m) - (module-name interface)))) - uses))) - interfaces) - ;; add interfaces to use list - (set-module-uses! module uses) - (for-each (lambda (interface) - (and duplicates-handlers? - ;; perform duplicate checking - (process-duplicates module interface)) - (set! uses (cons interface uses)) - (set-module-uses! module uses)) - interfaces) - ;; add duplicates interface - (if (module-duplicates-interface module) - (set-module-uses! module - (cons (module-duplicates-interface module) uses))) - (module-modified module))) + (for-each (lambda (interface) + (module-use! module interface)) + interfaces)) @@ -1861,8 +1836,20 @@ (set-module-public-interface! module interface)))) (if (and (not (memq the-scm-module (module-uses module))) (not (eq? module the-root-module))) - (set-module-uses! module - (append (module-uses module) (list the-scm-module))))) + ;; Import the default set of bindings (from the SCM module) in MODULE + ;; and process duplicates between the SCM module and MODULE. + (begin + ;; Copy the pre-compiled import obarray for `the-scm-module'. This + ;; is twice as fast as populating a new hash table by iterating over + ;; the bindings of `the-scm-module'. + (set-module-import-obarray! module + (hash-table-copy %scm-import-obarray)) + (module-observe-weak the-scm-module module + (lambda (interface) + ;;(format (current-error-port) + ;; "~%~%root module changed, updating module ~a~%" + ;; module) + (process-duplicates module interface)))))) ;; NOTE: This binding is used in libguile/modules.c. ;; @@ -2007,6 +1994,10 @@ #f "no binding `~A' to hide in module ~A" binding name)))) hide) + + ;; XXX: Such modules are _not_ updated when the interfaces they use + ;; are modified! + custom-i)))) (define (symbol-prefix-proc prefix) @@ -2027,7 +2018,8 @@ (reversed-interfaces '()) (exports '()) (re-exports '()) - (replacements '())) + (replacements '()) + (autoloads '())) (if (null? kws) (call-with-deferred-observers @@ -2035,7 +2027,9 @@ (module-use-interfaces! module (reverse reversed-interfaces)) (module-export! module exports) (module-replace! module replacements) - (module-re-export! module re-exports))) + (module-re-export! module re-exports) + (if (not (null? autoloads)) + (apply module-autoload! module autoloads)))) (case (car kws) ((#:use-module #:use-syntax) (or (pair? (cdr kws)) @@ -2055,31 +2049,35 @@ (cons interface reversed-interfaces) exports re-exports - replacements))) + replacements + autoloads))) ((#:autoload) (or (and (pair? (cdr kws)) (pair? (cddr kws))) (unrecognized kws)) (loop (cdddr kws) - (cons (make-autoload-interface module - (cadr kws) - (caddr kws)) - reversed-interfaces) + reversed-interfaces exports re-exports - replacements)) + replacements + (let ((name (cadr kws)) + (bindings (caddr kws))) + (cons* name bindings autoloads)))) ((#:no-backtrace) (set-system-module! module #t) - (loop (cdr kws) reversed-interfaces exports re-exports replacements)) + (loop (cdr kws) reversed-interfaces exports re-exports + replacements autoloads)) ((#:pure) (purify-module! module) - (loop (cdr kws) reversed-interfaces exports re-exports replacements)) + (loop (cdr kws) reversed-interfaces exports re-exports + replacements autoloads)) ((#:duplicates) (if (not (pair? (cdr kws))) (unrecognized kws)) (set-module-duplicates-handlers! module (lookup-duplicates-handlers (cadr kws))) - (loop (cddr kws) reversed-interfaces exports re-exports replacements)) + (loop (cddr kws) reversed-interfaces exports re-exports + replacements autoloads)) ((#:export #:export-syntax) (or (pair? (cdr kws)) (unrecognized kws)) @@ -2087,7 +2085,8 @@ reversed-interfaces (append (cadr kws) exports) re-exports - replacements)) + replacements + autoloads)) ((#:re-export #:re-export-syntax) (or (pair? (cdr kws)) (unrecognized kws)) @@ -2095,7 +2094,8 @@ reversed-interfaces exports (append (cadr kws) re-exports) - replacements)) + replacements + autoloads)) ((#:replace #:replace-syntax) (or (pair? (cdr kws)) (unrecognized kws)) @@ -2103,7 +2103,8 @@ reversed-interfaces exports re-exports - (append (cadr kws) replacements))) + (append (cadr kws) replacements) + autoloads)) (else (unrecognized kws))))) (run-hook module-defined-hook module) @@ -2119,20 +2120,64 @@ ;;; {Autoload} ;;; -(define (make-autoload-interface module name bindings) - (let ((b (lambda (a sym definep) - (and (memq sym bindings) - (let ((i (module-public-interface (resolve-module name)))) - (if (not i) - (error "missing interface for module" name)) - (let ((autoload (memq a (module-uses module)))) - ;; Replace autoload-interface with actual interface if - ;; that has not happened yet. - (if (pair? autoload) - (set-car! autoload i))) - (module-local-variable i sym)))))) - (module-constructor (make-hash-table 0) '() b #f #f name 'autoload #f #f - '() (make-weak-value-hash-table 31) 0))) +(define (module-autoload! module . name+binding) + "Have @var{module} autoload the given module for the specified bindings. +For instance, @code{(module-autoload! m '(ice-9 debugger) '(debug))} results +in @var{m} autoloading module @code{(ice-9 debugger)} whenever binding +@code{debug} is accessed." + + (define (binding->name-alist name+binding) + (let loop ((binding->name '()) + (name+binding name+binding)) + (if (null? name+binding) + binding->name + (let ((module-name (car name+binding)) + (bindings (cadr name+binding))) + (loop (append (map (lambda (binding) + (cons binding module-name)) + bindings) + binding->name) + (cddr name+binding)))))) + + (let* ((binding-alist (binding->name-alist name+binding)) + (binder (module-binder module)) + (new-binder + (lambda (a sym define?) + (or (and (procedure? binder) + (binder a sym define?)) + (let* ((bind (assq sym binding-alist)) + (module-name (and (pair? bind) (cdr bind)))) + (and module-name + (let ((i (module-public-interface + (resolve-module module-name)))) + (if (not i) + (error "missing interface for module" name)) + + ;;(format #t "autoloaded ~a for ~a because of ~a~%" + ;; module-name module sym) + + (set! binding-alist + (assq-remove! binding-alist sym)) + (module-use-interfaces! module (list i)) + + (module-variable i sym)))))))) + + ;; Make sure the given bindings are not already imported. This allows + ;; autoloading to work even when the triggering symbols would replace + ;; already existing bindings. IOW, this sets a special duplicate + ;; handling policy where the explicitly autoloaded symbols override other + ;; same-named imported symbols. + (let ((imports (module-import-obarray module))) + (let loop ((name+binding name+binding)) + (if (not (null? name+binding)) + (let ((bindings (cadr name+binding))) + (for-each (lambda (autoloaded-binding) + (hashq-set! imports autoloaded-binding #f)) + bindings) + (loop (cddr name+binding)))))) + + ;; Install the new binder. + (set-module-binder! module new-binder))) ;;; {Compiled module} @@ -3133,34 +3178,38 @@ (lookup-duplicates-handlers handler-names)) handler-names))) -(define (make-duplicates-interface) - (let ((m (make-module))) - (set-module-kind! m 'custom-interface) - (set-module-name! m 'duplicates) - m)) - (define (process-duplicates module interface) + ;; Process duplicate bindings as MODULE imports INTERFACE (typically a + ;; module's public interface). + + (define (%module-for-each proc module) + ;; Some modules re-export bindings from other modules. They do so by + ;; having the public interface import the public interface of those other + ;; bindings (see, e.g., `(oop goops internal)'). Thus, we must traverse + ;; both bindings internal to INTERFACE and bindings _imported_ by + ;; INTERFACE. + (hash-for-each proc (module-obarray module)) + (hash-for-each (lambda (sym interface) + (let ((var (module-variable interface sym))) + (if (not var) + (format (current-error-port) "`~a' from `~a' => ~a~%" + sym interface var)) + (proc sym var))) + (module-import-obarray module))) + (let* ((duplicates-handlers (or (module-duplicates-handlers module) (default-duplicate-binding-procedures))) - (duplicates-interface (module-duplicates-interface module))) - (module-for-each + (imports (module-import-obarray module))) + (%module-for-each (lambda (name var) - (cond ((module-import-interface module name) + (cond ((hashq-ref imports name) => (lambda (prev-interface) - (let ((var1 (module-local-variable prev-interface name)) - (var2 (module-local-variable interface name))) + (let ((var1 (module-variable prev-interface name)) + (var2 (module-variable interface name))) (if (not (eq? var1 var2)) (begin - (if (not duplicates-interface) - (begin - (set! duplicates-interface - (make-duplicates-interface)) - (set-module-duplicates-interface! - module - duplicates-interface))) - (let* ((var (module-local-variable duplicates-interface - name)) + (let* ((var var1) (val (and var (variable-bound? var) (variable-ref var)))) @@ -3179,9 +3228,14 @@ val) => (lambda (var) - (module-add! duplicates-interface name var))) + (hashq-set! imports name + (if (eq? var1 var) + prev-interface + interface)))) (else - (loop (cdr duplicates-handlers))))))))))))) + (loop (cdr duplicates-handlers))))))))))) + (else + (hashq-set! imports name interface)))) interface))) @@ -3398,10 +3452,7 @@ '(((ice-9 threads))) '()))) ;; load debugger on demand - (module-use! guile-user-module - (make-autoload-interface guile-user-module - '(ice-9 debugger) '(debug))) - + (module-autoload! guile-user-module '(ice-9 debugger) '(debug)) ;; Note: SIGFPE, SIGSEGV and SIGBUS are actually "query-only" (see ;; scmsigs.c scm_sigaction_for_thread), so the handlers setup here have @@ -3477,6 +3528,30 @@ +(define %scm-import-obarray + ;; A pre-compiled "import obarray" for use by modules that use + ;; `the-scm-module'. This allows to halve the time spent in + ;; `beautify-user-module!'. + (begin + (define (make-scm-import-obarray) + (let ((imports (make-hash-table 2000))) + (module-for-each (lambda (sym var) + (hashq-set! imports sym the-scm-module)) + the-scm-module) + imports)) + + (module-observe-weak the-scm-module #t + (lambda (interface) + ;; Update it. + (format (current-error-port) + "updating `%scm-import-obarray'~%") + (set! %scm-import-obarray + (make-scm-import-obarray)))) + + (make-scm-import-obarray))) + + + ;;; Place the user in the guile-user module. ;;; --- orig/libguile/goops.c +++ mod/libguile/goops.c @@ -2587,10 +2587,14 @@ * **********************************************************************/ +/* A module holding SMOB classes. */ +static SCM smob_class_module = SCM_UNSPECIFIED; + static SCM make_class_from_template (char const *template, char const *type_name, SCM supers, int applicablep) { SCM class, name; + if (type_name) { char buffer[100]; @@ -2609,8 +2613,12 @@ /* Only define name if doesn't already exist. */ if (!SCM_GOOPS_UNBOUNDP (name) - && scm_is_false (scm_call_2 (scm_goops_lookup_closure, name, SCM_BOOL_F))) - DEFVAR (name, class); + && scm_is_false (scm_module_local_variable (smob_class_module, name))) + { + scm_module_define (smob_class_module, name, class); + scm_module_export (smob_class_module, scm_list_1 (name)); + } + return class; } @@ -2665,6 +2673,11 @@ { long i; + /* Create the module that will hold the SMOB classes. */ + smob_class_module = scm_c_define_module ("oop goops smob-classes", + NULL, NULL); + smob_class_module = scm_permanent_object (smob_class_module); + scm_smob_class = (SCM *) scm_malloc (255 * sizeof (SCM)); for (i = 0; i < 255; ++i) scm_smob_class[i] = 0; --- orig/libguile/hashtab.c +++ mod/libguile/hashtab.c @@ -76,37 +76,65 @@ #define HASHTABLE_SIZE_N (sizeof(hashtable_size)/sizeof(unsigned long)) +static inline unsigned long +find_suitable_bucket_count (unsigned long element_count, int *size_index) +{ + unsigned i = 0; + + while (i < HASHTABLE_SIZE_N && element_count > hashtable_size[i]) + ++i; + + *size_index = i; + + return (hashtable_size[i]); +} + + static char *s_hashtable = "hashtable"; SCM weak_hashtables = SCM_EOL; +static inline SCM +make_hash_table_from_buckets (SCM buckets, scm_t_hashtable *c_table, int flags) +{ + SCM table, link; + + if (flags) + link = weak_hashtables; + else + link = SCM_EOL; + + SCM_NEWSMOB3 (table, scm_tc16_hashtable, buckets, c_table, link); + + if (flags) + weak_hashtables = table; + + return table; +} + static SCM make_hash_table (int flags, unsigned long k, const char *func_name) { SCM table, vector; scm_t_hashtable *t; - int i = 0, n = k ? k : 31; - while (i < HASHTABLE_SIZE_N && n > hashtable_size[i]) - ++i; - n = hashtable_size[i]; + int size_index = 0, n = k ? k : 31; + + n = find_suitable_bucket_count (k, &size_index); + if (flags) vector = scm_i_allocate_weak_vector (flags, scm_from_int (n), SCM_EOL); else vector = scm_c_make_vector (n, SCM_EOL); t = scm_gc_malloc (sizeof (*t), s_hashtable); - t->min_size_index = t->size_index = i; + t->min_size_index = t->size_index = size_index; t->n_items = 0; t->lower = 0; t->upper = 9 * n / 10; t->flags = flags; t->hash_fn = NULL; - if (flags) - { - SCM_NEWSMOB3 (table, scm_tc16_hashtable, vector, t, weak_hashtables); - weak_hashtables = table; - } - else - SCM_NEWSMOB3 (table, scm_tc16_hashtable, vector, t, SCM_EOL); + + table = make_hash_table_from_buckets (vector, t, flags); + return table; } @@ -309,6 +337,71 @@ } #undef FUNC_NAME +/* Copy the given alist, i.e., duplicate all its pairs recursively. */ +static inline SCM +alist_copy (SCM alist) +{ + SCM it, pair, result = SCM_EOL; + + for (it = alist; + !scm_is_null (it); + it = SCM_CDR (it)) + { + pair = SCM_CAR (it); + result = scm_cons (scm_cons (SCM_CAR (pair), SCM_CDR (pair)), + result); + } + + return result; +} + +SCM_DEFINE (scm_hash_table_copy, "hash-table-copy", 1, 0, 0, + (SCM table), + "Return a newly allocated hash table whose contents are the " + "same as those of @var{hashtab}. This should be faster than " + "traversing @var{table} and invoking @code{hash-set!} on a " + "new (empty) table for each element since the new table will " + "be readily balanced.") +#define FUNC_NAME s_scm_hash_table_copy +{ + size_t bucket_count, i; + scm_t_hashtable *c_new_table = NULL; + SCM buckets, new_buckets, new_table; + + if (SCM_HASHTABLE_P (table)) + { + buckets = SCM_HASHTABLE_VECTOR (table); + c_new_table = scm_gc_malloc (sizeof (* c_new_table), s_hashtable); + } + else + { + SCM_VALIDATE_VECTOR (1, table); + buckets = table; + } + + bucket_count = SCM_SIMPLE_VECTOR_LENGTH (buckets); + new_buckets = scm_c_make_vector (bucket_count, SCM_BOOL_F); + + for (i = 0; i < bucket_count; i++) + { + SCM alist; + + alist = alist_copy (SCM_SIMPLE_VECTOR_REF (buckets, i)); + SCM_SIMPLE_VECTOR_SET (new_buckets, i, alist); + } + + if (SCM_HASHTABLE_P (table)) + { + *c_new_table = *SCM_HASHTABLE (table); + new_table = make_hash_table_from_buckets (new_buckets, c_new_table, 0); + } + else + new_table = new_buckets; + + return new_table; +} +#undef FUNC_NAME + SCM_DEFINE (scm_make_weak_key_hash_table, "make-weak-key-hash-table", 0, 1, 0, (SCM n), "@deffnx {Scheme Procedure} make-weak-value-hash-table size\n" @@ -409,6 +502,9 @@ #undef FUNC_NAME + +/* Accessors. */ + SCM scm_hash_fn_get_handle (SCM table, SCM obj, unsigned long (*hash_fn)(), SCM (*assoc_fn)(), void * closure) #define FUNC_NAME "scm_hash_fn_get_handle" --- orig/libguile/modules.c +++ mod/libguile/modules.c @@ -162,12 +162,8 @@ static SCM module_export_x_var; - -/* - TODO: should export this function? --hwn. - */ -static SCM -scm_export (SCM module, SCM namelist) +SCM +scm_module_export (SCM module, SCM namelist) { return scm_call_2 (SCM_VARIABLE_REF (module_export_x_var), module, namelist); @@ -203,7 +199,7 @@ tail = SCM_CDRLOC (*tail); } va_end (ap); - scm_export (scm_current_module(), names); + scm_module_export (scm_current_module (), names); } } @@ -280,40 +276,113 @@ static SCM module_make_local_var_x_var; -static SCM -module_variable (SCM module, SCM sym) +SCM_DEFINE (scm_module_local_variable, "module-local-variable", 2, 0, 0, + (SCM module, SCM sym), + "Return the variable bound to @var{sym} in @var{module}. Return " + "@code{#f} is @var{sym} is not bound locally in @var{module}.") +#define FUNC_NAME s_scm_module_local_variable { #define SCM_BOUND_THING_P(b) \ (scm_is_true (b)) + register SCM b; + + /* SCM_MODULE_TAG is not initialized yet when `boot-9.scm' is being + evaluated. */ + if (scm_module_system_booted_p) + SCM_VALIDATE_MODULE (1, module); + + SCM_VALIDATE_SYMBOL (2, sym); + + /* 1. Check module obarray */ - SCM b = scm_hashq_ref (SCM_MODULE_OBARRAY (module), sym, SCM_UNDEFINED); + b = scm_hashq_ref (SCM_MODULE_OBARRAY (module), sym, SCM_UNDEFINED); if (SCM_BOUND_THING_P (b)) return b; + + /* 2. Search imported bindings. In order to be consistent with + `module-variable', the binder gets called only when no imported binding + matches SYM. */ + b = scm_hashq_ref (SCM_MODULE_IMPORT_OBARRAY (module), sym, SCM_UNDEFINED); + if (SCM_BOUND_THING_P (b)) + return SCM_BOOL_F; + { + /* 3. Query the custom binder. */ SCM binder = SCM_MODULE_BINDER (module); + if (scm_is_true (binder)) - /* 2. Custom binder */ { b = scm_call_3 (binder, module, sym, SCM_BOOL_F); if (SCM_BOUND_THING_P (b)) return b; } } + + return SCM_BOOL_F; + +#undef SCM_BOUND_THING_P +} +#undef FUNC_NAME + +SCM_DEFINE (scm_module_variable, "module-variable", 2, 0, 0, + (SCM module, SCM sym), + "Return the variable bound to @var{sym} in @var{module}. This " + "may be both a local variable or an imported variable. Return " + "@code{#f} is @var{sym} is not bound in @var{module}.") +#define FUNC_NAME s_scm_module_variable +{ + SCM_VALIDATE_MODULE (1, module); + SCM_VALIDATE_SYMBOL (2, sym); + +#define SCM_BOUND_THING_P(b) \ + (scm_is_true (b)) + + /* 1. Check module obarray */ + register SCM b, binder; + + lookup: + binder = SCM_MODULE_BINDER (module); + + b = scm_hashq_ref (SCM_MODULE_OBARRAY (module), sym, SCM_UNDEFINED); + if (SCM_BOUND_THING_P (b)) + return b; + + { + /* 2. Search imported bindings. */ + SCM iface, imports = SCM_MODULE_IMPORT_OBARRAY (module); + + iface = scm_hashq_ref (imports, sym, SCM_UNDEFINED); + if (SCM_MODULEP (iface)) + { + if (scm_is_false (binder)) + { + /* Tail-recursive call. */ + module = iface; + goto lookup; + } + + b = scm_module_variable (iface, sym); + if (SCM_BOUND_THING_P (b)) + return b; + } + } + { - /* 3. Search the use list */ - SCM uses = SCM_MODULE_USES (module); - while (scm_is_pair (uses)) + /* 3. Query the custom binder. */ + if (scm_is_true (binder)) { - b = module_variable (SCM_CAR (uses), sym); + b = scm_call_3 (binder, module, sym, SCM_BOOL_F); if (SCM_BOUND_THING_P (b)) return b; - uses = SCM_CDR (uses); } - return SCM_BOOL_F; } + + return SCM_BOOL_F; + #undef SCM_BOUND_THING_P } +#undef FUNC_NAME scm_t_bits scm_tc16_eval_closure; @@ -335,7 +404,7 @@ module, sym); } else - return module_variable (module, sym); + return scm_module_variable (module, sym); } SCM_DEFINE (scm_standard_eval_closure, "standard-eval-closure", 1, 0, 0, @@ -398,38 +467,16 @@ SCM_DEFINE (scm_module_import_interface, "module-import-interface", 2, 0, 0, (SCM module, SCM sym), - "") + "Return the module or interface from which @var{sym} is imported " + "in @var{module}. If @var{sym} is not imported (i.e., it is not " + "defined in @var{module} or it is a module-local binding instead " + "of an imported one), then @code{#f} is returned.") #define FUNC_NAME s_scm_module_import_interface { -#define SCM_BOUND_THING_P(b) (scm_is_true (b)) - SCM uses; - SCM_VALIDATE_MODULE (SCM_ARG1, module); - /* Search the use list */ - uses = SCM_MODULE_USES (module); - while (scm_is_pair (uses)) - { - SCM _interface = SCM_CAR (uses); - /* 1. Check module obarray */ - SCM b = scm_hashq_ref (SCM_MODULE_OBARRAY (_interface), sym, SCM_BOOL_F); - if (SCM_BOUND_THING_P (b)) - return _interface; - { - SCM binder = SCM_MODULE_BINDER (_interface); - if (scm_is_true (binder)) - /* 2. Custom binder */ - { - b = scm_call_3 (binder, _interface, sym, SCM_BOOL_F); - if (SCM_BOUND_THING_P (b)) - return _interface; - } - } - /* 3. Search use list recursively. */ - _interface = scm_module_import_interface (_interface, sym); - if (scm_is_true (_interface)) - return _interface; - uses = SCM_CDR (uses); - } - return SCM_BOOL_F; + SCM_VALIDATE_MODULE (1, module); + SCM_VALIDATE_SYMBOL (2, sym); + + return (scm_hashq_ref (SCM_MODULE_IMPORT_OBARRAY (module), sym, SCM_BOOL_F)); } #undef FUNC_NAME @@ -560,52 +607,76 @@ return var; } -SCM -scm_module_reverse_lookup (SCM module, SCM variable) -#define FUNC_NAME "module-reverse-lookup" +SCM_DEFINE (scm_module_reverse_lookup, "module-reverse-lookup", 2, 0, 0, + (SCM module, SCM variable), + "Return the symbol under which @var{variable} is bound in " + "@var{module} or @var{#f} if @var{variable} is not visible " + "from @var{module}. If @var{module} is @code{#f}, then the " + "pre-module obarray is used.") +#define FUNC_NAME s_scm_module_reverse_lookup { - SCM obarray; - long i, n; + unsigned long i, n; + SCM obarray, import_obarray; if (scm_is_false (module)) - obarray = scm_pre_modules_obarray; + { + obarray = scm_pre_modules_obarray; + import_obarray = SCM_BOOL_F; + } else { SCM_VALIDATE_MODULE (1, module); obarray = SCM_MODULE_OBARRAY (module); + import_obarray = SCM_MODULE_IMPORT_OBARRAY (module); } - if (!SCM_HASHTABLE_P (obarray)) - return SCM_BOOL_F; - - /* XXX - We do not use scm_hash_fold here to avoid searching the - whole obarray. We should have a scm_hash_find procedure. */ + SCM_VALIDATE_VARIABLE (2, variable); + /* Search the module's obarray. + XXX - We do not use scm_hash_fold here to avoid searching the whole + obarray. We should have a scm_hash_find procedure. */ n = SCM_HASHTABLE_N_BUCKETS (obarray); for (i = 0; i < n; ++i) { - SCM ls = SCM_HASHTABLE_BUCKET (obarray, i), handle; - while (!scm_is_null (ls)) + SCM handle, ls; + + for (ls = SCM_HASHTABLE_BUCKET (obarray, i); + !scm_is_null (ls); + ls = SCM_CDR (ls)) { handle = SCM_CAR (ls); if (SCM_CDR (handle) == variable) return SCM_CAR (handle); - ls = SCM_CDR (ls); } } - /* Try the `uses' list. - */ - { - SCM uses = SCM_MODULE_USES (module); - while (scm_is_pair (uses)) - { - SCM sym = scm_module_reverse_lookup (SCM_CAR (uses), variable); - if (scm_is_true (sym)) - return sym; - uses = SCM_CDR (uses); - } - } + if (scm_is_true (import_obarray)) + { + /* Now, search the import obarray (which requires some more work). */ + n = SCM_HASHTABLE_N_BUCKETS (import_obarray); + for (i = 0; i < n; ++i) + { + SCM handle, iface, ls; + + for (ls = SCM_HASHTABLE_BUCKET (import_obarray, i); + !scm_is_null (ls); + ls = SCM_CDR (ls)) + { + handle = SCM_CAR (ls); + iface = SCM_CDR (handle); + if (SCM_MODULEP (iface)) + { + SCM sym, var; + + sym = SCM_CAR (handle); + var = scm_module_variable (iface, sym); + + if (scm_is_eq (var, variable)) + return sym; + } + } + } + } return SCM_BOOL_F; } --- orig/libguile/modules.h +++ mod/libguile/modules.h @@ -45,6 +45,7 @@ #define scm_module_index_binder 2 #define scm_module_index_eval_closure 3 #define scm_module_index_transformer 4 +#define scm_module_index_import_obarray 12 #define SCM_MODULE_OBARRAY(module) \ SCM_PACK (SCM_STRUCT_DATA (module) [scm_module_index_obarray]) @@ -56,6 +57,8 @@ SCM_PACK (SCM_STRUCT_DATA (module)[scm_module_index_eval_closure]) #define SCM_MODULE_TRANSFORMER(module) \ SCM_PACK (SCM_STRUCT_DATA (module)[scm_module_index_transformer]) +#define SCM_MODULE_IMPORT_OBARRAY(module) \ + SCM_PACK (SCM_STRUCT_DATA (module)[scm_module_index_import_obarray]) SCM_API scm_t_bits scm_tc16_eval_closure; @@ -64,6 +67,8 @@ SCM_API SCM scm_current_module (void); +SCM_API SCM scm_module_variable (SCM module, SCM sym); +SCM_API SCM scm_module_local_variable (SCM module, SCM sym); SCM_API SCM scm_interaction_environment (void); SCM_API SCM scm_set_current_module (SCM module); @@ -80,6 +85,7 @@ SCM_API SCM scm_c_module_define (SCM module, const char *name, SCM val); SCM_API SCM scm_module_lookup (SCM module, SCM symbol); SCM_API SCM scm_module_define (SCM module, SCM symbol, SCM val); +SCM_API SCM scm_module_export (SCM module, SCM symbol_list); SCM_API SCM scm_module_reverse_lookup (SCM module, SCM variable); SCM_API SCM scm_c_resolve_module (const char *name); --- orig/oop/goops.scm +++ mod/oop/goops.scm @@ -85,7 +85,67 @@ ;; Then load the rest of GOOPS (use-modules (oop goops util) (oop goops dispatch) - (oop goops compile)) + (oop goops compile) + (oop goops smob-classes)) + +;; Re-export the SMOB classes defined in the `smob-classes' module. The +;; `smob-classes' module is a "virtual" module created and populated by +;; `create_smob_classes ()'. + +(re-export ;; FIXME: We certainly don't need all of them. + + + + + + + + + + + + + +;; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +) + (define min-fixnum (- (expt 2 29))) --- orig/oop/goops/internal.scm +++ mod/oop/goops/internal.scm @@ -21,5 +21,10 @@ (define-module (oop goops internal) :use-module (oop goops)) -(set-module-uses! %module-public-interface - (list (nested-ref the-root-module '(app modules oop goops)))) +;; Export all the bindings that are internal to `(oop goops)'. +(let ((public-i (module-public-interface (current-module)))) + (module-for-each (lambda (name var) + (if (eq? name '%module-public-interface) + #t + (module-add! public-i name var))) + (resolve-module '(oop goops)))) --- orig/srfi/srfi-34.scm +++ mod/srfi/srfi-34.scm @@ -27,8 +27,8 @@ ;;; Code: (define-module (srfi srfi-34) - #:export (with-exception-handler - raise) + #:export (with-exception-handler) + #:replace (raise) #:export-syntax (guard)) (cond-expand-provide (current-module) '(srfi-34)) --- orig/test-suite/tests/hash.test +++ mod/test-suite/tests/hash.test @@ -72,3 +72,37 @@ (hashx-set! hashq assq table 'x 123) (hashx-remove! hashq assq table 'x) (null? (hash-map->list noop table))))) + +;;; +;;; hash-table-copy +;;; + +(with-test-prefix "hash-table-copy" + + (pass-if "hash-table" + (let ((table (make-hash-table)) + (pairlist cons table) pairlist cons new-table) pairlist cons table) pairlist cons new-table) pairbool (every (lambda (id) + (hashq-ref observers-invoked id)) + observer-ids)))) + + (pass-if "imported bindings updated" + (let ((m (make-module)) + (imported (make-module))) + ;; Beautify them, notably adding them a public interface. + (beautify-user-module! m) + (beautify-user-module! imported) + + (module-use! m (module-public-interface imported)) + (module-define! imported 'imported-binding #t) + + ;; At this point, `imported-binding' is local to IMPORTED. + (and (not (module-variable m 'imported-binding)) + (begin + ;; Export `imported-binding' from IMPORTED. + (module-export! imported '(imported-binding)) + + ;; Make sure it is now visible from M. + (module-ref m 'imported-binding)))))) + + + +;;; +;;; Duplicate bindings handling. +;;; + +(with-test-prefix "duplicate bindings" + + (pass-if "simple duplicate handler" + ;; Import the same binding twice. + (let* ((m (make-module)) + (import1 (make-module)) + (import2 (make-module)) + (handler-invoked? #f) + (handler (lambda (module name int1 val1 int2 val2 var val) + (set! handler-invoked? #t) + ;; Keep the first binding. + (or var (module-local-variable int1 name))))) + + (set-module-duplicates-handlers! m (list handler)) + (module-define! m 'something 'something) + (module-define! import1 'imported 'imported-1) + (module-define! import2 'imported 'imported-2) + (module-use! m import1) + (module-use! m import2) + (and handler-invoked? + (eq? (module-ref m 'imported) 'imported-1))))) + + +;;; +;;; Lazy binder. +;;; + +(with-test-prefix "lazy binder" + + (pass-if "not invoked" + (let ((m (make-module)) + (invoked? #f)) + (module-define! m 'something 2) + (set-module-binder! m (lambda args (set! invoked? #t) #f)) + (and (module-ref m 'something) + (not invoked?)))) + + (pass-if "not invoked (module-add!)" + (let ((m (make-module)) + (invoked? #f)) + (set-module-binder! m (lambda args (set! invoked? #t) #f)) + (module-add! m 'something (make-variable 2)) + (and (module-ref m 'something) + (not invoked?)))) + + (pass-if "invoked (module-ref)" + (let ((m (make-module)) + (invoked? #f)) + (set-module-binder! m (lambda args (set! invoked? #t) #f)) + (false-if-exception (module-ref m 'something)) + invoked?)) + + (pass-if "invoked (module-define!)" + (let ((m (make-module)) + (invoked? #f)) + (set-module-binder! m (lambda args (set! invoked? #t) #f)) + (module-define! m 'something 2) + (and invoked? + (eq? (module-ref m 'something) 2)))) + + (pass-if "honored (ref)" + (let ((m (make-module)) + (invoked? #f) + (value (cons 'x 'y))) + (set-module-binder! m + (lambda (mod sym define?) + (set! invoked? #t) + (cond ((not (eq? m mod)) + (error "invalid module" mod)) + (define? + (error "DEFINE? shouldn't be set")) + (else + (make-variable value))))) + (and (eq? (module-ref m 'something) value) + invoked?)))) + + + +;;; +;;; Higher-level features. +;;; (with-test-prefix "autoload" + (pass-if "module-autoload!" + (let ((m (make-module))) + (module-autoload! m '(ice-9 q) '(make-q)) + (not (not (module-ref m 'make-q))))) + (pass-if "autoloaded" (catch #t (lambda ()