8156738: Use StackWalker for DynamicLinker.getLinkedCallSiteLocation
authorattila
Wed, 11 May 2016 19:24:29 +0200
changeset 37920 0251d4f16648
parent 37919 3120a4aa6a96
child 37921 4c013cbb5e27
8156738: Use StackWalker for DynamicLinker.getLinkedCallSiteLocation Reviewed-by: hannesw, sundar
nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/DynamicLinker.java
nashorn/test/src/jdk/dynalink/test/LinkedCallSiteLocationTest.java
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/DynamicLinker.java	Wed May 11 11:05:28 2016 +0200
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/DynamicLinker.java	Wed May 11 19:24:29 2016 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -83,6 +83,7 @@
 
 package jdk.dynalink;
 
+import java.lang.StackWalker.StackFrame;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
@@ -172,6 +173,8 @@
     private static final String INITIAL_LINK_METHOD_NAME = "linkCallSite";
     private static final String INVOKE_PACKAGE_PREFIX = "java.lang.invoke.";
 
+    private static final StackWalker stackWalker = StackWalker.getInstance(StackWalker.Option.SHOW_HIDDEN_FRAMES);
+
     private final LinkerServices linkerServices;
     private final GuardedInvocationTransformer prelinkTransformer;
     private final boolean syncOnRelink;
@@ -300,21 +303,16 @@
      *         site is being linked.
      */
     public static StackTraceElement getLinkedCallSiteLocation() {
-        final StackTraceElement[] trace = new Throwable().getStackTrace();
-        for(int i = 0; i < trace.length - 1; ++i) {
-            final StackTraceElement frame = trace[i];
-            // If we found any of our linking entry points on the stack...
-            if(isRelinkFrame(frame) || isInitialLinkFrame(frame)) {
+        return stackWalker.walk(s -> s
+                // Find one of our linking entry points on the stack...
+                .dropWhile(f -> !(isRelinkFrame(f) || isInitialLinkFrame(f)))
+                .skip(1)
                 // ... then look for the first thing calling it that isn't j.l.invoke
-                for (int j = i + 1; j < trace.length; ++j) {
-                    final StackTraceElement frame2 = trace[j];
-                    if (!frame2.getClassName().startsWith(INVOKE_PACKAGE_PREFIX)) {
-                        return frame2;
-                    }
-                }
-            }
-        }
-        return null;
+                .dropWhile(f -> f.getClassName().startsWith(INVOKE_PACKAGE_PREFIX))
+                .findFirst()
+                .map(StackFrame::toStackTraceElement)
+                .orElse(null)
+        );
     }
 
     /**
@@ -326,7 +324,7 @@
      *
      * @return {@code true} if this frame represents {@code MethodHandleNatives.linkCallSite()}.
      */
-    private static boolean isInitialLinkFrame(final StackTraceElement frame) {
+    private static boolean isInitialLinkFrame(final StackFrame frame) {
         return testFrame(frame, INITIAL_LINK_METHOD_NAME, INITIAL_LINK_CLASS_NAME);
     }
 
@@ -339,11 +337,11 @@
      *
      * @return {@code true} if this frame represents {@code DynamicLinker.relink()}.
      */
-    private static boolean isRelinkFrame(final StackTraceElement frame) {
+    private static boolean isRelinkFrame(final StackFrame frame) {
         return testFrame(frame, RELINK_METHOD_NAME, CLASS_NAME);
     }
 
-    private static boolean testFrame(final StackTraceElement frame, final String methodName, final String className) {
+    private static boolean testFrame(final StackFrame frame, final String methodName, final String className) {
         return methodName.equals(frame.getMethodName()) && className.equals(frame.getClassName());
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/src/jdk/dynalink/test/LinkedCallSiteLocationTest.java	Wed May 11 19:24:29 2016 +0200
@@ -0,0 +1,86 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package jdk.dynalink.test;
+
+import static jdk.dynalink.StandardOperation.CALL_METHOD;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import jdk.dynalink.CallSiteDescriptor;
+import jdk.dynalink.DynamicLinker;
+import jdk.dynalink.DynamicLinkerFactory;
+import jdk.dynalink.NamedOperation;
+import jdk.dynalink.linker.GuardingDynamicLinker;
+import jdk.dynalink.support.SimpleRelinkableCallSite;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class LinkedCallSiteLocationTest {
+    @Test
+    public void testLinkedCallSiteLocation() throws Throwable {
+        final StackTraceElement[] lastLinked = new StackTraceElement[1];
+
+        final GuardingDynamicLinker testLinker =
+                (r, s) -> { lastLinked[0] = DynamicLinker.getLinkedCallSiteLocation(); return null; };
+
+        final DynamicLinkerFactory factory = new DynamicLinkerFactory();
+        factory.setPrioritizedLinker(testLinker);
+        final DynamicLinker linker = factory.createLinker();
+        final SimpleRelinkableCallSite callSite = new SimpleRelinkableCallSite(
+                new CallSiteDescriptor(
+                        MethodHandles.lookup(),
+                        new NamedOperation(CALL_METHOD, "foo"),
+                        MethodType.methodType(void.class, Object.class)));
+        linker.link(callSite);
+
+        // Test initial linking
+        callSite.dynamicInvoker().invoke(new TestClass1()); final int l1 = getLineNumber();
+        assertLocation(lastLinked[0], l1);
+
+        // Test relinking
+        callSite.dynamicInvoker().invoke(new TestClass2()); final int l2 = getLineNumber();
+        assertLocation(lastLinked[0], l2);
+    }
+
+    private void assertLocation(final StackTraceElement frame, final int lineNumber) {
+        Assert.assertNotNull(frame);
+        Assert.assertEquals(frame.getLineNumber(), lineNumber);
+        Assert.assertEquals(frame.getClassName(), this.getClass().getName());
+    }
+
+    private static int getLineNumber() {
+        return StackWalker.getInstance().walk(s -> s.skip(1).findFirst().get().getLineNumber().getAsInt());
+    }
+
+    public static class TestClass1 {
+        public void foo() {
+        }
+    }
+
+    public static class TestClass2 {
+        public void foo() {
+        }
+    }
+}