Skip to content

Commit b957443

Browse files
authored
Fix a bug that scanning methods that don't use Regexp don't clear named capture groups (#142)
Fix GH-135
1 parent c4e4795 commit b957443

File tree

3 files changed

+55
-20
lines changed

3 files changed

+55
-20
lines changed

ext/jruby/org/jruby/ext/strscan/RubyStringScanner.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,15 @@ private void clearMatched() {
108108
matched = false;
109109
}
110110

111+
private void clearNamedCaptures() {
112+
pattern = null;
113+
}
114+
115+
private void clearMatchStatus() {
116+
clearMatched();
117+
clearNamedCaptures();
118+
}
119+
111120
private void setMatched() {
112121
matched = true;
113122
}
@@ -167,15 +176,15 @@ public IRubyObject initialize_copy(ThreadContext context, IRubyObject other) {
167176
public IRubyObject reset(ThreadContext context) {
168177
check(context);
169178
curr = 0;
170-
clearMatched();
179+
clearMatchStatus();
171180
return this;
172181
}
173182

174183
@JRubyMethod(name = "terminate")
175184
public IRubyObject terminate(ThreadContext context) {
176185
check(context);
177186
curr = str.getByteList().getRealSize();
178-
clearMatched();
187+
clearMatchStatus();
179188
return this;
180189
}
181190

@@ -198,7 +207,7 @@ public RubyString string() {
198207
public IRubyObject set_string(ThreadContext context, IRubyObject str) {
199208
this.str = RubyString.stringValue(str);
200209
curr = 0;
201-
clearMatched();
210+
clearMatchStatus();
202211
return str;
203212
}
204213

@@ -265,7 +274,7 @@ private IRubyObject extractBegLen(Ruby runtime, int beg, int len) {
265274
private IRubyObject scan(ThreadContext context, IRubyObject regex, boolean succptr, boolean getstr, boolean headonly) {
266275
final Ruby runtime = context.runtime;
267276
check(context);
268-
clearMatched();
277+
clearMatchStatus();
269278

270279
int restLen = restLen();
271280
if (restLen < 0) {
@@ -453,7 +462,7 @@ public IRubyObject getch(ThreadContext context) {
453462

454463
public IRubyObject getchCommon(ThreadContext context) {
455464
check(context);
456-
clearMatched();
465+
clearMatchStatus();
457466
ByteList strBL = str.getByteList();
458467
int strSize = strBL.getRealSize();
459468

@@ -481,7 +490,7 @@ public IRubyObject getchCommon(ThreadContext context) {
481490
@JRubyMethod(name = "get_byte")
482491
public IRubyObject get_byte(ThreadContext context) {
483492
check(context);
484-
clearMatched();
493+
clearMatchStatus();
485494
if (curr >= str.getByteList().getRealSize()) return context.nil;
486495

487496
prev = curr;
@@ -508,7 +517,7 @@ public IRubyObject getbyte(ThreadContext context) {
508517
public IRubyObject scan_byte(ThreadContext context) {
509518
Ruby runtime = context.runtime;
510519
check(context);
511-
clearMatched();
520+
clearMatchStatus();
512521
ByteList byteList = str.getByteList();
513522
int curr = this.curr;
514523
if (curr >= byteList.getRealSize()) return context.nil;
@@ -562,7 +571,7 @@ public IRubyObject peep(ThreadContext context, IRubyObject length) {
562571
public IRubyObject scan_base10_integer(ThreadContext context) {
563572
final Ruby runtime = context.runtime;
564573
check(context);
565-
clearMatched();
574+
clearMatchStatus();
566575

567576
strscanMustAsciiCompat(runtime);
568577

@@ -597,7 +606,7 @@ public IRubyObject scan_base10_integer(ThreadContext context) {
597606
public IRubyObject scan_base16_integer(ThreadContext context) {
598607
final Ruby runtime = context.runtime;
599608
check(context);
600-
clearMatched();
609+
clearMatchStatus();
601610

602611
strscanMustAsciiCompat(runtime);
603612

@@ -667,7 +676,7 @@ public IRubyObject unscan(ThreadContext context) {
667676
}
668677

669678
curr = prev;
670-
clearMatched();
679+
clearMatchStatus();
671680

672681
return this;
673682
}

ext/strscan/strscan.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,13 @@ struct strscanner
5858
};
5959

6060
#define MATCHED_P(s) ((s)->flags & FLAG_MATCHED)
61-
#define MATCHED(s) (s)->flags |= FLAG_MATCHED
62-
#define CLEAR_MATCH_STATUS(s) (s)->flags &= ~FLAG_MATCHED
61+
#define MATCHED(s) ((s)->flags |= FLAG_MATCHED)
62+
#define CLEAR_MATCHED(s) ((s)->flags &= ~FLAG_MATCHED)
63+
#define CLEAR_NAMED_CAPTURES(s) ((s)->regex = Qnil)
64+
#define CLEAR_MATCH_STATUS(s) do {\
65+
CLEAR_MATCHED(s);\
66+
CLEAR_NAMED_CAPTURES(s);\
67+
} while (0)
6368

6469
#define S_PBEG(s) (RSTRING_PTR((s)->str))
6570
#define S_LEN(s) (RSTRING_LEN((s)->str))
@@ -216,7 +221,6 @@ strscan_s_allocate(VALUE klass)
216221
CLEAR_MATCH_STATUS(p);
217222
onig_region_init(&(p->regs));
218223
p->str = Qnil;
219-
p->regex = Qnil;
220224
return obj;
221225
}
222226

test/strscan/test_stringscanner.rb

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ def test_peek_byte
2222
def test_scan_byte
2323
omit("not implemented on TruffleRuby") if RUBY_ENGINE == "truffleruby"
2424
s = create_string_scanner('ab')
25+
assert_equal(2, s.match?(/(?<a>ab)/)) # set named_captures
2526
assert_equal(97, s.scan_byte)
27+
assert_equal({}, s.named_captures)
2628
assert_equal(98, s.scan_byte)
2729
assert_nil(s.scan_byte)
2830

@@ -176,11 +178,13 @@ def test_bol?
176178
end
177179

178180
def test_string
181+
omit("not implemented on TruffleRuby") if RUBY_ENGINE == "truffleruby"
179182
s = create_string_scanner('test string')
180183
assert_equal('test string', s.string)
181-
s.scan(/test/)
184+
s.scan(/(?<t>test)/) # set named_captures
182185
assert_equal('test string', s.string)
183186
s.string = 'a'
187+
assert_equal({}, s.named_captures)
184188
assert_equal('a', s.string)
185189
s.scan(/a/)
186190
s.string = 'b'
@@ -366,8 +370,11 @@ def test_skip_with_begenning_of_line_anchor_match
366370
end
367371

368372
def test_getch
373+
omit("not implemented on TruffleRuby") if RUBY_ENGINE == "truffleruby"
369374
s = create_string_scanner('abcde')
375+
assert_equal(3, s.match?(/(?<a>abc)/)) # set named_captures
370376
assert_equal('a', s.getch)
377+
assert_equal({}, s.named_captures)
371378
assert_equal('b', s.getch)
372379
assert_equal('c', s.getch)
373380
assert_equal('d', s.getch)
@@ -385,8 +392,11 @@ def test_getch
385392
end
386393

387394
def test_get_byte
395+
omit("not implemented on TruffleRuby") if RUBY_ENGINE == "truffleruby"
388396
s = create_string_scanner('abcde')
397+
assert_equal(3, s.match?(/(?<a>abc)/)) # set named_captures
389398
assert_equal('a', s.get_byte)
399+
assert_equal({}, s.named_captures)
390400
assert_equal('b', s.get_byte)
391401
assert_equal('c', s.get_byte)
392402
assert_equal('d', s.get_byte)
@@ -602,18 +612,22 @@ def test_post_match_string
602612
end
603613

604614
def test_terminate
605-
s = create_string_scanner('ssss')
606-
s.getch
615+
omit("not implemented on TruffleRuby") if RUBY_ENGINE == "truffleruby"
616+
s = create_string_scanner('abcd')
617+
s.scan(/(?<a>ab)/) # set named_captures
607618
s.terminate
619+
assert_equal({}, s.named_captures)
608620
assert_equal(true, s.eos?)
609621
s.terminate
610622
assert_equal(true, s.eos?)
611623
end
612624

613625
def test_reset
614-
s = create_string_scanner('ssss')
615-
s.getch
626+
omit("not implemented on TruffleRuby") if RUBY_ENGINE == "truffleruby"
627+
s = create_string_scanner('abcd')
628+
s.scan(/(?<a>ab)/) # set named_captures
616629
s.reset
630+
assert_equal({}, s.named_captures)
617631
assert_equal(0, s.pos)
618632
s.scan(/\w+/)
619633
s.reset
@@ -848,9 +862,11 @@ def test_peek
848862
end
849863

850864
def test_unscan
865+
omit("not implemented on TruffleRuby") if RUBY_ENGINE == "truffleruby"
851866
s = create_string_scanner('test string')
852-
assert_equal("test", s.scan(/\w+/))
867+
assert_equal(4, s.skip(/(?<t>test)/)) # set named_captures
853868
s.unscan
869+
assert_equal({}, s.named_captures)
854870
assert_equal("te", s.scan(/../))
855871
assert_equal(nil, s.scan(/\d/))
856872
assert_raise(ScanError) { s.unscan }
@@ -939,18 +955,22 @@ def test_scan_aref_repeatedly
939955
end
940956

941957
def test_named_captures
942-
omit("not implemented on TruffleRuby") if ["truffleruby"].include?(RUBY_ENGINE)
958+
omit("not implemented on TruffleRuby") if RUBY_ENGINE == "truffleruby"
943959
scan = StringScanner.new("foobarbaz")
944960
assert_equal({}, scan.named_captures)
945961
assert_equal(9, scan.match?(/(?<f>foo)(?<r>bar)(?<z>baz)/))
946962
assert_equal({"f" => "foo", "r" => "bar", "z" => "baz"}, scan.named_captures)
963+
assert_equal(9, scan.match?("foobarbaz"))
964+
assert_equal({}, scan.named_captures)
947965
end
948966

949967
def test_scan_integer
950968
omit("scan_integer isn't implemented on TruffleRuby yet") if RUBY_ENGINE == "truffleruby"
951969

952970
s = create_string_scanner('abc')
971+
assert_equal(3, s.match?(/(?<a>abc)/)) # set named_captures
953972
assert_nil(s.scan_integer)
973+
assert_equal({}, s.named_captures)
954974
assert_equal(0, s.pos)
955975
refute_predicate(s, :matched?)
956976

@@ -1022,7 +1042,9 @@ def test_scan_integer_base_16
10221042
assert_predicate(s, :matched?)
10231043

10241044
s = create_string_scanner('abc')
1045+
assert_equal(3, s.match?(/(?<a>abc)/)) # set named_captures
10251046
assert_equal(0xabc, s.scan_integer(base: 16))
1047+
assert_equal({}, s.named_captures)
10261048
assert_equal(3, s.pos)
10271049
assert_predicate(s, :matched?)
10281050

0 commit comments

Comments
 (0)