55
66import com .semmle .jcorn .Identifiers .Dialect ;
77import com .semmle .jcorn .Options .AllowReserved ;
8+ import com .semmle .jcorn .TokenType .Properties ;
89import com .semmle .js .ast .ArrayExpression ;
910import com .semmle .js .ast .ArrayPattern ;
1011import com .semmle .js .ast .ArrowFunctionExpression ;
4445import com .semmle .js .ast .IPattern ;
4546import com .semmle .js .ast .Identifier ;
4647import com .semmle .js .ast .IfStatement ;
48+ import com .semmle .js .ast .FieldDefinition ;
4749import com .semmle .js .ast .ImportDeclaration ;
4850import com .semmle .js .ast .ImportDefaultSpecifier ;
4951import com .semmle .js .ast .ImportNamespaceSpecifier ;
@@ -124,6 +126,7 @@ public class Parser {
124126 private boolean inModule ;
125127 protected boolean inFunction ;
126128 protected boolean inGenerator ;
129+ protected boolean inClass ;
127130 protected boolean inAsync ;
128131 protected boolean inTemplateElement ;
129132 protected int pos ;
@@ -240,8 +243,8 @@ public Parser(Options options, String input, int startPos) {
240243 // Used to signify the start of a potential arrow function
241244 this .potentialArrowAt = -1 ;
242245
243- // Flags to track whether we are in a function, a generator, an async function.
244- this .inFunction = this .inGenerator = this .inAsync = false ;
246+ // Flags to track whether we are in a function, a generator, an async function, a class .
247+ this .inFunction = this .inGenerator = this .inAsync = this . inClass = false ;
245248 // Positions to delayed-check that yield/await does not exist in default parameters.
246249 this .yieldPos = this .awaitPos = 0 ;
247250 // Labels in scope.
@@ -651,6 +654,9 @@ protected Token getTokenFromCode(int code) {
651654 case 58 :
652655 ++this .pos ;
653656 return this .finishToken (TokenType .colon );
657+ case 35 :
658+ ++this .pos ;
659+ return this .finishToken (TokenType .pound );
654660 case 63 :
655661 return this .readToken_question ();
656662
@@ -2191,6 +2197,7 @@ protected Expression processExprListItem(Expression e) {
21912197 // identifiers.
21922198 protected Identifier parseIdent (boolean liberal ) {
21932199 Position startLoc = this .startLoc ;
2200+ boolean isPrivateField = liberal && this .eat (TokenType .pound );
21942201 if (liberal && this .options .allowReserved () == AllowReserved .NEVER ) liberal = false ;
21952202 String name = null ;
21962203 if (this .type == TokenType .name ) {
@@ -2199,9 +2206,9 @@ protected Identifier parseIdent(boolean liberal) {
21992206 && (this .options .ecmaVersion () >= 6
22002207 || inputSubstring (this .start , this .end ).indexOf ("\\ " ) == -1 ))
22012208 this .raiseRecoverable (this .start , "The keyword '" + this .value + "' is reserved" );
2202- if (this .inGenerator && this .value .equals ("yield" ))
2209+ if (! isPrivateField && this .inGenerator && this .value .equals ("yield" ))
22032210 this .raiseRecoverable (this .start , "Can not use 'yield' as identifier inside a generator" );
2204- if (this .inAsync && this .value .equals ("await" ))
2211+ if (! isPrivateField && this .inAsync && this .value .equals ("await" ))
22052212 this .raiseRecoverable (
22062213 this .start , "Can not use 'await' as identifier inside an async function" );
22072214 name = String .valueOf (this .value );
@@ -2213,6 +2220,12 @@ protected Identifier parseIdent(boolean liberal) {
22132220 this .unexpected ();
22142221 }
22152222 this .next ();
2223+ if (isPrivateField ) {
2224+ if (!this .inClass ) {
2225+ this .raiseRecoverable (this .start , "Cannot use private fields outside a class" );
2226+ }
2227+ name = "#" + name ;
2228+ }
22162229 Identifier node = new Identifier (new SourceLocation (startLoc ), name );
22172230 return this .finishNode (node );
22182231 }
@@ -3127,6 +3140,8 @@ protected List<Expression> parseFunctionParams() {
31273140 // Parse a class declaration or literal (depending on the
31283141 // `isStatement` parameter).
31293142 protected Node parseClass (Position startLoc , boolean isStatement ) {
3143+ boolean oldInClass = this .inClass ;
3144+ this .inClass = true ;
31303145 SourceLocation loc = new SourceLocation (startLoc );
31313146 this .next ();
31323147 Identifier id = this .parseClassId (isStatement );
@@ -3145,6 +3160,8 @@ protected Node parseClass(Position startLoc, boolean isStatement) {
31453160 Node node ;
31463161 if (isStatement ) node = new ClassDeclaration (loc , id , superClass , classBody );
31473162 else node = new ClassExpression (loc , id , superClass , classBody );
3163+
3164+ this .inClass = oldInClass ;
31483165 return this .finishNode (node );
31493166 }
31503167
@@ -3221,6 +3238,9 @@ protected MemberDefinition<?> parseClassPropertyBody(
32213238 if (pi .kind .equals ("set" ) && node .getValue ().hasRest ())
32223239 this .raiseRecoverable (params .get (params .size () - 1 ), "Setter cannot use rest params" );
32233240 }
3241+ if (pi .key instanceof Identifier && ((Identifier )pi .key ).getName ().startsWith ("#" )) {
3242+ raiseRecoverable (pi .key , "Only fields, not methods, can be declared private." );
3243+ }
32243244 return node ;
32253245 }
32263246
0 commit comments