• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2008 The RE2 Authors.  All Rights Reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 
5 // Regular expression engine tester -- test all the implementations against each other.
6 
7 #include "util/util.h"
8 #include "util/flags.h"
9 #include "re2/testing/tester.h"
10 #include "re2/prog.h"
11 #include "re2/re2.h"
12 #include "re2/regexp.h"
13 
14 DEFINE_bool(dump_prog, false, "dump regexp program");
15 DEFINE_bool(log_okay, false, "log successful runs");
16 DEFINE_bool(dump_rprog, false, "dump reversed regexp program");
17 
18 DEFINE_int32(max_regexp_failures, 100,
19              "maximum number of regexp test failures (-1 = unlimited)");
20 
21 DEFINE_string(regexp_engines, "", "pattern to select regexp engines to test");
22 
23 namespace re2 {
24 
25 enum {
26   kMaxSubmatch = 1+16,  // $0...$16
27 };
28 
29 const char* engine_types[kEngineMax] = {
30   "Backtrack",
31   "NFA",
32   "DFA",
33   "DFA1",
34   "OnePass",
35   "BitState",
36   "RE2",
37   "RE2a",
38   "RE2b",
39   "PCRE",
40 };
41 
42 // Returns the name string for the type t.
EngineString(Engine t)43 static string EngineString(Engine t) {
44   if (t < 0 || t >= arraysize(engine_types) || engine_types[t] == NULL) {
45     return StringPrintf("type%d", static_cast<int>(t));
46   }
47   return engine_types[t];
48 }
49 
50 // Returns bit mask of engines to use.
Engines()51 static uint32 Engines() {
52   static uint32 cached_engines;
53   static bool did_parse;
54 
55   if (did_parse)
56     return cached_engines;
57 
58   if (FLAGS_regexp_engines.empty()) {
59     cached_engines = ~0;
60   } else {
61     for (Engine i = static_cast<Engine>(0); i < kEngineMax; i++)
62       if (strstr(EngineString(i).c_str(), FLAGS_regexp_engines.c_str()))
63         cached_engines |= 1<<i;
64   }
65 
66   if (cached_engines == 0)
67     LOG(INFO) << "Warning: no engines enabled.";
68   if (!UsingPCRE)
69     cached_engines &= ~(1<<kEnginePCRE);
70   for (Engine i = static_cast<Engine>(0); i < kEngineMax; i++) {
71     if (cached_engines & (1<<i))
72       LOG(INFO) << EngineString(i) << " enabled";
73   }
74   did_parse = true;
75   return cached_engines;
76 }
77 
78 // The result of running a match.
79 struct TestInstance::Result {
80   bool skipped;         // test skipped: wasn't applicable
81   bool matched;         // found a match
82   bool untrusted;       // don't really trust the answer
83   bool have_submatch;   // computed all submatch info
84   bool have_submatch0;  // computed just submatch[0]
85   StringPiece submatch[kMaxSubmatch];
86 };
87 
88 typedef TestInstance::Result Result;
89 
90 // Formats a single capture range s in text in the form (a,b)
91 // where a and b are the starting and ending offsets of s in text.
FormatCapture(const StringPiece & text,const StringPiece & s)92 static string FormatCapture(const StringPiece& text, const StringPiece& s) {
93   if (s.begin() == NULL)
94     return "(?,?)";
95   return StringPrintf("(%d,%d)",
96                       static_cast<int>(s.begin() - text.begin()),
97                       static_cast<int>(s.end() - text.begin()));
98 }
99 
100 // Returns whether text contains non-ASCII (>= 0x80) bytes.
NonASCII(const StringPiece & text)101 static bool NonASCII(const StringPiece& text) {
102   for (int i = 0; i < text.size(); i++)
103     if ((uint8)text[i] >= 0x80)
104       return true;
105   return false;
106 }
107 
108 // Returns string representation of match kind.
FormatKind(Prog::MatchKind kind)109 static string FormatKind(Prog::MatchKind kind) {
110   switch (kind) {
111     case Prog::kFullMatch:
112       return "full match";
113     case Prog::kLongestMatch:
114       return "longest match";
115     case Prog::kFirstMatch:
116       return "first match";
117     case Prog::kManyMatch:
118       return "many match";
119   }
120   return "???";
121 }
122 
123 // Returns string representation of anchor kind.
FormatAnchor(Prog::Anchor anchor)124 static string FormatAnchor(Prog::Anchor anchor) {
125   switch (anchor) {
126     case Prog::kAnchored:
127       return "anchored";
128     case Prog::kUnanchored:
129       return "unanchored";
130   }
131   return "???";
132 }
133 
134 struct ParseMode {
135   Regexp::ParseFlags parse_flags;
136   string desc;
137 };
138 
139 static const Regexp::ParseFlags single_line =
140   Regexp::LikePerl;
141 static const Regexp::ParseFlags multi_line =
142   static_cast<Regexp::ParseFlags>(Regexp::LikePerl & ~Regexp::OneLine);
143 
144 static ParseMode parse_modes[] = {
145   { single_line,                   "single-line"          },
146   { single_line|Regexp::Latin1,    "single-line, latin1"  },
147   { multi_line,                    "multiline"            },
148   { multi_line|Regexp::NonGreedy,  "multiline, nongreedy" },
149   { multi_line|Regexp::Latin1,     "multiline, latin1"    },
150 };
151 
FormatMode(Regexp::ParseFlags flags)152 static string FormatMode(Regexp::ParseFlags flags) {
153   for (int i = 0; i < arraysize(parse_modes); i++)
154     if (parse_modes[i].parse_flags == flags)
155       return parse_modes[i].desc;
156   return StringPrintf("%#x", static_cast<uint>(flags));
157 }
158 
159 // Constructs and saves all the matching engines that
160 // will be required for the given tests.
TestInstance(const StringPiece & regexp_str,Prog::MatchKind kind,Regexp::ParseFlags flags)161 TestInstance::TestInstance(const StringPiece& regexp_str, Prog::MatchKind kind,
162                            Regexp::ParseFlags flags)
163   : regexp_str_(regexp_str),
164     kind_(kind),
165     flags_(flags),
166     error_(false),
167     regexp_(NULL),
168     num_captures_(0),
169     prog_(NULL),
170     rprog_(NULL),
171     re_(NULL),
172     re2_(NULL) {
173 
174   VLOG(1) << CEscape(regexp_str);
175 
176   // Compile regexp to prog.
177   // Always required - needed for backtracking (reference implementation).
178   RegexpStatus status;
179   regexp_ = Regexp::Parse(regexp_str, flags, &status);
180   if (regexp_ == NULL) {
181     LOG(INFO) << "Cannot parse: " << CEscape(regexp_str_)
182               << " mode: " << FormatMode(flags);
183     error_ = true;
184     return;
185   }
186   num_captures_ = regexp_->NumCaptures();
187   prog_ = regexp_->CompileToProg(0);
188   if (prog_ == NULL) {
189     LOG(INFO) << "Cannot compile: " << CEscape(regexp_str_);
190     error_ = true;
191     return;
192   }
193   if (FLAGS_dump_prog) {
194     LOG(INFO) << "Prog for "
195               << " regexp "
196               << CEscape(regexp_str_)
197               << " (" << FormatKind(kind_)
198               << ", " << FormatMode(flags_)
199               << ")\n"
200               << prog_->Dump();
201   }
202 
203   // Compile regexp to reversed prog.  Only needed for DFA engines.
204   if (Engines() & ((1<<kEngineDFA)|(1<<kEngineDFA1))) {
205     rprog_ = regexp_->CompileToReverseProg(0);
206     if (rprog_ == NULL) {
207       LOG(INFO) << "Cannot reverse compile: " << CEscape(regexp_str_);
208       error_ = true;
209       return;
210     }
211     if (FLAGS_dump_rprog)
212       LOG(INFO) << rprog_->Dump();
213   }
214 
215   // Create re string that will be used for RE and RE2.
216   string re = regexp_str.as_string();
217   // Accomodate flags.
218   // Regexp::Latin1 will be accomodated below.
219   if (!(flags & Regexp::OneLine))
220     re = "(?m)" + re;
221   if (flags & Regexp::NonGreedy)
222     re = "(?U)" + re;
223   if (flags & Regexp::DotNL)
224     re = "(?s)" + re;
225 
226   // Compile regexp to RE2.
227   if (Engines() & ((1<<kEngineRE2)|(1<<kEngineRE2a)|(1<<kEngineRE2b))) {
228     RE2::Options options;
229     if (flags & Regexp::Latin1)
230       options.set_encoding(RE2::Options::EncodingLatin1);
231     if (kind_ == Prog::kLongestMatch)
232       options.set_longest_match(true);
233     re2_ = new RE2(re, options);
234     if (!re2_->error().empty()) {
235       LOG(INFO) << "Cannot RE2: " << CEscape(re);
236       error_ = true;
237       return;
238     }
239   }
240 
241   // Compile regexp to RE.
242   // PCRE as exposed by the RE interface isn't always usable.
243   // 1. It disagrees about handling of empty-string reptitions
244   //    like matching (a*)* against "b".  PCRE treats the (a*) as
245   //    occurring once, while we treat it as occurring not at all.
246   // 2. It treats $ as this weird thing meaning end of string
247   //    or before the \n at the end of the string.
248   // 3. It doesn't implement POSIX leftmost-longest matching.
249   // MimicsPCRE() detects 1 and 2.
250   if ((Engines() & (1<<kEnginePCRE)) && regexp_->MimicsPCRE() &&
251       kind_ != Prog::kLongestMatch) {
252     PCRE_Options o;
253     o.set_option(PCRE::UTF8);
254     if (flags & Regexp::Latin1)
255       o.set_option(PCRE::None);
256     // PCRE has interface bug keeping us from finding $0, so
257     // add one more layer of parens.
258     re_ = new PCRE("("+re+")", o);
259     if (!re_->error().empty()) {
260       LOG(INFO) << "Cannot PCRE: " << CEscape(re);
261       error_ = true;
262       return;
263     }
264   }
265 }
266 
~TestInstance()267 TestInstance::~TestInstance() {
268   if (regexp_)
269     regexp_->Decref();
270   delete prog_;
271   delete rprog_;
272   delete re_;
273   delete re2_;
274 }
275 
276 // Runs a single search using the named engine type.
277 // This interface hides all the irregularities of the various
278 // engine interfaces from the rest of this file.
RunSearch(Engine type,const StringPiece & orig_text,const StringPiece & orig_context,Prog::Anchor anchor,Result * result)279 void TestInstance::RunSearch(Engine type,
280                              const StringPiece& orig_text,
281                              const StringPiece& orig_context,
282                              Prog::Anchor anchor,
283                              Result *result) {
284   memset(result, 0, sizeof *result);
285   if (regexp_ == NULL) {
286     result->skipped = true;
287     return;
288   }
289   int nsubmatch = 1 + num_captures_;  // NumCaptures doesn't count $0
290   if (nsubmatch > kMaxSubmatch)
291     nsubmatch = kMaxSubmatch;
292 
293   StringPiece text = orig_text;
294   StringPiece context = orig_context;
295 
296   switch (type) {
297     default:
298       LOG(FATAL) << "Bad RunSearch type: " << (int)type;
299 
300     case kEngineBacktrack:
301       if (prog_ == NULL) {
302         result->skipped = true;
303         break;
304       }
305       result->matched =
306         prog_->UnsafeSearchBacktrack(text, context, anchor, kind_,
307                                      result->submatch, nsubmatch);
308       result->have_submatch = true;
309       break;
310 
311     case kEngineNFA:
312       if (prog_ == NULL) {
313         result->skipped = true;
314         break;
315       }
316       result->matched =
317         prog_->SearchNFA(text, context, anchor, kind_,
318                         result->submatch, nsubmatch);
319       result->have_submatch = true;
320       break;
321 
322     case kEngineDFA:
323       if (prog_ == NULL) {
324         result->skipped = true;
325         break;
326       }
327       result->matched = prog_->SearchDFA(text, context, anchor, kind_, NULL,
328                                          &result->skipped, NULL);
329       break;
330 
331     case kEngineDFA1:
332       if (prog_ == NULL || rprog_ == NULL) {
333         result->skipped = true;
334         break;
335       }
336       result->matched =
337         prog_->SearchDFA(text, context, anchor, kind_, result->submatch,
338                          &result->skipped, NULL);
339       // If anchored, no need for second run,
340       // but do it anyway to find more bugs.
341       if (result->matched) {
342         if (!rprog_->SearchDFA(result->submatch[0], context,
343                                Prog::kAnchored, Prog::kLongestMatch,
344                                result->submatch,
345                                &result->skipped, NULL)) {
346           LOG(ERROR) << "Reverse DFA inconsistency: " << CEscape(regexp_str_)
347                      << " on " << CEscape(text);
348           result->matched = false;
349         }
350       }
351       result->have_submatch0 = true;
352       break;
353 
354     case kEngineOnePass:
355       if (prog_ == NULL ||
356           anchor == Prog::kUnanchored ||
357           !prog_->IsOnePass() ||
358           nsubmatch > Prog::kMaxOnePassCapture) {
359         result->skipped = true;
360         break;
361       }
362       result->matched = prog_->SearchOnePass(text, context, anchor, kind_,
363                                       result->submatch, nsubmatch);
364       result->have_submatch = true;
365       break;
366 
367     case kEngineBitState:
368       if (prog_ == NULL) {
369         result->skipped = true;
370         break;
371       }
372       result->matched = prog_->SearchBitState(text, context, anchor, kind_,
373                                               result->submatch, nsubmatch);
374       result->have_submatch = true;
375       break;
376 
377     case kEngineRE2:
378     case kEngineRE2a:
379     case kEngineRE2b: {
380       if (!re2_ || text.end() != context.end()) {
381         result->skipped = true;
382         break;
383       }
384 
385       RE2::Anchor re_anchor;
386       if (anchor == Prog::kAnchored)
387         re_anchor = RE2::ANCHOR_START;
388       else
389         re_anchor = RE2::UNANCHORED;
390       if (kind_ == Prog::kFullMatch)
391         re_anchor = RE2::ANCHOR_BOTH;
392 
393       result->matched = re2_->Match(context,
394                                     text.begin() - context.begin(),
395                                     text.end() - context.begin(),
396                                     re_anchor, result->submatch, nsubmatch);
397       result->have_submatch = nsubmatch > 0;
398       break;
399     }
400 
401     case kEnginePCRE: {
402       if (!re_ || text.begin() != context.begin() ||
403           text.end() != context.end()) {
404         result->skipped = true;
405         break;
406       }
407 
408       const PCRE::Arg **argptr = new const PCRE::Arg*[nsubmatch];
409       PCRE::Arg *a = new PCRE::Arg[nsubmatch];
410       for (int i = 0; i < nsubmatch; i++) {
411         a[i] = PCRE::Arg(&result->submatch[i]);
412         argptr[i] = &a[i];
413       }
414       int consumed;
415       PCRE::Anchor pcre_anchor;
416       if (anchor == Prog::kAnchored)
417         pcre_anchor = PCRE::ANCHOR_START;
418       else
419         pcre_anchor = PCRE::UNANCHORED;
420       if (kind_ == Prog::kFullMatch)
421         pcre_anchor = PCRE::ANCHOR_BOTH;
422       re_->ClearHitLimit();
423       result->matched =
424         re_->DoMatch(text,
425                      pcre_anchor,
426                      &consumed,
427                      argptr, nsubmatch);
428       if (re_->HitLimit()) {
429         result->untrusted = true;
430         delete[] argptr;
431         delete[] a;
432         break;
433       }
434       result->have_submatch = true;
435 
436       // Work around RE interface bug: PCRE returns -1 as the
437       // offsets for an unmatched subexpression, and RE should
438       // turn that into StringPiece(NULL) but in fact it uses
439       // StringPiece(text.begin() - 1, 0).  Oops.
440       for (int i = 0; i < nsubmatch; i++)
441         if (result->submatch[i].begin() == text.begin() - 1)
442           result->submatch[i] = NULL;
443       delete[] argptr;
444       delete[] a;
445       break;
446     }
447   }
448 
449   if (!result->matched)
450     memset(result->submatch, 0, sizeof result->submatch);
451 }
452 
453 // Checks whether r is okay given that correct is the right answer.
454 // Specifically, r's answers have to match (but it doesn't have to
455 // claim to have all the answers).
ResultOkay(const Result & r,const Result & correct)456 static bool ResultOkay(const Result& r, const Result& correct) {
457   if (r.skipped)
458     return true;
459   if (r.matched != correct.matched)
460     return false;
461   if (r.have_submatch || r.have_submatch0) {
462     for (int i = 0; i < kMaxSubmatch; i++) {
463       if (correct.submatch[i].begin() != r.submatch[i].begin() ||
464           correct.submatch[i].size() != r.submatch[i].size())
465         return false;
466       if (!r.have_submatch)
467         break;
468     }
469   }
470   return true;
471 }
472 
473 // Runs a single test.
RunCase(const StringPiece & text,const StringPiece & context,Prog::Anchor anchor)474 bool TestInstance::RunCase(const StringPiece& text, const StringPiece& context,
475                            Prog::Anchor anchor) {
476   // Backtracking is the gold standard.
477   Result correct;
478   RunSearch(kEngineBacktrack, text, context, anchor, &correct);
479   if (correct.skipped) {
480     if (regexp_ == NULL)
481       return true;
482     LOG(ERROR) << "Skipped backtracking! " << CEscape(regexp_str_)
483                << " " << FormatMode(flags_);
484     return false;
485   }
486   VLOG(1) << "Try: regexp " << CEscape(regexp_str_)
487           << " text " << CEscape(text)
488           << " (" << FormatKind(kind_)
489           << ", " << FormatAnchor(anchor)
490           << ", " << FormatMode(flags_)
491           << ")";
492 
493   // Compare the others.
494   bool all_okay = true;
495   for (Engine i = kEngineBacktrack+1; i < kEngineMax; i++) {
496     if (!(Engines() & (1<<i)))
497       continue;
498 
499     Result r;
500     RunSearch(i, text, context, anchor, &r);
501     if (ResultOkay(r, correct)) {
502       if (FLAGS_log_okay)
503         LogMatch(r.skipped ? "Skipped: " : "Okay: ", i, text, context, anchor);
504       continue;
505     }
506 
507     // We disagree with PCRE on the meaning of some Unicode matches.
508     // In particular, we treat all non-ASCII UTF-8 as word characters.
509     // We also treat "empty" character sets like [^\w\W] as being
510     // impossible to match, while PCRE apparently excludes some code
511     // points (e.g., 0x0080) from both \w and \W.
512     if (i == kEnginePCRE && NonASCII(text))
513       continue;
514 
515     if (!r.untrusted)
516       all_okay = false;
517 
518     LogMatch(r.untrusted ? "(Untrusted) Mismatch: " : "Mismatch: ", i, text,
519              context, anchor);
520     if (r.matched != correct.matched) {
521       if (r.matched) {
522         LOG(INFO) << "   Should not match (but does).";
523       } else {
524         LOG(INFO) << "   Should match (but does not).";
525         continue;
526       }
527     }
528     for (int i = 0; i < 1+num_captures_; i++) {
529       if (r.submatch[i].begin() != correct.submatch[i].begin() ||
530           r.submatch[i].end() != correct.submatch[i].end()) {
531         LOG(INFO) <<
532           StringPrintf("   $%d: should be %s is %s",
533                        i,
534                        FormatCapture(text, correct.submatch[i]).c_str(),
535                        FormatCapture(text, r.submatch[i]).c_str());
536       } else {
537         LOG(INFO) <<
538           StringPrintf("   $%d: %s ok", i,
539                        FormatCapture(text, r.submatch[i]).c_str());
540       }
541     }
542   }
543 
544   if (!all_okay) {
545     if (FLAGS_max_regexp_failures > 0 && --FLAGS_max_regexp_failures == 0)
546       LOG(QFATAL) << "Too many regexp failures.";
547   }
548 
549   return all_okay;
550 }
551 
LogMatch(const char * prefix,Engine e,const StringPiece & text,const StringPiece & context,Prog::Anchor anchor)552 void TestInstance::LogMatch(const char* prefix, Engine e,
553                             const StringPiece& text, const StringPiece& context,
554                             Prog::Anchor anchor) {
555   LOG(INFO) << prefix
556     << EngineString(e)
557     << " regexp "
558     << CEscape(regexp_str_)
559     << " "
560     << CEscape(regexp_->ToString())
561     << " text "
562     << CEscape(text)
563     << " ("
564     << text.begin() - context.begin()
565     << ","
566     << text.end() - context.begin()
567     << ") of context "
568     << CEscape(context)
569     << " (" << FormatKind(kind_)
570     << ", " << FormatAnchor(anchor)
571     << ", " << FormatMode(flags_)
572     << ")";
573 }
574 
575 static Prog::MatchKind kinds[] = {
576   Prog::kFirstMatch,
577   Prog::kLongestMatch,
578   Prog::kFullMatch,
579 };
580 
581 // Test all possible match kinds and parse modes.
Tester(const StringPiece & regexp)582 Tester::Tester(const StringPiece& regexp) {
583   error_ = false;
584   for (int i = 0; i < arraysize(kinds); i++) {
585     for (int j = 0; j < arraysize(parse_modes); j++) {
586       TestInstance* t = new TestInstance(regexp, kinds[i],
587                                          parse_modes[j].parse_flags);
588       error_ |= t->error();
589       v_.push_back(t);
590     }
591   }
592 }
593 
~Tester()594 Tester::~Tester() {
595   for (int i = 0; i < v_.size(); i++)
596     delete v_[i];
597 }
598 
TestCase(const StringPiece & text,const StringPiece & context,Prog::Anchor anchor)599 bool Tester::TestCase(const StringPiece& text, const StringPiece& context,
600                          Prog::Anchor anchor) {
601   bool okay = true;
602   for (int i = 0; i < v_.size(); i++)
603     okay &= (!v_[i]->error() && v_[i]->RunCase(text, context, anchor));
604   return okay;
605 }
606 
607 static Prog::Anchor anchors[] = {
608   Prog::kAnchored,
609   Prog::kUnanchored
610 };
611 
TestInput(const StringPiece & text)612 bool Tester::TestInput(const StringPiece& text) {
613   bool okay = TestInputInContext(text, text);
614   if (text.size() > 0) {
615     StringPiece sp;
616     sp = text;
617     sp.remove_prefix(1);
618     okay &= TestInputInContext(sp, text);
619     sp = text;
620     sp.remove_suffix(1);
621     okay &= TestInputInContext(sp, text);
622   }
623   return okay;
624 }
625 
TestInputInContext(const StringPiece & text,const StringPiece & context)626 bool Tester::TestInputInContext(const StringPiece& text,
627                                 const StringPiece& context) {
628   bool okay = true;
629   for (int i = 0; i < arraysize(anchors); i++)
630     okay &= TestCase(text, context, anchors[i]);
631   return okay;
632 }
633 
TestRegexpOnText(const StringPiece & regexp,const StringPiece & text)634 bool TestRegexpOnText(const StringPiece& regexp,
635                       const StringPiece& text) {
636   Tester t(regexp);
637   return t.TestInput(text);
638 }
639 
640 }  // namespace re2
641