|
1 /* |
|
2 * Any copyright is dedicated to the Public Domain. |
|
3 * http://creativecommons.org/licenses/publicdomain/ |
|
4 */ |
|
5 |
|
6 var BUGNUMBER = 646490; |
|
7 var summary = |
|
8 "RegExp.prototype.exec doesn't get the lastIndex and ToInteger() it for " + |
|
9 "non-global regular expressions when it should"; |
|
10 |
|
11 print(BUGNUMBER + ": " + summary); |
|
12 |
|
13 /************** |
|
14 * BEGIN TEST * |
|
15 **************/ |
|
16 |
|
17 function expectThrowTypeError(fun) |
|
18 { |
|
19 try |
|
20 { |
|
21 var r = fun(); |
|
22 throw new Error("didn't throw TypeError, returned " + r); |
|
23 } |
|
24 catch (e) |
|
25 { |
|
26 assertEq(e instanceof TypeError, true, |
|
27 "didn't throw TypeError, got: " + e); |
|
28 } |
|
29 } |
|
30 |
|
31 function checkExec(description, regex, args, obj) |
|
32 { |
|
33 var lastIndex = obj.lastIndex; |
|
34 var index = obj.index; |
|
35 var input = obj.input; |
|
36 var indexArray = obj.indexArray; |
|
37 |
|
38 var res = regex.exec.apply(regex, args); |
|
39 |
|
40 assertEq(Array.isArray(res), true, description + ": not an array"); |
|
41 assertEq(regex.lastIndex, lastIndex, description + ": wrong lastIndex"); |
|
42 assertEq(res.index, index, description + ": wrong index"); |
|
43 assertEq(res.input, input, description + ": wrong input"); |
|
44 assertEq(res.length, indexArray.length, description + ": wrong length"); |
|
45 for (var i = 0, sz = indexArray.length; i < sz; i++) |
|
46 assertEq(res[i], indexArray[i], description + " " + i + ": wrong index value"); |
|
47 } |
|
48 |
|
49 var exec = RegExp.prototype.exec; |
|
50 var r, res, called, obj; |
|
51 |
|
52 /* 1. Let R be this RegExp object. */ |
|
53 expectThrowTypeError(function() { exec.call(null); }); |
|
54 expectThrowTypeError(function() { exec.call(""); }); |
|
55 expectThrowTypeError(function() { exec.call(5); }); |
|
56 expectThrowTypeError(function() { exec.call({}); }); |
|
57 expectThrowTypeError(function() { exec.call([]); }); |
|
58 expectThrowTypeError(function() { exec.call(); }); |
|
59 expectThrowTypeError(function() { exec.call(true); }); |
|
60 expectThrowTypeError(function() { exec.call(Object.create(RegExp.prototype)); }); |
|
61 expectThrowTypeError(function() { exec.call(Object.create(/a/)); }); |
|
62 |
|
63 |
|
64 /* 2. Let S be the value of ToString(string). */ |
|
65 called = false; |
|
66 r = /a/; |
|
67 assertEq(r.lastIndex, 0); |
|
68 |
|
69 checkExec("/a/", r, [{ toString: function() { called = true; return 'ba'; } }], |
|
70 { lastIndex: 0, |
|
71 index: 1, |
|
72 input: "ba", |
|
73 indexArray: ["a"] }); |
|
74 assertEq(called, true); |
|
75 |
|
76 called = false; |
|
77 try |
|
78 { |
|
79 res = r.exec({ toString: null, valueOf: function() { called = true; throw 17; } }); |
|
80 throw new Error("didn't throw"); |
|
81 } |
|
82 catch (e) |
|
83 { |
|
84 assertEq(e, 17); |
|
85 } |
|
86 |
|
87 assertEq(called, true); |
|
88 |
|
89 called = false; |
|
90 obj = r.lastIndex = { valueOf: function() { assertEq(true, false, "shouldn't have been called"); } }; |
|
91 try |
|
92 { |
|
93 res = r.exec({ toString: null, valueOf: function() { assertEq(called, false); called = true; throw 17; } }); |
|
94 throw new Error("didn't throw"); |
|
95 } |
|
96 catch (e) |
|
97 { |
|
98 assertEq(e, 17); |
|
99 } |
|
100 |
|
101 assertEq(called, true); |
|
102 assertEq(r.lastIndex, obj); |
|
103 |
|
104 // We don't test lack of an argument because of RegExp statics non-standard |
|
105 // behaviors overriding what really should happen for lack of an argument, sigh. |
|
106 |
|
107 |
|
108 /* |
|
109 * 3. Let length be the length of S. |
|
110 * 4. Let lastIndex be the result of calling the [[Get]] internal method of R with argument "lastIndex". |
|
111 * 5. Let i be the value of ToInteger(lastIndex). |
|
112 */ |
|
113 r = /b/; |
|
114 r.lastIndex = { valueOf: {}, toString: {} }; |
|
115 expectThrowTypeError(function() { r.exec("foopy"); }); |
|
116 r.lastIndex = { valueOf: function() { throw new TypeError(); } }; |
|
117 expectThrowTypeError(function() { r.exec("foopy"); }); |
|
118 |
|
119 |
|
120 /* |
|
121 * 6. Let global be the result of calling the [[Get]] internal method of R with argument "global". |
|
122 * 7. If global is false, then let i = 0. |
|
123 */ |
|
124 obj = { valueOf: function() { return 5; } }; |
|
125 r = /abc/; |
|
126 r.lastIndex = obj; |
|
127 |
|
128 checkExec("/abc/ take one", r, ["abc-------abc"], |
|
129 { lastIndex: obj, |
|
130 index: 0, |
|
131 input: "abc-------abc", |
|
132 indexArray: ["abc"] }); |
|
133 |
|
134 checkExec("/abc/ take two", r, ["abc-------abc"], |
|
135 { lastIndex: obj, |
|
136 index: 0, |
|
137 input: "abc-------abc", |
|
138 indexArray: ["abc"] }); |
|
139 |
|
140 |
|
141 /* |
|
142 * 8. Let matchSucceeded be false. |
|
143 * 9. Repeat, while matchSucceeded is false |
|
144 * a. If i < 0 or i > length, then |
|
145 * i. Call the [[Put]] internal method of R with arguments "lastIndex", 0, and true. |
|
146 * ii. Return null. |
|
147 * b. Call the [[Match]] internal method of R with arguments S and i. |
|
148 * c. If [[Match]] returned failure, then |
|
149 * i. Let i = i+1. |
|
150 * d. else |
|
151 * i. Let r be the State result of the call to [[Match]]. |
|
152 * ii. Set matchSucceeded to true. |
|
153 * e. Let i = i+1. |
|
154 */ |
|
155 r = /abc()?/; |
|
156 r.lastIndex = -5; |
|
157 checkExec("/abc()?/ with lastIndex -5", r, ["abc-------abc"], |
|
158 { lastIndex: -5, |
|
159 index: 0, |
|
160 input: "abc-------abc", |
|
161 indexArray: ["abc", undefined] }); |
|
162 |
|
163 |
|
164 r = /abc/; |
|
165 r.lastIndex = -17; |
|
166 res = r.exec("cdefg"); |
|
167 assertEq(res, null); |
|
168 assertEq(r.lastIndex, 0); |
|
169 |
|
170 r = /abc/g; |
|
171 r.lastIndex = -42; |
|
172 res = r.exec("cdefg"); |
|
173 assertEq(res, null); |
|
174 assertEq(r.lastIndex, 0); |
|
175 |
|
176 |
|
177 /* |
|
178 * 10. Let e be r's endIndex value. |
|
179 * 11. If global is true, |
|
180 * a. Call the [[Put]] internal method of R with arguments "lastIndex", e, and true. |
|
181 */ |
|
182 r = /abc/g; |
|
183 r.lastIndex = 17; |
|
184 assertEq(r.exec("sdfs"), null); |
|
185 assertEq(r.lastIndex, 0); |
|
186 |
|
187 r = /abc/g; |
|
188 r.lastIndex = 2; |
|
189 checkExec("/abc/g", r, ["00abc"], |
|
190 { lastIndex: 5, |
|
191 index: 2, |
|
192 input: "00abc", |
|
193 indexArray: ["abc"] }); |
|
194 |
|
195 |
|
196 |
|
197 r = /a(b)c/g; |
|
198 r.lastIndex = 2; |
|
199 checkExec("/a(b)c/g take two", r, ["00abcd"], |
|
200 { lastIndex: 5, |
|
201 index: 2, |
|
202 input: "00abcd", |
|
203 indexArray: ["abc", "b"] }); |
|
204 |
|
205 |
|
206 /* |
|
207 * 12. Let n be the length of r's captures array. (This is the same value as |
|
208 * 15.10.2.1's NCapturingParens.) |
|
209 * 13. Let A be a new array created as if by the expression new Array() where |
|
210 * Array is the standard built-in constructor with that name. |
|
211 * 14. Let matchIndex be the position of the matched substring within the |
|
212 * complete String S. |
|
213 * 15. Call the [[DefineOwnProperty]] internal method of A with arguments |
|
214 * "index", Property Descriptor {[[Value]]: matchIndex, [[Writable]: true, |
|
215 * [[Enumerable]]: true, [[Configurable]]: true}, and true. |
|
216 * 16. Call the [[DefineOwnProperty]] internal method of A with arguments |
|
217 * "input", Property Descriptor {[[Value]]: S, [[Writable]: true, |
|
218 * [[Enumerable]]: true, [[Configurable]]: true}, and true. |
|
219 * 17. Call the [[DefineOwnProperty]] internal method of A with arguments |
|
220 * "length", Property Descriptor {[[Value]]: n + 1}, and true. |
|
221 * 18. Let matchedSubstr be the matched substring (i.e. the portion of S |
|
222 * between offset i inclusive and offset e exclusive). |
|
223 * 19. Call the [[DefineOwnProperty]] internal method of A with arguments "0", |
|
224 * Property Descriptor {[[Value]]: matchedSubstr, [[Writable]: true, |
|
225 * [[Enumerable]]: true, [[Configurable]]: true}, and true. |
|
226 * 20. For each integer i such that I > 0 and I ≤ n |
|
227 * a. Let captureI be i th element of r's captures array. |
|
228 * b. Call the [[DefineOwnProperty]] internal method of A with arguments |
|
229 * ToString(i), Property Descriptor {[[Value]]: captureI, [[Writable]: |
|
230 * true, [[Enumerable]]: true, [[Configurable]]: true}, and true. |
|
231 * 21. Return A. |
|
232 */ |
|
233 // throughout, above (and in other tests) |
|
234 |
|
235 /******************************************************************************/ |
|
236 |
|
237 if (typeof reportCompare === "function") |
|
238 reportCompare(true, true); |
|
239 |
|
240 print("All tests passed!"); |