1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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 #ifndef vm_Printer_h 8 #define vm_Printer_h 9 10 #include "mozilla/Attributes.h" 11 #include "mozilla/Range.h" 12 13 #include <stdarg.h> 14 #include <stddef.h> 15 #include <stdio.h> 16 #include <string.h> 17 18 #include "js/TypeDecls.h" 19 #include "js/Utility.h" 20 21 namespace js { 22 23 class LifoAlloc; 24 25 // Generic printf interface, similar to an ostream in the standard library. 26 // 27 // This class is useful to make generic printers which can work either with a 28 // file backend, with a buffer allocated with an JSContext or a link-list 29 // of chunks allocated with a LifoAlloc. 30 class GenericPrinter { 31 protected: 32 bool hadOOM_; // whether reportOutOfMemory() has been called. 33 GenericPrinter()34 constexpr GenericPrinter() : hadOOM_(false) {} 35 36 public: 37 // Puts |len| characters from |s| at the current position and 38 // return true on success, false on failure. 39 virtual bool put(const char* s, size_t len) = 0; flush()40 virtual void flush() { /* Do nothing */ 41 } 42 put(const char * s)43 inline bool put(const char* s) { return put(s, strlen(s)); } putChar(const char c)44 inline bool putChar(const char c) { return put(&c, 1); } 45 46 // Prints a formatted string into the buffer. 47 bool printf(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3); 48 bool vprintf(const char* fmt, va_list ap) MOZ_FORMAT_PRINTF(2, 0); 49 50 // Report that a string operation failed to get the memory it requested. 51 virtual void reportOutOfMemory(); 52 53 // Return true if this Sprinter ran out of memory. 54 virtual bool hadOutOfMemory() const; 55 }; 56 57 // Sprintf, but with unlimited and automatically allocated buffering. 58 class Sprinter final : public GenericPrinter { 59 public: 60 struct InvariantChecker { 61 const Sprinter* parent; 62 InvariantCheckerInvariantChecker63 explicit InvariantChecker(const Sprinter* p) : parent(p) { 64 parent->checkInvariants(); 65 } 66 ~InvariantCheckerInvariantChecker67 ~InvariantChecker() { parent->checkInvariants(); } 68 }; 69 70 JSContext* context; // context executing the decompiler 71 72 private: 73 static const size_t DefaultSize; 74 #ifdef DEBUG 75 bool initialized; // true if this is initialized, use for debug builds 76 #endif 77 bool shouldReportOOM; // whether to report OOM to the context 78 char* base; // malloc'd buffer address 79 size_t size; // size of buffer allocated at base 80 ptrdiff_t offset; // offset of next free char in buffer 81 82 [[nodiscard]] bool realloc_(size_t newSize); 83 84 public: 85 explicit Sprinter(JSContext* cx, bool shouldReportOOM = true); 86 ~Sprinter(); 87 88 // Initialize this sprinter, returns false on error. 89 [[nodiscard]] bool init(); 90 91 void checkInvariants() const; 92 string()93 const char* string() const { return base; } stringEnd()94 const char* stringEnd() const { return base + offset; } 95 JS::UniqueChars release(); 96 97 // Returns the string at offset |off|. 98 char* stringAt(ptrdiff_t off) const; 99 // Returns the char at offset |off|. 100 char& operator[](size_t off); 101 102 // Attempt to reserve len + 1 space (for a trailing nullptr byte). If the 103 // attempt succeeds, return a pointer to the start of that space and adjust 104 // the internal content. The caller *must* completely fill this space on 105 // success. 106 char* reserve(size_t len); 107 108 // Puts |len| characters from |s| at the current position and 109 // return true on success, false on failure. 110 virtual bool put(const char* s, size_t len) override; 111 using GenericPrinter::put; // pick up |inline bool put(const char* s);| 112 113 // Format the given format/arguments as if by JS_vsmprintf, then put it. 114 // Return true on success, else return false and report an error (typically 115 // OOM). 116 [[nodiscard]] bool jsprintf(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3); 117 118 bool putString(JSString* str); 119 120 ptrdiff_t getOffset() const; 121 122 // Report that a string operation failed to get the memory it requested. The 123 // first call to this function calls JS_ReportOutOfMemory, and sets this 124 // Sprinter's outOfMemory flag; subsequent calls do nothing. 125 virtual void reportOutOfMemory() override; 126 }; 127 128 // Fprinter, print a string directly into a file. 129 class Fprinter final : public GenericPrinter { 130 private: 131 FILE* file_; 132 bool init_; 133 134 public: 135 explicit Fprinter(FILE* fp); 136 Fprinter()137 constexpr Fprinter() : file_(nullptr), init_(false) {} 138 139 #ifdef DEBUG 140 ~Fprinter(); 141 #endif 142 143 // Initialize this printer, returns false on error. 144 [[nodiscard]] bool init(const char* path); 145 void init(FILE* fp); isInitialized()146 bool isInitialized() const { return file_ != nullptr; } 147 void flush() override; 148 void finish(); 149 150 // Puts |len| characters from |s| at the current position and 151 // return true on success, false on failure. 152 virtual bool put(const char* s, size_t len) override; 153 using GenericPrinter::put; // pick up |inline bool put(const char* s);| 154 }; 155 156 // LSprinter, is similar to Sprinter except that instead of using an 157 // JSContext to allocate strings, it use a LifoAlloc as a backend for the 158 // allocation of the chunk of the string. 159 class LSprinter final : public GenericPrinter { 160 private: 161 struct Chunk { 162 Chunk* next; 163 size_t length; 164 charsChunk165 char* chars() { return reinterpret_cast<char*>(this + 1); } endChunk166 char* end() { return chars() + length; } 167 }; 168 169 private: 170 LifoAlloc* alloc_; // LifoAlloc used as a backend of chunk allocations. 171 Chunk* head_; 172 Chunk* tail_; 173 size_t unused_; 174 175 public: 176 explicit LSprinter(LifoAlloc* lifoAlloc); 177 ~LSprinter(); 178 179 // Copy the content of the chunks into another printer, such that we can 180 // flush the content of this printer to a file. 181 void exportInto(GenericPrinter& out) const; 182 183 // Drop the current string, and let them be free with the LifoAlloc. 184 void clear(); 185 186 // Puts |len| characters from |s| at the current position and 187 // return true on success, false on failure. 188 virtual bool put(const char* s, size_t len) override; 189 using GenericPrinter::put; // pick up |inline bool put(const char* s);| 190 }; 191 192 // Map escaped code to the letter/symbol escaped with a backslash. 193 extern const char js_EscapeMap[]; 194 195 // Return a C-string containing the chars in str, with any non-printing chars 196 // escaped. If the optional quote parameter is present and is not '\0', quotes 197 // (as specified by the quote argument) are also escaped, and the quote 198 // character is appended at the beginning and end of the result string. 199 // The returned string is guaranteed to contain only ASCII characters. 200 extern JS::UniqueChars QuoteString(JSContext* cx, JSString* str, 201 char quote = '\0'); 202 203 // Appends the quoted string to the given Sprinter. Follows the same semantics 204 // as QuoteString from above. 205 extern bool QuoteString(Sprinter* sp, JSString* str, char quote = '\0'); 206 207 // Appends the quoted string to the given Sprinter. Follows the same 208 // Appends the JSON quoted string to the given Sprinter. 209 extern bool JSONQuoteString(Sprinter* sp, JSString* str); 210 211 // Internal implementation code for QuoteString methods above. 212 enum class QuoteTarget { String, JSON }; 213 214 template <QuoteTarget target, typename CharT> 215 bool QuoteString(Sprinter* sp, const mozilla::Range<const CharT> chars, 216 char quote = '\0'); 217 218 } // namespace js 219 220 #endif // vm_Printer_h 221