json 2.4.1 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,365 @@
1
+ # -*- coding: us-ascii -*-
2
+ # frozen_string_literal: true
3
+ require "open3"
4
+ require "timeout"
5
+ require_relative "find_executable"
6
+ begin
7
+ require 'rbconfig'
8
+ rescue LoadError
9
+ end
10
+ begin
11
+ require "rbconfig/sizeof"
12
+ rescue LoadError
13
+ end
14
+
15
+ module EnvUtil
16
+ def rubybin
17
+ if ruby = ENV["RUBY"]
18
+ return ruby
19
+ end
20
+ ruby = "ruby"
21
+ exeext = RbConfig::CONFIG["EXEEXT"]
22
+ rubyexe = (ruby + exeext if exeext and !exeext.empty?)
23
+ 3.times do
24
+ if File.exist? ruby and File.executable? ruby and !File.directory? ruby
25
+ return File.expand_path(ruby)
26
+ end
27
+ if rubyexe and File.exist? rubyexe and File.executable? rubyexe
28
+ return File.expand_path(rubyexe)
29
+ end
30
+ ruby = File.join("..", ruby)
31
+ end
32
+ if defined?(RbConfig.ruby)
33
+ RbConfig.ruby
34
+ else
35
+ "ruby"
36
+ end
37
+ end
38
+ module_function :rubybin
39
+
40
+ LANG_ENVS = %w"LANG LC_ALL LC_CTYPE"
41
+
42
+ DEFAULT_SIGNALS = Signal.list
43
+ DEFAULT_SIGNALS.delete("TERM") if /mswin|mingw/ =~ RUBY_PLATFORM
44
+
45
+ RUBYLIB = ENV["RUBYLIB"]
46
+
47
+ class << self
48
+ attr_accessor :timeout_scale
49
+ attr_reader :original_internal_encoding, :original_external_encoding,
50
+ :original_verbose, :original_warning
51
+
52
+ def capture_global_values
53
+ @original_internal_encoding = Encoding.default_internal
54
+ @original_external_encoding = Encoding.default_external
55
+ @original_verbose = $VERBOSE
56
+ @original_warning = %i[deprecated experimental].to_h {|i| [i, Warning[i]]} if RUBY_VERSION > "2.7"
57
+ end
58
+ end
59
+
60
+ def apply_timeout_scale(t)
61
+ if scale = EnvUtil.timeout_scale
62
+ t * scale
63
+ else
64
+ t
65
+ end
66
+ end
67
+ module_function :apply_timeout_scale
68
+
69
+ def timeout(sec, klass = nil, message = nil, &blk)
70
+ return yield(sec) if sec == nil or sec.zero?
71
+ sec = apply_timeout_scale(sec)
72
+ Timeout.timeout(sec, klass, message, &blk)
73
+ end
74
+ module_function :timeout
75
+
76
+ def terminate(pid, signal = :TERM, pgroup = nil, reprieve = 1)
77
+ reprieve = apply_timeout_scale(reprieve) if reprieve
78
+
79
+ signals = Array(signal).select do |sig|
80
+ DEFAULT_SIGNALS[sig.to_s] or
81
+ DEFAULT_SIGNALS[Signal.signame(sig)] rescue false
82
+ end
83
+ signals |= [:ABRT, :KILL]
84
+ case pgroup
85
+ when 0, true
86
+ pgroup = -pid
87
+ when nil, false
88
+ pgroup = pid
89
+ end
90
+
91
+ lldb = true if /darwin/ =~ RUBY_PLATFORM
92
+
93
+ while signal = signals.shift
94
+
95
+ if lldb and [:ABRT, :KILL].include?(signal)
96
+ lldb = false
97
+ # sudo -n: --non-interactive
98
+ # lldb -p: attach
99
+ # -o: run command
100
+ system(*%W[sudo -n lldb -p #{pid} --batch -o bt\ all -o call\ rb_vmdebug_stack_dump_all_threads() -o quit])
101
+ true
102
+ end
103
+
104
+ begin
105
+ Process.kill signal, pgroup
106
+ rescue Errno::EINVAL
107
+ next
108
+ rescue Errno::ESRCH
109
+ break
110
+ end
111
+ if signals.empty? or !reprieve
112
+ Process.wait(pid)
113
+ else
114
+ begin
115
+ Timeout.timeout(reprieve) {Process.wait(pid)}
116
+ rescue Timeout::Error
117
+ else
118
+ break
119
+ end
120
+ end
121
+ end
122
+ $?
123
+ end
124
+ module_function :terminate
125
+
126
+ def invoke_ruby(args, stdin_data = "", capture_stdout = false, capture_stderr = false,
127
+ encoding: nil, timeout: 10, reprieve: 1, timeout_error: Timeout::Error,
128
+ stdout_filter: nil, stderr_filter: nil,
129
+ signal: :TERM,
130
+ rubybin: EnvUtil.rubybin, precommand: nil,
131
+ **opt)
132
+ timeout = apply_timeout_scale(timeout)
133
+
134
+ in_c, in_p = IO.pipe
135
+ out_p, out_c = IO.pipe if capture_stdout
136
+ err_p, err_c = IO.pipe if capture_stderr && capture_stderr != :merge_to_stdout
137
+ opt[:in] = in_c
138
+ opt[:out] = out_c if capture_stdout
139
+ opt[:err] = capture_stderr == :merge_to_stdout ? out_c : err_c if capture_stderr
140
+ if encoding
141
+ out_p.set_encoding(encoding) if out_p
142
+ err_p.set_encoding(encoding) if err_p
143
+ end
144
+ c = "C"
145
+ child_env = {}
146
+ LANG_ENVS.each {|lc| child_env[lc] = c}
147
+ if Array === args and Hash === args.first
148
+ child_env.update(args.shift)
149
+ end
150
+ if RUBYLIB and lib = child_env["RUBYLIB"]
151
+ child_env["RUBYLIB"] = [lib, RUBYLIB].join(File::PATH_SEPARATOR)
152
+ end
153
+ child_env['ASAN_OPTIONS'] = ENV['ASAN_OPTIONS'] if ENV['ASAN_OPTIONS']
154
+ args = [args] if args.kind_of?(String)
155
+ pid = spawn(child_env, *precommand, rubybin, *args, **opt)
156
+ in_c.close
157
+ out_c&.close
158
+ out_c = nil
159
+ err_c&.close
160
+ err_c = nil
161
+ if block_given?
162
+ return yield in_p, out_p, err_p, pid
163
+ else
164
+ th_stdout = Thread.new { out_p.read } if capture_stdout
165
+ th_stderr = Thread.new { err_p.read } if capture_stderr && capture_stderr != :merge_to_stdout
166
+ in_p.write stdin_data.to_str unless stdin_data.empty?
167
+ in_p.close
168
+ if (!th_stdout || th_stdout.join(timeout)) && (!th_stderr || th_stderr.join(timeout))
169
+ timeout_error = nil
170
+ else
171
+ status = terminate(pid, signal, opt[:pgroup], reprieve)
172
+ terminated = Time.now
173
+ end
174
+ stdout = th_stdout.value if capture_stdout
175
+ stderr = th_stderr.value if capture_stderr && capture_stderr != :merge_to_stdout
176
+ out_p.close if capture_stdout
177
+ err_p.close if capture_stderr && capture_stderr != :merge_to_stdout
178
+ status ||= Process.wait2(pid)[1]
179
+ stdout = stdout_filter.call(stdout) if stdout_filter
180
+ stderr = stderr_filter.call(stderr) if stderr_filter
181
+ if timeout_error
182
+ bt = caller_locations
183
+ msg = "execution of #{bt.shift.label} expired timeout (#{timeout} sec)"
184
+ msg = failure_description(status, terminated, msg, [stdout, stderr].join("\n"))
185
+ raise timeout_error, msg, bt.map(&:to_s)
186
+ end
187
+ return stdout, stderr, status
188
+ end
189
+ ensure
190
+ [th_stdout, th_stderr].each do |th|
191
+ th.kill if th
192
+ end
193
+ [in_c, in_p, out_c, out_p, err_c, err_p].each do |io|
194
+ io&.close
195
+ end
196
+ [th_stdout, th_stderr].each do |th|
197
+ th.join if th
198
+ end
199
+ end
200
+ module_function :invoke_ruby
201
+
202
+ def verbose_warning
203
+ class << (stderr = "".dup)
204
+ alias write concat
205
+ def flush; end
206
+ end
207
+ stderr, $stderr = $stderr, stderr
208
+ $VERBOSE = true
209
+ yield stderr
210
+ return $stderr
211
+ ensure
212
+ stderr, $stderr = $stderr, stderr
213
+ $VERBOSE = EnvUtil.original_verbose
214
+ EnvUtil.original_warning.each {|i, v| Warning[i] = v}
215
+ end
216
+ module_function :verbose_warning
217
+
218
+ def default_warning
219
+ $VERBOSE = false
220
+ yield
221
+ ensure
222
+ $VERBOSE = EnvUtil.original_verbose
223
+ end
224
+ module_function :default_warning
225
+
226
+ def suppress_warning
227
+ $VERBOSE = nil
228
+ yield
229
+ ensure
230
+ $VERBOSE = EnvUtil.original_verbose
231
+ end
232
+ module_function :suppress_warning
233
+
234
+ def under_gc_stress(stress = true)
235
+ stress, GC.stress = GC.stress, stress
236
+ yield
237
+ ensure
238
+ GC.stress = stress
239
+ end
240
+ module_function :under_gc_stress
241
+
242
+ def with_default_external(enc)
243
+ suppress_warning { Encoding.default_external = enc }
244
+ yield
245
+ ensure
246
+ suppress_warning { Encoding.default_external = EnvUtil.original_external_encoding }
247
+ end
248
+ module_function :with_default_external
249
+
250
+ def with_default_internal(enc)
251
+ suppress_warning { Encoding.default_internal = enc }
252
+ yield
253
+ ensure
254
+ suppress_warning { Encoding.default_internal = EnvUtil.original_internal_encoding }
255
+ end
256
+ module_function :with_default_internal
257
+
258
+ def labeled_module(name, &block)
259
+ Module.new do
260
+ singleton_class.class_eval {
261
+ define_method(:to_s) {name}
262
+ alias inspect to_s
263
+ alias name to_s
264
+ }
265
+ class_eval(&block) if block
266
+ end
267
+ end
268
+ module_function :labeled_module
269
+
270
+ def labeled_class(name, superclass = Object, &block)
271
+ Class.new(superclass) do
272
+ singleton_class.class_eval {
273
+ define_method(:to_s) {name}
274
+ alias inspect to_s
275
+ alias name to_s
276
+ }
277
+ class_eval(&block) if block
278
+ end
279
+ end
280
+ module_function :labeled_class
281
+
282
+ if /darwin/ =~ RUBY_PLATFORM
283
+ DIAGNOSTIC_REPORTS_PATH = File.expand_path("~/Library/Logs/DiagnosticReports")
284
+ DIAGNOSTIC_REPORTS_TIMEFORMAT = '%Y-%m-%d-%H%M%S'
285
+ @ruby_install_name = RbConfig::CONFIG['RUBY_INSTALL_NAME']
286
+
287
+ def self.diagnostic_reports(signame, pid, now)
288
+ return unless %w[ABRT QUIT SEGV ILL TRAP].include?(signame)
289
+ cmd = File.basename(rubybin)
290
+ cmd = @ruby_install_name if "ruby-runner#{RbConfig::CONFIG["EXEEXT"]}" == cmd
291
+ path = DIAGNOSTIC_REPORTS_PATH
292
+ timeformat = DIAGNOSTIC_REPORTS_TIMEFORMAT
293
+ pat = "#{path}/#{cmd}_#{now.strftime(timeformat)}[-_]*.crash"
294
+ first = true
295
+ 30.times do
296
+ first ? (first = false) : sleep(0.1)
297
+ Dir.glob(pat) do |name|
298
+ log = File.read(name) rescue next
299
+ if /\AProcess:\s+#{cmd} \[#{pid}\]$/ =~ log
300
+ File.unlink(name)
301
+ File.unlink("#{path}/.#{File.basename(name)}.plist") rescue nil
302
+ return log
303
+ end
304
+ end
305
+ end
306
+ nil
307
+ end
308
+ else
309
+ def self.diagnostic_reports(signame, pid, now)
310
+ end
311
+ end
312
+
313
+ def self.failure_description(status, now, message = "", out = "")
314
+ pid = status.pid
315
+ if signo = status.termsig
316
+ signame = Signal.signame(signo)
317
+ sigdesc = "signal #{signo}"
318
+ end
319
+ log = diagnostic_reports(signame, pid, now)
320
+ if signame
321
+ sigdesc = "SIG#{signame} (#{sigdesc})"
322
+ end
323
+ if status.coredump?
324
+ sigdesc = "#{sigdesc} (core dumped)"
325
+ end
326
+ full_message = ''.dup
327
+ message = message.call if Proc === message
328
+ if message and !message.empty?
329
+ full_message << message << "\n"
330
+ end
331
+ full_message << "pid #{pid}"
332
+ full_message << " exit #{status.exitstatus}" if status.exited?
333
+ full_message << " killed by #{sigdesc}" if sigdesc
334
+ if out and !out.empty?
335
+ full_message << "\n" << out.b.gsub(/^/, '| ')
336
+ full_message.sub!(/(?<!\n)\z/, "\n")
337
+ end
338
+ if log
339
+ full_message << "Diagnostic reports:\n" << log.b.gsub(/^/, '| ')
340
+ end
341
+ full_message
342
+ end
343
+
344
+ def self.gc_stress_to_class?
345
+ unless defined?(@gc_stress_to_class)
346
+ _, _, status = invoke_ruby(["-e""exit GC.respond_to?(:add_stress_to_class)"])
347
+ @gc_stress_to_class = status.success?
348
+ end
349
+ @gc_stress_to_class
350
+ end
351
+ end
352
+
353
+ if defined?(RbConfig)
354
+ module RbConfig
355
+ @ruby = EnvUtil.rubybin
356
+ class << self
357
+ undef ruby if method_defined?(:ruby)
358
+ attr_reader :ruby
359
+ end
360
+ dir = File.dirname(ruby)
361
+ CONFIG['bindir'] = dir
362
+ end
363
+ end
364
+
365
+ EnvUtil.capture_global_values
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ require "rbconfig"
3
+
4
+ module EnvUtil
5
+ def find_executable(cmd, *args)
6
+ exts = RbConfig::CONFIG["EXECUTABLE_EXTS"].split | [RbConfig::CONFIG["EXEEXT"]]
7
+ ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
8
+ next if path.empty?
9
+ path = File.join(path, cmd)
10
+ exts.each do |ext|
11
+ cmdline = [path + ext, *args]
12
+ begin
13
+ return cmdline if yield(IO.popen(cmdline, "r", err: [:child, :out], &:read))
14
+ rescue
15
+ next
16
+ end
17
+ end
18
+ end
19
+ nil
20
+ end
21
+ module_function :find_executable
22
+ end
@@ -0,0 +1,4 @@
1
+ require "test/unit"
2
+ require_relative "core_assertions"
3
+
4
+ Test::Unit::TestCase.include Test::Unit::CoreAssertions
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ require 'test_helper'
5
+
6
+ class JSONInRactorTest < Test::Unit::TestCase
7
+ def test_generate
8
+ assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
9
+ begin;
10
+ $VERBOSE = nil
11
+ require "json"
12
+ r = Ractor.new do
13
+ json = JSON.generate({
14
+ 'a' => 2,
15
+ 'b' => 3.141,
16
+ 'c' => 'c',
17
+ 'd' => [ 1, "b", 3.14 ],
18
+ 'e' => { 'foo' => 'bar' },
19
+ 'g' => "\"\0\037",
20
+ 'h' => 1000.0,
21
+ 'i' => 0.001
22
+ })
23
+ JSON.parse(json)
24
+ end
25
+ expected_json = '{"a":2,"b":3.141,"c":"c","d":[1,"b",3.14],"e":{"foo":"bar"},' +
26
+ '"g":"\\"\\u0000\\u001f","h":1000.0,"i":0.001}'
27
+ assert_equal(JSON.parse(expected_json), r.take)
28
+ end;
29
+ end
30
+ end if defined?(Ractor)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.1
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-17 00:00:00.000000000 Z
11
+ date: 2020-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -132,6 +132,11 @@ files:
132
132
  - tests/json_generic_object_test.rb
133
133
  - tests/json_parser_test.rb
134
134
  - tests/json_string_matching_test.rb
135
+ - tests/lib/core_assertions.rb
136
+ - tests/lib/envutil.rb
137
+ - tests/lib/find_executable.rb
138
+ - tests/lib/helper.rb
139
+ - tests/ractor_test.rb
135
140
  - tests/test_helper.rb
136
141
  homepage: http://flori.github.com/json
137
142
  licenses:
@@ -162,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
162
167
  - !ruby/object:Gem::Version
163
168
  version: '0'
164
169
  requirements: []
165
- rubygems_version: 3.2.1
170
+ rubygems_version: 3.2.2
166
171
  signing_key:
167
172
  specification_version: 4
168
173
  summary: JSON Implementation for Ruby