WIP: CatchClause now stores its declared variables in a lexical scope draft
authorDavid Teller <dteller@mozilla.com>
Wed, 30 Aug 2017 13:30:11 +0200
changeset 655860 34e8cb9f05a8451b47bdeb5e7219c1f07cc9b603
parent 655859 6e21dd38cf4b4b970df618e2a857fb914e77e74e
child 728917 67f3a1d2aeeb108680dabf9625f1dbac7bf03a34
push id76964
push userdteller@mozilla.com
push dateWed, 30 Aug 2017 11:33:11 +0000
milestone57.0a1
WIP: CatchClause now stores its declared variables in a lexical scope MozReview-Commit-ID: 67DRF272EZl
js/src/frontend/BinSource.cpp
js/src/frontend/BinSource.h
--- a/js/src/frontend/BinSource.cpp
+++ b/js/src/frontend/BinSource.cpp
@@ -78,17 +78,16 @@ ASTReader::parse(char* start, char* stop
     }
 
     *out = result.release();
     return true;
 }
 
 bool
 ASTReader::parseProgram(SimpleTokenReader* reader, UniqueNode& out) {
-    fprintf(stderr, "ASTReader::parseProgram\n");
     if (out) {
         return this->raiseAlreadyParsed(reader, "Program");
     }
     BinKind kind;
     SimpleTokenReader::BinFields fields(this->cx);
     SimpleTokenReader sub(this->cx);
 
     if (!reader->taggedTuple(kind, fields, &sub)) {
@@ -103,17 +102,16 @@ ASTReader::parseProgram(SimpleTokenReade
         return false;
     }
 
     return true;
 }
 
 bool
 ASTReader::parseBlockStatement(SimpleTokenReader* reader, UniqueNode& out) {
-    fprintf(stderr, "ASTReader::parseBlockStatement\n");
     if (out) {
         // Already parsed.
         return this->raiseAlreadyParsed(reader, "BlockStatement");
     }
     BinKind kind;
     SimpleTokenReader::BinFields fields(this->cx);
     SimpleTokenReader sub(this->cx);
 
@@ -137,17 +135,16 @@ ASTReader::parseBlockStatement(SimpleTok
     out = Move(block);
     return true;
 }
 
 bool
 ASTReader::parseScope(SimpleTokenReader* reader,
     ScopeData& out)
 {
-    fprintf(stderr, "ASTReader::parseScope\n");
     if (out.isSome()) {
         // Already parsed.
         return this->raiseAlreadyParsed(reader, "Scope");
     }
 
     BinKind kind;
     SimpleTokenReader::BinFields fields(this->cx);
     SimpleTokenReader sub(this->cx);
@@ -158,19 +155,17 @@ ASTReader::parseScope(SimpleTokenReader*
 
     if (kind == BinKind::binjs_null) {
         return true;
     }
     if (kind != BinKind::binjs_scope) {
         return this->raiseInvalidKind(reader, "Scope", kind);
     }
 
-    uint32_t counter = 0;
     for (auto field: fields) {
-        fprintf(stderr, "=> field %d (%u/%zu)\n", field, counter++, fields.length());
         switch (field) {
             case BinField::has_direct_eval:
                 if (!this->readBool(&sub, out.hasDirectEval)) {
                     return false;
                 }
                 break;
             case BinField::let_decl_names:
                 if (!this->parseStringList(&sub, &out.letNames)) {
@@ -201,23 +196,21 @@ ASTReader::parseScope(SimpleTokenReader*
 }
 
 bool
 ASTReader::parseBlockStatementAux(SimpleTokenReader* reader,
     const BinKind name,
     const SimpleTokenReader::BinFields& fields,
     UniqueNode& out)
 {
-    fprintf(stderr, "ASTReader::parseBlockStatementAux (%lu fields)\n", fields.length());
     UniqueNode body(nullptr, this->nodeFree);
     UniqueNode directives(nullptr, this->nodeFree); // Ignored
     ScopeData scope(this->cx);
 
     for (auto field: fields) {
-        fprintf(stderr, "ASTReader::parseBlockStatementAux => field %d\n", field);
         switch (field) {
             case BinField::binjs_scope:
                 if (!this->parseScope(reader, scope)) {
                     return false;
                 }
                 break;
             case BinField::body:
                 if (!this->parseStatementList(reader, body)) {
@@ -241,45 +234,60 @@ ASTReader::parseBlockStatementAux(Simple
         return this->raiseMissingField(reader, "BlockStatement");
     }
 
     if (scope.hasLexNames()) {
         if (!this->promoteToLexicalScope(body)) {
             return false;
         }
 
-        LexicalScope::Data* bindings = body->pn_u.scope.bindings;
-        bindings->length = scope.letNames->length() + scope.constNames->length();
-
-        BindingName* cursor = bindings->names;
-        for (auto& name: *scope.letNames.get()) {
-            JS::Rooted<JSAtom*> atom(cx, AtomizeString(this->cx, name));
-            bool isCaptured = scope.capturedNames->has(name);
-            BindingName binding(atom, isCaptured);
-            PodCopy(cursor, &binding, 1); // FIXME: Why does this work?
-            cursor++;
-            bindings->length++;
+        if (!this->storeLexicalScope(body, Move(scope))) {
+            return false;
         }
-        bindings->constStart = bindings->length;
-        for (auto& name: *scope.constNames.get()) {
-            JS::Rooted<JSAtom*> atom(cx, AtomizeString(this->cx, name));
-            bool isCaptured = scope.capturedNames->has(name);
-            BindingName binding(atom, isCaptured);
-            PodCopy(cursor, &binding, 1); // FIXME: Why does this work?
-            cursor++;
-            bindings->length++;
-        }
-        // FIXME: Validate capturedNames, etc.
     }
 
     out = Move(body);
     return true;
 }
 
 bool
+ASTReader::storeLexicalScope(UniqueNode& body, ScopeData&& scope) {
+    LexicalScope::Data* bindings = body->pn_u.scope.bindings;
+    bindings->length = 0;
+
+    BindingName* cursor = bindings->names;
+    for (auto& name: *scope.letNames.get()) {
+        JS::Rooted<JSAtom*> atom(cx, AtomizeString(this->cx, name));
+        if (!atom) {
+            return this->raiseOOM();
+        }
+        bool isCaptured = scope.capturedNames->has(name);
+        BindingName binding(atom, isCaptured);
+        PodCopy(cursor, &binding, 1); // FIXME: Why does this work?
+        cursor++;
+        bindings->length++; // Augment progressively in case we need to return early because of an error.
+    }
+    bindings->constStart = bindings->length;
+    for (auto& name: *scope.constNames.get()) {
+        JS::Rooted<JSAtom*> atom(cx, AtomizeString(this->cx, name));
+        if (!atom) {
+            return this->raiseOOM();
+        }
+        bool isCaptured = scope.capturedNames->has(name);
+        BindingName binding(atom, isCaptured);
+        PodCopy(cursor, &binding, 1); // FIXME: Why does this work?
+        cursor++;
+        bindings->length++;
+    }
+    // FIXME: Validate capturedNames, etc.
+
+    return true;
+}
+
+bool
 ASTReader::promoteToLexicalScope(UniqueNode& node) {
     if (node->isKind(PNK_LEXICALSCOPE)) {
         return true;
     }
 
     js::UniquePtr<LexicalScope::Data> bindings(NewEmptyBindingData<LexicalScope>(this->cx, this->alloc, 0));
     if (!bindings) {
         return this->raiseOOM();
@@ -357,17 +365,16 @@ ASTReader::parseStringList(SimpleTokenRe
     }
 
     out.set(Move(Some(Move(result))));
     return true;
 }
 
 bool
 ASTReader::parseStatementList(SimpleTokenReader* reader, UniqueNode& out) {
-    fprintf(stderr, "ASTReader::parseStatementList\n");
     if (out) {
         return this->raiseAlreadyParsed(reader, "[Statement]");
     }
     uint32_t length;
     SimpleTokenReader sub(this->cx);
 
     UniqueNode result(new_<ListNode>(PNK_STATEMENTLIST, TokenPos(0, 0)), this->nodeFree);
     if (!result) {
@@ -2288,17 +2295,16 @@ ASTReader::parseDirectiveLiteral(SimpleT
     }
 
     out = Move(result);
     return true;
 }
 
 bool
 ASTReader::parseDirectiveList(SimpleTokenReader* reader, UniqueNode& out) {
-    fprintf(stderr, "ASTReader::parseDirectiveList\n");
     if (out) {
         return this->raiseAlreadyParsed(reader, "[Directive]");
     }
 
     uint32_t length;
     SimpleTokenReader sub(this->cx);
     if (!reader->readList(length, sub)) {
         return this->raiseTokenError(reader, "[Directive]");
@@ -2384,35 +2390,44 @@ ASTReader::parseCatchClause(SimpleTokenR
     BinKind kind;
     SimpleTokenReader::BinFields fields(this->cx);
     SimpleTokenReader sub(this->cx);
 
     if (!reader->taggedTuple(kind, fields, &sub)) {
         return this->raiseTokenError(reader, "CatchClause");
     }
 
+    if (kind == BinKind::binjs_null) {
+        return true;
+    }
     if (kind != BinKind::catch_clause) {
         return this->raiseInvalidKind(reader, "CatchClause", kind);
     }
 
     UniqueNode param(nullptr, this->nodeFree);
     UniqueNode body(nullptr, this->nodeFree);
+    ScopeData scope(this->cx); // Optional
 
     for (auto field: fields) {
         switch (field) {
             case BinField::param:
                 if (!this->parsePattern(&sub, param)) {
                     return false;
                 }
                 break;
             case BinField::body:
                 if (!this->parseBlockStatement(&sub, body)) {
                     return false;
                 }
                 break;
+            case BinField::binjs_scope:
+                if (!this->parseScope(&sub, scope)) {
+                    return false;
+                }
+                break;
             default:
                 return this->raiseInvalidField(&sub, "CatchClause", field);
         }
     }
 
     if (!param || !body) {
         return this->raiseMissingField(&sub, "CatchClause");
     }
@@ -2424,28 +2439,32 @@ ASTReader::parseCatchClause(SimpleTokenR
     UniqueNode catchClause(new_<TernaryNode>(PNK_CATCH, JSOP_NOP, param.get(), nullptr, body.get()), this->nodeFree);
     if (!catchClause) {
         return this->raiseOOM();
     }
 
     Unused << param.release();
     Unused << body.release();
 
-    UniqueNode lexNode(new_<LexicalScopeNode>(/*FIXME: Implement*/nullptr, catchClause.get()), this->nodeFree);
-    if (!lexNode) {
-        return this->raiseOOM();
+    if (!this->promoteToLexicalScope(catchClause)) {
+        return false;
     }
-    Unused << catchClause.release(); // Now part of `lexNode`.
+
+    if (scope.isSome() && scope.hasLexNames()) {
+        if (!this->storeLexicalScope(catchClause, Move(scope))) {
+            return false;
+        }
+    }
 
     UniqueNode catchList(new_<ListNode>(PNK_CATCHLIST, TokenPos(0, 0)), this->nodeFree);// FIXME: We need a PNK_CATCHLIST of a single PNK_CATCH
     if (!catchList) {
         return this->raiseOOM();
     }
 
-    catchList->append(lexNode.release());
+    catchList->append(catchClause.release());
 
     out = Move(catchList);
     return true;
 }
 
 bool
 ASTReader::parsePatternList(SimpleTokenReader* reader, UniqueNode& out) {
     if (out) {
@@ -2773,19 +2792,18 @@ ASTReader::raiseAlreadyParsed(SimpleToke
 bool
 ASTReader::raiseEmpty(SimpleTokenReader* reader, const std::string& kind) {
     JS_ReportErrorASCII(cx, "BinAST parse error: in %s, empty data", kind.c_str());
     return this->raiseError(reader, "raiseEmpty");
 }
 
 bool
 ASTReader::raiseOOM() {
-    fprintf(stderr, "OOM\n");
     cx->recoverFromOutOfMemory();
-    MOZ_CRASH("Debugging");
+    MOZ_CRASH("Debugging OOM");
     return false;
 }
 
 
 bool
 ASTReader::raiseError(SimpleTokenReader* reader, const std::string& method) {
     TokenPos pos;
     reader->latestToken(pos);
--- a/js/src/frontend/BinSource.h
+++ b/js/src/frontend/BinSource.h
@@ -146,16 +146,17 @@ private:
     bool parseBlockStatementAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out);
     bool parseExpressionStatementAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out);
     bool parseExpressionAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out);
     bool parseVariableDeclarationAux(SimpleTokenReader* reader, const BinKind kind, const SimpleTokenReader::BinFields& fields, UniqueNode& out);
 
     // --- Utilities.
 
     bool promoteToLexicalScope(UniqueNode& node);
+    bool storeLexicalScope(UniqueNode& body, ScopeData&& scope);
 
     bool readString(SimpleTokenReader* reader, mozilla::Maybe<std::string>&);
     bool readString(SimpleTokenReader* reader, mozilla::Maybe<std::u16string>&);
     bool readString(SimpleTokenReader* reader, MutableHandleString);
     bool readString(SimpleTokenReader* reader, MutableHandleAtom);
     bool readString(SimpleTokenReader* reader, MutableHandle<PropertyName*>);
     bool readBool(SimpleTokenReader* reader, mozilla::Maybe<bool>&);
     bool readNumber(SimpleTokenReader* reader, mozilla::Maybe<double>&);