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