1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-07-21 16:05:25 +02:00

Bug 459186. Added safeguards against misuse of content assist contexts

This commit is contained in:
Sergey Prigogin 2015-02-06 15:25:09 -08:00
parent eaabc8eb34
commit 3cbb643870
3 changed files with 75 additions and 24 deletions

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2004, 2014 IBM Corporation and others. * Copyright (c) 2004, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -11,6 +11,7 @@
* Bryan Wilkinson (QNX) * Bryan Wilkinson (QNX)
* Markus Schorn (Wind River Systems) * Markus Schorn (Wind River Systems)
* Thomas Corbat (IFS) * Thomas Corbat (IFS)
* Sergey Prigogin (Google)
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.ui.tests.text.contentassist2; package org.eclipse.cdt.ui.tests.text.contentassist2;
@ -39,6 +40,7 @@ import org.eclipse.cdt.ui.testplugin.EditorTestHelper;
import org.eclipse.cdt.ui.tests.BaseUITestCase; import org.eclipse.cdt.ui.tests.BaseUITestCase;
import org.eclipse.cdt.ui.text.ICCompletionProposal; import org.eclipse.cdt.ui.text.ICCompletionProposal;
import org.eclipse.cdt.ui.text.ICPartitions; import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTNameBase; import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTNameBase;
@ -72,23 +74,23 @@ public abstract class AbstractContentAssistTest extends BaseUITestCase {
fCFile= setUpProjectContent(fCProject.getProject()); fCFile= setUpProjectContent(fCProject.getProject());
assertNotNull(fCFile); assertNotNull(fCFile);
waitForIndexer(fCProject); waitForIndexer(fCProject);
fEditor= (ITextEditor)EditorTestHelper.openInEditor(fCFile, true); fEditor= (ITextEditor) EditorTestHelper.openInEditor(fCFile, true);
assertNotNull(fEditor); assertNotNull(fEditor);
CPPASTNameBase.sAllowNameComputation= true; CPPASTNameBase.sAllowNameComputation= true;
// EditorTestHelper.joinBackgroundActivities((AbstractTextEditor)fEditor); // EditorTestHelper.joinBackgroundActivities((AbstractTextEditor) fEditor);
} }
/** /**
* Setup the project's content. * Setup the project's content.
* @param project * @param project
* @return the file to be opened in the editor * @return the file to be opened in the editor
* @throws Exception
*/ */
protected abstract IFile setUpProjectContent(IProject project) throws Exception; protected abstract IFile setUpProjectContent(IProject project) throws Exception;
@Override @Override
protected void tearDown() throws Exception { protected void tearDown() throws Exception {
ContentAssistInvocationContext.assertNoUndisposedContexts();
EditorTestHelper.closeEditor(fEditor); EditorTestHelper.closeEditor(fEditor);
fEditor= null; fEditor= null;
CProjectHelper.delete(fCProject); CProjectHelper.delete(fCProject);
@ -97,12 +99,13 @@ public abstract class AbstractContentAssistTest extends BaseUITestCase {
super.tearDown(); super.tearDown();
} }
protected void assertContentAssistResults(int offset, int length, String[] expected, boolean isCompletion, boolean isTemplate, boolean filterResults, CompareType compareType) throws Exception { protected void assertContentAssistResults(int offset, int length, String[] expected,
boolean isCompletion, boolean isTemplate, boolean filterResults, CompareType compareType) throws Exception {
if (CTestPlugin.getDefault().isDebugging()) { if (CTestPlugin.getDefault().isDebugging()) {
System.out.println("\n\n\n\n\nTesting "+this.getClass().getName()); System.out.println("\n\n\n\n\nTesting " + this.getClass().getName());
} }
//Call the CContentAssistProcessor // Call the CContentAssistProcessor
ISourceViewer sourceViewer= EditorTestHelper.getSourceViewer((AbstractTextEditor)fEditor); ISourceViewer sourceViewer= EditorTestHelper.getSourceViewer((AbstractTextEditor)fEditor);
String contentType= TextUtilities.getContentType(sourceViewer.getDocument(), ICPartitions.C_PARTITIONING, offset, true); String contentType= TextUtilities.getContentType(sourceViewer.getDocument(), ICPartitions.C_PARTITIONING, offset, true);
boolean isCode= IDocument.DEFAULT_CONTENT_TYPE.equals(contentType); boolean isCode= IDocument.DEFAULT_CONTENT_TYPE.equals(contentType);
@ -288,8 +291,6 @@ public abstract class AbstractContentAssistTest extends BaseUITestCase {
return EditorTestHelper.getDocument(fEditor); return EditorTestHelper.getDocument(fEditor);
} }
protected void setCommaAfterFunctionParameter(String value) { protected void setCommaAfterFunctionParameter(String value) {
fCProject.setOption( fCProject.setOption(
DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_METHOD_DECLARATION_PARAMETERS, value); DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_COMMA_IN_METHOD_DECLARATION_PARAMETERS, value);

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2005, 2014 IBM Corporation and others. * Copyright (c) 2005, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -10,6 +10,7 @@
* Anton Leherbauer (Wind River Systems) * Anton Leherbauer (Wind River Systems)
* Bryan Wilkinson (QNX) * Bryan Wilkinson (QNX)
* Thomas Corbat (IFS) * Thomas Corbat (IFS)
* Sergey Prigogin (Google)
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.internal.ui.text.contentassist; package org.eclipse.cdt.internal.ui.text.contentassist;
@ -41,16 +42,18 @@ import org.eclipse.cdt.internal.ui.text.Symbols;
/** /**
* Describes the context of a content assist invocation in a C/C++ editor. * Describes the context of a content assist invocation in a C/C++ editor.
* <p> * <p>
* Clients may use but not subclass this class. * Clients may instantiate. A client that created a context is responsible for its disposal.
* </p> * </p>
* *
* @since 4.0 * @since 4.0
*/ */
public class CContentAssistInvocationContext extends ContentAssistInvocationContext implements ICEditorContentAssistInvocationContext { public class CContentAssistInvocationContext extends ContentAssistInvocationContext
implements ICEditorContentAssistInvocationContext {
private final IEditorPart fEditor; private final IEditorPart fEditor;
private final boolean fIsCompletion; private final boolean fIsCompletion;
private final boolean fIsAutoActivated; private final boolean fIsAutoActivated;
private IIndex fIndex = null; private IIndex fIndex;
private Lazy<Integer> fContextInfoPosition = new Lazy<Integer>() { private Lazy<Integer> fContextInfoPosition = new Lazy<Integer>() {
@Override @Override
protected Integer calculateValue() { protected Integer calculateValue() {
@ -247,6 +250,7 @@ public class CContentAssistInvocationContext extends ContentAssistInvocationCont
*/ */
@Override @Override
public ITranslationUnit getTranslationUnit() { public ITranslationUnit getTranslationUnit() {
assertNotDisposed();
return fTU.value(); return fTU.value();
} }
@ -258,12 +262,14 @@ public class CContentAssistInvocationContext extends ContentAssistInvocationCont
*/ */
@Override @Override
public ICProject getProject() { public ICProject getProject() {
assertNotDisposed();
ITranslationUnit unit= getTranslationUnit(); ITranslationUnit unit= getTranslationUnit();
return unit == null ? null : unit.getCProject(); return unit == null ? null : unit.getCProject();
} }
@Override @Override
public IASTCompletionNode getCompletionNode() { public IASTCompletionNode getCompletionNode() {
assertNotDisposed();
// For scalability. // For scalability.
if (fEditor != null && fEditor instanceof CEditor) { if (fEditor != null && fEditor instanceof CEditor) {
CEditor editor = (CEditor) fEditor; CEditor editor = (CEditor) fEditor;
@ -287,6 +293,7 @@ public class CContentAssistInvocationContext extends ContentAssistInvocationCont
@Override @Override
public int getParseOffset() { public int getParseOffset() {
assertNotDisposed();
return fParseOffset.value(); return fParseOffset.value();
} }
@ -295,6 +302,7 @@ public class CContentAssistInvocationContext extends ContentAssistInvocationCont
*/ */
@Override @Override
public int getContextInformationOffset() { public int getContextInformationOffset() {
assertNotDisposed();
return fContextInfoPosition.value(); return fContextInfoPosition.value();
} }
@ -306,6 +314,7 @@ public class CContentAssistInvocationContext extends ContentAssistInvocationCont
* @return a sensible completion offset * @return a sensible completion offset
*/ */
protected int guessCompletionPosition(int contextPosition) { protected int guessCompletionPosition(int contextPosition) {
assertNotDisposed();
CHeuristicScanner scanner= new CHeuristicScanner(getDocument()); CHeuristicScanner scanner= new CHeuristicScanner(getDocument());
int bound= Math.max(-1, contextPosition - 200); int bound= Math.max(-1, contextPosition - 200);
@ -354,6 +363,7 @@ public class CContentAssistInvocationContext extends ContentAssistInvocationCont
* offset is not inside a function call (or similar) * offset is not inside a function call (or similar)
*/ */
protected int guessContextInformationPosition() { protected int guessContextInformationPosition() {
assertNotDisposed();
final int contextPosition= getInvocationOffset(); final int contextPosition= getInvocationOffset();
CHeuristicScanner scanner= new CHeuristicScanner(getDocument()); CHeuristicScanner scanner= new CHeuristicScanner(getDocument());
@ -386,15 +396,18 @@ public class CContentAssistInvocationContext extends ContentAssistInvocationCont
*/ */
@Override @Override
public IEditorPart getEditor() { public IEditorPart getEditor() {
assertNotDisposed();
return fEditor; return fEditor;
} }
@Override @Override
public boolean isContextInformationStyle() { public boolean isContextInformationStyle() {
assertNotDisposed();
return !fIsCompletion || (getParseOffset() != getInvocationOffset()); return !fIsCompletion || (getParseOffset() != getInvocationOffset());
} }
public boolean isAutoActivated() { public boolean isAutoActivated() {
assertNotDisposed();
return fIsAutoActivated; return fIsAutoActivated;
} }
@ -402,31 +415,38 @@ public class CContentAssistInvocationContext extends ContentAssistInvocationCont
public void dispose() { public void dispose() {
if (fIndex != null) { if (fIndex != null) {
fIndex.releaseReadLock(); fIndex.releaseReadLock();
fIndex = null;
} }
super.dispose(); super.dispose();
} }
public boolean isAfterOpeningParenthesis() { public boolean isAfterOpeningParenthesis() {
assertNotDisposed();
return afterOpeningParenthesis.value(); return afterOpeningParenthesis.value();
} }
public boolean isAfterOpeningAngleBracket() { public boolean isAfterOpeningAngleBracket() {
assertNotDisposed();
return afterOpeningAngleBracket.value(); return afterOpeningAngleBracket.value();
} }
public boolean isInUsingDirective() { public boolean isInUsingDirective() {
assertNotDisposed();
return inUsingDeclaration.value(); return inUsingDeclaration.value();
} }
public boolean isFollowedBySemicolon() { public boolean isFollowedBySemicolon() {
assertNotDisposed();
return followedBySemicolon.value(); return followedBySemicolon.value();
} }
public String getFunctionParameterDelimiter() { public String getFunctionParameterDelimiter() {
assertNotDisposed();
return functionParameterDelimiter.value(); return functionParameterDelimiter.value();
} }
public String getTemplateParameterDelimiter() { public String getTemplateParameterDelimiter() {
assertNotDisposed();
return templateParameterDelimiter.value(); return templateParameterDelimiter.value();
} }
} }

View file

@ -1,5 +1,5 @@
/******************************************************************************* /*******************************************************************************
* Copyright (c) 2005, 2009 IBM Corporation and others. * Copyright (c) 2005, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at * which accompanies this distribution, and is available at
@ -9,9 +9,12 @@
* IBM Corporation - initial API and implementation * IBM Corporation - initial API and implementation
* Anton Leherbauer (Wind River Systems) * Anton Leherbauer (Wind River Systems)
* Bryan Wilkinson (QNX) * Bryan Wilkinson (QNX)
* Sergey Prigogin (Google)
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.ui.text.contentassist; package org.eclipse.cdt.ui.text.contentassist;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocument;
@ -26,20 +29,32 @@ import org.eclipse.jface.text.ITextViewer;
* specific context information such as an AST. * specific context information such as an AST.
* </p> * </p>
* <p> * <p>
* Clients may instantiate. Any created context has to be disposed. * Clients may instantiate. A client that created a context is responsible for its disposal.
* </p> * </p>
* @noextend This class is not intended to be subclassed by clients. * @noextend This class is not intended to be subclassed by clients.
* @since 4.0 * @since 4.0
*/ */
public class ContentAssistInvocationContext { public class ContentAssistInvocationContext {
/* state */ private static final AtomicInteger numberOfUndisposedContexts = new AtomicInteger();
/* State. */
private final ITextViewer fViewer; private final ITextViewer fViewer;
private final IDocument fDocument; private final IDocument fDocument;
private final int fOffset; private final int fOffset;
private boolean fDisposed;
/* cached additional info */
/* Cached additional info. */
private CharSequence fPrefix; private CharSequence fPrefix;
/**
* For tests only.
* @since 5.9
*/
public static void assertNoUndisposedContexts() {
Assert.isTrue(numberOfUndisposedContexts.get() == 0,
numberOfUndisposedContexts.get() + " ContentAssistInvocationContext objects have not been disposed."); //$NON-NLS-1$
}
/** /**
* Equivalent to * Equivalent to
* {@linkplain #ContentAssistInvocationContext(ITextViewer, int) ContentAssistInvocationContext(viewer, viewer.getSelectedRange().x)}. * {@linkplain #ContentAssistInvocationContext(ITextViewer, int) ContentAssistInvocationContext(viewer, viewer.getSelectedRange().x)}.
@ -61,6 +76,7 @@ public class ContentAssistInvocationContext {
fViewer= viewer; fViewer= viewer;
fDocument= null; fDocument= null;
fOffset= offset; fOffset= offset;
numberOfUndisposedContexts.incrementAndGet();
} }
/** /**
@ -70,6 +86,7 @@ public class ContentAssistInvocationContext {
fDocument= null; fDocument= null;
fViewer= null; fViewer= null;
fOffset= -1; fOffset= -1;
numberOfUndisposedContexts.incrementAndGet();
} }
/** /**
@ -84,6 +101,7 @@ public class ContentAssistInvocationContext {
fViewer= null; fViewer= null;
fDocument= document; fDocument= document;
fOffset= offset; fOffset= offset;
numberOfUndisposedContexts.incrementAndGet();
} }
/** /**
@ -92,6 +110,7 @@ public class ContentAssistInvocationContext {
* @return the invocation offset * @return the invocation offset
*/ */
public final int getInvocationOffset() { public final int getInvocationOffset() {
assertNotDisposed();
return fOffset; return fOffset;
} }
@ -101,6 +120,7 @@ public class ContentAssistInvocationContext {
* @return the viewer, possibly <code>null</code> * @return the viewer, possibly <code>null</code>
*/ */
public final ITextViewer getViewer() { public final ITextViewer getViewer() {
assertNotDisposed();
return fViewer; return fViewer;
} }
@ -110,6 +130,7 @@ public class ContentAssistInvocationContext {
* @return the document or <code>null</code> * @return the document or <code>null</code>
*/ */
public IDocument getDocument() { public IDocument getDocument() {
assertNotDisposed();
if (fDocument == null) { if (fDocument == null) {
if (fViewer == null) if (fViewer == null)
return null; return null;
@ -127,6 +148,7 @@ public class ContentAssistInvocationContext {
* @throws BadLocationException if accessing the document fails * @throws BadLocationException if accessing the document fails
*/ */
public CharSequence computeIdentifierPrefix() throws BadLocationException { public CharSequence computeIdentifierPrefix() throws BadLocationException {
assertNotDisposed();
if (fPrefix == null) { if (fPrefix == null) {
IDocument document= getDocument(); IDocument document= getDocument();
if (document == null) if (document == null)
@ -145,12 +167,23 @@ public class ContentAssistInvocationContext {
} }
/** /**
* Called upon completion of the content assist. Used to free any resources * Must be called upon completion of the content assist. Used to free any resources
* used by the context. * used by the context.
*/ */
public void dispose() { public void dispose() {
assertNotDisposed();
fDisposed = true;
numberOfUndisposedContexts.decrementAndGet();
} }
/**
* @since 5.9
*/
protected void assertNotDisposed() {
if (fDisposed)
throw new IllegalArgumentException("The content assist context has been disposed already"); //$NON-NLS-1$
}
/** /**
* Invocation contexts are equal if they describe the same context and are of the same type. * Invocation contexts are equal if they describe the same context and are of the same type.
* This implementation checks for <code>null</code> values and class equality. Subclasses * This implementation checks for <code>null</code> values and class equality. Subclasses
@ -193,9 +226,6 @@ public class ContentAssistInvocationContext {
return (fViewer == null && other.fViewer == null || fViewer != null && fViewer.equals(other.fViewer)) && fOffset == other.fOffset && (fDocument == null && other.fDocument == null || fDocument != null && fDocument.equals(other.fDocument)); return (fViewer == null && other.fViewer == null || fViewer != null && fViewer.equals(other.fViewer)) && fOffset == other.fOffset && (fDocument == null && other.fDocument == null || fDocument != null && fDocument.equals(other.fDocument));
} }
/*
* @see java.lang.Object#hashCode()
*/
@Override @Override
public int hashCode() { public int hashCode() {
return 23459213 << 5 | (fViewer == null ? 0 : fViewer.hashCode() << 3) | fOffset; return 23459213 << 5 | (fViewer == null ? 0 : fViewer.hashCode() << 3) | fOffset;