8024846: keep separate internal arguments variable
authorattila
Mon, 16 Sep 2013 14:44:20 +0200
changeset 19898 58c39862aa3f
parent 19897 4235a28bb5e8
child 19899 3afa46cd7e01
child 20209 92c2787c959a
8024846: keep separate internal arguments variable Reviewed-by: lagergren, sundar
nashorn/src/jdk/nashorn/internal/codegen/Attr.java
nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java
nashorn/src/jdk/nashorn/internal/parser/Parser.java
nashorn/test/script/basic/JDK-8024846.js
--- a/nashorn/src/jdk/nashorn/internal/codegen/Attr.java	Mon Sep 16 15:08:36 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/Attr.java	Mon Sep 16 14:44:20 2013 +0200
@@ -26,6 +26,7 @@
 package jdk.nashorn.internal.codegen;
 
 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
+import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
 import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
@@ -172,7 +173,9 @@
             initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL);
             if (functionNode.needsArguments()) {
                 initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | IS_ALWAYS_DEFINED);
-                addLocalDef(ARGUMENTS.symbolName());
+                final String argumentsName = ARGUMENTS_VAR.symbolName();
+                newType(defineSymbol(body, argumentsName, IS_VAR | IS_ALWAYS_DEFINED), Type.typeFor(ARGUMENTS_VAR.type()));
+                addLocalDef(argumentsName);
             }
         }
 
@@ -491,30 +494,29 @@
             objectifySymbols(body);
         }
 
+        List<VarNode> syntheticInitializers = null;
+
         if (body.getFlag(Block.NEEDS_SELF_SYMBOL)) {
-            final IdentNode callee = compilerConstant(CALLEE);
-            VarNode selfInit =
-                new VarNode(
-                    newFunctionNode.getLineNumber(),
-                    newFunctionNode.getToken(),
-                    newFunctionNode.getFinish(),
-                    newFunctionNode.getIdent(),
-                    callee);
-
-            LOG.info("Accepting self symbol init ", selfInit, " for ", newFunctionNode.getName());
+            syntheticInitializers = new ArrayList<>(2);
+            LOG.info("Accepting self symbol init for ", newFunctionNode.getName());
+            // "var fn = :callee"
+            syntheticInitializers.add(createSyntheticInitializer(newFunctionNode.getIdent(), CALLEE, newFunctionNode));
+        }
 
-            final List<Statement> newStatements = new ArrayList<>();
-            assert callee.getSymbol() != null && callee.getSymbol().hasSlot();
-
-            final IdentNode name       = selfInit.getName();
-            final Symbol    nameSymbol = body.getExistingSymbol(name.getName());
+        if(newFunctionNode.needsArguments()) {
+            if(syntheticInitializers == null) {
+                syntheticInitializers = new ArrayList<>(1);
+            }
+            // "var arguments = :arguments"
+            syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()),
+                    ARGUMENTS, newFunctionNode));
+        }
 
-            assert nameSymbol != null;
-
-            selfInit = selfInit.setName((IdentNode)name.setSymbol(lc, nameSymbol));
-
-            newStatements.add(selfInit);
-            newStatements.addAll(body.getStatements());
+        if(syntheticInitializers != null) {
+            final List<Statement> stmts = body.getStatements();
+            final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size());
+            newStatements.addAll(syntheticInitializers);
+            newStatements.addAll(stmts);
             newFunctionNode = newFunctionNode.setBody(lc, body.setStatements(lc, newStatements));
         }
 
@@ -533,6 +535,28 @@
         return newFunctionNode;
     }
 
+    /**
+     * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically
+     * used to create assignmnent of {@code :callee} to the function name symbol in self-referential function
+     * expressions as well as for assignment of {@code :arguments} to {@code arguments}.
+     *
+     * @param name the ident node identifying the variable to initialize
+     * @param initConstant the compiler constant it is initialized to
+     * @param fn the function node the assignment is for
+     * @return a var node with the appropriate assignment
+     */
+    private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) {
+        final IdentNode init = compilerConstant(initConstant);
+        assert init.getSymbol() != null && init.getSymbol().hasSlot();
+
+        VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init);
+
+        final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName());
+        assert nameSymbol != null;
+
+        return synthVar.setName((IdentNode)name.setSymbol(lc, nameSymbol));
+    }
+
     @Override
     public Node leaveCONVERT(final UnaryNode unaryNode) {
         assert false : "There should be no convert operators in IR during Attribution";
@@ -974,15 +998,18 @@
     }
 
     private IdentNode compilerConstant(CompilerConstants cc) {
-        final FunctionNode functionNode = lc.getCurrentFunction();
-        return (IdentNode)
-            new IdentNode(
-                functionNode.getToken(),
-                functionNode.getFinish(),
-                cc.symbolName()).
-                setSymbol(
-                    lc,
-                    functionNode.compilerConstant(cc));
+        return (IdentNode)createImplicitIdentifier(cc.symbolName()).setSymbol(lc, lc.getCurrentFunction().compilerConstant(cc));
+    }
+
+    /**
+     * Creates an ident node for an implicit identifier within the function (one not declared in the script source
+     * code). These identifiers are defined with function's token and finish.
+     * @param name the name of the identifier
+     * @return an ident node representing the implicit identifier.
+     */
+    private IdentNode createImplicitIdentifier(final String name) {
+        final FunctionNode fn = lc.getCurrentFunction();
+        return new IdentNode(fn.getToken(), fn.getFinish(), name);
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Mon Sep 16 15:08:36 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Mon Sep 16 14:44:20 2013 +0200
@@ -102,8 +102,12 @@
     /** the varargs variable when necessary */
     VARARGS(":varargs", Object[].class),
 
-    /** the arguments vector when necessary and the slot */
-    ARGUMENTS("arguments", ScriptObject.class, 2),
+    /** the arguments variable (visible to function body). Initially set to ARGUMENTS, but can be reassigned by code in
+     * the function body.*/
+    ARGUMENTS_VAR("arguments", Object.class),
+
+    /** the internal arguments object, when necessary (not visible to scripts, can't be reassigned). */
+    ARGUMENTS(":arguments", ScriptObject.class),
 
     /** prefix for iterators for for (x in ...) */
     ITERATOR_PREFIX(":i", Iterator.class),
--- a/nashorn/src/jdk/nashorn/internal/parser/Parser.java	Mon Sep 16 15:08:36 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/parser/Parser.java	Mon Sep 16 14:44:20 2013 +0200
@@ -25,7 +25,6 @@
 
 package jdk.nashorn.internal.parser;
 
-import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
 import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
 import static jdk.nashorn.internal.codegen.CompilerConstants.FUNCTION_PREFIX;
 import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT;
@@ -115,6 +114,8 @@
  * Builds the IR.
  */
 public class Parser extends AbstractParser {
+    private static final String ARGUMENTS_NAME = CompilerConstants.ARGUMENTS_VAR.symbolName();
+
     /** Current script environment. */
     private final ScriptEnvironment env;
 
@@ -511,13 +512,19 @@
      * @param ident Referenced property.
      */
     private void detectSpecialProperty(final IdentNode ident) {
-        final String name = ident.getName();
-
-        if (ARGUMENTS.symbolName().equals(name)) {
+        if (isArguments(ident)) {
             lc.setFlag(lc.getCurrentFunction(), FunctionNode.USES_ARGUMENTS);
         }
     }
 
+    private static boolean isArguments(final String name) {
+        return ARGUMENTS_NAME.equals(name);
+    }
+
+    private static boolean isArguments(final IdentNode ident) {
+        return isArguments(ident.getName());
+    }
+
     /**
      * Tells whether a IdentNode can be used as L-value of an assignment
      *
@@ -2449,7 +2456,7 @@
             } else if (env._function_statement == ScriptEnvironment.FunctionStatementBehavior.WARNING) {
                 warning(JSErrorType.SYNTAX_ERROR, AbstractParser.message("no.func.decl.here.warn"), functionToken);
             }
-            if (ARGUMENTS.symbolName().equals(name.getName())) {
+            if (isArguments(name)) {
                 lc.setFlag(lc.getCurrentFunction(), FunctionNode.DEFINES_ARGUMENTS);
             }
         }
@@ -2468,7 +2475,7 @@
                 final IdentNode parameter = parameters.get(i);
                 String parameterName = parameter.getName();
 
-                if (ARGUMENTS.symbolName().equals(parameterName)) {
+                if (isArguments(parameterName)) {
                     functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS);
                 }
 
@@ -2486,7 +2493,7 @@
                 parametersSet.add(parameterName);
             }
         } else if (arity == 1) {
-            if (ARGUMENTS.symbolName().equals(parameters.get(0).getName())) {
+            if (isArguments(parameters.get(0))) {
                 functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS);
             }
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8024846.js	Mon Sep 16 14:44:20 2013 +0200
@@ -0,0 +1,37 @@
+/*
+ * 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-8024846: keep separate internal arguments variable
+ *
+ * @test
+ */
+
+function f() { print(arguments); }
+
+function func(obj) {
+    var arguments = obj;
+    for (var i in arguments) {
+    }
+    return obj;
+}