Backed out 22 changesets (bug 1839396) for causing multiple spidermonkey bustages and jittest failures. CLOSED TREE

Backed out changeset c17523ff7eb5 (bug 1839396)
Backed out changeset 771c47934e72 (bug 1839396)
Backed out changeset d219a4366950 (bug 1839396)
Backed out changeset f1accceff8b8 (bug 1839396)
Backed out changeset 1dd3b4a4da66 (bug 1839396)
Backed out changeset 8645391a6359 (bug 1839396)
Backed out changeset 03082f8db52c (bug 1839396)
Backed out changeset 93a56d4cd58f (bug 1839396)
Backed out changeset 96be1bbf8fff (bug 1839396)
Backed out changeset b0a898413a0f (bug 1839396)
Backed out changeset b0dc61dbe899 (bug 1839396)
Backed out changeset d89ee847ad56 (bug 1839396)
Backed out changeset 42f370321925 (bug 1839396)
Backed out changeset 0321a4a743de (bug 1839396)
Backed out changeset d3f56d11a4b4 (bug 1839396)
Backed out changeset c193770e3745 (bug 1839396)
Backed out changeset d68820286c44 (bug 1839396)
Backed out changeset f10974287494 (bug 1839396)
Backed out changeset 6bbf058933c5 (bug 1839396)
Backed out changeset 101beccd6098 (bug 1839396)
Backed out changeset 030fc635d134 (bug 1839396)
Backed out changeset 89e13efa6b37 (bug 1839396)
This commit is contained in:
Iulian Moraru 2023-09-20 20:58:00 +03:00
Родитель f44f30add8
Коммит 98e5cc913d
21 изменённых файлов: 828 добавлений и 815 удалений

Просмотреть файл

@ -17078,6 +17078,7 @@ neqo_http3conn_is_zero_rtt
?ClassName@js@@YA?AV?$Handle@PAVPropertyName@js@@@JS@@W4JSProtoKey@@PAUJSContext@@@Z ?ClassName@js@@YA?AV?$Handle@PAVPropertyName@js@@@JS@@W4JSProtoKey@@PAUJSContext@@@Z
?FormatStackDump@JS@@YA?AV?$UniquePtr@$$BY0A@DUFreePolicy@JS@@@mozilla@@PAUJSContext@@_N11@Z ?FormatStackDump@JS@@YA?AV?$UniquePtr@$$BY0A@DUFreePolicy@JS@@@mozilla@@PAUJSContext@@_N11@Z
?callee@FrameIter@js@@QBEPAVJSFunction@@PAUJSContext@@@Z ?callee@FrameIter@js@@QBEPAVJSFunction@@PAUJSContext@@@Z
?jsprintf@Sprinter@js@@QAA_NPBDZZ
?vprintf@GenericPrinter@js@@QAE_NPBDPAD@Z ?vprintf@GenericPrinter@js@@QAE_NPBDPAD@Z
?release@Sprinter@js@@QAE?AV?$UniquePtr@$$BY0A@DUFreePolicy@JS@@@mozilla@@XZ ?release@Sprinter@js@@QAE?AV?$UniquePtr@$$BY0A@DUFreePolicy@JS@@@mozilla@@XZ
??1Sprinter@js@@QAE@XZ ??1Sprinter@js@@QAE@XZ

Просмотреть файл

@ -16849,6 +16849,7 @@ neqo_http3conn_is_zero_rtt
?ClassName@js@@YA?AV?$Handle@PEAVPropertyName@js@@@JS@@W4JSProtoKey@@PEAUJSContext@@@Z ?ClassName@js@@YA?AV?$Handle@PEAVPropertyName@js@@@JS@@W4JSProtoKey@@PEAUJSContext@@@Z
?FormatStackDump@JS@@YA?AV?$UniquePtr@$$BY0A@DUFreePolicy@JS@@@mozilla@@PEAUJSContext@@_N11@Z ?FormatStackDump@JS@@YA?AV?$UniquePtr@$$BY0A@DUFreePolicy@JS@@@mozilla@@PEAUJSContext@@_N11@Z
?callee@FrameIter@js@@QEBAPEAVJSFunction@@PEAUJSContext@@@Z ?callee@FrameIter@js@@QEBAPEAVJSFunction@@PEAUJSContext@@@Z
?jsprintf@Sprinter@js@@QEAA_NPEBDZZ
?vprintf@GenericPrinter@js@@QEAA_NPEBDPEAD@Z ?vprintf@GenericPrinter@js@@QEAA_NPEBDPEAD@Z
?release@Sprinter@js@@QEAA?AV?$UniquePtr@$$BY0A@DUFreePolicy@JS@@@mozilla@@XZ ?release@Sprinter@js@@QEAA?AV?$UniquePtr@$$BY0A@DUFreePolicy@JS@@@mozilla@@XZ
??1Sprinter@js@@QEAA@XZ ??1Sprinter@js@@QEAA@XZ

Просмотреть файл

@ -18,111 +18,6 @@
#include "js/TypeDecls.h" #include "js/TypeDecls.h"
#include "js/Utility.h" #include "js/Utility.h"
// [SMDOC] *Printer, Sprinter, Fprinter, ...
//
// # Motivation
//
// In many places, we want to have functions which are capable of logging
// various data structures. Previously, we had logging functions for each
// storage, such as using `fwrite`, `printf` or `snprintf`. In additional cases,
// many of these logging options were using a string serializing logging
// function, only to discard the allocated string after it had been copied to a
// file.
//
// GenericPrinter is an answer to avoid excessive amount of temporary
// allocations which are used once, and a way to make logging functions work
// independently of the backend they are used with.
//
// # Design
//
// The GenericPrinter implements most of `put`, `printf`, `vprintf` and
// `putChar` functions, which are implemented using `put` and `putChar`
// functions in the derivative classes. Thus, one does not have to reimplement
// `putString` nor `printf` for each printer.
//
// // Logging the value N to whatever printer is provided such as
// // a file or a string.
// void logN(GenericPrinter& out) {
// out.printf("[Logging] %d\n", this->n);
// }
//
// The printing functions are infallible, from the logging functions
// perspective. If an issue happens while printing, this would be recorded by
// the Printer, and this can be tested using `hadOutOfMemory` function by the
// owner of the Printer instance.
//
// Even in case of failure, printing functions should remain safe to use. Thus
// calling `put` twice in a row is safe even if no check for `hadOutOfMemory` is
// performed. This is necessary to simplify the control flow and avoid bubble up
// failures out of logging functions.
//
// Note, being safe to use does not imply correctness. In case of failure the
// correctness of the printed characters is no longer guarantee. One should use
// `hadOutOfMemory` function to know if any failure happened which might have
// caused incorrect content to be saved. In some cases, such as `Sprinter`,
// where the string buffer can be extracted, the returned value would account
// for checking `hadOutOfMemory`.
//
// # Implementations
//
// The GenericPrinter is a base class where the derivative classes are providing
// different implementations which have their own advantages and disadvantages:
//
// - Fprinter: FILE* printer. Write the content directly to a file.
//
// - Sprinter: System allocator C-string buffer. Write the content to a buffer
// which is reallocated as more content is added. The buffer can then be
// extracted into a C-string or a JSString, respectively using `release` and
// `releaseJS`.
//
// - LSprinter: LifoAlloc C-string rope. Write the content to a list of chunks
// in a LifoAlloc buffer, no-reallocation occur but one should use
// `exportInto` to serialize its content to a Sprinter or a Fprinter. This is
// useful to avoid reallocation copies, while using an existing LifoAlloc.
//
// - EscapePrinter: Wrapper around other printers, to escape characters when
// necessary.
//
// # Print UTF-16
//
// The GenericPrinter only handle `char` inputs, which is good enough for ASCII
// and Latin1 character sets. However, to handle UTF-16, one should use an
// EscapePrinter as well as a policy for escaping characters.
//
// One might require different escaping policies based on the escape sequences
// and based on the set of accepted character for the content generated. For
// example, JSON does not specify \x<XX> escape sequences.
//
// Today the following escape policies exists:
//
// - StringEscape: Produce C-like escape sequences: \<c>, \x<XX> and \u<XXXX>.
// - JSONEscape: Produce JSON escape sequences: \<c> and \u<XXXX>.
//
// An escape policy is defined by 2 functions:
//
// bool isSafeChar(char16_t c):
// Returns whether a character can be printed without being escaped.
//
// void convertInto(GenericPrinter& out, char16_t c):
// Calls the printer with the escape sequence for the character given as
// argument.
//
// To use an escape policy, the printer should be wrapped using an EscapePrinter
// as follows:
//
// {
// // The escaped string is surrounded by double-quotes, escape the double
// // quotes as well.
// StringEscape esc('"');
//
// // Wrap our existing `GenericPrinter& out` using the `EscapePrinter`.
// EscapePrinter ep(out, esc);
//
// // Append a sequence of characters which might contain UTF-16 characters.
// ep.put(chars);
// }
//
namespace js { namespace js {
class LifoAlloc; class LifoAlloc;
@ -139,67 +34,19 @@ class JS_PUBLIC_API GenericPrinter {
constexpr GenericPrinter() : hadOOM_(false) {} constexpr GenericPrinter() : hadOOM_(false) {}
public: public:
// Puts |len| characters from |s| at the current position. This function might // Puts |len| characters from |s| at the current position and
// silently fail and the error can be tested using `hadOutOfMemory()`. Calling // return true on success, false on failure.
// this function or any other printing functions after a failures is accepted, virtual bool put(const char* s, size_t len) = 0;
// but the outcome would still remain incorrect and `hadOutOfMemory()` would
// still report any of the previous errors.
virtual void put(const char* s, size_t len) = 0;
inline void put(const char* s) { put(s, strlen(s)); }
// Put a mozilla::Span / mozilla::Range of Latin1Char or char16_t characters
// in the output.
//
// Note that the char16_t variant is expected to crash unless putChar is
// overriden to handle properly the full set of WTF-16 character set.
virtual void put(mozilla::Span<const JS::Latin1Char> str);
virtual void put(mozilla::Span<const char16_t> str);
// Same as the various put function but only appending a single character.
//
// Note that the char16_t variant is expected to crash unless putChar is
// overriden to handle properly the full set of WTF-16 character set.
virtual inline void putChar(const char c) { put(&c, 1); }
virtual inline void putChar(const JS::Latin1Char c) { putChar(char(c)); }
virtual inline void putChar(const char16_t c) {
MOZ_CRASH("Use an EscapePrinter to handle all characters");
}
virtual void putString(JSContext* cx, JSString* str);
// Prints a formatted string into the buffer.
void printf(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
void vprintf(const char* fmt, va_list ap) MOZ_FORMAT_PRINTF(2, 0);
// In some cases, such as handling JSRopes in a less-quadratic worse-case,
// it might be useful to copy content which has already been generated.
//
// If the buffer is back-readable, then this function should return `true`
// and `putFromIndex` should be implemented to delegate to a `put` call at
// the matching index and the corresponding length. To provide the index
// argument of `putFromIndex`, the `index` method should also be implemented
// to return the index within the inner buffer used by the printer.
virtual bool canPutFromIndex() const { return false; }
// Append to the current buffer, bytes which have previously been appended
// before.
virtual void putFromIndex(size_t index, size_t length) {
MOZ_CRASH("Calls to putFromIndex should be guarded by canPutFromIndex.");
}
// When the printer has a seekable buffer and `canPutFromIndex` returns
// `true`, this function can return the `index` of the next character to be
// added to the buffer.
//
// This function is monotonic. Thus, if the printer encounter an
// Out-Of-Memory issue, then the returned index should be the maximal value
// ever returned.
virtual size_t index() const { return 0; }
// In some printers, this ensure that the content is fully written.
virtual void flush() { /* Do nothing */ 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. // Report that a string operation failed to get the memory it requested.
virtual void reportOutOfMemory(); virtual void reportOutOfMemory();
@ -251,8 +98,17 @@ class JS_PUBLIC_API Sprinter final : public GenericPrinter {
void checkInvariants() const; void checkInvariants() const;
const char* string() const {
MOZ_ASSERT(!hadOutOfMemory());
return base;
}
const char* stringEnd() const { return string() + offset; }
JS::UniqueChars release(); JS::UniqueChars release();
JSString* releaseJS(JSContext* cx);
// Returns the string at offset |off|.
char* stringAt(ptrdiff_t off) const;
// Returns the char at offset |off|.
char& operator[](size_t off);
// Attempt to reserve len + 1 space (for a trailing nullptr byte). If the // Attempt to reserve len + 1 space (for a trailing nullptr byte). If the
// attempt succeeds, return a pointer to the start of that space and adjust // attempt succeeds, return a pointer to the start of that space and adjust
@ -262,27 +118,22 @@ class JS_PUBLIC_API Sprinter final : public GenericPrinter {
// Puts |len| characters from |s| at the current position and // Puts |len| characters from |s| at the current position and
// return true on success, false on failure. // return true on success, false on failure.
virtual void put(const char* s, size_t len) override; virtual bool put(const char* s, size_t len) override;
using GenericPrinter::put; // pick up |inline bool put(const char* s);| using GenericPrinter::put; // pick up |inline bool put(const char* s);|
virtual bool canPutFromIndex() const override { return true; } // Format the given format/arguments as if by JS_vsmprintf, then put it.
virtual void putFromIndex(size_t index, size_t length) override { // Return true on success, else return false and report an error (typically
MOZ_ASSERT(index <= this->index()); // OOM).
MOZ_ASSERT(index + length <= this->index()); [[nodiscard]] bool jsprintf(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3);
put(base + index, length);
}
virtual size_t index() const override { return length(); }
virtual void putString(JSContext* cx, JSString* str) override; bool putString(JSString* str);
size_t length() const; ptrdiff_t getOffset() const;
// When an OOM has already been reported on the Sprinter, this function will // Report that a string operation failed to get the memory it requested. The
// forward this error to the JSContext given in the Sprinter initialization. // first call to this function calls JS_ReportOutOfMemory, and sets this
// // Sprinter's outOfMemory flag; subsequent calls do nothing.
// If no JSContext had been provided or the Sprinter is configured to not virtual void reportOutOfMemory() override;
// report OOM, then nothing happens.
void forwardOutOfMemory();
}; };
// Fprinter, print a string directly into a file. // Fprinter, print a string directly into a file.
@ -309,7 +160,7 @@ class JS_PUBLIC_API Fprinter final : public GenericPrinter {
// Puts |len| characters from |s| at the current position and // Puts |len| characters from |s| at the current position and
// return true on success, false on failure. // return true on success, false on failure.
virtual void put(const char* s, size_t len) override; virtual bool put(const char* s, size_t len) override;
using GenericPrinter::put; // pick up |inline bool put(const char* s);| using GenericPrinter::put; // pick up |inline bool put(const char* s);|
}; };
@ -345,102 +196,10 @@ class JS_PUBLIC_API LSprinter final : public GenericPrinter {
// Puts |len| characters from |s| at the current position and // Puts |len| characters from |s| at the current position and
// return true on success, false on failure. // return true on success, false on failure.
virtual void put(const char* s, size_t len) override; virtual bool put(const char* s, size_t len) override;
using GenericPrinter::put; // pick up |inline bool put(const char* s);| using GenericPrinter::put; // pick up |inline bool put(const char* s);|
}; };
// Escaping printers work like any other printer except that any added character
// are checked for escaping sequences. This one would escape a string such that
// it can safely be embedded in a JS string.
template <typename Delegate, typename Escape>
class JS_PUBLIC_API EscapePrinter final : public GenericPrinter {
size_t lengthOfSafeChars(const char* s, size_t len) {
for (size_t i = 0; i < len; i++) {
if (!esc.isSafeChar(s[i])) {
return i;
}
}
return len;
}
private:
Delegate& out;
Escape& esc;
public:
EscapePrinter(Delegate& out, Escape& esc) : out(out), esc(esc) {}
~EscapePrinter() {}
using GenericPrinter::put;
void put(const char* s, size_t len) override {
const char* b = s;
while (len) {
size_t index = lengthOfSafeChars(b, len);
if (index) {
out.put(b, index);
len -= index;
b += index;
}
if (len) {
esc.convertInto(out, char16_t(*b));
len -= 1;
b += 1;
}
}
}
inline void putChar(const char c) override {
if (esc.isSafeChar(char16_t(c))) {
out.putChar(char(c));
return;
}
esc.convertInto(out, char16_t(c));
}
inline void putChar(const JS::Latin1Char c) override {
if (esc.isSafeChar(char16_t(c))) {
out.putChar(char(c));
return;
}
esc.convertInto(out, char16_t(c));
}
inline void putChar(const char16_t c) override {
if (esc.isSafeChar(c)) {
out.putChar(char(c));
return;
}
esc.convertInto(out, c);
}
// Forward calls to delegated printer.
bool canPutFromIndex() const override { return out.canPutFromIndex(); }
void putFromIndex(size_t index, size_t length) final {
out.putFromIndex(index, length);
}
size_t index() const final { return out.index(); }
void flush() final { out.flush(); }
void reportOutOfMemory() final { out.reportOutOfMemory(); }
bool hadOutOfMemory() const final { return out.hadOutOfMemory(); }
};
class JS_PUBLIC_API JSONEscape {
public:
bool isSafeChar(char16_t c);
void convertInto(GenericPrinter& out, char16_t c);
};
class JS_PUBLIC_API StringEscape {
private:
const char quote = '\0';
public:
explicit StringEscape(const char quote = '\0') : quote(quote) {}
bool isSafeChar(char16_t c);
void convertInto(GenericPrinter& out, char16_t c);
};
// Map escaped code to the letter/symbol escaped with a backslash. // Map escaped code to the letter/symbol escaped with a backslash.
extern const char js_EscapeMap[]; extern const char js_EscapeMap[];
@ -454,18 +213,18 @@ extern JS_PUBLIC_API JS::UniqueChars QuoteString(JSContext* cx, JSString* str,
// Appends the quoted string to the given Sprinter. Follows the same semantics // Appends the quoted string to the given Sprinter. Follows the same semantics
// as QuoteString from above. // as QuoteString from above.
extern JS_PUBLIC_API void QuoteString(Sprinter* sp, JSString* str, extern JS_PUBLIC_API bool QuoteString(Sprinter* sp, JSString* str,
char quote = '\0'); char quote = '\0');
// Appends the quoted string to the given Sprinter. Follows the same // Appends the quoted string to the given Sprinter. Follows the same
// Appends the JSON quoted string to the given Sprinter. // Appends the JSON quoted string to the given Sprinter.
extern JS_PUBLIC_API void JSONQuoteString(Sprinter* sp, JSString* str); extern JS_PUBLIC_API bool JSONQuoteString(Sprinter* sp, JSString* str);
// Internal implementation code for QuoteString methods above. // Internal implementation code for QuoteString methods above.
enum class QuoteTarget { String, JSON }; enum class QuoteTarget { String, JSON };
template <QuoteTarget target, typename CharT> template <QuoteTarget target, typename CharT>
void JS_PUBLIC_API QuoteString(Sprinter* sp, bool JS_PUBLIC_API QuoteString(Sprinter* sp,
const mozilla::Range<const CharT> chars, const mozilla::Range<const CharT> chars,
char quote = '\0'); char quote = '\0');

Просмотреть файл

@ -1653,11 +1653,12 @@ static bool DisassembleNative(JSContext* cx, unsigned argc, Value* vp) {
uint8_t* jit_end = nullptr; uint8_t* jit_end = nullptr;
if (fun->isAsmJSNative() || fun->isWasmWithJitEntry()) { if (fun->isAsmJSNative() || fun->isWasmWithJitEntry()) {
if (fun->isAsmJSNative()) { if (fun->isAsmJSNative() && !sprinter.jsprintf("; backend=asmjs\n")) {
return false;
}
if (!sprinter.jsprintf("; backend=wasm\n")) {
return false; return false;
} }
sprinter.printf("; backend=asmjs\n");
sprinter.printf("; backend=wasm\n");
js::wasm::Instance& inst = fun->wasmInstance(); js::wasm::Instance& inst = fun->wasmInstance();
const js::wasm::Code& code = inst.code(); const js::wasm::Code& code = inst.code();
@ -1683,11 +1684,17 @@ static bool DisassembleNative(JSContext* cx, unsigned argc, Value* vp) {
js::jit::BaselineScript* baseline = js::jit::BaselineScript* baseline =
script->hasBaselineScript() ? script->baselineScript() : nullptr; script->hasBaselineScript() ? script->baselineScript() : nullptr;
if (ion && ion->method()) { if (ion && ion->method()) {
sprinter.printf("; backend=ion\n"); if (!sprinter.jsprintf("; backend=ion\n")) {
return false;
}
jit_begin = ion->method()->raw(); jit_begin = ion->method()->raw();
jit_end = ion->method()->rawEnd(); jit_end = ion->method()->rawEnd();
} else if (baseline) { } else if (baseline) {
sprinter.printf("; backend=baseline\n"); if (!sprinter.jsprintf("; backend=baseline\n")) {
return false;
}
jit_begin = baseline->method()->raw(); jit_begin = baseline->method()->raw();
jit_end = baseline->method()->rawEnd(); jit_end = baseline->method()->rawEnd();
} }
@ -1714,7 +1721,7 @@ static bool DisassembleNative(JSContext* cx, unsigned argc, Value* vp) {
ReportOutOfMemory(cx); ReportOutOfMemory(cx);
return false; return false;
} }
sprinter.putString(cx, sresult); sprinter.putString(sresult);
if (args.length() > 1 && args[1].isString()) { if (args.length() > 1 && args[1].isString()) {
RootedString str(cx, args[1].toString()); RootedString str(cx, args[1].toString());
@ -1742,7 +1749,7 @@ static bool DisassembleNative(JSContext* cx, unsigned argc, Value* vp) {
fclose(f); fclose(f);
} }
JSString* str = sprinter.releaseJS(cx); JSString* str = JS_NewStringCopyZ(cx, sprinter.string());
if (!str) { if (!str) {
return false; return false;
} }
@ -6906,7 +6913,11 @@ static bool GetStringRepresentation(JSContext* cx, unsigned argc, Value* vp) {
} }
str->dumpRepresentation(out, 0); str->dumpRepresentation(out, 0);
JSString* rep = out.releaseJS(cx); if (out.hadOutOfMemory()) {
return false;
}
JSString* rep = JS_NewStringCopyN(cx, out.string(), out.getOffset());
if (!rep) { if (!rep) {
return false; return false;
} }

Просмотреть файл

@ -1004,7 +1004,9 @@ UniqueChars ToPrintableStringImpl(mozilla::Range<CharT> str,
if (!sprinter.init()) { if (!sprinter.init()) {
return nullptr; return nullptr;
} }
QuoteString<QuoteTarget::String>(&sprinter, str, quote); if (!QuoteString<QuoteTarget::String>(&sprinter, str, quote)) {
return nullptr;
}
return sprinter.release(); return sprinter.release();
} }

Просмотреть файл

@ -909,10 +909,9 @@ void js::Nursery::printCollectionProfile(JS::GCReason reason,
stats().maybePrintProfileHeaders(); stats().maybePrintProfileHeaders();
Sprinter sprinter; Sprinter sprinter;
if (!sprinter.init()) { if (!sprinter.init() || !sprinter.put(gcstats::MinorGCProfilePrefix)) {
return; return;
} }
sprinter.put(gcstats::MinorGCProfilePrefix);
size_t pid = getpid(); size_t pid = getpid();
JSRuntime* runtime = gc->rt; JSRuntime* runtime = gc->rt;
@ -924,56 +923,55 @@ void js::Nursery::printCollectionProfile(JS::GCReason reason,
size_t dedupCount = stats().getStat(gcstats::STAT_STRINGS_DEDUPLICATED); size_t dedupCount = stats().getStat(gcstats::STAT_STRINGS_DEDUPLICATED);
#define PRINT_FIELD_VALUE(_1, _2, format, value) \ #define PRINT_FIELD_VALUE(_1, _2, format, value) \
sprinter.printf(" " format, value); if (!sprinter.jsprintf(" " format, value)) { \
return; \
}
FOR_EACH_NURSERY_PROFILE_METADATA(PRINT_FIELD_VALUE) FOR_EACH_NURSERY_PROFILE_METADATA(PRINT_FIELD_VALUE)
#undef PRINT_FIELD_VALUE #undef PRINT_FIELD_VALUE
printProfileDurations(profileDurations_, sprinter); printProfileDurations(profileDurations_, sprinter);
JS::UniqueChars str = sprinter.release(); fputs(sprinter.string(), stats().profileFile());
if (!str) {
return;
}
fputs(str.get(), stats().profileFile());
} }
void js::Nursery::printProfileHeader() { void js::Nursery::printProfileHeader() {
Sprinter sprinter; Sprinter sprinter;
if (!sprinter.init()) { if (!sprinter.init() || !sprinter.put(gcstats::MinorGCProfilePrefix)) {
return; return;
} }
sprinter.put(gcstats::MinorGCProfilePrefix);
#define PRINT_FIELD_NAME(name, width, _1, _2) \
sprinter.printf(" %-*s", width, name);
#define PRINT_FIELD_NAME(name, width, _1, _2) \
if (!sprinter.jsprintf(" %-*s", width, name)) { \
return; \
}
FOR_EACH_NURSERY_PROFILE_METADATA(PRINT_FIELD_NAME) FOR_EACH_NURSERY_PROFILE_METADATA(PRINT_FIELD_NAME)
#undef PRINT_FIELD_NAME #undef PRINT_FIELD_NAME
#define PRINT_PROFILE_NAME(_1, text) sprinter.printf(" %-6.6s", text); #define PRINT_PROFILE_NAME(_1, text) \
if (!sprinter.jsprintf(" %-6.6s", text)) { \
return; \
}
FOR_EACH_NURSERY_PROFILE_TIME(PRINT_PROFILE_NAME) FOR_EACH_NURSERY_PROFILE_TIME(PRINT_PROFILE_NAME)
#undef PRINT_PROFILE_NAME #undef PRINT_PROFILE_NAME
sprinter.put("\n"); if (!sprinter.put("\n")) {
JS::UniqueChars str = sprinter.release();
if (!str) {
return; return;
} }
fputs(str.get(), stats().profileFile());
fputs(sprinter.string(), stats().profileFile());
} }
// static // static
void js::Nursery::printProfileDurations(const ProfileDurations& times, bool js::Nursery::printProfileDurations(const ProfileDurations& times,
Sprinter& sprinter) { Sprinter& sprinter) {
for (auto time : times) { for (auto time : times) {
int64_t micros = int64_t(time.ToMicroseconds()); int64_t micros = int64_t(time.ToMicroseconds());
sprinter.printf(" %6" PRIi64, micros); if (!sprinter.jsprintf(" %6" PRIi64, micros)) {
return false;
}
} }
sprinter.put("\n"); return sprinter.put("\n");
} }
static constexpr size_t NurserySliceMetadataFormatWidth() { static constexpr size_t NurserySliceMetadataFormatWidth() {
@ -998,10 +996,9 @@ void js::Nursery::printTotalProfileTimes() {
} }
Sprinter sprinter; Sprinter sprinter;
if (!sprinter.init()) { if (!sprinter.init() || !sprinter.put(gcstats::MinorGCProfilePrefix)) {
return; return;
} }
sprinter.put(gcstats::MinorGCProfilePrefix);
size_t pid = getpid(); size_t pid = getpid();
JSRuntime* runtime = gc->rt; JSRuntime* runtime = gc->rt;
@ -1012,23 +1009,24 @@ void js::Nursery::printTotalProfileTimes() {
MOZ_ASSERT(r > 0 && r < int(sizeof(collections))); MOZ_ASSERT(r > 0 && r < int(sizeof(collections)));
#define PRINT_FIELD_VALUE(_1, _2, format, value) \ #define PRINT_FIELD_VALUE(_1, _2, format, value) \
sprinter.printf(" " format, value); if (!sprinter.jsprintf(" " format, value)) { \
return; \
}
FOR_EACH_NURSERY_PROFILE_COMMON_METADATA(PRINT_FIELD_VALUE) FOR_EACH_NURSERY_PROFILE_COMMON_METADATA(PRINT_FIELD_VALUE)
#undef PRINT_FIELD_VALUE #undef PRINT_FIELD_VALUE
// Use whole width of per-slice metadata to print total slices so the profile // Use whole width of per-slice metadata to print total slices so the profile
// totals that follow line up. // totals that follow line up.
size_t width = NurserySliceMetadataFormatWidth(); size_t width = NurserySliceMetadataFormatWidth();
sprinter.printf(" %-*s", int(width), collections); if (!sprinter.jsprintf(" %-*s", int(width), collections)) {
printProfileDurations(totalDurations_, sprinter);
JS::UniqueChars str = sprinter.release();
if (!str) {
return; return;
} }
fputs(str.get(), stats().profileFile());
if (!printProfileDurations(totalDurations_, sprinter)) {
return;
}
fputs(sprinter.string(), stats().profileFile());
} }
void js::Nursery::maybeClearProfileDurations() { void js::Nursery::maybeClearProfileDurations() {

Просмотреть файл

@ -699,7 +699,7 @@ class Nursery {
void maybeClearProfileDurations(); void maybeClearProfileDurations();
void startProfile(ProfileKey key); void startProfile(ProfileKey key);
void endProfile(ProfileKey key); void endProfile(ProfileKey key);
static void printProfileDurations(const ProfileDurations& times, static bool printProfileDurations(const ProfileDurations& times,
Sprinter& sprinter); Sprinter& sprinter);
mozilla::TimeStamp collectionStartTime() const; mozilla::TimeStamp collectionStartTime() const;

Просмотреть файл

@ -1640,29 +1640,29 @@ void Statistics::printProfileHeader() {
} }
Sprinter sprinter; Sprinter sprinter;
if (!sprinter.init()) { if (!sprinter.init() || !sprinter.put(MajorGCProfilePrefix)) {
return; return;
} }
sprinter.put(MajorGCProfilePrefix);
#define PRINT_METADATA_NAME(name, width, _1, _2) \
sprinter.printf(" %-*s", width, name);
#define PRINT_METADATA_NAME(name, width, _1, _2) \
if (!sprinter.jsprintf(" %-*s", width, name)) { \
return; \
}
FOR_EACH_GC_PROFILE_METADATA(PRINT_METADATA_NAME) FOR_EACH_GC_PROFILE_METADATA(PRINT_METADATA_NAME)
#undef PRINT_METADATA_NAME #undef PRINT_METADATA_NAME
#define PRINT_PROFILE_NAME(_1, text, _2) sprinter.printf(" %-6.6s", text); #define PRINT_PROFILE_NAME(_1, text, _2) \
if (!sprinter.jsprintf(" %-6.6s", text)) { \
return; \
}
FOR_EACH_GC_PROFILE_TIME(PRINT_PROFILE_NAME) FOR_EACH_GC_PROFILE_TIME(PRINT_PROFILE_NAME)
#undef PRINT_PROFILE_NAME #undef PRINT_PROFILE_NAME
sprinter.put("\n"); if (!sprinter.put("\n")) {
JS::UniqueChars str = sprinter.release();
if (!str) {
return; return;
} }
fputs(str.get(), profileFile());
fputs(sprinter.string(), profileFile());
} }
static TimeDuration SumAllPhaseKinds(const Statistics::PhaseKindTimes& times) { static TimeDuration SumAllPhaseKinds(const Statistics::PhaseKindTimes& times) {
@ -1681,10 +1681,9 @@ void Statistics::printSliceProfile() {
updateTotalProfileTimes(times); updateTotalProfileTimes(times);
Sprinter sprinter; Sprinter sprinter;
if (!sprinter.init()) { if (!sprinter.init() || !sprinter.put(MajorGCProfilePrefix)) {
return; return;
} }
sprinter.put(MajorGCProfilePrefix);
size_t pid = getpid(); size_t pid = getpid();
JSRuntime* runtime = gc->rt; JSRuntime* runtime = gc->rt;
@ -1696,18 +1695,17 @@ void Statistics::printSliceProfile() {
size_t realmCount = zoneStats.realmCount; size_t realmCount = zoneStats.realmCount;
#define PRINT_FIELD_VALUE(_1, _2, format, value) \ #define PRINT_FIELD_VALUE(_1, _2, format, value) \
sprinter.printf(" " format, value); if (!sprinter.jsprintf(" " format, value)) { \
return; \
}
FOR_EACH_GC_PROFILE_METADATA(PRINT_FIELD_VALUE) FOR_EACH_GC_PROFILE_METADATA(PRINT_FIELD_VALUE)
#undef PRINT_FIELD_VALUE #undef PRINT_FIELD_VALUE
printProfileTimes(times, sprinter); if (!printProfileTimes(times, sprinter)) {
JS::UniqueChars str = sprinter.release();
if (!str) {
return; return;
} }
fputs(str.get(), profileFile());
fputs(sprinter.string(), profileFile());
} }
Statistics::ProfileDurations Statistics::getProfileTimes( Statistics::ProfileDurations Statistics::getProfileTimes(
@ -1772,14 +1770,16 @@ const char* Statistics::formatBudget(const SliceData& slice) {
} }
/* static */ /* static */
void Statistics::printProfileTimes(const ProfileDurations& times, bool Statistics::printProfileTimes(const ProfileDurations& times,
Sprinter& sprinter) { Sprinter& sprinter) {
for (auto time : times) { for (auto time : times) {
int64_t millis = int64_t(time.ToMilliseconds()); int64_t millis = int64_t(time.ToMilliseconds());
sprinter.printf(" %6" PRIi64, millis); if (!sprinter.jsprintf(" %6" PRIi64, millis)) {
return false;
}
} }
sprinter.put("\n"); return sprinter.put("\n");
} }
constexpr size_t SliceMetadataFormatWidth() { constexpr size_t SliceMetadataFormatWidth() {
@ -1804,31 +1804,32 @@ void Statistics::printTotalProfileTimes() {
} }
Sprinter sprinter; Sprinter sprinter;
if (!sprinter.init()) { if (!sprinter.init() || !sprinter.put(MajorGCProfilePrefix)) {
return; return;
} }
sprinter.put(MajorGCProfilePrefix);
size_t pid = getpid(); size_t pid = getpid();
JSRuntime* runtime = gc->rt; JSRuntime* runtime = gc->rt;
#define PRINT_FIELD_VALUE(_1, _2, format, value) \ #define PRINT_FIELD_VALUE(_1, _2, format, value) \
sprinter.printf(" " format, value); if (!sprinter.jsprintf(" " format, value)) { \
return; \
}
FOR_EACH_GC_PROFILE_COMMON_METADATA(PRINT_FIELD_VALUE) FOR_EACH_GC_PROFILE_COMMON_METADATA(PRINT_FIELD_VALUE)
#undef PRINT_FIELD_VALUE #undef PRINT_FIELD_VALUE
// Use whole width of per-slice metadata to print total slices so the profile // Use whole width of per-slice metadata to print total slices so the profile
// totals that follow line up. // totals that follow line up.
size_t width = SliceMetadataFormatWidth(); size_t width = SliceMetadataFormatWidth();
sprinter.printf(" %-*s", int(width), formatTotalSlices()); if (!sprinter.jsprintf(" %-*s", int(width), formatTotalSlices())) {
printProfileTimes(totalTimes_, sprinter);
JS::UniqueChars str = sprinter.release();
if (!str) {
return; return;
} }
fputs(str.get(), profileFile());
if (!printProfileTimes(totalTimes_, sprinter)) {
return;
}
fputs(sprinter.string(), profileFile());
} }
const char* Statistics::formatTotalSlices() { const char* Statistics::formatTotalSlices() {

Просмотреть файл

@ -497,7 +497,7 @@ struct Statistics {
const char* formatGCFlags(const SliceData& slice); const char* formatGCFlags(const SliceData& slice);
const char* formatBudget(const SliceData& slice); const char* formatBudget(const SliceData& slice);
const char* formatTotalSlices(); const char* formatTotalSlices();
static void printProfileTimes(const ProfileDurations& times, static bool printProfileTimes(const ProfileDurations& times,
Sprinter& sprinter); Sprinter& sprinter);
}; };

Просмотреть файл

@ -7041,8 +7041,8 @@ struct ScriptCountBlockState {
~ScriptCountBlockState() { ~ScriptCountBlockState() {
masm.setPrinter(nullptr); masm.setPrinter(nullptr);
if (JS::UniqueChars str = printer.release()) { if (!printer.hadOutOfMemory()) {
block.setCode(str.get()); block.setCode(printer.string());
} }
} }
}; };

Просмотреть файл

@ -457,12 +457,12 @@ UniqueChars LAllocation::toString() const {
break; break;
case MIRType::String: case MIRType::String:
// If a JSContext is a available, output the actual string // If a JSContext is a available, output the actual string
if (JSContext* cx = TlsContext.get()) { if (JSContext* maybeCx = TlsContext.get()) {
Sprinter spr(cx); Sprinter spr(maybeCx);
if (!spr.init()) { if (!spr.init()) {
oomUnsafe.crash("LAllocation::toString()"); oomUnsafe.crash("LAllocation::toString()");
} }
spr.putString(cx, c->toString()); spr.putString(c->toString());
buf = spr.release(); buf = spr.release();
} else { } else {
buf = JS_smprintf("string"); buf = JS_smprintf("string");

Просмотреть файл

@ -495,7 +495,7 @@ void IonPerfSpewer::recordInstruction(MacroAssembler& masm, LInstruction* ins) {
if (PerfIROpsEnabled()) { if (PerfIROpsEnabled()) {
Sprinter buf; Sprinter buf;
CHECK_RETURN(buf.init()); CHECK_RETURN(buf.init());
buf.put(LIRCodeName(op)); CHECK_RETURN(buf.put(LIRCodeName(op)));
ins->printOperands(buf); ins->printOperands(buf);
opcodeStr = buf.release(); opcodeStr = buf.release();
} }
@ -509,53 +509,53 @@ void IonPerfSpewer::recordInstruction(MacroAssembler& masm, LInstruction* ins) {
} }
#ifdef JS_JITSPEW #ifdef JS_JITSPEW
static void PrintStackValue(JSContext* cx, StackValue* stackVal, static void PrintStackValue(StackValue* stackVal, CompilerFrameInfo& frame,
CompilerFrameInfo& frame, Sprinter& buf) { Sprinter& buf) {
switch (stackVal->kind()) { switch (stackVal->kind()) {
/****** Constant ******/ /****** Constant ******/
case StackValue::Constant: { case StackValue::Constant: {
js::Value constantVal = stackVal->constant(); js::Value constantVal = stackVal->constant();
if (constantVal.isInt32()) { if (constantVal.isInt32()) {
buf.printf("%d", constantVal.toInt32()); CHECK_RETURN(buf.jsprintf("%d", constantVal.toInt32()));
} else if (constantVal.isObjectOrNull()) { } else if (constantVal.isObjectOrNull()) {
buf.printf("obj:%p", constantVal.toObjectOrNull()); CHECK_RETURN(buf.jsprintf("obj:%p", constantVal.toObjectOrNull()));
} else if (constantVal.isString()) { } else if (constantVal.isString()) {
buf.put("str:"); CHECK_RETURN(buf.put("str:"));
buf.putString(cx, constantVal.toString()); CHECK_RETURN(buf.putString(constantVal.toString()));
} else if (constantVal.isNumber()) { } else if (constantVal.isNumber()) {
buf.printf("num:%f", constantVal.toNumber()); CHECK_RETURN(buf.jsprintf("num:%f", constantVal.toNumber()));
} else if (constantVal.isSymbol()) { } else if (constantVal.isSymbol()) {
buf.put("sym:"); CHECK_RETURN(buf.put("sym:"));
constantVal.toSymbol()->dump(buf); constantVal.toSymbol()->dump(buf);
} else { } else {
buf.printf("raw:%" PRIx64, constantVal.asRawBits()); CHECK_RETURN(buf.jsprintf("raw:%" PRIx64, constantVal.asRawBits()));
} }
} break; } break;
/****** Register ******/ /****** Register ******/
case StackValue::Register: { case StackValue::Register: {
Register reg = stackVal->reg().payloadOrValueReg(); Register reg = stackVal->reg().payloadOrValueReg();
buf.put(reg.name()); CHECK_RETURN(buf.put(reg.name()));
} break; } break;
/****** Stack ******/ /****** Stack ******/
case StackValue::Stack: case StackValue::Stack:
buf.put("stack"); CHECK_RETURN(buf.put("stack"));
break; break;
/****** ThisSlot ******/ /****** ThisSlot ******/
case StackValue::ThisSlot: { case StackValue::ThisSlot: {
# ifdef JS_HAS_HIDDEN_SP # ifdef JS_HAS_HIDDEN_SP
buf.put("this"); CHECK_RETURN(buf.put("this"));
# else # else
Address addr = frame.addressOfThis(); Address addr = frame.addressOfThis();
buf.printf("this:%s(%d)", addr.base.name(), addr.offset); CHECK_RETURN(buf.jsprintf("this:%s(%d)", addr.base.name(), addr.offset));
# endif # endif
} break; } break;
/****** LocalSlot ******/ /****** LocalSlot ******/
case StackValue::LocalSlot: case StackValue::LocalSlot:
buf.printf("local:%u", stackVal->localSlot()); CHECK_RETURN(buf.jsprintf("local:%u", stackVal->localSlot()));
break; break;
/****** ArgSlot ******/ /****** ArgSlot ******/
case StackValue::ArgSlot: case StackValue::ArgSlot:
buf.printf("arg:%u", stackVal->argSlot()); CHECK_RETURN(buf.jsprintf("arg:%u", stackVal->argSlot()));
break; break;
default: default:
@ -582,7 +582,7 @@ void BaselinePerfSpewer::recordInstruction(JSContext* cx, MacroAssembler& masm,
Sprinter buf(cx); Sprinter buf(cx);
CHECK_RETURN(buf.init()); CHECK_RETURN(buf.init());
buf.put(js::CodeName(op)); CHECK_RETURN(buf.put(js::CodeName(op)));
switch (op) { switch (op) {
case JSOp::SetName: case JSOp::SetName:
@ -593,8 +593,8 @@ void BaselinePerfSpewer::recordInstruction(JSContext* cx, MacroAssembler& masm,
case JSOp::GetGName: { case JSOp::GetGName: {
// Emit the name used for these ops // Emit the name used for these ops
Rooted<PropertyName*> name(cx, script->getName(pc)); Rooted<PropertyName*> name(cx, script->getName(pc));
buf.put(" "); CHECK_RETURN(buf.put(" "));
buf.putString(cx, name); CHECK_RETURN(buf.putString(name));
} break; } break;
default: default:
break; break;
@ -602,14 +602,14 @@ void BaselinePerfSpewer::recordInstruction(JSContext* cx, MacroAssembler& masm,
// Output should be "JSOp (operand1), (operand2), ..." // Output should be "JSOp (operand1), (operand2), ..."
for (unsigned i = 1; i <= numOperands; i++) { for (unsigned i = 1; i <= numOperands; i++) {
buf.put(" ("); CHECK_RETURN(buf.put(" ("));
StackValue* stackVal = frame.peek(-int(i)); StackValue* stackVal = frame.peek(-int(i));
PrintStackValue(cx, stackVal, frame, buf); PrintStackValue(stackVal, frame, buf);
if (i < numOperands) { if (i < numOperands) {
buf.put("),"); CHECK_RETURN(buf.put("),"));
} else { } else {
buf.put(")"); CHECK_RETURN(buf.put(")"));
} }
} }
opcodeStr = buf.release(); opcodeStr = buf.release();

Просмотреть файл

@ -1959,12 +1959,8 @@ bool RangeAnalysis::analyzeLoop(MBasicBlock* header) {
return false; return false;
} }
iterationBound->boundSum.dump(sp); iterationBound->boundSum.dump(sp);
JS::UniqueChars str = sp.release();
if (!str) {
return false;
}
JitSpew(JitSpew_Range, "computed symbolic bound on backedges: %s", JitSpew(JitSpew_Range, "computed symbolic bound on backedges: %s",
str.get()); sp.string());
} }
#endif #endif

Просмотреть файл

@ -3407,7 +3407,7 @@ static bool Notes(JSContext* cx, unsigned argc, Value* vp) {
} }
} }
JSString* str = sprinter.releaseJS(cx); JSString* str = JS_NewStringCopyZ(cx, sprinter.string());
if (!str) { if (!str) {
return false; return false;
} }
@ -3489,7 +3489,7 @@ static bool DisassembleToSprinter(JSContext* cx, unsigned argc, Value* vp,
} }
} }
return true; return !sprinter->hadOutOfMemory();
} }
static bool DisassembleToString(JSContext* cx, unsigned argc, Value* vp) { static bool DisassembleToString(JSContext* cx, unsigned argc, Value* vp) {
@ -3502,7 +3502,16 @@ static bool DisassembleToString(JSContext* cx, unsigned argc, Value* vp) {
return false; return false;
} }
JSString* str = sprinter.releaseJS(cx); const char* chars = sprinter.string();
size_t len;
JS::UniqueTwoByteChars buf(
JS::LossyUTF8CharsToNewTwoByteCharsZ(
cx, JS::UTF8Chars(chars, strlen(chars)), &len, js::MallocArena)
.get());
if (!buf) {
return false;
}
JSString* str = JS_NewUCStringCopyN(cx, buf.get(), len);
if (!str) { if (!str) {
return false; return false;
} }
@ -3526,11 +3535,7 @@ static bool Disassemble(JSContext* cx, unsigned argc, Value* vp) {
return false; return false;
} }
JS::UniqueChars str = sprinter.release(); fprintf(gOutFile->fp, "%s\n", sprinter.string());
if (!str) {
return false;
}
fprintf(gOutFile->fp, "%s\n", str.get());
args.rval().setUndefined(); args.rval().setUndefined();
return true; return true;
} }
@ -3588,11 +3593,7 @@ static bool DisassFile(JSContext* cx, unsigned argc, Value* vp) {
return false; return false;
} }
JS::UniqueChars chars = sprinter.release(); fprintf(gOutFile->fp, "%s\n", sprinter.string());
if (!chars) {
return false;
}
fprintf(gOutFile->fp, "%s\n", chars.get());
args.rval().setUndefined(); args.rval().setUndefined();
return true; return true;
@ -3655,11 +3656,15 @@ static bool DisassWithSrc(JSContext* cx, unsigned argc, Value* vp) {
if (line2 < line1) { if (line2 < line1) {
if (bupline != line2) { if (bupline != line2) {
bupline = line2; bupline = line2;
sprinter.printf("%s %3u: BACKUP\n", sep, line2); if (!sprinter.jsprintf("%s %3u: BACKUP\n", sep, line2)) {
return false;
}
} }
} else { } else {
if (bupline && line1 == line2) { if (bupline && line1 == line2) {
sprinter.printf("%s %3u: RESTORE\n", sep, line2); if (!sprinter.jsprintf("%s %3u: RESTORE\n", sep, line2)) {
return false;
}
} }
bupline = 0; bupline = 0;
while (line1 < line2) { while (line1 < line2) {
@ -3669,7 +3674,9 @@ static bool DisassWithSrc(JSContext* cx, unsigned argc, Value* vp) {
return false; return false;
} }
line1++; line1++;
sprinter.printf("%s %3u: %s", sep, line1, linebuf); if (!sprinter.jsprintf("%s %3u: %s", sep, line1, linebuf)) {
return false;
}
} }
} }
@ -3682,11 +3689,7 @@ static bool DisassWithSrc(JSContext* cx, unsigned argc, Value* vp) {
pc += len; pc += len;
} }
JS::UniqueChars str = sprinter.release(); fprintf(gOutFile->fp, "%s\n", sprinter.string());
if (!str) {
return false;
}
fprintf(gOutFile->fp, "%s\n", str.get());
} }
args.rval().setUndefined(); args.rval().setUndefined();

Просмотреть файл

@ -285,11 +285,17 @@ static bool FormatFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp,
if (!funbytes) { if (!funbytes) {
return false; return false;
} }
sp.printf("%d %s(", num, funbytes.get()); if (!sp.printf("%d %s(", num, funbytes.get())) {
return false;
}
} else if (fun) { } else if (fun) {
sp.printf("%d anonymous(", num); if (!sp.printf("%d anonymous(", num)) {
return false;
}
} else { } else {
sp.printf("%d <TOP LEVEL>", num); if (!sp.printf("%d <TOP LEVEL>", num)) {
return false;
}
} }
if (showArgs && iter.hasArgs()) { if (showArgs && iter.hasArgs()) {
@ -340,23 +346,28 @@ static bool FormatFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp,
} }
if (value) { if (value) {
sp.printf("%s%s%s%s%s%s", !first ? ", " : "", name ? name : "", if (!sp.printf("%s%s%s%s%s%s", !first ? ", " : "", name ? name : "",
name ? " = " : "", arg.isString() ? "\"" : "", value, name ? " = " : "", arg.isString() ? "\"" : "", value,
arg.isString() ? "\"" : ""); arg.isString() ? "\"" : "")) {
return false;
}
first = false; first = false;
} else { } else {
sp.put( if (!sp.put(" <Failed to get argument while inspecting stack "
" <Failed to get argument while inspecting stack " "frame>\n")) {
"frame>\n"); return false;
}
} }
} }
} }
// print filename, line number and column // print filename, line number and column
sp.printf("%s [\"%s\":%u:%u]\n", fun ? ")" : "", if (!sp.printf("%s [\"%s\":%u:%u]\n", fun ? ")" : "",
filename ? filename : "<unknown>", lineno, filename ? filename : "<unknown>", lineno,
column.zeroOriginValue()); column.zeroOriginValue())) {
return false;
}
// Note: Right now we don't dump the local variables anymore, because // Note: Right now we don't dump the local variables anymore, because
// that is hard to support across all the JITs etc. // that is hard to support across all the JITs etc.
@ -376,9 +387,13 @@ static bool FormatFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp,
if (!thisValBytes) { if (!thisValBytes) {
return false; return false;
} }
sp.printf(" this = %s\n", thisValBytes.get()); if (!sp.printf(" this = %s\n", thisValBytes.get())) {
return false;
}
} else { } else {
sp.put(" <failed to get 'this' value>\n"); if (!sp.put(" <failed to get 'this' value>\n")) {
return false;
}
} }
} }
} }
@ -404,9 +419,10 @@ static bool FormatFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp,
return false; return false;
} }
cx->clearPendingException(); cx->clearPendingException();
sp.put( if (!sp.put(" <Failed to fetch property while inspecting stack "
" <Failed to fetch property while inspecting stack " "frame>\n")) {
"frame>\n"); return false;
}
continue; continue;
} }
@ -429,12 +445,15 @@ static bool FormatFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp,
} }
if (name && value) { if (name && value) {
sp.printf(" this.%s = %s%s%s\n", name, v.isString() ? "\"" : "", if (!sp.printf(" this.%s = %s%s%s\n", name, v.isString() ? "\"" : "",
value, v.isString() ? "\"" : ""); value, v.isString() ? "\"" : "")) {
return false;
}
} else { } else {
sp.put( if (!sp.put(" <Failed to format values while inspecting stack "
" <Failed to format values while inspecting stack " "frame>\n")) {
"frame>\n"); return false;
}
} }
} }
} }
@ -453,10 +472,15 @@ static bool FormatWasmFrame(JSContext* cx, const FrameIter& iter, Sprinter& sp,
} }
} }
sp.printf("%d %s()", num, nameStr ? nameStr.get() : "<wasm-function>"); if (!sp.printf("%d %s()", num, nameStr ? nameStr.get() : "<wasm-function>")) {
sp.printf(" [\"%s\":wasm-function[%u]:0x%x]\n", return false;
iter.filename() ? iter.filename() : "<unknown>", }
iter.wasmFuncIndex(), iter.wasmBytecodeOffset());
if (!sp.printf(" [\"%s\":wasm-function[%u]:0x%x]\n",
iter.filename() ? iter.filename() : "<unknown>",
iter.wasmFuncIndex(), iter.wasmBytecodeOffset())) {
return false;
}
MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(!cx->isExceptionPending());
return true; return true;
@ -482,7 +506,9 @@ JS::UniqueChars JS::FormatStackDump(JSContext* cx, bool showArgs,
} }
if (num == 0) { if (num == 0) {
sp.put("JavaScript stack is empty\n"); if (!sp.put("JavaScript stack is empty\n")) {
return nullptr;
}
} }
return sp.release(); return sp.release();

Просмотреть файл

@ -368,7 +368,9 @@ size_t js::PutEscapedStringImpl(char* buffer, size_t bufferSize,
buffer = nullptr; buffer = nullptr;
} }
} else if (out) { } else if (out) {
out->put(&c, 1); if (!out->put(&c, 1)) {
return size_t(-1);
}
} }
n++; n++;
} }

Просмотреть файл

@ -104,7 +104,9 @@ static bool DecompileArgumentFromStack(JSContext* cx, int formalIndex,
[[nodiscard]] static bool DumpIonScriptCounts(Sprinter* sp, HandleScript script, [[nodiscard]] static bool DumpIonScriptCounts(Sprinter* sp, HandleScript script,
jit::IonScriptCounts* ionCounts) { jit::IonScriptCounts* ionCounts) {
sp->printf("IonScript [%zu blocks]:\n", ionCounts->numBlocks()); if (!sp->jsprintf("IonScript [%zu blocks]:\n", ionCounts->numBlocks())) {
return false;
}
for (size_t i = 0; i < ionCounts->numBlocks(); i++) { for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
const jit::IonBlockCounts& block = ionCounts->block(i); const jit::IonBlockCounts& block = ionCounts->block(i);
@ -112,16 +114,27 @@ static bool DecompileArgumentFromStack(JSContext* cx, int formalIndex,
JS::LimitedColumnNumberZeroOrigin columnNumber; JS::LimitedColumnNumberZeroOrigin columnNumber;
lineNumber = PCToLineNumber(script, script->offsetToPC(block.offset()), lineNumber = PCToLineNumber(script, script->offsetToPC(block.offset()),
&columnNumber); &columnNumber);
sp->printf("BB #%" PRIu32 " [%05u,%u,%u]", block.id(), block.offset(), if (!sp->jsprintf("BB #%" PRIu32 " [%05u,%u,%u]", block.id(),
lineNumber, columnNumber.zeroOriginValue()); block.offset(), lineNumber,
columnNumber.zeroOriginValue())) {
return false;
}
if (block.description()) { if (block.description()) {
sp->printf(" [inlined %s]", block.description()); if (!sp->jsprintf(" [inlined %s]", block.description())) {
return false;
}
} }
for (size_t j = 0; j < block.numSuccessors(); j++) { for (size_t j = 0; j < block.numSuccessors(); j++) {
sp->printf(" -> #%" PRIu32, block.successor(j)); if (!sp->jsprintf(" -> #%" PRIu32, block.successor(j))) {
return false;
}
}
if (!sp->jsprintf(" :: %" PRIu64 " hits\n", block.hitCount())) {
return false;
}
if (!sp->jsprintf("%s\n", block.code())) {
return false;
} }
sp->printf(" :: %" PRIu64 " hits\n", block.hitCount());
sp->printf("%s\n", block.code());
} }
return true; return true;
@ -143,13 +156,19 @@ static bool DecompileArgumentFromStack(JSContext* cx, int formalIndex,
return false; return false;
} }
sp->put(" {"); if (!sp->put(" {")) {
return false;
}
PCCounts* counts = script->maybeGetPCCounts(pc); PCCounts* counts = script->maybeGetPCCounts(pc);
if (double val = counts ? counts->numExec() : 0.0) { if (double val = counts ? counts->numExec() : 0.0) {
sp->printf("\"%s\": %.0f", PCCounts::numExecName, val); if (!sp->jsprintf("\"%s\": %.0f", PCCounts::numExecName, val)) {
return false;
}
}
if (!sp->put("}\n")) {
return false;
} }
sp->put("}\n");
pc = next; pc = next;
} }
@ -197,11 +216,7 @@ bool js::DumpRealmPCCounts(JSContext* cx) {
if (!DumpPCCounts(cx, script, &sprinter)) { if (!DumpPCCounts(cx, script, &sprinter)) {
return false; return false;
} }
JS::UniqueChars out = sprinter.release(); fputs(sprinter.string(), stdout);
if (!out) {
return false;
}
fputs(out.get(), stdout);
fprintf(stdout, "--- END SCRIPT %s:%u ---\n", filename, script->lineno()); fprintf(stdout, "--- END SCRIPT %s:%u ---\n", filename, script->lineno());
} }
@ -1004,47 +1019,78 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
} }
if (showAll) { if (showAll) {
sp->printf("%s:%u\n", script->filename(), unsigned(script->lineno())); if (!sp->jsprintf("%s:%u\n", script->filename(),
unsigned(script->lineno()))) {
return false;
}
} }
if (pc != nullptr) { if (pc != nullptr) {
sp->put(" "); if (!sp->put(" ")) {
return false;
}
} }
if (showAll) { if (showAll) {
sp->put("sn stack "); if (!sp->put("sn stack ")) {
return false;
}
}
if (!sp->put("loc ")) {
return false;
} }
sp->put("loc ");
if (lines) { if (lines) {
sp->put("line"); if (!sp->put("line")) {
return false;
}
}
if (!sp->put(" op\n")) {
return false;
} }
sp->put(" op\n");
if (pc != nullptr) { if (pc != nullptr) {
sp->put(" "); if (!sp->put(" ")) {
return false;
}
} }
if (showAll) { if (showAll) {
sp->put("-- ----- "); if (!sp->put("-- ----- ")) {
return false;
}
}
if (!sp->put("----- ")) {
return false;
} }
sp->put("----- ");
if (lines) { if (lines) {
sp->put("----"); if (!sp->put("----")) {
return false;
}
}
if (!sp->put(" --\n")) {
return false;
} }
sp->put(" --\n");
jsbytecode* next = script->code(); jsbytecode* next = script->code();
jsbytecode* end = script->codeEnd(); jsbytecode* end = script->codeEnd();
while (next < end) { while (next < end) {
if (next == script->main()) { if (next == script->main()) {
sp->put("main:\n"); if (!sp->put("main:\n")) {
return false;
}
} }
if (pc != nullptr) { if (pc != nullptr) {
sp->put(pc == next ? "--> " : " "); if (!sp->put(pc == next ? "--> " : " ")) {
return false;
}
} }
if (showAll) { if (showAll) {
if (parser && parser->isReachable(next)) { if (parser && parser->isReachable(next)) {
sp->printf("%05u ", parser->stackDepthAtPC(next)); if (!sp->jsprintf("%05u ", parser->stackDepthAtPC(next))) {
return false;
}
} else { } else {
sp->put(" "); if (!sp->put(" ")) {
return false;
}
} }
} }
unsigned len = Disassemble1(cx, script, next, script->pcToOffset(next), unsigned len = Disassemble1(cx, script, next, script->pcToOffset(next),
@ -1077,11 +1123,7 @@ JS_PUBLIC_API bool js::DumpPC(JSContext* cx, FILE* fp) {
} }
RootedScript script(cx, iter.script()); RootedScript script(cx, iter.script());
bool ok = DisassembleAtPC(cx, script, true, iter.pc(), false, &sprinter); bool ok = DisassembleAtPC(cx, script, true, iter.pc(), false, &sprinter);
JS::UniqueChars out = sprinter.release(); fprintf(fp, "%s", sprinter.string());
if (!out) {
return false;
}
fprintf(fp, "%s", out.get());
return ok; return ok;
} }
@ -1094,11 +1136,7 @@ JS_PUBLIC_API bool js::DumpScript(JSContext* cx, JSScript* scriptArg,
} }
RootedScript script(cx, scriptArg); RootedScript script(cx, scriptArg);
bool ok = Disassemble(cx, script, true, &sprinter); bool ok = Disassemble(cx, script, true, &sprinter);
JS::UniqueChars out = sprinter.release(); fprintf(fp, "%s", sprinter.string());
if (!out) {
return false;
}
fprintf(fp, "%s", out.get());
return ok; return ok;
} }
@ -1221,9 +1259,13 @@ static bool DumpJumpOrigins(HandleScript script, jsbytecode* pc,
BytecodeParser::JumpKind kind) { BytecodeParser::JumpKind kind) {
if (!called) { if (!called) {
called = true; called = true;
sp->put("\n# "); if (!sp->put("\n# ")) {
return false;
}
} else { } else {
sp->put(", "); if (!sp->put(", ")) {
return false;
}
} }
switch (kind) { switch (kind) {
@ -1231,24 +1273,34 @@ static bool DumpJumpOrigins(HandleScript script, jsbytecode* pc,
break; break;
case BytecodeParser::JumpKind::SwitchCase: case BytecodeParser::JumpKind::SwitchCase:
sp->put("switch-case "); if (!sp->put("switch-case ")) {
return false;
}
break; break;
case BytecodeParser::JumpKind::SwitchDefault: case BytecodeParser::JumpKind::SwitchDefault:
sp->put("switch-default "); if (!sp->put("switch-default ")) {
return false;
}
break; break;
case BytecodeParser::JumpKind::TryCatch: case BytecodeParser::JumpKind::TryCatch:
sp->put("try-catch "); if (!sp->put("try-catch ")) {
return false;
}
break; break;
case BytecodeParser::JumpKind::TryFinally: case BytecodeParser::JumpKind::TryFinally:
sp->put("try-finally "); if (!sp->put("try-finally ")) {
return false;
}
break; break;
} }
sp->printf("from %s @ %05u", CodeName(JSOp(*pc)), if (!sp->jsprintf("from %s @ %05u", CodeName(JSOp(*pc)),
unsigned(script->pcToOffset(pc))); unsigned(script->pcToOffset(pc)))) {
return false;
}
return true; return true;
}; };
@ -1256,7 +1308,9 @@ static bool DumpJumpOrigins(HandleScript script, jsbytecode* pc,
return false; return false;
} }
if (called) { if (called) {
sp->put("\n"); if (!sp->put("\n")) {
return false;
}
} }
return true; return true;
@ -1277,7 +1331,9 @@ static bool PrintShapeProperties(JSContext* cx, Sprinter* sp,
} }
} }
sp->put("{"); if (!sp->put("{")) {
return false;
}
for (size_t i = props.length(); i > 0; i--) { for (size_t i = props.length(); i > 0; i--) {
PropertyKey key = props[i - 1]; PropertyKey key = props[i - 1];
@ -1287,14 +1343,17 @@ static bool PrintShapeProperties(JSContext* cx, Sprinter* sp,
ReportOutOfMemory(cx); ReportOutOfMemory(cx);
return false; return false;
} }
sp->putString(cx, str); if (!sp->putString(str)) {
return false;
}
if (i > 1) { if (i > 1) {
sp->put(", "); if (!sp->put(", ")) {
return false;
}
} }
} }
sp->put("}"); return sp->put("}");
return true;
} }
static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc, static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
@ -1306,7 +1365,7 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
} }
} }
size_t before = sp->length(); size_t before = sp->stringEnd() - sp->string();
bool stackDumped = false; bool stackDumped = false;
auto dumpStack = [&cx, &script, &pc, &parser, &sp, &before, &stackDumped]() { auto dumpStack = [&cx, &script, &pc, &parser, &sp, &before, &stackDumped]() {
if (!parser) { if (!parser) {
@ -1317,24 +1376,32 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
} }
stackDumped = true; stackDumped = true;
size_t after = sp->length(); size_t after = sp->stringEnd() - sp->string();
MOZ_ASSERT(after >= before); MOZ_ASSERT(after >= before);
static const size_t stack_column = 40; static const size_t stack_column = 40;
for (size_t i = after - before; i < stack_column - 1; i++) { for (size_t i = after - before; i < stack_column - 1; i++) {
sp->put(" "); if (!sp->put(" ")) {
return false;
}
} }
sp->put(" # "); if (!sp->put(" # ")) {
return false;
}
if (!parser->isReachable(pc)) { if (!parser->isReachable(pc)) {
sp->put("!!! UNREACHABLE !!!"); if (!sp->put("!!! UNREACHABLE !!!")) {
return false;
}
} else { } else {
uint32_t depth = parser->stackDepthAfterPC(pc); uint32_t depth = parser->stackDepthAfterPC(pc);
for (uint32_t i = 0; i < depth; i++) { for (uint32_t i = 0; i < depth; i++) {
if (i) { if (i) {
sp->put(" "); if (!sp->put(" ")) {
return false;
}
} }
const OffsetAndDefIndex& offsetAndDefIndex = const OffsetAndDefIndex& offsetAndDefIndex =
@ -1362,11 +1429,17 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
JSOp op = JSOp(*pc); JSOp op = JSOp(*pc);
const JSCodeSpec& cs = CodeSpec(op); const JSCodeSpec& cs = CodeSpec(op);
const unsigned len = cs.length; const unsigned len = cs.length;
sp->printf("%05u:", loc); if (!sp->jsprintf("%05u:", loc)) {
if (lines) { return 0;
sp->printf("%4u", PCToLineNumber(script, pc)); }
if (lines) {
if (!sp->jsprintf("%4u", PCToLineNumber(script, pc))) {
return 0;
}
}
if (!sp->jsprintf(" %s", CodeName(op))) {
return 0;
} }
sp->printf(" %s", CodeName(op));
int i; int i;
switch (JOF_TYPE(cs.format)) { switch (JOF_TYPE(cs.format)) {
@ -1375,7 +1448,9 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
case JOF_JUMP: { case JOF_JUMP: {
ptrdiff_t off = GET_JUMP_OFFSET(pc); ptrdiff_t off = GET_JUMP_OFFSET(pc);
sp->printf(" %u (%+d)", unsigned(loc + int(off)), int(off)); if (!sp->jsprintf(" %u (%+d)", unsigned(loc + int(off)), int(off))) {
return 0;
}
break; break;
} }
@ -1385,7 +1460,9 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
if (!ToDisassemblySource(cx, scope, &bytes)) { if (!ToDisassemblySource(cx, scope, &bytes)) {
return 0; return 0;
} }
sp->printf(" %s", bytes.get()); if (!sp->jsprintf(" %s", bytes.get())) {
return 0;
}
break; break;
} }
@ -1396,13 +1473,17 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
return 0; return 0;
} }
EnvironmentCoordinate ec(pc); EnvironmentCoordinate ec(pc);
sp->printf(" %s (hops = %u, slot = %u)", bytes.get(), ec.hops(), if (!sp->jsprintf(" %s (hops = %u, slot = %u)", bytes.get(), ec.hops(),
ec.slot()); ec.slot())) {
return 0;
}
break; break;
} }
case JOF_DEBUGCOORD: { case JOF_DEBUGCOORD: {
EnvironmentCoordinate ec(pc); EnvironmentCoordinate ec(pc);
sp->printf("(hops = %u, slot = %u)", ec.hops(), ec.slot()); if (!sp->jsprintf("(hops = %u, slot = %u)", ec.hops(), ec.slot())) {
return 0;
}
break; break;
} }
case JOF_ATOM: { case JOF_ATOM: {
@ -1411,7 +1492,9 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
if (!bytes) { if (!bytes) {
return 0; return 0;
} }
sp->printf(" %s", bytes.get()); if (!sp->jsprintf(" %s", bytes.get())) {
return 0;
}
break; break;
} }
case JOF_STRING: { case JOF_STRING: {
@ -1420,13 +1503,17 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
if (!bytes) { if (!bytes) {
return 0; return 0;
} }
sp->printf(" %s", bytes.get()); if (!sp->jsprintf(" %s", bytes.get())) {
return 0;
}
break; break;
} }
case JOF_DOUBLE: { case JOF_DOUBLE: {
double d = GET_INLINE_VALUE(pc).toDouble(); double d = GET_INLINE_VALUE(pc).toDouble();
sp->printf(" %lf", d); if (!sp->jsprintf(" %lf", d)) {
return 0;
}
break; break;
} }
@ -1436,7 +1523,9 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
if (!bytes) { if (!bytes) {
return 0; return 0;
} }
sp->printf(" %s", bytes.get()); if (!sp->jsprintf(" %s", bytes.get())) {
return 0;
}
break; break;
} }
@ -1448,14 +1537,18 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
if (!bytes) { if (!bytes) {
return 0; return 0;
} }
sp->printf(" %s", bytes.get()); if (!sp->jsprintf(" %s", bytes.get())) {
return 0;
}
} }
break; break;
} }
case JOF_SHAPE: { case JOF_SHAPE: {
SharedShape* shape = script->getShape(pc); SharedShape* shape = script->getShape(pc);
sp->put(" "); if (!sp->put(" ")) {
return 0;
}
if (!PrintShapeProperties(cx, sp, shape)) { if (!PrintShapeProperties(cx, sp, shape)) {
return 0; return 0;
} }
@ -1469,7 +1562,9 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
if (!bytes) { if (!bytes) {
return 0; return 0;
} }
sp->printf(" %s", bytes.get()); if (!sp->jsprintf(" %s", bytes.get())) {
return 0;
}
break; break;
} }
@ -1482,7 +1577,10 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
pc2 += JUMP_OFFSET_LEN; pc2 += JUMP_OFFSET_LEN;
high = GET_JUMP_OFFSET(pc2); high = GET_JUMP_OFFSET(pc2);
pc2 += JUMP_OFFSET_LEN; pc2 += JUMP_OFFSET_LEN;
sp->printf(" defaultOffset %d low %d high %d", int(off), low, high); if (!sp->jsprintf(" defaultOffset %d low %d high %d", int(off), low,
high)) {
return 0;
}
// Display stack dump before diplaying the offsets for each case. // Display stack dump before diplaying the offsets for each case.
if (!dumpStack()) { if (!dumpStack()) {
@ -1492,42 +1590,60 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
for (i = low; i <= high; i++) { for (i = low; i <= high; i++) {
off = off =
script->tableSwitchCaseOffset(pc, i - low) - script->pcToOffset(pc); script->tableSwitchCaseOffset(pc, i - low) - script->pcToOffset(pc);
sp->printf("\n\t%d: %d", i, int(off)); if (!sp->jsprintf("\n\t%d: %d", i, int(off))) {
return 0;
}
} }
break; break;
} }
case JOF_QARG: case JOF_QARG:
sp->printf(" %u", GET_ARGNO(pc)); if (!sp->jsprintf(" %u", GET_ARGNO(pc))) {
return 0;
}
break; break;
case JOF_LOCAL: case JOF_LOCAL:
sp->printf(" %u", GET_LOCALNO(pc)); if (!sp->jsprintf(" %u", GET_LOCALNO(pc))) {
return 0;
}
break; break;
case JOF_GCTHING: case JOF_GCTHING:
sp->printf(" %u", unsigned(GET_GCTHING_INDEX(pc))); if (!sp->jsprintf(" %u", unsigned(GET_GCTHING_INDEX(pc)))) {
return 0;
}
break; break;
case JOF_UINT32: case JOF_UINT32:
sp->printf(" %u", GET_UINT32(pc)); if (!sp->jsprintf(" %u", GET_UINT32(pc))) {
return 0;
}
break; break;
case JOF_ICINDEX: case JOF_ICINDEX:
sp->printf(" (ic: %u)", GET_ICINDEX(pc)); if (!sp->jsprintf(" (ic: %u)", GET_ICINDEX(pc))) {
return 0;
}
break; break;
case JOF_LOOPHEAD: case JOF_LOOPHEAD:
sp->printf(" (ic: %u, depthHint: %u)", GET_ICINDEX(pc), if (!sp->jsprintf(" (ic: %u, depthHint: %u)", GET_ICINDEX(pc),
LoopHeadDepthHint(pc)); LoopHeadDepthHint(pc))) {
return 0;
}
break; break;
case JOF_TWO_UINT8: { case JOF_TWO_UINT8: {
int one = (int)GET_UINT8(pc); int one = (int)GET_UINT8(pc);
int two = (int)GET_UINT8(pc + 1); int two = (int)GET_UINT8(pc + 1);
sp->printf(" %d", one); if (!sp->jsprintf(" %d", one)) {
sp->printf(" %d", two); return 0;
}
if (!sp->jsprintf(" %d", two)) {
return 0;
}
break; break;
} }
@ -1554,7 +1670,9 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
MOZ_ASSERT(op == JSOp::Int32); MOZ_ASSERT(op == JSOp::Int32);
i = GET_INT32(pc); i = GET_INT32(pc);
print_int: print_int:
sp->printf(" %d", i); if (!sp->jsprintf(" %d", i)) {
return 0;
}
break; break;
default: { default: {
@ -1570,7 +1688,9 @@ static unsigned Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
return 0; return 0;
} }
sp->put("\n"); if (!sp->put("\n")) {
return 0;
}
return len; return len;
} }
@ -1798,8 +1918,7 @@ bool ExpressionDecompiler::decompilePC(jsbytecode* pc, uint8_t defIndex) {
case JSOp::Uint16: case JSOp::Uint16:
case JSOp::Uint24: case JSOp::Uint24:
case JSOp::Int32: case JSOp::Int32:
sprinter.printf("%d", GetBytecodeInteger(pc)); return sprinter.printf("%d", GetBytecodeInteger(pc));
return true;
case JSOp::String: case JSOp::String:
return quote(loadString(pc), '"'); return quote(loadString(pc), '"');
case JSOp::Symbol: { case JSOp::Symbol: {
@ -1955,8 +2074,7 @@ bool ExpressionDecompiler::decompilePC(jsbytecode* pc, uint8_t defIndex) {
return write("arguments.length"); return write("arguments.length");
case JSOp::GetFrameArg: case JSOp::GetFrameArg:
sprinter.printf("arguments[%u]", GET_ARGNO(pc)); return sprinter.printf("arguments[%u]", GET_ARGNO(pc));
return true;
case JSOp::GetActualArg: case JSOp::GetActualArg:
return write("arguments[") && decompilePCForStackOperand(pc, -1) && return write("arguments[") && decompilePCForStackOperand(pc, -1) &&
@ -1979,8 +2097,7 @@ bool ExpressionDecompiler::decompilePC(jsbytecode* pc, uint8_t defIndex) {
return write("OBJ"); return write("OBJ");
case JSOp::Double: case JSOp::Double:
sprinter.printf("%lf", GET_INLINE_VALUE(pc).toDouble()); return sprinter.printf("%lf", GET_INLINE_VALUE(pc).toDouble());
return true;
case JSOp::Exception: case JSOp::Exception:
return write("EXCEPTION"); return write("EXCEPTION");
@ -2165,10 +2282,7 @@ bool ExpressionDecompiler::init() {
return sprinter.init(); return sprinter.init();
} }
bool ExpressionDecompiler::write(const char* s) { bool ExpressionDecompiler::write(const char* s) { return sprinter.put(s); }
sprinter.put(s);
return true;
}
bool ExpressionDecompiler::write(JSString* str) { bool ExpressionDecompiler::write(JSString* str) {
if (str == cx->names().dot_this_) { if (str == cx->names().dot_this_) {
@ -2177,13 +2291,11 @@ bool ExpressionDecompiler::write(JSString* str) {
if (str == cx->names().dot_newTarget_) { if (str == cx->names().dot_newTarget_) {
return write("new.target"); return write("new.target");
} }
sprinter.putString(cx, str); return sprinter.putString(str);
return true;
} }
bool ExpressionDecompiler::quote(JSString* s, char quote) { bool ExpressionDecompiler::quote(JSString* s, char quote) {
QuoteString(&sprinter, s, quote); return QuoteString(&sprinter, s, quote);
return true;
} }
JSAtom* ExpressionDecompiler::loadAtom(jsbytecode* pc) { JSAtom* ExpressionDecompiler::loadAtom(jsbytecode* pc) {
@ -2213,7 +2325,16 @@ JSAtom* ExpressionDecompiler::getArg(unsigned slot) {
MOZ_CRASH("No binding"); MOZ_CRASH("No binding");
} }
UniqueChars ExpressionDecompiler::getOutput() { return sprinter.release(); } UniqueChars ExpressionDecompiler::getOutput() {
ptrdiff_t len = sprinter.stringEnd() - sprinter.stringAt(0);
auto res = cx->make_pod_array<char>(len + 1);
if (!res) {
return nullptr;
}
js_memcpy(res.get(), sprinter.stringAt(0), len);
res[len] = 0;
return res;
}
} // anonymous namespace } // anonymous namespace
@ -2246,8 +2367,7 @@ static bool DecompileAtPCForStackDump(
return false; return false;
} }
sp->put(result.get()); return sp->put(result.get());
return true;
} }
#endif /* defined(DEBUG) || defined(JS_JITSPEW) */ #endif /* defined(DEBUG) || defined(JS_JITSPEW) */
@ -2603,7 +2723,9 @@ size_t JS::GetPCCountScriptCount(JSContext* cx) {
[[nodiscard]] static bool JSONStringProperty(Sprinter& sp, JSONPrinter& json, [[nodiscard]] static bool JSONStringProperty(Sprinter& sp, JSONPrinter& json,
const char* name, JSString* str) { const char* name, JSString* str) {
json.beginStringProperty(name); json.beginStringProperty(name);
JSONQuoteString(&sp, str); if (!JSONQuoteString(&sp, str)) {
return false;
}
json.endStringProperty(); json.endStringProperty();
return true; return true;
} }
@ -2682,7 +2804,11 @@ JSString* JS::GetPCCountScriptSummary(JSContext* cx, size_t index) {
json.endObject(); json.endObject();
return sp.releaseJS(cx); if (sp.hadOutOfMemory()) {
return nullptr;
}
return NewStringCopyZ<CanGC>(cx, sp.string());
} }
static bool GetPCCountJSON(JSContext* cx, const ScriptAndCounts& sac, static bool GetPCCountJSON(JSContext* cx, const ScriptAndCounts& sac,
@ -2842,7 +2968,11 @@ JSString* JS::GetPCCountScriptContents(JSContext* cx, size_t index) {
} }
} }
return sp.releaseJS(cx); if (sp.hadOutOfMemory()) {
return nullptr;
}
return NewStringCopyZ<CanGC>(cx, sp.string());
} }
struct CollectedScripts { struct CollectedScripts {
@ -2957,6 +3087,10 @@ static bool GenerateLcovInfo(JSContext* cx, JS::Realm* realm,
bool isEmpty = true; bool isEmpty = true;
lcovRealm->exportInto(out, &isEmpty); lcovRealm->exportInto(out, &isEmpty);
if (out.hadOutOfMemory()) {
return false;
}
return true; return true;
} }
@ -2973,8 +3107,8 @@ JS_PUBLIC_API UniqueChars js::GetCodeCoverageSummaryAll(JSContext* cx,
} }
} }
*length = out.length(); *length = out.getOffset();
return out.release(); return js::DuplicateString(cx, out.string(), *length);
} }
JS_PUBLIC_API UniqueChars js::GetCodeCoverageSummary(JSContext* cx, JS_PUBLIC_API UniqueChars js::GetCodeCoverageSummary(JSContext* cx,
@ -2988,6 +3122,6 @@ JS_PUBLIC_API UniqueChars js::GetCodeCoverageSummary(JSContext* cx,
return nullptr; return nullptr;
} }
*length = out.length(); *length = out.getOffset();
return out.release(); return js::DuplicateString(cx, out.string(), *length);
} }

Просмотреть файл

@ -4163,7 +4163,7 @@ static bool AnalyzeEntrainedVariablesInScript(JSContext* cx,
buf.printf("Script "); buf.printf("Script ");
if (JSAtom* name = script->function()->displayAtom()) { if (JSAtom* name = script->function()->displayAtom()) {
buf.putString(cx, name); buf.putString(name);
buf.printf(" "); buf.printf(" ");
} }
@ -4171,7 +4171,7 @@ static bool AnalyzeEntrainedVariablesInScript(JSContext* cx,
script->lineno()); script->lineno());
if (JSAtom* name = innerScript->function()->displayAtom()) { if (JSAtom* name = innerScript->function()->displayAtom()) {
buf.putString(cx, name); buf.putString(name);
buf.printf(" "); buf.printf(" ");
} }
@ -4180,14 +4180,10 @@ static bool AnalyzeEntrainedVariablesInScript(JSContext* cx,
for (PropertyNameSet::Range r = remainingNames.all(); !r.empty(); for (PropertyNameSet::Range r = remainingNames.all(); !r.empty();
r.popFront()) { r.popFront()) {
buf.printf(" "); buf.printf(" ");
buf.putString(cx, r.front()); buf.putString(r.front());
} }
JS::UniqueChars str = buf.release(); printf("%s\n", buf.string());
if (!str) {
return false;
}
printf("%s\n", str.get());
} }
RootedFunction fun(cx); RootedFunction fun(cx);

Просмотреть файл

@ -3247,11 +3247,7 @@ void JSScript::dump(JSContext* cx) {
return; return;
} }
JS::UniqueChars str = sp.release(); fprintf(stderr, "%s\n", sp.string());
if (!str) {
return;
}
fprintf(stderr, "%s\n", str.get());
} }
void JSScript::dumpRecursive(JSContext* cx) { void JSScript::dumpRecursive(JSContext* cx) {
@ -3269,11 +3265,7 @@ void JSScript::dumpRecursive(JSContext* cx) {
return; return;
} }
JS::UniqueChars str = sp.release(); fprintf(stderr, "%s\n", sp.string());
if (!str) {
return;
}
fprintf(stderr, "%s\n", str.get());
} }
static void DumpMutableScriptFlags(js::JSONPrinter& json, static void DumpMutableScriptFlags(js::JSONPrinter& json,
@ -3399,7 +3391,9 @@ bool JSScript::dump(JSContext* cx, JS::Handle<JSScript*> script,
return false; return false;
} }
sp->put("\n"); if (!sp->put("\n")) {
return false;
}
if (!Disassemble(cx, script, /* lines = */ true, sp)) { if (!Disassemble(cx, script, /* lines = */ true, sp)) {
return false; return false;
@ -3425,7 +3419,9 @@ bool JSScript::dump(JSContext* cx, JS::Handle<JSScript*> script,
JSObject* obj = &gcThing.as<JSObject>(); JSObject* obj = &gcThing.as<JSObject>();
if (obj->is<JSFunction>()) { if (obj->is<JSFunction>()) {
sp->put("\n"); if (!sp->put("\n")) {
return false;
}
JS::Rooted<JSFunction*> fun(cx, &obj->as<JSFunction>()); JS::Rooted<JSFunction*> fun(cx, &obj->as<JSFunction>());
if (fun->isInterpreted()) { if (fun->isInterpreted()) {
@ -3438,7 +3434,9 @@ bool JSScript::dump(JSContext* cx, JS::Handle<JSScript*> script,
return false; return false;
} }
} else { } else {
sp->put("[native code]\n"); if (!sp->put("[native code]\n")) {
return false;
}
} }
} }
} }
@ -3450,10 +3448,13 @@ bool JSScript::dump(JSContext* cx, JS::Handle<JSScript*> script,
/* static */ /* static */
bool JSScript::dumpSrcNotes(JSContext* cx, JS::Handle<JSScript*> script, bool JSScript::dumpSrcNotes(JSContext* cx, JS::Handle<JSScript*> script,
js::Sprinter* sp) { js::Sprinter* sp) {
sp->put("\nSource notes:\n"); if (!sp->put("\nSource notes:\n") ||
sp->printf("%4s %4s %6s %5s %6s %-16s %s\n", "ofs", "line", "column", "pc", !sp->jsprintf("%4s %4s %6s %5s %6s %-16s %s\n", "ofs", "line", "column",
"delta", "desc", "args"); "pc", "delta", "desc", "args") ||
sp->put("---- ---- ------ ----- ------ ---------------- ------\n"); !sp->put("---- ---- ------ ----- ------ ---------------- ------\n")) {
return false;
}
unsigned offset = 0; unsigned offset = 0;
unsigned lineno = script->lineno(); unsigned lineno = script->lineno();
JS::LimitedColumnNumberZeroOrigin column = script->column(); JS::LimitedColumnNumberZeroOrigin column = script->column();
@ -3466,8 +3467,10 @@ bool JSScript::dumpSrcNotes(JSContext* cx, JS::Handle<JSScript*> script,
offset += delta; offset += delta;
SrcNoteType type = sn->type(); SrcNoteType type = sn->type();
const char* name = sn->name(); const char* name = sn->name();
sp->printf("%3u: %4u %6u %5u [%4u] %-16s", unsigned(sn - notes), lineno, if (!sp->jsprintf("%3u: %4u %6u %5u [%4u] %-16s", unsigned(sn - notes),
column.zeroOriginValue(), offset, delta, name); lineno, column.zeroOriginValue(), offset, delta, name)) {
return false;
}
switch (type) { switch (type) {
case SrcNoteType::Breakpoint: case SrcNoteType::Breakpoint:
@ -3477,21 +3480,28 @@ bool JSScript::dumpSrcNotes(JSContext* cx, JS::Handle<JSScript*> script,
case SrcNoteType::ColSpan: { case SrcNoteType::ColSpan: {
JS::ColumnNumberOffset colspan = SrcNote::ColSpan::getSpan(sn); JS::ColumnNumberOffset colspan = SrcNote::ColSpan::getSpan(sn);
sp->printf(" colspan %u", colspan.value()); if (!sp->jsprintf(" colspan %u", colspan.value())) {
return false;
}
column += colspan; column += colspan;
break; break;
} }
case SrcNoteType::SetLine: case SrcNoteType::SetLine:
lineno = SrcNote::SetLine::getLine(sn, script->lineno()); lineno = SrcNote::SetLine::getLine(sn, script->lineno());
sp->printf(" lineno %u", lineno); if (!sp->jsprintf(" lineno %u", lineno)) {
return false;
}
column = JS::LimitedColumnNumberZeroOrigin::zero(); column = JS::LimitedColumnNumberZeroOrigin::zero();
break; break;
case SrcNoteType::SetLineColumn: case SrcNoteType::SetLineColumn:
lineno = SrcNote::SetLineColumn::getLine(sn, script->lineno()); lineno = SrcNote::SetLineColumn::getLine(sn, script->lineno());
column = SrcNote::SetLineColumn::getColumn(sn); column = SrcNote::SetLineColumn::getColumn(sn);
sp->printf(" lineno %u column %u", lineno, column.zeroOriginValue()); if (!sp->jsprintf(" lineno %u column %u", lineno,
column.zeroOriginValue())) {
return false;
}
break; break;
case SrcNoteType::NewLine: case SrcNoteType::NewLine:
@ -3501,14 +3511,19 @@ bool JSScript::dumpSrcNotes(JSContext* cx, JS::Handle<JSScript*> script,
case SrcNoteType::NewLineColumn: case SrcNoteType::NewLineColumn:
column = SrcNote::NewLineColumn::getColumn(sn); column = SrcNote::NewLineColumn::getColumn(sn);
sp->printf(" column %u", column.zeroOriginValue()); if (!sp->jsprintf(" column %u", column.zeroOriginValue())) {
return false;
}
++lineno; ++lineno;
break; break;
default: default:
MOZ_ASSERT_UNREACHABLE("unrecognized srcnote"); MOZ_ASSERT_UNREACHABLE("unrecognized srcnote");
} }
sp->put("\n"); if (!sp->put("\n")) {
return false;
}
} }
return true; return true;
@ -3538,11 +3553,16 @@ static const char* TryNoteName(TryNoteKind kind) {
/* static */ /* static */
bool JSScript::dumpTryNotes(JSContext* cx, JS::Handle<JSScript*> script, bool JSScript::dumpTryNotes(JSContext* cx, JS::Handle<JSScript*> script,
js::Sprinter* sp) { js::Sprinter* sp) {
sp->put("\nException table:\nkind stack start end\n"); if (!sp->put(
"\nException table:\nkind stack start end\n")) {
return false;
}
for (const js::TryNote& tn : script->trynotes()) { for (const js::TryNote& tn : script->trynotes()) {
sp->printf(" %-16s %6u %8u %8u\n", TryNoteName(tn.kind()), tn.stackDepth, if (!sp->jsprintf(" %-16s %6u %8u %8u\n", TryNoteName(tn.kind()),
tn.start, tn.start + tn.length); tn.stackDepth, tn.start, tn.start + tn.length)) {
return false;
}
} }
return true; return true;
} }
@ -3550,20 +3570,32 @@ bool JSScript::dumpTryNotes(JSContext* cx, JS::Handle<JSScript*> script,
/* static */ /* static */
bool JSScript::dumpScopeNotes(JSContext* cx, JS::Handle<JSScript*> script, bool JSScript::dumpScopeNotes(JSContext* cx, JS::Handle<JSScript*> script,
js::Sprinter* sp) { js::Sprinter* sp) {
sp->put("\nScope notes:\n index parent start end\n"); if (!sp->put("\nScope notes:\n index parent start end\n")) {
return false;
}
for (const ScopeNote& note : script->scopeNotes()) { for (const ScopeNote& note : script->scopeNotes()) {
if (note.index == ScopeNote::NoScopeIndex) { if (note.index == ScopeNote::NoScopeIndex) {
sp->printf("%8s ", "(none)"); if (!sp->jsprintf("%8s ", "(none)")) {
return false;
}
} else { } else {
sp->printf("%8u ", note.index.index); if (!sp->jsprintf("%8u ", note.index.index)) {
return false;
}
} }
if (note.parent == ScopeNote::NoScopeIndex) { if (note.parent == ScopeNote::NoScopeIndex) {
sp->printf("%8s ", "(none)"); if (!sp->jsprintf("%8s ", "(none)")) {
return false;
}
} else { } else {
sp->printf("%8u ", note.parent); if (!sp->jsprintf("%8u ", note.parent)) {
return false;
}
}
if (!sp->jsprintf("%8u %8u\n", note.start, note.start + note.length)) {
return false;
} }
sp->printf("%8u %8u\n", note.start, note.start + note.length);
} }
return true; return true;
} }
@ -3571,27 +3603,41 @@ bool JSScript::dumpScopeNotes(JSContext* cx, JS::Handle<JSScript*> script,
/* static */ /* static */
bool JSScript::dumpGCThings(JSContext* cx, JS::Handle<JSScript*> script, bool JSScript::dumpGCThings(JSContext* cx, JS::Handle<JSScript*> script,
js::Sprinter* sp) { js::Sprinter* sp) {
sp->put("\nGC things:\n index type value\n"); if (!sp->put("\nGC things:\n index type value\n")) {
return false;
}
size_t i = 0; size_t i = 0;
for (JS::GCCellPtr gcThing : script->gcthings()) { for (JS::GCCellPtr gcThing : script->gcthings()) {
sp->printf("%8zu ", i); if (!sp->jsprintf("%8zu ", i)) {
return false;
}
if (gcThing.is<JS::BigInt>()) { if (gcThing.is<JS::BigInt>()) {
sp->put("BigInt "); if (!sp->put("BigInt ")) {
return false;
}
gcThing.as<JS::BigInt>().dump(*sp); gcThing.as<JS::BigInt>().dump(*sp);
sp->put("\n"); if (!sp->put("\n")) {
return false;
}
} else if (gcThing.is<Scope>()) { } else if (gcThing.is<Scope>()) {
sp->put("Scope "); if (!sp->put("Scope ")) {
return false;
}
JS::Rooted<Scope*> scope(cx, &gcThing.as<Scope>()); JS::Rooted<Scope*> scope(cx, &gcThing.as<Scope>());
if (!Scope::dumpForDisassemble(cx, scope, *sp, if (!Scope::dumpForDisassemble(cx, scope, *sp,
" ")) { " ")) {
return false; return false;
} }
sp->put("\n"); if (!sp->put("\n")) {
return false;
}
} else if (gcThing.is<JSObject>()) { } else if (gcThing.is<JSObject>()) {
JSObject* obj = &gcThing.as<JSObject>(); JSObject* obj = &gcThing.as<JSObject>();
if (obj->is<JSFunction>()) { if (obj->is<JSFunction>()) {
sp->put("Function "); if (!sp->put("Function ")) {
return false;
}
JS::Rooted<JSFunction*> fun(cx, &obj->as<JSFunction>()); JS::Rooted<JSFunction*> fun(cx, &obj->as<JSFunction>());
if (fun->displayAtom()) { if (fun->displayAtom()) {
JS::Rooted<JSAtom*> name(cx, fun->displayAtom()); JS::Rooted<JSAtom*> name(cx, fun->displayAtom());
@ -3599,23 +3645,35 @@ bool JSScript::dumpGCThings(JSContext* cx, JS::Handle<JSScript*> script,
if (!utf8chars) { if (!utf8chars) {
return false; return false;
} }
sp->put(utf8chars.get()); if (!sp->put(utf8chars.get())) {
return false;
}
} else { } else {
sp->put("(anonymous)"); if (!sp->put("(anonymous)")) {
return false;
}
} }
if (fun->hasBaseScript()) { if (fun->hasBaseScript()) {
BaseScript* script = fun->baseScript(); BaseScript* script = fun->baseScript();
sp->printf(" @ %u:%u\n", script->lineno(), if (!sp->jsprintf(" @ %u:%u\n", script->lineno(),
script->column().zeroOriginValue()); script->column().zeroOriginValue())) {
return false;
}
} else { } else {
sp->put(" (no script)\n"); if (!sp->put(" (no script)\n")) {
return false;
}
} }
} else { } else {
if (obj->is<RegExpObject>()) { if (obj->is<RegExpObject>()) {
sp->put("RegExp "); if (!sp->put("RegExp ")) {
return false;
}
} else { } else {
sp->put("Object "); if (!sp->put("Object ")) {
return false;
}
} }
JS::Rooted<JS::Value> objValue(cx, ObjectValue(*obj)); JS::Rooted<JS::Value> objValue(cx, ObjectValue(*obj));
@ -3627,24 +3685,39 @@ bool JSScript::dumpGCThings(JSContext* cx, JS::Handle<JSScript*> script,
if (!utf8chars) { if (!utf8chars) {
return false; return false;
} }
sp->put(utf8chars.get()); if (!sp->put(utf8chars.get())) {
sp->put("\n"); return false;
}
if (!sp->put("\n")) {
return false;
}
} }
} else if (gcThing.is<JSString>()) { } else if (gcThing.is<JSString>()) {
JS::Rooted<JSString*> str(cx, &gcThing.as<JSString>()); JS::Rooted<JSString*> str(cx, &gcThing.as<JSString>());
if (str->isAtom()) { if (str->isAtom()) {
sp->put("Atom "); if (!sp->put("Atom ")) {
return false;
}
} else { } else {
sp->put("String "); if (!sp->put("String ")) {
return false;
}
} }
JS::UniqueChars chars = QuoteString(cx, str, '"'); JS::UniqueChars chars = QuoteString(cx, str, '"');
if (!chars) { if (!chars) {
return false; return false;
} }
sp->put(chars.get()); if (!sp->put(chars.get())) {
sp->put("\n"); return false;
}
if (!sp->put("\n")) {
return false;
}
} else { } else {
sp->put("Unknown\n"); if (!sp->put("Unknown\n")) {
return false;
}
} }
i++; i++;
} }

Просмотреть файл

@ -29,8 +29,7 @@ class GenericPrinterPrintfTarget : public mozilla::PrintfTarget {
explicit GenericPrinterPrintfTarget(js::GenericPrinter& p) : printer(p) {} explicit GenericPrinterPrintfTarget(js::GenericPrinter& p) : printer(p) {}
bool append(const char* sp, size_t len) override { bool append(const char* sp, size_t len) override {
printer.put(sp, len); return printer.put(sp, len);
return true;
} }
private: private:
@ -48,64 +47,32 @@ void GenericPrinter::reportOutOfMemory() {
hadOOM_ = true; hadOOM_ = true;
} }
void GenericPrinter::put(mozilla::Span<const JS::Latin1Char> str) { bool GenericPrinter::printf(const char* fmt, ...) {
if (!str.Length()) {
return;
}
put(reinterpret_cast<const char*>(&str[0]), str.Length());
}
void GenericPrinter::put(mozilla::Span<const char16_t> str) {
for (char16_t c : str) {
putChar(c);
}
}
void GenericPrinter::putString(JSContext* cx, JSString* str) {
StringSegmentRange iter(cx);
if (!iter.init(str)) {
reportOutOfMemory();
return;
}
JS::AutoCheckCannotGC nogc;
while (!iter.empty()) {
JSLinearString* linear = iter.front();
if (linear->hasLatin1Chars()) {
put(linear->latin1Range(nogc));
} else {
put(linear->twoByteRange(nogc));
}
if (!iter.popFront()) {
reportOutOfMemory();
return;
}
}
}
void GenericPrinter::printf(const char* fmt, ...) {
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
vprintf(fmt, va); bool r = vprintf(fmt, va);
va_end(va); va_end(va);
return r;
} }
void GenericPrinter::vprintf(const char* fmt, va_list ap) { bool GenericPrinter::vprintf(const char* fmt, va_list ap) {
// Simple shortcut to avoid allocating strings. // Simple shortcut to avoid allocating strings.
if (strchr(fmt, '%') == nullptr) { if (strchr(fmt, '%') == nullptr) {
put(fmt); return put(fmt);
} }
GenericPrinterPrintfTarget printer(*this); GenericPrinterPrintfTarget printer(*this);
(void)printer.vprint(fmt, ap); if (!printer.vprint(fmt, ap)) {
reportOutOfMemory();
return false;
}
return true;
} }
const size_t Sprinter::DefaultSize = 64; const size_t Sprinter::DefaultSize = 64;
bool Sprinter::realloc_(size_t newSize) { bool Sprinter::realloc_(size_t newSize) {
MOZ_ASSERT(newSize > (size_t)offset); MOZ_ASSERT(newSize > (size_t)offset);
if (hadOOM_) {
return false;
}
char* newBuf = (char*)js_realloc(base, newSize); char* newBuf = (char*)js_realloc(base, newSize);
if (!newBuf) { if (!newBuf) {
reportOutOfMemory(); reportOutOfMemory();
@ -142,7 +109,6 @@ bool Sprinter::init() {
base = js_pod_malloc<char>(DefaultSize); base = js_pod_malloc<char>(DefaultSize);
if (!base) { if (!base) {
reportOutOfMemory(); reportOutOfMemory();
forwardOutOfMemory();
return false; return false;
} }
#ifdef DEBUG #ifdef DEBUG
@ -161,12 +127,11 @@ void Sprinter::checkInvariants() const {
} }
UniqueChars Sprinter::release() { UniqueChars Sprinter::release() {
checkInvariants();
if (hadOOM_) { if (hadOOM_) {
forwardOutOfMemory();
return nullptr; return nullptr;
} }
checkInvariants();
char* str = base; char* str = base;
base = nullptr; base = nullptr;
offset = size = 0; offset = size = 0;
@ -176,58 +141,16 @@ UniqueChars Sprinter::release() {
return UniqueChars(str); return UniqueChars(str);
} }
JSString* Sprinter::releaseJS(JSContext* cx) { char* Sprinter::stringAt(ptrdiff_t off) const {
if (hadOOM_) { MOZ_ASSERT(!hadOutOfMemory());
MOZ_ASSERT_IF(maybeCx, maybeCx == cx); MOZ_ASSERT(off >= 0 && (size_t)off < size);
forwardOutOfMemory(); return base + off;
return nullptr; }
}
checkInvariants(); char& Sprinter::operator[](size_t off) {
MOZ_ASSERT(!hadOutOfMemory());
// Extract Sprinter data. MOZ_ASSERT(off < size);
size_t len = length(); return *(base + off);
UniqueChars str(base);
// Reset Sprinter.
base = nullptr;
offset = 0;
size = 0;
#ifdef DEBUG
initialized = false;
#endif
// Convert extracted data to a JSString, reusing the current buffer whenever
// possible.
JS::UTF8Chars utf8(str.get(), len);
JS::SmallestEncoding encoding = JS::FindSmallestEncoding(utf8);
if (encoding == JS::SmallestEncoding::ASCII) {
UniqueLatin1Chars latin1(reinterpret_cast<JS::Latin1Char*>(str.release()));
return js::NewString<js::CanGC>(cx, std::move(latin1), len);
}
size_t length;
if (encoding == JS::SmallestEncoding::Latin1) {
UniqueLatin1Chars latin1(
UTF8CharsToNewLatin1CharsZ(cx, utf8, &length, js::StringBufferArena)
.get());
if (!latin1) {
return nullptr;
}
return js::NewString<js::CanGC>(cx, std::move(latin1), length);
}
MOZ_ASSERT(encoding == JS::SmallestEncoding::UTF16);
UniqueTwoByteChars utf16(
UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length, js::StringBufferArena)
.get());
if (!utf16) {
return nullptr;
}
return js::NewString<js::CanGC>(cx, std::move(utf16), length);
} }
char* Sprinter::reserve(size_t len) { char* Sprinter::reserve(size_t len) {
@ -244,7 +167,7 @@ char* Sprinter::reserve(size_t len) {
return sb; return sb;
} }
void Sprinter::put(const char* s, size_t len) { bool Sprinter::put(const char* s, size_t len) {
InvariantChecker ic(this); InvariantChecker ic(this);
const char* oldBase = base; const char* oldBase = base;
@ -252,36 +175,38 @@ void Sprinter::put(const char* s, size_t len) {
char* bp = reserve(len); char* bp = reserve(len);
if (!bp) { if (!bp) {
return; return false;
} }
// s is within the buffer already /* s is within the buffer already */
if (s >= oldBase && s < oldEnd) { if (s >= oldBase && s < oldEnd) {
// Update the source pointer in case of a realloc-ation. /* buffer was realloc'ed */
size_t index = s - oldBase; if (base != oldBase) {
s = &base[index]; s = stringAt(s - oldBase); /* this is where it lives now */
}
memmove(bp, s, len); memmove(bp, s, len);
} else { } else {
js_memcpy(bp, s, len); js_memcpy(bp, s, len);
} }
bp[len] = '\0'; bp[len] = '\0';
return true;
} }
void Sprinter::putString(JSContext* cx, JSString* s) { bool Sprinter::putString(JSString* s) {
MOZ_ASSERT(cx); MOZ_ASSERT(maybeCx);
InvariantChecker ic(this); InvariantChecker ic(this);
JSLinearString* linear = s->ensureLinear(cx); JSLinearString* linear = s->ensureLinear(maybeCx);
if (!linear) { if (!linear) {
return; return false;
} }
size_t length = JS::GetDeflatedUTF8StringLength(linear); size_t length = JS::GetDeflatedUTF8StringLength(linear);
char* buffer = reserve(length); char* buffer = reserve(length);
if (!buffer) { if (!buffer) {
return; return false;
} }
mozilla::DebugOnly<size_t> written = mozilla::DebugOnly<size_t> written =
@ -289,15 +214,29 @@ void Sprinter::putString(JSContext* cx, JSString* s) {
MOZ_ASSERT(written == length); MOZ_ASSERT(written == length);
buffer[length] = '\0'; buffer[length] = '\0';
return true;
} }
size_t Sprinter::length() const { return size_t(offset); } ptrdiff_t Sprinter::getOffset() const { return offset; }
void Sprinter::forwardOutOfMemory() { void Sprinter::reportOutOfMemory() {
MOZ_ASSERT(hadOOM_); if (hadOOM_) {
return;
}
if (maybeCx && shouldReportOOM) { if (maybeCx && shouldReportOOM) {
ReportOutOfMemory(maybeCx); ReportOutOfMemory(maybeCx);
} }
hadOOM_ = true;
}
bool Sprinter::jsprintf(const char* format, ...) {
va_list ap;
va_start(ap, format);
bool r = vprintf(format, ap);
va_end(ap);
return r;
} }
const char js_EscapeMap[] = { const char js_EscapeMap[] = {
@ -329,53 +268,118 @@ static const char JSONEscapeMap[] = {
}; };
template <QuoteTarget target, typename CharT> template <QuoteTarget target, typename CharT>
JS_PUBLIC_API void QuoteString(Sprinter* sp, JS_PUBLIC_API bool QuoteString(Sprinter* sp,
const mozilla::Range<const CharT> chars, const mozilla::Range<const CharT> chars,
char quote) { char quote) {
MOZ_ASSERT_IF(target == QuoteTarget::JSON, quote == '\0'); MOZ_ASSERT_IF(target == QuoteTarget::JSON, quote == '\0');
using CharPtr = mozilla::RangedPtr<const CharT>;
const char* escapeMap =
(target == QuoteTarget::String) ? js_EscapeMap : JSONEscapeMap;
if (quote) { if (quote) {
sp->putChar(quote); if (!sp->putChar(quote)) {
return false;
}
} }
if (target == QuoteTarget::String) {
StringEscape esc(quote); const CharPtr end = chars.end();
EscapePrinter ep(*sp, esc);
ep.put(chars); /* Loop control variables: end points at end of string sentinel. */
} else { for (CharPtr t = chars.begin(); t < end; ++t) {
MOZ_ASSERT(target == QuoteTarget::JSON); /* Move t forward from s past un-quote-worthy characters. */
JSONEscape esc; const CharPtr s = t;
EscapePrinter ep(*sp, esc); char16_t c = *t;
ep.put(chars); while (c < 127 && c != '\\') {
if (target == QuoteTarget::String) {
if (!IsAsciiPrintable(c) || c == quote || c == '\t') {
break;
}
} else {
if (c < ' ' || c == '"') {
break;
}
}
++t;
if (t == end) {
break;
}
c = *t;
}
{
ptrdiff_t len = t - s;
ptrdiff_t base = sp->getOffset();
if (!sp->reserve(len)) {
return false;
}
for (ptrdiff_t i = 0; i < len; ++i) {
(*sp)[base + i] = char(s[i]);
}
(*sp)[base + len] = '\0';
}
if (t == end) {
break;
}
/* Use escapeMap, \u, or \x only if necessary. */
const char* escape;
if (!(c >> 8) && c != 0 &&
(escape = strchr(escapeMap, int(c))) != nullptr) {
if (!sp->jsprintf("\\%c", escape[1])) {
return false;
}
} else {
/*
* Use \x only if the high byte is 0 and we're in a quoted string,
* because ECMA-262 allows only \u, not \x, in Unicode identifiers
* (see bug 621814).
*/
if (!sp->jsprintf((quote && !(c >> 8)) ? "\\x%02X" : "\\u%04X", c)) {
return false;
}
}
} }
/* Sprint the closing quote and return the quoted string. */
if (quote) { if (quote) {
sp->putChar(quote); if (!sp->putChar(quote)) {
return false;
}
} }
return true;
} }
template JS_PUBLIC_API void QuoteString<QuoteTarget::String, Latin1Char>( template JS_PUBLIC_API bool QuoteString<QuoteTarget::String, Latin1Char>(
Sprinter* sp, const mozilla::Range<const Latin1Char> chars, char quote); Sprinter* sp, const mozilla::Range<const Latin1Char> chars, char quote);
template JS_PUBLIC_API void QuoteString<QuoteTarget::String, char16_t>( template JS_PUBLIC_API bool QuoteString<QuoteTarget::String, char16_t>(
Sprinter* sp, const mozilla::Range<const char16_t> chars, char quote); Sprinter* sp, const mozilla::Range<const char16_t> chars, char quote);
template JS_PUBLIC_API void QuoteString<QuoteTarget::JSON, Latin1Char>( template JS_PUBLIC_API bool QuoteString<QuoteTarget::JSON, Latin1Char>(
Sprinter* sp, const mozilla::Range<const Latin1Char> chars, char quote); Sprinter* sp, const mozilla::Range<const Latin1Char> chars, char quote);
template JS_PUBLIC_API void QuoteString<QuoteTarget::JSON, char16_t>( template JS_PUBLIC_API bool QuoteString<QuoteTarget::JSON, char16_t>(
Sprinter* sp, const mozilla::Range<const char16_t> chars, char quote); Sprinter* sp, const mozilla::Range<const char16_t> chars, char quote);
JS_PUBLIC_API void QuoteString(Sprinter* sp, JSString* str, JS_PUBLIC_API bool QuoteString(Sprinter* sp, JSString* str,
char quote /*= '\0' */) { char quote /*= '\0' */) {
MOZ_ASSERT(sp->maybeCx); MOZ_ASSERT(sp->maybeCx);
if (quote) { JSLinearString* linear = str->ensureLinear(sp->maybeCx);
sp->putChar(quote); if (!linear) {
} return false;
StringEscape esc(quote);
EscapePrinter ep(*sp, esc);
ep.putString(sp->maybeCx, str);
if (quote) {
sp->putChar(quote);
} }
JS::AutoCheckCannotGC nogc;
return linear->hasLatin1Chars() ? QuoteString<QuoteTarget::String>(
sp, linear->latin1Range(nogc), quote)
: QuoteString<QuoteTarget::String>(
sp, linear->twoByteRange(nogc), quote);
} }
JS_PUBLIC_API UniqueChars QuoteString(JSContext* cx, JSString* str, JS_PUBLIC_API UniqueChars QuoteString(JSContext* cx, JSString* str,
@ -384,15 +388,24 @@ JS_PUBLIC_API UniqueChars QuoteString(JSContext* cx, JSString* str,
if (!sprinter.init()) { if (!sprinter.init()) {
return nullptr; return nullptr;
} }
QuoteString(&sprinter, str, quote); if (!QuoteString(&sprinter, str, quote)) {
return nullptr;
}
return sprinter.release(); return sprinter.release();
} }
JS_PUBLIC_API void JSONQuoteString(Sprinter* sp, JSString* str) { JS_PUBLIC_API bool JSONQuoteString(Sprinter* sp, JSString* str) {
MOZ_ASSERT(sp->maybeCx); MOZ_ASSERT(sp->maybeCx);
JSONEscape esc; JSLinearString* linear = str->ensureLinear(sp->maybeCx);
EscapePrinter ep(*sp, esc); if (!linear) {
ep.putString(sp->maybeCx, str); return false;
}
JS::AutoCheckCannotGC nogc;
return linear->hasLatin1Chars() ? QuoteString<QuoteTarget::JSON>(
sp, linear->latin1Range(nogc), '\0')
: QuoteString<QuoteTarget::JSON>(
sp, linear->twoByteRange(nogc), '\0');
} }
Fprinter::Fprinter(FILE* fp) : file_(nullptr), init_(false) { init(fp); } Fprinter::Fprinter(FILE* fp) : file_(nullptr), init_(false) { init(fp); }
@ -430,27 +443,24 @@ void Fprinter::finish() {
file_ = nullptr; file_ = nullptr;
} }
void Fprinter::put(const char* s, size_t len) { bool Fprinter::put(const char* s, size_t len) {
if (hadOutOfMemory()) {
return;
}
MOZ_ASSERT(file_); MOZ_ASSERT(file_);
int i = fwrite(s, /*size=*/1, /*nitems=*/len, file_); int i = fwrite(s, /*size=*/1, /*nitems=*/len, file_);
if (size_t(i) != len) { if (size_t(i) != len) {
reportOutOfMemory(); reportOutOfMemory();
return; return false;
} }
#ifdef XP_WIN #ifdef XP_WIN
if ((file_ == stderr) && (IsDebuggerPresent())) { if ((file_ == stderr) && (IsDebuggerPresent())) {
UniqueChars buf = DuplicateString(s, len); UniqueChars buf = DuplicateString(s, len);
if (!buf) { if (!buf) {
reportOutOfMemory(); reportOutOfMemory();
return; return false;
} }
OutputDebugStringA(buf.get()); OutputDebugStringA(buf.get());
} }
#endif #endif
return true;
} }
LSprinter::LSprinter(LifoAlloc* lifoAlloc) LSprinter::LSprinter(LifoAlloc* lifoAlloc)
@ -479,11 +489,7 @@ void LSprinter::clear() {
hadOOM_ = false; hadOOM_ = false;
} }
void LSprinter::put(const char* s, size_t len) { bool LSprinter::put(const char* s, size_t len) {
if (hadOutOfMemory()) {
return;
}
// Compute how much data will fit in the current chunk. // Compute how much data will fit in the current chunk.
size_t existingSpaceWrite = 0; size_t existingSpaceWrite = 0;
size_t overflow = len; size_t overflow = len;
@ -503,7 +509,7 @@ void LSprinter::put(const char* s, size_t len) {
last = reinterpret_cast<Chunk*>(alloc_->alloc(allocLength)); last = reinterpret_cast<Chunk*>(alloc_->alloc(allocLength));
if (!last) { if (!last) {
reportOutOfMemory(); reportOutOfMemory();
return; return false;
} }
} }
@ -547,36 +553,7 @@ void LSprinter::put(const char* s, size_t len) {
} }
MOZ_ASSERT(len <= INT_MAX); MOZ_ASSERT(len <= INT_MAX);
} return true;
bool JSONEscape::isSafeChar(char16_t c) {
return js::IsAsciiPrintable(c) && c != '"' && c != '\\';
}
void JSONEscape::convertInto(GenericPrinter& out, char16_t c) {
const char* escape = nullptr;
if (!(c >> 8) && c != 0 &&
(escape = strchr(JSONEscapeMap, int(c))) != nullptr) {
out.printf("\\%c", escape[1]);
} else {
out.printf("\\u%04X", c);
}
}
bool StringEscape::isSafeChar(char16_t c) {
return js::IsAsciiPrintable(c) && c != quote && c != '\\';
}
void StringEscape::convertInto(GenericPrinter& out, char16_t c) {
const char* escape = nullptr;
if (!(c >> 8) && c != 0 &&
(escape = strchr(js_EscapeMap, int(c))) != nullptr) {
out.printf("\\%c", escape[1]);
} else {
// Use \x only if the high byte is 0 and we're in a quoted string, because
// ECMA-262 allows only \u, not \x, in Unicode identifiers (see bug 621814).
out.printf(!(c >> 8) ? "\\x%02X" : "\\u%04X", c);
}
} }
} // namespace js } // namespace js

Просмотреть файл

@ -392,55 +392,88 @@ void Scope::dump() {
/* static */ /* static */
bool Scope::dumpForDisassemble(JSContext* cx, JS::Handle<Scope*> scope, bool Scope::dumpForDisassemble(JSContext* cx, JS::Handle<Scope*> scope,
GenericPrinter& out, const char* indent) { GenericPrinter& out, const char* indent) {
out.put(ScopeKindString(scope->kind())); if (!out.put(ScopeKindString(scope->kind()))) {
out.put(" {"); return false;
}
if (!out.put(" {")) {
return false;
}
size_t i = 0; size_t i = 0;
for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++, i++) { for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++, i++) {
if (i == 0) { if (i == 0) {
out.put("\n"); if (!out.put("\n")) {
return false;
}
} }
UniqueChars bytes = AtomToPrintableString(cx, bi.name()); UniqueChars bytes = AtomToPrintableString(cx, bi.name());
if (!bytes) { if (!bytes) {
return false; return false;
} }
out.put(indent); if (!out.put(indent)) {
out.printf(" %2zu: %s %s ", i, BindingKindString(bi.kind()), bytes.get()); return false;
}
if (!out.printf(" %2zu: %s %s ", i, BindingKindString(bi.kind()),
bytes.get())) {
return false;
}
switch (bi.location().kind()) { switch (bi.location().kind()) {
case BindingLocation::Kind::Global: case BindingLocation::Kind::Global:
if (bi.isTopLevelFunction()) { if (bi.isTopLevelFunction()) {
out.put("(global function)\n"); if (!out.put("(global function)\n")) {
return false;
}
} else { } else {
out.put("(global)\n"); if (!out.put("(global)\n")) {
return false;
}
} }
break; break;
case BindingLocation::Kind::Argument: case BindingLocation::Kind::Argument:
out.printf("(arg slot %u)\n", bi.location().argumentSlot()); if (!out.printf("(arg slot %u)\n", bi.location().argumentSlot())) {
return false;
}
break; break;
case BindingLocation::Kind::Frame: case BindingLocation::Kind::Frame:
out.printf("(frame slot %u)\n", bi.location().slot()); if (!out.printf("(frame slot %u)\n", bi.location().slot())) {
return false;
}
break; break;
case BindingLocation::Kind::Environment: case BindingLocation::Kind::Environment:
out.printf("(env slot %u)\n", bi.location().slot()); if (!out.printf("(env slot %u)\n", bi.location().slot())) {
return false;
}
break; break;
case BindingLocation::Kind::NamedLambdaCallee: case BindingLocation::Kind::NamedLambdaCallee:
out.put("(named lambda callee)\n"); if (!out.put("(named lambda callee)\n")) {
return false;
}
break; break;
case BindingLocation::Kind::Import: case BindingLocation::Kind::Import:
out.put("(import)\n"); if (!out.put("(import)\n")) {
return false;
}
break; break;
} }
} }
if (i > 0) { if (i > 0) {
out.put(indent); if (!out.put(indent)) {
return false;
}
}
if (!out.put("}")) {
return false;
} }
out.put("}");
ScopeIter si(scope); ScopeIter si(scope);
si++; si++;
for (; si; si++) { for (; si; si++) {
out.put(" -> "); if (!out.put(" -> ")) {
out.put(ScopeKindString(si.kind())); return false;
}
if (!out.put(ScopeKindString(si.kind()))) {
return false;
}
} }
return true; return true;
} }