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_BinASTParserPerTokenizer_h
8 #define frontend_BinASTParserPerTokenizer_h
9 
10 /**
11  * A Binary AST parser.
12  *
13  * At the time of this writing, this parser implements the grammar of ES5
14  * and trusts its input (in particular, variable declarations).
15  */
16 
17 #include "mozilla/Maybe.h"
18 
19 #include <type_traits>
20 
21 #include "frontend/BCEParserHandle.h"
22 #include "frontend/BinASTEnum.h"
23 #include "frontend/BinASTParserBase.h"
24 #include "frontend/BinASTToken.h"
25 #include "frontend/BinASTTokenReaderContext.h"
26 #include "frontend/BinASTTokenReaderMultipart.h"
27 #include "frontend/FullParseHandler.h"
28 #include "frontend/FunctionSyntaxKind.h"  // FunctionSyntaxKind
29 #include "frontend/ParseContext.h"
30 #include "frontend/ParseNode.h"
31 #include "frontend/SharedContext.h"
32 #include "js/CompileOptions.h"
33 #include "js/GCHashTable.h"
34 #include "js/GCVector.h"
35 #include "js/Result.h"
36 #include "vm/ErrorReporting.h"
37 #include "vm/GeneratorAndAsyncKind.h"  // js::GeneratorKind, js::FunctionAsyncKind
38 
39 namespace js {
40 namespace frontend {
41 
42 template <typename Tok>
43 class BinASTParser;
44 
45 /**
46  * The parser for a Binary AST.
47  *
48  * By design, this parser never needs to backtrack or look ahead. Errors are not
49  * recoverable.
50  */
51 template <typename Tok>
52 class BinASTParserPerTokenizer : public BinASTParserBase,
53                                  public ErrorReporter,
54                                  public BCEParserHandle {
55  public:
56   using Tokenizer = Tok;
57 
58   using AutoList = typename Tokenizer::AutoList;
59   using AutoTaggedTuple = typename Tokenizer::AutoTaggedTuple;
60   using Chars = typename Tokenizer::Chars;
61   using RootContext = BinASTTokenReaderBase::RootContext;
62   using FieldOrRootContext = BinASTTokenReaderBase::FieldOrRootContext;
63 
64  public:
65   // Auto-generated types.
66   using AssertedDeclaredKind = binast::AssertedDeclaredKind;
67   using VariableDeclarationKind = binast::VariableDeclarationKind;
68 
69  public:
70   BinASTParserPerTokenizer(JSContext* cx, CompilationInfo& compilationInfo,
71                            const JS::ReadOnlyCompileOptions& options,
72                            Handle<BaseScript*> lazyScript = nullptr);
73 
74   /**
75    * Parse a buffer, returning a node (which may be nullptr) in case of success
76    * or Nothing() in case of error.
77    *
78    * The instance of `ParseNode` MAY NOT survive the
79    * `BinASTParserPerTokenizer`. Indeed, destruction of the
80    * `BinASTParserPerTokenizer` will also destroy the `ParseNode`.
81    *
82    * In case of error, the parser reports the JS error.
83    */
84   JS::Result<ParseNode*> parse(GlobalSharedContext* globalsc,
85                                const uint8_t* start, const size_t length,
86                                BinASTSourceMetadata** metadataPtr = nullptr);
87   JS::Result<ParseNode*> parse(GlobalSharedContext* globalsc,
88                                const Vector<uint8_t>& data,
89                                BinASTSourceMetadata** metadataPtr = nullptr);
90 
91   JS::Result<FunctionNode*> parseLazyFunction(ScriptSource* scriptSource,
92                                               const size_t firstOffset);
93 
94  protected:
95   MOZ_MUST_USE JS::Result<ParseNode*> parseAux(
96       GlobalSharedContext* globalsc, const uint8_t* start, const size_t length,
97       BinASTSourceMetadata** metadataPtr = nullptr);
98 
99   // --- Raise errors.
100   //
101   // These methods return a (failed) JS::Result for convenience.
102 
103   MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidClosedVar(
104       JSAtom* name);
105   MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&>
106   raiseMissingVariableInAssertedScope(JSAtom* name);
107   MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&>
108   raiseMissingDirectEvalInAssertedScope();
109   MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidKind(
110       const char* superKind, const BinASTKind kind);
111   MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseInvalidVariant(
112       const char* kind, const BinASTVariant value);
113   MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseMissingField(
114       const char* kind, const BinASTField field);
115   MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseEmpty(
116       const char* description);
117   MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseOOM();
118   MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseError(
119       const char* description);
120   MOZ_MUST_USE mozilla::GenericErrorResult<JS::Error&> raiseError(
121       BinASTKind kind, const char* description);
122 
123   // Ensure that this parser will never be used again.
124   void poison();
125 
126   // The owner or the target of Asserted*Scope.
127   enum class AssertedScopeKind {
128     Block,
129     Catch,
130     Global,
131     Parameter,
132     Var,
133   };
134 
135   // --- Auxiliary parsing functions
136 
137   // Build a function object for a function-producing production. Called AFTER
138   // creating the scope.
139   JS::Result<FunctionBox*> buildFunctionBox(GeneratorKind generatorKind,
140                                             FunctionAsyncKind functionAsyncKind,
141                                             FunctionSyntaxKind syntax,
142                                             ParseNode* name);
143 
144   JS::Result<FunctionNode*> makeEmptyFunctionNode(
145       const size_t start, const FunctionSyntaxKind syntaxKind,
146       FunctionBox* funbox);
147   MOZ_MUST_USE JS::Result<Ok> setFunctionParametersAndBody(FunctionNode* fun,
148                                                            ListNode* params,
149                                                            ParseNode* body);
150 
151   MOZ_MUST_USE JS::Result<Ok> finishEagerFunction(FunctionBox* funbox,
152                                                   uint32_t nargs);
153   MOZ_MUST_USE JS::Result<Ok> finishLazyFunction(FunctionBox* funbox,
154                                                  uint32_t nargs, size_t start,
155                                                  size_t end);
156 
157   // Add name to a given scope.
158   MOZ_MUST_USE JS::Result<Ok> addScopeName(
159       AssertedScopeKind scopeKind, HandleAtom name, ParseContext::Scope* scope,
160       DeclarationKind declKind, bool isCaptured, bool allowDuplicateName);
161 
162   void captureFunctionName();
163 
164   // Map AssertedScopeKind and AssertedDeclaredKind for single binding to
165   // corresponding ParseContext::Scope to store the binding, and
166   // DeclarationKind for the binding.
167   MOZ_MUST_USE JS::Result<Ok> getDeclaredScope(AssertedScopeKind scopeKind,
168                                                AssertedDeclaredKind kind,
169                                                ParseContext::Scope*& scope,
170                                                DeclarationKind& declKind);
171   MOZ_MUST_USE JS::Result<Ok> getBoundScope(AssertedScopeKind scopeKind,
172                                             ParseContext::Scope*& scope,
173                                             DeclarationKind& declKind);
174 
175   MOZ_MUST_USE JS::Result<Ok> checkBinding(JSAtom* name);
176 
177   MOZ_MUST_USE JS::Result<Ok> checkPositionalParameterIndices(
178       Handle<GCVector<JSAtom*>> positionalParams, ListNode* params);
179 
180   MOZ_MUST_USE JS::Result<Ok> checkFunctionLength(uint32_t expectedLength);
181 
182   // When leaving a scope, check that none of its bindings are known closed over
183   // and un-marked.
184   MOZ_MUST_USE JS::Result<Ok> checkClosedVars(ParseContext::Scope& scope);
185 
186   // As a convenience, a helper that checks the body, parameter, and recursive
187   // binding scopes.
188   MOZ_MUST_USE JS::Result<Ok> checkFunctionClosedVars();
189 
190   // --- Utilities.
191 
192   MOZ_MUST_USE JS::Result<Ok> prependDirectivesToBody(ListNode* body,
193                                                       ListNode* directives);
194 
195   MOZ_MUST_USE JS::Result<Ok> prependDirectivesImpl(ListNode* body,
196                                                     ParseNode* directive);
197 
198   // Optionally force a strict context without restarting the parse when we see
199   // a strict directive.
200   void forceStrictIfNecessary(SharedContext* sc, ListNode* directives);
201 
202   // Whether invalid BinASTKind/BinASTVariant can be encoded in the file.
203   // This is used to avoid generating unnecessary branches for more
204   // optimized format.
isInvalidKindPossible()205   static constexpr bool isInvalidKindPossible() {
206     return std::is_same_v<Tok, BinASTTokenReaderMultipart>;
207   }
isInvalidVariantPossible()208   static constexpr bool isInvalidVariantPossible() {
209     return std::is_same_v<Tok, BinASTTokenReaderMultipart>;
210   }
211 
212  protected:
213   // Implement ErrorReportMixin.
214   const JS::ReadOnlyCompileOptions& options_;
215 
options()216   const JS::ReadOnlyCompileOptions& options() const override {
217     return this->options_;
218   }
219 
getContext()220   JSContext* getContext() const override { return cx_; };
221 
strictMode()222   MOZ_MUST_USE bool strictMode() const override { return pc_->sc()->strict(); }
223 
224   MOZ_MUST_USE bool computeErrorMetadata(ErrorMetadata* err,
225                                          const ErrorOffset& offset) override;
226 
227  private:
228   void trace(JSTracer* trc) final;
229 
230  public:
errorReporter()231   virtual ErrorReporter& errorReporter() override { return *this; }
errorReporter()232   virtual const ErrorReporter& errorReporter() const override { return *this; }
233 
astGenerator()234   virtual FullParseHandler& astGenerator() override { return handler_; }
235 
236  public:
237   // Implement ErrorReporter.
238 
lineAndColumnAt(size_t offset,uint32_t * line,uint32_t * column)239   virtual void lineAndColumnAt(size_t offset, uint32_t* line,
240                                uint32_t* column) const override {
241     *line = lineAt(offset);
242     *column = columnAt(offset);
243   }
lineAt(size_t offset)244   virtual uint32_t lineAt(size_t offset) const override { return 0; }
columnAt(size_t offset)245   virtual uint32_t columnAt(size_t offset) const override { return offset; }
246 
isOnThisLine(size_t offset,uint32_t lineNum,bool * isOnSameLine)247   virtual bool isOnThisLine(size_t offset, uint32_t lineNum,
248                             bool* isOnSameLine) const override {
249     if (lineNum != 0) {
250       return false;
251     }
252     *isOnSameLine = true;
253     return true;
254   }
255 
currentLineAndColumn(uint32_t * line,uint32_t * column)256   virtual void currentLineAndColumn(uint32_t* line,
257                                     uint32_t* column) const override {
258     *line = 0;
259     *column = offset();
260   }
offset()261   size_t offset() const {
262     if (tokenizer_.isSome()) {
263       return tokenizer_->offset();
264     }
265 
266     return 0;
267   }
hasTokenizationStarted()268   virtual bool hasTokenizationStarted() const override {
269     return tokenizer_.isSome();
270   }
getFilename()271   virtual const char* getFilename() const override {
272     return this->options_.filename();
273   }
274 
275  protected:
276   Rooted<BaseScript*> lazyScript_;
277   FullParseHandler handler_;
278 
279   mozilla::Maybe<Tokenizer> tokenizer_;
280   VariableDeclarationKind variableDeclarationKind_;
281 
282   friend class BinASTParseContext;
283   friend class AutoVariableDeclarationKind;
284 
285   // Helper class: Restore field `variableDeclarationKind` upon leaving a scope.
286   class MOZ_RAII AutoVariableDeclarationKind {
287    public:
AutoVariableDeclarationKind(BinASTParserPerTokenizer<Tok> * parser MOZ_GUARD_OBJECT_NOTIFIER_PARAM)288     explicit AutoVariableDeclarationKind(
289         BinASTParserPerTokenizer<Tok>* parser MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
290         : parser_(parser), kind(parser->variableDeclarationKind_) {
291       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
292     }
~AutoVariableDeclarationKind()293     ~AutoVariableDeclarationKind() { parser_->variableDeclarationKind_ = kind; }
294 
295    private:
296     BinASTParserPerTokenizer<Tok>* parser_;
297     BinASTParserPerTokenizer<Tok>::VariableDeclarationKind kind;
298     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
299   };
300 
301   template <typename CharsT>
parseRegExpFlags(CharsT flags,size_t length,JS::RegExpFlags * reflags)302   bool parseRegExpFlags(CharsT flags, size_t length, JS::RegExpFlags* reflags) {
303     MOZ_ASSERT(*reflags == JS::RegExpFlag::NoFlags);
304     for (size_t i = 0; i < length; i++) {
305       auto c = flags[i];
306       if (c == 'g' && !reflags->global()) {
307         *reflags |= JS::RegExpFlag::Global;
308       } else if (c == 'i' && !reflags->ignoreCase()) {
309         *reflags |= JS::RegExpFlag::IgnoreCase;
310       } else if (c == 'm' && !reflags->multiline()) {
311         *reflags |= JS::RegExpFlag::Multiline;
312       }
313 #ifdef ENABLE_NEW_REGEXP
314       else if (c == 's' && !reflags->dotAll()) {
315         *reflags |= JS::RegExpFlag::DotAll;
316       }
317 #endif
318       else if (c == 'u' && !reflags->unicode()) {
319         *reflags |= JS::RegExpFlag::Unicode;
320       } else if (c == 'y' && !reflags->sticky()) {
321         *reflags |= JS::RegExpFlag::Sticky;
322       } else {
323         return false;
324       }
325     }
326 
327     return true;
328   }
329 
parseRegExpFlags(Chars flags,JS::RegExpFlags * reflags)330   bool parseRegExpFlags(Chars flags, JS::RegExpFlags* reflags) {
331     return parseRegExpFlags(flags.begin(), flags.end() - flags.begin(),
332                             reflags);
333   }
334 
parseRegExpFlags(HandleAtom flags,JS::RegExpFlags * reflags)335   bool parseRegExpFlags(HandleAtom flags, JS::RegExpFlags* reflags) {
336     JS::AutoCheckCannotGC nogc;
337     if (flags->hasLatin1Chars()) {
338       return parseRegExpFlags(flags->latin1Chars(nogc), flags->length(),
339                               reflags);
340     }
341     return parseRegExpFlags(flags->twoByteChars(nogc), flags->length(),
342                             reflags);
343   }
344 
345  private:
346   // Some methods in this class require access to auto-generated methods in
347   // BinASTParser which derives this class.
348   // asFinalParser methods provide the access to BinASTParser class methods
349   // of this instance.
350   using FinalParser = BinASTParser<Tok>;
351 
352   inline FinalParser* asFinalParser();
353   inline const FinalParser* asFinalParser() const;
354 };
355 
356 class BinASTParseContext : public ParseContext {
357  public:
358   template <typename Tok>
BinASTParseContext(JSContext * cx,BinASTParserPerTokenizer<Tok> * parser,SharedContext * sc,Directives * newDirectives)359   BinASTParseContext(JSContext* cx, BinASTParserPerTokenizer<Tok>* parser,
360                      SharedContext* sc, Directives* newDirectives)
361       : ParseContext(cx, parser->pc_, sc, *parser, parser->getCompilationInfo(),
362                      newDirectives, /* isFull = */ true) {}
363 };
364 
365 extern template class BinASTParserPerTokenizer<BinASTTokenReaderContext>;
366 extern template class BinASTParserPerTokenizer<BinASTTokenReaderMultipart>;
367 
368 }  // namespace frontend
369 }  // namespace js
370 
371 #endif  // frontend_BinASTParserPerTokenizer_h
372