1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef frontend_BinASTTokenReaderMultipart_h
#define frontend_BinASTTokenReaderMultipart_h
#include "mozilla/Maybe.h"
#include "frontend/BinASTRuntimeSupport.h"
#include "frontend/BinASTToken.h"
#include "frontend/BinASTTokenReaderBase.h"
#include "js/Result.h"
namespace js {
namespace frontend {
/**
* A token reader implementing the "multipart" serialization format for BinAST.
*
* This serialization format, which is also supported by the reference
* implementation of the BinAST compression suite, is designed to be
* space- and time-efficient.
*
* As other token readers for the BinAST:
*
* - the reader does not support error recovery;
* - the reader does not support lookahead or pushback.
*/
class MOZ_STACK_CLASS BinASTTokenReaderMultipart
: public BinASTTokenReaderBase {
public:
class AutoList;
class AutoTaggedTuple;
using CharSlice = BinaryASTSupport::CharSlice;
// This implementation of `BinASTFields` is effectively `void`, as the format
// does not embed field information.
class BinASTFields {
public:
explicit BinASTFields(JSContext*) {}
};
using Chars = CharSlice;
public:
/**
* Construct a token reader.
*
* Does NOT copy the buffer.
*/
BinASTTokenReaderMultipart(JSContext* cx, ErrorReporter* er,
const uint8_t* start, const size_t length);
/**
* Construct a token reader.
*
* Does NOT copy the buffer.
*/
BinASTTokenReaderMultipart(JSContext* cx, ErrorReporter* er,
const Vector<uint8_t>& chars);
~BinASTTokenReaderMultipart();
/**
* Read the header of the file.
*/
MOZ_MUST_USE JS::Result<Ok> readHeader();
// --- Primitive values.
//
// Note that the underlying format allows for a `null` value for primitive
// values.
//
// Reading will return an error either in case of I/O error or in case of
// a format problem. Reading if an exception in pending is an error and
// will cause assertion failures. Do NOT attempt to read once an exception
// has been cleared: the token reader does NOT support recovery, by design.
/**
* Read a single `true | false` value.
*/
MOZ_MUST_USE JS::Result<bool> readBool();
/**
* Read a single `number` value.
*/
MOZ_MUST_USE JS::Result<double> readDouble();
/**
* Read a single `string | null` value.
*
* Fails if that string is not valid UTF-8.
*/
MOZ_MUST_USE JS::Result<JSAtom*> readMaybeAtom();
MOZ_MUST_USE JS::Result<JSAtom*> readAtom();
/**
* Read a single IdentifierName value.
*/
MOZ_MUST_USE JS::Result<JSAtom*> readMaybeIdentifierName();
MOZ_MUST_USE JS::Result<JSAtom*> readIdentifierName();
/**
* Read a single PropertyKey value.
*/
MOZ_MUST_USE JS::Result<JSAtom*> readPropertyKey();
/**
* Read a single `string | null` value.
*
* MAY check if that string is not valid UTF-8.
*/
MOZ_MUST_USE JS::Result<Ok> readChars(Chars&);
/**
* Read a single `BinASTVariant | null` value.
*/
MOZ_MUST_USE JS::Result<mozilla::Maybe<BinASTVariant>> readMaybeVariant();
MOZ_MUST_USE JS::Result<BinASTVariant> readVariant();
/**
* Read over a single `[Skippable]` subtree value.
*
* This does *not* attempt to parse the subtree itself. Rather, the
* returned `SkippableSubTree` contains the necessary information
* to parse/tokenize the subtree at a later stage
*/
MOZ_MUST_USE JS::Result<SkippableSubTree> readSkippableSubTree();
// --- Composite values.
//
// The underlying format does NOT allows for a `null` composite value.
//
// Reading will return an error either in case of I/O error or in case of
// a format problem. Reading from a poisoned tokenizer is an error and
// will cause assertion failures.
/**
* Start reading a list.
*
* @param length (OUT) The number of elements in the list.
* @param guard (OUT) A guard, ensuring that we read the list correctly.
*
* The `guard` is dedicated to ensuring that reading the list has consumed
* exactly all the bytes from that list. The `guard` MUST therefore be
* destroyed at the point where the caller has reached the end of the list.
* If the caller has consumed too few/too many bytes, this will be reported
* in the call go `guard.done()`.
*/
MOZ_MUST_USE JS::Result<Ok> enterList(uint32_t& length, AutoList& guard);
/**
* Start reading a tagged tuple.
*
* @param tag (OUT) The tag of the tuple.
* @param fields Ignored, provided for API compatibility.
* @param guard (OUT) A guard, ensuring that we read the tagged tuple
* correctly.
*
* The `guard` is dedicated to ensuring that reading the list has consumed
* exactly all the bytes from that tuple. The `guard` MUST therefore be
* destroyed at the point where the caller has reached the end of the tuple.
* If the caller has consumed too few/too many bytes, this will be reported
* in the call go `guard.done()`.
*
* @return out If the header of the tuple is invalid.
*/
MOZ_MUST_USE JS::Result<Ok> enterTaggedTuple(
BinASTKind& tag, BinASTTokenReaderMultipart::BinASTFields& fields,
AutoTaggedTuple& guard);
/**
* Read a single unsigned long.
*/
MOZ_MUST_USE JS::Result<uint32_t> readUnsignedLong() {
return readInternalUint32();
}
private:
/**
* Read a single uint32_t.
*/
MOZ_MUST_USE JS::Result<uint32_t> readInternalUint32();
private:
// A mapping string index => BinASTVariant as extracted from the [STRINGS]
// section of the file. Populated lazily.
js::HashMap<uint32_t, BinASTVariant, DefaultHasher<uint32_t>,
SystemAllocPolicy>
variantsTable_;
enum class MetadataOwnership { Owned, Unowned };
MetadataOwnership metadataOwned_ = MetadataOwnership::Owned;
BinASTSourceMetadata* metadata_;
const uint8_t* posBeforeTree_;
BinASTTokenReaderMultipart(const BinASTTokenReaderMultipart&) = delete;
BinASTTokenReaderMultipart(BinASTTokenReaderMultipart&&) = delete;
BinASTTokenReaderMultipart& operator=(BinASTTokenReaderMultipart&) = delete;
public:
void traceMetadata(JSTracer* trc);
BinASTSourceMetadata* takeMetadata();
MOZ_MUST_USE JS::Result<Ok> initFromScriptSource(ScriptSource* scriptSource);
public:
// The following classes are used whenever we encounter a tuple/tagged
// tuple/list to make sure that:
//
// - if the construct "knows" its byte length, we have exactly consumed all
// the bytes (otherwise, this means that the file is corrupted, perhaps on
// purpose, so we need to reject the stream);
// - if the construct has a footer, once we are done reading it, we have
// reached the footer (this is to aid with debugging).
//
// In either case, the caller MUST call method `done()` of the guard once
// it is done reading the tuple/tagged tuple/list, to report any pending
// error.
// Base class used by other Auto* classes.
class MOZ_STACK_CLASS AutoBase {
protected:
explicit AutoBase(BinASTTokenReaderMultipart& reader);
~AutoBase();
// Raise an error if we are not in the expected position.
MOZ_MUST_USE JS::Result<Ok> checkPosition(const uint8_t* expectedPosition);
friend BinASTTokenReaderMultipart;
void init();
// Set to `true` if `init()` has been called. Reset to `false` once
// all conditions have been checked.
bool initialized_;
BinASTTokenReaderMultipart& reader_;
};
// Guard class used to ensure that `enterList` is used properly.
class MOZ_STACK_CLASS AutoList : public AutoBase {
public:
explicit AutoList(BinASTTokenReaderMultipart& reader);
// Check that we have properly read to the end of the list.
MOZ_MUST_USE JS::Result<Ok> done();
protected:
friend BinASTTokenReaderMultipart;
void init();
};
// Guard class used to ensure that `enterTaggedTuple` is used properly.
class MOZ_STACK_CLASS AutoTaggedTuple : public AutoBase {
public:
explicit AutoTaggedTuple(BinASTTokenReaderMultipart& reader);
// Check that we have properly read to the end of the tuple.
MOZ_MUST_USE JS::Result<Ok> done();
};
// Compare a `Chars` and a string literal (ONLY a string literal).
template <size_t N>
static bool equals(const Chars& left, const char (&right)[N]) {
MOZ_ASSERT(N > 0);
MOZ_ASSERT(right[N - 1] == 0);
if (left.byteLen_ + 1 /* implicit NUL */ != N) {
return false;
}
if (!std::equal(left.start_, left.start_ + left.byteLen_, right)) {
return false;
}
return true;
}
template <size_t N>
static JS::Result<Ok, JS::Error&> checkFields(
const BinASTKind kind, const BinASTFields& actual,
const BinASTField (&expected)[N]) {
// Not implemented in this tokenizer.
return Ok();
}
static JS::Result<Ok, JS::Error&> checkFields0(const BinASTKind kind,
const BinASTFields& actual) {
// Not implemented in this tokenizer.
return Ok();
}
};
} // namespace frontend
} // namespace js
#endif // frontend_BinASTTokenReaderMultipart_h