Skip to content

Commit

Permalink
Merge pull request #648 from casperisfine/v2.7-rubygems-workaround
Browse files Browse the repository at this point in the history
Workaround rubygems $LOAD_PATH bug
  • Loading branch information
byroot authored Oct 25, 2024
2 parents 7a3b482 + 10981db commit 9e9b749
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 41 deletions.
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changes

* Workaround a bug in 3.4.8 and older https://github.com/rubygems/rubygems/pull/6490.
* Workaround different versions of `json` and `json_pure` being loaded (not officially supported).
* Make `json_pure` Ractor compatible.

### 2024-10-24 (2.7.3)

* Numerous performance optimizations in `JSON.generate` and `JSON.dump` (up to 2 times faster).
Expand Down
11 changes: 11 additions & 0 deletions ext/json/ext/generator/generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,12 @@ static VALUE cState_generate(VALUE self, VALUE obj)
return result;
}

static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
{
rb_warn("The json gem extension was loaded with the stdlib ruby code. You should upgrade rubygems with `gem update --system`");
return self;
}

/*
* call-seq: initialize_copy(orig)
*
Expand Down Expand Up @@ -1408,6 +1414,9 @@ void Init_generator(void)
cState = rb_define_class_under(mGenerator, "State", rb_cObject);
rb_define_alloc_func(cState, cState_s_allocate);
rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
rb_define_method(cState, "initialize", cState_initialize, -1);
rb_define_alias(cState, "initialize", "initialize"); // avoid method redefinition warnings

rb_define_method(cState, "initialize_copy", cState_init_copy, 1);
rb_define_method(cState, "indent", cState_indent, 0);
rb_define_method(cState, "indent=", cState_indent_set, 1);
Expand Down Expand Up @@ -1498,4 +1507,6 @@ void Init_generator(void)
usascii_encindex = rb_usascii_encindex();
utf8_encindex = rb_utf8_encindex();
binary_encindex = rb_ascii8bit_encindex();

rb_require("json/ext/generator/state");
}
3 changes: 0 additions & 3 deletions lib/json/ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ module Ext
else
require 'json/ext/parser'
require 'json/ext/generator'
unless RUBY_ENGINE == 'jruby'
require 'json/ext/generator/state'
end
$DEBUG and warn "Using Ext extension for JSON."
JSON.parser = Parser
JSON.generator = Generator
Expand Down
29 changes: 15 additions & 14 deletions lib/json/pure/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,25 +148,26 @@ def convert_encoding(source)
end

# Unescape characters in strings.
UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
UNESCAPE_MAP.update({
?" => '"',
?\\ => '\\',
?/ => '/',
?b => "\b",
?f => "\f",
?n => "\n",
?r => "\r",
?t => "\t",
?u => nil,
})
# UNESCAPE_MAP = Hash.new { |h, k| puts; p [:k, k]; h[k] = k.chr }
UNESCAPE_MAP = {
'"' => '"',
'\\' => '\\',
'/' => '/',
'b' => "\b",
'f' => "\f",
'n' => "\n",
'r' => "\r",
't' => "\t",
'u' => nil,
}.freeze

STR_UMINUS = ''.respond_to?(:-@)
def parse_string
if scan(STRING)
return '' if self[1].empty?
string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
if u = UNESCAPE_MAP[$&[1]]
string = self[1].gsub(%r{(?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff])}n) do |c|
k = $&[1]
if u = UNESCAPE_MAP.fetch(k) { k.chr }
u
else # \uXXXX
bytes = ''.b
Expand Down
24 changes: 12 additions & 12 deletions test/json/json_generator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -261,19 +261,19 @@ def test_buffer_initial_length
end

def test_gc
if respond_to?(:assert_in_out_err) && !(RUBY_PLATFORM =~ /java/)
assert_in_out_err(%w[-rjson -Ilib -Iext], <<-EOS, [], [])
bignum_too_long_to_embed_as_string = 1234567890123456789012345
expect = bignum_too_long_to_embed_as_string.to_s
GC.stress = true
10.times do |i|
tmp = bignum_too_long_to_embed_as_string.to_json
raise "'\#{expect}' is expected, but '\#{tmp}'" unless tmp == expect
end
EOS
pid = fork do
bignum_too_long_to_embed_as_string = 1234567890123456789012345
expect = bignum_too_long_to_embed_as_string.to_s
GC.stress = true

10.times do |i|
tmp = bignum_too_long_to_embed_as_string.to_json
raise "#{expect}' is expected, but '#{tmp}'" unless tmp == expect
end
end
end if GC.respond_to?(:stress=)
_, status = Process.waitpid2(pid)
assert_predicate status, :success?
end if GC.respond_to?(:stress=) && Process.respond_to?(:fork)

def test_configure_using_configure_and_merge
numbered_state = {
Expand Down
28 changes: 19 additions & 9 deletions test/json/ractor_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@

class JSONInRactorTest < Test::Unit::TestCase
def test_generate
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true)
begin;
$VERBOSE = nil
require "json"
pid = fork do
r = Ractor.new do
json = JSON.generate({
'a' => 2,
Expand All @@ -26,9 +23,22 @@ def test_generate
})
JSON.parse(json)
end
expected_json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
'"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
assert_equal(JSON.parse(expected_json), r.take)
end;
expected_json = JSON.parse('{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
'"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}')
actual_json = r.take

if expected_json == actual_json
exit 0
else
puts "Expected:"
puts expected_json
puts "Acutual:"
puts actual_json
puts
exit 1
end
end
_, status = Process.waitpid2(pid)
assert_predicate status, :success?
end
end if defined?(Ractor)
end if defined?(Ractor) && Process.respond_to?(:fork)
6 changes: 3 additions & 3 deletions test/json/test_helper.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
case ENV['JSON']
when 'pure'
$:.unshift File.join(__dir__, '../../lib')
$LOAD_PATH.unshift(File.expand_path('../../../lib', __FILE__))
require 'json/pure'
when 'ext'
$:.unshift File.join(__dir__, '../../ext'), File.join(__dir__, '../../lib')
$LOAD_PATH.unshift(File.expand_path('../../../ext', __FILE__), File.expand_path('../../../lib', __FILE__))
require 'json/ext'
else
$:.unshift File.join(__dir__, '../../ext'), File.join(__dir__, '../../lib')
$LOAD_PATH.unshift(File.expand_path('../../../ext', __FILE__), File.expand_path('../../../lib', __FILE__))
require 'json'
end

Expand Down

0 comments on commit 9e9b749

Please sign in to comment.