зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1147403 part 1 - Move Sprinter into its own header and add a FILE & LifoAlloc variants. r=h4writer
This commit is contained in:
Родитель
1ae56c0a90
Коммит
48bae2ca42
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
|
@ -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
|
Загрузка…
Ссылка в новой задаче