Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinite loop in SearchEngine.replaceAll if a DocumentFilter blocks replace attempt #401

Closed
paul-griffith opened this issue Sep 9, 2021 · 3 comments
Assignees
Labels

Comments

@paul-griffith
Copy link
Contributor

e.g in SSCCE FindReplaceDemoApp on
https://github.com/paul-griffith/RSyntaxTextArea/tree/restricted-document-filter

The gist:

  1. Create a text area, add a document filter that doesn't allow replacement within a certain subsection.
		textArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA);
		textArea.setText("public static void main(String[] args) {\n\t// does nothing\n}");
		RSyntaxDocument document = (RSyntaxDocument) textArea.getDocument();
		document.setDocumentFilter(new DocumentFilter() {
			@Override
			public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
				throws BadLocationException {
				if (offset >= 40) {
					super.replace(fb, offset, length, text, attrs);
				}
			}
		});
  1. Call SearchEngine.replaceAll for some search text that's in the 'restricted' area, i.e. public, static, void, main in the above snippet.
  2. UI thread will block indefinitely due to an infinite loop. From thread dumps, it looks like the replace "context" never moves past the restricted area, so SearchEngine keeps trying to replace the same text infinitely.
"AWT-EventQueue-0@1189" prio=6 tid=0x14 nid=NA runnable
  java.lang.Thread.State: RUNNABLE
	  at java.security.AccessController.getStackAccessControlContext(AccessController.java:-1)
	  at java.security.AccessController.getContext(AccessController.java:833)
	  at java.awt.AWTEvent.<init>(AWTEvent.java:115)
	  at java.awt.event.InvocationEvent.<init>(InvocationEvent.java:286)
	  at java.awt.event.InvocationEvent.<init>(InvocationEvent.java:172)
	  at java.awt.EventQueue.invokeLater(EventQueue.java:1313)
	  at javax.swing.SwingUtilities.invokeLater(SwingUtilities.java:1421)
	  at javax.swing.text.DefaultCaret.changeCaretPosition(DefaultCaret.java:1299)
	  at javax.swing.text.DefaultCaret.handleMoveDot(DefaultCaret.java:1110)
	  at javax.swing.text.DefaultCaret$DefaultFilterBypass.moveDot(DefaultCaret.java:1951)
	  at javax.swing.text.NavigationFilter.moveDot(NavigationFilter.java:79)
	  at org.fife.ui.rtextarea.ConfigurableCaret$FoldAwareNavigationFilter.moveDot(ConfigurableCaret.java:730)
	  at javax.swing.text.DefaultCaret.moveDot(DefaultCaret.java:1101)
	  at javax.swing.text.DefaultCaret.moveDot(DefaultCaret.java:1072)
	  at javax.swing.text.JTextComponent.moveCaretPosition(JTextComponent.java:1563)
	  at javax.swing.text.JTextComponent.select(JTextComponent.java:1934)
	  at javax.swing.text.JTextComponent.setSelectionStart(JTextComponent.java:1852)
	  at org.fife.ui.rsyntaxtextarea.RSyntaxUtilities.selectAndPossiblyCenter(RSyntaxUtilities.java:1337)
	  at org.fife.ui.rtextarea.SearchEngine.find(SearchEngine.java:146)
	  at org.fife.ui.rtextarea.SearchEngine.replace(SearchEngine.java:941)
	  at org.fife.ui.rtextarea.SearchEngine.replaceAll(SearchEngine.java:1026)
	  at org.fife.ui.rsyntaxtextarea.demo.FindAndReplaceDemo.actionPerformed(FindAndReplaceDemo.java:97)
	  at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1967)
	  at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2308)
	  at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
	  at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
	  at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279)
	  at java.awt.Component.processMouseEvent(Component.java:6635)
	  at javax.swing.JComponent.processMouseEvent(JComponent.java:3342)
	  at java.awt.Component.processEvent(Component.java:6400)
	  at java.awt.Container.processEvent(Container.java:2263)
	  at java.awt.Component.dispatchEventImpl(Component.java:5011)
	  at java.awt.Container.dispatchEventImpl(Container.java:2321)
	  at java.awt.Component.dispatchEvent(Component.java:4843)
	  at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4918)
	  at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4547)
	  at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4488)
	  at java.awt.Container.dispatchEventImpl(Container.java:2307)
	  at java.awt.Window.dispatchEventImpl(Window.java:2772)
	  at java.awt.Component.dispatchEvent(Component.java:4843)
	  at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:772)
	  at java.awt.EventQueue$4.run(EventQueue.java:721)
	  at java.awt.EventQueue$4.run(EventQueue.java:715)
	  at java.security.AccessController.doPrivileged(AccessController.java:-1)
	  at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
	  at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:95)
	  at java.awt.EventQueue$5.run(EventQueue.java:745)
	  at java.awt.EventQueue$5.run(EventQueue.java:743)
	  at java.security.AccessController.doPrivileged(AccessController.java:-1)
	  at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
	  at java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
	  at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	  at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	  at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	  at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	  at java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
@paul-griffith
Copy link
Contributor Author

I've been working on trying to patch this as a consumer for a little while and had no success. Even within RSyntaxTextArea, it seems like a thorny problem; hoping you might have some ideas. I'll keep working at it and contribute a PR if I come up with anything.

@DevCharly
Copy link

Also had recently a endless loop in SearchEngine.replaceAll().
In my case, I had search wrap enabled, which caused the endless loop.

The fix was:

context.setSearchWrap( false );

@bobbylight I think SearchEngine.replaceAll() should ignore the wrap flag

@bobbylight bobbylight self-assigned this Sep 11, 2021
@bobbylight bobbylight added the bug label Sep 11, 2021
@bobbylight
Copy link
Owner

Thanks for the bug report, fixed in the latest 3.1.4-SNAPSHOT. Note that SearchResult.getCount() will be wrong in the case of a DocumentFilter silently preventing certain edits to the document. It'll just be the number of matches and (attempted) replacements. I'm not sure how to get around that, but maybe it's not such a big deal - if either of you have any thoughts about that, let me know :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants