From d867ed9a8def5e02602c2aec5e097b04e73e9663 Mon Sep 17 00:00:00 2001 From: Srinivas Dama Date: Mon, 14 Nov 2016 22:33:33 -0800 Subject: [PATCH] 8156615: Catch parameter can be a BindingPattern in ES6 mode Added parser support for catch parameter being a binding pattern Reviewed-by: sundar, hannesw, attila --- .../jdk/nashorn/api/tree/IRTranslator.java | 2 +- .../internal/codegen/AssignSymbols.java | 2 +- .../internal/codegen/CodeGenerator.java | 2 +- .../codegen/LocalVariableTypesCalculator.java | 2 +- .../jdk/nashorn/internal/codegen/Lower.java | 9 + .../jdk/nashorn/internal/ir/CatchNode.java | 54 ++- .../jdk/nashorn/internal/parser/Parser.java | 20 +- .../test/script/basic/es6/destructuring.js | 7 + .../basic/es6/destructuring.js.EXPECTED | 21 + .../nosecurity/treeapi/destructuring_catch.js | 51 +++ .../treeapi/destructuring_catch.js.EXPECTED | 399 ++++++++++++++++++ 11 files changed, 544 insertions(+), 25 deletions(-) create mode 100644 nashorn/test/script/nosecurity/treeapi/destructuring_catch.js create mode 100644 nashorn/test/script/nosecurity/treeapi/destructuring_catch.js.EXPECTED diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java index 974f56c7ccc..041ab612c34 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java @@ -384,7 +384,7 @@ final class IRTranslator extends SimpleNodeVisitor { final List catchTrees = new ArrayList<>(catchNodes.size()); for (final CatchNode catchNode : catchNodes) { catchTrees.add(new CatchTreeImpl(catchNode, - translateIdent(catchNode.getException()), + translateExpr(catchNode.getException()), (BlockTree) translateBlock(catchNode.getBody()), translateExpr(catchNode.getExceptionCondition()))); } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java index fdc828b50b1..3bb44b5aa13 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java @@ -462,7 +462,7 @@ final class AssignSymbols extends SimpleNodeVisitor implements Loggable { @Override public boolean enterCatchNode(final CatchNode catchNode) { - final IdentNode exception = catchNode.getException(); + final IdentNode exception = catchNode.getExceptionIdentifier(); final Block block = lc.getCurrentBlock(); start(catchNode); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java index f04f8959339..c4053bcf1e6 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java @@ -3255,7 +3255,7 @@ final class CodeGenerator extends NodeOperatorVisitor implements Lo return checkEval(callNode.setFunction(markerFunction(callNode.getFunction()))); } + @Override + public boolean enterCatchNode(final CatchNode catchNode) { + Expression exception = catchNode.getException(); + if ((exception != null) && !(exception instanceof IdentNode)) { + throwNotImplementedYet("es6.destructuring", exception); + } + return true; + } + @Override public Node leaveCatchNode(final CatchNode catchNode) { return addStatement(catchNode); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CatchNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CatchNode.java index 80ca3a509c9..9431f3b3acb 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CatchNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CatchNode.java @@ -35,8 +35,8 @@ import jdk.nashorn.internal.ir.visitor.NodeVisitor; public final class CatchNode extends Statement { private static final long serialVersionUID = 1L; - /** Exception identifier. */ - private final IdentNode exception; + /** Exception binding identifier or binding pattern. */ + private final Expression exception; /** Exception condition. */ private final Expression exceptionCondition; @@ -52,21 +52,27 @@ public final class CatchNode extends Statement { * @param lineNumber lineNumber * @param token token * @param finish finish - * @param exception variable name of exception + * @param exception variable name or pattern of exception * @param exceptionCondition exception condition * @param body catch body * @param isSyntheticRethrow true if this node is a synthetically generated rethrow node. */ - public CatchNode(final int lineNumber, final long token, final int finish, final IdentNode exception, + public CatchNode(final int lineNumber, final long token, final int finish, final Expression exception, final Expression exceptionCondition, final Block body, final boolean isSyntheticRethrow) { super(lineNumber, token, finish); - this.exception = exception == null ? null : exception.setIsInitializedHere(); + if (exception instanceof IdentNode) { + this.exception = ((IdentNode) exception).setIsInitializedHere(); + } else if ((exception instanceof LiteralNode.ArrayLiteralNode) || (exception instanceof ObjectNode)) { + this.exception = exception; + } else { + throw new IllegalArgumentException("invalid catch parameter"); + } this.exceptionCondition = exceptionCondition; - this.body = body; + this.body = body; this.isSyntheticRethrow = isSyntheticRethrow; } - private CatchNode(final CatchNode catchNode, final IdentNode exception, final Expression exceptionCondition, + private CatchNode(final CatchNode catchNode, final Expression exception, final Expression exceptionCondition, final Block body, final boolean isSyntheticRethrow) { super(catchNode); this.exception = exception; @@ -83,11 +89,10 @@ public final class CatchNode extends Statement { public Node accept(final NodeVisitor visitor) { if (visitor.enterCatchNode(this)) { return visitor.leaveCatchNode( - setException((IdentNode)exception.accept(visitor)). - setExceptionCondition(exceptionCondition == null ? null : (Expression)exceptionCondition.accept(visitor)). - setBody((Block)body.accept(visitor))); + setException((Expression) exception.accept(visitor)). + setExceptionCondition(exceptionCondition == null ? null : (Expression) exceptionCondition.accept(visitor)). + setBody((Block) body.accept(visitor))); } - return this; } @@ -109,13 +114,24 @@ public final class CatchNode extends Statement { } /** - * Get the identifier representing the exception thrown - * @return the exception identifier + * Get the binding pattern representing the exception thrown + * + * @return the exception binding pattern */ - public IdentNode getException() { + public Expression getException() { return exception; } + /** + * Get the identifier representing the exception thrown + * + * @return the exception identifier + * @throws ClassCastException if exception set is not binding identifier + */ + public IdentNode getExceptionIdentifier() { + return (IdentNode) exception; + } + /** * Get the exception condition for this catch block * @return the exception condition @@ -146,13 +162,19 @@ public final class CatchNode extends Statement { /** * Resets the exception of a catch block - * @param exception new exception + * + * @param exception new exception which can be binding identifier or binding + * pattern * @return new catch node if changed, same otherwise */ - public CatchNode setException(final IdentNode exception) { + public CatchNode setException(final Expression exception) { if (this.exception == exception) { return this; } + /*check if exception is legitimate*/ + if (!((exception instanceof IdentNode) || (exception instanceof LiteralNode.ArrayLiteralNode) || (exception instanceof ObjectNode))) { + throw new IllegalArgumentException("invalid catch parameter"); + } return new CatchNode(this, exception, exceptionCondition, body, isSyntheticRethrow); } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java index c609a0d18c7..fb4fd433b42 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java @@ -2619,13 +2619,23 @@ public class Parser extends AbstractParser implements Loggable { next(); expect(LPAREN); - // FIXME: ES6 catch parameter can be a BindingIdentifier or a BindingPattern - // We need to generalize this here! + // ES6 catch parameter can be a BindingIdentifier or a BindingPattern // http://www.ecma-international.org/ecma-262/6.0/ - final IdentNode exception = getIdent(); + final String contextString = "catch argument"; + final Expression exception = bindingIdentifierOrPattern(contextString); + final boolean isDestructuring = !(exception instanceof IdentNode); + if (isDestructuring) { + verifyDestructuringBindingPattern(exception, new Consumer() { + @Override + public void accept(final IdentNode identNode) { + verifyIdent(identNode, contextString); + } + }); + } else { + // ECMA 12.4.1 strict mode restrictions + verifyStrictIdent((IdentNode) exception, "catch argument"); + } - // ECMA 12.4.1 strict mode restrictions - verifyStrictIdent(exception, "catch argument"); // Nashorn extension: catch clause can have optional // condition. So, a single try can have more than one diff --git a/nashorn/test/script/basic/es6/destructuring.js b/nashorn/test/script/basic/es6/destructuring.js index a7f7a59de04..d2b2a1bd31e 100644 --- a/nashorn/test/script/basic/es6/destructuring.js +++ b/nashorn/test/script/basic/es6/destructuring.js @@ -62,4 +62,11 @@ check("(function({ x }) { return x; })()"); check("(function([x]) { return x; })()"); check("for (var [[x, y, z] = [4, 5, 6]] = [7, 8, 9]; iterCount < 1; ) ;"); check("for ([ arrow = () => {} ] of [[]]) ;"); +check("try { throw null;} catch({}) { }"); +check("try { throw {} } catch ({}) { }"); +check("try { throw [] } catch ([,]) { }"); +check("try { throw { w: [7, undefined, ] }} catch ({ w: [x, y, z] = [4, 5, 6] }) { }"); +check("try { throw { a: 2, b: 3} } catch ({a, b}) { }"); +check("try { throw [null] } catch ([[x]]) { }"); +check("try { throw { w: undefined } } catch ({ w: { x, y, z } = { x: 4, y: 5, z: 6 } }) { }"); diff --git a/nashorn/test/script/basic/es6/destructuring.js.EXPECTED b/nashorn/test/script/basic/es6/destructuring.js.EXPECTED index 65e2eda1dc9..54ecd96a3fa 100644 --- a/nashorn/test/script/basic/es6/destructuring.js.EXPECTED +++ b/nashorn/test/script/basic/es6/destructuring.js.EXPECTED @@ -70,3 +70,24 @@ for (var [[x, y, z] = [4, 5, 6]] = [7, 8, 9]; iterCount < 1; ) ; java.lang.RuntimeException: test/script/basic/es6/destructuring.js#35:6:1:0 ES6 destructuring is not yet implemented for ([ arrow = () => {} ] of [[]]) ; ^ +java.lang.RuntimeException: test/script/basic/es6/destructuring.js#35:6:1:25 ES6 destructuring is not yet implemented +try { throw null;} catch({}) { } + ^ +java.lang.RuntimeException: test/script/basic/es6/destructuring.js#35:6:1:24 ES6 destructuring is not yet implemented +try { throw {} } catch ({}) { } + ^ +java.lang.RuntimeException: test/script/basic/es6/destructuring.js#35:6:1:24 ES6 destructuring is not yet implemented +try { throw [] } catch ([,]) { } + ^ +java.lang.RuntimeException: test/script/basic/es6/destructuring.js#35:6:1:44 ES6 destructuring is not yet implemented +try { throw { w: [7, undefined, ] }} catch ({ w: [x, y, z] = [4, 5, 6] }) { } + ^ +java.lang.RuntimeException: test/script/basic/es6/destructuring.js#35:6:1:35 ES6 destructuring is not yet implemented +try { throw { a: 2, b: 3} } catch ({a, b}) { } + ^ +java.lang.RuntimeException: test/script/basic/es6/destructuring.js#35:6:1:28 ES6 destructuring is not yet implemented +try { throw [null] } catch ([[x]]) { } + ^ +java.lang.RuntimeException: test/script/basic/es6/destructuring.js#35:6:1:38 ES6 destructuring is not yet implemented +try { throw { w: undefined } } catch ({ w: { x, y, z } = { x: 4, y: 5, z: 6 } }) { } + ^ diff --git a/nashorn/test/script/nosecurity/treeapi/destructuring_catch.js b/nashorn/test/script/nosecurity/treeapi/destructuring_catch.js new file mode 100644 index 00000000000..ff399fee444 --- /dev/null +++ b/nashorn/test/script/nosecurity/treeapi/destructuring_catch.js @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016, 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. + */ + +/** + * Tests to check representation of ES6 catch parameter as binding pattern. + * + * @test + * @option -scripting + * @run + */ + +load(__DIR__ + "utils.js") + +var code = <