Source code

Revision control

Other Tools

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
#include "frontend/ParseNode.h"
8
9
#include "mozilla/ArrayUtils.h"
10
#include "mozilla/FloatingPoint.h"
11
12
#include "jsnum.h"
13
14
#include "frontend/Parser.h"
15
16
#include "vm/JSContext-inl.h"
17
18
using namespace js;
19
using namespace js::frontend;
20
21
using mozilla::ArrayLength;
22
using mozilla::IsFinite;
23
24
#ifdef DEBUG
25
void ListNode::checkConsistency() const {
26
ParseNode* const* tailNode;
27
uint32_t actualCount = 0;
28
if (const ParseNode* last = head()) {
29
const ParseNode* pn = last;
30
while (pn) {
31
last = pn;
32
pn = pn->pn_next;
33
actualCount++;
34
}
35
36
tailNode = &last->pn_next;
37
} else {
38
tailNode = &head_;
39
}
40
MOZ_ASSERT(tail() == tailNode);
41
MOZ_ASSERT(count() == actualCount);
42
}
43
#endif
44
45
/*
46
* Allocate a ParseNode from parser's node freelist or, failing that, from
47
* cx's temporary arena.
48
*/
49
void* ParseNodeAllocator::allocNode(size_t size) {
50
LifoAlloc::AutoFallibleScope fallibleAllocator(&alloc);
51
void* p = alloc.alloc(size);
52
if (!p) {
53
ReportOutOfMemory(cx);
54
}
55
return p;
56
}
57
58
ParseNode* ParseNode::appendOrCreateList(ParseNodeKind kind, ParseNode* left,
59
ParseNode* right,
60
FullParseHandler* handler,
61
ParseContext* pc) {
62
// The asm.js specification is written in ECMAScript grammar terms that
63
// specify *only* a binary tree. It's a royal pain to implement the asm.js
64
// spec to act upon n-ary lists as created below. So for asm.js, form a
65
// binary tree of lists exactly as ECMAScript would by skipping the
66
// following optimization.
67
if (!pc->useAsmOrInsideUseAsm()) {
68
// Left-associative trees of a given operator (e.g. |a + b + c|) are
69
// binary trees in the spec: (+ (+ a b) c) in Lisp terms. Recursively
70
// processing such a tree, exactly implemented that way, would blow the
71
// the stack. We use a list node that uses O(1) stack to represent
72
// such operations: (+ a b c).
73
//
74
// (**) is right-associative; per spec |a ** b ** c| parses as
75
// (** a (** b c)). But we treat this the same way, creating a list
76
// node: (** a b c). All consumers must understand that this must be
77
// processed with a right fold, whereas the list (+ a b c) must be
78
// processed with a left fold because (+) is left-associative.
79
//
80
if (left->isKind(kind) &&
81
(kind == ParseNodeKind::PowExpr ? !left->isInParens()
82
: left->isBinaryOperation())) {
83
ListNode* list = &left->as<ListNode>();
84
85
list->append(right);
86
list->pn_pos.end = right->pn_pos.end;
87
88
return list;
89
}
90
}
91
92
ListNode* list = handler->new_<ListNode>(kind, left);
93
if (!list) {
94
return nullptr;
95
}
96
97
list->append(right);
98
return list;
99
}
100
101
const ParseNode::TypeCode ParseNode::typeCodeTable[] = {
102
#define TYPE_CODE(_name, type) type::classTypeCode(),
103
FOR_EACH_PARSE_NODE_KIND(TYPE_CODE)
104
#undef TYPE_CODE
105
};
106
107
#ifdef DEBUG
108
109
const size_t ParseNode::sizeTable[] = {
110
# define NODE_SIZE(_name, type) sizeof(type),
111
FOR_EACH_PARSE_NODE_KIND(NODE_SIZE)
112
# undef NODE_SIZE
113
};
114
115
static const char* const parseNodeNames[] = {
116
# define STRINGIFY(name, _type) # name,
117
FOR_EACH_PARSE_NODE_KIND(STRINGIFY)
118
# undef STRINGIFY
119
};
120
121
void frontend::DumpParseTree(ParseNode* pn, GenericPrinter& out, int indent) {
122
if (pn == nullptr) {
123
out.put("#NULL");
124
} else {
125
pn->dump(out, indent);
126
}
127
}
128
129
static void IndentNewLine(GenericPrinter& out, int indent) {
130
out.putChar('\n');
131
for (int i = 0; i < indent; ++i) {
132
out.putChar(' ');
133
}
134
}
135
136
void ParseNode::dump(GenericPrinter& out) {
137
dump(out, 0);
138
out.putChar('\n');
139
}
140
141
void ParseNode::dump() {
142
js::Fprinter out(stderr);
143
dump(out);
144
}
145
146
void ParseNode::dump(GenericPrinter& out, int indent) {
147
switch (getKind()) {
148
# define DUMP(K, T) \
149
case ParseNodeKind::K: \
150
as<T>().dumpImpl(out, indent); \
151
break;
152
FOR_EACH_PARSE_NODE_KIND(DUMP)
153
# undef DUMP
154
default:
155
out.printf("#<BAD NODE %p, kind=%u>", (void*)this, unsigned(getKind()));
156
}
157
}
158
159
void NullaryNode::dumpImpl(GenericPrinter& out, int indent) {
160
switch (getKind()) {
161
case ParseNodeKind::TrueExpr:
162
out.put("#true");
163
break;
164
case ParseNodeKind::FalseExpr:
165
out.put("#false");
166
break;
167
case ParseNodeKind::NullExpr:
168
out.put("#null");
169
break;
170
case ParseNodeKind::RawUndefinedExpr:
171
out.put("#undefined");
172
break;
173
174
default:
175
out.printf("(%s)", parseNodeNames[getKindAsIndex()]);
176
}
177
}
178
179
void NumericLiteral::dumpImpl(GenericPrinter& out, int indent) {
180
ToCStringBuf cbuf;
181
const char* cstr = NumberToCString(nullptr, &cbuf, value());
182
if (!IsFinite(value())) {
183
out.put("#");
184
}
185
if (cstr) {
186
out.printf("%s", cstr);
187
} else {
188
out.printf("%g", value());
189
}
190
}
191
192
void BigIntLiteral::dumpImpl(GenericPrinter& out, int indent) {
193
out.printf("(%s)", parseNodeNames[getKindAsIndex()]);
194
}
195
196
void RegExpLiteral::dumpImpl(GenericPrinter& out, int indent) {
197
out.printf("(%s)", parseNodeNames[getKindAsIndex()]);
198
}
199
200
void LoopControlStatement::dumpImpl(GenericPrinter& out, int indent) {
201
const char* name = parseNodeNames[getKindAsIndex()];
202
out.printf("(%s", name);
203
if (label()) {
204
out.printf(" ");
205
label()->dumpCharsNoNewline(out);
206
}
207
out.printf(")");
208
}
209
210
void UnaryNode::dumpImpl(GenericPrinter& out, int indent) {
211
const char* name = parseNodeNames[getKindAsIndex()];
212
out.printf("(%s ", name);
213
indent += strlen(name) + 2;
214
DumpParseTree(kid(), out, indent);
215
out.printf(")");
216
}
217
218
void BinaryNode::dumpImpl(GenericPrinter& out, int indent) {
219
if (isKind(ParseNodeKind::DotExpr)) {
220
out.put("(.");
221
222
DumpParseTree(right(), out, indent + 2);
223
224
out.putChar(' ');
225
if (as<PropertyAccess>().isSuper()) {
226
out.put("super");
227
} else {
228
DumpParseTree(left(), out, indent + 2);
229
}
230
231
out.printf(")");
232
return;
233
}
234
235
const char* name = parseNodeNames[getKindAsIndex()];
236
out.printf("(%s ", name);
237
indent += strlen(name) + 2;
238
DumpParseTree(left(), out, indent);
239
IndentNewLine(out, indent);
240
DumpParseTree(right(), out, indent);
241
out.printf(")");
242
}
243
244
void TernaryNode::dumpImpl(GenericPrinter& out, int indent) {
245
const char* name = parseNodeNames[getKindAsIndex()];
246
out.printf("(%s ", name);
247
indent += strlen(name) + 2;
248
DumpParseTree(kid1(), out, indent);
249
IndentNewLine(out, indent);
250
DumpParseTree(kid2(), out, indent);
251
IndentNewLine(out, indent);
252
DumpParseTree(kid3(), out, indent);
253
out.printf(")");
254
}
255
256
void FunctionNode::dumpImpl(GenericPrinter& out, int indent) {
257
const char* name = parseNodeNames[getKindAsIndex()];
258
out.printf("(%s ", name);
259
indent += strlen(name) + 2;
260
DumpParseTree(body(), out, indent);
261
out.printf(")");
262
}
263
264
void ModuleNode::dumpImpl(GenericPrinter& out, int indent) {
265
const char* name = parseNodeNames[getKindAsIndex()];
266
out.printf("(%s ", name);
267
indent += strlen(name) + 2;
268
DumpParseTree(body(), out, indent);
269
out.printf(")");
270
}
271
272
void ListNode::dumpImpl(GenericPrinter& out, int indent) {
273
const char* name = parseNodeNames[getKindAsIndex()];
274
out.printf("(%s [", name);
275
if (ParseNode* listHead = head()) {
276
indent += strlen(name) + 3;
277
DumpParseTree(listHead, out, indent);
278
for (ParseNode* item : contentsFrom(listHead->pn_next)) {
279
IndentNewLine(out, indent);
280
DumpParseTree(item, out, indent);
281
}
282
}
283
out.printf("])");
284
}
285
286
template <typename CharT>
287
static void DumpName(GenericPrinter& out, const CharT* s, size_t len) {
288
if (len == 0) {
289
out.put("#<zero-length name>");
290
}
291
292
for (size_t i = 0; i < len; i++) {
293
char16_t c = s[i];
294
if (c > 32 && c < 127) {
295
out.putChar(c);
296
} else if (c <= 255) {
297
out.printf("\\x%02x", unsigned(c));
298
} else {
299
out.printf("\\u%04x", unsigned(c));
300
}
301
}
302
}
303
304
void NameNode::dumpImpl(GenericPrinter& out, int indent) {
305
switch (getKind()) {
306
case ParseNodeKind::StringExpr:
307
case ParseNodeKind::TemplateStringExpr:
308
case ParseNodeKind::ObjectPropertyName:
309
atom()->dumpCharsNoNewline(out);
310
return;
311
312
case ParseNodeKind::Name:
313
case ParseNodeKind::PrivateName: // atom() already includes the '#', no
314
// need to specially include it.
315
case ParseNodeKind::PropertyNameExpr:
316
if (!atom()) {
317
out.put("#<null name>");
318
} else {
319
JS::AutoCheckCannotGC nogc;
320
if (atom()->hasLatin1Chars()) {
321
DumpName(out, atom()->latin1Chars(nogc), atom()->length());
322
} else {
323
DumpName(out, atom()->twoByteChars(nogc), atom()->length());
324
}
325
}
326
return;
327
328
case ParseNodeKind::LabelStmt: {
329
this->as<LabeledStatement>().dumpImpl(out, indent);
330
return;
331
}
332
333
default: {
334
const char* name = parseNodeNames[getKindAsIndex()];
335
out.printf("(%s)", name);
336
return;
337
}
338
}
339
}
340
341
void LabeledStatement::dumpImpl(GenericPrinter& out, int indent) {
342
const char* name = parseNodeNames[getKindAsIndex()];
343
out.printf("(%s ", name);
344
atom()->dumpCharsNoNewline(out);
345
out.printf(" ");
346
indent += strlen(name) + atom()->length() + 3;
347
DumpParseTree(statement(), out, indent);
348
out.printf(")");
349
}
350
351
void LexicalScopeNode::dumpImpl(GenericPrinter& out, int indent) {
352
const char* name = parseNodeNames[getKindAsIndex()];
353
out.printf("(%s [", name);
354
int nameIndent = indent + strlen(name) + 3;
355
if (!isEmptyScope()) {
356
LexicalScope::Data* bindings = scopeBindings();
357
for (uint32_t i = 0; i < bindings->length; i++) {
358
JSAtom* name = bindings->trailingNames[i].name();
359
JS::AutoCheckCannotGC nogc;
360
if (name->hasLatin1Chars()) {
361
DumpName(out, name->latin1Chars(nogc), name->length());
362
} else {
363
DumpName(out, name->twoByteChars(nogc), name->length());
364
}
365
if (i < bindings->length - 1) {
366
IndentNewLine(out, nameIndent);
367
}
368
}
369
}
370
out.putChar(']');
371
indent += 2;
372
IndentNewLine(out, indent);
373
DumpParseTree(scopeBody(), out, indent);
374
out.printf(")");
375
}
376
#endif
377
378
TraceListNode::TraceListNode(js::gc::Cell* gcThing, TraceListNode* traceLink)
379
: gcThing(gcThing), traceLink(traceLink) {
380
MOZ_ASSERT(gcThing->isTenured());
381
}
382
383
BigIntBox* TraceListNode::asBigIntBox() {
384
MOZ_ASSERT(isBigIntBox());
385
return static_cast<BigIntBox*>(this);
386
}
387
388
ObjectBox* TraceListNode::asObjectBox() {
389
MOZ_ASSERT(isObjectBox());
390
return static_cast<ObjectBox*>(this);
391
}
392
393
BigIntBox::BigIntBox(BigInt* bi, TraceListNode* traceLink)
394
: TraceListNode(bi, traceLink) {}
395
396
ObjectBox::ObjectBox(JSObject* obj, TraceListNode* traceLink)
397
: TraceListNode(obj, traceLink), emitLink(nullptr) {
398
MOZ_ASSERT(!object()->is<JSFunction>());
399
}
400
401
ObjectBox::ObjectBox(JSFunction* function, TraceListNode* traceLink)
402
: TraceListNode(function, traceLink), emitLink(nullptr) {
403
MOZ_ASSERT(object()->is<JSFunction>());
404
MOZ_ASSERT(asFunctionBox()->function() == function);
405
}
406
407
FunctionBox* ObjectBox::asFunctionBox() {
408
MOZ_ASSERT(isFunctionBox());
409
return static_cast<FunctionBox*>(this);
410
}
411
412
/* static */
413
void TraceListNode::TraceList(JSTracer* trc, TraceListNode* listHead) {
414
for (TraceListNode* node = listHead; node; node = node->traceLink) {
415
node->trace(trc);
416
}
417
}
418
419
void TraceListNode::trace(JSTracer* trc) {
420
if (gcThing) {
421
TraceGenericPointerRoot(trc, &gcThing, "parser.traceListNode");
422
}
423
}
424
425
void FunctionBox::trace(JSTracer* trc) {
426
ObjectBox::trace(trc);
427
if (enclosingScope_) {
428
TraceRoot(trc, &enclosingScope_, "funbox-enclosingScope");
429
}
430
if (explicitName_) {
431
TraceRoot(trc, &explicitName_, "funbox-explicitName");
432
}
433
if (functionCreationData_) {
434
functionCreationData_->trace(trc);
435
}
436
}
437
438
bool js::frontend::IsAnonymousFunctionDefinition(ParseNode* pn) {
439
// ES 2017 draft
440
// 12.15.2 (ArrowFunction, AsyncArrowFunction).
441
// 14.1.12 (FunctionExpression).
442
// 14.4.8 (Generatoression).
443
// 14.6.8 (AsyncFunctionExpression)
444
if (pn->is<FunctionNode>() &&
445
!pn->as<FunctionNode>().funbox()->explicitName()) {
446
return true;
447
}
448
449
// 14.5.8 (ClassExpression)
450
if (pn->is<ClassNode>() && !pn->as<ClassNode>().names()) {
451
return true;
452
}
453
454
return false;
455
}