Skip to content

Commit 50c78ea

Browse files
committed
Eliminate SORT_KEYS_PROC const and make default proc shareable
1 parent 645122e commit 50c78ea

6 files changed

Lines changed: 51 additions & 9 deletions

File tree

ext/json/ext/generator/generator.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ typedef struct JSON_Generator_StateStruct {
3737
VALUE sort_keys;
3838
} JSON_Generator_State;
3939

40-
static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8;
40+
static VALUE mJSON, cState, cFragment, eGeneratorError, eNestingError, Encoding_UTF_8, default_sort_keys_proc;
4141

4242
static ID i_to_s, i_to_json, i_new, i_encode;
4343
static VALUE sym_indent, sym_space, sym_space_before, sym_object_nl, sym_array_nl, sym_max_nesting, sym_allow_nan, sym_allow_duplicate_key,
@@ -1732,12 +1732,25 @@ static VALUE cState_ascii_only_set(VALUE self, VALUE enable)
17321732
return Qnil;
17331733
}
17341734

1735+
static VALUE cState_set_default_sort_keys_proc(VALUE self, VALUE proc)
1736+
{
1737+
if (!rb_obj_is_proc(proc)) {
1738+
rb_raise(rb_eTypeError, "sort_key_proc must be a Proc");
1739+
}
1740+
return default_sort_keys_proc = proc;
1741+
}
1742+
17351743
static VALUE normalize_sort_keys(VALUE value)
17361744
{
17371745
if (rb_obj_is_proc(value)) {
17381746
return value;
1747+
} else if (value == Qtrue) {
1748+
return default_sort_keys_proc;
1749+
} else if (RTEST(value)) {
1750+
rb_raise(rb_eTypeError, "The `sort_keys` argument must be a boolean or a Proc");
1751+
} else {
1752+
return Qfalse;
17391753
}
1740-
return RTEST(value) ? rb_const_get_at(mJSON, rb_intern("SORT_KEYS_PROC")) : Qfalse;
17411754
}
17421755

17431756
/*
@@ -1958,6 +1971,8 @@ void Init_generator(void)
19581971
VALUE mExt = rb_define_module_under(mJSON, "Ext");
19591972
VALUE mGenerator = rb_define_module_under(mExt, "Generator");
19601973

1974+
rb_global_variable(&default_sort_keys_proc);
1975+
19611976
rb_global_variable(&eGeneratorError);
19621977
eGeneratorError = rb_path2class("JSON::GeneratorError");
19631978

@@ -1967,6 +1982,8 @@ void Init_generator(void)
19671982
cState = rb_define_class_under(mGenerator, "State", rb_cObject);
19681983
rb_define_alloc_func(cState, cState_s_allocate);
19691984
rb_define_singleton_method(cState, "from_state", cState_from_state_s, 1);
1985+
rb_define_singleton_method(cState, "default_sort_keys_proc=", cState_set_default_sort_keys_proc, 1);
1986+
19701987
rb_define_method(cState, "initialize", cState_initialize, -1);
19711988
rb_define_alias(cState, "initialize", "initialize"); // avoid method redefinition warnings
19721989
rb_define_private_method(cState, "_configure", cState_configure, 1);

java/src/json/ext/Generator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.jruby.RubyProc;
2121
import org.jruby.RubyString;
2222
import org.jruby.RubySymbol;
23+
import org.jruby.anno.JRubyMethod;
2324
import org.jruby.runtime.Helpers;
2425
import org.jruby.runtime.ThreadContext;
2526
import org.jruby.runtime.builtin.IRubyObject;

java/src/json/ext/GeneratorState.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public class GeneratorState extends RubyObject {
3737
private boolean allowDuplicateKey = false;
3838
private boolean deprecateDuplicateKey = true;
3939

40+
private static IRubyObject defaultSortKeyProc;
41+
4042
/**
4143
* The indenting unit string. Will be repeated several times for larger
4244
* indenting levels.
@@ -165,6 +167,12 @@ static GeneratorState fromState(ThreadContext context, RuntimeInfo info,
165167
return (GeneratorState)klass.newInstance(context, context.nil);
166168
}
167169

170+
@JRubyMethod(meta=true, name="default_sort_keys_proc=")
171+
public static IRubyObject setDefaultSortKeyProc(IRubyObject klass, IRubyObject proc) {
172+
defaultSortKeyProc = proc;
173+
return proc;
174+
}
175+
168176
/**
169177
* <code>State#initialize(opts = {})</code>
170178
* <p>
@@ -451,7 +459,7 @@ public RubyProc getSortKeysProc() {
451459
private static IRubyObject normalizeSortKeys(ThreadContext context, IRubyObject value) {
452460
if (value instanceof RubyProc) return value;
453461
if (value != null && value.isTrue()) {
454-
return context.runtime.getModule("JSON").getConstant("SORT_KEYS_PROC");
462+
return defaultSortKeyProc;
455463
}
456464
return null;
457465
}

lib/json/common.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,15 @@ def parser=(parser) # :nodoc:
155155
# Set the module _generator_ to be used by JSON.
156156
def generator=(generator) # :nodoc:
157157
old, $VERBOSE = $VERBOSE, nil
158+
159+
# The default proc used when the +sort_keys+ generation option is +true+.
160+
# It returns a new hash with the entries sorted by their keys.
161+
sort_keys_proc = ->(hash) { hash.sort.to_h }
162+
if defined?(::Ractor) && Ractor.respond_to?(:shareable_lambda)
163+
sort_keys_proc = Ractor.shareable_lambda(&sort_keys_proc)
164+
end
165+
generator::State.default_sort_keys_proc = sort_keys_proc
166+
158167
@generator = generator
159168
if generator.const_defined?(:GeneratorMethods)
160169
generator_methods = generator::GeneratorMethods
@@ -243,10 +252,6 @@ def self.create_id
243252

244253
MinusInfinity = -Infinity
245254

246-
# The default proc used when the +sort_keys+ generation option is +true+.
247-
# It returns a new hash with the entries sorted by their keys.
248-
SORT_KEYS_PROC = ->(hash) { hash.sort.to_h } # :nodoc:
249-
250255
# The base exception for JSON errors.
251256
class JSONError < StandardError; end
252257

lib/json/truffle_ruby/generator.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ def self.valid_utf8?(string)
111111
# This class is used to create State instances, that are use to hold data
112112
# while generating a JSON text from a Ruby data structure.
113113
class State
114+
singleton_class.attr_accessor :default_sort_keys_proc # :nodoc:
115+
114116
def self.generate(obj, opts = nil, io = nil)
115117
new(opts).generate(obj, io)
116118
end
@@ -207,7 +209,16 @@ def initialize(opts = nil)
207209
attr_reader :sort_keys
208210

209211
def sort_keys=(value) # :nodoc:
210-
@sort_keys = Proc === value ? value : (value ? JSON::SORT_KEYS_PROC : false)
212+
@sort_keys = case value
213+
when Proc
214+
value
215+
when true
216+
State.default_sort_keys_proc
217+
when nil, false
218+
false
219+
else
220+
raise TypeError, "The `sort_keys` argument must be a boolean or a Proc"
221+
end
211222
end
212223

213224
# :stopdoc:

test/json/json_generator_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ def test_generate_sort_keys_with_proc
240240

241241
# A truthy sort_keys is normalized to the default sorting proc.
242242
state = State.new(sort_keys: true)
243-
assert_same JSON::SORT_KEYS_PROC, state.sort_keys
243+
assert_instance_of Proc, state.sort_keys
244244
end
245245

246246
def test_generate_custom

0 commit comments

Comments
 (0)