8114838: Anonymous functions escape to surrounding scope when defined under "with" statement
Reviewed-by: attila, hannesw, lagergren
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/samples/javabind.js Tue Jun 30 13:10:37 2015 +0530
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// bind on a Java method
+
+// #javascript "bind" function
+var bind = Function.prototype.bind;
+
+// Java console object
+var console = java.lang.System.console();
+
+// arguments "this" and prompt string of Console.readLine method are bound
+var readName = bind.call(console.readLine, console, "Your name: ");
+
+// Now call it like a function that takes no arguments!
+print("Hello,", readName());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/samples/javaconstructorbind.js Tue Jun 30 13:10:37 2015 +0530
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// bind on a Java constructor
+
+// See Function.prototype.bind:
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
+var bind = Function.prototype.bind;
+
+var URL = Java.type("java.net.URL");
+
+// get the constructor that accepts URL, String parameters.
+// constructor signatures are properties of type object.
+var newURL = URL["(URL, String)"];
+
+// bind "context" URL parameter.
+var TwitterURL = bind.call(newURL, null, new URL('https://www.twitter.com'));
+
+// now you can create context relative URLs using the bound constructor
+print(new TwitterURL("sundararajan_a"));
+
+// read the URL content and print (optional part)
+
+var BufferedReader = Java.type("java.io.BufferedReader");
+var InputStreamReader = Java.type("java.io.InputStreamReader");
+
+// function to retrieve text content of the given URL
+function readTextFromURL(url) {
+ var str = '';
+ var u = new URL(url);
+ var reader = new BufferedReader(
+ new InputStreamReader(u.openStream()));
+ try {
+ reader.lines().forEach(function(x) str += x);
+ return str;
+ } finally {
+ reader.close();
+ }
+}
+
+print(readTextFromURL(new TwitterURL("sundararajan_a")));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/samples/mapwith.js Tue Jun 30 13:10:37 2015 +0530
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Using a Java map with Javascript "with" statement
+
+var map = new java.util.HashMap();
+map.put("foo", 34);
+map.put("bar", "hello");
+
+var obj = {
+ __noSuchProperty__: function(name) {
+ return map.get(name);
+ }
+};
+
+with(obj) {
+ print(foo);
+ print(bar);
+}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTreeImpl.java Mon Jun 29 10:42:57 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTreeImpl.java Tue Jun 30 13:10:37 2015 +0530
@@ -40,7 +40,7 @@
final BlockTree body) {
super(node);
funcNode = node;
- assert !funcNode.isDeclared() : "function expression expected";
+ assert !funcNode.isDeclared() || funcNode.isAnonymous() : "function expression expected";
final FunctionNode.Kind kind = node.getKind();
if (node.isAnonymous() || kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) {
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java Mon Jun 29 10:42:57 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java Tue Jun 30 13:10:37 2015 +0530
@@ -224,7 +224,7 @@
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
- assert !functionNode.isDeclared() : "should not reach here for function declaration";
+ assert !functionNode.isDeclared() || functionNode.isAnonymous() : "should not reach here for function declaration";
final List<? extends ExpressionTree> paramTrees
= translateExprs(functionNode.getParameters());
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Mon Jun 29 10:42:57 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java Tue Jun 30 13:10:37 2015 +0530
@@ -551,9 +551,7 @@
private void defineVarIdent(final VarNode varNode) {
final IdentNode ident = varNode.getName();
final int flags;
- if (varNode.isAnonymousFunctionDeclaration()) {
- flags = IS_INTERNAL;
- } else if (!varNode.isBlockScoped() && lc.getCurrentFunction().isProgram()) {
+ if (!varNode.isBlockScoped() && lc.getCurrentFunction().isProgram()) {
flags = IS_SCOPE;
} else {
flags = 0;
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Mon Jun 29 10:42:57 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Tue Jun 30 13:10:37 2015 +0530
@@ -312,10 +312,6 @@
assert !varNode.isBlockScoped(); //TODO: we must handle these too, but we currently don't
final Expression init = varNode.getInit();
- if (varNode.isAnonymousFunctionDeclaration()) {
- // We ain't moving anonymous function declarations.
- return super.enterVarNode(varNode);
- }
// Move a declaration-only var statement to the top of the outermost function.
getCurrentFunctionState().varStatements.add(varNode.setInit(null));
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/VarNode.java Mon Jun 29 10:42:57 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/VarNode.java Tue Jun 30 13:10:37 2015 +0530
@@ -290,12 +290,4 @@
public boolean isFunctionDeclaration() {
return init instanceof FunctionNode && ((FunctionNode)init).isDeclared();
}
-
- /**
- * Returns true if this is an anonymous function declaration.
- * @return true if this is an anonymous function declaration.
- */
- public boolean isAnonymousFunctionDeclaration() {
- return isFunctionDeclaration() && ((FunctionNode)init).isAnonymous();
- }
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Mon Jun 29 10:42:57 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Tue Jun 30 13:10:37 2015 +0530
@@ -2765,6 +2765,11 @@
functionBody);
if (isStatement) {
+ if (isAnonymous) {
+ appendStatement(new ExpressionStatement(functionLine, functionToken, finish, function));
+ return function;
+ }
+
// mark ES6 block functions as lexically scoped
final int varFlags = (topLevel || !useBlockScope()) ? 0 : VarNode.IS_LET;
final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, function, varFlags);
--- a/nashorn/test/script/basic/JDK-8075454.js Mon Jun 29 10:42:57 2015 +0200
+++ b/nashorn/test/script/basic/JDK-8075454.js Tue Jun 30 13:10:37 2015 +0530
@@ -40,4 +40,4 @@
EOF, print);
-Assert.assertNull(ast.sourceElements[0].name);
+Assert.assertNull(ast.sourceElements[0].expression.name);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8114838.js Tue Jun 30 13:10:37 2015 +0530
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015 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-8114838: Anonymous functions escape to surrounding scope when defined under "with" statement
+ *
+ * @test
+ * @run
+ */
+
+// do *not* introduce new lines! The next line should be 32
+with({}) { function () {} }
+if (typeof this["L:32"] != 'undefined') {
+ throw new Error("anonymous name spills into global scope");
+}
+
+var func = eval("function() {}");
+if (typeof func != 'function') {
+ throw new Error("eval of anonymous function does not work!");
+}
+
+var ScriptEngineManager = Java.type("javax.script.ScriptEngineManager");
+var engine = new ScriptEngineManager().getEngineByName("nashorn");
+var func2 = engine.eval("function() {}");
+if (typeof func2 != 'function') {
+ throw new Error("eval of anonymous function does not work from script engine!");
+}