Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 46ac756330c8
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 605ab8587ee7
Choose a head ref
  • 4 commits
  • 6 files changed
  • 1 contributor

Commits on Apr 27, 2016

  1. Reimplement refinement-spiced method searching.

    First pass before my fixes recently: search for the self class in
    refinements, use methods there, otherwise proceed to normal
    hierarchy search.
    
    Second pass, my improvements: search all classes in hierarchy to
    see if any are refined and use that refined method. Otherwise,
    proceed to normal hierarchy search.
    
    This version should be closer to MRI in that it does the
    refinement search as part of the hierarchy search, allowing
    subclasses to override refinements of their superclasses. This
    fixes current red CI and brings us a bit closer to fully working
    refinements.
    
    This commit also removes some MRI TestRefinement excludes that
    pass now.
    headius committed Apr 27, 2016
    Copy the full SHA
    5fc281d View commit details
  2. Clean up imports.

    headius committed Apr 27, 2016
    Copy the full SHA
    36d8e82 View commit details
  3. Unused (never used?) method.

    headius committed Apr 27, 2016
    Copy the full SHA
    ffdfbb2 View commit details
  4. Missing doco.

    headius committed Apr 27, 2016
    Copy the full SHA
    605ab85 View commit details
57 changes: 48 additions & 9 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
@@ -82,9 +82,9 @@
import org.jruby.internal.runtime.methods.WrapperMethod;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRMethod;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.javasupport.binding.Initializer;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
import org.jruby.runtime.CallSite;
import org.jruby.runtime.ClassIndex;
@@ -93,21 +93,18 @@
import org.jruby.runtime.MethodFactory;
import org.jruby.runtime.MethodIndex;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.Signature;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.builtin.Variable;
import org.jruby.runtime.callsite.CacheEntry;
import org.jruby.runtime.callsite.FunctionalCachingCallSite;
import org.jruby.runtime.ivars.MethodData;
import org.jruby.runtime.load.IAutoloadMethod;
import org.jruby.runtime.marshal.MarshalStream;
import org.jruby.runtime.marshal.UnmarshalStream;
import org.jruby.runtime.opto.Invalidator;
import org.jruby.runtime.opto.OptoFactory;
import org.jruby.runtime.profile.MethodEnhancer;
import org.jruby.util.ByteList;
import org.jruby.util.ClassProvider;
import org.jruby.util.IdUtil;
import org.jruby.util.TypeConverter;
@@ -468,10 +465,6 @@ public RubyModule getDelegate() {
return this;
}

public RubyModule getNonPrependedClass() {
return this;
}

/**
* Get the base name of this class, or null if it is an anonymous class.
*
@@ -690,7 +683,7 @@ public IRubyObject using(ThreadContext context, IRubyObject refinedModule) {

// I pass the cref even though I don't need to so that the concept is simpler to read
StaticScope staticScope = context.getCurrentStaticScope();
RubyModule overlayModule = staticScope.getOverlayModule(context);
RubyModule overlayModule = staticScope.getOverlayModuleForWrite(context);
usingModule(context, overlayModule, refinedModule);

return this;
@@ -1237,10 +1230,39 @@ public DynamicMethod searchMethod(String name) {
return searchWithCache(name).method;
}

/**
* Search for the named method in this class and in superclasses, and if found return the CacheEntry representing
* the method and this class's serial number.
*
* @param name the method name
* @return the CacheEntry corresponding to the method and this class's serial number
*/
public CacheEntry searchWithCache(String name) {
return searchWithCache(name, true);
}

/**
* Search for the named method in this class and in superclasses applying refinements from the given scope. If
* found return the method; otherwise, return UndefinedMethod.
*
* @param name the method name
* @param refinedScope the scope containing refinements to search
* @return the method or UndefinedMethod
*/
public DynamicMethod searchWithRefinements(String name, StaticScope refinedScope) {
DynamicMethod method = searchMethodWithRefinementsInner(name, refinedScope);

if (method instanceof CacheableMethod) {
method = ((CacheableMethod) method).getMethodForCaching();
}

if (method != null) {
return method;
}

return UndefinedMethod.INSTANCE;
}

/**
* Search through this module and supermodules for method definitions. Cache superclass definitions in this class.
*
@@ -1432,6 +1454,23 @@ public DynamicMethod searchMethodInner(String name) {
return null;
}

public DynamicMethod searchMethodWithRefinementsInner(String name, StaticScope refinedScope) {
// This flattens some of the recursion that would be otherwise be necessary.
// Used to recurse up the class hierarchy which got messy with prepend.
for (RubyModule module = this; module != null; module = module.getSuperClass()) {
// Check for refinements in the given scope
DynamicMethod method = IRRuntimeHelpers.getRefinedMethodForClass(refinedScope, this.getNonIncludedClass(), name);
if (method != null && !method.isNull()) return method;

// Only recurs if module is an IncludedModuleWrapper.
// This way only the recursion needs to be handled differently on
// IncludedModuleWrapper.
method = module.searchMethodCommon(name);
if (method != null) return method.isNull() ? null : method;
}
return null;
}

// The local method resolution logic. Overridden in IncludedModuleWrapper for recursion.
protected DynamicMethod searchMethodCommon(String name) {
return getMethods().get(name);
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/TopSelfFactory.java
Original file line number Diff line number Diff line change
@@ -109,7 +109,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
Arity.checkArgumentCount(context.runtime, args, 1, 1);
RubyModule cref = context.getCurrentStaticScope().getOverlayModule(context);
RubyModule cref = context.getCurrentStaticScope().getOverlayModuleForWrite(context);
// unclear what the equivalent check would be for us
// rb_control_frame_t * prev_cfp = previous_frame(GET_THREAD());
//
25 changes: 14 additions & 11 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -1860,37 +1860,40 @@ public static IRubyObject callOptimizedAref(ThreadContext context, IRubyObject c
return site.call(context, caller, target, keyStr.strDup(context.runtime));
}

public static DynamicMethod getRefinedMethod(ThreadContext context, RubyClass selfType, String methodName) {
StaticScope refinedScope = context.getCurrentStaticScope();
public static DynamicMethod getRefinedMethodForClass(StaticScope refinedScope, RubyModule target, String methodName) {
Map<RubyClass, RubyModule> refinements;
RubyModule refinement;
DynamicMethod method = null;
RubyModule overlay;

while (true) {
if (refinedScope == null) break;

refinements = refinedScope.getOverlayModule(context).getRefinements();
overlay = refinedScope.getOverlayModuleForRead();

if (!refinements.isEmpty()) {
if (overlay != null) {

for (Map.Entry<RubyClass, RubyModule> refinementEntry : refinements.entrySet()) {
refinements = overlay.getRefinements();

if (selfType.isKindOfModule(refinementEntry.getKey())) {
if (!refinements.isEmpty()) {

refinement = refinementEntry.getValue();
method = refinement.searchMethod(methodName);
refinement = refinements.get(target);

if (!method.isUndefined()) {
if (refinement != null) {

DynamicMethod maybeMethod = refinement.searchMethod(methodName);

if (!maybeMethod.isUndefined()) {
method = maybeMethod;
break;
}

method = null;
}
}
}

refinedScope = refinedScope.getEnclosingScope();
}

return method;
}
}
6 changes: 5 additions & 1 deletion core/src/main/java/org/jruby/parser/StaticScope.java
Original file line number Diff line number Diff line change
@@ -536,7 +536,11 @@ public StaticScope duplicate() {
return dupe;
}

public RubyModule getOverlayModule(ThreadContext context) {
public RubyModule getOverlayModuleForRead() {
return overlayModule;
}

public RubyModule getOverlayModuleForWrite(ThreadContext context) {
RubyModule omod = overlayModule;
if (omod == null) {
overlayModule = omod = RubyModule.newModule(context.runtime);
Original file line number Diff line number Diff line change
@@ -20,109 +20,109 @@ public RefinedCachingCallSite(String methodName, CallType callType) {
@Override
public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject... args) {
RubyClass selfType = getClass(self);
DynamicMethod method = IRRuntimeHelpers.getRefinedMethod(context, selfType, methodName);
DynamicMethod method = selfType.searchWithRefinements(methodName, context.getCurrentStaticScope());

if (method == null) {
return super.call(context, caller, self, args);
if (methodMissing(method, caller)) {
return callMethodMissing(context, self, method, args);
}

return method.call(context, self, selfType, methodName, args);
}

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject[] args, Block block) {
RubyClass selfType = getClass(self);
DynamicMethod method = IRRuntimeHelpers.getRefinedMethod(context, selfType, methodName);
DynamicMethod method = selfType.searchWithRefinements(methodName, context.getCurrentStaticScope());

if (method == null) {
return super.call(context, caller, self, args, block);
if (methodMissing(method, caller)) {
return callMethodMissing(context, self, method, args, block);
}

return method.call(context, self, selfType, methodName, args, block);
}

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self) {
RubyClass selfType = getClass(self);
DynamicMethod method = IRRuntimeHelpers.getRefinedMethod(context, selfType, methodName);
DynamicMethod method = selfType.searchWithRefinements(methodName, context.getCurrentStaticScope());

if (method == null) {
return super.call(context, caller, self);
if (methodMissing(method, caller)) {
return callMethodMissing(context, self, method);
}

return method.call(context, self, selfType, methodName);
}

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, Block block) {
RubyClass selfType = getClass(self);
DynamicMethod method = IRRuntimeHelpers.getRefinedMethod(context, selfType, methodName);
DynamicMethod method = selfType.searchWithRefinements(methodName, context.getCurrentStaticScope());

if (method == null) {
return super.call(context, caller, self, block);
if (methodMissing(method, caller)) {
return callMethodMissing(context, self, method, block);
}

return method.call(context, self, selfType, methodName, block);
}

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg0) {
RubyClass selfType = getClass(self);
DynamicMethod method = IRRuntimeHelpers.getRefinedMethod(context, selfType, methodName);
DynamicMethod method = selfType.searchWithRefinements(methodName, context.getCurrentStaticScope());

if (method == null) {
return super.call(context, caller, self, arg0);
if (methodMissing(method, caller)) {
return callMethodMissing(context, self, method, arg0);
}

return method.call(context, self, selfType, methodName, arg0);
}

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg0, Block block) {
RubyClass selfType = getClass(self);
DynamicMethod method = IRRuntimeHelpers.getRefinedMethod(context, selfType, methodName);
DynamicMethod method = selfType.searchWithRefinements(methodName, context.getCurrentStaticScope());

if (method == null) {
return super.call(context, caller, self, arg0, block);
if (methodMissing(method, caller)) {
return callMethodMissing(context, self, method, arg0, block);
}

return method.call(context, self, selfType, methodName, arg0, block);
}

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg0, IRubyObject arg1) {
RubyClass selfType = getClass(self);
DynamicMethod method = IRRuntimeHelpers.getRefinedMethod(context, selfType, methodName);
DynamicMethod method = selfType.searchWithRefinements(methodName, context.getCurrentStaticScope());

if (method == null) {
return super.call(context, caller, self, arg0, arg1);
if (methodMissing(method, caller)) {
return callMethodMissing(context, self, method, arg0, arg1);
}

return method.call(context, self, selfType, methodName, arg0, arg1);
}

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg0, IRubyObject arg1, Block block) {
RubyClass selfType = getClass(self);
DynamicMethod method = IRRuntimeHelpers.getRefinedMethod(context, selfType, methodName);
DynamicMethod method = selfType.searchWithRefinements(methodName, context.getCurrentStaticScope());

if (method == null) {
return super.call(context, caller, self, arg0, arg1, block);
if (methodMissing(method, caller)) {
return callMethodMissing(context, self, method, arg0, arg1, block);
}

return method.call(context, self, selfType, methodName, arg0, arg1, block);
}

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
RubyClass selfType = getClass(self);
DynamicMethod method = IRRuntimeHelpers.getRefinedMethod(context, selfType, methodName);
DynamicMethod method = selfType.searchWithRefinements(methodName, context.getCurrentStaticScope());

if (method == null) {
return super.call(context, caller, self, arg0, arg1, arg2);
if (methodMissing(method, caller)) {
return callMethodMissing(context, self, method, arg0, arg1, arg2);
}

return method.call(context, self, selfType, methodName, arg0, arg1, arg2);
}

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
RubyClass selfType = getClass(self);
DynamicMethod method = IRRuntimeHelpers.getRefinedMethod(context, selfType, methodName);
DynamicMethod method = selfType.searchWithRefinements(methodName, context.getCurrentStaticScope());

if (method == null) {
return super.call(context, caller, self, arg0, arg1, arg2, block);
if (methodMissing(method, caller)) {
return callMethodMissing(context, self, method, arg0, arg1, arg2, block);
}

return method.call(context, self, selfType, methodName, arg0, arg1, arg2);
9 changes: 0 additions & 9 deletions test/mri/excludes/TestRefinement.rb
Original file line number Diff line number Diff line change
@@ -6,30 +6,21 @@
exclude :test_inspect, "needs investigation"
exclude :test_main_using_is_private, "needs investigation"
exclude :test_making_private_method_public, "needs investigation"
exclude :test_mixed_using, "needs investigation"
exclude :test_module_inclusion, "needs investigation"
exclude :test_module_inclusion2, "needs investigation"
exclude :test_module_using, "needs investigation"
exclude :test_module_using_class, "needs investigation"
exclude :test_module_using_in_method, "needs investigation"
exclude :test_module_using_invalid_self, "needs investigation"
exclude :test_new_method, "needs investigation"
exclude :test_new_method_on_subclass, "needs investigation"
exclude :test_override, "needs investigation"
exclude :test_prepend_into_refinement, "needs investigation"
exclude :test_refine_after_using, "needs investigation"
exclude :test_refine_in_class, "needs investigation"
exclude :test_refine_mutual_recursion, "needs investigation"
exclude :test_refine_recursion, "needs investigation"
exclude :test_refine_scoping, "needs investigation"
exclude :test_refine_with_proc, "needs investigation"
exclude :test_refined_method_defined, "needs investigation"
exclude :test_remove_refined_method, "needs investigation"
exclude :test_remove_undefined_refined_method, "needs investigation"
exclude :test_reopen_refinement_module, "needs investigation"
exclude :test_singleton_method_should_not_use_refinements, "needs investigation"
exclude :test_super, "needs investigation"
exclude :test_undef_original_method, "needs investigation"
exclude :test_undefined_refined_method_defined, "needs investigation"
exclude :test_using_in_method, "needs investigation"
exclude :test_using_in_module, "needs investigation"