mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
CompositeElement.getPsi() and LPE' .createAllPsiForChildren were under different locks which lead to data race
This commit is contained in:
@@ -301,7 +301,7 @@ public class CompositeElement extends TreeElement {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected int textMatches(CharSequence buffer, int start) {
|
||||
protected int textMatches(@NotNull CharSequence buffer, int start) {
|
||||
int curOffset = start;
|
||||
for (TreeElement child = getFirstChildNode(); child != null; child = child.getTreeNext()) {
|
||||
curOffset = child.textMatches(buffer, curOffset);
|
||||
@@ -310,6 +310,36 @@ public class CompositeElement extends TreeElement {
|
||||
return curOffset;
|
||||
}
|
||||
|
||||
/*
|
||||
protected int textMatches(final CharSequence buffer, final int start) {
|
||||
final int[] curOffset = {start};
|
||||
acceptTree(new RecursiveTreeElementWalkingVisitor() {
|
||||
@Override
|
||||
public void visitLeaf(LeafElement leaf) {
|
||||
matchText(leaf);
|
||||
}
|
||||
|
||||
private void matchText(TreeElement leaf) {
|
||||
curOffset[0] = leaf.textMatches(buffer, curOffset[0]);
|
||||
if (curOffset[0] == -1) {
|
||||
stopWalking();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitComposite(CompositeElement composite) {
|
||||
if (composite instanceof LazyParseableElement && !((LazyParseableElement)composite).isParsed()) {
|
||||
matchText(composite);
|
||||
}
|
||||
else {
|
||||
super.visitComposite(composite);
|
||||
}
|
||||
}
|
||||
});
|
||||
return curOffset[0];
|
||||
}
|
||||
*/
|
||||
|
||||
@Nullable
|
||||
public final PsiElement findChildByRoleAsPsiElement(int role) {
|
||||
ASTNode element = findChildByRole(role);
|
||||
@@ -635,7 +665,7 @@ public class CompositeElement extends TreeElement {
|
||||
public void replaceAllChildrenToChildrenOf(final ASTNode anotherParent) {
|
||||
TreeUtil.ensureParsed(getFirstChildNode());
|
||||
TreeUtil.ensureParsed(anotherParent.getFirstChildNode());
|
||||
final ASTNode firstChild1 = anotherParent.getFirstChildNode();
|
||||
final ASTNode firstChild = anotherParent.getFirstChildNode();
|
||||
ChangeUtil.prepareAndRunChangeAction(new ChangeUtil.ChangeAction(){
|
||||
public void makeChange(TreeChangeEvent destinationTreeChange) {
|
||||
destinationTreeChange.addElementaryChange(anotherParent, ChangeInfoImpl.create(ChangeInfo.CONTENTS_CHANGED, anotherParent));
|
||||
@@ -643,7 +673,7 @@ public class CompositeElement extends TreeElement {
|
||||
}
|
||||
}, (TreeElement)anotherParent);
|
||||
|
||||
if (firstChild1 != null) {
|
||||
if (firstChild != null) {
|
||||
ChangeUtil.prepareAndRunChangeAction(new ChangeUtil.ChangeAction(){
|
||||
public void makeChange(TreeChangeEvent destinationTreeChange) {
|
||||
if(getTreeParent() != null){
|
||||
@@ -651,12 +681,12 @@ public class CompositeElement extends TreeElement {
|
||||
changeInfo.setOldLength(getTextLength());
|
||||
destinationTreeChange.addElementaryChange(CompositeElement.this, changeInfo);
|
||||
rawRemoveAllChildren();
|
||||
rawAddChildren((TreeElement)firstChild1);
|
||||
rawAddChildren((TreeElement)firstChild);
|
||||
}
|
||||
else{
|
||||
final TreeElement first = getFirstChildNode();
|
||||
remove(destinationTreeChange, first, null);
|
||||
add(destinationTreeChange, CompositeElement.this, (TreeElement)firstChild1);
|
||||
add(destinationTreeChange, CompositeElement.this, (TreeElement)firstChild);
|
||||
repairRemovedElement(CompositeElement.this, first);
|
||||
}
|
||||
}
|
||||
@@ -702,7 +732,7 @@ public class CompositeElement extends TreeElement {
|
||||
return LeafElement.getPsi(clazz, getPsi(), LOG);
|
||||
}
|
||||
|
||||
final PsiElement createAndStorePsi() {
|
||||
private PsiElement createAndStorePsi() {
|
||||
PsiElement psi = createPsiNoLock();
|
||||
myWrapper = psi;
|
||||
return psi;
|
||||
@@ -741,7 +771,7 @@ public class CompositeElement extends TreeElement {
|
||||
last.rawInsertAfterMe(first);
|
||||
}
|
||||
|
||||
if (DebugUtil.CHECK) DebugUtil.checkTreeStructure(this);
|
||||
DebugUtil.checkTreeStructure(this);
|
||||
}
|
||||
|
||||
public void rawRemoveAllChildren() {
|
||||
@@ -751,6 +781,23 @@ public class CompositeElement extends TreeElement {
|
||||
}
|
||||
}
|
||||
|
||||
void createAllChildrenPsi() {
|
||||
synchronized (PsiLock.LOCK) { // guard for race condition with getPsi()
|
||||
acceptTree(CREATE_CHILDREN_PSI);
|
||||
}
|
||||
}
|
||||
private static final RecursiveTreeElementWalkingVisitor CREATE_CHILDREN_PSI = new RecursiveTreeElementWalkingVisitor(false) {
|
||||
@Override
|
||||
public void visitLeaf(LeafElement leaf) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitComposite(CompositeElement composite) {
|
||||
composite.createAndStorePsi();
|
||||
super.visitComposite(composite);
|
||||
}
|
||||
};
|
||||
|
||||
private static void repairRemovedElement(final CompositeElement oldParent, final TreeElement oldChild) {
|
||||
if(oldChild == null) return;
|
||||
final FileElement treeElement = DummyHolderFactory.createHolder(oldParent.getManager(), null, false).getTreeElement();
|
||||
@@ -797,7 +844,7 @@ public class CompositeElement extends TreeElement {
|
||||
final TreeElement oldChild,
|
||||
final TreeElement newChild) {
|
||||
oldChild.rawReplaceWithList(newChild);
|
||||
final ReplaceChangeInfoImpl change = (ReplaceChangeInfoImpl)ChangeInfoImpl.create(ChangeInfo.REPLACE, newChild);
|
||||
final ReplaceChangeInfoImpl change = new ReplaceChangeInfoImpl(newChild);
|
||||
sourceTreeChange.addElementaryChange(newChild, change);
|
||||
change.setReplaced(oldChild);
|
||||
}
|
||||
|
||||
+40
-56
@@ -27,34 +27,23 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.ILazyParseableElementType;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class LazyParseableElement extends CompositeElement {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.LazyParseableElement");
|
||||
|
||||
private static final RecursiveTreeElementWalkingVisitor CREATE_PSI = new RecursiveTreeElementWalkingVisitor(false) {
|
||||
@Override
|
||||
public void visitLeaf(LeafElement leaf) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitComposite(CompositeElement composite) {
|
||||
composite.createAndStorePsi();
|
||||
super.visitComposite(composite);
|
||||
}
|
||||
};
|
||||
|
||||
private static class ChameleonLock {
|
||||
private ChameleonLock() {}
|
||||
|
||||
@NonNls
|
||||
@Override
|
||||
public String toString() {
|
||||
return "chameleon parsing lock";
|
||||
}
|
||||
}
|
||||
|
||||
private final Object lock = new ChameleonLock();
|
||||
|
||||
private final ChameleonLock lock = new ChameleonLock();
|
||||
private CharSequence myText;
|
||||
|
||||
public LazyParseableElement(@NotNull IElementType type, CharSequence text) {
|
||||
@@ -80,74 +69,68 @@ public class LazyParseableElement extends CompositeElement {
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
synchronized (lock) {
|
||||
if (myText != null) {
|
||||
return myText.toString();
|
||||
}
|
||||
CharSequence text = myText();
|
||||
if (text != null) {
|
||||
return text.toString();
|
||||
}
|
||||
return super.getText();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CharSequence getChars() {
|
||||
synchronized (lock) {
|
||||
if (myText != null) {
|
||||
return myText;
|
||||
}
|
||||
CharSequence text = myText();
|
||||
if (text != null) {
|
||||
return text;
|
||||
}
|
||||
return getText();
|
||||
return super.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTextLength() {
|
||||
synchronized (lock) {
|
||||
if (myText != null) {
|
||||
return myText.length();
|
||||
}
|
||||
CharSequence text = myText();
|
||||
if (text != null) {
|
||||
return text.length();
|
||||
}
|
||||
return super.getTextLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNotCachedLength() {
|
||||
synchronized (lock) {
|
||||
if (myText != null) {
|
||||
return myText.length();
|
||||
}
|
||||
CharSequence text = myText();
|
||||
if (text != null) {
|
||||
return text.length();
|
||||
}
|
||||
return super.getNotCachedLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hc() {
|
||||
synchronized (lock) {
|
||||
if (myText != null) {
|
||||
return LeafElement.leafHC(myText);
|
||||
}
|
||||
else {
|
||||
return super.hc();
|
||||
}
|
||||
}
|
||||
CharSequence text = myText();
|
||||
return text == null ? super.hc() : LeafElement.leafHC(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int textMatches(CharSequence buffer, int start) {
|
||||
synchronized (lock) {
|
||||
if (myText != null) {
|
||||
return LeafElement.leafTextMatches(myText, buffer, start);
|
||||
}
|
||||
protected int textMatches(@NotNull CharSequence buffer, int start) {
|
||||
CharSequence text = myText();
|
||||
if (text != null) {
|
||||
return LeafElement.leafTextMatches(text, buffer, start);
|
||||
}
|
||||
return super.textMatches(buffer, start);
|
||||
}
|
||||
|
||||
public boolean isParsed() {
|
||||
return myText() == null;
|
||||
}
|
||||
|
||||
private CharSequence myText() {
|
||||
synchronized (lock) {
|
||||
return myText == null;
|
||||
return myText;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFirstChildNode(TreeElement child) {
|
||||
if (myText != null) {
|
||||
if (myText() != null) {
|
||||
LOG.error("Mutating collapsed chameleon");
|
||||
}
|
||||
super.setFirstChildNode(child);
|
||||
@@ -155,7 +138,7 @@ public class LazyParseableElement extends CompositeElement {
|
||||
|
||||
@Override
|
||||
public void setLastChildNode(TreeElement child) {
|
||||
if (myText != null) {
|
||||
if (myText() != null) {
|
||||
LOG.error("Mutating collapsed chameleon");
|
||||
}
|
||||
super.setLastChildNode(child);
|
||||
@@ -199,14 +182,16 @@ public class LazyParseableElement extends CompositeElement {
|
||||
//}
|
||||
|
||||
//ensure PSI is created all at once, to reduce contention of PsiLock in CompositeElement.getPsi()
|
||||
((TreeElement)parsedNode).acceptTree(CREATE_PSI);
|
||||
if (parsedNode instanceof CompositeElement) {
|
||||
((CompositeElement)parsedNode).createAllChildrenPsi();
|
||||
}
|
||||
parsedNode.getTreeNext();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rawAddChildren(@NotNull TreeElement first) {
|
||||
if (myText != null) {
|
||||
if (myText() != null) {
|
||||
LOG.error("Mutating collapsed chameleon");
|
||||
}
|
||||
super.rawAddChildren(first);
|
||||
@@ -229,13 +214,12 @@ public class LazyParseableElement extends CompositeElement {
|
||||
}
|
||||
|
||||
public int copyTo(char[] buffer, int start) {
|
||||
synchronized (lock) {
|
||||
if (myText == null) return -1;
|
||||
|
||||
if (buffer != null) {
|
||||
CharArrayUtil.getChars(myText, buffer, start);
|
||||
}
|
||||
return start + myText.length();
|
||||
CharSequence text = myText();
|
||||
if (text == null) return -1;
|
||||
|
||||
if (buffer != null) {
|
||||
CharArrayUtil.getChars(text, buffer, start);
|
||||
}
|
||||
return start + text.length();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user