methods chains completion on one index

This commit is contained in:
Dmitry Batkovich
2013-11-01 15:10:37 +04:00
parent 4f6bbf3771
commit f5ed1c11ea
8 changed files with 74 additions and 349 deletions
@@ -1,20 +0,0 @@
package com.intellij.codeInsight.completion.methodChains;
/**
* @author Dmitry Batkovich
*/
public final class Constants {
private Constants() {
}
/**
* magic numbers
*/
public static final int SINGLETON_MAGIC_RATIO = 100;
public static final int SINGLETON_MAGIC_RATIO2 = 5;
public static final int CHAIN_SEARCH_MAGIC_RATIO = 12;
}
@@ -1,117 +0,0 @@
package com.intellij.compilerOutputIndex.impl;
import com.intellij.codeInsight.completion.methodChains.ChainCompletionStringUtil;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.ClassVisitor;
import org.jetbrains.asm4.MethodVisitor;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.tree.ClassNode;
import java.util.ArrayList;
import java.util.List;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class ClassFileData {
private final List<MethodData> myMethodDatas;
public ClassFileData(final ClassNode classNode) {
this(classNode, true);
}
public ClassFileData(final ClassNode classNode, final boolean checkForPrimitiveReturn) {
myMethodDatas = new ArrayList<MethodData>();
classNode.accept(new ClassVisitor(Opcodes.ASM4) {
@Override
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
final MethodDataAccumulator methodDataAccumulator = new MethodDataAccumulator(checkForPrimitiveReturn);
myMethodDatas.add(methodDataAccumulator.getMethodData());
return methodDataAccumulator;
}
});
}
public List<MethodData> getMethodDatas() {
return myMethodDatas;
}
public static class MethodData {
private final List<MethodInsnSignature> myMethodInsnSignatures = new ArrayList<MethodInsnSignature>();
private void addSign(final MethodInsnSignature signature) {
myMethodInsnSignatures.add(signature);
}
public List<MethodInsnSignature> getMethodInsnSignatures() {
return myMethodInsnSignatures;
}
}
private static class MethodDataAccumulator extends MethodVisitor {
private final MethodData myMethodData = new MethodData();
private final boolean myCheckForPrimitiveReturn;
public MethodDataAccumulator(final boolean checkForPrimitiveReturn) {
super(Opcodes.ASM4);
myCheckForPrimitiveReturn = checkForPrimitiveReturn;
}
private MethodData getMethodData() {
return myMethodData;
}
@Override
public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
if (MethodIncompleteSignature.CONSTRUCTOR_METHOD_NAME.equals(name)) {
return;
}
final String ownerClassName = AsmUtil.getQualifiedClassName(owner);
if (ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(ownerClassName)) {
return;
}
if (myCheckForPrimitiveReturn) {
final String returnType = AsmUtil.getReturnType(desc);
if (ChainCompletionStringUtil.isPrimitiveOrArrayOfPrimitives(returnType)) {
return;
}
}
myMethodData.addSign(new MethodInsnSignature(opcode, owner, name, desc));
}
}
public static class MethodInsnSignature {
private final int myOpcode;
private final String myOwner;
private final String myName;
private final String myDesc;
private MethodInsnSignature(final int opcode, final String owner, final String name, final String desc) {
myOpcode = opcode;
myOwner = owner;
myName = name;
myDesc = desc;
}
public int getOpcode() {
return myOpcode;
}
public String getOwner() {
return myOwner;
}
public String getName() {
return myName;
}
public String getDesc() {
return myDesc;
}
}
}
@@ -1,48 +0,0 @@
package com.intellij.compilerOutputIndex.impl;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputBaseIndex;
import com.intellij.openapi.project.Project;
import com.intellij.util.indexing.StorageException;
import com.intellij.util.indexing.ValueContainer;
import com.intellij.util.io.KeyDescriptor;
import java.util.TreeSet;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public abstract class CompilerOutputBaseGramsIndex<K> extends CompilerOutputBaseIndex<K, Multiset<MethodIncompleteSignature>> {
protected CompilerOutputBaseGramsIndex(final KeyDescriptor<K> keyDescriptor, final Project project) {
super(keyDescriptor, new GuavaHashMultiSetExternalizer<MethodIncompleteSignature>(MethodIncompleteSignature.createKeyDescriptor()), project);
}
public TreeSet<UsageIndexValue> getValues(final K key) {
try {
final ValueContainer<Multiset<MethodIncompleteSignature>> valueContainer = myIndex.getData(key);
final Multiset<MethodIncompleteSignature> rawValues = HashMultiset.create();
valueContainer.forEach(new ValueContainer.ContainerAction<Multiset<MethodIncompleteSignature>>() {
@Override
public boolean perform(final int id, final Multiset<MethodIncompleteSignature> values) {
for (final Multiset.Entry<MethodIncompleteSignature> entry : values.entrySet()) {
rawValues.add(entry.getElement(), entry.getCount());
}
return true;
}
});
return rawValuesToValues(rawValues);
} catch (StorageException e) {
throw new RuntimeException();
}
}
private static TreeSet<UsageIndexValue> rawValuesToValues(final Multiset<MethodIncompleteSignature> rawValues) {
final TreeSet<UsageIndexValue> values = new TreeSet<UsageIndexValue>();
for (final Multiset.Entry<MethodIncompleteSignature> entry : rawValues.entrySet()) {
values.add(new UsageIndexValue(entry.getElement(), entry.getCount()));
}
return values;
}
}
@@ -1,22 +0,0 @@
package com.intellij.compilerOutputIndex.impl.bigram;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class Bigram<E> extends Pair<E, E> {
public Bigram(@NotNull final E first, @NotNull final E second) {
super(first, second);
}
public Bigram<E> swap() {
return new Bigram<E>(second, first);
}
@Override
public String toString() {
return String.format("%s - %s", first, second);
}
}
@@ -1,87 +0,0 @@
package com.intellij.compilerOutputIndex.impl.bigram;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.intellij.compilerOutputIndex.api.fs.AsmUtil;
import com.intellij.compilerOutputIndex.api.indexer.CompilerOutputIndexer;
import com.intellij.compilerOutputIndex.impl.ClassFileData;
import com.intellij.compilerOutputIndex.impl.CompilerOutputBaseGramsIndex;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignatureChain;
import com.intellij.openapi.project.Project;
import com.intellij.util.SmartList;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.ID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.asm4.ClassReader;
import org.jetbrains.asm4.Opcodes;
import org.jetbrains.asm4.tree.ClassNode;
import java.util.*;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class BigramMethodsUsageIndex extends CompilerOutputBaseGramsIndex<MethodIncompleteSignature> {
public static BigramMethodsUsageIndex getInstance(final Project project) {
return CompilerOutputIndexer.getInstance(project).getIndex(BigramMethodsUsageIndex.class);
}
public BigramMethodsUsageIndex( final Project project) {
super(MethodIncompleteSignature.createKeyDescriptor(), project);
}
@Override
protected ID<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>> getIndexId() {
return generateIndexId("BigramMethodsUsage");
}
@Override
protected int getVersion() {
return 0;
}
@Override
protected DataIndexer<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>,ClassNode> getIndexer() {
//
// not fair way, but works fast
//
return new DataIndexer<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>, ClassNode>() {
@NotNull
@Override
public Map<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>> map(final ClassNode inputData) {
final Map<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>> map =
new HashMap<MethodIncompleteSignature, Multiset<MethodIncompleteSignature>>();
for (final ClassFileData.MethodData data : new ClassFileData(inputData).getMethodDatas()) {
final SimpleBigramsExtractor extractor = new SimpleBigramsExtractor(new SimpleBigramsExtractor.BigramMethodIncompleteSignatureProcessor() {
@Override
public void process(final Bigram<MethodIncompleteSignature> bigram) {
final MethodIncompleteSignature secondGram = bigram.getSecond();
Multiset<MethodIncompleteSignature> occurrences = map.get(secondGram);
if (occurrences == null) {
occurrences = HashMultiset.create();
map.put(secondGram, occurrences);
}
occurrences.add(bigram.getFirst());
}
});
for (final ClassFileData.MethodInsnSignature ms : data.getMethodInsnSignatures()) {
final List<MethodIncompleteSignature> methodInvocations = new SmartList<MethodIncompleteSignature>();
final String ownerClassName = AsmUtil.getQualifiedClassName(ms.getOwner());
final String returnType = AsmUtil.getReturnType(ms.getDesc());
if (ms.getName().equals(MethodIncompleteSignature.CONSTRUCTOR_METHOD_NAME)) {
methodInvocations.add(MethodIncompleteSignature.constructor(ownerClassName));
}
else {
methodInvocations.add(new MethodIncompleteSignature(ownerClassName, returnType, ms.getName(), ms.getOpcode() == Opcodes.INVOKESTATIC));
}
extractor.addChain(new MethodIncompleteSignatureChain(methodInvocations));
}
}
return map;
}
};
}
}
@@ -1,55 +0,0 @@
package com.intellij.compilerOutputIndex.impl.bigram;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignature;
import com.intellij.compilerOutputIndex.impl.MethodIncompleteSignatureChain;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
class SimpleBigramsExtractor {
private final Map<String, MethodIncompleteSignature> myHolder = new HashMap<String, MethodIncompleteSignature>();
private final BigramMethodIncompleteSignatureProcessor myProcessor;
public SimpleBigramsExtractor(final BigramMethodIncompleteSignatureProcessor processor) {
myProcessor = processor;
}
public void addChain(final MethodIncompleteSignatureChain chain) {
if (chain.isEmpty()) {
return;
}
final MethodIncompleteSignature firstInvocation = chain.getFirstInvocation();
assert firstInvocation != null;
final MethodIncompleteSignature head = firstInvocation.isStatic() ? null : myHolder.get(firstInvocation.getOwner());
for (final Bigram<MethodIncompleteSignature> bigram : toBigrams(head, chain)) {
myProcessor.process(bigram);
}
final MethodIncompleteSignature lastInvocation = chain.getLastInvocation();
assert lastInvocation != null;
myHolder.put(lastInvocation.getReturnType(), lastInvocation);
}
private static Collection<Bigram<MethodIncompleteSignature>> toBigrams(final @Nullable MethodIncompleteSignature head,
final @NotNull MethodIncompleteSignatureChain chain) {
MethodIncompleteSignature currentLast = null;
if (head != null) {
currentLast = head;
}
final List<Bigram<MethodIncompleteSignature>> bigrams = new ArrayList<Bigram<MethodIncompleteSignature>>(chain.size());
for (final MethodIncompleteSignature current : chain.list()) {
if (currentLast != null) {
bigrams.add(new Bigram<MethodIncompleteSignature>(currentLast, current));
}
currentLast = current;
}
return bigrams;
}
public interface BigramMethodIncompleteSignatureProcessor {
void process(Bigram<MethodIncompleteSignature> bigram);
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
interface PsiManager {
}
interface PsiElement {
PsiManager getManager();
}
interface PsiClass extends PsiElement {
}
interface PsiMethod extends PsiElement {
PsiClass getContainingClass();
}
interface PsiMethodCallExpression extends PsiElement {
PsiMethod resolveMethod();
}
public class TestCompletion {
public void method() {
PsiManager m = <caret>
}
}
@@ -0,0 +1,39 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
public class TestIndex {
public void statMethod(PsiMethodCallExpression e) {
e.resolveMethod().getContainingClass().getManager();
e.resolveMethod().getContainingClass().getManager();
e.resolveMethod().getContainingClass().getManager();
e.resolveMethod().getContainingClass().getManager();
e.resolveMethod().getContainingClass().getManager();
}
}
interface PsiManager {
}
interface PsiElement {
PsiManager getManager();
}
interface PsiClass extends PsiElement {
}
interface PsiMethod extends PsiElement {
PsiClass getContainingClass();
}
interface PsiMethodCallExpression extends PsiElement {
PsiMethod resolveMethod();
}