comparison src/fns.c @ 5241:d579d76f3dcc

Be more careful about side-effects from Lisp code, #'reduce src/ChangeLog addition: 2010-07-24 Aidan Kehoe <kehoea@parhasard.net> * lisp.h (PARSE_KEYWORDS): Always accept a nil :allow-other-keys keyword argument, as described in the ALLOW-OTHER-KEYS-NIL Common Lisp issue writeup, and as necessary for Paul Dietz' tests for #'reduce. * fns.c (mapping_interaction_error): New. (Freduce): Call mapping_interaction_error when KEY or FUNCTION have modified a string SEQUENCE such that the byte length of the string has changed, or such that the current cursor pointer doesn't point to the beginning of a character. Cf. the MAPPING-DESTRUCTIVE-INTERACTION Common Lisp issue writeup. When traversing a list, GCPRO the part of it we still have to traverse, to avoid any crashes if FUNCTION or KEY amputate it behind us and force a garbage collection. tests/ChangeLog addition: 2010-07-24 Aidan Kehoe <kehoea@parhasard.net> * automated/lisp-tests.el: Test a couple of things #'reduce was just made more careful about.
author Aidan Kehoe <kehoea@parhasard.net>
date Sat, 24 Jul 2010 15:56:57 +0100
parents fbd1485af104
children b6a398dbb403
comparison
equal deleted inserted replaced
5240:fca0cf0971de 5241:d579d76f3dcc
62 62
63 Lisp_Object Vpath_separator; 63 Lisp_Object Vpath_separator;
64 64
65 static int internal_old_equal (Lisp_Object, Lisp_Object, int); 65 static int internal_old_equal (Lisp_Object, Lisp_Object, int);
66 Lisp_Object safe_copy_tree (Lisp_Object arg, Lisp_Object vecp, int depth); 66 Lisp_Object safe_copy_tree (Lisp_Object arg, Lisp_Object vecp, int depth);
67
68 static DOESNT_RETURN
69 mapping_interaction_error (Lisp_Object func, Lisp_Object object)
70 {
71 invalid_state_2 ("object modified while traversing it", func, object);
72 }
67 73
68 static Lisp_Object 74 static Lisp_Object
69 mark_bit_vector (Lisp_Object UNUSED (obj)) 75 mark_bit_vector (Lisp_Object UNUSED (obj))
70 { 76 {
71 return Qnil; 77 return Qnil;
4993 { 4999 {
4994 accum = KEY (key, make_char (itext_ichar (cursor))); 5000 accum = KEY (key, make_char (itext_ichar (cursor)));
4995 starting++; 5001 starting++;
4996 startp = XSTRING_DATA (sequence); 5002 startp = XSTRING_DATA (sequence);
4997 cursor = startp + cursor_offset; 5003 cursor = startp + cursor_offset;
5004
5005 if (byte_len != XSTRING_LENGTH (sequence)
5006 || !valid_ibyteptr_p (cursor))
5007 {
5008 mapping_interaction_error (Qreduce, sequence);
5009 }
5010
4998 INC_IBYTEPTR (cursor); 5011 INC_IBYTEPTR (cursor);
4999 cursor_offset = cursor - startp; 5012 cursor_offset = cursor - startp;
5000 } 5013 }
5001 5014
5002 while (cursor_offset < byte_len && starting < ending) 5015 while (cursor_offset < byte_len && starting < ending)
5003 { 5016 {
5004 if (cursor_offset > XSTRING_LENGTH (sequence)) 5017 accum = call2 (function, accum,
5018 KEY (key, make_char (itext_ichar (cursor))));
5019
5020 startp = XSTRING_DATA (sequence);
5021 cursor = startp + cursor_offset;
5022
5023 if (byte_len != XSTRING_LENGTH (sequence)
5024 || !valid_ibyteptr_p (cursor))
5005 { 5025 {
5006 invalid_state ("sequence modified during reduce", sequence); 5026 mapping_interaction_error (Qreduce, sequence);
5007 } 5027 }
5008 5028
5009 startp = XSTRING_DATA (sequence);
5010 cursor = startp + cursor_offset;
5011 accum = call2 (function, accum,
5012 KEY (key, make_char (itext_ichar (cursor))));
5013 INC_IBYTEPTR (cursor); 5029 INC_IBYTEPTR (cursor);
5014 cursor_offset = cursor - startp; 5030 cursor_offset = cursor - startp;
5015 ++starting; 5031 ++starting;
5016 } 5032 }
5017 } 5033 }
5018 else 5034 else
5019 { 5035 {
5020 Elemcount len = string_char_length (sequence); 5036 Elemcount len = string_char_length (sequence);
5021 Bytecount cursor_offset; 5037 Bytecount cursor_offset, byte_len = XSTRING_LENGTH (sequence);
5022 const Ibyte *cursor; 5038 const Ibyte *cursor;
5023 5039
5024 ending = min (ending, len); 5040 ending = min (ending, len);
5025 cursor = string_char_addr (sequence, ending - 1); 5041 cursor = string_char_addr (sequence, ending - 1);
5026 cursor_offset = cursor - XSTRING_DATA (sequence); 5042 cursor_offset = cursor - XSTRING_DATA (sequence);
5033 { 5049 {
5034 accum = KEY (key, make_char (itext_ichar (cursor))); 5050 accum = KEY (key, make_char (itext_ichar (cursor)));
5035 ending--; 5051 ending--;
5036 if (ending > 0) 5052 if (ending > 0)
5037 { 5053 {
5054 cursor = XSTRING_DATA (sequence) + cursor_offset;
5055
5056 if (!valid_ibyteptr_p (cursor))
5057 {
5058 mapping_interaction_error (Qreduce, sequence);
5059 }
5060
5038 DEC_IBYTEPTR (cursor); 5061 DEC_IBYTEPTR (cursor);
5039 cursor_offset = cursor - XSTRING_DATA (sequence); 5062 cursor_offset = cursor - XSTRING_DATA (sequence);
5040 } 5063 }
5041 } 5064 }
5042 5065
5043 for (ii = ending - 1; ii >= starting; --ii) 5066 for (ii = ending - 1; ii >= starting; --ii)
5044 { 5067 {
5045 if (cursor_offset > XSTRING_LENGTH (sequence))
5046 {
5047 invalid_state ("sequence modified during reduce", sequence);
5048 }
5049
5050 cursor = XSTRING_DATA (sequence) + cursor_offset;
5051 accum = call2 (function, KEY (key, 5068 accum = call2 (function, KEY (key,
5052 make_char (itext_ichar (cursor))), 5069 make_char (itext_ichar (cursor))),
5053 accum); 5070 accum);
5054 if (ii > 1) 5071 if (ii > 0)
5055 { 5072 {
5056 cursor = XSTRING_DATA (sequence) + cursor_offset; 5073 cursor = XSTRING_DATA (sequence) + cursor_offset;
5074
5075 if (byte_len != XSTRING_LENGTH (sequence)
5076 || !valid_ibyteptr_p (cursor))
5077 {
5078 mapping_interaction_error (Qreduce, sequence);
5079 }
5080
5057 DEC_IBYTEPTR (cursor); 5081 DEC_IBYTEPTR (cursor);
5058 cursor_offset = cursor - XSTRING_DATA (sequence); 5082 cursor_offset = cursor - XSTRING_DATA (sequence);
5059 } 5083 }
5060 } 5084 }
5061 } 5085 }
5062 } 5086 }
5063 else if (LISTP (sequence)) 5087 else if (LISTP (sequence))
5064 { 5088 {
5065 if (NILP (from_end)) 5089 if (NILP (from_end))
5066 { 5090 {
5091 struct gcpro gcpro1;
5092 Lisp_Object tailed = Qnil;
5093
5094 GCPRO1 (tailed);
5095
5067 if (!UNBOUNDP (initial_value)) 5096 if (!UNBOUNDP (initial_value))
5068 { 5097 {
5069 accum = initial_value; 5098 accum = initial_value;
5070 } 5099 }
5071 else if (ending - starting && starting < ending) 5100 else if (ending - starting && starting < ending)
5072 { 5101 {
5073 Elemcount counting = 0; 5102 Elemcount counting = 0;
5074 EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) 5103 EXTERNAL_LIST_LOOP_3 (elt, sequence, tail)
5075 { 5104 {
5105 /* KEY may amputate the list behind us; make sure what
5106 remains to be processed is still reachable. */
5107 tailed = tail;
5076 if (counting == starting) 5108 if (counting == starting)
5077 { 5109 {
5078 accum = KEY (key, elt); 5110 accum = KEY (key, elt);
5079 starting++; 5111 starting++;
5080 break; 5112 break;
5087 { 5119 {
5088 Elemcount counting = 0; 5120 Elemcount counting = 0;
5089 5121
5090 EXTERNAL_LIST_LOOP_3 (elt, sequence, tail) 5122 EXTERNAL_LIST_LOOP_3 (elt, sequence, tail)
5091 { 5123 {
5124 /* KEY or FUNCTION may amputate the list behind us; make
5125 sure what remains to be processed is still
5126 reachable. */
5127 tailed = tail;
5092 if (counting >= starting) 5128 if (counting >= starting)
5093 { 5129 {
5094 if (counting < ending) 5130 if (counting < ending)
5095 { 5131 {
5096 accum = call2 (function, accum, KEY (key, elt)); 5132 accum = call2 (function, accum, KEY (key, elt));
5101 } 5137 }
5102 } 5138 }
5103 ++counting; 5139 ++counting;
5104 } 5140 }
5105 } 5141 }
5142
5143 UNGCPRO;
5106 } 5144 }
5107 else 5145 else
5108 { 5146 {
5109 Boolint need_accum = 0; 5147 Boolint need_accum = 0;
5110 Lisp_Object *subsequence = NULL; 5148 Lisp_Object *subsequence = NULL;