Index: src/org/jruby/javasupport/JavaClass.java =================================================================== --- src/org/jruby/javasupport/JavaClass.java (revision 3480) +++ src/org/jruby/javasupport/JavaClass.java (working copy) @@ -45,9 +45,11 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -182,11 +184,31 @@ return JavaClass.get(recv.getRuntime(), klass); } + + private Set getPublicFieldNames(boolean isStatic) { + // we're not checking for final here; since the purpose is to prevent + // shortcut property names from being created that conflict with + // field names, it won't make sense to allow the property setter (name=) + // to be created but not the getter. + int mask = Modifier.PUBLIC | Modifier.STATIC; + int want = isStatic ? Modifier.PUBLIC | Modifier.STATIC : Modifier.PUBLIC; + Set names = new HashSet(); + names.add("class"); + Field[] fields = ((Class)getValue()).getFields(); + for (int i = fields.length; --i >= 0; ) { + if ((fields[i].getModifiers() & mask) == want) { + names.add(fields[i].getName()); + } + } + return names; + } + + /** * Get all methods grouped by name (e.g. 'new => {new(), new(int), new(int, int)}, ...') * @param isStatic determines whether you want static or instance methods from the class */ - private Map getMethodsClumped(boolean isStatic) { + private Map getMethodsClumped(boolean isStatic, Set assignedNames) { Map map = new HashMap(); if(((Class)getValue()).isInterface()) { return map; @@ -205,6 +227,7 @@ if (methodsWithName == null) { methodsWithName = RubyArray.newArrayLight(getRuntime()); map.put(key, methodsWithName); + assignedNames.add(key); } methodsWithName.append(JavaMethod.create(getRuntime(), methods[i])); @@ -213,7 +236,7 @@ return map; } - private Map getPropertysClumped() { + private Map getPropertysClumped(Set assignedNames) { Map map = new HashMap(); BeanInfo info; @@ -242,7 +265,9 @@ readMethod.getReturnType() == boolean.class) { aliases.add(descriptors[i].getName() + "?"); } - aliases.add(descriptors[i].getName()); + if (!assignedNames.contains(descriptors[i].getName())) { + aliases.add(descriptors[i].getName()); + } } Method writeMethod = descriptors[i].getWriteMethod(); @@ -256,7 +281,9 @@ map.put(key, aliases); } - aliases.add(descriptors[i].getName() + "="); + if (!assignedNames.contains(descriptors[i].getName())) { + aliases.add(descriptors[i].getName() + "="); + } } } @@ -369,8 +396,9 @@ public IRubyObject define_instance_methods_for_proxy(IRubyObject arg) { assert arg instanceof RubyClass; - Map aliasesClump = getPropertysClumped(); - Map methodsClump = getMethodsClumped(false); + Set assignedNames = getPublicFieldNames(false); + Map methodsClump = getMethodsClumped(false,assignedNames); + Map aliasesClump = getPropertysClumped(assignedNames); RubyClass proxy = (RubyClass) arg; proxy.defineFastMethod("__jsend!", __jsend_method); @@ -525,6 +553,8 @@ } public RubyArray declared_classes() { + // TODO: should be able to partially work around this by calling + // javaClass().getClasses, which will return any public inner classes. if (Ruby.isSecurityRestricted()) // Can't even get inner classes? return getRuntime().newArray(0); Class[] classes = javaClass().getDeclaredClasses();