Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*.pch
*.pdb
*.rbinc
*.rbbin
*.rej
*.s
*.sav
Expand Down
35 changes: 18 additions & 17 deletions ext/json/lib/json/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def array_class_proc(array_class, on_load)
end
end

# TODO: exctract :create_additions support to another gem for version 3.0
# TODO: extract :create_additions support to another gem for version 3.0
def create_additions_proc(opts)
if opts[:symbolize_names]
raise ArgumentError, "options :symbolize_names and :create_additions cannot be used in conjunction"
Expand Down Expand Up @@ -87,31 +87,32 @@ def create_additions_proc(opts)
opts
end

GEM_ROOT = File.expand_path("../../../", __FILE__) + "/"
def create_additions_warning
message = "JSON.load implicit support for `create_additions: true` is deprecated " \
JSON.deprecation_warning "JSON.load implicit support for `create_additions: true` is deprecated " \
"and will be removed in 3.0, use JSON.unsafe_load or explicitly " \
"pass `create_additions: true`"
end
end
end

uplevel = 4
caller_locations(uplevel, 10).each do |frame|
if frame.path.nil? || frame.path.start_with?(GEM_ROOT) || frame.path.end_with?("/truffle/cext_ruby.rb", ".c")
uplevel += 1
else
break
end
end

if RUBY_VERSION >= "3.0"
warn(message, uplevel: uplevel - 1, category: :deprecated)
class << self
def deprecation_warning(message, uplevel = 3) # :nodoc:
gem_root = File.expand_path("../../../", __FILE__) + "/"
caller_locations(uplevel, 10).each do |frame|
if frame.path.nil? || frame.path.start_with?(gem_root) || frame.path.end_with?("/truffle/cext_ruby.rb", ".c")
uplevel += 1
else
warn(message, uplevel: uplevel - 1)
break
end
end

if RUBY_VERSION >= "3.0"
warn(message, uplevel: uplevel, category: :deprecated)
else
warn(message, uplevel: uplevel)
end
end
end

class << self
# :call-seq:
# JSON[object] -> new_array or new_string
#
Expand Down
53 changes: 49 additions & 4 deletions ext/json/parser/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -422,10 +422,12 @@ static void emit_parse_warning(const char *message, JSON_ParserState *state)
long line, column;
cursor_position(state, &line, &column);

rb_warn("%s at line %ld column %ld", message, line, column);
VALUE warning = rb_sprintf("%s at line %ld column %ld", message, line, column);
rb_funcall(mJSON, rb_intern("deprecation_warning"), 1, warning);
}

#define PARSE_ERROR_FRAGMENT_LEN 32

#ifdef RBIMPL_ATTR_NORETURN
RBIMPL_ATTR_NORETURN()
#endif
Expand Down Expand Up @@ -830,21 +832,64 @@ static inline VALUE json_decode_array(JSON_ParserState *state, JSON_ParserConfig
return array;
}

static VALUE json_find_duplicated_key(size_t count, const VALUE *pairs)
{
VALUE set = rb_hash_new_capa(count / 2);
for (size_t index = 0; index < count; index += 2) {
size_t before = RHASH_SIZE(set);
VALUE key = pairs[index];
rb_hash_aset(set, key, Qtrue);
if (RHASH_SIZE(set) == before) {
if (RB_SYMBOL_P(key)) {
return rb_sym2str(key);
}
return key;
}
}
return Qfalse;
}

static void emit_duplicate_key_warning(JSON_ParserState *state, VALUE duplicate_key)
{
VALUE message = rb_sprintf(
"detected duplicate key %"PRIsVALUE" in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`",
rb_inspect(duplicate_key)
);

emit_parse_warning(RSTRING_PTR(message), state);
RB_GC_GUARD(message);
}

#ifdef RBIMPL_ATTR_NORETURN
RBIMPL_ATTR_NORETURN()
#endif
static void raise_duplicate_key_error(JSON_ParserState *state, VALUE duplicate_key)
{
VALUE message = rb_sprintf(
"duplicate key %"PRIsVALUE,
rb_inspect(duplicate_key)
);

raise_parse_error(RSTRING_PTR(message), state);
RB_GC_GUARD(message);
}

static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfig *config, size_t count)
{
size_t entries_count = count / 2;
VALUE object = rb_hash_new_capa(entries_count);
rb_hash_bulk_insert(count, rvalue_stack_peek(state->stack, count), object);
const VALUE *pairs = rvalue_stack_peek(state->stack, count);
rb_hash_bulk_insert(count, pairs, object);

if (RB_UNLIKELY(RHASH_SIZE(object) < entries_count)) {
switch (config->on_duplicate_key) {
case JSON_IGNORE:
break;
case JSON_DEPRECATED:
emit_parse_warning("detected duplicate keys in JSON object. This will raise an error in json 3.0 unless enabled via `allow_duplicate_key: true`", state);
emit_duplicate_key_warning(state, json_find_duplicated_key(count, pairs));
break;
case JSON_RAISE:
raise_parse_error("duplicate key", state);
raise_duplicate_key_error(state, json_find_duplicated_key(count, pairs));
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/bundler/cli/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def self.patch_level_options(options)
def self.clean_after_install?
clean = Bundler.settings[:clean]
return clean unless clean.nil?
clean ||= Bundler.feature_flag.bundler_4_mode? && Bundler.settings[:path].nil?
clean ||= Bundler.feature_flag.bundler_5_mode? && Bundler.settings[:path].nil?
clean &&= !Bundler.use_system_gems?
clean
end
Expand Down
2 changes: 1 addition & 1 deletion lib/bundler/feature_flag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def self.settings_method(name, key, &default)
settings_flag(:allow_offline_install) { bundler_4_mode? }
settings_flag(:cache_all) { bundler_4_mode? }
settings_flag(:forget_cli_options) { bundler_4_mode? }
settings_flag(:global_gem_cache) { bundler_4_mode? }
settings_flag(:global_gem_cache) { bundler_5_mode? }
settings_flag(:lockfile_checksums) { bundler_4_mode? }
settings_flag(:plugins) { @bundler_version >= Gem::Version.new("1.14") }
settings_flag(:update_requires_all_flag) { bundler_5_mode? }
Expand Down
2 changes: 1 addition & 1 deletion lib/bundler/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ def path
def use_system_gems?
return true if system_path
return false if explicit_path
!Bundler.feature_flag.bundler_4_mode?
!Bundler.feature_flag.bundler_5_mode?
end

def base_path
Expand Down
4 changes: 2 additions & 2 deletions spec/bundler/commands/clean_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def should_not_have_gems(*gems)
bundle :clean

digest = Digest(:SHA1).hexdigest(git_path.to_s)
cache_path = Bundler.feature_flag.global_gem_cache? ? home(".bundle/cache/git/foo-1.0-#{digest}") : vendored_gems("cache/bundler/git/foo-1.0-#{digest}")
cache_path = vendored_gems("cache/bundler/git/foo-1.0-#{digest}")
expect(cache_path).to exist
end

Expand Down Expand Up @@ -427,7 +427,7 @@ def should_not_have_gems(*gems)
should_not_have_gems "foo-1.0"
end

it "automatically cleans when path has not been set", bundler: "4" do
it "automatically cleans when path has not been set", bundler: "5" do
build_repo2

install_gemfile <<-G
Expand Down
15 changes: 11 additions & 4 deletions spec/bundler/commands/install_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,29 @@
end

it "does not create ./.bundle by default" do
gemfile <<-G
install_gemfile <<-G
source "https://gem.repo1"
gem "myrack"
G

bundle :install # can't use install_gemfile since it sets retry
expect(bundled_app(".bundle")).not_to exist
end

it "will create a ./.bundle by default", bundler: "5" do
install_gemfile <<-G
source "https://gem.repo1"
gem "myrack"
G

expect(bundled_app(".bundle")).to exist
end

it "does not create ./.bundle by default when installing to system gems" do
gemfile <<-G
install_gemfile <<-G, env: { "BUNDLE_PATH__SYSTEM" => "true" }
source "https://gem.repo1"
gem "myrack"
G

bundle :install, env: { "BUNDLE_PATH__SYSTEM" => "true" } # can't use install_gemfile since it sets retry
expect(bundled_app(".bundle")).not_to exist
end

Expand Down
6 changes: 1 addition & 5 deletions spec/bundler/install/gems/compact_index_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1010,11 +1010,7 @@ def start
gem "myrack"
G

gem_path = if Bundler.feature_flag.global_gem_cache?
default_cache_path.dirname.join("cache", "gems", "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "myrack-1.0.0.gem")
else
default_cache_path.dirname.join("myrack-1.0.0.gem")
end
gem_path = default_cache_path.dirname.join("myrack-1.0.0.gem")

expect(exitstatus).to eq(37)
expect(err).to eq <<~E.strip
Expand Down
4 changes: 2 additions & 2 deletions spec/bundler/support/filters.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def inspect

config.filter_run_when_matching :focus unless ENV["CI"]

config.before(:each, bundler: "4") do
bundle "config simulate_version 4"
config.before(:each, :bundler) do |example|
bundle "config simulate_version #{example.metadata[:bundler]}"
end
end
12 changes: 2 additions & 10 deletions spec/bundler/support/path.rb
Original file line number Diff line number Diff line change
Expand Up @@ -136,19 +136,11 @@ def home(*path)
end

def default_bundle_path(*path)
if Bundler.feature_flag.bundler_4_mode?
local_gem_path(*path)
else
system_gem_path(*path)
end
system_gem_path(*path)
end

def default_cache_path(*path)
if Bundler.feature_flag.global_gem_cache?
home(".bundle/cache", *path)
else
default_bundle_path("cache/bundler", *path)
end
default_bundle_path("cache/bundler", *path)
end

def compact_index_cache_path
Expand Down
27 changes: 26 additions & 1 deletion test/json/json_parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,36 @@ def test_parse_big_integers

def test_parse_duplicate_key
expected = {"a" => 2}
expected_sym = {a: 2}

assert_equal expected, parse('{"a": 1, "a": 2}', allow_duplicate_key: true)
assert_raise(ParserError) { parse('{"a": 1, "a": 2}', allow_duplicate_key: false) }
assert_deprecated_warning(/duplicate keys/) do
assert_raise(ParserError) { parse('{"a": 1, "a": 2}', allow_duplicate_key: false, symbolize_names: true) }

assert_deprecated_warning(/duplicate key "a"/) do
assert_equal expected, parse('{"a": 1, "a": 2}')
end
assert_deprecated_warning(/duplicate key "a"/) do
assert_equal expected_sym, parse('{"a": 1, "a": 2}', symbolize_names: true)
end

if RUBY_ENGINE == 'RUBY_ENGINE'
assert_deprecated_warning(/#{File.basename(__FILE__)}\:#{__LINE__ + 1}/) do
assert_equal expected, parse('{"a": 1, "a": 2}')
end
end

unless RUBY_ENGINE == 'jruby'
assert_raise(ParserError) do
fake_key = Object.new
JSON.load('{"a": 1, "a": 2}', -> (obj) { obj == "a" ? fake_key : obj }, allow_duplicate_key: false)
end

assert_deprecated_warning(/duplicate key #<Object:0x/) do
fake_key = Object.new
JSON.load('{"a": 1, "a": 2}', -> (obj) { obj == "a" ? fake_key : obj })
end
end
end

def test_some_wrong_inputs
Expand Down