LCOV - code coverage report
Current view: top level - js/src/vm - Printer.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 124 272 45.6 %
Date: 2017-07-14 16:53:18 Functions: 22 40 55.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "vm/Printer.h"
       8             : 
       9             : #include "mozilla/PodOperations.h"
      10             : #include "mozilla/Printf.h"
      11             : 
      12             : #include <ctype.h>
      13             : #include <stdarg.h>
      14             : #include <stdio.h>
      15             : 
      16             : #include "jscntxt.h"
      17             : #include "jsutil.h"
      18             : 
      19             : #include "ds/LifoAlloc.h"
      20             : 
      21             : using mozilla::PodCopy;
      22             : 
      23             : namespace
      24             : {
      25             : 
      26           8 : class GenericPrinterPrintfTarget : public mozilla::PrintfTarget
      27             : {
      28             : public:
      29             : 
      30           8 :     explicit GenericPrinterPrintfTarget(js::GenericPrinter& p)
      31           8 :         : printer(p)
      32             :     {
      33           8 :     }
      34             : 
      35           8 :     bool append(const char* sp, size_t len) {
      36           8 :         return printer.put(sp, len);
      37             :     }
      38             : 
      39             : private:
      40             : 
      41             :     js::GenericPrinter& printer;
      42             : };
      43             : 
      44             : }
      45             : 
      46             : namespace js {
      47             : 
      48         691 : GenericPrinter::GenericPrinter()
      49         691 :   : hadOOM_(false)
      50             : {
      51         691 : }
      52             : 
      53             : void
      54           0 : GenericPrinter::reportOutOfMemory()
      55             : {
      56           0 :     if (hadOOM_)
      57           0 :         return;
      58           0 :     hadOOM_ = true;
      59             : }
      60             : 
      61             : bool
      62           0 : GenericPrinter::hadOutOfMemory() const
      63             : {
      64           0 :     return hadOOM_;
      65             : }
      66             : 
      67             : bool
      68           0 : GenericPrinter::printf(const char* fmt, ...)
      69             : {
      70             :     va_list va;
      71           0 :     va_start(va, fmt);
      72           0 :     bool r = vprintf(fmt, va);
      73           0 :     va_end(va);
      74           0 :     return r;
      75             : }
      76             : 
      77             : bool
      78           8 : GenericPrinter::vprintf(const char* fmt, va_list ap)
      79             : {
      80             :     // Simple shortcut to avoid allocating strings.
      81           8 :     if (strchr(fmt, '%') == nullptr)
      82           0 :         return put(fmt);
      83             : 
      84          16 :     GenericPrinterPrintfTarget printer(*this);
      85           8 :     if (!printer.vprint(fmt, ap)) {
      86           0 :         reportOutOfMemory();
      87           0 :         return false;
      88             :     }
      89           8 :     return true;
      90             : }
      91             : 
      92             : const size_t Sprinter::DefaultSize = 64;
      93             : 
      94             : bool
      95           0 : Sprinter::realloc_(size_t newSize)
      96             : {
      97           0 :     MOZ_ASSERT(newSize > (size_t) offset);
      98           0 :     char* newBuf = (char*) js_realloc(base, newSize);
      99           0 :     if (!newBuf) {
     100           0 :         reportOutOfMemory();
     101           0 :         return false;
     102             :     }
     103           0 :     base = newBuf;
     104           0 :     size = newSize;
     105           0 :     base[size - 1] = 0;
     106           0 :     return true;
     107             : }
     108             : 
     109           5 : Sprinter::Sprinter(JSContext* cx, bool shouldReportOOM)
     110             :   : context(cx),
     111             : #ifdef DEBUG
     112             :     initialized(false),
     113             : #endif
     114             :     shouldReportOOM(shouldReportOOM),
     115           5 :     base(nullptr), size(0), offset(0)
     116           5 : { }
     117             : 
     118          10 : Sprinter::~Sprinter()
     119             : {
     120             : #ifdef DEBUG
     121           5 :     if (initialized)
     122           5 :         checkInvariants();
     123             : #endif
     124           5 :     js_free(base);
     125           5 : }
     126             : 
     127             : bool
     128           5 : Sprinter::init()
     129             : {
     130           5 :     MOZ_ASSERT(!initialized);
     131           5 :     base = (char*) js_malloc(DefaultSize);
     132           5 :     if (!base) {
     133           0 :         reportOutOfMemory();
     134           0 :         return false;
     135             :     }
     136             : #ifdef DEBUG
     137           5 :     initialized = true;
     138             : #endif
     139           5 :     *base = 0;
     140           5 :     size = DefaultSize;
     141           5 :     base[size - 1] = 0;
     142           5 :     return true;
     143             : }
     144             : 
     145             : void
     146          61 : Sprinter::checkInvariants() const
     147             : {
     148          61 :     MOZ_ASSERT(initialized);
     149          61 :     MOZ_ASSERT((size_t) offset < size);
     150          61 :     MOZ_ASSERT(base[size - 1] == 0);
     151          61 : }
     152             : 
     153             : char*
     154           0 : Sprinter::release()
     155             : {
     156           0 :     checkInvariants();
     157           0 :     if (hadOOM_)
     158           0 :         return nullptr;
     159             : 
     160           0 :     char* str = base;
     161           0 :     base = nullptr;
     162           0 :     offset = size = 0;
     163             : #ifdef DEBUG
     164           0 :     initialized = false;
     165             : #endif
     166           0 :     return str;
     167             : }
     168             : 
     169             : char*
     170           8 : Sprinter::stringAt(ptrdiff_t off) const
     171             : {
     172           8 :     MOZ_ASSERT(off >= 0 && (size_t) off < size);
     173           8 :     return base + off;
     174             : }
     175             : 
     176             : char&
     177          88 : Sprinter::operator[](size_t off)
     178             : {
     179          88 :     MOZ_ASSERT(off < size);
     180          88 :     return *(base + off);
     181             : }
     182             : 
     183             : char*
     184          16 : Sprinter::reserve(size_t len)
     185             : {
     186          32 :     InvariantChecker ic(this);
     187             : 
     188          16 :     while (len + 1 > size - offset) { /* Include trailing \0 */
     189           0 :         if (!realloc_(size * 2))
     190           0 :             return nullptr;
     191             :     }
     192             : 
     193          16 :     char* sb = base + offset;
     194          16 :     offset += len;
     195          16 :     return sb;
     196             : }
     197             : 
     198             : bool
     199          10 : Sprinter::put(const char* s, size_t len)
     200             : {
     201          20 :     InvariantChecker ic(this);
     202             : 
     203          10 :     const char* oldBase = base;
     204          10 :     const char* oldEnd = base + size;
     205             : 
     206          10 :     char* bp = reserve(len);
     207          10 :     if (!bp)
     208           0 :         return false;
     209             : 
     210             :     /* s is within the buffer already */
     211          10 :     if (s >= oldBase && s < oldEnd) {
     212             :         /* buffer was realloc'ed */
     213           0 :         if (base != oldBase)
     214           0 :             s = stringAt(s - oldBase);  /* this is where it lives now */
     215           0 :         memmove(bp, s, len);
     216             :     } else {
     217          10 :         js_memcpy(bp, s, len);
     218             :     }
     219             : 
     220          10 :     bp[len] = 0;
     221          10 :     return true;
     222             : }
     223             : 
     224             : bool
     225           2 : Sprinter::putString(JSString* s)
     226             : {
     227           4 :     InvariantChecker ic(this);
     228             : 
     229           2 :     size_t length = s->length();
     230           2 :     size_t size = length;
     231             : 
     232           2 :     char* buffer = reserve(size);
     233           2 :     if (!buffer)
     234           0 :         return false;
     235             : 
     236           2 :     JSLinearString* linear = s->ensureLinear(context);
     237           2 :     if (!linear)
     238           0 :         return false;
     239             : 
     240           4 :     JS::AutoCheckCannotGC nogc;
     241           2 :     if (linear->hasLatin1Chars())
     242           2 :         PodCopy(reinterpret_cast<Latin1Char*>(buffer), linear->latin1Chars(nogc), length);
     243             :     else
     244           0 :         DeflateStringToBuffer(nullptr, linear->twoByteChars(nogc), length, buffer, &size);
     245             : 
     246           2 :     buffer[size] = 0;
     247           2 :     return true;
     248             : }
     249             : 
     250             : ptrdiff_t
     251          12 : Sprinter::getOffset() const
     252             : {
     253          12 :     return offset;
     254             : }
     255             : 
     256             : void
     257           0 : Sprinter::reportOutOfMemory()
     258             : {
     259           0 :     if (hadOOM_)
     260           0 :         return;
     261           0 :     if (context && shouldReportOOM)
     262           0 :         ReportOutOfMemory(context);
     263           0 :     hadOOM_ = true;
     264             : }
     265             : 
     266             : bool
     267           8 : Sprinter::jsprintf(const char* format, ...)
     268             : {
     269             :     va_list ap;
     270           8 :     va_start(ap, format);
     271             : 
     272           8 :     bool r = vprintf(format, ap);
     273           8 :     va_end(ap);
     274             : 
     275           8 :     return r;
     276             : }
     277             : 
     278             : const char js_EscapeMap[] = {
     279             :     '\b', 'b',
     280             :     '\f', 'f',
     281             :     '\n', 'n',
     282             :     '\r', 'r',
     283             :     '\t', 't',
     284             :     '\v', 'v',
     285             :     '"',  '"',
     286             :     '\'', '\'',
     287             :     '\\', '\\',
     288             :     '\0'
     289             : };
     290             : 
     291             : template <typename CharT>
     292             : static char*
     293           4 : QuoteString(Sprinter* sp, const mozilla::Range<const CharT> chars, char16_t quote)
     294             : {
     295             :     using CharPtr = mozilla::RangedPtr<const CharT>;
     296             : 
     297             :     /* Sample off first for later return value pointer computation. */
     298           4 :     ptrdiff_t offset = sp->getOffset();
     299             : 
     300           4 :     if (quote) {
     301           4 :         if (!sp->jsprintf("%c", char(quote)))
     302           0 :             return nullptr;
     303             :     }
     304             : 
     305           4 :     const CharPtr end = chars.end();
     306             : 
     307             :     /* Loop control variables: end points at end of string sentinel. */
     308           4 :     for (CharPtr t = chars.begin(); t < end; ++t) {
     309             :         /* Move t forward from s past un-quote-worthy characters. */
     310           4 :         const CharPtr s = t;
     311           4 :         char16_t c = *t;
     312         164 :         while (c < 127 && isprint(c) && c != quote && c != '\\' && c != '\t') {
     313          84 :             ++t;
     314          84 :             if (t == end)
     315           4 :                 break;
     316          80 :             c = *t;
     317             :         }
     318             : 
     319             :         {
     320           4 :             ptrdiff_t len = t - s;
     321           4 :             ptrdiff_t base = sp->getOffset();
     322           4 :             if (!sp->reserve(len))
     323           0 :                 return nullptr;
     324             : 
     325          88 :             for (ptrdiff_t i = 0; i < len; ++i)
     326          84 :                 (*sp)[base + i] = char(s[i]);
     327           4 :             (*sp)[base + len] = 0;
     328             :         }
     329             : 
     330           4 :         if (t == end)
     331           4 :             break;
     332             : 
     333             :         /* Use js_EscapeMap, \u, or \x only if necessary. */
     334             :         const char* escape;
     335           0 :         if (!(c >> 8) && c != 0 && (escape = strchr(js_EscapeMap, int(c))) != nullptr) {
     336           0 :             if (!sp->jsprintf("\\%c", escape[1]))
     337           0 :                 return nullptr;
     338             :         } else {
     339             :             /*
     340             :              * Use \x only if the high byte is 0 and we're in a quoted string,
     341             :              * because ECMA-262 allows only \u, not \x, in Unicode identifiers
     342             :              * (see bug 621814).
     343             :              */
     344           0 :             if (!sp->jsprintf((quote && !(c >> 8)) ? "\\x%02X" : "\\u%04X", c))
     345           0 :                 return nullptr;
     346             :         }
     347             :     }
     348             : 
     349             :     /* Sprint the closing quote and return the quoted string. */
     350           4 :     if (quote) {
     351           4 :         if (!sp->jsprintf("%c", char(quote)))
     352           0 :             return nullptr;
     353             :     }
     354             : 
     355             :     /*
     356             :      * If we haven't Sprint'd anything yet, Sprint an empty string so that
     357             :      * the return below gives a valid result.
     358             :      */
     359           4 :     if (offset == sp->getOffset()) {
     360           0 :         if (!sp->put(""))
     361           0 :             return nullptr;
     362             :     }
     363             : 
     364           4 :     return sp->stringAt(offset);
     365             : }
     366             : 
     367             : char*
     368           4 : QuoteString(Sprinter* sp, JSString* str, char16_t quote)
     369             : {
     370           4 :     JSLinearString* linear = str->ensureLinear(sp->context);
     371           4 :     if (!linear)
     372           0 :         return nullptr;
     373             : 
     374           8 :     JS::AutoCheckCannotGC nogc;
     375           4 :     return linear->hasLatin1Chars()
     376           4 :            ? QuoteString(sp, linear->latin1Range(nogc), quote)
     377           4 :            : QuoteString(sp, linear->twoByteRange(nogc), quote);
     378             : }
     379             : 
     380             : JSString*
     381           3 : QuoteString(JSContext* cx, JSString* str, char16_t quote)
     382             : {
     383           6 :     Sprinter sprinter(cx);
     384           3 :     if (!sprinter.init())
     385           0 :         return nullptr;
     386           3 :     char* bytes = QuoteString(&sprinter, str, quote);
     387           3 :     if (!bytes)
     388           0 :         return nullptr;
     389           3 :     return NewStringCopyZ<CanGC>(cx, bytes);
     390             : }
     391             : 
     392           0 : Fprinter::Fprinter(FILE* fp)
     393             :   : file_(nullptr),
     394           0 :     init_(false)
     395             : {
     396           0 :     init(fp);
     397           0 : }
     398             : 
     399          13 : Fprinter::Fprinter()
     400             :   : file_(nullptr),
     401          13 :     init_(false)
     402          13 : { }
     403             : 
     404           0 : Fprinter::~Fprinter()
     405             : {
     406           0 :     MOZ_ASSERT_IF(init_, !file_);
     407           0 : }
     408             : 
     409             : bool
     410           0 : Fprinter::init(const char* path)
     411             : {
     412           0 :     MOZ_ASSERT(!file_);
     413           0 :     file_ = fopen(path, "w");
     414           0 :     if (!file_)
     415           0 :         return false;
     416           0 :     init_ = true;
     417           0 :     return true;
     418             : }
     419             : 
     420             : void
     421           0 : Fprinter::init(FILE *fp)
     422             : {
     423           0 :     MOZ_ASSERT(!file_);
     424           0 :     file_ = fp;
     425           0 :     init_ = false;
     426           0 : }
     427             : 
     428             : void
     429           0 : Fprinter::flush()
     430             : {
     431           0 :     MOZ_ASSERT(file_);
     432           0 :     fflush(file_);
     433           0 : }
     434             : 
     435             : void
     436           0 : Fprinter::finish()
     437             : {
     438           0 :     MOZ_ASSERT(file_);
     439           0 :     if (init_)
     440           0 :         fclose(file_);
     441           0 :     file_ = nullptr;
     442           0 : }
     443             : 
     444             : bool
     445           0 : Fprinter::put(const char* s, size_t len)
     446             : {
     447           0 :     MOZ_ASSERT(file_);
     448           0 :     int i = fwrite(s, /*size=*/ 1, /*nitems=*/ len, file_);
     449           0 :     if (size_t(i) != len) {
     450           0 :         reportOutOfMemory();
     451           0 :         return false;
     452             :     }
     453           0 :     return true;
     454             : }
     455             : 
     456         673 : LSprinter::LSprinter(LifoAlloc* lifoAlloc)
     457             :   : alloc_(lifoAlloc),
     458             :     head_(nullptr),
     459             :     tail_(nullptr),
     460         673 :     unused_(0)
     461         673 : { }
     462             : 
     463         338 : LSprinter::~LSprinter()
     464             : {
     465             :     // This LSprinter might be allocated as part of the same LifoAlloc, so we
     466             :     // should not expect the destructor to be called.
     467         338 : }
     468             : 
     469             : void
     470           0 : LSprinter::exportInto(GenericPrinter& out) const
     471             : {
     472           0 :     if (!head_)
     473           0 :         return;
     474             : 
     475           0 :     for (Chunk* it = head_; it != tail_; it = it->next)
     476           0 :         out.put(it->chars(), it->length);
     477           0 :     out.put(tail_->chars(), tail_->length - unused_);
     478             : }
     479             : 
     480             : void
     481           0 : LSprinter::clear()
     482             : {
     483           0 :     head_ = nullptr;
     484           0 :     tail_ = nullptr;
     485           0 :     unused_ = 0;
     486           0 :     hadOOM_ = false;
     487           0 : }
     488             : 
     489             : bool
     490           0 : LSprinter::put(const char* s, size_t len)
     491             : {
     492             :     // Compute how much data will fit in the current chunk.
     493           0 :     size_t existingSpaceWrite = 0;
     494           0 :     size_t overflow = len;
     495           0 :     if (unused_ > 0 && tail_) {
     496           0 :         existingSpaceWrite = std::min(unused_, len);
     497           0 :         overflow = len - existingSpaceWrite;
     498             :     }
     499             : 
     500             :     // If necessary, allocate a new chunk for overflow data.
     501           0 :     size_t allocLength = 0;
     502           0 :     Chunk* last = nullptr;
     503           0 :     if (overflow > 0) {
     504           0 :         allocLength = AlignBytes(sizeof(Chunk) + overflow, js::detail::LIFO_ALLOC_ALIGN);
     505             : 
     506           0 :         LifoAlloc::AutoFallibleScope fallibleAllocator(alloc_);
     507           0 :         last = reinterpret_cast<Chunk*>(alloc_->alloc(allocLength));
     508           0 :         if (!last) {
     509           0 :             reportOutOfMemory();
     510           0 :             return false;
     511             :         }
     512             :     }
     513             : 
     514             :     // All fallible operations complete: now fill up existing space, then
     515             :     // overflow space in any new chunk.
     516           0 :     MOZ_ASSERT(existingSpaceWrite + overflow == len);
     517             : 
     518           0 :     if (existingSpaceWrite > 0) {
     519           0 :         PodCopy(tail_->end() - unused_, s, existingSpaceWrite);
     520           0 :         unused_ -= existingSpaceWrite;
     521           0 :         s += existingSpaceWrite;
     522             :     }
     523             : 
     524           0 :     if (overflow > 0) {
     525           0 :         if (tail_ && reinterpret_cast<char*>(last) == tail_->end()) {
     526             :             // tail_ and last are consecutive in memory.  LifoAlloc has no
     527             :             // metadata and is just a bump allocator, so we can cheat by
     528             :             // appending the newly-allocated space to tail_.
     529           0 :             unused_ = allocLength;
     530           0 :             tail_->length += allocLength;
     531             :         } else {
     532             :             // Remove the size of the header from the allocated length.
     533           0 :             size_t availableSpace = allocLength - sizeof(Chunk);
     534           0 :             last->next = nullptr;
     535           0 :             last->length = availableSpace;
     536             : 
     537           0 :             unused_ = availableSpace;
     538           0 :             if (!head_)
     539           0 :                 head_ = last;
     540             :             else
     541           0 :                 tail_->next = last;
     542             : 
     543           0 :             tail_ = last;
     544             :         }
     545             : 
     546           0 :         PodCopy(tail_->end() - unused_, s, overflow);
     547             : 
     548           0 :         MOZ_ASSERT(unused_ >= overflow);
     549           0 :         unused_ -= overflow;
     550             :     }
     551             : 
     552           0 :     MOZ_ASSERT(len <= INT_MAX);
     553           0 :     return true;
     554             : }
     555             : 
     556             : } // namespace js

Generated by: LCOV version 1.13