Skip to content

Commit

Permalink
Showing 13 changed files with 330 additions and 66 deletions.
13 changes: 13 additions & 0 deletions core/src/main/java/org/jruby/RubyRational.java
Original file line number Diff line number Diff line change
@@ -189,6 +189,11 @@ private static RubyRational newRationalBang(ThreadContext context, IRubyObject c
private static RubyRational newRationalBang(ThreadContext context, IRubyObject clazz, IRubyObject x) {
return newRationalBang(context, clazz, x, RubyFixnum.one(context.runtime));
}

@Override
public ClassIndex getNativeClassIndex() {
return ClassIndex.RATIONAL;
}

private IRubyObject num;
private IRubyObject den;
@@ -405,6 +410,14 @@ public IRubyObject denominator(ThreadContext context) {
return den;
}

public IRubyObject getNumerator() {
return num;
}

public IRubyObject getDenominator() {
return den;
}

/** f_imul
*
*/
101 changes: 64 additions & 37 deletions core/src/main/java/org/jruby/RubyTime.java
Original file line number Diff line number Diff line change
@@ -82,8 +82,8 @@
@JRubyClass(name="Time", include="Comparable")
public class RubyTime extends RubyObject {
public static final String UTC = "UTC";
@Deprecated // no longer used
public static final BigDecimal ONE_MILLION_BD = BigDecimal.valueOf(1000000);
public static final BigDecimal ONE_BILLION_BD = BigDecimal.valueOf(1000000000);
public static final BigInteger ONE_MILLION_BI = BigInteger.valueOf(1000000);
public static final BigDecimal ONE_THOUSAND_BD = BigDecimal.valueOf(1000);
private DateTime dt;
@@ -259,29 +259,59 @@ public static DateTimeZone getTimeZoneFromUtcOffset(Ruby runtime, IRubyObject ut
// mri: time.c num_exact
private static IRubyObject numExact(Ruby runtime, IRubyObject v) {
IRubyObject tmp;
if (v instanceof RubyFixnum || v instanceof RubyBignum) return v;
if (v.isNil()) exactTypeError(runtime, v);
if (!(v instanceof RubyRational)) { // Default unknown
if (v.respondsTo("to_r")) {
tmp = v.callMethod(runtime.getCurrentContext(), "to_r");
// WTF is this condition for? It responds to to_r and makes something which thinks it is a String?
if (tmp != null && v.respondsTo("to_str")) exactTypeError(runtime, v);
} else {
tmp = TypeConverter.checkIntegerType(runtime, v, "to_int");
if (tmp.isNil()) exactTypeError(runtime, v);
}
v = tmp;
boolean typeError = false;

switch (v.getMetaClass().getClassIndex()) {
case FIXNUM:
case BIGNUM:
return v;

case RATIONAL:
break;

case STRING:
case NIL:
typeError = true;
break;

default:
if ((tmp = v.getMetaClass().finvokeChecked(runtime.getCurrentContext(), v, "to_r")) != null) {
/* test to_int method availability to reject non-Numeric
* objects such as String, Time, etc which have to_r method. */
if (!v.respondsTo("to_int")) {
typeError = true;
break;
}
v = tmp;
break;
}
if (!(tmp = TypeConverter.checkIntegerType(runtime, v, "to_int")).isNil()) {
v = tmp;
break;
}
typeError = true;
break;
}

if (v instanceof RubyFixnum || v instanceof RubyBignum) {
return v;
} else if (v instanceof RubyRational) {
RubyRational r = (RubyRational) v;
if (r.denominator(runtime.getCurrentContext()) == RubyFixnum.newFixnum(runtime, 1)) {
return r.numerator(runtime.getCurrentContext());
}
} else {
exactTypeError(runtime, v);
switch (v.getMetaClass().getClassIndex()) {
case FIXNUM:
case BIGNUM:
return v;

case RATIONAL:
if (((RubyRational) v).getDenominator() == RubyFixnum.one(runtime)) {
v = ((RubyRational) v).getNumerator();
}
break;

default:
typeError = true;
break;
}

if (typeError) {
if (v.isNil()) throw runtime.newTypeError("can't convert nil into an exact number");
throw runtime.newTypeError("can't convert " + v.getMetaClass() + " into an exact number");
}

return v;
@@ -1032,6 +1062,8 @@ public static IRubyObject at(ThreadContext context, IRubyObject recv, IRubyObjec
long nanosecs;
long millisecs;

arg = numExact(runtime, arg);

// 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
@@ -1057,23 +1089,15 @@ public static IRubyObject at(ThreadContext context, IRubyObject recv, IRubyObjec
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();
long numerator = rational.getNumerator().convertToInteger().getLongValue();
long denominator = rational.getDenominator().convertToInteger().getLongValue();

final BigDecimal secs;
if ( numerator <= Integer.MAX_VALUE ) {
secs = new BigDecimal(numerator * 1000);
}
else {
secs = BigDecimal.valueOf(numerator).multiply(ONE_THOUSAND_BD);
}
final BigDecimal millis = secs.divide(BigDecimal.valueOf(denominator), 12, RoundingMode.DOWN);

final BigInteger roundMillis = millis.toBigInteger();
BigInteger remainingNanos = millis.movePointRight(6).toBigInteger().subtract( roundMillis.multiply(ONE_MILLION_BI) );
BigDecimal nanosBD = BigDecimal.valueOf(numerator).divide(BigDecimal.valueOf(denominator), 50, BigDecimal.ROUND_HALF_UP).multiply(ONE_BILLION_BD);
BigInteger millis = nanosBD.divide(ONE_MILLION_BD).toBigInteger();
BigInteger nanos = nanosBD.remainder(ONE_MILLION_BD).toBigInteger();

millisecs = roundMillis.longValue();
nanosecs = remainingNanos.longValue();
millisecs = millis.longValue();
nanosecs = nanos.longValue();
}
} else {
nanosecs = 0;
@@ -1109,6 +1133,9 @@ public static IRubyObject at(ThreadContext context, IRubyObject recv, IRubyObjec
long millisecs;
long nanosecs = 0;

arg1 = numExact(runtime, arg1);
arg2 = numExact(runtime, arg2);

if (arg1 instanceof RubyFloat || arg1 instanceof RubyRational) {
double dbl = RubyNumeric.num2dbl(arg1);
millisecs = (long) (dbl * 1000);
30 changes: 5 additions & 25 deletions core/src/main/java/org/jruby/ext/bigdecimal/RubyBigDecimal.java
Original file line number Diff line number Diff line change
@@ -1518,31 +1518,11 @@ public IRubyObject to_int() {
public IRubyObject to_r(ThreadContext context) {
checkFloatDomain();

RubyArray i = split(context);
long sign = (long)i.get(0);
String digits = (String)i.get(1).toString();
long base = (long)i.get(2);
long power = (long)i.get(3);
long denomi_power = power - digits.length();

IRubyObject bigDigits = RubyBignum.newBignum(getRuntime(), (String)digits).op_mul(context, sign);
RubyBignum numerator;
if(bigDigits instanceof RubyBignum) {
numerator = (RubyBignum)bigDigits;
}
else {
numerator = RubyBignum.newBignum(getRuntime(), bigDigits.toString());
}
IRubyObject num, den;
if(denomi_power < 0) {
num = numerator;
den = RubyFixnum.newFixnum(getRuntime(), base).op_mul(context, RubyFixnum.newFixnum(getRuntime(), -denomi_power));
}
else {
num = numerator.op_pow(context, RubyFixnum.newFixnum(getRuntime(), base).op_mul(context, RubyFixnum.newFixnum(getRuntime(), denomi_power)));
den = RubyFixnum.newFixnum(getRuntime(), 1);
}
return RubyRational.newInstance(context, context.runtime.getRational(), num, den);
int scale = value.scale();
BigInteger numerator = value.scaleByPowerOfTen(scale).toBigInteger();
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));
}

public IRubyObject to_int19() {
95 changes: 95 additions & 0 deletions lib/ruby/truffle/truffle/truffle/execjs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
# code is released under a tri EPL/GPL/LGPL license. You can use it,
# redistribute it and/or modify it under the terms of the:
#
# Eclipse Public License version 1.0
# GNU General Public License version 2
# GNU Lesser General Public License version 2.1

# Designed for ExecJS 2.6.0, but I'll allow it to try to work with anything for now

require 'execjs/runtime'
require 'json'

module ExecJS

class TruffleRuntime < Runtime

JS_MIME_TYPE = 'application/javascript'

class Context < Runtime::Context

STRINGIFY = Truffle::Interop.eval(JS_MIME_TYPE, 'JSON.stringify')
PARSE = Truffle::Interop.eval(JS_MIME_TYPE, 'JSON.parse')

def initialize(runtime, source = '')
exec source
end

def exec(source, options = {})
Truffle::Interop.eval JS_MIME_TYPE, source
nil
end

def eval(source, options = {})
unbox(Truffle::Interop.eval(JS_MIME_TYPE, source))
end

def call(identifier, *args)
function = Truffle::Interop.eval(JS_MIME_TYPE, identifier)
unbox(function.call(function, *args.map { |arg| box(arg) }))
end

private

def unbox(value)
if Truffle::Interop.boxed_primitive?(value)
Truffle::Interop.unbox_value(value)
else
JSON.parse(Truffle::Interop.java_string_to_ruby(STRINGIFY.call(STRINGIFY, value)))
end
end

def box(value)
if Truffle::Interop.boxed_primitive?(value) || value.is_a?(String)
value
else
PARSE.call(PARSE, JSON.generate(value))
end
end

end

def name
'Truffle'
end

def available?
defined?(Truffle::Interop) && Truffle::Interop.eval(JS_MIME_TYPE, 'true')
rescue RubyTruffleError
false
end

end

# Monkey patches

module Runtimes

@runtimes = nil

class << self

alias_method :original_runtimes, :runtimes

def runtimes
@runtimes ||= ([TruffleRuntime.new] + original_runtimes)
end

end

end

self.runtime = Runtimes.autodetect

end
2 changes: 0 additions & 2 deletions spec/tags/ruby/core/time/at_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/tags/ruby/library/bigdecimal/mode_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/tags/ruby/library/bigdecimal/new_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
critical(JRUBY-3749):BigDecimal.new doesn't segfault when using a very large string to build the number
fails:BigDecimal.new creates a new object of class BigDecimal
16 changes: 16 additions & 0 deletions test/truffle/integration/execjs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash

set -e

# Run with for example JRUBY_OPTS='-J-cp ..../trufflejs.jar'

if [[ $JRUBY_OPTS != *"trufflejs.jar"* ]]
then
echo 'No trufflejs.jar found in $JRUBY_OPTS - skipping ExecJS integration test'
exit 0
fi

bin/jruby -X-T bin/gem install execjs -v 2.6.0
ruby -X+T -Ilib/ruby/gems/shared/gems/execjs-2.6.0/lib test/truffle/integration/execjs/checkruntime.rb
ruby -X+T -Ilib/ruby/gems/shared/gems/execjs-2.6.0/lib test/truffle/integration/execjs/simple.rb
ruby -X+T -Ilib/ruby/gems/shared/gems/execjs-2.6.0/lib test/truffle/integration/execjs/coffeescript.rb
4 changes: 4 additions & 0 deletions test/truffle/integration/execjs/checkruntime.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
require 'execjs'
require 'truffle/execjs'

exit 1 unless ExecJS.runtime.name == 'Truffle'
12 changes: 12 additions & 0 deletions test/truffle/integration/execjs/coffeescript.js

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions test/truffle/integration/execjs/coffeescript.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Example from the ExecJS README.md

# Copyright (c) 2015-2016 Sam Stephenson
# Copyright (c) 2015-2016 Josh Peek
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

require "execjs"
require "truffle/execjs"

# Not using the remote file as we can't inflate the data it gives us at the moment
#require "open-uri"
source = File.read(File.join(File.dirname(__FILE__), 'coffeescript.js')) # open("http://coffeescript.org/extras/coffee-script.js").read

context = ExecJS.compile(source)
exit 1 unless context.call("CoffeeScript.compile", "square = (x) -> x * x", bare: true) == "var square;\n\nsquare = function(x) {\n return x * x;\n};\n"
# => "var square;\nsquare = function(x) {\n return x * x;\n};"
27 changes: 27 additions & 0 deletions test/truffle/integration/execjs/simple.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Example from the ExecJS README.md

# Copyright (c) 2015-2016 Sam Stephenson
# Copyright (c) 2015-2016 Josh Peek
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

require "execjs"
require "truffle/execjs"
exit 1 unless ExecJS.eval("'red yellow blue'.split(' ')") == ["red", "yellow", "blue"]
Original file line number Diff line number Diff line change
@@ -113,6 +113,36 @@ public IsBoxedPrimitiveNode(RubyContext context, SourceSection sourceSection) {
this.node = Message.IS_BOXED.createNode();
}

@Specialization
public boolean isBoxedPrimitive(VirtualFrame frame, boolean receiver) {
return receiver;
}

@Specialization
public boolean isBoxedPrimitive(VirtualFrame frame, byte receiver) {
return true;
}

@Specialization
public boolean isBoxedPrimitive(VirtualFrame frame, short receiver) {
return true;
}

@Specialization
public boolean isBoxedPrimitive(VirtualFrame frame, long receiver) {
return true;
}

@Specialization
public boolean isBoxedPrimitive(VirtualFrame frame, float receiver) {
return true;
}

@Specialization
public boolean isBoxedPrimitive(VirtualFrame frame, double receiver) {
return true;
}

@Specialization
public boolean isBoxedPrimitive(VirtualFrame frame, CharSequence receiver) {
return true;
@@ -277,6 +307,36 @@ public UnboxValueNode(RubyContext context, SourceSection sourceSection) {
this.node = Message.UNBOX.createNode();
}

@Specialization
public boolean unbox(VirtualFrame frame, boolean receiver) {
return receiver;
}

@Specialization
public byte unbox(VirtualFrame frame, byte receiver) {
return receiver;
}

@Specialization
public short unbox(VirtualFrame frame, short receiver) {
return receiver;
}

@Specialization
public long unbox(VirtualFrame frame, long receiver) {
return receiver;
}

@Specialization
public float unbox(VirtualFrame frame, float receiver) {
return receiver;
}

@Specialization
public double unbox(VirtualFrame frame, double receiver) {
return receiver;
}

@Specialization
public DynamicObject executeForeign(VirtualFrame frame, CharSequence receiver) {
// TODO CS-21-Dec-15 this shouldn't be needed - we need to convert j.l.String to Ruby's String automatically

0 comments on commit 1e4cfde

Please sign in to comment.