--- a/js/src/frontend/BinaryAST.cpp
+++ b/js/src/frontend/BinaryAST.cpp
@@ -1,49 +1,57 @@
// ---- Ongoing experiment: binary ast serializer/parser
+#include <algorithm>
#include <iostream>
#include <istream>
#include <sstream>
#include <strstream>
+#include <unordered_map>
+#include <vector>
#include "mozilla/Attributes.h"
#include "frontend/BinaryAST.h"
#include "frontend/ParseNode.h"
#include "jsatom.h"
#include "jsopcode.h"
namespace js {
namespace frontend {
// #define DEBUG_HEADER 1
using VariantKind = StringTree::VariantKind;
+using StringTable = std::unordered_map<std::string, uint32_t>;
+using Occurrences = std::pair<std::string, uint32_t>;
-/**
- * A marker for presence/absence.
- */
-enum OptionValue {
- OptionIsHere = -1,
- OptionIsAbsent = -2,
+bool compareOccurrences(const Occurrences& left, const Occurrences& right) {
+ return left.second < right.second;
};
// A (pretty long) header/footer to simplify debugging.
// Useful for prototyping, probably useless for a real product.
const char HEADER[] = "I am a header.";
const char FOOTER[] = "Behold the footer.";
// List of names of ParseNodeKind, to simplify logging.
const char* NAMES[] = {
#define EMIT_SLOT(name) "PNK_" #name,
FOR_EACH_PARSE_NODE_KIND(EMIT_SLOT)
#undef EMIT_SLOT
"PNK_LIMIT"
};
+void writeUnsigned(std::ostringstream& out, uint32_t value)
+{
+ jit::CompactBufferWriter writer;
+ writer.writeUnsigned(value);
+ out.write((const char*)writer.buffer(), writer.length());
+}
+
// FIXME: We should rather have a StringTreeWriter.
StringTree::~StringTree()
{
switch (variantKind) {
case VariantKind::IsLeaf:
delete data.leaf;
return;
case VariantKind::IsConcat:
@@ -55,45 +63,47 @@ StringTree::~StringTree()
StringTree::StringTree(ParseNodeKind k, Leaf* leaf)
: variantKind(VariantKind::IsLeaf)
, data(leaf)
, kind(k)
{}
StringTree::Leaf::Leaf()
- : data("")
+ : data()
{ }
StringTree::Leaf::Leaf(const std::string&& data_)
- : data(data_)
+ : data(Move(data_))
{ }
+
/*static*/ StringTree*
StringTree::makeLeaf(JSContext* cx, ParseNodeKind kind)
{
- return cx->new_<StringTree>(kind, cx->new_<StringTree::Leaf>());
+ UniquePtr<Leaf> leaf(cx->new_<StringTree::Leaf>());
+ return cx->new_<StringTree>(kind, leaf.release());
}
/*static*/ StringTree*
StringTree::makeLeaf(JSContext* cx, ParseNodeKind kind, std::string&& data)
{
UniquePtr<Leaf> leaf(cx->new_<StringTree::Leaf>(Move(data)));
return cx->new_<StringTree>(kind, leaf.release());
}
bool
StringTree::isLeaf() const {
return variantKind == StringTree::VariantKind::IsLeaf;
}
-const StringTree::Leaf&
+const std::string&
StringTree::asLeaf() const {
MOZ_ASSERT(isLeaf());
- return *data.leaf;
+ return data.leaf->data;
}
StringTree::StringTree(ParseNodeKind k, Concat* concat)
: variantKind(VariantKind::IsConcat)
, data(concat)
, kind(k)
{}
@@ -125,109 +135,33 @@ StringTree::asConcat() const {
return *data.concat;
}
const mozilla::Vector<UniquePtr<StringTree>>&
StringTree::children() const {
return asConcat().children;
}
-void
-StringTree::write(std::ostringstream& out) const {
- // std::cerr << "StringTree::write at " << out.tellp() << "\n";
-#if DEBUG_HEADER
- // std::cerr << "StringTree::write HEADER at " << out.tellp() << "\n";
- out.write(HEADER, sizeof HEADER - 1);
-#endif // DEBUG_HEADER
-
- // std::cerr << "StringTree::write kind at " << out.tellp() << "\n";
- out.write((const char*)&kind, sizeof kind);
-
- // std::cerr << "StringTree::write " << NAMES[kind] << "\n";
- if (kind != PNK_LIMIT) {
-
-#if DEBUG_HEADER
- // std::cerr << "StringTree::write variantKind at " << out.tellp() << "\n";
- const unsigned char char_variant = variantKind;
- out.write((const char*)&char_variant, sizeof char_variant);
- MOZ_ASSERT(variantKind == VariantKind::IsLeaf || variantKind == VariantKind::IsConcat);
-#endif // DEBUG_HEADER
-
- std::ostringstream sub_out;
- if (isLeaf()) {
- const Leaf& leaf = asLeaf();
-
- const size_t byteLength = leaf.data.length();
-
- jit::CompactBufferWriter lengthWriter;
- lengthWriter.writeUnsigned((uint32_t)byteLength);
- out.write((const char*)lengthWriter.buffer(), lengthWriter.length());
-
- const size_t byteStart = out.tellp();
- out << leaf.data;
- const size_t byteStop = out.tellp();
- MOZ_ASSERT(byteLength == byteStop - byteStart);
-
- } else {
- const Concat& concat = asConcat();
-
- {
- jit::CompactBufferWriter lengthWriter;
- lengthWriter.writeUnsigned((uint32_t)concat.children.length());
- sub_out.write((const char*)lengthWriter.buffer(), lengthWriter.length());
- for (const UniquePtr<StringTree>& tree: concat.children) {
- tree->write(sub_out);
- }
- }
-
- const std::string sub_str = sub_out.str();
- const size_t byteLength = sub_str.length();
- {
- jit::CompactBufferWriter lengthWriter;
- lengthWriter.writeUnsigned((uint32_t)byteLength);
-
- // std::cerr << "StringTree::write byteLength at " << out.tellp() << "\n";
- out.write((const char*)lengthWriter.buffer(), lengthWriter.length());
-
- // std::cerr << "StringTree::write concat byteLength " << byteLength << "\n";
- // std::cerr << "StringTree::write concat length " << concat.children.length() << "\n";
- }
-
-
- const size_t byteStart = out.tellp();
- out << sub_str;
- const size_t byteStop = out.tellp();
- // std::cerr << "StringTree::write length at " << byteStart << "\n";
- MOZ_ASSERT(byteLength == byteStop - byteStart);
- }
- }
-
-
-#if DEBUG_HEADER
- out.write(FOOTER, sizeof FOOTER - 1);
-#endif // DEBUG_HEADER
-}
-
-
using Leaf = StringTree::Leaf;
using Concat = StringTree::Concat;
void serializeAtom(JSContext* cx, JSAtom* atom, std::string& result)
{
std::ostringstream out;
if (atom) {
RootedString string(cx, atom);
JSAutoByteString bs;
if (!bs.encodeUtf8(cx, string)) { // 0 terminated, right?
MOZ_CRASH();
}
out.write(bs.ptr(), bs.length());
} else {
- const char zero[2] = { 0, 0 };
+ // FIXME: We could magically map this to string identifier 0. Is it worth it?
+ const char zero[2] = { 0, 0 }; // Magic constant (invalid utf8 string).
out.write((const char*)&zero, sizeof zero);
}
result = Move(out.str());
}
JSAtom* deserializeAtom(JSContext* cx, const std::string& data)
{
// Special constant '\0\0' for empty atoms.
@@ -246,337 +180,544 @@ bool deserializePropertyName(JSContext*
if (atom) {
label.set(atom->asPropertyName());
} else {
label.set(nullptr);
}
return true;
}
-MOZ_MUST_USE StringTree*
-treeSerialize(JSContext* cx, const ParseNode* node, std::ostringstream& debug)
-{
- const ParseNodeKind kind = node ? node->getKind() : PNK_LIMIT;
-// // std::cerr << "treeSerialize " << NAMES[kind] << "\n";
- debug << NAMES[kind] << " ";
+// FIXME: We could make it a bit smaller by sorting strings to ensure that the
+// ones used most often have a shorter variable-length id.
+class TreeSerializer {
+private:
+ JSContext* cx;
+ std::ostringstream& debug;
+ std::unordered_map<std::string, uint32_t> strings;
- switch (kind) {
- // Empty tree
- case PNK_LIMIT: {
- return StringTree::makeLeaf(cx, PNK_LIMIT);
- }
- // Body
- case PNK_LEXICALSCOPE:
- // fprintf(stderr, "PNK_LEXICALSCOPE: placeholder implementation\n");
- MOZ_FALLTHROUGH;
- case PNK_FUNCTION: {
- UniquePtr<StringTree> body(treeSerialize(cx, node->pn_body, debug));
- if (!body) {
- return nullptr;
- }
- return StringTree::makeConcat(cx, kind, Move(body));
- }
- // kid1, kid2, kid3
- case PNK_IF: MOZ_FALLTHROUGH;
- case PNK_IMPORT_SPEC: MOZ_FALLTHROUGH;
- case PNK_EXPORT: MOZ_FALLTHROUGH;
- case PNK_FORHEAD: MOZ_FALLTHROUGH;
- case PNK_CONDITIONAL: MOZ_FALLTHROUGH;
- case PNK_CLASS: MOZ_FALLTHROUGH;
- case PNK_TRY: MOZ_FALLTHROUGH;
- case PNK_CATCH: {
- mozilla::Vector<UniquePtr<StringTree>> children;
- if (!children.append(UniquePtr<StringTree>(treeSerialize(cx, node->pn_kid1, debug)))) {
+ uint32_t getStringId(const std::string& data) const {
+ const uint32_t result = strings.find(data)->second;
+ return result;
+#if 0
+ const auto search = strings.find(data);
+ if (search != strings.end()) {
+ return search->second;
+ } else {
+ const uint32_t key = ++string_counter;
+ if (!strings.insert({data, key}).second) {
MOZ_CRASH();
}
- if (!children.append(UniquePtr<StringTree>(treeSerialize(cx, node->pn_kid2, debug)))) {
- MOZ_CRASH();
- }
- if (!children.append(UniquePtr<StringTree>(treeSerialize(cx, node->pn_kid3, debug)))) {
- MOZ_CRASH();
- }
- return StringTree::makeConcat(cx, kind, Move(children));
+ return key;
+ }
+#endif // 0
+ }
+public:
+ const StringTable getStringTable() const {
+ return this->strings;
+ }
+ TreeSerializer(JSContext* cx_, std::ostringstream& debug_)
+ : cx(cx_)
+ , debug(debug_)
+ {
+ }
+
+ MOZ_MUST_USE bool treeSerialize(const ParseNode* node, std::ostringstream& out)
+ {
+ UniquePtr<StringTree> tree(treeSerializeAux(node));
+ if (!tree) {
+ return false;
}
- // left, right
- case PNK_CASE: MOZ_FALLTHROUGH;
- case PNK_WHILE: MOZ_FALLTHROUGH;
- case PNK_DOWHILE: MOZ_FALLTHROUGH;
- case PNK_FOR: MOZ_FALLTHROUGH;
- case PNK_COMPREHENSIONFOR: MOZ_FALLTHROUGH;
- case PNK_WITH: MOZ_FALLTHROUGH;
- case PNK_EXPORT_FROM: MOZ_FALLTHROUGH;
- case PNK_ASSIGN: MOZ_FALLTHROUGH;
- case PNK_ADDASSIGN: MOZ_FALLTHROUGH;
- case PNK_SUBASSIGN: MOZ_FALLTHROUGH;
- case PNK_BITORASSIGN: MOZ_FALLTHROUGH;
- case PNK_BITXORASSIGN: MOZ_FALLTHROUGH;
- case PNK_BITANDASSIGN: MOZ_FALLTHROUGH;
- case PNK_LSHASSIGN: MOZ_FALLTHROUGH;
- case PNK_RSHASSIGN: MOZ_FALLTHROUGH;
- case PNK_URSHASSIGN: MOZ_FALLTHROUGH;
- case PNK_MULASSIGN: MOZ_FALLTHROUGH;
- case PNK_DIVASSIGN: MOZ_FALLTHROUGH;
- case PNK_MODASSIGN: MOZ_FALLTHROUGH;
- case PNK_POWASSIGN: MOZ_FALLTHROUGH;
- case PNK_ELEM: MOZ_FALLTHROUGH;
- case PNK_COLON: MOZ_FALLTHROUGH;
- case PNK_SHORTHAND: MOZ_FALLTHROUGH;
- case PNK_SETTHIS: MOZ_FALLTHROUGH;
- case PNK_SWITCH: MOZ_FALLTHROUGH;
- case PNK_IMPORT: MOZ_FALLTHROUGH;
- case PNK_EXPORT_SPEC: MOZ_FALLTHROUGH;
- case PNK_CLASSMETHOD: MOZ_FALLTHROUGH;
- case PNK_CLASSNAMES: MOZ_FALLTHROUGH;
- case PNK_NEWTARGET: {
- mozilla::Vector<UniquePtr<StringTree>> children;
- if (!children.append(Move(UniquePtr<StringTree>(treeSerialize(cx, node->pn_left, debug))))) {
- MOZ_CRASH();
- }
- if (!children.append(Move(UniquePtr<StringTree>(treeSerialize(cx, node->pn_right, debug))))) {
- MOZ_CRASH();
- }
- return StringTree::makeConcat(cx, kind, Move(children));
+ // Find number of occurrences of each string.
+ std::unordered_map<std::string, uint32_t> occurrences;
+ computeOccurrences(tree.get(), occurrences);
+
+ // Sort by number of occurrences.
+ std::vector<Occurrences> heap;
+ for (auto iter: occurrences) {
+ heap.push_back(iter);
+ }
+ std::make_heap(heap.begin(), heap.end(), compareOccurrences); // FIXME: Check order.
+
+ std::unordered_map<std::string, uint32_t> strings;
+
+ // Assign an identifier to each string
+ uint32_t i = 0;
+ for (auto iter: heap) {
+ strings.insert({iter.first, i});
+ ++i;
+ }
+ this->strings = Move(strings);
+
+ // Prepare and write lengths table
+ std::vector<uint32_t> lengths;
+ for (auto iter: heap) {
+ lengths.push_back(iter.first.length());
}
- // kid1, kid3
- case PNK_FORIN: MOZ_FALLTHROUGH;
- case PNK_FOROF: {
- mozilla::Vector<UniquePtr<StringTree>> children;
- // No node->pn_kid2 for these nodes.
- if (!children.append(UniquePtr<StringTree>(treeSerialize(cx, node->pn_kid1, debug)))) {
- MOZ_CRASH();
- }
- if (!children.append(UniquePtr<StringTree>(treeSerialize(cx, node->pn_kid3, debug)))) {
- MOZ_CRASH();
- }
- return StringTree::makeConcat(cx, kind, Move(children));
+ writeUnsigned(out, lengths.size());
+ size_t totalLength = 0;
+ for (auto iter: lengths) {
+ writeUnsigned(out, iter);
+ totalLength += iter;
}
- // pn_kid
- case PNK_THROW: MOZ_FALLTHROUGH;
- case PNK_EXPORT_DEFAULT: MOZ_FALLTHROUGH;
- case PNK_COMPUTED_NAME: MOZ_FALLTHROUGH;
- case PNK_ARRAYPUSH: MOZ_FALLTHROUGH;
- case PNK_SPREAD: MOZ_FALLTHROUGH;
- case PNK_RETURN: MOZ_FALLTHROUGH;
- case PNK_SEMI: MOZ_FALLTHROUGH;
- case PNK_THIS: MOZ_FALLTHROUGH;
- case PNK_YIELD: MOZ_FALLTHROUGH;
- case PNK_YIELD_STAR: MOZ_FALLTHROUGH;
- case PNK_AWAIT: MOZ_FALLTHROUGH;
- case PNK_SUPERBASE: MOZ_FALLTHROUGH;
- case PNK_INITIALYIELD: MOZ_FALLTHROUGH;
- case PNK_POS: MOZ_FALLTHROUGH;
- case PNK_NEG: MOZ_FALLTHROUGH;
- case PNK_VOID: MOZ_FALLTHROUGH;
- case PNK_NOT: MOZ_FALLTHROUGH;
- case PNK_BITNOT: MOZ_FALLTHROUGH;
- case PNK_TYPEOFNAME: MOZ_FALLTHROUGH;
- case PNK_TYPEOFEXPR: MOZ_FALLTHROUGH;
- case PNK_PREINCREMENT: MOZ_FALLTHROUGH;
- case PNK_POSTINCREMENT: MOZ_FALLTHROUGH;
- case PNK_PREDECREMENT: MOZ_FALLTHROUGH;
- case PNK_POSTDECREMENT: MOZ_FALLTHROUGH;
- case PNK_DELETENAME: MOZ_FALLTHROUGH;
- case PNK_DELETEPROP: MOZ_FALLTHROUGH;
- case PNK_DELETEELEM: MOZ_FALLTHROUGH;
- case PNK_DELETEEXPR: MOZ_FALLTHROUGH;
- case PNK_MUTATEPROTO: {
- UniquePtr<StringTree> kid(treeSerialize(cx, node->pn_kid, debug));
- return StringTree::makeConcat(cx, kind, Move(kid));
+ // std::cerr << "treeSerialize: writing " << lengths.size() << " strings, for a total length of " << totalLength << "\n";
+
+ // Now write strings in order.
+ // To extract a string, you first need to have read the lengths table.
+ MOZ_ASSERT(heap.size() == lengths.size());
+ const size_t startStrings = out.tellp();
+ for (auto iter: heap) {
+ out.write(&iter.first[0], iter.first.length());
}
- // Linked lists
- case PNK_CLASSMETHODLIST: MOZ_FALLTHROUGH;
- case PNK_EXPORT_SPEC_LIST: MOZ_FALLTHROUGH;
- case PNK_STATEMENTLIST: MOZ_FALLTHROUGH;
- case PNK_CATCHLIST: MOZ_FALLTHROUGH;
- case PNK_VAR: MOZ_FALLTHROUGH;
- case PNK_LET: MOZ_FALLTHROUGH;
- case PNK_CONST: MOZ_FALLTHROUGH;
- case PNK_COMMA: MOZ_FALLTHROUGH;
- case PNK_CALL: MOZ_FALLTHROUGH;
- case PNK_GENEXP: MOZ_FALLTHROUGH;
- case PNK_ARRAY: MOZ_FALLTHROUGH;
- case PNK_OBJECT: MOZ_FALLTHROUGH;
- case PNK_PARAMSBODY: MOZ_FALLTHROUGH;
- case PNK_OR: MOZ_FALLTHROUGH;
- case PNK_AND: MOZ_FALLTHROUGH;
- case PNK_BITOR: MOZ_FALLTHROUGH;
- case PNK_BITXOR: MOZ_FALLTHROUGH;
- case PNK_BITAND: MOZ_FALLTHROUGH;
- case PNK_EQ: MOZ_FALLTHROUGH;
- case PNK_NE: MOZ_FALLTHROUGH;
- case PNK_STRICTEQ: MOZ_FALLTHROUGH;
- case PNK_STRICTNE: MOZ_FALLTHROUGH;
- case PNK_LT: MOZ_FALLTHROUGH;
- case PNK_LE: MOZ_FALLTHROUGH;
- case PNK_GT: MOZ_FALLTHROUGH;
- case PNK_GE: MOZ_FALLTHROUGH;
- case PNK_LSH: MOZ_FALLTHROUGH;
- case PNK_RSH: MOZ_FALLTHROUGH;
- case PNK_URSH: MOZ_FALLTHROUGH;
- case PNK_ADD: MOZ_FALLTHROUGH;
- case PNK_SUB: MOZ_FALLTHROUGH;
- case PNK_STAR: MOZ_FALLTHROUGH;
- case PNK_DIV: MOZ_FALLTHROUGH;
- case PNK_MOD: MOZ_FALLTHROUGH;
- case PNK_POW: MOZ_FALLTHROUGH;
- case PNK_NEW: MOZ_FALLTHROUGH;
- case PNK_TEMPLATE_STRING_LIST: MOZ_FALLTHROUGH;
- case PNK_TAGGED_TEMPLATE: MOZ_FALLTHROUGH;
- case PNK_CALLSITEOBJ: MOZ_FALLTHROUGH;
- case PNK_IMPORT_SPEC_LIST: MOZ_FALLTHROUGH;
- case PNK_SUPERCALL: MOZ_FALLTHROUGH;
- case PNK_ARRAYCOMP: MOZ_FALLTHROUGH;
- case PNK_INSTANCEOF: MOZ_FALLTHROUGH;
- case PNK_IN: {
- mozilla::Vector<UniquePtr<StringTree>> children;
+ MOZ_ASSERT((size_t)out.tellp() == (size_t)(startStrings + totalLength));
+
+ // Finally, write tree
+ write(tree.get(), out);
+
+ return true;
+ }
+
+private:
+ void computeOccurrences(StringTree* tree, std::unordered_map<std::string, uint32_t>& occurrences) {
+ if (tree->isLeaf()) {
+ const std::string& data = tree->asLeaf();
+ auto search = occurrences.find(data);
+ if (search != occurrences.end()) {
+ search->second++;
+ } else {
+ occurrences.insert({data, 1});
+ }
+ return;
+ }
+ const Concat& concat = tree->asConcat();
+ for (const UniquePtr<StringTree>& tree: concat.children) {
+ computeOccurrences(tree.get(), occurrences);
+ }
+ }
+
+ MOZ_MUST_USE StringTree* treeSerializeAux(const ParseNode* node)
+ {
+ const ParseNodeKind kind = node ? node->getKind() : PNK_LIMIT;
+ // // std::cerr << "treeSerialize " << NAMES[kind] << "\n";
+ debug << NAMES[kind] << " ";
- for (ParseNode* statement = node->pn_head; statement != nullptr; statement = statement->pn_next) {
- if (!children.append(treeSerialize(cx, statement, debug))) {
+ switch (kind) {
+ // Empty tree
+ case PNK_LIMIT: {
+ return StringTree::makeLeaf(cx, PNK_LIMIT);
+ }
+ // Body
+ case PNK_LEXICALSCOPE:
+ // fprintf(stderr, "PNK_LEXICALSCOPE: placeholder implementation\n");
+ MOZ_FALLTHROUGH;
+ case PNK_FUNCTION: {
+ UniquePtr<StringTree> body(treeSerializeAux(node->pn_body));
+ if (!body) {
+ return nullptr;
+ }
+ return StringTree::makeConcat(cx, kind, Move(body));
+ }
+ // kid1, kid2, kid3
+ case PNK_IF: MOZ_FALLTHROUGH;
+ case PNK_IMPORT_SPEC: MOZ_FALLTHROUGH;
+ case PNK_EXPORT: MOZ_FALLTHROUGH;
+ case PNK_FORHEAD: MOZ_FALLTHROUGH;
+ case PNK_CONDITIONAL: MOZ_FALLTHROUGH;
+ case PNK_CLASS: MOZ_FALLTHROUGH;
+ case PNK_TRY: MOZ_FALLTHROUGH;
+ case PNK_CATCH: {
+ mozilla::Vector<UniquePtr<StringTree>> children;
+ if (!children.append(UniquePtr<StringTree>(treeSerializeAux(node->pn_kid1)))) {
+ MOZ_CRASH();
+ }
+ if (!children.append(UniquePtr<StringTree>(treeSerializeAux(node->pn_kid2)))) {
+ MOZ_CRASH();
+ }
+ if (!children.append(UniquePtr<StringTree>(treeSerializeAux(node->pn_kid3)))) {
+ MOZ_CRASH();
+ }
+ return StringTree::makeConcat(cx, kind, Move(children));
+ }
+
+ // left, right
+ case PNK_CASE: MOZ_FALLTHROUGH;
+ case PNK_WHILE: MOZ_FALLTHROUGH;
+ case PNK_DOWHILE: MOZ_FALLTHROUGH;
+ case PNK_FOR: MOZ_FALLTHROUGH;
+ case PNK_COMPREHENSIONFOR: MOZ_FALLTHROUGH;
+ case PNK_WITH: MOZ_FALLTHROUGH;
+ case PNK_EXPORT_FROM: MOZ_FALLTHROUGH;
+ case PNK_ASSIGN: MOZ_FALLTHROUGH;
+ case PNK_ADDASSIGN: MOZ_FALLTHROUGH;
+ case PNK_SUBASSIGN: MOZ_FALLTHROUGH;
+ case PNK_BITORASSIGN: MOZ_FALLTHROUGH;
+ case PNK_BITXORASSIGN: MOZ_FALLTHROUGH;
+ case PNK_BITANDASSIGN: MOZ_FALLTHROUGH;
+ case PNK_LSHASSIGN: MOZ_FALLTHROUGH;
+ case PNK_RSHASSIGN: MOZ_FALLTHROUGH;
+ case PNK_URSHASSIGN: MOZ_FALLTHROUGH;
+ case PNK_MULASSIGN: MOZ_FALLTHROUGH;
+ case PNK_DIVASSIGN: MOZ_FALLTHROUGH;
+ case PNK_MODASSIGN: MOZ_FALLTHROUGH;
+ case PNK_POWASSIGN: MOZ_FALLTHROUGH;
+ case PNK_ELEM: MOZ_FALLTHROUGH;
+ case PNK_COLON: MOZ_FALLTHROUGH;
+ case PNK_SHORTHAND: MOZ_FALLTHROUGH;
+ case PNK_SETTHIS: MOZ_FALLTHROUGH;
+ case PNK_SWITCH: MOZ_FALLTHROUGH;
+ case PNK_IMPORT: MOZ_FALLTHROUGH;
+ case PNK_EXPORT_SPEC: MOZ_FALLTHROUGH;
+ case PNK_CLASSMETHOD: MOZ_FALLTHROUGH;
+ case PNK_CLASSNAMES: MOZ_FALLTHROUGH;
+ case PNK_NEWTARGET: {
+ mozilla::Vector<UniquePtr<StringTree>> children;
+ if (!children.append(Move(UniquePtr<StringTree>(treeSerializeAux(node->pn_left))))) {
+ MOZ_CRASH();
+ }
+ if (!children.append(Move(UniquePtr<StringTree>(treeSerializeAux(node->pn_right))))) {
+ MOZ_CRASH();
+ }
+ return StringTree::makeConcat(cx, kind, Move(children));
+ }
+
+ // kid1, kid3
+ case PNK_FORIN: MOZ_FALLTHROUGH;
+ case PNK_FOROF: {
+ mozilla::Vector<UniquePtr<StringTree>> children;
+ // No node->pn_kid2 for these nodes.
+ if (!children.append(UniquePtr<StringTree>(treeSerializeAux(node->pn_kid1)))) {
+ MOZ_CRASH();
+ }
+ if (!children.append(UniquePtr<StringTree>(treeSerializeAux(node->pn_kid3)))) {
MOZ_CRASH();
}
+ return StringTree::makeConcat(cx, kind, Move(children));
}
- return StringTree::makeConcat(cx, kind, Move(children));
+
+ // pn_kid
+ case PNK_THROW: MOZ_FALLTHROUGH;
+ case PNK_EXPORT_DEFAULT: MOZ_FALLTHROUGH;
+ case PNK_COMPUTED_NAME: MOZ_FALLTHROUGH;
+ case PNK_ARRAYPUSH: MOZ_FALLTHROUGH;
+ case PNK_SPREAD: MOZ_FALLTHROUGH;
+ case PNK_RETURN: MOZ_FALLTHROUGH;
+ case PNK_SEMI: MOZ_FALLTHROUGH;
+ case PNK_THIS: MOZ_FALLTHROUGH;
+ case PNK_YIELD: MOZ_FALLTHROUGH;
+ case PNK_YIELD_STAR: MOZ_FALLTHROUGH;
+ case PNK_AWAIT: MOZ_FALLTHROUGH;
+ case PNK_SUPERBASE: MOZ_FALLTHROUGH;
+ case PNK_INITIALYIELD: MOZ_FALLTHROUGH;
+ case PNK_POS: MOZ_FALLTHROUGH;
+ case PNK_NEG: MOZ_FALLTHROUGH;
+ case PNK_VOID: MOZ_FALLTHROUGH;
+ case PNK_NOT: MOZ_FALLTHROUGH;
+ case PNK_BITNOT: MOZ_FALLTHROUGH;
+ case PNK_TYPEOFNAME: MOZ_FALLTHROUGH;
+ case PNK_TYPEOFEXPR: MOZ_FALLTHROUGH;
+ case PNK_PREINCREMENT: MOZ_FALLTHROUGH;
+ case PNK_POSTINCREMENT: MOZ_FALLTHROUGH;
+ case PNK_PREDECREMENT: MOZ_FALLTHROUGH;
+ case PNK_POSTDECREMENT: MOZ_FALLTHROUGH;
+ case PNK_DELETENAME: MOZ_FALLTHROUGH;
+ case PNK_DELETEPROP: MOZ_FALLTHROUGH;
+ case PNK_DELETEELEM: MOZ_FALLTHROUGH;
+ case PNK_DELETEEXPR: MOZ_FALLTHROUGH;
+ case PNK_MUTATEPROTO: {
+ UniquePtr<StringTree> kid(treeSerializeAux(node->pn_kid));
+ return StringTree::makeConcat(cx, kind, Move(kid));
+ }
+
+ // Linked lists
+ case PNK_CLASSMETHODLIST: MOZ_FALLTHROUGH;
+ case PNK_EXPORT_SPEC_LIST: MOZ_FALLTHROUGH;
+ case PNK_STATEMENTLIST: MOZ_FALLTHROUGH;
+ case PNK_CATCHLIST: MOZ_FALLTHROUGH;
+ case PNK_VAR: MOZ_FALLTHROUGH;
+ case PNK_LET: MOZ_FALLTHROUGH;
+ case PNK_CONST: MOZ_FALLTHROUGH;
+ case PNK_COMMA: MOZ_FALLTHROUGH;
+ case PNK_CALL: MOZ_FALLTHROUGH;
+ case PNK_GENEXP: MOZ_FALLTHROUGH;
+ case PNK_ARRAY: MOZ_FALLTHROUGH;
+ case PNK_OBJECT: MOZ_FALLTHROUGH;
+ case PNK_PARAMSBODY: MOZ_FALLTHROUGH;
+ case PNK_OR: MOZ_FALLTHROUGH;
+ case PNK_AND: MOZ_FALLTHROUGH;
+ case PNK_BITOR: MOZ_FALLTHROUGH;
+ case PNK_BITXOR: MOZ_FALLTHROUGH;
+ case PNK_BITAND: MOZ_FALLTHROUGH;
+ case PNK_EQ: MOZ_FALLTHROUGH;
+ case PNK_NE: MOZ_FALLTHROUGH;
+ case PNK_STRICTEQ: MOZ_FALLTHROUGH;
+ case PNK_STRICTNE: MOZ_FALLTHROUGH;
+ case PNK_LT: MOZ_FALLTHROUGH;
+ case PNK_LE: MOZ_FALLTHROUGH;
+ case PNK_GT: MOZ_FALLTHROUGH;
+ case PNK_GE: MOZ_FALLTHROUGH;
+ case PNK_LSH: MOZ_FALLTHROUGH;
+ case PNK_RSH: MOZ_FALLTHROUGH;
+ case PNK_URSH: MOZ_FALLTHROUGH;
+ case PNK_ADD: MOZ_FALLTHROUGH;
+ case PNK_SUB: MOZ_FALLTHROUGH;
+ case PNK_STAR: MOZ_FALLTHROUGH;
+ case PNK_DIV: MOZ_FALLTHROUGH;
+ case PNK_MOD: MOZ_FALLTHROUGH;
+ case PNK_POW: MOZ_FALLTHROUGH;
+ case PNK_NEW: MOZ_FALLTHROUGH;
+ case PNK_TEMPLATE_STRING_LIST: MOZ_FALLTHROUGH;
+ case PNK_TAGGED_TEMPLATE: MOZ_FALLTHROUGH;
+ case PNK_CALLSITEOBJ: MOZ_FALLTHROUGH;
+ case PNK_IMPORT_SPEC_LIST: MOZ_FALLTHROUGH;
+ case PNK_SUPERCALL: MOZ_FALLTHROUGH;
+ case PNK_ARRAYCOMP: MOZ_FALLTHROUGH;
+ case PNK_INSTANCEOF: MOZ_FALLTHROUGH;
+ case PNK_IN: {
+ mozilla::Vector<UniquePtr<StringTree>> children;
+
+ for (ParseNode* statement = node->pn_head; statement != nullptr; statement = statement->pn_next) {
+ if (!children.append(treeSerializeAux(statement))) {
+ MOZ_CRASH();
+ }
+ }
+ return StringTree::makeConcat(cx, kind, Move(children));
+ }
+
+ // Labels
+ case PNK_BREAK: MOZ_FALLTHROUGH;
+ case PNK_CONTINUE: {
+ const LoopControlStatement* cast = reinterpret_cast<const LoopControlStatement*>(node);
+ std::string label;
+ serializeAtom(cx, cast->label(), label);
+ return StringTree::makeLeaf(cx, kind, Move(label));
+ }
+ case PNK_LABEL: {
+ const LabeledStatement* cast = reinterpret_cast<const LabeledStatement*>(node);
+ std::string label;
+ serializeAtom(cx, cast->label(), label);
+
+ mozilla::Vector<UniquePtr<StringTree>> children;
+ if (!children.append(StringTree::makeLeaf(cx, kind, Move(label)))) {
+ MOZ_CRASH();
+ }
+ if (!children.append(treeSerializeAux(node->pn_expr))) {
+ MOZ_CRASH();
+ }
+ return StringTree::makeConcat(cx, kind, Move(children));
+ }
+ case PNK_DOT: {
+ std::string label;
+ serializeAtom(cx, node->pn_atom, label);
+
+ mozilla::Vector<UniquePtr<StringTree>> children;
+ if (!children.append(StringTree::makeLeaf(cx, PNK_DOT, Move(label)))) {
+ MOZ_CRASH();
+ }
+ if (!children.append(treeSerializeAux(node->pn_expr))) {
+ MOZ_CRASH();
+ }
+ return StringTree::makeConcat(cx, kind, Move(children));
+ }
+
+ // Strings
+ case PNK_NAME: MOZ_FALLTHROUGH;
+ case PNK_STRING: MOZ_FALLTHROUGH;
+ case PNK_TEMPLATE_STRING: {
+ std::string data;
+ serializeAtom(cx, node->pn_atom, data);
+ return StringTree::makeLeaf(cx, kind, Move(data));
+ }
+
+ case PNK_NUMBER: {
+ std::string data;
+ double dval = node->pn_dval;
+ data.append((const char*)&dval, sizeof(dval));
+ DecimalPoint point = node->pn_u.number.decimalPoint;
+ data.append((const char*)&point, sizeof(point));
+ return StringTree::makeLeaf(cx, kind, Move(data));
+ }
+
+ // Nullaries
+ case PNK_TRUE: MOZ_FALLTHROUGH;
+ case PNK_FALSE: MOZ_FALLTHROUGH;
+ case PNK_NULL: MOZ_FALLTHROUGH;
+ case PNK_RAW_UNDEFINED: MOZ_FALLTHROUGH;
+ case PNK_GENERATOR: MOZ_FALLTHROUGH;
+ case PNK_NOP: MOZ_FALLTHROUGH;
+ case PNK_EXPORT_BATCH_SPEC: MOZ_FALLTHROUGH;
+ case PNK_POSHOLDER:
+ return StringTree::makeLeaf(cx, kind);
+
+ // Entirely undocumented nodes:
+ case PNK_MODULE: MOZ_FALLTHROUGH;
+ case PNK_DEBUGGER: MOZ_FALLTHROUGH;
+ case PNK_ELISION: MOZ_FALLTHROUGH;
+ case PNK_OBJECT_PROPERTY_NAME: {
+ return StringTree::makeLeaf(cx, kind);
+ }
+
+ // Stuff we don't handle yet:
+ case PNK_REGEXP: {
+ // fprintf(stderr, "Placeholder %s\n", NAMES[kind]);
+ std::ostringstream placeholder("/Placeholder regexp/");
+ placeholder << debug.tellp() << "/g"; // FIXME: Just a way to make each regexp unique.
+ return StringTree::makeLeaf(cx, kind, Move(placeholder.str()));
+ }
+ }
+ MOZ_CRASH("Shouldn't reach that point.");
+ }
+
+public:
+ void write(StringTree* tree, std::ostringstream& out) const {
+ // std::cerr << "StringTree::write at " << out.tellp() << "\n";
+ #if DEBUG_HEADER
+ // std::cerr << "StringTree::write HEADER at " << out.tellp() << "\n";
+ out.write(HEADER, sizeof HEADER - 1);
+ #endif // DEBUG_HEADER
+
+ // std::cerr << "StringTree::write kind at " << out.tellp() << "\n";
+ out.write((const char*)&tree->kind, sizeof tree->kind);
+
+ // std::cerr << "StringTree::write " << NAMES[tree->kind] << "\n";
+ if (tree->kind != PNK_LIMIT) {
+
+ #if DEBUG_HEADER
+ // std::cerr << "StringTree::write variantKind at " << out.tellp() << "\n";
+ const unsigned char char_variant = tree->variantKind;
+ out.write((const char*)&char_variant, sizeof char_variant);
+ MOZ_ASSERT(tree->variantKind == VariantKind::IsLeaf || tree->variantKind == VariantKind::IsConcat);
+ #endif // DEBUG_HEADER
+
+ if (tree->isLeaf()) {
+ // A leaf is a simple integer, so no byteLength.
+ const uint32_t id = getStringId(tree->asLeaf());
+ writeUnsigned(out, id);
+ } else {
+ std::ostringstream sub_out;
+
+ const Concat& concat = tree->asConcat();
+ writeUnsigned(sub_out, concat.children.length());
+ for (const UniquePtr<StringTree>& tree: concat.children) {
+ write(tree.get(), sub_out);
+ }
+
+ const std::string sub_str = sub_out.str();
+ const size_t byteLength = sub_str.length();
+ writeUnsigned(out, byteLength);
+ MOZ_ASSERT(byteLength != 0);
+
+ const size_t byteStart = out.tellp();
+ out << sub_str;
+ const size_t byteStop = out.tellp();
+ // std::cerr << "StringTree::write length at " << byteStart << "\n";
+ MOZ_ASSERT(byteLength == byteStop - byteStart);
+ }
}
- // Labels
- case PNK_BREAK: MOZ_FALLTHROUGH;
- case PNK_CONTINUE: {
- const LoopControlStatement* cast = reinterpret_cast<const LoopControlStatement*>(node);
- std::string label;
- serializeAtom(cx, cast->label(), label);
- return StringTree::makeLeaf(cx, kind, Move(label));
- }
- case PNK_LABEL: {
- const LabeledStatement* cast = reinterpret_cast<const LabeledStatement*>(node);
- std::string label;
- serializeAtom(cx, cast->label(), label);
- mozilla::Vector<UniquePtr<StringTree>> children;
- if (!children.append(StringTree::makeLeaf(cx, kind, Move(label)))) {
- MOZ_CRASH();
- }
- if (!children.append(treeSerialize(cx, node->pn_expr, debug))) {
- MOZ_CRASH();
- }
- return StringTree::makeConcat(cx, kind, Move(children));
- }
- case PNK_DOT: {
- std::string label;
- serializeAtom(cx, node->pn_atom, label);
-
- mozilla::Vector<UniquePtr<StringTree>> children;
- if (!children.append(StringTree::makeLeaf(cx, PNK_DOT, Move(label)))) {
- MOZ_CRASH();
- }
- if (!children.append(treeSerialize(cx, node->pn_expr, debug))) {
- MOZ_CRASH();
- }
- return StringTree::makeConcat(cx, kind, Move(children));
- }
-
- // Strings
- case PNK_NAME: MOZ_FALLTHROUGH;
- case PNK_STRING: MOZ_FALLTHROUGH;
- case PNK_TEMPLATE_STRING: {
- std::string data;
- serializeAtom(cx, node->pn_atom, data);
- return StringTree::makeLeaf(cx, kind, Move(data));
- }
-
- case PNK_NUMBER: {
- std::string data;
- double dval = node->pn_dval;
- data.append((const char*)&dval, sizeof(dval));
- DecimalPoint point = node->pn_u.number.decimalPoint;
- data.append((const char*)&point, sizeof(point));
- return StringTree::makeLeaf(cx, kind, Move(data));
- }
-
- // Nullaries
- case PNK_TRUE: MOZ_FALLTHROUGH;
- case PNK_FALSE: MOZ_FALLTHROUGH;
- case PNK_NULL: MOZ_FALLTHROUGH;
- case PNK_RAW_UNDEFINED: MOZ_FALLTHROUGH;
- case PNK_GENERATOR: MOZ_FALLTHROUGH;
- case PNK_NOP: MOZ_FALLTHROUGH;
- case PNK_EXPORT_BATCH_SPEC: MOZ_FALLTHROUGH;
- case PNK_POSHOLDER:
- return StringTree::makeLeaf(cx, kind);
-
- // Entirely undocumented nodes:
- case PNK_MODULE: MOZ_FALLTHROUGH;
- case PNK_DEBUGGER: MOZ_FALLTHROUGH;
- case PNK_ELISION: MOZ_FALLTHROUGH;
- case PNK_OBJECT_PROPERTY_NAME: {
- return StringTree::makeLeaf(cx, kind);
- }
-
- // Stuff we don't handle yet:
- case PNK_REGEXP: {
- // fprintf(stderr, "Placeholder %s\n", NAMES[kind]);
- return StringTree::makeLeaf(cx, kind, "/Placeholder regexp/");
- }
+ #if DEBUG_HEADER
+ out.write(FOOTER, sizeof FOOTER - 1);
+ #endif // DEBUG_HEADER
}
- MOZ_CRASH("Shouldn't reach that point.");
-}
+};
class TreeParser MOZ_RAII {
public:
TreeParser(JSContext* cx_, ParseNodeAllocator& alloc, std::istringstream& in_, std::ostringstream& debug_)
: cx(cx_)
, in(in_)
, debug(debug_)
, functionDepth(0)
, numberOfNodes(0)
+ , currentByteLength((size_t)-1)
+ , currentByteStart((size_t)-1)
+ , currentKind((ParseNodeKind)-1)
+ , currentVariant((VariantKind)-1)
, allocator(alloc)
{
// std::cerr << "TreeParser starts with offset " << in.tellg() << "\n";
}
~TreeParser() {
MOZ_ASSERT(in.peek() == std::char_traits<char>::eof());
MOZ_ASSERT(functionDepth == 0);
// std::cerr << "TreeParser complete after " << numberOfNodes << " nodes\n";
}
+ ParseNode* parseNode() {
+ // Read table of lengths of strings
+ const uint32_t numberOfStrings = readVariableLength();
+ if (!lengthsTable.initCapacity(numberOfStrings)) {
+ MOZ_CRASH();
+ }
+ for (uint32_t i = 0; i < numberOfStrings; ++i) {
+ if (!lengthsTable.append(readVariableLength())) {
+ MOZ_CRASH();
+ }
+ }
+
+ // Read table of actual strings. Could be read lazily.
+ if (!stringsTable.initCapacity(numberOfStrings)) {
+ MOZ_CRASH();
+ }
+ for (const auto iter: lengthsTable) {
+ std::string data;
+ data.reserve(iter);
+ in.read(&data[0], iter);
+ if (!stringsTable.append(Move(data))) {
+ MOZ_CRASH();
+ }
+ }
+
+ return parseNodeAux();
+ }
+
+private:
// Parse a subtree as a string. The subtree must have been serialized as a string.
// FIXME: Crappy documentation suggests crappy concepts. Clean this up.
- void parseBuf(ParseNodeKind& kind, std::string& buf)
+ uint32_t parseLeaf(ParseNodeKind& kind)
{
- // std::cerr << "parseBuf\n";
+ // std::cerr << "parseLeaf\n";
const size_t previousByteLength = currentByteLength;
const size_t previousByteStart = currentByteStart;
const ParseNodeKind previousKind = currentKind;
+
+ // Debugging values.
+ currentByteLength = -1;
+ currentByteStart = -1;
+ currentKind = (ParseNodeKind)-1;
+
#if DEBUG_HEADER
const VariantKind previousVariant = currentVariant;
#endif // DEBUG_HEADER
handleHeader();
#if DEBUG_HEADER
MOZ_ASSERT(currentVariant == VariantKind::IsLeaf);
#endif
kind = currentKind;
MOZ_ASSERT(currentKind != PNK_LIMIT);
- readNodeAsLeaf(buf);
+ const uint32_t leaf = readNodeAsLeaf();
handleFooter();
currentByteLength = previousByteLength;
currentByteStart = previousByteStart;
currentKind = previousKind;
#if DEBUG_HEADER
currentVariant = previousVariant;
#endif // DEBUG_HEADER
+
+ return leaf;
}
// Parse a subtree as a ParseNode.
- MOZ_MUST_USE ParseNode* parseNode()
+ MOZ_MUST_USE ParseNode* parseNodeAux()
{
// std::cerr << "parseNode " << in.tellg() << "\n";
const size_t previousByteLength = currentByteLength;
const size_t previousByteStart = currentByteStart;
const ParseNodeKind previousKind = currentKind;
#if DEBUG_HEADER
const VariantKind previousVariant = currentVariant;
@@ -595,33 +736,43 @@ public:
#if DEBUG_HEADER
currentVariant = previousVariant;
#endif // DEBUG_HEADER
return result.release();
}
// Note: This should NOT be called if the kind is PNK_LIMIT.
- void readNodeAsLeaf(std::string& buf)
+ uint32_t readNodeAsLeaf()
{
- // std::cerr << "readNodeAsLeaf\n";
+ MOZ_ASSERT(currentKind != PNK_LIMIT);
#if DEBUG_HEADER
MOZ_ASSERT(currentVariant == VariantKind::IsLeaf);
+#else
+ currentVariant = VariantKind::IsLeaf;
#endif
+ currentByteStart = -1; // Ignored for leaves.
- buf.resize(currentByteLength);
- in.read(&buf[0], currentByteLength);
+ return readVariableLength();
}
size_t readNodeAsConcat() {
+ MOZ_ASSERT(currentKind != PNK_LIMIT);
#if DEBUG_HEADER
MOZ_ASSERT(currentVariant == VariantKind::IsConcat);
+#else
+ currentVariant = VariantKind::IsConcat;
#endif
// std::cerr << "readNodeAsConcat length at " << in.tellg() << "\n";
+ currentByteLength = readVariableLength();
+ MOZ_ASSERT(currentByteLength > 0);
+
+ currentByteStart = in.tellg();
+
const size_t length = readVariableLength();
// std::cerr << "readNodeAsConcat: length " << length << "\n";
return length;
}
ParseNode* freeTree(ParseNode* pn) {
return allocator.freeTree(pn);
}
@@ -630,20 +781,19 @@ private:
JSContext* cx;
std::istringstream& in;
std::ostringstream& debug;
size_t functionDepth;
size_t numberOfNodes;
size_t currentByteLength;
size_t currentByteStart;
ParseNodeKind currentKind;
-#if DEBUG_HEADER
VariantKind currentVariant;
-#endif // DEBUG_HEADER
-
+ mozilla::Vector<uint32_t> lengthsTable;
+ mozilla::Vector<std::string> stringsTable;
JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
ParseNodeAllocator& allocator;
ParseNode* allocParseNode(size_t size) {
MOZ_ASSERT(size == sizeof(ParseNode));
return static_cast<ParseNode*>(allocator.allocNode());
}
@@ -664,104 +814,101 @@ private:
in.read((char*)&char_kind, sizeof char_kind);
if (char_kind >= mozilla::ArrayLength(NAMES)) {
MOZ_CRASH("Bad kind");
}
currentKind = (ParseNodeKind)char_kind;
if (currentKind == PNK_LIMIT) {
currentByteLength = 0;
-#if DEBUG_HEADER
currentVariant = VariantKind::IsLeaf;
-#endif // DEBUG_HEADER
} else {
#if DEBUG_HEADER
// std::cerr << "handleHeader variantKind at " << in.tellg() << "\n";
unsigned char char_variant;
in.read((char*)&char_variant, sizeof char_variant);
currentVariant = (VariantKind)char_variant;
// std::cerr << "handleHeader reading variant: " << currentVariant << "\n";
MOZ_ASSERT(currentVariant == VariantKind::IsLeaf || currentVariant == VariantKind::IsConcat);
#endif // DEBUG_HEADER
-
- // std::cerr << "handleHeader byteLength at " << in.tellg() << "\n";
-
- currentByteLength = readVariableLength();
}
-
- currentByteStart = in.tellg();
}
void handleFooter() {
// std::cerr << "handleFooter " << in.tellg() << "\n";
// Check byteLength, footer
- const size_t byteStop = in.tellg();
- MOZ_ASSERT(byteStop - currentByteStart == currentByteLength);
+ if (currentVariant == VariantKind::IsConcat) {
+ const size_t byteStop = in.tellg();
+ MOZ_ASSERT(byteStop - currentByteStart == currentByteLength);
+ }
#if DEBUG_HEADER
std::string footer(sizeof FOOTER - 1, '\0');
in.read(&footer[0], sizeof FOOTER - 1);
if (footer.compare(0, sizeof FOOTER - 1, FOOTER) != 0) {
MOZ_CRASH("Bad footer");
}
#endif // DEBUG_HEADER
// std::cerr << "handleFooter done " << in.tellg() << "\n";
}
uint32_t readVariableLength() {
uint32_t val = 0;
uint32_t shift = 0;
while (true) {
- // std::cerr << "readVariableLength at " << in.tellg() << "\n";
MOZ_ASSERT(shift < 32);
int get = in.get();
// FIXME: BIG HACK because istringstream doesn't like embedded \0.
if (get < 0) {
get = 0;
in.clear();
}
const uint8_t byte = (uint8_t)get;
val |= (uint32_t(byte) >> 1) << shift;
shift += 7;
if (!(byte & 1))
return val;
}
}
+ void getLeafAsString(uint32_t id, std::string& data)
+ {
+ data = stringsTable[id];
+ }
+
MOZ_MUST_USE ParseNode* parseNode(ParseNodeKind kind)
{
debug << NAMES[kind] << " ";
// std::cerr << "parseNode " << NAMES[kind] << "\n";
switch (kind) {
// Empty subtree.
case PNK_LIMIT: {
- std::string leaf;
return nullptr;
}
case PNK_LEXICALSCOPE: {
const size_t length = readNodeAsConcat();
MOZ_ASSERT(length == 1);
- UniquePtr<ParseNode> body(parseNode());
+ UniquePtr<ParseNode> body(parseNodeAux());
MOZ_ASSERT(body);
return new_<LexicalScopeNode>(nullptr, body.release());
}
case PNK_FUNCTION: {
++functionDepth;
const size_t length = readNodeAsConcat();
MOZ_ASSERT(length == 1);
- UniquePtr<ParseNode> body(parseNode());
+ UniquePtr<ParseNode> body(parseNodeAux());
MOZ_ASSERT(body);
CodeNode* result(new_<CodeNode>(kind, JSOP_NOP, TokenPos()));
result->pn_body = body.release();
--functionDepth;
return result;
}
@@ -771,19 +918,19 @@ private:
case PNK_FORHEAD: MOZ_FALLTHROUGH;
case PNK_CONDITIONAL: MOZ_FALLTHROUGH;
case PNK_CLASS: MOZ_FALLTHROUGH;
case PNK_TRY: MOZ_FALLTHROUGH;
case PNK_CATCH: {
const size_t length = readNodeAsConcat();
MOZ_ASSERT(length == 3);
- UniquePtr<ParseNode> kid1(parseNode());
- UniquePtr<ParseNode> kid2(parseNode());
- UniquePtr<ParseNode> kid3(parseNode());
+ UniquePtr<ParseNode> kid1(parseNodeAux());
+ UniquePtr<ParseNode> kid2(parseNodeAux());
+ UniquePtr<ParseNode> kid3(parseNodeAux());
return new_<TernaryNode>(kind, JSOP_NOP, kid1.release(), kid2.release(), kid3.release(), TokenPos());
}
case PNK_CASE: MOZ_FALLTHROUGH;
case PNK_WHILE: MOZ_FALLTHROUGH;
case PNK_DOWHILE: MOZ_FALLTHROUGH;
case PNK_FOR: MOZ_FALLTHROUGH;
case PNK_COMPREHENSIONFOR: MOZ_FALLTHROUGH;
@@ -812,18 +959,18 @@ private:
case PNK_CLASSMETHOD: MOZ_FALLTHROUGH;
case PNK_CLASSNAMES: MOZ_FALLTHROUGH;
case PNK_NEWTARGET: MOZ_FALLTHROUGH;
case PNK_FORIN: MOZ_FALLTHROUGH;
case PNK_FOROF: {
const size_t length = readNodeAsConcat();
MOZ_ASSERT(length == 2);
- UniquePtr<ParseNode> left(parseNode());
- UniquePtr<ParseNode> right(parseNode());
+ UniquePtr<ParseNode> left(parseNodeAux());
+ UniquePtr<ParseNode> right(parseNodeAux());
if (kind == PNK_CASE) {
return new_<CaseClause>(left.release(), right.release(), 0);
} else if (kind == PNK_FORIN || kind == PNK_FOROF) {
return new_<TernaryNode>(kind, JSOP_NOP, left.release(), nullptr, right.release());
} else {
return new_<BinaryNode>(kind, JSOP_NOP, TokenPos(), left.release(), right.release());
}
@@ -857,17 +1004,17 @@ private:
case PNK_DELETENAME: MOZ_FALLTHROUGH;
case PNK_DELETEPROP: MOZ_FALLTHROUGH;
case PNK_DELETEELEM: MOZ_FALLTHROUGH;
case PNK_DELETEEXPR: MOZ_FALLTHROUGH;
case PNK_MUTATEPROTO: {
const size_t length = readNodeAsConcat();
MOZ_ASSERT(length == 1);
- return new_<UnaryNode>(kind, JSOP_NOP, TokenPos(), parseNode());
+ return new_<UnaryNode>(kind, JSOP_NOP, TokenPos(), parseNodeAux());
}
// Linked lists
case PNK_CLASSMETHODLIST: MOZ_FALLTHROUGH;
case PNK_EXPORT_SPEC_LIST: MOZ_FALLTHROUGH;
case PNK_STATEMENTLIST: MOZ_FALLTHROUGH;
case PNK_CATCHLIST: MOZ_FALLTHROUGH;
case PNK_VAR: MOZ_FALLTHROUGH;
@@ -911,17 +1058,17 @@ private:
case PNK_INSTANCEOF: MOZ_FALLTHROUGH;
case PNK_IN: {
const size_t length = readNodeAsConcat();
ParseNode* latest = nullptr;
UniquePtr<ListNode> result(new_<ListNode>(kind, TokenPos()));
for (uint32_t i = 0; i < length; ++i) {
- ParseNode* current = parseNode();
+ ParseNode* current = parseNodeAux();
if (i == 0) {
result->pn_head = current;
result->pn_tail = &result->pn_head;
} else {
latest->pn_next = current;
result->pn_tail = &latest->pn_next;
}
latest = current;
@@ -930,17 +1077,17 @@ private:
return result.release();
}
// Labels
case PNK_BREAK: MOZ_FALLTHROUGH;
case PNK_CONTINUE: {
std::string leaf;
- readNodeAsLeaf(leaf);
+ getLeafAsString(readNodeAsLeaf(), leaf);
RootedPropertyName label(cx);
if (!deserializePropertyName(cx, leaf, &label)) {
label = nullptr;
}
if (kind == PNK_BREAK) {
return new_<BreakStatement>(label, TokenPos());
} else {
return new_<ContinueStatement>(label, TokenPos());
@@ -949,49 +1096,49 @@ private:
case PNK_LABEL: MOZ_FALLTHROUGH;
case PNK_DOT: {
const size_t length = readNodeAsConcat();
MOZ_ASSERT(length == 2);
ParseNodeKind subkind;
std::string leaf;
- parseBuf(subkind, leaf);
+ getLeafAsString(parseLeaf(subkind), leaf);
MOZ_ASSERT(subkind == kind);
RootedAtom atom(cx, deserializeAtom(cx, leaf));
RootedPropertyName label(cx);
if (atom) {
label.set(atom->asPropertyName());
}
- UniquePtr<ParseNode> expr(parseNode());
+ UniquePtr<ParseNode> expr(parseNodeAux());
if (kind == PNK_LABEL) {
return new_<LabeledStatement>(label, expr.release(), 0);
} else {
return new_<PropertyAccess>(expr.release(), label, 0, 0);
}
}
// Strings
case PNK_NAME: MOZ_FALLTHROUGH;
case PNK_STRING: MOZ_FALLTHROUGH;
case PNK_TEMPLATE_STRING: {
std::string leaf;
- readNodeAsLeaf(leaf);
+ getLeafAsString(readNodeAsLeaf(), leaf);
RootedAtom atom(cx, deserializeAtom(cx, leaf));
if (kind == PNK_NAME) {
return new_<NameNode>(kind, JSOP_NOP, atom, TokenPos());
} else {
return new_<NullaryNode>(kind, JSOP_NOP, TokenPos(), atom);
}
}
case PNK_NUMBER: {
std::string leaf;
- readNodeAsLeaf(leaf);
+ getLeafAsString(readNodeAsLeaf(), leaf);
std::istringstream input(leaf); // FIXME: Useless copy.
double dval;
input.read((char*)&dval, sizeof dval);
DecimalPoint point;
input.read((char*)&point, sizeof point);
@@ -1004,58 +1151,49 @@ private:
case PNK_TRUE: MOZ_FALLTHROUGH;
case PNK_FALSE: MOZ_FALLTHROUGH;
case PNK_NULL: MOZ_FALLTHROUGH;
case PNK_RAW_UNDEFINED: MOZ_FALLTHROUGH;
case PNK_GENERATOR: MOZ_FALLTHROUGH;
case PNK_NOP: MOZ_FALLTHROUGH;
case PNK_EXPORT_BATCH_SPEC: MOZ_FALLTHROUGH;
case PNK_POSHOLDER: {
- std::string leaf;
- readNodeAsLeaf(leaf);
- MOZ_ASSERT(leaf.length() == 0);
+ mozilla::Unused << readNodeAsLeaf();
return new_<NullaryNode>(kind, JSOP_NOP, TokenPos());
}
// Entirely undocumented nodes:
case PNK_MODULE: MOZ_FALLTHROUGH;
case PNK_DEBUGGER: MOZ_FALLTHROUGH;
case PNK_ELISION: MOZ_FALLTHROUGH;
case PNK_OBJECT_PROPERTY_NAME: {
- std::string leaf;
- readNodeAsLeaf(leaf);
- MOZ_ASSERT(leaf.length() == 0);
+ mozilla::Unused << readNodeAsLeaf();
return new_<NullaryNode>(kind, JSOP_NOP, TokenPos());
}
// Stuff we don't handle yet:
case PNK_REGEXP: {
std::string leaf;
- readNodeAsLeaf(leaf);
+ getLeafAsString(readNodeAsLeaf(), leaf);
return new_<RegExpLiteral>(nullptr, TokenPos());
}
}
MOZ_CRASH("treeParse: out of switch()");
return nullptr;
}
};
MOZ_MUST_USE bool binSerialize(JSContext* cx, const ParseNode* node, std::ostringstream& out, std::ostringstream& debug) {
- const UniquePtr<StringTree> tree(treeSerialize(cx, node, debug));
- if (!tree) {
- return false;
- }
- tree->write(out);
- // std::cerr << "binSerialize produced " << out.tellp() << " bytes\n";
- return true;
+ TreeSerializer serializer(cx, debug);
+ return serializer.treeSerialize(node, out);
}
MOZ_MUST_USE ParseNode* binParse(JSContext* cx, ParseNodeAllocator& alloc, std::istringstream& in, std::ostringstream& debug) {
TreeParser parser(cx, alloc, in, debug);
return parser.parseNode();
}
} // namespace frontend
} // namespace js