WIP: Implemented FunctionDeclaration, gc stuff draft
authorDavid Teller <dteller@mozilla.com>
Wed, 26 Jul 2017 13:59:23 +0200
changeset 641343 21e5d2f1eb38fbb83b4a17d7c093c27df904ac5d
parent 641342 8a0ea18ab5c732d5bd0d6bf4761b5f59496f1419
child 641344 30028eec2850f783a21ddde50c814ebd0f623c7a
push id72504
push userdteller@mozilla.com
push dateSun, 06 Aug 2017 22:28:40 +0000
milestone57.0a1
WIP: Implemented FunctionDeclaration, gc stuff MozReview-Commit-ID: JBej1Qn0CJz
js/src/frontend/BinSource.cpp
js/src/frontend/BytecodeCompiler.h
js/src/gc/RootMarking.cpp
js/src/jspubtd.h
--- a/js/src/frontend/BinSource.cpp
+++ b/js/src/frontend/BinSource.cpp
@@ -12,37 +12,38 @@
 
 using namespace mozilla;
 using NameBag = GCHashSet<JSString*>;
 using Names = GCVector<JSString*, 8>;
 
 // FIXME: Better error handling.
 // FIXME: Check that subnodes have the right type.
 // FIXME: Check scope information.
+// FIXME: Handle directives.
 
 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;
 }
 
+
 struct ScopeData MOZ_STACK_CLASS {
 public:
     ScopeData(JSContext* cx)
       : letNames(cx)
       , constNames(cx)
       , varNames(cx)
       , capturedNames(cx)
     { }
@@ -70,19 +71,27 @@ public:
 
 const std::string BINJS_SCOPE = "BINJS:Scope";
 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
+class ASTReader: private JS::AutoGCRooter
 {
 public:
+    ASTReader(JSContext* cx_, LifoAlloc& alloc_)
+        : AutoGCRooter(cx_, BINPARSER)
+        , traceListHead(nullptr)
+        , allocator(cx_, alloc_)
+        , cx(cx_)
+        , alloc(alloc_)
+    { }
+
     bool parse(char* start, char* stop, GlobalSharedContext* sc, UniquePtr<ParseNode>& out);
 private:
     bool raiseError();
 
     // --- Parse full nodes.
 
     bool parseBool(SimpleTokenReader*, Maybe<bool>*);
     bool parseProgram(SimpleTokenReader* reader, UniquePtr<ParseNode>& out);
@@ -94,32 +103,45 @@ private:
     bool parseStringSet(SimpleTokenReader* reader, MutableHandle<NameBag>);
     bool parseScope(SimpleTokenReader* reader, ScopeData& out);
     bool parseExpression(SimpleTokenReader* reader, UniquePtr<ParseNode>& out);
     bool parsePropertyName(SimpleTokenReader* reader, UniquePtr<PropertyName>& out);
     bool parseSwitchCaseList(SimpleTokenReader* reader, UniquePtr<ParseNode>& out);
     bool parseBlockStatement(SimpleTokenReader* reader, UniquePtr<ParseNode>&);
     bool parseCatchClause(SimpleTokenReader* reader, UniquePtr<ParseNode>&);
     bool parseVariableDeclarator(SimpleTokenReader* reader, UniquePtr<ParseNode>&);
+    bool parsePatternList(SimpleTokenReader* reader, UniquePtr<ParseNode>&);
 
     // --- Parse the contents of a node whose kind has already been determined.
 
     bool parseFunctionAux(SimpleTokenReader* reader, const std::string& name, const SimpleTokenReader::Fields* fields, UniquePtr<ParseNode>& out);
     bool parsePatternAux(SimpleTokenReader* reader, const std::string& name, const SimpleTokenReader::Fields* fields, UniquePtr<ParseNode>& out);
     bool parseBlockStatementAux(SimpleTokenReader* reader, const std::string& name, const SimpleTokenReader::Fields* fields, UniquePtr<ParseNode>& out);
     bool parseExpressionStatementAux(SimpleTokenReader* reader, const std::string& name, const SimpleTokenReader::Fields* fields, UniquePtr<ParseNode>& out);
     bool parseExpressionAux(SimpleTokenReader* reader, const std::string& name, const SimpleTokenReader::Fields* fields, UniquePtr<ParseNode>& out);
     bool parseVariableDeclarationAux(SimpleTokenReader* reader, const std::string& name, const SimpleTokenReader::Fields* fields, UniquePtr<ParseNode>& out);
 
     // --- Utilities.
+
     bool readString(SimpleTokenReader* reader, std::string&);
     bool readString(SimpleTokenReader* reader, MutableHandleString);
+    bool readString(SimpleTokenReader* reader, MutableHandleAtom);
 
+    const Directives& currentDirectives() const;
     const ReadOnlyCompileOptions& options() const;
 
+    // --- GC.
+
+    /* List of objects allocated during parsing, for GC tracing. */
+    ObjectBox* traceListHead;
+    void trace(JSTracer* trc)
+    {
+        ObjectBox::TraceList(trc, this->traceListHead);
+    }
+
     ParseNodeAllocator allocator;
 
     ParseNode* allocParseNode(size_t size) {
         MOZ_ASSERT(size == sizeof(ParseNode));
         return static_cast<ParseNode*>(allocator.allocNode());
     }
 
     ParseNode* cloneNode(const ParseNode& other) {
@@ -130,16 +152,19 @@ private:
         return node;
     }
 
     JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
 
 private:
     JSContext* cx;
     LifoAlloc& alloc;
+
+    // Needs access to AutoGCRooter.
+    friend void TraceBinParser(JSTracer* trc, AutoGCRooter* parser);
 };
 
 class BinParseContext: public ParseContext {
 public:
     BinParseContext(ASTReader*, GlobalSharedContext*, Directives*);
 };
 
 bool
@@ -880,17 +905,94 @@ ASTReader::parseStatement(SimpleTokenRea
 
         Unused << forHead.release();
         Unused << body.release();
 
         out = Move(result);
 
     } else if (name == "FunctionDeclaration") {
 
-        // FIXME: Implement
+        RootedAtom funName(this->cx);
+        UniquePtr<ParseNode> params;
+        UniquePtr<ParseNode> body;
+        ScopeData scope(this->cx);
+
+        for (auto field: fields) {
+            if (field == "id") {
+                if (!this->readString(&sub, &funName)) {
+                    return false;
+                }
+            } else if (field == "params") {
+                if (!this->parsePatternList(&sub, params)) {
+                    return false;
+                }
+            } else if (field == "body") {
+                if (!this->parseStatement(&sub, body)) {
+                    return false;
+                }
+            } else if (field == BINJS_SCOPE) {
+                if (!this->parseScope(&sub, scope)) {
+                    return false;
+                }
+            } else {
+                return this->raiseError();
+            }
+        }
+
+        if (!funName || !params || !body || !scope.isSome()) {
+            return this->raiseError();
+        }
+
+        RootedFunction fun(this->cx,
+            NewFunctionWithProto(this->cx,
+                /*native*/nullptr,
+                /*nargs ?*/0,
+                /*flags */ JSFunction::INTERPRETED_NORMAL,
+                /*enclosing env*/nullptr,
+                /*name*/ funName,
+                /*proto*/ nullptr,
+                /*alloc*/gc::AllocKind::FUNCTION,
+                TenuredObject
+        ));
+        if (!fun) {
+            return false;
+        }
+
+        FunctionBox* funbox = new_<FunctionBox>(this->cx,
+            this->alloc,
+            this->traceListHead,
+            fun,
+            /*toStringStart*/0,
+            /*Directives*/this->currentDirectives(),
+            /*extraWarning*/false,
+            GeneratorKind::NotGenerator,
+            FunctionAsyncKind::SyncFunction
+        );
+        if (!funbox) {
+            return false;
+        }
+        this->traceListHead = funbox;
+
+        MOZ_ASSERT(params->isArity(PN_LIST));
+        params->setKind(PNK_PARAMSBODY);
+        params->setOp(JSOP_NOP);
+
+        MOZ_ASSERT(body->getKind() == PNK_STATEMENTLIST); // FIXME: If that's not the case, promote to PNK_STATEMENTLIST
+        params->append(body.release());
+
+        UniquePtr<ParseNode> result(new_<CodeNode>(PNK_FUNCTION, JSOP_NOP, TokenPos()));
+        if (!result) {
+            return false;
+        }
+        result->pn_funbox = funbox;
+        result->pn_body   = params.release();
+
+        out = Move(result);
+
+        // FIXME: Scope
 
     } else if (name == "VariableDeclaration") {
 
         if (!this->parseVariableDeclarationAux(&sub, name, &fields, out)) {
             return false;
         }
 
     } else {
@@ -996,11 +1098,16 @@ ASTReader::parseBool(SimpleTokenReader* 
 }
 
 bool
 ASTReader::raiseError() {
     // FIXME: Implement actual error conditions.
     return false;
 }
 
+void
+TraceBinParser(JSTracer* trc, AutoGCRooter* parser)
+{
+    static_cast<ASTReader*>(parser)->trace(trc);
+}
 
 } // namespace frontend
 } // namespace js
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -124,16 +124,21 @@ IsIdentifier(const char16_t* chars, size
 /* True if str is a keyword. Defined in TokenStream.cpp. */
 bool
 IsKeyword(JSLinearString* str);
 
 /* Trace all GC things reachable from parser. Defined in Parser.cpp. */
 void
 TraceParser(JSTracer* trc, JS::AutoGCRooter* parser);
 
+/* Trace all GC things reachable from binjs parser. Defined in BinSource.cpp. */
+void
+TraceBinParser(JSTracer* trc, JS::AutoGCRooter* parser);
+
+
 class MOZ_STACK_CLASS AutoFrontendTraceLog
 {
 #ifdef JS_TRACE_LOGGING
     TraceLoggerThread* logger_;
     mozilla::Maybe<TraceLoggerEvent> frontendEvent_;
     mozilla::Maybe<AutoTraceLog> frontendLog_;
     mozilla::Maybe<AutoTraceLog> typeLog_;
 #endif
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -149,16 +149,20 @@ JS_FOR_EACH_TRACEKIND(FINISH_ROOT_LIST)
 inline void
 AutoGCRooter::trace(JSTracer* trc)
 {
     switch (tag_) {
       case PARSER:
         frontend::TraceParser(trc, this);
         return;
 
+      case BINPARSER:
+        frontend::TraceBinParser(trc, this);
+        return;
+
       case VALARRAY: {
         /*
          * We don't know the template size parameter, but we can safely treat it
          * as an AutoValueArray<1> because the length is stored separately.
          */
         AutoValueArray<1>* array = static_cast<AutoValueArray<1>*>(this);
         TraceRootRange(trc, array->length(), array->begin(), "js::AutoValueArray");
         return;
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -239,16 +239,17 @@ class JS_PUBLIC_API(AutoGCRooter)
      * below.  Any other negative value indicates some deeper problem such as
      * memory corruption.
      */
     ptrdiff_t tag_;
 
     enum {
         VALARRAY =     -2, /* js::AutoValueArray */
         PARSER =       -3, /* js::frontend::Parser */
+        BINPARSER =    -4, /* js::frontend::BinSource */
         VALVECTOR =   -10, /* js::AutoValueVector */
         IDVECTOR =    -11, /* js::AutoIdVector */
         OBJVECTOR =   -14, /* js::AutoObjectVector */
         IONMASM =     -19, /* js::jit::MacroAssembler */
         WRAPVECTOR =  -20, /* js::AutoWrapperVector */
         WRAPPER =     -21, /* js::AutoWrapperRooter */
         CUSTOM =      -26  /* js::CustomAutoRooter */
     };