From d0ed6dc99b495cfe2b813d3c750b8ae9290ababf Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Mon, 4 Feb 2019 13:07:32 +0900 Subject: [PATCH] JavaScript: capture function objects in Self-Invoking Anonymous Function Statements Signed-off-by: Masatake YAMATO --- .../self-invoking-anon-func.d/args.ctags | 1 + .../self-invoking-anon-func.d/expected.tags | 10 +++ .../self-invoking-anon-func.d/input.js | 26 ++++++ parsers/jscript.c | 83 ++++++++++++++++++- 4 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 Units/parser-javascript.r/self-invoking-anon-func.d/args.ctags create mode 100644 Units/parser-javascript.r/self-invoking-anon-func.d/expected.tags create mode 100644 Units/parser-javascript.r/self-invoking-anon-func.d/input.js diff --git a/Units/parser-javascript.r/self-invoking-anon-func.d/args.ctags b/Units/parser-javascript.r/self-invoking-anon-func.d/args.ctags new file mode 100644 index 0000000000..5ee5f79f70 --- /dev/null +++ b/Units/parser-javascript.r/self-invoking-anon-func.d/args.ctags @@ -0,0 +1 @@ +--sort=no diff --git a/Units/parser-javascript.r/self-invoking-anon-func.d/expected.tags b/Units/parser-javascript.r/self-invoking-anon-func.d/expected.tags new file mode 100644 index 0000000000..a5bf2f6fe2 --- /dev/null +++ b/Units/parser-javascript.r/self-invoking-anon-func.d/expected.tags @@ -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 diff --git a/Units/parser-javascript.r/self-invoking-anon-func.d/input.js b/Units/parser-javascript.r/self-invoking-anon-func.d/input.js new file mode 100644 index 0000000000..d8ebac59af --- /dev/null +++ b/Units/parser-javascript.r/self-invoking-anon-func.d/input.js @@ -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) + ))))); diff --git a/parsers/jscript.c b/parsers/jscript.c index cfecae52fd..32fa5e38f7 100644 --- a/parsers/jscript.c +++ b/parsers/jscript.c @@ -124,7 +124,8 @@ typedef enum eTokenType { * Used only in readTokenFull or lower functions. */ TOKEN_ATMARK, TOKEN_BINARY_OPERATOR, - TOKEN_ARROW + TOKEN_ARROW, + TOKEN_BANG, } tokenType; typedef struct sTokenInfo { @@ -1066,6 +1067,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 '>': @@ -2924,6 +2928,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); + } + } + } else { /*