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