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

Accept unlabelled function statements outside of top level for strict functions #2213

Merged
merged 3 commits into from
Dec 9, 2019
Merged
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
4 changes: 4 additions & 0 deletions RELEASES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3587,3 +3587,7 @@ Planned

* Remove prepared source variants other than src/ (matches Duktape 2.x
src-noline/) from the distributable (GH-2209)

* Accept unlabelled function statements outside of top level for strict
functions (using hoist semantics), previously they were rejected with
a SyntaxError (GH-2213)
9 changes: 6 additions & 3 deletions src-input/duk_js_compiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -6385,10 +6385,13 @@ DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_
*/
test_func_decl = allow_source_elem;
#if defined(DUK_USE_NONSTD_FUNC_STMT)
/* Lenient: allow function declarations outside top level in
* non-strict mode but reject them in strict mode.
/* Lenient: allow function declarations outside top level in both
* strict and non-strict modes. However, don't allow labelled
* function declarations in strict mode.
*/
test_func_decl = test_func_decl || !comp_ctx->curr_func.is_strict;
test_func_decl = test_func_decl ||
!comp_ctx->curr_func.is_strict ||
label_id < 0;
#endif /* DUK_USE_NONSTD_FUNC_STMT */
/* Strict: never allow function declarations outside top level. */
if (test_func_decl) {
Expand Down
12 changes: 7 additions & 5 deletions tests/ecmascript/test-dev-func-decl-outside-top.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,14 @@ try {

/*===
function declaration inside try (strict)
SyntaxError
fun1 undefined
fun1 undefined
===*/

function functionDeclarationInsideTryStrictTest() {
/* Same as above, but in strict mode. V8 behavior is to reject
* function declarationts outside top level in strict mode. We
* follow that behavior.
/* Same as above, but in strict mode. V8 used to reject function
* declarations outside top level in strict mode but now accepts
* them with block level semantics.
*
* Rhino allows such declarations even in strict mode, and provides
* the same semantics as in non-strict mode.
Expand All @@ -190,8 +191,9 @@ function functionDeclarationInsideTryStrictTest() {
'throw new Error("test");' +
'} catch (e) {' +
'function fun1() { print("fun1", typeof e); }' +
'fun1();' + // V8: 'fun1 object'
'}' +
'fun1();' +
'fun1();' + // V8: TypeError, fun1 not visible
'})()');
}

Expand Down
12 changes: 6 additions & 6 deletions tests/ecmascript/test-dev-label-source-elem.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
*
* This is the E5 standards compliant behavior. However, this test
* case tests for the default Duktape behavior (modelled after V8):
* function declarations are allowed outside top level in non-strict
* mode, and are treated like ordinary function declarations. In
* strict mode they are not allowed.
* function declarations are allowed outside top level and are
* treated like ordinary function declarations.
*/

/*---
Expand Down Expand Up @@ -41,9 +40,10 @@ try {
}

try {
/* Strict mode should have no effect on this, but this test
* illustrates V8 behavior (i.e. V8 gives a SyntaxError here
* but only in strict mode).
/* This test illustrates V8 behavior, i.e. V8 gives a SyntaxError
* here but only in strict mode, even in Node.js v12.7.0:
* SyntaxError: In strict mode code, functions can only be declared
* at top level or inside a block.
*/
eval("function f1() { 'use strict'; mylabel: function f2() {} }");
print("try finished");
Expand Down
32 changes: 32 additions & 0 deletions tests/ecmascript/test-stmt-func-stmt-nonstrict.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*===
a called
a called
b called
b called
===*/

try {
function a() {
print('a called');
}
a();
} catch (e) {
print(e.stack || e);
}
// In Node.js v12.7.0 'a' is still visible here, i.e. declaration
// is hoisted.
a();

function test() {
try {
function b() {
print('b called');
}
b();
} catch (e) {
print(e.stack || e);
}
// Also hoisted here.
b();
}
test();
51 changes: 51 additions & 0 deletions tests/ecmascript/test-stmt-func-stmt-strict.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*===
a called
a called
b called
b called
===*/

/*---
{
"use_strict": true
}
---*/

'use strict';

// Duktape 2.5.0 and prior reject strict mode function statements.
// Duktape >2.5.0 allows them to improve compatibility with existing
// code base.
try {
function a() {
print('a called');
}
a();
} catch (e) {
print(e.stack || e);
}
// In Node.js v12.7.0 'a' is not visible here, i.e. 'a' has block visibility
// (ReferenceError here). Duktape 2.x uses hoist semantics.
try {
a();
} catch (e) {
print(e.name);
}

function test() {
try {
function b() {
print('b called');
}
b();
} catch (e) {
print(e.stack || e);
}
// Same here.
try {
b();
} catch (e) {
print(e.name);
}
}
test();
34 changes: 34 additions & 0 deletions tests/ecmascript/test-stmt-labelled-func-stmt-nonstrict.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*===
a called
a called
b called
b called
===*/

try {
label1:
function a() {
print('a called');
}
a();
} catch (e) {
print(e.stack || e);
}
// In Node.js v12.7.0 'a' is still visible here, i.e. declaration
// is hoisted.
a();

function test() {
try {
label2:
function b() {
print('b called');
}
b();
} catch (e) {
print(e.stack || e);
}
// Also hoisted here.
b();
}
test();
37 changes: 37 additions & 0 deletions tests/ecmascript/test-stmt-labelled-func-stmt-strict.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*===
SyntaxError
SyntaxError
===*/

/*---
{
"use_strict": true
}
---*/

'use strict';

// Labelled function declarations are rejected altogether.

try {
eval('label1:\n' +
' function a() {\n' +
' print("a called");\n' +
' }\n' +
'a();\n');
} catch (e) {
print(e.name);
}

function test() {
try {
eval('label2:\n' +
' function b() {\n' +
' print("b called");\n' +
' }\n' +
'b();\n');
} catch (e) {
print(e.name);
}
}
test();