--- a/js/src/frontend/BinaryAST.cpp
+++ b/js/src/frontend/BinaryAST.cpp
@@ -19,18 +19,18 @@ namespace frontend {
*/
enum OptionValue {
OptionIsHere = -1,
OptionIsAbsent = -2,
};
// 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.";
+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"
};
@@ -567,21 +567,22 @@ treeSerialize(JSContext* cx, const Parse
return StringTree::makeLeaf(cx, kind, "/Placeholder regexp/");
}
}
MOZ_CRASH("Shouldn't reach that point.");
}
class TreeParser MOZ_RAII {
public:
- TreeParser(JSContext* cx_, std::istringstream& in_, std::ostringstream& debug_)
+ TreeParser(JSContext* cx_, ParseNodeAllocator& alloc, std::istringstream& in_, std::ostringstream& debug_)
: cx(cx_)
, in(in_)
, debug(debug_)
, functionDepth(0)
+ , allocator(alloc)
{ }
~TreeParser() {
MOZ_ASSERT(in.peek() == std::char_traits<char>::eof());
MOZ_ASSERT(functionDepth == 0);
}
// Parse a subtree as a string. The subtree must have been serialized as a string.
// FIXME: Crappy documentation suggests crappy concepts. Clean this up.
@@ -634,22 +635,30 @@ public:
in.read((char*)&variant, sizeof variant);
MOZ_ASSERT(variant == StringTree::VariantKind::IsConcat);
size_t length;
in.read((char*)&length, sizeof length);
return length;
}
+ ParseNode* freeTree(ParseNode* pn) { return allocator.freeTree(pn); }
private:
JSContext* cx;
std::istringstream& in;
std::ostringstream& debug;
size_t functionDepth;
+ 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());
+ }
+
void readHeader(ParseNodeKind& kind, size_t& byteLength)
{
std::string header(sizeof HEADER, '\0');
in.read(&header[0], sizeof HEADER);
if (header.compare(0, sizeof HEADER - 1, HEADER) != 0) {
MOZ_CRASH("Bad header");
}
@@ -692,28 +701,28 @@ private:
case PNK_LEXICALSCOPE: {
const size_t length = readNodeAsConcat();
MOZ_ASSERT(length == 1);
UniquePtr<ParseNode> body(parseNode());
MOZ_ASSERT(body);
- return cx->new_<LexicalScopeNode>(nullptr, body.release());
+ return new_<LexicalScopeNode>(nullptr, body.release());
}
case PNK_FUNCTION: {
++functionDepth;
const size_t length = readNodeAsConcat();
MOZ_ASSERT(length == 1);
UniquePtr<ParseNode> body(parseNode());
MOZ_ASSERT(body);
- CodeNode* result(cx->new_<CodeNode>(kind, JSOP_NOP, TokenPos()));
+ CodeNode* result(new_<CodeNode>(kind, JSOP_NOP, TokenPos()));
result->pn_body = body.release();
--functionDepth;
return result;
}
case PNK_IF: MOZ_FALLTHROUGH;
case PNK_IMPORT_SPEC: MOZ_FALLTHROUGH;
case PNK_EXPORT: MOZ_FALLTHROUGH;
@@ -723,17 +732,17 @@ private:
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());
- return cx->new_<TernaryNode>(kind, JSOP_NOP, kid1.release(), kid2.release(), kid3.release(), TokenPos());
+ 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;
case PNK_WITH: MOZ_FALLTHROUGH;
@@ -765,21 +774,21 @@ private:
case PNK_FOROF: {
const size_t length = readNodeAsConcat();
MOZ_ASSERT(length == 2);
UniquePtr<ParseNode> left(parseNode());
UniquePtr<ParseNode> right(parseNode());
if (kind == PNK_CASE) {
- return cx->new_<CaseClause>(left.release(), right.release(), 0);
+ return new_<CaseClause>(left.release(), right.release(), 0);
} else if (kind == PNK_FORIN || kind == PNK_FOROF) {
- return cx->new_<TernaryNode>(kind, JSOP_NOP, left.release(), nullptr, right.release());
+ return new_<TernaryNode>(kind, JSOP_NOP, left.release(), nullptr, right.release());
} else {
- return cx->new_<BinaryNode>(kind, JSOP_NOP, TokenPos(), left.release(), right.release());
+ return new_<BinaryNode>(kind, JSOP_NOP, TokenPos(), left.release(), right.release());
}
}
// pn_kid
case PNK_THROW: MOZ_FALLTHROUGH;
case PNK_EXPORT_DEFAULT: MOZ_FALLTHROUGH;
case PNK_COMPUTED_NAME: MOZ_FALLTHROUGH;
case PNK_ARRAYPUSH: MOZ_FALLTHROUGH;
@@ -806,17 +815,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 cx->new_<UnaryNode>(kind, JSOP_NOP, TokenPos(), parseNode());
+ return new_<UnaryNode>(kind, JSOP_NOP, TokenPos(), parseNode());
}
// 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;
@@ -857,47 +866,47 @@ private:
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: {
const size_t length = readNodeAsConcat();
ParseNode* latest = nullptr;
- UniquePtr<ListNode> result(cx->new_<ListNode>(kind, TokenPos()));
+ UniquePtr<ListNode> result(new_<ListNode>(kind, TokenPos()));
for (uint32_t i = 0; i < length; ++i) {
- auto link = parseNode();
+ ParseNode* current = parseNode();
if (i == 0) {
- result->pn_head = link;
+ result->pn_head = current;
result->pn_tail = &result->pn_head;
} else {
- latest->pn_next = link;
+ latest->pn_next = current;
result->pn_tail = &latest->pn_next;
}
- latest = link;
+ latest = current;
result->pn_count++; // Incrementing progressively in case it helps if the destructor is called early.
}
return result.release();
}
// Labels
case PNK_BREAK: MOZ_FALLTHROUGH;
case PNK_CONTINUE: {
std::string leaf;
readNodeAsLeaf(leaf);
RootedPropertyName label(cx);
if (!deserializePropertyName(cx, leaf, &label)) {
label = nullptr;
}
if (kind == PNK_BREAK) {
- return cx->new_<BreakStatement>(label, TokenPos());
+ return new_<BreakStatement>(label, TokenPos());
} else {
- return cx->new_<ContinueStatement>(label, TokenPos());
+ return new_<ContinueStatement>(label, TokenPos());
}
}
case PNK_LABEL: MOZ_FALLTHROUGH;
case PNK_DOT: {
const size_t length = readNodeAsConcat();
MOZ_ASSERT(length == 2);
@@ -908,44 +917,48 @@ private:
RootedAtom atom(cx, deserializeAtom(cx, leaf));
RootedPropertyName label(cx);
if (atom) {
label.set(atom->asPropertyName());
}
UniquePtr<ParseNode> expr(parseNode());
if (kind == PNK_LABEL) {
- return cx->new_<LabeledStatement>(label, expr.release(), 0);
+ return new_<LabeledStatement>(label, expr.release(), 0);
} else {
- return cx->new_<PropertyAccess>(expr.release(), label, 0, 0);
+ 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);
RootedAtom atom(cx, deserializeAtom(cx, leaf));
- return cx->new_<NullaryNode>(kind, JSOP_NOP, TokenPos(), atom);
+ 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);
std::istringstream input(leaf); // FIXME: Useless copy.
double dval;
input.read((char*)&dval, sizeof dval);
DecimalPoint point;
input.read((char*)&point, sizeof point);
- UniquePtr<ParseNode> node(cx->new_<ParseNode>(kind, JSOP_NOP, PN_NULLARY));
+ UniquePtr<ParseNode> node(new_<ParseNode>(kind, JSOP_NOP, PN_NULLARY));
node->initNumber(dval, point);
return node.release();
}
// Nullaries
case PNK_TRUE: MOZ_FALLTHROUGH;
case PNK_FALSE: MOZ_FALLTHROUGH;
case PNK_NULL: MOZ_FALLTHROUGH;
@@ -953,53 +966,54 @@ private:
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);
- return cx->new_<NullaryNode>(kind, JSOP_NOP, TokenPos());
+ 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);
- return cx->new_<NullaryNode>(kind, JSOP_NOP, TokenPos());
+ return new_<NullaryNode>(kind, JSOP_NOP, TokenPos());
}
// Stuff we don't handle yet:
case PNK_REGEXP: {
std::string leaf;
readNodeAsLeaf(leaf);
- return cx->new_<RegExpLiteral>(nullptr, TokenPos());
+ 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;
}
-MOZ_MUST_USE ParseNode* binParse(JSContext* cx, std::istringstream& in, std::ostringstream& debug) {
- TreeParser parser(cx, in, debug);
+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
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -38,16 +38,19 @@
#include "wasm/AsmJS.h"
#include "jsatominlines.h"
#include "jsscriptinlines.h"
#include "frontend/ParseNode-inl.h"
#include "vm/EnvironmentObject-inl.h"
+
+#include <fstream>
+
using namespace js;
using namespace js::gc;
using mozilla::Maybe;
using mozilla::Move;
using mozilla::Nothing;
using mozilla::PodCopy;
using mozilla::PodZero;
@@ -2160,18 +2163,16 @@ Parser<FullParseHandler>::evalBody(EvalS
return body;
}
template <>
ParseNode*
Parser<FullParseHandler>::globalBody(GlobalSharedContext* globalsc)
{
- fprintf(stderr, "Parser::globalBody\n");
-
ParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr);
if (!globalpc.init())
return nullptr;
ParseContext::VarScope varScope(this);
if (!varScope.init(pc))
return nullptr;
@@ -2203,23 +2204,57 @@ Parser<FullParseHandler>::globalBody(Glo
MOZ_CRASH("Serialization failed");
}
std::cerr << "Parser::globalBody serialization success\n";
const auto buf_serialize = serialize.str();
const auto witness = debug.str();
+ // Write to disk, for various measures.
+ {
+ std::ostringstream out_filename;
+ out_filename << "/tmp/";
+
+ std::istringstream in_filename(options().filename());
+ do {
+ char c;
+ in_filename >> c;
+ if (in_filename.eof()) {
+ break;
+ }
+ if (c == '/') {
+ out_filename << '_';
+ } else {
+ out_filename << c;
+ }
+ }
+ out_filename << ".bin";
+ std::cerr << "Dumping cache to " << out_filename.str() << "\n";
+ std::ofstream dump{};
+ dump.open(out_filename.str(), std::ios_base::binary | std::ios_base::trunc | std::ios_base::out);
+ dump << buf_serialize;
+ }
+
+ // Destroy the cache, for benchmarking purposes.
+ {
+ std::ostringstream cache_destroyer;
+ for (size_t i = 0; i < witness.length(); ++i) {
+ cache_destroyer << i;
+ }
+ }
+
std::cerr << "Parser::globalBody binary parsing\n";
std::ostringstream debug_parse;
std::istringstream in_tree(buf_serialize);
const mozilla::TimeStamp binStart = mozilla::TimeStamp::Now();
- UniquePtr<ParseNode> body2(binParse(context, in_tree, debug_parse));
+ ParseNodeAllocator alloc(context, context->tempLifoAlloc());
+ UniquePtr<ParseNode> body2(binParse(context, alloc, in_tree, debug_parse));
const mozilla::TimeStamp binStop = mozilla::TimeStamp::Now();
const mozilla::TimeDuration binDelta = binStop - binStart;
// Sanity checks.
MOZ_ASSERT(body2);
if (debug_parse.str().compare(witness) != 0) {
auto witness_parse = debug_parse.str();
@@ -2260,18 +2295,21 @@ Parser<FullParseHandler>::globalBody(Glo
<< "Common prefix: (" << i << " chars)\n '" << std::string(&witness_reserialize[0], i) << "'"
<< "\n\n Witness: '" << std::string(&witness[i], witness.length() - i) << "'\n"
<< "\n\n Debug: '" << std::string(&witness_reserialize[i], witness_reserialize.length() - i) << "'\n"
<< "\n\n Length difference: " << (witness.length() - witness_reserialize.length()) << "\n";
MOZ_CRASH("Parser::globalBody re-serialization doesn't produce the same witness.");
}
}
+ alloc.freeTree(body2.release()); // FIXME: Kind of defeats the purpose of the UniquePtr.
+
std::cerr << "Parser::globalBody from bin: " << binDelta.ToMilliseconds() << "\n";
+ std::cout << sourceDelta.ToMilliseconds() << ", " << binDelta.ToMilliseconds() << "\n";
return body;
}
template <>
ParseNode*
Parser<FullParseHandler>::moduleBody(ModuleSharedContext* modulesc)
{
MOZ_ASSERT(checkOptionsCalled);