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: 40a470702fc5
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 619610106806
Choose a head ref
  • 6 commits
  • 13 files changed
  • 1 contributor

Commits on Apr 8, 2015

  1. Copy the full SHA
    766fe37 View commit details
  2. Copy the full SHA
    d7e5fd8 View commit details
  3. Add top-level #using logic.

    headius committed Apr 8, 2015
    Copy the full SHA
    1d0b3c2 View commit details
  4. Copy the full SHA
    040f140 View commit details
  5. Minor reformat.

    headius committed Apr 8, 2015
    Copy the full SHA
    88db27e View commit details
  6. Copy the full SHA
    6196101 View commit details
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
@@ -118,7 +118,7 @@ public static RubyModule createKernelModule(Ruby runtime) {

module.defineAnnotatedMethods(RubyKernel.class);

module.setFlag(RubyObject.USER7_F, false); //Kernel is the only Module that doesn't need an implementor
module.setFlag(RubyObject.USER7_F, false); //Kernel is the only normal Module that doesn't need an implementor

runtime.setPrivateMethodMissing(new MethodMissingMethod(module) {
@Override
27 changes: 16 additions & 11 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
@@ -552,8 +552,8 @@ public IRubyObject refine(ThreadContext context, IRubyObject classArg, Block blo
if (!block.isGiven()) throw context.runtime.newArgumentError("no block given");
if (block.isEscaped()) throw context.runtime.newArgumentError("can't pass a Proc as a block to Module#refine");
if (!(classArg instanceof RubyClass)) throw context.runtime.newTypeError(classArg, context.runtime.getClassClass());
if (refinements == null) refinements = new HashMap<>();
if (activatedRefinements == null) activatedRefinements = new HashMap<>();
if (refinements == Collections.EMPTY_MAP) refinements = new HashMap<>();
if (activatedRefinements == Collections.EMPTY_MAP) activatedRefinements = new HashMap<>();

RubyClass classWeAreRefining = (RubyClass) classArg;
RubyModule refinement = refinements.get(classWeAreRefining);
@@ -573,6 +573,7 @@ public IRubyObject refine(ThreadContext context, IRubyObject classArg, Block blo
private RubyModule createNewRefinedModule(ThreadContext context, RubyClass classWeAreRefining) {
RubyModule newRefinement = new RubyModule(context.runtime, classWeAreRefining);
newRefinement.setFlag(REFINED_MODULE_F, true);
newRefinement.setFlag(RubyObject.USER7_F, false); // Refinement modules should not do implementer check
newRefinement.refinedClass = classWeAreRefining;
newRefinement.definedAt = this;
refinements.put(classWeAreRefining, newRefinement);
@@ -641,14 +642,14 @@ public IRubyObject using(ThreadContext context, IRubyObject refinedModule) {
}

// mri: rb_using_module
public void usingModule(ThreadContext context, RubyModule cref, IRubyObject refinedModule) {
public static void usingModule(ThreadContext context, RubyModule cref, IRubyObject refinedModule) {
if (!(refinedModule instanceof RubyModule))throw context.runtime.newTypeError(refinedModule, context.runtime.getModule());

usingModuleRecursive(cref, (RubyModule) refinedModule);
}

// mri: using_module_recursive
private void usingModuleRecursive(RubyModule cref, RubyModule refinedModule) {
private static void usingModuleRecursive(RubyModule cref, RubyModule refinedModule) {
RubyClass superClass = refinedModule.getSuperClass();

// For each superClass of the refined module also use their refinements for the given cref
@@ -670,7 +671,7 @@ private void usingModuleRecursive(RubyModule cref, RubyModule refinedModule) {
// 1. class being refined has never had any refines happen to it yet: return itself
// 2. class has been refined: return already existing refinementwrapper (chain of modules to call against)
// 3. refinement is already in the refinementwrapper so we do not need to add it to the wrapper again: return null
private RubyModule getAlreadyRefinementWrapper(RubyModule cref, RubyClass classWeAreRefining, RubyModule refinement) {
private static RubyModule getAlreadyRefinementWrapper(RubyModule cref, RubyClass classWeAreRefining, RubyModule refinement) {
// We have already encountered at least one refine on this class. Return that wrapper.
RubyModule moduleWrapperForRefinment = cref.refinements.get(classWeAreRefining);
if (moduleWrapperForRefinment == null) return classWeAreRefining;
@@ -688,21 +689,21 @@ private RubyModule getAlreadyRefinementWrapper(RubyModule cref, RubyClass classW
* conflict if the same module was used in two places but the cref must be a lexically containing
* module so it cannot live in two files.
*/
private void usingRefinement(RubyModule cref, RubyClass classWeAreRefining, RubyModule refinement) {
private static void usingRefinement(RubyModule cref, RubyClass classWeAreRefining, RubyModule refinement) {
// Our storage cubby in cref for all known refinements
if (cref.refinements == null) cref.refinements = new HashMap<>();
if (cref.refinements == Collections.EMPTY_MAP) cref.refinements = new HashMap<>();

RubyModule superClass = getAlreadyRefinementWrapper(cref, classWeAreRefining, refinement);
if (superClass == null) return; // already been refined and added to refinementwrapper

refinement.setFlag(IS_OVERLAID_F, true);
RubyModule lookup = new IncludedModuleWrapper(getRuntime(), (RubyClass) superClass, refinement);
RubyModule lookup = new IncludedModuleWrapper(cref.getRuntime(), (RubyClass) superClass, refinement);
RubyModule iclass = lookup;
lookup.refinedClass = classWeAreRefining;

for (refinement = refinement.getSuperClass(); refinement != null && refinement != classWeAreRefining; refinement = refinement.getSuperClass()) {
refinement.setFlag(IS_OVERLAID_F, true);
RubyClass newInclude = new IncludedModuleWrapper(getRuntime(), lookup.getSuperClass(), refinement);
RubyClass newInclude = new IncludedModuleWrapper(cref.getRuntime(), lookup.getSuperClass(), refinement);
lookup.setSuperClass(newInclude);
lookup = newInclude;
lookup.refinedClass = classWeAreRefining;
@@ -4308,6 +4309,10 @@ public Set<String> discoverInstanceVariables() {
return set;
}

public boolean isRefinement() {
return (flags & REFINED_MODULE_F) == REFINED_MODULE_F;
}

/**
* Return true if the given method is defined on this class and is a builtin
* (defined in Java at boot).
@@ -4373,10 +4378,10 @@ public void setRefinements(Map<RubyClass, RubyModule> refinements) {
private volatile Map<String, IRubyObject> classVariables = Collections.EMPTY_MAP;

/** Refinements added to this module are stored here **/
private volatile Map<RubyClass, RubyModule> refinements = null;
private volatile Map<RubyClass, RubyModule> refinements = Collections.EMPTY_MAP;

/** A list of refinement hosts for this refinement */
private volatile Map<RubyClass, IncludedModuleWrapper> activatedRefinements = null;
private volatile Map<RubyClass, IncludedModuleWrapper> activatedRefinements = Collections.EMPTY_MAP;

/** The class this refinement refines */
volatile RubyClass refinedClass = null;
18 changes: 18 additions & 0 deletions core/src/main/java/org/jruby/TopSelfFactory.java
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@
package org.jruby;

import org.jruby.internal.runtime.methods.JavaMethod;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
@@ -103,6 +104,23 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
return klass.define_method(context, arg0, arg1, block);
}
});

singletonClass.addMethod("using", new JavaMethod.JavaMethodN(singletonClass, Visibility.PRIVATE) {
@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().getModule();
// unclear what the equivalent check would be for us
// rb_control_frame_t * prev_cfp = previous_frame(GET_THREAD());
//
// if (CREF_NEXT(cref) || (prev_cfp && prev_cfp->me)) {
// rb_raise(rb_eRuntimeError,
// "main.using is permitted only at toplevel");
// }
RubyModule.usingModule(context, cref, args[0]);
return self;
}
});

return topSelf;
}
7 changes: 5 additions & 2 deletions core/src/main/java/org/jruby/ir/instructions/CallBase.java
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
import org.jruby.parser.StaticScope;
import org.jruby.runtime.*;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.RefinedCachingCallSite;

import java.util.ArrayList;
import java.util.List;
@@ -46,7 +47,7 @@ protected CallBase(Operation op, CallType callType, String name, Operand receive
hasClosure = closure != null;
this.name = name;
this.callType = callType;
this.callSite = getCallSiteFor(callType, name);
this.callSite = getCallSiteFor(callType, name, potentiallyRefined);
containsArgSplat = containsArgSplat(args);
flagsComputed = false;
canBeEval = true;
@@ -147,9 +148,11 @@ public boolean inliningBlocked() {
return dontInline;
}

private static CallSite getCallSiteFor(CallType callType, String name) {
private static CallSite getCallSiteFor(CallType callType, String name, boolean potentiallyRefined) {
assert callType != null: "Calltype should never be null";

if (potentiallyRefined) return new RefinedCachingCallSite(name, callType);

switch (callType) {
case NORMAL: return MethodIndex.getCallSite(name);
case FUNCTIONAL: return MethodIndex.getFunctionalCallSite(name);
18 changes: 15 additions & 3 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.FunctionalCachingCallSite;
import org.jruby.runtime.callsite.NormalCachingCallSite;
import org.jruby.runtime.callsite.RefinedCachingCallSite;
import org.jruby.runtime.callsite.VariableCachingCallSite;
import org.jruby.runtime.ivars.VariableAccessor;
import org.jruby.util.ByteList;
@@ -1170,23 +1171,29 @@ public static void defInterpretedClassMethod(ThreadContext context, IRScope meth
newMethod = new MixedModeIRMethod(method, Visibility.PUBLIC, rubyClass);
}
rubyClass.addMethod(method.getName(), newMethod);
obj.callMethod(context, "singleton_method_added", context.runtime.fastNewSymbol(method.getName()));
if (!rubyClass.isRefinement()) {
obj.callMethod(context, "singleton_method_added", context.runtime.fastNewSymbol(method.getName()));
}
}

@JIT
public static void defCompiledClassMethod(ThreadContext context, MethodHandle handle, IRScope method, IRubyObject obj) {
RubyClass rubyClass = checkClassForDef(context, method, obj);

rubyClass.addMethod(method.getName(), new CompiledIRMethod(handle, method, Visibility.PUBLIC, rubyClass, method.receivesKeywordArgs()));
obj.callMethod(context, "singleton_method_added", context.runtime.fastNewSymbol(method.getName()));
if (!rubyClass.isRefinement()) {
obj.callMethod(context, "singleton_method_added", context.runtime.fastNewSymbol(method.getName()));
}
}

@JIT
public static void defCompiledClassMethod(ThreadContext context, MethodHandle variable, MethodHandle specific, int specificArity, IRScope method, IRubyObject obj) {
RubyClass rubyClass = checkClassForDef(context, method, obj);

rubyClass.addMethod(method.getName(), new CompiledIRMethod(variable, specific, specificArity, method, Visibility.PUBLIC, rubyClass, method.receivesKeywordArgs()));
obj.callMethod(context, "singleton_method_added", context.runtime.fastNewSymbol(method.getName()));
if (!rubyClass.isRefinement()) {
obj.callMethod(context, "singleton_method_added", context.runtime.fastNewSymbol(method.getName()));
}
}

private static RubyClass checkClassForDef(ThreadContext context, IRScope method, IRubyObject obj) {
@@ -1341,6 +1348,11 @@ public static VariableCachingCallSite newVariableCachingCallSite(String name) {
return new VariableCachingCallSite(name);
}

@JIT
public static RefinedCachingCallSite newRefinedCachingCallSite(String name, String callType) {
return new RefinedCachingCallSite(name, CallType.valueOf(callType));
}

@JIT
public static IRScope decodeScopeFromBytes(Ruby runtime, byte[] scopeBytes) {
try {
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ir/targets/ClassData.java
Original file line number Diff line number Diff line change
@@ -80,7 +80,7 @@ public void popmethod() {
}

public ClassVisitor cls;
public String clsName;
public final String clsName;
Stack<MethodData> methodStack = new Stack();
public AtomicInteger callSiteCount = new AtomicInteger(0);
public Set<Integer> arrayMethodsDefined = new HashSet();
Original file line number Diff line number Diff line change
@@ -307,7 +307,7 @@ public void pushBlockBody(Handle handle, org.jruby.runtime.Signature signature,
* @param arity arity of the call
* @param hasClosure whether a closure will be on the stack for passing
*/
public abstract void invokeOther(String name, int arity, boolean hasClosure);
public abstract void invokeOther(String name, int arity, boolean hasClosure, boolean isPotentiallyRefined);

/**
* Invoke a fixnum-receiving method on an object other than self.
@@ -338,7 +338,7 @@ public void pushBlockBody(Handle handle, org.jruby.runtime.Signature signature,
* @param hasClosure whether a closure will be on the stack for passing
* @param callType
*/
public abstract void invokeSelf(String name, int arity, boolean hasClosure, CallType callType);
public abstract void invokeSelf(String name, int arity, boolean hasClosure, CallType callType, boolean isPotentiallyRefined);

/**
* Invoke a superclass method from an instance context.
49 changes: 29 additions & 20 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter6.java
Original file line number Diff line number Diff line change
@@ -38,6 +38,7 @@
import org.jruby.runtime.callsite.CachingCallSite;
import org.jruby.runtime.callsite.FunctionalCachingCallSite;
import org.jruby.runtime.callsite.NormalCachingCallSite;
import org.jruby.runtime.callsite.RefinedCachingCallSite;
import org.jruby.runtime.callsite.VariableCachingCallSite;
import org.jruby.runtime.ivars.VariableAccessor;
import org.jruby.util.ByteList;
@@ -279,11 +280,11 @@ public void run() {
});
}

public void invokeOther(String name, int arity, boolean hasClosure) {
invoke(name, arity, hasClosure, CallType.NORMAL);
public void invokeOther(String name, int arity, boolean hasClosure, boolean isPotentiallyRefined) {
invoke(name, arity, hasClosure, CallType.NORMAL, isPotentiallyRefined);
}

public void invoke(String name, int arity, boolean hasClosure, CallType callType) {
public void invoke(String name, int arity, boolean hasClosure, CallType callType, boolean isPotentiallyRefined) {
if (arity > MAX_ARGUMENTS) throw new NotCompilableException("call to `" + name + "' has more than " + MAX_ARGUMENTS + " arguments");

SkinnyMethodAdapter adapter2;
@@ -349,20 +350,28 @@ public void invoke(String name, int arity, boolean hasClosure, CallType callType
adapter2.pop();
adapter2.ldc(name);
Class<? extends CachingCallSite> siteClass;
switch (callType) {
case NORMAL:
siteClass = NormalCachingCallSite.class;
break;
case FUNCTIONAL:
siteClass = FunctionalCachingCallSite.class;
break;
case VARIABLE:
siteClass = VariableCachingCallSite.class;
break;
default:
throw new RuntimeException("BUG: Unexpected call type " + callType + " in JVM6 invoke logic");
String signature;
if (isPotentiallyRefined) {
siteClass = RefinedCachingCallSite.class;
signature = sig(siteClass, String.class, String.class);
adapter2.ldc(callType.name());
} else {
switch (callType) {
case NORMAL:
siteClass = NormalCachingCallSite.class;
break;
case FUNCTIONAL:
siteClass = FunctionalCachingCallSite.class;
break;
case VARIABLE:
siteClass = VariableCachingCallSite.class;
break;
default:
throw new RuntimeException("BUG: Unexpected call type " + callType + " in JVM6 invoke logic");
}
signature = sig(siteClass, String.class);
}
adapter2.invokestatic(p(IRRuntimeHelpers.class), "new" + siteClass.getSimpleName(), sig(siteClass, String.class));
adapter2.invokestatic(p(IRRuntimeHelpers.class), "new" + siteClass.getSimpleName(), signature);
adapter2.dup();
adapter2.putstatic(getClassData().clsName, methodName, ci(CachingCallSite.class));

@@ -430,7 +439,7 @@ public static void buildArrayFromLocals(SkinnyMethodAdapter adapter2, int base,
public void invokeOtherOneFixnum(String name, long fixnum) {
if (!MethodIndex.hasFastFixnumOps(name)) {
pushFixnum(fixnum);
invokeOther(name, 1, false);
invokeOther(name, 1, false, false);
}
SkinnyMethodAdapter adapter2;
String incomingSig = sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT));
@@ -478,7 +487,7 @@ public void invokeOtherOneFixnum(String name, long fixnum) {
public void invokeOtherOneFloat(String name, double flote) {
if (!MethodIndex.hasFastFloatOps(name)) {
pushFloat(flote);
invokeOther(name, 1, false);
invokeOther(name, 1, false, false);
}
SkinnyMethodAdapter adapter2;
String incomingSig = sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT));
@@ -523,10 +532,10 @@ public void invokeOtherOneFloat(String name, double flote) {
adapter.invokestatic(getClassData().clsName, methodName, incomingSig);
}

public void invokeSelf(String name, int arity, boolean hasClosure, CallType callType) {
public void invokeSelf(String name, int arity, boolean hasClosure, CallType callType, boolean isPotentiallyRefined) {
if (arity > MAX_ARGUMENTS) throw new NotCompilableException("call to `" + name + "' has more than " + MAX_ARGUMENTS + " arguments");

invoke(name, arity, hasClosure, callType);
invoke(name, arity, hasClosure, callType, isPotentiallyRefined);
}

public void invokeInstanceSuper(String name, int arity, boolean hasClosure, boolean[] splatmap) {
14 changes: 11 additions & 3 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter7.java
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@
*
* @author headius
*/
public class IRBytecodeAdapter7 extends IRBytecodeAdapter {
public class IRBytecodeAdapter7 extends IRBytecodeAdapter6 {

public IRBytecodeAdapter7(SkinnyMethodAdapter adapter, Signature signature, ClassData classData) {
super(adapter, signature, classData);
@@ -121,8 +121,12 @@ public void pushEncoding(Encoding encoding) {
adapter.invokedynamic("encoding", sig(RubyEncoding.class, ThreadContext.class), Bootstrap.contextValueString(), new String(encoding.getName()));
}

public void invokeOther(String name, int arity, boolean hasClosure) {
public void invokeOther(String name, int arity, boolean hasClosure, boolean isPotentiallyRefined) {
if (arity > MAX_ARGUMENTS) throw new NotCompilableException("call to `" + name + "' has more than " + MAX_ARGUMENTS + " arguments");
if (isPotentiallyRefined) {
super.invokeOther(name, arity, hasClosure, isPotentiallyRefined);
return;
}

if (hasClosure) {
if (arity == -1) {
@@ -163,8 +167,12 @@ public void invokeOtherOneFloat(String name, double flote) {
0);
}

public void invokeSelf(String name, int arity, boolean hasClosure, CallType callType) {
public void invokeSelf(String name, int arity, boolean hasClosure, CallType callType, boolean isPotentiallyRefined) {
if (arity > MAX_ARGUMENTS) throw new NotCompilableException("call to `" + name + "' has more than " + MAX_ARGUMENTS + " arguments");
if (isPotentiallyRefined) {
super.invokeSelf(name, arity, hasClosure, callType, isPotentiallyRefined);
return;
}

String action = callType == CallType.FUNCTIONAL ? "callFunctional" : "callVariable";
if (hasClosure) {
Loading