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