Skip to content

Commit 9e9b749

Browse files
authored
Merge pull request #648 from casperisfine/v2.7-rubygems-workaround
Workaround rubygems $LOAD_PATH bug
2 parents 7a3b482 + 10981db commit 9e9b749

File tree

7 files changed

+64
-41
lines changed

7 files changed

+64
-41
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changes
22

3+
* Workaround a bug in 3.4.8 and older https://github.com/rubygems/rubygems/pull/6490.
4+
* Workaround different versions of `json` and `json_pure` being loaded (not officially supported).
5+
* Make `json_pure` Ractor compatible.
6+
37
### 2024-10-24 (2.7.3)
48

59
* Numerous performance optimizations in `JSON.generate` and `JSON.dump` (up to 2 times faster).

ext/json/ext/generator/generator.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,12 @@ static VALUE cState_generate(VALUE self, VALUE obj)
963963
return result;
964964
}
965965

966+
static VALUE cState_initialize(int argc, VALUE *argv, VALUE self)
967+
{
968+
rb_warn("The json gem extension was loaded with the stdlib ruby code. You should upgrade rubygems with `gem update --system`");
969+
return self;
970+
}
971+
966972
/*
967973
* call-seq: initialize_copy(orig)
968974
*
@@ -1408,6 +1414,9 @@ void Init_generator(void)
14081414
cState = rb_define_class_under(mGenerator, "State", rb_cObject);
14091415
rb_define_alloc_func(cState, cState_s_allocate);
14101416
rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
1417+
rb_define_method(cState, "initialize", cState_initialize, -1);
1418+
rb_define_alias(cState, "initialize", "initialize"); // avoid method redefinition warnings
1419+
14111420
rb_define_method(cState, "initialize_copy", cState_init_copy, 1);
14121421
rb_define_method(cState, "indent", cState_indent, 0);
14131422
rb_define_method(cState, "indent=", cState_indent_set, 1);
@@ -1498,4 +1507,6 @@ void Init_generator(void)
14981507
usascii_encindex = rb_usascii_encindex();
14991508
utf8_encindex = rb_utf8_encindex();
15001509
binary_encindex = rb_ascii8bit_encindex();
1510+
1511+
rb_require("json/ext/generator/state");
15011512
}

lib/json/ext.rb

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ module Ext
1515
else
1616
require 'json/ext/parser'
1717
require 'json/ext/generator'
18-
unless RUBY_ENGINE == 'jruby'
19-
require 'json/ext/generator/state'
20-
end
2118
$DEBUG and warn "Using Ext extension for JSON."
2219
JSON.parser = Parser
2320
JSON.generator = Generator

lib/json/pure/parser.rb

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -148,25 +148,26 @@ def convert_encoding(source)
148148
end
149149

150150
# Unescape characters in strings.
151-
UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
152-
UNESCAPE_MAP.update({
153-
?" => '"',
154-
?\\ => '\\',
155-
?/ => '/',
156-
?b => "\b",
157-
?f => "\f",
158-
?n => "\n",
159-
?r => "\r",
160-
?t => "\t",
161-
?u => nil,
162-
})
151+
# UNESCAPE_MAP = Hash.new { |h, k| puts; p [:k, k]; h[k] = k.chr }
152+
UNESCAPE_MAP = {
153+
'"' => '"',
154+
'\\' => '\\',
155+
'/' => '/',
156+
'b' => "\b",
157+
'f' => "\f",
158+
'n' => "\n",
159+
'r' => "\r",
160+
't' => "\t",
161+
'u' => nil,
162+
}.freeze
163163

164164
STR_UMINUS = ''.respond_to?(:-@)
165165
def parse_string
166166
if scan(STRING)
167167
return '' if self[1].empty?
168-
string = self[1].gsub(%r((?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff]))n) do |c|
169-
if u = UNESCAPE_MAP[$&[1]]
168+
string = self[1].gsub(%r{(?:\\[\\bfnrt"/]|(?:\\u(?:[A-Fa-f\d]{4}))+|\\[\x20-\xff])}n) do |c|
169+
k = $&[1]
170+
if u = UNESCAPE_MAP.fetch(k) { k.chr }
170171
u
171172
else # \uXXXX
172173
bytes = ''.b

test/json/json_generator_test.rb

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -261,19 +261,19 @@ def test_buffer_initial_length
261261
end
262262

263263
def test_gc
264-
if respond_to?(:assert_in_out_err) && !(RUBY_PLATFORM =~ /java/)
265-
assert_in_out_err(%w[-rjson -Ilib -Iext], <<-EOS, [], [])
266-
bignum_too_long_to_embed_as_string = 1234567890123456789012345
267-
expect = bignum_too_long_to_embed_as_string.to_s
268-
GC.stress = true
269-
270-
10.times do |i|
271-
tmp = bignum_too_long_to_embed_as_string.to_json
272-
raise "'\#{expect}' is expected, but '\#{tmp}'" unless tmp == expect
273-
end
274-
EOS
264+
pid = fork do
265+
bignum_too_long_to_embed_as_string = 1234567890123456789012345
266+
expect = bignum_too_long_to_embed_as_string.to_s
267+
GC.stress = true
268+
269+
10.times do |i|
270+
tmp = bignum_too_long_to_embed_as_string.to_json
271+
raise "#{expect}' is expected, but '#{tmp}'" unless tmp == expect
272+
end
275273
end
276-
end if GC.respond_to?(:stress=)
274+
_, status = Process.waitpid2(pid)
275+
assert_predicate status, :success?
276+
end if GC.respond_to?(:stress=) && Process.respond_to?(:fork)
277277

278278
def test_configure_using_configure_and_merge
279279
numbered_state = {

test/json/ractor_test.rb

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@
99

1010
class JSONInRactorTest < Test::Unit::TestCase
1111
def test_generate
12-
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}", ignore_stderr: true)
13-
begin;
14-
$VERBOSE = nil
15-
require "json"
12+
pid = fork do
1613
r = Ractor.new do
1714
json = JSON.generate({
1815
'a' => 2,
@@ -26,9 +23,22 @@ def test_generate
2623
})
2724
JSON.parse(json)
2825
end
29-
expected_json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
30-
'"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
31-
assert_equal(JSON.parse(expected_json), r.take)
32-
end;
26+
expected_json = JSON.parse('{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
27+
'"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}')
28+
actual_json = r.take
29+
30+
if expected_json == actual_json
31+
exit 0
32+
else
33+
puts "Expected:"
34+
puts expected_json
35+
puts "Acutual:"
36+
puts actual_json
37+
puts
38+
exit 1
39+
end
40+
end
41+
_, status = Process.waitpid2(pid)
42+
assert_predicate status, :success?
3343
end
34-
end if defined?(Ractor)
44+
end if defined?(Ractor) && Process.respond_to?(:fork)

test/json/test_helper.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
case ENV['JSON']
22
when 'pure'
3-
$:.unshift File.join(__dir__, '../../lib')
3+
$LOAD_PATH.unshift(File.expand_path('../../../lib', __FILE__))
44
require 'json/pure'
55
when 'ext'
6-
$:.unshift File.join(__dir__, '../../ext'), File.join(__dir__, '../../lib')
6+
$LOAD_PATH.unshift(File.expand_path('../../../ext', __FILE__), File.expand_path('../../../lib', __FILE__))
77
require 'json/ext'
88
else
9-
$:.unshift File.join(__dir__, '../../ext'), File.join(__dir__, '../../lib')
9+
$LOAD_PATH.unshift(File.expand_path('../../../ext', __FILE__), File.expand_path('../../../lib', __FILE__))
1010
require 'json'
1111
end
1212

0 commit comments

Comments
 (0)