8019822: Duplicate name and signature in finally block
authorattila
Fri, 12 Jul 2013 11:58:42 +0200
changeset 18872 bfb736c5aa43
parent 18871 a27c6a5b999c
child 18873 7907640d3be5
child 18874 8ba96bd382d3
8019822: Duplicate name and signature in finally block Reviewed-by: jlaskey, sundar
nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
nashorn/test/script/basic/JDK-8019822.js
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Fri Jul 12 15:27:16 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Fri Jul 12 11:58:42 2013 +0200
@@ -55,10 +55,12 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.EnumSet;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 import java.util.TreeMap;
 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
@@ -184,6 +186,8 @@
     /** From what size should we use spill instead of fields for JavaScript objects? */
     private static final int OBJECT_SPILL_THRESHOLD = 300;
 
+    private final Set<String> emittedMethods = new HashSet<>();
+
     /**
      * Constructor.
      *
@@ -490,6 +494,9 @@
 
     @Override
     public boolean enterBlock(final Block block) {
+        if(lc.isFunctionBody() && emittedMethods.contains(lc.getCurrentFunction().getName())) {
+            return false;
+        }
         method.label(block.getEntryLabel());
         initLocals(block);
 
@@ -1007,17 +1014,28 @@
             return false;
         }
 
-        LOG.info("=== BEGIN ", functionNode.getName());
-
-        assert functionNode.getCompileUnit() != null : "no compile unit for " + functionNode.getName() + " " + Debug.id(functionNode);
-        unit = lc.pushCompileUnit(functionNode.getCompileUnit());
-        assert lc.hasCompileUnits();
-
-        method = lc.pushMethodEmitter(unit.getClassEmitter().method(functionNode));
-        // new method - reset last line number
-        lastLineNumber = -1;
-        // Mark end for variable tables.
-        method.begin();
+        final String fnName = functionNode.getName();
+        // NOTE: we only emit the method for a function with the given name once. We can have multiple functions with
+        // the same name as a result of inlining finally blocks. However, in the future -- with type specialization,
+        // notably -- we might need to check for both name *and* signature. Of course, even that might not be
+        // sufficient; the function might have a code dependency on the type of the variables in its enclosing scopes,
+        // and the type of such a variable can be different in catch and finally blocks. So, in the future we will have
+        // to decide to either generate a unique method for each inlined copy of the function, maybe figure out its
+        // exact type closure and deduplicate based on that, or just decide that functions in finally blocks aren't
+        // worth it, and generate one method with most generic type closure.
+        if(!emittedMethods.contains(fnName)) {
+            LOG.info("=== BEGIN ", fnName);
+
+            assert functionNode.getCompileUnit() != null : "no compile unit for " + fnName + " " + Debug.id(functionNode);
+            unit = lc.pushCompileUnit(functionNode.getCompileUnit());
+            assert lc.hasCompileUnits();
+
+            method = lc.pushMethodEmitter(unit.getClassEmitter().method(functionNode));
+            // new method - reset last line number
+            lastLineNumber = -1;
+            // Mark end for variable tables.
+            method.begin();
+        }
 
         return true;
     }
@@ -1025,13 +1043,14 @@
     @Override
     public Node leaveFunctionNode(final FunctionNode functionNode) {
         try {
-            method.end(); // wrap up this method
-            unit   = lc.popCompileUnit(functionNode.getCompileUnit());
-            method = lc.popMethodEmitter(method);
-            LOG.info("=== END ", functionNode.getName());
+            if(emittedMethods.add(functionNode.getName())) {
+                method.end(); // wrap up this method
+                unit   = lc.popCompileUnit(functionNode.getCompileUnit());
+                method = lc.popMethodEmitter(method);
+                LOG.info("=== END ", functionNode.getName());
+            }
 
             final FunctionNode newFunctionNode = functionNode.setState(lc, CompilationState.EMITTED);
-
             newFunctionObject(newFunctionNode, functionNode);
             return newFunctionNode;
         } catch (final Throwable t) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8019822.js	Fri Jul 12 11:58:42 2013 +0200
@@ -0,0 +1,29 @@
+/*
+ * 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-8019822: Duplicate name/signature for a function in finally block
+ *
+ * @test
+ */
+try { function (x) /x/ } finally { (function(id) { return id }); }