On Jul 30, 2013, at 7:45 AM, Peter Levart wrote: > I think there are various techniques to use new API when running on JDK8 and > still use old API when running on JDK6 or 7. One way is to have 2 classes > implementing the same interface or extending basic class where one is > compiled with JDK6/7 and the other with JDK8. During runtime the "correct" > implementation is choosen, but code only depends on the interface/basic class…
Now there's an interesting idea I hadn't considered. It sure wouldn't be easy, though. It would make it harder for projects to fix bugs related to this when discovered, and still slow down adoption of Java 8. > > There are other tricks. > > But I have an idea. What if Reflection.getCallerClass(int) is restored in > JDK7 and JDK8 without a @CallerSensitive annotation on it. In order to > prevent inadvertent internal JDK usage, the method could throw exception when > called from any internal JDK class… I think this MUST happen in Java 7, because adding a public API isn't an option there. As for Java 8, I'm almost done with StackTraceFrame and associated C/C++ code, and it includes both an @CallerSensitive getCallerClass() method and a getCallerClass(int) method. While I would prefer your option for Java 8 over what we have now (obviously), the public StackTraceFrame API is a better approach by the mere fact of being public (and universally available on all JVMs). Nick > > Regards, Peter > > > On 07/30/2013 02:32 PM, Nick Williams wrote: >> "For caller-sensitive methods, the approach taken with new >> Reflection.getCallerClass() is the right one, I think. There's no need to >> support a fragile API when caller-sensitivity is concerned, so the lack of >> "int" parameter, combined with annotation for marking such methods is >> correct approach, I think." >> >> Not when the code running on Java 8 was compiled on Java 6 or 7 and thus >> can't be annotated @CallerSensitive. In these cases, use of any new public >> API must use reflection (sudo code: If Java 8, use new public >> caller-sensitive API), so it needs code that it can pass a number into. >> >> Nick >> >> On Jul 30, 2013, at 7:17 AM, Peter Levart wrote: >> >>> >>> On 07/27/2013 09:01 PM, Nick Williams wrote: >>>> All, >>>> >>>> In the last two months, there have been a number of discussions >>>> surrounding stack traces, Classes on the stack trace, and caller classes >>>> [1], [2], [3]. These are all related discussions and the solution to them >>>> is equally related, so I wanted to consolidate it all into this one >>>> discussion where I hope we can finalize on a solution and get it >>>> implemented for Java 8. >>>> >>>> In a nut shell, here are the underlying needs that I have seen expressed >>>> through many, many messages: >>>> >>>> - Some code needs to get the Class of the caller of the current method, >>>> skipping any reflection methods. >>>> - Some code needs to get the Class of the caller /n/ stack frames before >>>> the current method, skipping any reflection methods. >>>> - Some code needs to get the current stack trace, populated with Classes, >>>> Executables, file names, line numbers, and native flags instead of the >>>> String class names and String method names in StackTraceElement. This >>>> /should/ include any reflection methods, just like StackTraceElement[]s. >>>> - Some code needs to get the stack trace from when a Throwable was >>>> created, populated with Classes, Executables, file names, line numbers, >>>> and native flags instead of the String class names and String method names >>>> in StackTraceElement. This /should/ include any reflection methods, just >>>> like StackTraceElement[]s. >>>> - There needs to be a reflection way to achieve all of this since some >>>> libraries (e.g., Log4j) need to be compiled against Java 6 but run on 7 >>>> and 8 (and thus can't use @CallerSensitive). >>>> >>>> I believe the solutions to these needs are all related. Importantly, I >>>> think it is very important that action be taken in Java 8 due to the >>>> changes made to sun.reflect.Reflection#getCallerClass(...). While we all >>>> understand that relying on private sun.* APIs is not safe, the fact is >>>> that many people have relied on sun.reflect.Reflection#getCallerClass(...) >>>> due to the fact that there is simply no other way to do this in the >>>> standard API. This includes Log4j 2, Logback, SLF4j, and Groovy, some >>>> features of which will stop working correctly in Java 7 >= u25. >>> >>> Hi, >>> >>> The needs described above may seem related, but from what I see in this >>> commit: >>> >>> http://hg.openjdk.java.net/jdk8/tl/jdk/rev/da6addef956e >>> >>> my observations are as following (please comment if I missed or >>> misunderstood anything): >>> >>> sun.reflect.Reflection.getCallerClass(int) is/was used internally in JDK >>> more or less for different purposes than outside the JDK. Inside it was >>> used basically for implementing security-sensitive checks like >>> optimizations for public methods which can avoid calling SecurityManager >>> API wen called from withing JDK classes. It was used for security-unrelated >>> purposes too, like for example Class.forName(String) or >>> ResourceBundle.getBundle(String). All internal JDK uses do share one common >>> thing though: it is very important that the right direct caller of a >>> caller-sensitive method is established, since any failure to do so can have >>> devastating effect on security or correctness. >>> >>> The API taking an "int" to count the frames between the top of the >>> call-stack to the indirect caller was convenient, but too fragile to >>> support such important use cases. Every time some code was refactored, >>> there was danger that some call-frame was inadvertently inserted or >>> removed. So I think it was decided to "cripple" the API to only support >>> obtaining the immediate caller of the method making call to the >>> Reflection.getCallerClass() and all uses modified accordingly to make the >>> internal JDK code more robust to refactorings. >>> >>> And there's also MethodHandles which are early-bound. Meaning that the >>> caller is established and bound when the MethodHandle instance is >>> looked-up. The "lookupClass" which is associated with the Lookup object and >>> used in permission checks when obtaining MHs is also used as the bound >>> caller class when the MH is invoked. Now there's a method: >>> >>> java.lang.invoke.MethodHandles.Lookup { >>> public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?> >>> requestedLookupClass) >>> >>> that returns a Lookup object which reports a different "lookupClass" and >>> has capabilities to lookup MHs which are combined from capabilities of the >>> original Lookup object and new lookupClass (tipicaly less, never more). >>> Most of such Lookup objects are prevented from looking-up MHs for >>> caller-sensitive methods, since they could be used to "pose" as a caller >>> that is not the one having obtained the MH and therefore gain access to >>> restricted resource, for example: >>> >>> MethodHandle mh = MethodHandles.lookup().in(Object.class).lookupXXX(....) >>> >>> ...such mh could be used to pose as being called from withing Object if >>> allowed to be obtained for caller-sensitive methods. So here comes >>> @CallerSensitive annotation to mark such methods and prevent such lookups >>> (before that - in JDK7, all internal caller-sensitive methods were >>> hand-maintained in a list). >>> >>> So this is, what I think, the background and rationale for changing the API. >>> >>> For outside JDK use, I think there are two main needs, which are actually >>> distinct: >>> >>> a) the caller-sensitive methods >>> b) anything else that is not caller-sensitive, but wants to fiddle with the >>> call-stack >>> >>> For caller-sensitive methods, the approach taken with new >>> Reflection.getCallerClass() is the right one, I think. There's no need to >>> support a fragile API when caller-sensitivity is concerned, so the lack of >>> "int" parameter, combined with annotation for marking such methods is >>> correct approach, I think. The refactorings to support this change in JDK >>> show that this API is adequate. The "surface" public API methods must >>> capture the caller class and pass it down the internal API where it can be >>> used. >>> >>> So what would give Groovy or other language runtimes headaches when all >>> there was was a parameter-less getCallerClass() API? Aren't the >>> intermediate frames inserted by those runtimes controlled by the runtimes? >>> Couldn't the "surface" runtime-inserted methods capture the caller and pass >>> it down? I guess the problem is supporting calling the caller-sensitive >>> methods like Class.forName(String) and such which don't have the overloaded >>> variant taking caller Class or ClassLoader as an argument... >>> >>> John Rose suggested to "capture" the caller in the "surface" method and >>> bind it with a MethodHandle and then pass such MH down the runtime API and >>> finally call that method via MH. For that to work, two problems would have >>> to be resolved first: >>> >>> 1) the runtime-inserted "surface" method would have to be annotated with >>> @CallerSensitive so that illegal "posers" could be prevented >>> 2) this "surface" method would have to be given permission to "pose as" the >>> caller class when looking-up the MethodHandle of the target >>> caller-sensitive method. >>> >>> The 1st part is not a problem I think, but the 2nd part is a problem. What >>> makes the runtime-inserted "surface" method so special that it can be >>> allowed to "pose" as its caller? >>> >>> Now that is the question for mlvm-dev mailing list: Isn't preventing almost >>> all Lookup objects obtained by Lookup.in(RequestedLookupClass.class) from >>> obtaining MHs of @CallerSensitive methods too restrictive? >>> >>> Currently classes are only allowed to "pose as" it's nest-mate classes - >>> the classes that share the same outermost enclosing class, since the check >>> to look-up @CallerSensitive methods is based on the ability to look-up >>> PRIVATE members. So the ability to "pose as" another class when binding >>> caller already exists even for @CallerSensitive methods, just the >>> restriction is too conservative, isn't it? >>> >>> Perhaps a class that is visible from the calling class could be allowed to >>> look-up MHs of @CallerSensitive methods and "pose" as the calling class, >>> bearing all other security checks for combined abilities have passed. For >>> example, why wouldn't class A be allowed to "pose as" class B if they are >>> loaded by the same ClassLoader or if class B is loaded by a ClassLoader >>> that directly or indirectly delegates to the ClassLoader of class A? >>> >>> These are my thoughts about caller-sensitivity and why I think it requires >>> special restricted API. Anything else that needs to examine the whole >>> call-stack is a separate need that is not infected by the strict >>> constraints of caller-sensitivity and for that purpose an API like the one >>> presented below (StackTraceFrame) is a good starting-point, maybe it just >>> doesn't need the static getCallerFrame() method which suggests that it's >>> use is for implementing caller-sensitive methods. >>> >>> Regards, Peter >>> >>>> I would point out that this could all easily be solved simply by adding a >>>> getElementClass() method to StackTraceElement, but there was strong >>>> opposition to this, largely due to serialization issues. Since that is >>>> apparently not an option, I propose the following API, based on the >>>> various discussions in the last two months, StackTraceElement, and the API >>>> that .NET provides to achieve the same needs as listed above: >>>> >>>> CallerSensitive.java: >>>> package java.lang; >>>> >>>> /** Previously private API, now public */ >>>> public @interface CallerSensitive { >>>> ... >>>> } >>>> >>>> StackTraceFrame.java: >>>> package java.lang; >>>> >>>> import java.util.Objects. >>>> >>>> public final class StackTraceFrame { >>>> private final Class<?> declaringClass; >>>> private final Executable executable; >>>> private final String fileName; >>>> private final int lineNumber; >>>> >>>> public StackTraceFrame(Class<?> declaringClass, Executable executable, >>>> String fileName, int lineNumber) { >>>> this.declaringClass = Objects.requireNonNull(declaringClass, >>>> "Declaring class is null"); >>>> this.executable = Objects.requireNonNull(executable, "Executable >>>> is null"); >>>> this.fileName = fileName; >>>> this.lineNumber = lineNumber; >>>> } >>>> >>>> public Class<?> getDeclaringClass() { >>>> return this.declaringClass; >>>> } >>>> >>>> public Executable getExecutable() { >>>> return this.executable; >>>> } >>>> >>>> public String getFileName() { >>>> return this.fileName; >>>> } >>>> >>>> public int getLineNumber() { >>>> return this.lineNumber; >>>> } >>>> >>>> public boolean isNative() { >>>> return this.lineNumber == -2; >>>> } >>>> >>>> public String toString() { /* Same as StackTraceElement */ } >>>> public boolean equals() { /* Ditto */ } >>>> public int hashCode() { /* Ditto */ } >>>> >>>> /** Uses @CallerSensitive */ >>>> public static native StackTraceFrame getCallerFrame(); >>>> >>>> /** Works like Java < 7u25 sun.reflect.Reflection#getCallerClass() */ >>>> public static native StackTraceFrame getCallerFrame(int skipFrames); >>>> >>>> public static native StackTraceFrame[] getCurrentStackTrace(); >>>> } >>>> >>>> Throwable.java: >>>> package java.lang; >>>> >>>> ... >>>> >>>> public class Throwable { >>>> ... >>>> public synchronized Throwable fillInStackTraceFrames() { ... } >>>> >>>> private native Throwable fillInStackTraceFrames(int dummy); >>>> >>>> public StackTraceFrame[] getStackTraceFrames() { >>>> return this.getOurStackTraceFrames().clone(); >>>> } >>>> >>>> private synchronized StackTraceFrame[] getOurStackTraceFrames() { ... } >>>> ... >>>> } >>>> >>>> Furthermore, I propose that we restore the behavior of >>>> sun.reflect.Reflection#getCallerClass(int) /just for Java 7/ since the >>>> proposed above solution cannot be added to Java 7. >>>> >>>> I would love if we could quickly coalesce around this solution or a >>>> derivative thereof so that it can be implemented before Feature Complete. >>>> The absence of any replacement or alternative for >>>> sun.reflect.Reflection#getCallerClass(int) will be a serious issue in Java >>>> 8 that will cause hardships for many projects. >>>> >>>> [1] >>>> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-June/018049.html >>>> [2] >>>> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-June/018349.html, >>>> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-July/019098.html >>>> [3] >>>> http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-July/018855.html >>> >> >
_______________________________________________ mlvm-dev mailing list mlvm-dev@openjdk.java.net http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev