new file mode 100644
--- /dev/null
+++ b/js/src/frontend/BinSource.cpp
@@ -0,0 +1,370 @@
+#include <mozilla/ArrayUtils.h>
+#include <mozilla/Casting.h>
+#include <mozilla/Maybe.h>
+#include <mozilla/Move.h>
+#include <mozilla/PodOperations.h>
+#include <mozilla/Vector.h>
+
+#include <frontend/BinTokenReader.h>
+#include <frontend/ParseNode.h>
+
+
+using namespace mozilla;
+using NameBag = GCHashSet<JSString*>;
+using Names = GCVector<JSString*, 8>;
+
+namespace js {
+namespace frontend {
+
+
+// Copied from Parser.cpp
+
+template <typename Scope>
+static typename Scope::Data*
+NewEmptyBindingData(JSContext* cx, LifoAlloc& alloc, uint32_t numBindings)
+{
+ size_t allocSize = Scope::sizeOfData(numBindings);
+ typename Scope::Data* bindings = static_cast<typename Scope::Data*>(alloc.alloc(allocSize));
+ if (!bindings) {
+ ReportOutOfMemory(cx);
+ return nullptr;
+ }
+ PodZero(bindings);
+ return bindings;
+}
+
+const std::string BINJS_VAR_NAME = "BINJS:VarDeclaredNames";
+const std::string BINJS_LET_NAME = "BINJS:LetDeclaredNames";
+const std::string BINJS_CONST_NAME = "BINJS:ConstDeclaredNames";
+const std::string BINJS_CAPTURED_NAME = "BINJS:CapturedNames";
+const std::string BINJS_DIRECT_EVAL = "BINJS:HasDirectEval";
+
+class ASTReader
+{
+public:
+ bool parse(char* start, char* stop, UniquePtr<ParseNode>& out);
+private:
+ bool raiseError();
+
+ // --- Parse full nodes.
+
+ bool parseBool(SimpleTokenReader*, Maybe<bool>*);
+ bool parseProgram(SimpleTokenReader* reader, UniquePtr<ParseNode>& out);
+ bool parseStatement(SimpleTokenReader* reader, UniquePtr<ParseNode>& out);
+ bool parseStatementList(SimpleTokenReader* reader, UniquePtr<ParseNode>& out);
+ bool parseStringList(SimpleTokenReader* reader, MutableHandle<Maybe<Names>> out);
+ bool parseStringSet(SimpleTokenReader* reader, MutableHandle<Maybe<NameBag>>);
+
+ // --- Parse the contents of a node whose kind has already been determined.
+ bool parseBlockStatementAux(SimpleTokenReader* reader, const SimpleTokenReader::Fields* fields, UniquePtr<ParseNode>& out);
+ bool parseExpressionStatementAux(SimpleTokenReader* reader, const SimpleTokenReader::Fields* fields, UniquePtr<ParseNode>& out);
+
+ // --- Utilities.
+ bool readString(SimpleTokenReader* reader, MutableHandleString);
+
+ ParseNodeAllocator allocator;
+
+ ParseNode* allocParseNode(size_t size) {
+ MOZ_ASSERT(size == sizeof(ParseNode));
+ return static_cast<ParseNode*>(allocator.allocNode());
+ }
+
+ ParseNode* cloneNode(const ParseNode& other) {
+ ParseNode* node = allocParseNode(sizeof(ParseNode));
+ if (!node)
+ return nullptr;
+ mozilla::PodAssign(node, &other);
+ return node;
+ }
+
+ JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
+
+private:
+ JSContext* cx;
+ LifoAlloc& alloc;
+};
+
+bool
+ASTReader::parse(char* start, char* stop, UniquePtr<ParseNode>& out) {
+ SimpleTokenReader reader(this->cx);
+ reader.init(start, stop, nullptr);
+
+ if (!this->parseProgram(&reader, out) || !reader.uninit()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+ASTReader::parseProgram(SimpleTokenReader* reader, UniquePtr<ParseNode>& out) {
+ if (out) {
+ // Already parsed.
+ return this->raiseError();
+ }
+ SimpleTokenReader::Fields fields(this->cx);
+ SimpleTokenReader sub(this->cx);
+
+ std::string name;
+ if (!reader->taggedTuple(&name, &fields, &sub)) {
+ return false;
+ }
+
+ if (name != "Program") {
+ return false;
+ }
+
+ return this->parseBlockStatementAux(&sub, &fields, out);
+}
+
+bool
+ASTReader::parseBlockStatementAux(SimpleTokenReader* reader, const SimpleTokenReader::Fields* fields, UniquePtr<ParseNode>& out) {
+ UniquePtr<ParseNode> body;
+ Maybe<bool> hasDirectEval;
+ Rooted<Maybe<Names>> letNames(this->cx);
+ Rooted<Maybe<Names>> constNames(this->cx);
+ Rooted<Maybe<Names>> varNames(this->cx);
+ Rooted<Maybe<NameBag>> capturedNames(this->cx);
+
+ // FIXME: There must be a nicer way to express all of this.
+ // FIXME: Could we somehow generate an optimized parser from the header?
+ for (auto field: *fields) {
+ // Fields inherited from `BINJS::Scope`.
+ // FIXME: We could share this code.
+ if (field == BINJS_DIRECT_EVAL) {
+ if (!this->parseBool(reader, &hasDirectEval)) {
+ return false;
+ }
+ } else if (field == BINJS_LET_NAME) {
+ if (!this->parseStringList(reader, &letNames)) {
+ return false;
+ }
+ } else if (field == BINJS_CONST_NAME) {
+ if (!this->parseStringList(reader, &constNames)) {
+ return false;
+ }
+ } else if (field == BINJS_VAR_NAME) {
+ if (!this->parseStringList(reader, &varNames)) {
+ return false;
+ }
+ } else if (field == BINJS_CAPTURED_NAME) {
+ if (!this->parseStringSet(reader, &capturedNames)) {
+ return false;
+ }
+ }
+ // Own fields
+ else if (field == "body") {
+ if (!this->parseStatementList(reader, body)) {
+ return false;
+ }
+ } else {
+ this->raiseError();
+ return false;
+ }
+ }
+
+ // Check if all fields have been parsed.
+ if (!body || hasDirectEval.isNothing()
+ || letNames.get().isNothing()
+ || constNames.get().isNothing()
+ || varNames.get().isNothing()
+ || capturedNames.get().isNothing()) {
+ // FIXME: Once we have headers + header validation, this error check
+ // should become an assertion.
+ return this->raiseError();
+ }
+
+ // FIXME: For the moment, we are conflating `let` and `const`.
+ // Probably not a good idea.
+ UniquePtr<LexicalScope::Data> bindings(
+ NewEmptyBindingData<LexicalScope>(this->cx, this->alloc, letNames->length() + constNames->length())
+ );
+ if (!bindings) {
+ return this->raiseError();
+ }
+
+ BindingName* cursor = bindings->names;
+ for (auto& name: *letNames.get()) {
+ JS::Rooted<JSAtom*> atom(cx, AtomizeString(this->cx, name));
+ bool isCaptured = capturedNames->has(name);
+ BindingName binding(atom, isCaptured);
+ PodCopy(cursor, &binding, 1); // FIXME: Why does this work?
+ cursor++;
+ bindings->length++;
+ }
+ for (auto& name: *constNames.get()) {
+ JS::Rooted<JSAtom*> atom(cx, AtomizeString(this->cx, name));
+ bool isCaptured = capturedNames->has(name);
+ BindingName binding(atom, isCaptured);
+ PodCopy(cursor, &binding, 1); // FIXME: Why does this work?
+ cursor++;
+ bindings->length++;
+ }
+ bindings->constStart = letNames->length();
+
+ UniquePtr<ParseNode> result(new_<LexicalScopeNode>(bindings.release(), body.release()));
+ out = Move(result);
+ // FIXME: Validate capturedNames, etc.
+ return true;
+}
+
+bool
+ASTReader::parseStringList(SimpleTokenReader* reader, MutableHandle<Maybe<Names>> out) {
+ if (out.get()) {
+ return this->raiseError();
+ }
+ uint32_t length;
+ SimpleTokenReader sub(this->cx);
+
+ Names result(this->cx);
+
+ if (!reader->readList(&length, &sub)) {
+ return false;
+ }
+
+ if (!result.reserve(length)) {
+ return this->raiseError();
+ }
+ for (uint32_t i = 0; i < length; ++i) {
+ RootedString string(this->cx);
+ if (!this->readString(&sub, &string)) {
+ return false;
+ }
+
+ if (!result.append(Move(string))) {
+ MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE(); // Checked in the call to `reserve`.
+ }
+ }
+
+ out.set(Move(Some(Move(result))));
+ return true;
+}
+
+bool
+ASTReader::parseStatementList(SimpleTokenReader* reader, UniquePtr<ParseNode>& out) {
+ if (out) {
+ return this->raiseError();
+ }
+ uint32_t length;
+ SimpleTokenReader sub(this->cx);
+
+ UniquePtr<ParseNode> result(new_<ListNode>(PNK_STATEMENTLIST, TokenPos()));
+ if (!result) {
+ return this->raiseError();
+ }
+
+ if (!reader->readList(&length, &sub)) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < length; ++i) {
+ UniquePtr<ParseNode> statement;
+ if (!this->parseStatement(&sub, statement)) {
+ return false;
+ }
+
+ result->append(statement.release()); // `result` knows how to deallocate `statement`.
+ }
+
+ result.swap(out);
+ return true;
+}
+
+bool
+ASTReader::parseStatement(SimpleTokenReader* reader, UniquePtr<ParseNode>& out) {
+ if (out) {
+ return this->raiseError();
+ }
+ SimpleTokenReader::Fields fields(this->cx);
+ SimpleTokenReader sub(this->cx);
+
+ std::string name;
+ if (!reader->taggedTuple(&name, &fields, &sub)) {
+ return false;
+ }
+
+ if (name == "EmptyStatement") {
+ UniquePtr<ParseNode> result(new_<NullaryNode>(PNK_NOP, TokenPos()));
+ if (!result) {
+ return false;
+ }
+ out = Move(result);
+ } else if (name == "BlockStatement") {
+ UniquePtr<ParseNode> body;
+ if (!this->parseBlockStatementAux(&sub, &fields, body)) {
+ return false;
+ }
+ out = Move(body);
+ } else if (name == "ExpressionStatement") {
+ UniquePtr<ParseNode> body;
+ if (!this->parseExpressionStatementAux(&sub, &fields, body)) {
+ return false;
+ }
+ out = Move(body);
+ } else if (name == "DebuggerStatement") {
+ UniquePtr<ParseNode> result(new_<DebuggerStatement>(TokenPos()));
+ if (!result) {
+ return false;
+ }
+ out = Move(result);
+ } else if (name == "WithStatement") {
+ // FIXME: Implement
+ } else if (name == "ReturnStatement") {
+ // FIXME: Implement
+ } else if (name == "LabeledStatement") {
+ // FIXME: Implement
+ } else if (name == "BreakStatement") {
+ // FIXME: Implement
+ } else if (name == "ContinueStatement") {
+ // FIXME: Implement
+ } else if (name == "IfStatement") {
+ // FIXME: Implement
+ } else if (name == "SwitchStatement") {
+ // FIXME: Implement
+ } else if (name == "SwitchCase") {
+ // FIXME: Implement
+ } else if (name == "ThrowStatement") {
+ // FIXME: Implement
+ } else if (name == "TryStatement") {
+ // FIXME: Implement
+ } else if (name == "WhileStatement") {
+ // FIXME: Implement
+ } else if (name == "DoWhileStatement") {
+ // FIXME: Implement
+ } else if (name == "ForStatement") {
+ // FIXME: Implement
+ } else if (name == "ForInStatement") {
+ // FIXME: Implement
+ } else if (name == "FunctionDeclaration") {
+ // FIXME: Implement
+ } else if (name == "VariableDeclaration") {
+ // FIXME: Implement
+ } else if (name == "SwitchStatement") {
+ // FIXME: Implement
+ } else {
+ return this->raiseError();
+ }
+
+ return true;
+}
+
+bool
+ASTReader::parseBool(SimpleTokenReader* reader, Maybe<bool>* result) {
+ if (result->isSome()) {
+ // Already parsed.
+ // FIXME: Can replace with an assert once we have
+ // implemented headers and header validation.
+ return this->raiseError();
+ }
+ return reader->readBool(result);
+}
+
+bool
+ASTReader::raiseError() {
+ // FIXME: Implement actual error conditions.
+ return false;
+}
+
+
+} // namespace frontend
+} // namespace js
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -163,16 +163,17 @@ UNIFIED_SOURCES += [
'builtin/TestingFunctions.cpp',
'builtin/TypedObject.cpp',
'builtin/WeakMapObject.cpp',
'builtin/WeakSetObject.cpp',
'devtools/sharkctl.cpp',
'ds/Bitmap.cpp',
'ds/LifoAlloc.cpp',
'ds/MemoryProtectionExceptionHandler.cpp',
+ 'frontend/BinSource.cpp',
'frontend/BinTokenReader.cpp',
'frontend/BytecodeCompiler.cpp',
'frontend/BytecodeEmitter.cpp',
'frontend/FoldConstants.cpp',
'frontend/NameFunctions.cpp',
'frontend/ParseNode.cpp',
'frontend/TokenStream.cpp',
'gc/Allocator.cpp',