dynamic-require: fast path for re-exported variables

When `dynamic-require` is used to access an export that isn't a
variable defined in the same module, `dynamic-require` falls
back to `eval` in a fresh namespace, which can be expensive.
The new fast path handles the case that a variable is re-exported.

The new fast path is relevant to deserialization, which now
uses a submodule that re-exports from an enclosing module.
This commit is contained in:
Matthew Flatt 2013-12-19 12:22:05 -07:00
parent abc174e16c
commit 01eece18a7
2 changed files with 98 additions and 5 deletions

View File

@ -1004,6 +1004,51 @@
(begin-for-syntax
(begin-for-syntax
(define y 2))))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Check `dynamic-require` re-export fast path
(for ([specs (list (list '(provide)
'x)
(list '(provide x)
'x)
(list '(provide (rename-out [x xx]))
'x)
(list '(provide (rename-out [y x]))
'x)
(list '(provide)
'(rename-out [y x])
"y")
(list '(provide)
'(rename-out [z x])
"x"
;; slow:
"exp\nexp\nrun\nexp\nexp\n"))])
(define ns (make-base-namespace))
(define o (open-output-string))
(parameterize ([current-output-port o])
(eval `(module m racket/base
(require (for-syntax racket/base))
(begin-for-syntax (displayln "exp"))
(define x "x")
(define y "y")
(define-syntax (z stx) #'x)
,(car specs)
(module* sub #f
(displayln "run")
(provide ,(cadr specs))))
ns))
(define expected (if (null? (cddr specs)) "x" (caddr specs)))
(define expected-out (if ((length specs) . < . 4)
"exp\nexp\nrun\n"
(list-ref specs 3)))
(define (dynamic-require/o m x)
(parameterize ([current-output-port o])
(dynamic-require m x)))
(parameterize ([current-namespace ns])
(test expected dynamic-require/o '(submod 'm sub) 'x)
(test expected dynamic-require/o '(submod 'm sub) 'x))
(test expected-out get-output-string o))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -1205,8 +1205,57 @@ static Scheme_Object *_dynamic_require(int argc, Scheme_Object *argv[],
break;
} else {
if (fail_with_error) {
if (!phase) {
/* Evaluate id in a fresh namespace */
if (!phase
&& srcm->me->rt->provide_srcs
&& SCHEME_TRUEP(srcm->me->rt->provide_srcs[i])) {
/* Handle simple re-exporting */
int j;
Scheme_Module *srcm2;
srcmname = srcm->me->rt->provide_srcs[i];
srcmname = scheme_modidx_shift(srcmname, srcm->me->src_modidx, srcm->self_modidx);
srcmname = scheme_module_resolve(srcmname, 1);
srcname = srcm->me->rt->provide_src_names[i];
srcm2 = module_load(srcmname, env, errname);
for (j = srcm2->me->rt->num_var_provides; j--; ) {
if (SCHEME_FALSEP(srcm2->me->rt->provide_srcs[j])
&& SAME_OBJ(srcname, srcm2->me->rt->provide_src_names[j])) {
/* simple re-export applies: */
srcm = srcm2;
count = srcm->me->rt->num_provides;
name = srcm2->me->rt->provides[j];
i = j;
break;
}
}
if (j < 0) {
/* Try indirect: */
Scheme_Module_Export_Info *exp_info = srcm2->exp_infos[0];
for (j = exp_info->num_indirect_provides; j--; ) {
if (SAME_OBJ(srcname, exp_info->indirect_provides[j])) {
srcm = srcm2;
name = srcname;
count = srcm->me->rt->num_provides;
i = count;
position = j;
indirect_ok = 1;
break;
}
}
if (j < 0) {
/* simple re-exporting doesn't work */
srcmname = NULL;
}
}
}
if (srcmname) {
/* Simple re-exporting shortcut worked */
break;
} else if (!phase) {
/* The long way: evaluate id in a fresh namespace */
Scheme_Object *a[3], *ns;
Scheme_Config *config;
Scheme_Cont_Frame_Data cframe;
@ -1243,7 +1292,7 @@ static Scheme_Object *_dynamic_require(int argc, Scheme_Object *argv[],
NULL);
}
}
return NULL;
return NULL;
}
}
}
@ -1265,8 +1314,7 @@ static Scheme_Object *_dynamic_require(int argc, Scheme_Object *argv[],
if (i == count) {
if (indirect_ok) {
/* Try indirect provides: */
Scheme_Module_Export_Info *exp_info = m->exp_infos[0];
srcm = m;
Scheme_Module_Export_Info *exp_info = srcm->exp_infos[0];
count = exp_info->num_indirect_provides;
if (position >= 0) {
i = position;