diff options
Diffstat (limited to '')
-rw-r--r-- | js/src/frontend/ParseNode.cpp | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp new file mode 100644 index 0000000000..b9d16f0be8 --- /dev/null +++ b/js/src/frontend/ParseNode.cpp @@ -0,0 +1,430 @@ +/* -*- 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/CompilationStencil.h" // ExtensibleCompilationStencil +#include "frontend/FullParseHandler.h" +#include "frontend/ParseContext.h" +#include "frontend/Parser.h" // ParserBase +#include "frontend/ParserAtom.h" // ParserAtomsTable, TaggedParserAtomIndex +#include "frontend/SharedContext.h" +#include "js/Printer.h" +#include "vm/Scope.h" // GetScopeDataTrailingNames + +using namespace js; +using namespace js::frontend; + +#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(fc); + } + 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 +}; + +static void DumpParseTree(const ParserAtomsTable* parserAtoms, ParseNode* pn, + GenericPrinter& out, int indent) { + if (pn == nullptr) { + out.put("#NULL"); + } else { + pn->dump(parserAtoms, out, indent); + } +} + +void frontend::DumpParseTree(ParserBase* parser, ParseNode* pn, + GenericPrinter& out, int indent) { + ParserAtomsTable* parserAtoms = parser ? &parser->parserAtoms() : nullptr; + ::DumpParseTree(parserAtoms, pn, out, indent); +} + +static void IndentNewLine(GenericPrinter& out, int indent) { + out.putChar('\n'); + for (int i = 0; i < indent; ++i) { + out.putChar(' '); + } +} + +void ParseNode::dump() { dump(nullptr); } + +void ParseNode::dump(const ParserAtomsTable* parserAtoms) { + js::Fprinter out(stderr); + dump(parserAtoms, out); +} + +void ParseNode::dump(const ParserAtomsTable* parserAtoms, GenericPrinter& out) { + dump(parserAtoms, out, 0); + out.putChar('\n'); +} + +void ParseNode::dump(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent) { + switch (getKind()) { +# define DUMP(K, T) \ + case ParseNodeKind::K: \ + as<T>().dumpImpl(parserAtoms, 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(const ParserAtomsTable* parserAtoms, + 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(const ParserAtomsTable* parserAtoms, + GenericPrinter& out, int indent) { + ToCStringBuf cbuf; + const char* cstr = NumberToCString(&cbuf, value()); + MOZ_ASSERT(cstr); + if (!std::isfinite(value())) { + out.put("#"); + } + out.printf("%s", cstr); +} + +void BigIntLiteral::dumpImpl(const ParserAtomsTable* parserAtoms, + GenericPrinter& out, int indent) { + out.printf("(%s)", parseNodeNames[getKindAsIndex()]); +} + +void RegExpLiteral::dumpImpl(const ParserAtomsTable* parserAtoms, + GenericPrinter& out, int indent) { + out.printf("(%s)", parseNodeNames[getKindAsIndex()]); +} + +static void DumpCharsNoNewline(const ParserAtomsTable* parserAtoms, + TaggedParserAtomIndex index, + GenericPrinter& out) { + out.put("\""); + if (parserAtoms) { + parserAtoms->dumpCharsNoQuote(out, index); + } else { + DumpTaggedParserAtomIndexNoQuote(out, index, nullptr); + } + out.put("\""); +} + +void LoopControlStatement::dumpImpl(const ParserAtomsTable* parserAtoms, + GenericPrinter& out, int indent) { + const char* name = parseNodeNames[getKindAsIndex()]; + out.printf("(%s", name); + if (label_) { + out.printf(" "); + DumpCharsNoNewline(parserAtoms, label_, out); + } + out.printf(")"); +} + +void UnaryNode::dumpImpl(const ParserAtomsTable* parserAtoms, + GenericPrinter& out, int indent) { + const char* name = parseNodeNames[getKindAsIndex()]; + out.printf("(%s ", name); + indent += strlen(name) + 2; + ::DumpParseTree(parserAtoms, kid(), out, indent); + out.printf(")"); +} + +void BinaryNode::dumpImpl(const ParserAtomsTable* parserAtoms, + GenericPrinter& out, int indent) { + if (isKind(ParseNodeKind::DotExpr)) { + out.put("(."); + + ::DumpParseTree(parserAtoms, right(), out, indent + 2); + + out.putChar(' '); + if (as<PropertyAccess>().isSuper()) { + out.put("super"); + } else { + ::DumpParseTree(parserAtoms, left(), out, indent + 2); + } + + out.printf(")"); + return; + } + + const char* name = parseNodeNames[getKindAsIndex()]; + out.printf("(%s ", name); + indent += strlen(name) + 2; + ::DumpParseTree(parserAtoms, left(), out, indent); + IndentNewLine(out, indent); + ::DumpParseTree(parserAtoms, right(), out, indent); + out.printf(")"); +} + +void TernaryNode::dumpImpl(const ParserAtomsTable* parserAtoms, + GenericPrinter& out, int indent) { + const char* name = parseNodeNames[getKindAsIndex()]; + out.printf("(%s ", name); + indent += strlen(name) + 2; + ::DumpParseTree(parserAtoms, kid1(), out, indent); + IndentNewLine(out, indent); + ::DumpParseTree(parserAtoms, kid2(), out, indent); + IndentNewLine(out, indent); + ::DumpParseTree(parserAtoms, kid3(), out, indent); + out.printf(")"); +} + +void FunctionNode::dumpImpl(const ParserAtomsTable* parserAtoms, + GenericPrinter& out, int indent) { + const char* name = parseNodeNames[getKindAsIndex()]; + out.printf("(%s ", name); + indent += strlen(name) + 2; + ::DumpParseTree(parserAtoms, body(), out, indent); + out.printf(")"); +} + +void ModuleNode::dumpImpl(const ParserAtomsTable* parserAtoms, + GenericPrinter& out, int indent) { + const char* name = parseNodeNames[getKindAsIndex()]; + out.printf("(%s ", name); + indent += strlen(name) + 2; + ::DumpParseTree(parserAtoms, body(), out, indent); + out.printf(")"); +} + +void ListNode::dumpImpl(const ParserAtomsTable* parserAtoms, + GenericPrinter& out, int indent) { + const char* name = parseNodeNames[getKindAsIndex()]; + out.printf("(%s [", name); + if (ParseNode* listHead = head()) { + indent += strlen(name) + 3; + ::DumpParseTree(parserAtoms, listHead, out, indent); + for (ParseNode* item : contentsFrom(listHead->pn_next)) { + IndentNewLine(out, indent); + ::DumpParseTree(parserAtoms, item, out, indent); + } + } + out.printf("])"); +} + +void NameNode::dumpImpl(const ParserAtomsTable* parserAtoms, + GenericPrinter& out, int indent) { + switch (getKind()) { + case ParseNodeKind::StringExpr: + case ParseNodeKind::TemplateStringExpr: + case ParseNodeKind::ObjectPropertyName: + DumpCharsNoNewline(parserAtoms, 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 (parserAtoms) { + if (atom_ == TaggedParserAtomIndex::WellKnown::empty()) { + out.put("#<zero-length name>"); + } else { + parserAtoms->dumpCharsNoQuote(out, atom_); + } + } else { + DumpTaggedParserAtomIndexNoQuote(out, atom_, nullptr); + } + return; + + case ParseNodeKind::LabelStmt: { + this->as<LabeledStatement>().dumpImpl(parserAtoms, out, indent); + return; + } + + default: { + const char* name = parseNodeNames[getKindAsIndex()]; + out.printf("(%s)", name); + return; + } + } +} + +void LabeledStatement::dumpImpl(const ParserAtomsTable* parserAtoms, + GenericPrinter& out, int indent) { + const char* name = parseNodeNames[getKindAsIndex()]; + out.printf("(%s ", name); + DumpCharsNoNewline(parserAtoms, label(), out); + indent += strlen(name) + 2; + IndentNewLine(out, indent); + ::DumpParseTree(parserAtoms, statement(), out, indent); + out.printf(")"); +} + +template <ParseNodeKind Kind, typename ScopeType> +void BaseScopeNode<Kind, ScopeType>::dumpImpl( + const ParserAtomsTable* parserAtoms, GenericPrinter& out, int indent) { + const char* name = parseNodeNames[getKindAsIndex()]; + out.printf("(%s [", name); + int nameIndent = indent + strlen(name) + 3; + if (!isEmptyScope()) { + typename ScopeType::ParserData* bindings = scopeBindings(); + auto names = GetScopeDataTrailingNames(bindings); + for (uint32_t i = 0; i < names.size(); i++) { + auto index = names[i].name(); + if (parserAtoms) { + if (index == TaggedParserAtomIndex::WellKnown::empty()) { + out.put("#<zero-length name>"); + } else { + parserAtoms->dumpCharsNoQuote(out, index); + } + } else { + DumpTaggedParserAtomIndexNoQuote(out, index, nullptr); + } + if (i < names.size() - 1) { + IndentNewLine(out, nameIndent); + } + } + } + out.putChar(']'); + indent += 2; + IndentNewLine(out, indent); + ::DumpParseTree(parserAtoms, scopeBody(), out, indent); + out.printf(")"); +} +#endif + +TaggedParserAtomIndex NumericLiteral::toAtom( + FrontendContext* fc, ParserAtomsTable& parserAtoms) const { + return NumberToParserAtom(fc, parserAtoms, value()); +} + +RegExpObject* RegExpLiteral::create( + JSContext* cx, FrontendContext* fc, ParserAtomsTable& parserAtoms, + CompilationAtomCache& atomCache, + ExtensibleCompilationStencil& stencil) const { + return stencil.regExpData[index_].createRegExpAndEnsureAtom( + cx, fc, parserAtoms, atomCache); +} + +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; +} |