| /* |
| * Any copyright is dedicated to the Public Domain. |
| * http://creativecommons.org/licenses/publicdomain/ |
| */ |
| |
| var BUGNUMBER = 646490; |
| var summary = |
| "RegExp.prototype.exec doesn't get the lastIndex and ToInteger() it for " + |
| "non-global regular expressions when it should"; |
| |
| print(BUGNUMBER + ": " + summary); |
| |
| /************** |
| * BEGIN TEST * |
| **************/ |
| |
| function expectThrowTypeError(fun) |
| { |
| try |
| { |
| var r = fun(); |
| throw new Error("didn't throw TypeError, returned " + r); |
| } |
| catch (e) |
| { |
| assertEq(e instanceof TypeError, true, |
| "didn't throw TypeError, got: " + e); |
| } |
| } |
| |
| function checkExec(description, regex, args, obj) |
| { |
| var lastIndex = obj.lastIndex; |
| var index = obj.index; |
| var input = obj.input; |
| var indexArray = obj.indexArray; |
| |
| var res = regex.exec.apply(regex, args); |
| |
| assertEq(Array.isArray(res), true, description + ": not an array"); |
| assertEq(regex.lastIndex, lastIndex, description + ": wrong lastIndex"); |
| assertEq(res.index, index, description + ": wrong index"); |
| assertEq(res.input, input, description + ": wrong input"); |
| assertEq(res.length, indexArray.length, description + ": wrong length"); |
| for (var i = 0, sz = indexArray.length; i < sz; i++) |
| assertEq(res[i], indexArray[i], description + " " + i + ": wrong index value"); |
| } |
| |
| var exec = RegExp.prototype.exec; |
| var r, res, called, obj; |
| |
| /* 1. Let R be this RegExp object. */ |
| expectThrowTypeError(function() { exec.call(null); }); |
| expectThrowTypeError(function() { exec.call(""); }); |
| expectThrowTypeError(function() { exec.call(5); }); |
| expectThrowTypeError(function() { exec.call({}); }); |
| expectThrowTypeError(function() { exec.call([]); }); |
| expectThrowTypeError(function() { exec.call(); }); |
| expectThrowTypeError(function() { exec.call(true); }); |
| expectThrowTypeError(function() { exec.call(Object.create(RegExp.prototype)); }); |
| expectThrowTypeError(function() { exec.call(Object.create(/a/)); }); |
| |
| |
| /* 2. Let S be the value of ToString(string). */ |
| called = false; |
| r = /a/; |
| assertEq(r.lastIndex, 0); |
| |
| checkExec("/a/", r, [{ toString: function() { called = true; return 'ba'; } }], |
| { lastIndex: 0, |
| index: 1, |
| input: "ba", |
| indexArray: ["a"] }); |
| assertEq(called, true); |
| |
| called = false; |
| try |
| { |
| res = r.exec({ toString: null, valueOf: function() { called = true; throw 17; } }); |
| throw new Error("didn't throw"); |
| } |
| catch (e) |
| { |
| assertEq(e, 17); |
| } |
| |
| assertEq(called, true); |
| |
| called = false; |
| obj = r.lastIndex = { valueOf: function() { assertEq(true, false, "shouldn't have been called"); } }; |
| try |
| { |
| res = r.exec({ toString: null, valueOf: function() { assertEq(called, false); called = true; throw 17; } }); |
| throw new Error("didn't throw"); |
| } |
| catch (e) |
| { |
| assertEq(e, 17); |
| } |
| |
| assertEq(called, true); |
| assertEq(r.lastIndex, obj); |
| |
| // We don't test lack of an argument because of RegExp statics non-standard |
| // behaviors overriding what really should happen for lack of an argument, sigh. |
| |
| |
| /* |
| * 3. Let length be the length of S. |
| * 4. Let lastIndex be the result of calling the [[Get]] internal method of R with argument "lastIndex". |
| * 5. Let i be the value of ToInteger(lastIndex). |
| */ |
| r = /b/; |
| r.lastIndex = { valueOf: {}, toString: {} }; |
| expectThrowTypeError(function() { r.exec("foopy"); }); |
| r.lastIndex = { valueOf: function() { throw new TypeError(); } }; |
| expectThrowTypeError(function() { r.exec("foopy"); }); |
| |
| |
| /* |
| * 6. Let global be the result of calling the [[Get]] internal method of R with argument "global". |
| * 7. If global is false, then let i = 0. |
| */ |
| obj = { valueOf: function() { return 5; } }; |
| r = /abc/; |
| r.lastIndex = obj; |
| |
| checkExec("/abc/ take one", r, ["abc-------abc"], |
| { lastIndex: obj, |
| index: 0, |
| input: "abc-------abc", |
| indexArray: ["abc"] }); |
| |
| checkExec("/abc/ take two", r, ["abc-------abc"], |
| { lastIndex: obj, |
| index: 0, |
| input: "abc-------abc", |
| indexArray: ["abc"] }); |
| |
| |
| /* |
| * 8. Let matchSucceeded be false. |
| * 9. Repeat, while matchSucceeded is false |
| * a. If i < 0 or i > length, then |
| * i. Call the [[Put]] internal method of R with arguments "lastIndex", 0, and true. |
| * ii. Return null. |
| * b. Call the [[Match]] internal method of R with arguments S and i. |
| * c. If [[Match]] returned failure, then |
| * i. Let i = i+1. |
| * d. else |
| * i. Let r be the State result of the call to [[Match]]. |
| * ii. Set matchSucceeded to true. |
| * e. Let i = i+1. |
| */ |
| r = /abc()?/; |
| r.lastIndex = -5; |
| checkExec("/abc()?/ with lastIndex -5", r, ["abc-------abc"], |
| { lastIndex: -5, |
| index: 0, |
| input: "abc-------abc", |
| indexArray: ["abc", undefined] }); |
| |
| |
| r = /abc/; |
| r.lastIndex = -17; |
| res = r.exec("cdefg"); |
| assertEq(res, null); |
| assertEq(r.lastIndex, 0); |
| |
| r = /abc/g; |
| r.lastIndex = -42; |
| res = r.exec("cdefg"); |
| assertEq(res, null); |
| assertEq(r.lastIndex, 0); |
| |
| |
| /* |
| * 10. Let e be r's endIndex value. |
| * 11. If global is true, |
| * a. Call the [[Put]] internal method of R with arguments "lastIndex", e, and true. |
| */ |
| r = /abc/g; |
| r.lastIndex = 17; |
| assertEq(r.exec("sdfs"), null); |
| assertEq(r.lastIndex, 0); |
| |
| r = /abc/g; |
| r.lastIndex = 2; |
| checkExec("/abc/g", r, ["00abc"], |
| { lastIndex: 5, |
| index: 2, |
| input: "00abc", |
| indexArray: ["abc"] }); |
| |
| |
| |
| r = /a(b)c/g; |
| r.lastIndex = 2; |
| checkExec("/a(b)c/g take two", r, ["00abcd"], |
| { lastIndex: 5, |
| index: 2, |
| input: "00abcd", |
| indexArray: ["abc", "b"] }); |
| |
| |
| /* |
| * 12. Let n be the length of r's captures array. (This is the same value as |
| * 15.10.2.1's NCapturingParens.) |
| * 13. Let A be a new array created as if by the expression new Array() where |
| * Array is the standard built-in constructor with that name. |
| * 14. Let matchIndex be the position of the matched substring within the |
| * complete String S. |
| * 15. Call the [[DefineOwnProperty]] internal method of A with arguments |
| * "index", Property Descriptor {[[Value]]: matchIndex, [[Writable]: true, |
| * [[Enumerable]]: true, [[Configurable]]: true}, and true. |
| * 16. Call the [[DefineOwnProperty]] internal method of A with arguments |
| * "input", Property Descriptor {[[Value]]: S, [[Writable]: true, |
| * [[Enumerable]]: true, [[Configurable]]: true}, and true. |
| * 17. Call the [[DefineOwnProperty]] internal method of A with arguments |
| * "length", Property Descriptor {[[Value]]: n + 1}, and true. |
| * 18. Let matchedSubstr be the matched substring (i.e. the portion of S |
| * between offset i inclusive and offset e exclusive). |
| * 19. Call the [[DefineOwnProperty]] internal method of A with arguments "0", |
| * Property Descriptor {[[Value]]: matchedSubstr, [[Writable]: true, |
| * [[Enumerable]]: true, [[Configurable]]: true}, and true. |
| * 20. For each integer i such that I > 0 and I ≤ n |
| * a. Let captureI be i th element of r's captures array. |
| * b. Call the [[DefineOwnProperty]] internal method of A with arguments |
| * ToString(i), Property Descriptor {[[Value]]: captureI, [[Writable]: |
| * true, [[Enumerable]]: true, [[Configurable]]: true}, and true. |
| * 21. Return A. |
| */ |
| // throughout, above (and in other tests) |
| |
| /******************************************************************************/ |
| |
| if (typeof reportCompare === "function") |
| reportCompare(true, true); |
| |
| print("All tests passed!"); |