diff options
| author | Philipp Stephani | 2025-01-30 16:12:49 +0100 |
|---|---|---|
| committer | Philipp Stephani | 2025-02-28 01:45:35 +0100 |
| commit | 32da093e524d5e28945557701f7c50d7c4a898cd (patch) | |
| tree | 00275d27c38ef719d99b03b4ccb68023fae71571 /src | |
| parent | ea715b0183f6a19d491cad36eb18c2c9cf0f0dd3 (diff) | |
| download | emacs-32da093e524d5e28945557701f7c50d7c4a898cd.tar.gz emacs-32da093e524d5e28945557701f7c50d7c4a898cd.zip | |
Don't overwrite non-local exit symbol and data (Bug#65796).
The previous approach would incorrectly invalidate the returned module
values if another non-local exit occurred while dealing with a non-local
exit. See Bug#65796. Instead, allocate the values from the usual
environment storage, and return statically-allocated objects if that
fails.
* src/emacs-module.c (struct emacs_env_private): Turn non-local exit
symbol and data into normal Lisp objects.
(initialize_environment): Initialize them.
(mark_module_environment): Prevent them from being garbage-collected.
(module_signal_or_throw, module_non_local_exit_signal_1)
(module_non_local_exit_throw_1): Adapt uses.
(value_to_lisp): No longer scan for them with module assertions enabled.
(module_out_of_memory_signal, module_out_of_memory_data): New
statically-allocated module values to return in case of allocation
failure.
(syms_of_module): Initialize them.
(module_non_local_exit_get): Allocate module values normally. If that
fails, return statically-allocated values.
* doc/lispref/internals.texi (Module Nonlocal): Document new behavior.
Diffstat (limited to 'src')
| -rw-r--r-- | src/emacs-module.c | 63 |
1 files changed, 43 insertions, 20 deletions
diff --git a/src/emacs-module.c b/src/emacs-module.c index 0a67433ec70..ab6b900df8d 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c | |||
| @@ -167,7 +167,7 @@ struct emacs_env_private | |||
| 167 | /* Dedicated storage for non-local exit symbol and data so that | 167 | /* Dedicated storage for non-local exit symbol and data so that |
| 168 | storage is always available for them, even in an out-of-memory | 168 | storage is always available for them, even in an out-of-memory |
| 169 | situation. */ | 169 | situation. */ |
| 170 | struct emacs_value_tag non_local_exit_symbol, non_local_exit_data; | 170 | Lisp_Object non_local_exit_symbol, non_local_exit_data; |
| 171 | 171 | ||
| 172 | struct emacs_value_storage storage; | 172 | struct emacs_value_storage storage; |
| 173 | }; | 173 | }; |
| @@ -500,6 +500,9 @@ module_non_local_exit_clear (emacs_env *env) | |||
| 500 | env->private_members->pending_non_local_exit = emacs_funcall_exit_return; | 500 | env->private_members->pending_non_local_exit = emacs_funcall_exit_return; |
| 501 | } | 501 | } |
| 502 | 502 | ||
| 503 | static struct emacs_value_tag module_out_of_memory_symbol; | ||
| 504 | static struct emacs_value_tag module_out_of_memory_data; | ||
| 505 | |||
| 503 | static enum emacs_funcall_exit | 506 | static enum emacs_funcall_exit |
| 504 | module_non_local_exit_get (emacs_env *env, | 507 | module_non_local_exit_get (emacs_env *env, |
| 505 | emacs_value *symbol, emacs_value *data) | 508 | emacs_value *symbol, emacs_value *data) |
| @@ -507,12 +510,23 @@ module_non_local_exit_get (emacs_env *env, | |||
| 507 | module_assert_thread (); | 510 | module_assert_thread (); |
| 508 | module_assert_env (env); | 511 | module_assert_env (env); |
| 509 | struct emacs_env_private *p = env->private_members; | 512 | struct emacs_env_private *p = env->private_members; |
| 510 | if (p->pending_non_local_exit != emacs_funcall_exit_return) | 513 | enum emacs_funcall_exit ret = p->pending_non_local_exit; |
| 514 | if (ret != emacs_funcall_exit_return) | ||
| 511 | { | 515 | { |
| 512 | *symbol = &p->non_local_exit_symbol; | 516 | emacs_value sym |
| 513 | *data = &p->non_local_exit_data; | 517 | = allocate_emacs_value (env, p->non_local_exit_symbol); |
| 518 | emacs_value dat | ||
| 519 | = allocate_emacs_value (env, p->non_local_exit_data); | ||
| 520 | if (sym == NULL || dat == NULL) | ||
| 521 | { | ||
| 522 | sym = &module_out_of_memory_symbol; | ||
| 523 | dat = &module_out_of_memory_data; | ||
| 524 | ret = emacs_funcall_exit_signal; | ||
| 525 | } | ||
| 526 | *symbol = sym; | ||
| 527 | *data = dat; | ||
| 514 | } | 528 | } |
| 515 | return p->pending_non_local_exit; | 529 | return ret; |
| 516 | } | 530 | } |
| 517 | 531 | ||
| 518 | /* Like for `signal', DATA must be a list. */ | 532 | /* Like for `signal', DATA must be a list. */ |
| @@ -1185,11 +1199,11 @@ module_signal_or_throw (struct emacs_env_private *env) | |||
| 1185 | case emacs_funcall_exit_return: | 1199 | case emacs_funcall_exit_return: |
| 1186 | return; | 1200 | return; |
| 1187 | case emacs_funcall_exit_signal: | 1201 | case emacs_funcall_exit_signal: |
| 1188 | xsignal (value_to_lisp (&env->non_local_exit_symbol), | 1202 | xsignal (env->non_local_exit_symbol, |
| 1189 | value_to_lisp (&env->non_local_exit_data)); | 1203 | env->non_local_exit_data); |
| 1190 | case emacs_funcall_exit_throw: | 1204 | case emacs_funcall_exit_throw: |
| 1191 | Fthrow (value_to_lisp (&env->non_local_exit_symbol), | 1205 | Fthrow (env->non_local_exit_symbol, |
| 1192 | value_to_lisp (&env->non_local_exit_data)); | 1206 | env->non_local_exit_data); |
| 1193 | default: | 1207 | default: |
| 1194 | eassume (false); | 1208 | eassume (false); |
| 1195 | } | 1209 | } |
| @@ -1389,8 +1403,8 @@ module_non_local_exit_signal_1 (emacs_env *env, Lisp_Object sym, | |||
| 1389 | if (p->pending_non_local_exit == emacs_funcall_exit_return) | 1403 | if (p->pending_non_local_exit == emacs_funcall_exit_return) |
| 1390 | { | 1404 | { |
| 1391 | p->pending_non_local_exit = emacs_funcall_exit_signal; | 1405 | p->pending_non_local_exit = emacs_funcall_exit_signal; |
| 1392 | p->non_local_exit_symbol.v = sym; | 1406 | p->non_local_exit_symbol = sym; |
| 1393 | p->non_local_exit_data.v = data; | 1407 | p->non_local_exit_data = data; |
| 1394 | } | 1408 | } |
| 1395 | } | 1409 | } |
| 1396 | 1410 | ||
| @@ -1402,8 +1416,8 @@ module_non_local_exit_throw_1 (emacs_env *env, Lisp_Object tag, | |||
| 1402 | if (p->pending_non_local_exit == emacs_funcall_exit_return) | 1416 | if (p->pending_non_local_exit == emacs_funcall_exit_return) |
| 1403 | { | 1417 | { |
| 1404 | p->pending_non_local_exit = emacs_funcall_exit_throw; | 1418 | p->pending_non_local_exit = emacs_funcall_exit_throw; |
| 1405 | p->non_local_exit_symbol.v = tag; | 1419 | p->non_local_exit_symbol = tag; |
| 1406 | p->non_local_exit_data.v = value; | 1420 | p->non_local_exit_data = value; |
| 1407 | } | 1421 | } |
| 1408 | } | 1422 | } |
| 1409 | 1423 | ||
| @@ -1439,13 +1453,6 @@ value_to_lisp (emacs_value v) | |||
| 1439 | { | 1453 | { |
| 1440 | const emacs_env *env = pdl->unwind_ptr.arg; | 1454 | const emacs_env *env = pdl->unwind_ptr.arg; |
| 1441 | struct emacs_env_private *priv = env->private_members; | 1455 | struct emacs_env_private *priv = env->private_members; |
| 1442 | /* The value might be one of the nonlocal exit values. Note | ||
| 1443 | that we don't check whether a nonlocal exit is currently | ||
| 1444 | pending, because the module might have cleared the flag | ||
| 1445 | in the meantime. */ | ||
| 1446 | if (&priv->non_local_exit_symbol == v | ||
| 1447 | || &priv->non_local_exit_data == v) | ||
| 1448 | goto ok; | ||
| 1449 | if (value_storage_contains_p (&priv->storage, v, &num_values)) | 1456 | if (value_storage_contains_p (&priv->storage, v, &num_values)) |
| 1450 | goto ok; | 1457 | goto ok; |
| 1451 | ++num_environments; | 1458 | ++num_environments; |
| @@ -1536,6 +1543,8 @@ mark_module_environment (void *ptr) | |||
| 1536 | { | 1543 | { |
| 1537 | emacs_env *env = ptr; | 1544 | emacs_env *env = ptr; |
| 1538 | struct emacs_env_private *priv = env->private_members; | 1545 | struct emacs_env_private *priv = env->private_members; |
| 1546 | mark_object (priv->non_local_exit_symbol); | ||
| 1547 | mark_object (priv->non_local_exit_data); | ||
| 1539 | for (struct emacs_value_frame *frame = &priv->storage.initial; frame != NULL; | 1548 | for (struct emacs_value_frame *frame = &priv->storage.initial; frame != NULL; |
| 1540 | frame = frame->next) | 1549 | frame = frame->next) |
| 1541 | for (int i = 0; i < frame->offset; ++i) | 1550 | for (int i = 0; i < frame->offset; ++i) |
| @@ -1561,6 +1570,8 @@ initialize_environment (emacs_env *env, struct emacs_env_private *priv) | |||
| 1561 | } | 1570 | } |
| 1562 | 1571 | ||
| 1563 | priv->pending_non_local_exit = emacs_funcall_exit_return; | 1572 | priv->pending_non_local_exit = emacs_funcall_exit_return; |
| 1573 | priv->non_local_exit_symbol = Qnil; | ||
| 1574 | priv->non_local_exit_data = Qnil; | ||
| 1564 | initialize_storage (&priv->storage); | 1575 | initialize_storage (&priv->storage); |
| 1565 | env->size = sizeof *env; | 1576 | env->size = sizeof *env; |
| 1566 | env->private_members = priv; | 1577 | env->private_members = priv; |
| @@ -1711,6 +1722,18 @@ syms_of_module (void) | |||
| 1711 | Vmodule_refs_hash | 1722 | Vmodule_refs_hash |
| 1712 | = make_hash_table (&hashtest_eq, DEFAULT_HASH_SIZE, Weak_None); | 1723 | = make_hash_table (&hashtest_eq, DEFAULT_HASH_SIZE, Weak_None); |
| 1713 | 1724 | ||
| 1725 | DEFSYM (Qmodule_out_of_memory, "module-out-of-memory"); | ||
| 1726 | Fput (Qmodule_out_of_memory, Qerror_conditions, | ||
| 1727 | list2 (Qmodule_out_of_memory, Qerror)); | ||
| 1728 | Fput (Qmodule_out_of_memory, Qerror_message, | ||
| 1729 | build_unibyte_string ("Module out of memory")); | ||
| 1730 | |||
| 1731 | staticpro (&module_out_of_memory_symbol.v); | ||
| 1732 | module_out_of_memory_symbol.v = Qmodule_out_of_memory; | ||
| 1733 | |||
| 1734 | staticpro (&module_out_of_memory_data.v); | ||
| 1735 | module_out_of_memory_data.v = Qnil; | ||
| 1736 | |||
| 1714 | DEFSYM (Qmodule_load_failed, "module-load-failed"); | 1737 | DEFSYM (Qmodule_load_failed, "module-load-failed"); |
| 1715 | Fput (Qmodule_load_failed, Qerror_conditions, | 1738 | Fput (Qmodule_load_failed, Qerror_conditions, |
| 1716 | list (Qmodule_load_failed, Qerror)); | 1739 | list (Qmodule_load_failed, Qerror)); |