in rhino/src/main/java/org/mozilla/javascript/JavaMembers.java [431:639]
private void 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);
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();
}
} 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;
Map<String, BeanProperty> toAdd = new HashMap<>();
// 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;
// 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);
}
}
}
// 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);
// 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);
}
}
// 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());
}