Skip to content

Dynamic regex seems to return incorrect, cached regex object (in --dev) #6992

@orhantoy

Description

@orhantoy

Environment Information

Provide at least:

  • JRuby version (jruby -v) and command line (flags, JRUBY_OPTS, etc)
    • jruby 9.2.16.0 (2.5.7) 2021-03-03 f82228dc32 OpenJDK 64-Bit Server VM 25.292-b10 on 1.8.0_292-b10 +jit [darwin-x86_64]
    • JRUBY_OPTS=--dev
  • Operating system and platform (e.g. uname -a)
    • Darwin OT-MBP.local 20.6.0 Darwin Kernel Version 20.6.0: Wed Nov 10 22:23:07 PST 2021; root:xnu-7195.141.14~1/RELEASE_X86_64 x86_64

Expected Behavior

File: jruby_dynamic_regex_bug.rb

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  gem 'concurrent-ruby'
end

alpha = "a-z"
digit = "0-9"

def keep_chars(component, character_class)
  component.gsub(/[^#{character_class}]/, '')
end

n_success = Concurrent::AtomicFixnum.new(0)
n_failed = Concurrent::AtomicFixnum.new(0)

threads = 50.times.map do
  Thread.new do
    1_000.times do
      hex = SecureRandom.hex

      only_alpha = keep_chars(hex, alpha)
      only_digit = keep_chars(hex, digit)

      if only_digit.match?(/\A[0-9]+\z/) && only_alpha.match?(/\A[a-z]+\z/i)
        n_success.increment
      else
        puts "[#{Thread.current.object_id}] [FAILED] hex = #{hex} only_alpha = #{only_alpha} only_digit = #{only_digit}"
        n_failed.increment
      end

      sleep 0.01
    end
  end
end

threads.each(&:join)

puts
puts "*" * 80
puts [n_success.value, n_failed.value].inspect
puts "*" * 80
$ jruby jruby_dynamic_regex_bug.rb

[...]

********************************************************************************
[50000, 0]
********************************************************************************

Note: without --dev we get the expected result.

Actual Behavior

$ jruby --dev jruby_dynamic_regex_bug.rb

[...]

********************************************************************************
[49675, 325]
********************************************************************************

Note: the exact results vary but should be [50000, 0].

If the method is changed to

def keep_chars(component, character_class)
  component.gsub(Regexp.new("[^#{character_class}]"), '')
end

it works fine.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions