feat: improve JavaMembers.java add cache for java lookup

Signed-off-by: qxo <49526356@qq.com>
This commit is contained in:
qxo 2024-09-16 22:34:27 +08:00
Родитель f8a0a82fa8
Коммит c18dea32f7
2 изменённых файлов: 601 добавлений и 222 удалений

Просмотреть файл

@ -347,10 +347,28 @@ public class Context implements Closeable {
* Internationalization API implementation (see https://tc39.github.io/ecma402) can be activated
* using this feature.
*
* @since 1.7 Release 15
* @since 1.7 Release 16
*/
public static final int FEATURE_INTL_402 = 22;
/**
* Configure whether JavaMembers lazy init off.
*
* <p>default is lazy init.
*
* @since 1.7 Release 16
*/
public static final int FEATURE_JAVAMEMBERS_LAZY_INIT_OFF = 22;
/**
* Configure whether JavaMembers reflect cache off.
*
* <p>default is cache on.
*
* @since 1.7 Release 15
*/
public static final int FEATURE_JAVAMEMBERS_REFLECT_CACHE_OFF = 23;
public static final String languageVersionProperty = "language version";
public static final String errorReporterProperty = "error reporter";

Просмотреть файл

@ -20,10 +20,14 @@ import java.security.AllPermission;
import java.security.Permission;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Mike Shaver
@ -37,11 +41,18 @@ class JavaMembers {
private static final Permission allPermission = new AllPermission();
private final boolean includePrivate;
private final ClassReflectBean cfCache;
private final Scriptable javaMemberScope;
private final boolean lazyInit;
private final boolean reflectCacheOn;
JavaMembers(Scriptable scope, Class<?> cl) {
this(scope, cl, false);
}
JavaMembers(Scriptable scope, Class<?> cl, boolean includeProtected) {
this.javaMemberScope = scope;
try (Context cx = ContextFactory.getGlobal().enterContext()) {
ClassShutter shutter = cx.getClassShutter();
if (shutter != null && !shutter.visibleToScripts(cl.getName())) {
@ -50,8 +61,10 @@ class JavaMembers {
this.members = new HashMap<>();
this.staticMembers = new HashMap<>();
this.cl = cl;
boolean includePrivate = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS);
reflect(cx, scope, includeProtected, includePrivate);
includePrivate = cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS);
lazyInit = !cx.hasFeature(Context.FEATURE_JAVAMEMBERS_LAZY_INIT_OFF);
reflectCacheOn = !cx.hasFeature(Context.FEATURE_JAVAMEMBERS_REFLECT_CACHE_OFF);
cfCache = reflect(cx, scope, includeProtected, includePrivate);
}
}
@ -69,21 +82,15 @@ class JavaMembers {
}
boolean has(String name, boolean isStatic) {
Map<String, Object> ht = isStatic ? staticMembers : members;
Object obj = ht.get(name);
if (obj != null) {
if (cfCache.has(name, isStatic)) {
return true;
}
return findExplicitFunction(name, isStatic) != null;
}
Object get(Scriptable scope, String name, Object javaObject, boolean isStatic) {
Map<String, Object> ht = isStatic ? staticMembers : members;
Object member = ht.get(name);
if (!isStatic && member == null) {
// Try to get static member from instance (LC3)
member = staticMembers.get(name);
}
Context cx = Context.getContext();
Object member = getMember2(cx, scope, name, isStatic);
if (member == null) {
member =
this.getExplicitFunction(
@ -94,7 +101,6 @@ class JavaMembers {
if (member instanceof Scriptable) {
return member;
}
Context cx = Context.getContext();
Object rval;
Class<?> type;
try {
@ -116,6 +122,257 @@ class JavaMembers {
return cx.getWrapFactory().wrap(cx, scope, rval, type);
}
private Object getMember2(Context cx, Scriptable scope, String name, boolean isStatic) {
Object member = getMember(cx, scope, name, isStatic);
if (member == null && !isStatic) {
// Try to get static member from instance (LC3)
member = getMember(cx, scope, name, true);
if (member == null) {
return null;
}
}
return member == Scriptable.NOT_FOUND ? null : member;
}
private final Object getMember(
final Context cx, final Scriptable scope, final String name, final boolean isStatic) {
final Map<String, Object> ht = isStatic ? staticMembers : members;
Object member = ht.get(name);
if (lazyInit && member == null) {
final Object m1 = initFieldAndMethod(cx, name, ht, isStatic);
Map<String, String> props =
isStatic ? cfCache.staticBeanProperties : cfCache.instBeanProperties;
final String nameComponent = props.get(name);
if (nameComponent != null) {
member = initBeanProperty(cx, name, nameComponent, ht, isStatic);
if (member == null) {
member = m1;
}
} else {
member = m1;
}
if (member != null) {
ht.put(name, member);
}
}
return member;
}
private static Object[] createBeanProperties(Iterable<Method> methods) {
final Map<String, String> cache1 = new HashMap<String, String>();
final Map<String, String> cache2 = new HashMap<String, String>();
// Now, For each member, make "bean" properties.
for (Method m : methods) {
String name = m.getName();
// Is this a getter?
boolean memberIsGetMethod = name.startsWith("get");
boolean memberIsSetMethod = name.startsWith("set");
boolean memberIsIsMethod = name.startsWith("is");
if (memberIsGetMethod || memberIsIsMethod || memberIsSetMethod) {
// Double check name component.
String nameComponent = name.substring(memberIsIsMethod ? 2 : 3);
if (nameComponent.length() == 0) continue;
// Make the bean property name.
String beanPropertyName = nameComponent;
char ch0 = nameComponent.charAt(0);
if (Character.isUpperCase(ch0)) {
if (nameComponent.length() == 1) {
beanPropertyName = nameComponent.toLowerCase();
} else {
char ch1 = nameComponent.charAt(1);
if (!Character.isUpperCase(ch1)) {
beanPropertyName =
Character.toLowerCase(ch0) + nameComponent.substring(1);
}
}
}
if (Modifier.isStatic(m.getModifiers())) {
cache1.put(beanPropertyName, nameComponent);
} else {
cache2.put(beanPropertyName, nameComponent);
}
}
}
return new Object[] {cache1, cache2};
}
private BeanProperty initBeanProperty(
final Context cx,
final String beanPropertyName,
String nameComponent,
Map<String, Object> ht,
boolean isStatic) {
Object v = ht.get(beanPropertyName);
if (v != null) {
// A private field shouldn't mask a public getter/setter
if (!includePrivate
|| !(v instanceof Member)
|| !Modifier.isPrivate(((Member) v).getModifiers())) {
return null;
}
}
// Find the getter method, or if there is none, the is-
// method.
MemberBox getter = null;
getter = findGetter(isStatic, ht, "get", nameComponent);
// If there was no valid getter, check for an is- method.
if (getter == null) {
getter = findGetter(isStatic, ht, "is", nameComponent);
}
// setter
MemberBox setter = null;
NativeJavaMethod setters = null;
String setterName = "set".concat(nameComponent);
// Is this value a method?
Object member = ht.get(setterName);
if (member == null && lazyInit) {
member = initFieldAndMethod(cx, setterName, ht, isStatic);
}
if (member instanceof NativeJavaMethod) {
NativeJavaMethod njmSet = (NativeJavaMethod) member;
if (getter != null) {
// We have a getter. Now, do we have a matching
// setter?
Class<?> type = getter.method().getReturnType();
setter = extractSetMethod(type, njmSet.methods, isStatic);
} else {
// No getter, find any set method
setter = extractSetMethod(njmSet.methods, isStatic);
}
if (njmSet.methods.length > 1) {
setters = njmSet;
}
}
// Make the property.
BeanProperty bp = new BeanProperty(getter, setter, setters);
return bp;
}
protected NativeJavaMethod toNativeJavaMethod(Context cx, Scriptable scope, Object value) {
MemberBox[] methodBoxes;
if (value instanceof Method) {
methodBoxes = new MemberBox[1];
methodBoxes[0] = new MemberBox((Method) value);
} else {
ArrayList<Object> overloadedMethods = (ArrayList<Object>) value;
int N = overloadedMethods.size();
if (N < 2) Kit.codeBug();
methodBoxes = new MemberBox[N];
for (int i = 0; i != N; ++i) {
Method method = (Method) overloadedMethods.get(i);
methodBoxes[i] = new MemberBox(method);
}
}
NativeJavaMethod fun = new NativeJavaMethod(methodBoxes);
if (scope != null) {
ScriptRuntime.setFunctionProtoAndParent(fun, cx, scope, false);
}
return fun;
}
protected void initField(final Context cx, final Scriptable scope, final Field field) {
String name = field.getName();
int mods = field.getModifiers();
try {
boolean isStatic = Modifier.isStatic(mods);
Map<String, Object> ht = isStatic ? staticMembers : members;
Object member = ht.get(name);
if (member == null) {
ht.put(name, field);
} else if (member instanceof NativeJavaMethod) {
final FieldAndMethods fam =
initFieldAndMethods(scope, field, name, isStatic, member);
ht.put(name, fam);
} else if (member instanceof Field) {
Field oldField = (Field) member;
// If this newly reflected field shadows an inherited field,
// then replace it. Otherwise, since access to the field
// would be ambiguous from Java, no field should be
// reflected.
// For now, the first field found wins, unless another field
// explicitly shadows it.
if (oldField.getDeclaringClass().isAssignableFrom(field.getDeclaringClass())) {
ht.put(name, field);
}
} else {
// "unknown member type"
Kit.codeBug();
}
} catch (SecurityException e) {
// skip this field
Context.reportWarning(
"Could not access field "
+ name
+ " of class "
+ cl.getName()
+ " due to lack of privileges.");
}
}
private Object initFieldAndMethod(
final Context cx,
final String name,
final Map<String, Object> ht,
final boolean isStatic) {
Object member;
member = isStatic ? cfCache.getStaticField(name) : cfCache.getInstField(name);
final Map<String, Object> mbers = cfCache.getMembers(isStatic);
final Object value = mbers.get(name);
NativeJavaMethod jm = value == null ? null : toNativeJavaMethod(cx, javaMemberScope, value);
if (jm != null) {
if (member != null) {
Field fld = (Field) member;
member =
initFieldAndMethods(
javaMemberScope,
fld,
fld.getName(),
Modifier.isStatic(fld.getModifiers()),
jm);
} else {
member = jm;
}
}
if (member != null) {
ht.put(name, member);
}
return member;
}
private FieldAndMethods initFieldAndMethods(
Scriptable scope, Field field, String name, boolean isStatic, Object member) {
NativeJavaMethod method = (NativeJavaMethod) member;
FieldAndMethods fam = new FieldAndMethods(scope, method.methods, field);
Map<String, FieldAndMethods> fmht = isStatic ? staticFieldAndMethods : fieldAndMethods;
if (fmht == null) {
fmht = new HashMap<String, FieldAndMethods>();
if (isStatic) {
staticFieldAndMethods = fmht;
} else {
fieldAndMethods = fmht;
}
}
fmht.put(name, fam);
return fam;
}
protected static NativeJavaMethod createConstructorMethod(Class cl, boolean includePrivate) {
NativeJavaMethod ctors;
Constructor<?>[] constructors = getAccessibleConstructors(cl, includePrivate);
MemberBox[] ctorMembers = new MemberBox[constructors.length];
for (int i = 0; i != constructors.length; ++i) {
ctorMembers[i] = new MemberBox(constructors[i]);
}
ctors = new NativeJavaMethod(ctorMembers, cl.getSimpleName());
return ctors;
}
void put(Scriptable scope, String name, Object javaObject, Object value, boolean isStatic) {
Map<String, Object> ht = isStatic ? staticMembers : members;
Object member = ht.get(name);
@ -181,8 +438,7 @@ class JavaMembers {
}
Object[] getIds(boolean isStatic) {
Map<String, Object> map = isStatic ? staticMembers : members;
return map.keySet().toArray(new Object[0]);
return cfCache.getIds(isStatic);
}
static String javaSignature(Class<?> type) {
@ -298,19 +554,210 @@ class JavaMembers {
return member;
}
private static final class ClassReflectBean {
private final NativeJavaMethod constructorMethod;
// private final Map<String,MethodSignature> methodMap;
private final Map<MethodSignature, Method> mSignatureMap;
private final Field[] fields;
private final Map<String, Field> fieldMap;
private final Map<String, Field> staticFieldMap;
private final Map<String, Field> instFieldMap;
private final Map<String, String> staticBeanProperties;
private final Map<String, String> instBeanProperties;
private final Map<String, Object> members;
private final Map<String, Object> staticMembers;
private final Set<String> staticMemberNames;
private final Set<String> instMemberNames;
private final Set<String> staticFieldAndMethods;
private final Set<String> instFieldAndMethods;
Map<String, Object> getMembers(final boolean isStatic) {
return isStatic ? staticMembers : members;
}
boolean has(final String key, final boolean isStatic) {
return isStatic ? staticMemberNames.contains(key) : instMemberNames.contains(key);
}
Object[] getIds(final boolean isStatic) {
Map<String, Object> map = isStatic ? staticMembers : members;
return map.keySet().toArray(new Object[map.size()]);
}
@SuppressWarnings("unchecked")
ClassReflectBean(
NativeJavaMethod constructorMethod,
Map<MethodSignature, Method> mSignatureMap,
Field[] fields) {
super();
this.staticMemberNames = new HashSet<String>();
this.instMemberNames = new HashSet<String>();
this.constructorMethod = constructorMethod;
this.mSignatureMap = mSignatureMap;
this.fields = fields;
this.members = new HashMap<String, Object>();
this.staticMembers = new HashMap<String, Object>();
// methodMap = new HashMap<String,MethodSignature>();
for (Map.Entry<MethodSignature, Method> entry : mSignatureMap.entrySet()) {
final Method m = entry.getValue();
// methodMap.put(m.getName(),entry.getKey());
final MethodSignature key = entry.getKey();
if (key.staticMethod) {
staticMemberNames.add(key.name);
} else {
instMemberNames.add(key.name);
}
}
Set<String> staticMethodNames = new HashSet<String>(staticMemberNames);
Set<String> instMethodNames = new HashSet<String>(instMemberNames);
fieldMap = new HashMap<String, Field>();
for (Field fld : fields) {
final String name = fld.getName();
Field oldField = (Field) fieldMap.get(name);
if (oldField == null) {
fieldMap.put(name, fld);
continue;
}
// If this newly reflected field shadows an inherited field,
// then replace it. Otherwise, since access to the field
// would be ambiguous from Java, no field should be
// reflected.
// For now, the first field found wins, unless another field
// explicitly shadows it.
if (oldField.getDeclaringClass().isAssignableFrom(fld.getDeclaringClass())
|| (fld.isAccessible() && !oldField.isAccessible())) {
fieldMap.put(name, fld);
}
}
instFieldMap = new HashMap<String, Field>();
staticFieldMap = new HashMap<String, Field>();
for (Map.Entry<String, Field> entry : fieldMap.entrySet()) {
final Field fld = entry.getValue();
final String key = entry.getKey();
if (Modifier.isStatic(fld.getModifiers())) {
staticFieldMap.put(key, fld);
staticMemberNames.add(key);
} else {
instFieldMap.put(key, fld);
instMemberNames.add(key);
}
}
staticFieldAndMethods = intersection(staticMethodNames, staticFieldMap.keySet());
instFieldAndMethods = intersection(instMethodNames, instFieldMap.keySet());
final Object[] beanProperties = createBeanProperties(mSignatureMap.values());
staticBeanProperties = (Map<String, String>) beanProperties[0];
instBeanProperties = (Map<String, String>) beanProperties[1];
staticMemberNames.addAll(staticBeanProperties.keySet());
instMemberNames.addAll(instBeanProperties.keySet());
for (Map.Entry<MethodSignature, Method> entry : mSignatureMap.entrySet()) {
Method method = entry.getValue();
String name = method.getName();
final MethodSignature m = entry.getKey();
// int mods = method.getModifiers();
final boolean isStatic = m.staticMethod;
Map<String, Object> ht = isStatic ? staticMembers : members;
Object value = ht.get(name);
if (value == null) {
ht.put(name, method);
} else {
ArrayList<Object> overloadedMethods;
if (value instanceof ArrayList) {
overloadedMethods = (ArrayList<Object>) value;
} else {
if (!(value instanceof Method)) Kit.codeBug();
// value should be instance of Method as at this stage
// staticMembers and members can only contain methods
overloadedMethods = new ArrayList<>();
overloadedMethods.add(value);
ht.put(name, overloadedMethods);
}
overloadedMethods.add(method);
}
}
}
Field getStaticField(final String key) {
return staticFieldMap.get(key);
}
Field getInstField(final String key) {
return instFieldMap.get(key);
}
private static final <T> Set<T> intersection(Collection<T> a, Collection<T> b) {
final Set<T> ret = new HashSet<T>();
for (T v : a) {
if (b.contains(v)) {
ret.add(v);
}
}
return ret;
}
} // ClassReflectBean
private static final Map<String, Object> cache = new ConcurrentHashMap<String, Object>();
private static final Map<Class, ClassReflectBean> CACHE_PROTECTED =
new ConcurrentHashMap<Class, ClassReflectBean>();
private static final Map<Class, ClassReflectBean> CACHE_PRIVATE =
new ConcurrentHashMap<Class, ClassReflectBean>();
private static final Map<Class, ClassReflectBean> CACHE_DEFAULT =
new ConcurrentHashMap<Class, ClassReflectBean>();
private static Map<Class, ClassReflectBean> getCache(
final Class<?> clazz, final boolean includeProtected, final boolean includePrivate) {
if (includeProtected) {
return CACHE_PROTECTED;
}
if (includePrivate) {
return CACHE_PRIVATE;
}
return CACHE_DEFAULT;
}
private ClassReflectBean createClassReflectBean(
Class<?> clazz, boolean includeProtected, boolean includePrivate) {
final Map<Class, ClassReflectBean> cache =
reflectCacheOn ? getCache(clazz, includeProtected, includePrivate) : null;
ClassReflectBean ret = null;
if (cache != null) {
ret = cache.get(clazz);
if (ret != null) {
return ret;
}
}
Map<MethodSignature, Method> map = new HashMap<MethodSignature, Method>();
discoverAccessibleMethods(clazz, map, includeProtected, includePrivate);
Field[] fields = getAccessibleFields(clazz, includeProtected, includePrivate);
final NativeJavaMethod constructorMethod = createConstructorMethod(clazz, includePrivate);
ret = new ClassReflectBean(constructorMethod, map, fields);
// for(Iterator iter = map.keySet().iterator();iter.hasNext();) {
// final MethodSignature next =(MethodSignature) iter.next();
// if(!"getSelf".equals(next.name)) {
// iter.remove();
// }
// }
if (cache != null) {
cache.put(clazz, ret);
}
return ret;
}
/**
* Retrieves mapping of methods to accessible methods for a class. In case the class is not
* public, retrieves methods with same signature as its public methods from public superclasses
* and interfaces (if they exist). Basically upcasts every method to the nearest accessible
* method.
*/
private Method[] discoverAccessibleMethods(
Class<?> clazz, boolean includeProtected, boolean includePrivate) {
Map<MethodSignature, Method> map = new HashMap<>();
discoverAccessibleMethods(clazz, map, includeProtected, includePrivate);
return map.values().toArray(new Method[0]);
}
private void discoverAccessibleMethods(
Class<?> clazz,
Map<MethodSignature, Method> map,
@ -395,14 +842,17 @@ class JavaMembers {
static final class MethodSignature {
private final String name;
private final Class<?>[] args;
private final boolean staticMethod;
private final boolean privateMethod;
private final Method method;
private MethodSignature(String name, Class<?>[] args) {
this.name = name;
this.args = args;
}
MethodSignature(Method method) {
this(method.getName(), method.getParameterTypes());
private MethodSignature(final Method method) {
name = method.getName();
args = method.getParameterTypes();
this.method = method;
int mods = method.getModifiers();
staticMethod = Modifier.isStatic(mods);
privateMethod = Modifier.isPrivate(mods);
}
@Override
@ -421,219 +871,129 @@ class JavaMembers {
}
@SuppressWarnings("unchecked")
private void reflect(
private ClassReflectBean reflect(
Context cx, Scriptable scope, boolean includeProtected, boolean includePrivate) {
// We reflect methods first, because we want overloaded field/method
// names to be allocated to the NativeJavaMethod before the field
// gets in the way.
Method[] methods = discoverAccessibleMethods(cl, includeProtected, includePrivate);
for (Method method : methods) {
int mods = method.getModifiers();
boolean isStatic = Modifier.isStatic(mods);
Map<String, Object> ht = isStatic ? staticMembers : members;
String name = method.getName();
Object value = ht.get(name);
if (value == null) {
ht.put(name, method);
} else {
ArrayList<Object> overloadedMethods;
if (value instanceof ArrayList) {
overloadedMethods = (ArrayList<Object>) value;
} else {
if (!(value instanceof Method)) Kit.codeBug();
// value should be instance of Method as at this stage
// staticMembers and members can only contain methods
overloadedMethods = new ArrayList<>();
overloadedMethods.add(value);
ht.put(name, overloadedMethods);
}
overloadedMethods.add(method);
}
}
// replace Method instances by wrapped NativeJavaMethod objects
// first in staticMembers and then in members
for (int tableCursor = 0; tableCursor != 2; ++tableCursor) {
boolean isStatic = (tableCursor == 0);
Map<String, Object> ht = isStatic ? staticMembers : members;
for (Map.Entry<String, Object> entry : ht.entrySet()) {
MemberBox[] methodBoxes;
Object value = entry.getValue();
if (value instanceof Method) {
methodBoxes = new MemberBox[1];
methodBoxes[0] = new MemberBox((Method) value);
} else {
ArrayList<Object> overloadedMethods = (ArrayList<Object>) value;
int N = overloadedMethods.size();
if (N < 2) Kit.codeBug();
methodBoxes = new MemberBox[N];
for (int i = 0; i != N; ++i) {
Method method = (Method) overloadedMethods.get(i);
methodBoxes[i] = new MemberBox(method);
}
}
NativeJavaMethod fun = new NativeJavaMethod(methodBoxes);
if (scope != null) {
ScriptRuntime.setFunctionProtoAndParent(fun, cx, scope, false);
}
entry.setValue(fun);
}
}
// Reflect fields.
Field[] fields = getAccessibleFields(includeProtected, includePrivate);
for (Field field : fields) {
String name = field.getName();
int mods = field.getModifiers();
try {
boolean isStatic = Modifier.isStatic(mods);
final ClassReflectBean cfCache =
createClassReflectBean(cl, includeProtected, includePrivate);
if (!lazyInit) {
// replace Method instances by wrapped NativeJavaMethod objects
// first in staticMembers and then in members
for (int tableCursor = 0; tableCursor != 2; ++tableCursor) {
boolean isStatic = (tableCursor == 0);
Map<String, Object> ht = isStatic ? staticMembers : members;
Object member = ht.get(name);
if (member == null) {
ht.put(name, field);
} else if (member instanceof NativeJavaMethod) {
NativeJavaMethod method = (NativeJavaMethod) member;
FieldAndMethods fam = new FieldAndMethods(scope, method.methods, field);
Map<String, FieldAndMethods> fmht =
isStatic ? staticFieldAndMethods : fieldAndMethods;
if (fmht == null) {
fmht = new HashMap<>();
if (isStatic) {
staticFieldAndMethods = fmht;
} else {
fieldAndMethods = fmht;
}
}
fmht.put(name, fam);
ht.put(name, fam);
} else if (member instanceof Field) {
Field oldField = (Field) member;
// If this newly reflected field shadows an inherited field,
// then replace it. Otherwise, since access to the field
// would be ambiguous from Java, no field should be
// reflected.
// For now, the first field found wins, unless another field
// explicitly shadows it.
if (oldField.getDeclaringClass().isAssignableFrom(field.getDeclaringClass())) {
ht.put(name, field);
}
} else {
// "unknown member type"
Kit.codeBug();
for (Map.Entry<String, Object> entry : ht.entrySet()) {
Object value = entry.getValue();
NativeJavaMethod fun = toNativeJavaMethod(cx, scope, value);
entry.setValue(fun);
}
} catch (SecurityException e) {
// skip this field
Context.reportWarning(
"Could not access field "
+ name
+ " of class "
+ cl.getName()
+ " due to lack of privileges.");
}
}
// Create bean properties from corresponding get/set methods first for
// static members and then for instance members
for (int tableCursor = 0; tableCursor != 2; ++tableCursor) {
boolean isStatic = (tableCursor == 0);
Map<String, Object> ht = isStatic ? staticMembers : members;
// Reflect fields.
Field[] fields = cfCache.fields;
for (Field field : fields) {
initField(cx, scope, field);
}
Map<String, BeanProperty> toAdd = new HashMap<>();
// Create bean properties from corresponding get/set methods first for
// static members and then for instance members
for (int tableCursor = 0; tableCursor != 2; ++tableCursor) {
boolean isStatic = (tableCursor == 0);
Map<String, Object> ht = isStatic ? staticMembers : members;
// Now, For each member, make "bean" properties.
for (String name : ht.keySet()) {
// Is this a getter?
boolean memberIsGetMethod = name.startsWith("get");
boolean memberIsSetMethod = name.startsWith("set");
boolean memberIsIsMethod = name.startsWith("is");
if (memberIsGetMethod || memberIsIsMethod || memberIsSetMethod) {
// Double check name component.
String nameComponent = name.substring(memberIsIsMethod ? 2 : 3);
if (nameComponent.length() == 0) continue;
Map<String, BeanProperty> toAdd = new HashMap<>();
// Make the bean property name.
String beanPropertyName = nameComponent;
char ch0 = nameComponent.charAt(0);
if (Character.isUpperCase(ch0)) {
if (nameComponent.length() == 1) {
beanPropertyName = nameComponent.toLowerCase(Locale.ROOT);
} else {
char ch1 = nameComponent.charAt(1);
if (!Character.isUpperCase(ch1)) {
beanPropertyName =
Character.toLowerCase(ch0) + nameComponent.substring(1);
}
}
}
// Now, For each member, make "bean" properties.
for (String name : ht.keySet()) {
// Is this a getter?
boolean memberIsGetMethod = name.startsWith("get");
boolean memberIsSetMethod = name.startsWith("set");
boolean memberIsIsMethod = name.startsWith("is");
if (memberIsGetMethod || memberIsIsMethod || memberIsSetMethod) {
// Double check name component.
String nameComponent = name.substring(memberIsIsMethod ? 2 : 3);
if (nameComponent.length() == 0) continue;
// If we already have a member by this name, don't do this
// property.
if (toAdd.containsKey(beanPropertyName)) continue;
Object v = ht.get(beanPropertyName);
if (v != null) {
// A private field shouldn't mask a public getter/setter
if (!includePrivate
|| !(v instanceof Member)
|| !Modifier.isPrivate(((Member) v).getModifiers())) {
continue;
}
}
// Find the getter method, or if there is none, the is-
// method.
MemberBox getter = null;
getter = findGetter(isStatic, ht, "get", nameComponent);
// If there was no valid getter, check for an is- method.
if (getter == null) {
getter = findGetter(isStatic, ht, "is", nameComponent);
}
// setter
MemberBox setter = null;
NativeJavaMethod setters = null;
String setterName = "set".concat(nameComponent);
if (ht.containsKey(setterName)) {
// Is this value a method?
Object member = ht.get(setterName);
if (member instanceof NativeJavaMethod) {
NativeJavaMethod njmSet = (NativeJavaMethod) member;
if (getter != null) {
// We have a getter. Now, do we have a matching
// setter?
Class<?> type = getter.method().getReturnType();
setter = extractSetMethod(type, njmSet.methods, isStatic);
// Make the bean property name.
String beanPropertyName = nameComponent;
char ch0 = nameComponent.charAt(0);
if (Character.isUpperCase(ch0)) {
if (nameComponent.length() == 1) {
beanPropertyName = nameComponent.toLowerCase(Locale.ROOT);
} else {
// No getter, find any set method
setter = extractSetMethod(njmSet.methods, isStatic);
}
if (njmSet.methods.length > 1) {
setters = njmSet;
char ch1 = nameComponent.charAt(1);
if (!Character.isUpperCase(ch1)) {
beanPropertyName =
Character.toLowerCase(ch0) + nameComponent.substring(1);
}
}
}
// If we already have a member by this name, don't do this
// property.
if (toAdd.containsKey(beanPropertyName)) continue;
Object v = ht.get(beanPropertyName);
if (v != null) {
// A private field shouldn't mask a public getter/setter
if (!includePrivate
|| !(v instanceof Member)
|| !Modifier.isPrivate(((Member) v).getModifiers())) {
continue;
}
}
// Find the getter method, or if there is none, the is-
// method.
MemberBox getter = null;
getter = findGetter(isStatic, ht, "get", nameComponent);
// If there was no valid getter, check for an is- method.
if (getter == null) {
getter = findGetter(isStatic, ht, "is", nameComponent);
}
// setter
MemberBox setter = null;
NativeJavaMethod setters = null;
String setterName = "set".concat(nameComponent);
if (ht.containsKey(setterName)) {
// Is this value a method?
Object member = ht.get(setterName);
if (member instanceof NativeJavaMethod) {
NativeJavaMethod njmSet = (NativeJavaMethod) member;
if (getter != null) {
// We have a getter. Now, do we have a matching
// setter?
Class<?> type = getter.method().getReturnType();
setter = extractSetMethod(type, njmSet.methods, isStatic);
} else {
// No getter, find any set method
setter = extractSetMethod(njmSet.methods, isStatic);
}
if (njmSet.methods.length > 1) {
setters = njmSet;
}
}
}
// Make the property.
BeanProperty bp = new BeanProperty(getter, setter, setters);
toAdd.put(beanPropertyName, bp);
}
// Make the property.
BeanProperty bp = new BeanProperty(getter, setter, setters);
toAdd.put(beanPropertyName, bp);
}
// Add the new bean properties.
ht.putAll(toAdd);
}
// Add the new bean properties.
ht.putAll(toAdd);
}
// Reflect constructors
Constructor<?>[] constructors = getAccessibleConstructors(includePrivate);
MemberBox[] ctorMembers = new MemberBox[constructors.length];
for (int i = 0; i != constructors.length; ++i) {
ctorMembers[i] = new MemberBox(constructors[i]);
}
ctors = new NativeJavaMethod(ctorMembers, cl.getSimpleName());
}
ctors = cfCache.constructorMethod;
return cfCache;
} // end reflect
private Constructor<?>[] getAccessibleConstructors(boolean includePrivate) {
private static Constructor<?>[] getAccessibleConstructors(Class cl, boolean includePrivate) {
// The JVM currently doesn't allow changing access on java.lang.Class
// constructors, so don't try
if (includePrivate && cl != ScriptRuntime.ClassClass) {
@ -654,7 +1014,8 @@ class JavaMembers {
return cl.getConstructors();
}
private Field[] getAccessibleFields(boolean includeProtected, boolean includePrivate) {
private Field[] getAccessibleFields(
Class cl, boolean includeProtected, boolean includePrivate) {
if (includePrivate || includeProtected) {
try {
List<Field> fieldsList = new ArrayList<>();