8156738: Use StackWalker for DynamicLinker.getLinkedCallSiteLocation

Reviewed-by: hannesw, sundar
This commit is contained in:
Attila Szegedi 2016-05-11 19:24:29 +02:00
parent 1de3636e0f
commit 860e71a134
2 changed files with 102 additions and 18 deletions

View File

@ -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 @@ public final class DynamicLinker {
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 @@ public final class DynamicLinker {
* 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 @@ public final class DynamicLinker {
*
* @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 @@ public final class DynamicLinker {
*
* @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());
}
}

View File

@ -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() {
}
}
}