1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 14:17:42 +00:00

LibJS: Tweak generated source in 'new Function()' to match ES 2015 spec

ES 5(.1) described parsing of the function body string as:

https://www.ecma-international.org/ecma-262/5.1/#sec-15.3.2.1

7. If P is not parsable as a FormalParameterList[opt] then throw a SyntaxError exception.
8. If body is not parsable as FunctionBody then throw a SyntaxError exception.

We implemented it as building the source string of a complete function
and feeding that to the parser, with the same outcome. ES 2015+ does
exactly that, but with newlines at certain positions:

https://tc39.es/ecma262/#sec-createdynamicfunction

16. Let bodyString be the string-concatenation of 0x000A (LINE FEED), ? ToString(bodyArg), and 0x000A (LINE FEED).
17. Let prefix be the prefix associated with kind in Table 49.
18. Let sourceString be the string-concatenation of prefix, " anonymous(", P, 0x000A (LINE FEED), ") {", bodyString, and "}".

This patch updates the generated source string to match these
requirements. This will make certain edge cases work, e.g.
'new Function("-->")', where the user supplied input must be placed on
its own line to be valid syntax.
This commit is contained in:
Linus Groh 2020-10-29 18:11:35 +00:00 committed by Andreas Kling
parent bed270ca47
commit a10d09faba
3 changed files with 16 additions and 8 deletions

View file

@ -81,7 +81,7 @@ Value FunctionConstructor::construct(Function&)
if (vm.exception()) if (vm.exception())
return {}; return {};
} }
auto source = String::formatted("function anonymous({}) {{ {} }}", parameters_source, body_source); auto source = String::formatted("function anonymous({}\n) {{\n{}\n}}", parameters_source, body_source);
auto parser = Parser(Lexer(source)); auto parser = Parser(Lexer(source));
auto function_expression = parser.parse_function_node<FunctionExpression>(); auto function_expression = parser.parse_function_node<FunctionExpression>();
if (parser.has_errors()) { if (parser.has_errors()) {

View file

@ -30,6 +30,8 @@ describe("correct behavior", () => {
expect(new Function("return typeof Function()")()).toBe("function"); expect(new Function("return typeof Function()")()).toBe("function");
expect(new Function("x", "return function (y) { return x + y };")(1)(2)).toBe(3); expect(new Function("x", "return function (y) { return x + y };")(1)(2)).toBe(3);
expect(new Function("-->")()).toBeUndefined();
expect(new Function().name).toBe("anonymous"); expect(new Function().name).toBe("anonymous");
expect(new Function().toString()).toBe("function anonymous() {\n ???\n}"); expect(new Function().toString()).toBe("function anonymous() {\n ???\n}");
}); });
@ -45,7 +47,7 @@ describe("errors", () => {
// This is in line with what other engines are reporting. // This is in line with what other engines are reporting.
.toThrowWithMessage( .toThrowWithMessage(
SyntaxError, SyntaxError,
"Unexpected token CurlyClose. Expected BracketClose (line: 1, column: 26)" "Unexpected token CurlyClose. Expected BracketClose (line: 4, column: 1)"
); );
}); });
}); });

View file

@ -1,5 +1,11 @@
/* /*
These tests deliberately produce syntax errors to check what line the parser thinks we're on. These tests deliberately produce syntax errors to check what line the parser thinks we're on.
Note that line numbers are higher than you might expect as the parsed code is:
function anonymous(
) {
<code>
}
PLEASE MAKE SURE TO NOT LET YOUR EDITOR REMOVE THE LS/PS LINE TERMINATORS! PLEASE MAKE SURE TO NOT LET YOUR EDITOR REMOVE THE LS/PS LINE TERMINATORS!
*/ */
@ -7,31 +13,31 @@ These tests deliberately produce syntax errors to check what line the parser thi
test("LINE FEED is a line terminator", () => { test("LINE FEED is a line terminator", () => {
expect(() => { expect(() => {
Function("\n\n@"); Function("\n\n@");
}).toThrowWithMessage(SyntaxError, "line: 3, column: 1"); }).toThrowWithMessage(SyntaxError, "line: 5, column: 1");
}); });
test("CARRIAGE RETURN is a line terminator", () => { test("CARRIAGE RETURN is a line terminator", () => {
expect(() => { expect(() => {
Function("\r\r@"); Function("\r\r@");
}).toThrowWithMessage(SyntaxError, "line: 3, column: 1"); }).toThrowWithMessage(SyntaxError, "line: 5, column: 1");
}); });
test("LINE SEPARATOR is a line terminator", () => { test("LINE SEPARATOR is a line terminator", () => {
expect(() => { expect(() => {
Function("@"); Function("@");
}).toThrowWithMessage(SyntaxError, "line: 3, column: 1"); }).toThrowWithMessage(SyntaxError, "line: 5, column: 1");
}); });
test("PARAGRAPH SEPARATOR is a line terminator", () => { test("PARAGRAPH SEPARATOR is a line terminator", () => {
expect(() => { expect(() => {
Function("@"); Function("@");
}).toThrowWithMessage(SyntaxError, "line: 3, column: 1"); }).toThrowWithMessage(SyntaxError, "line: 5, column: 1");
}); });
test("CR LF is counted as only one line terminator", () => { test("CR LF is counted as only one line terminator", () => {
expect(() => { expect(() => {
Function("\r\n\r\n@"); Function("\r\n\r\n@");
}).toThrowWithMessage(SyntaxError, "line: 3, column: 1"); }).toThrowWithMessage(SyntaxError, "line: 5, column: 1");
}); });
test("LF/CR are not allowed in string literal", () => { test("LF/CR are not allowed in string literal", () => {
@ -49,7 +55,7 @@ test("LS/PS are allowed in string literal", () => {
test("line terminators can be mixed (but please don't)", () => { test("line terminators can be mixed (but please don't)", () => {
expect(() => { expect(() => {
Function("\r\r\n\n\r@"); Function("\r\r\n\n\r@");
}).toThrowWithMessage(SyntaxError, "line: 7, column: 1"); }).toThrowWithMessage(SyntaxError, "line: 9, column: 1");
}); });
test("all line terminators are valid for line continuations", () => { test("all line terminators are valid for line continuations", () => {