RFR (M): 7198429: need checked categorization of caller-sensitive methods in the JDK
Peter Levart
peter.levart at gmail.com
Thu Mar 28 08:56:38 UTC 2013
On 03/28/2013 04:34 AM, Mandy Chung wrote:
> Hi Peter,
>
> On 3/21/2013 11:47 AM, Peter Levart wrote:
>> ...
>>
>> I have seen a utility that uses it to establish the context (package
>> and ClassLoader) of where to start searching for resources for GUI
>> components construction. And a utility that wraps
>> Class.forName(String) - specifying Class.forName(name, true,
>> Reflection.getCallerClass(2).getClassLoader()) instead to use the
>> caller's class loader to load the class...
>>
>
> This seems to me that this case is more of visibility issue rather
> than caller-sensitivity.
Hi Mandy,
The package name and class name of the caller is "caller-sensitivity"
and the ClassLoader of the caller is "visibility". This is a mixed-case.
>
>> Usually it is only necessary to get the immediate caller.
>>
>>>
>>>> Is there a reason to not "unofficially" support also classes with
>>>> @CS annotated methods and which are not loaded by bootstrap or
>>>> extension class-loader ?
>>>
>>> We expect that most of the caller-sensitive cases are in the JDK and
>>> should be rare for non-system libraries to have caller-sensitive
>>> code. It'd be good to understand the use cases and the requirements
>>> to determine the appropriate support for it.
>>
>> Ok, but why limit? Is it an optimization?
>
> Just out of scope and there will be much more consideration to expand
> the scope to cover the user caller-sensitive methods (e.g. SE API and
> implementable by different JDK vendors).
>
> The scope of JEP 176 is target for the system code where it has all
> permissions that we expect majority of the caller-sensitive cases are in.
I understand that. It's just that there is already some code in the wild
that does the unforgivable and uses Reflection.getCallerClass() and is
not loaded by system or extension ClassLoader... I guess it will have to
be "fixed" to be able to run on JDK8. And the fix will be to pack that
part of code into an extension bundle and put it into the ext
ClassLoader's dir...
>
>>
>> Is it for security? For example: one might think that creating an
>> anonymous class and holding a j.l.Class reference somewhere safe is
>> enough for safety, so the class itself could have public API. Now if
>> an untrusted callback object is passed to the code of such class and
>> it is invoked from within that code, the anonymous class can get
>> "revealed" to the untrusted code which can use it's public API to
>> invoke it. Are there any other implications of allowing non-system
>> code to get the caller?
>
> It'd help if you can have a test case showing this to help the
> discussion. However, restricting Reflection.getCallerClass be called
> by the system code is no difference than defining a module-private
> class. sun.reflect.* classes are private to the JDK implementation
> that are not supported. If there were modules, applications would not
> have been able to call it since sun.reflect would not be exported.
The code I'm talking about is being deployed using a kind of modules
system (NetBeans container) and it seems JDK classes are treated
specially in this modules system, since the code works without
specifying special dependencies (whole JDK is treated as one big module
that exports all it's classes).
Here's the relevant code to illustrate the use-case (see the top 3
factory methods that enable concise in-line use):
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import sun.reflect.Reflection;
import java.io.Serializable;
import java.util.Arrays;
/**
* A Resource Bundle Locator + Resource Key + Optional Arguments
*/
public final class RBKey implements Serializable {
private static final long serialVersionUID = 1L;
public static RBKey of() {
return new RBKey(Reflection.getCallerClass(2));
}
public static RBKey of(String subKey) {
return new RBKey(Reflection.getCallerClass(2)).sub(subKey);
}
public static RBKey of(String subKey, Object... arguments) {
return new RBKey(Reflection.getCallerClass(2)).sub(subKey,
arguments);
}
public static String bundleName(Class<?> clazz) {
String className = namedClass(clazz).getName();
int lastDot = className.lastIndexOf('.');
return lastDot >= 0 ? className.substring(0, lastDot + 1) +
"Bundle" : "Bundle";
}
public static String keyPrefix(Class<?> clazz) {
String className = namedClass(clazz).getName();
int lastDot = className.lastIndexOf('.');
return lastDot >= 0 ? className.substring(lastDot + 1) : className;
}
private static Class namedClass(Class clazz) {
while (clazz.isAnonymousClass() && clazz.getEnclosingClass() !=
null)
clazz = clazz.getEnclosingClass();
return clazz;
}
private final Class<?> clazz;
private final String bundleName, key;
private final Object[] arguments;
public RBKey(Class<?> clazz) {
this(clazz, bundleName(clazz), keyPrefix(clazz), (Object[]) null);
}
public RBKey(Class<?> clazz, String bundleName, String keyPrefix,
Object... arguments) {
this.clazz = Preconditions.checkNotNull(clazz, "'clazz' should
be non-null");
this.bundleName = Preconditions.checkNotNull(bundleName,
"'bundleName' should be non-null");
this.key = keyPrefix;
this.arguments = arguments == null || arguments.length == 0 ?
null : arguments;
}
public Class<?> getClazz() {
return clazz;
}
public String getBundleName() {
return bundleName;
}
public String getKey() {
return key;
}
public Object[] getArguments() {
return arguments == null ? null : arguments.clone();
}
Object[] getArgumentsNoClone() {
return arguments;
}
public RBKey sub(String subKey) {
if (subKey == null) {
throw new NullPointerException("'subKey' should not be null");
}
return new RBKey(clazz, bundleName, (key == null ? subKey : key
+ "." + subKey), arguments);
}
public RBKey sub(String subKey, Object... arguments) {
if (subKey == null) {
throw new NullPointerException("'subKey' should not be null");
}
return new RBKey(clazz, bundleName, (key == null ? subKey : key
+ "." + subKey), arguments);
}
public RBKey args(Object... arguments) {
return new RBKey(clazz, bundleName, key, arguments);
}
@Override
public int hashCode() {
return clazz.getClassLoader().hashCode() + 31 * (
bundleName.hashCode() + 31 * (
(key == null ? 0 : key.hashCode()) + 31 *
Arrays.hashCode(arguments)
)
);
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (obj == this) return true;
if (obj.getClass() != RBKey.class) return false;
RBKey other = (RBKey) obj;
return this.clazz.getClassLoader() ==
other.clazz.getClassLoader() &&
Objects.equal(this.key, other.key) &&
Arrays.equals(this.arguments, other.arguments);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(bundleName);
if (key != null)
sb.append('#').append(key);
if (arguments != null)
sb.append(Arrays.toString(arguments));
return sb.toString();
}
}
>
> I would recommend to file a RFE and describe the use cases that
> require an API to get the caller class or find caller's context so
> that we can address it properly.
>
> With this fix, the only way to call Reflection.getCallerClass must be
> system code (in bootclasspath or extension library). As a
> workaround, can you put it on the bootclasspath?
More likely on the extension ClassLoader's classpath. I guess I could
with some packaging acrobatics.
Regards, Peter
>
> Mandy
More information about the core-libs-dev
mailing list