mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-11 12:25:53 +00:00
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:
parent
b7a121e17d
commit
e9755ee44c
@ -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
|
||||
};
|
||||
|
||||
|
70
js/src/jit-test/tests/basic/dumpStringRepresentation.js
Normal file
70
js/src/jit-test/tests/basic/dumpStringRepresentation.js
Normal 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);
|
@ -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
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user