Bug 1144371: Implement DEBUG-only JS shell function |dumpStringRepresentation|. r=jandem

--HG--
extra : rebase_source : ebb25a0f97e59a24f8a4d7b9a022732f538f1c13
extra : amend_source : 131df33a2b698205d6b385f48c1501d87ba52f7d
This commit is contained in:
Jim Blandy 2015-03-17 13:45:35 -07:00
parent b7a121e17d
commit e9755ee44c
4 changed files with 235 additions and 0 deletions

View File

@ -2414,6 +2414,23 @@ SetImmutablePrototype(JSContext *cx, unsigned argc, Value *vp)
return true;
}
#ifdef DEBUG
static bool
DumpStringRepresentation(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedString str(cx, ToString(cx, args.get(0)));
if (!str)
return false;
str->dumpRepresentation(stderr, 0);
args.rval().setUndefined();
return true;
}
#endif
static const JSFunctionSpecWithHelp TestingFunctions[] = {
JS_FN_HELP("gc", ::GC, 0, 0,
"gc([obj] | 'compartment' [, 'shrinking'])",
@ -2790,6 +2807,12 @@ gc::ZealModeHelpText),
" of internal error, or if the operation doesn't even make sense (for example,\n"
" because the object is a revoked proxy)."),
#ifdef DEBUG
JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation, 1, 0,
"dumpStringRepresentation(str)",
" Print a human-readable description of how the string |str| is represented.\n"),
#endif
JS_FS_HELP_END
};

View File

@ -0,0 +1,70 @@
// Try the dumpStringRepresentation shell function on various types of
// strings, and make sure it doesn't crash.
if (typeof dumpStringRepresentation !== 'function')
quit(0);
print("Empty string:");
dumpStringRepresentation("");
print("\nResult of coercion to string:");
dumpStringRepresentation();
print("\ns = Simple short atom:");
var s = "xxxxxxxx";
dumpStringRepresentation(s);
// Simple non-atom flat.
print("\ns + s: Non-atom flat:");
var s2 = s + s;
dumpStringRepresentation(s2);
print("\nNon-Latin1 flat:");
var j = "渋谷区";
dumpStringRepresentation(j);
print("\nt = Non-inline atom:");
var t = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // 31 characters
dumpStringRepresentation(t);
print("\nr1 = t + s: Simple rope:");
var r1 = t + s;
dumpStringRepresentation(r1);
// Flatten that rope, and re-examine the representations of both the result and
// its former leaves. This should be an extensible string.
print("\nr1, former rope after flattening, now extensible:");
r1.match(/x/);
dumpStringRepresentation(r1);
print("\nt, s: Original leaves, representation unchanged:");
dumpStringRepresentation(t);
dumpStringRepresentation(s);
// Create a new rope with the extensible string as its left child.
print("\nr2 = r1 + s: Rope with extensible leftmost child:");
var r2 = r1 + s;
dumpStringRepresentation(r2);
// Flatten that; this should re-use the extensible string's buffer.
print("\nr2: flattened, stole r1's buffer:");
r2.match(/x/);
dumpStringRepresentation(r2);
print("\nr1: mutated into a dependent string:");
dumpStringRepresentation(r1);
print("\nr3 = r2 + s: a new rope with an extensible leftmost child:");
r3 = r2 + s;
r3.match(/x/);
dumpStringRepresentation(r3);
print("\nr2: now mutated into a dependent string");
dumpStringRepresentation(r2);
print("\nr1: now a doubly-dependent string, because of r2's mutation:");
dumpStringRepresentation(r1);
print("\nt, s: Original leaves, representation unchanged:");
dumpStringRepresentation(t);
dumpStringRepresentation(s);

View File

@ -137,6 +137,48 @@ JSString::dump()
fputc('\n', stderr);
}
void
JSString::dumpRepresentation(FILE *fp, int indent) const
{
if (isRope()) asRope() .dumpRepresentation(fp, indent);
else if (isDependent()) asDependent() .dumpRepresentation(fp, indent);
else if (isExternal()) asExternal() .dumpRepresentation(fp, indent);
else if (isExtensible()) asExtensible() .dumpRepresentation(fp, indent);
else if (isInline()) asInline() .dumpRepresentation(fp, indent);
else if (isFlat()) asFlat() .dumpRepresentation(fp, indent);
else
MOZ_CRASH("Unexpected JSString representation");
}
void
JSString::dumpRepresentationHeader(FILE *fp, int indent, const char *subclass) const
{
uint32_t flags = d.u1.flags;
// Print the string's address as an actual C++ expression, to facilitate
// copy-and-paste into a debugger.
fprintf(fp, "((%s *) %p) length: %zu flags: 0x%x", subclass, this, length(), flags);
if (flags & FLAT_BIT) fputs(" FLAT", fp);
if (flags & HAS_BASE_BIT) fputs(" HAS_BASE", fp);
if (flags & INLINE_CHARS_BIT) fputs(" INLINE_CHARS", fp);
if (flags & ATOM_BIT) fputs(" ATOM", fp);
if (isPermanentAtom()) fputs(" PERMANENT", fp);
if (flags & LATIN1_CHARS_BIT) fputs(" LATIN1", fp);
fputc('\n', fp);
}
void
JSLinearString::dumpRepresentationChars(FILE *fp, int indent) const
{
if (hasLatin1Chars()) {
fprintf(fp, "%*schars: ((Latin1Char *) %p) ", indent, "", rawLatin1Chars());
dumpChars(rawLatin1Chars(), length());
} else {
fprintf(fp, "%*schars: ((char16_t *) %p) ", indent, "", rawTwoByteChars());
dumpChars(rawTwoByteChars(), length());
}
fputc('\n', fp);
}
bool
JSString::equals(const char *s)
{
@ -245,6 +287,21 @@ JSRope::copyCharsInternal(ExclusiveContext *cx, ScopedJSFreePtr<CharT> &out,
return true;
}
#ifdef DEBUG
void
JSRope::dumpRepresentation(FILE *fp, int indent) const
{
dumpRepresentationHeader(fp, indent, "JSRope");
indent += 2;
fprintf(fp, "%*sleft: ", indent, "");
leftChild()->dumpRepresentation(fp, indent);
fprintf(fp, "%*sright: ", indent, "");
rightChild()->dumpRepresentation(fp, indent);
}
#endif
namespace js {
template <>
@ -593,6 +650,19 @@ JSDependentString::undepend(ExclusiveContext *cx)
: undependInternal<char16_t>(cx);
}
#ifdef DEBUG
void
JSDependentString::dumpRepresentation(FILE *fp, int indent) const
{
dumpRepresentationHeader(fp, indent, "JSDependentString");
indent += 2;
fprintf(fp, "%*soffset: %zu\n", indent, "", baseOffset());
fprintf(fp, "%*sbase: ", indent, "");
base()->dumpRepresentation(fp, indent);
}
#endif
template <typename CharT>
/* static */ bool
JSFlatString::isIndexSlow(const CharT *s, size_t length, uint32_t *indexp)
@ -850,6 +920,17 @@ JSAtom::dump()
fprintf(stderr, "JSAtom* (%p) = ", (void *) this);
this->JSString::dump();
}
void
JSExternalString::dumpRepresentation(FILE *fp, int indent) const
{
dumpRepresentationHeader(fp, indent, "JSExternalString");
indent += 2;
fprintf(fp, "%*sfinalizer: ((JSStringFinalizer *) %p)\n", indent, "", externalFinalizer());
fprintf(fp, "%*sbase: ", indent, "");
base()->dumpRepresentation(fp, indent);
}
#endif /* DEBUG */
JSLinearString *
@ -1082,3 +1163,34 @@ template JSFlatString *
NewStringCopyN<NoGC>(ExclusiveContext *cx, const Latin1Char *s, size_t n);
} /* namespace js */
#ifdef DEBUG
void
JSExtensibleString::dumpRepresentation(FILE *fp, int indent) const
{
dumpRepresentationHeader(fp, indent, "JSExtensibleString");
indent += 2;
fprintf(fp, "%*scapacity: %zu\n", indent, "", capacity());
dumpRepresentationChars(fp, indent);
}
void
JSInlineString::dumpRepresentation(FILE *fp, int indent) const
{
dumpRepresentationHeader(fp, indent,
isFatInline() ? "JSFatInlineString" : "JSThinInlineString");
indent += 2;
dumpRepresentationChars(fp, indent);
}
void
JSFlatString::dumpRepresentation(FILE *fp, int indent) const
{
dumpRepresentationHeader(fp, indent, "JSFlatString");
indent += 2;
dumpRepresentationChars(fp, indent);
}
#endif

View File

@ -500,6 +500,8 @@ class JSString : public js::gc::TenuredCell
#ifdef DEBUG
void dump();
void dumpCharsNoNewline(FILE *fp=stderr);
void dumpRepresentation(FILE *fp, int indent) const;
void dumpRepresentationHeader(FILE *fp, int indent, const char *subclass) const;
template <typename CharT>
static void dumpChars(const CharT *s, size_t len, FILE *fp=stderr);
@ -587,6 +589,10 @@ class JSRope : public JSString
static size_t offsetOfRight() {
return offsetof(JSRope, d.s.u3.right);
}
#ifdef DEBUG
void dumpRepresentation(FILE *fp, int indent) const;
#endif
};
static_assert(sizeof(JSRope) == sizeof(JSString),
@ -666,6 +672,10 @@ class JSLinearString : public JSString
JS::AutoCheckCannotGC nogc;
return hasLatin1Chars() ? latin1Chars(nogc)[index] : twoByteChars(nogc)[index];
}
#ifdef DEBUG
void dumpRepresentationChars(FILE *fp, int indent) const;
#endif
};
static_assert(sizeof(JSLinearString) == sizeof(JSString),
@ -706,6 +716,10 @@ class JSDependentString : public JSLinearString
inline static size_t offsetOfBase() {
return offsetof(JSDependentString, d.s.u3.base);
}
#ifdef DEBUG
void dumpRepresentation(FILE *fp, int indent) const;
#endif
};
static_assert(sizeof(JSDependentString) == sizeof(JSString),
@ -767,6 +781,10 @@ class JSFlatString : public JSLinearString
}
inline void finalize(js::FreeOp *fop);
#ifdef DEBUG
void dumpRepresentation(FILE *fp, int indent) const;
#endif
};
static_assert(sizeof(JSFlatString) == sizeof(JSString),
@ -784,6 +802,10 @@ class JSExtensibleString : public JSFlatString
MOZ_ASSERT(JSString::isExtensible());
return d.s.u3.capacity;
}
#ifdef DEBUG
void dumpRepresentation(FILE *fp, int indent) const;
#endif
};
static_assert(sizeof(JSExtensibleString) == sizeof(JSString),
@ -812,6 +834,10 @@ class JSInlineString : public JSFlatString
static size_t offsetOfInlineStorage() {
return offsetof(JSInlineString, d.inlineStorageTwoByte);
}
#ifdef DEBUG
void dumpRepresentation(FILE *fp, int indent) const;
#endif
};
static_assert(sizeof(JSInlineString) == sizeof(JSString),
@ -920,6 +946,10 @@ class JSExternalString : public JSFlatString
/* Only called by the GC for strings with the AllocKind::EXTERNAL_STRING kind. */
inline void finalize(js::FreeOp *fop);
#ifdef DEBUG
void dumpRepresentation(FILE *fp, int indent) const;
#endif
};
static_assert(sizeof(JSExternalString) == sizeof(JSString),