--- a/js/src/frontend/BinaryAST.cpp
+++ b/js/src/frontend/BinaryAST.cpp
@@ -18,49 +18,49 @@
namespace js {
namespace frontend {
// #define DEBUG_HEADER 1
// An abstract data structure on top of string concatenation.
struct StringTree {
- struct Leaf {
+ struct Data {
const std::string data;
- Leaf(const std::string&& data);
- Leaf(); // Empty leaf
+ Data(const std::string&& data);
+ Data(); // Empty leaf
};
struct Concat {
const mozilla::Vector<UniquePtr<StringTree>> children;
Concat(mozilla::Vector<UniquePtr<StringTree>>&&);
};
enum VariantKind {
- IsLeaf = 128,
+ IsData = 128,
IsConcat = 129
};
const VariantKind variantKind;
union Variant {
- Leaf* leaf;
+ Data* leaf;
Concat* concat;
- Variant(Leaf* leaf_):
+ Variant(Data* leaf_):
leaf(leaf_)
{}
Variant(Concat* concat_):
concat(concat_)
{}
};
const Variant data;
const unsigned char kind; // Size-efficient representation of ParseNodeKind.
- // Leaf constructors
- StringTree(ParseNodeKind, Leaf* leaf);
- static StringTree* makeLeaf(JSContext* cx, ParseNodeKind);
- static StringTree* makeLeaf(JSContext* cx, ParseNodeKind, std::string&&);
- bool isLeaf() const;
- const std::string& asLeaf() const;
+ // Data constructors
+ StringTree(ParseNodeKind, Data* leaf);
+ static StringTree* makeData(JSContext* cx, ParseNodeKind);
+ static StringTree* makeData(JSContext* cx, ParseNodeKind, std::string&&);
+ bool isData() const;
+ const std::string& asData() const;
// Concat constructors
StringTree(ParseNodeKind, Concat* concat);
static StringTree* makeConcat(JSContext* cx, ParseNodeKind, UniquePtr<StringTree>&& singleChild);
static StringTree* makeConcat(JSContext* cx, ParseNodeKind, mozilla::Vector<UniquePtr<StringTree>>&& children);
bool isConcat() const;
@@ -97,63 +97,63 @@ void writeUnsigned(std::ostringstream& o
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:
+ case VariantKind::IsData:
delete data.leaf;
return;
case VariantKind::IsConcat:
delete data.concat;
return;
}
MOZ_CRASH();
}
-StringTree::StringTree(ParseNodeKind k, Leaf* leaf)
- : variantKind(VariantKind::IsLeaf)
+StringTree::StringTree(ParseNodeKind k, Data* leaf)
+ : variantKind(VariantKind::IsData)
, data(leaf)
, kind(k)
{}
-StringTree::Leaf::Leaf()
+StringTree::Data::Data()
: data()
{ }
-StringTree::Leaf::Leaf(const std::string&& data_)
+StringTree::Data::Data(const std::string&& data_)
: data(Move(data_))
{ }
/*static*/ StringTree*
-StringTree::makeLeaf(JSContext* cx, ParseNodeKind kind)
+StringTree::makeData(JSContext* cx, ParseNodeKind kind)
{
- UniquePtr<Leaf> leaf(cx->new_<StringTree::Leaf>());
+ UniquePtr<Data> leaf(cx->new_<StringTree::Data>());
return cx->new_<StringTree>(kind, leaf.release());
}
/*static*/ StringTree*
-StringTree::makeLeaf(JSContext* cx, ParseNodeKind kind, std::string&& data)
+StringTree::makeData(JSContext* cx, ParseNodeKind kind, std::string&& data)
{
- UniquePtr<Leaf> leaf(cx->new_<StringTree::Leaf>(Move(data)));
+ UniquePtr<Data> leaf(cx->new_<StringTree::Data>(Move(data)));
return cx->new_<StringTree>(kind, leaf.release());
}
bool
-StringTree::isLeaf() const {
- return variantKind == StringTree::VariantKind::IsLeaf;
+StringTree::isData() const {
+ return variantKind == StringTree::VariantKind::IsData;
}
const std::string&
-StringTree::asLeaf() const {
- MOZ_ASSERT(isLeaf());
+StringTree::asData() const {
+ MOZ_ASSERT(isData());
return data.leaf->data;
}
StringTree::StringTree(ParseNodeKind k, Concat* concat)
: variantKind(VariantKind::IsConcat)
, data(concat)
, kind(k)
{}
@@ -186,35 +186,34 @@ StringTree::asConcat() const {
return *data.concat;
}
const mozilla::Vector<UniquePtr<StringTree>>&
StringTree::children() const {
return asConcat().children;
}
-using Leaf = StringTree::Leaf;
+using Data = StringTree::Data;
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();
}
+ MOZ_ASSERT(bs.ptr()[bs.length()] == 0);
out.write(bs.ptr(), bs.length());
} else {
- // FIXME: We could magically map this to string identifier 0. Is it worth it?
- // FIXME: This constant is poorly chosen. Pick something else that's invalid UTF-8.
- const char zero[2] = { 0, 0 }; // Magic constant (invalid utf8 string).
- out.write((const char*)&zero, sizeof zero);
+ const unsigned char empty[1] = { 255 }; // Magic constant (invalid utf8 string).
+ out.write((const char*)&empty, sizeof empty);
}
result = Move(out.str());
}
class TreeSerializer {
private:
JSContext* cx;
std::ostringstream& debug;
@@ -303,18 +302,18 @@ public:
// 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();
+ if (tree->isData()) {
+ const std::string& data = tree->asData();
auto search = occurrences.find(data);
if (search != occurrences.end()) {
search->second++;
} else {
occurrences.insert({data, 1});
}
return;
}
@@ -330,17 +329,17 @@ private:
// std::cerr << "treeSerialize " << NAMES[kind] << "\n";
#if defined(DEBUG)
debug << NAMES[kind] << " ";
#endif // defined(DEBUG)
switch (kind) {
// Empty tree
case PNK_LIMIT: {
- return StringTree::makeLeaf(cx, PNK_LIMIT);
+ return StringTree::makeData(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) {
@@ -515,89 +514,89 @@ private:
}
// 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));
+ return StringTree::makeData(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)))) {
+ if (!children.append(StringTree::makeData(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)))) {
+ if (!children.append(StringTree::makeData(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));
+ return StringTree::makeData(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));
+ return StringTree::makeData(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);
+ return StringTree::makeData(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);
+ return StringTree::makeData(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()));
+ return StringTree::makeData(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";
@@ -611,22 +610,22 @@ public:
// 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);
+ MOZ_ASSERT(tree->variantKind == VariantKind::IsData || tree->variantKind == VariantKind::IsConcat);
#endif // DEBUG_HEADER
- if (tree->isLeaf()) {
+ if (tree->isData()) {
// A leaf is a simple integer, so no byteLength.
- const uint32_t id = getStringId(tree->asLeaf());
+ const uint32_t id = getStringId(tree->asData());
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);
@@ -655,17 +654,19 @@ public:
};
class TreeParser MOZ_RAII {
public:
TreeParser(JSContext* cx_, ParseNodeAllocator& alloc, std::istringstream& in_, std::ostringstream& debug_,
BinParseOptions& options_)
: cx(cx_)
, in(in_)
+#if defined(DEBUG)
, debug(debug_)
+#endif // defined(DEBUG)
, functionDepth(0)
, numberOfNodes(0)
, currentByteLength((size_t)-1)
, currentByteStart((size_t)-1)
, currentKind((ParseNodeKind)-1)
, currentVariant((VariantKind)-1)
, atomsTable(cx)
, allocator(alloc)
@@ -702,56 +703,59 @@ public:
}
for (const auto iter: lengthsTable) {
std::string data;
data.reserve(iter);
in.read(&data[0], iter);
if (!stringsTable.append(Move(data))) {
MOZ_CRASH();
}
+ if (!atomsTable.append(nullptr)) {
+ MOZ_CRASH();
+ }
}
globalBodyStart = in.tellg();
// std::cerr << "ParseNode: body starts at " << globalBodyStart << "\n";
const mozilla::TimeStamp stringsStop = mozilla::TimeStamp::Now();
std::cerr << "ReadBinaryAST strings: " << (stringsStop-start).ToMilliseconds() << "ms\n";
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.
- uint32_t parseLeaf(ParseNodeKind& kind)
+ uint32_t parseData(ParseNodeKind& kind)
{
- // std::cerr << "parseLeaf\n";
+ // std::cerr << "parseData\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);
+ MOZ_ASSERT(currentVariant == VariantKind::IsData);
#endif
kind = currentKind;
MOZ_ASSERT(currentKind != PNK_LIMIT);
- const uint32_t leaf = readNodeAsLeaf();
+ const uint32_t leaf = readNodeAsData();
handleFooter();
currentByteLength = previousByteLength;
currentByteStart = previousByteStart;
currentKind = previousKind;
#if DEBUG_HEADER
currentVariant = previousVariant;
#endif // DEBUG_HEADER
@@ -784,23 +788,23 @@ private:
#if DEBUG_HEADER
currentVariant = previousVariant;
#endif // DEBUG_HEADER
return result.release();
}
// Note: This should NOT be called if the kind is PNK_LIMIT.
- uint32_t readNodeAsLeaf()
+ uint32_t readNodeAsData()
{
MOZ_ASSERT(currentKind != PNK_LIMIT);
#if DEBUG_HEADER
- MOZ_ASSERT(currentVariant == VariantKind::IsLeaf);
+ MOZ_ASSERT(currentVariant == VariantKind::IsData);
#else
- currentVariant = VariantKind::IsLeaf;
+ currentVariant = VariantKind::IsData;
#endif
currentByteStart = -1; // Ignored for leaves.
return readVariableLength();
}
void skipConcatNode() {
MOZ_ASSERT(currentKind == PNK_FUNCTION);
@@ -835,17 +839,19 @@ private:
}
ParseNode* freeTree(ParseNode* pn) {
return allocator.freeTree(pn);
}
private:
JSContext* cx;
std::istringstream& in;
- mozilla::DebugOnly<std::ostringstream&> debug;
+#if defined(DEBUG)
+ std::ostringstream& debug;
+#endif // defined(DEBUG)
size_t functionDepth;
size_t numberOfNodes;
size_t currentByteLength;
size_t currentByteStart;
size_t globalBodyStart;
ParseNodeKind currentKind;
VariantKind currentVariant;
mozilla::Vector<uint32_t> lengthsTable;
@@ -885,35 +891,35 @@ private:
if (char_kind >= mozilla::ArrayLength(NAMES)) {
MOZ_CRASH("Bad kind");
}
currentKind = (ParseNodeKind)char_kind;
// std::cerr << "handleHeader: " << NAMES[char_kind] << "\n";
if (currentKind == PNK_LIMIT) {
currentByteLength = 0;
- currentVariant = VariantKind::IsLeaf;
+ currentVariant = VariantKind::IsData;
} 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);
+ MOZ_ASSERT(currentVariant == VariantKind::IsData || currentVariant == VariantKind::IsConcat);
#endif // DEBUG_HEADER
}
}
void handleFooter() {
// std::cerr << "handleFooter " << in.tellg() << "\n";
// Check byteLength, footer
#if defined(DEBUG)
- MOZ_ASSERT(currentVariant == VariantKind::IsConcat || currentVariant == VariantKind::IsLeaf);
+ MOZ_ASSERT(currentVariant == VariantKind::IsConcat || currentVariant == VariantKind::IsData);
if (currentVariant == VariantKind::IsConcat) {
const size_t byteStop = in.tellg();
MOZ_ASSERT(byteStop - currentByteStart == currentByteLength);
}
#endif // defined(DEBUG)
#if DEBUG_HEADER
std::string footer(sizeof FOOTER - 1, '\0');
@@ -940,48 +946,45 @@ private:
const uint8_t byte = (uint8_t)get;
val |= (uint32_t(byte) >> 1) << shift;
shift += 7;
if (!(byte & 1))
return val;
}
}
- void getLeafAsPropertyName(uint32_t id, JS::MutableHandle<PropertyName*> data)
+ void getDataAsPropertyName(uint32_t id, JS::MutableHandle<PropertyName*> data)
{
RootedAtom atom(cx);
- getLeafAsAtom(id, &atom);
+ getDataAsAtom(id, &atom);
if (atom) {
data.set(atom->asPropertyName());
} else {
data.set(nullptr);
}
}
- void getLeafAsAtom(uint32_t id, MutableHandleAtom data)
+ void getDataAsAtom(uint32_t id, MutableHandleAtom data)
{
if (atomsTable[id]) {
data.set(atomsTable[id]);
return;
}
- const std::string& leaf = getLeafAsData(id);
+ const std::string& leaf = getDataAsData(id);
- // Special constant '\0\0' for empty atoms.
- if (leaf.length() == 2) {
- if (leaf[0] == 0 && leaf[1] == 0) {
- data.set(nullptr);
- return;
- }
+ // Special constant '\255' (which is invalid utf-8) for empty atoms.
+ if (leaf.length() == 1 && (unsigned char)leaf[0] == 255) {
+ data.set(nullptr);
}
data.set(js::Atomize(cx, &leaf[0], leaf.length()));
}
- const std::string& getLeafAsData(uint32_t id)
+ const std::string& getDataAsData(uint32_t id)
{
return stringsTable[id];
}
MOZ_MUST_USE ParseNode* parseNode(ParseNodeKind kind)
{
#if defined(DEBUG)
debug << NAMES[kind] << " ";
@@ -1202,57 +1205,57 @@ private:
#endif // defined(DEBUG)
return result.release();
}
// Labels
case PNK_BREAK: MOZ_FALLTHROUGH;
case PNK_CONTINUE: {
RootedPropertyName label(cx);
- getLeafAsPropertyName(readNodeAsLeaf(), &label);
+ getDataAsPropertyName(readNodeAsData(), &label);
if (kind == PNK_BREAK) {
return new_<BreakStatement>(label, TokenPos(0, 0));
} else {
return new_<ContinueStatement>(label, TokenPos(0, 0));
}
}
case PNK_LABEL: MOZ_FALLTHROUGH;
case PNK_DOT: {
const mozilla::DebugOnly<size_t> length = readNodeAsConcat();
MOZ_ASSERT(length == 2);
ParseNodeKind subkind;
RootedPropertyName label(cx);
- getLeafAsPropertyName(parseLeaf(subkind), &label);
+ getDataAsPropertyName(parseData(subkind), &label);
MOZ_ASSERT(subkind == kind);
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: {
RootedAtom atom(cx);
- getLeafAsAtom(readNodeAsLeaf(), &atom);
+ getDataAsAtom(readNodeAsData(), &atom);
if (kind == PNK_NAME) {
return new_<NameNode>(kind, JSOP_NOP, atom, TokenPos(0, 0));
} else {
return new_<NullaryNode>(kind, JSOP_NOP, TokenPos(0, 0), atom);
}
}
case PNK_NUMBER: {
- const std::string& leaf = getLeafAsData(readNodeAsLeaf());
+ const std::string& leaf = getDataAsData(readNodeAsData());
std::istringstream input(leaf); // FIXME: Useless copy.
double dval;
input.read((char*)&dval, sizeof dval);
DecimalPoint point;
input.read((char*)&point, sizeof point);
@@ -1267,35 +1270,35 @@ 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: {
- mozilla::Unused << readNodeAsLeaf();
+ mozilla::Unused << readNodeAsData();
return new_<NullaryNode>(kind, JSOP_NOP, TokenPos(0, 0));
}
// Entirely undocumented nodes:
case PNK_MODULE: MOZ_FALLTHROUGH;
case PNK_DEBUGGER: MOZ_FALLTHROUGH;
case PNK_ELISION: MOZ_FALLTHROUGH;
case PNK_OBJECT_PROPERTY_NAME: {
- mozilla::Unused << readNodeAsLeaf();
+ mozilla::Unused << readNodeAsData();
return new_<NullaryNode>(kind, JSOP_NOP, TokenPos(0, 0));
}
// Stuff we don't handle yet:
case PNK_REGEXP: {
RootedAtom atom(cx);
- getLeafAsAtom(readNodeAsLeaf(), &atom);
+ getDataAsAtom(readNodeAsData(), &atom);
return new_<RegExpLiteral>(nullptr, TokenPos(0, 0));
}
}
MOZ_CRASH("treeParse: out of switch()");
return nullptr;
}