summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/ParseNode.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/frontend/ParseNode.cpp442
1 files changed, 442 insertions, 0 deletions
diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp
new file mode 100644
index 0000000000..916157d1ba
--- /dev/null
+++ b/js/src/frontend/ParseNode.cpp
@@ -0,0 +1,442 @@
+/* -*- 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/. */
+
+#include "frontend/ParseNode.h"
+
+#include "mozilla/FloatingPoint.h"
+
+#include "jsnum.h"
+
+#include "frontend/FullParseHandler.h"
+#include "frontend/ParseContext.h"
+#include "frontend/ParserAtom.h"
+#include "frontend/SharedContext.h"
+#include "vm/BigIntType.h"
+#include "vm/Printer.h"
+#include "vm/RegExpObject.h"
+
+using namespace js;
+using namespace js::frontend;
+
+using mozilla::IsFinite;
+
+#ifdef DEBUG
+void ListNode::checkConsistency() const {
+ ParseNode* const* tailNode;
+ uint32_t actualCount = 0;
+ if (const ParseNode* last = head()) {
+ const ParseNode* pn = last;
+ while (pn) {
+ last = pn;
+ pn = pn->pn_next;
+ actualCount++;
+ }
+
+ tailNode = &last->pn_next;
+ } else {
+ tailNode = &head_;
+ }
+ MOZ_ASSERT(tail() == tailNode);
+ MOZ_ASSERT(count() == actualCount);
+}
+#endif
+
+/*
+ * Allocate a ParseNode from parser's node freelist or, failing that, from
+ * cx's temporary arena.
+ */
+void* ParseNodeAllocator::allocNode(size_t size) {
+ LifoAlloc::AutoFallibleScope fallibleAllocator(&alloc);
+ void* p = alloc.alloc(size);
+ if (!p) {
+ ReportOutOfMemory(cx);
+ }
+ return p;
+}
+
+ParseNode* ParseNode::appendOrCreateList(ParseNodeKind kind, ParseNode* left,
+ ParseNode* right,
+ FullParseHandler* handler,
+ ParseContext* pc) {
+ // The asm.js specification is written in ECMAScript grammar terms that
+ // specify *only* a binary tree. It's a royal pain to implement the asm.js
+ // spec to act upon n-ary lists as created below. So for asm.js, form a
+ // binary tree of lists exactly as ECMAScript would by skipping the
+ // following optimization.
+ if (!pc->useAsmOrInsideUseAsm()) {
+ // Left-associative trees of a given operator (e.g. |a + b + c|) are
+ // binary trees in the spec: (+ (+ a b) c) in Lisp terms. Recursively
+ // processing such a tree, exactly implemented that way, would blow the
+ // the stack. We use a list node that uses O(1) stack to represent
+ // such operations: (+ a b c).
+ //
+ // (**) is right-associative; per spec |a ** b ** c| parses as
+ // (** a (** b c)). But we treat this the same way, creating a list
+ // node: (** a b c). All consumers must understand that this must be
+ // processed with a right fold, whereas the list (+ a b c) must be
+ // processed with a left fold because (+) is left-associative.
+ //
+ if (left->isKind(kind) &&
+ (kind == ParseNodeKind::PowExpr ? !left->isInParens()
+ : left->isBinaryOperation())) {
+ ListNode* list = &left->as<ListNode>();
+
+ list->append(right);
+ list->pn_pos.end = right->pn_pos.end;
+
+ return list;
+ }
+ }
+
+ ListNode* list = handler->new_<ListNode>(kind, left);
+ if (!list) {
+ return nullptr;
+ }
+
+ list->append(right);
+ return list;
+}
+
+const ParseNode::TypeCode ParseNode::typeCodeTable[] = {
+#define TYPE_CODE(_name, type) type::classTypeCode(),
+ FOR_EACH_PARSE_NODE_KIND(TYPE_CODE)
+#undef TYPE_CODE
+};
+
+#ifdef DEBUG
+
+const size_t ParseNode::sizeTable[] = {
+# define NODE_SIZE(_name, type) sizeof(type),
+ FOR_EACH_PARSE_NODE_KIND(NODE_SIZE)
+# undef NODE_SIZE
+};
+
+static const char* const parseNodeNames[] = {
+# define STRINGIFY(name, _type) # name,
+ FOR_EACH_PARSE_NODE_KIND(STRINGIFY)
+# undef STRINGIFY
+};
+
+void frontend::DumpParseTree(ParseNode* pn, GenericPrinter& out, int indent) {
+ if (pn == nullptr) {
+ out.put("#NULL");
+ } else {
+ pn->dump(out, indent);
+ }
+}
+
+static void IndentNewLine(GenericPrinter& out, int indent) {
+ out.putChar('\n');
+ for (int i = 0; i < indent; ++i) {
+ out.putChar(' ');
+ }
+}
+
+void ParseNode::dump(GenericPrinter& out) {
+ dump(out, 0);
+ out.putChar('\n');
+}
+
+void ParseNode::dump() {
+ js::Fprinter out(stderr);
+ dump(out);
+}
+
+void ParseNode::dump(GenericPrinter& out, int indent) {
+ switch (getKind()) {
+# define DUMP(K, T) \
+ case ParseNodeKind::K: \
+ as<T>().dumpImpl(out, indent); \
+ break;
+ FOR_EACH_PARSE_NODE_KIND(DUMP)
+# undef DUMP
+ default:
+ out.printf("#<BAD NODE %p, kind=%u>", (void*)this, unsigned(getKind()));
+ }
+}
+
+void NullaryNode::dumpImpl(GenericPrinter& out, int indent) {
+ switch (getKind()) {
+ case ParseNodeKind::TrueExpr:
+ out.put("#true");
+ break;
+ case ParseNodeKind::FalseExpr:
+ out.put("#false");
+ break;
+ case ParseNodeKind::NullExpr:
+ out.put("#null");
+ break;
+ case ParseNodeKind::RawUndefinedExpr:
+ out.put("#undefined");
+ break;
+
+ default:
+ out.printf("(%s)", parseNodeNames[getKindAsIndex()]);
+ }
+}
+
+void NumericLiteral::dumpImpl(GenericPrinter& out, int indent) {
+ ToCStringBuf cbuf;
+ const char* cstr = NumberToCString(nullptr, &cbuf, value());
+ if (!IsFinite(value())) {
+ out.put("#");
+ }
+ if (cstr) {
+ out.printf("%s", cstr);
+ } else {
+ out.printf("%g", value());
+ }
+}
+
+void BigIntLiteral::dumpImpl(GenericPrinter& out, int indent) {
+ out.printf("(%s)", parseNodeNames[getKindAsIndex()]);
+}
+
+void RegExpLiteral::dumpImpl(GenericPrinter& out, int indent) {
+ out.printf("(%s)", parseNodeNames[getKindAsIndex()]);
+}
+
+static void DumpCharsNoNewline(const ParserAtom* atom,
+ js::GenericPrinter& out) {
+ if (atom->hasLatin1Chars()) {
+ out.put("[Latin 1]");
+ JSString::dumpChars(atom->latin1Chars(), atom->length(), out);
+ } else {
+ out.put("[2 byte]");
+ JSString::dumpChars(atom->twoByteChars(), atom->length(), out);
+ }
+}
+
+void LoopControlStatement::dumpImpl(GenericPrinter& out, int indent) {
+ const char* name = parseNodeNames[getKindAsIndex()];
+ out.printf("(%s", name);
+ if (label()) {
+ out.printf(" ");
+ DumpCharsNoNewline(label(), out);
+ }
+ out.printf(")");
+}
+
+void UnaryNode::dumpImpl(GenericPrinter& out, int indent) {
+ const char* name = parseNodeNames[getKindAsIndex()];
+ out.printf("(%s ", name);
+ indent += strlen(name) + 2;
+ DumpParseTree(kid(), out, indent);
+ out.printf(")");
+}
+
+void BinaryNode::dumpImpl(GenericPrinter& out, int indent) {
+ if (isKind(ParseNodeKind::DotExpr)) {
+ out.put("(.");
+
+ DumpParseTree(right(), out, indent + 2);
+
+ out.putChar(' ');
+ if (as<PropertyAccess>().isSuper()) {
+ out.put("super");
+ } else {
+ DumpParseTree(left(), out, indent + 2);
+ }
+
+ out.printf(")");
+ return;
+ }
+
+ const char* name = parseNodeNames[getKindAsIndex()];
+ out.printf("(%s ", name);
+ indent += strlen(name) + 2;
+ DumpParseTree(left(), out, indent);
+ IndentNewLine(out, indent);
+ DumpParseTree(right(), out, indent);
+ out.printf(")");
+}
+
+void TernaryNode::dumpImpl(GenericPrinter& out, int indent) {
+ const char* name = parseNodeNames[getKindAsIndex()];
+ out.printf("(%s ", name);
+ indent += strlen(name) + 2;
+ DumpParseTree(kid1(), out, indent);
+ IndentNewLine(out, indent);
+ DumpParseTree(kid2(), out, indent);
+ IndentNewLine(out, indent);
+ DumpParseTree(kid3(), out, indent);
+ out.printf(")");
+}
+
+void FunctionNode::dumpImpl(GenericPrinter& out, int indent) {
+ const char* name = parseNodeNames[getKindAsIndex()];
+ out.printf("(%s ", name);
+ indent += strlen(name) + 2;
+ DumpParseTree(body(), out, indent);
+ out.printf(")");
+}
+
+void ModuleNode::dumpImpl(GenericPrinter& out, int indent) {
+ const char* name = parseNodeNames[getKindAsIndex()];
+ out.printf("(%s ", name);
+ indent += strlen(name) + 2;
+ DumpParseTree(body(), out, indent);
+ out.printf(")");
+}
+
+void ListNode::dumpImpl(GenericPrinter& out, int indent) {
+ const char* name = parseNodeNames[getKindAsIndex()];
+ out.printf("(%s [", name);
+ if (ParseNode* listHead = head()) {
+ indent += strlen(name) + 3;
+ DumpParseTree(listHead, out, indent);
+ for (ParseNode* item : contentsFrom(listHead->pn_next)) {
+ IndentNewLine(out, indent);
+ DumpParseTree(item, out, indent);
+ }
+ }
+ out.printf("])");
+}
+
+template <typename CharT>
+static void DumpName(GenericPrinter& out, const CharT* s, size_t len) {
+ if (len == 0) {
+ out.put("#<zero-length name>");
+ }
+
+ for (size_t i = 0; i < len; i++) {
+ char16_t c = s[i];
+ if (c > 32 && c < 127) {
+ out.putChar(c);
+ } else if (c <= 255) {
+ out.printf("\\x%02x", unsigned(c));
+ } else {
+ out.printf("\\u%04x", unsigned(c));
+ }
+ }
+}
+
+void NameNode::dumpImpl(GenericPrinter& out, int indent) {
+ switch (getKind()) {
+ case ParseNodeKind::StringExpr:
+ case ParseNodeKind::TemplateStringExpr:
+ case ParseNodeKind::ObjectPropertyName:
+ DumpCharsNoNewline(atom(), out);
+ return;
+
+ case ParseNodeKind::Name:
+ case ParseNodeKind::PrivateName: // atom() already includes the '#', no
+ // need to specially include it.
+ case ParseNodeKind::PropertyNameExpr:
+ if (!atom()) {
+ out.put("#<null name>");
+ } else {
+ if (atom()->hasLatin1Chars()) {
+ DumpName(out, atom()->latin1Chars(), atom()->length());
+ } else {
+ DumpName(out, atom()->twoByteChars(), atom()->length());
+ }
+ }
+ return;
+
+ case ParseNodeKind::LabelStmt: {
+ this->as<LabeledStatement>().dumpImpl(out, indent);
+ return;
+ }
+
+ default: {
+ const char* name = parseNodeNames[getKindAsIndex()];
+ out.printf("(%s)", name);
+ return;
+ }
+ }
+}
+
+void LabeledStatement::dumpImpl(GenericPrinter& out, int indent) {
+ const char* name = parseNodeNames[getKindAsIndex()];
+ out.printf("(%s ", name);
+ DumpCharsNoNewline(atom(), out);
+ out.printf(" ");
+ indent += strlen(name) + atom()->length() + 3;
+ DumpParseTree(statement(), out, indent);
+ out.printf(")");
+}
+
+void LexicalScopeNode::dumpImpl(GenericPrinter& out, int indent) {
+ const char* name = parseNodeNames[getKindAsIndex()];
+ out.printf("(%s [", name);
+ int nameIndent = indent + strlen(name) + 3;
+ if (!isEmptyScope()) {
+ LexicalScope::ParserData* bindings = scopeBindings();
+ for (uint32_t i = 0; i < bindings->slotInfo.length; i++) {
+ auto index = bindings->trailingNames[i].name();
+ JSONPrinter json(out);
+ json.setIndentLevel((nameIndent + 1) / 2);
+ json.beginObject();
+ DumpTaggedParserAtomIndex(json, index, nullptr);
+ json.endObject();
+ if (i < bindings->slotInfo.length - 1) {
+ IndentNewLine(out, nameIndent);
+ }
+ }
+ }
+ out.putChar(']');
+ indent += 2;
+ IndentNewLine(out, indent);
+ DumpParseTree(scopeBody(), out, indent);
+ out.printf(")");
+}
+#endif
+
+BigInt* BigIntLiteral::create(JSContext* cx) {
+ return stencil_.bigIntData[index_].createBigInt(cx);
+}
+
+bool BigIntLiteral::isZero() { return stencil_.bigIntData[index_].isZero(); }
+
+const ParserAtom* NumericLiteral::toAtom(JSContext* cx,
+ ParserAtomsTable& parserAtoms) const {
+ return NumberToParserAtom(cx, parserAtoms, value());
+}
+
+RegExpObject* RegExpStencil::createRegExp(
+ JSContext* cx, CompilationAtomCache& atomCache) const {
+ RootedAtom atom(cx, atomCache.getExistingAtomAt(cx, atom_));
+ return RegExpObject::createSyntaxChecked(cx, atom, flags(), TenuredObject);
+}
+
+RegExpObject* RegExpStencil::createRegExpAndEnsureAtom(
+ JSContext* cx, CompilationAtomCache& atomCache,
+ BaseCompilationStencil& stencil) const {
+ const ParserAtom* parserAtom = stencil.getParserAtomAt(cx, atom_);
+ MOZ_ASSERT(parserAtom);
+ RootedAtom atom(cx, parserAtom->toJSAtom(cx, atomCache));
+ if (!atom) {
+ return nullptr;
+ }
+ return RegExpObject::createSyntaxChecked(cx, atom, flags(), TenuredObject);
+}
+
+RegExpObject* RegExpLiteral::create(JSContext* cx,
+ CompilationAtomCache& atomCache,
+ BaseCompilationStencil& stencil) const {
+ return stencil.regExpData[index_].createRegExpAndEnsureAtom(cx, atomCache,
+ stencil);
+}
+
+bool js::frontend::IsAnonymousFunctionDefinition(ParseNode* pn) {
+ // ES 2017 draft
+ // 12.15.2 (ArrowFunction, AsyncArrowFunction).
+ // 14.1.12 (FunctionExpression).
+ // 14.4.8 (Generatoression).
+ // 14.6.8 (AsyncFunctionExpression)
+ if (pn->is<FunctionNode>() &&
+ !pn->as<FunctionNode>().funbox()->explicitName()) {
+ return true;
+ }
+
+ // 14.5.8 (ClassExpression)
+ if (pn->is<ClassNode>() && !pn->as<ClassNode>().names()) {
+ return true;
+ }
+
+ return false;
+}