diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index afe2b8bd4947..48e955638f0a 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -1700,26 +1700,31 @@ LastIndexOfImpl(const TextChar* text, size_t textLen, const PatChar* pat, size_t return -1; } +// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e +// 21.1.3.9 String.prototype.lastIndexOf ( searchString [ , position ] ) bool js::str_lastIndexOf(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - RootedString textstr(cx, ToStringForStringFunction(cx, args.thisv())); - if (!textstr) + + // Steps 1-2. + RootedString str(cx, ToStringForStringFunction(cx, args.thisv())); + if (!str) return false; - RootedLinearString pat(cx, ArgToRootedString(cx, args, 0)); - if (!pat) + // Step 3. + RootedLinearString searchStr(cx, ArgToRootedString(cx, args, 0)); + if (!searchStr) return false; - size_t textLen = textstr->length(); - size_t patLen = pat->length(); - int start = textLen - patLen; // Start searching here - if (start < 0) { - args.rval().setInt32(-1); - return true; - } + // Step 6. + size_t len = str->length(); + // Step 8. + size_t searchLen = searchStr->length(); + + // Steps 4-5, 7. + int start = len - searchLen; // Start searching here if (args.hasDefined(1)) { if (args[1].isInt32()) { int i = args[1].toInt32(); @@ -1741,29 +1746,36 @@ js::str_lastIndexOf(JSContext* cx, unsigned argc, Value* vp) } } - if (patLen == 0) { - args.rval().setInt32(start); + if (searchLen > len) { + args.rval().setInt32(-1); return true; } - JSLinearString* text = textstr->ensureLinear(cx); + if (searchLen == 0) { + args.rval().setInt32(start); + return true; + } + MOZ_ASSERT(0 <= start && size_t(start) < len); + + JSLinearString* text = str->ensureLinear(cx); if (!text) return false; + // Step 9. int32_t res; AutoCheckCannotGC nogc; if (text->hasLatin1Chars()) { const Latin1Char* textChars = text->latin1Chars(nogc); - if (pat->hasLatin1Chars()) - res = LastIndexOfImpl(textChars, textLen, pat->latin1Chars(nogc), patLen, start); + if (searchStr->hasLatin1Chars()) + res = LastIndexOfImpl(textChars, len, searchStr->latin1Chars(nogc), searchLen, start); else - res = LastIndexOfImpl(textChars, textLen, pat->twoByteChars(nogc), patLen, start); + res = LastIndexOfImpl(textChars, len, searchStr->twoByteChars(nogc), searchLen, start); } else { const char16_t* textChars = text->twoByteChars(nogc); - if (pat->hasLatin1Chars()) - res = LastIndexOfImpl(textChars, textLen, pat->latin1Chars(nogc), patLen, start); + if (searchStr->hasLatin1Chars()) + res = LastIndexOfImpl(textChars, len, searchStr->latin1Chars(nogc), searchLen, start); else - res = LastIndexOfImpl(textChars, textLen, pat->twoByteChars(nogc), patLen, start); + res = LastIndexOfImpl(textChars, len, searchStr->twoByteChars(nogc), searchLen, start); } args.rval().setInt32(res); diff --git a/js/src/tests/ecma/String/lastIndexOf-ToNumber-when-searchStr-larger-than-string.js b/js/src/tests/ecma/String/lastIndexOf-ToNumber-when-searchStr-larger-than-string.js new file mode 100644 index 000000000000..a4c3c316e07b --- /dev/null +++ b/js/src/tests/ecma/String/lastIndexOf-ToNumber-when-searchStr-larger-than-string.js @@ -0,0 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var str = "a"; +var searchStr = "abc"; + +var position = new Object(); +var valueOfCalled = false; +function positionValueOf() { + assertEq(valueOfCalled, false); + valueOfCalled = true; + return 0; +} +position.valueOf = positionValueOf; + +var result = str.lastIndexOf(searchStr, position); +assertEq(result, -1); +assertEq(valueOfCalled, true); + +if (typeof reportCompare == "function") + reportCompare(0, 0);