Skip to content

Commit

Permalink
Showing 14 changed files with 498 additions and 304 deletions.
9 changes: 7 additions & 2 deletions core/src/main/java/org/jruby/AbstractRubyMethod.java
Original file line number Diff line number Diff line change
@@ -83,7 +83,12 @@ public final IRubyObject op_eql19(ThreadContext context, IRubyObject other) {

@JRubyMethod(name = "eql?", required = 1)
public IRubyObject op_eql(ThreadContext context, IRubyObject other) {
return op_equal(context, other);
return context.runtime.newBoolean( equals(other) );
}

@Override
public boolean equals(Object other) {
return this == other;
}

public abstract AbstractRubyMethod rbClone();
@@ -111,7 +116,7 @@ public IRubyObject source_location(ThreadContext context) {
return runtime.newArray(runtime.newString(filename), runtime.newFixnum(getLine()));
}

return context.runtime.getNil();
return context.nil;
}

public String getFilename() {
87 changes: 59 additions & 28 deletions core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
@@ -1661,22 +1661,37 @@ public boolean isBuiltin(String methodName) {
}

@JRubyMethod(name = "singleton_method_added", module = true, visibility = PRIVATE)
public static IRubyObject singleton_method_added19(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
return context.runtime.getNil();
public static IRubyObject singleton_method_added(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
return context.nil;
}

@JRubyMethod(name = "singleton_method_removed", module = true, visibility = PRIVATE)
public static IRubyObject singleton_method_removed19(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
return context.runtime.getNil();
public static IRubyObject singleton_method_removed(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
return context.nil;
}

@JRubyMethod(name = "singleton_method_undefined", module = true, visibility = PRIVATE)
public static IRubyObject singleton_method_undefined(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
return context.nil;
}

@Deprecated
public static IRubyObject singleton_method_added19(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
return singleton_method_added(context, recv, symbolId, block);
}

@Deprecated
public static IRubyObject singleton_method_removed19(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
return singleton_method_removed(context, recv, symbolId, block);
}

@Deprecated
public static IRubyObject singleton_method_undefined19(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
return context.runtime.getNil();
return singleton_method_undefined(context, recv, symbolId, block);
}

@JRubyMethod(name = "method_missing", rest = true, module = true, visibility = PRIVATE)
public static IRubyObject method_missing19(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
public static IRubyObject method_missing(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
Visibility lastVis = context.getLastVisibility();
CallType lastCallType = context.getLastCallType();

@@ -1687,26 +1702,31 @@ public static IRubyObject method_missing19(ThreadContext context, IRubyObject re
return RubyKernel.methodMissingDirect(context, recv, (RubySymbol)args[0], lastVis, lastCallType, args);
}

@Deprecated
public static IRubyObject method_missing19(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
return method_missing(context, recv, args, block);
}

@JRubyMethod(name = "__send__", omit = true)
public IRubyObject send19(ThreadContext context, IRubyObject arg0, Block block) {
public IRubyObject send(ThreadContext context, IRubyObject arg0, Block block) {
String name = RubySymbol.objectToSymbolString(arg0);

return getMetaClass().finvoke(context, this, name, block);
}
@JRubyMethod(name = "__send__", omit = true)
public IRubyObject send19(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
public IRubyObject send(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
String name = RubySymbol.objectToSymbolString(arg0);

return getMetaClass().finvoke(context, this, name, arg1, block);
}
@JRubyMethod(name = "__send__", omit = true)
public IRubyObject send19(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
public IRubyObject send(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
String name = RubySymbol.objectToSymbolString(arg0);

return getMetaClass().finvoke(context, this, name, arg1, arg2, block);
}
@JRubyMethod(name = "__send__", required = 1, rest = true, omit = true)
public IRubyObject send19(ThreadContext context, IRubyObject[] args, Block block) {
public IRubyObject send(ThreadContext context, IRubyObject[] args, Block block) {
String name = RubySymbol.objectToSymbolString(args[0]);

final int length = args.length - 1;
@@ -2328,13 +2348,15 @@ public RubyBoolean kind_of_p(ThreadContext context, IRubyObject type) {
* k.methods.length #=> 42
*/
public IRubyObject methods(ThreadContext context, IRubyObject[] args) {
return methods(context, args, false);
return methods(context, args, true);
}

@Deprecated
public IRubyObject methods19(ThreadContext context, IRubyObject[] args) {
return methods(context, args, true);
return methods(context, args);
}

public IRubyObject methods(ThreadContext context, IRubyObject[] args, boolean useSymbols) {
public final IRubyObject methods(ThreadContext context, IRubyObject[] args, boolean useSymbols) {
boolean all = args.length == 1 ? args[0].isTrue() : true;
Ruby runtime = getRuntime();
RubyArray methods = runtime.newArray();
@@ -2367,8 +2389,9 @@ public IRubyObject public_methods(ThreadContext context, IRubyObject[] args) {
return getMetaClass().instanceMethods(args, PUBLIC, true, false);
}

@Deprecated
public IRubyObject public_methods19(ThreadContext context, IRubyObject[] args) {
return getMetaClass().instanceMethods(args, PUBLIC, true, false);
return public_methods(context, args);
}

/** rb_obj_protected_methods
@@ -2387,8 +2410,9 @@ public IRubyObject protected_methods(ThreadContext context, IRubyObject[] args)
return getMetaClass().instanceMethods(args, PROTECTED, true, false);
}

@Deprecated
public IRubyObject protected_methods19(ThreadContext context, IRubyObject[] args) {
return getMetaClass().instanceMethods(args, PROTECTED, true, false);
return protected_methods(context, args);
}

/** rb_obj_private_methods
@@ -2407,8 +2431,9 @@ public IRubyObject private_methods(ThreadContext context, IRubyObject[] args) {
return getMetaClass().instanceMethods(args, PRIVATE, true, false);
}

@Deprecated
public IRubyObject private_methods19(ThreadContext context, IRubyObject[] args) {
return getMetaClass().instanceMethods(args, PRIVATE, true, false);
return private_methods(context, args);
}

/** rb_obj_singleton_methods
@@ -2511,12 +2536,13 @@ public RubyArray singleton_methods(ThreadContext context, IRubyObject[] args) {
* m.call #=> "Hello, @iv = Fred"
*/
public IRubyObject method(IRubyObject name) {
return getMetaClass().newMethod(this, name.asJavaString(), true, null);
final RubySymbol symbol = TypeConverter.checkID(name);
return getMetaClass().newMethod(this, symbol.asJavaString(), true, null, true);
}

@Deprecated
public IRubyObject method19(IRubyObject name) {
final RubySymbol symbol = TypeConverter.checkID(name);
return getMetaClass().newMethod(this, symbol.asJavaString(), true, null, true);
return method(name);
}

/** rb_any_to_s
@@ -2688,21 +2714,26 @@ public IRubyObject extend(IRubyObject[] args) {
*
* @return the result of invoking the method identified by aSymbol.
*/
@Deprecated
public IRubyObject send(ThreadContext context, Block block) {
throw context.runtime.newArgumentError(0, 1);
}
public IRubyObject send(ThreadContext context, IRubyObject arg0, Block block) {
return send19(context, arg0, block);
@Deprecated
public IRubyObject send19(ThreadContext context, IRubyObject arg0, Block block) {
return send(context, arg0, block);
}
public IRubyObject send(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
return send19(context, arg0, arg1, block);
@Deprecated
public IRubyObject send19(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
return send(context, arg0, arg1, block);
}
public IRubyObject send(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
return send19(context, arg0, arg1, arg2, block);
@Deprecated
public IRubyObject send19(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
return send(context, arg0, arg1, arg2, block);
}
public IRubyObject send(ThreadContext context, IRubyObject[] args, Block block) {
if (args.length == 0) return send(context, block);
return send19(context, args, block);
@Deprecated
public IRubyObject send19(ThreadContext context, IRubyObject[] args, Block block) {
if (args.length == 0) throw context.runtime.newArgumentError(0, 1);
return send(context, args, block);
}

/** rb_false
10 changes: 5 additions & 5 deletions core/src/main/java/org/jruby/RubyDir.java
Original file line number Diff line number Diff line change
@@ -315,7 +315,7 @@ public static IRubyObject chdir(ThreadContext context, IRubyObject recv, IRubyOb
realPath = dir.canonicalPath();
}

IRubyObject result = null;
IRubyObject result;
if (block.isGiven()) {
// FIXME: Don't allow multiple threads to do this at once
runtime.setCurrentDirectory(realPath);
@@ -799,11 +799,11 @@ public static IRubyObject getHomeDirectoryPath(ThreadContext context, String use
String passwd;
try {
FileInputStream stream = new FileInputStream("/etc/passwd");
int totalBytes = stream.available();
byte[] bytes = new byte[totalBytes];
stream.read(bytes);
int readBytes = stream.available();
byte[] bytes = new byte[readBytes];
readBytes = stream.read(bytes);
stream.close();
passwd = new String(bytes);
passwd = new String(bytes, 0, readBytes);
} catch (IOException ioe) {
return runtime.getNil();
}
56 changes: 29 additions & 27 deletions core/src/main/java/org/jruby/RubyFile.java
Original file line number Diff line number Diff line change
@@ -493,11 +493,11 @@ public IRubyObject truncate(ThreadContext context, IRubyObject len) {
public IRubyObject inspect() {
StringBuilder val = new StringBuilder();
val.append("#<File:").append(getPath());
if(!openFile.isOpen()) {
if (!openFile.isOpen()) {
val.append(" (closed)");
}
val.append('>');
return getRuntime().newString(val.toString());
return RubyString.newString(getRuntime(), val);
}

private static final String URI_PREFIX_STRING = "^(uri|jar|file|classpath):([^:/]{2,}:([^:/]{2,}:)?)?";
@@ -670,7 +670,7 @@ public static String dirname(ThreadContext context, String jfilename) {
}
String name = jfilename;
if (altSeparator != null) {
name = jfilename.replace(altSeparator, separator);
name = replace(jfilename, altSeparator, separator);
}
int minPathLength = 1;
boolean trimmedSlashes = false;
@@ -1447,16 +1447,15 @@ public static JRubyFile file(IRubyObject pathOrFile) {

@Override
public String toString() {
return "RubyFile(" + openFile.getPath() + ", " + openFile.getMode();
return "RubyFile(" + openFile.getPath() + ", " + openFile.getMode() + ')';
}

@Deprecated // private
public static ZipEntry getFileEntry(ZipFile zf, String path) throws IOException {
ZipEntry entry = zf.getEntry(path);
private static ZipEntry getFileEntry(ZipFile jar, String path, final String prefixForNoEntry) throws IOException {
ZipEntry entry = jar.getEntry(path);
if (entry == null) {
// try canonicalizing the path to eliminate . and .. (JRUBY-4760, JRUBY-4879)
String prefix = new File(".").getCanonicalPath();
entry = zf.getEntry(new File(path).getCanonicalPath().substring(prefix.length() + 1).replaceAll("\\\\", "/"));
path = new File(path).getCanonicalPath().substring(prefixForNoEntry.length() + 1);
entry = jar.getEntry(path.replaceAll("\\\\", "/"));
}
return entry;
}
@@ -1466,21 +1465,21 @@ public static ZipEntry getDirOrFileEntry(String jar, String path) throws IOExcep
return getDirOrFileEntry(new JarFile(jar), path);
}

@Deprecated // private
public static ZipEntry getDirOrFileEntry(ZipFile zf, String path) throws IOException {
@Deprecated // not-used
public static ZipEntry getDirOrFileEntry(ZipFile jar, String path) throws IOException {
String dirPath = path + '/';
ZipEntry entry = zf.getEntry(dirPath); // first try as directory
ZipEntry entry = jar.getEntry(dirPath); // first try as directory
if (entry == null) {
if (dirPath.length() == 1) {
return new ZipEntry(dirPath);
}
// try canonicalizing the path to eliminate . and .. (JRUBY-4760, JRUBY-4879)
String prefix = new File(".").getCanonicalPath();
entry = zf.getEntry(new File(dirPath).getCanonicalPath().substring(prefix.length() + 1).replaceAll("\\\\", "/"));
final String prefix = new File(".").getCanonicalPath();
entry = jar.getEntry(new File(dirPath).getCanonicalPath().substring(prefix.length() + 1).replaceAll("\\\\", "/"));

// JRUBY-6119
if (entry == null) {
Enumeration<? extends ZipEntry> entries = zf.entries();
Enumeration<? extends ZipEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
String zipEntry = entries.nextElement().getName();
if (zipEntry.startsWith(dirPath)) {
@@ -1489,9 +1488,8 @@ public static ZipEntry getDirOrFileEntry(ZipFile zf, String path) throws IOExcep
}
}

if (entry == null) {
// try as file
entry = getFileEntry(zf, path);
if (entry == null) { // try as file
entry = getFileEntry(jar, path, prefix);
}
}
return entry;
@@ -1807,11 +1805,9 @@ private static IRubyObject expandPathInternal(ThreadContext context, IRubyObject
}

private static RubyString concatStrings(final Ruby runtime, String s1, String s2, String s3, Encoding enc) {
return RubyString.newString(runtime,
new StringBuilder(s1.length() + s2.length() + s3.length()).
append(s1).append(s2).append(s3).toString(),
enc
);
StringBuilder str = new StringBuilder(s1.length() + s2.length() + s3.length())
.append(s1).append(s2).append(s3);
return new RubyString(runtime, runtime.getString(), str, enc);
}

private static String canonicalizePath(String path) {
@@ -1842,8 +1838,7 @@ public static String[] splitURI(String path) {
URL u = new URL(pathWithoutJarPrefix);
String pathPart = u.getPath();
return new String[] {path.substring(0, path.indexOf(pathPart)), pathPart};
} catch (Exception e2) {
}
} catch (MalformedURLException e2) { /* ignore */ }
}
}
return null;
@@ -1919,7 +1914,7 @@ public static String expandUserPath(ThreadContext context, String path, final bo
* @param stringToCheck
* @return
*/
private static String countSlashes( String stringToCheck ) {
private static String countSlashes(String stringToCheck) {
// Count number of extra slashes in the beginning of the string.
int slashCount = 0;
for (int i = 0; i < stringToCheck.length(); i++) {
@@ -2089,7 +2084,7 @@ private static StringBuilder joinImplInspecting(final String separator,
}
}

// FIXME: MRI and JRuby are both broken here since it does not actually look up
// NOTE: MRI and JRuby are both broken here since it does not actually look up
// File::{SEPARATOR,ALT_SEPARATOR} but merely hardcodes depending on whether we are on Windows.
private static boolean isDirSeparator(char c) {
return c == '/' || Platform.IS_WINDOWS && c == '\\';
@@ -2142,6 +2137,13 @@ private static int lastIndexOf(final CharSequence str, final char c, int index)
return -1;
}

private static String replace(final String str, CharSequence target, CharSequence replace) {
if (target.length() == 1 && replace.length() == 1) {
return str.replace(target.charAt(0), replace.charAt(0));
}
return str.replace(target, replace);
}

private static IRubyObject truncateCommon(ThreadContext context, IRubyObject recv, IRubyObject arg1, IRubyObject arg2) {
RubyString filename = arg1.convertToString(); // TODO: SafeStringValue here
Ruby runtime = context.runtime;
18 changes: 9 additions & 9 deletions core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
@@ -1952,7 +1952,7 @@ public static RubyBoolean kind_of_p(ThreadContext context, IRubyObject self, IRu

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

@JRubyMethod(name = "object_id")
@@ -1962,17 +1962,17 @@ public static IRubyObject object_id(IRubyObject self) {

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

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

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

@JRubyMethod(name = "singleton_methods", optional = 1)
@@ -1982,7 +1982,7 @@ public static RubyArray singleton_methods19(ThreadContext context, IRubyObject s

@JRubyMethod(name = "method", required = 1)
public static IRubyObject method19(IRubyObject self, IRubyObject symbol) {
return ((RubyBasicObject)self).method19(symbol);
return ((RubyBasicObject)self).method(symbol);
}

@JRubyMethod(name = "to_s")
@@ -1997,19 +1997,19 @@ public static IRubyObject extend(IRubyObject self, IRubyObject[] args) {

@JRubyMethod(name = {"send"}, omit = true)
public static IRubyObject send19(ThreadContext context, IRubyObject self, IRubyObject arg0, Block block) {
return ((RubyBasicObject)self).send19(context, arg0, block);
return ((RubyBasicObject)self).send(context, arg0, block);
}
@JRubyMethod(name = {"send"}, omit = true)
public static IRubyObject send19(ThreadContext context, IRubyObject self, IRubyObject arg0, IRubyObject arg1, Block block) {
return ((RubyBasicObject)self).send19(context, arg0, arg1, block);
return ((RubyBasicObject)self).send(context, arg0, arg1, block);
}
@JRubyMethod(name = {"send"}, omit = true)
public static IRubyObject send19(ThreadContext context, IRubyObject self, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
return ((RubyBasicObject)self).send19(context, arg0, arg1, arg2, block);
return ((RubyBasicObject)self).send(context, arg0, arg1, arg2, block);
}
@JRubyMethod(name = {"send"}, required = 1, rest = true, omit = true)
public static IRubyObject send19(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) {
return ((RubyBasicObject)self).send19(context, args, block);
return ((RubyBasicObject)self).send(context, args, block);
}

@JRubyMethod(name = "nil?")
77 changes: 49 additions & 28 deletions core/src/main/java/org/jruby/RubyMethod.java
Original file line number Diff line number Diff line change
@@ -143,34 +143,51 @@ public RubyFixnum arity() {
return getRuntime().newFixnum(value);
}

@JRubyMethod(name = "eql?", required = 1)
public IRubyObject op_eql(ThreadContext context, IRubyObject other) {
return op_equal(context, other);
}

@Override
@JRubyMethod(name = "==", required = 1)
public RubyBoolean op_equal(ThreadContext context, IRubyObject other) {
return context.runtime.newBoolean( equals(other) );
}

@Override
public boolean equals(Object other) {
if (!(other instanceof RubyMethod)) {
return context.runtime.getFalse();
return false;
}
if (method instanceof ProcMethod) {
return context.runtime.newBoolean(((ProcMethod) method).isSame(((RubyMethod) other).getMethod()));
return ((ProcMethod) method).isSame(((RubyMethod) other).getMethod());
}
RubyMethod otherMethod = (RubyMethod)other;
return context.runtime.newBoolean(receiver == otherMethod.receiver &&
originModule == otherMethod.originModule &&
(isMethodMissingMatch(otherMethod.getMethod().getRealMethod()) || isSerialMatch(otherMethod.getMethod().getRealMethod()))
);
return receiver == otherMethod.receiver && originModule == otherMethod.originModule &&
( isSerialMatch(otherMethod.method) || isMethodMissingMatch(otherMethod.getMethod().getRealMethod()) );
}

private boolean isMethodMissingMatch(DynamicMethod other) {
return (method.getRealMethod() instanceof RubyModule.RespondToMissingMethod) &&
((RubyModule.RespondToMissingMethod)method.getRealMethod()).equals(other);
((RubyModule.RespondToMissingMethod) method.getRealMethod()).equals(other);
}

private boolean isSerialMatch(DynamicMethod otherMethod) {
return method.getRealMethod().getSerialNumber() == otherMethod.getRealMethod().getSerialNumber();
}

@JRubyMethod(name = "eql?", required = 1)
public IRubyObject op_eql(ThreadContext context, IRubyObject other) {
return op_equal(context, other);
@JRubyMethod
public RubyFixnum hash(ThreadContext context) {
return context.runtime.newFixnum(hashCodeImpl());
}

@Override
public int hashCode() {
return (int) hashCodeImpl();
}

private long hashCodeImpl() {
return receiver.hashCode() * method.getRealMethod().getSerialNumber();
}

@JRubyMethod(name = "clone")
@@ -217,36 +234,40 @@ public RubyUnboundMethod unbind() {
@JRubyMethod(name = {"inspect", "to_s"})
@Override
public IRubyObject inspect() {
StringBuilder buf = new StringBuilder("#<");
char delimeter = '#';

buf.append(getMetaClass().getRealClass().getName()).append(": ");
StringBuilder str = new StringBuilder(24).append("#<");
char sharp = '#';

str.append(getMetaClass().getRealClass().getName()).append(": ");

if (implementationModule.isSingleton()) {
IRubyObject attached = ((MetaClass) implementationModule).getAttached();
if (receiver == null) {
buf.append(implementationModule.inspect().toString());
str.append(implementationModule.inspect().toString());
} else if (receiver == attached) {
buf.append(attached.inspect().toString());
delimeter = '.';
str.append(attached.inspect().toString());
sharp = '.';
} else {
buf.append(receiver.inspect().toString());
buf.append('(').append(attached.inspect().toString()).append(')');
delimeter = '.';
str.append(receiver.inspect().toString());
str.append('(').append(attached.inspect().toString()).append(')');
sharp = '.';
}
} else {
buf.append(originModule.getName());

str.append(originModule.getName());
if (implementationModule != originModule) {
buf.append('(').append(implementationModule.getName()).append(')');
str.append('(').append(implementationModule.getName()).append(')');
}
}

str.append(sharp).append(methodName); // (real-name) if alias
final String realName= method.getRealMethod().getName();
if ( realName != null && ! methodName.equals(realName) ) {
str.append('(').append(realName).append(')');
}
str.append('>');

buf.append(delimeter).append(methodName).append('>');

RubyString str = RubyString.newString(getRuntime(), buf);
str.setTaint(isTaint());
return str;
RubyString res = RubyString.newString(getRuntime(), str);
res.setTaint(isTaint());
return res;
}

@JRubyMethod
231 changes: 134 additions & 97 deletions core/src/main/java/org/jruby/RubyTime.java

Large diffs are not rendered by default.

56 changes: 37 additions & 19 deletions core/src/main/java/org/jruby/RubyUnboundMethod.java
Original file line number Diff line number Diff line change
@@ -82,19 +82,32 @@ public static RubyClass defineUnboundMethodClass(Ruby runtime) {
return newClass;
}

@JRubyMethod(name = "==", required = 1)
@Override
@JRubyMethod(name = "==", required = 1)
public RubyBoolean op_equal(ThreadContext context, IRubyObject other) {
if (!(other instanceof AbstractRubyMethod)) {
return context.runtime.getFalse();
}
return context.runtime.newBoolean( equals(other) );
}

@Override
public boolean equals(Object other) {
if (!(other instanceof AbstractRubyMethod)) return false;
if (method instanceof ProcMethod) {
return context.runtime.newBoolean(((ProcMethod) method).isSame(((AbstractRubyMethod) other).getMethod()));
return ((ProcMethod) method).isSame(((AbstractRubyMethod) other).getMethod());
}
AbstractRubyMethod otherMethod = (AbstractRubyMethod)other;
return context.runtime.newBoolean(
originModule == otherMethod.originModule &&
method.getRealMethod().getSerialNumber() == otherMethod.method.getRealMethod().getSerialNumber());
AbstractRubyMethod otherMethod = (AbstractRubyMethod) other;
return originModule == otherMethod.originModule &&
method.getRealMethod().getSerialNumber() == otherMethod.method.getRealMethod().getSerialNumber();
}

@JRubyMethod
public RubyFixnum hash(ThreadContext context) {
return context.runtime.newFixnum(hashCode());
}

@Override
public int hashCode() {
long serial = method.getRealMethod().getSerialNumber();
return 997 * ((int) (serial >> 32) ^ (int) serial & 0xFF);
}

@JRubyMethod
@@ -115,26 +128,31 @@ public RubyUnboundMethod rbClone() {
@JRubyMethod(name = {"inspect", "to_s"})
@Override
public IRubyObject inspect() {
StringBuilder buf = new StringBuilder("#<");
char delimeter = '#';
StringBuilder str = new StringBuilder(24).append("#<");
char sharp = '#';

buf.append(getMetaClass().getRealClass().getName()).append(": ");
str.append(getMetaClass().getRealClass().getName()).append(": ");

if (implementationModule.isSingleton()) {
buf.append(implementationModule.inspect().toString());
str.append(implementationModule.inspect().toString());
} else {
buf.append(originModule.getName());
str.append(originModule.getName());

if (implementationModule != originModule) {
buf.append('(').append(implementationModule.getName()).append(')');
str.append('(').append(implementationModule.getName()).append(')');
}
}

buf.append(delimeter).append(methodName).append('>');
str.append(sharp).append(methodName); // (real-name) if alias
final String realName= method.getRealMethod().getName();
if ( realName != null && ! methodName.equals(realName) ) {
str.append('(').append(realName).append(')');
}
str.append('>');

RubyString str = getRuntime().newString(buf.toString());
str.setTaint(isTaint());
return str;
RubyString res = RubyString.newString(getRuntime(), str);
res.setTaint(isTaint());
return res;
}

@JRubyMethod
175 changes: 93 additions & 82 deletions core/src/main/java/org/jruby/ext/bigdecimal/RubyBigDecimal.java
Original file line number Diff line number Diff line change
@@ -1404,24 +1404,28 @@ public IRubyObject round(ThreadContext context, IRubyObject scale, IRubyObject m
//this relies on the Ruby rounding enumerations == Java ones, which they (currently) all are
private static RoundingMode javaRoundingModeFromRubyRoundingMode(Ruby runtime, IRubyObject arg) {
if (arg instanceof RubySymbol) {
RubySymbol roundingModeSymbol = (RubySymbol) arg;
String roundingModeString = roundingModeSymbol.asJavaString();
if (roundingModeString.equals("up")) {
return RoundingMode.UP;
} else if (roundingModeString.equals("down") || roundingModeString.equals("truncate")) {
return RoundingMode.DOWN;
} else if (roundingModeString.equals("half_up") || roundingModeString.equals("default")) {
return RoundingMode.HALF_UP;
} else if (roundingModeString.equals("half_down")) {
return RoundingMode.HALF_DOWN;
} else if (roundingModeString.equals("half_even") || roundingModeString.equals("banker")) {
return RoundingMode.HALF_EVEN;
} else if (roundingModeString.equals("ceiling") || roundingModeString.equals("ceil")) {
return RoundingMode.CEILING;
} else if (roundingModeString.equals("floor")) {
return RoundingMode.FLOOR;
} else {
throw runtime.newArgumentError("invalid rounding mode");
String roundingMode = ((RubySymbol) arg).asJavaString();
switch (roundingMode) {
case "up" :
return RoundingMode.UP;
case "down" :
case "truncate" :
return RoundingMode.DOWN;
case "half_up" :
case "default" :
return RoundingMode.HALF_UP;
case "half_down" :
return RoundingMode.HALF_DOWN;
case "half_even" :
case "banker" :
return RoundingMode.HALF_EVEN;
case "ceiling" :
case "ceil" :
return RoundingMode.CEILING;
case "floor" :
return RoundingMode.FLOOR;
default :
throw runtime.newArgumentError("invalid rounding mode");
}
} else {
try {
@@ -1534,64 +1538,51 @@ public IRubyObject to_r(ThreadContext context) {

int scale = value.scale();
BigInteger numerator = value.scaleByPowerOfTen(scale).toBigInteger();
BigInteger denominator = BigInteger.valueOf((long)Math.pow(10, scale));
BigInteger denominator = BigInteger.valueOf((long) Math.pow(10, scale));

return RubyRational.newInstance(context, context.runtime.getRational(), RubyBignum.newBignum(context.runtime, numerator), RubyBignum.newBignum(context.runtime, denominator));
}

@Deprecated // not-used
public IRubyObject to_int19() {
return to_int();
}

private String removeTrailingZeroes(String in) {
while(in.length() > 0 && in.charAt(in.length()-1)=='0') {
in = in.substring(0, in.length()-1);
}
return in;
private static String removeTrailingZeroes(final String str) {
int l = str.length();
while (l > 0 && str.charAt(l-1) == '0') l--;
return str.substring(0, l);
}

public static boolean formatHasLeadingPlus(String format) {
return format.startsWith("+");
return format.length() > 0 && format.charAt(0) == '+';
}

public static boolean formatHasLeadingSpace(String format) {
return format.startsWith(" ");
return format.length() > 0 && format.charAt(0) == ' ';
}

public static boolean formatHasFloatingPointNotation(String format) {
return format.endsWith("F");
return format.length() > 0 && format.charAt(format.length()-1) == 'F';
}

public static int formatFractionalDigitGroups(String format) {
Matcher m = Pattern.compile("(\\+| )?(\\d+)(E|F)?").matcher(format);

return m.matches() ? Integer.parseInt(m.group(2)) : 0;
}
private static final Pattern FRACTIONAL_DIGIT_GROUPS = Pattern.compile("(\\+| )?(\\d+)(E|F)?");

private static String firstArgument(IRubyObject[] args) {
if ( args.length == 0 ) return null;
final IRubyObject arg = args[0];
return arg.isNil() ? null : arg.toString();
public static int formatFractionalDigitGroups(String format) {
Matcher match = FRACTIONAL_DIGIT_GROUPS.matcher(format);
return match.matches() ? Integer.parseInt(match.group(2)) : 0;
}

private static boolean posSpace(String arg) {
if ( arg == null ) return false;
return formatHasLeadingSpace(arg);
return arg == null ? false : formatHasLeadingSpace(arg);
}

private static boolean posSign(String arg) {
if ( arg == null ) return false;
return formatHasLeadingPlus(arg) || posSpace(arg);
}

private static boolean asEngineering(String arg) {
if ( arg == null ) return true;
return ! formatHasFloatingPointNotation(arg);
return arg == null ? false : (formatHasLeadingPlus(arg) || posSpace(arg));
}

private static int groups(String arg) {
if (arg == null) return 0;
return formatFractionalDigitGroups(arg);
return arg == null ? 0 : formatFractionalDigitGroups(arg);
}

private boolean isZero() {
@@ -1614,81 +1605,99 @@ private String unscaledValue() {
return value.abs().unscaledValue().toString();
}

private String sign(String arg, int signum) {
private static String sign(String arg, int signum) {
return signum == -1 ? "-" : (signum == 1 ? (posSign(arg) ? (posSpace(arg) ? " " : "+") : "") : "");
}

private CharSequence engineeringValue(String arg) {
private CharSequence engineeringValue(final String arg) {
final String s = removeTrailingZeroes(unscaledValue());

StringBuilder build = new StringBuilder();
build.append( sign(arg, value.signum()) ).append("0.");
String s = removeTrailingZeroes(unscaledValue());

if (groups(arg) == 0) {
build.append("".equals(s) ? "0" : s);
final int groups = groups(arg);
if (groups == 0) {
build.append(s.isEmpty() ? "0" : s);
} else {
int length = s.length();
final int len = s.length();
String sep = "";

for (int index = 0; index < length; index += groups(arg)) {
int next = index + groups(arg);
build.append(sep).append(s.substring(index, next > length ? length : next));
for (int index = 0; index < len; index += groups) {
int next = index + groups;
build.append(sep).append(s.substring(index, next > len ? len : next));
sep = " ";
}
}
build.append('E').append(getExponent());
return build;
}

private CharSequence floatingPointValue(String arg) {
private CharSequence floatingPointValue(final String arg) {
List<String> values = StringSupport.split(value.abs().stripTrailingZeros().toPlainString(), '.');
String whole = values.size() > 0 ? values.get(0) : "0";
String after = values.size() > 1 ? values.get(1) : "0";
StringBuilder build = new StringBuilder().append(sign(arg, value.signum()));
final String whole = values.size() > 0 ? values.get(0) : "0";
final String after = values.size() > 1 ? values.get(1) : "0";

StringBuilder build = new StringBuilder();
build.append( sign(arg, value.signum()) );

if (groups(arg) == 0) {
final int groups = groups(arg);
if (groups == 0) {
build.append(whole);
if (after != null) build.append('.').append(after);
} else {
int index = 0;
int index = 0, len = whole.length();
String sep = "";
while (index < whole.length()) {
int next = index + groups(arg);
if (next > whole.length()) next = whole.length();
while (index < len) {
int next = index + groups;
if (next > len) next = len;

build.append(sep).append(whole.substring(index, next));
sep = " ";
index += groups(arg);
index += groups;
}
if (null != after) {
if (after != null) {
build.append('.');
index = 0;
index = 0; len = after.length();
sep = "";
while (index < after.length()) {
int next = index + groups(arg);
if (next > after.length()) next = after.length();
while (index < len) {
int next = index + groups;
if (next > len) next = len;

build.append(sep).append(after.substring(index, next));
sep = " ";
index += groups(arg);
index += groups;
}
}
}
return build;
}

@JRubyMethod(optional = 1)
public IRubyObject to_s(IRubyObject[] args) {
if ( isNaN() ) return getRuntime().newString("NaN");
if ( isInfinity() ) return getRuntime().newString(infinityString());
if ( isZero() ) return getRuntime().newString(zeroSign < 0 ? "-0.0" : "0.0");
@JRubyMethod
public RubyString to_s(ThreadContext context) {
return toStringImpl(context.runtime, null);
}

@JRubyMethod
public RubyString to_s(ThreadContext context, IRubyObject arg) {
return toStringImpl(context.runtime, arg.isNil() ? null : arg.toString());
}

String arg = firstArgument(args);
private RubyString toStringImpl(final Ruby runtime, String arg) {
if ( isNaN() ) return runtime.newString("NaN");
if ( isInfinity() ) return runtime.newString(infinityString());
if ( isZero() ) return runtime.newString(zeroSign < 0 ? "-0.0" : "0.0");

return getRuntime().newString(
( asEngineering(arg) ? engineeringValue(arg) : floatingPointValue(arg) ).toString()
boolean asEngineering = arg == null || ! formatHasFloatingPointNotation(arg);

return RubyString.newString(runtime,
( asEngineering ? engineeringValue(arg) : floatingPointValue(arg) )
);
}

@Deprecated
public IRubyObject to_s(IRubyObject[] args) {
return toStringImpl(getRuntime(), args.length == 0 ? null : (args[0].isNil() ? null : args[0].toString()));
}

// Note: #fix has only no-arg form, but truncate allows optional parameter.

@JRubyMethod
@@ -1702,8 +1711,10 @@ private RubyBigDecimal truncateInternal(int arg) {

int precision = value.precision() - value.scale() + arg;

if (precision > 0) return new RubyBigDecimal(getRuntime(),
if (precision > 0) {
return new RubyBigDecimal(getRuntime(),
value.round(new MathContext(precision, RoundingMode.DOWN)));
}

return new RubyBigDecimal(getRuntime(), BigDecimal.ZERO); // FIXME: proper sign
}
5 changes: 2 additions & 3 deletions core/src/main/java/org/jruby/ext/socket/RubyUDPSocket.java
Original file line number Diff line number Diff line change
@@ -47,7 +47,6 @@
import java.nio.channels.AlreadyBoundException;
import java.nio.channels.Channel;
import java.nio.channels.DatagramChannel;
import java.nio.channels.IllegalBlockingModeException;
import java.nio.channels.NotYetConnectedException;

import jnr.constants.platform.AddressFamily;
@@ -190,8 +189,8 @@ else if (host instanceof RubyFixnum) {
catch (BindException e) {
throw runtime.newErrnoEADDRFromBindException(e);
}
catch (AlreadyBoundException abe) {
throw runtime.newErrnoEINVALError("bind");
catch (AlreadyBoundException e) {
throw runtime.newErrnoEINVALError("bind(2) for " + host.inspect() + " port " + port);
}
catch (SocketException e) {
final String message = e.getMessage();
Original file line number Diff line number Diff line change
@@ -123,4 +123,12 @@ public DynamicMethod getRealMethod() {
public long getSerialNumber() {
return oldMethod.getSerialNumber();
}

@Override
public RubyModule getDefinedClass() {
RubyModule definedClass = this.definedClass;
if (definedClass != null) return definedClass;
return oldMethod.getDefinedClass();
}

}
Original file line number Diff line number Diff line change
@@ -108,4 +108,12 @@ public DynamicMethod getRealMethod() {
public Arity getArity() {
return method.getArity();
}

@Override
public RubyModule getDefinedClass() {
RubyModule definedClass = this.definedClass;
if (definedClass != null) return definedClass;
return method.getDefinedClass();
}

}
57 changes: 56 additions & 1 deletion test/jruby/test_method.rb
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ def test_function_break
def obj.broken_method
break # TODO this is a SyntaxError on MRI 2.2.2
end
assert_raise(LocalJumpError){obj.broken_method}
assert_raise(LocalJumpError){ obj.broken_method }
end

module Methods
@@ -74,4 +74,59 @@ def test_callee # (passing) part from *mri/ruby/test_method.rb*
assert_nil(eval("class TestCallee; __callee__; end"))
end

def test_eq # (modified) part from *mri/ruby/test_method.rb*
o = Object.new
class << o
def foo; end
alias muu foo
def baz; end
alias bar baz
end
assert_not_equal(o.method(:foo), nil)
m = o.method(:foo)
def o.foo; end
assert_not_equal(o.method(:foo), m)
assert_equal(o.method(:foo), o.method(:foo))
assert_equal(o.method(:baz), o.method(:bar))
assert_not_equal(o.method(:foo), o.method(:baz))
assert_not_equal(o.method(:foo), o.method(:muu))

assert_equal(String.instance_method(:to_s), String.instance_method(:to_str))

assert_not_equal([0].method(:map), [].method(:map))
end

def test_hash
o = Object.new
def o.foo; end
class << o
alias bar foo
end

hash = o.method(:foo).hash
assert_kind_of(Integer, hash)
assert_equal(hash, o.method(:bar).hash)

hash = String.instance_method(:to_s).hash
assert_kind_of(Integer, hash)
assert_equal(hash, String.instance_method(:to_str).hash)

assert_not_equal([0].method(:map).hash, [].method(:map).hash)
end

def test_inspect
c = Class.new do
def foo; end; alias bar foo
end
m = c.new.method(:foo)
assert_equal("#<Method: #{c.inspect}#foo>", m.inspect)
m = c.instance_method(:foo)
assert_equal("#<UnboundMethod: #{c.inspect}#foo>", m.inspect)

m = c.new.method(:bar)
assert_equal("#<Method: #{c.inspect}#bar(foo)>", m.inspect)
m = c.instance_method(:bar)
assert_equal("#<UnboundMethod: #{c.inspect}#bar(foo)>", m.inspect)
end

end
5 changes: 2 additions & 3 deletions test/mri/excludes/TestMethod.rb
Original file line number Diff line number Diff line change
@@ -4,10 +4,9 @@
exclude :test_clone, "needs investigation"
exclude :test_define_method_visibility, "needs investigation"
exclude :test_define_method_with_symbol, "scope changes in define_method methods?"
exclude :test_eq, "needs investigation"
exclude :test_eq, "weird logic for != when patching an object retuned from o.method, likely irrelevant"
exclude :test_gced_bmethod, "often 'Timeout::Error: execution of assert_normal_exit expired' on CI"
exclude :test_hash, "needs investigation"
exclude :test_inspect, "needs investigation"
exclude :test_hash, "won't pass since Array#map is not a Array#collect alias as in MRI"
exclude :test_orphan_callee, "needs investigation"
exclude :test_singleton_method, "needs investigation"
exclude :test_splat_long_array, "passes locally but fails on travis OOME"

0 comments on commit adaea9b

Please sign in to comment.