Skip to content

Commit

Permalink
Bring introspection methods in line behaviorally with MRI.
Browse files Browse the repository at this point in the history
This should fix remaining issues running mocha head's tests.
  • Loading branch information
headius committed Apr 15, 2015
1 parent 9b99d6c commit a9ce5d3
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 55 deletions.
63 changes: 25 additions & 38 deletions core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -2286,12 +2286,12 @@ public IRubyObject methods(ThreadContext context, IRubyObject[] args, boolean us
Set<String> seen = new HashSet<String>();

if (getMetaClass().isSingleton()) {
getMetaClass().populateInstanceMethodNames(seen, methods, PRIVATE, true, useSymbols, false);
getMetaClass().populateInstanceMethodNames(seen, methods, PRIVATE, false, true, false);
if (all) {
getMetaClass().getSuperClass().populateInstanceMethodNames(seen, methods, PRIVATE, true, useSymbols, true);
getMetaClass().getSuperClass().populateInstanceMethodNames(seen, methods, PRIVATE, false, true, true);
}
} else if (all) {
getMetaClass().populateInstanceMethodNames(seen, methods, PRIVATE, true, useSymbols, true);
getMetaClass().populateInstanceMethodNames(seen, methods, PRIVATE, false, true, true);
} else {
// do nothing, leave empty
}
Expand Down Expand Up @@ -2395,56 +2395,43 @@ private IRubyObject[] trueIfNoArgument(ThreadContext context, IRubyObject[] args
*/
// TODO: This is almost RubyModule#instance_methods on the metaClass. Perhaps refactor.
public RubyArray singleton_methods(ThreadContext context, IRubyObject[] args) {
return singletonMethods(context, args, methodsCollector);
}

public RubyArray singleton_methods19(ThreadContext context, IRubyObject[] args) {
return singletonMethods(context, args, methodsCollector19);
}

private RubyArray singletonMethods(ThreadContext context, IRubyObject[] args, MethodsCollector collect) {
Ruby runtime = context.runtime;
boolean all = true;
if(args.length == 1) {
all = args[0].isTrue();
}

if (getMetaClass().isSingleton()) {
IRubyObject[] methodsArgs = new IRubyObject[]{context.runtime.getFalse()};
RubyArray singletonMethods = collect.instanceMethods(getMetaClass(), methodsArgs);
RubyClass klass = metaClass;
RubyModule origin = klass.getMethodLocation();

if (klass.isSingleton()) {
Set<RubySymbol> names = new HashSet<>();
for (Map.Entry<String, DynamicMethod> entry : klass.getMethods().entrySet()) {
if (entry.getValue().getVisibility() == PRIVATE) continue;
// TODO: needs to use method_entry_i logic from MRI
names.add(runtime.newSymbol(entry.getKey()));
}

if (all) {
RubyClass superClass = getMetaClass().getSuperClass();
while (superClass.isSingleton() || superClass.isIncluded()) {
singletonMethods.concat(collect.instanceMethods(superClass, methodsArgs));
superClass = superClass.getSuperClass();
klass = klass.getSuperClass();
while (klass != null && (klass.isSingleton() || klass.isIncluded())) {
if (klass != origin) {
for (Map.Entry<String, DynamicMethod> entry : klass.getMethods().entrySet()) {
if (entry.getValue().getVisibility() == PRIVATE) continue;
// TODO: needs to use method_entry_i logic from MRI
names.add(runtime.newSymbol(entry.getKey()));
}
}
klass = klass.getSuperClass();
}
}

singletonMethods.uniq_bang(context);
return singletonMethods;
return RubyArray.newArray(runtime, names);
}

return context.runtime.newEmptyArray();
}

private abstract static class MethodsCollector {
public abstract RubyArray instanceMethods(RubyClass rubyClass, IRubyObject[] args);
};

private static final MethodsCollector methodsCollector = new MethodsCollector() {
@Override
public RubyArray instanceMethods(RubyClass rubyClass, IRubyObject[] args) {
return rubyClass.instance_methods(args);
}
};

private static final MethodsCollector methodsCollector19 = new MethodsCollector() {
@Override
public RubyArray instanceMethods(RubyClass rubyClass, IRubyObject[] args) {
return rubyClass.instance_methods19(args);
}
};

/** rb_obj_method
*
* call-seq:
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
Expand Up @@ -1990,7 +1990,7 @@ public static IRubyObject private_methods19(ThreadContext context, IRubyObject s

@JRubyMethod(name = "singleton_methods", optional = 1)
public static RubyArray singleton_methods19(ThreadContext context, IRubyObject self, IRubyObject[] args) {
return ((RubyBasicObject)self).singleton_methods19(context, args);
return ((RubyBasicObject)self).singleton_methods(context, args);
}

@JRubyMethod(name = "method", required = 1)
Expand Down
42 changes: 26 additions & 16 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
import org.jruby.anno.JavaMethodDescriptor;
import org.jruby.anno.TypePopulator;
import org.jruby.common.IRubyWarnings.ID;
import org.jruby.common.RubyWarnings;
import org.jruby.embed.Extension;
import org.jruby.exceptions.RaiseException;
import org.jruby.internal.runtime.methods.AliasMethod;
Expand Down Expand Up @@ -2241,41 +2240,52 @@ public IRubyObject attr_accessor(ThreadContext context, IRubyObject[] args) {
* @param not if true only find methods not matching supplied visibility
* @return a RubyArray of instance method names
*/
private RubyArray instance_methods(IRubyObject[] args, final Visibility visibility, boolean not, boolean useSymbols) {
private RubyArray instance_methods(IRubyObject[] args, final Visibility visibility, boolean not) {
boolean includeSuper = args.length > 0 ? args[0].isTrue() : true;
return instanceMethods(visibility, includeSuper, false, not);
}

public RubyArray instanceMethods(final Visibility visibility, boolean includeSuper, boolean obj, boolean not) {
Ruby runtime = getRuntime();
RubyArray ary = runtime.newArray();
Set<String> seen = new HashSet<String>();

populateInstanceMethodNames(seen, ary, visibility, not, useSymbols, includeSuper);
populateInstanceMethodNames(seen, ary, visibility, obj, not, includeSuper);

return ary;
}

public void populateInstanceMethodNames(Set<String> seen, RubyArray ary, final Visibility visibility, boolean not, boolean useSymbols, boolean includeSuper) {
public void populateInstanceMethodNames(Set<String> seen, RubyArray ary, final Visibility visibility, boolean obj, boolean not, boolean recur) {
Ruby runtime = getRuntime();
RubyModule mod = this;
boolean prepended = false;

for (RubyModule type = this; type != null; type = type.getSuperClass()) {
RubyModule realType = type.getNonIncludedClass();
for (Map.Entry entry : type.getMethods().entrySet()) {
if (!recur && methodLocation != this) {
mod = methodLocation;
prepended = true;
}

for (; mod != null; mod = mod.getSuperClass()) {
RubyModule realType = mod.getNonIncludedClass();
for (Map.Entry entry : mod.getMethods().entrySet()) {
String methodName = (String) entry.getKey();

if (! seen.contains(methodName)) {
seen.add(methodName);

DynamicMethod method = (DynamicMethod) entry.getValue();
if ((method.isImplementedBy(realType) || method.isImplementedBy(type)) &&
if ((method.isImplementedBy(realType) || method.isImplementedBy(mod)) &&
(!not && method.getVisibility() == visibility || (not && method.getVisibility() != visibility)) &&
! method.isUndefined()) {

ary.append(useSymbols ? runtime.newSymbol(methodName) : runtime.newString(methodName));
ary.append(runtime.newSymbol(methodName));
}
}
}

if (!includeSuper && type == methodLocation) {
break;
}
if (mod.isIncluded() && !prepended) continue;
if (obj && mod.isSingleton()) continue;
if (!recur) break;
}
}

Expand All @@ -2285,7 +2295,7 @@ public RubyArray instance_methods(IRubyObject[] args) {

@JRubyMethod(name = "instance_methods", optional = 1)
public RubyArray instance_methods19(IRubyObject[] args) {
return instance_methods(args, PRIVATE, true, true);
return instance_methods(args, PRIVATE, true);
}

public RubyArray public_instance_methods(IRubyObject[] args) {
Expand All @@ -2294,7 +2304,7 @@ public RubyArray public_instance_methods(IRubyObject[] args) {

@JRubyMethod(name = "public_instance_methods", optional = 1)
public RubyArray public_instance_methods19(IRubyObject[] args) {
return instance_methods(args, PUBLIC, false, true);
return instance_methods(args, PUBLIC, false);
}

@JRubyMethod(name = "instance_method", required = 1)
Expand All @@ -2316,7 +2326,7 @@ public RubyArray protected_instance_methods(IRubyObject[] args) {

@JRubyMethod(name = "protected_instance_methods", optional = 1)
public RubyArray protected_instance_methods19(IRubyObject[] args) {
return instance_methods(args, PROTECTED, false, true);
return instance_methods(args, PROTECTED, false);
}

/** rb_class_private_instance_methods
Expand All @@ -2328,7 +2338,7 @@ public RubyArray private_instance_methods(IRubyObject[] args) {

@JRubyMethod(name = "private_instance_methods", optional = 1)
public RubyArray private_instance_methods19(IRubyObject[] args) {
return instance_methods(args, PRIVATE, false, true);
return instance_methods(args, PRIVATE, false);
}

/** rb_mod_prepend_features
Expand Down

0 comments on commit a9ce5d3

Please sign in to comment.