JavaScript: fix handling of parentheses around an rvalue

Properly skip parentheses around an rvalue, and then properly recognize
the surrounded value.  This allows to properly recognize e.g. rvalue
`({...})` as an object, or `(function(){})` as a function.  As the
implementation is tolerant regarding garbage after the statement,
function expressions called straight away (`(function(){})()`) are
implicitly supported.

This however removes support for the following invalid JavaScript
syntax that was previously supported as a function/method declaration:

	var func = () {}

This syntax is not present in the ECMA standard nor is supported by
popular JavaScript engines.

See:
 * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
   section 13, "Function Definition"
 * http://ecma262-5.com/ELS5_HTML.htm#Section_13
 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope#Defining_functions
This commit is contained in:
Colomban Wendling 2014-08-03 23:55:25 +02:00
parent 35e8fbbe28
commit 8341228ffa
5 changed files with 85 additions and 37 deletions

View File

@ -1368,8 +1368,17 @@ static boolean parseStatement (tokenInfo *const token, boolean is_inside_class)
if ( isType (token, TOKEN_EQUAL_SIGN) )
{
int parenDepth = 0;
readToken (token);
/* rvalue might be surrounded with parentheses */
while (isType (token, TOKEN_OPEN_PAREN))
{
parenDepth++;
readToken (token);
}
if ( isKeyword (token, KEYWORD_function) )
{
readToken (token);
@ -1426,37 +1435,9 @@ static boolean parseStatement (tokenInfo *const token, boolean is_inside_class)
if ( vStringLength(secondary_name->string) > 0 )
makeFunctionTag (secondary_name);
/*
* Find to the end of the statement
*/
goto cleanUp;
}
}
}
else if (isType (token, TOKEN_OPEN_PAREN))
{
/*
* Handle nameless functions
* this.method_name = () {}
* Also assignments starting with parentheses
* var foo = (1 + 2) * 3;
*/
skipArgumentList(token);
if (isType (token, TOKEN_OPEN_CURLY))
{
/*
* Nameless functions are only setup as methods.
*/
makeJsTag (name, JSTAG_METHOD);
parseBlock (token, name);
}
else if (isType (token, TOKEN_CLOSE_CURLY))
is_terminated = FALSE;
else if (token->nestLevel == 0 && is_global)
makeJsTag (name, JSTAG_VARIABLE);
}
else if (isType (token, TOKEN_OPEN_CURLY))
{
/*
@ -1506,11 +1487,7 @@ static boolean parseStatement (tokenInfo *const token, boolean is_inside_class)
if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) &&
! stringListHas(ClassNames, vStringValue (fulltag)) )
{
readToken (token);
if ( ! isType (token, TOKEN_SEMICOLON))
findCmdTerm (token);
if (isType (token, TOKEN_SEMICOLON))
makeJsTag (name, JSTAG_VARIABLE);
makeJsTag (name, JSTAG_VARIABLE);
}
vStringDelete (fulltag);
}
@ -1598,13 +1575,25 @@ static boolean parseStatement (tokenInfo *const token, boolean is_inside_class)
if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) &&
! stringListHas(ClassNames, vStringValue (fulltag)) )
{
findCmdTerm (token);
if (isType (token, TOKEN_SEMICOLON))
makeJsTag (name, JSTAG_VARIABLE);
makeJsTag (name, JSTAG_VARIABLE);
}
vStringDelete (fulltag);
}
}
if (parenDepth > 0)
{
while (parenDepth > 0)
{
if (isType (token, TOKEN_OPEN_PAREN))
parenDepth++;
else if (isType (token, TOKEN_CLOSE_PAREN))
parenDepth--;
readToken (token);
}
if (isType (token, TOKEN_CLOSE_CURLY))
is_terminated = FALSE;
}
}
/* if we aren't already at the cmd end, advance to it and check whether

View File

@ -200,6 +200,7 @@ test_sources = \
objectivec_protocol.mm \
Package.pm \
php5_5_class_kw.php \
parenthesis-rvalue.js \
preprocessor.f90 \
procedure_pointer_module.f90 \
procpoint.f90 \

View File

@ -0,0 +1,35 @@
// plain values
var a1 = 42;
var a2 = (42);
// functions
var b1 = function(){
function b1sub(){}
};
var b2 = (function(){
function b2sub(){}
});
var b3 = ((function(){
function b3sub(){}
}));
// objects
var c1 = {};
var c2 = ({});
var d1 = {a:'hello',b:'hi'};
var d2 = ({a:'hello',b:'hi'});
// function expressions called straight away
var e1 = function(){
function e1sub(){}
return 42;
}();
var e2 = (function(){
function e2sub(){}
return 42
})();
var e3 = ((function(){
function e3sub(){}
return 42
})());

View File

@ -0,0 +1,23 @@
# format=tagmanager
aÌ64Îd1Ö0
aÌ64Îd2Ö0
a1Ì16384Ö0
a2Ì16384Ö0
bÌ64Îd1Ö0
bÌ64Îd2Ö0
b1Ì16Ö0
b1subÌ16Îb1Ö0
b2Ì16Ö0
b2subÌ16Îb2Ö0
b3Ì16Ö0
b3subÌ16Îb3Ö0
c1Ì16384Ö0
c2Ì16384Ö0
d1Ì1Ö0
d2Ì1Ö0
e1Ì16Ö0
e1subÌ16Îe1Ö0
e2Ì16Ö0
e2subÌ16Îe2Ö0
e3Ì16Ö0
e3subÌ16Îe3Ö0

View File

@ -69,7 +69,7 @@ ValidClassTwo = function ()
this.validMethodThree = function() {}
// unnamed method
this.validMethodFour = () {}
this.validMethodFour = function() {}
}
var my_global_var4 = document.getElementsByTagName("input");