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