Skip to content

Commit

Permalink
Showing 107 changed files with 2,040 additions and 872 deletions.
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -58,6 +58,12 @@ matrix:
- JT=check_ambiguous_arguments
- SKIP_BUILD=true
jdk: oraclejdk8
- env:
- USE_BUILD_PACK=yes
- JT='test fast'
jdk: oraclejdk8
- env: JT='test mri'
jdk: oraclejdk8
allow_failures:
- env: JT='test mri'
jdk: oraclejdk8
2 changes: 0 additions & 2 deletions core/src/main/java/org/jruby/RubyRegexp.java
Original file line number Diff line number Diff line change
@@ -41,7 +41,6 @@
import org.jcodings.Encoding;
import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.USASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.joni.Matcher;
import org.joni.NameEntry;
import org.joni.Option;
@@ -68,7 +67,6 @@
import org.jruby.util.KCode;
import org.jruby.util.RegexpOptions;
import org.jruby.util.RegexpSupport;
import org.jruby.util.Sprintf;
import org.jruby.util.StringSupport;
import org.jruby.util.TypeConverter;
import org.jruby.util.cli.Options;
Original file line number Diff line number Diff line change
@@ -495,8 +495,6 @@ public IRubyObject rubyEncodingFromObject(IRubyObject str) {
* @return the charset
*/
public Charset charsetForEncoding(Encoding encoding) {
Charset charset = encoding.getCharset();

if (encoding.toString().equals("ASCII-8BIT")) {
return Charset.forName("ISO-8859-1");
}
2 changes: 2 additions & 0 deletions spec/ruby/core/array/delete_if_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
require File.expand_path('../shared/enumeratorize', __FILE__)
require File.expand_path('../shared/delete_if', __FILE__)
require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__)

describe "Array#delete_if" do
@@ -61,4 +62,5 @@
end

it_behaves_like :enumeratorized_with_origin_size, :delete_if, [1,2,3]
it_behaves_like :delete_if, :delete_if
end
2 changes: 2 additions & 0 deletions spec/ruby/core/array/reject_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require File.expand_path('../../../spec_helper', __FILE__)
require File.expand_path('../fixtures/classes', __FILE__)
require File.expand_path('../shared/enumeratorize', __FILE__)
require File.expand_path('../shared/delete_if', __FILE__)
require File.expand_path('../../enumerable/shared/enumeratorized', __FILE__)

describe "Array#reject" do
@@ -112,4 +113,5 @@

it_behaves_like :enumeratorize, :reject!
it_behaves_like :enumeratorized_with_origin_size, :reject!, [1,2,3]
it_behaves_like :delete_if, :reject!
end
27 changes: 27 additions & 0 deletions spec/ruby/core/array/shared/delete_if.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
describe :delete_if, shared: true do
before :each do
@object = [1,2,3]
end

ruby_version_is "2.3" do
it "updates the receiver after all blocks" do
@object.send(@method) do |e|
@object.length.should == 3
true
end
@object.length.should == 0
end
end

ruby_version_is ""..."2.3" do
it "updates the receiver after each true block" do
count = 0
@object.send(@method) do |e|
@object.length.should == (3 - count)
count += 1
true
end
@object.length.should == 0
end
end
end
9 changes: 9 additions & 0 deletions spec/ruby/core/array/shared/keep_if.rb
Original file line number Diff line number Diff line change
@@ -11,6 +11,15 @@
[1, 2, 3].send(@method).should be_an_instance_of(enumerator_class)
end

it "updates the receiver after all blocks" do
a = [1, 2, 3]
a.send(@method) do |e|
a.length.should == 3
false
end
a.length.should == 0
end

before :all do
@object = [1,2,3]
end
19 changes: 19 additions & 0 deletions spec/ruby/core/exception/cause_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
require File.expand_path('../../../spec_helper', __FILE__)

describe "Exception#cause" do
it "returns the active exception when an exception is raised" do
begin
raise Exception, "the cause"
rescue Exception
begin
raise RuntimeError, "the consequence"
rescue RuntimeError => e
e.should be_an_instance_of(RuntimeError)
e.message.should == "the consequence"

e.cause.should be_an_instance_of(Exception)
e.cause.message.should == "the cause"
end
end
end
end
58 changes: 30 additions & 28 deletions spec/ruby/core/fiber/resume_spec.rb
Original file line number Diff line number Diff line change
@@ -13,40 +13,42 @@
fiber2.resume.should == :fiber2
end

# Redmine #595
it "executes the ensure clause" do
rd, wr = IO.pipe

pid = Kernel::fork do
rd.close
f = Fiber.new do
begin
Fiber.yield
ensure
wr.write "executed"
with_feature :fork do
# Redmine #595
it "executes the ensure clause" do
rd, wr = IO.pipe

pid = Kernel::fork do
rd.close
f = Fiber.new do
begin
Fiber.yield
ensure
wr.write "executed"
end
end
end

# The apparent issue is that when Fiber.yield executes, control
# "leaves" the "ensure block" and so the ensure clause should run. But
# control really does NOT leave the ensure block when Fiber.yield
# executes. It merely pauses there. To require ensure to run when a
# Fiber is suspended then makes ensure-in-a-Fiber-context different
# than ensure-in-a-Thread-context and this would be very confusing.
f.resume
# The apparent issue is that when Fiber.yield executes, control
# "leaves" the "ensure block" and so the ensure clause should run. But
# control really does NOT leave the ensure block when Fiber.yield
# executes. It merely pauses there. To require ensure to run when a
# Fiber is suspended then makes ensure-in-a-Fiber-context different
# than ensure-in-a-Thread-context and this would be very confusing.
f.resume

# When we execute the second #resume call, the ensure block DOES exit,
# the ensure clause runs. This is Ruby behavior as of 2.3.1.
f.resume
# When we execute the second #resume call, the ensure block DOES exit,
# the ensure clause runs. This is Ruby behavior as of 2.3.1.
f.resume

exit 0
end
exit 0
end

wr.close
Process.waitpid pid
wr.close
Process.waitpid pid

rd.read.should == "executed"
rd.close
rd.read.should == "executed"
rd.close
end
end
end
end
8 changes: 8 additions & 0 deletions spec/ruby/core/hash/shift_spec.rb
Original file line number Diff line number Diff line change
@@ -26,6 +26,14 @@
h.shift.should == [h, nil]
end

it "preserves Hash invariants when removing the last item" do
h = new_hash(:a => 1, :b => 2)
h.shift.should == [:a, 1]
h.shift.should == [:b, 2]
h[:c] = 3
h.should == {:c => 3}
end

it "raises a RuntimeError if called on a frozen instance" do
lambda { HashSpecs.frozen_hash.shift }.should raise_error(RuntimeError)
lambda { HashSpecs.empty_frozen_hash.shift }.should raise_error(RuntimeError)
7 changes: 7 additions & 0 deletions spec/ruby/core/io/popen_spec.rb
Original file line number Diff line number Diff line change
@@ -39,6 +39,13 @@
rm_r @fname
end

it "sees an infinitely looping subprocess exit when read pipe is closed" do
io = IO.popen "#{RUBY_EXE} -e 'r = loop{puts \"y\"; 0} rescue 1; exit r'", 'r'
io.close

$?.exitstatus.should_not == 0
end

it "writes to a write-only pipe" do
@io = IO.popen("#{RUBY_EXE} -e 'IO.copy_stream(STDIN,STDOUT)' > #{@fname}", "w")
@io.write("bar")
17 changes: 17 additions & 0 deletions spec/ruby/core/io/read_spec.rb
Original file line number Diff line number Diff line change
@@ -302,6 +302,23 @@
it "raises IOError on closed stream" do
lambda { IOSpecs.closed_io.read }.should raise_error(IOError)
end

it "raises IOError when stream is closed by another thread" do
r, w = IO.pipe
t = Thread.new do
begin
r.read(1)
rescue => e
e
end
end

Thread.pass until t.stop?
r.close
t.join
t.value.should be_kind_of(IOError)
w.close
end
end

platform_is :windows do
28 changes: 28 additions & 0 deletions spec/ruby/core/io/reopen_spec.rb
Original file line number Diff line number Diff line change
@@ -193,6 +193,34 @@
end
end

describe "IO#reopen with an IO at EOF" do
before :each do
@name = tmp("io_reopen.txt")
touch(@name) { |f| f.puts "a line" }
@other_name = tmp("io_reopen_other.txt")
touch(@other_name) do |f|
f.puts "Line 1"
f.puts "Line 2"
end

@io = new_io @name, "r"
@other_io = new_io @other_name, "r"
@io.read
end

after :each do
@io.close unless @io.closed?
@other_io.close unless @other_io.closed?
rm_r @name, @other_name
end

it "resets the EOF status to false" do
@io.eof?.should be_true
@io.reopen @other_io
@io.eof?.should be_false
end
end

describe "IO#reopen with an IO" do
before :each do
@name = tmp("io_reopen.txt")
12 changes: 0 additions & 12 deletions spec/ruby/core/io/ungetc_spec.rb
Original file line number Diff line number Diff line change
@@ -81,18 +81,6 @@
@io.pos.should == pos - 1
end

# TODO: file MRI bug
# Another specified behavior that MRI doesn't follow:
# "Has no effect with unbuffered reads (such as IO#sysread)."
#
#it "has no effect with unbuffered reads" do
# length = File.size(@io_name)
# content = @io.sysread(length)
# @io.rewind
# @io.ungetc(100)
# @io.sysread(length).should == content
#end

it "makes subsequent unbuffered operations to raise IOError" do
@io.getc
@io.ungetc(100)
15 changes: 0 additions & 15 deletions spec/ruby/core/io/write_spec.rb
Original file line number Diff line number Diff line change
@@ -20,21 +20,6 @@
rm_r @filename
end

# TODO: impl detail? discuss this with matz. This spec is useless. - rdavis
# I agree. I've marked it not compliant on macruby, as we don't buffer input. -pthomson
not_compliant_on :macruby do
it "writes all of the string's bytes but buffers them" do
written = @file.write("abcde")
written.should == 5
File.open(@filename) do |file|
file.read.should == "012345678901234567890123456789"
@file.fsync
file.rewind
file.read.should == "abcde5678901234567890123456789"
end
end
end

it "does not check if the file is writable if writing zero bytes" do
lambda { @readonly_file.write("") }.should_not raise_error
end
63 changes: 63 additions & 0 deletions spec/ruby/language/fixtures/rescue.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
module RescueSpecs
def self.begin_else(raise_exception)
begin
ScratchPad << :one
raise "an error occurred" if raise_exception
rescue
ScratchPad << :rescue_ran
:rescue_val
else
ScratchPad << :else_ran
:val
end
end

def self.begin_else_ensure(raise_exception)
begin
ScratchPad << :one
raise "an error occurred" if raise_exception
rescue
ScratchPad << :rescue_ran
:rescue_val
else
ScratchPad << :else_ran
:val
ensure
ScratchPad << :ensure_ran
:ensure_val
end
end

def self.begin_else_return(raise_exception)
begin
ScratchPad << :one
raise "an error occurred" if raise_exception
rescue
ScratchPad << :rescue_ran
:rescue_val
else
ScratchPad << :else_ran
:val
end
ScratchPad << :outside_begin
:return_val
end

def self.begin_else_return_ensure(raise_exception)
begin
ScratchPad << :one
raise "an error occurred" if raise_exception
rescue
ScratchPad << :rescue_ran
:rescue_val
else
ScratchPad << :else_ran
:val
ensure
ScratchPad << :ensure_ran
:ensure_val
end
ScratchPad << :outside_begin
:return_val
end
end
82 changes: 82 additions & 0 deletions spec/ruby/language/rescue_spec.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require File.expand_path('../../spec_helper', __FILE__)
require File.expand_path('../fixtures/rescue', __FILE__)

class SpecificExampleException < StandardError
end
@@ -84,6 +85,46 @@ class ArbitraryException < StandardError
ScratchPad.recorded.should == [:one, :two]
end

it "will execute an else block with ensure only if no exceptions were raised" do
result = begin
ScratchPad << :one
rescue
ScratchPad << :does_not_run
else
ScratchPad << :two
:val
ensure
ScratchPad << :ensure
:ensure_val
end
result.should == :val
ScratchPad.recorded.should == [:one, :two, :ensure]
end

it "will execute an else block only if no exceptions were raised in a method" do
result = RescueSpecs.begin_else(false)
result.should == :val
ScratchPad.recorded.should == [:one, :else_ran]
end

it "will execute an else block with ensure only if no exceptions were raised in a method" do
result = RescueSpecs.begin_else_ensure(false)
result.should == :val
ScratchPad.recorded.should == [:one, :else_ran, :ensure_ran]
end

it "will execute an else block but use the outer scope return value in a method" do
result = RescueSpecs.begin_else_return(false)
result.should == :return_val
ScratchPad.recorded.should == [:one, :else_ran, :outside_begin]
end

it "will execute an else block with ensure but use the outer scope return value in a method" do
result = RescueSpecs.begin_else_return_ensure(false)
result.should == :return_val
ScratchPad.recorded.should == [:one, :else_ran, :ensure_ran, :outside_begin]
end

it "will not execute an else block if an exception was raised" do
result = begin
ScratchPad << :one
@@ -98,6 +139,47 @@ class ArbitraryException < StandardError
ScratchPad.recorded.should == [:one, :two]
end

it "will not execute an else block with ensure if an exception was raised" do
result = begin
ScratchPad << :one
raise "an error occurred"
rescue
ScratchPad << :two
:val
else
ScratchPad << :does_not_run
ensure
ScratchPad << :ensure
:ensure_val
end
result.should == :val
ScratchPad.recorded.should == [:one, :two, :ensure]
end

it "will not execute an else block if an exception was raised in a method" do
result = RescueSpecs.begin_else(true)
result.should == :rescue_val
ScratchPad.recorded.should == [:one, :rescue_ran]
end

it "will not execute an else block with ensure if an exception was raised in a method" do
result = RescueSpecs.begin_else_ensure(true)
result.should == :rescue_val
ScratchPad.recorded.should == [:one, :rescue_ran, :ensure_ran]
end

it "will not execute an else block but use the outer scope return value in a method" do
result = RescueSpecs.begin_else_return(true)
result.should == :return_val
ScratchPad.recorded.should == [:one, :rescue_ran, :outside_begin]
end

it "will not execute an else block with ensure but use the outer scope return value in a method" do
result = RescueSpecs.begin_else_return_ensure(true)
result.should == :return_val
ScratchPad.recorded.should == [:one, :rescue_ran, :ensure_ran, :outside_begin]
end

it "will not rescue errors raised in an else block in the rescue block above it" do
lambda do
begin
68 changes: 68 additions & 0 deletions spec/ruby/language/safe_navigator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
require File.expand_path("../../spec_helper", __FILE__)

ruby_version_is "2.3" do
describe "Safe navigator" do
it "requires a method name to be provided" do
lambda { eval("obj&. {}") }.should raise_error(SyntaxError)
end

context "when context is nil" do
it "always returns nil" do
eval("nil&.unknown").should == nil
eval("[][10]&.unknown").should == nil
end

it "can be chained" do
eval("nil&.one&.two&.three").should == nil
end

it "doesn't evaluate arguments" do
obj = Object.new
obj.should_not_receive(:m)
eval("nil&.unknown(obj.m) { obj.m }")
end
end

context "when context is false" do
it "calls the method" do
eval("false&.to_s").should == "false"

lambda { eval("false&.unknown") }.should raise_error(NoMethodError)
end
end

context "when context is truthy" do
it "calls the method" do
eval("1&.to_s").should == "1"

lambda { eval("1&.unknown") }.should raise_error(NoMethodError)
end
end

it "takes a list of arguments" do
eval("[1,2,3]&.first(2)").should == [1,2]
end

it "takes a block" do
eval("[1,2]&.map { |i| i * 2 }").should == [2, 4]
end

it "allows attribute assignment" do
klass = Class.new do
attr_accessor :m

def initialize
@m = 0
end
end

obj = klass.new

eval("obj&.m += 3")
obj.m.should == 3

obj = nil
eval("obj&.m += 3").should == nil
end
end
end
27 changes: 27 additions & 0 deletions spec/ruby/language/send_spec.rb
Original file line number Diff line number Diff line change
@@ -206,6 +206,33 @@ def foobar; 200; end

end

describe "Invoking a public setter method" do
it 'returns the set value' do
klass = Class.new do
def foobar=(*)
1
end
end

(klass.new.foobar = 'bar').should == 'bar'
(klass.new.foobar = 'bar', 'baz').should == ["bar", "baz"]
end
end

describe "Invoking []= methods" do
it 'returns the set value' do
klass = Class.new do
def []=(*)
1
end
end

(klass.new[33] = 'bar').should == 'bar'
(klass.new[33] = 'bar', 'baz').should == ['bar', 'baz']
(klass.new[33, 34] = 'bar', 'baz').should == ['bar', 'baz']
end
end

describe "Invoking a private setter method" do
describe "permits self as a receiver" do
it "for normal assignment" do
117 changes: 61 additions & 56 deletions spec/ruby/library/socket/basicsocket/setsockopt_spec.rb
Original file line number Diff line number Diff line change
@@ -36,70 +36,75 @@
end
end

it "sets the socket option Socket::SO_OOBINLINE" do
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, true).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should_not == [0].pack("i")

@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, false).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should == [0].pack("i")

@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 1).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should_not == [0].pack("i")

@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 0).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should == [0].pack("i")

@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 2).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should_not == [0].pack("i")

platform_is_not os: :windows do
lambda {
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "")
}.should raise_error(SystemCallError)
end
platform_is_not :aix do
# A known bug in AIX. getsockopt(2) does not properly set
# the fifth argument for SO_TYPE, SO_OOBINLINE, SO_BROADCAST, etc.

@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "blah").should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should_not == [0].pack("i")
it "sets the socket option Socket::SO_OOBINLINE" do
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, true).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should_not == [0].pack("i")

platform_is_not os: :windows do
lambda {
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "0")
}.should raise_error(SystemCallError)
end
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, false).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should == [0].pack("i")

@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "\x00\x00\x00\x00").should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should == [0].pack("i")
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 1).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should_not == [0].pack("i")

platform_is_not os: :windows do
lambda {
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "1")
}.should raise_error(SystemCallError)
end
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 0).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should == [0].pack("i")

platform_is_not os: :windows do
lambda {
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "\x00\x00\x00")
}.should raise_error(SystemCallError)
end
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, 2).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should_not == [0].pack("i")

@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [1].pack('i')).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should_not == [0].pack("i")
platform_is_not os: :windows do
lambda {
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "")
}.should raise_error(SystemCallError)
end

@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [0].pack('i')).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should == [0].pack("i")
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "blah").should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should_not == [0].pack("i")

@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [1000].pack('i')).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should_not == [0].pack("i")
platform_is_not os: :windows do
lambda {
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "0")
}.should raise_error(SystemCallError)
end

@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "\x00\x00\x00\x00").should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should == [0].pack("i")

platform_is_not os: :windows do
lambda {
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "1")
}.should raise_error(SystemCallError)
end

platform_is_not os: :windows do
lambda {
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, "\x00\x00\x00")
}.should raise_error(SystemCallError)
end

@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [1].pack('i')).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should_not == [0].pack("i")

@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [0].pack('i')).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should == [0].pack("i")

@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE, [1000].pack('i')).should == 0
n = @sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_OOBINLINE).to_s
n.should_not == [0].pack("i")
end
end

it "sets the socket option Socket::SO_SNDBUF" do
4 changes: 4 additions & 0 deletions spec/tags/ruby/core/kernel/spawn_tags.txt
Original file line number Diff line number Diff line change
@@ -144,3 +144,7 @@ windows:Kernel.spawn when passed close_others: true does not close STDERR
windows:Kernel.spawn when passed close_others: false does not close STDIN
windows:Kernel.spawn when passed close_others: false does not close STDOUT
windows:Kernel.spawn when passed close_others: false does not close STDERR
fails:Kernel#spawn unsets other environment variables when given a true :unsetenv_others option
fails:Kernel#spawn unsets other environment variables when given a non-false :unsetenv_others option
fails:Kernel.spawn unsets other environment variables when given a true :unsetenv_others option
fails:Kernel.spawn unsets other environment variables when given a non-false :unsetenv_others option
1 change: 1 addition & 0 deletions spec/truffle/tags/core/exception/cause_tags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fails:Exception#cause returns the active exception when an exception is raised
2 changes: 0 additions & 2 deletions spec/truffle/tags/core/exception/errno_tags.txt

This file was deleted.

1 change: 1 addition & 0 deletions spec/truffle/tags/core/io/popen_tags.txt
Original file line number Diff line number Diff line change
@@ -33,3 +33,4 @@ fails:IO.popen with a leading Array argument accepts [env, command, arg1, arg2,
fails:IO.popen with a leading Array argument accepts '[env, command, arg1, arg2, ..., exec options], mode'
fails:IO.popen with a leading Array argument accepts '[env, command, arg1, arg2, ..., exec options], mode, IO options'
fails:IO.popen with a leading Array argument accepts '[env, command, arg1, arg2, ...], mode, IO + exec options'
slow:IO.popen sees an infinitely looping subprocess exit when read pipe is closed
1 change: 1 addition & 0 deletions spec/truffle/tags/core/io/read_tags.txt
Original file line number Diff line number Diff line change
@@ -2,5 +2,6 @@ fails:IO.read from a pipe runs the rest as a subprocess and returns the standard
fails:IO.read from a pipe opens a pipe to a fork if the rest is -
fails:IO.read from a pipe reads only the specified number of bytes requested
fails:IO.read from a pipe raises Errno::ESPIPE if passed an offset
fails(hangs):IO#read raises IOError when stream is closed by another thread
fails(windows):IO#read on Windows normalizes line endings in text mode
fails(windows):IO#read on Windows does not normalize line endings in binary mode
1 change: 1 addition & 0 deletions spec/truffle/tags/core/io/reopen_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
slow:IO#reopen with a String effects exec/system/fork performed after it
fails:IO#reopen with a String affects exec/system/fork performed after it
fails:IO#reopen with an IO at EOF resets the EOF status to false
3 changes: 0 additions & 3 deletions spec/truffle/tags/core/kernel/sprintf_tags.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
fails:Kernel#sprintf passes some tests for positive %x
fails:Kernel#sprintf passes some tests for negative %x
fails:Kernel#sprintf passes some tests for negative %u
fails:Kernel#sprintf passes some tests for positive %u
fails:Kernel#sprintf passes some tests for positive %d
fails:Kernel#sprintf passes kstephens's tests
fails:Kernel#sprintf returns a String in the same encoding as the format String if compatible
fails:Kernel#sprintf returns a String in the argument encoding if format encoding is more restrictive
35 changes: 0 additions & 35 deletions spec/truffle/tags/core/string/modulo_tags.txt
Original file line number Diff line number Diff line change
@@ -1,44 +1,9 @@
fails:String#% formats single % characters before a newline or NULL as literal %s
fails:String#% raises an ArgumentError for unused arguments when $DEBUG is true
fails:String#% replaces trailing absolute argument specifier without type with percent sign
fails:String#% raises an ArgumentError when multiple positional argument tokens are given for one format specifier
fails:String#% raises an ArgumentError when absolute and relative argument numbers are mixed
fails:String#% allows positional arguments for width star and precision star arguments
fails:String#% allows negative width to imply '-' flag
fails:String#% ignores negative precision
fails:String#% allows a star to take an argument number to use as the width
fails:String#% calls to_int on width star and precision star tokens
fails:String#% does not call #to_a to convert the argument
fails:String#% doesn't return subclass instances when called on a subclass
fails:String#% always taints the result when the format string is tainted
fails:String#% supports character formats using %c
fails:String#% supports single character strings as argument for %c
fails:String#% raises an exception for multiple character strings as argument for %c
fails:String#% calls to_str on argument for %c formats
fails:String#% calls #to_ary on argument for %c formats
fails:String#% calls #to_int on argument for %c formats, if the argument does not respond to #to_ary
fails:String#% supports integer formats using %d
fails:String#% supports negative integers using %d
fails:String#% supports negative integers using %d, giving priority to `-`
fails:String#% supports integer formats using %i
fails:String#% supports negative integers using %i
fails:String#% supports negative integers using %i, giving priority to `-`
fails:String#% supports float formats using %e
fails:String#% supports float formats using %E, but Inf, -Inf, and NaN are not floats
fails:String#% supports float formats using %E
fails:String#% supports float formats using %f
fails:String#% supports float formats using %g
fails:String#% supports float formats using %G
fails:String#% supports octal formats using %o for positive numbers
fails:String#% supports octal formats using %o for negative numbers
fails:String#% supports inspect formats using %p
fails:String#% supports string formats using %s
fails:String#% taints result for %s when argument is tainted
fails:String#% raises an ArgumentError for huge precisions for %s
fails:String#% supports unsigned formats using %u
fails:String#% supports hex formats using %x for positive numbers
fails:String#% supports hex formats using %x for negative numbers
fails:String#% supports hex formats using %X for positive numbers
fails:String#% supports hex formats using %X for negative numbers
fails:String#% behaves as if calling Kernel#Float for %e arguments, when the passed argument does not respond to #to_ary
fails:String#% behaves as if calling Kernel#Float for %e arguments, when the passed argument is hexadecimal string
1 change: 1 addition & 0 deletions spec/truffle/tags/language/safe_navigator_tags.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fails:Safe navigator allows attribute assignment
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/CGICoreTest.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exclude :"test_cgi_core_params_encoding_check", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/Complex_Test.rb
Original file line number Diff line number Diff line change
@@ -9,3 +9,4 @@
exclude :test_respond, "needs investigation"
exclude :test_ruby19, "needs investigation"
exclude :test_supp, "needs investigation"
exclude :"test_mul", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/PPTestModule/PPFileStatTest.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exclude :"test_nothing_raised", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestAlias.rb
Original file line number Diff line number Diff line change
@@ -3,3 +3,4 @@
exclude :test_alias_wb_miss, "needs investigation"
exclude :test_cyclic_zsuper, "needs investigation"
exclude :test_nonexistmethod, "needs investigation"
exclude :"test_alias_method_equation", "needs investigation"
2 changes: 2 additions & 0 deletions test/mri/excludes_truffle/TestAutoload.rb
Original file line number Diff line number Diff line change
@@ -7,3 +7,5 @@
exclude :test_require_explicit, "needs investigation"
exclude :test_threaded_accessing_constant, "needs investigation"
exclude :test_threaded_accessing_inner_constant, "needs investigation"
exclude :"test_autoload_while_autoloading", "needs investigation"
exclude :"test_require_implemented_in_ruby_is_called", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestBase64.rb
Original file line number Diff line number Diff line change
@@ -2,3 +2,4 @@
exclude :test_sample, "needs investigation"
exclude :test_strict_decode64, "needs investigation"
exclude :test_urlsafe_decode64, "needs investigation"
exclude :"test_urlsafe_decode64_unpadded", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestBeginEndBlock.rb
Original file line number Diff line number Diff line change
@@ -10,3 +10,4 @@
exclude :test_propagate_signaled, "needs investigation"
exclude :test_raise_in_at_exit, "needs investigation"
exclude :test_rescue_at_exit, "needs investigation"
exclude :"test_endblockwarn_in_eval", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestCall.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exclude :"test_safe_call", "needs investigation"
4 changes: 4 additions & 0 deletions test/mri/excludes_truffle/TestClass.rb
Original file line number Diff line number Diff line change
@@ -20,3 +20,7 @@
exclude :test_singleton_class_of_frozen_object, "needs investigation"
exclude :test_singleton_class_p, "needs investigation"
exclude :test_cloned_singleton_method_added, "needs investigation"
exclude :"test_invalid_superclass", "needs investigation"
exclude :"test_redefinition_mismatch", "needs investigation"
exclude :"test_should_not_expose_singleton_class_without_metaclass", "needs investigation"
exclude :"test_singleton_class_should_has_own_namespace", "needs investigation"
5 changes: 5 additions & 0 deletions test/mri/excludes_truffle/TestDigest/TestDigestParen.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
exclude :"test_no_lib", "needs investigation"
exclude :"test_no_lib_no_def", "needs investigation"
exclude :"test_race", "needs investigation"
exclude :"test_race_mixed", "needs investigation"
exclude :"test_sha2", "needs investigation"
3 changes: 3 additions & 0 deletions test/mri/excludes_truffle/TestDir_M17N.rb
Original file line number Diff line number Diff line change
@@ -13,3 +13,6 @@
exclude :test_glob_compose, "needs investigation"
exclude :test_inspect_nonascii, "needs investigation"
exclude :test_filename_utf8_raw_windows_1251_name, "needs investigation"
exclude :"test_glob_warning_match_all", "needs investigation"
exclude :"test_glob_warning_match_dir", "needs investigation"
exclude :test_glob_warning_opendir, "needs investigation"
8 changes: 8 additions & 0 deletions test/mri/excludes_truffle/TestException.rb
Original file line number Diff line number Diff line change
@@ -28,3 +28,11 @@
exclude :"test_too_many_args_in_eval", "needs investigation"
exclude :"test_type_error_message_encoding", "needs investigation"
exclude :"test_unknown_option", "needs investigation"
exclude :"test_catch_throw_noarg", "needs investigation"
exclude :"test_message_of_name_error", "needs investigation"
exclude :"test_method_missing_reason_clear", "needs investigation"
exclude :"test_multibyte_and_newline", "needs investigation"
exclude :"test_name_error_info", "needs investigation"
exclude :"test_name_error_local_variables", "needs investigation"
exclude :"test_output_string_encoding", "needs investigation"
exclude :"test_uncaught_throw", "needs investigation"
2 changes: 2 additions & 0 deletions test/mri/excludes_truffle/TestFile.rb
Original file line number Diff line number Diff line change
@@ -28,3 +28,5 @@
exclude :"test_truncate_beyond_eof", "needs investigation"
exclude :"test_truncate_rbuf", "needs investigation"
exclude :"test_truncate_wbuf", "needs investigation"
exclude :"test_file_share_delete", "needs investigation"
exclude :"test_realpath_encoding", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestFileExhaustive.rb
Original file line number Diff line number Diff line change
@@ -101,3 +101,4 @@
exclude :test_writable_p, "needs investigation"
exclude :test_writable_real_p, "needs investigation"
exclude :test_zero_p, "needs investigation"
exclude :"test_closed_io_identical_p", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestGB18030.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exclude :"test_left_adjust_char_head", "needs investigation"
2 changes: 2 additions & 0 deletions test/mri/excludes_truffle/TestGc.rb
Original file line number Diff line number Diff line change
@@ -17,3 +17,5 @@
exclude :test_sweep_in_finalizer, "needs investigation"
exclude :test_verify_internal_consistency, "needs investigation"
exclude :test_singleton_method, "needs investigation"
exclude :"test_gc_disabled_start", "needs investigation"
exclude :"test_interrupt_in_finalizer", "needs investigation"
3 changes: 3 additions & 0 deletions test/mri/excludes_truffle/TestIO_M17N.rb
Original file line number Diff line number Diff line change
@@ -139,3 +139,6 @@
exclude :test_write_mode, "needs investigation"
exclude :test_write_mode_fail, "needs investigation"
exclude :test_write_noenc, "needs investigation"
exclude :"test_bom_non_utf", "needs investigation"
exclude :"test_bom_too_long_utfname", "needs investigation"
exclude :"test_each_codepoint_need_more", "needs investigation"
2 changes: 2 additions & 0 deletions test/mri/excludes_truffle/TestLambdaParameters.rb
Original file line number Diff line number Diff line change
@@ -2,3 +2,5 @@
exclude :test_call_with_block, "needs investigation"
exclude :test_do_lambda_source_location, "needs investigation"
exclude :test_lambda_as_iterator, "needs investigation"
exclude :"test_yield_relaxed(array,&lambda)", "needs investigation"
exclude :"test_yield_relaxed(to_ary,&lambda)", "needs investigation"
2 changes: 2 additions & 0 deletions test/mri/excludes_truffle/TestMath.rb
Original file line number Diff line number Diff line change
@@ -6,3 +6,5 @@
exclude :test_sin, "needs investigation"
exclude :test_sqrt, "needs investigation"
exclude :test_tan, "needs investigation"
exclude :"test_override_bignum_to_f", "needs investigation"
exclude :"test_override_fixnum_to_f", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestMatrix.rb
Original file line number Diff line number Diff line change
@@ -20,3 +20,4 @@
exclude :test_lup , "needs investigation"
exclude :test_rank , "needs investigation"
exclude :test_rank2 , "needs investigation"
exclude :"test_det", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestMethod.rb
Original file line number Diff line number Diff line change
@@ -43,3 +43,4 @@
exclude :test_caller_top_level, "needs investigation"
exclude :test_insecure_method, "needs investigation"
exclude :test_to_proc_binding, "needs investigation"
exclude :"test_define_method_with_symbol", "needs investigation"
12 changes: 12 additions & 0 deletions test/mri/excludes_truffle/TestObjSpace.rb
Original file line number Diff line number Diff line change
@@ -7,3 +7,15 @@
exclude :test_reachable_objects_size, "needs spawn"
exclude :test_dump_all, "needs spawn"
exclude :test_argf_memsize, "we store ext as an ivar so it doesn't show up in the object size as they expect"
exclude :"test_count_imemo_objects", "needs investigation"
exclude :"test_count_symbols", "needs investigation"
exclude :"test_count_tdata_objects", "needs investigation"
exclude :"test_dump_dynamic_symbol", "needs investigation"
exclude :"test_internal_class_of", "needs investigation"
exclude :"test_internal_super_of", "needs investigation"
exclude :"test_memsize_of_iseq", "needs investigation"
exclude :"test_trace_object_allocations", "needs investigation"
exclude :test_count_objects_size, "needs investigation"
exclude :test_dump_special_consts, "needs investigation"
exclude :test_memsize_of, "needs investigation"
exclude :test_memsize_of_all, "needs investigation"
2 changes: 2 additions & 0 deletions test/mri/excludes_truffle/TestObject.rb
Original file line number Diff line number Diff line change
@@ -30,3 +30,5 @@
exclude :test_type_error_message, "needs investigation"
exclude :test_redefine_method_which_may_case_serious_problem, "needs investigation"
exclude :test_send_with_block, "needs investigation"
exclude :"test_convert_array", "needs investigation"
exclude :"test_convert_string", "needs investigation"
3 changes: 3 additions & 0 deletions test/mri/excludes_truffle/TestObjectSpace.rb
Original file line number Diff line number Diff line change
@@ -12,3 +12,6 @@
exclude :test_id2ref_30, "needs investigation"
exclude :test_id2ref_31, "needs investigation"
exclude :test_id2ref_32, "needs investigation"
exclude :"test_each_object_no_gabage", "needs investigation"
exclude :"test_each_object_recursive_key", "needs investigation"
exclude :"test_each_object_singleton_class", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestOpen3.rb
Original file line number Diff line number Diff line change
@@ -21,3 +21,4 @@
exclude :test_capture2_stdin_data, "needs investigation"
exclude :test_capture2e_stdin_data, "needs investigation"
exclude :test_capture3_flip, "needs investigation"
exclude :"test_env", "needs investigation"
5 changes: 5 additions & 0 deletions test/mri/excludes_truffle/TestPack.rb
Original file line number Diff line number Diff line change
@@ -14,3 +14,8 @@
exclude :"test_short_with_block", "needs investigation"
exclude :test_pack_unpack_U, "needs investigation"
exclude :test_pack_unpack_m0, "needs investigation"
exclude :"test_integer_endian", "needs investigation"
exclude :"test_integer_endian_explicit", "needs investigation"
exclude :"test_invalid_warning", "needs investigation"
exclude :"test_pack_resize", "needs investigation"
exclude :"test_pack_unpack_jJ", "needs investigation"
10 changes: 10 additions & 0 deletions test/mri/excludes_truffle/TestPathname.rb
Original file line number Diff line number Diff line change
@@ -165,3 +165,13 @@
exclude :test_writable? , "needs investigation"
exclude :test_writable_real? , "needs investigation"
exclude :test_zero? , "needs investigation"
exclude :"test_ascend_447", "needs investigation"
exclude :"test_ascend_448", "needs investigation"
exclude :"test_ascend_449", "needs investigation"
exclude :"test_ascend_450", "needs investigation"
exclude :"test_blockless_ascend_is_enumerator", "needs investigation"
exclude :"test_blockless_descend_is_enumerator", "needs investigation"
exclude :"test_descend_438", "needs investigation"
exclude :"test_descend_439", "needs investigation"
exclude :"test_descend_440", "needs investigation"
exclude :"test_descend_441", "needs investigation"
3 changes: 3 additions & 0 deletions test/mri/excludes_truffle/TestProc.rb
Original file line number Diff line number Diff line change
@@ -41,3 +41,6 @@
exclude :test_to_proc , "needs investigation"
exclude :test_to_s , "needs investigation"
exclude :test_proc_args_opt_signle , "needs investigation"
exclude :"test_curry_binding", "needs investigation"
exclude :"test_proc_args_opt_single", "needs investigation"
exclude :"test_proc_mark", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestRand.rb
Original file line number Diff line number Diff line change
@@ -33,3 +33,4 @@
exclude :test_srand, "needs investigation"
exclude :test_types, "needs investigation"
exclude :test_fork_shuffle, "needs investigation"
exclude :"test_default_seed", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestRange.rb
Original file line number Diff line number Diff line change
@@ -16,3 +16,4 @@
exclude :test_range_numeric_string, "needs investigation"
exclude :test_size, "needs investigation"
exclude :test_eqq_time, "needs investigation"
exclude :"test_bsearch_typechecks_return_values", "needs investigation"
3 changes: 3 additions & 0 deletions test/mri/excludes_truffle/TestRegexp.rb
Original file line number Diff line number Diff line change
@@ -33,3 +33,6 @@
exclude :"test_unescape", "needs investigation"
exclude :"test_uninitialized", "needs investigation"
exclude :"test_union", "needs investigation"
exclude :"test_KCODE_warning", "needs investigation"
exclude :"test_ignorecase_warning", "needs investigation"
exclude :"test_match_without_regexp", "needs investigation"
5 changes: 4 additions & 1 deletion test/mri/excludes_truffle/TestRequire.rb
Original file line number Diff line number Diff line change
@@ -28,4 +28,7 @@
exclude :test_define_class_under, "needs investigation"
exclude :test_define_module, "needs investigation"
exclude :test_define_class, "needs investigation"
exclude :test_define_module_under, "needs investigation"
exclude :test_define_module_under, "needs investigation"
exclude :"test_load_ospath", "needs investigation"
exclude :"test_load_scope", "needs investigation"
exclude :"test_throw_while_loading", "needs investigation"
3 changes: 3 additions & 0 deletions test/mri/excludes_truffle/TestRubyLiteral.rb
Original file line number Diff line number Diff line change
@@ -3,3 +3,6 @@
exclude :test_integer, "needs investigation"
exclude :test_string, "needs investigation"
exclude :test_xstring, "needs investigation"
exclude :"test_big_hash_literal", "needs investigation"
exclude :"test_dstring_encoding", "needs investigation"
exclude :"test_symbol_list", "needs investigation"
4 changes: 4 additions & 0 deletions test/mri/excludes_truffle/TestRubyOptimization.rb
Original file line number Diff line number Diff line change
@@ -28,3 +28,7 @@
exclude :test_array_empty? , "needs investigation"
exclude :test_string_empty? , "needs investigation"
exclude :test_hash_empty?, "needs investigation"
exclude :test_string_freeze_saves_memory, "hangs / needs investigation"
exclude :"test_eqq", "needs investigation"
exclude :"test_opt_case_dispatch", "needs investigation"
exclude :"test_string_freeze_block", "needs investigation"
2 changes: 2 additions & 0 deletions test/mri/excludes_truffle/TestRubyOptions.rb
Original file line number Diff line number Diff line change
@@ -43,3 +43,5 @@
exclude :test_warning, "needs investigation"
exclude :test_yydebug, "needs investigation"
exclude :test_chdir, "needs investigation"
exclude :"test_frozen_string_literal", "needs investigation"
exclude :"test_frozen_string_literal_debug", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestRubyYieldGen.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
exclude :test_yield, "needs investigation"
exclude :test_yield_enum, "needs investigation"
exclude :test_yield_lambda, "needs investigation"
exclude :"test_block_cached_argc", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestSprintf.rb
Original file line number Diff line number Diff line change
@@ -17,3 +17,4 @@
exclude :test_star, "needs investigation"
exclude :test_string, "needs investigation"
exclude :test_negative_hex , "needs investigation"
exclude :"test_named_default", "needs investigation"
8 changes: 8 additions & 0 deletions test/mri/excludes_truffle/TestStringIO.rb
Original file line number Diff line number Diff line change
@@ -38,3 +38,11 @@
exclude :test_readlines , "needs investigation"
exclude :test_truncate , "needs investigation"
exclude :test_ungetc_pos , "needs investigation"
exclude :"test_close", "needs investigation"
exclude :"test_close_read", "needs investigation"
exclude :"test_close_write", "needs investigation"
exclude :"test_read", "needs investigation"
exclude :"test_set_encoding", "needs investigation"
exclude :"test_sysread", "needs investigation"
exclude :"test_ungetbyte_padding", "needs investigation"
exclude :"test_ungetc_padding", "needs investigation"
3 changes: 2 additions & 1 deletion test/mri/excludes_truffle/TestStruct/SubStruct.rb
Original file line number Diff line number Diff line change
@@ -7,4 +7,5 @@
exclude :test_nonascii, "needs investigation"
exclude :test_redefinition_warning, "needs investigation"
exclude :test_struct_new, "needs investigation"
exclude :test_values_at, "needs investigation"
exclude :test_values_at, "needs investigation"
exclude :test_big_struct, "needs investigation"
3 changes: 2 additions & 1 deletion test/mri/excludes_truffle/TestStruct/TopStruct.rb
Original file line number Diff line number Diff line change
@@ -6,4 +6,5 @@
exclude :test_nonascii, "needs investigation"
exclude :test_redefinition_warning, "needs investigation"
exclude :test_struct_new, "needs investigation"
exclude :test_values_at, "needs investigation"
exclude :test_values_at, "needs investigation"
exclude :test_big_struct, "needs investigation"
2 changes: 2 additions & 0 deletions test/mri/excludes_truffle/TestSuper.rb
Original file line number Diff line number Diff line change
@@ -10,3 +10,5 @@
exclude :test_super_in_module_unbound_method, "needs investigation"
exclude :test_super_in_orphan_block_with_instance_eval, "needs investigation"
exclude :test_super_in_END, "needs investigation"
exclude :"test_double_include", "needs investigation"
exclude :"test_missing_super", "needs investigation"
5 changes: 5 additions & 0 deletions test/mri/excludes_truffle/TestSymbol.rb
Original file line number Diff line number Diff line change
@@ -6,3 +6,8 @@
exclude :test_to_proc, "needs investigation"
exclude :test_hash_redefinition, "hangs"
exclude :test_symbol_fstr_leak, "hangs"
exclude :"test_to_proc_arg", "needs investigation"
exclude :"test_to_proc_for_hash_each", "needs investigation"
exclude :"test_to_proc_new_proc", "needs investigation"
exclude :"test_to_proc_no_method", "needs investigation"
exclude :"test_to_proc_yield", "needs investigation"
3 changes: 3 additions & 0 deletions test/mri/excludes_truffle/TestSyntax.rb
Original file line number Diff line number Diff line change
@@ -57,3 +57,6 @@
exclude :test_keyword_empty_splat, "needs investigation"
exclude :test_null_range_cmdarg, "needs investigation"
exclude :test_too_big_nth_ref, "needs investigation"
exclude :"test_keyword_duplicated_splat", "needs investigation"
exclude :test_keyword_duplicated, "needs investigation"

1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestTempfile.rb
Original file line number Diff line number Diff line change
@@ -26,3 +26,4 @@
exclude :test_unlink_before_close_works_on_posix_systems, "needs investigation"
exclude :test_unlink_silently_fails_on_windows, "needs investigation"
exclude :test_tempfile_finalizer_does_not_run_if_unlinked, "needs investigation"
exclude :"test_default_basename", "needs investigation"
4 changes: 4 additions & 0 deletions test/mri/excludes_truffle/TestThread.rb
Original file line number Diff line number Diff line change
@@ -51,3 +51,7 @@
exclude :test_handle_interrupt_with_break, "needs investigation"
exclude :test_handle_interrupt_with_return, "needs investigation"
exclude :test_fork_in_thread, "needs investigation"
exclude :test_subclass_no_initialize, "hangs / needs investigation"
exclude :"test_thread_invalid_name", "needs investigation"
exclude :"test_thread_invalid_object", "needs investigation"
exclude :"test_thread_name", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestTime.rb
Original file line number Diff line number Diff line change
@@ -41,3 +41,4 @@
exclude :test_utc_or_local, "needs investigation"
exclude :test_utc_p, "needs investigation"
exclude :test_time_subt, "needs investigation"
exclude :"test_zone", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestTranscode.rb
Original file line number Diff line number Diff line change
@@ -23,3 +23,4 @@
exclude :test_utf_32 , "needs investigation"
exclude :"test_pseudo_encoding_inspect(UTF-16)", "needs investigation"
exclude :"test_pseudo_encoding_inspect(UTF-32)", "needs investigation"
exclude :"test_loading_race", "needs investigation"
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestVariable.rb
Original file line number Diff line number Diff line change
@@ -4,3 +4,4 @@
exclude :test_shadowing_block_local_variables, "needs investigation"
exclude :test_shadowing_local_variables, "needs investigation"
exclude :test_variable, "needs investigation"
exclude :test_special_constant_ivars, "needs investigation"
6 changes: 6 additions & 0 deletions test/mri/excludes_truffle/TestWEBrickUtils.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
exclude :"test_nested_timeout_inner_custom_exception", "needs investigation"
exclude :"test_nested_timeout_outer", "needs investigation"
exclude :"test_nested_timeout_outer_custom_exception", "needs investigation"
exclude :"test_no_timeout", "needs investigation"
exclude :"test_timeout_custom_exception", "needs investigation"
exclude :"test_timeout_default_execption", "needs investigation"
6 changes: 3 additions & 3 deletions test/mri_truffle.index
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ ruby/test_basicinstructions.rb
ruby/test_beginendblock.rb
ruby/test_bignum.rb
ruby/test_call.rb
ruby/test_case.rb
# ruby/test_case.rb # uses refinements
ruby/test_class.rb
ruby/test_clone.rb
ruby/test_comparable.rb
@@ -189,11 +189,11 @@ fileutils/test_verbose.rb
# io/nonblock/test_flush.rb
io/console/test_io_console.rb

json/test_json.rb
# json/test_json.rb # Needs review for inclusion in test suite
json/test_json_addition.rb
json/test_json_encoding.rb
json/test_json_fixtures.rb
json/test_json_generate.rb
# json/test_json_generate.rb # Needs review for inclusion in test suite
json/test_json_generic_object.rb

# json/test_json_rails.rb

This file was deleted.

This file was deleted.

172 changes: 90 additions & 82 deletions truffle/src/main/java/org/jruby/truffle/core/CoreLibrary.java
Original file line number Diff line number Diff line change
@@ -261,7 +261,6 @@ public class CoreLibrary {
@CompilationFinal private ArrayNodes.MinBlock arrayMinBlock;
@CompilationFinal private ArrayNodes.MaxBlock arrayMaxBlock;

private final DynamicObject rubyInternalMethod;
private final Map<Errno, DynamicObject> errnoClasses = new HashMap<>();

@CompilationFinal private InternalMethod basicObjectSendMethod;
@@ -271,7 +270,9 @@ public class CoreLibrary {

private static final Object systemObject = TruffleOptions.AOT ? null : JavaInterop.asTruffleObject(System.class);

public String getCoreLoadPath() {
private final String coreLoadPath;

private String buildCoreLoadPath() {
String path = context.getOptions().CORE_LOAD_PATH;

while (path.endsWith("/")) {
@@ -289,82 +290,6 @@ public String getCoreLoadPath() {
}
}

public DynamicObject getRuntimeErrorClass() {
return runtimeErrorClass;
}

public DynamicObject getSystemStackErrorClass() {
return systemStackErrorClass;
}

public DynamicObject getArgumentErrorClass() {
return argumentErrorClass;
}

public DynamicObject getIndexErrorClass() {
return indexErrorClass;
}

public DynamicObject getLocalJumpErrorClass() {
return localJumpErrorClass;
}

public DynamicObject getNotImplementedErrorClass() {
return notImplementedErrorClass;
}

public DynamicObject getSyntaxErrorClass() {
return syntaxErrorClass;
}

public DynamicObject getFloatDomainErrorClass() {
return floatDomainErrorClass;
}

public DynamicObject getIOErrorClass() {
return ioErrorClass;
}

public DynamicObject getRangeErrorClass() {
return rangeErrorClass;
}

public DynamicObject getRegexpErrorClass() {
return regexpErrorClass;
}

public DynamicObject getEncodingCompatibilityErrorClass() {
return encodingCompatibilityErrorClass;
}

public DynamicObject getFiberErrorClass() {
return fiberErrorClass;
}

public DynamicObject getThreadErrorClass() {
return threadErrorClass;
}

public DynamicObject getSecurityErrorClass() {
return securityErrorClass;
}

public DynamicObject getSystemCallErrorClass() {
return systemCallErrorClass;
}

public DynamicObject getEagainWaitReadable() {
return eagainWaitReadable;
}

public DynamicObject getEagainWaitWritable() {
return eagainWaitWritable;
}

public DynamicObject getTruffleModule() {
return truffleModule;
}

private enum State {
INITIALIZING,
LOADING_RUBY_CORE,
@@ -400,6 +325,7 @@ public Object execute(VirtualFrame frame) {

public CoreLibrary(RubyContext context) {
this.context = context;
this.coreLoadPath = buildCoreLoadPath();
this.node = new CoreLibraryNode(context);

// Nothing in this constructor can use RubyContext.getCoreLibrary() as we are building it!
@@ -478,6 +404,9 @@ public CoreLibrary(RubyContext context) {

for (Errno errno : Errno.values()) {
if (errno.name().startsWith("E")) {
if(errno.equals(Errno.EWOULDBLOCK) && Errno.EWOULDBLOCK.intValue() == Errno.EAGAIN.intValue()){
continue; // Don't define it as a class, define it as constant later.
}
errnoClasses.put(errno, defineClass(errnoModule, systemCallErrorClass, errno.name()));
}
}
@@ -673,10 +602,6 @@ public CoreLibrary(RubyContext context) {
randomizerFactory = Layouts.RANDOMIZER.createRandomizerShape(randomizerClass, randomizerClass);
Layouts.CLASS.setInstanceFactoryUnsafe(randomizerClass, randomizerFactory);

// Interop

rubyInternalMethod = null;

// Include the core modules

includeModules(comparableModule);
@@ -970,6 +895,9 @@ private void initializeConstants() {
Layouts.CLASS.getFields(errnoClass).setConstant(context, node, "Errno", errno.intValue());
}

if (Errno.EWOULDBLOCK.intValue() == Errno.EAGAIN.intValue()) {
Layouts.MODULE.getFields(errnoModule).setConstant(context, node, Errno.EWOULDBLOCK.name(), errnoClasses.get(Errno.EAGAIN));
}

}

@@ -1217,6 +1145,10 @@ public RubyContext getContext() {
return context;
}

public String getCoreLoadPath() {
return coreLoadPath;
}

public DynamicObject getArrayClass() {
return arrayClass;
}
@@ -1501,6 +1433,82 @@ public DynamicObjectFactory getHandleFactory() {
return handleFactory;
}

public DynamicObject getRuntimeErrorClass() {
return runtimeErrorClass;
}

public DynamicObject getSystemStackErrorClass() {
return systemStackErrorClass;
}

public DynamicObject getArgumentErrorClass() {
return argumentErrorClass;
}

public DynamicObject getIndexErrorClass() {
return indexErrorClass;
}

public DynamicObject getLocalJumpErrorClass() {
return localJumpErrorClass;
}

public DynamicObject getNotImplementedErrorClass() {
return notImplementedErrorClass;
}

public DynamicObject getSyntaxErrorClass() {
return syntaxErrorClass;
}

public DynamicObject getFloatDomainErrorClass() {
return floatDomainErrorClass;
}

public DynamicObject getIOErrorClass() {
return ioErrorClass;
}

public DynamicObject getRangeErrorClass() {
return rangeErrorClass;
}

public DynamicObject getRegexpErrorClass() {
return regexpErrorClass;
}

public DynamicObject getEncodingCompatibilityErrorClass() {
return encodingCompatibilityErrorClass;
}

public DynamicObject getFiberErrorClass() {
return fiberErrorClass;
}

public DynamicObject getThreadErrorClass() {
return threadErrorClass;
}

public DynamicObject getSecurityErrorClass() {
return securityErrorClass;
}

public DynamicObject getSystemCallErrorClass() {
return systemCallErrorClass;
}

public DynamicObject getEagainWaitReadable() {
return eagainWaitReadable;
}

public DynamicObject getEagainWaitWritable() {
return eagainWaitWritable;
}

public DynamicObject getTruffleModule() {
return truffleModule;
}

private static final String[] coreFiles = {
"/core/pre.rb",
"/core/lookuptable.rb",
Original file line number Diff line number Diff line change
@@ -119,6 +119,7 @@ public Object doCatch(VirtualFrame frame, Object tag, DynamicObject block,
@Primitive(name = "vm_gc_start", needsSelf = false)
public static abstract class VMGCStartPrimitiveNode extends PrimitiveArrayArgumentsNode {

@TruffleBoundary
@Specialization
public DynamicObject vmGCStart() {
System.gc();
Original file line number Diff line number Diff line change
@@ -1715,7 +1715,7 @@ protected int getCacheLimit() {

}

@CoreMethod(names = "pop", raiseIfFrozenSelf = true, optional = 1)
@CoreMethod(names = "pop", raiseIfFrozenSelf = true, optional = 1, lowerFixnum = 1)
public abstract static class PopNode extends ArrayCoreMethodNode {

@Child private ToIntNode toIntNode;
Original file line number Diff line number Diff line change
@@ -174,7 +174,7 @@ public Object instanceEval(VirtualFrame frame, Object receiver, DynamicObject st
final Rope code = StringOperations.rope(string);

// TODO (pitr 15-Oct-2015): fix this ugly hack, required for AS, copy-paste
final String space = new String(new char[Math.max(line - 1, 0)]).replace("\0", "\n");
final String space = getSpace(line);
final Source source = getContext().getSourceLoader().loadFragment(space + code.toString(), StringOperations.rope(fileName).toString());

final RubyRootNode rootNode = getContext().getCodeLoader().parse(source, code.getEncoding(), ParserContext.EVAL, null, true, this);
@@ -198,6 +198,11 @@ public Object instanceEval(VirtualFrame frame, Object receiver, NotProvided stri
return yield.dispatchWithModifiedSelf(frame, block, receiver, receiver);
}

@TruffleBoundary
private String getSpace(int line) {
return new String(new char[Math.max(line - 1, 0)]).replace("\0", "\n");
}

}

@CoreMethod(names = "instance_exec", needsBlock = true, rest = true)
Original file line number Diff line number Diff line change
@@ -71,6 +71,10 @@ public DynamicObject argumentErrorNegativeArraySize(Node currentNode) {
return argumentError(coreStrings().NEGATIVE_ARRAY_SIZE.getRope(), currentNode, null);
}

public DynamicObject argumentErrorCharacterRequired(Node currentNode) {
return argumentError("%c requires a character", currentNode);
}

public DynamicObject argumentErrorCantOmitPrecision(Node currentNode) {
return argumentError("can't omit precision for a Float.", currentNode);
}
Original file line number Diff line number Diff line change
@@ -113,26 +113,6 @@ public DynamicObject captureBacktrace(DynamicObject exception, int offset) {

}

@CoreMethod(names = "message")
public abstract static class MessageNode extends CoreMethodArrayArgumentsNode {

@Specialization
public Object message(
DynamicObject exception,
@Cached("createBinaryProfile()") ConditionProfile messageProfile) {
final Object message = Layouts.EXCEPTION.getMessage(exception);

if (messageProfile.profile(message == nil())) {
final String className = Layouts.MODULE.getFields(
Layouts.BASIC_OBJECT.getLogicalClass(exception)).getName();
return createString(StringOperations.encodeRope(className, UTF8Encoding.INSTANCE));
} else {
return message;
}
}

}

@Primitive(name = "exception_message")
public abstract static class MessagePrimitiveNode extends CoreMethodArrayArgumentsNode {

@@ -170,10 +150,14 @@ public DynamicObject exceptionErrnoError(
int errno,
DynamicObject location) {
final String errorMessage;
if (RubyGuards.isRubyString(location)) {
errorMessage = " @ " + location.toString() + " - " + message.toString();
if (message != nil()) {
if (RubyGuards.isRubyString(location)) {
errorMessage = " @ " + location.toString() + " - " + message.toString();
} else {
errorMessage = " - " + message.toString();
}
} else {
errorMessage = " - " + message.toString();
errorMessage = "";
}
return coreExceptions().errnoError(errno, errorMessage, this);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* 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
*/
package org.jruby.truffle.core.format.format;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.format.FormatNode;
import org.jruby.truffle.core.format.LiteralFormatNode;
import org.jruby.truffle.core.format.convert.ToIntegerNode;
import org.jruby.truffle.core.format.convert.ToIntegerNodeGen;
import org.jruby.truffle.core.format.convert.ToStringNode;
import org.jruby.truffle.core.format.convert.ToStringNodeGen;
import org.jruby.truffle.core.format.exceptions.NoImplicitConversionException;
import org.jruby.truffle.core.format.write.bytes.WriteByteNodeGen;
import org.jruby.truffle.language.control.RaiseException;

import java.nio.charset.StandardCharsets;

@NodeChildren({
@NodeChild(value = "width", type = FormatNode.class),
@NodeChild(value = "value", type = FormatNode.class),
})
public abstract class FormatCharacterNode extends FormatNode {

private final boolean hasMinusFlag;

@Child private ToIntegerNode toIntegerNode;
@Child private ToStringNode toStringNode;

public FormatCharacterNode(RubyContext context, boolean hasMinusFlag) {
super(context);
this.hasMinusFlag = hasMinusFlag;
}

@Specialization(
guards = {
"width == cachedWidth"
},
limit = "getLimit()"
)
byte[] formatCached(VirtualFrame frame, int width, Object value,
@Cached("width") int cachedWidth,
@Cached("makeFormatString(width)") String cachedFormatString) {
final String charString = getCharString(frame, value);
return doFormat(charString, cachedFormatString);
}

@Specialization(contains = "formatCached")
protected byte[] format(VirtualFrame frame, int width, Object value) {
final String charString = getCharString(frame, value);
return doFormat(charString, makeFormatString(width));
}

protected String getCharString(VirtualFrame frame, Object value) {
if (toStringNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toStringNode = insert(ToStringNodeGen.create(getContext(),
false,
"to_str",
false,
null,
WriteByteNodeGen.create(getContext(), new LiteralFormatNode(getContext(), value))));
}
Object toStrResult;
try {
toStrResult = toStringNode.executeToString(frame, value);
} catch (NoImplicitConversionException e) {
toStrResult = null;
}

final String charString;
if (toStrResult == null || isNil(toStrResult)) {
if (toIntegerNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toIntegerNode = insert(ToIntegerNodeGen.create(getContext(), null));
}
final int charValue = (int) toIntegerNode.executeToInteger(frame, value);
// TODO BJF check char length is > 0
charString = Character.toString((char) charValue);
} else {
final String resultString = new String((byte[]) toStrResult);
final int size = resultString.length();
if (size > 1) {
throw new RaiseException(getContext().getCoreExceptions().argumentErrorCharacterRequired(this));
}
charString = resultString;
}
return charString;
}

@TruffleBoundary
protected String makeFormatString(int width) {
final boolean leftJustified = hasMinusFlag || width < 0;
if (width < 0) {
width = -width;
}
return "%" + (leftJustified ? "-" : "") + width + "." + width + "s";
}

@TruffleBoundary
private byte[] doFormat(String charString, String formatString) {
return String.format(formatString, charString).getBytes(StandardCharsets.US_ASCII);
}

protected int getLimit() {
return getContext().getOptions().PACK_CACHE;
}

}
Original file line number Diff line number Diff line change
@@ -15,42 +15,82 @@
import com.oracle.truffle.api.dsl.Specialization;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.format.FormatNode;
import org.jruby.truffle.core.format.printf.PrintfTreeBuilder;
import org.jruby.truffle.core.format.printf.PrintfSimpleTreeBuilder;

import java.nio.charset.StandardCharsets;

@NodeChildren({
@NodeChild(value = "value", type = FormatNode.class),
@NodeChild(value = "width", type = FormatNode.class),
@NodeChild(value = "precision", type = FormatNode.class),
@NodeChild(value = "value", type = FormatNode.class),
})
public abstract class FormatFloatNode extends FormatNode {

private final String infiniteFormatString;
private final String finiteFormatString;
private final char format;
private final boolean hasSpaceFlag;
private final boolean hasZeroFlag;
private final boolean hasPlusFlag;
private final boolean hasMinusFlag;

public FormatFloatNode(RubyContext context, int width, int precision, char format, boolean hasSpaceFlag, boolean hasZeroFlag) {
public FormatFloatNode(RubyContext context, char format, boolean hasSpaceFlag, boolean hasZeroFlag, boolean hasPlusFlag, boolean hasMinusFlag) {
super(context);
final StringBuilder inifiniteFormatBuilder = new StringBuilder();
inifiniteFormatBuilder.append("%");
this.format = format;
this.hasSpaceFlag = hasSpaceFlag;
this.hasZeroFlag = hasZeroFlag;
this.hasPlusFlag = hasPlusFlag;
this.hasMinusFlag = hasMinusFlag;
}

if (hasSpaceFlag) {
inifiniteFormatBuilder.append(" ");
inifiniteFormatBuilder.append(width + 5);
}
if (hasZeroFlag && width != 0) {
inifiniteFormatBuilder.append("0");
inifiniteFormatBuilder.append(width + 5);
}
if(!hasSpaceFlag && !hasZeroFlag){
inifiniteFormatBuilder.append(width + 5);
}

@TruffleBoundary
@Specialization(guards = "isInfinite(value)")
public byte[] formatInfinite(int width, int precision, double value) {
final String infinityString = String.format(getInfiniteFormatString(width), value);
return mapInfiniteResult(infinityString).getBytes(StandardCharsets.US_ASCII);
}

inifiniteFormatBuilder.append(format);
@TruffleBoundary
@Specialization(guards = "!isInfinite(value)")
public byte[] formatFinite(int width, int precision,double value) {
return mapFiniteResult(String.format(getFiniteFormatString(width, precision), value)).getBytes(StandardCharsets.US_ASCII);
}

protected boolean isInfinite(double value) {
return Double.isInfinite(value);
}

infiniteFormatString = inifiniteFormatBuilder.toString();
private static String mapFiniteResult(String input){
// Map java finite strings to the Ruby style NaN
if(input.contains("NAN")){
return input.replaceFirst("NAN", "NaN");
}
return input;
}

private static String mapInfiniteResult(String input){
// Map java infinite strings to the Ruby style Inf
if(input.contains("-Infinity")) {
return input.replaceFirst("-Infinity", "-Inf");
} else if(input.contains("-INFINITY")){
return input.replaceFirst("-INFINITY", "-Inf");
}
else if(input.contains("INFINITY")){
return input.replaceFirst("INFINITY", "Inf");
} else if(input.contains("Infinity")) {
return input.replaceFirst("Infinity", "Inf");
}
return input;
}

private String getFiniteFormatString(final int width, final int precision){
final StringBuilder finiteFormatBuilder = new StringBuilder();
finiteFormatBuilder.append("%");
if(hasMinusFlag){
finiteFormatBuilder.append("-");
}
if(hasPlusFlag){
finiteFormatBuilder.append("+");
}

if (hasSpaceFlag) {
finiteFormatBuilder.append(" ");
@@ -71,32 +111,41 @@ public FormatFloatNode(RubyContext context, int width, int precision, char forma
finiteFormatBuilder.append(width);
}

if (precision != PrintfTreeBuilder.DEFAULT) {
if (precision != PrintfSimpleTreeBuilder.DEFAULT) {
finiteFormatBuilder.append(".");
finiteFormatBuilder.append(precision);
}

finiteFormatBuilder.append(format);

finiteFormatString = finiteFormatBuilder.toString();
return finiteFormatBuilder.toString();
}

@TruffleBoundary
@Specialization(guards = "isInfinite(value)")
public byte[] formatInfinite(double value) {
final String infinityString = String.format(infiniteFormatString, value);
final String shortenInfinityString = infinityString.substring(0, infinityString.length() - 5);
return shortenInfinityString.getBytes(StandardCharsets.US_ASCII);
}
private String getInfiniteFormatString(final int width){
final StringBuilder infiniteFormatBuilder = new StringBuilder();
infiniteFormatBuilder.append("%");
if(hasMinusFlag){
infiniteFormatBuilder.append("-");
}
if(hasPlusFlag){
infiniteFormatBuilder.append("+");
}

@TruffleBoundary
@Specialization(guards = "!isInfinite(value)")
public byte[] formatFinite(double value) {
return String.format(finiteFormatString, value).getBytes(StandardCharsets.US_ASCII);
}
if (hasSpaceFlag) {
infiniteFormatBuilder.append(" ");
infiniteFormatBuilder.append(width + 5);
}
if (hasZeroFlag && width != 0) {
infiniteFormatBuilder.append("0");
infiniteFormatBuilder.append(width + 5);
}
if(!hasSpaceFlag && !hasZeroFlag){
infiniteFormatBuilder.append(width + 5);
}

protected boolean isInfinite(double value) {
return Double.isInfinite(value);
infiniteFormatBuilder.append(format);

return infiniteFormatBuilder.toString();
}

}
Original file line number Diff line number Diff line change
@@ -9,58 +9,56 @@
*/
package org.jruby.truffle.core.format.format;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;

import org.jruby.truffle.Layouts;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.format.FormatNode;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.core.format.printf.PrintfTreeBuilder;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.format.FormatNode;
import org.jruby.truffle.core.format.printf.PrintfSimpleTreeBuilder;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;

@NodeChildren({
@NodeChild(value = "width", type = FormatNode.class),
@NodeChild(value = "precision", type = FormatNode.class),
@NodeChild(value = "value", type = FormatNode.class),
})
public abstract class FormatIntegerBinaryNode extends FormatNode {

private final char format;
private final boolean hasPlusFlag;
private final boolean useAlternativeFormat;
private final boolean isLeftJustified;
private final boolean hasMinusFlag;
private final boolean hasSpaceFlag;
private final boolean hasZeroFlag;
private final int precision;

public FormatIntegerBinaryNode(RubyContext context, char format, int precision, boolean hasPlusFlag, boolean useAlternativeFormat,
boolean isLeftJustified, boolean hasSpaceFlag, boolean hasZeroFlag) {
public FormatIntegerBinaryNode(RubyContext context, char format, boolean hasPlusFlag, boolean useAlternativeFormat,
boolean hasMinusFlag, boolean hasSpaceFlag, boolean hasZeroFlag) {
super(context);
this.format = format;
this.hasPlusFlag = hasPlusFlag;
this.useAlternativeFormat = useAlternativeFormat;
this.isLeftJustified = isLeftJustified;
this.hasMinusFlag = hasMinusFlag;
this.hasSpaceFlag = hasSpaceFlag;
this.precision = precision;
this.hasZeroFlag = hasZeroFlag;
}

@Specialization
public byte[] format(int width, int value) {
public byte[] format(int width, int precision, int value) {
final boolean isNegative = value < 0;
final boolean negativeAndPadded = isNegative && (this.hasSpaceFlag || this.hasPlusFlag);
final String formatted = negativeAndPadded ? Integer.toBinaryString(-value) : Integer.toBinaryString(value);
return getFormattedString(formatted, width, this.precision, isNegative, this.hasSpaceFlag, this.hasPlusFlag,
this.hasZeroFlag, this.useAlternativeFormat, this.isLeftJustified, this.format);
return getFormattedString(formatted, width, precision, isNegative, this.hasSpaceFlag, this.hasPlusFlag,
this.hasZeroFlag, this.useAlternativeFormat, this.hasMinusFlag, this.format);
}

@TruffleBoundary
@Specialization(guards = "isRubyBignum(value)")
public byte[] format(int width, DynamicObject value) {
public byte[] format(int width, int precision, DynamicObject value) {
final BigInteger bigInteger = Layouts.BIGNUM.getValue(value);
final boolean isNegative = bigInteger.signum() == -1;
final boolean negativeAndPadded = isNegative && (this.hasSpaceFlag || this.hasPlusFlag);
@@ -78,18 +76,18 @@ public byte[] format(int width, DynamicObject value) {
}
formatted = builder.toString();
}
return getFormattedString(formatted, width, this.precision, isNegative, this.hasSpaceFlag, this.hasPlusFlag,
this.hasZeroFlag, this.useAlternativeFormat, this.isLeftJustified, this.format);
return getFormattedString(formatted, width, precision, isNegative, this.hasSpaceFlag, this.hasPlusFlag,
this.hasZeroFlag, this.useAlternativeFormat, this.hasMinusFlag, this.format);
}

@TruffleBoundary
private static byte[] getFormattedString(String formatted, int width, int precision, boolean isNegative,
boolean isSpacePadded, boolean hasPlusFlag, boolean hasZeroFlag,
boolean useAlternativeFormat, boolean isLeftJustified,
boolean useAlternativeFormat, boolean hasMinusFlag,
char format) {
if(width < 0 && width != PrintfTreeBuilder.DEFAULT){
if(width < 0 && width != PrintfSimpleTreeBuilder.DEFAULT){
width = -width;
isLeftJustified = true;
hasMinusFlag = true;
}

if (isNegative && !(isSpacePadded || hasPlusFlag)) {
@@ -106,9 +104,9 @@ private static byte[] getFormattedString(String formatted, int width, int precis
formatted = "..1";
}
} else {
if(hasZeroFlag || precision != PrintfTreeBuilder.DEFAULT) {
if(!isLeftJustified){
final int padZeros = precision != PrintfTreeBuilder.DEFAULT ? precision : width;
if(hasZeroFlag || precision != PrintfSimpleTreeBuilder.DEFAULT) {
if(!hasMinusFlag){
final int padZeros = precision != PrintfSimpleTreeBuilder.DEFAULT ? precision : width;
while (formatted.length() < padZeros) {
formatted = "0" + formatted;
}
@@ -117,7 +115,7 @@ private static byte[] getFormattedString(String formatted, int width, int precis
}

while (formatted.length() < width) {
if(!isLeftJustified){
if(!hasMinusFlag){
formatted = " " + formatted;
} else {
formatted = formatted + " ";
Original file line number Diff line number Diff line change
@@ -10,67 +10,203 @@
package org.jruby.truffle.core.format.format;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.format.FormatNode;
import org.jruby.truffle.core.format.printf.PrintfTreeBuilder;
import org.jruby.truffle.core.format.printf.PrintfSimpleTreeBuilder;
import org.jruby.util.ConvertBytes;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.Locale;

@NodeChildren({
@NodeChild(value = "width", type = FormatNode.class),
@NodeChild(value = "precision", type = FormatNode.class),
@NodeChild(value = "value", type = FormatNode.class),
})
public abstract class FormatIntegerNode extends FormatNode {

private final char format;
private final boolean hasSpaceFlag;
private final boolean hasZeroFlag;
private final int precision;
private final boolean hasPlusFlag;
private final boolean hasMinusFlag;
private final boolean hasFSharp;

public FormatIntegerNode(RubyContext context, char format, boolean hasSpaceFlag, boolean hasZeroFlag, int precision) {
public FormatIntegerNode(RubyContext context, char format, boolean hasSpaceFlag, boolean hasZeroFlag, boolean hasPlusFlag, boolean hasMinusFlag, boolean hasFSharp) {
super(context);
this.format = format;
this.hasSpaceFlag = hasSpaceFlag;
this.hasZeroFlag = hasZeroFlag;
this.precision = precision;
this.hasPlusFlag = hasPlusFlag;
this.hasMinusFlag = hasMinusFlag;
this.hasFSharp = hasFSharp;
}

@Specialization(
guards = {
"!isRubyBignum(value)",
"width == cachedWidth"
},
limit = "getLimit()"
)
public byte[] formatCached(int width,
Object value,
@Cached("width") int cachedWidth,
@Cached("makeFormatString(width)") String cachedFormatString) {
return doFormat(value, cachedFormatString);
@TruffleBoundary
@Specialization
public byte[] format(int width, int precision, int value) {
String formatted;
switch (this.format) {
case 'X':
formatted = Integer.toString(value, 16).toUpperCase();
if (hasFSharp && value > 0) {
formatted = "0X" + formatted;
}
if (value < 0) {
formatted = "..F" + Integer.toString(-value, 16).toUpperCase();
}
break;
case 'x':
formatted = Integer.toString(value, 16);
if (hasFSharp && value > 0) {
formatted = "0x" + formatted;
}
if (value < 0) {
formatted = "..f" + Integer.toString(-value, 16);
}
break;
case 'o':
formatted = Integer.toString(value, 8);
if (hasFSharp && value > 0) {
formatted = "0" + formatted;
}
if (value < 0) {
formatted = "..7" + new String(elide(ConvertBytes.intToOctalBytes(value), format, precision));
;
}
break;
case 'd':
case 'i':
case 'u':
if (value < 0) {
formatted = Long.toString(-value);
} else {
formatted = Long.toString(value);
}
break;
default:
throw new UnsupportedOperationException();
}

return formatStart(width, precision, formatted, value < 0);
}

@TruffleBoundary
@Specialization(guards = "!isRubyBignum(value)", contains = "formatCached")
public byte[] formatUncached(int width,
Object value) {
return doFormat(value, makeFormatString(width));
@Specialization
public byte[] format(int width, int precision, long value) {
String formatted;
switch (this.format) {
case 'X':
formatted = Long.toString(value, 16).toUpperCase();
if (hasFSharp && value > 0) {
formatted = "0X" + formatted;
}
if (value < 0) {
formatted = "..F" + Long.toString(-value, 16).toUpperCase();
}
break;
case 'x':
formatted = Long.toString(value, 16);
if (hasFSharp && value > 0) {
formatted = "0x" + formatted;
}
if (value < 0) {
formatted = "..f" + Long.toString(-value, 16);
}
break;
case 'o':
formatted = Long.toString(value, 8);
if (hasFSharp && value > 0) {
formatted = "0" + formatted;
}
if (value < 0) {
formatted = "..7" + new String(elide(ConvertBytes.longToOctalBytes(value), format, precision));
}
break;
case 'd':
case 'i':
case 'u':
if (value < 0) {
formatted = Long.toString(-value);
} else {
formatted = Long.toString(value);
}
break;
default:
throw new UnsupportedOperationException();
}
return formatStart(width, precision, formatted, value < 0);
}

private static byte[] elide(byte[] bytes, char format, int precision) {
// TODO BJF need to implement the skipping of bytes per format type when value is negative
return bytes;
}

private byte[] formatStart(int width, int precision, String formatted, boolean isNegative) {
boolean leftJustified = hasMinusFlag;
if (width < 0 && width != PrintfSimpleTreeBuilder.DEFAULT) { // TODO handle default width better
width = -width;
leftJustified = true;
}

final boolean addNegative = isNegative && (format == 'd' || format == 'i' || format == 'u');

if ((hasZeroFlag && !hasMinusFlag) || precision != PrintfSimpleTreeBuilder.DEFAULT) {
int padZeros;
if (precision != PrintfSimpleTreeBuilder.DEFAULT) {
padZeros = precision;
} else {
padZeros = width;
if (addNegative) {
padZeros -= 1;
}
}

while (formatted.length() < padZeros) {
formatted = "0" + formatted;
}
}

if (addNegative) {
formatted = "-" + formatted;
}

while (formatted.length() < width) {
if (leftJustified) {
formatted = formatted + " ";
} else {
formatted = " " + formatted;
}
}

if (!isNegative) {
if (hasSpaceFlag || hasPlusFlag) {
if (!hasMinusFlag) {
if (hasPlusFlag) {
formatted = "+" + formatted;
} else {
formatted = " " + formatted;
}
}
}
}

return formatted.getBytes(StandardCharsets.US_ASCII);
}

@TruffleBoundary
@Specialization(guards = "isRubyBignum(value)")
public byte[] format(int width, DynamicObject value) {
public byte[] format(int width, int precision, DynamicObject value) {
final BigInteger bigInteger = Layouts.BIGNUM.getValue(value);

String formatted;

switch (format) {
case 'd':
case 'i':
@@ -94,7 +230,7 @@ public byte[] format(int width, DynamicObject value) {
throw new UnsupportedOperationException();
}

while (formatted.length() < this.precision) {
while (formatted.length() < precision) {
formatted = "0" + formatted;
}

@@ -105,38 +241,4 @@ public byte[] format(int width, DynamicObject value) {
return formatted.getBytes(StandardCharsets.US_ASCII);
}

@TruffleBoundary
protected byte[] doFormat(Object value, String formatString) {
return String.format(formatString, value).getBytes(StandardCharsets.US_ASCII);
}

protected String makeFormatString(int width) {
final StringBuilder builder = new StringBuilder();

builder.append("%");

final int padZeros = precision != PrintfTreeBuilder.DEFAULT ? precision : width;

if (this.hasSpaceFlag) {
builder.append(" ");
builder.append(width);

if (this.hasZeroFlag || precision != PrintfTreeBuilder.DEFAULT) {
builder.append(".");
builder.append(padZeros);
}
} else if (this.hasZeroFlag || precision != PrintfTreeBuilder.DEFAULT) {
builder.append("0");
builder.append(padZeros);
}

builder.append(format);

return builder.toString();
}

protected int getLimit() {
return getContext().getOptions().PACK_CACHE;
}

}
Original file line number Diff line number Diff line change
@@ -11,15 +11,14 @@

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.Truffle;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.format.DescriptionTruncater;
import org.jruby.truffle.core.format.FormatEncoding;
import org.jruby.truffle.core.format.FormatErrorListener;
import org.jruby.truffle.core.format.FormatRootNode;
import org.jruby.truffle.language.RubyNode;

import java.util.List;

public class PrintfCompiler {

private final RubyContext context;
@@ -31,29 +30,13 @@ public PrintfCompiler(RubyContext context, RubyNode currentNode) {
}

public CallTarget compile(String formatString, byte[] format) {
final FormatErrorListener errorListener = new FormatErrorListener(context, currentNode);

final ANTLRInputStream input = new ANTLRInputStream(bytesToChars(format), format.length);

final PrintfLexer lexer = new PrintfLexer(input);
lexer.removeErrorListeners();
lexer.addErrorListener(errorListener);

final CommonTokenStream tokens = new CommonTokenStream(lexer);

final PrintfParser parser = new PrintfParser(tokens);

final PrintfTreeBuilder builder = new PrintfTreeBuilder(context, format);
parser.addParseListener(builder);

parser.removeErrorListeners();
parser.addErrorListener(errorListener);

parser.sequence();
final PrintfSimpleParser parser = new PrintfSimpleParser(bytesToChars(format));
final List<SprintfConfig> configs = parser.parse();
final PrintfSimpleTreeBuilder builder = new PrintfSimpleTreeBuilder(context, configs);

return Truffle.getRuntime().createCallTarget(
new FormatRootNode(DescriptionTruncater.trunate(formatString),
FormatEncoding.DEFAULT, builder.getNode()));
new FormatRootNode(DescriptionTruncater.trunate(formatString),
FormatEncoding.DEFAULT, builder.getNode()));
}

private static char[] bytesToChars(byte[] bytes) {
Original file line number Diff line number Diff line change
@@ -0,0 +1,393 @@
/*
* 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
*/
package org.jruby.truffle.core.format.printf;

import org.jruby.truffle.core.format.exceptions.InvalidFormatException;

import java.util.ArrayList;
import java.util.List;

public class PrintfSimpleParser {

private final char[] source;

public PrintfSimpleParser(char[] source) {
this.source = source;
}

public List<SprintfConfig> parse() {
List<SprintfConfig> configs = new ArrayList<>();
ArgType argType = ArgType.NONE;

final int end = source.length;

for (int i = 0; i < end; ) {

// Add literal bytes up to the first %
int literalEnd = i;
for (; literalEnd < end && source[literalEnd] != '%'; literalEnd++) {
}
final int literalLength = literalEnd - i;
if (literalLength > 0) {
SprintfConfig config = new SprintfConfig();
config.setLiteral(true);
final char[] literalBytes = new char[literalLength];
System.arraycopy(source, i, literalBytes, 0, literalLength);
config.setLiteralBytes(charsToBytes(literalBytes));
configs.add(config);
}
if (literalEnd >= end) {
break; // format string ends with a literal
}

i = literalEnd + 1; // skip first %

SprintfConfig config = new SprintfConfig();
configs.add(config);

boolean finished = false;
boolean argTypeSet = false;

while (!finished) {
char p = i >= this.source.length ? '\0' : this.source[i];

switch (p) {
case ' ':
config.checkForFlags();
config.setHasSpace(true);
i++;
break;
case '#':
config.checkForFlags();
config.setFsharp(true);
i++;
break;
case '+':
config.checkForFlags();
config.setPlus(true);
i++;
break;
case '-':
config.checkForFlags();
config.setMinus(true);
i++;
break;
case '0':
config.checkForFlags();
config.setZero(true);
i++;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
LookAheadResult r = getNum(i, end);
i = r.getNextI();
if (valueAt(i) != null && valueAt(i) == '$') {
if (config.getAbsoluteArgumentIndex() != null) {
throw new InvalidFormatException("value given twice - " + r.getNumber() + "$");
}
checkPosArg(argType, r.getNumber());
argType = ArgType.NUMBERED;
argTypeSet = true;
config.setAbsoluteArgumentIndex(r.getNumber());
i++;
break;
}

config.checkForWidth();
config.setWidth(r.getNumber());
break;
case '<':
case '{':
char term = (p == '<') ? '>' : '}';

int j = i;
for (; j < end && this.source[j] != term; ) {
j++;
}
if (j >= end) {
throw new InvalidFormatException("malformed name - unmatched parenthesis");
}
final int nameLength = j - (i + 1);
final char[] nameBytes = new char[nameLength];
System.arraycopy(this.source, (i + 1), nameBytes, 0, nameLength);
config.setNamesBytes(charsToBytes(nameBytes));
i = j + 1;
checkNameArg(argType, nameBytes);
argType = ArgType.NAMED;
argTypeSet = true;
if (term == '}') {
config.setFormatType(SprintfConfig.FormatType.OTHER);
config.setFormat('s');
finished = true;
}
break;
case '*':
config.checkForWidth();

LookAheadResult numberDollarWidth = getNumberDollar(i + 1, end);
if (numberDollarWidth.getNumber() != null) {
config.setArgWidth(true);
config.setWidth(numberDollarWidth.getNumber());
checkPosArg(argType, numberDollarWidth.getNumber());
argType = ArgType.NUMBERED;
i = numberDollarWidth.getNextI();
} else {
checkNextArg(argType, 1); // TODO index next args
argType = ArgType.UNNUMBERED;
config.setWidthStar(true);
i++;
}
break;
case '.':
if (config.hasPrecision()) {
throw new InvalidFormatException("precision given twice");
}
config.setPrecisionVisited(true);
if (valueAt(i + 1) != null && valueAt(i + 1) == '*') {
LookAheadResult numberDollar = getNumberDollar(i + 2, end);
if (numberDollar.getNumber() != null) {
config.setPrecision(numberDollar.getNumber());
config.setPrecisionArg(true);
checkPosArg(argType, numberDollar.getNumber());
argType = ArgType.NUMBERED;
i = numberDollar.getNextI();
} else {
checkNextArg(argType, 1); // TODO idx
argType = ArgType.UNNUMBERED;
config.setPrecisionStar(true);
i += 2;
}
break;
}

LookAheadResult re = getNum(i + 1, end);
config.setPrecision(re.getNumber());
i = re.getNextI();
break;
case '\n':
case '\0':
i--;
case '%':
if (config.hasFlags()) {
throw new InvalidFormatException("invalid format character - %");
}
config.setLiteral(true);
byte[] literal = {(byte) '%'};
config.setLiteralBytes(literal);
i++;
finished = true;
break;
case 'c':
config.setFormatType(SprintfConfig.FormatType.OTHER);
config.setFormat(p);
i++;
if (!argTypeSet) {
checkNextArg(argType, 1);
argType = ArgType.UNNUMBERED;
}
finished = true;
break;
case 's':
case 'p':
config.setFormatType(SprintfConfig.FormatType.OTHER);
config.setFormat(p);
i++;
if (!argTypeSet) { // Speculative
checkNextArg(argType, 1);
argType = ArgType.UNNUMBERED;
}
finished = true;
break;
case 'd':
case 'i':
case 'o':
case 'x':
case 'X':
case 'b':
case 'B':
case 'u':
if (!argTypeSet) {
checkNextArg(argType, 1); // TODO idx correctly
argType = ArgType.UNNUMBERED;
}
config.setFormatType(SprintfConfig.FormatType.INTEGER);
config.setFormat(p);
finished = true;
i++;
break;
case 'g':
case 'G':
case 'e':
case 'E':
case 'a':
case 'A':
case 'f':
if (!argTypeSet) {
checkNextArg(argType, 1);
argType = ArgType.UNNUMBERED;
}
config.setFormatType(SprintfConfig.FormatType.FLOAT);
config.setFormat(p);
finished = true;
i++;
break;
default:
throw new InvalidFormatException("malformed format string - %" + p);
}
}
}
return configs;
}


private static void checkNextArg(ArgType argType, int nextArgumentIndex) {
switch (argType) {
case NUMBERED:
throw new InvalidFormatException("unnumbered(" + nextArgumentIndex + ") mixed with numbered");
case NAMED:
throw new InvalidFormatException("unnumbered(" + nextArgumentIndex + ") mixed with named");
}
}

private static void checkPosArg(ArgType posarg, int nextArgumentIndex) {
if (posarg == ArgType.UNNUMBERED) {
throw new InvalidFormatException("numbered(" + nextArgumentIndex + ") after unnumbered(" + posarg + ")");
}
if (posarg == ArgType.NAMED) {
throw new InvalidFormatException("numbered(" + nextArgumentIndex + ") after named");
}
if (nextArgumentIndex < 1) {
throw new InvalidFormatException("invalid index - " + nextArgumentIndex + "$");
}
}

private static void checkNameArg(ArgType argType, char[] name) {
if (argType == ArgType.UNNUMBERED) {
throw new InvalidFormatException("named" + new String(name) + " after unnumbered(%d)");
}
if (argType == ArgType.NUMBERED) {
throw new InvalidFormatException("named" + new String(name) + " after numbered");
}
}

private enum ArgType {
NONE,
NUMBERED,
UNNUMBERED,
NAMED
}

private void checkPosArg(int relativeArgumentIndex, int absoluteArgumentIndex) {
if (relativeArgumentIndex > 0) {
throw new InvalidFormatException("numbered(" + absoluteArgumentIndex + ") after unnumbered(" + relativeArgumentIndex + ")");
}
if (relativeArgumentIndex == -2) {
throw new InvalidFormatException("numbered(" + absoluteArgumentIndex + ") after named");
}
if (absoluteArgumentIndex < 1) {
throw new InvalidFormatException("invalid index - " + absoluteArgumentIndex + "$");
}
}

public LookAheadResult getNum(int startI, int end) {
StringBuilder sb = new StringBuilder();

int moreChars = 0;
for (int i = startI; i < end; i++) {
char nextChar = source[i];
if (!isDigit(nextChar)) {
break;
} else {
sb.append(nextChar);
moreChars += 1;
}
}

final int nextI = startI + moreChars;

if (nextI >= end) {
throw new InvalidFormatException("malformed format string - %%*[0-9]");
}

Integer result;
if (sb.length() > 0) {
result = Integer.parseInt(sb.toString());
} else {
result = null;
}
return new LookAheadResult(result, nextI);
}

public LookAheadResult getNumberDollar(int startI, int end) {
LookAheadResult lar = getNum(startI, end);
Integer result = null;
int newI = startI;
if (lar.getNumber() != null) {
final int nextI = lar.getNextI();
if (valueAt(nextI) != null && valueAt(nextI) == '$') {
result = lar.getNumber();
newI = nextI + 1;
if (result < 1) {
throw new InvalidFormatException("invalid index - " + result + "$");
}
}
}
return new LookAheadResult(result, newI);
}

public static class LookAheadResult {
private Integer number;
private int nextI;

public LookAheadResult(Integer number, int nextI) {
this.number = number;
this.nextI = nextI;
}

public Integer getNumber() {
return number;
}

public int getNextI() {
return nextI;
}
}

public static boolean isDigit(char c) {
return c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7' || c == '8' || c == '9';
}

public Character valueAt(int index) {
assert index >= 0;
if (index < this.source.length) {
return this.source[index];
} else {
return null;
}
}

private static byte[] charsToBytes(char[] chars) {
final byte[] bytes = new byte[chars.length];

for (int n = 0; n < chars.length; n++) {
bytes[n] = (byte) chars[n];
}

return bytes;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
/*
* 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
*/
package org.jruby.truffle.core.format.printf;

import com.oracle.truffle.api.object.DynamicObject;
import org.jcodings.specific.USASCIIEncoding;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.format.FormatNode;
import org.jruby.truffle.core.format.LiteralFormatNode;
import org.jruby.truffle.core.format.control.SequenceNode;
import org.jruby.truffle.core.format.convert.ToDoubleWithCoercionNodeGen;
import org.jruby.truffle.core.format.convert.ToIntegerNodeGen;
import org.jruby.truffle.core.format.convert.ToStringNodeGen;
import org.jruby.truffle.core.format.format.FormatCharacterNodeGen;
import org.jruby.truffle.core.format.format.FormatFloatHumanReadableNodeGen;
import org.jruby.truffle.core.format.format.FormatFloatNodeGen;
import org.jruby.truffle.core.format.format.FormatIntegerBinaryNodeGen;
import org.jruby.truffle.core.format.format.FormatIntegerNodeGen;
import org.jruby.truffle.core.format.read.SourceNode;
import org.jruby.truffle.core.format.read.array.ReadArgumentIndexValueNodeGen;
import org.jruby.truffle.core.format.read.array.ReadHashValueNodeGen;
import org.jruby.truffle.core.format.read.array.ReadIntegerNodeGen;
import org.jruby.truffle.core.format.read.array.ReadStringNodeGen;
import org.jruby.truffle.core.format.read.array.ReadValueNodeGen;
import org.jruby.truffle.core.format.write.bytes.WriteBytesNodeGen;
import org.jruby.truffle.core.format.write.bytes.WritePaddedBytesNodeGen;
import org.jruby.truffle.core.rope.CodeRange;

import java.util.ArrayList;
import java.util.List;

public class PrintfSimpleTreeBuilder {

private final RubyContext context;
private final List<FormatNode> sequence = new ArrayList<>();
private final List<SprintfConfig> configs;

public static int DEFAULT = -1;

private static final byte[] EMPTY_BYTES = new byte[]{};

public PrintfSimpleTreeBuilder(RubyContext context, List<SprintfConfig> configs) {
this.context = context;
this.configs = configs;
}

private void buildTree() {
for (SprintfConfig config : configs) {
final FormatNode node;
if (config.isLiteral()) {
node = WriteBytesNodeGen.create(context, new LiteralFormatNode(context, config.getLiteralBytes()));
} else {
final FormatNode valueNode;

if (config.getNamesBytes() != null) {
final DynamicObject key = context.getSymbolTable().getSymbol(context.getRopeTable().getRope(config.getNamesBytes(), USASCIIEncoding.INSTANCE, CodeRange.CR_7BIT));
valueNode = ReadHashValueNodeGen.create(context, key, new SourceNode());
} else if (config.getAbsoluteArgumentIndex() != null) {
valueNode = ReadArgumentIndexValueNodeGen.create(context, config.getAbsoluteArgumentIndex(), new SourceNode());
} else {
valueNode = ReadValueNodeGen.create(context, new SourceNode());
}

final FormatNode widthNode;
if (config.isWidthStar()) {
widthNode = ReadIntegerNodeGen.create(context, new SourceNode());
} else if (config.isArgWidth()){
widthNode = ReadArgumentIndexValueNodeGen.create(context, config.getWidth(), new SourceNode());
} else {
widthNode = new LiteralFormatNode(context, config.getWidth() == null ? -1 : config.getWidth());
}

final FormatNode precisionNode;
if(config.isPrecisionStar()){
precisionNode = ReadIntegerNodeGen.create(context, new SourceNode());
} else if(config.isPrecisionArg()){
precisionNode = ReadArgumentIndexValueNodeGen.create(context, config.getPrecision(), new SourceNode());
} else {
precisionNode = new LiteralFormatNode(context, config.getPrecision() == null ? -1 : config.getPrecision());
}


switch (config.getFormatType()){
case INTEGER:
final char format;
switch (config.getFormat()) {
case 'b':
case 'B':
format = config.getFormat();
break;
case 'd':
case 'i':
case 'u':
format = 'd';
break;
case 'o':
format = 'o';
break;
case 'x':
case 'X':
format = config.getFormat();
break;
default:
throw new UnsupportedOperationException();
}

if(config.getFormat() == 'b' || config.getFormat() == 'B'){
node = WriteBytesNodeGen.create(context,
FormatIntegerBinaryNodeGen.create(context, format,
config.isPlus(), config.isFsharp(),
config.isMinus(),
config.isHasSpace(),
config.isZero(),
widthNode,
precisionNode,
ToIntegerNodeGen.create(context, valueNode)));
} else {
node = WriteBytesNodeGen.create(context,
FormatIntegerNodeGen.create(context, format, config.isHasSpace(), config.isZero(), config.isPlus(), config.isMinus(), config.isFsharp(),
widthNode,
precisionNode,
ToIntegerNodeGen.create(context, valueNode)));
}
break;
case FLOAT:
switch (config.getFormat()){
case 'f':
case 'e':
case 'E':
node = WriteBytesNodeGen.create(context,
FormatFloatNodeGen.create(context,
config.getFormat(), config.isHasSpace(), config.isZero(), config.isPlus(), config.isMinus(),
widthNode,
precisionNode,
ToDoubleWithCoercionNodeGen.create(context,
valueNode)));
break;
case 'g':
case 'G':
node = WriteBytesNodeGen.create(context,
FormatFloatHumanReadableNodeGen.create(context,
ToDoubleWithCoercionNodeGen.create(context,
valueNode)));
break;
default:
throw new UnsupportedOperationException();
}
break;
case OTHER:
switch (config.getFormat()){
case 'c':
node = WriteBytesNodeGen.create(context,
FormatCharacterNodeGen.create(context, config.isMinus(), widthNode,
valueNode));
break;
case 's':
case 'p':
final String conversionMethodName = config.getFormat() == 's' ? "to_s" : "inspect";
final FormatNode conversionNode;

if(config.getAbsoluteArgumentIndex() == null && config.getNamesBytes() == null) {
conversionNode = ReadStringNodeGen.create(context, true, conversionMethodName, false, EMPTY_BYTES, new SourceNode());
} else {
conversionNode = ToStringNodeGen.create(context, true, conversionMethodName, false, EMPTY_BYTES, valueNode);
}

if (config.getWidth() != null || config.isWidthStar()) {
node = WritePaddedBytesNodeGen.create(context, config.isMinus(), widthNode, conversionNode);
} else {
node = WriteBytesNodeGen.create(context, conversionNode);
}
break;
default:
throw new UnsupportedOperationException();
}
break;
default:
throw new UnsupportedOperationException("unsupported type: " + config.getFormatType().toString());
}

}
sequence.add(node);
}


}



public FormatNode getNode() {
buildTree();
return new SequenceNode(context, sequence.toArray(new FormatNode[sequence.size()]));
}

}
Loading

0 comments on commit 1bf5aee

Please sign in to comment.