aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorStefan Monnier2022-10-17 17:11:40 -0400
committerStefan Monnier2022-10-25 14:24:54 -0400
commit1b1ffe07897ebe06cf96ab423fad3cde9fd6c981 (patch)
treea8a6eaf194cf0dce4758742d04b537e81d8a7a12 /src
parent7e60246ab3cb7a3c40ca48d8ea9c107f00a6aea6 (diff)
downloademacs-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.c27
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