Skip to content

Commit

Permalink
Showing 13 changed files with 147 additions and 77 deletions.
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/RubyHash.java
Original file line number Diff line number Diff line change
@@ -1015,7 +1015,7 @@ protected void op_asetForString(Ruby runtime, RubyString key, IRubyObject value)
entry.value = value;
} else {
checkIterating();
if (!key.isFrozen()) key = runtime.freezeAndDedupString(key);
if (!key.isFrozen()) key = (RubyString)key.dupFrozen();
internalPut(key, value, false);
}
}
@@ -1026,7 +1026,7 @@ protected void op_asetSmallForString(Ruby runtime, RubyString key, IRubyObject v
entry.value = value;
} else {
checkIterating();
if (!key.isFrozen()) key = runtime.freezeAndDedupString(key);
if (!key.isFrozen()) key = (RubyString)key.dupFrozen();
internalPutSmall(key, value, false);
}
}
11 changes: 8 additions & 3 deletions core/src/main/java/org/jruby/RubyRational.java
Original file line number Diff line number Diff line change
@@ -150,9 +150,14 @@ static IRubyObject newRationalCanonicalize(ThreadContext context, IRubyObject x)
/** rb_rational_new
*
*/
private static IRubyObject newRationalCanonicalize(ThreadContext context, IRubyObject x, IRubyObject y) {
static IRubyObject newRationalCanonicalize(ThreadContext context, IRubyObject x, IRubyObject y) {
return canonicalizeInternal(context, context.runtime.getRational(), x, y);
}

public static IRubyObject newRationalCanonicalize(ThreadContext context, long x, long y) {
Ruby runtime = context.runtime;
return canonicalizeInternal(context, runtime.getRational(), runtime.newFixnum(x), runtime.newFixnum(y));
}

/** f_rational_new2
*
@@ -227,7 +232,7 @@ private static IRubyObject canonicalizeInternal(ThreadContext context, IRubyObje
num = f_negate(context, num);
den = f_negate(context, den);
} else if (res == RubyFixnum.zero(runtime)) {
throw runtime.newZeroDivisionError();
throw runtime.newZeroDivisionError();
}

IRubyObject gcd = f_gcd(context, num, den);
@@ -240,7 +245,7 @@ private static IRubyObject canonicalizeInternal(ThreadContext context, IRubyObje

return new RubyRational(context.runtime, clazz, num, den);
}

/** nurat_s_canonicalize_internal_no_reduce
*
*/
106 changes: 68 additions & 38 deletions core/src/main/java/org/jruby/RubyTime.java
Original file line number Diff line number Diff line change
@@ -37,15 +37,6 @@
***** END LICENSE BLOCK *****/
package org.jruby;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jcodings.specific.USASCIIEncoding;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@@ -58,18 +49,29 @@
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Block;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import static org.jruby.runtime.Visibility.PRIVATE;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.util.RubyDateFormatter;
import org.jruby.runtime.Helpers;
import org.jruby.util.TypeConverter;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.jruby.RubyComparable.invcmp;
import static org.jruby.runtime.Helpers.invokedynamic;
import static org.jruby.runtime.Visibility.PRIVATE;
import static org.jruby.runtime.invokedynamic.MethodNames.OP_CMP;

/** The Time class.
@@ -79,6 +81,9 @@
@JRubyClass(name="Time", include="Comparable")
public class RubyTime extends RubyObject {
public static final String UTC = "UTC";
public static final BigDecimal ONE_MILLION_BD = new BigDecimal(1000000);
public static final BigInteger ONE_MILLION_BI = BigInteger.valueOf(1000000);
public static final BigDecimal ONE_THOUSAND_BD = new BigDecimal(1000);
private DateTime dt;
private long nsec;

@@ -433,7 +438,7 @@ public IRubyObject initialize_copy(IRubyObject original) {

@JRubyMethod
public RubyTime succ() {
return newTime(getRuntime(),dt.plusSeconds(1));
return newTime(getRuntime(), dt.plusSeconds(1));
}

@JRubyMethod(name = {"gmtime", "utc"})
@@ -744,8 +749,10 @@ public RubyInteger nsec() {

@JRubyMethod
public IRubyObject to_r(ThreadContext context) {
IRubyObject rational = to_f().to_r(context);
return rational;
return RubyRational.newRationalCanonicalize(
context,
getTimeInMillis() * 1000000 + nsec,
1000000000);
}

@JRubyMethod(name = {"usec", "tv_usec"})
@@ -887,7 +894,7 @@ public Date getJavaDate() {
@Override
public RubyFixnum hash() {
// modified to match how hash is calculated in 1.8.2
return getRuntime().newFixnum((int)(((dt.getMillis() / 1000) ^ microseconds()) << 1) >> 1);
return getRuntime().newFixnum((int) (((dt.getMillis() / 1000) ^ microseconds()) << 1) >> 1);
}

@JRubyMethod(name = "_dump", optional = 1)
@@ -1020,41 +1027,64 @@ public static IRubyObject at(ThreadContext context, IRubyObject recv, IRubyObjec
time = new RubyTime(runtime, (RubyClass) recv, other.dt);
time.setNSec(other.getNSec());
} else {
time = new RubyTime(runtime, (RubyClass) recv,
new DateTime(0L, getLocalTimeZone(runtime)));

long seconds = RubyNumeric.num2long(arg);
long millisecs = 0;
long nanosecs = 0;
long nanosecs;
long millisecs;

// In the case of two arguments, MRI will discard the portion of
// the first argument after a decimal point (i.e., "floor").
// However in the case of a single argument, any portion after
// the decimal point is honored.
if (arg instanceof RubyFloat || arg instanceof RubyRational) {
double dbl = RubyNumeric.num2dbl(arg);
long nano;

nano = Math.round((dbl - seconds) * 1000000000);
if (arg instanceof RubyFloat) {
// use integral and decimal forms to calculate nanos
long seconds = RubyNumeric.num2long(arg);
double dbl = RubyNumeric.num2dbl(arg);

nano = (long)((dbl - seconds) * 1000000000);

if (dbl < 0 && nano != 0) {
nano += 1000000000;
}

millisecs = seconds * 1000 + nano / 1000000;
nanosecs = nano % 1000000;
} else {
// use Rational numerator and denominator to calculate nanos
RubyRational rational = (RubyRational) arg;

// These could have rounding errors if numerator or denominator are not integral and < long. Can they be?
long numerator = rational.numerator(context).convertToInteger().getLongValue();
long denominator = rational.denominator(context).convertToInteger().getLongValue();

BigDecimal accurateSeconds = new BigDecimal(numerator).divide(new BigDecimal(denominator));
BigDecimal accurateMillis = accurateSeconds.multiply(ONE_THOUSAND_BD);
BigInteger integralMillis = accurateMillis.toBigInteger();
BigInteger remainingNanos = accurateMillis.multiply(ONE_MILLION_BD).toBigInteger().subtract(integralMillis.multiply(ONE_MILLION_BI));

if (dbl < 0 && nano != 0) {
nano += 1000000000;
millisecs = integralMillis.longValue();
nanosecs = remainingNanos.longValue();
}
millisecs = nano / 1000000;
nanosecs = nano % 1000000;
} else {
nanosecs = 0;
millisecs = RubyNumeric.num2long(arg) * 1000;
}

try {
time = new RubyTime(runtime, (RubyClass) recv,
new DateTime(millisecs, getLocalTimeZone(runtime)));
}
// joda-time 2.5 can throw this exception - seen locally
catch(ArithmeticException e1) {
throw runtime.newRangeError(e1.getMessage());
}
// joda-time 2.5 can throw this exception - seen on travis
catch(IllegalFieldValueException e2) {
throw runtime.newRangeError(e2.getMessage());
}

time.setNSec(nanosecs);
try {
time.dt = time.dt.withMillis(seconds * 1000 + millisecs);
}
// joda-time 2.5 can throw this exception - seen locally
catch(ArithmeticException e1) {
throw runtime.newRangeError(e1.getMessage());
}
// joda-time 2.5 can throw this exception - seen on travis
catch(IllegalFieldValueException e2) {
throw runtime.newRangeError(e2.getMessage());
}
}

time.getMetaClass().getBaseCallSite(RubyClass.CS_IDX_INITIALIZE).call(context, recv, time);
3 changes: 3 additions & 0 deletions core/src/main/java/org/jruby/ext/jruby/JRubyUtilLibrary.java
Original file line number Diff line number Diff line change
@@ -76,6 +76,9 @@ public static IRubyObject getObjectSpaceEnabled(IRubyObject recv) {
@JRubyMethod(name = "objectspace=", module = true)
public static IRubyObject setObjectSpaceEnabled(IRubyObject recv, IRubyObject arg) {
Ruby runtime = recv.getRuntime();
if (arg.isTrue()) {
runtime.getWarnings().warn("ObjectSpace impacts performance. See http://wiki.jruby.org/PerformanceTuning#dont-enable-objectspace");
}
runtime.setObjectSpaceEnabled(arg.isTrue());
return runtime.getNil();
}
9 changes: 8 additions & 1 deletion core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -2531,7 +2531,14 @@ public Operand buildHash(HashNode hashNode) {
addInstr(new RuntimeHelperCall(hash, MERGE_KWARGS, new Operand[] { hash, splat}));
continue;
} else {
keyOperand = buildWithOrder(key, hasAssignments);
// TODO: This isn't super pretty. If AST were aware of literal hash string keys being "special"
// it could have an appropriate AST node for frozen string and this code would just go away.
if (key instanceof StrNode) {
StrNode strKey = (StrNode)key;
keyOperand = new FrozenString(strKey.getValue(), strKey.getCodeRange());
} else {
keyOperand = buildWithOrder(key, hasAssignments);
}
}

args.add(new KeyValuePair<>(keyOperand, buildWithOrder(pair.getValue(), hasAssignments)));
2 changes: 2 additions & 0 deletions core/src/main/java/org/jruby/ir/operands/FrozenString.java
Original file line number Diff line number Diff line change
@@ -15,6 +15,8 @@
* This is not an immutable literal because I can gsub!,
* for example, and modify the contents of the string.
* This is not like a Java string.
*
* TODO: This really should be ImmutableLiteral so it can constant propagate, etc.
*/
public class FrozenString extends StringLiteral {
/**
13 changes: 0 additions & 13 deletions core/src/main/java/org/jruby/util/cli/ArgumentProcessor.java
Original file line number Diff line number Diff line change
@@ -387,7 +387,6 @@ private void processArgument() {
config.setCompileMode(RubyInstanceConfig.CompileMode.OFF);
config.setDisableGems(false);
} else if (extendedOption.equals("+T")) {
checkGraalVersion();
Options.PARSER_WARN_GROUPED_EXPRESSIONS.force(Boolean.FALSE.toString());
config.setCompileMode(RubyInstanceConfig.CompileMode.TRUFFLE);
config.setDisableGems(true);
@@ -713,18 +712,6 @@ private void logScriptResolutionFailure(String path) {
}
}

public static void checkGraalVersion() {
final String graalVersion = System.getProperty("graal.version", "unknown");
final String expectedGraalVersion = "0.11-dev";

if (graalVersion.equals("unknown")) {
return;
} else if (!graalVersion.equals(expectedGraalVersion)) {
throw new RuntimeException("This version of JRuby is built against Graal " + expectedGraalVersion +
" but you are using it with version " + graalVersion + " - either update Graal or use with (-J)-original to disable Graal and ignore this error");
}
}

private static final Set<String> KNOWN_PROPERTIES = new HashSet<>();

static {
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/util/io/EncodingUtils.java
Original file line number Diff line number Diff line change
@@ -879,7 +879,7 @@ public static Encoding strTranscode0(ThreadContext context, int argc, IRubyObjec
if ((ecflags & EConvFlags.INVALID_MASK) != 0 && explicitlyInvalidReplace) {
IRubyObject rep = context.nil;
if (!ecopts.isNil()) {
rep = ((RubyHash)ecopts).op_aref(context, runtime.newString("replace"));
rep = ((RubyHash)ecopts).op_aref(context, runtime.newSymbol("replace"));
}
dest = ((RubyString)str).scrub(context, rep, Block.NULL_BLOCK);
if (dest.isNil()) dest = str;
43 changes: 34 additions & 9 deletions core/src/test/java/org/jruby/test/TestMethodFactories.java
Original file line number Diff line number Diff line change
@@ -30,8 +30,10 @@

import org.jruby.CompatVersion;
import org.jruby.Ruby;
import org.jruby.RubyMethod;
import org.jruby.RubyModule;
import org.jruby.anno.JRubyMethod;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

@@ -55,15 +57,6 @@ public void testReflectionMethodFactory() {
confirmMethods(mod);
}

// #1194: ClassFormatError with Nokogiri 1.6.0
public void testVersionedMethods() {
RubyModule mod = runtime.defineModule("GH1194");

mod.defineAnnotatedMethods(VersionedMethods.class);

assertNotNull(mod.searchMethod("method"));
}

private void confirmMethods(RubyModule mod) {
ThreadContext context = runtime.getCurrentContext();

@@ -86,6 +79,29 @@ public static IRubyObject four_arg_method(IRubyObject self, IRubyObject[] obj) {
}
}

public static class GH3463Module {
@JRubyMethod(module = true)
public static IRubyObject a_module_method(IRubyObject self) {
return self;
}
}

// Module methods define a second copy on singleton with proper implClass
// jruby/jruby#3463
public void testModuleMethodOwner() {
RubyModule mod = runtime.defineModule("GH3463Module");

mod.defineAnnotatedMethods(GH3463Module.class);

DynamicMethod method = mod.getSingletonClass().searchMethod("a_module_method");

assertEquals(mod.getSingletonClass(), method.getImplementationClass());

RubyMethod rubyMethod = (RubyMethod)mod.method(runtime.newSymbol("a_module_method"));

assertEquals(mod.getSingletonClass(), rubyMethod.owner(runtime.getCurrentContext()));
}

public static class VersionedMethods {
@JRubyMethod(name = "method", compat = CompatVersion.RUBY1_8)
public static IRubyObject method18(IRubyObject self) {
@@ -96,4 +112,13 @@ public static IRubyObject method19(IRubyObject self) {
return self;
}
}

// #1194: ClassFormatError with Nokogiri 1.6.0
public void testVersionedMethods() {
RubyModule mod = runtime.defineModule("GH1194");

mod.defineAnnotatedMethods(VersionedMethods.class);

assertNotNull(mod.searchMethod("method"));
}
}
12 changes: 12 additions & 0 deletions spec/regression/GH-3402_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# coding: utf-8

require 'rspec'

# https://github.com/jruby/jruby/issues/3402
describe 'String#encode with :replace option' do
it 'returns correct value' do
str = "testing\xC2".encode("UTF-8", :invalid => :replace, :undef => :replace, :replace => "foo123")
expect(str).to eq "testingfoo123"
end
end

9 changes: 9 additions & 0 deletions spec/ruby/core/time/at_spec.rb
Original file line number Diff line number Diff line change
@@ -21,6 +21,15 @@
t = c.at(0)
t.should be_an_instance_of(c)
end

it "roundtrips a Rational produced by #to_r" do
t = Time.now()
t2 = Time.at(t.to_r)

t2.should == t
t2.usec.should == t.usec
t2.nsec.should == t.nsec
end
end

describe "passed Time" do
1 change: 0 additions & 1 deletion spec/tags/ruby/core/time/to_r_tags.txt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -63,15 +63,6 @@ public static Object rootCall(DynamicObject proc, Object... args) {
return Layouts.PROC.getCallTargetForType(proc).call(packArguments(proc, args));
}

public static DynamicObject createRubyProc(DynamicObject procClass, Type type, SharedMethodInfo sharedMethodInfo, CallTarget callTargetForProcs,
CallTarget callTargetForLambdas, MaterializedFrame declarationFrame, InternalMethod method,
Object self, DynamicObject block) {
return createRubyProc(Layouts.CLASS.getInstanceFactory(procClass),
type, sharedMethodInfo, callTargetForProcs,
callTargetForLambdas, declarationFrame, method,
self, block);
}

public static DynamicObject createRubyProc(DynamicObjectFactory instanceFactory, Type type, SharedMethodInfo sharedMethodInfo, CallTarget callTargetForProcs,
CallTarget callTargetForLambdas, MaterializedFrame declarationFrame, InternalMethod method,
Object self, DynamicObject block) {

0 comments on commit e2e862c

Please sign in to comment.