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_BinASTSupport_h
8 #define frontend_BinASTSupport_h
9 
10 #include "mozilla/Assertions.h"     // MOZ_ASSERT
11 #include "mozilla/HashFunctions.h"  // mozilla::HashString
12 #include "mozilla/Likely.h"         // MOZ_UNLIKELY
13 
14 #include <stdint.h>  // uint8_t, uint32_t
15 #include <string.h>  // strncmp
16 
17 #include "frontend/BinASTToken.h"  // BinASTVariant, BinASTField, BinASTKind
18 #include "gc/DeletePolicy.h"       // GCManagedDeletePolicy
19 
20 #include "js/AllocPolicy.h"  // SystemAllocPolicy
21 #include "js/HashTable.h"    // HashMap, HashNumber
22 #include "js/Result.h"       // JS::Result
23 #include "js/TracingAPI.h"   // JSTracer
24 #include "js/UniquePtr.h"    // UniquePtr
25 #include "js/Vector.h"       // Vector
26 
27 class JS_PUBLIC_API JSAtom;
28 struct JS_PUBLIC_API JSContext;
29 
30 namespace js {
31 
32 class ScriptSource;
33 
34 // Support for parsing JS Binary ASTs.
35 struct BinaryASTSupport {
36   using BinASTVariant = js::frontend::BinASTVariant;
37   using BinASTField = js::frontend::BinASTField;
38   using BinASTKind = js::frontend::BinASTKind;
39 
40   // A structure designed to perform fast char* + length lookup
41   // without copies.
42   struct CharSlice {
43     const char* start_;
44     uint32_t byteLen_;
45     CharSlice(const CharSlice& other) = default;
CharSliceBinaryASTSupport::CharSlice46     CharSlice(const char* start, const uint32_t byteLen)
47         : start_(start), byteLen_(byteLen) {}
CharSliceBinaryASTSupport::CharSlice48     explicit CharSlice(JSContext*) : CharSlice(nullptr, 0) {}
beginBinaryASTSupport::CharSlice49     const char* begin() const { return start_; }
endBinaryASTSupport::CharSlice50     const char* end() const { return start_ + byteLen_; }
51 #ifdef DEBUG
dumpBinaryASTSupport::CharSlice52     void dump() const {
53       for (auto c : *this) {
54         fprintf(stderr, "%c", c);
55       }
56 
57       fprintf(stderr, " (%u)", byteLen_);
58     }
59 #endif  // DEBUG
60 
61     using Lookup = const CharSlice;
hashBinaryASTSupport::CharSlice62     static js::HashNumber hash(Lookup l) {
63       return mozilla::HashString(l.start_, l.byteLen_);
64     }
matchBinaryASTSupport::CharSlice65     static bool match(const Lookup key, Lookup lookup) {
66       if (key.byteLen_ != lookup.byteLen_) {
67         return false;
68       }
69       return strncmp(key.start_, lookup.start_, key.byteLen_) == 0;
70     }
71   };
72 
73   BinaryASTSupport();
74 
75   JS::Result<const BinASTVariant*> binASTVariant(JSContext*, const CharSlice);
76   JS::Result<const BinASTKind*> binASTKind(JSContext*, const CharSlice);
77 
78   bool ensureBinTablesInitialized(JSContext*);
79 
80  private:
81   bool ensureBinASTKindsInitialized(JSContext*);
82   bool ensureBinASTVariantsInitialized(JSContext*);
83 
84  private:
85   // A HashMap that can be queried without copies from a CharSlice key.
86   // Initialized on first call. Keys are CharSlices into static strings.
87   using BinASTKindMap = js::HashMap<const CharSlice, BinASTKind, CharSlice,
88                                     js::SystemAllocPolicy>;
89   BinASTKindMap binASTKindMap_;
90 
91   using BinASTFieldMap = js::HashMap<const CharSlice, BinASTField, CharSlice,
92                                      js::SystemAllocPolicy>;
93   BinASTFieldMap binASTFieldMap_;
94 
95   using BinASTVariantMap = js::HashMap<const CharSlice, BinASTVariant,
96                                        CharSlice, js::SystemAllocPolicy>;
97   BinASTVariantMap binASTVariantMap_;
98 };
99 
100 namespace frontend {
101 
102 class BinASTSourceMetadataMultipart;
103 class BinASTSourceMetadataContext;
104 
105 class BinASTSourceMetadata {
106  public:
107   enum class Type : uint8_t {
108     Multipart = 0,
109     Context = 1,
110   };
111 
112  private:
113   Type type_;
114 
115  public:
116   BinASTSourceMetadata() = delete;
BinASTSourceMetadata(Type type)117   explicit BinASTSourceMetadata(Type type) : type_(type) {}
118 #ifdef JS_BUILD_BINAST
119   ~BinASTSourceMetadata();
120 #else
121   // If JS_BUILD_BINAST isn't defined, BinASTRuntimeSupport.cpp isn't built,
122   // but still the destructor is referred from ScriptSource::BinAST.
~BinASTSourceMetadata()123   ~BinASTSourceMetadata() {}
124 #endif  // JS_BUILD_BINAST
125 
type()126   Type type() const { return type_; }
127 
setType(Type type)128   void setType(Type type) { type_ = type; }
129 
isMultipart()130   bool isMultipart() const { return type_ == Type::Multipart; }
isContext()131   bool isContext() const { return type_ == Type::Context; }
132 
133   inline BinASTSourceMetadataMultipart* asMultipart();
134   inline BinASTSourceMetadataContext* asContext();
135 
136   void trace(JSTracer* tracer);
137 };
138 
alignas(uintptr_t)139 class alignas(uintptr_t) BinASTSourceMetadataMultipart
140     : public BinASTSourceMetadata {
141   using CharSlice = BinaryASTSupport::CharSlice;
142 
143   const uint32_t numStrings_;
144   const uint32_t numBinASTKinds_;
145 
146   // The data lives inline in the allocation, after this class.
147   inline JSAtom** atomsBase() {
148     return reinterpret_cast<JSAtom**>(reinterpret_cast<uintptr_t>(this + 1));
149   }
150   inline CharSlice* sliceBase() {
151     return reinterpret_cast<CharSlice*>(
152         reinterpret_cast<uintptr_t>(atomsBase()) +
153         numStrings_ * sizeof(JSAtom*));
154   }
155   inline BinASTKind* binASTKindBase() {
156     return reinterpret_cast<BinASTKind*>(
157         reinterpret_cast<uintptr_t>(sliceBase()) +
158         numStrings_ * sizeof(CharSlice));
159   }
160 
161   static inline size_t totalSize(uint32_t numBinASTKinds, uint32_t numStrings) {
162     return sizeof(BinASTSourceMetadataMultipart) +
163            numStrings * sizeof(JSAtom*) + numStrings * sizeof(CharSlice) +
164            numBinASTKinds * sizeof(BinASTKind);
165   }
166 
167   BinASTSourceMetadataMultipart(uint32_t numBinASTKinds, uint32_t numStrings)
168       : BinASTSourceMetadata(Type::Multipart),
169         numStrings_(numStrings),
170         numBinASTKinds_(numBinASTKinds) {}
171 
172   void release() {}
173 
174   friend class BinASTSourceMetadata;
175 
176   friend class js::ScriptSource;
177 
178  public:
179   // Create the BinASTSourceMetadataMultipart instance, with copying
180   // `binASTKinds`, and allocating `numStrings` atoms/slices.
181   //
182   // Use this when the list of BinASTKind is known at the point of allocating
183   // metadata, like when parsing .binjs file.
184   static BinASTSourceMetadataMultipart* create(
185       const Vector<BinASTKind>& binASTKinds, uint32_t numStrings);
186 
187   // Create the BinASTSourceMetadataMultipart instance, with allocating
188   // `numBinASTKinds` BinASTKinds and `numStrings` atoms/slices.
189   //
190   // Use this when the list of BinASTKind is unknown at the point of allocating
191   // metadata, like when performing XDR decode.
192   static BinASTSourceMetadataMultipart* create(uint32_t numBinASTKinds,
193                                                uint32_t numStrings);
194 
195   inline uint32_t numBinASTKinds() { return numBinASTKinds_; }
196 
197   inline uint32_t numStrings() { return numStrings_; }
198 
199   inline BinASTKind& getBinASTKind(uint32_t index) {
200     MOZ_ASSERT(index < numBinASTKinds_);
201     return binASTKindBase()[index];
202   }
203 
204   inline CharSlice& getSlice(uint32_t index) {
205     MOZ_ASSERT(index < numStrings_);
206     return sliceBase()[index];
207   }
208 
209   inline JSAtom*& getAtom(uint32_t index) {
210     MOZ_ASSERT(index < numStrings_);
211     return atomsBase()[index];
212   }
213 
214   void trace(JSTracer* tracer);
215 };
216 
217 class HuffmanDictionaryForMetadata;
218 
alignas(uintptr_t)219 class alignas(uintptr_t) BinASTSourceMetadataContext
220     : public BinASTSourceMetadata {
221   const uint32_t numStrings_;
222   HuffmanDictionaryForMetadata* dictionary_;
223 
224   // The data lives inline in the allocation, after this class.
225   inline JSAtom** atomsBase() {
226     return reinterpret_cast<JSAtom**>(reinterpret_cast<uintptr_t>(this + 1));
227   }
228 
229   static inline size_t totalSize(uint32_t numStrings) {
230     return sizeof(BinASTSourceMetadataContext) + numStrings * sizeof(JSAtom*);
231   }
232 
233   explicit BinASTSourceMetadataContext(uint32_t numStrings)
234       : BinASTSourceMetadata(Type::Context),
235         numStrings_(numStrings),
236         dictionary_(nullptr) {}
237 
238   void release();
239 
240   friend class BinASTSourceMetadata;
241 
242   friend class js::ScriptSource;
243 
244  public:
245   // Create the BinASTSourceMetadataContext instance, with allocating
246   // `numStrings` atoms.
247   static BinASTSourceMetadataContext* create(uint32_t numStrings);
248 
249   HuffmanDictionaryForMetadata* dictionary() { return dictionary_; }
250   void setDictionary(HuffmanDictionaryForMetadata* dictionary) {
251     MOZ_ASSERT(!dictionary_);
252     MOZ_ASSERT(dictionary);
253 
254     dictionary_ = dictionary;
255   }
256 
257   inline uint32_t numStrings() { return numStrings_; }
258 
259   inline JSAtom*& getAtom(uint32_t index) {
260     MOZ_ASSERT(index < numStrings_);
261     return atomsBase()[index];
262   }
263 
264   void trace(JSTracer* tracer);
265 };
266 
asMultipart()267 BinASTSourceMetadataMultipart* BinASTSourceMetadata::asMultipart() {
268   MOZ_ASSERT(isMultipart());
269   return reinterpret_cast<BinASTSourceMetadataMultipart*>(this);
270 }
271 
asContext()272 BinASTSourceMetadataContext* BinASTSourceMetadata::asContext() {
273   MOZ_ASSERT(isContext());
274   return reinterpret_cast<BinASTSourceMetadataContext*>(this);
275 }
276 
277 }  // namespace frontend
278 
279 using UniqueBinASTSourceMetadataPtr =
280     UniquePtr<frontend::BinASTSourceMetadata,
281               GCManagedDeletePolicy<frontend::BinASTSourceMetadata>>;
282 
283 }  // namespace js
284 
285 #endif  // frontend_BinASTSupport_h
286