author | David Teller <dteller@mozilla.com> |
Thu, 07 Sep 2017 15:22:38 +0200 | |
changeset 380458 | 95bb616125726c8bd4c300a1eb182fc8268c7141 |
parent 380457 | 784819df6f88b7fd90c63fb3448e422e3bac37e6 |
child 380459 | a376f9a9cd537bf83f8d0c02662a883c95ddc704 |
child 380696 | 1888ec2f277f6bb26271b8808e08914a21db9efe |
push id | 51080 |
push user | dteller@mozilla.com |
push date | Wed, 13 Sep 2017 09:05:35 +0000 |
treeherder | autoland@95bb61612572 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | nbp |
bugs | 1397717 |
milestone | 57.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
|
--- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -3579,17 +3579,18 @@ 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); + Fprinter out(stderr); + str->dumpRepresentation(out, 0); args.rval().setUndefined(); return true; } #endif static bool SetLazyParsingDisabled(JSContext* cx, unsigned argc, Value* vp)
--- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -618,250 +618,257 @@ ParseNode::appendOrCreateList(ParseNodeK static const char * const parseNodeNames[] = { #define STRINGIFY(name) #name, FOR_EACH_PARSE_NODE_KIND(STRINGIFY) #undef STRINGIFY }; void -frontend::DumpParseTree(ParseNode* pn, int indent) +frontend::DumpParseTree(ParseNode* pn, GenericPrinter& out, int indent) { if (pn == nullptr) - fprintf(stderr, "#NULL"); + out.put("#NULL"); else - pn->dump(indent); + pn->dump(out, indent); } static void -IndentNewLine(int indent) +IndentNewLine(GenericPrinter& out, int indent) { - fputc('\n', stderr); + out.putChar('\n'); for (int i = 0; i < indent; ++i) - fputc(' ', stderr); + out.putChar(' '); +} + +void +ParseNode::dump(GenericPrinter& out) +{ + dump(out, 0); + out.putChar('\n'); } void ParseNode::dump() { - dump(0); - fputc('\n', stderr); + js::Fprinter out(stderr); + dump(out); } void -ParseNode::dump(int indent) +ParseNode::dump(GenericPrinter& out, int indent) { switch (pn_arity) { case PN_NULLARY: - ((NullaryNode*) this)->dump(); + ((NullaryNode*) this)->dump(out); break; case PN_UNARY: - ((UnaryNode*) this)->dump(indent); + ((UnaryNode*) this)->dump(out, indent); break; case PN_BINARY: - ((BinaryNode*) this)->dump(indent); + ((BinaryNode*) this)->dump(out, indent); break; case PN_TERNARY: - ((TernaryNode*) this)->dump(indent); + ((TernaryNode*) this)->dump(out, indent); break; case PN_CODE: - ((CodeNode*) this)->dump(indent); + ((CodeNode*) this)->dump(out, indent); break; case PN_LIST: - ((ListNode*) this)->dump(indent); + ((ListNode*) this)->dump(out, indent); break; case PN_NAME: - ((NameNode*) this)->dump(indent); + ((NameNode*) this)->dump(out, indent); break; case PN_SCOPE: - ((LexicalScopeNode*) this)->dump(indent); + ((LexicalScopeNode*) this)->dump(out, indent); break; default: - fprintf(stderr, "#<BAD NODE %p, kind=%u, arity=%u>", + out.printf("#<BAD NODE %p, kind=%u, arity=%u>", (void*) this, unsigned(getKind()), unsigned(pn_arity)); break; } } void -NullaryNode::dump() +NullaryNode::dump(GenericPrinter& out) { switch (getKind()) { - case PNK_TRUE: fprintf(stderr, "#true"); break; - case PNK_FALSE: fprintf(stderr, "#false"); break; - case PNK_NULL: fprintf(stderr, "#null"); break; - case PNK_RAW_UNDEFINED: fprintf(stderr, "#undefined"); break; + case PNK_TRUE: out.put("#true"); break; + case PNK_FALSE: out.put("#false"); break; + case PNK_NULL: out.put("#null"); break; + case PNK_RAW_UNDEFINED: out.put("#undefined"); break; case PNK_NUMBER: { ToCStringBuf cbuf; const char* cstr = NumberToCString(nullptr, &cbuf, pn_dval); if (!IsFinite(pn_dval)) - fputc('#', stderr); + out.put("#"); if (cstr) - fprintf(stderr, "%s", cstr); + out.printf("%s", cstr); else - fprintf(stderr, "%g", pn_dval); + out.printf("%g", pn_dval); break; } case PNK_STRING: - pn_atom->dumpCharsNoNewline(); + pn_atom->dumpCharsNoNewline(out); break; default: - fprintf(stderr, "(%s)", parseNodeNames[getKind()]); + out.printf("(%s)", parseNodeNames[getKind()]); } } void -UnaryNode::dump(int indent) +UnaryNode::dump(GenericPrinter& out, int indent) { const char* name = parseNodeNames[getKind()]; - fprintf(stderr, "(%s ", name); + out.printf("(%s ", name); indent += strlen(name) + 2; - DumpParseTree(pn_kid, indent); - fprintf(stderr, ")"); + DumpParseTree(pn_kid, out, indent); + out.printf(")"); } void -BinaryNode::dump(int indent) +BinaryNode::dump(GenericPrinter& out, int indent) { const char* name = parseNodeNames[getKind()]; - fprintf(stderr, "(%s ", name); + out.printf("(%s ", name); indent += strlen(name) + 2; - DumpParseTree(pn_left, indent); - IndentNewLine(indent); - DumpParseTree(pn_right, indent); - fprintf(stderr, ")"); + DumpParseTree(pn_left, out, indent); + IndentNewLine(out, indent); + DumpParseTree(pn_right, out, indent); + out.printf(")"); } void -TernaryNode::dump(int indent) +TernaryNode::dump(GenericPrinter& out, int indent) { const char* name = parseNodeNames[getKind()]; - fprintf(stderr, "(%s ", name); + out.printf("(%s ", name); indent += strlen(name) + 2; - DumpParseTree(pn_kid1, indent); - IndentNewLine(indent); - DumpParseTree(pn_kid2, indent); - IndentNewLine(indent); - DumpParseTree(pn_kid3, indent); - fprintf(stderr, ")"); + DumpParseTree(pn_kid1, out, indent); + IndentNewLine(out, indent); + DumpParseTree(pn_kid2, out, indent); + IndentNewLine(out, indent); + DumpParseTree(pn_kid3, out, indent); + out.printf(")"); } void -CodeNode::dump(int indent) +CodeNode::dump(GenericPrinter& out, int indent) { const char* name = parseNodeNames[getKind()]; - fprintf(stderr, "(%s ", name); + out.printf("(%s ", name); indent += strlen(name) + 2; - DumpParseTree(pn_body, indent); - fprintf(stderr, ")"); + DumpParseTree(pn_body, out, indent); + out.printf(")"); } void -ListNode::dump(int indent) +ListNode::dump(GenericPrinter& out, int indent) { const char* name = parseNodeNames[getKind()]; - fprintf(stderr, "(%s [", name); + out.printf("(%s [", name); if (pn_head != nullptr) { indent += strlen(name) + 3; - DumpParseTree(pn_head, indent); + DumpParseTree(pn_head, out, indent); ParseNode* pn = pn_head->pn_next; while (pn != nullptr) { - IndentNewLine(indent); - DumpParseTree(pn, indent); + IndentNewLine(out, indent); + DumpParseTree(pn, out, indent); pn = pn->pn_next; } } - fprintf(stderr, "])"); + out.printf("])"); } template <typename CharT> static void -DumpName(const CharT* s, size_t len) +DumpName(GenericPrinter& out, const CharT* s, size_t len) { if (len == 0) - fprintf(stderr, "#<zero-length name>"); + out.put("#<zero-length name>"); for (size_t i = 0; i < len; i++) { char16_t c = s[i]; if (c > 32 && c < 127) fputc(c, stderr); else if (c <= 255) - fprintf(stderr, "\\x%02x", unsigned(c)); + out.printf("\\x%02x", unsigned(c)); else - fprintf(stderr, "\\u%04x", unsigned(c)); + out.printf("\\u%04x", unsigned(c)); } } void -NameNode::dump(int indent) +NameNode::dump(GenericPrinter& out, int indent) { if (isKind(PNK_NAME) || isKind(PNK_DOT)) { if (isKind(PNK_DOT)) - fprintf(stderr, "(."); + out.put("(."); if (!pn_atom) { - fprintf(stderr, "#<null name>"); + out.put("#<null name>"); } else if (getOp() == JSOP_GETARG && pn_atom->length() == 0) { // Dump destructuring parameter. - fprintf(stderr, "(#<zero-length name> "); - DumpParseTree(expr(), indent + 21); - fputc(')', stderr); + out.put("(#<zero-length name> "); + DumpParseTree(expr(), out, indent + 21); + out.printf(")"); } else { JS::AutoCheckCannotGC nogc; if (pn_atom->hasLatin1Chars()) - DumpName(pn_atom->latin1Chars(nogc), pn_atom->length()); + DumpName(out, pn_atom->latin1Chars(nogc), pn_atom->length()); else - DumpName(pn_atom->twoByteChars(nogc), pn_atom->length()); + DumpName(out, pn_atom->twoByteChars(nogc), pn_atom->length()); } if (isKind(PNK_DOT)) { - fputc(' ', stderr); + out.putChar(' '); if (as<PropertyAccess>().isSuper()) - fprintf(stderr, "super"); + out.put("super"); else - DumpParseTree(expr(), indent + 2); - fputc(')', stderr); + DumpParseTree(expr(), out, indent + 2); + out.printf(")"); } return; } const char* name = parseNodeNames[getKind()]; - fprintf(stderr, "(%s ", name); + out.printf("(%s ", name); indent += strlen(name) + 2; - DumpParseTree(expr(), indent); - fprintf(stderr, ")"); + DumpParseTree(expr(), out, indent); + out.printf(")"); } void -LexicalScopeNode::dump(int indent) +LexicalScopeNode::dump(GenericPrinter& out, int indent) { const char* name = parseNodeNames[getKind()]; - fprintf(stderr, "(%s [", name); + out.printf("(%s [", name); int nameIndent = indent + strlen(name) + 3; if (!isEmptyScope()) { LexicalScope::Data* bindings = scopeBindings(); for (uint32_t i = 0; i < bindings->length; i++) { JSAtom* name = bindings->names[i].name(); JS::AutoCheckCannotGC nogc; if (name->hasLatin1Chars()) - DumpName(name->latin1Chars(nogc), name->length()); + DumpName(out, name->latin1Chars(nogc), name->length()); else - DumpName(name->twoByteChars(nogc), name->length()); + DumpName(out, name->twoByteChars(nogc), name->length()); if (i < bindings->length - 1) - IndentNewLine(nameIndent); + IndentNewLine(out, nameIndent); } } - fprintf(stderr, "]"); + out.putChar(']'); indent += 2; - IndentNewLine(indent); - DumpParseTree(scopeBody(), indent); - fprintf(stderr, ")"); + IndentNewLine(out, indent); + DumpParseTree(scopeBody(), out, indent); + out.printf(")"); } #endif ObjectBox::ObjectBox(JSObject* object, ObjectBox* traceLink) : object(object), traceLink(traceLink), emitLink(nullptr) {
--- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -6,16 +6,17 @@ #ifndef frontend_ParseNode_h #define frontend_ParseNode_h #include "mozilla/Attributes.h" #include "builtin/ModuleObject.h" #include "frontend/TokenStream.h" +#include "vm/Printer.h" namespace js { namespace frontend { class ParseContext; class FullParseHandler; class FunctionBox; class ObjectBox; @@ -813,18 +814,20 @@ class ParseNode template <class NodeType> inline const NodeType& as() const { MOZ_ASSERT(NodeType::test(*this)); return *static_cast<const NodeType*>(this); } #ifdef DEBUG + // Debugger-friendly stderr printer. void dump(); - void dump(int indent); + void dump(GenericPrinter& out); + void dump(GenericPrinter& out, int indent); #endif }; struct NullaryNode : public ParseNode { NullaryNode(ParseNodeKind kind, const TokenPos& pos) : ParseNode(kind, JSOP_NOP, PN_NULLARY, pos) {} NullaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos) @@ -835,30 +838,30 @@ struct NullaryNode : public ParseNode // that nullary nodes shouldn't use -- bogus. NullaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos, JSAtom* atom) : ParseNode(kind, op, PN_NULLARY, pos) { pn_atom = atom; } #ifdef DEBUG - void dump(); + void dump(GenericPrinter& out); #endif }; struct UnaryNode : public ParseNode { UnaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos, ParseNode* kid) : ParseNode(kind, op, PN_UNARY, pos) { pn_kid = kid; } #ifdef DEBUG - void dump(int indent); + void dump(GenericPrinter& out, int indent); #endif }; struct BinaryNode : public ParseNode { BinaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos, ParseNode* left, ParseNode* right) : ParseNode(kind, op, PN_BINARY, pos) { @@ -869,17 +872,17 @@ struct BinaryNode : public ParseNode BinaryNode(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right) : ParseNode(kind, op, PN_BINARY, TokenPos::box(left->pn_pos, right->pn_pos)) { pn_left = left; pn_right = right; } #ifdef DEBUG - void dump(int indent); + void dump(GenericPrinter& out, int indent); #endif }; struct TernaryNode : public ParseNode { TernaryNode(ParseNodeKind kind, JSOp op, ParseNode* kid1, ParseNode* kid2, ParseNode* kid3) : ParseNode(kind, op, PN_TERNARY, TokenPos((kid1 ? kid1 : kid2 ? kid2 : kid3)->pn_pos.begin, @@ -895,17 +898,17 @@ struct TernaryNode : public ParseNode : ParseNode(kind, op, PN_TERNARY, pos) { pn_kid1 = kid1; pn_kid2 = kid2; pn_kid3 = kid3; } #ifdef DEBUG - void dump(int indent); + void dump(GenericPrinter& out, int indent); #endif }; struct ListNode : public ParseNode { ListNode(ParseNodeKind kind, const TokenPos& pos) : ParseNode(kind, JSOP_NOP, PN_LIST, pos) { @@ -924,17 +927,17 @@ struct ListNode : public ParseNode initList(kid); } static bool test(const ParseNode& node) { return node.isArity(PN_LIST); } #ifdef DEBUG - void dump(int indent); + void dump(GenericPrinter& out, int indent); #endif }; struct CodeNode : public ParseNode { CodeNode(ParseNodeKind kind, JSOp op, const TokenPos& pos) : ParseNode(kind, op, PN_CODE, pos) { @@ -944,31 +947,31 @@ struct CodeNode : public ParseNode op == JSOP_LAMBDA_ARROW || // arrow function op == JSOP_LAMBDA); // expression, method, comprehension, accessor, &c. MOZ_ASSERT(!pn_body); MOZ_ASSERT(!pn_objbox); } public: #ifdef DEBUG - void dump(int indent); + void dump(GenericPrinter& out, int indent); #endif }; struct NameNode : public ParseNode { NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, const TokenPos& pos) : ParseNode(kind, op, PN_NAME, pos) { pn_atom = atom; pn_expr = nullptr; } #ifdef DEBUG - void dump(int indent); + void dump(GenericPrinter& out, int indent); #endif }; struct LexicalScopeNode : public ParseNode { LexicalScopeNode(LexicalScope::Data* bindings, ParseNode* body) : ParseNode(PNK_LEXICALSCOPE, JSOP_NOP, PN_SCOPE, body->pn_pos) { @@ -976,17 +979,17 @@ struct LexicalScopeNode : public ParseNo pn_u.scope.body = body; } static bool test(const ParseNode& node) { return node.isKind(PNK_LEXICALSCOPE); } #ifdef DEBUG - void dump(int indent); + void dump(GenericPrinter& out, int indent); #endif }; class LabeledStatement : public ParseNode { public: LabeledStatement(PropertyName* label, ParseNode* stmt, uint32_t begin) : ParseNode(PNK_LABEL, JSOP_NOP, PN_NAME, TokenPos(begin, stmt->pn_pos.end)) @@ -1347,17 +1350,17 @@ struct ClassNode : public TernaryNode { } Handle<LexicalScope::Data*> scopeBindings() const { MOZ_ASSERT(pn_kid3->is<LexicalScopeNode>()); return pn_kid3->scopeBindings(); } }; #ifdef DEBUG -void DumpParseTree(ParseNode* pn, int indent = 0); +void DumpParseTree(ParseNode* pn, GenericPrinter& out, int indent = 0); #endif class ParseNodeAllocator { public: explicit ParseNodeAllocator(JSContext* cx, LifoAlloc& alloc) : cx(cx), alloc(alloc), freelist(nullptr) {}
--- a/js/src/gc/Heap.h +++ b/js/src/gc/Heap.h @@ -25,16 +25,18 @@ #include "ds/BitArray.h" #include "gc/Memory.h" #include "js/GCAPI.h" #include "js/HeapAPI.h" #include "js/RootingAPI.h" #include "js/TracingAPI.h" +#include "vm/Printer.h" + struct JSRuntime; namespace js { class AutoLockGC; class FreeOp; extern bool @@ -273,17 +275,17 @@ struct Cell inline StoreBuffer* storeBuffer() const; inline JS::TraceKind getTraceKind() const; static MOZ_ALWAYS_INLINE bool needWriteBarrierPre(JS::Zone* zone); #ifdef DEBUG inline bool isAligned() const; - void dump(FILE* fp) const; + void dump(js::GenericPrinter& out) const; void dump() const; #endif protected: inline uintptr_t address() const; inline Chunk* chunk() const; } JS_HAZ_GC_THING;
--- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -658,44 +658,113 @@ JS_CloneObject(JSContext* cx, HandleObje // |obj| might be in a different compartment. assertSameCompartment(cx, protoArg); Rooted<TaggedProto> proto(cx, TaggedProto(protoArg.get())); return CloneObject(cx, obj, proto); } #ifdef DEBUG +// We don't want jsfriendapi.h to depend on GenericPrinter, +// so these functions are declared directly in the cpp. + +namespace js { + +extern JS_FRIEND_API(void) +DumpString(JSString* str, js::GenericPrinter& out); + +extern JS_FRIEND_API(void) +DumpAtom(JSAtom* atom, js::GenericPrinter& out); + +extern JS_FRIEND_API(void) +DumpObject(JSObject* obj, js::GenericPrinter& out); + +extern JS_FRIEND_API(void) +DumpChars(const char16_t* s, size_t n, js::GenericPrinter& out); + +extern JS_FRIEND_API(void) +DumpValue(const JS::Value& val, js::GenericPrinter& out); + +extern JS_FRIEND_API(void) +DumpId(jsid id, js::GenericPrinter& out); + +extern JS_FRIEND_API(void) +DumpInterpreterFrame(JSContext* cx, js::GenericPrinter& out, InterpreterFrame* start = nullptr); + +} // namespace js + +JS_FRIEND_API(void) +js::DumpString(JSString* str, js::GenericPrinter& out) +{ + str->dump(out); +} + +JS_FRIEND_API(void) +js::DumpAtom(JSAtom* atom, js::GenericPrinter& out) +{ + atom->dump(out); +} + +JS_FRIEND_API(void) +js::DumpChars(const char16_t* s, size_t n, js::GenericPrinter& out) +{ + out.printf("char16_t * (%p) = ", (void*) s); + JSString::dumpChars(s, n, out); + out.putChar('\n'); +} + +JS_FRIEND_API(void) +js::DumpObject(JSObject* obj, js::GenericPrinter& out) +{ + if (!obj) { + out.printf("NULL\n"); + return; + } + obj->dump(out); +} + JS_FRIEND_API(void) js::DumpString(JSString* str, FILE* fp) { - str->dump(fp); + Fprinter out(fp); + js::DumpString(str, out); } JS_FRIEND_API(void) js::DumpAtom(JSAtom* atom, FILE* fp) { - atom->dump(fp); + Fprinter out(fp); + js::DumpAtom(atom, out); } JS_FRIEND_API(void) js::DumpChars(const char16_t* s, size_t n, FILE* fp) { - fprintf(fp, "char16_t * (%p) = ", (void*) s); - JSString::dumpChars(s, n, fp); - fputc('\n', fp); + Fprinter out(fp); + js::DumpChars(s, n, out); } JS_FRIEND_API(void) js::DumpObject(JSObject* obj, FILE* fp) { - if (!obj) { - fprintf(fp, "NULL\n"); - return; - } - obj->dump(fp); + Fprinter out(fp); + js::DumpObject(obj, out); +} + +JS_FRIEND_API(void) +js::DumpId(jsid id, FILE* fp) +{ + Fprinter out(fp); + js::DumpId(id, out); +} + +JS_FRIEND_API(void) +js::DumpValue(const JS::Value& val, FILE* fp) { + Fprinter out(fp); + js::DumpValue(val, out); } JS_FRIEND_API(void) js::DumpString(JSString* str) { DumpString(str, stderr); } JS_FRIEND_API(void) js::DumpAtom(JSAtom* atom) { @@ -715,17 +784,18 @@ js::DumpValue(const JS::Value& val) { } JS_FRIEND_API(void) js::DumpId(jsid id) { DumpId(id, stderr); } JS_FRIEND_API(void) js::DumpInterpreterFrame(JSContext* cx, InterpreterFrame* start) { - DumpInterpreterFrame(cx, stderr, start); + Fprinter out(stderr); + DumpInterpreterFrame(cx, out, start); } JS_FRIEND_API(bool) js::DumpPC(JSContext* cx) { return DumpPC(cx, stdout); } JS_FRIEND_API(bool) js::DumpScript(JSContext* cx, JSScript* scriptArg) {
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -229,16 +229,17 @@ #include "gc/Policy.h" #include "jit/BaselineJIT.h" #include "jit/IonCode.h" #include "jit/JitcodeMap.h" #include "js/SliceBudget.h" #include "proxy/DeadObjectProxy.h" #include "vm/Debugger.h" #include "vm/GeckoProfiler.h" +#include "vm/Printer.h" #include "vm/ProxyObject.h" #include "vm/Shape.h" #include "vm/String.h" #include "vm/Symbol.h" #include "vm/Time.h" #include "vm/TraceLogging.h" #include "vm/WrapperObject.h" @@ -8730,42 +8731,54 @@ AutoEmptyNursery::AutoEmptyNursery(JSCon cx->runtime()->gc.stats().resumePhases(); checkCondition(cx); } } /* namespace gc */ } /* namespace js */ #ifdef DEBUG -void -js::gc::Cell::dump(FILE* fp) const + +namespace js { + +// We don't want jsfriendapi.h to depend on GenericPrinter, +// so these functions are declared directly in the cpp. + +extern JS_FRIEND_API(void) +DumpString(JSString* str, js::GenericPrinter& out); + +} + +void +js::gc::Cell::dump(js::GenericPrinter& out) const { switch (getTraceKind()) { case JS::TraceKind::Object: - reinterpret_cast<const JSObject*>(this)->dump(fp); + reinterpret_cast<const JSObject*>(this)->dump(out); break; case JS::TraceKind::String: - js::DumpString(reinterpret_cast<JSString*>(const_cast<Cell*>(this)), fp); + js::DumpString(reinterpret_cast<JSString*>(const_cast<Cell*>(this)), out); break; case JS::TraceKind::Shape: - reinterpret_cast<const Shape*>(this)->dump(fp); + reinterpret_cast<const Shape*>(this)->dump(out); break; default: - fprintf(fp, "%s(%p)\n", JS::GCTraceKindToAscii(getTraceKind()), (void*) this); + out.printf("%s(%p)\n", JS::GCTraceKindToAscii(getTraceKind()), (void*) this); } } // For use in a debugger. void js::gc::Cell::dump() const { - dump(stderr); + js::Fprinter out(stderr); + dump(out); } #endif static inline bool CanCheckGrayBits(const Cell* cell) { MOZ_ASSERT(cell); if (!cell->isTenured())
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3408,345 +3408,379 @@ GetObjectSlotNameFunctor::operator()(JS: /* * Routines to print out values during debugging. These are FRIEND_API to help * the debugger find them and to support temporarily hacking js::Dump* calls * into other code. */ static void -dumpValue(const Value& v, FILE* fp) +dumpValue(const Value& v, js::GenericPrinter& out) { if (v.isNull()) - fprintf(fp, "null"); + out.put("null"); else if (v.isUndefined()) - fprintf(fp, "undefined"); + out.put("undefined"); else if (v.isInt32()) - fprintf(fp, "%d", v.toInt32()); + out.printf("%d", v.toInt32()); else if (v.isDouble()) - fprintf(fp, "%g", v.toDouble()); + out.printf("%g", v.toDouble()); else if (v.isString()) - v.toString()->dump(fp); + v.toString()->dump(out); else if (v.isSymbol()) - v.toSymbol()->dump(fp); + v.toSymbol()->dump(out); else if (v.isObject() && v.toObject().is<JSFunction>()) { JSFunction* fun = &v.toObject().as<JSFunction>(); if (fun->displayAtom()) { - fputs("<function ", fp); - FileEscapedString(fp, fun->displayAtom(), 0); + out.put("<function "); + EscapedStringPrinter(out, fun->displayAtom(), 0); } else { - fputs("<unnamed function", fp); + out.put("<unnamed function"); } if (fun->hasScript()) { JSScript* script = fun->nonLazyScript(); - fprintf(fp, " (%s:%zu)", + out.printf(" (%s:%zu)", script->filename() ? script->filename() : "", script->lineno()); } - fprintf(fp, " at %p>", (void*) fun); + out.printf(" at %p>", (void*) fun); } else if (v.isObject()) { JSObject* obj = &v.toObject(); const Class* clasp = obj->getClass(); - fprintf(fp, "<%s%s at %p>", + out.printf("<%s%s at %p>", clasp->name, (clasp == &PlainObject::class_) ? "" : " object", (void*) obj); } else if (v.isBoolean()) { if (v.toBoolean()) - fprintf(fp, "true"); + out.put("true"); else - fprintf(fp, "false"); + out.put("false"); } else if (v.isMagic()) { - fprintf(fp, "<invalid"); + out.put("<invalid"); #ifdef DEBUG switch (v.whyMagic()) { - case JS_ELEMENTS_HOLE: fprintf(fp, " elements hole"); break; - case JS_NO_ITER_VALUE: fprintf(fp, " no iter value"); break; - case JS_GENERATOR_CLOSING: fprintf(fp, " generator closing"); break; - case JS_OPTIMIZED_OUT: fprintf(fp, " optimized out"); break; - default: fprintf(fp, " ?!"); break; + case JS_ELEMENTS_HOLE: out.put(" elements hole"); break; + case JS_NO_ITER_VALUE: out.put(" no iter value"); break; + case JS_GENERATOR_CLOSING: out.put(" generator closing"); break; + case JS_OPTIMIZED_OUT: out.put(" optimized out"); break; + default: out.put(" ?!"); break; } #endif - fprintf(fp, ">"); + out.putChar('>'); } else { - fprintf(fp, "unexpected value"); + out.put("unexpected value"); } } +namespace js { + +// We don't want jsfriendapi.h to depend on GenericPrinter, +// so these functions are declared directly in the cpp. + +JS_FRIEND_API(void) +DumpValue(const JS::Value& val, js::GenericPrinter& out); + +JS_FRIEND_API(void) +DumpId(jsid id, js::GenericPrinter& out); + +JS_FRIEND_API(void) +DumpInterpreterFrame(JSContext* cx, js::GenericPrinter& out, InterpreterFrame* start = nullptr); + +} // namespace js + +JS_FRIEND_API(void) +js::DumpValue(const Value& val, js::GenericPrinter& out) +{ + dumpValue(val, out); + out.putChar('\n'); +} + JS_FRIEND_API(void) -js::DumpValue(const Value& val, FILE* fp) +js::DumpId(jsid id, js::GenericPrinter& out) { - dumpValue(val, fp); - fputc('\n', fp); -} - -JS_FRIEND_API(void) -js::DumpId(jsid id, FILE* fp) -{ - fprintf(fp, "jsid %p = ", (void*) JSID_BITS(id)); - dumpValue(IdToValue(id), fp); - fputc('\n', fp); + out.printf("jsid %p = ", (void*) JSID_BITS(id)); + dumpValue(IdToValue(id), out); + out.putChar('\n'); } static void -DumpProperty(const NativeObject* obj, Shape& shape, FILE* fp) +DumpProperty(const NativeObject* obj, Shape& shape, js::GenericPrinter& out) { jsid id = shape.propid(); uint8_t attrs = shape.attributes(); - fprintf(fp, " ((js::Shape*) %p) ", (void*) &shape); - if (attrs & JSPROP_ENUMERATE) fprintf(fp, "enumerate "); - if (attrs & JSPROP_READONLY) fprintf(fp, "readonly "); - if (attrs & JSPROP_PERMANENT) fprintf(fp, "permanent "); - if (attrs & JSPROP_SHARED) fprintf(fp, "shared "); + out.printf(" ((js::Shape*) %p) ", (void*) &shape); + if (attrs & JSPROP_ENUMERATE) out.put("enumerate "); + if (attrs & JSPROP_READONLY) out.put("readonly "); + if (attrs & JSPROP_PERMANENT) out.put("permanent "); + if (attrs & JSPROP_SHARED) out.put("shared "); if (shape.hasGetterValue()) - fprintf(fp, "getterValue=%p ", (void*) shape.getterObject()); + out.printf("getterValue=%p ", (void*) shape.getterObject()); else if (!shape.hasDefaultGetter()) - fprintf(fp, "getterOp=%p ", JS_FUNC_TO_DATA_PTR(void*, shape.getterOp())); + out.printf("getterOp=%p ", JS_FUNC_TO_DATA_PTR(void*, shape.getterOp())); if (shape.hasSetterValue()) - fprintf(fp, "setterValue=%p ", (void*) shape.setterObject()); + out.printf("setterValue=%p ", (void*) shape.setterObject()); else if (!shape.hasDefaultSetter()) - fprintf(fp, "setterOp=%p ", JS_FUNC_TO_DATA_PTR(void*, shape.setterOp())); + out.printf("setterOp=%p ", JS_FUNC_TO_DATA_PTR(void*, shape.setterOp())); if (JSID_IS_ATOM(id) || JSID_IS_INT(id) || JSID_IS_SYMBOL(id)) - dumpValue(js::IdToValue(id), fp); + dumpValue(js::IdToValue(id), out); else - fprintf(fp, "unknown jsid %p", (void*) JSID_BITS(id)); + out.printf("unknown jsid %p", (void*) JSID_BITS(id)); uint32_t slot = shape.hasSlot() ? shape.maybeSlot() : SHAPE_INVALID_SLOT; - fprintf(fp, ": slot %d", slot); + out.printf(": slot %d", slot); if (shape.hasSlot()) { - fprintf(fp, " = "); - dumpValue(obj->getSlot(slot), fp); + out.put(" = "); + dumpValue(obj->getSlot(slot), out); } else if (slot != SHAPE_INVALID_SLOT) { - fprintf(fp, " (INVALID!)"); + out.printf(" (INVALID!)"); } - fprintf(fp, "\n"); + out.putChar('\n'); } bool JSObject::uninlinedIsProxy() const { return is<ProxyObject>(); } bool JSObject::uninlinedNonProxyIsExtensible() const { return nonProxyIsExtensible(); } void -JSObject::dump(FILE* fp) const +JSObject::dump(js::GenericPrinter& out) const { const JSObject* obj = this; JSObject* globalObj = &global(); - fprintf(fp, "object %p from global %p [%s]\n", (void*) obj, + out.printf("object %p from global %p [%s]\n", (void*) obj, (void*) globalObj, globalObj->getClass()->name); const Class* clasp = obj->getClass(); - fprintf(fp, "class %p %s\n", (const void*)clasp, clasp->name); + out.printf("class %p %s\n", (const void*)clasp, clasp->name); if (obj->hasLazyGroup()) { - fprintf(fp, "lazy group\n"); + out.put("lazy group\n"); } else { const ObjectGroup* group = obj->group(); - fprintf(fp, "group %p\n", (const void*)group); + out.printf("group %p\n", (const void*)group); } - fprintf(fp, "flags:"); - if (obj->isDelegate()) fprintf(fp, " delegate"); - if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) fprintf(fp, " not_extensible"); - if (obj->isIndexed()) fprintf(fp, " indexed"); - if (obj->maybeHasInterestingSymbolProperty()) fprintf(fp, " maybe_has_interesting_symbol"); - if (obj->isBoundFunction()) fprintf(fp, " bound_function"); - if (obj->isQualifiedVarObj()) fprintf(fp, " varobj"); - if (obj->isUnqualifiedVarObj()) fprintf(fp, " unqualified_varobj"); - if (obj->watched()) fprintf(fp, " watched"); - if (obj->isIteratedSingleton()) fprintf(fp, " iterated_singleton"); - if (obj->isNewGroupUnknown()) fprintf(fp, " new_type_unknown"); - if (obj->hasUncacheableProto()) fprintf(fp, " has_uncacheable_proto"); - if (obj->hadElementsAccess()) fprintf(fp, " had_elements_access"); - if (obj->wasNewScriptCleared()) fprintf(fp, " new_script_cleared"); + out.put("flags:"); + if (obj->isDelegate()) out.put(" delegate"); + if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) out.put(" not_extensible"); + if (obj->isIndexed()) out.put(" indexed"); + if (obj->maybeHasInterestingSymbolProperty()) out.put(" maybe_has_interesting_symbol"); + if (obj->isBoundFunction()) out.put(" bound_function"); + if (obj->isQualifiedVarObj()) out.put(" varobj"); + if (obj->isUnqualifiedVarObj()) out.put(" unqualified_varobj"); + if (obj->watched()) out.put(" watched"); + if (obj->isIteratedSingleton()) out.put(" iterated_singleton"); + if (obj->isNewGroupUnknown()) out.put(" new_type_unknown"); + if (obj->hasUncacheableProto()) out.put(" has_uncacheable_proto"); + if (obj->hadElementsAccess()) out.put(" had_elements_access"); + if (obj->wasNewScriptCleared()) out.put(" new_script_cleared"); if (obj->hasStaticPrototype() && obj->staticPrototypeIsImmutable()) - fprintf(fp, " immutable_prototype"); + out.put(" immutable_prototype"); if (obj->isNative()) { const NativeObject* nobj = &obj->as<NativeObject>(); if (nobj->inDictionaryMode()) - fprintf(fp, " inDictionaryMode"); + out.put(" inDictionaryMode"); if (nobj->hasShapeTable()) - fprintf(fp, " hasShapeTable"); + out.put(" hasShapeTable"); } - fprintf(fp, "\n"); + out.putChar('\n'); if (obj->isNative()) { const NativeObject* nobj = &obj->as<NativeObject>(); uint32_t slots = nobj->getDenseInitializedLength(); if (slots) { - fprintf(fp, "elements\n"); + out.put("elements\n"); for (uint32_t i = 0; i < slots; i++) { - fprintf(fp, " %3d: ", i); - dumpValue(nobj->getDenseElement(i), fp); - fprintf(fp, "\n"); - fflush(fp); + out.printf(" %3d: ", i); + dumpValue(nobj->getDenseElement(i), out); + out.putChar('\n'); + out.flush(); } } } - fprintf(fp, "proto "); + out.put("proto "); TaggedProto proto = obj->taggedProto(); if (proto.isDynamic()) - fprintf(fp, "<dynamic>"); + out.put("<dynamic>"); else - dumpValue(ObjectOrNullValue(proto.toObjectOrNull()), fp); - fputc('\n', fp); + dumpValue(ObjectOrNullValue(proto.toObjectOrNull()), out); + out.putChar('\n'); if (clasp->flags & JSCLASS_HAS_PRIVATE) - fprintf(fp, "private %p\n", obj->as<NativeObject>().getPrivate()); + out.printf("private %p\n", obj->as<NativeObject>().getPrivate()); if (!obj->isNative()) - fprintf(fp, "not native\n"); + out.put("not native\n"); uint32_t reservedEnd = JSCLASS_RESERVED_SLOTS(clasp); uint32_t slots = obj->isNative() ? obj->as<NativeObject>().slotSpan() : 0; uint32_t stop = obj->isNative() ? reservedEnd : slots; if (stop > 0) - fprintf(fp, obj->isNative() ? "reserved slots:\n" : "slots:\n"); + out.printf(obj->isNative() ? "reserved slots:\n" : "slots:\n"); for (uint32_t i = 0; i < stop; i++) { - fprintf(fp, " %3d ", i); + out.printf(" %3d ", i); if (i < reservedEnd) - fprintf(fp, "(reserved) "); - fprintf(fp, "= "); - dumpValue(obj->as<NativeObject>().getSlot(i), fp); - fputc('\n', fp); + out.printf("(reserved) "); + out.put("= "); + dumpValue(obj->as<NativeObject>().getSlot(i), out); + out.putChar('\n'); } if (obj->isNative()) { - fprintf(fp, "properties:\n"); + out.put("properties:\n"); Vector<Shape*, 8, SystemAllocPolicy> props; for (Shape::Range<NoGC> r(obj->as<NativeObject>().lastProperty()); !r.empty(); r.popFront()) { if (!props.append(&r.front())) { - fprintf(fp, "(OOM while appending properties)\n"); + out.printf("(OOM while appending properties)\n"); break; } } for (size_t i = props.length(); i-- != 0;) - DumpProperty(&obj->as<NativeObject>(), *props[i], fp); + DumpProperty(&obj->as<NativeObject>(), *props[i], out); } - fputc('\n', fp); + out.putChar('\n'); } // For debuggers. void JSObject::dump() const { - dump(stderr); + Fprinter out(stderr); + dump(out); } static void -MaybeDumpScope(Scope* scope, FILE* fp) +MaybeDumpScope(Scope* scope, js::GenericPrinter& out) { if (scope) { - fprintf(fp, " scope: %s\n", ScopeKindString(scope->kind())); + out.printf(" scope: %s\n", ScopeKindString(scope->kind())); for (BindingIter bi(scope); bi; bi++) { - fprintf(fp, " "); - dumpValue(StringValue(bi.name()), fp); - fputc('\n', fp); + out.put(" "); + dumpValue(StringValue(bi.name()), out); + out.putChar('\n'); } } } static void -MaybeDumpValue(const char* name, const Value& v, FILE* fp) +MaybeDumpValue(const char* name, const Value& v, js::GenericPrinter& out) { if (!v.isNull()) { - fprintf(fp, " %s: ", name); - dumpValue(v, fp); - fputc('\n', fp); + out.printf(" %s: ", name); + dumpValue(v, out); + out.putChar('\n'); } } JS_FRIEND_API(void) -js::DumpInterpreterFrame(JSContext* cx, FILE* fp, InterpreterFrame* start) +js::DumpInterpreterFrame(JSContext* cx, js::GenericPrinter& out, InterpreterFrame* start) { /* This should only called during live debugging. */ ScriptFrameIter i(cx); if (!start) { if (i.done()) { - fprintf(fp, "no stack for cx = %p\n", (void*) cx); + out.printf("no stack for cx = %p\n", (void*) cx); return; } } else { while (!i.done() && !i.isJSJit() && i.interpFrame() != start) ++i; if (i.done()) { - fprintf(fp, "fp = %p not found in cx = %p\n", + out.printf("fp = %p not found in cx = %p\n", (void*)start, (void*)cx); return; } } for (; !i.done(); ++i) { if (i.isJSJit()) - fprintf(fp, "JIT frame\n"); + out.put("JIT frame\n"); else - fprintf(fp, "InterpreterFrame at %p\n", (void*) i.interpFrame()); + out.printf("InterpreterFrame at %p\n", (void*) i.interpFrame()); if (i.isFunctionFrame()) { - fprintf(fp, "callee fun: "); + out.put("callee fun: "); RootedValue v(cx); JSObject* fun = i.callee(cx); v.setObject(*fun); - dumpValue(v, fp); + dumpValue(v, out); } else { - fprintf(fp, "global or eval frame, no callee"); + out.put("global or eval frame, no callee"); } - fputc('\n', fp); - - fprintf(fp, "file %s line %zu\n", + out.putChar('\n'); + + out.printf("file %s line %zu\n", i.script()->filename(), i.script()->lineno()); if (jsbytecode* pc = i.pc()) { - fprintf(fp, " pc = %p\n", pc); - fprintf(fp, " current op: %s\n", CodeName[*pc]); - MaybeDumpScope(i.script()->lookupScope(pc), fp); + out.printf(" pc = %p\n", pc); + out.printf(" current op: %s\n", CodeName[*pc]); + MaybeDumpScope(i.script()->lookupScope(pc), out); } if (i.isFunctionFrame()) - MaybeDumpValue("this", i.thisArgument(cx), fp); + MaybeDumpValue("this", i.thisArgument(cx), out); if (!i.isJSJit()) { - fprintf(fp, " rval: "); - dumpValue(i.interpFrame()->returnValue(), fp); - fputc('\n', fp); + out.put(" rval: "); + dumpValue(i.interpFrame()->returnValue(), out); + out.putChar('\n'); } - fprintf(fp, " flags:"); + out.put(" flags:"); if (i.isConstructing()) - fprintf(fp, " constructing"); + out.put(" constructing"); if (!i.isJSJit() && i.interpFrame()->isDebuggerEvalFrame()) - fprintf(fp, " debugger eval"); + out.put(" debugger eval"); if (i.isEvalFrame()) - fprintf(fp, " eval"); - fputc('\n', fp); - - fprintf(fp, " envChain: (JSObject*) %p\n", (void*) i.environmentChain(cx)); - - fputc('\n', fp); + out.put(" eval"); + out.putChar('\n'); + + out.printf(" envChain: (JSObject*) %p\n", (void*) i.environmentChain(cx)); + + out.putChar('\n'); } } #endif /* DEBUG */ +namespace js { + +// We don't want jsfriendapi.h to depend on GenericPrinter, +// so these functions are declared directly in the cpp. + +JS_FRIEND_API(void) +DumpBacktrace(JSContext* cx, js::GenericPrinter& out); + +} + JS_FRIEND_API(void) js::DumpBacktrace(JSContext* cx, FILE* fp) { + Fprinter out(fp); + js::DumpBacktrace(cx, out); +} + +JS_FRIEND_API(void) +js::DumpBacktrace(JSContext* cx, js::GenericPrinter& out) +{ Sprinter sprinter(cx, false); if (!sprinter.init()) { - fprintf(fp, "js::DumpBacktrace: OOM\n"); + out.put("js::DumpBacktrace: OOM\n"); return; } size_t depth = 0; for (AllFramesIter i(cx); !i.done(); ++i, ++depth) { const char* filename; unsigned line; if (i.hasScript()) { filename = JS_GetScriptFilename(i.script()); @@ -3767,17 +3801,17 @@ js::DumpBacktrace(JSContext* cx, FILE* f if (i.hasScript()) { sprinter.printf(" (%p @ %zu)\n", i.script(), i.script()->pcToOffset(i.pc())); } else { sprinter.printf(" (%p)\n", i.pc()); } } - fprintf(fp, "%s", sprinter.string()); + out.printf("%s", sprinter.string()); #ifdef XP_WIN32 if (IsDebuggerPresent()) OutputDebugStringA(sprinter.string()); #endif } JS_FRIEND_API(void) js::DumpBacktrace(JSContext* cx)
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -19,16 +19,17 @@ #include "mozilla/MemoryReporting.h" #include "gc/Barrier.h" #include "gc/Marking.h" #include "js/Conversions.h" #include "js/GCAPI.h" #include "js/GCVector.h" #include "js/HeapAPI.h" +#include "vm/Printer.h" #include "vm/Shape.h" #include "vm/String.h" #include "vm/Xdr.h" namespace JS { struct ClassInfo; } // namespace JS @@ -554,17 +555,17 @@ class JSObject : public js::gc::Cell template <class T> const T& as() const { MOZ_ASSERT(this->is<T>()); return *static_cast<const T*>(this); } #ifdef DEBUG - void dump(FILE* fp) const; + void dump(js::GenericPrinter& fp) const; void dump() const; #endif /* JIT Accessors */ static size_t offsetOfGroup() { return offsetof(JSObject, group_); } // Maximum size in bytes of a JSObject.
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -88,16 +88,17 @@ #include "vm/ArgumentsObject.h" #include "vm/AsyncFunction.h" #include "vm/AsyncIteration.h" #include "vm/Compression.h" #include "vm/Debugger.h" #include "vm/HelperThreads.h" #include "vm/Monitor.h" #include "vm/MutexIDs.h" +#include "vm/Printer.h" #include "vm/Shape.h" #include "vm/SharedArrayObject.h" #include "vm/StringBuffer.h" #include "vm/Time.h" #include "vm/TypedArrayObject.h" #include "vm/WrapperObject.h" #include "wasm/WasmJS.h" @@ -4305,18 +4306,19 @@ Parse(JSContext* cx, unsigned argc, Valu nullptr); if (!parser.checkOptions()) return false; ParseNode* pn = parser.parse(); if (!pn) return false; #ifdef DEBUG - DumpParseTree(pn); - fputc('\n', stderr); + js::Fprinter out(stderr); + DumpParseTree(pn, out); + out.putChar('\n'); #endif args.rval().setUndefined(); return true; } static bool SyntaxParse(JSContext* cx, unsigned argc, Value* vp) {
--- a/js/src/vm/Printer.h +++ b/js/src/vm/Printer.h @@ -33,20 +33,24 @@ class GenericPrinter bool hadOOM_; // whether reportOutOfMemory() has been called. GenericPrinter(); public: // Puts |len| characters from |s| at the current position and // return true on success, false on failure. virtual bool put(const char* s, size_t len) = 0; + virtual void flush() { /* Do nothing */ } inline bool put(const char* s) { return put(s, strlen(s)); } + inline bool putChar(const char c) { + return put(&c, 1); + } // Prints a formatted string into the buffer. bool printf(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3); bool vprintf(const char* fmt, va_list ap) MOZ_FORMAT_PRINTF(2, 0); // Report that a string operation failed to get the memory it requested. virtual void reportOutOfMemory(); @@ -141,17 +145,17 @@ class Fprinter final : public GenericPri ~Fprinter(); // Initialize this printer, returns false on error. MOZ_MUST_USE bool init(const char* path); void init(FILE* fp); bool isInitialized() const { return file_ != nullptr; } - void flush(); + void flush() override; void finish(); // Puts |len| characters from |s| at the current position and // return true on success, false on failure. virtual bool put(const char* s, size_t len) override; using GenericPrinter::put; // pick up |inline bool put(const char* s);| };
--- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -39,16 +39,17 @@ #include "gc/Policy.h" #include "jit/AtomicOperations.h" #include "jit/InlinableNatives.h" #include "js/CharacterEncoding.h" #include "js/Date.h" #include "vm/Compression.h" #include "vm/GeneratorObject.h" #include "vm/Interpreter.h" +#include "vm/Printer.h" #include "vm/RegExpObject.h" #include "vm/String.h" #include "vm/StringBuffer.h" #include "vm/TypedArrayObject.h" #include "vm/WrapperObject.h" #include "jsatominlines.h" #include "jsfuninlines.h" @@ -405,19 +406,20 @@ static bool intrinsic_AssertionFailed(JSContext* cx, unsigned argc, Value* vp) { #ifdef DEBUG CallArgs args = CallArgsFromVp(argc, vp); if (args.length() > 0) { // try to dump the informative string JSString* str = ToString<CanGC>(cx, args[0]); if (str) { - fprintf(stderr, "Self-hosted JavaScript assertion info: "); - str->dumpCharsNoNewline(); - fputc('\n', stderr); + js::Fprinter out(stderr); + out.put("Self-hosted JavaScript assertion info: "); + str->dumpCharsNoNewline(out); + out.putChar('\n'); } } #endif MOZ_ASSERT(false); return false; } /** @@ -425,20 +427,21 @@ intrinsic_AssertionFailed(JSContext* cx, */ static bool intrinsic_DumpMessage(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); #ifdef DEBUG if (args.length() > 0) { // try to dump the informative string + js::Fprinter out(stderr); JSString* str = ToString<CanGC>(cx, args[0]); if (str) { - str->dumpCharsNoNewline(); - fputc('\n', stderr); + str->dumpCharsNoNewline(out); + out.putChar('\n'); } else { cx->recoverFromOutOfMemory(); } } #endif args.rval().setUndefined(); return true; }
--- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -1771,89 +1771,89 @@ KidsPointer::checkConsistency(Shape* aKi MOZ_ASSERT(isHash()); KidsHash* hash = toHash(); KidsHash::Ptr ptr = hash->lookup(StackShape(aKid)); MOZ_ASSERT(*ptr == aKid); } } void -Shape::dump(FILE* fp) const +Shape::dump(js::GenericPrinter& out) const { jsid propid = this->propid(); MOZ_ASSERT(!JSID_IS_VOID(propid)); if (JSID_IS_INT(propid)) { - fprintf(fp, "[%ld]", (long) JSID_TO_INT(propid)); + out.printf("[%ld]", (long) JSID_TO_INT(propid)); } else if (JSID_IS_ATOM(propid)) { if (JSLinearString* str = JSID_TO_ATOM(propid)) - FileEscapedString(fp, str, '"'); + EscapedStringPrinter(out, str, '"'); else - fputs("<error>", fp); + out.put("<error>"); } else { MOZ_ASSERT(JSID_IS_SYMBOL(propid)); - JSID_TO_SYMBOL(propid)->dump(fp); + JSID_TO_SYMBOL(propid)->dump(out); } - fprintf(fp, " g/s %p/%p slot %d attrs %x ", - JS_FUNC_TO_DATA_PTR(void*, getter()), - JS_FUNC_TO_DATA_PTR(void*, setter()), - hasSlot() ? slot() : -1, attrs); + out.printf(" g/s %p/%p slot %d attrs %x ", + JS_FUNC_TO_DATA_PTR(void*, getter()), + JS_FUNC_TO_DATA_PTR(void*, setter()), + hasSlot() ? slot() : -1, attrs); if (attrs) { int first = 1; - fputs("(", fp); -#define DUMP_ATTR(name, display) if (attrs & JSPROP_##name) fputs(&(" " #display)[first], fp), first = 0 + out.putChar('('); +#define DUMP_ATTR(name, display) if (attrs & JSPROP_##name) out.put(&(" " #display)[first]), first = 0 DUMP_ATTR(ENUMERATE, enumerate); DUMP_ATTR(READONLY, readonly); DUMP_ATTR(PERMANENT, permanent); DUMP_ATTR(GETTER, getter); DUMP_ATTR(SETTER, setter); DUMP_ATTR(SHARED, shared); #undef DUMP_ATTR - fputs(") ", fp); + out.putChar(')'); } - fprintf(fp, "flags %x ", flags); + out.printf("flags %x ", flags); if (flags) { int first = 1; - fputs("(", fp); -#define DUMP_FLAG(name, display) if (flags & name) fputs(&(" " #display)[first], fp), first = 0 + out.putChar('('); +#define DUMP_FLAG(name, display) if (flags & name) out.put(&(" " #display)[first]), first = 0 DUMP_FLAG(IN_DICTIONARY, in_dictionary); #undef DUMP_FLAG - fputs(") ", fp); + out.putChar(')'); } } void -Shape::dumpSubtree(int level, FILE* fp) const +Shape::dumpSubtree(int level, js::GenericPrinter& out) const { if (!parent) { MOZ_ASSERT(level == 0); MOZ_ASSERT(JSID_IS_EMPTY(propid_)); - fprintf(fp, "class %s emptyShape\n", getObjectClass()->name); + out.printf("class %s emptyShape\n", getObjectClass()->name); } else { - fprintf(fp, "%*sid ", level, ""); - dump(fp); + out.printf("%*sid ", level, ""); + dump(out); } if (!kids.isNull()) { ++level; if (kids.isShape()) { Shape* kid = kids.toShape(); MOZ_ASSERT(kid->parent == this); - kid->dumpSubtree(level, fp); + kid->dumpSubtree(level, out); } else { const KidsHash& hash = *kids.toHash(); for (KidsHash::Range range = hash.all(); !range.empty(); range.popFront()) { Shape* kid = range.front(); MOZ_ASSERT(kid->parent == this); - kid->dumpSubtree(level, fp); + kid->dumpSubtree(level, out); } } } } #endif static bool
--- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -25,16 +25,17 @@ #include "gc/Heap.h" #include "gc/Marking.h" #include "gc/Rooting.h" #include "js/HashTable.h" #include "js/MemoryMetrics.h" #include "js/RootingAPI.h" #include "js/UbiNode.h" #include "vm/ObjectGroup.h" +#include "vm/Printer.h" #include "vm/String.h" #include "vm/Symbol.h" #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4800) #pragma warning(push) #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */ @@ -1135,18 +1136,18 @@ class Shape : public gc::TenuredCell bool res = isBigEnoughForAShapeTableSlow(); if (res) flags |= CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE; flags |= HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE; return res; } #ifdef DEBUG - void dump(FILE* fp) const; - void dumpSubtree(int level, FILE* fp) const; + void dump(js::GenericPrinter& out) const; + void dumpSubtree(int level, js::GenericPrinter& out) const; #endif void sweep(); void finalize(FreeOp* fop); void removeChild(Shape* child); static const JS::TraceKind TraceKind = JS::TraceKind::Shape;
--- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -100,136 +100,132 @@ JS::ubi::Concrete<JSString>::size(mozill } const char16_t JS::ubi::Concrete<JSString>::concreteTypeName[] = u"JSString"; #ifdef DEBUG template <typename CharT> /*static */ void -JSString::dumpChars(const CharT* s, size_t n, FILE* fp) +JSString::dumpChars(const CharT* s, size_t n, js::GenericPrinter& out) { if (n == SIZE_MAX) { n = 0; while (s[n]) n++; } - fputc('"', fp); + out.put("\""); for (size_t i = 0; i < n; i++) { char16_t c = s[i]; if (c == '\n') - fprintf(fp, "\\n"); + out.put("\\n"); else if (c == '\t') - fprintf(fp, "\\t"); + out.put("\\t"); else if (c >= 32 && c < 127) - fputc(s[i], fp); + out.putChar((char)s[i]); else if (c <= 255) - fprintf(fp, "\\x%02x", unsigned(c)); + out.printf("\\x%02x", unsigned(c)); else - fprintf(fp, "\\u%04x", unsigned(c)); + out.printf("\\u%04x", unsigned(c)); } - fputc('"', fp); + out.putChar('"'); } template void -JSString::dumpChars(const Latin1Char* s, size_t n, FILE* fp); +JSString::dumpChars(const Latin1Char* s, size_t n, js::GenericPrinter& out); template void -JSString::dumpChars(const char16_t* s, size_t n, FILE* fp); +JSString::dumpChars(const char16_t* s, size_t n, js::GenericPrinter& out); void -JSString::dumpCharsNoNewline(FILE* fp) +JSString::dumpCharsNoNewline(js::GenericPrinter& out) { if (JSLinearString* linear = ensureLinear(nullptr)) { AutoCheckCannotGC nogc; if (hasLatin1Chars()) - dumpChars(linear->latin1Chars(nogc), length(), fp); + dumpChars(linear->latin1Chars(nogc), length(), out); else - dumpChars(linear->twoByteChars(nogc), length(), fp); + dumpChars(linear->twoByteChars(nogc), length(), out); } else { - fprintf(fp, "(oom in JSString::dumpCharsNoNewline)"); + out.put("(oom in JSString::dumpCharsNoNewline)"); } } void -JSString::dump(FILE* fp) +JSString::dump() +{ + js::Fprinter out(stderr); + dump(out); +} + +void +JSString::dump(js::GenericPrinter& out) { if (JSLinearString* linear = ensureLinear(nullptr)) { AutoCheckCannotGC nogc; if (hasLatin1Chars()) { const Latin1Char* chars = linear->latin1Chars(nogc); - fprintf(fp, "JSString* (%p) = Latin1Char * (%p) = ", (void*) this, + out.printf("JSString* (%p) = Latin1Char * (%p) = ", (void*) this, (void*) chars); - dumpChars(chars, length(), fp); + dumpChars(chars, length(), out); } else { const char16_t* chars = linear->twoByteChars(nogc); - fprintf(fp, "JSString* (%p) = char16_t * (%p) = ", (void*) this, + out.printf("JSString* (%p) = char16_t * (%p) = ", (void*) this, (void*) chars); - dumpChars(chars, length(), fp); + dumpChars(chars, length(), out); } } else { - fprintf(fp, "(oom in JSString::dump)"); + out.put("(oom in JSString::dump)"); } - fputc('\n', fp); + out.putChar('\n'); } + void -JSString::dumpCharsNoNewline() -{ - dumpCharsNoNewline(stderr); -} - -void -JSString::dump() +JSString::dumpRepresentation(js::GenericPrinter& out, int indent) const { - dump(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); + if (isRope()) asRope() .dumpRepresentation(out, indent); + else if (isDependent()) asDependent() .dumpRepresentation(out, indent); + else if (isExternal()) asExternal() .dumpRepresentation(out, indent); + else if (isExtensible()) asExtensible() .dumpRepresentation(out, indent); + else if (isInline()) asInline() .dumpRepresentation(out, indent); + else if (isFlat()) asFlat() .dumpRepresentation(out, indent); else MOZ_CRASH("Unexpected JSString representation"); } void -JSString::dumpRepresentationHeader(FILE* fp, int indent, const char* subclass) const +JSString::dumpRepresentationHeader(js::GenericPrinter& out, 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); - if (flags & INDEX_VALUE_BIT) fprintf(fp, " INDEX_VALUE(%u)", getIndexValue()); - fputc('\n', fp); + out.printf("((%s*) %p) length: %zu flags: 0x%x", subclass, this, length(), flags); + if (flags & FLAT_BIT) out.put(" FLAT"); + if (flags & HAS_BASE_BIT) out.put(" HAS_BASE"); + if (flags & INLINE_CHARS_BIT) out.put(" INLINE_CHARS"); + if (flags & ATOM_BIT) out.put(" ATOM"); + if (isPermanentAtom()) out.put(" PERMANENT"); + if (flags & LATIN1_CHARS_BIT) out.put(" LATIN1"); + if (flags & INDEX_VALUE_BIT) out.put(" INDEX_VALUE(%u)", getIndexValue()); + out.putChar('\n'); } void -JSLinearString::dumpRepresentationChars(FILE* fp, int indent) const +JSLinearString::dumpRepresentationChars(js::GenericPrinter& out, int indent) const { if (hasLatin1Chars()) { - fprintf(fp, "%*schars: ((Latin1Char*) %p) ", indent, "", rawLatin1Chars()); - dumpChars(rawLatin1Chars(), length()); + out.printf("%*schars: ((Latin1Char*) %p) ", indent, "", rawLatin1Chars()); + dumpChars(rawLatin1Chars(), length(), out); } else { - fprintf(fp, "%*schars: ((char16_t*) %p) ", indent, "", rawTwoByteChars()); - dumpChars(rawTwoByteChars(), length()); + out.printf("%*schars: ((char16_t*) %p) ", indent, "", rawTwoByteChars()); + dumpChars(rawTwoByteChars(), length(), out); } - fputc('\n', fp); + out.putChar('\n'); } bool JSString::equals(const char* s) { JSLinearString* linear = ensureLinear(nullptr); if (!linear) { fprintf(stderr, "OOM in JSString::equals!\n"); @@ -332,26 +328,26 @@ JSRope::copyCharsInternal(JSContext* cx, if (nullTerminate) out[n] = 0; return true; } #ifdef DEBUG void -JSRope::dumpRepresentation(FILE* fp, int indent) const +JSRope::dumpRepresentation(js::GenericPrinter& out, int indent) const { - dumpRepresentationHeader(fp, indent, "JSRope"); + dumpRepresentationHeader(out, indent, "JSRope"); indent += 2; - fprintf(fp, "%*sleft: ", indent, ""); - leftChild()->dumpRepresentation(fp, indent); + out.printf("%*sleft: ", indent, ""); + leftChild()->dumpRepresentation(out, indent); - fprintf(fp, "%*sright: ", indent, ""); - rightChild()->dumpRepresentation(fp, indent); + out.printf("%*sright: ", indent, ""); + rightChild()->dumpRepresentation(out, indent); } #endif namespace js { template <> void CopyChars(char16_t* dest, const JSLinearString& str) @@ -710,26 +706,26 @@ JSDependentString::undepend(JSContext* c MOZ_ASSERT(JSString::isDependent()); return hasLatin1Chars() ? undependInternal<Latin1Char>(cx) : undependInternal<char16_t>(cx); } #ifdef DEBUG void -JSDependentString::dumpRepresentation(FILE* fp, int indent) const +JSDependentString::dumpRepresentation(js::GenericPrinter& out, int indent) const { - dumpRepresentationHeader(fp, indent, "JSDependentString"); + dumpRepresentationHeader(out, indent, "JSDependentString"); indent += 2; if (mozilla::Maybe<size_t> offset = baseOffset()) - fprintf(fp, "%*soffset: %zu\n", indent, "", *offset); + out.printf("%*soffset: %zu\n", indent, "", *offset); - fprintf(fp, "%*sbase: ", indent, ""); - base()->dumpRepresentation(fp, indent); + out.printf("%*sbase: ", indent, ""); + base()->dumpRepresentation(out, indent); } #endif template <typename CharT> /* static */ bool JSFlatString::isIndexSlow(const CharT* s, size_t length, uint32_t* indexp) { CharT ch = *s; @@ -1103,36 +1099,37 @@ JSExternalString::ensureFlat(JSContext* setNonInlineChars<char16_t>(s); d.u1.flags = FLAT_BIT; return &this->asFlat(); } #ifdef DEBUG void -JSAtom::dump(FILE* fp) +JSAtom::dump(js::GenericPrinter& out) { - fprintf(fp, "JSAtom* (%p) = ", (void*) this); - this->JSString::dump(fp); + out.printf("JSAtom* (%p) = ", (void*) this); + this->JSString::dump(out); } void JSAtom::dump() { - dump(stderr); + Fprinter out(stderr); + dump(out); } void -JSExternalString::dumpRepresentation(FILE* fp, int indent) const +JSExternalString::dumpRepresentation(js::GenericPrinter& out, int indent) const { - dumpRepresentationHeader(fp, indent, "JSExternalString"); + dumpRepresentationHeader(out, indent, "JSExternalString"); indent += 2; - fprintf(fp, "%*sfinalizer: ((JSStringFinalizer*) %p)\n", indent, "", externalFinalizer()); - dumpRepresentationChars(fp, indent); + out.printf("%*sfinalizer: ((JSStringFinalizer*) %p)\n", indent, "", externalFinalizer()); + dumpRepresentationChars(out, indent); } #endif /* DEBUG */ JSLinearString* js::NewDependentString(JSContext* cx, JSString* baseArg, size_t start, size_t length) { if (length == 0) return cx->emptyString(); @@ -1484,42 +1481,42 @@ NewMaybeExternalString(JSContext* cx, co cache.put(str); return str; } } /* namespace js */ #ifdef DEBUG void -JSExtensibleString::dumpRepresentation(FILE* fp, int indent) const +JSExtensibleString::dumpRepresentation(js::GenericPrinter& out, int indent) const { - dumpRepresentationHeader(fp, indent, "JSExtensibleString"); + dumpRepresentationHeader(out, indent, "JSExtensibleString"); indent += 2; - fprintf(fp, "%*scapacity: %zu\n", indent, "", capacity()); - dumpRepresentationChars(fp, indent); + out.printf("%*scapacity: %zu\n", indent, "", capacity()); + dumpRepresentationChars(out, indent); } void -JSInlineString::dumpRepresentation(FILE* fp, int indent) const +JSInlineString::dumpRepresentation(js::GenericPrinter& out, int indent) const { - dumpRepresentationHeader(fp, indent, + dumpRepresentationHeader(out, indent, isFatInline() ? "JSFatInlineString" : "JSThinInlineString"); indent += 2; - dumpRepresentationChars(fp, indent); + dumpRepresentationChars(out, indent); } void -JSFlatString::dumpRepresentation(FILE* fp, int indent) const +JSFlatString::dumpRepresentation(js::GenericPrinter& out, int indent) const { - dumpRepresentationHeader(fp, indent, "JSFlatString"); + dumpRepresentationHeader(out, indent, "JSFlatString"); indent += 2; - dumpRepresentationChars(fp, indent); + dumpRepresentationChars(out, indent); } #endif static void FinalizeRepresentativeExternalString(const JSStringFinalizer* fin, char16_t* chars); static const JSStringFinalizer RepresentativeExternalStringFinalizer = { FinalizeRepresentativeExternalString };
--- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -18,16 +18,18 @@ #include "gc/Barrier.h" #include "gc/Heap.h" #include "gc/Marking.h" #include "gc/Rooting.h" #include "js/CharacterEncoding.h" #include "js/GCAPI.h" #include "js/RootingAPI.h" +#include "vm/Printer.h" + class JSDependentString; class JSExtensibleString; class JSExternalString; class JSInlineString; class JSRope; namespace js { @@ -521,25 +523,24 @@ class JSString : public js::gc::TenuredC offsetof(JSString, d.s.u2.nonInlineCharsLatin1), "nonInlineCharsTwoByte and nonInlineCharsLatin1 must have same offset"); return offsetof(JSString, d.s.u2.nonInlineCharsTwoByte); } static const JS::TraceKind TraceKind = JS::TraceKind::String; #ifdef DEBUG - void dump(FILE* fp); - void dumpCharsNoNewline(FILE* fp); - void dump(); - void dumpCharsNoNewline(); - void dumpRepresentation(FILE* fp, int indent) const; - void dumpRepresentationHeader(FILE* fp, int indent, const char* subclass) const; + void dump(); // Debugger-friendly stderr dump. + void dump(js::GenericPrinter& out); + void dumpCharsNoNewline(js::GenericPrinter& out); + void dumpRepresentation(js::GenericPrinter& out, int indent) const; + void dumpRepresentationHeader(js::GenericPrinter& out, int indent, const char* subclass) const; template <typename CharT> - static void dumpChars(const CharT* s, size_t len, FILE* fp=stderr); + static void dumpChars(const CharT* s, size_t len, js::GenericPrinter& out); bool equals(const char* s); #endif void traceChildren(JSTracer* trc); static MOZ_ALWAYS_INLINE void readBarrier(JSString* thing) { if (thing->isPermanentAtom()) @@ -613,17 +614,17 @@ class JSRope : public JSString static size_t offsetOfLeft() { return offsetof(JSRope, d.s.u2.left); } static size_t offsetOfRight() { return offsetof(JSRope, d.s.u3.right); } #ifdef DEBUG - void dumpRepresentation(FILE* fp, int indent) const; + void dumpRepresentation(js::GenericPrinter& out, int indent) const; #endif }; static_assert(sizeof(JSRope) == sizeof(JSString), "string subclasses must be binary-compatible with JSString"); class JSLinearString : public JSString { @@ -696,17 +697,17 @@ class JSLinearString : public JSString char16_t latin1OrTwoByteChar(size_t index) const { MOZ_ASSERT(JSString::isLinear()); MOZ_ASSERT(index < length()); JS::AutoCheckCannotGC nogc; return hasLatin1Chars() ? latin1Chars(nogc)[index] : twoByteChars(nogc)[index]; } #ifdef DEBUG - void dumpRepresentationChars(FILE* fp, int indent) const; + void dumpRepresentationChars(js::GenericPrinter& out, int indent) const; #endif }; static_assert(sizeof(JSLinearString) == sizeof(JSString), "string subclasses must be binary-compatible with JSString"); class JSDependentString : public JSLinearString { @@ -742,17 +743,17 @@ class JSDependentString : public JSLinea static inline JSLinearString* new_(JSContext* cx, JSLinearString* base, size_t start, size_t length); inline static size_t offsetOfBase() { return offsetof(JSDependentString, d.s.u3.base); } #ifdef DEBUG - void dumpRepresentation(FILE* fp, int indent) const; + void dumpRepresentation(js::GenericPrinter& out, int indent) const; #endif }; static_assert(sizeof(JSDependentString) == sizeof(JSString), "string subclasses must be binary-compatible with JSString"); class JSFlatString : public JSLinearString { @@ -827,17 +828,17 @@ class JSFlatString : public JSLinearStri * operation changes the string to the JSAtom type, in place. */ MOZ_ALWAYS_INLINE JSAtom* morphAtomizedStringIntoAtom(js::HashNumber hash); MOZ_ALWAYS_INLINE JSAtom* morphAtomizedStringIntoPermanentAtom(js::HashNumber hash); inline void finalize(js::FreeOp* fop); #ifdef DEBUG - void dumpRepresentation(FILE* fp, int indent) const; + void dumpRepresentation(js::GenericPrinter& out, int indent) const; #endif }; static_assert(sizeof(JSFlatString) == sizeof(JSString), "string subclasses must be binary-compatible with JSString"); class JSExtensibleString : public JSFlatString { @@ -848,17 +849,17 @@ class JSExtensibleString : public JSFlat public: MOZ_ALWAYS_INLINE size_t capacity() const { MOZ_ASSERT(JSString::isExtensible()); return d.s.u3.capacity; } #ifdef DEBUG - void dumpRepresentation(FILE* fp, int indent) const; + void dumpRepresentation(js::GenericPrinter& out, int indent) const; #endif }; static_assert(sizeof(JSExtensibleString) == sizeof(JSString), "string subclasses must be binary-compatible with JSString"); class JSInlineString : public JSFlatString { @@ -880,17 +881,17 @@ class JSInlineString : public JSFlatStri template<typename CharT> static bool lengthFits(size_t length); static size_t offsetOfInlineStorage() { return offsetof(JSInlineString, d.inlineStorageTwoByte); } #ifdef DEBUG - void dumpRepresentation(FILE* fp, int indent) const; + void dumpRepresentation(js::GenericPrinter& out, int indent) const; #endif }; static_assert(sizeof(JSInlineString) == sizeof(JSString), "string subclasses must be binary-compatible with JSString"); /* * On 32-bit platforms, JSThinInlineString can store 7 Latin1 characters or 3 @@ -994,17 +995,17 @@ class JSExternalString : public JSLinear /* Only called by the GC for strings with the AllocKind::EXTERNAL_STRING kind. */ inline void finalize(js::FreeOp* fop); JSFlatString* ensureFlat(JSContext* cx); #ifdef DEBUG - void dumpRepresentation(FILE* fp, int indent) const; + void dumpRepresentation(js::GenericPrinter& out, int indent) const; #endif }; static_assert(sizeof(JSExternalString) == sizeof(JSString), "string subclasses must be binary-compatible with JSString"); class JSUndependedString : public JSFlatString { @@ -1040,17 +1041,17 @@ class JSAtom : public JSFlatString MOZ_ALWAYS_INLINE void morphIntoPermanentAtom() { d.u1.flags |= PERMANENT_ATOM_MASK; } inline js::HashNumber hash() const; inline void initHash(js::HashNumber hash); #ifdef DEBUG - void dump(FILE* fp); + void dump(js::GenericPrinter& out); void dump(); #endif }; static_assert(sizeof(JSAtom) == sizeof(JSString), "string subclasses must be binary-compatible with JSString"); namespace js {
--- a/js/src/vm/Symbol.cpp +++ b/js/src/vm/Symbol.cpp @@ -92,35 +92,42 @@ Symbol::for_(JSContext* cx, HandleString } } cx->markAtom(sym); return sym; } #ifdef DEBUG void -Symbol::dump(FILE* fp) +Symbol::dump() +{ + js::Fprinter out(stderr); + dump(out); +} + +void +Symbol::dump(js::GenericPrinter& out) { if (isWellKnownSymbol()) { // All the well-known symbol names are ASCII. - description_->dumpCharsNoNewline(fp); + description_->dumpCharsNoNewline(out); } else if (code_ == SymbolCode::InSymbolRegistry || code_ == SymbolCode::UniqueSymbol) { - fputs(code_ == SymbolCode::InSymbolRegistry ? "Symbol.for(" : "Symbol(", fp); + out.printf(code_ == SymbolCode::InSymbolRegistry ? "Symbol.for(" : "Symbol("); if (description_) - description_->dumpCharsNoNewline(fp); + description_->dumpCharsNoNewline(out); else - fputs("undefined", fp); + out.printf("undefined"); - fputc(')', fp); + out.putChar(')'); if (code_ == SymbolCode::UniqueSymbol) - fprintf(fp, "@%p", (void*) this); + out.printf("@%p", (void*) this); } else { - fprintf(fp, "<Invalid Symbol code=%u>", unsigned(code_)); + out.printf("<Invalid Symbol code=%u>", unsigned(code_)); } } #endif // DEBUG bool js::SymbolDescriptiveString(JSContext* cx, Symbol* sym, MutableHandleValue result) { // steps 2-5
--- a/js/src/vm/Symbol.h +++ b/js/src/vm/Symbol.h @@ -15,16 +15,17 @@ #include "jsapi.h" #include "gc/Barrier.h" #include "gc/Marking.h" #include "js/GCHashTable.h" #include "js/RootingAPI.h" #include "js/TypeDecls.h" #include "js/Utility.h" +#include "vm/Printer.h" #include "vm/String.h" namespace js { class AutoLockForExclusiveAccess; } // namespace js namespace JS { @@ -89,17 +90,18 @@ class Symbol : public js::gc::TenuredCel thing->asTenured().writeBarrierPre(thing); } size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { return mallocSizeOf(this); } #ifdef DEBUG - void dump(FILE* fp = stderr); + void dump(); // Debugger-friendly stderr dump. + void dump(js::GenericPrinter& out); #endif }; } /* namespace JS */ namespace js { /* Hash policy used by the SymbolRegistry. */
--- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -26,16 +26,17 @@ #include "jit/CompileInfo.h" #include "jit/Ion.h" #include "jit/IonAnalysis.h" #include "jit/JitCompartment.h" #include "jit/OptimizationTracking.h" #include "js/MemoryMetrics.h" #include "vm/HelperThreads.h" #include "vm/Opcodes.h" +#include "vm/Printer.h" #include "vm/Shape.h" #include "vm/Time.h" #include "vm/UnboxedObject.h" #include "jsatominlines.h" #include "jsscriptinlines.h" #include "vm/NativeObject-inl.h" @@ -4632,29 +4633,30 @@ void TypeScript::printTypes(JSContext* cx, HandleScript script) const { MOZ_ASSERT(script->types() == this); if (!script->hasBaselineScript()) return; AutoEnterAnalysis enter(nullptr, script->zone()); + Fprinter out(stderr); if (script->functionNonDelazifying()) fprintf(stderr, "Function"); else if (script->isForEval()) fprintf(stderr, "Eval"); else fprintf(stderr, "Main"); fprintf(stderr, " %#" PRIxPTR " %s:%zu ", uintptr_t(script.get()), script->filename(), script->lineno()); if (script->functionNonDelazifying()) { if (JSAtom* name = script->functionNonDelazifying()->explicitName()) - name->dumpCharsNoNewline(); + name->dumpCharsNoNewline(out); } fprintf(stderr, "\n this:"); TypeScript::ThisTypes(script)->print(); for (unsigned i = 0; script->functionNonDelazifying() && i < script->functionNonDelazifying()->nargs(); i++)