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 frontend_ErrorReporter_h
8 #define frontend_ErrorReporter_h
9 
10 #include "mozilla/Variant.h"
11 
12 #include <stdarg.h>  // for va_list
13 #include <stddef.h>  // for size_t
14 #include <stdint.h>  // for uint32_t
15 
16 #include "js/CompileOptions.h"
17 #include "js/UniquePtr.h"
18 #include "vm/ErrorReporting.h"  // ErrorMetadata, ReportCompile{Error,Warning}
19 
20 class JSErrorNotes;
21 
22 namespace js {
23 namespace frontend {
24 
25 // An interface class to provide strictMode getter method, which is used by
26 // ErrorReportMixin::strictModeError* methods.
27 //
28 // This class is separated to be used as a back-channel from TokenStream to the
29 // strict mode flag which is available inside Parser, to avoid exposing the
30 // rest of SharedContext to TokenStream.
31 class StrictModeGetter {
32  public:
33   virtual bool strictMode() const = 0;
34 };
35 
36 // This class provides error reporting methods, including warning, extra
37 // warning, and strict mode error.
38 //
39 // A class that inherits this class must provide the following methods:
40 //   * options
41 //   * getContext
42 //   * computeErrorMetadata
43 class ErrorReportMixin : public StrictModeGetter {
44  public:
45   // Returns a compile options (extra warning, warning as error) for current
46   // compilation.
47   virtual const JS::ReadOnlyCompileOptions& options() const = 0;
48 
49   // Returns the current context.
50   virtual JSContext* getContext() const = 0;
51 
52   // A variant class for the offset of the error or warning.
53   struct Current {};
54   struct NoOffset {};
55   using ErrorOffset = mozilla::Variant<uint32_t, Current, NoOffset>;
56 
57   // Fills ErrorMetadata fields for an error or warning at given offset.
58   //   * offset is uint32_t if methods ending with "At" is called
59   //   * offset is NoOffset if methods ending with "NoOffset" is called
60   //   * offset is Current otherwise
61   [[nodiscard]] virtual bool computeErrorMetadata(
62       ErrorMetadata* err, const ErrorOffset& offset) = 0;
63 
64   // ==== error ====
65   //
66   // Reports an error.
67   //
68   // Methods ending with "At" are for an error at given offset.
69   // The offset is passed to computeErrorMetadata method and is transparent
70   // for this class.
71   //
72   // Methods ending with "NoOffset" are for an error that doesn't correspond
73   // to any offset. NoOffset is passed to computeErrorMetadata for them.
74   //
75   // Other methods except errorWithNotesAtVA are for an error at the current
76   // offset. Current is passed to computeErrorMetadata for them.
77   //
78   // Methods contains "WithNotes" can be used if there are error notes.
79   //
80   // errorWithNotesAtVA is the actual implementation for all of above.
81   // This can be called if the caller already has a va_list.
82 
error(unsigned errorNumber,...)83   void error(unsigned errorNumber, ...) {
84     va_list args;
85     va_start(args, errorNumber);
86 
87     errorWithNotesAtVA(nullptr, mozilla::AsVariant(Current()), errorNumber,
88                        &args);
89 
90     va_end(args);
91   }
errorWithNotes(UniquePtr<JSErrorNotes> notes,unsigned errorNumber,...)92   void errorWithNotes(UniquePtr<JSErrorNotes> notes, unsigned errorNumber,
93                       ...) {
94     va_list args;
95     va_start(args, errorNumber);
96 
97     errorWithNotesAtVA(std::move(notes), mozilla::AsVariant(Current()),
98                        errorNumber, &args);
99 
100     va_end(args);
101   }
errorAt(uint32_t offset,unsigned errorNumber,...)102   void errorAt(uint32_t offset, unsigned errorNumber, ...) {
103     va_list args;
104     va_start(args, errorNumber);
105 
106     errorWithNotesAtVA(nullptr, mozilla::AsVariant(offset), errorNumber, &args);
107 
108     va_end(args);
109   }
errorWithNotesAt(UniquePtr<JSErrorNotes> notes,uint32_t offset,unsigned errorNumber,...)110   void errorWithNotesAt(UniquePtr<JSErrorNotes> notes, uint32_t offset,
111                         unsigned errorNumber, ...) {
112     va_list args;
113     va_start(args, errorNumber);
114 
115     errorWithNotesAtVA(std::move(notes), mozilla::AsVariant(offset),
116                        errorNumber, &args);
117 
118     va_end(args);
119   }
errorNoOffset(unsigned errorNumber,...)120   void errorNoOffset(unsigned errorNumber, ...) {
121     va_list args;
122     va_start(args, errorNumber);
123 
124     errorWithNotesAtVA(nullptr, mozilla::AsVariant(NoOffset()), errorNumber,
125                        &args);
126 
127     va_end(args);
128   }
errorWithNotesNoOffset(UniquePtr<JSErrorNotes> notes,unsigned errorNumber,...)129   void errorWithNotesNoOffset(UniquePtr<JSErrorNotes> notes,
130                               unsigned errorNumber, ...) {
131     va_list args;
132     va_start(args, errorNumber);
133 
134     errorWithNotesAtVA(std::move(notes), mozilla::AsVariant(NoOffset()),
135                        errorNumber, &args);
136 
137     va_end(args);
138   }
errorWithNotesAtVA(UniquePtr<JSErrorNotes> notes,const ErrorOffset & offset,unsigned errorNumber,va_list * args)139   void errorWithNotesAtVA(UniquePtr<JSErrorNotes> notes,
140                           const ErrorOffset& offset, unsigned errorNumber,
141                           va_list* args) {
142     ErrorMetadata metadata;
143     if (!computeErrorMetadata(&metadata, offset)) {
144       return;
145     }
146 
147     ReportCompileErrorLatin1(getContext(), std::move(metadata),
148                              std::move(notes), errorNumber, args);
149   }
150 
151   // ==== warning ====
152   //
153   // Reports a warning.
154   //
155   // Returns true if the warning is reported.
156   // Returns false if the warning is treated as an error, or an error occurs
157   // while reporting.
158   //
159   // See the comment on the error section for details on what the arguments
160   // and function names indicate for all these functions.
161 
warning(unsigned errorNumber,...)162   [[nodiscard]] bool warning(unsigned errorNumber, ...) {
163     va_list args;
164     va_start(args, errorNumber);
165 
166     bool result = warningWithNotesAtVA(nullptr, mozilla::AsVariant(Current()),
167                                        errorNumber, &args);
168 
169     va_end(args);
170 
171     return result;
172   }
warningAt(uint32_t offset,unsigned errorNumber,...)173   [[nodiscard]] bool warningAt(uint32_t offset, unsigned errorNumber, ...) {
174     va_list args;
175     va_start(args, errorNumber);
176 
177     bool result = warningWithNotesAtVA(nullptr, mozilla::AsVariant(offset),
178                                        errorNumber, &args);
179 
180     va_end(args);
181 
182     return result;
183   }
warningNoOffset(unsigned errorNumber,...)184   [[nodiscard]] bool warningNoOffset(unsigned errorNumber, ...) {
185     va_list args;
186     va_start(args, errorNumber);
187 
188     bool result = warningWithNotesAtVA(nullptr, mozilla::AsVariant(NoOffset()),
189                                        errorNumber, &args);
190 
191     va_end(args);
192 
193     return result;
194   }
warningWithNotesAtVA(UniquePtr<JSErrorNotes> notes,const ErrorOffset & offset,unsigned errorNumber,va_list * args)195   [[nodiscard]] bool warningWithNotesAtVA(UniquePtr<JSErrorNotes> notes,
196                                           const ErrorOffset& offset,
197                                           unsigned errorNumber, va_list* args) {
198     ErrorMetadata metadata;
199     if (!computeErrorMetadata(&metadata, offset)) {
200       return false;
201     }
202 
203     return compileWarning(std::move(metadata), std::move(notes), errorNumber,
204                           args);
205   }
206 
207   // ==== strictModeError ====
208   //
209   // Reports an error if in strict mode code, or warn if not.
210   //
211   // Returns true if not in strict mode and a warning is reported.
212   // Returns false if the error reported, or an error occurs while reporting.
213   //
214   // See the comment on the error section for details on what the arguments
215   // and function names indicate for all these functions.
216 
strictModeError(unsigned errorNumber,...)217   [[nodiscard]] bool strictModeError(unsigned errorNumber, ...) {
218     va_list args;
219     va_start(args, errorNumber);
220 
221     bool result = strictModeErrorWithNotesAtVA(
222         nullptr, mozilla::AsVariant(Current()), errorNumber, &args);
223 
224     va_end(args);
225 
226     return result;
227   }
strictModeErrorWithNotes(UniquePtr<JSErrorNotes> notes,unsigned errorNumber,...)228   [[nodiscard]] bool strictModeErrorWithNotes(UniquePtr<JSErrorNotes> notes,
229                                               unsigned errorNumber, ...) {
230     va_list args;
231     va_start(args, errorNumber);
232 
233     bool result = strictModeErrorWithNotesAtVA(
234         std::move(notes), mozilla::AsVariant(Current()), errorNumber, &args);
235 
236     va_end(args);
237 
238     return result;
239   }
strictModeErrorAt(uint32_t offset,unsigned errorNumber,...)240   [[nodiscard]] bool strictModeErrorAt(uint32_t offset, unsigned errorNumber,
241                                        ...) {
242     va_list args;
243     va_start(args, errorNumber);
244 
245     bool result = strictModeErrorWithNotesAtVA(
246         nullptr, mozilla::AsVariant(offset), errorNumber, &args);
247 
248     va_end(args);
249 
250     return result;
251   }
strictModeErrorWithNotesAt(UniquePtr<JSErrorNotes> notes,uint32_t offset,unsigned errorNumber,...)252   [[nodiscard]] bool strictModeErrorWithNotesAt(UniquePtr<JSErrorNotes> notes,
253                                                 uint32_t offset,
254                                                 unsigned errorNumber, ...) {
255     va_list args;
256     va_start(args, errorNumber);
257 
258     bool result = strictModeErrorWithNotesAtVA(
259         std::move(notes), mozilla::AsVariant(offset), errorNumber, &args);
260 
261     va_end(args);
262 
263     return result;
264   }
strictModeErrorNoOffset(unsigned errorNumber,...)265   [[nodiscard]] bool strictModeErrorNoOffset(unsigned errorNumber, ...) {
266     va_list args;
267     va_start(args, errorNumber);
268 
269     bool result = strictModeErrorWithNotesAtVA(
270         nullptr, mozilla::AsVariant(NoOffset()), errorNumber, &args);
271 
272     va_end(args);
273 
274     return result;
275   }
strictModeErrorWithNotesNoOffset(UniquePtr<JSErrorNotes> notes,unsigned errorNumber,...)276   [[nodiscard]] bool strictModeErrorWithNotesNoOffset(
277       UniquePtr<JSErrorNotes> notes, unsigned errorNumber, ...) {
278     va_list args;
279     va_start(args, errorNumber);
280 
281     bool result = strictModeErrorWithNotesAtVA(
282         std::move(notes), mozilla::AsVariant(NoOffset()), errorNumber, &args);
283 
284     va_end(args);
285 
286     return result;
287   }
strictModeErrorWithNotesAtVA(UniquePtr<JSErrorNotes> notes,const ErrorOffset & offset,unsigned errorNumber,va_list * args)288   [[nodiscard]] bool strictModeErrorWithNotesAtVA(UniquePtr<JSErrorNotes> notes,
289                                                   const ErrorOffset& offset,
290                                                   unsigned errorNumber,
291                                                   va_list* args) {
292     if (!strictMode()) {
293       return true;
294     }
295 
296     ErrorMetadata metadata;
297     if (!computeErrorMetadata(&metadata, offset)) {
298       return false;
299     }
300 
301     ReportCompileErrorLatin1(getContext(), std::move(metadata),
302                              std::move(notes), errorNumber, args);
303     return false;
304   }
305 
306   // Reports a warning, or an error if the warning is treated as an error.
compileWarning(ErrorMetadata && metadata,UniquePtr<JSErrorNotes> notes,unsigned errorNumber,va_list * args)307   [[nodiscard]] bool compileWarning(ErrorMetadata&& metadata,
308                                     UniquePtr<JSErrorNotes> notes,
309                                     unsigned errorNumber, va_list* args) {
310     return ReportCompileWarning(getContext(), std::move(metadata),
311                                 std::move(notes), errorNumber, args);
312   }
313 };
314 
315 // An interface class to provide miscellaneous methods used by error reporting
316 // etc.  They're mostly used by BytecodeCompiler, BytecodeEmitter, and helper
317 // classes for emitter.
318 class ErrorReporter : public ErrorReportMixin {
319  public:
320   // Returns the line and column numbers for given offset.
321   virtual void lineAndColumnAt(size_t offset, uint32_t* line,
322                                uint32_t* column) const = 0;
323 
324   // Returns the line and column numbers for current offset.
325   virtual void currentLineAndColumn(uint32_t* line, uint32_t* column) const = 0;
326 
327   // Sets *onThisLine to true if the given offset is inside the given line
328   // number `lineNum`, or false otherwise, and returns true.
329   //
330   // Return false if an error happens.  This method itself doesn't report an
331   // error, and any failure is supposed to be reported as OOM in the caller.
332   virtual bool isOnThisLine(size_t offset, uint32_t lineNum,
333                             bool* onThisLine) const = 0;
334 
335   // Returns the line number for given offset.
336   virtual uint32_t lineAt(size_t offset) const = 0;
337 
338   // Returns the column number for given offset.
339   virtual uint32_t columnAt(size_t offset) const = 0;
340 
341   // Returns true if tokenization is already started and hasn't yet finished.
342   // currentLineAndColumn returns meaningful value only if this is true.
343   virtual bool hasTokenizationStarted() const = 0;
344 
345   // Returns the filename which is currently being compiled.
346   virtual const char* getFilename() const = 0;
347 };
348 
349 }  // namespace frontend
350 }  // namespace js
351 
352 #endif  // frontend_ErrorReporter_h
353