From 211723308ba75e4eab9550bedd9fc8234111a974 Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 19 Oct 2016 12:22:10 +0200 Subject: [PATCH 1/3] InstanceofCheckerGenerator rewritten to work with just asm, without cglib (IDEA-160578) --- platform/core-api/core-api.iml | 1 + .../util/InstanceofCheckerGenerator.java | 134 ++++++++---------- .../src/com/intellij/util/ReflectionUtil.java | 10 ++ 3 files changed, 73 insertions(+), 72 deletions(-) diff --git a/platform/core-api/core-api.iml b/platform/core-api/core-api.iml index b56ccffb06aa..d8bbd4ebc7c8 100644 --- a/platform/core-api/core-api.iml +++ b/platform/core-api/core-api.iml @@ -16,5 +16,6 @@ + \ No newline at end of file diff --git a/platform/core-api/src/com/intellij/util/InstanceofCheckerGenerator.java b/platform/core-api/src/com/intellij/util/InstanceofCheckerGenerator.java index eee6fb6a1b71..4100e19d2137 100644 --- a/platform/core-api/src/com/intellij/util/InstanceofCheckerGenerator.java +++ b/platform/core-api/src/com/intellij/util/InstanceofCheckerGenerator.java @@ -15,14 +15,13 @@ */ package com.intellij.util; -import com.intellij.openapi.application.PathManager; import com.intellij.openapi.util.Condition; import com.intellij.util.containers.ConcurrentFactoryMap; -import net.sf.cglib.asm.$ClassVisitor; -import net.sf.cglib.asm.$Label; -import net.sf.cglib.asm.$Type; -import net.sf.cglib.core.*; import org.jetbrains.annotations.NotNull; +import org.jetbrains.org.objectweb.asm.ClassWriter; +import org.jetbrains.org.objectweb.asm.MethodVisitor; +import org.jetbrains.org.objectweb.asm.Opcodes; +import org.jetbrains.org.objectweb.asm.Type; import java.lang.reflect.Modifier; @@ -33,13 +32,6 @@ public class InstanceofCheckerGenerator { private static final InstanceofCheckerGenerator ourInstance; static { - try { - ClassGenerator.class.getDeclaredMethod("generateClass", $ClassVisitor.class); - } - catch (NoSuchMethodException e) { - throw new IllegalStateException("Incorrect cglib version in the classpath, source=" + PathManager.getJarPathForClass(ClassGenerator.class)); - } - try { ourInstance = new InstanceofCheckerGenerator(); } @@ -52,6 +44,7 @@ public class InstanceofCheckerGenerator { return ourInstance; } + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private final ConcurrentFactoryMap> myCache = new ConcurrentFactoryMap>() { @Override protected Condition create(final Class key) { @@ -64,76 +57,73 @@ public class InstanceofCheckerGenerator { }; } - return new InstanceofClassGenerator(key).createClass(); + String name = "com.intellij.util.InstanceofChecker$$$$$" + key.getName().replace('.', '$'); + //noinspection unchecked + return (Condition)ReflectionUtil.newInstance(obtainClass(key, name, generateConditionClass(key, name))); } }; + private synchronized Class obtainClass(Class checkedClass, String name, byte[] bytes) { + ClassLoader loader = checkedClass.getClassLoader(); + if (loader == null) loader = InstanceofCheckerGenerator.class.getClassLoader(); + try { + return loader.loadClass(name); + } + catch (ClassNotFoundException ignore) { + } + + try { + return ReflectionUtil.defineClass(name, bytes, loader); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + @NotNull public Condition getInstanceofChecker(final Class someClass) { return myCache.get(someClass); } private static String toInternalName(Class someClass) { - return someClass.getName().replace('.', '/'); + return toInternalName(someClass.getName()); } - private static class InstanceofClassGenerator extends AbstractClassGenerator { - private static final Source SOURCE = new Source("IntellijInstanceof"); - private final Class myCheckedClass; - - public InstanceofClassGenerator(Class checkedClass) { - super(SOURCE); - myCheckedClass = checkedClass; - } - - @Override - protected ClassLoader getDefaultClassLoader() { - return myCheckedClass.getClassLoader(); - } - - public Condition createClass() { - return (Condition)super.create(myCheckedClass); - } - - @Override - protected Object firstInstance(Class type) throws Exception { - return type.newInstance(); - } - - @Override - protected Object nextInstance(Object instance) throws Exception { - return instance; - } - - @Override - public void generateClass($ClassVisitor classVisitor) throws Exception { - ClassEmitter cv = new ClassEmitter(classVisitor); - - cv.visit(Constants.V1_2, Modifier.PUBLIC, "com/intellij/util/InstanceofChecker$$$$$" + myCheckedClass.getName().replace('.', '$'), null, toInternalName(Object.class), new String[]{toInternalName(Condition.class)}); - cv.visitSource(Constants.SOURCE_FILE, null); - final Signature signature = new Signature("", "()V"); - final CodeEmitter cons = cv.begin_method(Modifier.PUBLIC, signature, new $Type[0]); - cons.load_this(); - cons.dup(); - cons.super_invoke_constructor(signature); - cons.return_value(); - cons.end_method(); - - final CodeEmitter e = cv.begin_method(Modifier.PUBLIC, new Signature("value", "(L" + toInternalName(Object.class) + ";)Z"), new $Type[0]); - e.load_arg(0); - e.instance_of($Type.getType(myCheckedClass)); - - $Label fail = e.make_label(); - e.if_jump(CodeEmitter.EQ, fail); - e.push(true); - e.return_value(); - - e.mark(fail); - e.push(false); - e.return_value(); - e.end_method(); - - cv.visitEnd(); - } + @NotNull + private static String toInternalName(String name) { + return name.replace('.', '/'); } + + private static byte[] generateConditionClass(Class checkedClass, final String generatedName) { + ClassWriter cv = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + cv.visit(Opcodes.V1_2, Modifier.PUBLIC, toInternalName(generatedName), null, toInternalName(Object.class), new String[]{toInternalName(Condition.class)}); + + defaultConstructor(cv); + + conditionValue(checkedClass, cv); + + cv.visitEnd(); + return cv.toByteArray(); + } + + private static void defaultConstructor(ClassWriter cv) { + MethodVisitor mv = cv.visitMethod(Modifier.PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, toInternalName(Object.class), "", "()V", false); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + private static void conditionValue(Class checkedClass, ClassWriter cv) { + MethodVisitor mv = cv.visitMethod(Modifier.PUBLIC, "value", "(L" + toInternalName(Object.class) + ";)Z", null, null); + mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 1); + mv.visitTypeInsn(Opcodes.INSTANCEOF, Type.getType(checkedClass).getInternalName()); + mv.visitInsn(Opcodes.IRETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + } diff --git a/platform/util/src/com/intellij/util/ReflectionUtil.java b/platform/util/src/com/intellij/util/ReflectionUtil.java index e110ef10f256..8afe51eb8e5f 100644 --- a/platform/util/src/com/intellij/util/ReflectionUtil.java +++ b/platform/util/src/com/intellij/util/ReflectionUtil.java @@ -592,4 +592,14 @@ public class ReflectionUtil { return JBIterable.of(aClass.getSuperclass()).append(aClass.getInterfaces()); } }; + + private static final Method DEFINE_CLASS = + ObjectUtils.assertNotNull(getDeclaredMethod(ClassLoader.class, "defineClass", String.class, byte[].class, int.class, int.class)); + + public static Class defineClass(@NotNull String className, @NotNull byte[] bytes, ClassLoader loader) throws Exception { + Class c = (Class)DEFINE_CLASS.invoke(loader, className, bytes, 0, bytes.length); + Class.forName(className, true, loader); + return c; + } + } \ No newline at end of file From c5f5511688f38b6302527fff27e9fe4874b6d04b Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 19 Oct 2016 12:32:30 +0200 Subject: [PATCH 2/3] add items to goto popups in bulk mode (IDEA-162828) --- .../ide/util/gotoByName/ChooseByNameBase.java | 38 +++++++++---------- .../ide/util/gotoByName/ModelDiff.java | 21 ++++++---- .../intellij/ui/CollectionComboBoxModel.java | 3 -- .../com/intellij/ui/CollectionListModel.java | 25 ++++++++++-- 4 files changed, 55 insertions(+), 32 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java index 5b2390fd04c2..27855b73d169 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ChooseByNameBase.java @@ -435,9 +435,9 @@ public abstract class ChooseByNameBase { group.add(new ShowFindUsagesAction() { @Override public PsiElement[][] getElements() { - final Object[] objects = myListModel.toArray(); - final List prefixMatchElements = new ArrayList<>(objects.length); - final List nonPrefixMatchElements = new ArrayList<>(objects.length); + final List objects = myListModel.getItems(); + final List prefixMatchElements = new ArrayList<>(objects.size()); + final List nonPrefixMatchElements = new ArrayList<>(objects.size()); List curElements = prefixMatchElements; for (Object object : objects) { if (object instanceof PsiElement) { @@ -782,7 +782,7 @@ public abstract class ChooseByNameBase { close(ok); clearPostponedOkAction(ok); - myListModel.clear(); + myListModel.removeAll(); } protected boolean closeForbidden(boolean ok) { @@ -973,7 +973,7 @@ public abstract class ChooseByNameBase { final String text = getTrimmedText(); if (!canShowListForEmptyPattern() && text.isEmpty()) { - myListModel.clear(); + myListModel.removeAll(); hideList(); myTextFieldPanel.hideHint(); myCard.show(myCardContainer, CHECK_BOX_CARD); @@ -1033,7 +1033,7 @@ public abstract class ChooseByNameBase { } } if (elements.isEmpty()) { - myListModel.clear(); + myListModel.removeAll(); myTextField.setForeground(JBColor.red); myListUpdater.cancelAll(); hideList(); @@ -1041,7 +1041,7 @@ public abstract class ChooseByNameBase { return; } - Object[] oldElements = myListModel.toArray(); + Object[] oldElements = myListModel.getItems().toArray(); Object[] newElements = elements.toArray(); List commands = ModelDiff.createDiffCmds(myListModel, oldElements, newElements); if (commands == null) { @@ -1055,7 +1055,7 @@ public abstract class ChooseByNameBase { pos = calcSelectedIndex(newElements, getTrimmedText()); } - ScrollingUtil.selectItem(myList, Math.min(pos, myListModel.size() - 1)); + ScrollingUtil.selectItem(myList, Math.min(pos, myListModel.getSize() - 1)); myList.setVisibleRowCount(Math.min(VISIBLE_LIST_SIZE_LIMIT, myList.getModel().getSize())); showList(); myTextFieldPanel.repositionHint(); @@ -1113,21 +1113,21 @@ public abstract class ChooseByNameBase { return "choose_by_name#" + myModel.getPromptText() + "#" + myCheckBox.isSelected() + "#" + getTrimmedText(); } - private static class MyListModel extends DefaultListModel implements ModelDiff.Model { + private static class MyListModel extends CollectionListModel implements ModelDiff.Model { @Override public void addToModel(int idx, T element) { - if (idx < size()) { - add(idx, element); - } - else { - addElement(element); - } + add(Math.min(idx, getSize()), element); + } + + @Override + public void addAllToModel(int index, List elements) { + addAll(Math.min(index, getSize()), elements); } @Override public void removeRangeFromModel(int start, int end) { - if (start < size() && size() != 0) { - removeRange(start, Math.min(end, size()-1)); + if (start < getSize() && !isEmpty()) { + removeRange(start, Math.min(end, getSize() - 1)); } } } @@ -1175,8 +1175,8 @@ public abstract class ChooseByNameBase { myTextFieldPanel.repositionHint(); if (!myListModel.isEmpty()) { - int pos = selectionPos <= 0 ? calcSelectedIndex(myListModel.toArray(), ChooseByNameBase.this.getTrimmedText()) : selectionPos; - ScrollingUtil.selectItem(myList, Math.min(pos, myListModel.size() - 1)); + int pos = selectionPos <= 0 ? calcSelectedIndex(myListModel.getItems().toArray(), ChooseByNameBase.this.getTrimmedText()) : selectionPos; + ScrollingUtil.selectItem(myList, Math.min(pos, myListModel.getSize() - 1)); } } } diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ModelDiff.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ModelDiff.java index bade4b3b95d5..39e145bd6f97 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ModelDiff.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/ModelDiff.java @@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class ModelDiff { @@ -48,9 +49,8 @@ public class ModelDiff { } if (change.inserted > 0) { - for (int i = 0; i < change.inserted; i++) { - commands.add(new InsertCmd<>(listModel, change.line0 + i + inserted - deleted, newElements[change.line1 + i])); - } + List elements = new ArrayList<>(Arrays.asList(newElements).subList(change.line1, change.line1 + change.inserted)); + commands.add(new InsertCmd<>(listModel, change.line0 + inserted - deleted, elements)); } deleted += change.deleted; @@ -67,6 +67,13 @@ public class ModelDiff { public interface Model { void addToModel(int index, T element); + + default void addAllToModel(int index, List elements) { + for (int i = 0; i < elements.size(); i++) { + addToModel(index + i, elements.get(i)); + } + } + void removeRangeFromModel(int start, int end); } @@ -102,18 +109,18 @@ public class ModelDiff { private static class InsertCmd implements Cmd { private final Model myListModel; private final int idx; - private final T element; + private final List elements; - private InsertCmd(@NotNull Model model, final int idx, @NotNull T element) { + private InsertCmd(@NotNull Model model, final int idx, @NotNull List elements) { myListModel = model; this.idx = idx; - this.element = element; + this.elements = elements; } @Override public void apply() { //System.out.println("Adding: "+this+"-> "+element); - myListModel.addToModel(idx, element); + myListModel.addAllToModel(idx, elements); } @Override diff --git a/platform/platform-api/src/com/intellij/ui/CollectionComboBoxModel.java b/platform/platform-api/src/com/intellij/ui/CollectionComboBoxModel.java index d1ad94d53bec..a1e714efaef6 100644 --- a/platform/platform-api/src/com/intellij/ui/CollectionComboBoxModel.java +++ b/platform/platform-api/src/com/intellij/ui/CollectionComboBoxModel.java @@ -66,7 +66,4 @@ public class CollectionComboBoxModel extends CollectionListModel implement super.fireContentsChanged(this, -1, -1); } - public boolean contains(T item) { - return getElementIndex(item) != -1; - } } \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/ui/CollectionListModel.java b/platform/platform-api/src/com/intellij/ui/CollectionListModel.java index 0a515924e76c..47a1bbc7480f 100644 --- a/platform/platform-api/src/com/intellij/ui/CollectionListModel.java +++ b/platform/platform-api/src/com/intellij/ui/CollectionListModel.java @@ -75,10 +75,14 @@ public class CollectionListModel extends AbstractListModel implements Edit } public void add(@NotNull final List elements) { + addAll(myItems.size(), elements); + } + + public void addAll(int index, @NotNull final List elements) { if (elements.isEmpty()) return; - int i = myItems.size(); - myItems.addAll(elements); - fireIntervalAdded(this, i, i + elements.size() - 1); + + myItems.addAll(index, elements); + fireIntervalAdded(this, index, index + elements.size() - 1); } public void remove(@NotNull T element) { @@ -167,4 +171,19 @@ public class CollectionListModel extends AbstractListModel implements Edit public boolean isEmpty() { return myItems.isEmpty(); } + + public boolean contains(T item) { + return getElementIndex(item) >= 0; + } + + public void removeRange(int fromIndex, int toIndex) { + if (fromIndex > toIndex) { + throw new IllegalArgumentException("fromIndex must be <= toIndex"); + } + for(int i = toIndex; i >= fromIndex; i--) { + itemReplaced(myItems.remove(i), null); + } + fireIntervalRemoved(this, fromIndex, toIndex); + } + } From c5627d5bf4428a922497f6d97927bd93f6ac182b Mon Sep 17 00:00:00 2001 From: peter Date: Wed, 19 Oct 2016 15:23:53 +0200 Subject: [PATCH 3/3] allow non-physical file documents to be committed outside transaction, since it'll cause no events anyway --- .../com/intellij/psi/impl/PsiDocumentManagerBase.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java index 605413be5560..1189fe00d901 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java @@ -292,8 +292,7 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen public void commitDocument(@NotNull final Document doc) { final Document document = doc instanceof DocumentWindow ? ((DocumentWindow)doc).getDelegate() : doc; - VirtualFile vFile = getVirtualFile(document); - if (vFile != null && !isFreeThreaded(vFile)) { + if (isEventSystemEnabled(document)) { ((TransactionGuardImpl)TransactionGuard.getInstance()).assertWriteActionAllowed(); } @@ -302,6 +301,14 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen } } + private boolean isEventSystemEnabled(Document document) { + VirtualFile vFile = getVirtualFile(document); + if (vFile == null || isFreeThreaded(vFile)) return false; + + FileViewProvider viewProvider = getCachedViewProvider(document); + return viewProvider != null && viewProvider.isEventSystemEnabled(); + } + // public for Upsource public boolean finishCommit(@NotNull final Document document, @NotNull final List> finishProcessors,