8114838: Anonymous functions escape to surrounding scope when defined under "with" statement
authorsundar
Tue, 30 Jun 2015 13:10:37 +0530
changeset 31485 be1acaee8e9e
parent 31484 3e7c40dd5bac
child 31488 4ddc3a4d5629
8114838: Anonymous functions escape to surrounding scope when defined under "with" statement Reviewed-by: attila, hannesw, lagergren
nashorn/samples/javabind.js
nashorn/samples/javaconstructorbind.js
nashorn/samples/mapwith.js
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTreeImpl.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/VarNode.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java
nashorn/test/script/basic/JDK-8075454.js
nashorn/test/script/basic/JDK-8114838.js
--- /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!");
+}