5252import com .semmle .js .ast .ForStatement ;
5353import com .semmle .js .ast .FunctionDeclaration ;
5454import com .semmle .js .ast .FunctionExpression ;
55+ import com .semmle .js .ast .GeneratedCodeExpr ;
5556import com .semmle .js .ast .IFunction ;
5657import com .semmle .js .ast .INode ;
5758import com .semmle .js .ast .IPattern ;
@@ -537,7 +538,7 @@ private Token readToken_question() { // '?'
537538 }
538539 return this .finishOp (TokenType .questionquestion , 2 );
539540 }
540-
541+
541542 }
542543 return this .finishOp (TokenType .question , 1 );
543544 }
@@ -1929,10 +1930,16 @@ MethodDefinition.Kind getMethodKind() {
19291930 // Parse an object literal or binding pattern.
19301931 protected Expression parseObj (boolean isPattern , DestructuringErrors refDestructuringErrors ) {
19311932 Position startLoc = this .startLoc ;
1933+ if (!isPattern && options .allowGeneratedCodeExprs () && charAt (pos ) == '{' ) {
1934+ // Parse mustache-style placeholder expression: {{ ... }} or {{{ ... }}}
1935+ return charAt (pos + 1 ) == '{'
1936+ ? parseGeneratedCodeExpr (startLoc , "{{{" , "}}}" )
1937+ : parseGeneratedCodeExpr (startLoc , "{{" , "}}" );
1938+ }
19321939 boolean first = true ;
19331940 Map <String , PropInfo > propHash = new LinkedHashMap <>();
19341941 List <Property > properties = new ArrayList <Property >();
1935- this .next ();
1942+ this .next (); // skip '{'
19361943 while (!this .eat (TokenType .braceR )) {
19371944 if (!first ) {
19381945 this .expect (TokenType .comma );
@@ -1949,6 +1956,42 @@ protected Expression parseObj(boolean isPattern, DestructuringErrors refDestruct
19491956 return this .finishNode (node );
19501957 }
19511958
1959+ /** Emit a token ranging from the current position until <code>endOfToken</code>. */
1960+ private Token generateTokenEndingAt (int endOfToken , TokenType tokenType ) {
1961+ this .lastTokEnd = this .end ;
1962+ this .lastTokStart = this .start ;
1963+ this .lastTokEndLoc = this .endLoc ;
1964+ this .lastTokStartLoc = this .startLoc ;
1965+ this .start = this .pos ;
1966+ this .startLoc = this .curPosition ();
1967+ this .pos = endOfToken ;
1968+ return finishToken (tokenType );
1969+ }
1970+
1971+ /** Parse a generated expression. The current token refers to the opening delimiter. */
1972+ protected Expression parseGeneratedCodeExpr (Position startLoc , String openingDelimiter , String closingDelimiter ) {
1973+ // Emit a token for what's left of the opening delimiter, if there are any remaining characters
1974+ int startOfBody = startLoc .getOffset () + openingDelimiter .length ();
1975+ if (this .pos != startOfBody ) {
1976+ this .generateTokenEndingAt (startOfBody , TokenType .generatedCodeDelimiter );
1977+ }
1978+
1979+ // Emit a token for the generated code body
1980+ int endOfBody = this .input .indexOf (closingDelimiter , startOfBody );
1981+ if (endOfBody == -1 ) {
1982+ this .unexpected (startLoc );
1983+ }
1984+ Token bodyToken = this .generateTokenEndingAt (endOfBody , TokenType .generatedCodeExpr );
1985+
1986+ // Emit a token for the closing delimiter
1987+ this .generateTokenEndingAt (endOfBody + closingDelimiter .length (), TokenType .generatedCodeDelimiter );
1988+
1989+ this .next (); // produce lookahead token
1990+
1991+ return finishNode (new GeneratedCodeExpr (new SourceLocation (startLoc ), openingDelimiter , closingDelimiter ,
1992+ bodyToken .getValue ()));
1993+ }
1994+
19521995 protected Property parseProperty (
19531996 boolean isPattern ,
19541997 DestructuringErrors refDestructuringErrors ,
0 commit comments