Skip to content

Commit

Permalink
Merge non-RubyHash parts of improve_kwargs branch.
Browse files Browse the repository at this point in the history
This does not include @ChrisBr work on open addressing Hash.

Merge commit '4116c0c6da725d7586569eb62797b2091df6db71'
  • Loading branch information
headius committed Jul 17, 2018
2 parents 772832a + 4116c0c commit 76c2df8
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 66 deletions.
17 changes: 17 additions & 0 deletions core/src/main/java/org/jruby/RubyHash.java
Expand Up @@ -692,6 +692,23 @@ private <T> void visitLimited(ThreadContext context, VisitorWithState visitor, l
if (count > 0) throw concurrentModification();
}

public <T> boolean allSymbols() {
int startGeneration = generation;
// visit not more than size entries
RubyHashEntry head = this.head;
for (RubyHashEntry entry = head.nextAdded; entry != head; entry = entry.nextAdded) {
if (startGeneration != generation) {
startGeneration = generation;
entry = head.nextAdded;
if (entry == head) break;
}
if (entry != null && entry.isLive()) {
if (!(entry.key instanceof RubySymbol)) return false;
}
}
return true;
}

/* ============================
* End of hash internals
* ============================
Expand Down
6 changes: 2 additions & 4 deletions core/src/main/java/org/jruby/compiler/MethodJITTask.java
Expand Up @@ -130,8 +130,7 @@ public void run() {
variable,
method.getIRScope(),
method.getVisibility(),
method.getImplementationClass(),
method.getIRScope().receivesKeywordArgs()));
method.getImplementationClass()));

} else {
// also specific-arity
Expand All @@ -143,8 +142,7 @@ public void run() {
entry.getKey(),
method.getIRScope(),
method.getVisibility(),
method.getImplementationClass(),
method.getIRScope().receivesKeywordArgs()));
method.getImplementationClass()));
break; // FIXME: only supports one arity
}
}
Expand Down
Expand Up @@ -25,50 +25,28 @@ public class CompiledIRMethod extends AbstractIRMethod {
private final MethodHandle specific;
private final int specificArity;

private final boolean hasKwargs;

public CompiledIRMethod(MethodHandle variable, IRScope method, Visibility visibility,
RubyModule implementationClass, boolean hasKwargs) {
this(variable, null, -1, method, visibility, implementationClass, hasKwargs);
RubyModule implementationClass) {
this(variable, null, -1, method, visibility, implementationClass);
}

public CompiledIRMethod(MethodHandle variable, MethodHandle specific, int specificArity, IRScope method,
Visibility visibility, RubyModule implementationClass, boolean hasKwargs) {
Visibility visibility, RubyModule implementationClass) {
super(method, visibility, implementationClass);

if (hasKwargs) {
MethodType type = variable.type();
int params = type.parameterCount();
MethodHandle frobnicate = Binder
.from(type.changeReturnType(IRubyObject[].class))
.dropLast(params - 4)
.drop(1, 2)
.append(signature)
.invoke(FROBNICATE);
this.variable = Binder
.from(type)
.fold(frobnicate)
.permute(type.parameterType(params - 1) == Block.class ? new int[] {1, 2, 3, 0, 5, 6, 7, 8} : new int[] {1, 2, 3, 0, 5, 6, 7})
.invoke(variable);
} else {
this.variable = variable;
}

this.variable = variable;
this.specific = specific;
// deopt unboxing if we have to process kwargs hash (although this really has nothing to do with arg
// unboxing -- it was a simple path to hacking this in).
this.specificArity = hasKwargs ? -1 : specificArity;
this.specificArity = method.receivesKeywordArgs() ? -1 : specificArity;
this.method.getStaticScope().determineModule();
this.hasKwargs = hasKwargs;

assert method.hasExplicitCallProtocol();

setHandle(variable);
}

private static final MethodHandle FROBNICATE = Binder
.from(IRubyObject[].class, ThreadContext.class, IRubyObject[].class, Signature.class)
.invokeStaticQuiet(MethodHandles.lookup(), IRRuntimeHelpers.class, "frobnicateKwargsArgument");

public MethodHandle getHandleFor(int arity) {
if (specificArity != -1 && arity == specificArity) {
return specific;
Expand Down Expand Up @@ -232,8 +210,4 @@ public String toString() {
return getClass().getName() + '@' + Integer.toHexString(hashCode()) + ' ' + method + ' ' + getSignature();
}

public boolean hasKwargs() {
return hasKwargs;
}

}
70 changes: 40 additions & 30 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Expand Up @@ -536,40 +536,50 @@ public static void checkArity(ThreadContext context, StaticScope scope, Object[]
}
}

public static IRubyObject[] frobnicateKwargsArgument(ThreadContext context, IRubyObject[] args,
org.jruby.runtime.Signature signature) {
return frobnicateKwargsArgument(context, args, signature.required());
}

public static IRubyObject[] frobnicateKwargsArgument(ThreadContext context, IRubyObject[] args, int requiredArgsCount) {
// No kwarg because required args slurp them up.
return args.length <= requiredArgsCount ? args : frobnicateKwargsArgument(context, args);
}
int length = args.length;

if (length <= requiredArgsCount) return args;

final IRubyObject maybeKwargs = toHash(args[length - 1], context);

private static IRubyObject[] frobnicateKwargsArgument(final ThreadContext context, IRubyObject[] args) {
final IRubyObject kwargs = toHash(args[args.length - 1], context);
if (kwargs != null) {
if (kwargs.isNil()) { // nil on to_hash is supposed to keep itself as real value so we need to make kwargs hash
if (maybeKwargs != null) {
if (maybeKwargs.isNil()) { // nil on to_hash is supposed to keep itself as real value so we need to make kwargs hash
return ArraySupport.newCopy(args, RubyHash.newSmallHash(context.runtime));
}

DivvyKeywordsVisitor visitor = new DivvyKeywordsVisitor();
// We know toHash makes null, nil, or Hash
((RubyHash) kwargs).visitAll(context, visitor, null);
RubyHash kwargs = (RubyHash) maybeKwargs;

if (visitor.syms == null) {
// no symbols, use empty kwargs hash
visitor.syms = RubyHash.newSmallHash(context.runtime);
if (kwargs.allSymbols()) {
args[length - 1] = kwargs;
} else {
args = homogenizeKwargs(context, args, kwargs);
}
}

if (visitor.others != null) { // rest args exists too expand args
IRubyObject[] newArgs = new IRubyObject[args.length + 1];
System.arraycopy(args, 0, newArgs, 0, args.length);
args = newArgs;
args[args.length - 2] = visitor.others; // opt args
}
args[args.length - 1] = visitor.syms; // kwargs hash
return args;
}

private static IRubyObject[] homogenizeKwargs(ThreadContext context, IRubyObject[] args, RubyHash kwargs) {
DivvyKeywordsVisitor visitor = new DivvyKeywordsVisitor();

// We know toHash makes null, nil, or Hash
kwargs.visitAll(context, visitor, null);

if (visitor.syms == null) {
// no symbols, use empty kwargs hash
visitor.syms = RubyHash.newSmallHash(context.runtime);
}

if (visitor.others != null) { // rest args exists too expand args
IRubyObject[] newArgs = new IRubyObject[args.length + 1];
System.arraycopy(args, 0, newArgs, 0, args.length);
args = newArgs;
args[args.length - 2] = visitor.others; // opt args
}
args[args.length - 1] = visitor.syms; // kwargs hash

return args;
}

Expand Down Expand Up @@ -1356,7 +1366,7 @@ public static DynamicMethod newInterpretedModuleBody(ThreadContext context, IRSc
@JIT
public static DynamicMethod newCompiledModuleBody(ThreadContext context, MethodHandle handle, IRScope irModule, Object rubyContainer) {
RubyModule newRubyModule = newRubyModuleFromIR(context, irModule, rubyContainer);
return new CompiledIRMethod(handle, irModule, Visibility.PUBLIC, newRubyModule, false);
return new CompiledIRMethod(handle, irModule, Visibility.PUBLIC, newRubyModule);
}

private static RubyModule newRubyModuleFromIR(ThreadContext context, IRScope irModule, Object rubyContainer) {
Expand All @@ -1380,7 +1390,7 @@ public static DynamicMethod newInterpretedClassBody(ThreadContext context, IRSco
public static DynamicMethod newCompiledClassBody(ThreadContext context, MethodHandle handle, IRScope irClassBody, Object container, Object superClass) {
RubyModule newRubyClass = newRubyClassFromIR(context.runtime, irClassBody, superClass, container);

return new CompiledIRMethod(handle, irClassBody, Visibility.PUBLIC, newRubyClass, false);
return new CompiledIRMethod(handle, irClassBody, Visibility.PUBLIC, newRubyClass);
}

public static RubyModule newRubyClassFromIR(Ruby runtime, IRScope irClassBody, Object superClass, Object container) {
Expand Down Expand Up @@ -1431,7 +1441,7 @@ public static void defCompiledClassMethod(ThreadContext context, MethodHandle ha
RubyClass rubyClass = checkClassForDef(context, method, obj);

// FIXME: needs checkID and proper encoding to force hard symbol
rubyClass.addMethod(methodName.idString(), new CompiledIRMethod(handle, method, Visibility.PUBLIC, rubyClass, method.receivesKeywordArgs()));
rubyClass.addMethod(methodName.idString(), new CompiledIRMethod(handle, method, Visibility.PUBLIC, rubyClass));
if (!rubyClass.isRefinement()) {
// FIXME: needs checkID and proper encoding to force hard symbol
obj.callMethod(context, "singleton_method_added", methodName);
Expand All @@ -1442,7 +1452,7 @@ public static void defCompiledClassMethod(ThreadContext context, MethodHandle ha
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.getId(), new CompiledIRMethod(variable, specific, specificArity, method, Visibility.PUBLIC, rubyClass, method.receivesKeywordArgs()));
rubyClass.addMethod(method.getId(), new CompiledIRMethod(variable, specific, specificArity, method, Visibility.PUBLIC, rubyClass));

if (!rubyClass.isRefinement()) obj.callMethod(context, "singleton_method_added", method.getName());
}
Expand Down Expand Up @@ -1485,7 +1495,7 @@ public static void defCompiledInstanceMethod(ThreadContext context, MethodHandle
Visibility currVisibility = context.getCurrentVisibility();
Visibility newVisibility = Helpers.performNormalMethodChecksAndDetermineVisibility(runtime, clazz, methodName, currVisibility);

DynamicMethod newMethod = new CompiledIRMethod(handle, method, newVisibility, clazz, method.receivesKeywordArgs());
DynamicMethod newMethod = new CompiledIRMethod(handle, method, newVisibility, clazz);

// FIXME: needs checkID and proper encoding to force hard symbol
Helpers.addInstanceMethod(clazz, methodName, newMethod, currVisibility, context, runtime);
Expand All @@ -1500,7 +1510,7 @@ public static void defCompiledInstanceMethod(ThreadContext context, MethodHandle
Visibility currVisibility = context.getCurrentVisibility();
Visibility newVisibility = Helpers.performNormalMethodChecksAndDetermineVisibility(runtime, clazz, methodName, currVisibility);

DynamicMethod newMethod = new CompiledIRMethod(variable, specific, specificArity, method, newVisibility, clazz, method.receivesKeywordArgs());
DynamicMethod newMethod = new CompiledIRMethod(variable, specific, specificArity, method, newVisibility, clazz);

// FIXME: needs checkID and proper encoding to force hard symbol
Helpers.addInstanceMethod(clazz, methodName, newMethod, currVisibility, context, runtime);
Expand Down
11 changes: 11 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Expand Up @@ -205,6 +205,17 @@ protected void emitScope(IRScope scope, String name, Signature signature, boolea
syntheticEndForStart.put(currentBlockStart, syntheticEnd);
}

if (scope instanceof IRMethod) {
if (scope.receivesKeywordArgs()) {
// pre-frobnicate the args on the way in
m.loadContext();
m.loadArgs();
m.adapter.pushInt(scope.getStaticScope().getSignature().required());
m.invokeIRHelper("frobnicateKwargsArgument", sig(IRubyObject[].class, ThreadContext.class, IRubyObject[].class, int.class));
m.storeArgs();
}
}

for (BasicBlock bb: bbs) {
org.objectweb.asm.Label start = jvm.methodData().getLabel(bb.getLabel());
Label rescueLabel = exceptionTable.get(bb);
Expand Down

0 comments on commit 76c2df8

Please sign in to comment.