diff options
| author | Stefan Monnier | 2022-10-17 17:11:40 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2022-10-25 14:24:54 -0400 |
| commit | 1b1ffe07897ebe06cf96ab423fad3cde9fd6c981 (patch) | |
| tree | a8a6eaf194cf0dce4758742d04b537e81d8a7a12 /src | |
| parent | 7e60246ab3cb7a3c40ca48d8ea9c107f00a6aea6 (diff) | |
| download | emacs-1b1ffe07897ebe06cf96ab423fad3cde9fd6c981.tar.gz emacs-1b1ffe07897ebe06cf96ab423fad3cde9fd6c981.zip | |
(Ffunction): Make interpreted closures safe for space
Interpreted closures currently just grab a reference to the complete
lexical environment, so (lambda (x) (+ x y)) can end up looking like
(closure ((foo ...) (y 7) (bar ...) ...)
(x) (+ x y))
where the foo/bar/... bindings are not only useless but can prevent
the GC from collecting that memory (i.e. it's a representation that is
not "safe for space") and it can also make that closure "unwritable"
(or more specifically, it can cause the closure's print
representation to be u`read`able).
Compiled closures don't suffer from this problem because `cconv.el`
actually looks at the code and only stores in the compiled closure
those variables which are actually used.
So, we fix this discrepancy by letting the existing code in `cconv.el` tell
`Ffunction` which variables are actually used by the body of the
function such that it can filter out the irrelevant elements and
return a closure of the form:
(closure ((y 7)) (x) (+ x y))
* lisp/loadup.el: Preload `cconv` and set
`internal-filter-closure-env-function` once we have a usable `cconv-fv`.
* lisp/emacs-lisp/bytecomp.el (byte-compile-preprocess): Adjust to new
calling convention of `cconv-closure-convert`.
(byte-compile-not-lexical-var-p): Delete function, moved to `cconv.el`.
(byte-compile-bind): Use `cconv--not-lexical-var-p`.
* lisp/emacs-lisp/cconv.el (cconv--dynbound-variables): New var.
(cconv-closure-convert): New arg `dynbound-vars`
(cconv--warn-unused-msg): Remove special case for `ignored`,
so we don't get confused when a function uses an argument called
`ignored`, e.g. holding a list of things that it should ignore.
(cconv--not-lexical-var-p): New function, moved from `bytecomp.el`.
Don't special case keywords and `nil` and `t` since they are already
`special-variable-p`.
(cconv--analyze-function): Use `cconv--not-lexical-var-p`.
(cconv--dynbindings): New dynbound var.
(cconv-analyze-form): Use `cconv--not-lexical-var-p`.
Remember in `cconv--dynbindings` the vars for which we used
dynamic scoping.
(cconv-analyze-form): Use `cconv--dynbound-variables` rather than
`byte-compile-bound-variables`.
(cconv-fv): New function.
* src/eval.c (Fsetq, eval_sub): Remove optimization designed when
`lexical-binding == nil` was the common case.
(Ffunction): Use `internal-filter-closure-env-function` when available.
(eval_sub, Ffuncall): Improve error info for `excessive_lisp_nesting`.
(internal-filter-closure-env-function): New defvar.
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval.c | 27 |
1 files changed, 18 insertions, 9 deletions
diff --git a/src/eval.c b/src/eval.c index 8810136c041..d2cab006d11 100644 --- a/src/eval.c +++ b/src/eval.c | |||
| @@ -484,8 +484,7 @@ usage: (setq [SYM VAL]...) */) | |||
| 484 | /* Like for eval_sub, we do not check declared_special here since | 484 | /* Like for eval_sub, we do not check declared_special here since |
| 485 | it's been done when let-binding. */ | 485 | it's been done when let-binding. */ |
| 486 | Lisp_Object lex_binding | 486 | Lisp_Object lex_binding |
| 487 | = ((!NILP (Vinternal_interpreter_environment) /* Mere optimization! */ | 487 | = (SYMBOLP (sym) |
| 488 | && SYMBOLP (sym)) | ||
| 489 | ? Fassq (sym, Vinternal_interpreter_environment) | 488 | ? Fassq (sym, Vinternal_interpreter_environment) |
| 490 | : Qnil); | 489 | : Qnil); |
| 491 | if (!NILP (lex_binding)) | 490 | if (!NILP (lex_binding)) |
| @@ -551,8 +550,15 @@ usage: (function ARG) */) | |||
| 551 | CHECK_STRING (docstring); | 550 | CHECK_STRING (docstring); |
| 552 | cdr = Fcons (XCAR (cdr), Fcons (docstring, XCDR (XCDR (cdr)))); | 551 | cdr = Fcons (XCAR (cdr), Fcons (docstring, XCDR (XCDR (cdr)))); |
| 553 | } | 552 | } |
| 554 | return Fcons (Qclosure, Fcons (Vinternal_interpreter_environment, | 553 | Lisp_Object env |
| 555 | cdr)); | 554 | = NILP (Vinternal_filter_closure_env_function) |
| 555 | ? Vinternal_interpreter_environment | ||
| 556 | /* FIXME: This macroexpands the body, so we should use the resulting | ||
| 557 | macroexpanded code! */ | ||
| 558 | : call2 (Vinternal_filter_closure_env_function, | ||
| 559 | Fcons (Qprogn, CONSP (cdr) ? XCDR (cdr) : cdr), | ||
| 560 | Vinternal_interpreter_environment); | ||
| 561 | return Fcons (Qclosure, Fcons (env, cdr)); | ||
| 556 | } | 562 | } |
| 557 | else | 563 | else |
| 558 | /* Simply quote the argument. */ | 564 | /* Simply quote the argument. */ |
| @@ -2374,9 +2380,7 @@ eval_sub (Lisp_Object form) | |||
| 2374 | We do not pay attention to the declared_special flag here, since we | 2380 | We do not pay attention to the declared_special flag here, since we |
| 2375 | already did that when let-binding the variable. */ | 2381 | already did that when let-binding the variable. */ |
| 2376 | Lisp_Object lex_binding | 2382 | Lisp_Object lex_binding |
| 2377 | = (!NILP (Vinternal_interpreter_environment) /* Mere optimization! */ | 2383 | = Fassq (form, Vinternal_interpreter_environment); |
| 2378 | ? Fassq (form, Vinternal_interpreter_environment) | ||
| 2379 | : Qnil); | ||
| 2380 | return !NILP (lex_binding) ? XCDR (lex_binding) : Fsymbol_value (form); | 2384 | return !NILP (lex_binding) ? XCDR (lex_binding) : Fsymbol_value (form); |
| 2381 | } | 2385 | } |
| 2382 | 2386 | ||
| @@ -2392,7 +2396,7 @@ eval_sub (Lisp_Object form) | |||
| 2392 | if (max_lisp_eval_depth < 100) | 2396 | if (max_lisp_eval_depth < 100) |
| 2393 | max_lisp_eval_depth = 100; | 2397 | max_lisp_eval_depth = 100; |
| 2394 | if (lisp_eval_depth > max_lisp_eval_depth) | 2398 | if (lisp_eval_depth > max_lisp_eval_depth) |
| 2395 | xsignal0 (Qexcessive_lisp_nesting); | 2399 | xsignal1 (Qexcessive_lisp_nesting, make_fixnum (lisp_eval_depth)); |
| 2396 | } | 2400 | } |
| 2397 | 2401 | ||
| 2398 | Lisp_Object original_fun = XCAR (form); | 2402 | Lisp_Object original_fun = XCAR (form); |
| @@ -2966,7 +2970,7 @@ usage: (funcall FUNCTION &rest ARGUMENTS) */) | |||
| 2966 | if (max_lisp_eval_depth < 100) | 2970 | if (max_lisp_eval_depth < 100) |
| 2967 | max_lisp_eval_depth = 100; | 2971 | max_lisp_eval_depth = 100; |
| 2968 | if (lisp_eval_depth > max_lisp_eval_depth) | 2972 | if (lisp_eval_depth > max_lisp_eval_depth) |
| 2969 | xsignal0 (Qexcessive_lisp_nesting); | 2973 | xsignal1 (Qexcessive_lisp_nesting, make_fixnum (lisp_eval_depth)); |
| 2970 | } | 2974 | } |
| 2971 | 2975 | ||
| 2972 | count = record_in_backtrace (args[0], &args[1], nargs - 1); | 2976 | count = record_in_backtrace (args[0], &args[1], nargs - 1); |
| @@ -4357,6 +4361,11 @@ alist of active lexical bindings. */); | |||
| 4357 | (Just imagine if someone makes it buffer-local). */ | 4361 | (Just imagine if someone makes it buffer-local). */ |
| 4358 | Funintern (Qinternal_interpreter_environment, Qnil); | 4362 | Funintern (Qinternal_interpreter_environment, Qnil); |
| 4359 | 4363 | ||
| 4364 | DEFVAR_LISP ("internal-filter-closure-env-function", | ||
| 4365 | Vinternal_filter_closure_env_function, | ||
| 4366 | doc: /* Function to filter the env when constructing a closure. */); | ||
| 4367 | Vinternal_filter_closure_env_function = Qnil; | ||
| 4368 | |||
| 4360 | Vrun_hooks = intern_c_string ("run-hooks"); | 4369 | Vrun_hooks = intern_c_string ("run-hooks"); |
| 4361 | staticpro (&Vrun_hooks); | 4370 | staticpro (&Vrun_hooks); |
| 4362 | 4371 | ||