Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(71)

Side by Side Diff: runtime/vm/regexp.h

Issue 2974233002: VM: Re-format to use at most one newline between functions (Closed)
Patch Set: Rebase and merge Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « runtime/vm/redundancy_elimination.cc ('k') | runtime/vm/regexp.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #ifndef RUNTIME_VM_REGEXP_H_ 5 #ifndef RUNTIME_VM_REGEXP_H_
6 #define RUNTIME_VM_REGEXP_H_ 6 #define RUNTIME_VM_REGEXP_H_
7 7
8 #include "vm/assembler.h" 8 #include "vm/assembler.h"
9 #include "vm/flow_graph_compiler.h"
9 #include "vm/intermediate_language.h" 10 #include "vm/intermediate_language.h"
10 #include "vm/flow_graph_compiler.h"
11 #include "vm/object.h" 11 #include "vm/object.h"
12 #include "vm/regexp_assembler.h" 12 #include "vm/regexp_assembler.h"
13 13
14 namespace dart { 14 namespace dart {
15 15
16 class NodeVisitor; 16 class NodeVisitor;
17 class RegExpCompiler; 17 class RegExpCompiler;
18 class RegExpMacroAssembler; 18 class RegExpMacroAssembler;
19 class RegExpNode; 19 class RegExpNode;
20 class RegExpTree; 20 class RegExpTree;
21 class BoyerMooreLookahead; 21 class BoyerMooreLookahead;
22 22
23
24 // Represents code units in the range from from_ to to_, both ends are 23 // Represents code units in the range from from_ to to_, both ends are
25 // inclusive. 24 // inclusive.
26 class CharacterRange { 25 class CharacterRange {
27 public: 26 public:
28 CharacterRange() : from_(0), to_(0) {} 27 CharacterRange() : from_(0), to_(0) {}
29 CharacterRange(uint16_t from, uint16_t to) : from_(from), to_(to) {} 28 CharacterRange(uint16_t from, uint16_t to) : from_(from), to_(to) {}
30 29
31 static void AddClassEscape(uint16_t type, 30 static void AddClassEscape(uint16_t type,
32 ZoneGrowableArray<CharacterRange>* ranges); 31 ZoneGrowableArray<CharacterRange>* ranges);
33 static GrowableArray<const intptr_t> GetWordBounds(); 32 static GrowableArray<const intptr_t> GetWordBounds();
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
71 static const intptr_t kStartMarker = (1 << 24); 70 static const intptr_t kStartMarker = (1 << 24);
72 static const intptr_t kPayloadMask = (1 << 24) - 1; 71 static const intptr_t kPayloadMask = (1 << 24) - 1;
73 72
74 private: 73 private:
75 uint16_t from_; 74 uint16_t from_;
76 uint16_t to_; 75 uint16_t to_;
77 76
78 DISALLOW_ALLOCATION(); 77 DISALLOW_ALLOCATION();
79 }; 78 };
80 79
81
82 // A set of unsigned integers that behaves especially well on small 80 // A set of unsigned integers that behaves especially well on small
83 // integers (< 32). May do zone-allocation. 81 // integers (< 32). May do zone-allocation.
84 class OutSet : public ZoneAllocated { 82 class OutSet : public ZoneAllocated {
85 public: 83 public:
86 OutSet() : first_(0), remaining_(NULL), successors_(NULL) {} 84 OutSet() : first_(0), remaining_(NULL), successors_(NULL) {}
87 OutSet* Extend(unsigned value, Zone* zone); 85 OutSet* Extend(unsigned value, Zone* zone);
88 bool Get(unsigned value) const; 86 bool Get(unsigned value) const;
89 static const unsigned kFirstLimit = 32; 87 static const unsigned kFirstLimit = 32;
90 88
91 private: 89 private:
92 // Destructively set a value in this set. In most cases you want 90 // Destructively set a value in this set. In most cases you want
93 // to use Extend instead to ensure that only one instance exists 91 // to use Extend instead to ensure that only one instance exists
94 // that contains the same values. 92 // that contains the same values.
95 void Set(unsigned value, Zone* zone); 93 void Set(unsigned value, Zone* zone);
96 94
97 // The successors are a list of sets that contain the same values 95 // The successors are a list of sets that contain the same values
98 // as this set and the one more value that is not present in this 96 // as this set and the one more value that is not present in this
99 // set. 97 // set.
100 ZoneGrowableArray<OutSet*>* successors() { return successors_; } 98 ZoneGrowableArray<OutSet*>* successors() { return successors_; }
101 99
102 OutSet(uint32_t first, ZoneGrowableArray<unsigned>* remaining) 100 OutSet(uint32_t first, ZoneGrowableArray<unsigned>* remaining)
103 : first_(first), remaining_(remaining), successors_(NULL) {} 101 : first_(first), remaining_(remaining), successors_(NULL) {}
104 uint32_t first_; 102 uint32_t first_;
105 ZoneGrowableArray<unsigned>* remaining_; 103 ZoneGrowableArray<unsigned>* remaining_;
106 ZoneGrowableArray<OutSet*>* successors_; 104 ZoneGrowableArray<OutSet*>* successors_;
107 friend class Trace; 105 friend class Trace;
108 }; 106 };
109 107
110
111 #define FOR_EACH_NODE_TYPE(VISIT) \ 108 #define FOR_EACH_NODE_TYPE(VISIT) \
112 VISIT(End) \ 109 VISIT(End) \
113 VISIT(Action) \ 110 VISIT(Action) \
114 VISIT(Choice) \ 111 VISIT(Choice) \
115 VISIT(BackReference) \ 112 VISIT(BackReference) \
116 VISIT(Assertion) \ 113 VISIT(Assertion) \
117 VISIT(Text) 114 VISIT(Text)
118 115
119
120 #define FOR_EACH_REG_EXP_TREE_TYPE(VISIT) \ 116 #define FOR_EACH_REG_EXP_TREE_TYPE(VISIT) \
121 VISIT(Disjunction) \ 117 VISIT(Disjunction) \
122 VISIT(Alternative) \ 118 VISIT(Alternative) \
123 VISIT(Assertion) \ 119 VISIT(Assertion) \
124 VISIT(CharacterClass) \ 120 VISIT(CharacterClass) \
125 VISIT(Atom) \ 121 VISIT(Atom) \
126 VISIT(Quantifier) \ 122 VISIT(Quantifier) \
127 VISIT(Capture) \ 123 VISIT(Capture) \
128 VISIT(Lookahead) \ 124 VISIT(Lookahead) \
129 VISIT(BackReference) \ 125 VISIT(BackReference) \
130 VISIT(Empty) \ 126 VISIT(Empty) \
131 VISIT(Text) 127 VISIT(Text)
132 128
133
134 #define FORWARD_DECLARE(Name) class RegExp##Name; 129 #define FORWARD_DECLARE(Name) class RegExp##Name;
135 FOR_EACH_REG_EXP_TREE_TYPE(FORWARD_DECLARE) 130 FOR_EACH_REG_EXP_TREE_TYPE(FORWARD_DECLARE)
136 #undef FORWARD_DECLARE 131 #undef FORWARD_DECLARE
137 132
138
139 class TextElement { 133 class TextElement {
140 public: 134 public:
141 enum TextType { ATOM, CHAR_CLASS }; 135 enum TextType { ATOM, CHAR_CLASS };
142 136
143 static TextElement Atom(RegExpAtom* atom); 137 static TextElement Atom(RegExpAtom* atom);
144 static TextElement CharClass(RegExpCharacterClass* char_class); 138 static TextElement CharClass(RegExpCharacterClass* char_class);
145 139
146 intptr_t cp_offset() const { return cp_offset_; } 140 intptr_t cp_offset() const { return cp_offset_; }
147 void set_cp_offset(intptr_t cp_offset) { cp_offset_ = cp_offset; } 141 void set_cp_offset(intptr_t cp_offset) { cp_offset_ = cp_offset; }
148 intptr_t length() const; 142 intptr_t length() const;
(...skipping 16 matching lines...) Expand all
165 TextElement(TextType text_type, RegExpTree* tree) 159 TextElement(TextType text_type, RegExpTree* tree)
166 : cp_offset_(-1), text_type_(text_type), tree_(tree) {} 160 : cp_offset_(-1), text_type_(text_type), tree_(tree) {}
167 161
168 intptr_t cp_offset_; 162 intptr_t cp_offset_;
169 TextType text_type_; 163 TextType text_type_;
170 RegExpTree* tree_; 164 RegExpTree* tree_;
171 165
172 DISALLOW_ALLOCATION(); 166 DISALLOW_ALLOCATION();
173 }; 167 };
174 168
175
176 class Trace; 169 class Trace;
177 struct PreloadState; 170 struct PreloadState;
178 class GreedyLoopState; 171 class GreedyLoopState;
179 class AlternativeGenerationList; 172 class AlternativeGenerationList;
180 173
181 struct NodeInfo { 174 struct NodeInfo {
182 NodeInfo() 175 NodeInfo()
183 : being_analyzed(false), 176 : being_analyzed(false),
184 been_analyzed(false), 177 been_analyzed(false),
185 follows_word_interest(false), 178 follows_word_interest(false),
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
232 // character was. 225 // character was.
233 bool follows_word_interest : 1; 226 bool follows_word_interest : 1;
234 bool follows_newline_interest : 1; 227 bool follows_newline_interest : 1;
235 bool follows_start_interest : 1; 228 bool follows_start_interest : 1;
236 229
237 bool at_end : 1; 230 bool at_end : 1;
238 bool visited : 1; 231 bool visited : 1;
239 bool replacement_calculated : 1; 232 bool replacement_calculated : 1;
240 }; 233 };
241 234
242
243 // Details of a quick mask-compare check that can look ahead in the 235 // Details of a quick mask-compare check that can look ahead in the
244 // input stream. 236 // input stream.
245 class QuickCheckDetails { 237 class QuickCheckDetails {
246 public: 238 public:
247 QuickCheckDetails() 239 QuickCheckDetails()
248 : characters_(0), mask_(0), value_(0), cannot_match_(false) {} 240 : characters_(0), mask_(0), value_(0), cannot_match_(false) {}
249 explicit QuickCheckDetails(intptr_t characters) 241 explicit QuickCheckDetails(intptr_t characters)
250 : characters_(characters), mask_(0), value_(0), cannot_match_(false) {} 242 : characters_(characters), mask_(0), value_(0), cannot_match_(false) {}
251 bool Rationalize(bool one_byte); 243 bool Rationalize(bool one_byte);
252 // Merge in the information from another branch of an alternation. 244 // Merge in the information from another branch of an alternation.
(...skipping 27 matching lines...) Expand all
280 // These values are the condensate of the above array after Rationalize(). 272 // These values are the condensate of the above array after Rationalize().
281 uint32_t mask_; 273 uint32_t mask_;
282 uint32_t value_; 274 uint32_t value_;
283 // If set to true, there is no way this quick check can match at all. 275 // If set to true, there is no way this quick check can match at all.
284 // E.g., if it requires to be at the start of the input, and isn't. 276 // E.g., if it requires to be at the start of the input, and isn't.
285 bool cannot_match_; 277 bool cannot_match_;
286 278
287 DISALLOW_ALLOCATION(); 279 DISALLOW_ALLOCATION();
288 }; 280 };
289 281
290
291 class RegExpNode : public ZoneAllocated { 282 class RegExpNode : public ZoneAllocated {
292 public: 283 public:
293 explicit RegExpNode(Zone* zone) 284 explicit RegExpNode(Zone* zone)
294 : replacement_(NULL), trace_count_(0), zone_(zone) { 285 : replacement_(NULL), trace_count_(0), zone_(zone) {
295 bm_info_[0] = bm_info_[1] = NULL; 286 bm_info_[0] = bm_info_[1] = NULL;
296 } 287 }
297 virtual ~RegExpNode(); 288 virtual ~RegExpNode();
298 virtual void Accept(NodeVisitor* visitor) = 0; 289 virtual void Accept(NodeVisitor* visitor) = 0;
299 // Generates a goto to this node or actually generates the code at this point. 290 // Generates a goto to this node or actually generates the code at this point.
300 virtual void Emit(RegExpCompiler* compiler, Trace* trace) = 0; 291 virtual void Emit(RegExpCompiler* compiler, Trace* trace) = 0;
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
408 // This variable keeps track of how many times code has been generated for 399 // This variable keeps track of how many times code has been generated for
409 // this node (in different traces). We don't keep track of where the 400 // this node (in different traces). We don't keep track of where the
410 // generated code is located unless the code is generated at the start of 401 // generated code is located unless the code is generated at the start of
411 // a trace, in which case it is generic and can be reused by flushing the 402 // a trace, in which case it is generic and can be reused by flushing the
412 // deferred operations in the current trace and generating a goto. 403 // deferred operations in the current trace and generating a goto.
413 intptr_t trace_count_; 404 intptr_t trace_count_;
414 BoyerMooreLookahead* bm_info_[2]; 405 BoyerMooreLookahead* bm_info_[2];
415 Zone* zone_; 406 Zone* zone_;
416 }; 407 };
417 408
418
419 // A simple closed interval. 409 // A simple closed interval.
420 class Interval { 410 class Interval {
421 public: 411 public:
422 Interval() : from_(kNone), to_(kNone) {} 412 Interval() : from_(kNone), to_(kNone) {}
423 Interval(intptr_t from, intptr_t to) : from_(from), to_(to) {} 413 Interval(intptr_t from, intptr_t to) : from_(from), to_(to) {}
424 414
425 Interval Union(Interval that) { 415 Interval Union(Interval that) {
426 if (that.from_ == kNone) 416 if (that.from_ == kNone)
427 return *this; 417 return *this;
428 else if (from_ == kNone) 418 else if (from_ == kNone)
(...skipping 11 matching lines...) Expand all
440 static Interval Empty() { return Interval(); } 430 static Interval Empty() { return Interval(); }
441 static const intptr_t kNone = -1; 431 static const intptr_t kNone = -1;
442 432
443 private: 433 private:
444 intptr_t from_; 434 intptr_t from_;
445 intptr_t to_; 435 intptr_t to_;
446 436
447 DISALLOW_ALLOCATION(); 437 DISALLOW_ALLOCATION();
448 }; 438 };
449 439
450
451 class SeqRegExpNode : public RegExpNode { 440 class SeqRegExpNode : public RegExpNode {
452 public: 441 public:
453 explicit SeqRegExpNode(RegExpNode* on_success) 442 explicit SeqRegExpNode(RegExpNode* on_success)
454 : RegExpNode(on_success->zone()), on_success_(on_success) {} 443 : RegExpNode(on_success->zone()), on_success_(on_success) {}
455 RegExpNode* on_success() { return on_success_; } 444 RegExpNode* on_success() { return on_success_; }
456 void set_on_success(RegExpNode* node) { on_success_ = node; } 445 void set_on_success(RegExpNode* node) { on_success_ = node; }
457 virtual RegExpNode* FilterOneByte(intptr_t depth, bool ignore_case); 446 virtual RegExpNode* FilterOneByte(intptr_t depth, bool ignore_case);
458 virtual void FillInBMInfo(intptr_t offset, 447 virtual void FillInBMInfo(intptr_t offset,
459 intptr_t budget, 448 intptr_t budget,
460 BoyerMooreLookahead* bm, 449 BoyerMooreLookahead* bm,
461 bool not_at_start) { 450 bool not_at_start) {
462 on_success_->FillInBMInfo(offset, budget - 1, bm, not_at_start); 451 on_success_->FillInBMInfo(offset, budget - 1, bm, not_at_start);
463 if (offset == 0) set_bm_info(not_at_start, bm); 452 if (offset == 0) set_bm_info(not_at_start, bm);
464 } 453 }
465 454
466 protected: 455 protected:
467 RegExpNode* FilterSuccessor(intptr_t depth, bool ignore_case); 456 RegExpNode* FilterSuccessor(intptr_t depth, bool ignore_case);
468 457
469 private: 458 private:
470 RegExpNode* on_success_; 459 RegExpNode* on_success_;
471 }; 460 };
472 461
473
474 class ActionNode : public SeqRegExpNode { 462 class ActionNode : public SeqRegExpNode {
475 public: 463 public:
476 enum ActionType { 464 enum ActionType {
477 SET_REGISTER, 465 SET_REGISTER,
478 INCREMENT_REGISTER, 466 INCREMENT_REGISTER,
479 STORE_POSITION, 467 STORE_POSITION,
480 BEGIN_SUBMATCH, 468 BEGIN_SUBMATCH,
481 POSITIVE_SUBMATCH_SUCCESS, 469 POSITIVE_SUBMATCH_SUCCESS,
482 EMPTY_MATCH_CHECK, 470 EMPTY_MATCH_CHECK,
483 CLEAR_CAPTURES 471 CLEAR_CAPTURES
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
552 intptr_t range_from; 540 intptr_t range_from;
553 intptr_t range_to; 541 intptr_t range_to;
554 } u_clear_captures; 542 } u_clear_captures;
555 } data_; 543 } data_;
556 ActionNode(ActionType action_type, RegExpNode* on_success) 544 ActionNode(ActionType action_type, RegExpNode* on_success)
557 : SeqRegExpNode(on_success), action_type_(action_type) {} 545 : SeqRegExpNode(on_success), action_type_(action_type) {}
558 ActionType action_type_; 546 ActionType action_type_;
559 friend class DotPrinter; 547 friend class DotPrinter;
560 }; 548 };
561 549
562
563 class TextNode : public SeqRegExpNode { 550 class TextNode : public SeqRegExpNode {
564 public: 551 public:
565 TextNode(ZoneGrowableArray<TextElement>* elms, RegExpNode* on_success) 552 TextNode(ZoneGrowableArray<TextElement>* elms, RegExpNode* on_success)
566 : SeqRegExpNode(on_success), elms_(elms) {} 553 : SeqRegExpNode(on_success), elms_(elms) {}
567 TextNode(RegExpCharacterClass* that, RegExpNode* on_success) 554 TextNode(RegExpCharacterClass* that, RegExpNode* on_success)
568 : SeqRegExpNode(on_success), 555 : SeqRegExpNode(on_success),
569 elms_(new (zone()) ZoneGrowableArray<TextElement>(1)) { 556 elms_(new (zone()) ZoneGrowableArray<TextElement>(1)) {
570 elms_->Add(TextElement::CharClass(that)); 557 elms_->Add(TextElement::CharClass(that));
571 } 558 }
572 virtual void Accept(NodeVisitor* visitor); 559 virtual void Accept(NodeVisitor* visitor);
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
604 void TextEmitPass(RegExpCompiler* compiler, 591 void TextEmitPass(RegExpCompiler* compiler,
605 TextEmitPassType pass, 592 TextEmitPassType pass,
606 bool preloaded, 593 bool preloaded,
607 Trace* trace, 594 Trace* trace,
608 bool first_element_checked, 595 bool first_element_checked,
609 intptr_t* checked_up_to); 596 intptr_t* checked_up_to);
610 intptr_t Length(); 597 intptr_t Length();
611 ZoneGrowableArray<TextElement>* elms_; 598 ZoneGrowableArray<TextElement>* elms_;
612 }; 599 };
613 600
614
615 class AssertionNode : public SeqRegExpNode { 601 class AssertionNode : public SeqRegExpNode {
616 public: 602 public:
617 enum AssertionType { 603 enum AssertionType {
618 AT_END, 604 AT_END,
619 AT_START, 605 AT_START,
620 AT_BOUNDARY, 606 AT_BOUNDARY,
621 AT_NON_BOUNDARY, 607 AT_NON_BOUNDARY,
622 AFTER_NEWLINE 608 AFTER_NEWLINE
623 }; 609 };
624 static AssertionNode* AtEnd(RegExpNode* on_success) { 610 static AssertionNode* AtEnd(RegExpNode* on_success) {
(...skipping 30 matching lines...) Expand all
655 void EmitBoundaryCheck(RegExpCompiler* compiler, Trace* trace); 641 void EmitBoundaryCheck(RegExpCompiler* compiler, Trace* trace);
656 enum IfPrevious { kIsNonWord, kIsWord }; 642 enum IfPrevious { kIsNonWord, kIsWord };
657 void BacktrackIfPrevious(RegExpCompiler* compiler, 643 void BacktrackIfPrevious(RegExpCompiler* compiler,
658 Trace* trace, 644 Trace* trace,
659 IfPrevious backtrack_if_previous); 645 IfPrevious backtrack_if_previous);
660 AssertionNode(AssertionType t, RegExpNode* on_success) 646 AssertionNode(AssertionType t, RegExpNode* on_success)
661 : SeqRegExpNode(on_success), assertion_type_(t) {} 647 : SeqRegExpNode(on_success), assertion_type_(t) {}
662 AssertionType assertion_type_; 648 AssertionType assertion_type_;
663 }; 649 };
664 650
665
666 class BackReferenceNode : public SeqRegExpNode { 651 class BackReferenceNode : public SeqRegExpNode {
667 public: 652 public:
668 BackReferenceNode(intptr_t start_reg, 653 BackReferenceNode(intptr_t start_reg,
669 intptr_t end_reg, 654 intptr_t end_reg,
670 RegExpNode* on_success) 655 RegExpNode* on_success)
671 : SeqRegExpNode(on_success), start_reg_(start_reg), end_reg_(end_reg) {} 656 : SeqRegExpNode(on_success), start_reg_(start_reg), end_reg_(end_reg) {}
672 virtual void Accept(NodeVisitor* visitor); 657 virtual void Accept(NodeVisitor* visitor);
673 intptr_t start_register() { return start_reg_; } 658 intptr_t start_register() { return start_reg_; }
674 intptr_t end_register() { return end_reg_; } 659 intptr_t end_register() { return end_reg_; }
675 virtual void Emit(RegExpCompiler* compiler, Trace* trace); 660 virtual void Emit(RegExpCompiler* compiler, Trace* trace);
676 virtual intptr_t EatsAtLeast(intptr_t still_to_find, 661 virtual intptr_t EatsAtLeast(intptr_t still_to_find,
677 intptr_t recursion_depth, 662 intptr_t recursion_depth,
678 bool not_at_start); 663 bool not_at_start);
679 virtual void GetQuickCheckDetails(QuickCheckDetails* details, 664 virtual void GetQuickCheckDetails(QuickCheckDetails* details,
680 RegExpCompiler* compiler, 665 RegExpCompiler* compiler,
681 intptr_t characters_filled_in, 666 intptr_t characters_filled_in,
682 bool not_at_start) { 667 bool not_at_start) {
683 return; 668 return;
684 } 669 }
685 virtual void FillInBMInfo(intptr_t offset, 670 virtual void FillInBMInfo(intptr_t offset,
686 intptr_t budget, 671 intptr_t budget,
687 BoyerMooreLookahead* bm, 672 BoyerMooreLookahead* bm,
688 bool not_at_start); 673 bool not_at_start);
689 674
690 private: 675 private:
691 intptr_t start_reg_; 676 intptr_t start_reg_;
692 intptr_t end_reg_; 677 intptr_t end_reg_;
693 }; 678 };
694 679
695
696 class EndNode : public RegExpNode { 680 class EndNode : public RegExpNode {
697 public: 681 public:
698 enum Action { ACCEPT, BACKTRACK, NEGATIVE_SUBMATCH_SUCCESS }; 682 enum Action { ACCEPT, BACKTRACK, NEGATIVE_SUBMATCH_SUCCESS };
699 explicit EndNode(Action action, Zone* zone) 683 explicit EndNode(Action action, Zone* zone)
700 : RegExpNode(zone), action_(action) {} 684 : RegExpNode(zone), action_(action) {}
701 virtual void Accept(NodeVisitor* visitor); 685 virtual void Accept(NodeVisitor* visitor);
702 virtual void Emit(RegExpCompiler* compiler, Trace* trace); 686 virtual void Emit(RegExpCompiler* compiler, Trace* trace);
703 virtual intptr_t EatsAtLeast(intptr_t still_to_find, 687 virtual intptr_t EatsAtLeast(intptr_t still_to_find,
704 intptr_t recursion_depth, 688 intptr_t recursion_depth,
705 bool not_at_start) { 689 bool not_at_start) {
(...skipping 11 matching lines...) Expand all
717 BoyerMooreLookahead* bm, 701 BoyerMooreLookahead* bm,
718 bool not_at_start) { 702 bool not_at_start) {
719 // Returning 0 from EatsAtLeast should ensure we never get here. 703 // Returning 0 from EatsAtLeast should ensure we never get here.
720 UNREACHABLE(); 704 UNREACHABLE();
721 } 705 }
722 706
723 private: 707 private:
724 Action action_; 708 Action action_;
725 }; 709 };
726 710
727
728 class NegativeSubmatchSuccess : public EndNode { 711 class NegativeSubmatchSuccess : public EndNode {
729 public: 712 public:
730 NegativeSubmatchSuccess(intptr_t stack_pointer_reg, 713 NegativeSubmatchSuccess(intptr_t stack_pointer_reg,
731 intptr_t position_reg, 714 intptr_t position_reg,
732 intptr_t clear_capture_count, 715 intptr_t clear_capture_count,
733 intptr_t clear_capture_start, 716 intptr_t clear_capture_start,
734 Zone* zone) 717 Zone* zone)
735 : EndNode(NEGATIVE_SUBMATCH_SUCCESS, zone), 718 : EndNode(NEGATIVE_SUBMATCH_SUCCESS, zone),
736 stack_pointer_register_(stack_pointer_reg), 719 stack_pointer_register_(stack_pointer_reg),
737 current_position_register_(position_reg), 720 current_position_register_(position_reg),
738 clear_capture_count_(clear_capture_count), 721 clear_capture_count_(clear_capture_count),
739 clear_capture_start_(clear_capture_start) {} 722 clear_capture_start_(clear_capture_start) {}
740 virtual void Emit(RegExpCompiler* compiler, Trace* trace); 723 virtual void Emit(RegExpCompiler* compiler, Trace* trace);
741 724
742 private: 725 private:
743 intptr_t stack_pointer_register_; 726 intptr_t stack_pointer_register_;
744 intptr_t current_position_register_; 727 intptr_t current_position_register_;
745 intptr_t clear_capture_count_; 728 intptr_t clear_capture_count_;
746 intptr_t clear_capture_start_; 729 intptr_t clear_capture_start_;
747 }; 730 };
748 731
749
750 class Guard : public ZoneAllocated { 732 class Guard : public ZoneAllocated {
751 public: 733 public:
752 enum Relation { LT, GEQ }; 734 enum Relation { LT, GEQ };
753 Guard(intptr_t reg, Relation op, intptr_t value) 735 Guard(intptr_t reg, Relation op, intptr_t value)
754 : reg_(reg), op_(op), value_(value) {} 736 : reg_(reg), op_(op), value_(value) {}
755 intptr_t reg() { return reg_; } 737 intptr_t reg() { return reg_; }
756 Relation op() { return op_; } 738 Relation op() { return op_; }
757 intptr_t value() { return value_; } 739 intptr_t value() { return value_; }
758 740
759 private: 741 private:
760 intptr_t reg_; 742 intptr_t reg_;
761 Relation op_; 743 Relation op_;
762 intptr_t value_; 744 intptr_t value_;
763 }; 745 };
764 746
765
766 class GuardedAlternative { 747 class GuardedAlternative {
767 public: 748 public:
768 explicit GuardedAlternative(RegExpNode* node) : node_(node), guards_(NULL) {} 749 explicit GuardedAlternative(RegExpNode* node) : node_(node), guards_(NULL) {}
769 void AddGuard(Guard* guard, Zone* zone); 750 void AddGuard(Guard* guard, Zone* zone);
770 RegExpNode* node() { return node_; } 751 RegExpNode* node() { return node_; }
771 void set_node(RegExpNode* node) { node_ = node; } 752 void set_node(RegExpNode* node) { node_ = node; }
772 ZoneGrowableArray<Guard*>* guards() { return guards_; } 753 ZoneGrowableArray<Guard*>* guards() { return guards_; }
773 754
774 private: 755 private:
775 RegExpNode* node_; 756 RegExpNode* node_;
776 ZoneGrowableArray<Guard*>* guards_; 757 ZoneGrowableArray<Guard*>* guards_;
777 758
778 DISALLOW_ALLOCATION(); 759 DISALLOW_ALLOCATION();
779 }; 760 };
780 761
781
782 struct AlternativeGeneration; 762 struct AlternativeGeneration;
783 763
784
785 class ChoiceNode : public RegExpNode { 764 class ChoiceNode : public RegExpNode {
786 public: 765 public:
787 explicit ChoiceNode(intptr_t expected_size, Zone* zone) 766 explicit ChoiceNode(intptr_t expected_size, Zone* zone)
788 : RegExpNode(zone), 767 : RegExpNode(zone),
789 alternatives_(new (zone) 768 alternatives_(new (zone)
790 ZoneGrowableArray<GuardedAlternative>(expected_size)), 769 ZoneGrowableArray<GuardedAlternative>(expected_size)),
791 not_at_start_(false), 770 not_at_start_(false),
792 being_calculated_(false) {} 771 being_calculated_(false) {}
793 virtual void Accept(NodeVisitor* visitor); 772 virtual void Accept(NodeVisitor* visitor);
794 void AddAlternative(GuardedAlternative node) { alternatives()->Add(node); } 773 void AddAlternative(GuardedAlternative node) { alternatives()->Add(node); }
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
854 AlternativeGenerationList* alt_gens, 833 AlternativeGenerationList* alt_gens,
855 intptr_t first_choice, 834 intptr_t first_choice,
856 Trace* trace, 835 Trace* trace,
857 PreloadState* preloads); 836 PreloadState* preloads);
858 // If true, this node is never checked at the start of the input. 837 // If true, this node is never checked at the start of the input.
859 // Allows a new trace to start with at_start() set to false. 838 // Allows a new trace to start with at_start() set to false.
860 bool not_at_start_; 839 bool not_at_start_;
861 bool being_calculated_; 840 bool being_calculated_;
862 }; 841 };
863 842
864
865 class NegativeLookaheadChoiceNode : public ChoiceNode { 843 class NegativeLookaheadChoiceNode : public ChoiceNode {
866 public: 844 public:
867 explicit NegativeLookaheadChoiceNode(GuardedAlternative this_must_fail, 845 explicit NegativeLookaheadChoiceNode(GuardedAlternative this_must_fail,
868 GuardedAlternative then_do_this, 846 GuardedAlternative then_do_this,
869 Zone* zone) 847 Zone* zone)
870 : ChoiceNode(2, zone) { 848 : ChoiceNode(2, zone) {
871 AddAlternative(this_must_fail); 849 AddAlternative(this_must_fail);
872 AddAlternative(then_do_this); 850 AddAlternative(then_do_this);
873 } 851 }
874 virtual intptr_t EatsAtLeast(intptr_t still_to_find, 852 virtual intptr_t EatsAtLeast(intptr_t still_to_find,
(...skipping 15 matching lines...) Expand all
890 // alternative that is expected to fail. This is because quick check code 868 // alternative that is expected to fail. This is because quick check code
891 // starts by loading enough characters for the alternative that takes fewest 869 // starts by loading enough characters for the alternative that takes fewest
892 // characters, but on a negative lookahead the negative branch did not take 870 // characters, but on a negative lookahead the negative branch did not take
893 // part in that calculation (EatsAtLeast) so the assumptions don't hold. 871 // part in that calculation (EatsAtLeast) so the assumptions don't hold.
894 virtual bool try_to_emit_quick_check_for_alternative(bool is_first) { 872 virtual bool try_to_emit_quick_check_for_alternative(bool is_first) {
895 return !is_first; 873 return !is_first;
896 } 874 }
897 virtual RegExpNode* FilterOneByte(intptr_t depth, bool ignore_case); 875 virtual RegExpNode* FilterOneByte(intptr_t depth, bool ignore_case);
898 }; 876 };
899 877
900
901 class LoopChoiceNode : public ChoiceNode { 878 class LoopChoiceNode : public ChoiceNode {
902 public: 879 public:
903 explicit LoopChoiceNode(bool body_can_be_zero_length, Zone* zone) 880 explicit LoopChoiceNode(bool body_can_be_zero_length, Zone* zone)
904 : ChoiceNode(2, zone), 881 : ChoiceNode(2, zone),
905 loop_node_(NULL), 882 loop_node_(NULL),
906 continue_node_(NULL), 883 continue_node_(NULL),
907 body_can_be_zero_length_(body_can_be_zero_length) {} 884 body_can_be_zero_length_(body_can_be_zero_length) {}
908 void AddLoopAlternative(GuardedAlternative alt); 885 void AddLoopAlternative(GuardedAlternative alt);
909 void AddContinueAlternative(GuardedAlternative alt); 886 void AddContinueAlternative(GuardedAlternative alt);
910 virtual void Emit(RegExpCompiler* compiler, Trace* trace); 887 virtual void Emit(RegExpCompiler* compiler, Trace* trace);
(...skipping 20 matching lines...) Expand all
931 // goes back to the node itself. 908 // goes back to the node itself.
932 void AddAlternative(GuardedAlternative node) { 909 void AddAlternative(GuardedAlternative node) {
933 ChoiceNode::AddAlternative(node); 910 ChoiceNode::AddAlternative(node);
934 } 911 }
935 912
936 RegExpNode* loop_node_; 913 RegExpNode* loop_node_;
937 RegExpNode* continue_node_; 914 RegExpNode* continue_node_;
938 bool body_can_be_zero_length_; 915 bool body_can_be_zero_length_;
939 }; 916 };
940 917
941
942 // Improve the speed that we scan for an initial point where a non-anchored 918 // Improve the speed that we scan for an initial point where a non-anchored
943 // regexp can match by using a Boyer-Moore-like table. This is done by 919 // regexp can match by using a Boyer-Moore-like table. This is done by
944 // identifying non-greedy non-capturing loops in the nodes that eat any 920 // identifying non-greedy non-capturing loops in the nodes that eat any
945 // character one at a time. For example in the middle of the regexp 921 // character one at a time. For example in the middle of the regexp
946 // /foo[\s\S]*?bar/ we find such a loop. There is also such a loop implicitly 922 // /foo[\s\S]*?bar/ we find such a loop. There is also such a loop implicitly
947 // inserted at the start of any non-anchored regexp. 923 // inserted at the start of any non-anchored regexp.
948 // 924 //
949 // When we have found such a loop we look ahead in the nodes to find the set of 925 // When we have found such a loop we look ahead in the nodes to find the set of
950 // characters that can come at given distances. For example for the regexp 926 // characters that can come at given distances. For example for the regexp
951 // /.?foo/ we know that there are at least 3 characters ahead of us, and the 927 // /.?foo/ we know that there are at least 3 characters ahead of us, and the
(...skipping 12 matching lines...) Expand all
964 // We also have another lookahead mechanism (called quick check in the code), 940 // We also have another lookahead mechanism (called quick check in the code),
965 // which uses a wide load of multiple characters followed by a mask and compare 941 // which uses a wide load of multiple characters followed by a mask and compare
966 // to determine whether a match is possible at this point. 942 // to determine whether a match is possible at this point.
967 enum ContainedInLattice { 943 enum ContainedInLattice {
968 kNotYet = 0, 944 kNotYet = 0,
969 kLatticeIn = 1, 945 kLatticeIn = 1,
970 kLatticeOut = 2, 946 kLatticeOut = 2,
971 kLatticeUnknown = 3 // Can also mean both in and out. 947 kLatticeUnknown = 3 // Can also mean both in and out.
972 }; 948 };
973 949
974
975 inline ContainedInLattice Combine(ContainedInLattice a, ContainedInLattice b) { 950 inline ContainedInLattice Combine(ContainedInLattice a, ContainedInLattice b) {
976 return static_cast<ContainedInLattice>(a | b); 951 return static_cast<ContainedInLattice>(a | b);
977 } 952 }
978 953
979
980 ContainedInLattice AddRange(ContainedInLattice a, 954 ContainedInLattice AddRange(ContainedInLattice a,
981 const intptr_t* ranges, 955 const intptr_t* ranges,
982 intptr_t ranges_size, 956 intptr_t ranges_size,
983 Interval new_range); 957 Interval new_range);
984 958
985
986 class BoyerMoorePositionInfo : public ZoneAllocated { 959 class BoyerMoorePositionInfo : public ZoneAllocated {
987 public: 960 public:
988 explicit BoyerMoorePositionInfo(Zone* zone) 961 explicit BoyerMoorePositionInfo(Zone* zone)
989 : map_(new (zone) ZoneGrowableArray<bool>(kMapSize)), 962 : map_(new (zone) ZoneGrowableArray<bool>(kMapSize)),
990 map_count_(0), 963 map_count_(0),
991 w_(kNotYet), 964 w_(kNotYet),
992 s_(kNotYet), 965 s_(kNotYet),
993 d_(kNotYet), 966 d_(kNotYet),
994 surrogate_(kNotYet) { 967 surrogate_(kNotYet) {
995 for (intptr_t i = 0; i < kMapSize; i++) { 968 for (intptr_t i = 0; i < kMapSize; i++) {
(...skipping 16 matching lines...) Expand all
1012 985
1013 private: 986 private:
1014 ZoneGrowableArray<bool>* map_; 987 ZoneGrowableArray<bool>* map_;
1015 intptr_t map_count_; // Number of set bits in the map. 988 intptr_t map_count_; // Number of set bits in the map.
1016 ContainedInLattice w_; // The \w character class. 989 ContainedInLattice w_; // The \w character class.
1017 ContainedInLattice s_; // The \s character class. 990 ContainedInLattice s_; // The \s character class.
1018 ContainedInLattice d_; // The \d character class. 991 ContainedInLattice d_; // The \d character class.
1019 ContainedInLattice surrogate_; // Surrogate UTF-16 code units. 992 ContainedInLattice surrogate_; // Surrogate UTF-16 code units.
1020 }; 993 };
1021 994
1022
1023 class BoyerMooreLookahead : public ZoneAllocated { 995 class BoyerMooreLookahead : public ZoneAllocated {
1024 public: 996 public:
1025 BoyerMooreLookahead(intptr_t length, RegExpCompiler* compiler, Zone* Zone); 997 BoyerMooreLookahead(intptr_t length, RegExpCompiler* compiler, Zone* Zone);
1026 998
1027 intptr_t length() { return length_; } 999 intptr_t length() { return length_; }
1028 intptr_t max_char() { return max_char_; } 1000 intptr_t max_char() { return max_char_; }
1029 RegExpCompiler* compiler() { return compiler_; } 1001 RegExpCompiler* compiler() { return compiler_; }
1030 1002
1031 intptr_t Count(intptr_t map_number) { 1003 intptr_t Count(intptr_t map_number) {
1032 return bitmaps_->At(map_number)->map_count(); 1004 return bitmaps_->At(map_number)->map_count();
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
1072 intptr_t GetSkipTable(intptr_t min_lookahead, 1044 intptr_t GetSkipTable(intptr_t min_lookahead,
1073 intptr_t max_lookahead, 1045 intptr_t max_lookahead,
1074 const TypedData& boolean_skip_table); 1046 const TypedData& boolean_skip_table);
1075 bool FindWorthwhileInterval(intptr_t* from, intptr_t* to); 1047 bool FindWorthwhileInterval(intptr_t* from, intptr_t* to);
1076 intptr_t FindBestInterval(intptr_t max_number_of_chars, 1048 intptr_t FindBestInterval(intptr_t max_number_of_chars,
1077 intptr_t old_biggest_points, 1049 intptr_t old_biggest_points,
1078 intptr_t* from, 1050 intptr_t* from,
1079 intptr_t* to); 1051 intptr_t* to);
1080 }; 1052 };
1081 1053
1082
1083 // There are many ways to generate code for a node. This class encapsulates 1054 // There are many ways to generate code for a node. This class encapsulates
1084 // the current way we should be generating. In other words it encapsulates 1055 // the current way we should be generating. In other words it encapsulates
1085 // the current state of the code generator. The effect of this is that we 1056 // the current state of the code generator. The effect of this is that we
1086 // generate code for paths that the matcher can take through the regular 1057 // generate code for paths that the matcher can take through the regular
1087 // expression. A given node in the regexp can be code-generated several times 1058 // expression. A given node in the regexp can be code-generated several times
1088 // as it can be part of several traces. For example for the regexp: 1059 // as it can be part of several traces. For example for the regexp:
1089 // /foo(bar|ip)baz/ the code to match baz will be generated twice, once as part 1060 // /foo(bar|ip)baz/ the code to match baz will be generated twice, once as part
1090 // of the foo-bar-baz trace and once as part of the foo-ip-baz trace. The code 1061 // of the foo-bar-baz trace and once as part of the foo-ip-baz trace. The code
1091 // to match foo is generated only once (the traces have a common prefix). The 1062 // to match foo is generated only once (the traces have a common prefix). The
1092 // code to store the capture is deferred and generated (twice) after the places 1063 // code to store the capture is deferred and generated (twice) after the places
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
1245 BlockLabel* loop_label_; 1216 BlockLabel* loop_label_;
1246 intptr_t characters_preloaded_; 1217 intptr_t characters_preloaded_;
1247 intptr_t bound_checked_up_to_; 1218 intptr_t bound_checked_up_to_;
1248 QuickCheckDetails quick_check_performed_; 1219 QuickCheckDetails quick_check_performed_;
1249 intptr_t flush_budget_; 1220 intptr_t flush_budget_;
1250 TriBool at_start_; 1221 TriBool at_start_;
1251 1222
1252 DISALLOW_ALLOCATION(); 1223 DISALLOW_ALLOCATION();
1253 }; 1224 };
1254 1225
1255
1256 class GreedyLoopState { 1226 class GreedyLoopState {
1257 public: 1227 public:
1258 explicit GreedyLoopState(bool not_at_start); 1228 explicit GreedyLoopState(bool not_at_start);
1259 1229
1260 BlockLabel* label() { return &label_; } 1230 BlockLabel* label() { return &label_; }
1261 Trace* counter_backtrack_trace() { return &counter_backtrack_trace_; } 1231 Trace* counter_backtrack_trace() { return &counter_backtrack_trace_; }
1262 1232
1263 private: 1233 private:
1264 BlockLabel label_; 1234 BlockLabel label_;
1265 Trace counter_backtrack_trace_; 1235 Trace counter_backtrack_trace_;
1266 }; 1236 };
1267 1237
1268
1269 struct PreloadState { 1238 struct PreloadState {
1270 static const intptr_t kEatsAtLeastNotYetInitialized = -1; 1239 static const intptr_t kEatsAtLeastNotYetInitialized = -1;
1271 bool preload_is_current_; 1240 bool preload_is_current_;
1272 bool preload_has_checked_bounds_; 1241 bool preload_has_checked_bounds_;
1273 intptr_t preload_characters_; 1242 intptr_t preload_characters_;
1274 intptr_t eats_at_least_; 1243 intptr_t eats_at_least_;
1275 void init() { eats_at_least_ = kEatsAtLeastNotYetInitialized; } 1244 void init() { eats_at_least_ = kEatsAtLeastNotYetInitialized; }
1276 1245
1277 DISALLOW_ALLOCATION(); 1246 DISALLOW_ALLOCATION();
1278 }; 1247 };
1279 1248
1280
1281 class NodeVisitor : public ValueObject { 1249 class NodeVisitor : public ValueObject {
1282 public: 1250 public:
1283 virtual ~NodeVisitor() {} 1251 virtual ~NodeVisitor() {}
1284 #define DECLARE_VISIT(Type) virtual void Visit##Type(Type##Node* that) = 0; 1252 #define DECLARE_VISIT(Type) virtual void Visit##Type(Type##Node* that) = 0;
1285 FOR_EACH_NODE_TYPE(DECLARE_VISIT) 1253 FOR_EACH_NODE_TYPE(DECLARE_VISIT)
1286 #undef DECLARE_VISIT 1254 #undef DECLARE_VISIT
1287 virtual void VisitLoopChoice(LoopChoiceNode* that) { VisitChoice(that); } 1255 virtual void VisitLoopChoice(LoopChoiceNode* that) { VisitChoice(that); }
1288 }; 1256 };
1289 1257
1290
1291 // Assertion propagation moves information about assertions such as 1258 // Assertion propagation moves information about assertions such as
1292 // \b to the affected nodes. For instance, in /.\b./ information must 1259 // \b to the affected nodes. For instance, in /.\b./ information must
1293 // be propagated to the first '.' that whatever follows needs to know 1260 // be propagated to the first '.' that whatever follows needs to know
1294 // if it matched a word or a non-word, and to the second '.' that it 1261 // if it matched a word or a non-word, and to the second '.' that it
1295 // has to check if it succeeds a word or non-word. In this case the 1262 // has to check if it succeeds a word or non-word. In this case the
1296 // result will be something like: 1263 // result will be something like:
1297 // 1264 //
1298 // +-------+ +------------+ 1265 // +-------+ +------------+
1299 // | . | | . | 1266 // | . | | . |
1300 // +-------+ ---> +------------+ 1267 // +-------+ ---> +------------+
(...skipping 20 matching lines...) Expand all
1321 void fail(const char* error_message) { error_message_ = error_message; } 1288 void fail(const char* error_message) { error_message_ = error_message; }
1322 1289
1323 private: 1290 private:
1324 bool ignore_case_; 1291 bool ignore_case_;
1325 bool is_one_byte_; 1292 bool is_one_byte_;
1326 const char* error_message_; 1293 const char* error_message_;
1327 1294
1328 DISALLOW_IMPLICIT_CONSTRUCTORS(Analysis); 1295 DISALLOW_IMPLICIT_CONSTRUCTORS(Analysis);
1329 }; 1296 };
1330 1297
1331
1332 struct RegExpCompileData : public ZoneAllocated { 1298 struct RegExpCompileData : public ZoneAllocated {
1333 RegExpCompileData() 1299 RegExpCompileData()
1334 : tree(NULL), 1300 : tree(NULL),
1335 node(NULL), 1301 node(NULL),
1336 simple(true), 1302 simple(true),
1337 contains_anchor(false), 1303 contains_anchor(false),
1338 error(String::Handle(String::null())), 1304 error(String::Handle(String::null())),
1339 capture_count(0) {} 1305 capture_count(0) {}
1340 RegExpTree* tree; 1306 RegExpTree* tree;
1341 RegExpNode* node; 1307 RegExpNode* node;
1342 bool simple; 1308 bool simple;
1343 bool contains_anchor; 1309 bool contains_anchor;
1344 String& error; 1310 String& error;
1345 intptr_t capture_count; 1311 intptr_t capture_count;
1346 }; 1312 };
1347 1313
1348
1349 class RegExpEngine : public AllStatic { 1314 class RegExpEngine : public AllStatic {
1350 public: 1315 public:
1351 struct CompilationResult { 1316 struct CompilationResult {
1352 explicit CompilationResult(const char* error_message) 1317 explicit CompilationResult(const char* error_message)
1353 : error_message(error_message), 1318 : error_message(error_message),
1354 #if !defined(DART_PRECOMPILED_RUNTIME) 1319 #if !defined(DART_PRECOMPILED_RUNTIME)
1355 backtrack_goto(NULL), 1320 backtrack_goto(NULL),
1356 graph_entry(NULL), 1321 graph_entry(NULL),
1357 num_blocks(-1), 1322 num_blocks(-1),
1358 num_stack_locals(-1), 1323 num_stack_locals(-1),
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
1416 const String& pattern, 1381 const String& pattern,
1417 bool multi_line, 1382 bool multi_line,
1418 bool ignore_case); 1383 bool ignore_case);
1419 1384
1420 static void DotPrint(const char* label, RegExpNode* node, bool ignore_case); 1385 static void DotPrint(const char* label, RegExpNode* node, bool ignore_case);
1421 }; 1386 };
1422 1387
1423 } // namespace dart 1388 } // namespace dart
1424 1389
1425 #endif // RUNTIME_VM_REGEXP_H_ 1390 #endif // RUNTIME_VM_REGEXP_H_
OLDNEW
« no previous file with comments | « runtime/vm/redundancy_elimination.cc ('k') | runtime/vm/regexp.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698