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_BinASTTokenReaderMultipart_h 8 #define frontend_BinASTTokenReaderMultipart_h 9 10 #include "mozilla/Maybe.h" 11 12 #include "frontend/BinASTRuntimeSupport.h" 13 #include "frontend/BinASTToken.h" 14 #include "frontend/BinASTTokenReaderBase.h" 15 16 #include "js/Result.h" 17 18 namespace js { 19 namespace frontend { 20 21 /** 22 * A token reader implementing the "multipart" serialization format for BinAST. 23 * 24 * This serialization format, which is also supported by the reference 25 * implementation of the BinAST compression suite, is designed to be 26 * space- and time-efficient. 27 * 28 * As other token readers for the BinAST: 29 * 30 * - the reader does not support error recovery; 31 * - the reader does not support lookahead or pushback. 32 */ 33 class MOZ_STACK_CLASS BinASTTokenReaderMultipart 34 : public BinASTTokenReaderBase { 35 public: 36 class AutoList; 37 class AutoTaggedTuple; 38 39 using CharSlice = BinaryASTSupport::CharSlice; 40 using RootContext = BinASTTokenReaderBase::RootContext; 41 using ListContext = BinASTTokenReaderBase::ListContext; 42 using FieldContext = BinASTTokenReaderBase::FieldContext; 43 using FieldOrRootContext = BinASTTokenReaderBase::FieldOrRootContext; 44 using FieldOrListContext = BinASTTokenReaderBase::FieldOrListContext; 45 using Chars = CharSlice; 46 47 public: 48 /** 49 * Construct a token reader. 50 * 51 * Does NOT copy the buffer. 52 */ 53 BinASTTokenReaderMultipart(JSContext* cx, ErrorReporter* er, 54 const uint8_t* start, const size_t length); 55 56 /** 57 * Construct a token reader. 58 * 59 * Does NOT copy the buffer. 60 */ 61 BinASTTokenReaderMultipart(JSContext* cx, ErrorReporter* er, 62 const Vector<uint8_t>& chars); 63 64 ~BinASTTokenReaderMultipart(); 65 66 /** 67 * Read the header of the file. 68 */ 69 MOZ_MUST_USE JS::Result<Ok> readHeader(); 70 71 /** 72 * Read the footer of the tree, that multipart format doesn't have. 73 */ readTreeFooter()74 MOZ_MUST_USE JS::Result<Ok> readTreeFooter() { return Ok(); } 75 76 // --- Primitive values. 77 // 78 // Note that the underlying format allows for a `null` value for primitive 79 // values. 80 // 81 // Reading will return an error either in case of I/O error or in case of 82 // a format problem. Reading if an exception in pending is an error and 83 // will cause assertion failures. Do NOT attempt to read once an exception 84 // has been cleared: the token reader does NOT support recovery, by design. 85 86 /** 87 * Read a single `true | false` value. 88 */ 89 MOZ_MUST_USE JS::Result<bool> readBool(const FieldContext&); 90 91 /** 92 * Read a single `number` value. 93 */ 94 MOZ_MUST_USE JS::Result<double> readDouble(const FieldContext&); 95 96 /** 97 * Read a single `string | null` value. 98 * 99 * Fails if that string is not valid UTF-8. 100 */ 101 MOZ_MUST_USE JS::Result<JSAtom*> readMaybeAtom(const FieldContext&); 102 MOZ_MUST_USE JS::Result<JSAtom*> readAtom(const FieldContext&); 103 104 /** 105 * Read a single IdentifierName value. 106 */ 107 MOZ_MUST_USE JS::Result<JSAtom*> readMaybeIdentifierName(const FieldContext&); 108 MOZ_MUST_USE JS::Result<JSAtom*> readIdentifierName(const FieldContext&); 109 110 /** 111 * Read a single PropertyKey value. 112 */ 113 MOZ_MUST_USE JS::Result<JSAtom*> readPropertyKey(const FieldContext&); 114 115 /** 116 * Read a single `string | null` value. 117 * 118 * MAY check if that string is not valid UTF-8. 119 */ 120 MOZ_MUST_USE JS::Result<Ok> readChars(Chars&, const FieldContext&); 121 122 /** 123 * Read a single `BinASTVariant | null` value. 124 */ 125 private: 126 MOZ_MUST_USE JS::Result<BinASTVariant> readVariant(); 127 128 public: readVariant(const ListContext & context)129 MOZ_MUST_USE JS::Result<BinASTVariant> readVariant( 130 const ListContext& context) { 131 return readVariant(); 132 } readVariant(const FieldContext & context)133 MOZ_MUST_USE JS::Result<BinASTVariant> readVariant( 134 const FieldContext& context) { 135 return readVariant(); 136 } 137 138 /** 139 * Read over a single `[Skippable]` subtree value. 140 * 141 * This does *not* attempt to parse the subtree itself. Rather, the 142 * returned `SkippableSubTree` contains the necessary information 143 * to parse/tokenize the subtree at a later stage 144 */ 145 MOZ_MUST_USE JS::Result<SkippableSubTree> readSkippableSubTree( 146 const FieldContext&); 147 148 /** 149 * Register lazy script for later modification. 150 * Not used in multipart format. 151 */ registerLazyScript(FunctionBox * lazy)152 MOZ_MUST_USE JS::Result<Ok> registerLazyScript(FunctionBox* lazy) { 153 return Ok(); 154 } 155 156 // --- Composite values. 157 // 158 // The underlying format does NOT allows for a `null` composite value. 159 // 160 // Reading will return an error either in case of I/O error or in case of 161 // a format problem. Reading from a poisoned tokenizer is an error and 162 // will cause assertion failures. 163 164 /** 165 * Start reading a list. 166 * 167 * @param length (OUT) The number of elements in the list. 168 * @param guard (OUT) A guard, ensuring that we read the list correctly. 169 * 170 * The `guard` is dedicated to ensuring that reading the list has consumed 171 * exactly all the bytes from that list. The `guard` MUST therefore be 172 * destroyed at the point where the caller has reached the end of the list. 173 * If the caller has consumed too few/too many bytes, this will be reported 174 * in the call go `guard.done()`. 175 */ 176 MOZ_MUST_USE JS::Result<Ok> enterList(uint32_t& length, const ListContext&); 177 178 /** 179 * Start reading a tagged tuple. 180 * 181 * @param tag (OUT) The tag of the tuple. 182 * @param fields Ignored, provided for API compatibility. 183 * @param guard (OUT) A guard, ensuring that we read the tagged tuple 184 * correctly. 185 * 186 * The `guard` is dedicated to ensuring that reading the list has consumed 187 * exactly all the bytes from that tuple. The `guard` MUST therefore be 188 * destroyed at the point where the caller has reached the end of the tuple. 189 * If the caller has consumed too few/too many bytes, this will be reported 190 * in the call go `guard.done()`. 191 * 192 * @return out If the header of the tuple is invalid. 193 */ 194 MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinASTKind& tag); 195 enterTaggedTuple(BinASTKind & tag,const FieldOrRootContext &)196 MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinASTKind& tag, 197 const FieldOrRootContext&) { 198 return enterTaggedTuple(tag); 199 } enterTaggedTuple(BinASTKind & tag,const FieldOrListContext &)200 MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinASTKind& tag, 201 const FieldOrListContext&) { 202 return enterTaggedTuple(tag); 203 } enterTaggedTuple(BinASTKind & tag,const RootContext &)204 MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinASTKind& tag, 205 const RootContext&) { 206 return enterTaggedTuple(tag); 207 } enterTaggedTuple(BinASTKind & tag,const ListContext &)208 MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinASTKind& tag, 209 const ListContext&) { 210 return enterTaggedTuple(tag); 211 } enterTaggedTuple(BinASTKind & tag,const FieldContext &)212 MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(BinASTKind& tag, 213 const FieldContext&) { 214 return enterTaggedTuple(tag); 215 } enterInterface(BinASTKind & tag,const FieldOrRootContext &)216 MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag, 217 const FieldOrRootContext&) { 218 return enterTaggedTuple(tag); 219 } enterInterface(BinASTKind & tag,const FieldOrListContext &)220 MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag, 221 const FieldOrListContext&) { 222 return enterTaggedTuple(tag); 223 } enterInterface(BinASTKind & tag,const RootContext &)224 MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag, 225 const RootContext&) { 226 return enterTaggedTuple(tag); 227 } enterInterface(BinASTKind & tag,const ListContext &)228 MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag, 229 const ListContext&) { 230 return enterTaggedTuple(tag); 231 } enterInterface(BinASTKind & tag,const FieldContext &)232 MOZ_MUST_USE JS::Result<Ok> enterInterface(BinASTKind& tag, 233 const FieldContext&) { 234 return enterTaggedTuple(tag); 235 } enterSum(BinASTKind & tag,const FieldOrRootContext &)236 MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag, 237 const FieldOrRootContext&) { 238 return enterTaggedTuple(tag); 239 } enterSum(BinASTKind & tag,const FieldOrListContext &)240 MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag, 241 const FieldOrListContext&) { 242 return enterTaggedTuple(tag); 243 } enterSum(BinASTKind & tag,const RootContext &)244 MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag, const RootContext&) { 245 return enterTaggedTuple(tag); 246 } enterSum(BinASTKind & tag,const ListContext &)247 MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag, const ListContext&) { 248 return enterTaggedTuple(tag); 249 } enterSum(BinASTKind & tag,const FieldContext &)250 MOZ_MUST_USE JS::Result<Ok> enterSum(BinASTKind& tag, const FieldContext&) { 251 return enterTaggedTuple(tag); 252 } enterOptionalInterface(BinASTKind & tag,const FieldOrRootContext &)253 MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface( 254 BinASTKind& tag, const FieldOrRootContext&) { 255 return enterTaggedTuple(tag); 256 } enterOptionalInterface(BinASTKind & tag,const FieldOrListContext &)257 MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface( 258 BinASTKind& tag, const FieldOrListContext&) { 259 return enterTaggedTuple(tag); 260 } enterOptionalInterface(BinASTKind & tag,const RootContext &)261 MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface(BinASTKind& tag, 262 const RootContext&) { 263 return enterTaggedTuple(tag); 264 } enterOptionalInterface(BinASTKind & tag,const ListContext &)265 MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface(BinASTKind& tag, 266 const ListContext&) { 267 return enterTaggedTuple(tag); 268 } enterOptionalInterface(BinASTKind & tag,const FieldContext &)269 MOZ_MUST_USE JS::Result<Ok> enterOptionalInterface(BinASTKind& tag, 270 const FieldContext&) { 271 return enterTaggedTuple(tag); 272 } 273 274 /** 275 * Read a single unsigned long. 276 */ readUnsignedLong(const FieldContext &)277 MOZ_MUST_USE JS::Result<uint32_t> readUnsignedLong(const FieldContext&) { 278 return readInternalUint32(); 279 } 280 281 private: 282 /** 283 * Read a single uint32_t. 284 */ 285 MOZ_MUST_USE JS::Result<uint32_t> readInternalUint32(); 286 287 private: 288 // A mapping string index => BinASTVariant as extracted from the [STRINGS] 289 // section of the file. Populated lazily. 290 js::HashMap<uint32_t, BinASTVariant, DefaultHasher<uint32_t>, 291 SystemAllocPolicy> 292 variantsTable_; 293 294 enum class MetadataOwnership { Owned, Unowned }; 295 MetadataOwnership metadataOwned_ = MetadataOwnership::Owned; 296 BinASTSourceMetadataMultipart* metadata_; 297 298 const uint8_t* posBeforeTree_; 299 300 BinASTTokenReaderMultipart(const BinASTTokenReaderMultipart&) = delete; 301 BinASTTokenReaderMultipart(BinASTTokenReaderMultipart&&) = delete; 302 BinASTTokenReaderMultipart& operator=(BinASTTokenReaderMultipart&) = delete; 303 304 public: 305 void traceMetadata(JSTracer* trc); 306 BinASTSourceMetadata* takeMetadata(); 307 MOZ_MUST_USE JS::Result<Ok> initFromScriptSource(ScriptSource* scriptSource); 308 309 public: 310 // The following classes are used whenever we encounter a tuple/tagged 311 // tuple/list to make sure that: 312 // 313 // - if the construct "knows" its byte length, we have exactly consumed all 314 // the bytes (otherwise, this means that the file is corrupted, perhaps on 315 // purpose, so we need to reject the stream); 316 // - if the construct has a footer, once we are done reading it, we have 317 // reached the footer (this is to aid with debugging). 318 // 319 // In either case, the caller MUST call method `done()` of the guard once 320 // it is done reading the tuple/tagged tuple/list, to report any pending 321 // error. 322 323 // Base class used by other Auto* classes. 324 class MOZ_STACK_CLASS AutoBase { 325 protected: 326 explicit AutoBase(BinASTTokenReaderMultipart& reader); 327 ~AutoBase(); 328 329 friend BinASTTokenReaderMultipart; 330 331 public: 332 void init(); 333 334 protected: 335 // Set to `true` if `init()` has been called. Reset to `false` once 336 // all conditions have been checked. 337 bool initialized_; 338 BinASTTokenReaderMultipart& reader_; 339 }; 340 341 // Guard class used to ensure that `enterList` is used properly. 342 class MOZ_STACK_CLASS AutoList : public AutoBase { 343 public: 344 explicit AutoList(BinASTTokenReaderMultipart& reader); 345 346 // Check that we have properly read to the end of the list. 347 MOZ_MUST_USE JS::Result<Ok> done(); 348 349 protected: 350 friend BinASTTokenReaderMultipart; 351 }; 352 353 // Guard class used to ensure that `enterTaggedTuple` is used properly. 354 class MOZ_STACK_CLASS AutoTaggedTuple : public AutoBase { 355 public: 356 explicit AutoTaggedTuple(BinASTTokenReaderMultipart& reader); 357 358 // Check that we have properly read to the end of the tuple. 359 MOZ_MUST_USE JS::Result<Ok> done(); 360 }; 361 362 // Compare a `Chars` and a string literal (ONLY a string literal). 363 template <size_t N> equals(const Chars & left,const char (& right)[N])364 static bool equals(const Chars& left, const char (&right)[N]) { 365 MOZ_ASSERT(N > 0); 366 MOZ_ASSERT(right[N - 1] == 0); 367 if (left.byteLen_ + 1 /* implicit NUL */ != N) { 368 return false; 369 } 370 371 if (!std::equal(left.start_, left.start_ + left.byteLen_, right)) { 372 return false; 373 } 374 375 return true; 376 } 377 }; 378 379 } // namespace frontend 380 } // namespace js 381 382 #endif // frontend_BinASTTokenReaderMultipart_h 383