Bug 1398354 part 1. Expose StringIsArrayIndex taking a char pointer in jsfriendapi. r=waldo
authorBoris Zbarsky <bzbarsky@mit.edu>
Wed, 17 Oct 2018 23:00:49 -0400
changeset 497842 b7142afba71297a36d86eb651a795b33223c2fb6
parent 497841 9b412ade97d9c655a7599a5ea1cc95b9179c63a4
child 497843 59e0564b74358de53bb51500382b99fcca19a508
push id10002
push userarchaeopteryx@coole-files.de
push dateFri, 19 Oct 2018 23:09:29 +0000
treeherdermozilla-beta@01378c910610 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo
bugs1398354
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1398354 part 1. Expose StringIsArrayIndex taking a char pointer in jsfriendapi. r=waldo
js/src/builtin/Array.cpp
js/src/jsapi-tests/moz.build
js/src/jsapi-tests/testStringIsArrayIndex.cpp
js/src/jsfriendapi.h
--- a/js/src/builtin/Array.cpp
+++ b/js/src/builtin/Array.cpp
@@ -218,17 +218,17 @@ GetLengthProperty(JSContext* cx, HandleO
  * except that by using signed 31-bit integers we miss the top half of the
  * valid range. This function checks the string representation itself; note
  * that calling a standard conversion routine might allow strings such as
  * "08" or "4.0" as array indices, which they are not.
  *
  */
 template <typename CharT>
 static bool
-StringIsArrayIndex(const CharT* s, uint32_t length, uint32_t* indexp)
+StringIsArrayIndexHelper(const CharT* s, uint32_t length, uint32_t* indexp)
 {
     const CharT* end = s + length;
 
     if (length == 0 || length > (sizeof("4294967294") - 1) || !IsAsciiDigit(*s)) {
         return false;
     }
 
     uint32_t c = 0, previous = 0;
@@ -260,18 +260,30 @@ StringIsArrayIndex(const CharT* s, uint3
     return false;
 }
 
 JS_FRIEND_API(bool)
 js::StringIsArrayIndex(JSLinearString* str, uint32_t* indexp)
 {
     AutoCheckCannotGC nogc;
     return str->hasLatin1Chars()
-           ? ::StringIsArrayIndex(str->latin1Chars(nogc), str->length(), indexp)
-           : ::StringIsArrayIndex(str->twoByteChars(nogc), str->length(), indexp);
+           ? StringIsArrayIndexHelper(str->latin1Chars(nogc), str->length(), indexp)
+           : StringIsArrayIndexHelper(str->twoByteChars(nogc), str->length(), indexp);
+}
+
+JS_FRIEND_API(bool)
+js::StringIsArrayIndex(const char16_t* str, uint32_t length, uint32_t* indexp)
+{
+    return StringIsArrayIndexHelper(str, length, indexp);
+}
+
+JS_FRIEND_API(bool)
+js::StringIsArrayIndex(const char* str, uint32_t length, uint32_t* indexp)
+{
+    return StringIsArrayIndexHelper(str, length, indexp);
 }
 
 template <typename T>
 static bool
 ToId(JSContext* cx, T index, MutableHandleId id);
 
 template <>
 bool
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -92,16 +92,17 @@ UNIFIED_SOURCES += [
     'testSavedStacks.cpp',
     'testScriptInfo.cpp',
     'testScriptObject.cpp',
     'testSetProperty.cpp',
     'testSetPropertyIgnoringNamedGetter.cpp',
     'testSharedImmutableStringsCache.cpp',
     'testSourcePolicy.cpp',
     'testStringBuffer.cpp',
+    'testStringIsArrayIndex.cpp',
     'testStructuredClone.cpp',
     'testSymbol.cpp',
     'testThreadingConditionVariable.cpp',
     'testThreadingExclusiveData.cpp',
     'testThreadingMutex.cpp',
     'testThreadingThread.cpp',
     'testToSignedOrUnsignedInteger.cpp',
     'testTypedArrays.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testStringIsArrayIndex.cpp
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ */
+/* 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/. */
+
+#include "jsfriendapi.h"
+
+#include "jsapi-tests/tests.h"
+
+using mozilla::ArrayLength;
+
+// Need to account for string literals including the \0 at the end.
+#define STR(x) x, (ArrayLength(x)-1)
+
+static const struct TestTuple {
+    /* The string being tested. */
+    const char* string;
+    /* The number of characters from the string to use. */
+    size_t length;
+    /* Whether this string is an index. */
+    bool isindex;
+    /* If it's an index, what index it is.  Ignored if not an index. */
+    uint32_t index;
+} tests[] = {
+    { STR("0"), true, 0 },
+    { STR("1"), true, 1 },
+    { STR("2"), true, 2 },
+    { STR("9"), true, 9 },
+    { STR("10"), true, 10 },
+    { STR("15"), true, 15 },
+    { STR("16"), true, 16 },
+    { STR("17"), true, 17 },
+    { STR("99"), true, 99 },
+    { STR("100"), true, 100 },
+    { STR("255"), true, 255 },
+    { STR("256"), true, 256 },
+    { STR("257"), true, 257 },
+    { STR("999"), true, 999 },
+    { STR("1000"), true, 1000 },
+    { STR("4095"), true, 4095 },
+    { STR("4096"), true, 4096 },
+    { STR("9999"), true, 9999 },
+    { STR("1073741823"), true, 1073741823 },
+    { STR("1073741824"), true, 1073741824 },
+    { STR("1073741825"), true, 1073741825 },
+    { STR("2147483647"), true, 2147483647 },
+    { STR("2147483648"), true, 2147483648u },
+    { STR("2147483649"), true, 2147483649u },
+    { STR("4294967294"), true, 4294967294u },
+    { STR("4294967295"), false, 0 },  // Not an array index because need to be able to represent length
+    { STR("-1"), false, 0 },
+    { STR("abc"), false, 0 },
+    { STR(" 0"), false, 0 },
+    { STR("0 "), false, 0 },
+    // Tests to make sure the passed-in length is taken into account
+    { "0 ", 1, true, 0 },
+    { "123abc", 3, true, 123 },
+    { "123abc", 2, true, 12 },
+};
+
+BEGIN_TEST(testStringIsArrayIndex)
+{
+    for (size_t i = 0, sz = ArrayLength(tests); i < sz; i++) {
+        uint32_t index;
+        bool isindex = js::StringIsArrayIndex(tests[i].string,
+                                              tests[i].length,
+                                              &index);
+        CHECK_EQUAL(isindex, tests[i].isindex);
+        if (isindex) {
+            CHECK_EQUAL(index, tests[i].index);
+        }
+    }
+
+    return true;
+}
+END_TEST(testStringIsArrayIndex)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1002,19 +1002,36 @@ CopyFlatStringChars(char16_t* dest, JSFl
  * results that match the output of Reflect.ownKeys.
  */
 JS_FRIEND_API(bool)
 GetPropertyKeys(JSContext* cx, JS::HandleObject obj, unsigned flags, JS::AutoIdVector* props);
 
 JS_FRIEND_API(bool)
 AppendUnique(JSContext* cx, JS::AutoIdVector& base, JS::AutoIdVector& others);
 
+/**
+ * Determine whether the given string is an array index in the sense of <https://tc39.github.io/ecma262/#array-index>.
+ *
+ * If it isn't, returns false.
+ *
+ * If it is, returns true and outputs the index in *indexp.
+ */
 JS_FRIEND_API(bool)
 StringIsArrayIndex(JSLinearString* str, uint32_t* indexp);
 
+/**
+ * Overloads of StringIsArrayIndex taking (char*,length) pairs.  These
+ * behave the same as the JSLinearString version.
+ */
+JS_FRIEND_API(bool)
+StringIsArrayIndex(const char* str, uint32_t length, uint32_t* indexp);
+
+JS_FRIEND_API(bool)
+StringIsArrayIndex(const char16_t* str, uint32_t length, uint32_t* indexp);
+
 JS_FRIEND_API(void)
 SetPreserveWrapperCallback(JSContext* cx, PreserveWrapperCallback callback);
 
 JS_FRIEND_API(bool)
 IsObjectInContextCompartment(JSObject* obj, const JSContext* cx);
 
 /*
  * NB: keep these in sync with the copy in builtin/SelfHostingDefines.h.