--- old/src/jdk/nashorn/internal/objects/NativeFunction.java 2013-06-03 14:05:21.785617627 +0530 +++ new/src/jdk/nashorn/internal/objects/NativeFunction.java 2013-06-03 14:05:21.561616517 +0530 @@ -33,10 +33,14 @@ import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.Function; import jdk.nashorn.internal.objects.annotations.ScriptClass; +import jdk.nashorn.internal.parser.Parser; +import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.JSType; +import jdk.nashorn.internal.runtime.ParserException; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; +import jdk.nashorn.internal.runtime.Source; /** * ECMA 15.3 Function Objects @@ -187,16 +191,25 @@ sb.append("(function ("); if (args.length > 0) { + final StringBuilder paramListBuf = new StringBuilder(); for (int i = 0; i < args.length - 1; i++) { - sb.append(JSType.toString(args[i])); + paramListBuf.append(JSType.toString(args[i])); if (i < args.length - 2) { - sb.append(","); + paramListBuf.append(","); } } + + final String paramList = paramListBuf.toString(); + if (! paramList.isEmpty()) { + checkFunctionParameters(paramList); + sb.append(paramList); + } } sb.append(") {\n"); if (args.length > 0) { - sb.append(JSType.toString(args[args.length - 1])); + final String funcBody = JSType.toString(args[args.length - 1]); + checkFunctionBody(funcBody); + sb.append(funcBody); sb.append('\n'); } sb.append("})"); @@ -205,4 +218,24 @@ return Global.directEval(global, sb.toString(), global, "", Global.isStrict()); } + + private static void checkFunctionParameters(final String params) { + final Source src = new Source("", params); + final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager()); + try { + parser.parseFormalParameterList(); + } catch (final ParserException pe) { + pe.throwAsEcmaException(); + } + } + + private static void checkFunctionBody(final String funcBody) { + final Source src = new Source("", funcBody); + final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager()); + try { + parser.parseFunctionBody(); + } catch (final ParserException pe) { + pe.throwAsEcmaException(); + } + } } --- old/src/jdk/nashorn/internal/parser/Parser.java 2013-06-03 14:05:22.813622723 +0530 +++ new/src/jdk/nashorn/internal/parser/Parser.java 2013-06-03 14:05:22.521621274 +0530 @@ -192,36 +192,110 @@ // Begin parse. return program(scriptName); } catch (final Exception e) { - // Extract message from exception. The message will be in error - // message format. - String message = e.getMessage(); - - // If empty message. - if (message == null) { - message = e.toString(); - } + handleParseException(e); - // Issue message. - if (e instanceof ParserException) { - errors.error((ParserException)e); + return null; + } finally { + final String end = this + " end '" + scriptName + "'"; + if (Timing.isEnabled()) { + Timing.accumulateTime(toString(), System.currentTimeMillis() - t0); + LOG.info(end, "' in ", (System.currentTimeMillis() - t0), " ms"); } else { - errors.error(message); + LOG.info(end); } + } + } - if (env._dump_on_error) { - e.printStackTrace(env.getErr()); - } + /** + * Parse and return the list of function parameter list. A comma + * separated list of function parameter identifiers is expected to be parsed. + * Errors will be thrown and the error manager will contain information + * if parsing should fail. This method is used to check if parameter Strings + * passed to "Function" constructor is a valid or not. + * + * @return the list of IdentNodes representing the formal parameter list + */ + public List parseFormalParameterList() { + try { + stream = new TokenStream(); + lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions); + + // Set up first token (skips opening EOL.) + k = -1; + next(); + + return formalParameterList(TokenType.EOF); + } catch (final Exception e) { + handleParseException(e); + return null; + } + } + /** + * Execute parse and return the resulting function node. + * Errors will be thrown and the error manager will contain information + * if parsing should fail. This method is used to check if code String + * passed to "Function" constructor is a valid function body or not. + * + * @return function node resulting from successful parse + */ + public FunctionNode parseFunctionBody() { + try { + stream = new TokenStream(); + lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions); + + // Set up first token (skips opening EOL.) + k = -1; + next(); + + // Make a fake token for the function. + final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength()); + // Set up the function to append elements. + + FunctionNode function = newFunctionNode( + functionToken, + new IdentNode(functionToken, Token.descPosition(functionToken), RUN_SCRIPT.symbolName()), + new ArrayList(), + FunctionNode.Kind.NORMAL); + + functionDeclarations = new ArrayList<>(); + sourceElements(); + addFunctionDeclarations(function); + functionDeclarations = null; + + expect(EOF); + + function.setFinish(source.getLength() - 1); + + function = restoreFunctionNode(function, token); //commit code + function = function.setBody(lc, function.getBody().setNeedsScope(lc)); + return function; + } catch (final Exception e) { + handleParseException(e); return null; - } finally { - final String end = this + " end '" + scriptName + "'"; - if (Timing.isEnabled()) { - Timing.accumulateTime(toString(), System.currentTimeMillis() - t0); - LOG.info(end, "' in ", (System.currentTimeMillis() - t0), " ms"); - } else { - LOG.info(end); - } - } + } + } + + private void handleParseException(final Exception e) { + // Extract message from exception. The message will be in error + // message format. + String message = e.getMessage(); + + // If empty message. + if (message == null) { + message = e.toString(); + } + + // Issue message. + if (e instanceof ParserException) { + errors.error((ParserException)e); + } else { + errors.error(message); + } + + if (env._dump_on_error) { + e.printStackTrace(env.getErr()); + } } /** @@ -2424,12 +2498,28 @@ * @return List of parameter nodes. */ private List formalParameterList() { + return formalParameterList(RPAREN); + } + /** + * Same as the other method of the same name - except that the end + * token type expected is passed as argument to this method. + * + * FormalParameterList : + * Identifier + * FormalParameterList , Identifier + * + * See 13 + * + * Parse function parameter list. + * @return List of parameter nodes. + */ + private List formalParameterList(final TokenType endType) { // Prepare to gather parameters. final List parameters = new ArrayList<>(); // Track commas. boolean first = true; - while (type != RPAREN) { + while (type != endType) { // Comma prior to every argument except the first. if (!first) { expect(COMMARIGHT); --- old/test/script/basic/funcconstructor.js.EXPECTED 2013-06-03 14:05:23.937628304 +0530 +++ new/test/script/basic/funcconstructor.js.EXPECTED 2013-06-03 14:05:23.697627114 +0530 @@ -4,7 +4,7 @@ print('anon func'); return x*x; } syntax error? true -SyntaxError: :2:13 Missing close quote +SyntaxError: :1:13 Missing close quote print('hello) ^ done --- /dev/null 2013-06-03 13:46:53.413135868 +0530 +++ new/test/script/basic/JDK-8015345.js 2013-06-03 14:05:24.565631411 +0530 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * JDK-8015345: Function("}),print('test'),({") should throw SyntaxError + * + * @test + * @run + */ + +function checkFunction(code) { + try { + Function(code); + fail("should have thrown SyntaxError for :" + code); + } catch (e) { + if (! (e instanceof SyntaxError)) { + fail("SyntaxError expected, but got " + e); + } + print(e); + } +} + +// invalid body +checkFunction("}),print('test'),({"); + +// invalid param list +checkFunction("x**y", "print('x')"); + +// invalid param identifier +checkFunction("in", "print('hello')"); +//checkFunction("<>", "print('hello')") + +// invalid param list and body +checkFunction("x--y", ")"); + +// check few valid cases as well +var f = Function("x", "return x*x"); +print(f(10)) + +f = Function("x", "y", "return x+y"); +print(f(33, 22)); + +f = Function("x,y", "return x/y"); +print(f(24, 2)); --- /dev/null 2013-06-03 13:46:53.413135868 +0530 +++ new/test/script/basic/JDK-8015345.js.EXPECTED 2013-06-03 14:05:25.185634484 +0530 @@ -0,0 +1,15 @@ +SyntaxError: :1:0 Expected eof but found } +}),print('test'),({ +^ +SyntaxError: :1:2 Expected an operand but found * +x**y + ^ +SyntaxError: :1:0 Expected an operand but found in +in +^ +SyntaxError: :1:3 Expected ; but found y +x--y + ^ +100 +55 +12