Bug 1147403 part 1 - Move Sprinter into its own header and add a FILE & LifoAlloc variants. r=h4writer

This commit is contained in:
Nicolas B. Pierron 2015-05-15 20:19:01 +02:00
parent 10fbe5dfe8
commit d1f4cb62ee
6 changed files with 835 additions and 426 deletions

View File

@ -13,7 +13,6 @@
#include "mozilla/SizePrintfMacros.h"
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
@ -825,9 +824,6 @@ js::DumpScript(JSContext* cx, JSScript* scriptArg)
return ok;
}
static char*
QuoteString(Sprinter* sp, JSString* str, char16_t quote);
static bool
ToDisassemblySource(JSContext* cx, HandleValue v, JSAutoByteString* bytes)
{
@ -1087,336 +1083,6 @@ js::Disassemble1(JSContext* cx, HandleScript script, jsbytecode* pc,
#endif /* DEBUG */
/************************************************************************/
const size_t Sprinter::DefaultSize = 64;
bool
Sprinter::realloc_(size_t newSize)
{
MOZ_ASSERT(newSize > (size_t) offset);
char* newBuf = (char*) js_realloc(base, newSize);
if (!newBuf) {
reportOutOfMemory();
return false;
}
base = newBuf;
size = newSize;
base[size - 1] = 0;
return true;
}
Sprinter::Sprinter(ExclusiveContext* cx)
: context(cx),
#ifdef DEBUG
initialized(false),
#endif
base(nullptr), size(0), offset(0), reportedOOM(false)
{ }
Sprinter::~Sprinter()
{
#ifdef DEBUG
if (initialized)
checkInvariants();
#endif
js_free(base);
}
bool
Sprinter::init()
{
MOZ_ASSERT(!initialized);
base = (char*) js_malloc(DefaultSize);
if (!base) {
reportOutOfMemory();
return false;
}
#ifdef DEBUG
initialized = true;
#endif
*base = 0;
size = DefaultSize;
base[size - 1] = 0;
return true;
}
void
Sprinter::checkInvariants() const
{
MOZ_ASSERT(initialized);
MOZ_ASSERT((size_t) offset < size);
MOZ_ASSERT(base[size - 1] == 0);
}
const char*
Sprinter::string() const
{
return base;
}
const char*
Sprinter::stringEnd() const
{
return base + offset;
}
char*
Sprinter::stringAt(ptrdiff_t off) const
{
MOZ_ASSERT(off >= 0 && (size_t) off < size);
return base + off;
}
char&
Sprinter::operator[](size_t off)
{
MOZ_ASSERT(off < size);
return *(base + off);
}
char*
Sprinter::reserve(size_t len)
{
InvariantChecker ic(this);
while (len + 1 > size - offset) { /* Include trailing \0 */
if (!realloc_(size * 2))
return nullptr;
}
char* sb = base + offset;
offset += len;
return sb;
}
ptrdiff_t
Sprinter::put(const char* s, size_t len)
{
InvariantChecker ic(this);
const char* oldBase = base;
const char* oldEnd = base + size;
ptrdiff_t oldOffset = offset;
char* bp = reserve(len);
if (!bp)
return -1;
/* s is within the buffer already */
if (s >= oldBase && s < oldEnd) {
/* buffer was realloc'ed */
if (base != oldBase)
s = stringAt(s - oldBase); /* this is where it lives now */
memmove(bp, s, len);
} else {
js_memcpy(bp, s, len);
}
bp[len] = 0;
return oldOffset;
}
ptrdiff_t
Sprinter::put(const char* s)
{
return put(s, strlen(s));
}
ptrdiff_t
Sprinter::putString(JSString* s)
{
InvariantChecker ic(this);
size_t length = s->length();
size_t size = length;
ptrdiff_t oldOffset = offset;
char* buffer = reserve(size);
if (!buffer)
return -1;
JSLinearString* linear = s->ensureLinear(context);
if (!linear)
return -1;
AutoCheckCannotGC nogc;
if (linear->hasLatin1Chars())
mozilla::PodCopy(reinterpret_cast<Latin1Char*>(buffer), linear->latin1Chars(nogc), length);
else
DeflateStringToBuffer(nullptr, linear->twoByteChars(nogc), length, buffer, &size);
buffer[size] = 0;
return oldOffset;
}
int
Sprinter::printf(const char* fmt, ...)
{
InvariantChecker ic(this);
do {
va_list va;
va_start(va, fmt);
int i = vsnprintf(base + offset, size - offset, fmt, va);
va_end(va);
if (i > -1 && (size_t) i < size - offset) {
offset += i;
return i;
}
} while (realloc_(size * 2));
return -1;
}
ptrdiff_t
Sprinter::getOffset() const
{
return offset;
}
void
Sprinter::reportOutOfMemory()
{
if (reportedOOM)
return;
if (context)
ReportOutOfMemory(context);
reportedOOM = true;
}
bool
Sprinter::hadOutOfMemory() const
{
return reportedOOM;
}
ptrdiff_t
js::Sprint(Sprinter* sp, const char* format, ...)
{
va_list ap;
char* bp;
ptrdiff_t offset;
va_start(ap, format);
bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
va_end(ap);
if (!bp) {
sp->reportOutOfMemory();
return -1;
}
offset = sp->put(bp);
js_free(bp);
return offset;
}
const char js_EscapeMap[] = {
'\b', 'b',
'\f', 'f',
'\n', 'n',
'\r', 'r',
'\t', 't',
'\v', 'v',
'"', '"',
'\'', '\'',
'\\', '\\',
'\0'
};
template <typename CharT>
static char*
QuoteString(Sprinter* sp, const CharT* s, size_t length, char16_t quote)
{
/* Sample off first for later return value pointer computation. */
ptrdiff_t offset = sp->getOffset();
if (quote && Sprint(sp, "%c", char(quote)) < 0)
return nullptr;
const CharT* end = s + length;
/* Loop control variables: end points at end of string sentinel. */
for (const CharT* t = s; t < end; s = ++t) {
/* Move t forward from s past un-quote-worthy characters. */
char16_t c = *t;
while (c < 127 && isprint(c) && c != quote && c != '\\' && c != '\t') {
c = *++t;
if (t == end)
break;
}
{
ptrdiff_t len = t - s;
ptrdiff_t base = sp->getOffset();
if (!sp->reserve(len))
return nullptr;
for (ptrdiff_t i = 0; i < len; ++i)
(*sp)[base + i] = char(*s++);
(*sp)[base + len] = 0;
}
if (t == end)
break;
/* Use js_EscapeMap, \u, or \x only if necessary. */
const char* escape;
if (!(c >> 8) && c != 0 && (escape = strchr(js_EscapeMap, int(c))) != nullptr) {
if (Sprint(sp, "\\%c", escape[1]) < 0)
return nullptr;
} 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 (Sprint(sp, (quote && !(c >> 8)) ? "\\x%02X" : "\\u%04X", c) < 0)
return nullptr;
}
}
/* Sprint the closing quote and return the quoted string. */
if (quote && Sprint(sp, "%c", char(quote)) < 0)
return nullptr;
/*
* If we haven't Sprint'd anything yet, Sprint an empty string so that
* the return below gives a valid result.
*/
if (offset == sp->getOffset() && Sprint(sp, "") < 0)
return nullptr;
return sp->stringAt(offset);
}
static char*
QuoteString(Sprinter* sp, JSString* str, char16_t quote)
{
JSLinearString* linear = str->ensureLinear(sp->context);
if (!linear)
return nullptr;
AutoCheckCannotGC nogc;
return linear->hasLatin1Chars()
? QuoteString(sp, linear->latin1Chars(nogc), linear->length(), quote)
: QuoteString(sp, linear->twoByteChars(nogc), linear->length(), quote);
}
JSString*
js::QuoteString(ExclusiveContext* cx, JSString* str, char16_t quote)
{
Sprinter sprinter(cx);
if (!sprinter.init())
return nullptr;
char* bytes = QuoteString(&sprinter, str, quote);
if (!bytes)
return nullptr;
return NewStringCopyZ<CanGC>(cx, bytes);
}
/************************************************************************/
namespace {
/*
* The expression decompiler is invoked by error handling code to produce a

View File

@ -19,6 +19,7 @@
#include "frontend/SourceNotes.h"
#include "vm/Opcodes.h"
#include "vm/Printer.h"
/*
* JS operation bytecodes.
@ -382,7 +383,6 @@ struct JSCodeSpec {
extern const JSCodeSpec js_CodeSpec[];
extern const unsigned js_NumCodeSpecs;
extern const char * const js_CodeName[];
extern const char js_EscapeMap[];
/* Shorthand for type from opcode. */
@ -400,14 +400,6 @@ JOF_OPTYPE(JSOp op)
namespace js {
/*
* Return a GC'ed string containing the chars in str, with any non-printing
* chars or quotes (' or " as specified by the quote argument) escaped, and
* with the quote character at the beginning and end of the result string.
*/
extern JSString*
QuoteString(ExclusiveContext* cx, JSString* str, char16_t quote);
static inline bool
IsJumpOpcode(JSOp op)
{
@ -577,89 +569,6 @@ DecompileValueGenerator(JSContext* cx, int spindex, HandleValue v,
char*
DecompileArgument(JSContext* cx, int formalIndex, HandleValue v);
/*
* Sprintf, but with unlimited and automatically allocated buffering.
*/
class Sprinter
{
public:
struct InvariantChecker
{
const Sprinter* parent;
explicit InvariantChecker(const Sprinter* p) : parent(p) {
parent->checkInvariants();
}
~InvariantChecker() {
parent->checkInvariants();
}
};
ExclusiveContext* context; /* context executing the decompiler */
private:
static const size_t DefaultSize;
#ifdef DEBUG
bool initialized; /* true if this is initialized, use for debug builds */
#endif
char* base; /* malloc'd buffer address */
size_t size; /* size of buffer allocated at base */
ptrdiff_t offset; /* offset of next free char in buffer */
bool reportedOOM; /* this sprinter has reported OOM in string ops */
bool realloc_(size_t newSize);
public:
explicit Sprinter(ExclusiveContext* cx);
~Sprinter();
/* Initialize this sprinter, returns false on error */
bool init();
void checkInvariants() const;
const char* string() const;
const char* stringEnd() const;
/* 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 succeeds, return a pointer to the start of that space and adjust the
* internal content. The caller *must* completely fill this space on success.
*/
char* reserve(size_t len);
/*
* Puts |len| characters from |s| at the current position and return an offset to
* the beginning of this new data
*/
ptrdiff_t put(const char* s, size_t len);
ptrdiff_t put(const char* s);
ptrdiff_t putString(JSString* str);
/* Prints a formatted string into the buffer */
int printf(const char* fmt, ...);
ptrdiff_t getOffset() const;
/*
* Report that a string operation failed to get the memory it requested. The
* first call to this function calls JS_ReportOutOfMemory, and sets this
* Sprinter's outOfMemory flag; subsequent calls do nothing.
*/
void reportOutOfMemory();
/* Return true if this Sprinter ran out of memory. */
bool hadOutOfMemory() const;
};
extern ptrdiff_t
Sprint(Sprinter* sp, const char* format, ...);
extern bool
CallResultEscapes(jsbytecode* pc);

View File

@ -39,6 +39,7 @@
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/Opcodes.h"
#include "vm/Printer.h"
#include "vm/RegExpObject.h"
#include "vm/RegExpStatics.h"
#include "vm/ScopeObject.h"

View File

@ -295,6 +295,7 @@ UNIFIED_SOURCES += [
'vm/NativeObject.cpp',
'vm/ObjectGroup.cpp',
'vm/PIC.cpp',
'vm/Printer.cpp',
'vm/Probes.cpp',
'vm/ProxyObject.cpp',
'vm/ReceiverGuard.cpp',

609
js/src/vm/Printer.cpp Normal file
View File

@ -0,0 +1,609 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "vm/Printer.h"
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include "jscntxt.h"
#include "jsprf.h"
#include "jsutil.h"
#include "ds/LifoAlloc.h"
namespace js {
GenericPrinter::GenericPrinter()
: reportedOOM_(false)
{
}
void
GenericPrinter::reportOutOfMemory()
{
if (reportedOOM_)
return;
reportedOOM_ = true;
}
bool
GenericPrinter::hadOutOfMemory() const
{
return reportedOOM_;
}
int
GenericPrinter::put(const char* s)
{
return put(s, strlen(s));
}
int
GenericPrinter::printf(const char* fmt, ...)
{
va_list va;
va_start(va, fmt);
int i = vprintf(fmt, va);
va_end(va);
return i;
}
int
GenericPrinter::vprintf(const char* fmt, va_list ap)
{
// Simple shortcut to avoid allocating strings.
if (strchr(fmt, '%') == nullptr)
return put(fmt);
char* bp;
bp = JS_vsmprintf(fmt, ap); /* XXX vsaprintf */
if (!bp) {
reportOutOfMemory();
return -1;
}
int i = put(bp);
js_free(bp);
return i;
}
const size_t Sprinter::DefaultSize = 64;
bool
Sprinter::realloc_(size_t newSize)
{
MOZ_ASSERT(newSize > (size_t) offset);
char* newBuf = (char*) js_realloc(base, newSize);
if (!newBuf) {
reportOutOfMemory();
return false;
}
base = newBuf;
size = newSize;
base[size - 1] = 0;
return true;
}
Sprinter::Sprinter(ExclusiveContext* cx)
: context(cx),
#ifdef DEBUG
initialized(false),
#endif
base(nullptr), size(0), offset(0)
{ }
Sprinter::~Sprinter()
{
#ifdef DEBUG
if (initialized)
checkInvariants();
#endif
js_free(base);
}
bool
Sprinter::init()
{
MOZ_ASSERT(!initialized);
base = (char*) js_malloc(DefaultSize);
if (!base) {
reportOutOfMemory();
return false;
}
#ifdef DEBUG
initialized = true;
#endif
*base = 0;
size = DefaultSize;
base[size - 1] = 0;
return true;
}
void
Sprinter::checkInvariants() const
{
MOZ_ASSERT(initialized);
MOZ_ASSERT((size_t) offset < size);
MOZ_ASSERT(base[size - 1] == 0);
}
const char*
Sprinter::string() const
{
return base;
}
const char*
Sprinter::stringEnd() const
{
return base + offset;
}
char*
Sprinter::stringAt(ptrdiff_t off) const
{
MOZ_ASSERT(off >= 0 && (size_t) off < size);
return base + off;
}
char&
Sprinter::operator[](size_t off)
{
MOZ_ASSERT(off < size);
return *(base + off);
}
char*
Sprinter::reserve(size_t len)
{
InvariantChecker ic(this);
while (len + 1 > size - offset) { /* Include trailing \0 */
if (!realloc_(size * 2))
return nullptr;
}
char* sb = base + offset;
offset += len;
return sb;
}
int
Sprinter::put(const char* s, size_t len)
{
InvariantChecker ic(this);
const char* oldBase = base;
const char* oldEnd = base + size;
ptrdiff_t oldOffset = offset;
char* bp = reserve(len);
if (!bp)
return -1;
/* s is within the buffer already */
if (s >= oldBase && s < oldEnd) {
/* buffer was realloc'ed */
if (base != oldBase)
s = stringAt(s - oldBase); /* this is where it lives now */
memmove(bp, s, len);
} else {
js_memcpy(bp, s, len);
}
bp[len] = 0;
return oldOffset;
}
int
Sprinter::vprintf(const char* fmt, va_list ap)
{
InvariantChecker ic(this);
do {
va_list aq;
va_copy(aq, ap);
int i = vsnprintf(base + offset, size - offset, fmt, aq);
va_end(aq);
if (i > -1 && (size_t) i < size - offset) {
offset += i;
return i;
}
} while (realloc_(size * 2));
return -1;
}
int
Sprinter::putString(JSString* s)
{
InvariantChecker ic(this);
size_t length = s->length();
size_t size = length;
ptrdiff_t oldOffset = offset;
char* buffer = reserve(size);
if (!buffer)
return -1;
JSLinearString* linear = s->ensureLinear(context);
if (!linear)
return -1;
JS::AutoCheckCannotGC nogc;
if (linear->hasLatin1Chars())
mozilla::PodCopy(reinterpret_cast<Latin1Char*>(buffer), linear->latin1Chars(nogc), length);
else
DeflateStringToBuffer(nullptr, linear->twoByteChars(nogc), length, buffer, &size);
buffer[size] = 0;
return oldOffset;
}
ptrdiff_t
Sprinter::getOffset() const
{
return offset;
}
void
Sprinter::reportOutOfMemory()
{
if (reportedOOM_)
return;
if (context)
ReportOutOfMemory(context);
reportedOOM_ = true;
}
ptrdiff_t
Sprint(Sprinter* sp, const char* format, ...)
{
va_list ap;
char* bp;
ptrdiff_t offset;
va_start(ap, format);
bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
va_end(ap);
if (!bp) {
sp->reportOutOfMemory();
return -1;
}
offset = sp->put(bp);
js_free(bp);
return offset;
}
const char js_EscapeMap[] = {
'\b', 'b',
'\f', 'f',
'\n', 'n',
'\r', 'r',
'\t', 't',
'\v', 'v',
'"', '"',
'\'', '\'',
'\\', '\\',
'\0'
};
template <typename CharT>
static char*
QuoteString(Sprinter* sp, const CharT* s, size_t length, char16_t quote)
{
/* Sample off first for later return value pointer computation. */
ptrdiff_t offset = sp->getOffset();
if (quote && Sprint(sp, "%c", char(quote)) < 0)
return nullptr;
const CharT* end = s + length;
/* Loop control variables: end points at end of string sentinel. */
for (const CharT* t = s; t < end; s = ++t) {
/* Move t forward from s past un-quote-worthy characters. */
char16_t c = *t;
while (c < 127 && isprint(c) && c != quote && c != '\\' && c != '\t') {
c = *++t;
if (t == end)
break;
}
{
ptrdiff_t len = t - s;
ptrdiff_t base = sp->getOffset();
if (!sp->reserve(len))
return nullptr;
for (ptrdiff_t i = 0; i < len; ++i)
(*sp)[base + i] = char(*s++);
(*sp)[base + len] = 0;
}
if (t == end)
break;
/* Use js_EscapeMap, \u, or \x only if necessary. */
const char* escape;
if (!(c >> 8) && c != 0 && (escape = strchr(js_EscapeMap, int(c))) != nullptr) {
if (Sprint(sp, "\\%c", escape[1]) < 0)
return nullptr;
} 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 (Sprint(sp, (quote && !(c >> 8)) ? "\\x%02X" : "\\u%04X", c) < 0)
return nullptr;
}
}
/* Sprint the closing quote and return the quoted string. */
if (quote && Sprint(sp, "%c", char(quote)) < 0)
return nullptr;
/*
* If we haven't Sprint'd anything yet, Sprint an empty string so that
* the return below gives a valid result.
*/
if (offset == sp->getOffset() && Sprint(sp, "") < 0)
return nullptr;
return sp->stringAt(offset);
}
char*
QuoteString(Sprinter* sp, JSString* str, char16_t quote)
{
JSLinearString* linear = str->ensureLinear(sp->context);
if (!linear)
return nullptr;
JS::AutoCheckCannotGC nogc;
return linear->hasLatin1Chars()
? QuoteString(sp, linear->latin1Chars(nogc), linear->length(), quote)
: QuoteString(sp, linear->twoByteChars(nogc), linear->length(), quote);
}
JSString*
QuoteString(ExclusiveContext* cx, JSString* str, char16_t quote)
{
Sprinter sprinter(cx);
if (!sprinter.init())
return nullptr;
char* bytes = QuoteString(&sprinter, str, quote);
if (!bytes)
return nullptr;
return NewStringCopyZ<CanGC>(cx, bytes);
}
Fprinter::Fprinter(FILE* fp)
: file_(nullptr)
{
init(fp);
}
Fprinter::Fprinter()
: file_(nullptr)
{ }
Fprinter::~Fprinter()
{
MOZ_ASSERT_IF(init_, !file_);
}
bool
Fprinter::init(const char* path)
{
MOZ_ASSERT(!file_);
file_ = fopen(path, "w");
if (!file_)
return false;
init_ = true;
return true;
}
void
Fprinter::init(FILE *fp)
{
MOZ_ASSERT(!file_);
file_ = fp;
init_ = false;
}
void
Fprinter::flush()
{
MOZ_ASSERT(file_);
fflush(file_);
}
void
Fprinter::finish()
{
MOZ_ASSERT(file_);
if (init_)
fclose(file_);
file_ = nullptr;
}
int
Fprinter::put(const char* s, size_t len)
{
MOZ_ASSERT(file_);
int i = fwrite(s, len, 1, file_);
if (i == -1 || i != int(len))
reportOutOfMemory();
return i;
}
int
Fprinter::put(const char* s)
{
MOZ_ASSERT(file_);
int i = fputs(s, file_);
if (i == -1)
reportOutOfMemory();
return i;
}
int
Fprinter::printf(const char* fmt, ...)
{
MOZ_ASSERT(file_);
va_list ap;
va_start(ap, fmt);
int i = vfprintf(file_, fmt, ap);
if (i == -1)
reportOutOfMemory();
va_end(ap);
return i;
}
int
Fprinter::vprintf(const char* fmt, va_list ap)
{
MOZ_ASSERT(file_);
int i = vfprintf(file_, fmt, ap);
if (i == -1)
reportOutOfMemory();
return i;
}
LSprinter::LSprinter(LifoAlloc* lifoAlloc)
: alloc_(lifoAlloc),
head_(nullptr),
tail_(nullptr),
unused_(0)
{ }
LSprinter::~LSprinter()
{
// This LSprinter might be allocated as part of the same LifoAlloc, so we
// should not expect the destructor to be called.
}
void
LSprinter::exportInto(GenericPrinter& out) const
{
if (!head_)
return;
for (Chunk* it = head_; it != tail_; it = it->next)
out.put(it->chars(), it->length);
out.put(tail_->chars(), tail_->length - unused_);
}
void
LSprinter::clear()
{
head_ = nullptr;
tail_ = nullptr;
unused_ = 0;
reportedOOM_ = false;
}
int
LSprinter::put(const char* s, size_t len)
{
size_t origLen = len;
if (unused_ > 0 && tail_) {
size_t minLen = unused_ < len ? unused_ : len;
js_memcpy(tail_->end() - unused_, s, minLen);
unused_ -= minLen;
len -= minLen;
s += minLen;
}
if (len == 0)
return origLen;
size_t allocLength = AlignBytes(sizeof(Chunk) + len, js::detail::LIFO_ALLOC_ALIGN);
Chunk* last = reinterpret_cast<Chunk*>(alloc_->alloc(allocLength));
if (!last) {
reportOutOfMemory();
return origLen - len;
}
if (tail_ && reinterpret_cast<char*>(last) == tail_->end()) {
// tail_ and last are next to each others in memory, knowing that the
// TempAlloctator has no meta data and is just a bump allocator, we
// append the new allocated space to the tail_.
unused_ = allocLength;
tail_->length += allocLength;
} else {
// Remove the size of the header from the allocated length.
allocLength -= sizeof(Chunk);
last->next = nullptr;
last->length = allocLength;
unused_ = allocLength;
if (!head_)
head_ = last;
else
tail_->next = last;
tail_ = last;
}
MOZ_ASSERT(tail_->length >= unused_);
js_memcpy(tail_->end() - unused_, s, len);
unused_ -= len;
return origLen;
}
int
LSprinter::put(const char* s)
{
return put(s, strlen(s));
}
int
LSprinter::printf(const char* fmt, ...)
{
va_list va;
va_start(va, fmt);
int i = vprintf(fmt, va);
va_end(va);
return i;
}
int
LSprinter::vprintf(const char* fmt, va_list ap)
{
// Simple shortcut to avoid allocating strings.
if (strchr(fmt, '%') == nullptr)
return put(fmt);
char* bp;
bp = JS_vsmprintf(fmt, ap); /* XXX vsaprintf */
if (!bp) {
reportOutOfMemory();
return -1;
}
int i = put(bp);
js_free(bp);
return i;
}
void
LSprinter::reportOutOfMemory()
{
if (reportedOOM_)
return;
reportedOOM_ = true;
}
bool
LSprinter::hadOutOfMemory() const
{
return reportedOOM_;
}
} // namespace js

223
js/src/vm/Printer.h Normal file
View File

@ -0,0 +1,223 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_Printer_h
#define vm_Printer_h
#include <stdarg.h>
#include <stdio.h>
class JSString;
namespace js {
class ExclusiveContext;
class LifoAlloc;
// Generic printf interface, similar to an ostream in the standard library.
//
// This class is useful to make generic printers which can work either with a
// file backend, with a buffer allocated with an ExclusiveContext or a link-list
// of chunks allocated with a LifoAlloc.
class GenericPrinter
{
protected:
bool reportedOOM_; // record reported OOM.
GenericPrinter();
public:
// Puts |len| characters from |s| at the current position and return an offset to
// the beginning of this new data.
virtual int put(const char* s, size_t len) = 0;
virtual int put(const char* s);
// Prints a formatted string into the buffer.
virtual int printf(const char* fmt, ...);
virtual int vprintf(const char* fmt, va_list ap);
// Report that a string operation failed to get the memory it requested. The
// first call to this function calls JS_ReportOutOfMemory, and sets this
// Sprinter's outOfMemory flag; subsequent calls do nothing.
virtual void reportOutOfMemory();
// Return true if this Sprinter ran out of memory.
virtual bool hadOutOfMemory() const;
};
// Sprintf, but with unlimited and automatically allocated buffering.
class Sprinter final : public GenericPrinter
{
public:
struct InvariantChecker
{
const Sprinter* parent;
explicit InvariantChecker(const Sprinter* p) : parent(p) {
parent->checkInvariants();
}
~InvariantChecker() {
parent->checkInvariants();
}
};
ExclusiveContext* context; // context executing the decompiler
private:
static const size_t DefaultSize;
#ifdef DEBUG
bool initialized; // true if this is initialized, use for debug builds
#endif
char* base; // malloc'd buffer address
size_t size; // size of buffer allocated at base
ptrdiff_t offset; // offset of next free char in buffer
bool realloc_(size_t newSize);
public:
explicit Sprinter(ExclusiveContext* cx);
~Sprinter();
// Initialize this sprinter, returns false on error.
bool init();
void checkInvariants() const;
const char* string() const;
const char* stringEnd() const;
// 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 succeeds, return a pointer to the start of that space and adjust the
// internal content. The caller *must* completely fill this space on success.
char* reserve(size_t len);
// Puts |len| characters from |s| at the current position and return an offset to
// the beginning of this new data.
using GenericPrinter::put;
virtual int put(const char* s, size_t len) override;
// Prints a formatted string into the buffer.
virtual int vprintf(const char* fmt, va_list ap) override;
int putString(JSString* str);
ptrdiff_t getOffset() const;
// Report that a string operation failed to get the memory it requested. The
// first call to this function calls JS_ReportOutOfMemory, and sets this
// Sprinter's outOfMemory flag; subsequent calls do nothing.
virtual void reportOutOfMemory() override;
};
// Fprinter, print a string directly into a file.
class Fprinter final : public GenericPrinter
{
private:
FILE* file_;
bool init_;
public:
explicit Fprinter(FILE* fp);
Fprinter();
~Fprinter();
// Initialize this printer, returns false on error.
bool init(const char* path);
void init(FILE* fp);
bool isInitialized() {
return file_ != nullptr;
}
void flush();
void finish();
// Puts |len| characters from |s| at the current position and return an
// offset to the beginning of this new data.
virtual int put(const char* s, size_t len) override;
virtual int put(const char* s) override;
// Prints a formatted string into the buffer.
virtual int printf(const char* fmt, ...) override;
virtual int vprintf(const char* fmt, va_list ap) override;
};
// LSprinter, is similar to Sprinter except that instead of using an
// ExclusiveContext to allocate strings, it use a LifoAlloc as a backend for the
// allocation of the chunk of the string.
class LSprinter final : public GenericPrinter
{
private:
struct Chunk
{
Chunk* next;
size_t length;
char* chars() {
return reinterpret_cast<char*>(this + 1);
}
char* end() {
return chars() + length;
}
};
private:
LifoAlloc* alloc_; // LifoAlloc used as a backend of chunk allocations.
Chunk* head_;
Chunk* tail_;
size_t unused_;
public:
explicit LSprinter(LifoAlloc* lifoAlloc);
~LSprinter();
// Copy the content of the chunks into another printer, such that we can
// flush the content of this printer to a file.
void exportInto(GenericPrinter& out) const;
// Drop the current string, and let them be free with the LifoAlloc.
void clear();
// Puts |len| characters from |s| at the current position and return an
// offset to the beginning of this new data.
virtual int put(const char* s, size_t len) override;
virtual int put(const char* s) override;
// Prints a formatted string into the buffer.
virtual int printf(const char* fmt, ...) override;
virtual int vprintf(const char* fmt, va_list ap) override;
// Report that a string operation failed to get the memory it requested. The
// first call to this function calls JS_ReportOutOfMemory, and sets this
// Sprinter's outOfMemory flag; subsequent calls do nothing.
virtual void reportOutOfMemory() override;
// Return true if this Sprinter ran out of memory.
virtual bool hadOutOfMemory() const override;
};
extern ptrdiff_t
Sprint(Sprinter* sp, const char* format, ...);
// Map escaped code to the letter/symbol escaped with a backslash.
extern const char js_EscapeMap[];
// Return a GC'ed string containing the chars in str, with any non-printing
// chars or quotes (' or " as specified by the quote argument) escaped, and
// with the quote character at the beginning and end of the result string.
extern JSString*
QuoteString(ExclusiveContext* cx, JSString* str, char16_t quote);
extern char*
QuoteString(Sprinter* sp, JSString* str, char16_t quote);
} // namespace js
#endif // vm_Printer_h