提交 bbcc62de 编写于 作者: R rriggs

8164908: ReflectionFactory support for IIOP and custom serialization

Reviewed-by: chegar, psandoz
上级 7279024e
...@@ -680,7 +680,9 @@ public class MethodHandles { ...@@ -680,7 +680,9 @@ public class MethodHandles {
// disallow lookup more restricted packages // disallow lookup more restricted packages
if (allowedModes == ALL_MODES && lookupClass.getClassLoader() == null) { if (allowedModes == ALL_MODES && lookupClass.getClassLoader() == null) {
if (name.startsWith("java.") || if (name.startsWith("java.") ||
(name.startsWith("sun.") && !name.startsWith("sun.invoke."))) { (name.startsWith("sun.")
&& !name.startsWith("sun.invoke.")
&& !name.equals("sun.reflect.ReflectionFactory"))) {
throw newIllegalArgumentException("illegal lookupClass: " + lookupClass); throw newIllegalArgumentException("illegal lookupClass: " + lookupClass);
} }
} }
......
...@@ -25,16 +25,28 @@ ...@@ -25,16 +25,28 @@
package sun.reflect; package sun.reflect;
import java.io.Externalizable;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.OptionalDataException;
import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Executable; import java.lang.reflect.Executable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.security.AccessController; import java.security.AccessController;
import java.security.Permission; import java.security.Permission;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
import java.util.Objects;
import sun.reflect.misc.ReflectUtil; import sun.reflect.misc.ReflectUtil;
/** <P> The master factory for all reflective objects, both those in /** <P> The master factory for all reflective objects, both those in
java.lang.reflect (Fields, Methods, Constructors) as well as their java.lang.reflect (Fields, Methods, Constructors) as well as their
delegates (FieldAccessors, MethodAccessors, ConstructorAccessors). delegates (FieldAccessors, MethodAccessors, ConstructorAccessors).
...@@ -56,6 +68,9 @@ public class ReflectionFactory { ...@@ -56,6 +68,9 @@ public class ReflectionFactory {
// Provides access to package-private mechanisms in java.lang.reflect // Provides access to package-private mechanisms in java.lang.reflect
private static volatile LangReflectAccess langReflectAccess; private static volatile LangReflectAccess langReflectAccess;
/* Method for static class initializer <clinit>, or null */
private static volatile Method hasStaticInitializerMethod;
// //
// "Inflation" mechanism. Loading bytecodes to implement // "Inflation" mechanism. Loading bytecodes to implement
// Method.invoke() and Constructor.newInstance() currently costs // Method.invoke() and Constructor.newInstance() currently costs
...@@ -73,8 +88,7 @@ public class ReflectionFactory { ...@@ -73,8 +88,7 @@ public class ReflectionFactory {
private static boolean noInflation = false; private static boolean noInflation = false;
private static int inflationThreshold = 15; private static int inflationThreshold = 15;
private ReflectionFactory() { private ReflectionFactory() {}
}
/** /**
* A convenience class for acquiring the capability to instantiate * A convenience class for acquiring the capability to instantiate
...@@ -328,6 +342,14 @@ public class ReflectionFactory { ...@@ -328,6 +342,14 @@ public class ReflectionFactory {
// //
// //
/**
* Returns an accessible constructor capable of creating instances
* of the given class, initialized by the given constructor.
*
* @param classToInstantiate the class to instantiate
* @param constructorToCall the constructor to call
* @return an accessible constructor
*/
public Constructor<?> newConstructorForSerialization public Constructor<?> newConstructorForSerialization
(Class<?> classToInstantiate, Constructor<?> constructorToCall) (Class<?> classToInstantiate, Constructor<?> constructorToCall)
{ {
...@@ -335,6 +357,42 @@ public class ReflectionFactory { ...@@ -335,6 +357,42 @@ public class ReflectionFactory {
if (constructorToCall.getDeclaringClass() == classToInstantiate) { if (constructorToCall.getDeclaringClass() == classToInstantiate) {
return constructorToCall; return constructorToCall;
} }
return generateConstructor(classToInstantiate, constructorToCall);
}
/**
* Returns an accessible no-arg constructor for a class.
* The no-arg constructor is found searching the class and its supertypes.
*
* @param cl the class to instantiate
* @return a no-arg constructor for the class or {@code null} if
* the class or supertypes do not have a suitable no-arg constructor
*/
public final Constructor<?> newConstructorForSerialization(Class<?> cl) {
Class<?> initCl = cl;
while (Serializable.class.isAssignableFrom(initCl)) {
if ((initCl = initCl.getSuperclass()) == null) {
return null;
}
}
Constructor<?> constructorToCall;
try {
constructorToCall = initCl.getDeclaredConstructor();
int mods = constructorToCall.getModifiers();
if ((mods & Modifier.PRIVATE) != 0 ||
((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 &&
!packageEquals(cl, initCl))) {
return null;
}
} catch (NoSuchMethodException ex) {
return null;
}
return generateConstructor(cl, constructorToCall);
}
private final Constructor<?> generateConstructor(Class<?> classToInstantiate,
Constructor<?> constructorToCall) {
ConstructorAccessor acc = new MethodAccessorGenerator(). ConstructorAccessor acc = new MethodAccessorGenerator().
generateSerializationConstructor(classToInstantiate, generateSerializationConstructor(classToInstantiate,
...@@ -355,9 +413,222 @@ public class ReflectionFactory { ...@@ -355,9 +413,222 @@ public class ReflectionFactory {
langReflectAccess(). langReflectAccess().
getConstructorParameterAnnotations(constructorToCall)); getConstructorParameterAnnotations(constructorToCall));
setConstructorAccessor(c, acc); setConstructorAccessor(c, acc);
c.setAccessible(true);
return c; return c;
} }
/**
* Returns an accessible no-arg constructor for an externalizable class to be
* initialized using a public no-argument constructor.
*
* @param cl the class to instantiate
* @return A no-arg constructor for the class; returns {@code null} if
* the class does not implement {@link java.io.Externalizable}
*/
public final Constructor<?> newConstructorForExternalization(Class<?> cl) {
if (!Externalizable.class.isAssignableFrom(cl)) {
return null;
}
try {
Constructor<?> cons = cl.getConstructor();
cons.setAccessible(true);
return cons;
} catch (NoSuchMethodException ex) {
return null;
}
}
/**
* Returns a direct MethodHandle for the {@code readObject} method on
* a Serializable class.
* The first argument of {@link MethodHandle#invoke} is the serializable
* object and the second argument is the {@code ObjectInputStream} passed to
* {@code readObject}.
*
* @param cl a Serializable class
* @return a direct MethodHandle for the {@code readObject} method of the class or
* {@code null} if the class does not have a {@code readObject} method
*/
public final MethodHandle readObjectForSerialization(Class<?> cl) {
return findReadWriteObjectForSerialization(cl, "readObject", ObjectInputStream.class);
}
/**
* Returns a direct MethodHandle for the {@code readObjectNoData} method on
* a Serializable class.
* The first argument of {@link MethodHandle#invoke} is the serializable
* object and the second argument is the {@code ObjectInputStream} passed to
* {@code readObjectNoData}.
*
* @param cl a Serializable class
* @return a direct MethodHandle for the {@code readObjectNoData} method
* of the class or {@code null} if the class does not have a
* {@code readObjectNoData} method
*/
public final MethodHandle readObjectNoDataForSerialization(Class<?> cl) {
return findReadWriteObjectForSerialization(cl, "readObjectNoData", ObjectInputStream.class);
}
/**
* Returns a direct MethodHandle for the {@code writeObject} method on
* a Serializable class.
* The first argument of {@link MethodHandle#invoke} is the serializable
* object and the second argument is the {@code ObjectOutputStream} passed to
* {@code writeObject}.
*
* @param cl a Serializable class
* @return a direct MethodHandle for the {@code writeObject} method of the class or
* {@code null} if the class does not have a {@code writeObject} method
*/
public final MethodHandle writeObjectForSerialization(Class<?> cl) {
return findReadWriteObjectForSerialization(cl, "writeObject", ObjectOutputStream.class);
}
private final MethodHandle findReadWriteObjectForSerialization(Class<?> cl,
String methodName,
Class<?> streamClass) {
if (!Serializable.class.isAssignableFrom(cl)) {
return null;
}
try {
Method meth = cl.getDeclaredMethod(methodName, streamClass);
int mods = meth.getModifiers();
if (meth.getReturnType() != Void.TYPE ||
Modifier.isStatic(mods) ||
!Modifier.isPrivate(mods)) {
return null;
}
meth.setAccessible(true);
return MethodHandles.lookup().unreflect(meth);
} catch (NoSuchMethodException ex) {
return null;
} catch (IllegalAccessException ex1) {
throw new InternalError("Error", ex1);
}
}
/**
* Returns a direct MethodHandle for the {@code readResolve} method on
* a serializable class.
* The single argument of {@link MethodHandle#invoke} is the serializable
* object.
*
* @param cl the Serializable class
* @return a direct MethodHandle for the {@code readResolve} method of the class or
* {@code null} if the class does not have a {@code readResolve} method
*/
public final MethodHandle readResolveForSerialization(Class<?> cl) {
return getReplaceResolveForSerialization(cl, "readResolve");
}
/**
* Returns a direct MethodHandle for the {@code writeReplace} method on
* a serializable class.
* The single argument of {@link MethodHandle#invoke} is the serializable
* object.
*
* @param cl the Serializable class
* @return a direct MethodHandle for the {@code writeReplace} method of the class or
* {@code null} if the class does not have a {@code writeReplace} method
*/
public final MethodHandle writeReplaceForSerialization(Class<?> cl) {
return getReplaceResolveForSerialization(cl, "writeReplace");
}
/**
* Returns a direct MethodHandle for the {@code writeReplace} method on
* a serializable class.
* The single argument of {@link MethodHandle#invoke} is the serializable
* object.
*
* @param cl the Serializable class
* @return a direct MethodHandle for the {@code writeReplace} method of the class or
* {@code null} if the class does not have a {@code writeReplace} method
*/
private MethodHandle getReplaceResolveForSerialization(Class<?> cl,
String methodName) {
if (!Serializable.class.isAssignableFrom(cl)) {
return null;
}
Class<?> defCl = cl;
while (defCl != null) {
try {
Method m = defCl.getDeclaredMethod(methodName);
if (m.getReturnType() != Object.class) {
return null;
}
int mods = m.getModifiers();
if (Modifier.isStatic(mods) | Modifier.isAbstract(mods)) {
return null;
} else if (Modifier.isPublic(mods) | Modifier.isProtected(mods)) {
// fall through
} else if (Modifier.isPrivate(mods) && (cl != defCl)) {
return null;
} else if (!packageEquals(cl, defCl)) {
return null;
}
try {
// Normal return
m.setAccessible(true);
return MethodHandles.lookup().unreflect(m);
} catch (IllegalAccessException ex0) {
// setAccessible should prevent IAE
throw new InternalError("Error", ex0);
}
} catch (NoSuchMethodException ex) {
defCl = defCl.getSuperclass();
}
}
return null;
}
/**
* Returns true if the class has a static initializer.
* The presence of a static initializer is used to compute the serialVersionUID.
* @param cl a serializable classLook
* @return {@code true} if the class has a static initializer,
* otherwise {@code false}
*/
public final boolean hasStaticInitializerForSerialization(Class<?> cl) {
Method m = hasStaticInitializerMethod;
if (m == null) {
try {
m = ObjectStreamClass.class.getDeclaredMethod("hasStaticInitializer",
new Class<?>[]{Class.class});
m.setAccessible(true);
hasStaticInitializerMethod = m;
} catch (NoSuchMethodException ex) {
throw new InternalError("No such method hasStaticInitializer on "
+ ObjectStreamClass.class, ex);
}
}
try {
return (Boolean) m.invoke(null, cl);
} catch (InvocationTargetException | IllegalAccessException ex) {
throw new InternalError("Exception invoking hasStaticInitializer", ex);
}
}
/**
* Returns a new OptionalDataException with {@code eof} set to {@code true}
* or {@code false}.
* @param bool the value of {@code eof} in the created OptionalDataException
* @return a new OptionalDataException
*/
public final OptionalDataException newOptionalDataExceptionForSerialization(boolean bool) {
try {
Constructor<OptionalDataException> boolCtor =
OptionalDataException.class.getDeclaredConstructor(Boolean.TYPE);
boolCtor.setAccessible(true);
return boolCtor.newInstance(bool);
} catch (NoSuchMethodException | InstantiationException|
IllegalAccessException|InvocationTargetException ex) {
throw new InternalError("unable to create OptionalDataException", ex);
}
}
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// //
// Internals only below this point // Internals only below this point
...@@ -421,4 +692,17 @@ public class ReflectionFactory { ...@@ -421,4 +692,17 @@ public class ReflectionFactory {
} }
return langReflectAccess; return langReflectAccess;
} }
/**
* Returns true if classes are defined in the classloader and same package, false
* otherwise.
* @param cl1 a class
* @param cl2 another class
* @returns true if the two classes are in the same classloader and package
*/
private static boolean packageEquals(Class<?> cl1, Class<?> cl2) {
return cl1.getClassLoader() == cl2.getClassLoader() &&
Objects.equals(cl1.getPackage(), cl2.getPackage());
}
} }
/*
* 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.
*
* 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.
*/
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import sun.reflect.ReflectionFactory;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;
import org.testng.TestNG;
/*
* @test
* @bug 8137058 8164908 8168980
* @run testng ReflectionFactoryTest
* @run testng/othervm/policy=security.policy ReflectionFactoryTest
* @summary Basic test for the unsupported ReflectionFactory
*/
public class ReflectionFactoryTest {
// Initialized by init()
static ReflectionFactory factory;
@DataProvider(name = "ClassConstructors")
static Object[][] classConstructors() {
return new Object[][] {
{Object.class},
{Foo.class},
{Bar.class},
};
}
@BeforeClass
static void init() {
factory = ReflectionFactory.getReflectionFactory();
}
/**
* Test that the correct Constructor is selected and run.
* @param type type of object to create
* @throws NoSuchMethodException - error
* @throws InstantiationException - error
* @throws IllegalAccessException - error
* @throws InvocationTargetException - error
*/
@Test(dataProvider="ClassConstructors")
static void testConstructor(Class<?> type)
throws NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException
{
@SuppressWarnings("unchecked")
Constructor<?> c = factory.newConstructorForSerialization(type);
Object o = c.newInstance();
Assert.assertEquals(o.getClass(), type, "Instance is wrong type");
if (o instanceof Foo) {
Foo foo = (Foo)o;
foo.check();
}
}
@DataProvider(name = "NonSerialConstructors")
static Object[][] constructors() throws NoSuchMethodException {
return new Object[][] {
{Foo.class, Object.class.getDeclaredConstructor()},
{Foo.class, Foo.class.getDeclaredConstructor()},
{Baz.class, Object.class.getDeclaredConstructor()},
{Baz.class, Foo.class.getDeclaredConstructor()},
{Baz.class, Baz.class.getDeclaredConstructor()}
};
}
/**
* Tests that the given Constructor, in the hierarchy, is run.
*/
@Test(dataProvider="NonSerialConstructors")
static void testNonSerializableConstructor(Class<?> cl,
Constructor<?> constructorToCall)
throws ReflectiveOperationException
{
@SuppressWarnings("unchecked")
Constructor<?> c = factory.newConstructorForSerialization(cl,
constructorToCall);
Object o = c.newInstance();
Assert.assertEquals(o.getClass(), cl, "Instance is wrong type");
int expectedFoo = 0;
int expectedBaz = 0;
if (constructorToCall.getName().equals("ReflectionFactoryTest$Foo")) {
expectedFoo = 1;
} else if (constructorToCall.getName().equals("ReflectionFactoryTest$Baz")) {
expectedFoo = 1;
expectedBaz = 4;
}
Assert.assertEquals(((Foo)o).foo(), expectedFoo);
if (o instanceof Baz) {
Assert.assertEquals(((Baz)o).baz(), expectedBaz);
}
}
static class Foo {
private int foo;
public Foo() {
this.foo = 1;
}
public String toString() {
return "foo: " + foo;
}
public void check() {
int expectedFoo = 1;
Assert.assertEquals(foo, expectedFoo, "foo() constructor not run");
}
public int foo() { return foo; }
}
static class Bar extends Foo implements Serializable {
private static final long serialVersionUID = 3L;
private int bar;
public Bar() {
this.bar = 1;
}
public String toString() {
return super.toString() + ", bar: " + bar;
}
public void check() {
super.check();
int expectedBar = 0;
Assert.assertEquals(bar, expectedBar, "bar() constructor not run");
}
}
static class Baz extends Foo {
private static final long serialVersionUID = 4L;
private final int baz;
public Baz() { this.baz = 4; }
public int baz() { return baz; }
}
/**
* Test newConstructorForExternalization returns the constructor and it can be called.
* @throws NoSuchMethodException - error
* @throws InstantiationException - error
* @throws IllegalAccessException - error
* @throws InvocationTargetException - error
*/
@Test
static void newConstructorForExternalization()
throws NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException {
Constructor<?> cons = factory.newConstructorForExternalization(Ext.class);
Ext ext = (Ext)cons.newInstance();
Assert.assertEquals(ext.ext, 1, "Constructor not run");
}
static class Ext implements Externalizable {
private static final long serialVersionUID = 1L;
int ext;
public Ext() {
ext = 1;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {}
}
@Test
static void testReadWriteObjectForSerialization() throws Throwable {
MethodHandle readObjectMethod = factory.readObjectForSerialization(Ser.class);
Assert.assertNotNull(readObjectMethod, "readObjectMethod not found");
MethodHandle readObjectNoDataMethod = factory.readObjectNoDataForSerialization(Ser.class);
Assert.assertNotNull(readObjectNoDataMethod, "readObjectNoDataMethod not found");
MethodHandle writeObjectMethod = factory.writeObjectForSerialization(Ser.class);
Assert.assertNotNull(writeObjectMethod, "writeObjectMethod not found");
MethodHandle readResolveMethod = factory.readResolveForSerialization(Ser.class);
Assert.assertNotNull(readResolveMethod, "readResolveMethod not found");
MethodHandle writeReplaceMethod = factory.writeReplaceForSerialization(Ser.class);
Assert.assertNotNull(writeReplaceMethod, "writeReplaceMethod not found");
byte[] data = null;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
Ser ser = new Ser();
writeReplaceMethod.invoke(ser);
Assert.assertTrue(ser.writeReplaceCalled, "writeReplace not called");
Assert.assertFalse(ser.writeObjectCalled, "writeObject should not have been called");
writeObjectMethod.invoke(ser, oos);
Assert.assertTrue(ser.writeReplaceCalled, "writeReplace should have been called");
Assert.assertTrue(ser.writeObjectCalled, "writeObject not called");
oos.flush();
data = baos.toByteArray();
}
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bais)) {
Ser ser2 = new Ser();
readObjectMethod.invoke(ser2, ois);
Assert.assertTrue(ser2.readObjectCalled, "readObject not called");
Assert.assertFalse(ser2.readObjectNoDataCalled, "readObjectNoData should not be called");
Assert.assertFalse(ser2.readResolveCalled, "readResolve should not be called");
readObjectNoDataMethod.invoke(ser2, ois);
Assert.assertTrue(ser2.readObjectCalled, "readObject should have been called");
Assert.assertTrue(ser2.readObjectNoDataCalled, "readObjectNoData not called");
Assert.assertFalse(ser2.readResolveCalled, "readResolve should not be called");
readResolveMethod.invoke(ser2);
Assert.assertTrue(ser2.readObjectCalled, "readObject should have been called");
Assert.assertTrue(ser2.readObjectNoDataCalled, "readObjectNoData not called");
Assert.assertTrue(ser2.readResolveCalled, "readResolve not called");
}
}
@Test
static void hasStaticInitializer() {
boolean actual = factory.hasStaticInitializerForSerialization(Ser.class);
Assert.assertTrue(actual, "hasStaticInitializerForSerialization is wrong");
}
static class Ser implements Serializable {
private static final long serialVersionUID = 2L;
static {
// Define a static class initialization method
}
boolean readObjectCalled = false;
boolean readObjectNoDataCalled = false;
boolean writeObjectCalled = false;
boolean readResolveCalled = false;
boolean writeReplaceCalled = false;
public Ser() {}
private void readObject(ObjectInputStream ois) throws IOException {
Assert.assertFalse(writeObjectCalled, "readObject called too many times");
readObjectCalled = ois.readBoolean();
}
private void readObjectNoData(ObjectInputStream ois) throws IOException {
Assert.assertFalse(readObjectNoDataCalled, "readObjectNoData called too many times");
readObjectNoDataCalled = true;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
Assert.assertFalse(writeObjectCalled, "writeObject called too many times");
writeObjectCalled = true;
oos.writeBoolean(writeObjectCalled);
}
private Object writeReplace() {
Assert.assertFalse(writeReplaceCalled, "writeReplace called too many times");
writeReplaceCalled = true;
return this;
}
private Object readResolve() {
Assert.assertFalse(readResolveCalled, "readResolve called too many times");
readResolveCalled = true;
return this;
}
}
/**
* Test the constructor of OptionalDataExceptions.
*/
@Test
static void newOptionalDataException() {
OptionalDataException ode = factory.newOptionalDataExceptionForSerialization(true);
Assert.assertTrue(ode.eof, "eof wrong");
ode = factory.newOptionalDataExceptionForSerialization(false);
Assert.assertFalse(ode.eof, "eof wrong");
}
// Main can be used to run the tests from the command line with only testng.jar.
@SuppressWarnings("raw_types")
@Test(enabled = false)
public static void main(String[] args) {
Class<?>[] testclass = {ReflectionFactoryTest.class};
TestNG testng = new TestNG();
testng.setTestClasses(testclass);
testng.run();
}
}
// Individual Permissions for ReflectionFactoryTest
grant {
// Permissions needed to run the test
permission java.util.PropertyPermission "*", "read";
permission java.io.FilePermission "<<ALL FILES>>", "read,write,delete,execute";
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
permission java.lang.RuntimePermission "accessDeclaredMembers";
permission java.lang.RuntimePermission "accessClassInPackage.sun.reflect";
permission java.lang.RuntimePermission "reflectionFactoryAccess";
};
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册