Bug 1330593 part 2 - Add a representativeStringArray shell function. r=jwalden
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 17 Feb 2017 12:28:47 +0100
changeset 372603 c0ec674fd3010ad151b0285e59d4ef7d0beeb67d
parent 372602 65467acedb740d0145d16dc52ddb38847588de47
child 372604 c5761d8f5d23b6997201ca844f0eac1aed6585f0
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs1330593
milestone54.0a1
Bug 1330593 part 2 - Add a representativeStringArray shell function. r=jwalden
js/src/builtin/TestingFunctions.cpp
js/src/jit-test/tests/basic/external-strings.js
js/src/vm/String.cpp
js/src/vm/String.h
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1301,16 +1301,32 @@ EnsureFlatString(JSContext* cx, unsigned
     JSFlatString* flat = args[0].toString()->ensureFlat(cx);
     if (!flat)
         return false;
 
     args.rval().setString(flat);
     return true;
 }
 
+static bool
+RepresentativeStringArray(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    RootedObject array(cx, JS_NewArrayObject(cx, 0));
+    if (!array)
+        return false;
+
+    if (!JSString::fillWithRepresentatives(cx, array.as<ArrayObject>()))
+        return false;
+
+    args.rval().setObject(*array);
+    return true;
+}
+
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
 static bool
 OOMThreadTypes(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setInt32(js::oom::THREAD_TYPE_MAX);
     return true;
 }
@@ -4332,16 +4348,21 @@ static const JSFunctionSpecWithHelp Test
     JS_FN_HELP("newExternalString", NewExternalString, 1, 0,
 "newExternalString(str)",
 "  Copies str's chars and returns a new external string."),
 
     JS_FN_HELP("ensureFlatString", EnsureFlatString, 1, 0,
 "ensureFlatString(str)",
 "  Ensures str is a flat (null-terminated) string and returns it."),
 
+    JS_FN_HELP("representativeStringArray", RepresentativeStringArray, 0, 0,
+"representativeStringArray()",
+"  Returns an array of strings that represent the various internal string\n"
+"  types and character encodings."),
+
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
     JS_FN_HELP("oomThreadTypes", OOMThreadTypes, 0, 0,
 "oomThreadTypes()",
 "  Get the number of thread types that can be used as an argument for\n"
 "oomAfterAllocations() and oomAtAllocation()."),
 
     JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations, 2, 0,
 "oomAfterAllocations(count [,threadType])",
--- a/js/src/jit-test/tests/basic/external-strings.js
+++ b/js/src/jit-test/tests/basic/external-strings.js
@@ -10,16 +10,22 @@ assertEq(o[ext2], 4);
 
 eval(newExternalString("assertEq(1, 1)"));
 
 // Make sure ensureFlat does the right thing for external strings.
 ext = newExternalString("abc\0defg\0");
 assertEq(ensureFlatString(ext), "abc\0defg\0");
 assertEq(ensureFlatString(ext), "abc\0defg\0");
 
+for (var s of representativeStringArray())
+    assertEq(ensureFlatString(s), s);
+
+for (var s of representativeStringArray())
+    assertEq(newExternalString(s), s);
+
 function testQuote() {
     for (var data of [["abc", "abc"],
 		      ["abc\t", "abc\\t"],
 		      ["abc\t\t\0", "abc\\t\\t\\x00"],
 		      ["abc\\def", "abc\\\\def"]]) {
 	try {
 	    assertEq(newExternalString(data[0]), false);
 	} catch(e) {
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -1435,8 +1435,179 @@ void
 JSFlatString::dumpRepresentation(FILE* fp, int indent) const
 {
     dumpRepresentationHeader(fp, indent, "JSFlatString");
     indent += 2;
 
     dumpRepresentationChars(fp, indent);
 }
 #endif
+
+static void
+FinalizeRepresentativeExternalString(Zone* zone, const JSStringFinalizer* fin, char16_t* chars);
+
+static const JSStringFinalizer RepresentativeExternalStringFinalizer =
+    { FinalizeRepresentativeExternalString };
+
+static void
+FinalizeRepresentativeExternalString(Zone* zone, const JSStringFinalizer* fin, char16_t* chars)
+{
+    // Constant chars, nothing to free.
+    MOZ_ASSERT(fin == &RepresentativeExternalStringFinalizer);
+}
+
+template <typename CheckString, typename CharT>
+static bool
+FillWithRepresentatives(JSContext* cx, HandleArrayObject array, uint32_t* index,
+                        const CharT* chars, size_t len,
+                        size_t fatInlineMaxLength,
+                        const CheckString& check)
+{
+    auto AppendString =
+        [&check](JSContext* cx, HandleArrayObject array, uint32_t* index, HandleString s)
+    {
+        MOZ_ASSERT(check(s));
+        RootedValue val(cx, StringValue(s));
+        return JS_DefineElement(cx, array, (*index)++, val, 0);
+    };
+
+    MOZ_ASSERT(len > fatInlineMaxLength);
+
+    // Normal atom.
+    RootedString atom1(cx, AtomizeChars(cx, chars, len));
+    if (!atom1 || !AppendString(cx, array, index, atom1))
+        return false;
+    MOZ_ASSERT(atom1->isAtom());
+
+    // Inline atom.
+    RootedString atom2(cx, AtomizeChars(cx, chars, 2));
+    if (!atom2 || !AppendString(cx, array, index, atom2))
+        return false;
+    MOZ_ASSERT(atom2->isAtom());
+    MOZ_ASSERT(atom2->isInline());
+
+    // Fat inline atom.
+    RootedString atom3(cx, AtomizeChars(cx, chars, fatInlineMaxLength));
+    if (!atom3 || !AppendString(cx, array, index, atom3))
+        return false;
+    MOZ_ASSERT(atom3->isAtom());
+    MOZ_ASSERT(atom3->isFatInline());
+
+    // Normal flat string.
+    RootedString flat1(cx, NewStringCopyN<CanGC>(cx, chars, len));
+    if (!flat1 || !AppendString(cx, array, index, flat1))
+        return false;
+    MOZ_ASSERT(flat1->isFlat());
+
+    // Inline string.
+    RootedString flat2(cx, NewStringCopyN<CanGC>(cx, chars, 3));
+    if (!flat2 || !AppendString(cx, array, index, flat2))
+        return false;
+    MOZ_ASSERT(flat2->isFlat());
+    MOZ_ASSERT(flat2->isInline());
+
+    // Fat inline string.
+    RootedString flat3(cx, NewStringCopyN<CanGC>(cx, chars, fatInlineMaxLength));
+    if (!flat3 || !AppendString(cx, array, index, flat3))
+        return false;
+    MOZ_ASSERT(flat3->isFlat());
+    MOZ_ASSERT(flat3->isFatInline());
+
+    // Rope.
+    RootedString rope(cx, ConcatStrings<CanGC>(cx, atom1, atom3));
+    if (!rope || !AppendString(cx, array, index, rope))
+        return false;
+    MOZ_ASSERT(rope->isRope());
+
+    // Dependent.
+    RootedString dep(cx, NewDependentString(cx, atom1, 0, len - 2));
+    if (!dep || !AppendString(cx, array, index, dep))
+        return false;
+    MOZ_ASSERT(dep->isDependent());
+
+    // Undepended.
+    RootedString undep(cx, NewDependentString(cx, atom1, 0, len - 3));
+    if (!undep || !undep->ensureFlat(cx) || !AppendString(cx, array, index, undep))
+        return false;
+    MOZ_ASSERT(undep->isUndepended());
+
+    // Extensible.
+    RootedString temp1(cx, NewStringCopyN<CanGC>(cx, chars, len));
+    if (!temp1)
+        return false;
+    RootedString extensible(cx, ConcatStrings<CanGC>(cx, temp1, atom3));
+    if (!extensible || !extensible->ensureLinear(cx))
+        return false;
+    if (!AppendString(cx, array, index, extensible))
+        return false;
+    MOZ_ASSERT(extensible->isExtensible());
+
+    // External. Note that we currently only support TwoByte external strings.
+    RootedString external1(cx), external2(cx);
+    if (IsSame<CharT, char16_t>::value) {
+        external1 = JS_NewExternalString(cx, (const char16_t*)chars, len,
+                                         &RepresentativeExternalStringFinalizer);
+        if (!external1 || !AppendString(cx, array, index, external1))
+            return false;
+        MOZ_ASSERT(external1->isExternal());
+
+        external2 = JS_NewExternalString(cx, (const char16_t*)chars, 2,
+                                         &RepresentativeExternalStringFinalizer);
+        if (!external2 || !AppendString(cx, array, index, external2))
+            return false;
+        MOZ_ASSERT(external2->isExternal());
+    }
+
+    // Assert the strings still have the types we expect after creating the
+    // other strings.
+
+    MOZ_ASSERT(atom1->isAtom());
+    MOZ_ASSERT(atom2->isAtom());
+    MOZ_ASSERT(atom3->isAtom());
+    MOZ_ASSERT(atom2->isInline());
+    MOZ_ASSERT(atom3->isFatInline());
+
+    MOZ_ASSERT(flat1->isFlat());
+    MOZ_ASSERT(flat2->isFlat());
+    MOZ_ASSERT(flat3->isFlat());
+    MOZ_ASSERT(flat2->isInline());
+    MOZ_ASSERT(flat3->isFatInline());
+
+    MOZ_ASSERT(rope->isRope());
+    MOZ_ASSERT(dep->isDependent());
+    MOZ_ASSERT(undep->isUndepended());
+    MOZ_ASSERT(extensible->isExtensible());
+    MOZ_ASSERT_IF(external1, external1->isExternal());
+    MOZ_ASSERT_IF(external2, external2->isExternal());
+    return true;
+}
+
+/* static */ bool
+JSString::fillWithRepresentatives(JSContext* cx, HandleArrayObject array)
+{
+    uint32_t index = 0;
+
+    auto CheckTwoByte = [](JSString* str) { return str->hasTwoByteChars(); };
+    auto CheckLatin1 = [](JSString* str) { return str->hasLatin1Chars(); };
+
+    // Append TwoByte strings.
+    static const char16_t twoByteChars[] = u"\u1234abc\0def\u5678ghijklmasdfa\0xyz0123456789";
+    if (!FillWithRepresentatives(cx, array, &index,
+                                 twoByteChars, mozilla::ArrayLength(twoByteChars) - 1,
+                                 JSFatInlineString::MAX_LENGTH_TWO_BYTE,
+                                 CheckTwoByte))
+    {
+        return false;
+    }
+
+    // Append Latin1 strings.
+    static const Latin1Char latin1Chars[] = "abc\0defghijklmasdfa\0xyz0123456789";
+    if (!FillWithRepresentatives(cx, array, &index,
+                                 latin1Chars, mozilla::ArrayLength(latin1Chars) - 1,
+                                 JSFatInlineString::MAX_LENGTH_LATIN1,
+                                 CheckLatin1))
+    {
+        return false;
+    }
+
+    MOZ_ASSERT(index == 22);
+    return true;
+}
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -462,16 +462,20 @@ class JSString : public js::gc::TenuredC
     }
 
     MOZ_ALWAYS_INLINE
     JSAtom& asAtom() const {
         MOZ_ASSERT(isAtom());
         return *(JSAtom*)this;
     }
 
+    // Fills |array| with various strings that represent the different string
+    // kinds and character encodings.
+    static bool fillWithRepresentatives(JSContext* cx, js::HandleArrayObject array);
+
     /* Only called by the GC for dependent or undepended strings. */
 
     inline bool hasBase() const {
         return d.u1.flags & HAS_BASE_BIT;
     }
 
     inline JSLinearString* base() const;