Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript: capture function objects in Self-Invoking Anonymous Function Statements #2005

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--sort=no
10 changes: 10 additions & 0 deletions Units/parser-javascript.r/self-invoking-anon-func.d/expected.tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
anonymousFunction204aa80e0100 input.js /^(function(x) {$/;" f
a input.js /^ var a = function (o) { return o; };$/;" f function:anonymousFunction204aa80e0100
anonymousFunction204aa80e0200 input.js /^(function(y) {$/;" f
b input.js /^ const b = function (p) { return p; };$/;" f function:anonymousFunction204aa80e0200
anonymousFunction204aa80e0300 input.js /^!function(z) {$/;" f
c input.js /^ const c = function (r) { return r; };$/;" f function:anonymousFunction204aa80e0300
anonymousFunction204aa80e0400 input.js /^console.log("a" + (function (A) {$/;" f
anonymousFunction204aa80e0500 input.js /^}((function (B) {$/;" f
anonymousFunction204aa80e0600 input.js /^}(!function(C) {$/;" f
f input.js /^ var f = function (t) {$/;" f function:anonymousFunction204aa80e0600
26 changes: 26 additions & 0 deletions Units/parser-javascript.r/self-invoking-anon-func.d/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
(function(x) {
var a = function (o) { return o; };
return x + a (1);
})(1);

(function(y) {
const b = function (p) { return p; };
return y - b (1);
}(1));

!function(z) {
const c = function (r) { return r; };
return z * c (1);
}(1);

console.log("a" + (function (A) {
return A;
}((function (B) {
return B;
}(!function(C) {
var f = function (t) {
return !t;
}
return f(C);
}(true)
)))));
133 changes: 109 additions & 24 deletions parsers/jscript.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ typedef enum eTokenType {
TOKEN_BINARY_OPERATOR,
TOKEN_ARROW,
TOKEN_DOTS, /* ... */
TOKEN_BANG, /* ! */
} tokenType;

typedef struct sTokenInfo {
Expand Down Expand Up @@ -1096,6 +1097,9 @@ static void readTokenFullRaw (tokenInfo *const token, bool include_newlines, vSt
case '*':
token->type = TOKEN_STAR;
break;
case '!':
token->type = TOKEN_BANG;
break;
case '%':
case '?':
case '>':
Expand Down Expand Up @@ -2954,6 +2958,83 @@ static bool parseLine (tokenInfo *const token, bool is_inside_class)
break;
}
}
else if (isType (token, TOKEN_BANG))
{
/*
* Try to handle "Self-Invoking Anonymous Function Statements" like:
*
* !function(z) { # case A.
* return z * 1;
* }(1);
*
* case A is nothing special; just consuming the token for ! is enough.
*/
readToken (token);
is_terminated = parseLine (token, is_inside_class);
}
else if (isType (token, TOKEN_OPEN_PAREN))
{
/*
* Try to handle "Self-Invoking Anonymous Function Statements" like:
*
* (function(x) { # case B.
* return x + 1;
* })(1);
*
* (function(y) { # case C.
* return y - 1;
* }(1));
*
*/
readToken (token);
is_terminated = parseLine (token, is_inside_class);

/*
* In case B, next ')' should be consumed here.
* In case C, the last ')' after an argument list should be consumed here.
* In case B and C, this function returns true.
*/
if (is_terminated)
{
tokenType last_token_type = token->type;

readToken (token);
if (isType (token, TOKEN_CLOSE_PAREN))
{
/* case B. */
is_terminated = true;
}
else if (last_token_type == TOKEN_CLOSE_CURLY && isType (token, TOKEN_OPEN_PAREN))
{
/*
* maybe case C.
* Parse the argument list if there is.
*/
is_terminated = parseStatement (token, is_inside_class);
if (is_terminated)
{
readToken (token);
if (!isType (token, TOKEN_CLOSE_PAREN) && !isType (token, TOKEN_EOF))
{
/*
* In case B, ')' terminates the statement.
* If the token is not ')', the statement is
* not a "Self-Invoking Anonymous Function Statement".
*/
Assert (NextToken == NULL);
NextToken = newToken ();
copyToken (NextToken, token, false);
}
}
}
else if (!isType (token, TOKEN_EOF))
{
Assert (NextToken == NULL);
NextToken = newToken ();
copyToken (NextToken, token, false);
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really fancy having all this in parseLine() instead of parseStatement(), but I must admit that it's a lot easier that way, so why not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

..I recognize putting all new code to parseLine is not good.
At least I should introduce a new function to handle (function ()...).

I will update my patch.

else
{
/*
Expand Down Expand Up @@ -3051,32 +3132,36 @@ static const char *kindName(jsKind kind)
}

static const char *tokenTypeName(enum eTokenType e)
{ /* Generated by misc/enumstr.sh with cmdline "parsers/jscript.c" "eTokenType" "tokenTypeName" */
{ /* Generated by misc/enumstr.sh with cmdline:
parsers/jscript.c eTokenType tokenTypeName */
switch (e)
{
case TOKEN_BINARY_OPERATOR: return "TOKEN_BINARY_OPERATOR";
case TOKEN_CHARACTER: return "TOKEN_CHARACTER";
case TOKEN_CLOSE_CURLY: return "TOKEN_CLOSE_CURLY";
case TOKEN_CLOSE_PAREN: return "TOKEN_CLOSE_PAREN";
case TOKEN_CLOSE_SQUARE: return "TOKEN_CLOSE_SQUARE";
case TOKEN_COLON: return "TOKEN_COLON";
case TOKEN_COMMA: return "TOKEN_COMMA";
case TOKEN_EOF: return "TOKEN_EOF";
case TOKEN_EQUAL_SIGN: return "TOKEN_EQUAL_SIGN";
case TOKEN_IDENTIFIER: return "TOKEN_IDENTIFIER";
case TOKEN_KEYWORD: return "TOKEN_KEYWORD";
case TOKEN_OPEN_CURLY: return "TOKEN_OPEN_CURLY";
case TOKEN_OPEN_PAREN: return "TOKEN_OPEN_PAREN";
case TOKEN_OPEN_SQUARE: return "TOKEN_OPEN_SQUARE";
case TOKEN_PERIOD: return "TOKEN_PERIOD";
case TOKEN_POSTFIX_OPERATOR: return "TOKEN_POSTFIX_OPERATOR";
case TOKEN_REGEXP: return "TOKEN_REGEXP";
case TOKEN_SEMICOLON: return "TOKEN_SEMICOLON";
case TOKEN_STAR: return "TOKEN_STAR";
case TOKEN_STRING: return "TOKEN_STRING";
case TOKEN_TEMPLATE_STRING: return "TOKEN_TEMPLATE_STRING";
case TOKEN_UNDEFINED: return "TOKEN_UNDEFINED";
default: return "UNKNOWN";
case TOKEN_UNDEFINED: return "TOKEN_UNDEFINED";
case TOKEN_EOF: return "TOKEN_EOF";
case TOKEN_CHARACTER: return "TOKEN_CHARACTER";
case TOKEN_CLOSE_PAREN: return "TOKEN_CLOSE_PAREN";
case TOKEN_SEMICOLON: return "TOKEN_SEMICOLON";
case TOKEN_COLON: return "TOKEN_COLON";
case TOKEN_COMMA: return "TOKEN_COMMA";
case TOKEN_KEYWORD: return "TOKEN_KEYWORD";
case TOKEN_OPEN_PAREN: return "TOKEN_OPEN_PAREN";
case TOKEN_IDENTIFIER: return "TOKEN_IDENTIFIER";
case TOKEN_STRING: return "TOKEN_STRING";
case TOKEN_TEMPLATE_STRING: return "TOKEN_TEMPLATE_STRING";
case TOKEN_PERIOD: return "TOKEN_PERIOD";
case TOKEN_OPEN_CURLY: return "TOKEN_OPEN_CURLY";
case TOKEN_CLOSE_CURLY: return "TOKEN_CLOSE_CURLY";
case TOKEN_EQUAL_SIGN: return "TOKEN_EQUAL_SIGN";
case TOKEN_OPEN_SQUARE: return "TOKEN_OPEN_SQUARE";
case TOKEN_CLOSE_SQUARE: return "TOKEN_CLOSE_SQUARE";
case TOKEN_REGEXP: return "TOKEN_REGEXP";
case TOKEN_POSTFIX_OPERATOR: return "TOKEN_POSTFIX_OPERATOR";
case TOKEN_STAR: return "TOKEN_STAR";
case TOKEN_ATMARK: return "TOKEN_ATMARK";
case TOKEN_BINARY_OPERATOR: return "TOKEN_BINARY_OPERATOR";
case TOKEN_ARROW: return "TOKEN_ARROW";
case TOKEN_DOTS: return "TOKEN_DOTS";
default: return "UNKNOWN";
}
}
#endif
Expand Down