oj 3.13.13 → 3.13.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/ext/oj/compat.c +10 -10
- data/ext/oj/custom.c +6 -6
- data/ext/oj/dump.c +22 -12
- data/ext/oj/dump_compat.c +0 -5
- data/ext/oj/dump_object.c +2 -57
- data/ext/oj/extconf.rb +5 -4
- data/ext/oj/mimic_json.c +19 -9
- data/ext/oj/object.c +9 -9
- data/ext/oj/oj.c +17 -3
- data/ext/oj/oj.h +1 -1
- data/ext/oj/parse.c +50 -15
- data/ext/oj/parser.c +32 -4
- data/ext/oj/parser.h +1 -0
- data/ext/oj/rails.c +0 -5
- data/ext/oj/saj2.c +299 -45
- data/ext/oj/sparse.c +4 -0
- data/ext/oj/strict.c +13 -13
- data/ext/oj/wab.c +13 -18
- data/lib/oj/saj.rb +20 -6
- data/lib/oj/version.rb +1 -1
- data/test/activesupport7/abstract_unit.rb +49 -0
- data/test/activesupport7/decoding_test.rb +125 -0
- data/test/activesupport7/encoding_test.rb +486 -0
- data/test/activesupport7/encoding_test_cases.rb +104 -0
- data/test/activesupport7/time_zone_test_helpers.rb +47 -0
- data/test/bar.rb +8 -1
- data/test/json_gem/json_generator_test.rb +2 -0
- data/test/json_gem/json_parser_test.rb +7 -0
- data/test/test_compat.rb +16 -0
- data/test/test_file.rb +18 -0
- data/test/test_parser_saj.rb +55 -2
- data/test/test_various.rb +6 -0
- metadata +8 -116
data/ext/oj/wab.c
CHANGED
@@ -194,7 +194,6 @@ static void dump_time(VALUE obj, Out out) {
|
|
194
194
|
time_t sec;
|
195
195
|
long long nsec;
|
196
196
|
|
197
|
-
#ifdef HAVE_RB_TIME_TIMESPEC
|
198
197
|
if (16 <= sizeof(struct timespec)) {
|
199
198
|
struct timespec ts = rb_time_timespec(obj);
|
200
199
|
|
@@ -204,10 +203,6 @@ static void dump_time(VALUE obj, Out out) {
|
|
204
203
|
sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
205
204
|
nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
206
205
|
}
|
207
|
-
#else
|
208
|
-
sec = NUM2LL(rb_funcall2(obj, oj_tv_sec_id, 0, 0));
|
209
|
-
nsec = NUM2LL(rb_funcall2(obj, oj_tv_nsec_id, 0, 0));
|
210
|
-
#endif
|
211
206
|
|
212
207
|
assure_size(out, 36);
|
213
208
|
// 2012-01-05T23:58:07.123456000Z
|
@@ -317,13 +312,13 @@ static VALUE calc_hash_key(ParseInfo pi, Val parent) {
|
|
317
312
|
}
|
318
313
|
|
319
314
|
static void hash_end(ParseInfo pi) {
|
320
|
-
if (Yes == pi->options.trace) {
|
315
|
+
if (RB_UNLIKELY(Yes == pi->options.trace)) {
|
321
316
|
oj_trace_parse_hash_end(pi, __FILE__, __LINE__);
|
322
317
|
}
|
323
318
|
}
|
324
319
|
|
325
320
|
static void array_end(ParseInfo pi) {
|
326
|
-
if (Yes == pi->options.trace) {
|
321
|
+
if (RB_UNLIKELY(Yes == pi->options.trace)) {
|
327
322
|
oj_trace_parse_array_end(pi, __FILE__, __LINE__);
|
328
323
|
}
|
329
324
|
}
|
@@ -333,7 +328,7 @@ static VALUE noop_hash_key(ParseInfo pi, const char *key, size_t klen) {
|
|
333
328
|
}
|
334
329
|
|
335
330
|
static void add_value(ParseInfo pi, VALUE val) {
|
336
|
-
if (Yes == pi->options.trace) {
|
331
|
+
if (RB_UNLIKELY(Yes == pi->options.trace)) {
|
337
332
|
oj_trace_parse_call("add_value", pi, __FILE__, __LINE__, val);
|
338
333
|
}
|
339
334
|
pi->stack.head->val = val;
|
@@ -483,7 +478,7 @@ static VALUE cstr_to_rstr(ParseInfo pi, const char *str, size_t len) {
|
|
483
478
|
|
484
479
|
static void add_cstr(ParseInfo pi, const char *str, size_t len, const char *orig) {
|
485
480
|
pi->stack.head->val = cstr_to_rstr(pi, str, len);
|
486
|
-
if (Yes == pi->options.trace) {
|
481
|
+
if (RB_UNLIKELY(Yes == pi->options.trace)) {
|
487
482
|
oj_trace_parse_call("add_string", pi, __FILE__, __LINE__, pi->stack.head->val);
|
488
483
|
}
|
489
484
|
}
|
@@ -493,13 +488,13 @@ static void add_num(ParseInfo pi, NumInfo ni) {
|
|
493
488
|
oj_set_error_at(pi, oj_parse_error_class, __FILE__, __LINE__, "not a number or other value");
|
494
489
|
}
|
495
490
|
pi->stack.head->val = oj_num_as_value(ni);
|
496
|
-
if (Yes == pi->options.trace) {
|
491
|
+
if (RB_UNLIKELY(Yes == pi->options.trace)) {
|
497
492
|
oj_trace_parse_call("add_number", pi, __FILE__, __LINE__, pi->stack.head->val);
|
498
493
|
}
|
499
494
|
}
|
500
495
|
|
501
496
|
static VALUE start_hash(ParseInfo pi) {
|
502
|
-
if (Yes == pi->options.trace) {
|
497
|
+
if (RB_UNLIKELY(Yes == pi->options.trace)) {
|
503
498
|
oj_trace_parse_in("start_hash", pi, __FILE__, __LINE__);
|
504
499
|
}
|
505
500
|
if (Qnil != pi->options.hash_class) {
|
@@ -512,7 +507,7 @@ static void hash_set_cstr(ParseInfo pi, Val parent, const char *str, size_t len,
|
|
512
507
|
volatile VALUE rval = cstr_to_rstr(pi, str, len);
|
513
508
|
|
514
509
|
rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), rval);
|
515
|
-
if (Yes == pi->options.trace) {
|
510
|
+
if (RB_UNLIKELY(Yes == pi->options.trace)) {
|
516
511
|
oj_trace_parse_call("set_string", pi, __FILE__, __LINE__, rval);
|
517
512
|
}
|
518
513
|
}
|
@@ -525,20 +520,20 @@ static void hash_set_num(ParseInfo pi, Val parent, NumInfo ni) {
|
|
525
520
|
}
|
526
521
|
rval = oj_num_as_value(ni);
|
527
522
|
rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), rval);
|
528
|
-
if (Yes == pi->options.trace) {
|
523
|
+
if (RB_UNLIKELY(Yes == pi->options.trace)) {
|
529
524
|
oj_trace_parse_call("set_number", pi, __FILE__, __LINE__, rval);
|
530
525
|
}
|
531
526
|
}
|
532
527
|
|
533
528
|
static void hash_set_value(ParseInfo pi, Val parent, VALUE value) {
|
534
529
|
rb_hash_aset(stack_peek(&pi->stack)->val, calc_hash_key(pi, parent), value);
|
535
|
-
if (Yes == pi->options.trace) {
|
530
|
+
if (RB_UNLIKELY(Yes == pi->options.trace)) {
|
536
531
|
oj_trace_parse_call("set_value", pi, __FILE__, __LINE__, value);
|
537
532
|
}
|
538
533
|
}
|
539
534
|
|
540
535
|
static VALUE start_array(ParseInfo pi) {
|
541
|
-
if (Yes == pi->options.trace) {
|
536
|
+
if (RB_UNLIKELY(Yes == pi->options.trace)) {
|
542
537
|
oj_trace_parse_in("start_array", pi, __FILE__, __LINE__);
|
543
538
|
}
|
544
539
|
return rb_ary_new();
|
@@ -548,7 +543,7 @@ static void array_append_cstr(ParseInfo pi, const char *str, size_t len, const c
|
|
548
543
|
volatile VALUE rval = cstr_to_rstr(pi, str, len);
|
549
544
|
|
550
545
|
rb_ary_push(stack_peek(&pi->stack)->val, rval);
|
551
|
-
if (Yes == pi->options.trace) {
|
546
|
+
if (RB_UNLIKELY(Yes == pi->options.trace)) {
|
552
547
|
oj_trace_parse_call("set_value", pi, __FILE__, __LINE__, rval);
|
553
548
|
}
|
554
549
|
}
|
@@ -561,14 +556,14 @@ static void array_append_num(ParseInfo pi, NumInfo ni) {
|
|
561
556
|
}
|
562
557
|
rval = oj_num_as_value(ni);
|
563
558
|
rb_ary_push(stack_peek(&pi->stack)->val, rval);
|
564
|
-
if (Yes == pi->options.trace) {
|
559
|
+
if (RB_UNLIKELY(Yes == pi->options.trace)) {
|
565
560
|
oj_trace_parse_call("append_number", pi, __FILE__, __LINE__, rval);
|
566
561
|
}
|
567
562
|
}
|
568
563
|
|
569
564
|
static void array_append_value(ParseInfo pi, VALUE value) {
|
570
565
|
rb_ary_push(stack_peek(&pi->stack)->val, value);
|
571
|
-
if (Yes == pi->options.trace) {
|
566
|
+
if (RB_UNLIKELY(Yes == pi->options.trace)) {
|
572
567
|
oj_trace_parse_call("append_value", pi, __FILE__, __LINE__, value);
|
573
568
|
}
|
574
569
|
}
|
data/lib/oj/saj.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
module Oj
|
2
|
-
# A SAX style parse handler for JSON hence the acronym SAJ for Simple API
|
3
|
-
# JSON. The Oj::Saj handler class
|
4
|
-
# Oj::Saj key_parse() method
|
5
|
-
# is
|
2
|
+
# A SAX style parse handler for JSON hence the acronym SAJ for Simple API
|
3
|
+
# for JSON. The Oj::Saj handler class can be subclassed and then used with
|
4
|
+
# the Oj::Saj key_parse() method or with the more resent
|
5
|
+
# Oj::Parser.new(:saj). The Saj methods will then be called as the file is
|
6
|
+
# parsed.
|
7
|
+
#
|
8
|
+
# With Oj::Parser.new(:saj) each method can also include a line and column
|
9
|
+
# argument so hash_start(key) could also be hash_start(key, line,
|
10
|
+
# column). The error() method is no used with Oj::Parser.new(:saj) so it
|
11
|
+
# will never be called.
|
6
12
|
#
|
7
13
|
# @example
|
8
|
-
#
|
14
|
+
#
|
9
15
|
# require 'oj'
|
10
16
|
#
|
11
17
|
# class MySaj < ::Oj::Saj
|
@@ -23,6 +29,14 @@ module Oj
|
|
23
29
|
# Oj.saj_parse(cnt, f)
|
24
30
|
# end
|
25
31
|
#
|
32
|
+
# or
|
33
|
+
#
|
34
|
+
# p = Oj::Parser.new(:saj)
|
35
|
+
# p.handler = MySaj.new()
|
36
|
+
# File.open('any.json', 'r') do |f|
|
37
|
+
# p.parse(f.read)
|
38
|
+
# end
|
39
|
+
#
|
26
40
|
# To make the desired methods active while parsing the desired method should
|
27
41
|
# be made public in the subclasses. If the methods remain private they will
|
28
42
|
# not be called during parsing.
|
@@ -61,6 +75,6 @@ module Oj
|
|
61
75
|
|
62
76
|
def error(message, line, column)
|
63
77
|
end
|
64
|
-
|
78
|
+
|
65
79
|
end # Saj
|
66
80
|
end # Oj
|
data/lib/oj/version.rb
CHANGED
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
ORIG_ARGV = ARGV.dup
|
4
|
+
|
5
|
+
require "bundler/setup"
|
6
|
+
require "active_support/core_ext/kernel/reporting"
|
7
|
+
|
8
|
+
silence_warnings do
|
9
|
+
Encoding.default_internal = Encoding::UTF_8
|
10
|
+
Encoding.default_external = Encoding::UTF_8
|
11
|
+
end
|
12
|
+
|
13
|
+
require "active_support/testing/autorun"
|
14
|
+
require "active_support/testing/method_call_assertions"
|
15
|
+
|
16
|
+
ENV["NO_RELOAD"] = "1"
|
17
|
+
require "active_support"
|
18
|
+
|
19
|
+
Thread.abort_on_exception = true
|
20
|
+
|
21
|
+
# Show backtraces for deprecated behavior for quicker cleanup.
|
22
|
+
ActiveSupport::Deprecation.debug = true
|
23
|
+
|
24
|
+
# Default to old to_time behavior but allow running tests with new behavior
|
25
|
+
ActiveSupport.to_time_preserves_timezone = ENV["PRESERVE_TIMEZONES"] == "1"
|
26
|
+
|
27
|
+
# Disable available locale checks to avoid warnings running the test suite.
|
28
|
+
I18n.enforce_available_locales = false
|
29
|
+
|
30
|
+
class ActiveSupport::TestCase
|
31
|
+
if Process.respond_to?(:fork) && !Gem.win_platform?
|
32
|
+
parallelize
|
33
|
+
else
|
34
|
+
parallelize(with: :threads)
|
35
|
+
end
|
36
|
+
|
37
|
+
include ActiveSupport::Testing::MethodCallAssertions
|
38
|
+
|
39
|
+
private
|
40
|
+
# Skips the current run on Rubinius using Minitest::Assertions#skip
|
41
|
+
def rubinius_skip(message = "")
|
42
|
+
skip message if RUBY_ENGINE == "rbx"
|
43
|
+
end
|
44
|
+
|
45
|
+
# Skips the current run on JRuby using Minitest::Assertions#skip
|
46
|
+
def jruby_skip(message = "")
|
47
|
+
skip message if defined?(JRUBY_VERSION)
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "abstract_unit"
|
4
|
+
require "active_support/json"
|
5
|
+
require "active_support/time"
|
6
|
+
require_relative "time_zone_test_helpers"
|
7
|
+
|
8
|
+
class TestJSONDecoding < ActiveSupport::TestCase
|
9
|
+
include TimeZoneTestHelpers
|
10
|
+
|
11
|
+
class Foo
|
12
|
+
def self.json_create(object)
|
13
|
+
"Foo"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
TESTS = {
|
18
|
+
%q({"returnTo":{"\/categories":"\/"}}) => { "returnTo" => { "/categories" => "/" } },
|
19
|
+
%q({"return\\"To\\":":{"\/categories":"\/"}}) => { "return\"To\":" => { "/categories" => "/" } },
|
20
|
+
%q({"returnTo":{"\/categories":1}}) => { "returnTo" => { "/categories" => 1 } },
|
21
|
+
%({"returnTo":[1,"a"]}) => { "returnTo" => [1, "a"] },
|
22
|
+
%({"returnTo":[1,"\\"a\\",", "b"]}) => { "returnTo" => [1, "\"a\",", "b"] },
|
23
|
+
%({"a": "'", "b": "5,000"}) => { "a" => "'", "b" => "5,000" },
|
24
|
+
%({"a": "a's, b's and c's", "b": "5,000"}) => { "a" => "a's, b's and c's", "b" => "5,000" },
|
25
|
+
# multibyte
|
26
|
+
%({"matzue": "松江", "asakusa": "浅草"}) => { "matzue" => "松江", "asakusa" => "浅草" },
|
27
|
+
%({"a": "2007-01-01"}) => { "a" => Date.new(2007, 1, 1) },
|
28
|
+
%({"a": "2007-01-01 01:12:34 Z"}) => { "a" => Time.utc(2007, 1, 1, 1, 12, 34) },
|
29
|
+
%(["2007-01-01 01:12:34 Z"]) => [Time.utc(2007, 1, 1, 1, 12, 34)],
|
30
|
+
%(["2007-01-01 01:12:34 Z", "2007-01-01 01:12:35 Z"]) => [Time.utc(2007, 1, 1, 1, 12, 34), Time.utc(2007, 1, 1, 1, 12, 35)],
|
31
|
+
# no time zone
|
32
|
+
%({"a": "2007-01-01 01:12:34"}) => { "a" => Time.new(2007, 1, 1, 1, 12, 34, "-05:00") },
|
33
|
+
# invalid date
|
34
|
+
%({"a": "1089-10-40"}) => { "a" => "1089-10-40" },
|
35
|
+
# xmlschema date notation
|
36
|
+
%({"a": "2009-08-10T19:01:02"}) => { "a" => Time.new(2009, 8, 10, 19, 1, 2, "-04:00") },
|
37
|
+
%({"a": "2009-08-10T19:01:02Z"}) => { "a" => Time.utc(2009, 8, 10, 19, 1, 2) },
|
38
|
+
%({"a": "2009-08-10T19:01:02+02:00"}) => { "a" => Time.utc(2009, 8, 10, 17, 1, 2) },
|
39
|
+
%({"a": "2009-08-10T19:01:02-05:00"}) => { "a" => Time.utc(2009, 8, 11, 00, 1, 2) },
|
40
|
+
# needs to be *exact*
|
41
|
+
%({"a": " 2007-01-01 01:12:34 Z "}) => { "a" => " 2007-01-01 01:12:34 Z " },
|
42
|
+
%({"a": "2007-01-01 : it's your birthday"}) => { "a" => "2007-01-01 : it's your birthday" },
|
43
|
+
%({"a": "Today is:\\n2020-05-21"}) => { "a" => "Today is:\n2020-05-21" },
|
44
|
+
%({"a": "2007-01-01 01:12:34 Z\\nwas my birthday"}) => { "a" => "2007-01-01 01:12:34 Z\nwas my birthday" },
|
45
|
+
%([]) => [],
|
46
|
+
%({}) => {},
|
47
|
+
%({"a":1}) => { "a" => 1 },
|
48
|
+
%({"a": ""}) => { "a" => "" },
|
49
|
+
%({"a":"\\""}) => { "a" => "\"" },
|
50
|
+
%({"a": null}) => { "a" => nil },
|
51
|
+
%({"a": true}) => { "a" => true },
|
52
|
+
%({"a": false}) => { "a" => false },
|
53
|
+
'{"bad":"\\\\","trailing":""}' => { "bad" => "\\", "trailing" => "" },
|
54
|
+
%q({"a": "http:\/\/test.host\/posts\/1"}) => { "a" => "http://test.host/posts/1" },
|
55
|
+
%q({"a": "\u003cunicode\u0020escape\u003e"}) => { "a" => "<unicode escape>" },
|
56
|
+
'{"a": "\\\\u0020skip double backslashes"}' => { "a" => "\\u0020skip double backslashes" },
|
57
|
+
%q({"a": "\u003cbr /\u003e"}) => { "a" => "<br />" },
|
58
|
+
%q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => { "b" => ["<i>", "<b>", "<u>"] },
|
59
|
+
# test combination of dates and escaped or unicode encoded data in arrays
|
60
|
+
%q([{"d":"1970-01-01", "s":"\u0020escape"},{"d":"1970-01-01", "s":"\u0020escape"}]) =>
|
61
|
+
[{ "d" => Date.new(1970, 1, 1), "s" => " escape" }, { "d" => Date.new(1970, 1, 1), "s" => " escape" }],
|
62
|
+
%q([{"d":"1970-01-01","s":"http:\/\/example.com"},{"d":"1970-01-01","s":"http:\/\/example.com"}]) =>
|
63
|
+
[{ "d" => Date.new(1970, 1, 1), "s" => "http://example.com" },
|
64
|
+
{ "d" => Date.new(1970, 1, 1), "s" => "http://example.com" }],
|
65
|
+
# tests escaping of "\n" char with Yaml backend
|
66
|
+
%q({"a":"\n"}) => { "a" => "\n" },
|
67
|
+
%q({"a":"\u000a"}) => { "a" => "\n" },
|
68
|
+
%q({"a":"Line1\u000aLine2"}) => { "a" => "Line1\nLine2" },
|
69
|
+
# prevent json unmarshalling
|
70
|
+
'{"json_class":"TestJSONDecoding::Foo"}' => { "json_class" => "TestJSONDecoding::Foo" },
|
71
|
+
# json "fragments" - these are invalid JSON, but ActionPack relies on this
|
72
|
+
'"a string"' => "a string",
|
73
|
+
"1.1" => 1.1,
|
74
|
+
"1" => 1,
|
75
|
+
"-1" => -1,
|
76
|
+
"true" => true,
|
77
|
+
"false" => false,
|
78
|
+
"null" => nil
|
79
|
+
}
|
80
|
+
|
81
|
+
TESTS.each_with_index do |(json, expected), index|
|
82
|
+
fail_message = "JSON decoding failed for #{json}"
|
83
|
+
|
84
|
+
test "json decodes #{index}" do
|
85
|
+
with_tz_default "Eastern Time (US & Canada)" do
|
86
|
+
with_parse_json_times(true) do
|
87
|
+
silence_warnings do
|
88
|
+
if expected.nil?
|
89
|
+
assert_nil ActiveSupport::JSON.decode(json), fail_message
|
90
|
+
else
|
91
|
+
assert_equal expected, ActiveSupport::JSON.decode(json), fail_message
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
test "json decodes time json with time parsing disabled" do
|
100
|
+
with_parse_json_times(false) do
|
101
|
+
expected = { "a" => "2007-01-01 01:12:34 Z" }
|
102
|
+
assert_equal expected, ActiveSupport::JSON.decode(%({"a": "2007-01-01 01:12:34 Z"}))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_failed_json_decoding
|
107
|
+
assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%(undefined)) }
|
108
|
+
assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({a: 1})) }
|
109
|
+
assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%({: 1})) }
|
110
|
+
assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%()) }
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_cannot_pass_unsupported_options
|
114
|
+
assert_raise(ArgumentError) { ActiveSupport::JSON.decode("", create_additions: true) }
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
def with_parse_json_times(value)
|
119
|
+
old_value = ActiveSupport.parse_json_times
|
120
|
+
ActiveSupport.parse_json_times = value
|
121
|
+
yield
|
122
|
+
ensure
|
123
|
+
ActiveSupport.parse_json_times = old_value
|
124
|
+
end
|
125
|
+
end
|