Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

native implementation of set.rb #4690

Merged
merged 16 commits into from Sep 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/Ruby.java
Expand Up @@ -1719,6 +1719,7 @@ private void initBuiltins() {
addLazyBuiltin("tempfile.jar", "tempfile", "org.jruby.ext.tempfile.TempfileLibrary");
addLazyBuiltin("fcntl.rb", "fcntl", "org.jruby.ext.fcntl.FcntlLibrary");
addLazyBuiltin("pathname.jar", "pathname", "org.jruby.ext.pathname.PathnameLibrary");
addLazyBuiltin("set.rb", "set", "org.jruby.ext.set.SetLibrary");

addLazyBuiltin("mathn/complex.jar", "mathn/complex", "org.jruby.ext.mathn.Complex");
addLazyBuiltin("mathn/rational.jar", "mathn/rational", "org.jruby.ext.mathn.Rational");
Expand Down
154 changes: 122 additions & 32 deletions core/src/main/java/org/jruby/RubyArray.java
Expand Up @@ -3483,42 +3483,134 @@ public IRubyObject sort_bang19(ThreadContext context, Block block) {
return sort_bang(context, block);
}

protected IRubyObject sortInternal(final ThreadContext context, boolean honorOverride) {
Ruby runtime = context.runtime;

// One check per specialized fast-path to make the check invariant.
final boolean fixnumBypass = !honorOverride || runtime.getFixnum().isMethodBuiltin("<=>");
final boolean stringBypass = !honorOverride || runtime.getString().isMethodBuiltin("<=>");

protected IRubyObject sortInternal(final ThreadContext context, final boolean honorOverride) {
try {
Arrays.sort(values, begin, begin + realLength, new Comparator() {
public int compare(Object o1, Object o2) {
if (fixnumBypass && o1 instanceof RubyFixnum && o2 instanceof RubyFixnum) {
return compareFixnums((RubyFixnum) o1, (RubyFixnum) o2);
}
if (stringBypass && o1 instanceof RubyString && o2 instanceof RubyString) {
return ((RubyString) o1).op_cmp((RubyString) o2);
}
return compareOthers(context, (IRubyObject)o1, (IRubyObject)o2);
Arrays.sort(values, begin, begin + realLength, new DefaultComparator(context, honorOverride) {
protected int compareGeneric(IRubyObject o1, IRubyObject o2) {
//TODO: ary_sort_check should be done here
return super.compareGeneric(o1, o2);
}
});
} catch (ArrayIndexOutOfBoundsException ex) {
}
catch (ArrayIndexOutOfBoundsException ex) {
throw concurrentModification(context.runtime, ex);
}
return this;
}

// @Deprecated
protected static int compareFixnums(RubyFixnum o1, RubyFixnum o2) {
long a = o1.getLongValue();
long b = o2.getLongValue();
return a > b ? 1 : a == b ? 0 : -1;
return DefaultComparator.compareInteger(o1, o2);
}

// @Deprecated
protected static int compareOthers(ThreadContext context, IRubyObject o1, IRubyObject o2) {
IRubyObject ret = sites(context).op_cmp_sort.call(context, o1, o1, o2);
int n = RubyComparable.cmpint(context, ret, o1, o2);
//TODO: ary_sort_check should be done here
return n;
return DefaultComparator.compareGeneric(context, o1, o2);
}

public static class DefaultComparator implements Comparator<IRubyObject> {

final ThreadContext context;

private final boolean fixnumBypass;
private final boolean stringBypass;

public DefaultComparator(ThreadContext context) {
this(context, true);
}

DefaultComparator(ThreadContext context, final boolean honorOverride) {
this.context = context;
if ( honorOverride && context != null ) {
this.fixnumBypass = !honorOverride || context.runtime.getFixnum().isMethodBuiltin("<=>");
this.stringBypass = !honorOverride || context.runtime.getString().isMethodBuiltin("<=>");
}
else { // no-opt
this.fixnumBypass = false;
this.stringBypass = false;
}
}

/*
DefaultComparator(ThreadContext context, final boolean fixnumBypass, final boolean stringBypass) {
this.context = context;
this.fixnumBypass = fixnumBypass;
this.stringBypass = stringBypass;
} */

public int compare(IRubyObject obj1, IRubyObject obj2) {
if (fixnumBypass && obj1 instanceof RubyFixnum && obj2 instanceof RubyFixnum) {
return compareInteger((RubyFixnum) obj1, (RubyFixnum) obj2);
}
if (stringBypass && obj1 instanceof RubyString && obj2 instanceof RubyString) {
return compareString((RubyString) obj1, (RubyString) obj2);
}
return compareGeneric(obj1, obj2);
}

protected int compareGeneric(IRubyObject o1, IRubyObject o2) {
final ThreadContext context = context();
return compareGeneric(context, sites(context).op_cmp_sort, o1, o2);
}

protected ThreadContext context() {
return context;
}

public static int compareInteger(RubyFixnum o1, RubyFixnum o2) {
long a = o1.getLongValue();
long b = o2.getLongValue();
return a > b ? 1 : a == b ? 0 : -1;
}

public static int compareString(RubyString o1, RubyString o2) {
return o1.op_cmp(o2);
}

public static int compareGeneric(ThreadContext context, IRubyObject o1, IRubyObject o2) {
return compareGeneric(context, sites(context).op_cmp_sort, o1, o2);
}

public static int compareGeneric(ThreadContext context, CallSite op_cmp_sort, IRubyObject o1, IRubyObject o2) {
IRubyObject ret = op_cmp_sort.call(context, o1, o1, o2);
return RubyComparable.cmpint(context, ret, o1, o2);
}

}

static class BlockComparator implements Comparator<IRubyObject> {

final ThreadContext context;

protected final Block block;
protected final IRubyObject self;

private final CallSite gt;
private final CallSite lt;

BlockComparator(ThreadContext context, Block block, CallSite gt, CallSite lt) {
this(context, block, null, gt, lt);
}

BlockComparator(ThreadContext context, Block block, IRubyObject self, CallSite gt, CallSite lt) {
this.context = context == null ? self.getRuntime().getCurrentContext() : context;
this.block = block; this.self = self;
this.gt = gt; this.lt = lt;
}

public int compare(IRubyObject obj1, IRubyObject obj2) {
return RubyComparable.cmpint(context, gt, lt, yieldBlock(obj1, obj2), obj1, obj2);
}

protected final IRubyObject yieldBlock(IRubyObject obj1, IRubyObject obj2) {
final ThreadContext context = context();
return block.yieldArray(context, context.runtime.newArray(obj1, obj2), self);
}

protected final ThreadContext context() {
return context;
}

}

protected IRubyObject sortInternal(final ThreadContext context, final Block block) {
Expand All @@ -3528,15 +3620,13 @@ protected IRubyObject sortInternal(final ThreadContext context, final Block bloc
int length = realLength;

copyInto(newValues, 0);
Arrays.sort(newValues, 0, length, new Comparator() {
CallSite gt = sites(context).op_gt_sort;
CallSite lt = sites(context).op_lt_sort;
public int compare(Object o1, Object o2) {
IRubyObject obj1 = (IRubyObject) o1;
IRubyObject obj2 = (IRubyObject) o2;
IRubyObject ret = block.yieldArray(context, getRuntime().newArray(obj1, obj2), null);
CallSite gt = sites(context).op_gt_sort;
CallSite lt = sites(context).op_lt_sort;
Arrays.sort(newValues, 0, length, new BlockComparator(context, block, gt, lt) {
@Override
public int compare(IRubyObject obj1, IRubyObject obj2) {
//TODO: ary_sort_check should be done here
return RubyComparable.cmpint(context, gt, lt, ret, obj1, obj2);
return super.compare(obj1, obj2);
}
});

Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/org/jruby/RubyModule.java
Expand Up @@ -3522,7 +3522,8 @@ public IRubyObject prepended(ThreadContext context, IRubyObject other) {
return context.nil;
}

final void setConstantVisibility(Ruby runtime, String name, boolean hidden) {
// NOTE: internal API
public final void setConstantVisibility(Ruby runtime, String name, boolean hidden) {
ConstantEntry entry = getConstantMap().get(name);

if (entry == null) {
Expand Down
63 changes: 63 additions & 0 deletions core/src/main/java/org/jruby/ext/set/EnumerableExt.java
@@ -0,0 +1,63 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2016 Karol Bucek
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.ext.set;

import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.runtime.Block;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

/**
* Enumerable#to_set (from require 'set')
*
* @author kares
*/
public abstract class EnumerableExt {

//@JRubyMethod
public static IRubyObject to_set(final ThreadContext context, final IRubyObject self, final Block block) {
final Ruby runtime = context.runtime;

RubySet set = new RubySet(runtime, runtime.getClass("Set"));
set.initialize(context, self, block);
return set; // return runtime.getClass("Set").newInstance(context, self, block);
}

@JRubyMethod(rest = true) // to_set(klass = Set, *args, &block)
public static IRubyObject to_set(final ThreadContext context, final IRubyObject self,
final IRubyObject[] args, final Block block) {

if ( args.length == 0 ) return to_set(context, self, block);

final IRubyObject klass = args[0]; args[0] = self;
return ((RubyClass) klass).newInstance(context, args, block);
}

}