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