diff options
267 files changed, 27062 insertions, 1361 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/.classpath b/org.eclipse.jdt.core.tests.compiler/.classpath index b277a8ac8..01836c484 100644 --- a/org.eclipse.jdt.core.tests.compiler/.classpath +++ b/org.eclipse.jdt.core.tests.compiler/.classpath @@ -2,6 +2,6 @@ <classpath> <classpathentry kind="src" path="src"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/org.eclipse.jdt.core.tests.compiler/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jdt.core.tests.compiler/.settings/org.eclipse.jdt.core.prefs index 1c3392369..9426fc4d4 100644 --- a/org.eclipse.jdt.core.tests.compiler/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jdt.core.tests.compiler/.settings/org.eclipse.jdt.core.prefs @@ -15,9 +15,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -120,6 +120,6 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.compiler.source=1.8 org.eclipse.jdt.core.incompatibleJDKLevel=ignore org.eclipse.jdt.core.incompleteClasspath=error diff --git a/org.eclipse.jdt.core.tests.compiler/META-INF/MANIFEST.MF b/org.eclipse.jdt.core.tests.compiler/META-INF/MANIFEST.MF index dc40e8b47..e99046e54 100644 --- a/org.eclipse.jdt.core.tests.compiler/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.core.tests.compiler/META-INF/MANIFEST.MF @@ -24,7 +24,7 @@ Require-Bundle: org.junit;bundle-version="3.8.1", org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional, org.eclipse.objectteams.otdt Import-Package: org.eclipse.jdt.internal.compiler.apt.dispatch -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Eclipse-BundleShape: dir Bundle-Activator: org.eclipse.jdt.core.tests.compiler.Activator Bundle-ActivationPolicy: lazy diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/SelectionParserTest18.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/SelectionParserTest18.java index 852c395c4..01472ffab 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/SelectionParserTest18.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/SelectionParserTest18.java @@ -17,6 +17,7 @@ import junit.framework.Test; public class SelectionParserTest18 extends AbstractSelectionTest { static { // TESTS_NUMBERS = new int[] { 53 }; +// TESTS_NAMES = new String[] { "test495912" }; } public static Test suite() { return buildMinimalComplianceTestSuite(SelectionParserTest18.class, F_1_8); @@ -249,4 +250,207 @@ public void test476693() throws JavaModelException { expectedReplacedSource, testName); } +public void test495912() { + String string = + "package xy;\n" + + "public class Test {\n" + + " {\n" + + " Runnable r = () -> {\n" + + " Integer i= 1;\n" + + " byte b= i.byteValue();\n" + + " if (true) {\n" + + " if (false) {\n" + + " }\n" + + " }\n" + + " String s= new String();\n" + + " };\n" + + " }\n" + + " public void foo(Runnable r) {\n" + + " }\n" + + "}"; + + String expectedCompletionNodeToString = "<SelectOnMessageSend:i.byteValue()>"; + + String completionIdentifier = "byteValue"; + String expectedUnitDisplayString = + "package xy;\n" + + "public class Test {\n" + + " {\n" + + " Runnable r = () -> {\n" + + " Integer i;\n" + + " byte b = <SelectOnMessageSend:i.byteValue()>;\n" + + " if (true)\n" + + " {\n" + + " if (false)\n" + + " {\n" + + " }\n" + + " }\n" + + " String s;\n" + + " };\n" + + " }\n" + + " public Test() {\n" + + " }\n" + + " public void foo(Runnable r) {\n" + + " }\n" + + "}\n"; + String expectedReplacedSource = "i.byteValue()"; + String testName = "<select>"; + + int selectionStart = string.indexOf("byteValue"); + int selectionEnd = selectionStart + completionIdentifier.length() - 1; + + this.checkMethodParse( + string.toCharArray(), + selectionStart, + selectionEnd, + expectedCompletionNodeToString, + expectedUnitDisplayString, + completionIdentifier, + expectedReplacedSource, + testName); +} +public void test495912a() { + String string = + "package xy;\n" + + "public class Test {\n" + + " {\n" + + " Runnable r = () -> {\n" + + " Integer i= 1;\n" + + " byte b= i.byteValue();\n" + + " if (true) {\n" + + " if (false) {\n" + + " }\n" + + " }\n" + + " for (int i1 = 0; i1 < 42; i1++) {\n" + + " }\n" + + " };\n" + + " }\n" + + " public void foo(Runnable r) {\n" + + " }\n" + + "}"; + + String expectedCompletionNodeToString = "<SelectOnMessageSend:i.byteValue()>"; + + String completionIdentifier = "byteValue"; + String expectedUnitDisplayString = + "package xy;\n" + + "public class Test {\n" + + " {\n" + + " Runnable r = () -> {\n" + + " Integer i;\n" + + " byte b = <SelectOnMessageSend:i.byteValue()>;\n" + + " if (true)\n" + + " {\n" + + " if (false)\n" + + " {\n" + + " }\n" + + " }\n" + + " for (int i1;; (i1 < 42); i1 ++) \n" + + " {\n" + + " }\n" + + " };\n" + + " }\n" + + " public Test() {\n" + + " }\n" + + " public void foo(Runnable r) {\n" + + " }\n" + + "}\n"; + String expectedReplacedSource = "i.byteValue()"; + String testName = "<select>"; + + int selectionStart = string.indexOf("byteValue"); + int selectionEnd = selectionStart + completionIdentifier.length() - 1; + + this.checkMethodParse( + string.toCharArray(), + selectionStart, + selectionEnd, + expectedCompletionNodeToString, + expectedUnitDisplayString, + completionIdentifier, + expectedReplacedSource, + testName); +} +public void test495912b() { + String string = + "package xy;\n" + + "import org.eclipse.e4.ui.internal.workbench.PartServiceImpl;\n" + + "import org.eclipse.e4.ui.model.application.ui.basic.MPart;\n" + + "import org.eclipse.swt.widgets.Table;\n" + + "import org.eclipse.ui.IWorkbenchWindow;\n" + + "public abstract class CycleViewHandler extends CycleBaseHandler {\n" + + " @Override\n" + + " protected void addItems(Table table, WorkbenchPage page) {\n" + + " List<MPart> parts = null;\n" + + " parts.stream().sorted((firstPart, secondPart) -> {\n" + + " Long firstPartActivationTime = (Long) firstPart.getTransientData()\n" + + " .getOrDefault(PartServiceImpl.PART_ACTIVATION_TIME, Long.MIN_VALUE);\n" + + " Long secondPartActivationTime = (Long) secondPart.getTransientData()\n" + + " .getOrDefault(PartServiceImpl.PART_ACTIVATION_TIME, Long.MIN_VALUE);\n" + + " return 0;\n" + + " }).forEach(part -> {\n" + + " if (true) {\n" + + " if (true) {\n" + + " }\n" + + " } \n" + + " else {\n" + + " IWorkbenchWindow iwbw = page.getWorkbenchWindow();\n" + + " if (true){\n" + + " }\n" + + " }\n" + + " });\n" + + " }\n" + + "}"; + + String expectedSelectionNodeToString = "<SelectOnMessageSend:firstPart.getTransientData()>"; + + String selectionIdentifier = "getTransientData"; + String expectedUnitDisplayString = + "package xy;\n" + + "import org.eclipse.e4.ui.internal.workbench.PartServiceImpl;\n" + + "import org.eclipse.e4.ui.model.application.ui.basic.MPart;\n" + + "import org.eclipse.swt.widgets.Table;\n" + + "import org.eclipse.ui.IWorkbenchWindow;\n" + + "public abstract class CycleViewHandler extends CycleBaseHandler {\n" + + " public CycleViewHandler() {\n" + + " }\n" + + " protected @Override void addItems(Table table, WorkbenchPage page) {\n" + + " List<MPart> parts;\n" + // this is the missing part without the fix in RecoveredMethod#add(Block....) + " parts.stream().sorted((<no type> firstPart, <no type> secondPart) -> {\n" + + " Long firstPartActivationTime = (Long) <SelectOnMessageSend:firstPart.getTransientData()>.getOrDefault(PartServiceImpl.PART_ACTIVATION_TIME, Long.MIN_VALUE);\n" + + " Long secondPartActivationTime;\n" + + " return 0;\n" + + "}).forEach((<no type> part) -> {\n" + + " if (true)\n" + + " {\n" + + " if (true)\n" + + " {\n" + + " }\n" + + " }\n" + + " else\n" + + " {\n" + + " IWorkbenchWindow iwbw;\n" + + " if (true)\n" + + " {\n" + + " }\n" + + " }\n" + + "});\n" + + " }\n" + + "}\n"; + String expectedReplacedSource = "firstPart.getTransientData()"; + String testName = "<select>"; + + int selectionStart = string.indexOf("getTransientData"); + int selectionEnd = selectionStart + selectionIdentifier.length() - 1; + + this.checkMethodParse( + string.toCharArray(), + selectionStart, + selectionEnd, + expectedSelectionNodeToString, + expectedUnitDisplayString, + selectionIdentifier, + expectedReplacedSource, + testName); +} } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java index 28b58aa92..fb9c9a273 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java @@ -395,7 +395,11 @@ protected static class JavacTestOptions { } } : null, EclipseBug177715 = RUN_JAVAC ? // https://bugs.eclipse.org/bugs/show_bug.cgi?id=177715 - new EclipseHasABug(MismatchType.JavacErrorsEclipseNone) : null, + new EclipseHasABug(MismatchType.JavacErrorsEclipseNone) { + Excuse excuseFor(JavacCompiler compiler) { + return compiler.compliance < ClassFileConstants.JDK1_8 ? this : null; // in 1.8 rejected by both compilers + } + } : null, EclipseBug207935 = RUN_JAVAC ? // https://bugs.eclipse.org/bugs/show_bug.cgi?id=207935 new EclipseHasABug(MismatchType.EclipseErrorsJavacNone | MismatchType.EclipseWarningsJavacNone) : null, EclipseBug216558 = RUN_JAVAC ? // https://bugs.eclipse.org/bugs/show_bug.cgi?id=216558 diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AmbiguousMethodTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AmbiguousMethodTest.java index 7b65b81ec..41f359c00 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AmbiguousMethodTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AmbiguousMethodTest.java @@ -4362,24 +4362,29 @@ public void test086() { } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=321485 public void test087() { - this.runNegativeTest( - new String[] { - "X.java", - "import java.util.Collection;\n" + - "import java.util.List;\n" + - "public class X {\n" + - " public static <T> List<T> with(List<? extends T> p) { return null; } \n" + - " public static <T> Collection<T> with(Collection<T> p) { return null; }\n" + - " static { with(null); }\n" + - "} \n" - }, - "----------\n" + - "1. ERROR in X.java (at line 6)\n" + - " static { with(null); }\n" + - " ^^^^\n" + - "The method with(List<? extends Object>) is ambiguous for the type X\n" + - "----------\n" - ); + String source = + "import java.util.Collection;\n" + + "import java.util.List;\n" + + "public class X {\n" + + " public static <T> List<T> with(List<? extends T> p) { return null; } \n" + + " public static <T> Collection<T> with(Collection<T> p) { return null; }\n" + + " static { with(null); }\n" + + "} \n"; + if (this.complianceLevel < ClassFileConstants.JDK1_8) { + this.runNegativeTest( // FIXME: Eclipse has a bug + new String[] { "X.java", source }, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " static { with(null); }\n" + + " ^^^^\n" + + "The method with(List<? extends Object>) is ambiguous for the type X\n" + + "----------\n" + ); + } else { + this.runConformTest( + new String[] { "X.java", source } + ); + } } //https://bugs.eclipse.org/bugs/show_bug.cgi?id=354579 public void test088a() { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java index 0d715eed9..ceba32ce8 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java @@ -29841,6 +29841,9 @@ public void test0909() { //https://bugs.eclipse.org/bugs/show_bug.cgi?id=127583 public void test0910() { + int[] capIds = this.complianceLevel < ClassFileConstants.JDK1_8 + ? new int[]{ 1, 3, 4, 6, 13} + : new int[]{ 1, 2, 3, 4, 8}; this.runNegativeTest( new String[] { "X.java", @@ -29893,12 +29896,12 @@ public void test0910() { "4. ERROR in X.java (at line 13)\n" + " lc1 = lc3; //2 ko\n" + " ^^^\n" + - "Type mismatch: cannot convert from List<capture#1-of ? extends Collection<?>> to List<Collection>\n" + + "Type mismatch: cannot convert from List<capture#"+capIds[0]+"-of ? extends Collection<?>> to List<Collection>\n" + "----------\n" + "5. ERROR in X.java (at line 14)\n" + " lc1 = lc4; //3 ko\n" + " ^^^\n" + - "Type mismatch: cannot convert from List<capture#3-of ? extends Collection> to List<Collection>\n" + + "Type mismatch: cannot convert from List<capture#"+capIds[1]+"-of ? extends Collection> to List<Collection>\n" + "----------\n" + "6. ERROR in X.java (at line 15)\n" + " lc2 = lc1; //4 ko\n" + @@ -29908,12 +29911,12 @@ public void test0910() { "7. ERROR in X.java (at line 16)\n" + " lc2 = lc3; //5 ko\n" + " ^^^\n" + - "Type mismatch: cannot convert from List<capture#4-of ? extends Collection<?>> to List<Collection<?>>\n" + + "Type mismatch: cannot convert from List<capture#"+capIds[2]+"-of ? extends Collection<?>> to List<Collection<?>>\n" + "----------\n" + "8. ERROR in X.java (at line 17)\n" + " lc2 = lc4; //6 ko\n" + " ^^^\n" + - "Type mismatch: cannot convert from List<capture#6-of ? extends Collection> to List<Collection<?>>\n" + + "Type mismatch: cannot convert from List<capture#"+capIds[3]+"-of ? extends Collection> to List<Collection<?>>\n" + "----------\n" + "9. ERROR in X.java (at line 18)\n" + " lc3 = lc1; //7 ko\n" + @@ -29923,7 +29926,7 @@ public void test0910() { "10. ERROR in X.java (at line 20)\n" + " lc3 = lc4; //9 ko\n" + " ^^^\n" + - "Type mismatch: cannot convert from List<capture#13-of ? extends Collection> to List<? extends Collection<?>>\n" + + "Type mismatch: cannot convert from List<capture#"+capIds[4]+"-of ? extends Collection> to List<? extends Collection<?>>\n" + "----------\n" + "11. WARNING in X.java (at line 25)\n" + " private final List<Collection> aList = new ArrayList<Collection>();\n" + @@ -38756,11 +38759,7 @@ public void test1117() throws Exception { } //https://bugs.eclipse.org/bugs/show_bug.cgi?id=177715 public void test1118() { - runConformTest( - // test directory preparation - new String[] { /* test files */ - "X.java", - "import java.util.List;\n" + + String source = "import java.util.List;\n" + "\n" + "public class X {\n" + " X() {\n" + @@ -38771,10 +38770,21 @@ public void test1118() { " <I, T extends List<I>> T foo(Class<T> pClass) {\n" + " return null;\n" + " }\n" + - "}\n", // ================= - }, - // javac options - JavacTestOptions.EclipseHasABug.EclipseBug177715 /* javac test options */); + "}\n"; + if (this.complianceLevel < ClassFileConstants.JDK1_8) { + runConformTest( + new String[] { "X.java", source }, + JavacTestOptions.EclipseHasABug.EclipseBug177715 /* javac test options */); + } else { + runNegativeTest( + new String[] { "X.java", source }, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " foo(cls);\n" + + " ^^^\n" + + "The method foo(Class<T>) in the type X is not applicable for the arguments (Class<capture#1-of ? extends List<?>>)\n" + + "----------\n"); + } } //https://bugs.eclipse.org/bugs/show_bug.cgi?id=169728 public void test1119() { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java index 80a24af02..8375b7bda 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java @@ -5921,5 +5921,97 @@ public void testBug499048() { "}\n" }); } +public void testBug499126() { + runConformTest( + new String[] { + "bug_ise_immutablelist/$Immutable.java", + "package bug_ise_immutablelist;\n" + + "\n" + + "public class $Immutable<T> {\n" + + "}\n" + + "", + "bug_ise_immutablelist/Test.java", + "package bug_ise_immutablelist;\n" + + "\n" + + "public class Test {\n" + + " public static $Immutable<Object> f;\n" + + "}\n" + + "", + } + ); + runConformTest( + false, + new String[] { + "Usage.java", + "public class Usage {\n" + + " Object f() {return bug_ise_immutablelist.Test.f;}\n" + + "}\n" + + "", + }, + null, + null, + null, + null + ); +} +public void testBug441905() { + runConformTest( + new String[] { + "EclipseJava8Generics.java", + "import java.util.List;\n" + + "\n" + + "public class EclipseJava8Generics {\n" + + "\n" + + " public interface Foo<V> {\n" + + " }\n" + + "\n" + + " public static class FooBar<V, T extends Foo<V>> {\n" + + " }\n" + + "\n" + + " protected void doFoos(List<FooBar<?, ? extends Foo<?>>> fooBars) {\n" + + " FooBar<?, ? extends Foo<?>> fooBar = fooBars.iterator().next();\n" + + " doFoo(fooBar);\n" + + " }\n" + + "\n" + + " protected static <F> void doFoo(FooBar<F, ? extends Foo<F>> fooBar) {\n" + + " }\n" + + "\n" + + "}\n" + }); +} +public void testBug469297() { + String source = " import java.util.List;\n" + + " \n" + + " public class Test {\n" + + " \n" + + " static final void a(Class<? extends List<?>> type) {\n" + + " b(newList(type));\n" + + " }\n" + + " \n" + + " static final <T> List<T> b(List<T> list) {\n" + + " return list;\n" + + " }\n" + + " \n" + + " static final <L extends List<?>> L newList(Class<L> type) {\n" + + " try {\n" + + " return type.newInstance();\n" + + " }\n" + + " catch (Exception e) {\n" + + " throw new RuntimeException(e);\n" + + " }\n" + + " }\n" + + " }\n"; + if (this.complianceLevel < ClassFileConstants.JDK1_8) { + runConformTest(new String[] { "Test.java", source }); + } else { + runNegativeTest(new String[] { "Test.java", source }, + "----------\n" + + "1. ERROR in Test.java (at line 6)\n" + + " b(newList(type));\n" + + " ^\n" + + "The method b(List<T>) in the type Test is not applicable for the arguments (capture#1-of ? extends List<?>)\n" + + "----------\n"); + } +} } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java index f25e40f5c..e53c45149 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java @@ -6886,4 +6886,73 @@ public void testBug497239() { } ); } +public void testBug472851() { + runNegativeTest( + new String[] { + "Test.java", + "import java.util.*;\n" + + "\n" + + "public class Test {\n" + + " public static void main(String... arg) {\n" + + " List<Integer> l1=Arrays.asList(0, 1, 2);\n" + + " List<String> l2=Arrays.asList(\"0\", \"1\", \"2\");\n" + + " a(Arrays.asList(l1, l2));\n" + + "}\n" + + "static final void a(List<? extends List<?>> type) {\n" + + " test(type);\n" + + "}\n" + + "static final <Y,L extends List<Y>> void test(List<L> type) {\n" + + " L l1=type.get(0), l2=type.get(1);\n" + + " l2.set(0, l1.get(0));\n" + + "}\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in Test.java (at line 10)\n" + + " test(type);\n" + + " ^^^^\n" + + "The method test(List<L>) in the type Test is not applicable for the arguments (List<capture#1-of ? extends List<?>>)\n" + + "----------\n"); +} +public void testBug502350() { + runNegativeTest( + new String[] { + "makeCompilerFreeze/EclipseJava8Bug.java", + "package makeCompilerFreeze;\n" + + "\n" + + "interface Comparable<E> {} \n" + + "\n" + + "interface Comparator<A> {\n" + + " public static <B extends Comparable<B>> Comparator<B> naturalOrder() {\n" + + " return null;\n" + + " }\n" + + "}\n" + + "\n" + + "\n" + + "class Stuff {\n" + + " public static <T, S extends T> Object func(Comparator<T> comparator) {\n" + + " return null;\n" + + " }\n" + + "}\n" + + "\n" + + "public class EclipseJava8Bug {\n" + + " static final Object BORKED =\n" + + " Stuff.func(Comparator.naturalOrder());\n" + + "}\n" + + "\n" + + "", + }, + "----------\n" + + "1. ERROR in makeCompilerFreeze\\EclipseJava8Bug.java (at line 20)\n" + + " Stuff.func(Comparator.naturalOrder());\n" + + " ^^^^\n" + + "The method func(Comparator<T>) in the type Stuff is not applicable for the arguments (Comparator<Comparable<Comparable<B>>>)\n" + + "----------\n" + + "2. ERROR in makeCompilerFreeze\\EclipseJava8Bug.java (at line 20)\n" + + " Stuff.func(Comparator.naturalOrder());\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Type mismatch: cannot convert from Comparator<Comparable<Comparable<B>>> to Comparator<T>\n" + + "----------\n" + ); +} } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java index 98d0c5fd5..74b2de1f0 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LambdaExpressionsTest.java @@ -6182,6 +6182,111 @@ public void test476859a() { "private static java.lang.reflect.Method Test.lambda$0(java.lang.Void)\n" + "private java.lang.reflect.Method AnotherClass.lambda$0(java.lang.Void)"); } +public void testBug499258() { + runConformTest( + new String[] { + "bug499258/ShellTab.java", + "package bug499258;\n" + + "class Controller {\n" + + " public void newTerminal(Object... path) {\n" + + " }\n" + + "}\n" + + "\n" + + "interface EventHandler {\n" + + " void handle();\n" + + "}\n" + + "\n" + + "public class ShellTab {\n" + + " private final Controller controller;\n" + + "\n" + + " public ShellTab(Controller controller) {\n" + + " this.controller = controller;\n" + + " EventHandler h = this.controller::newTerminal;\n" + + " }\n" + + "}\n" + + "", + } + ); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=500374 Using a method reference to a generic method in a Base class gives me NoSuchMethodError +public void test500374() { + this.runConformTest( + new String[] { + "client/Client.java", + "package client;\n" + + "import lib.Sub;\n" + + "public class Client {\n" + + " public static void main(String[] args) throws Throwable {\n" + + " Sub s1 = new Sub();\n" + + " doSomething(() -> s1.m());\n" + + " doSomething(s1::m);\n" + + " }\n" + + " interface Aaa {\n" + + " Object f() throws Throwable;\n" + + " }\n" + + " public static void doSomething(Aaa a) throws Throwable {\n" + + " System.out.println(\"Done\");\n" + + " }\n" + + "}\n", + "lib/Sub.java", + "package lib;\n" + + "public class Sub extends Base<Sub> {}", + "lib/Base.java", + "package lib;\n" + + "class Base<T> {\n" + + " public T m() {\n" + + " System.out.println(\"m\");\n" + + " return thisInstance();\n" + + " }\n" + + " @SuppressWarnings(\"unchecked\")\n" + + " T thisInstance() {\n" + + " return (T) this;\n" + + " }\n" + + "}" + }, + "Done\n" + + "Done"); +} +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=500374 Using a method reference to a generic method in a Base class gives me NoSuchMethodError +public void test500374a() { + this.runConformTest( + new String[] { + "client/Client.java", + "package client;\n" + + "import java.lang.invoke.MethodHandle;\n" + + "import java.lang.invoke.MethodHandles;\n" + + "import java.lang.invoke.MethodType;\n" + + "import lib.Sub;\n" + + "public class Client {\n" + + " public static void main(String[] args) throws Throwable {\n" + + " MethodHandle mh = MethodHandles.lookup().findVirtual(Sub.class, \"m\", MethodType.methodType(Object.class));\n" + + " doSomething(mh::invoke);\n" + + " }\n" + + " interface Aaa {\n" + + " Object f() throws Throwable;\n" + + " }\n" + + " public static void doSomething(Aaa a) throws Throwable {\n" + + " System.out.println(\"Done\");\n" + + " }\n" + + "}\n", + "lib/Sub.java", + "package lib;\n" + + "public class Sub extends Base<Sub> {}", + "lib/Base.java", + "package lib;\n" + + "class Base<T> {\n" + + " public T m() {\n" + + " System.out.println(\"m\");\n" + + " return thisInstance();\n" + + " }\n" + + " @SuppressWarnings(\"unchecked\")\n" + + " T thisInstance() {\n" + + " return (T) this;\n" + + " }\n" + + "}" + }, + "Done"); +} public static Class testClass() { return LambdaExpressionsTest.class; } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java index 4f090c060..9e52dad32 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/MethodVerifyTest.java @@ -14300,4 +14300,31 @@ public void testBug467776_regression() { }, compilerOptions); } +public void testBug500673() { + runNegativeTest( + new String[] { + "mfi.java", + "interface mfi {\n" + + " public transient void a(Throwable throwable);\n" + + "}\n", + "mfa.java", + "final class mfa implements mfi {\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in mfi.java (at line 2)\n" + + " public transient void a(Throwable throwable);\n" + + " ^^^^^^^^^^^^^^^^^^^^^^\n" + + (this.complianceLevel < ClassFileConstants.JDK1_8 + ? "Illegal modifier for the interface method a; only public & abstract are permitted\n" + : "Illegal modifier for the interface method a; only public, abstract, default, static and strictfp are permitted\n" + ) + + "----------\n" + + "----------\n" + + "1. ERROR in mfa.java (at line 1)\n" + + " final class mfa implements mfi {\n" + + " ^^^\n" + + "The type mfa must implement the inherited abstract method mfi.a(Throwable)\n" + + "----------\n"); +} } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java index 14d4d1960..d2c889178 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java @@ -17974,4 +17974,181 @@ public void testBug486912PotNullInLoop() { "----------\n" ); } +public void testBug447695() { + runConformTest( + new String[] { + "test/Test447695.java", + "package test;\n" + + "\n" + + "public class Test447695 {\n" + + " public static void f() {\n" + + " int[] array = null;\n" + + " (array = new int[1])[0] = 42;\n" + + " }\n" + + " public static int g() {\n" + + " int[] array = null;\n" + + " return (array = new int[1])[0];\n" + + " }\n" + + "}\n" + } + ); +} +public void testBug447695b() { + if (this.complianceLevel < ClassFileConstants.JDK1_5) return; // uses foreach + runConformTest( + new String[] { + "X.java", + "import java.util.*;\n" + + "public class X {\n" + + " void test(String[] ss) {\n" + + " List<String> strings = null;\n" + + " for (String s : (strings = Arrays.asList(ss)))\n" + + " System.out.println(s);\n" + + " }\n" + + "}\n" + }); +} +public void testBug447695c() { + if (this.complianceLevel < ClassFileConstants.JDK1_5) return; // uses autoboxing + runConformTest( + new String[] { + "test/Test447695.java", + "package test;\n" + + "\n" + + "public class Test447695 {\n" + + " void f() {\n" + + " Integer l1 = null;\n" + + " Integer l2 = null;\n" + + " int b = (l1 = new Integer(2)) + (l2 = new Integer(1));\n" + + " }\n" + + "}\n" + } + ); +} +public void testBug447695d() { + if (this.complianceLevel < ClassFileConstants.JDK1_8) return; // uses reference expression + runConformTest( + new String[] { + "test/Test447695.java", + "package test;\n" + + "\n" + + "import java.util.function.Supplier;\n" + + "\n" + + "public class Test447695 {\n" + + " void f() {\n" + + " String s = null;\n" + + " Supplier<String> l = (s = \"\")::toString;\n" + + " }\n" + + "}\n" + } + ); +} +public void testBug447695e() { + if (this.complianceLevel < ClassFileConstants.JDK1_5) return; // uses autoboxing + runConformTest( + new String[] { + "test/Test447695.java", + "package test;\n" + + "\n" + + "public class Test447695 {\n" + + " void f() {\n" + + " Integer i = null;\n" + + " int j = -(i = new Integer(1));\n" + + " Boolean b1 = null;\n" + + " boolean b = !(b1 = new Boolean(false));\n" + + " }\n" + + "}\n" + } + ); +} +public void testBug447695f() { + if (this.complianceLevel < ClassFileConstants.JDK1_5) return; // uses autoboxing + runConformTest( + new String[] { + "test/Test447695.java", + "package test;\n" + + "\n" + + "public class Test447695 {\n" + + " void f() {\n" + + " int i = 0;\n" + + " Integer i1 = null;\n" + + " Integer i2 = null;\n" + + " Integer i3 = null;\n" + + " int j = (i1 = new Integer(1)) \n" + + " + (i2 = new Integer(1)) \n" + + " + i + i + i + i + i + i + i + i + i + i + i + i + i + i + i + i + i + i + i \n" + + " + (i3 = new Integer(2)) + i;\n" + + " }\n" + + "}\n" + } + ); +} +public void testBug447695g() { + runNegativeTest( + new String[] { + "test/Test447695.java", + "package test;\n" + + "\n" + + "class X {\n" + + " int i;\n" + + "}\n" + + "\n" + + "public class Test447695 {\n" + + " void f() {\n" + + " X x1 = null;\n" + + " X x2 = null;\n" + + " X x3 = null;\n" + + " X x4 = null;\n" + + " X x5 = null;\n" + + " X x6 = null;\n" + + " X x7 = null;\n" + + " X x8 = null;\n" + + " X x9 = null;\n" + + " X x10 = null;\n" + + " X x11 = null;\n" + + " x1.i = 1; // error 1 expected\n" + + " x2.i += 1; // error 2 expected\n" + + " (x3).i = 1; // error 3 expected\n" + + " (x4).i += 1; // error 4 expected\n" + + " (x5 = new X()).i = (x6 = new X()).i;\n" + + " (x7 = new X()).i += (x8 = new X()).i;\n" + + " int i1 = x9.i; // error 5 expected\n" + + " int i2 = (x10).i; // error 6 expected\n" + + " int i3 = (x11 = new X()).i;\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in test\\Test447695.java (at line 20)\n" + + " x1.i = 1; // error 1 expected\n" + + " ^^\n" + + "Null pointer access: The variable x1 can only be null at this location\n" + + "----------\n" + + "2. ERROR in test\\Test447695.java (at line 21)\n" + + " x2.i += 1; // error 2 expected\n" + + " ^^\n" + + "Null pointer access: The variable x2 can only be null at this location\n" + + "----------\n" + + "3. ERROR in test\\Test447695.java (at line 22)\n" + + " (x3).i = 1; // error 3 expected\n" + + " ^^^^\n" + + "Null pointer access: The variable x3 can only be null at this location\n" + + "----------\n" + + "4. ERROR in test\\Test447695.java (at line 23)\n" + + " (x4).i += 1; // error 4 expected\n" + + " ^^^^\n" + + "Null pointer access: The variable x4 can only be null at this location\n" + + "----------\n" + + "5. ERROR in test\\Test447695.java (at line 26)\n" + + " int i1 = x9.i; // error 5 expected\n" + + " ^^\n" + + "Null pointer access: The variable x9 can only be null at this location\n" + + "----------\n" + + "6. ERROR in test\\Test447695.java (at line 27)\n" + + " int i2 = (x10).i; // error 6 expected\n" + + " ^^^^^\n" + + "Null pointer access: The variable x10 can only be null at this location\n" + + "----------\n" + ); +} }
\ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java index 5db5078e2..4cb5285dc 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java @@ -12226,6 +12226,101 @@ public void testBug489245() { "----------\n" ); } +public void testBug489674() { + Map options = new HashMap<>(getCompilerOptions()); + options.put(JavaCore.COMPILER_NONNULL_ANNOTATION_SECONDARY_NAMES, "org.foo.NonNull"); + options.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_SECONDARY_NAMES, "org.foo.Nullable"); + runConformTest( + new String[] { + "org/foo/Nullable.java", + "package org.foo;\n" + + "import java.lang.annotation.*;\n" + + "import static java.lang.annotation.ElementType.*;\n" + + "@Retention(RetentionPolicy.CLASS)\n" + + "@Target({ FIELD, METHOD, PARAMETER, LOCAL_VARIABLE })\n" + + "public @interface Nullable {}\n", + "org/foo/NonNull.java", + "package org.foo;\n" + + "import java.lang.annotation.*;\n" + + "import static java.lang.annotation.ElementType.*;\n" + + "@Retention(RetentionPolicy.CLASS)\n" + + "@Target({ FIELD, METHOD, PARAMETER, LOCAL_VARIABLE })\n" + + "public @interface NonNull {}\n", + "}" + }, + options); + runConformTestWithLibs( + new String[] { + "with_other_nullable/P1.java", + "package with_other_nullable;\n" + + "\n" + + "import org.foo.Nullable;\n" + + "\n" + + "public class P1 {\n" + + " public static @Nullable String f0() {\n" + + " return null;\n" + + " }\n" + + "\n" + + " public static <T> T check(T t) {\n" + + " return t;\n" + + " }\n" + + "}\n" + + "", + "with_other_nullable/P2.java", + "package with_other_nullable;\n" + + "\n" + + "import org.foo.NonNull;\n" + + "\n" + + "public class P2 {\n" + + " public static void f(@NonNull String s) {\n" + + " }\n" + + "\n" + + " public static <T> T check(T t) {\n" + + " return t;\n" + + " }\n" + + "}\n" + + "", + }, + options, + "" + ); + runNegativeTestWithLibs( + new String[] { + "test/Test4.java", + "package test;\n" + + "\n" + + "import org.eclipse.jdt.annotation.NonNullByDefault;\n" + + "\n" + + "import with_other_nullable.P1;\n" + + "import with_other_nullable.P2;\n" + + "\n" + + "@NonNullByDefault\n" + + "public class Test4 {\n" + + " void m1(String s) {\n" + + " P1.f0().hashCode();\n" + + " s = P1.check(s);\n" + + " }\n" + + " void m2(String s) {\n" + + " P2.f(null);\n" + + " s = P2.check(s);\n" + + " }\n" + + "}\n" + + "", + }, + options, + "----------\n" + + "1. ERROR in test\\Test4.java (at line 11)\n" + + " P1.f0().hashCode();\n" + + " ^^^^^^^\n" + + "Potential null pointer access: The method f0() may return null\n" + + "----------\n" + + "2. ERROR in test\\Test4.java (at line 15)\n" + + " P2.f(null);\n" + + " ^^^^\n" + + "Null type mismatch: required \'String\' but the provided value is null\n" + + "----------\n" + ); +} public void testBug492327() { runConformTestWithLibs( new String[] { @@ -12651,4 +12746,166 @@ public void testBug492322withGenericBase() { "" ); } +public void testBug499862a() { + runConformTestWithLibs( + new String[] { + "Test.java", + "import org.eclipse.jdt.annotation.*;\n" + + "import java.util.*;\n" + + "public class Test {\n" + + " static void printChecked(Collection<? extends @Nullable String> collection) {\n" + + " for(String s : collection)\n" + + " if (s != null)\n" + + " System.out.println(s.toString());\n" + + " else\n" + + " System.out.println(\"NULL\");\n" + + " }\n" + + "}\n" + }, + getCompilerOptions(), + ""); +} +public void testBug499862b() { + runNegativeTestWithLibs( + new String[] { + "Test.java", + "import org.eclipse.jdt.annotation.*;\n" + + "import java.util.*;\n" + + "public class Test {\n" + + " static void printChecked(Collection<? extends @Nullable String> collection) {\n" + + " for(String s : collection)\n" + + " System.out.println(s.toString());\n" + + " }\n" + + "}\n" + }, + getCompilerOptions(), + "----------\n" + + "1. ERROR in Test.java (at line 6)\n" + + " System.out.println(s.toString());\n" + + " ^\n" + + "Potential null pointer access: The variable s may be null at this location\n" + + "----------\n"); +} +public void testBug499862c() { + runNegativeTestWithLibs( + new String[] { + "Test.java", + "import java.util.*;\n" + + "public class Test {\n" + + " static <T> void printUnchecked(Collection<T> collection) {\n" + + " for(T t : collection)\n" + + " System.out.println(t.toString());\n" + + " }\n" + + "}\n" + }, + getCompilerOptions(), + "----------\n" + + "1. ERROR in Test.java (at line 5)\n" + + " System.out.println(t.toString());\n" + + " ^\n" + + "Potential null pointer access: this expression has type \'T\', a free type variable that may represent a \'@Nullable\' type\n" + + "----------\n"); +} +public void testBug499597simplified() { + runConformTestWithLibs( + new String[] { + "Foo2.java", + "import org.eclipse.jdt.annotation.NonNull;\n" + + "import org.eclipse.jdt.annotation.NonNullByDefault;\n" + + "\n" + + "@NonNullByDefault\n" + + "class Foo2 {\n" + + " static <T> T of(T t) {\n" + + " return t;\n" + + " }\n" + + "\n" + + " static String foo() {\n" + + " return Foo2.<String>of(\"\"); // <-- warning here\n" + + " }\n" + + "\n" + + " static String bar() {\n" + + " return Foo2.<@NonNull String>of(\"\"); // <-- no warning\n" + + " }\n" + + "}\n" + + "", + }, + getCompilerOptions(), + "" + ); +} +public void testBug499597original() { + runNegativeTestWithLibs( + new String[] { + "Foo.java", + "import static org.eclipse.jdt.annotation.DefaultLocation.*;\n" + + "import org.eclipse.jdt.annotation.*;\n" + + "\n" + + "import java.util.Collection;\n" + + "import java.util.Collections;\n" + + "\n" + + "class Foo {\n" + + " static @NonNull String @NonNull [] X = { \"A\" };\n" + + "\n" + + " @NonNullByDefault({ PARAMETER, RETURN_TYPE, FIELD, TYPE_PARAMETER, TYPE_BOUND, TYPE_ARGUMENT, ARRAY_CONTENTS })\n" + + " @SafeVarargs\n" + + " static <T> Collection<T> of(@NonNull T @NonNull... elements) {\n" + + " return Collections.singleton(elements[0]);\n" + + " }\n" + + "\n" + + " @NonNullByDefault({ PARAMETER, RETURN_TYPE, FIELD, TYPE_PARAMETER, TYPE_BOUND, TYPE_ARGUMENT, ARRAY_CONTENTS })\n" + + " static Collection<String[]> foo() {\n" + + " return Foo.<String[]>of(X); // <-- warning here\n" + + " }\n" + + "\n" + + " @NonNullByDefault({ PARAMETER, RETURN_TYPE, FIELD, TYPE_PARAMETER, TYPE_BOUND, TYPE_ARGUMENT, ARRAY_CONTENTS })\n" + + " static Collection<String[]> bar() {\n" + + " return Foo.<String @NonNull []>of(X); // <-- no warning\n" + + " }\n" + + "}\n" + + "", + }, + getCompilerOptions(), + "----------\n" + + "1. WARNING in Foo.java (at line 12)\n" + + " static <T> Collection<T> of(@NonNull T @NonNull... elements) {\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "The nullness annotation is redundant with a default that applies to this location\n" + + "----------\n" + + "2. WARNING in Foo.java (at line 13)\n" + + " return Collections.singleton(elements[0]);\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Null type safety (type annotations): The expression of type \'Set<T>\' needs unchecked conversion to conform to \'@NonNull Collection<T>\', corresponding supertype is \'Collection<T>\'\n" + + "----------\n" + ); +} +public void testBug501449() { + runNegativeTestWithLibs( + new String[] { + "Test.java", + "import org.eclipse.jdt.annotation.Nullable;\n" + + "\n" + + "public class Test {\n" + + " <T, S extends T> void f(T[] objects, @Nullable T nullableValue, T value, S subclassValue) {\n" + + " objects[0] = null;\n" + + " objects[1] = nullableValue;\n" + + " objects[2] = value;\n" + + " objects[3] = subclassValue;\n" + + " }\n" + + "}\n" + + "", + }, + getCompilerOptions(), + "----------\n" + + "1. ERROR in Test.java (at line 5)\n" + + " objects[0] = null;\n" + + " ^^^^^^^^^^\n" + + "Null type mismatch (type annotations): \'null\' is not compatible to the free type variable \'T\'\n" + + "----------\n" + + "2. ERROR in Test.java (at line 6)\n" + + " objects[1] = nullableValue;\n" + + " ^^^^^^^^^^\n" + + "Null type mismatch (type annotations): required \'T\' but this expression has type \'@Nullable T\', where \'T\' is a free type variable\n" + + "----------\n" + ); +} } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PackageBindingTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PackageBindingTest.java index 827e53d39..f848e19c0 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PackageBindingTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PackageBindingTest.java @@ -29,8 +29,9 @@ public class PackageBindingTest extends AbstractCompilerTest /** * This test checks if it is searched for packages before searching for types. * The search for packages is much faster than searching for types, therefore it should get executed before searching for types. + * Commented since reverted to original behaviour as per bug 495598 */ - public void test01() { + public void _test01() { NameEnvironmentDummy nameEnv = new NameEnvironmentDummy(true); PackageBinding packageBinding = new PackageBinding(new LookupEnvironment(null, new CompilerOptions(), null, nameEnv)); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SerializableLambdaTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SerializableLambdaTest.java index 18e554d21..30541d602 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SerializableLambdaTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SerializableLambdaTest.java @@ -2158,6 +2158,47 @@ public class SerializableLambdaTest extends AbstractRegressionTest { null,true, new String[]{"-Ddummy"}); } + public void testbug503118() { + this.runConformTest( + new String[]{ + "lambdabug/App.java", + "package lambdabug;\n" + + "import java.io.ByteArrayInputStream;\n" + + "import java.io.ByteArrayOutputStream;\n" + + "import java.io.ObjectInputStream;\n" + + "import java.io.ObjectOutputStream;\n" + + "import java.io.Serializable;\n" + + "import java.util.function.Function;\n" + + "public class App {\n" + + " public static interface SerialFunction<T, R> extends Function<T, R>, Serializable {\n" + + " }\n" + + " public static interface TestInterface extends Serializable {\n" + + " public Integer method(Integer i);\n" + + " }\n" + + " public static class TestClass implements TestInterface {\n" + + " private static final long serialVersionUID = 1L;\n" + + " @Override\n" + + " public Integer method(Integer i) {\n" + + " return i;\n" + + " }\n" + + " }\n" + + " public static void main(String[] args) throws Exception {\n" + + " TestInterface testService = getService();\n" + + " SerialFunction<Integer, Integer> sf = testService::method;\n" + + " ByteArrayOutputStream bos = new ByteArrayOutputStream();\n" + + " new ObjectOutputStream(bos).writeObject(sf);\n" + + " Object o = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())).readObject();\n" + + " System.out.println(o.getClass().getInterfaces()[0]);\n" + + " }\n" + + " private static TestInterface getService() {\n" + + " return new TestClass();\n" + + " }\n" + + "}\n" + }, + "interface lambdabug.App$SerialFunction", + null,true, + new String[]{"-Ddummy"}); + } // --- private void checkExpected(String expected, String actual) { diff --git a/org.eclipse.jdt.core.tests.model/.classpath b/org.eclipse.jdt.core.tests.model/.classpath index f4a5c796a..00351188c 100644 --- a/org.eclipse.jdt.core.tests.model/.classpath +++ b/org.eclipse.jdt.core.tests.model/.classpath @@ -1,6 +1,6 @@ <classpath> <classpathentry kind="src" path="src"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/org.eclipse.jdt.core.tests.model/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jdt.core.tests.model/.settings/org.eclipse.jdt.core.prefs index 69354e766..0f7284181 100644 --- a/org.eclipse.jdt.core.tests.model/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jdt.core.tests.model/.settings/org.eclipse.jdt.core.prefs @@ -15,9 +15,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -119,6 +119,6 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.compiler.source=1.8 org.eclipse.jdt.core.incompatibleJDKLevel=ignore org.eclipse.jdt.core.incompleteClasspath=error diff --git a/org.eclipse.jdt.core.tests.model/META-INF/MANIFEST.MF b/org.eclipse.jdt.core.tests.model/META-INF/MANIFEST.MF index 175264914..7a26b38c7 100644 --- a/org.eclipse.jdt.core.tests.model/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.core.tests.model/META-INF/MANIFEST.MF @@ -25,7 +25,7 @@ Require-Bundle: org.eclipse.core.resources;bundle-version="[3.2.0,4.0.0)", org.eclipse.core.filesystem;bundle-version="[1.2.0,2.0.0)", org.eclipse.jdt.annotation;bundle-version="[1.1.0,2.0.0)";resolution:=optional, org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Eclipse-BundleShape: dir Bundle-Activator: org.eclipse.jdt.core.tests.Activator Bundle-ActivationPolicy: lazy diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/Activator.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/Activator.java index defb6d04c..2d221996e 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/Activator.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/Activator.java @@ -16,21 +16,20 @@ import org.eclipse.core.runtime.Status; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; -/** - * Make the PackageAdmin service accessible to tests. - * - * @deprecated uses deprecated class PackageAdmin. - */ @SuppressWarnings({"rawtypes", "unchecked"}) public class Activator extends Plugin { private static final String PLUGIN_ID = "org.eclipse.jdt.core.tests.model"; + /** + * @deprecated uses deprecated class PackageAdmin. + */ static org.osgi.service.packageadmin.PackageAdmin packageAdmin = null; static Plugin instance; + @SuppressWarnings("deprecation") public void start(BundleContext context) throws Exception { ServiceReference ref= context.getServiceReference(org.osgi.service.packageadmin.PackageAdmin.class.getName()); @@ -46,6 +45,11 @@ public class Activator extends Plugin { // nothing } + /** + * Make the PackageAdmin service accessible to tests. + * + * @deprecated uses deprecated class PackageAdmin. + */ public static org.osgi.service.packageadmin.PackageAdmin getPackageAdmin() { return packageAdmin; } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/APIDocumentationTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/APIDocumentationTests.java index d59474add..25368a365 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/APIDocumentationTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/APIDocumentationTests.java @@ -141,7 +141,7 @@ public void testJavaCoreAPI() throws CoreException, IllegalArgumentException, Il // fetch JavaCore source file // 1. attempt: workspace relative location in project org.eclipse.jdt.core: - @SuppressWarnings("deprecation")Bundle bundle = org.eclipse.jdt.core.tests.Activator.getInstance().getBundle(); + Bundle bundle = org.eclipse.jdt.core.tests.Activator.getInstance().getBundle(); URL url = bundle.getEntry("/"); IPath path = new Path(FileLocator.toFileURL(url).getPath()); path = path.removeLastSegments(1).append(ORG_ECLIPSE_JDT_CORE); diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterBugsTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterBugsTests.java index bf98fb74e..c0b797ccc 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterBugsTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterBugsTests.java @@ -12499,4 +12499,124 @@ public void testBug497245b() { "}"; formatSource(source); } +/** + * https://bugs.eclipse.org/500443 - [formatter] NPE on block comment before 'force-wrap' element + */ +public void testBug500443() { + this.formatterPrefs.alignment_for_enum_constants = Alignment.M_ONE_PER_LINE_SPLIT + Alignment.M_FORCE; + this.formatterPrefs.alignment_for_superclass_in_type_declaration = Alignment.M_ONE_PER_LINE_SPLIT + Alignment.M_FORCE; + String source = + "public class SomeClass\n" + + " /* */ extends\n" + + " Object {\n" + + " enum MyEnum {\n" + + " /* 1 */ ONE\n" + + " }\n" + + "}"; + formatSource(source); +} +/** + * https://bugs.eclipse.org/500092 - [formatter] Blank lines at beginning of method body doesn't work in constructors + */ +public void testBug500092() { + this.formatterPrefs.blank_lines_at_beginning_of_method_body = 1; + String source = + "public class Test {\n" + + " public Test() { int a; }\n" + + "}"; + formatSource(source, + "public class Test {\n" + + " public Test() {\n" + + "\n" + + " int a;\n" + + " }\n" + + "}" + ); +} +/** + * https://bugs.eclipse.org/500135 - [formatter] 'Parenthesis positions' ignores single member annotations + */ +public void testBug500135() { + this.formatterPrefs.parenthesis_positions_in_annotation = DefaultCodeFormatterConstants.SEPARATE_LINES; + String source = + "@SomeAnnotation(\n" + + " \"some value\"\n" + + ")\n" + + "public class Test {\n" + + "}"; + formatSource(source); +} +/** + * https://bugs.eclipse.org/500096 - [formatter] Indent declarations within enum declaration doesn't affect enum constants + */ +public void testBug500096a() { + this.formatterPrefs.indent_body_declarations_compare_to_enum_declaration_header = false; + String source = + "public enum Test {\n" + + "AAA, BBB;\n" + + "Test() {\n" + + "}\n" + + "}"; + formatSource(source); +} +/** + * https://bugs.eclipse.org/500096 - [formatter] Indent declarations within enum declaration doesn't affect enum constants + */ +public void testBug500096b() { + this.formatterPrefs.indent_body_declarations_compare_to_enum_declaration_header = false; + this.formatterPrefs.alignment_for_enum_constants = Alignment.M_COMPACT_SPLIT + Alignment.M_INDENT_BY_ONE; + String source = + "public enum Test {\n" + + " AAA, BBB;\n" + + "Test() {\n" + + "}\n" + + "}"; + formatSource(source); +} +/** + * https://bugs.eclipse.org/500093 - [formatter] AssertionError with 'Next line on wrap' for array initializers + */ +public void testBug500093() { + this.formatterPrefs.brace_position_for_array_initializer = DefaultCodeFormatterConstants.NEXT_LINE_ON_WRAP; + this.formatterPrefs.page_width = 60; + this.formatterPrefs.use_tabs_only_for_leading_indentations = true; + String source = + "public class SomeClass {\n" + + " void foo() {\n" + + " Arrays.asList(new String[] { \"ddd\", \"eee\", \"fff\" });\n" + + " Arrays.asList(new String[] { \"a\", \"b\", \"c\" },\n" + + " new String[]\n" + + " { \"a\", \"b\", \"c\", });\n" + + " Arrays.asList(//\n" + + " new String[]\n" + + " { \"ddd\", \"eee\", \"fff\" });\n" + + " Arrays.asList(\n" + + " new String[]\n" + + " { \"eedd\", \"eee\", \"fff\" });\n" + + " Arrays.asList(\n" + + " new String[]\n" + + " { \"aa\", \"bb\", \"cc\", \"dd\", \"ee\", \"ff\", \"gg\",\n" + + " \"hh\", \"ii\" });\n" + + " String[][] test = { { \"aaaaaa\", \"bbbbb\", \"ccccc\" },\n" + + " { \"aaaa\", \"bb\", \"ccc\" } };\n" + + " test[123456 //\n" + + " * (234567 + 345678 + 456789 - 567890\n" + + " - 678901)] = new String[]\n" + + " { \"a\", \"b\", \"c\" };\n" + + " }\n" + + "}"; + formatSource(source); +} +/** + * https://bugs.eclipse.org/500853 - [Formatter] java code formatter doesn't honour new parentheses settings + */ +public void testBug500853() { + this.formatterPrefs.parenthesis_positions_in_method_declaration = new String(DefaultCodeFormatterConstants.PRESERVE_POSITIONS); + String source = + "public class SomeClass {\n" + + " void foo() {\n" + + " }\n" + + "}"; + formatSource(source); +} } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterCommentsTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterCommentsTests.java index b67a696c4..9edcdc6bd 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterCommentsTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterCommentsTests.java @@ -76,20 +76,6 @@ public void setUpSuite() throws Exception { super.setUpSuite(); } -/** - * Reset the jar placeholder and delete project. - */ -public void tearDownSuite() throws Exception { - if (ALL_TEST_SUITES == null) { - deleteProject(JAVA_PROJECT); //$NON-NLS-1$ - } else { - ALL_TEST_SUITES.remove(getClass()); - if (ALL_TEST_SUITES.size() == 0) { - deleteProject(JAVA_PROJECT); //$NON-NLS-1$ - } - } -} - void compareFormattedSource(ICompilationUnit compilationUnit) throws JavaModelException { DefaultCodeFormatter codeFormatter = codeFormatter(); String source = compilationUnit.getSource(); diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java index a04e27998..2659d9960 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -32,11 +32,13 @@ import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.core.ClasspathEntry; import org.eclipse.jdt.internal.core.JavaCorePreferenceInitializer; import org.eclipse.jdt.internal.core.JavaElement; +import org.eclipse.jdt.internal.core.JavaElementDelta; import org.eclipse.jdt.internal.core.JavaModelManager; import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.NameLookup; import org.eclipse.jdt.internal.core.ResolvedSourceMethod; import org.eclipse.jdt.internal.core.ResolvedSourceType; +import org.eclipse.jdt.internal.core.nd.indexer.Indexer; import org.eclipse.jdt.internal.core.search.BasicSearchEngine; import org.eclipse.jdt.internal.core.util.Util; @@ -238,10 +240,17 @@ public abstract class AbstractJavaModelTests extends SuiteOfTestCases { public synchronized String stackTraces() { return this.stackTraces.toString(); } + public synchronized String toString() { - StringBuffer buffer = new StringBuffer(); - for (int i=0, length= this.deltas.length; i<length; i++) { + StringBuilder buffer = new StringBuilder(); + for (int i = 0, length= this.deltas.length; i < length; i++) { IJavaElementDelta delta = this.deltas[i]; + if (((JavaElementDelta) delta).ignoreFromTests) { + continue; + } + if (buffer.length() != 0) { + buffer.append("\n\n"); + } IJavaElementDelta[] children = delta.getAffectedChildren(); int childrenLength=children.length; IResourceDelta[] resourceDeltas = delta.getResourceDeltas(); @@ -250,28 +259,23 @@ public abstract class AbstractJavaModelTests extends SuiteOfTestCases { buffer.append(delta); } else { sortDeltas(children); - for (int j=0; j<childrenLength; j++) { - buffer.append(children[j]); - if (j != childrenLength-1) { - buffer.append("\n"); + for (int j = 0; j < childrenLength; j++) { + if (buffer.length() != 0 && buffer.charAt(buffer.length() - 1) != '\n') { + buffer.append('\n'); } + buffer.append(children[j]); } - for (int j=0; j<resourceDeltasLength; j++) { - if (j == 0 && buffer.length() != 0) { - buffer.append("\n"); + for (int j = 0; j < resourceDeltasLength; j++) { + if (buffer.length() != 0 && buffer.charAt(buffer.length() - 1) != '\n') { + buffer.append('\n'); } buffer.append(resourceDeltas[j]); - if (j != resourceDeltasLength-1) { - buffer.append("\n"); - } } } - if (i != length-1) { - buffer.append("\n\n"); - } } return buffer.toString(); } + public void waitForResourceDelta() { long start = System.currentTimeMillis(); while (!this.gotResourceDelta) { @@ -2422,6 +2426,7 @@ public abstract class AbstractJavaModelTests extends SuiteOfTestCases { protected void refreshExternalArchives(IJavaProject p) throws JavaModelException { waitForAutoBuild(); // ensure that the auto-build job doesn't interfere with external jar refreshing getJavaModel().refreshExternalArchives(new IJavaElement[] {p}, null); + Indexer.getInstance().waitForIndex(null); } protected void removeJavaNature(String projectName) throws CoreException { @@ -3139,6 +3144,7 @@ public abstract class AbstractJavaModelTests extends SuiteOfTestCases { do { try { Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, null); + Indexer.getInstance().waitForIndex(null); wasInterrupted = false; } catch (OperationCanceledException e) { e.printStackTrace(); @@ -3153,6 +3159,7 @@ public abstract class AbstractJavaModelTests extends SuiteOfTestCases { do { try { Job.getJobManager().join(ResourcesPlugin.FAMILY_MANUAL_REFRESH, null); + Indexer.getInstance().waitForIndex(null); wasInterrupted = false; } catch (OperationCanceledException e) { e.printStackTrace(); @@ -3167,6 +3174,7 @@ public abstract class AbstractJavaModelTests extends SuiteOfTestCases { SearchEngine engine = new SearchEngine(); IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); try { + Indexer.getInstance().waitForIndex(null); engine.searchAllTypeNames( null, SearchPattern.R_EXACT_MATCH, diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java index 6c82e92a4..24aaeeee1 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -16,6 +16,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.eclipse.jdt.core.tests.junit.extension.TestCase; +import org.eclipse.jdt.core.tests.nd.RunIndexTests; import junit.framework.Test; import junit.framework.TestSuite; @@ -57,6 +58,9 @@ private static Class[] getAllTestClasses() { // Java search tests RunJavaSearchTests.class, + // Testss for the new index + RunIndexTests.class, + // Working copy tests WorkingCopyTests.class, WorkingCopyNotInClasspathTests.class, diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AttachedJavadocTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AttachedJavadocTests.java index 9376aa60e..203b2bcd2 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AttachedJavadocTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AttachedJavadocTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -260,20 +260,7 @@ public class AttachedJavadocTests extends ModifyingResourceTests { try { IClasspathEntry[] entries = this.project.getRawClasspath(); savedEntries = entries.clone(); - IResource resource = this.project.getProject().findMember("/doc.zip"); //$NON-NLS-1$ - assertNotNull("doc folder cannot be null", resource); //$NON-NLS-1$ - URI locationURI = resource.getLocationURI(); - assertNotNull("doc folder cannot be null", locationURI); //$NON-NLS-1$ - URL docUrl = null; - try { - docUrl = locationURI.toURL(); - } catch (MalformedURLException e) { - assertTrue("Should not happen", false); //$NON-NLS-1$ - } catch(IllegalArgumentException e) { - assertTrue("Should not happen", false); //$NON-NLS-1$ - } - final String path = "jar:" + docUrl.toExternalForm() + "!/doc"; //$NON-NLS-1$ //$NON-NLS-2$ - //final String path = "jar:" + "platform:/resource/AttachedJavadocProject/doc.zip" + "!/doc"; + final String path = "jar:" + "platform:/resource/AttachedJavadocProject/doc.zip" + "!/doc"; IClasspathAttribute attribute = JavaCore.newClasspathAttribute(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, path); for (int i = 0, max = entries.length; i < max; i++) { final IClasspathEntry entry = entries[i]; @@ -1315,5 +1302,27 @@ public class AttachedJavadocTests extends ModifyingResourceTests { assertTrue("Should not happen", false); } } + public void testBug499196() throws JavaModelException { + try { + IClasspathAttribute attribute = + JavaCore.newClasspathAttribute( + IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, + "jar:platform:/resource/AttachedJavadocProject/bug499196_doc.zip!/"); + IClasspathEntry newEntry = JavaCore.newLibraryEntry(new Path("/AttachedJavadocProject/bug499196.jar"), null, null, null, new IClasspathAttribute[] {attribute}, true); + this.project.setRawClasspath(new IClasspathEntry[]{newEntry}, null); + this.project.getResolvedClasspath(false); + + IPackageFragmentRoot jarRoot = this.project.getPackageFragmentRoot(getFile("/AttachedJavadocProject/bug499196.jar")); + final IType type = jarRoot.getPackageFragment("p1.p2").getClassFile("Bug499196.class").getType(); + assertNotNull(type); + IMethod method = type.getMethod("Bug499196", new String[] {}); //$NON-NLS-1$ + assertNotNull("Constructor should not be null", method); + assertTrue(method.exists()); + String javadoc = method.getAttachedJavadoc(new NullProgressMonitor()); //$NON-NLS-1$ + assertNotNull("Should have a javadoc", javadoc); //$NON-NLS-1$ + } catch (IndexOutOfBoundsException e) { + assertTrue("Should not happen", false); + } + } } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompilationUnitTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompilationUnitTests.java index 2a52510a6..3c431dd94 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompilationUnitTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompilationUnitTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2012 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -2580,6 +2580,34 @@ public void testBug246594a() throws CoreException { typeParameters[0].getBoundsSignatures()); } - - +public void testBug495598_001() throws CoreException { + try { + createFolder("/P/src/a/b/C"); + createFile("/P/src/a/b/C/readme.txt", "This is not a Java file"); + + createFile( + "/P/src/a/b/C.java", + "package a.b;\n" + + "public class C{};\n" + ); + + createFile("/P/src/X.java", + "import a.b.C;\n" + + "public class X {}\n"); + ICompilationUnit cuD = getCompilationUnit("/P/src/X.java"); + + ASTParser parser = ASTParser.newParser(AST.JLS8); + parser.setProject(this.testProject); + parser.setSource(cuD); + parser.setResolveBindings(true); + org.eclipse.jdt.core.dom.CompilationUnit cuAST = (org.eclipse.jdt.core.dom.CompilationUnit) parser.createAST(null); + IProblem[] problems = cuAST.getProblems(); + assertEquals("Should have 1 problem", 1, problems.length); + assertEquals("Should have only an unused warning", "The import a.b.C is never used", problems[0].getMessage()); + } finally { + deleteFile("/P/src/X.java"); + deleteFile("/P/src/a/b/C.java"); + deleteFolder("/P/src/a"); + } +} } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java index 2be5e055d..7a5ecdaac 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests.java @@ -20676,11 +20676,11 @@ public void test276526a() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); // In the absence of the fix, it would have been: - // "IBar[TYPE_REF]{IBar, test, Ltest.IBar;, null, 44}\n" + - // "IFoo[TYPE_REF]{IFoo, test, Ltest.IFoo;, null, 44}" + // "IBar[TYPE_REF]{IBar, test, Ltest.IBar;, null, " + (R_DEFAULT + 39) + "}\n" + + // "IFoo[TYPE_REF]{IFoo, test, Ltest.IFoo;, null, " + (R_DEFAULT + 39) + "}" this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "IBar[TYPE_REF]{IBar, test, Ltest.IBar;, null, 44}", + "IBar[TYPE_REF]{IBar, test, Ltest.IBar;, null, " + (R_DEFAULT + 39) + "}", requestor.getResults()); } @@ -20712,8 +20712,8 @@ public void test276526b() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( // In the absence of the fix, it would also complete to Interface1: - //"Interface1[TYPE_REF]{Interface1, test, Ltest.Interface1;, null, 44}\n"+ - "Interface2[TYPE_REF]{Interface2, test, Ltest.Interface2;, null, 44}", + //"Interface1[TYPE_REF]{Interface1, test, Ltest.Interface1;, null, " + (R_DEFAULT + 39) + "}\n"+ + "Interface2[TYPE_REF]{Interface2, test, Ltest.Interface2;, null, " + (R_DEFAULT + 39) + "}", requestor.getResults()); } @@ -20743,7 +20743,7 @@ public void test276526c() throws JavaModelException { assertResults( // In the absence of the fix, it would suggest both p.Enclosing.Interface2, and // p.Enclosing.Interface1 while it should have suppressed the latter which is in use. - "Enclosing.Interface2[TYPE_REF]{p.Enclosing.Interface2, p, Lp.Enclosing$Interface2;, null, 44}", + "Enclosing.Interface2[TYPE_REF]{p.Enclosing.Interface2, p, Lp.Enclosing$Interface2;, null, " + (R_DEFAULT + 39) + "}", requestor.getResults()); } @@ -20778,8 +20778,8 @@ public void test276526d() throws JavaModelException { assertResults( // In the absence of the fix, it would suggest only p.Enclosing.Interface2, as it // was wrongly suppressing p.Enclosing.Interface1 confusing it with p.Interface1. - "Enclosing.Interface1[TYPE_REF]{p.Enclosing.Interface1, p, Lp.Enclosing$Interface1;, null, 44}\n" + - "Enclosing.Interface2[TYPE_REF]{p.Enclosing.Interface2, p, Lp.Enclosing$Interface2;, null, 44}", + "Enclosing.Interface1[TYPE_REF]{p.Enclosing.Interface1, p, Lp.Enclosing$Interface1;, null, " + (R_DEFAULT + 39) + "}\n" + + "Enclosing.Interface2[TYPE_REF]{p.Enclosing.Interface2, p, Lp.Enclosing$Interface2;, null, " + (R_DEFAULT + 39) + "}", requestor.getResults()); } @@ -21182,9 +21182,9 @@ public void testBug292087() throws JavaModelException { assertResults( // without the fix no proposals obtained. - "Tr[POTENTIAL_METHOD_DECLARATION]{Tr, Ltest.Try;, ()V, Tr, null, 14}\n" + - "transient[KEYWORD]{transient, null, null, transient, null, 14}\n" + - "Try[TYPE_REF]{Try, test, Ltest.Try;, null, null, 27}", + "Tr[POTENTIAL_METHOD_DECLARATION]{Tr, Ltest.Try;, ()V, Tr, null, " + (R_DEFAULT + 9) + "}\n" + + "transient[KEYWORD]{transient, null, null, transient, null, " + (R_DEFAULT + 9) + "}\n" + + "Try[TYPE_REF]{Try, test, Ltest.Try;, null, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } @@ -21220,10 +21220,10 @@ public void testBug249704() throws JavaModelException { assertResults( // without the fix no proposals obtained. - "Error[TYPE_REF]{Error, java.lang, Ljava.lang.Error;, null, null, 17}\n" + - "Exception[TYPE_REF]{Exception, java.lang, Ljava.lang.Exception;, null, null, 17}\n" + - "equals[METHOD_REF]{Try.this.equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 24}\n" + - "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 27}", + "Error[TYPE_REF]{Error, java.lang, Ljava.lang.Error;, null, null, " + (R_DEFAULT + 12) + "}\n" + + "Exception[TYPE_REF]{Exception, java.lang, Ljava.lang.Exception;, null, null, " + (R_DEFAULT + 12) + "}\n" + + "equals[METHOD_REF]{Try.this.equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), " + (R_DEFAULT + 19) + "}\n" + + "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), " + (R_DEFAULT + 22) + "}", requestor.getResults()); } @@ -21322,7 +21322,7 @@ public void testBug308980a() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "AClass[TYPE_REF]{AClass, test, Ltest.AClass;, null, null, 27}", + "AClass[TYPE_REF]{AClass, test, Ltest.AClass;, null, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } @@ -21366,7 +21366,7 @@ public void testBug308980b() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "AClass[TYPE_REF]{AClass, test, Ltest.AClass;, null, null, 27}", + "AClass[TYPE_REF]{AClass, test, Ltest.AClass;, null, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } @@ -21396,10 +21396,10 @@ public void testBug267091a() throws JavaModelException { assertResults( // without the fix no proposals obtained. - "Inn.Inn2[TYPE_REF]{test.Inn.Inn2, test, Ltest.Inn$Inn2;, null, null, 44}\n" + - "ABC.ABCInterface[TYPE_REF]{ABCInterface, test, Ltest.ABC$ABCInterface;, null, null, 47}\n" + - "In[TYPE_REF]{In, test, Ltest.In;, null, null, 47}\n" + - "Inn[TYPE_REF]{Inn, test, Ltest.Inn;, null, null, 47}", + "Inn.Inn2[TYPE_REF]{test.Inn.Inn2, test, Ltest.Inn$Inn2;, null, null, " + (R_DEFAULT + 39) + "}\n" + + "ABC.ABCInterface[TYPE_REF]{ABCInterface, test, Ltest.ABC$ABCInterface;, null, null, " + (R_DEFAULT + 42) + "}\n" + + "In[TYPE_REF]{In, test, Ltest.In;, null, null, " + (R_DEFAULT + 42) + "}\n" + + "Inn[TYPE_REF]{Inn, test, Ltest.Inn;, null, null, " + (R_DEFAULT + 42) + "}", requestor.getResults()); } @@ -21430,11 +21430,11 @@ public void testBug267091b() throws JavaModelException { assertResults( // without the fix no proposals obtained. - "Inn.Inn2[TYPE_REF]{test.Inn.Inn2, test, Ltest.Inn$Inn2;, null, null, 44}\n" + - "Inn.Inn2.Inn3[TYPE_REF]{test.Inn.Inn2.Inn3, test, Ltest.Inn$Inn2$Inn3;, null, null, 44}\n" + - "ABC[TYPE_REF]{ABC, test, Ltest.ABC;, null, null, 47}\n" + - "In[TYPE_REF]{In, test, Ltest.In;, null, null, 47}\n" + - "Inn[TYPE_REF]{Inn, test, Ltest.Inn;, null, null, 47}", + "Inn.Inn2[TYPE_REF]{test.Inn.Inn2, test, Ltest.Inn$Inn2;, null, null, " + (R_DEFAULT + 39) + "}\n" + + "Inn.Inn2.Inn3[TYPE_REF]{test.Inn.Inn2.Inn3, test, Ltest.Inn$Inn2$Inn3;, null, null, " + (R_DEFAULT + 39) + "}\n" + + "ABC[TYPE_REF]{ABC, test, Ltest.ABC;, null, null, " + (R_DEFAULT + 42) + "}\n" + + "In[TYPE_REF]{In, test, Ltest.In;, null, null, " + (R_DEFAULT + 42) + "}\n" + + "Inn[TYPE_REF]{Inn, test, Ltest.Inn;, null, null, " + (R_DEFAULT + 42) + "}", requestor.getResults()); } @@ -21463,9 +21463,9 @@ public void testBug310747() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - int relevance1 = R_RESOLVED + R_INTERESTING + R_CASE + R_NON_STATIC + R_EXACT_EXPECTED_TYPE; - int relevance2 = R_RESOLVED + R_INTERESTING + R_CASE + R_NON_STATIC; - int relevance3 = R_RESOLVED + R_CASE + R_NON_STATIC; + int relevance1 = R_DEFAULT + 52; + int relevance2 = R_DEFAULT + 22; + int relevance3 = R_DEFAULT + 17; int start1 = str.lastIndexOf("/**/") + "".length(); int end1 = start1 + "/**/".length(); assertResults( @@ -21620,7 +21620,7 @@ public void testBug310427b() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "myVar1[LOCAL_VARIABLE_REF]{myVar1, null, I, myVar1, null, 57}", + "myVar1[LOCAL_VARIABLE_REF]{myVar1, null, I, myVar1, null, " + (R_DEFAULT + 52) + "}", requestor.getResults()); } @@ -21942,10 +21942,10 @@ public void test325481() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "MyClass[TYPE_REF]{mypackage.MyClass, mypackage, Lmypackage.MyClass;, null, null, " + (R_NON_STATIC + R_UNQUALIFIED) + "}\n" + - "mypackage[PACKAGE_REF]{mypackage, mypackage, null, null, null, " + (R_NON_STATIC + R_UNQUALIFIED + R_CASE) + "}\n" + - "myString[FIELD_REF]{myString, Ltest.X;, Ljava.lang.String;, myString, null, " + (R_NON_STATIC + R_UNQUALIFIED + R_CASE + R_NON_RESTRICTED) + "}\n" + - "myString2[FIELD_REF]{myString2, Ltest.X;, Ljava.lang.String;, myString2, null, " + (R_NON_STATIC + R_UNQUALIFIED + R_CASE + R_NON_RESTRICTED) + "}", + "MyClass[TYPE_REF]{mypackage.MyClass, mypackage, Lmypackage.MyClass;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "mypackage[PACKAGE_REF]{mypackage, mypackage, null, null, null, " + (R_DEFAULT + 19) + "}\n" + + "myString[FIELD_REF]{myString, Ltest.X;, Ljava.lang.String;, myString, null, " + (R_DEFAULT + 22) + "}\n" + + "myString2[FIELD_REF]{myString2, Ltest.X;, Ljava.lang.String;, myString2, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=312603 @@ -21969,9 +21969,9 @@ public void test312603() throws JavaModelException { this.wcOwner); assertResults( - "MyClass[TYPE_REF]{mypackage.MyClass, mypackage, Lmypackage.MyClass;, null, null, 14}\n" + - "mypackage[PACKAGE_REF]{mypackage, mypackage, null, null, null, 24}\n" + - "myString[FIELD_REF]{myString, Ltest.X;, Ljava.lang.String;, myString, null, 57}", + "MyClass[TYPE_REF]{mypackage.MyClass, mypackage, Lmypackage.MyClass;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "mypackage[PACKAGE_REF]{mypackage, mypackage, null, null, null, " + (R_DEFAULT + 19) + "}\n" + + "myString[FIELD_REF]{myString, Ltest.X;, Ljava.lang.String;, myString, null, " + (R_DEFAULT + 52) + "}", requestor.getResults()); } @@ -22001,10 +22001,10 @@ public void test328674a() throws JavaModelException { this.wcOwner); assertResults( - "MyClass[TYPE_REF]{mypackage.MyClass, mypackage, Lmypackage.MyClass;, null, null, " + (R_NON_STATIC + R_UNQUALIFIED) + "}\n" + - "mypackage[PACKAGE_REF]{mypackage, mypackage, null, null, null, " + (R_NON_STATIC + R_UNQUALIFIED + R_CASE) + "}\n" + - "myString[LOCAL_VARIABLE_REF]{myString, null, Ljava.lang.String;, myString, null, " + (R_NON_STATIC + R_UNQUALIFIED + R_CASE + R_NON_RESTRICTED + R_EXACT_EXPECTED_TYPE) + "}\n" + - "myString2[LOCAL_VARIABLE_REF]{myString2, null, Ljava.lang.String;, myString2, null, " + (R_NON_STATIC + R_UNQUALIFIED + R_CASE + R_NON_RESTRICTED + R_EXACT_EXPECTED_TYPE) + "}", + "MyClass[TYPE_REF]{mypackage.MyClass, mypackage, Lmypackage.MyClass;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "mypackage[PACKAGE_REF]{mypackage, mypackage, null, null, null, " + (R_DEFAULT + 19) + "}\n" + + "myString[LOCAL_VARIABLE_REF]{myString, null, Ljava.lang.String;, myString, null, " + (R_DEFAULT + 52) + "}\n" + + "myString2[LOCAL_VARIABLE_REF]{myString2, null, Ljava.lang.String;, myString2, null, " + (R_DEFAULT + 52) + "}", requestor.getResults()); } @@ -22034,10 +22034,10 @@ public void test328674b() throws JavaModelException { this.wcOwner); assertResults( - "MyClass[TYPE_REF]{mypackage.MyClass, mypackage, Lmypackage.MyClass;, null, null, " + (R_NON_STATIC + R_UNQUALIFIED) + "}\n" + - "mypackage[PACKAGE_REF]{mypackage, mypackage, null, null, null, " + (R_NON_STATIC + R_UNQUALIFIED + R_CASE) + "}\n" + - "myString[LOCAL_VARIABLE_REF]{myString, null, Ljava.lang.String;, myString, null, " + (R_NON_STATIC + R_UNQUALIFIED + R_CASE + R_NON_RESTRICTED + R_EXACT_EXPECTED_TYPE) + "}\n" + - "myString1[LOCAL_VARIABLE_REF]{myString1, null, Ljava.lang.String;, myString1, null, " + (R_NON_STATIC + R_UNQUALIFIED + R_CASE + R_NON_RESTRICTED + R_EXACT_EXPECTED_TYPE) + "}", + "MyClass[TYPE_REF]{mypackage.MyClass, mypackage, Lmypackage.MyClass;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "mypackage[PACKAGE_REF]{mypackage, mypackage, null, null, null, " + (R_DEFAULT + 19) + "}\n" + + "myString[LOCAL_VARIABLE_REF]{myString, null, Ljava.lang.String;, myString, null, " + (R_DEFAULT + 52) + "}\n" + + "myString1[LOCAL_VARIABLE_REF]{myString1, null, Ljava.lang.String;, myString1, null, " + (R_DEFAULT + 52) + "}", requestor.getResults()); } @@ -22058,7 +22058,7 @@ public void test325481b() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); cu.codeComplete(cursorLocation, requestor); - int relevance = R_INTERFACE + R_UNQUALIFIED + R_NON_RESTRICTED; + int relevance = R_DEFAULT + 21; assumeEquals( "should have two completions", "element:ADD_CUSTOM_ATTRIBUTES completion:ADD_CUSTOM_ATTRIBUTES relevance:" + relevance +"\n" + @@ -23979,7 +23979,7 @@ public void testBug401487a() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "def[POTENTIAL_METHOD_DECLARATION]{def, Ltest.P;, ()V, def, null, 14}\n" + "default[KEYWORD]{default, null, null, default, null, 24}" , + "def[POTENTIAL_METHOD_DECLARATION]{def, Ltest.P;, ()V, def, null, " + (R_DEFAULT + 9) + "}\n" + "default[KEYWORD]{default, null, null, default, null, " + (R_DEFAULT + 19) + "}" , requestor.getResults()); } finally { // Restore compliance settings. @@ -24043,7 +24043,7 @@ public void testBug401487c() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "def[POTENTIAL_METHOD_DECLARATION]{def, Ltest.P;, ()V, def, null, 14}" , + "def[POTENTIAL_METHOD_DECLARATION]{def, Ltest.P;, ()V, def, null, " + (R_DEFAULT + 9) + "}" , requestor.getResults()); } finally { // Restore compliance settings. @@ -24109,7 +24109,7 @@ public void testBug401487e() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "def[POTENTIAL_METHOD_DECLARATION]{def, Ltest.ZZ$I;, ()V, def, null, 14}\n" + "default[KEYWORD]{default, null, null, default, null, 24}" , + "def[POTENTIAL_METHOD_DECLARATION]{def, Ltest.ZZ$I;, ()V, def, null, " + (R_DEFAULT + 9) + "}\n" + "default[KEYWORD]{default, null, null, default, null, " + (R_DEFAULT + 19) + "}" , requestor.getResults()); } finally { // Restore compliance settings. @@ -24573,10 +24573,10 @@ public void testBug351444() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, monitor); assertResults( - "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<TT;>;, (Ljava.lang.String;Ljava.lang.String;)V, TXYU, (s, s2), 30}\n" + - " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, 30}\n" + - "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<TT;>;, (TT;)V, TXYU, (t), 30}\n" + - " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, 30}", + "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<TT;>;, (Ljava.lang.String;Ljava.lang.String;)V, TXYU, (s, s2), " + (R_DEFAULT + 25) + "}\n" + + " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, " + (R_DEFAULT + 25) + "}\n" + + "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<TT;>;, (TT;)V, TXYU, (t), " + (R_DEFAULT + 25) + "}\n" + + " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, " + (R_DEFAULT + 25) + "}", requestor.getResults()); assertEquals(true, requestor.canUseDiamond(0)); @@ -24616,10 +24616,10 @@ public void testBug351444a() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, monitor); assertResults( - "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<TT;>;, (Ljava.lang.String;Ljava.lang.String;)V, TXYU, (s, s2), 30}\n" + - " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, 30}\n" + - "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<TT;>;, (TT;)V, TXYU, (t), 30}\n" + - " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, 30}", + "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<TT;>;, (Ljava.lang.String;Ljava.lang.String;)V, TXYU, (s, s2), " + (R_DEFAULT + 25) + "}\n" + + " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, " + (R_DEFAULT + 25) + "}\n" + + "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<TT;>;, (TT;)V, TXYU, (t), " + (R_DEFAULT + 25) + "}\n" + + " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, " + (R_DEFAULT + 25) + "}", requestor.getResults()); assertEquals(false, requestor.canUseDiamond(1)); @@ -24662,10 +24662,10 @@ public void testBug351444b() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, monitor); assertResults( - "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.Test<Ljava.lang.Object;>.TXYU;, (Ljava.lang.String;Ljava.lang.String;)V, TXYU, (s, s2), 30}\n" + - " Test.TXYU[TYPE_REF]{TXYU, test, Ltest.Test$TXYU;, null, null, 30}\n" + - "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.Test<Ljava.lang.Object;>.TXYU;, (TT;)V, TXYU, (t), 30}\n" + - " Test.TXYU[TYPE_REF]{TXYU, test, Ltest.Test$TXYU;, null, null, 30}", + "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.Test<Ljava.lang.Object;>.TXYU;, (Ljava.lang.String;Ljava.lang.String;)V, TXYU, (s, s2), " + (R_DEFAULT + 25) + "}\n" + + " Test.TXYU[TYPE_REF]{TXYU, test, Ltest.Test$TXYU;, null, null, " + (R_DEFAULT + 25) + "}\n" + + "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.Test<Ljava.lang.Object;>.TXYU;, (TT;)V, TXYU, (t), " + (R_DEFAULT + 25) + "}\n" + + " Test.TXYU[TYPE_REF]{TXYU, test, Ltest.Test$TXYU;, null, null, " + (R_DEFAULT + 25) + "}", requestor.getResults()); assertEquals(true, requestor.canUseDiamond(0)); @@ -24708,10 +24708,10 @@ public void testBug351444c() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, monitor); assertResults( - "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.Test<Ljava.lang.Object;>.TXYU;, (Ljava.lang.String;Ljava.lang.String;)V, TXYU, (s, s2), 30}\n" + - " Test.TXYU[TYPE_REF]{TXYU, test, Ltest.Test$TXYU;, null, null, 30}\n" + - "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.Test<Ljava.lang.Object;>.TXYU;, (TT;)V, TXYU, (t), 30}\n" + - " Test.TXYU[TYPE_REF]{TXYU, test, Ltest.Test$TXYU;, null, null, 30}", + "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.Test<Ljava.lang.Object;>.TXYU;, (Ljava.lang.String;Ljava.lang.String;)V, TXYU, (s, s2), " + (R_DEFAULT + 25) + "}\n" + + " Test.TXYU[TYPE_REF]{TXYU, test, Ltest.Test$TXYU;, null, null, " + (R_DEFAULT + 25) + "}\n" + + "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.Test<Ljava.lang.Object;>.TXYU;, (TT;)V, TXYU, (t), " + (R_DEFAULT + 25) + "}\n" + + " Test.TXYU[TYPE_REF]{TXYU, test, Ltest.Test$TXYU;, null, null, " + (R_DEFAULT + 25) + "}", requestor.getResults()); assertEquals(false, requestor.canUseDiamond(1)); @@ -24757,10 +24757,10 @@ public void testBug351444d() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, monitor); assertResults( - "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<LNumber;>;, (LNumber;)V, TXYU, (t), 60}\n" + - " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, 60}\n" + - "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<LNumber;>;, (Ljava.lang.String;Ljava.lang.String;)V, TXYU, (s, s2), 60}\n" + - " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, 60}", + "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<LNumber;>;, (LNumber;)V, TXYU, (t), " + (R_DEFAULT + 55) + "}\n" + + " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, " + (R_DEFAULT + 55) + "}\n" + + "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<LNumber;>;, (Ljava.lang.String;Ljava.lang.String;)V, TXYU, (s, s2), " + (R_DEFAULT + 55) + "}\n" + + " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, " + (R_DEFAULT + 55) + "}", requestor.getResults()); assertEquals(false, requestor.canUseDiamond(0)); @@ -24806,10 +24806,10 @@ public void testBug351444e() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, monitor); assertResults( - "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<LNumber;>;, (LNumber;)V, TXYU, (t), 60}\n" + - " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, 60}\n" + - "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<LNumber;>;, (Ljava.lang.String;Ljava.lang.String;)V, TXYU, (s, s2), 60}\n" + - " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, 60}", + "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<LNumber;>;, (LNumber;)V, TXYU, (t), " + (R_DEFAULT + 55) + "}\n" + + " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, " + (R_DEFAULT + 55) + "}\n" + + "TXYU[CONSTRUCTOR_INVOCATION]{(), Ltest.TXYU<LNumber;>;, (Ljava.lang.String;Ljava.lang.String;)V, TXYU, (s, s2), " + (R_DEFAULT + 55) + "}\n" + + " TXYU[TYPE_REF]{TXYU, test, Ltest.TXYU;, null, null, " + (R_DEFAULT + 55) + "}", requestor.getResults()); assertEquals(true, requestor.canUseDiamond(1)); @@ -24850,11 +24850,11 @@ public void testBug292087b() throws JavaModelException { "expectedTypesKeys={Ltest/Try~MyClass;}", requestor.getContext()); assertResults( - "mypackage[PACKAGE_REF]{mypackage, mypackage, null, null, null, " + (R_NON_STATIC + R_UNQUALIFIED) + "}\n" + - "MyClass[TYPE_REF]{mypackage.MyClass, mypackage, Lmypackage.MyClass;, null, null, " + (R_NON_STATIC + R_UNQUALIFIED + R_CASE) + "}\n" + - "MyClass[TYPE_REF]{MyClass, test, Ltest.MyClass;, null, null, " + (R_NON_STATIC + R_UNQUALIFIED + R_CASE + R_NON_RESTRICTED + R_EXACT_EXPECTED_TYPE) + "}\n" + - "MyClassField[FIELD_REF]{MyClassField, Ltest.Try;, Ltest.MyClass;, MyClassField, null, " + (R_NON_STATIC + R_UNQUALIFIED + R_CASE + R_NON_RESTRICTED + R_EXACT_EXPECTED_TYPE) + "}\n" + - "MyClassMethod[METHOD_REF]{MyClassMethod(), Ltest.Try;, ()Ltest.MyClass;, MyClassMethod, null, " + (R_NON_STATIC + R_UNQUALIFIED + R_CASE + R_NON_RESTRICTED + R_EXACT_EXPECTED_TYPE) + "}", + "mypackage[PACKAGE_REF]{mypackage, mypackage, null, null, null, " + (R_DEFAULT + 9) + "}\n" + + "MyClass[TYPE_REF]{mypackage.MyClass, mypackage, Lmypackage.MyClass;, null, null, " + (R_DEFAULT + 19) + "}\n" + + "MyClass[TYPE_REF]{MyClass, test, Ltest.MyClass;, null, null, " + (R_DEFAULT + 52) + "}\n" + + "MyClassField[FIELD_REF]{MyClassField, Ltest.Try;, Ltest.MyClass;, MyClassField, null, " + (R_DEFAULT + 52) + "}\n" + + "MyClassMethod[METHOD_REF]{MyClassMethod(), Ltest.Try;, ()Ltest.MyClass;, MyClassMethod, null, " + (R_DEFAULT + 52) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=292087 @@ -24888,21 +24888,21 @@ public void testBug292087c() throws JavaModelException { "expectedTypesKeys={Ltest/Try~MyClass;}", requestor.getContext()); assertResults( - "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_VOID) + "}\n" + - "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_VOID) + "}\n" + - "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_VOID) + "}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_VOID) + "}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_VOID) + "}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_VOID) + "}\n" + - "Try[TYPE_REF]{Try, test, Ltest.Try;, null, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS) + "}\n" + - "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS) + "}\n" + - "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS) + "}\n" + - "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class;, getClass, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS) + "}\n" + - "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS) + "}\n" + - "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS) + "}\n" + - "MyClass[TYPE_REF]{MyClass, test, Ltest.MyClass;, null, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_EXACT_EXPECTED_TYPE) + "}\n" + - "MyClassField[FIELD_REF]{MyClassField, Ltest.Try;, Ltest.MyClass;, MyClassField, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_EXACT_EXPECTED_TYPE) + "}\n" + - "MyClassMethod[METHOD_REF]{MyClassMethod(), Ltest.Try;, ()Ltest.MyClass;, MyClassMethod, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_EXACT_EXPECTED_TYPE) + "}", + "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, " + (R_DEFAULT + 17) + "}\n" + + "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, " + (R_DEFAULT + 17) + "}\n" + + "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, " + (R_DEFAULT + 17) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, " + (R_DEFAULT + 17) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), " + (R_DEFAULT + 17) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), " + (R_DEFAULT + 17) + "}\n" + + "Try[TYPE_REF]{Try, test, Ltest.Try;, null, null, " + (R_DEFAULT + 22) + "}\n" + + "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, " + (R_DEFAULT + 22) + "}\n" + + "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), " + (R_DEFAULT + 22) + "}\n" + + "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class;, getClass, null, " + (R_DEFAULT + 22) + "}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, " + (R_DEFAULT + 22) + "}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, " + (R_DEFAULT + 22) + "}\n" + + "MyClass[TYPE_REF]{MyClass, test, Ltest.MyClass;, null, null, " + (R_DEFAULT + 52) + "}\n" + + "MyClassField[FIELD_REF]{MyClassField, Ltest.Try;, Ltest.MyClass;, MyClassField, null, " + (R_DEFAULT + 52) + "}\n" + + "MyClassMethod[METHOD_REF]{MyClassMethod(), Ltest.Try;, ()Ltest.MyClass;, MyClassMethod, null, " + (R_DEFAULT + 52) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=292087 @@ -24938,21 +24938,21 @@ public void testBug292087d() throws JavaModelException { "expectedTypesKeys={Ltest/Try~MyClass;}", requestor.getContext()); assertResults( - "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_VOID) + "}\n" + - "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_VOID) + "}\n" + - "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_VOID) + "}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_VOID) + "}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_VOID) + "}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_VOID) + "}\n" + - "Try[TYPE_REF]{Try, test, Ltest.Try;, null, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS) + "}\n" + - "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS) + "}\n" + - "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS) + "}\n" + - "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class;, getClass, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS) + "}\n" + - "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS) + "}\n" + - "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS) + "}\n" + - "MyClass[TYPE_REF]{MyClass, test, Ltest.MyClass;, null, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_EXACT_EXPECTED_TYPE) + "}\n" + - "MyClassField[FIELD_REF]{MyClassField, Ltest.Try;, Ltest.MyClass;, MyClassField, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_EXACT_EXPECTED_TYPE) + "}\n" + - "MyClassMethod[METHOD_REF]{MyClassMethod(), Ltest.Try;, ()Ltest.MyClass;, MyClassMethod, null, " + (R_RESOLVED + R_NON_STATIC + R_NAME_LESS_NEW_CHARACTERS + R_EXACT_EXPECTED_TYPE) + "}", + "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, " + (R_DEFAULT + 17) + "}\n" + + "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, " + (R_DEFAULT + 17) + "}\n" + + "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, " + (R_DEFAULT + 17) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, " + (R_DEFAULT + 17) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), " + (R_DEFAULT + 17) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), " + (R_DEFAULT + 17) + "}\n" + + "Try[TYPE_REF]{Try, test, Ltest.Try;, null, null, " + (R_DEFAULT + 22) + "}\n" + + "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, " + (R_DEFAULT + 22) + "}\n" + + "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), " + (R_DEFAULT + 22) + "}\n" + + "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class;, getClass, null, " + (R_DEFAULT + 22) + "}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, " + (R_DEFAULT + 22) + "}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, " + (R_DEFAULT + 22) + "}\n" + + "MyClass[TYPE_REF]{MyClass, test, Ltest.MyClass;, null, null, " + (R_DEFAULT + 52) + "}\n" + + "MyClassField[FIELD_REF]{MyClassField, Ltest.Try;, Ltest.MyClass;, MyClassField, null, " + (R_DEFAULT + 52) + "}\n" + + "MyClassMethod[METHOD_REF]{MyClassMethod(), Ltest.Try;, ()Ltest.MyClass;, MyClassMethod, null, " + (R_DEFAULT + 52) + "}", requestor.getResults()); } @@ -25181,15 +25181,15 @@ public void testBug402574() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, monitor); assertResults( - "IT_MAY_BE_DUE_TO_MIXING_PERHAPS[FIELD_REF]{IT_MAY_BE_DUE_TO_MIXING_PERHAPS, Ltest.ExampleEnumNoAutocomplete;, Ltest.ExampleEnumNoAutocomplete;, IT_MAY_BE_DUE_TO_MIXING_PERHAPS, null, 26}\n" + - "MORE_STUFF[FIELD_REF]{MORE_STUFF, Ltest.ExampleEnumNoAutocomplete;, Ltest.ExampleEnumNoAutocomplete;, MORE_STUFF, null, 26}\n" + - "OTHER[FIELD_REF]{OTHER, Ltest.ExampleEnumNoAutocomplete;, Ltest.ExampleEnumNoAutocomplete;, OTHER, null, 26}\n" + - "STILL_OTHER[FIELD_REF]{STILL_OTHER, Ltest.ExampleEnumNoAutocomplete;, Ltest.ExampleEnumNoAutocomplete;, STILL_OTHER, null, 26}\n" + - "STUFF[FIELD_REF]{STUFF, Ltest.ExampleEnumNoAutocomplete;, Ltest.ExampleEnumNoAutocomplete;, STUFF, null, 26}\n" + - "THINGS[FIELD_REF]{THINGS, Ltest.ExampleEnumNoAutocomplete;, Ltest.ExampleEnumNoAutocomplete;, THINGS, null, 26}\n" + - "class[FIELD_REF]{class, null, Ljava.lang.Class<Ltest.ExampleEnumNoAutocomplete;>;, class, null, 26}\n" + - "valueOf[METHOD_REF]{valueOf(), Ltest.ExampleEnumNoAutocomplete;, (Ljava.lang.String;)Ltest.ExampleEnumNoAutocomplete;, valueOf, (arg0), 26}\n" + - "values[METHOD_REF]{values(), Ltest.ExampleEnumNoAutocomplete;, ()[Ltest.ExampleEnumNoAutocomplete;, values, null, 26}", + "IT_MAY_BE_DUE_TO_MIXING_PERHAPS[FIELD_REF]{IT_MAY_BE_DUE_TO_MIXING_PERHAPS, Ltest.ExampleEnumNoAutocomplete;, Ltest.ExampleEnumNoAutocomplete;, IT_MAY_BE_DUE_TO_MIXING_PERHAPS, null, " + (R_DEFAULT + 21) + "}\n" + + "MORE_STUFF[FIELD_REF]{MORE_STUFF, Ltest.ExampleEnumNoAutocomplete;, Ltest.ExampleEnumNoAutocomplete;, MORE_STUFF, null, " + (R_DEFAULT + 21) + "}\n" + + "OTHER[FIELD_REF]{OTHER, Ltest.ExampleEnumNoAutocomplete;, Ltest.ExampleEnumNoAutocomplete;, OTHER, null, " + (R_DEFAULT + 21) + "}\n" + + "STILL_OTHER[FIELD_REF]{STILL_OTHER, Ltest.ExampleEnumNoAutocomplete;, Ltest.ExampleEnumNoAutocomplete;, STILL_OTHER, null, " + (R_DEFAULT + 21) + "}\n" + + "STUFF[FIELD_REF]{STUFF, Ltest.ExampleEnumNoAutocomplete;, Ltest.ExampleEnumNoAutocomplete;, STUFF, null, " + (R_DEFAULT + 21) + "}\n" + + "THINGS[FIELD_REF]{THINGS, Ltest.ExampleEnumNoAutocomplete;, Ltest.ExampleEnumNoAutocomplete;, THINGS, null, " + (R_DEFAULT + 21) + "}\n" + + "class[FIELD_REF]{class, null, Ljava.lang.Class<Ltest.ExampleEnumNoAutocomplete;>;, class, null, " + (R_DEFAULT + 21) + "}\n" + + "valueOf[METHOD_REF]{valueOf(), Ltest.ExampleEnumNoAutocomplete;, (Ljava.lang.String;)Ltest.ExampleEnumNoAutocomplete;, valueOf, (arg0), " + (R_DEFAULT + 21) + "}\n" + + "values[METHOD_REF]{values(), Ltest.ExampleEnumNoAutocomplete;, ()[Ltest.ExampleEnumNoAutocomplete;, values, null, " + (R_DEFAULT + 21) + "}", requestor.getResults()); assertEquals(false, requestor.canUseDiamond(0)); @@ -25242,7 +25242,7 @@ public void testBug402812a() throws Exception { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, monitor); assertResults( - "staticMethod[METHOD_REF]{staticMethod(), Ltest.Test;, ()V, staticMethod, null, 27}", + "staticMethod[METHOD_REF]{staticMethod(), Ltest.Test;, ()V, staticMethod, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } finally { deleteProject("P"); @@ -25297,7 +25297,7 @@ public void testBug402812b() throws Exception { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, monitor); assertResults( - "staticMethod[METHOD_REF]{staticMethod(), Ltest.I;, ()V, staticMethod, null, 26}", + "staticMethod[METHOD_REF]{staticMethod(), Ltest.I;, ()V, staticMethod, null, " + (R_DEFAULT + 21) + "}", requestor.getResults()); } finally { deleteProject("P"); @@ -25353,7 +25353,7 @@ public void testBug402812c() throws Exception { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, monitor); assertResults( - "defaultMethod[METHOD_REF]{defaultMethod(), Ltest.I;, ()V, defaultMethod, null, 35}", + "defaultMethod[METHOD_REF]{defaultMethod(), Ltest.I;, ()V, defaultMethod, null, " + (R_DEFAULT + 30) + "}", requestor.getResults()); } finally { deleteProject("P"); @@ -25408,7 +25408,7 @@ public void testBug402812d() throws Exception { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, monitor); assertResults( - "defaultMethod[METHOD_REF]{defaultMethod(), Ltest.I;, ()V, defaultMethod, null, 27}", + "defaultMethod[METHOD_REF]{defaultMethod(), Ltest.I;, ()V, defaultMethod, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } finally { deleteProject("P"); @@ -25452,7 +25452,7 @@ public void testBug370971() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner, monitor); assertResults( - "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 65}", + "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, " + (R_DEFAULT + 60) + "}", requestor.getResults()); assertEquals(false, requestor.canUseDiamond(0)); @@ -25492,12 +25492,12 @@ public void testBug406468a() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "I[TYPE_REF]{I, test, Ltest.I;, null, null, 27}\n" + - "X[TYPE_REF]{X, test, Ltest.X;, null, null, 27}\n" + - "args[LOCAL_VARIABLE_REF]{args, null, [Ljava.lang.String;, args, null, 27}\n" + - "i[LOCAL_VARIABLE_REF]{i, null, Ltest.I;, i, null, 27}\n" + - "main[METHOD_REF]{main(), Ltest.X;, ([Ljava.lang.String;)V, main, (args), 27}\n" + - "x[LOCAL_VARIABLE_REF]{x, null, [[[Ltest.X;, x, null, 27}", + "I[TYPE_REF]{I, test, Ltest.I;, null, null, " + (R_DEFAULT + 22) + "}\n" + + "X[TYPE_REF]{X, test, Ltest.X;, null, null, " + (R_DEFAULT + 22) + "}\n" + + "args[LOCAL_VARIABLE_REF]{args, null, [Ljava.lang.String;, args, null, " + (R_DEFAULT + 22) + "}\n" + + "i[LOCAL_VARIABLE_REF]{i, null, Ltest.I;, i, null, " + (R_DEFAULT + 22) + "}\n" + + "main[METHOD_REF]{main(), Ltest.X;, ([Ljava.lang.String;)V, main, (args), " + (R_DEFAULT + 22) + "}\n" + + "x[LOCAL_VARIABLE_REF]{x, null, [[[Ltest.X;, x, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } finally { // Restore compliance settings. @@ -25534,13 +25534,13 @@ public void testBug406468b() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "I[TYPE_REF]{I, test, Ltest.I;, null, null, 27}\n" + - "S[TYPE_REF]{S, null, TS;, null, null, 27}\n" + - "X<S>[TYPE_REF]{X, test, Ltest.X<TS;>;, null, null, 27}\n" + - "args[LOCAL_VARIABLE_REF]{args, null, [Ljava.lang.String;, args, null, 27}\n" + - "i[LOCAL_VARIABLE_REF]{i, null, Ltest.I;, i, null, 27}\n" + - "main[METHOD_REF]{main(), Ltest.X<TS;>;, ([Ljava.lang.String;)V, main, (args), 27}\n" + - "x[LOCAL_VARIABLE_REF]{x, null, Ltest.X;, x, null, 27}", + "I[TYPE_REF]{I, test, Ltest.I;, null, null, " + (R_DEFAULT + 22) + "}\n" + + "S[TYPE_REF]{S, null, TS;, null, null, " + (R_DEFAULT + 22) + "}\n" + + "X<S>[TYPE_REF]{X, test, Ltest.X<TS;>;, null, null, " + (R_DEFAULT + 22) + "}\n" + + "args[LOCAL_VARIABLE_REF]{args, null, [Ljava.lang.String;, args, null, " + (R_DEFAULT + 22) + "}\n" + + "i[LOCAL_VARIABLE_REF]{i, null, Ltest.I;, i, null, " + (R_DEFAULT + 22) + "}\n" + + "main[METHOD_REF]{main(), Ltest.X<TS;>;, ([Ljava.lang.String;)V, main, (args), " + (R_DEFAULT + 22) + "}\n" + + "x[LOCAL_VARIABLE_REF]{x, null, Ltest.X;, x, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } finally { // Restore compliance settings. @@ -25694,7 +25694,7 @@ public void testBug421469() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "pqrqwerty[LOCAL_VARIABLE_REF]{pqrqwerty, null, I, pqrqwerty, null, 27}", + "pqrqwerty[LOCAL_VARIABLE_REF]{pqrqwerty, null, I, pqrqwerty, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } finally { // Restore compliance settings. @@ -25739,7 +25739,7 @@ public void testBug421469a() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "pqrqwerty[LOCAL_VARIABLE_REF]{pqrqwerty, null, I, pqrqwerty, null, 27}", + "pqrqwerty[LOCAL_VARIABLE_REF]{pqrqwerty, null, I, pqrqwerty, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } finally { // Restore compliance settings. diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java index 9002b4431..705605423 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests18.java @@ -26,7 +26,7 @@ import org.eclipse.jdt.internal.codeassist.RelevanceConstants; public class CompletionTests18 extends AbstractJavaModelCompletionTests { static { -// TESTS_NAMES = new String[] {"test001"}; +// TESTS_NAMES = new String[] {"test001"}; } public CompletionTests18(String name) { @@ -63,7 +63,7 @@ public void test001() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "first[LOCAL_VARIABLE_REF]{first, null, I, first, null, 27}", + "first[LOCAL_VARIABLE_REF]{first, null, I, first, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } public void test002() throws JavaModelException { @@ -86,7 +86,7 @@ public void test002() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "first[LOCAL_VARIABLE_REF]{first, null, I, first, null, 27}", + "first[LOCAL_VARIABLE_REF]{first, null, I, first, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } public void test003() throws JavaModelException { @@ -118,22 +118,22 @@ public void test003() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "CASE_INSENSITIVE_ORDER[FIELD_REF]{CASE_INSENSITIVE_ORDER, Ljava.lang.String;, Ljava.util.Comparator<Ljava.lang.String;>;, CASE_INSENSITIVE_ORDER, null, 14}\n" + - "copyValueOf[METHOD_REF]{copyValueOf(), Ljava.lang.String;, ([C)Ljava.lang.String;, copyValueOf, (arg0), 24}\n" + - "copyValueOf[METHOD_REF]{copyValueOf(), Ljava.lang.String;, ([CII)Ljava.lang.String;, copyValueOf, (arg0, arg1, arg2), 24}\n" + - "charAt[METHOD_REF]{charAt(), Ljava.lang.String;, (I)C, charAt, (arg0), 35}\n" + - "chars[METHOD_REF]{chars(), Ljava.lang.CharSequence;, ()Ljava.util.stream.IntStream;, chars, null, 35}\n" + - "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 35}\n" + - "codePointAt[METHOD_REF]{codePointAt(), Ljava.lang.String;, (I)I, codePointAt, (arg0), 35}\n" + - "codePointBefore[METHOD_REF]{codePointBefore(), Ljava.lang.String;, (I)I, codePointBefore, (arg0), 35}\n" + - "codePointCount[METHOD_REF]{codePointCount(), Ljava.lang.String;, (II)I, codePointCount, (arg0, arg1), 35}\n" + - "codePoints[METHOD_REF]{codePoints(), Ljava.lang.CharSequence;, ()Ljava.util.stream.IntStream;, codePoints, null, 35}\n" + - "compareTo[METHOD_REF]{compareTo(), Ljava.lang.String;, (Ljava.lang.String;)I, compareTo, (arg0), 35}\n" + - "compareToIgnoreCase[METHOD_REF]{compareToIgnoreCase(), Ljava.lang.String;, (Ljava.lang.String;)I, compareToIgnoreCase, (arg0), 35}\n" + - "concat[METHOD_REF]{concat(), Ljava.lang.String;, (Ljava.lang.String;)Ljava.lang.String;, concat, (arg0), 35}\n" + - "contains[METHOD_REF]{contains(), Ljava.lang.String;, (Ljava.lang.CharSequence;)Z, contains, (arg0), 35}\n" + - "contentEquals[METHOD_REF]{contentEquals(), Ljava.lang.String;, (Ljava.lang.CharSequence;)Z, contentEquals, (arg0), 35}\n" + - "contentEquals[METHOD_REF]{contentEquals(), Ljava.lang.String;, (Ljava.lang.StringBuffer;)Z, contentEquals, (arg0), 35}", + "CASE_INSENSITIVE_ORDER[FIELD_REF]{CASE_INSENSITIVE_ORDER, Ljava.lang.String;, Ljava.util.Comparator<Ljava.lang.String;>;, CASE_INSENSITIVE_ORDER, null, " + (R_DEFAULT + 9) + "}\n" + + "copyValueOf[METHOD_REF]{copyValueOf(), Ljava.lang.String;, ([C)Ljava.lang.String;, copyValueOf, (arg0), " + (R_DEFAULT + 19) + "}\n" + + "copyValueOf[METHOD_REF]{copyValueOf(), Ljava.lang.String;, ([CII)Ljava.lang.String;, copyValueOf, (arg0, arg1, arg2), " + (R_DEFAULT + 19) + "}\n" + + "charAt[METHOD_REF]{charAt(), Ljava.lang.String;, (I)C, charAt, (arg0), " + (R_DEFAULT + 30) + "}\n" + + "chars[METHOD_REF]{chars(), Ljava.lang.CharSequence;, ()Ljava.util.stream.IntStream;, chars, null, " + (R_DEFAULT + 30) + "}\n" + + "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, " + (R_DEFAULT + 30) + "}\n" + + "codePointAt[METHOD_REF]{codePointAt(), Ljava.lang.String;, (I)I, codePointAt, (arg0), " + (R_DEFAULT + 30) + "}\n" + + "codePointBefore[METHOD_REF]{codePointBefore(), Ljava.lang.String;, (I)I, codePointBefore, (arg0), " + (R_DEFAULT + 30) + "}\n" + + "codePointCount[METHOD_REF]{codePointCount(), Ljava.lang.String;, (II)I, codePointCount, (arg0, arg1), " + (R_DEFAULT + 30) + "}\n" + + "codePoints[METHOD_REF]{codePoints(), Ljava.lang.CharSequence;, ()Ljava.util.stream.IntStream;, codePoints, null, " + (R_DEFAULT + 30) + "}\n" + + "compareTo[METHOD_REF]{compareTo(), Ljava.lang.String;, (Ljava.lang.String;)I, compareTo, (arg0), " + (R_DEFAULT + 30) + "}\n" + + "compareToIgnoreCase[METHOD_REF]{compareToIgnoreCase(), Ljava.lang.String;, (Ljava.lang.String;)I, compareToIgnoreCase, (arg0), " + (R_DEFAULT + 30) + "}\n" + + "concat[METHOD_REF]{concat(), Ljava.lang.String;, (Ljava.lang.String;)Ljava.lang.String;, concat, (arg0), " + (R_DEFAULT + 30) + "}\n" + + "contains[METHOD_REF]{contains(), Ljava.lang.String;, (Ljava.lang.CharSequence;)Z, contains, (arg0), " + (R_DEFAULT + 30) + "}\n" + + "contentEquals[METHOD_REF]{contentEquals(), Ljava.lang.String;, (Ljava.lang.CharSequence;)Z, contentEquals, (arg0), " + (R_DEFAULT + 30) + "}\n" + + "contentEquals[METHOD_REF]{contentEquals(), Ljava.lang.String;, (Ljava.lang.StringBuffer;)Z, contentEquals, (arg0), " + (R_DEFAULT + 30) + "}", requestor.getResults()); } public void test004() throws JavaModelException { @@ -154,8 +154,8 @@ public void test004() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "lpx5[LOCAL_VARIABLE_REF]{lpx5, null, I, lpx5, null, 27}\n" + - "lpx6[LOCAL_VARIABLE_REF]{lpx6, null, I, lpx6, null, 27}", + "lpx5[LOCAL_VARIABLE_REF]{lpx5, null, I, lpx5, null, " + (R_DEFAULT + 22) + "}\n" + + "lpx6[LOCAL_VARIABLE_REF]{lpx6, null, I, lpx6, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } @@ -183,7 +183,7 @@ public void test005() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "argument[LOCAL_VARIABLE_REF]{argument, null, I, argument, null, 57}", + "argument[LOCAL_VARIABLE_REF]{argument, null, I, argument, null, " + (R_DEFAULT + 52) + "}", requestor.getResults()); } public void test006() throws JavaModelException { @@ -208,7 +208,7 @@ public void test006() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "argument[LOCAL_VARIABLE_REF]{argument, null, I, argument, null, 27}", + "argument[LOCAL_VARIABLE_REF]{argument, null, I, argument, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=405126, [1.8][code assist] Lambda parameters incorrectly recovered as fields. @@ -236,10 +236,10 @@ public void test007() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "class[FIELD_REF]{class, null, Ljava.lang.Class<LX;>;, class, null, 26}\n" + - "f[FIELD_REF]{f, LX;, LFoo;, f, null, 26}\n" + - "this[KEYWORD]{this, null, null, this, null, 26}\n" + - "x1[FIELD_REF]{x1, LX;, I, x1, null, 56}", + "class[FIELD_REF]{class, null, Ljava.lang.Class<LX;>;, class, null, " + (R_DEFAULT + 21) + "}\n" + + "f[FIELD_REF]{f, LX;, LFoo;, f, null, " + (R_DEFAULT + 21) + "}\n" + + "this[KEYWORD]{this, null, null, this, null, " + (R_DEFAULT + 21) + "}\n" + + "x1[FIELD_REF]{x1, LX;, I, x1, null, " + (R_DEFAULT + 51) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=422107, [1.8][code assist] Invoking code assist just before and after a variable initialized using lambda gives different result @@ -265,35 +265,35 @@ public void test008() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "[POTENTIAL_METHOD_DECLARATION]{, LX;, ()V, , null, 14}\n" + - "abstract[KEYWORD]{abstract, null, null, abstract, null, 24}\n" + + "[POTENTIAL_METHOD_DECLARATION]{, LX;, ()V, , null, " + (R_DEFAULT + 9) + "}\n" + + "abstract[KEYWORD]{abstract, null, null, abstract, null, " + (R_DEFAULT + 19) + "}\n" + //{ObjectTeams: - "callin[KEYWORD]{callin, null, null, callin, null, 24}\n" + + "callin[KEYWORD]{callin, null, null, callin, null, " + (R_DEFAULT + 19) + "}\n" + // SH} - "class[KEYWORD]{class, null, null, class, null, 24}\n" + - "enum[KEYWORD]{enum, null, null, enum, null, 24}\n" + - "final[KEYWORD]{final, null, null, final, null, 24}\n" + - "interface[KEYWORD]{interface, null, null, interface, null, 24}\n" + - "native[KEYWORD]{native, null, null, native, null, 24}\n" + - "private[KEYWORD]{private, null, null, private, null, 24}\n" + - "protected[KEYWORD]{protected, null, null, protected, null, 24}\n" + - "public[KEYWORD]{public, null, null, public, null, 24}\n" + - "static[KEYWORD]{static, null, null, static, null, 24}\n" + - "strictfp[KEYWORD]{strictfp, null, null, strictfp, null, 24}\n" + - "synchronized[KEYWORD]{synchronized, null, null, synchronized, null, 24}\n" + + "class[KEYWORD]{class, null, null, class, null, " + (R_DEFAULT + 19) + "}\n" + + "enum[KEYWORD]{enum, null, null, enum, null, " + (R_DEFAULT + 19) + "}\n" + + "final[KEYWORD]{final, null, null, final, null, " + (R_DEFAULT + 19) + "}\n" + + "interface[KEYWORD]{interface, null, null, interface, null, " + (R_DEFAULT + 19) + "}\n" + + "native[KEYWORD]{native, null, null, native, null, " + (R_DEFAULT + 19) + "}\n" + + "private[KEYWORD]{private, null, null, private, null, " + (R_DEFAULT + 19) + "}\n" + + "protected[KEYWORD]{protected, null, null, protected, null, " + (R_DEFAULT + 19) + "}\n" + + "public[KEYWORD]{public, null, null, public, null, " + (R_DEFAULT + 19) + "}\n" + + "static[KEYWORD]{static, null, null, static, null, " + (R_DEFAULT + 19) + "}\n" + + "strictfp[KEYWORD]{strictfp, null, null, strictfp, null, " + (R_DEFAULT + 19) + "}\n" + + "synchronized[KEYWORD]{synchronized, null, null, synchronized, null, " + (R_DEFAULT + 19) + "}\n" + //{ObjectTeams: - "team[KEYWORD]{team, null, null, team, null, 24}\n" + + "team[KEYWORD]{team, null, null, team, null, " + (R_DEFAULT + 19) + "}\n" + // SH} - "transient[KEYWORD]{transient, null, null, transient, null, 24}\n" + - "volatile[KEYWORD]{volatile, null, null, volatile, null, 24}\n" + - "I[TYPE_REF]{I, , LI;, null, null, 27}\n" + - "J[TYPE_REF]{J, , LJ;, null, null, 27}\n" + - "X[TYPE_REF]{X, , LX;, null, null, 27}\n" + - "clone[METHOD_DECLARATION]{protected Object clone() throws CloneNotSupportedException, Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 27}\n" + - "equals[METHOD_DECLARATION]{public boolean equals(Object obj), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 27}\n" + - "finalize[METHOD_DECLARATION]{protected void finalize() throws Throwable, Ljava.lang.Object;, ()V, finalize, null, 27}\n" + - "hashCode[METHOD_DECLARATION]{public int hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 27}\n" + - "toString[METHOD_DECLARATION]{public String toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 27}", + "transient[KEYWORD]{transient, null, null, transient, null, " + (R_DEFAULT + 19) + "}\n" + + "volatile[KEYWORD]{volatile, null, null, volatile, null, " + (R_DEFAULT + 19) + "}\n" + + "I[TYPE_REF]{I, , LI;, null, null, " + (R_DEFAULT + 22) + "}\n" + + "J[TYPE_REF]{J, , LJ;, null, null, " + (R_DEFAULT + 22) + "}\n" + + "X[TYPE_REF]{X, , LX;, null, null, " + (R_DEFAULT + 22) + "}\n" + + "clone[METHOD_DECLARATION]{protected Object clone() throws CloneNotSupportedException, Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, " + (R_DEFAULT + 22) + "}\n" + + "equals[METHOD_DECLARATION]{public boolean equals(Object obj), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), " + (R_DEFAULT + 22) + "}\n" + + "finalize[METHOD_DECLARATION]{protected void finalize() throws Throwable, Ljava.lang.Object;, ()V, finalize, null, " + (R_DEFAULT + 22) + "}\n" + + "hashCode[METHOD_DECLARATION]{public int hashCode(), Ljava.lang.Object;, ()I, hashCode, null, " + (R_DEFAULT + 22) + "}\n" + + "toString[METHOD_DECLARATION]{public String toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=422107, [1.8][code assist] Invoking code assist just before and after a variable initialized using lambda gives different result @@ -319,35 +319,35 @@ public void test009() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "[POTENTIAL_METHOD_DECLARATION]{, LX;, ()V, , null, 14}\n" + - "abstract[KEYWORD]{abstract, null, null, abstract, null, 24}\n" + + "[POTENTIAL_METHOD_DECLARATION]{, LX;, ()V, , null, " + (R_DEFAULT + 9) + "}\n" + + "abstract[KEYWORD]{abstract, null, null, abstract, null, " + (R_DEFAULT + 19) + "}\n" + //{ObjectTeams: - "callin[KEYWORD]{callin, null, null, callin, null, 24}\n" + + "callin[KEYWORD]{callin, null, null, callin, null, " + (R_DEFAULT + 9) + "}\n" + // SH} - "class[KEYWORD]{class, null, null, class, null, 24}\n" + - "enum[KEYWORD]{enum, null, null, enum, null, 24}\n" + - "final[KEYWORD]{final, null, null, final, null, 24}\n" + - "interface[KEYWORD]{interface, null, null, interface, null, 24}\n" + - "native[KEYWORD]{native, null, null, native, null, 24}\n" + - "private[KEYWORD]{private, null, null, private, null, 24}\n" + - "protected[KEYWORD]{protected, null, null, protected, null, 24}\n" + - "public[KEYWORD]{public, null, null, public, null, 24}\n" + - "static[KEYWORD]{static, null, null, static, null, 24}\n" + - "strictfp[KEYWORD]{strictfp, null, null, strictfp, null, 24}\n" + - "synchronized[KEYWORD]{synchronized, null, null, synchronized, null, 24}\n" + + "class[KEYWORD]{class, null, null, class, null, " + (R_DEFAULT + 19) + "}\n" + + "enum[KEYWORD]{enum, null, null, enum, null, " + (R_DEFAULT + 19) + "}\n" + + "final[KEYWORD]{final, null, null, final, null, " + (R_DEFAULT + 19) + "}\n" + + "interface[KEYWORD]{interface, null, null, interface, null, " + (R_DEFAULT + 19) + "}\n" + + "native[KEYWORD]{native, null, null, native, null, " + (R_DEFAULT + 19) + "}\n" + + "private[KEYWORD]{private, null, null, private, null, " + (R_DEFAULT + 19) + "}\n" + + "protected[KEYWORD]{protected, null, null, protected, null, " + (R_DEFAULT + 19) + "}\n" + + "public[KEYWORD]{public, null, null, public, null, " + (R_DEFAULT + 19) + "}\n" + + "static[KEYWORD]{static, null, null, static, null, " + (R_DEFAULT + 19) + "}\n" + + "strictfp[KEYWORD]{strictfp, null, null, strictfp, null, " + (R_DEFAULT + 19) + "}\n" + + "synchronized[KEYWORD]{synchronized, null, null, synchronized, null, " + (R_DEFAULT + 19) + "}\n" + //{ObjectTeams: - "team[KEYWORD]{team, null, null, team, null, 24}\n" + + "team[KEYWORD]{team, null, null, team, null, " + (R_DEFAULT + 9) + "}\n" + // SH} - "transient[KEYWORD]{transient, null, null, transient, null, 24}\n" + - "volatile[KEYWORD]{volatile, null, null, volatile, null, 24}\n" + - "I[TYPE_REF]{I, , LI;, null, null, 27}\n" + - "J[TYPE_REF]{J, , LJ;, null, null, 27}\n" + - "X[TYPE_REF]{X, , LX;, null, null, 27}\n" + - "clone[METHOD_DECLARATION]{protected Object clone() throws CloneNotSupportedException, Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 27}\n" + - "equals[METHOD_DECLARATION]{public boolean equals(Object obj), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 27}\n" + - "finalize[METHOD_DECLARATION]{protected void finalize() throws Throwable, Ljava.lang.Object;, ()V, finalize, null, 27}\n" + - "hashCode[METHOD_DECLARATION]{public int hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 27}\n" + - "toString[METHOD_DECLARATION]{public String toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 27}", + "transient[KEYWORD]{transient, null, null, transient, null, " + (R_DEFAULT + 19) + "}\n" + + "volatile[KEYWORD]{volatile, null, null, volatile, null, " + (R_DEFAULT + 19) + "}\n" + + "I[TYPE_REF]{I, , LI;, null, null, " + (R_DEFAULT + 22) + "}\n" + + "J[TYPE_REF]{J, , LJ;, null, null, " + (R_DEFAULT + 22) + "}\n" + + "X[TYPE_REF]{X, , LX;, null, null, " + (R_DEFAULT + 22) + "}\n" + + "clone[METHOD_DECLARATION]{protected Object clone() throws CloneNotSupportedException, Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, " + (R_DEFAULT + 22) + "}\n" + + "equals[METHOD_DECLARATION]{public boolean equals(Object obj), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), " + (R_DEFAULT + 22) + "}\n" + + "finalize[METHOD_DECLARATION]{protected void finalize() throws Throwable, Ljava.lang.Object;, ()V, finalize, null, " + (R_DEFAULT + 22) + "}\n" + + "hashCode[METHOD_DECLARATION]{public int hashCode(), Ljava.lang.Object;, ()I, hashCode, null, " + (R_DEFAULT + 22) + "}\n" + + "toString[METHOD_DECLARATION]{public String toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } public void test010() throws JavaModelException { @@ -377,20 +377,20 @@ public void test010() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "goo[METHOD_REF]{goo(), LX;, (LI;)V, goo, (i), 24}\n" + - "goo[METHOD_REF]{goo(), LX;, (Ljava.lang.String;)V, goo, (s), 24}\n" + - "main[METHOD_REF]{main(), LX;, ([Ljava.lang.String;)V, main, (args), 24}\n" + - "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, 35}\n" + - "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), 35}\n" + - "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, 35}\n" + - "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, getClass, null, 35}\n" + - "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, 35}\n" + - "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, 35}\n" + - "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, 35}\n" + - "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 35}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, 35}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), 35}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), 35}", + "goo[METHOD_REF]{goo(), LX;, (LI;)V, goo, (i), " + (R_DEFAULT + 19) + "}\n" + + "goo[METHOD_REF]{goo(), LX;, (Ljava.lang.String;)V, goo, (s), " + (R_DEFAULT + 19) + "}\n" + + "main[METHOD_REF]{main(), LX;, ([Ljava.lang.String;)V, main, (args), " + (R_DEFAULT + 19) + "}\n" + + "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, clone, null, " + (R_DEFAULT + 30) + "}\n" + + "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, equals, (obj), " + (R_DEFAULT + 30) + "}\n" + + "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, finalize, null, " + (R_DEFAULT + 30) + "}\n" + + "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, getClass, null, " + (R_DEFAULT + 30) + "}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, hashCode, null, " + (R_DEFAULT + 30) + "}\n" + + "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, notify, null, " + (R_DEFAULT + 30) + "}\n" + + "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, notifyAll, null, " + (R_DEFAULT + 30) + "}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, " + (R_DEFAULT + 30) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, wait, null, " + (R_DEFAULT + 30) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, wait, (millis), " + (R_DEFAULT + 30) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, wait, (millis, nanos), " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=422901, [1.8][code assist] Code assistant sensitive to scope.referenceContext type identity. @@ -509,8 +509,8 @@ public void test014() throws JavaModelException { // ensure higher relevance for String completeBehind = "arrayO"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("arrayOfStrings[LOCAL_VARIABLE_REF]{arrayOfStrings, null, [Ljava.lang.String;, null, null, arrayOfStrings, null, [168, 174], 27}\n" + - "arrayOfInts[LOCAL_VARIABLE_REF]{arrayOfInts, null, [I, null, null, arrayOfInts, null, [168, 174], 57}", requestor.getResults()); + assertResults("arrayOfStrings[LOCAL_VARIABLE_REF]{arrayOfStrings, null, [Ljava.lang.String;, null, null, arrayOfStrings, null, [168, 174], " + (R_DEFAULT + 22) + "}\n" + + "arrayOfInts[LOCAL_VARIABLE_REF]{arrayOfInts, null, [I, null, null, arrayOfInts, null, [168, 174], " + (R_DEFAULT + 52) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=422901, [1.8][code assist] Code assistant sensitive to scope.referenceContext type identity. public void test015() throws JavaModelException { // ensure higher relevance for matching return type. @@ -537,7 +537,7 @@ public void test015() throws JavaModelException { // ensure higher relevance for String completeBehind = "xyz"; int cursorLocation = str.indexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("xyzAfter[LOCAL_VARIABLE_REF]{xyzAfter, null, Ljava.lang.Object;, null, null, xyzAfter, null, [132, 135], 26}", requestor.getResults()); + assertResults("xyzAfter[LOCAL_VARIABLE_REF]{xyzAfter, null, Ljava.lang.Object;, null, null, xyzAfter, null, [132, 135], " + (R_DEFAULT + 21) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=422901, [1.8][code assist] Code assistant sensitive to scope.referenceContext type identity. public void test016() throws JavaModelException { // ensure higher relevance for matching return type. @@ -564,7 +564,7 @@ public void test016() throws JavaModelException { // ensure higher relevance for String completeBehind = "xyz"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("xyzBefore[LOCAL_VARIABLE_REF]{xyzBefore, null, Ljava.lang.Object;, null, null, xyzBefore, null, [163, 166], 26}", requestor.getResults()); + assertResults("xyzBefore[LOCAL_VARIABLE_REF]{xyzBefore, null, Ljava.lang.Object;, null, null, xyzBefore, null, [163, 166], " + (R_DEFAULT + 21) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=422901, [1.8][code assist] Code assistant sensitive to scope.referenceContext type identity. public void test017() throws JavaModelException { // ensure higher relevance for matching return type. @@ -732,20 +732,20 @@ public void testUnspecifiedReference() throws JavaModelException { // ensure com String completeBehind = "Stri"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("StringBufferInputStream[TYPE_REF]{java.io.StringBufferInputStream, java.io, Ljava.io.StringBufferInputStream;, null, null, null, null, [155, 159], 24}\n" + - "StringCharBuffer[TYPE_REF]{java.nio.StringCharBuffer, java.nio, Ljava.nio.StringCharBuffer;, null, null, null, null, [155, 159], 24}\n" + - "StringCharacterIterator[TYPE_REF]{java.text.StringCharacterIterator, java.text, Ljava.text.StringCharacterIterator;, null, null, null, null, [155, 159], 24}\n" + - "StringJoiner[TYPE_REF]{java.util.StringJoiner, java.util, Ljava.util.StringJoiner;, null, null, null, null, [155, 159], 24}\n" + - "StringReader[TYPE_REF]{java.io.StringReader, java.io, Ljava.io.StringReader;, null, null, null, null, [155, 159], 24}\n" + - "StringTokenizer[TYPE_REF]{java.util.StringTokenizer, java.util, Ljava.util.StringTokenizer;, null, null, null, null, [155, 159], 24}\n" + - "StringWriter[TYPE_REF]{java.io.StringWriter, java.io, Ljava.io.StringWriter;, null, null, null, null, [155, 159], 24}\n" + - "StrictMath[TYPE_REF]{StrictMath, java.lang, Ljava.lang.StrictMath;, null, null, null, null, [155, 159], 27}\n" + - "String[TYPE_REF]{String, java.lang, Ljava.lang.String;, null, null, null, null, [155, 159], 27}\n" + - "StringBuffer[TYPE_REF]{StringBuffer, java.lang, Ljava.lang.StringBuffer;, null, null, null, null, [155, 159], 27}\n" + - "StringBuilder[TYPE_REF]{StringBuilder, java.lang, Ljava.lang.StringBuilder;, null, null, null, null, [155, 159], 27}\n" + - "StringCoding[TYPE_REF]{StringCoding, java.lang, Ljava.lang.StringCoding;, null, null, null, null, [155, 159], 27}\n" + - "StringIndexOutOfBoundsException[TYPE_REF]{StringIndexOutOfBoundsException, java.lang, Ljava.lang.StringIndexOutOfBoundsException;, null, null, null, null, [155, 159], 27}\n" + - "StringParameter[LOCAL_VARIABLE_REF]{StringParameter, null, LX;, null, null, StringParameter, null, [155, 159], 27}", requestor.getResults()); + assertResults("StringBufferInputStream[TYPE_REF]{java.io.StringBufferInputStream, java.io, Ljava.io.StringBufferInputStream;, null, null, null, null, [155, 159], " + (R_DEFAULT + 19) + "}\n" + + "StringCharBuffer[TYPE_REF]{java.nio.StringCharBuffer, java.nio, Ljava.nio.StringCharBuffer;, null, null, null, null, [155, 159], " + (R_DEFAULT + 19) + "}\n" + + "StringCharacterIterator[TYPE_REF]{java.text.StringCharacterIterator, java.text, Ljava.text.StringCharacterIterator;, null, null, null, null, [155, 159], " + (R_DEFAULT + 19) + "}\n" + + "StringJoiner[TYPE_REF]{java.util.StringJoiner, java.util, Ljava.util.StringJoiner;, null, null, null, null, [155, 159], " + (R_DEFAULT + 19) + "}\n" + + "StringReader[TYPE_REF]{java.io.StringReader, java.io, Ljava.io.StringReader;, null, null, null, null, [155, 159], " + (R_DEFAULT + 19) + "}\n" + + "StringTokenizer[TYPE_REF]{java.util.StringTokenizer, java.util, Ljava.util.StringTokenizer;, null, null, null, null, [155, 159], " + (R_DEFAULT + 19) + "}\n" + + "StringWriter[TYPE_REF]{java.io.StringWriter, java.io, Ljava.io.StringWriter;, null, null, null, null, [155, 159], " + (R_DEFAULT + 19) + "}\n" + + "StrictMath[TYPE_REF]{StrictMath, java.lang, Ljava.lang.StrictMath;, null, null, null, null, [155, 159], " + (R_DEFAULT + 22) + "}\n" + + "String[TYPE_REF]{String, java.lang, Ljava.lang.String;, null, null, null, null, [155, 159], " + (R_DEFAULT + 22) + "}\n" + + "StringBuffer[TYPE_REF]{StringBuffer, java.lang, Ljava.lang.StringBuffer;, null, null, null, null, [155, 159], " + (R_DEFAULT + 22) + "}\n" + + "StringBuilder[TYPE_REF]{StringBuilder, java.lang, Ljava.lang.StringBuilder;, null, null, null, null, [155, 159], " + (R_DEFAULT + 22) + "}\n" + + "StringCoding[TYPE_REF]{StringCoding, java.lang, Ljava.lang.StringCoding;, null, null, null, null, [155, 159], " + (R_DEFAULT + 22) + "}\n" + + "StringIndexOutOfBoundsException[TYPE_REF]{StringIndexOutOfBoundsException, java.lang, Ljava.lang.StringIndexOutOfBoundsException;, null, null, null, null, [155, 159], " + (R_DEFAULT + 22) + "}\n" + + "StringParameter[LOCAL_VARIABLE_REF]{StringParameter, null, LX;, null, null, StringParameter, null, [155, 159], " + (R_DEFAULT + 22) + "}", requestor.getResults()); } public void testBrokenMethodCall() throws JavaModelException { // ensure completion works when the containing call is not terminated properly. this.workingCopies = new ICompilationUnit[1]; @@ -770,7 +770,7 @@ public void testBrokenMethodCall() throws JavaModelException { // ensure complet String completeBehind = "StringP"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("StringParameter[LOCAL_VARIABLE_REF]{StringParameter, null, LX;, null, null, StringParameter, null, [155, 162], 27}", requestor.getResults()); + assertResults("StringParameter[LOCAL_VARIABLE_REF]{StringParameter, null, LX;, null, null, StringParameter, null, [155, 162], " + (R_DEFAULT + 22) + "}", requestor.getResults()); } public void testExpressionBody() throws JavaModelException { this.workingCopies = new ICompilationUnit[1]; @@ -795,21 +795,21 @@ public void testExpressionBody() throws JavaModelException { String completeBehind = "xyz."; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("goo[METHOD_REF]{goo(), LX;, (LI;)V, null, null, goo, (i), [173, 173], 24}\n" + - "main[METHOD_REF]{main(), LX;, ([Ljava.lang.String;)V, null, null, main, (args), [173, 173], 24}\n" + - "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, null, null, clone, null, [173, 173], 35}\n" + - "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, null, null, equals, (obj), [173, 173], 35}\n" + - "field[FIELD_REF]{field, LX;, I, null, null, field, null, [173, 173], 35}\n" + - "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, null, null, finalize, null, [173, 173], 35}\n" + - "foo[METHOD_REF]{foo(), LX;, ()V, null, null, foo, null, [173, 173], 35}\n" + - "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [173, 173], 35}\n" + - "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, null, null, hashCode, null, [173, 173], 35}\n" + - "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, null, null, notify, null, [173, 173], 35}\n" + - "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, null, null, notifyAll, null, [173, 173], 35}\n" + - "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, null, null, toString, null, [173, 173], 35}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, null, null, wait, null, [173, 173], 35}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, null, null, wait, (millis), [173, 173], 35}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, null, null, wait, (millis, nanos), [173, 173], 35}", requestor.getResults()); + assertResults("goo[METHOD_REF]{goo(), LX;, (LI;)V, null, null, goo, (i), [173, 173], " + (R_DEFAULT + 19) + "}\n" + + "main[METHOD_REF]{main(), LX;, ([Ljava.lang.String;)V, null, null, main, (args), [173, 173], " + (R_DEFAULT + 19) + "}\n" + + "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, null, null, clone, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, null, null, equals, (obj), [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "field[FIELD_REF]{field, LX;, I, null, null, field, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, null, null, finalize, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "foo[METHOD_REF]{foo(), LX;, ()V, null, null, foo, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, null, null, hashCode, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, null, null, notify, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, null, null, notifyAll, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, null, null, toString, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, null, null, wait, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, null, null, wait, (millis), [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, null, null, wait, (millis, nanos), [173, 173], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } public void testExpressionBody2() throws JavaModelException { this.workingCopies = new ICompilationUnit[1]; @@ -834,21 +834,21 @@ public void testExpressionBody2() throws JavaModelException { String completeBehind = "xyz."; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("goo[METHOD_REF]{goo(), LX;, (LI;)V, null, null, goo, (i), [173, 173], 24}\n" + - "main[METHOD_REF]{main(), LX;, ([Ljava.lang.String;)V, null, null, main, (args), [173, 173], 24}\n" + - "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, null, null, clone, null, [173, 173], 35}\n" + - "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, null, null, equals, (obj), [173, 173], 35}\n" + - "field[FIELD_REF]{field, LX;, I, null, null, field, null, [173, 173], 35}\n" + - "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, null, null, finalize, null, [173, 173], 35}\n" + - "foo[METHOD_REF]{foo(), LX;, ()V, null, null, foo, null, [173, 173], 35}\n" + - "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [173, 173], 35}\n" + - "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, null, null, hashCode, null, [173, 173], 35}\n" + - "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, null, null, notify, null, [173, 173], 35}\n" + - "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, null, null, notifyAll, null, [173, 173], 35}\n" + - "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, null, null, toString, null, [173, 173], 35}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, null, null, wait, null, [173, 173], 35}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, null, null, wait, (millis), [173, 173], 35}\n" + - "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, null, null, wait, (millis, nanos), [173, 173], 35}", requestor.getResults()); + assertResults("goo[METHOD_REF]{goo(), LX;, (LI;)V, null, null, goo, (i), [173, 173], " + (R_DEFAULT + 19) + "}\n" + + "main[METHOD_REF]{main(), LX;, ([Ljava.lang.String;)V, null, null, main, (args), [173, 173], " + (R_DEFAULT + 19) + "}\n" + + "clone[METHOD_REF]{clone(), Ljava.lang.Object;, ()Ljava.lang.Object;, null, null, clone, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "equals[METHOD_REF]{equals(), Ljava.lang.Object;, (Ljava.lang.Object;)Z, null, null, equals, (obj), [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "field[FIELD_REF]{field, LX;, I, null, null, field, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "finalize[METHOD_REF]{finalize(), Ljava.lang.Object;, ()V, null, null, finalize, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "foo[METHOD_REF]{foo(), LX;, ()V, null, null, foo, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Object;, ()I, null, null, hashCode, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "notify[METHOD_REF]{notify(), Ljava.lang.Object;, ()V, null, null, notify, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "notifyAll[METHOD_REF]{notifyAll(), Ljava.lang.Object;, ()V, null, null, notifyAll, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, null, null, toString, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, ()V, null, null, wait, null, [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (J)V, null, null, wait, (millis), [173, 173], " + (R_DEFAULT + 30) + "}\n" + + "wait[METHOD_REF]{wait(), Ljava.lang.Object;, (JI)V, null, null, wait, (millis, nanos), [173, 173], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // Bug 405125 - [1.8][code assist] static members of an interface appearing after the declaration of a static member lambda expression are not being suggested. // https://bugs.eclipse.org/bugs/show_bug.cgi?id=405125 @@ -877,7 +877,7 @@ public void testBug405125a() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "bars[METHOD_REF]{bars(), LB;, ()I, bars, null, 27}", + "bars[METHOD_REF]{bars(), LB;, ()I, bars, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } public void testBug405125b() throws JavaModelException { @@ -905,7 +905,7 @@ public void testBug405125b() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "another[FIELD_REF]{another, LB;, I, another, null, 27}", + "another[FIELD_REF]{another, LB;, I, another, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=425084, [1.8][completion] Eclipse freeze while autocompleting try block in lambda. @@ -932,8 +932,8 @@ public void test425084() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "tryit[LOCAL_VARIABLE_REF]{tryit, null, I, null, null, tryit, null, [99, 102], 27}\n" + - "try[KEYWORD]{try, null, null, null, null, try, null, [99, 102], 28}", + "tryit[LOCAL_VARIABLE_REF]{tryit, null, I, null, null, tryit, null, [99, 102], " + (R_DEFAULT + 22) + "}\n" + + "try[KEYWORD]{try, null, null, null, null, try, null, [99, 102], " + (R_DEFAULT + 23) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=422901, [1.8][code assist] Code assistant sensitive to scope.referenceContext type identity. @@ -1017,7 +1017,7 @@ public void test426851() throws JavaModelException { String completeBehind = "Ty"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("TypeUse[TYPE_REF]{TypeUse, , LTypeUse;, null, null, null, null, [131, 133], 52}", requestor.getResults()); + assertResults("TypeUse[TYPE_REF]{TypeUse, , LTypeUse;, null, null, null, null, [131, 133], " + (R_DEFAULT + 47) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=427532, [1.8][code assist] Completion engine does not like intersection casts public void test427532() throws JavaModelException { @@ -1136,7 +1136,7 @@ public void test427464() throws JavaModelException { String completeBehind = "@Ann"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("Annotation[TYPE_REF]{Annotation, , LAnnotation;, null, null, null, null, [138, 141], 47}", requestor.getResults()); + assertResults("Annotation[TYPE_REF]{Annotation, , LAnnotation;, null, null, null, null, [138, 141], " + (R_DEFAULT + 42) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=428735, [1.8][assist] Missing completion proposals inside lambda body expression - other than first token public void test428735() throws JavaModelException { @@ -1159,8 +1159,8 @@ public void test428735() throws JavaModelException { String completeBehind = "p.get"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [186, 189], 35}\n" + - "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [186, 189], 35}", requestor.getResults()); + assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [186, 189], " + (R_DEFAULT + 30) + "}\n" + + "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [186, 189], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=428735, [1.8][assist] Missing completion proposals inside lambda body expression - other than first token public void test428735a() throws JavaModelException { @@ -1186,8 +1186,8 @@ public void test428735a() throws JavaModelException { String completeBehind = "x.get"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [267, 270], 35}\n" + - "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [267, 270], 35}", requestor.getResults()); + assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [267, 270], " + (R_DEFAULT + 30) + "}\n" + + "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [267, 270], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=428735, [1.8][assist] Missing completion proposals inside lambda body expression - other than first token public void test428735b() throws JavaModelException { @@ -1213,8 +1213,8 @@ public void test428735b() throws JavaModelException { String completeBehind = "y.get"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [293, 296], 35}\n" + - "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [293, 296], 35}", requestor.getResults()); + assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [293, 296], " + (R_DEFAULT + 30) + "}\n" + + "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [293, 296], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=428735, [1.8][assist] Missing completion proposals inside lambda body expression - other than first token public void test428735c() throws JavaModelException { @@ -1240,8 +1240,8 @@ public void test428735c() throws JavaModelException { String completeBehind = "y.get"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [285, 288], 35}\n" + - "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [285, 288], 65}", requestor.getResults()); + assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [285, 288], " + (R_DEFAULT + 30) + "}\n" + + "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [285, 288], " + (R_DEFAULT + 60) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=428735, [1.8][assist] Missing completion proposals inside lambda body expression - other than first token public void test428735d() throws JavaModelException { @@ -1267,8 +1267,8 @@ public void test428735d() throws JavaModelException { String completeBehind = "x.get"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [272, 275], 35}\n" + - "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [272, 275], 65}", requestor.getResults()); + assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [272, 275], " + (R_DEFAULT + 30) + "}\n" + + "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [272, 275], " + (R_DEFAULT + 60) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=428735, [1.8][assist] Missing completion proposals inside lambda body expression - other than first token public void test428735e() throws JavaModelException { @@ -1293,8 +1293,8 @@ public void test428735e() throws JavaModelException { String completeBehind = "x.get"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [203, 206], 35}\n" + - "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [203, 206], 65}", requestor.getResults()); + assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [203, 206], " + (R_DEFAULT + 30) + "}\n" + + "getLastName[METHOD_REF]{getLastName(), LPerson;, ()Ljava.lang.String;, null, null, getLastName, null, [203, 206], " + (R_DEFAULT + 60) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=402081, [1.8][code complete] No proposals while completing at method/constructor references public void test402081() throws JavaModelException { @@ -1321,7 +1321,7 @@ public void test402081() throws JavaModelException { String completeBehind = "long"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("longMethodName[METHOD_NAME_REFERENCE]{longMethodName, LX;, (Ljava.lang.String;)Ljava.lang.String;, null, null, longMethodName, (x), [183, 187], 35}", requestor.getResults()); + assertResults("longMethodName[METHOD_NAME_REFERENCE]{longMethodName, LX;, (Ljava.lang.String;)Ljava.lang.String;, null, null, longMethodName, (x), [183, 187], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=402081, [1.8][code complete] No proposals while completing at method/constructor references public void test402081a() throws JavaModelException { @@ -1351,7 +1351,7 @@ public void test402081a() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "longMethodName[METHOD_NAME_REFERENCE]{longMethodName, Ltest.X;, (Ljava.lang.String;)Ljava.lang.String;, longMethodName, (x), 35}", + "longMethodName[METHOD_NAME_REFERENCE]{longMethodName, Ltest.X;, (Ljava.lang.String;)Ljava.lang.String;, longMethodName, (x), " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=402081, [1.8][code complete] No proposals while completing at method/constructor references @@ -1377,7 +1377,7 @@ public void test402081b() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "longMethodName[METHOD_NAME_REFERENCE]{longMethodName, Ltest.X<Ljava.lang.String;>;, (Ljava.lang.String;)Ljava.lang.String;, longMethodName, (x), 35}", + "longMethodName[METHOD_NAME_REFERENCE]{longMethodName, Ltest.X<Ljava.lang.String;>;, (Ljava.lang.String;)Ljava.lang.String;, longMethodName, (x), " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=402081, [1.8][code complete] No proposals while completing at method/constructor references @@ -1406,7 +1406,7 @@ public void test402081c() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "longMethodName[METHOD_NAME_REFERENCE]{longMethodName, Ltest.Y;, (Ljava.lang.String;)Ljava.lang.String;, longMethodName, (x), 35}", + "longMethodName[METHOD_NAME_REFERENCE]{longMethodName, Ltest.Y;, (Ljava.lang.String;)Ljava.lang.String;, longMethodName, (x), " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=402081, [1.8][code complete] No proposals while completing at method/constructor references @@ -1435,7 +1435,7 @@ public void test402081d() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "longMethodName[METHOD_NAME_REFERENCE]{longMethodName, Ltest.Y;, (Ljava.lang.String;)Ljava.lang.String;, longMethodName, (x), 35}", + "longMethodName[METHOD_NAME_REFERENCE]{longMethodName, Ltest.Y;, (Ljava.lang.String;)Ljava.lang.String;, longMethodName, (x), " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=431402, [assist] NPE in AssistParser.triggerRecoveryUponLambdaClosure:483 using Content Assist @@ -1462,7 +1462,7 @@ public void test431402() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "asList[LOCAL_VARIABLE_REF]{asList, null, Ljava.lang.Object;, asList, null, 47}", + "asList[LOCAL_VARIABLE_REF]{asList, null, Ljava.lang.Object;, asList, null, " + (R_DEFAULT + 42) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=432527, Content Assist crashes sometimes using JDK8 @@ -1532,7 +1532,7 @@ public void test432527() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "removeNodeFromGraph[METHOD_REF]{removeNodeFromGraph(), Ltest.X;, (Ltest.X$Node;)V, removeNodeFromGraph, (node), 27}", + "removeNodeFromGraph[METHOD_REF]{removeNodeFromGraph(), Ltest.X;, (Ltest.X$Node;)V, removeNodeFromGraph, (node), " + (R_DEFAULT + 22) + "}", requestor.getResults()); } //https://bugs.eclipse.org/bugs/show_bug.cgi?id=430441, [compiler] NPE in ImplicitNullAnnotationVerifier.collectOverriddenMethods from Content Assist in a .jpage file @@ -1579,8 +1579,8 @@ public void test430656() throws JavaModelException { int cursorLocation = str.indexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "getClass[METHOD_NAME_REFERENCE]{getClass, Ljava.lang.Object;, ()Ljava.lang.Class<*>;, getClass, null, 35}\n" + - "getLastName[METHOD_NAME_REFERENCE]{getLastName, Ltest.Person;, ()Ljava.lang.String;, getLastName, null, 35}", + "getClass[METHOD_NAME_REFERENCE]{getClass, Ljava.lang.Object;, ()Ljava.lang.Class<*>;, getClass, null, " + (R_DEFAULT + 30) + "}\n" + + "getLastName[METHOD_NAME_REFERENCE]{getLastName, Ltest.Person;, ()Ljava.lang.String;, getLastName, null, " + (R_DEFAULT + 30) + "}", requestor.getResults()); } //https://bugs.eclipse.org/bugs/show_bug.cgi?id=433178 @@ -1656,7 +1656,7 @@ public void test435219() throws JavaModelException { String completeBehind = "System.o"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("out[FIELD_REF]{out, Ljava.lang.System;, Ljava.io.PrintStream;, null, null, out, null, [83, 84], 26}", requestor.getResults()); + assertResults("out[FIELD_REF]{out, Ljava.lang.System;, Ljava.io.PrintStream;, null, null, out, null, [83, 84], " + (R_DEFAULT + 21) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435219, [1.8][content assist] No proposals for some closure cases public void test435219a() throws JavaModelException { @@ -1675,27 +1675,27 @@ public void test435219a() throws JavaModelException { String completeBehind = "System.out.p"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("print[METHOD_REF]{print(), Ljava.io.PrintStream;, (C)V, null, null, print, (arg0), [87, 88], 35}\n" + - "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (D)V, null, null, print, (arg0), [87, 88], 35}\n" + - "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (F)V, null, null, print, (arg0), [87, 88], 35}\n" + - "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (I)V, null, null, print, (arg0), [87, 88], 35}\n" + - "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (J)V, null, null, print, (arg0), [87, 88], 35}\n" + - "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (Ljava.lang.Object;)V, null, null, print, (arg0), [87, 88], 35}\n" + - "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (Ljava.lang.String;)V, null, null, print, (arg0), [87, 88], 35}\n" + - "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (Z)V, null, null, print, (arg0), [87, 88], 35}\n" + - "print[METHOD_REF]{print(), Ljava.io.PrintStream;, ([C)V, null, null, print, (arg0), [87, 88], 35}\n" + - "printf[METHOD_REF]{printf(), Ljava.io.PrintStream;, (Ljava.lang.String;[Ljava.lang.Object;)Ljava.io.PrintStream;, null, null, printf, (arg0, arg1), [87, 88], 35}\n" + - "printf[METHOD_REF]{printf(), Ljava.io.PrintStream;, (Ljava.util.Locale;Ljava.lang.String;[Ljava.lang.Object;)Ljava.io.PrintStream;, null, null, printf, (arg0, arg1, arg2), [87, 88], 35}\n" + - "println[METHOD_REF]{println(), Ljava.io.PrintStream;, ()V, null, null, println, null, [87, 88], 35}\n" + - "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (C)V, null, null, println, (arg0), [87, 88], 35}\n" + - "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (D)V, null, null, println, (arg0), [87, 88], 35}\n" + - "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (F)V, null, null, println, (arg0), [87, 88], 35}\n" + - "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (I)V, null, null, println, (arg0), [87, 88], 35}\n" + - "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (J)V, null, null, println, (arg0), [87, 88], 35}\n" + - "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (Ljava.lang.Object;)V, null, null, println, (arg0), [87, 88], 35}\n" + - "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (Ljava.lang.String;)V, null, null, println, (arg0), [87, 88], 35}\n" + - "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (Z)V, null, null, println, (arg0), [87, 88], 35}\n" + - "println[METHOD_REF]{println(), Ljava.io.PrintStream;, ([C)V, null, null, println, (arg0), [87, 88], 35}", requestor.getResults()); + assertResults("print[METHOD_REF]{print(), Ljava.io.PrintStream;, (C)V, null, null, print, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (D)V, null, null, print, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (F)V, null, null, print, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (I)V, null, null, print, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (J)V, null, null, print, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (Ljava.lang.Object;)V, null, null, print, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (Ljava.lang.String;)V, null, null, print, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "print[METHOD_REF]{print(), Ljava.io.PrintStream;, (Z)V, null, null, print, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "print[METHOD_REF]{print(), Ljava.io.PrintStream;, ([C)V, null, null, print, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "printf[METHOD_REF]{printf(), Ljava.io.PrintStream;, (Ljava.lang.String;[Ljava.lang.Object;)Ljava.io.PrintStream;, null, null, printf, (arg0, arg1), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "printf[METHOD_REF]{printf(), Ljava.io.PrintStream;, (Ljava.util.Locale;Ljava.lang.String;[Ljava.lang.Object;)Ljava.io.PrintStream;, null, null, printf, (arg0, arg1, arg2), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "println[METHOD_REF]{println(), Ljava.io.PrintStream;, ()V, null, null, println, null, [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (C)V, null, null, println, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (D)V, null, null, println, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (F)V, null, null, println, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (I)V, null, null, println, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (J)V, null, null, println, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (Ljava.lang.Object;)V, null, null, println, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (Ljava.lang.String;)V, null, null, println, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "println[METHOD_REF]{println(), Ljava.io.PrintStream;, (Z)V, null, null, println, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}\n" + + "println[METHOD_REF]{println(), Ljava.io.PrintStream;, ([C)V, null, null, println, (arg0), [87, 88], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435219, [1.8][content assist] No proposals for some closure cases public void test435219b() throws JavaModelException { @@ -1714,9 +1714,9 @@ public void test435219b() throws JavaModelException { String completeBehind = "st"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("start[METHOD_REF]{start(), Ljava.lang.Thread;, ()V, null, null, start, null, [103, 105], 35}\n" + - "stop[METHOD_REF]{stop(), Ljava.lang.Thread;, ()V, null, null, stop, null, [103, 105], 35}\n" + - "stop[METHOD_REF]{stop(), Ljava.lang.Thread;, (Ljava.lang.Throwable;)V, null, null, stop, (arg0), [103, 105], 35}", requestor.getResults()); + assertResults("start[METHOD_REF]{start(), Ljava.lang.Thread;, ()V, null, null, start, null, [103, 105], " + (R_DEFAULT + 30) + "}\n" + + "stop[METHOD_REF]{stop(), Ljava.lang.Thread;, ()V, null, null, stop, null, [103, 105], " + (R_DEFAULT + 30) + "}\n" + + "stop[METHOD_REF]{stop(), Ljava.lang.Thread;, (Ljava.lang.Throwable;)V, null, null, stop, (arg0), [103, 105], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435219, [1.8][content assist] No proposals for some closure cases public void test435219c() throws JavaModelException { @@ -1738,9 +1738,9 @@ public void test435219c() throws JavaModelException { String completeBehind = "x.h"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("hashCode[METHOD_REF]{hashCode(), Ljava.lang.Integer;, (I)I, null, null, hashCode, (arg0), [187, 188], 54}\n" + - "highestOneBit[METHOD_REF]{highestOneBit(), Ljava.lang.Integer;, (I)I, null, null, highestOneBit, (arg0), [187, 188], 54}\n" + - "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Integer;, ()I, null, null, hashCode, null, [187, 188], 65}", requestor.getResults()); + assertResults("hashCode[METHOD_REF]{hashCode(), Ljava.lang.Integer;, (I)I, null, null, hashCode, (arg0), [187, 188], " + (R_DEFAULT + 49) + "}\n" + + "highestOneBit[METHOD_REF]{highestOneBit(), Ljava.lang.Integer;, (I)I, null, null, highestOneBit, (arg0), [187, 188], " + (R_DEFAULT + 49) + "}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Integer;, ()I, null, null, hashCode, null, [187, 188], " + (R_DEFAULT + 60) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435219, [1.8][content assist] No proposals for some closure cases public void test435219d() throws JavaModelException { @@ -1762,27 +1762,27 @@ public void test435219d() throws JavaModelException { String completeBehind = "pri"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (C)V, null, null, print, null, [219, 222], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (D)V, null, null, print, null, [219, 222], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (F)V, null, null, print, null, [219, 222], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (I)V, null, null, print, null, [219, 222], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (J)V, null, null, print, null, [219, 222], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (Ljava.lang.Object;)V, null, null, print, null, [219, 222], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (Ljava.lang.String;)V, null, null, print, null, [219, 222], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (Z)V, null, null, print, null, [219, 222], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, ([C)V, null, null, print, null, [219, 222], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, ()V, null, null, println, null, [219, 222], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (C)V, null, null, println, null, [219, 222], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (D)V, null, null, println, null, [219, 222], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (F)V, null, null, println, null, [219, 222], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (I)V, null, null, println, null, [219, 222], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (J)V, null, null, println, null, [219, 222], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (Ljava.lang.Object;)V, null, null, println, null, [219, 222], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (Ljava.lang.String;)V, null, null, println, null, [219, 222], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (Z)V, null, null, println, null, [219, 222], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, ([C)V, null, null, println, null, [219, 222], 30}\n" + - "printf[METHOD_NAME_REFERENCE]{printf, Ljava.io.PrintStream;, (Ljava.lang.String;[Ljava.lang.Object;)Ljava.io.PrintStream;, null, null, printf, null, [219, 222], 35}\n" + - "printf[METHOD_NAME_REFERENCE]{printf, Ljava.io.PrintStream;, (Ljava.util.Locale;Ljava.lang.String;[Ljava.lang.Object;)Ljava.io.PrintStream;, null, null, printf, null, [219, 222], 35}", requestor.getResults()); + assertResults("print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (C)V, null, null, print, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (D)V, null, null, print, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (F)V, null, null, print, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (I)V, null, null, print, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (J)V, null, null, print, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (Ljava.lang.Object;)V, null, null, print, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (Ljava.lang.String;)V, null, null, print, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (Z)V, null, null, print, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, ([C)V, null, null, print, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, ()V, null, null, println, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (C)V, null, null, println, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (D)V, null, null, println, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (F)V, null, null, println, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (I)V, null, null, println, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (J)V, null, null, println, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (Ljava.lang.Object;)V, null, null, println, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (Ljava.lang.String;)V, null, null, println, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (Z)V, null, null, println, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, ([C)V, null, null, println, null, [219, 222], " + (R_DEFAULT + 25) + "}\n" + + "printf[METHOD_NAME_REFERENCE]{printf, Ljava.io.PrintStream;, (Ljava.lang.String;[Ljava.lang.Object;)Ljava.io.PrintStream;, null, null, printf, null, [219, 222], " + (R_DEFAULT + 30) + "}\n" + + "printf[METHOD_NAME_REFERENCE]{printf, Ljava.io.PrintStream;, (Ljava.util.Locale;Ljava.lang.String;[Ljava.lang.Object;)Ljava.io.PrintStream;, null, null, printf, null, [219, 222], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435219, [1.8][content assist] No proposals for some closure cases public void test435219e() throws JavaModelException { @@ -1806,9 +1806,9 @@ public void test435219e() throws JavaModelException { String completeBehind = "dou"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("doubleToLongBits[METHOD_REF]{doubleToLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToLongBits, (arg0), [355, 358], 24}\n" + - "doubleToRawLongBits[METHOD_REF]{doubleToRawLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToRawLongBits, (arg0), [355, 358], 24}\n" + - "doubleValue[METHOD_REF]{doubleValue(), Ljava.lang.Double;, ()D, null, null, doubleValue, null, [355, 358], 35}", requestor.getResults()); + assertResults("doubleToLongBits[METHOD_REF]{doubleToLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToLongBits, (arg0), [355, 358], " + (R_DEFAULT + 19) + "}\n" + + "doubleToRawLongBits[METHOD_REF]{doubleToRawLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToRawLongBits, (arg0), [355, 358], " + (R_DEFAULT + 19) + "}\n" + + "doubleValue[METHOD_REF]{doubleValue(), Ljava.lang.Double;, ()D, null, null, doubleValue, null, [355, 358], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435219, [1.8][content assist] No proposals for some closure cases public void test435219f() throws JavaModelException { @@ -1832,8 +1832,8 @@ public void test435219f() throws JavaModelException { String completeBehind = "g"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [391, 392], 35}\n" + - "get[METHOD_REF]{get(), Ljava.util.Optional<Ljava.lang.Double;>;, ()Ljava.lang.Double;, null, null, get, null, [391, 392], 55}", requestor.getResults()); + assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [391, 392], " + (R_DEFAULT + 30) + "}\n" + + "get[METHOD_REF]{get(), Ljava.util.Optional<Ljava.lang.Double;>;, ()Ljava.lang.Double;, null, null, get, null, [391, 392], " + (R_DEFAULT + 50) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435219, [1.8][content assist] No proposals for some closure cases public void test435219g() throws JavaModelException { @@ -1857,9 +1857,9 @@ public void test435219g() throws JavaModelException { String completeBehind = "dou"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("doubleToLongBits[METHOD_REF]{doubleToLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToLongBits, (arg0), [376, 379], 54}\n" + - "doubleToRawLongBits[METHOD_REF]{doubleToRawLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToRawLongBits, (arg0), [376, 379], 54}\n" + - "doubleValue[METHOD_REF]{doubleValue(), Ljava.lang.Double;, ()D, null, null, doubleValue, null, [376, 379], 65}", requestor.getResults()); + assertResults("doubleToLongBits[METHOD_REF]{doubleToLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToLongBits, (arg0), [376, 379], " + (R_DEFAULT + 49) + "}\n" + + "doubleToRawLongBits[METHOD_REF]{doubleToRawLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToRawLongBits, (arg0), [376, 379], " + (R_DEFAULT + 49) + "}\n" + + "doubleValue[METHOD_REF]{doubleValue(), Ljava.lang.Double;, ()D, null, null, doubleValue, null, [376, 379], " + (R_DEFAULT + 60) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435682, [1.8] content assist not working inside lambda expression public void test435682() throws JavaModelException { @@ -1881,7 +1881,7 @@ public void test435682() throws JavaModelException { String completeBehind = "so.tr"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("trim[METHOD_REF]{trim(), Ljava.lang.String;, ()Ljava.lang.String;, null, null, trim, null, [237, 239], 35}", requestor.getResults()); + assertResults("trim[METHOD_REF]{trim(), Ljava.lang.String;, ()Ljava.lang.String;, null, null, trim, null, [237, 239], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435682, [1.8] content assist not working inside lambda expression public void test435682a() throws JavaModelException { @@ -1903,7 +1903,7 @@ public void test435682a() throws JavaModelException { String completeBehind = "so.tr"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("trim[METHOD_REF]{trim(), Ljava.lang.String;, ()Ljava.lang.String;, null, null, trim, null, [246, 248], 35}", requestor.getResults()); + assertResults("trim[METHOD_REF]{trim(), Ljava.lang.String;, ()Ljava.lang.String;, null, null, trim, null, [246, 248], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=430667, [1.8][content assist] no proposals around lambda as a field public void test430667() throws JavaModelException { @@ -1929,8 +1929,8 @@ public void test430667() throws JavaModelException { String completeBehind = "D_F"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("D_F[POTENTIAL_METHOD_DECLARATION]{D_F, LD_DemoRefactorings;, ()V, null, null, D_F, null, [195, 198], 14}\n" + - "D_FI[TYPE_REF]{D_FI, , LD_FI;, null, null, null, null, [195, 198], 27}", requestor.getResults()); + assertResults("D_F[POTENTIAL_METHOD_DECLARATION]{D_F, LD_DemoRefactorings;, ()V, null, null, D_F, null, [195, 198], " + (R_DEFAULT + 9) + "}\n" + + "D_FI[TYPE_REF]{D_FI, , LD_FI;, null, null, null, null, [195, 198], " + (R_DEFAULT + 22) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=430667, [1.8][content assist] no proposals around lambda as a field public void test430667a() throws JavaModelException { @@ -1957,8 +1957,8 @@ public void test430667a() throws JavaModelException { String completeBehind = "/*HERE*/D_F"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("D_F[POTENTIAL_METHOD_DECLARATION]{D_F, LD_DemoRefactorings;, ()V, null, null, D_F, null, [150, 153], 14}\n" + - "D_FI[TYPE_REF]{D_FI, , LD_FI;, null, null, null, null, [150, 153], 27}", requestor.getResults()); + assertResults("D_F[POTENTIAL_METHOD_DECLARATION]{D_F, LD_DemoRefactorings;, ()V, null, null, D_F, null, [150, 153], " + (R_DEFAULT + 9) + "}\n" + + "D_FI[TYPE_REF]{D_FI, , LD_FI;, null, null, null, null, [150, 153], " + (R_DEFAULT + 22) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=430667, [1.8][content assist] no proposals around lambda as a field public void test430667b() throws JavaModelException { @@ -1984,8 +1984,8 @@ public void test430667b() throws JavaModelException { String completeBehind = "/*HERE*/D_F"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("D_F[POTENTIAL_METHOD_DECLARATION]{D_F, LD_DemoRefactorings;, ()V, null, null, D_F, null, [36, 39], 14}\n" + - "D_FI[TYPE_REF]{D_FI, , LD_FI;, null, null, null, null, [36, 39], 27}", requestor.getResults()); + assertResults("D_F[POTENTIAL_METHOD_DECLARATION]{D_F, LD_DemoRefactorings;, ()V, null, null, D_F, null, [36, 39], " + (R_DEFAULT + 9) + "}\n" + + "D_FI[TYPE_REF]{D_FI, , LD_FI;, null, null, null, null, [36, 39], " + (R_DEFAULT + 22) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=443932, [1.8][code complete] method reference proposals not applied when caret inside method name public void test443932() throws JavaModelException { @@ -2003,15 +2003,15 @@ public void test443932() throws JavaModelException { String completeBehind = "to"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("toBinaryString[METHOD_NAME_REFERENCE]{toBinaryString, Ljava.lang.Integer;, (I)Ljava.lang.String;, null, null, toBinaryString, null, [90, 98], 24}\n" + - "toHexString[METHOD_NAME_REFERENCE]{toHexString, Ljava.lang.Integer;, (I)Ljava.lang.String;, null, null, toHexString, null, [90, 98], 24}\n" + - "toOctalString[METHOD_NAME_REFERENCE]{toOctalString, Ljava.lang.Integer;, (I)Ljava.lang.String;, null, null, toOctalString, null, [90, 98], 24}\n" + - "toString[METHOD_NAME_REFERENCE]{toString, Ljava.lang.Integer;, (I)Ljava.lang.String;, null, null, toString, null, [90, 98], 24}\n" + - "toString[METHOD_NAME_REFERENCE]{toString, Ljava.lang.Integer;, (II)Ljava.lang.String;, null, null, toString, null, [90, 98], 24}\n" + - "toUnsignedLong[METHOD_NAME_REFERENCE]{toUnsignedLong, Ljava.lang.Integer;, (I)J, null, null, toUnsignedLong, null, [90, 98], 24}\n" + - "toUnsignedString[METHOD_NAME_REFERENCE]{toUnsignedString, Ljava.lang.Integer;, (I)Ljava.lang.String;, null, null, toUnsignedString, null, [90, 98], 24}\n" + - "toUnsignedString[METHOD_NAME_REFERENCE]{toUnsignedString, Ljava.lang.Integer;, (II)Ljava.lang.String;, null, null, toUnsignedString, null, [90, 98], 24}\n" + - "toString[METHOD_NAME_REFERENCE]{toString, Ljava.lang.Integer;, ()Ljava.lang.String;, null, null, toString, null, [90, 98], 35}", requestor.getResults()); + assertResults("toBinaryString[METHOD_NAME_REFERENCE]{toBinaryString, Ljava.lang.Integer;, (I)Ljava.lang.String;, null, null, toBinaryString, null, [90, 98], " + (R_DEFAULT + 19) + "}\n" + + "toHexString[METHOD_NAME_REFERENCE]{toHexString, Ljava.lang.Integer;, (I)Ljava.lang.String;, null, null, toHexString, null, [90, 98], " + (R_DEFAULT + 19) + "}\n" + + "toOctalString[METHOD_NAME_REFERENCE]{toOctalString, Ljava.lang.Integer;, (I)Ljava.lang.String;, null, null, toOctalString, null, [90, 98], " + (R_DEFAULT + 19) + "}\n" + + "toString[METHOD_NAME_REFERENCE]{toString, Ljava.lang.Integer;, (I)Ljava.lang.String;, null, null, toString, null, [90, 98], " + (R_DEFAULT + 19) + "}\n" + + "toString[METHOD_NAME_REFERENCE]{toString, Ljava.lang.Integer;, (II)Ljava.lang.String;, null, null, toString, null, [90, 98], " + (R_DEFAULT + 19) + "}\n" + + "toUnsignedLong[METHOD_NAME_REFERENCE]{toUnsignedLong, Ljava.lang.Integer;, (I)J, null, null, toUnsignedLong, null, [90, 98], " + (R_DEFAULT + 19) + "}\n" + + "toUnsignedString[METHOD_NAME_REFERENCE]{toUnsignedString, Ljava.lang.Integer;, (I)Ljava.lang.String;, null, null, toUnsignedString, null, [90, 98], " + (R_DEFAULT + 19) + "}\n" + + "toUnsignedString[METHOD_NAME_REFERENCE]{toUnsignedString, Ljava.lang.Integer;, (II)Ljava.lang.String;, null, null, toUnsignedString, null, [90, 98], " + (R_DEFAULT + 19) + "}\n" + + "toString[METHOD_NAME_REFERENCE]{toString, Ljava.lang.Integer;, ()Ljava.lang.String;, null, null, toString, null, [90, 98], " + (R_DEFAULT + 30) + "}", requestor.getResults()); assertTrue(str.substring(90, 98).equals("toString")); } @@ -2034,8 +2034,8 @@ public void test444300() throws JavaModelException { String completeBehind = "so.ch"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("charAt[METHOD_REF]{charAt(), Ljava.lang.String;, (I)C, null, null, charAt, (arg0), [232, 234], 35}\n" + - "chars[METHOD_REF]{chars(), Ljava.lang.CharSequence;, ()Ljava.util.stream.IntStream;, null, null, chars, null, [232, 234], 35}", requestor.getResults()); + assertResults("charAt[METHOD_REF]{charAt(), Ljava.lang.String;, (I)C, null, null, charAt, (arg0), [232, 234], " + (R_DEFAULT + 30) + "}\n" + + "chars[METHOD_REF]{chars(), Ljava.lang.CharSequence;, ()Ljava.util.stream.IntStream;, null, null, chars, null, [232, 234], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435219, [1.8][content assist] No proposals for some closure cases // https://bugs.eclipse.org/bugs/show_bug.cgi?id=444300, [1.8] content assist not working inside lambda expression in case of fields @@ -2056,9 +2056,9 @@ public void test435219h() throws JavaModelException { String completeBehind = "x.h"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("hashCode[METHOD_REF]{hashCode(), Ljava.lang.Integer;, (I)I, null, null, hashCode, (arg0), [164, 165], 54}\n" + - "highestOneBit[METHOD_REF]{highestOneBit(), Ljava.lang.Integer;, (I)I, null, null, highestOneBit, (arg0), [164, 165], 54}\n" + - "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Integer;, ()I, null, null, hashCode, null, [164, 165], 65}", requestor.getResults()); + assertResults("hashCode[METHOD_REF]{hashCode(), Ljava.lang.Integer;, (I)I, null, null, hashCode, (arg0), [164, 165], " + (R_DEFAULT + 49) + "}\n" + + "highestOneBit[METHOD_REF]{highestOneBit(), Ljava.lang.Integer;, (I)I, null, null, highestOneBit, (arg0), [164, 165], " + (R_DEFAULT + 49) + "}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.lang.Integer;, ()I, null, null, hashCode, null, [164, 165], " + (R_DEFAULT + 60) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435219, [1.8][content assist] No proposals for some closure cases // https://bugs.eclipse.org/bugs/show_bug.cgi?id=444300, [1.8] content assist not working inside lambda expression in case of fields @@ -2079,27 +2079,27 @@ public void test435219i() throws JavaModelException { String completeBehind = "pri"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (C)V, null, null, print, null, [188, 191], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (D)V, null, null, print, null, [188, 191], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (F)V, null, null, print, null, [188, 191], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (I)V, null, null, print, null, [188, 191], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (J)V, null, null, print, null, [188, 191], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (Ljava.lang.Object;)V, null, null, print, null, [188, 191], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (Ljava.lang.String;)V, null, null, print, null, [188, 191], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (Z)V, null, null, print, null, [188, 191], 30}\n" + - "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, ([C)V, null, null, print, null, [188, 191], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, ()V, null, null, println, null, [188, 191], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (C)V, null, null, println, null, [188, 191], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (D)V, null, null, println, null, [188, 191], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (F)V, null, null, println, null, [188, 191], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (I)V, null, null, println, null, [188, 191], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (J)V, null, null, println, null, [188, 191], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (Ljava.lang.Object;)V, null, null, println, null, [188, 191], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (Ljava.lang.String;)V, null, null, println, null, [188, 191], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (Z)V, null, null, println, null, [188, 191], 30}\n" + - "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, ([C)V, null, null, println, null, [188, 191], 30}\n" + - "printf[METHOD_NAME_REFERENCE]{printf, Ljava.io.PrintStream;, (Ljava.lang.String;[Ljava.lang.Object;)Ljava.io.PrintStream;, null, null, printf, null, [188, 191], 35}\n" + - "printf[METHOD_NAME_REFERENCE]{printf, Ljava.io.PrintStream;, (Ljava.util.Locale;Ljava.lang.String;[Ljava.lang.Object;)Ljava.io.PrintStream;, null, null, printf, null, [188, 191], 35}", requestor.getResults()); + assertResults("print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (C)V, null, null, print, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (D)V, null, null, print, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (F)V, null, null, print, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (I)V, null, null, print, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (J)V, null, null, print, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (Ljava.lang.Object;)V, null, null, print, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (Ljava.lang.String;)V, null, null, print, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, (Z)V, null, null, print, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "print[METHOD_NAME_REFERENCE]{print, Ljava.io.PrintStream;, ([C)V, null, null, print, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, ()V, null, null, println, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (C)V, null, null, println, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (D)V, null, null, println, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (F)V, null, null, println, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (I)V, null, null, println, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (J)V, null, null, println, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (Ljava.lang.Object;)V, null, null, println, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (Ljava.lang.String;)V, null, null, println, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, (Z)V, null, null, println, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "println[METHOD_NAME_REFERENCE]{println, Ljava.io.PrintStream;, ([C)V, null, null, println, null, [188, 191], " + (R_DEFAULT + 25) + "}\n" + + "printf[METHOD_NAME_REFERENCE]{printf, Ljava.io.PrintStream;, (Ljava.lang.String;[Ljava.lang.Object;)Ljava.io.PrintStream;, null, null, printf, null, [188, 191], " + (R_DEFAULT + 30) + "}\n" + + "printf[METHOD_NAME_REFERENCE]{printf, Ljava.io.PrintStream;, (Ljava.util.Locale;Ljava.lang.String;[Ljava.lang.Object;)Ljava.io.PrintStream;, null, null, printf, null, [188, 191], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435219, [1.8][content assist] No proposals for some closure cases // https://bugs.eclipse.org/bugs/show_bug.cgi?id=444300, [1.8] content assist not working inside lambda expression in case of fields @@ -2121,9 +2121,9 @@ public void test435219j() throws JavaModelException { String completeBehind = "dou"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("doubleToLongBits[METHOD_REF]{doubleToLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToLongBits, (arg0), [235, 238], 24}\n" + - "doubleToRawLongBits[METHOD_REF]{doubleToRawLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToRawLongBits, (arg0), [235, 238], 24}\n" + - "doubleValue[METHOD_REF]{doubleValue(), Ljava.lang.Double;, ()D, null, null, doubleValue, null, [235, 238], 35}", requestor.getResults()); + assertResults("doubleToLongBits[METHOD_REF]{doubleToLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToLongBits, (arg0), [235, 238], " + (R_DEFAULT + 19) + "}\n" + + "doubleToRawLongBits[METHOD_REF]{doubleToRawLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToRawLongBits, (arg0), [235, 238], " + (R_DEFAULT + 19) + "}\n" + + "doubleValue[METHOD_REF]{doubleValue(), Ljava.lang.Double;, ()D, null, null, doubleValue, null, [235, 238], " + (R_DEFAULT + 30) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435219, [1.8][content assist] No proposals for some closure cases // https://bugs.eclipse.org/bugs/show_bug.cgi?id=444300, [1.8] content assist not working inside lambda expression in case of fields @@ -2145,8 +2145,8 @@ public void test435219k() throws JavaModelException { String completeBehind = "g"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [271, 272], 35}\n" + - "get[METHOD_REF]{get(), Ljava.util.Optional<Ljava.lang.Double;>;, ()Ljava.lang.Double;, null, null, get, null, [271, 272], 55}", requestor.getResults()); + assertResults("getClass[METHOD_REF]{getClass(), Ljava.lang.Object;, ()Ljava.lang.Class<*>;, null, null, getClass, null, [271, 272], " + (R_DEFAULT + 30) + "}\n" + + "get[METHOD_REF]{get(), Ljava.util.Optional<Ljava.lang.Double;>;, ()Ljava.lang.Double;, null, null, get, null, [271, 272], " + (R_DEFAULT + 50) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435219, [1.8][content assist] No proposals for some closure cases // https://bugs.eclipse.org/bugs/show_bug.cgi?id=444300, [1.8] content assist not working inside lambda expression in case of fields @@ -2168,22 +2168,22 @@ public void test435219l() throws JavaModelException { String completeBehind = "dou"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("doubleToLongBits[METHOD_REF]{doubleToLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToLongBits, (arg0), [256, 259], 54}\n" + - "doubleToRawLongBits[METHOD_REF]{doubleToRawLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToRawLongBits, (arg0), [256, 259], 54}\n" + - "doubleValue[METHOD_REF]{doubleValue(), Ljava.lang.Double;, ()D, null, null, doubleValue, null, [256, 259], 65}", requestor.getResults()); + assertResults("doubleToLongBits[METHOD_REF]{doubleToLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToLongBits, (arg0), [256, 259], " + (R_DEFAULT + 49) + "}\n" + + "doubleToRawLongBits[METHOD_REF]{doubleToRawLongBits(), Ljava.lang.Double;, (D)J, null, null, doubleToRawLongBits, (arg0), [256, 259], " + (R_DEFAULT + 49) + "}\n" + + "doubleValue[METHOD_REF]{doubleValue(), Ljava.lang.Double;, ()D, null, null, doubleValue, null, [256, 259], " + (R_DEFAULT + 60) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=435281, [1.8][code assist] No import or completion proposal for anonymous class inside lambda public void test435281() throws JavaModelException { this.workingCopies = new ICompilationUnit[3]; this.workingCopies[0] = getWorkingCopy( - "/Completion/src/p4a/FI1.java", + "/Completion/src/FI1.java", "package p4a;\n" + "@FunctionalInterface\n" + "public interface FI1<R> {\n" + " public R foo1();\n" + "}\n"); this.workingCopies[1] = getWorkingCopy( - "/Completion/src/p4a/FI2.java", + "/Completion/src/FI2.java", "package p4a;\n" + "@FunctionalInterface\n" + "public interface FI2 {\n" + @@ -2210,7 +2210,7 @@ public void test435281() throws JavaModelException { String completeBehind = "FI2"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[2].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("FI2[TYPE_REF]{p4a.FI2, p4a, Lp4a.FI2;, null, null, null, null, [104, 107], 28}", requestor.getResults()); + assertResults("FI2[TYPE_REF]{p4a.FI2, p4a, Lp4a.FI2;, null, null, null, null, [104, 107], " + (R_DEFAULT + 23) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=431811, content assist should propose keyword 'super' after type name public void test431811() throws JavaModelException { @@ -2238,7 +2238,7 @@ public void test431811() throws JavaModelException { String completeBehind = "su"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("super[KEYWORD]{super, null, null, null, null, super, null, [192, 194], 26}", requestor.getResults()); + assertResults("super[KEYWORD]{super, null, null, null, null, super, null, [192, 194], " + (R_DEFAULT + 21) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=447774, Auto complete does not work when using lambdas with cast public void test447774() throws JavaModelException { @@ -2300,8 +2300,8 @@ public void test449358() throws JavaModelException { String completeBehind = "/*HERE*/localMeth"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("localmethod1[METHOD_REF]{localmethod1(), LLambdaBug;, ()V, null, null, localmethod1, null, [181, 190], 17}\n" + - "localmethod2[METHOD_REF]{localmethod2(), LLambdaBug;, ()V, null, null, localmethod2, null, [181, 190], 17}", requestor.getResults()); + assertResults("localmethod1[METHOD_REF]{localmethod1(), LLambdaBug;, ()V, null, null, localmethod1, null, [181, 190], " + (R_DEFAULT + 12) + "}\n" + + "localmethod2[METHOD_REF]{localmethod2(), LLambdaBug;, ()V, null, null, localmethod2, null, [181, 190], " + (R_DEFAULT + 12) + "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=449358, Content assist inside lambda broken in all methods except last @@ -2333,8 +2333,8 @@ public void test449358a() throws JavaModelException { String completeBehind = "/*HERE*/localMeth"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - assertResults("localmethod1[METHOD_REF]{localmethod1(), LLambdaBug;, ()V, null, null, localmethod1, null, [282, 291], 17}\n" + - "localmethod2[METHOD_REF]{localmethod2(), LLambdaBug;, ()V, null, null, localmethod2, null, [282, 291], 17}", requestor.getResults()); + assertResults("localmethod1[METHOD_REF]{localmethod1(), LLambdaBug;, ()V, null, null, localmethod1, null, [282, 291], " + (R_DEFAULT + 12) + "}\n" + + "localmethod2[METHOD_REF]{localmethod2(), LLambdaBug;, ()V, null, null, localmethod2, null, [282, 291], " + (R_DEFAULT + 12) + "}", requestor.getResults()); } public void testBug459189_001() throws JavaModelException { @@ -2359,9 +2359,9 @@ public void testBug459189_001() throws JavaModelException { int cursorLocation = str.indexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "Retention[TYPE_REF]{java.lang.annotation.Retention, java.lang.annotation, Ljava.lang.annotation.Retention;, null, null, 14}\n"+ - "RetentionPolicy[TYPE_REF]{java.lang.annotation.RetentionPolicy, java.lang.annotation, Ljava.lang.annotation.RetentionPolicy;, null, null, 14}\n"+ - "return[KEYWORD]{return, null, null, return, null, 24}", + "Retention[TYPE_REF]{java.lang.annotation.Retention, java.lang.annotation, Ljava.lang.annotation.Retention;, null, null, " + (R_DEFAULT + 9) + "}\n"+ + "RetentionPolicy[TYPE_REF]{java.lang.annotation.RetentionPolicy, java.lang.annotation, Ljava.lang.annotation.RetentionPolicy;, null, null, " + (R_DEFAULT + 9) + "}\n"+ + "return[KEYWORD]{return, null, null, return, null, " + (R_DEFAULT + 19) + "}", requestor.getResults()); } public void testBug459189_002() throws JavaModelException { @@ -2386,9 +2386,9 @@ public void testBug459189_002() throws JavaModelException { int cursorLocation = str.indexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "Retention[TYPE_REF]{java.lang.annotation.Retention, java.lang.annotation, Ljava.lang.annotation.Retention;, null, null, 14}\n"+ - "RetentionPolicy[TYPE_REF]{java.lang.annotation.RetentionPolicy, java.lang.annotation, Ljava.lang.annotation.RetentionPolicy;, null, null, 14}\n" + - "return[KEYWORD]{return, null, null, return, null, 24}", + "Retention[TYPE_REF]{java.lang.annotation.Retention, java.lang.annotation, Ljava.lang.annotation.Retention;, null, null, " + (R_DEFAULT + 9) + "}\n"+ + "RetentionPolicy[TYPE_REF]{java.lang.annotation.RetentionPolicy, java.lang.annotation, Ljava.lang.annotation.RetentionPolicy;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "return[KEYWORD]{return, null, null, return, null, " + (R_DEFAULT + 19) + "}", requestor.getResults()); } public void testBug459189_003() throws JavaModelException { @@ -2413,8 +2413,8 @@ public void testBug459189_003() throws JavaModelException { int cursorLocation = str.indexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "catch[KEYWORD]{catch, null, null, catch, null, 24}\n"+ - "finally[KEYWORD]{finally, null, null, finally, null, 24}", + "catch[KEYWORD]{catch, null, null, catch, null, " + (R_DEFAULT + 19) + "}\n"+ + "finally[KEYWORD]{finally, null, null, finally, null, " + (R_DEFAULT + 19) + "}", requestor.getResults()); } public void testBug459189_004() throws JavaModelException { @@ -2439,7 +2439,7 @@ public void testBug459189_004() throws JavaModelException { int cursorLocation = str.indexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "while[KEYWORD]{while, null, null, while, null, 24}", + "while[KEYWORD]{while, null, null, while, null, " + (R_DEFAULT + 19) + "}", requestor.getResults()); } public void testBug460410() throws JavaModelException { @@ -2491,7 +2491,7 @@ public void testBug481564() throws JavaModelException { int cursorLocation = str.indexOf(completeBehind) ; this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "someMethod[METHOD_REF]{someMethod(), LX;, ()V, someMethod, null, 27}", requestor.getResults()); + "someMethod[METHOD_REF]{someMethod(), LX;, ()V, someMethod, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } //https://bugs.eclipse.org/bugs/show_bug.cgi?id=481215 public void testBug481215a() throws JavaModelException { @@ -2523,11 +2523,11 @@ public void testBug481215a() throws JavaModelException { int cursorLocation = str.indexOf(completeBehind) ; this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "ResourceBundle[TYPE_REF]{java.util.ResourceBundle, java.util, Ljava.util.ResourceBundle;, null, null, 14}\n" + - "ResponseCache[TYPE_REF]{java.net.ResponseCache, java.net, Ljava.net.ResponseCache;, null, null, 14}\n" + - "ResultSet[TYPE_REF]{java.sql.ResultSet, java.sql, Ljava.sql.ResultSet;, null, null, 14}\n" + - "ResultSetMetaData[TYPE_REF]{java.sql.ResultSetMetaData, java.sql, Ljava.sql.ResultSetMetaData;, null, null, 14}\n" + - "result[LOCAL_VARIABLE_REF]{result, null, Ljava.lang.String;, result, null, 27}", requestor.getResults()); + "ResourceBundle[TYPE_REF]{java.util.ResourceBundle, java.util, Ljava.util.ResourceBundle;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "ResponseCache[TYPE_REF]{java.net.ResponseCache, java.net, Ljava.net.ResponseCache;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "ResultSet[TYPE_REF]{java.sql.ResultSet, java.sql, Ljava.sql.ResultSet;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "ResultSetMetaData[TYPE_REF]{java.sql.ResultSetMetaData, java.sql, Ljava.sql.ResultSetMetaData;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "result[LOCAL_VARIABLE_REF]{result, null, Ljava.lang.String;, result, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } public void testBug481215b() throws JavaModelException { this.workingCopies = new ICompilationUnit[1]; @@ -2555,11 +2555,11 @@ public void testBug481215b() throws JavaModelException { int cursorLocation = str.indexOf(completeBehind) ; this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "ResourceBundle[TYPE_REF]{java.util.ResourceBundle, java.util, Ljava.util.ResourceBundle;, null, null, 14}\n" + - "ResponseCache[TYPE_REF]{java.net.ResponseCache, java.net, Ljava.net.ResponseCache;, null, null, 14}\n" + - "ResultSet[TYPE_REF]{java.sql.ResultSet, java.sql, Ljava.sql.ResultSet;, null, null, 14}\n" + - "ResultSetMetaData[TYPE_REF]{java.sql.ResultSetMetaData, java.sql, Ljava.sql.ResultSetMetaData;, null, null, 14}\n" + - "result[LOCAL_VARIABLE_REF]{result, null, Ljava.lang.String;, result, null, 27}", requestor.getResults()); + "ResourceBundle[TYPE_REF]{java.util.ResourceBundle, java.util, Ljava.util.ResourceBundle;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "ResponseCache[TYPE_REF]{java.net.ResponseCache, java.net, Ljava.net.ResponseCache;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "ResultSet[TYPE_REF]{java.sql.ResultSet, java.sql, Ljava.sql.ResultSet;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "ResultSetMetaData[TYPE_REF]{java.sql.ResultSetMetaData, java.sql, Ljava.sql.ResultSetMetaData;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "result[LOCAL_VARIABLE_REF]{result, null, Ljava.lang.String;, result, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } public void testBug481215c() throws JavaModelException { this.workingCopies = new ICompilationUnit[1]; @@ -2587,11 +2587,11 @@ public void testBug481215c() throws JavaModelException { int cursorLocation = str.indexOf(completeBehind) ; this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "ResourceBundle[TYPE_REF]{java.util.ResourceBundle, java.util, Ljava.util.ResourceBundle;, null, null, 14}\n" + - "ResponseCache[TYPE_REF]{java.net.ResponseCache, java.net, Ljava.net.ResponseCache;, null, null, 14}\n" + - "ResultSet[TYPE_REF]{java.sql.ResultSet, java.sql, Ljava.sql.ResultSet;, null, null, 14}\n" + - "ResultSetMetaData[TYPE_REF]{java.sql.ResultSetMetaData, java.sql, Ljava.sql.ResultSetMetaData;, null, null, 14}\n" + - "result[LOCAL_VARIABLE_REF]{result, null, Ljava.lang.String;, result, null, 27}", requestor.getResults()); + "ResourceBundle[TYPE_REF]{java.util.ResourceBundle, java.util, Ljava.util.ResourceBundle;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "ResponseCache[TYPE_REF]{java.net.ResponseCache, java.net, Ljava.net.ResponseCache;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "ResultSet[TYPE_REF]{java.sql.ResultSet, java.sql, Ljava.sql.ResultSet;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "ResultSetMetaData[TYPE_REF]{java.sql.ResultSetMetaData, java.sql, Ljava.sql.ResultSetMetaData;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "result[LOCAL_VARIABLE_REF]{result, null, Ljava.lang.String;, result, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } public void testBug481215d() throws JavaModelException { this.workingCopies = new ICompilationUnit[1]; @@ -2627,12 +2627,12 @@ public void testBug481215d() throws JavaModelException { int cursorLocation = str.indexOf(completeBehind) ; this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "ResourceBundle[TYPE_REF]{java.util.ResourceBundle, java.util, Ljava.util.ResourceBundle;, null, null, 14}\n" + - "ResponseCache[TYPE_REF]{java.net.ResponseCache, java.net, Ljava.net.ResponseCache;, null, null, 14}\n" + - "ResultSet[TYPE_REF]{java.sql.ResultSet, java.sql, Ljava.sql.ResultSet;, null, null, 14}\n" + - "ResultSetMetaData[TYPE_REF]{java.sql.ResultSetMetaData, java.sql, Ljava.sql.ResultSetMetaData;, null, null, 14}\n" + - "result[LOCAL_VARIABLE_REF]{result, null, Ljava.lang.String;, result, null, 27}\n" + - "result2[LOCAL_VARIABLE_REF]{result2, null, Ljava.lang.String;, result2, null, 27}", requestor.getResults()); + "ResourceBundle[TYPE_REF]{java.util.ResourceBundle, java.util, Ljava.util.ResourceBundle;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "ResponseCache[TYPE_REF]{java.net.ResponseCache, java.net, Ljava.net.ResponseCache;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "ResultSet[TYPE_REF]{java.sql.ResultSet, java.sql, Ljava.sql.ResultSet;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "ResultSetMetaData[TYPE_REF]{java.sql.ResultSetMetaData, java.sql, Ljava.sql.ResultSetMetaData;, null, null, " + (R_DEFAULT + 9) + "}\n" + + "result[LOCAL_VARIABLE_REF]{result, null, Ljava.lang.String;, result, null, " + (R_DEFAULT + 22) + "}\n" + + "result2[LOCAL_VARIABLE_REF]{result2, null, Ljava.lang.String;, result2, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } //https://bugs.eclipse.org/bugs/show_bug.cgi?id=484479 public void test484479() throws JavaModelException { diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests2.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests2.java index 827681ce6..ea37cbcd3 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests2.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests2.java @@ -5502,7 +5502,7 @@ public void testBug317264a() throws CoreException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); cu.codeComplete(cursorLocation, requestor); assertResults( - "Enum[TYPE_REF]{Enum, java.lang, Ljava.lang.Enum;, null, null, 17}", + "Enum[TYPE_REF]{Enum, java.lang, Ljava.lang.Enum;, null, null, " + (R_DEFAULT + 12) + "}", requestor.getResults()); } finally { @@ -5535,7 +5535,7 @@ public void testBug317264b() throws CoreException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); cu.codeComplete(cursorLocation, requestor); assertResults( - "Enum[TYPE_REF]{org.apache.commons.lang.enum.Enum, org.apache.commons.lang.enum, Lorg.apache.commons.lang.enum.Enum;, null, null, 14}", + "Enum[TYPE_REF]{org.apache.commons.lang.enum.Enum, org.apache.commons.lang.enum, Lorg.apache.commons.lang.enum.Enum;, null, null, " + (R_DEFAULT + 9) + "}", requestor.getResults()); } finally { @@ -5597,7 +5597,7 @@ public void testBug317264d() throws CoreException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); cu.codeComplete(cursorLocation, requestor); assertResults( - "org.apache.commons.lang.enum[PACKAGE_REF]{org.apache.commons.lang.enum.*;, org.apache.commons.lang.enum, null, null, null, 24}", + "org.apache.commons.lang.enum[PACKAGE_REF]{org.apache.commons.lang.enum.*;, org.apache.commons.lang.enum, null, null, null, " + (R_DEFAULT + 19) + "}", requestor.getResults()); } finally { @@ -6148,7 +6148,7 @@ public void testBug410207a() throws Exception { CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, false, false, true, true); cu.codeComplete(cursorLocation, requestor); assertResults( - "Main[TYPE_REF]{Main, p, Lp.Main;, null, null, 27}", + "Main[TYPE_REF]{Main, p, Lp.Main;, null, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } finally { deleteProject("P"); @@ -6206,7 +6206,7 @@ public void testBug410207b() throws Exception { CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, false, false, true, true); cu.codeComplete(cursorLocation, requestor); assertResults( - "Main[TYPE_REF]{Main, p, Lp.Main;, null, null, 27}", + "Main[TYPE_REF]{Main, p, Lp.Main;, null, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } finally { deleteProject("P"); @@ -6262,7 +6262,7 @@ public void testBug410207c() throws Exception { CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, false, false, true, true); cu.codeComplete(cursorLocation, requestor); assertResults( - "Main[TYPE_REF]{Main, p, Lp.Main;, null, null, 27}", + "Main[TYPE_REF]{Main, p, Lp.Main;, null, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } finally { deleteProjects(new String[] { "Lib", "P" }); @@ -6318,7 +6318,7 @@ public void testBug410207d() throws Exception { CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, false, false, true, true); cu.codeComplete(cursorLocation, requestor); assertResults( - "Main[TYPE_REF]{Main, p, Lp.Main;, null, null, 27}", + "Main[TYPE_REF]{Main, p, Lp.Main;, null, null, " + (R_DEFAULT + 22) + "}", requestor.getResults()); } finally { deleteProjects(new String[] { "Lib", "P" }); @@ -6409,7 +6409,7 @@ public void testBug376977() throws CoreException { this.workingCopies[2].codeComplete(cursorLocation, requestor, this.wcOwner, monitor); assertResults( - "Nested[TYPE_REF]{Nested, myannotations, Lmyannotations.Nested;, null, null, 47}", + "Nested[TYPE_REF]{Nested, myannotations, Lmyannotations.Nested;, null, null, " + (R_DEFAULT + 42) + "}", requestor.getResults()); } finally { deleteProject("P"); @@ -6483,7 +6483,7 @@ public void test479656() throws Exception { CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true, false, false, true, true); cu.codeComplete(cursorLocation, requestor); assertResults( - "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, 35}", + "toString[METHOD_REF]{toString(), Ljava.lang.Object;, ()Ljava.lang.String;, toString, null, " + (R_DEFAULT + 30) + "}", requestor.getResults()); } finally { deleteProject("P"); diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests_1_5.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests_1_5.java index 2a685d576..ac305745a 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests_1_5.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/CompletionTests_1_5.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -13503,8 +13503,8 @@ public void testCompletionOnExtends() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "ThisClassIsFinal[TYPE_REF]{ThisClassIsFinal, test, Ltest.ThisClassIsFinal;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED + R_CLASS) + "}\n" + - "ThisClassIsNotFinal[TYPE_REF]{ThisClassIsNotFinal, test, Ltest.ThisClassIsNotFinal;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED + R_CLASS) + "}", + "ThisClassIsFinal[TYPE_REF]{ThisClassIsFinal, test, Ltest.ThisClassIsFinal;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_EXPECTED_TYPE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" + + "ThisClassIsNotFinal[TYPE_REF]{ThisClassIsNotFinal, test, Ltest.ThisClassIsNotFinal;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_EXPECTED_TYPE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", requestor.getResults()); } @@ -13846,9 +13846,9 @@ public void testBug310423a() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "Inn.Inn2[TYPE_REF]{label.Inn.Inn2, label, Llabel.Inn$Inn2;, null, null, 44}\n" + - "In[TYPE_REF]{In, label, Llabel.In;, null, null, 47}\n" + - "Inn[TYPE_REF]{Inn, label, Llabel.Inn;, null, null, 47}", + "Inn.Inn2[TYPE_REF]{label.Inn.Inn2, label, Llabel.Inn$Inn2;, null, null, " + (R_DEFAULT + 39) + "}\n" + + "In[TYPE_REF]{In, label, Llabel.In;, null, null, " + (R_DEFAULT + 42) + "}\n" + + "Inn[TYPE_REF]{Inn, label, Llabel.Inn;, null, null, " + (R_DEFAULT + 42) + "}", requestor.getResults()); } @@ -13874,8 +13874,8 @@ public void testBug310423b() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "Inn.Inn2[TYPE_REF]{Inn2, label, Llabel.Inn$Inn2;, null, null, 44}\n" + - "Inn.Inn3[TYPE_REF]{Inn3, label, Llabel.Inn$Inn3;, null, null, 44}", + "Inn.Inn2[TYPE_REF]{Inn2, label, Llabel.Inn$Inn2;, null, null, " + (R_DEFAULT + 39) + "}\n" + + "Inn.Inn3[TYPE_REF]{Inn3, label, Llabel.Inn$Inn3;, null, null, " + (R_DEFAULT + 39) + "}", requestor.getResults()); } @@ -13891,8 +13891,10 @@ public void testBug343865a() throws JavaModelException { cu.codeComplete(cursorLocation, requestor); assertResults( - "name[ANNOTATION_ATTRIBUTE_REF]{name, Ltestxxx.YAAnnot;, Ljava.lang.String;, name, null, " + (R_NAME_FIRST_PREFIX + R_EXPECTED_TYPE + R_RESOLVED) + "}\n" + - "val[ANNOTATION_ATTRIBUTE_REF]{val, Ltestxxx.YAAnnot;, I, val, null, " + (R_NAME_FIRST_PREFIX + R_EXPECTED_TYPE + R_RESOLVED) + "}", + "name[ANNOTATION_ATTRIBUTE_REF]{name, Ltestxxx.YAAnnot;, Ljava.lang.String;, name, null, " + + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" + + "val[ANNOTATION_ATTRIBUTE_REF]{val, Ltestxxx.YAAnnot;, I, val, null, " + + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", requestor.getResults()); } @@ -13913,8 +13915,10 @@ public void testBug343865b() throws JavaModelException { requestor.getContext()); assertResults( - "xxyy[FIELD_REF]{xxyy, Ltestxxx.TestType2;, I, xxyy, null, " + (R_NAME_FIRST_PREFIX + R_EXPECTED_TYPE + R_RESOLVED) + "}\n" + - "xxyy1[FIELD_REF]{xxyy1, Ltestxxx.TestType2;, Ljava.lang.String;, xxyy1, null, " + (R_NAME_FIRST_PREFIX + R_EXPECTED_TYPE + R_RESOLVED + R_EXACT_EXPECTED_TYPE) + "}", + "xxyy[FIELD_REF]{xxyy, Ltestxxx.TestType2;, I, xxyy, null, " + + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" + + "xxyy1[FIELD_REF]{xxyy1, Ltestxxx.TestType2;, Ljava.lang.String;, xxyy1, null, " + + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED + R_EXACT_EXPECTED_TYPE) + "}", requestor.getResults()); } public void testBug351426() throws JavaModelException { @@ -13936,7 +13940,7 @@ public void testBug351426() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXPECTED_TYPE; + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED; assertResults( "expectedTypesSignatures={Ltest.X<Ljava.lang.String;>;}\n" + @@ -13965,7 +13969,7 @@ public void testBug351426b() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXPECTED_TYPE; + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED; assertResults( "expectedTypesSignatures={Ltest.X<Ljava.lang.String;>;}\n" + @@ -13996,7 +14000,7 @@ public void testBug351426c() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXPECTED_TYPE; + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED; assertResults( "expectedTypesSignatures={Ltest.X<Ljava.lang.String;>.X1<Ljava.lang.String;>;}\n" + @@ -14029,7 +14033,7 @@ public void testBug351426d() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXPECTED_TYPE; + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED; assertResults( "expectedTypesSignatures={Ltest.X<Ljava.lang.String;>.X1<Ljava.lang.Object;>.X11<Ljava.lang.String;>;}\n" + @@ -14061,7 +14065,7 @@ public void testBug351426e() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXPECTED_TYPE; + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED; assertResults( "expectedTypesSignatures={Ltest.X$X1<Ljava.lang.String;>;}\n" + @@ -14091,7 +14095,7 @@ public void testBug351426f() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXPECTED_TYPE; + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED; assertResults( "expectedTypesSignatures={Ltest.X<Ljava.lang.String;>;}\n" + @@ -14122,7 +14126,7 @@ public void testBug351426g() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXPECTED_TYPE; + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED; assertResults( "expectedTypesSignatures={Ltest.X<TT;>.X1<Ljava.lang.String;>;}\n" + @@ -14153,7 +14157,7 @@ public void testBug351426h() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXPECTED_TYPE; + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED; assertResults( "expectedTypesSignatures={Ltest.X$X1<Ljava.lang.String;>;}\n" + @@ -14181,7 +14185,7 @@ public void testBug351426i() throws JavaModelException { this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXPECTED_TYPE; + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED; assertResults( "expectedTypesSignatures={Ltest.X<Ljava.lang.String;>;}\n" + @@ -14213,7 +14217,7 @@ public void testBug351426j() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXPECTED_TYPE; + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED; assertResults( "expectedTypesSignatures={Ltest.X$X1<Ljava.lang.String;>;}\n" + @@ -14243,7 +14247,7 @@ public void testBug351426k() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXPECTED_TYPE; + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED; assertResults( "expectedTypesSignatures={Ltest.X<Ljava.lang.String;Ljava.lang.String;>;}\n" + @@ -14278,7 +14282,7 @@ public void testBug351426l() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); - int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXPECTED_TYPE; + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED; assertResults( "expectedTypesSignatures={Ltest.X1<Ljava.lang.String;>;}\n" + @@ -14305,7 +14309,8 @@ public void test361963() throws JavaModelException { int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "X<T>[TYPE_REF]{, , LX<TT;>;, null, null, replace[116, 116], token[116, 116], 51}", + "X<T>[TYPE_REF]{, , LX<TT;>;, null, null, replace[116, 116], token[116, 116], " + + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXACT_NAME + R_CASE + R_EXPECTED_TYPE + R_UNQUALIFIED + R_NON_RESTRICTED)+ "}", requestor.getResults()); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=326610 diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExclusionPatternsTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExclusionPatternsTests.java index 41ec92c7a..bfede2da1 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExclusionPatternsTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExclusionPatternsTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -289,12 +289,16 @@ public void testCreateExcludedPackage() throws CoreException { root.getNonJavaResources()); } /* - * Ensure that crearing an excluded package doesn't make it appear as a child of its package fragment root but it is a non-java resource. + * Ensure that creating an excluded package doesn't make it appear as a child of its package fragment root but it is a non-java resource. * (regression test for bug 65637 [model] Excluded package still in Java model) */ public void testCreateExcludedPackage2() throws CoreException { setClasspath(new String[] {"/P/src", "org/*|org/eclipse/*"}); + // Trigger population of cache to check if it is properly invalidated by the delta processor. + // See http://bugs.eclipse.org/500714 + getPackageFragmentRoot("/P/src").getChildren(); + clearDeltas(); createFolder("/P/src/org/eclipse/mypack"); diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java index 680ae6b30..bc2e74417 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.jdt.core.tests.model; +import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -350,6 +351,19 @@ public class ExternalAnnotations18Test extends ModifyingResourceTests { return ((AbstractCompilerTest.getPossibleComplianceLevels() & AbstractCompilerTest.F_1_8) != 0); } + String readFully(IFile file) throws IOException, CoreException { + try (BufferedInputStream bs = new BufferedInputStream(file.getContents())) { + int available = 0; + StringBuilder buf = new StringBuilder(); + while ((available = bs.available()) > 0) { + byte[] contents = new byte[available]; + bs.read(contents); + buf.append(new String(contents)); + } + return buf.toString(); + } + } + /** Perform full build. */ public void test1FullBuild() throws Exception { setupJavaProject("Test1"); @@ -1012,11 +1026,104 @@ public class ExternalAnnotations18Test extends ModifyingResourceTests { MergeStrategy.OVERWRITE_ANNOTATIONS, null); assertTrue("file should exist", annotationFile.exists()); - // check that the error is even worse now: + // check that the error is resolved now: reconciled = cu.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); assertNoProblems(reconciled.getProblems()); } + + public void testAnnotateConstructorParameter() throws Exception { + myCreateJavaProject("TestLibs"); + String lib1Content = + "package libs;\n" + + "\n" + + "public class Lib1<U> {\n" + + " public Lib1(int ignore, U string) {}\n" + + "}\n"; + addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] { + "/UnannotatedLib/libs/Lib1.java", + lib1Content + }, null); + + // type check sources: + IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null); + ICompilationUnit cu = fragment.createCompilationUnit("Test1.java", + "package tests;\n" + + "import org.eclipse.jdt.annotation.*;\n" + + "import libs.Lib1;\n" + + "\n" + + "public class Test1 {\n" + + " Object test0() {\n" + + " Lib1<@NonNull String> lib = new Lib1<>(1, null);\n" + + " return lib;\n" + + " }\n" + + "}\n", + true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor()); + CompilationUnit reconciled = cu.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); + assertProblems(reconciled.getProblems(), new String[] { + "Pb(910) Null type mismatch: required '@NonNull String' but the provided value is null", + }, new int[] { 7 }); + + // acquire library AST: + IType type = this.project.findType("libs.Lib1"); + ICompilationUnit libWorkingCopy = type.getClassFile().getWorkingCopy(this.wcOwner, null); + ASTParser parser = ASTParser.newParser(AST.JLS8); + parser.setSource(libWorkingCopy); + parser.setResolveBindings(true); + parser.setStatementsRecovery(false); + parser.setBindingsRecovery(false); + CompilationUnit unit = (CompilationUnit) parser.createAST(null); + libWorkingCopy.discardWorkingCopy(); + + // find type binding: + int start = lib1Content.indexOf("U string"); + ASTNode name = NodeFinder.perform(unit, start, 0); + assertTrue("should be simple name", name.getNodeType() == ASTNode.SIMPLE_NAME); + ASTNode method = name.getParent().getParent().getParent(); + IMethodBinding methodBinding = ((MethodDeclaration)method).resolveBinding(); + + // find annotation file (not yet existing): + IFile annotationFile = ExternalAnnotationUtil.getAnnotationFile(this.project, methodBinding.getDeclaringClass(), null); + assertFalse("file should not exist", annotationFile.exists()); + assertEquals("file path", "/TestLibs/annots/libs/Lib1.eea", annotationFile.getFullPath().toString()); + + // annotate: + String originalSignature = ExternalAnnotationUtil.extractGenericSignature(methodBinding); + ExternalAnnotationUtil.annotateMember("libs/Lib1", annotationFile, + "<init>", + originalSignature, + "(IT0U;)V", // <- @Nullable U + MergeStrategy.OVERWRITE_ANNOTATIONS, null); + assertTrue("file should exist", annotationFile.exists()); + + // check that the error is resolved now: + reconciled = cu.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); + assertNoProblems(reconciled.getProblems()); + + // invert annotation: + ExternalAnnotationUtil.annotateMethodParameterType("libs/Lib1", annotationFile, + "<init>", + originalSignature, + "T1U;", // <- @NonNull U + 1, // position + MergeStrategy.OVERWRITE_ANNOTATIONS, null); + assertTrue("file should exist", annotationFile.exists()); + + // check that the error is back now: + reconciled = cu.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); + assertProblems(reconciled.getProblems(), new String[] { + "Pb(910) Null type mismatch: required '@NonNull String' but the provided value is null", + }, new int[] { 7 }); + + // check that the previous entry has been overwritten: + assertEquals( + "class libs/Lib1\n" + + "<init>\n" + + " (ITU;)V\n" + + " (IT1U;)V\n", + readFully(annotationFile)); + } + // ===== white box tests for ExternalAnnotationUtil ===== public void testBug470666a() throws CoreException, IOException { diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/FreezeMonitor.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/FreezeMonitor.java new file mode 100644 index 000000000..19fd7a728 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/FreezeMonitor.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.model; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.ICoreRunnable; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.jobs.Job; + +public class FreezeMonitor { + + private static /* @Nullable */ Job monitorJob; + + public static void expectCompletionIn(final long millis) { + done(); + monitorJob = Job.create("", new ICoreRunnable() { + @Override + public void run(IProgressMonitor monitor) throws CoreException { + if (monitor.isCanceled()) { + throw new OperationCanceledException(); + } + StringBuilder result = new StringBuilder(); + result.append("Possible frozen test case\n"); + ThreadMXBean threadStuff = ManagementFactory.getThreadMXBean(); + ThreadInfo[] allThreads = threadStuff.getThreadInfo(threadStuff.getAllThreadIds(), 200); + for (ThreadInfo threadInfo : allThreads) { + result.append("\""); + result.append(threadInfo.getThreadName()); + result.append("\": "); + result.append(threadInfo.getThreadState()); + result.append("\n"); + final StackTraceElement[] elements = threadInfo.getStackTrace(); + for (StackTraceElement element : elements) { + result.append(" "); + result.append(element); + result.append("\n"); + } + result.append("\n"); + } + System.out.println(result.toString()); + } + }); + monitorJob.schedule(millis); + } + + public static void done() { + if (monitorJob != null) { + monitorJob.cancel(); + monitorJob = null; + } + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaProjectTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaProjectTests.java index ef9d23cd3..9ea4398ee 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaProjectTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaProjectTests.java @@ -2809,4 +2809,75 @@ public void testBug491354() throws CoreException { deleteProject(project15); } } + +public void testBug501220() throws CoreException { + IJavaProject jdkPrj = null, swtPrj = null, egitPrj = null; + try { + jdkPrj = createJavaProject("JDK8", new String[]{"src"}, new String[] {"JCL_LIB"}, null, null, "bin", new String[]{"bin"}, null, null, "1.8"); + createFolder("/JDK8/src/jdk8"); + createFile("/JDK8/src/jdk8/MyConsumer.java", + "package jdk8;\n" + + "@FunctionalInterface\n" + + "public interface MyConsumer<T> {\n" + + " void accept(T t);\n" + + "}\n"); + + swtPrj = createJavaProject("SWT", new String[]{"src"}, new String[] {"JCL_LIB"}, new String[]{"/JDK8"}, null, "bin", new String[]{"bin"}, null, null, "1.8"); + createFolder("/SWT/src/swt"); + createFile("/SWT/src/swt/EventObject.java", + "package swt;\n" + + "\n" + + "import jdk8.MyConsumer;\n" + + "\n" + + "public class EventObject {\n" + + "}"); + createFile("/SWT/src/swt/SelectionListener.java", + "package swt;\n" + + "\n" + + "import java.util.EventObject;\n" + + "\n" + + "import jdk8.MyConsumer;\n" + + "\n" + + "public interface SelectionListener {\n" + + " void widgetSelected(EventObject event);\n" + + "\n" + + " static SelectionListener widgetSelected(MyConsumer<EventObject> c) {\n" + + " return new SelectionListener() {\n" + + " public void widgetSelected(EventObject e) {\n" + + " c.accept(e);\n" + + " }\n" + + " };\n" + + " }\n" + + "}"); + egitPrj = createJavaProject("EGit", new String[]{"src"}, new String[] {"JCL_LIB"}, new String[]{"/SWT"}, null, "bin", new String[]{"bin"}, null, null, "1.8"); + egitPrj.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); + createFolder("/EGit/src/egit"); + createFile("/EGit/src/egit/UIUtils.java", + "package egit; // Error: The type jdk8.MyConsumer cannot be resolved. It is indirectly referenced from required .class files\n" + + "\n" + + "import swt.EventObject;\n" + + "\n" + + "import swt.SelectionListener;\n" + + "\n" + + "public class UIUtils {\n" + + " void foo() {\n" + + " SelectionListener listener = new SelectionListener() {\n" + + " public void widgetSelected(EventObject event) {\n" + + " }\n" + + " };\n" + + " listener.toString();\n" + + " }\n" + + "}\n"); + egitPrj.getProject().getWorkspace().build(IncrementalProjectBuilder.AUTO_BUILD, null); + IMarker[] markers = egitPrj.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + assertMarkers("Unexpected markers", "", markers); + } finally { + if (jdkPrj != null) + deleteProject(jdkPrj); + if (swtPrj != null) + deleteProject(swtPrj); + if (egitPrj != null) + deleteProject(egitPrj); + } +} } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocFieldCompletionModelTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocFieldCompletionModelTest.java index 696669ab6..2587d80d6 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocFieldCompletionModelTest.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocFieldCompletionModelTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -513,7 +513,7 @@ public void test030() throws JavaModelException { "}"; completeInJavadoc("/Completion/src/javadoc/fields/tags/BasicTestFields.java", source, true, "oTT"); assertResults( - "oneTwoThree[FIELD_REF]{oneTwoThree, Ljavadoc.fields.tags.BasicTestFields;, Ljava.lang.Object;, oneTwoThree, null, "+this.positions+"30}" + "oneTwoThree[FIELD_REF]{oneTwoThree, Ljavadoc.fields.tags.BasicTestFields;, Ljava.lang.Object;, oneTwoThree, null, "+this.positions+(R_DEFAULT + 25) + "}" ); } finally { JavaCore.setOptions(this.oldOptions); diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocMethodCompletionModelTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocMethodCompletionModelTest.java index af02169ac..eeae0e816 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocMethodCompletionModelTest.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocMethodCompletionModelTest.java @@ -1768,8 +1768,8 @@ public void test114() throws JavaModelException { "}\n"; completeInJavadoc("/Completion/src/javadoc/methods/tags/BasicTestMethods.java", source, true, "@param ", 0); // empty token assertSortedResults( - "xtm[JAVADOC_PARAM_REF]{xtm, null, null, xtm, null, "+this.positions+"19}\n" + - "xtc[JAVADOC_PARAM_REF]{xtc, null, null, xtc, null, "+this.positions+"18}\n" + + "xtm[JAVADOC_PARAM_REF]{xtm, null, null, xtm, null, "+this.positions+(R_DEFAULT + 14)+"}\n" + + "xtc[JAVADOC_PARAM_REF]{xtc, null, null, xtc, null, "+this.positions+(R_DEFAULT + 13)+"}\n" + "TM[JAVADOC_PARAM_REF]{<TM>, null, null, TM, null, "+this.positions+JAVADOC_RELEVANCE+"}" ); } @@ -1834,8 +1834,8 @@ public void test118() throws JavaModelException { "}\n"; completeInJavadoc("/Completion/src/javadoc/methods/tags/BasicTestMethods.java", source, true, "@param ", 0); // empty token assertSortedResults( - "xtm[JAVADOC_PARAM_REF]{xtm, null, null, xtm, null, [105, 108], 19}\n" + - "xtc[JAVADOC_PARAM_REF]{xtc, null, null, xtc, null, [105, 108], 18}\n" + + "xtm[JAVADOC_PARAM_REF]{xtm, null, null, xtm, null, [105, 108], " + (R_DEFAULT + 14) +"}\n" + + "xtc[JAVADOC_PARAM_REF]{xtc, null, null, xtc, null, [105, 108], " + (R_DEFAULT + 13) +"}\n" + "TM[JAVADOC_PARAM_REF]{<TM>, null, null, TM, null, [105, 108], "+JAVADOC_RELEVANCE+"}" ); } @@ -1883,8 +1883,8 @@ public void test121() throws JavaModelException { "}\n"; completeInJavadoc("/Completion/src/javadoc/methods/tags/BasicTestMethods.java", source, true, "@param ", 0); // empty token assertSortedResults( - "xtm[JAVADOC_PARAM_REF]{xtm, null, null, xtm, null, "+this.positions+"19}\n" + - "xtc[JAVADOC_PARAM_REF]{xtc, null, null, xtc, null, "+this.positions+"18}\n" + + "xtm[JAVADOC_PARAM_REF]{xtm, null, null, xtm, null, "+this.positions+(R_DEFAULT + 14)+"}\n" + + "xtc[JAVADOC_PARAM_REF]{xtc, null, null, xtc, null, "+this.positions+(R_DEFAULT + 13)+"}\n" + "TM[JAVADOC_PARAM_REF]{<TM>, null, null, TM, null, "+this.positions+JAVADOC_RELEVANCE+"}" ); } @@ -1902,8 +1902,8 @@ public void test122() throws JavaModelException { "}\n"; completeInJavadoc("/Completion/src/javadoc/methods/tags/BasicTestMethods.java", source, true, "@param ", 0); // empty token assertSortedResults( - "xtm[JAVADOC_PARAM_REF]{xtm, null, null, xtm, null, "+this.positions+"19}\n" + - "xtc[JAVADOC_PARAM_REF]{xtc, null, null, xtc, null, "+this.positions+"18}\n" + + "xtm[JAVADOC_PARAM_REF]{xtm, null, null, xtm, null, "+this.positions+(R_DEFAULT + 14)+"}\n" + + "xtc[JAVADOC_PARAM_REF]{xtc, null, null, xtc, null, "+this.positions+(R_DEFAULT + 13)+"}\n" + "TM[JAVADOC_PARAM_REF]{<TM>, null, null, TM, null, "+this.positions+JAVADOC_RELEVANCE+"}" ); } @@ -1921,8 +1921,8 @@ public void test123() throws JavaModelException { "}\n"; completeInJavadoc("/Completion/src/javadoc/methods/tags/BasicTestMethods.java", source, true, "@param ", 0); // empty token assertSortedResults( - "xtm[JAVADOC_PARAM_REF]{xtm, null, null, xtm, null, "+this.positions+"19}\n" + - "xtc[JAVADOC_PARAM_REF]{xtc, null, null, xtc, null, "+this.positions+"18}\n" + + "xtm[JAVADOC_PARAM_REF]{xtm, null, null, xtm, null, "+this.positions+(R_DEFAULT + 14)+"}\n" + + "xtc[JAVADOC_PARAM_REF]{xtc, null, null, xtc, null, "+this.positions+(R_DEFAULT + 13)+"}\n" + "TM[JAVADOC_PARAM_REF]{<TM>, null, null, TM, null, "+this.positions+JAVADOC_RELEVANCE+"}" ); } @@ -2536,7 +2536,7 @@ public void test161() throws JavaModelException { "}\n"; completeInJavadoc("/Completion/src/javadoc/methods/tags/BasicTestMethods.java", source, true, "oTT"); assertResults( - "oneTwoThree[METHOD_REF]{oneTwoThree(int), Ljavadoc.methods.tags.BasicTestMethods;, (I)V, oneTwoThree, (i), "+this.positions+"30}" + "oneTwoThree[METHOD_REF]{oneTwoThree(int), Ljavadoc.methods.tags.BasicTestMethods;, (I)V, oneTwoThree, (i), "+this.positions+(R_DEFAULT + 25)+"}" ); } finally { JavaCore.setOptions(this.oldOptions); diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocTypeCompletionModelTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocTypeCompletionModelTest.java index a567e074b..1452f4d48 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocTypeCompletionModelTest.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavadocTypeCompletionModelTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -1036,7 +1036,7 @@ public void test080() throws JavaModelException { " */\n" + "public class BasicTestTypes {}\n"; completeInJavadoc("/Completion/src/javadoc/types/tags/BasicTestTypes.java", source, true, "BTT"); - assertResults("BasicTestTypes[TYPE_REF]{BasicTestTypes, javadoc.types.tags, Ljavadoc.types.tags.BasicTestTypes;, null, null, "+this.positions+"22}"); + assertResults("BasicTestTypes[TYPE_REF]{BasicTestTypes, javadoc.types.tags, Ljavadoc.types.tags.BasicTestTypes;, null, null, "+this.positions+ (R_DEFAULT + 17) +"}"); } finally { JavaCore.setOptions(this.oldOptions); } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModifyingResourceTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModifyingResourceTests.java index 4a5036381..3f44c37b2 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModifyingResourceTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModifyingResourceTests.java @@ -19,6 +19,7 @@ import org.eclipse.jdt.core.*; import org.eclipse.jdt.core.compiler.batch.BatchCompiler; import org.eclipse.jdt.core.tests.util.Util; import org.eclipse.jdt.internal.core.JavaElement; +import org.eclipse.jdt.internal.core.nd.indexer.Indexer; public class ModifyingResourceTests extends AbstractJavaModelTests { @@ -102,6 +103,7 @@ protected IFile createFile(String path, InputStream content) throws CoreExceptio } catch (IOException e) { e.printStackTrace(); } + Indexer.getInstance().waitForIndex(null); return file; } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NameLookupTests2.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NameLookupTests2.java index a529c7761..1dd935489 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NameLookupTests2.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NameLookupTests2.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -14,9 +14,6 @@ package org.eclipse.jdt.core.tests.model; import java.io.IOException; import java.nio.file.FileSystems; import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.nio.file.attribute.FileTime; -import java.util.Arrays; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.runtime.CoreException; @@ -361,15 +358,7 @@ public void testFindTypeWithUnrelatedWorkingCopy() throws Exception { * JavaProjectElementInfo cache without restarting Eclipse or closing and reopening the project. */ public void testTransitionFromInvalidToValidJar() throws CoreException, IOException { - /* - * Since it is difficult to test intermittent IO errors, simulate it - * by creating two jars of equal size, one of which has an invalid format. - * Set up the classpath with the invalid jar, and then swap in the valid jar - * and reset its timestamp to be the same as the original file. - */ - String goodJar = getExternalPath() + "goodJar.jar"; String transitioningJar = getExternalPath() + "transitioningJar.jar"; - java.nio.file.Path goodJarPath = FileSystems.getDefault().getPath(goodJar); java.nio.file.Path transitioningJarPath = FileSystems.getDefault().getPath(transitioningJar); IPath transitioningIPath = Path.fromOSString(transitioningJar); @@ -385,33 +374,29 @@ public void testTransitionFromInvalidToValidJar() throws CoreException, IOExcept "META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" }, - goodJar, + transitioningJar, JavaCore.VERSION_1_4); - char[] invalidContents = new char[(int) goodJarPath.toFile().length()]; - Arrays.fill(invalidContents, ' '); - Util.createFile(transitioningJar, String.copyValueOf(invalidContents)); // Set up the project with the invalid jar and allow all of the classpath validation // and delta processing to complete. + JavaModelManager.throwIoExceptionsInGetZipFile = true; JavaProject proj = (JavaProject) createJavaProject("P", new String[] {}, new String[] {transitioningJar}, "bin"); JavaModelManager.getJavaModelManager().getJavaModel().refreshExternalArchives(null, null); waitForAutoBuild(); assertTrue("The invalid archive cache should report that the jar is invalid", - JavaModelManager.getJavaModelManager().isInvalidArchive(transitioningIPath)); + !JavaModelManager.getJavaModelManager().getArchiveValidity(transitioningIPath).isValid()); IType type = getNameLookup(proj).findType("test1.IResource", false, NameLookup.ACCEPT_CLASSES); assertEquals("Name lookup should fail when the jar is invalid", null, type); - // Substitute the good jar, maintaining the timestamp. - FileTime fileTime = Files.getLastModifiedTime(transitioningJarPath); - Files.move(goodJarPath, transitioningJarPath, StandardCopyOption.REPLACE_EXISTING); - Files.setLastModifiedTime(transitioningJarPath, fileTime); + // Cause IO exceptions to be thrown on all file operations + JavaModelManager.throwIoExceptionsInGetZipFile = false; // Since the timestamp hasn't changed, an external archive refresh isn't going // to update the caches or cause name lookups to work. JavaModelManager.getJavaModelManager().getJavaModel().refreshExternalArchives(null, null); assertTrue("External archive refresh sees no changes, so the invalid archive cache should be unchanged", - JavaModelManager.getJavaModelManager().isInvalidArchive(transitioningIPath)); + !JavaModelManager.getJavaModelManager().getArchiveValidity(transitioningIPath).isValid()); type = getNameLookup(proj).findType("test1.IResource", false, NameLookup.ACCEPT_CLASSES); assertEquals("External archive refresh sees no changes, so the project cache should be unchanged", null, type); @@ -423,11 +408,10 @@ public void testTransitionFromInvalidToValidJar() throws CoreException, IOExcept ClasspathEntry.validateClasspathEntry(proj, transitioningEntry, false, false); assertFalse("The invalid archive cache should no longer report the jar as invalid", - JavaModelManager.getJavaModelManager().isInvalidArchive(transitioningIPath)); + !JavaModelManager.getJavaModelManager().getArchiveValidity(transitioningIPath).isValid()); type = getNameLookup(proj).findType("test1.IResource", false, NameLookup.ACCEPT_CLASSES); assertFalse("Name lookup should be able to find types in the valid jar", type == null); } finally { - Files.deleteIfExists(goodJarPath); Files.deleteIfExists(transitioningJarPath); deleteProject("P"); } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SubstringCompletionTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SubstringCompletionTests.java index 81e976d43..4fea3b391 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SubstringCompletionTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SubstringCompletionTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 Gábor Kövesdán and others. + * Copyright (c) 2015, 2016 Gábor Kövesdán and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -17,59 +17,20 @@ import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import junit.framework.Test; -import junit.framework.TestSuite; public class SubstringCompletionTests extends AbstractJavaModelCompletionTests { public static Test suite() { - if (TESTS_PREFIX != null || TESTS_NAMES != null || TESTS_NUMBERS != null || TESTS_RANGE != null) { - return buildModelTestSuite(SubstringCompletionTests.class); - } - TestSuite suite = new Suite(SubstringCompletionTests.class.getName()); - suite.addTest(new SubstringCompletionTests("testQualifiedNonStaticMethod")); - suite.addTest(new SubstringCompletionTests("testQualifiedStaticMethod")); - suite.addTest(new SubstringCompletionTests("testUnqualifiedNonStaticMethod")); - suite.addTest(new SubstringCompletionTests("testUnqualifiedStaticMethod")); - suite.addTest(new SubstringCompletionTests("testQualifiedNonStaticField")); - suite.addTest(new SubstringCompletionTests("testQualifiedStaticField")); - suite.addTest(new SubstringCompletionTests("testUnqualifiedNonStaticField")); - suite.addTest(new SubstringCompletionTests("testUnqualifiedStaticField")); - suite.addTest(new SubstringCompletionTests("testLocalVariable")); - suite.addTest(new SubstringCompletionTests("testMethodParamVariable")); - suite.addTest(new SubstringCompletionTests("testClassTypeInstantiation")); - suite.addTest(new SubstringCompletionTests("testClassTypeFieldDeclaration")); - suite.addTest(new SubstringCompletionTests("testClassTypeParamDeclaration")); - suite.addTest(new SubstringCompletionTests("testClassTypeLocalVarDeclaration")); - suite.addTest(new SubstringCompletionTests("testClassTypeThrowsDeclaration")); - suite.addTest(new SubstringCompletionTests("testClassTypeExtends")); - suite.addTest(new SubstringCompletionTests("testClassTypeImplements")); - suite.addTest(new SubstringCompletionTests("testInnerClassTypeInstantiation")); - suite.addTest(new SubstringCompletionTests("testInnerClassTypeFieldDeclaration")); - suite.addTest(new SubstringCompletionTests("testInnerClassTypeParamDeclaration")); - suite.addTest(new SubstringCompletionTests("testInnerClassTypeLocalVarDeclaration")); - suite.addTest(new SubstringCompletionTests("testInnerClassTypeThrowsDeclaration")); - suite.addTest(new SubstringCompletionTests("testInnerClassTypeExtends")); - suite.addTest(new SubstringCompletionTests("testInnerClassTypeImplements")); - suite.addTest(new SubstringCompletionTests("testStaticNestedClassTypeInstantiation")); - suite.addTest(new SubstringCompletionTests("testStaticNestedClassTypeFieldDeclaration")); - suite.addTest(new SubstringCompletionTests("testStaticNestedClassTypeParamDeclaration")); - suite.addTest(new SubstringCompletionTests("testStaticNestedClassTypeLocalVarDeclaration")); - suite.addTest(new SubstringCompletionTests("testStaticNestedClassTypeThrowsDeclaration")); - suite.addTest(new SubstringCompletionTests("testStaticNestedClassTypeExtends")); - suite.addTest(new SubstringCompletionTests("testStaticNestedClassTypeImplements")); - suite.addTest(new SubstringCompletionTests("testLocalClassTypeInstantiation")); - suite.addTest(new SubstringCompletionTests("testLocalClassTypeLocalVarDeclaration")); - suite.addTest(new SubstringCompletionTests("testLocalClassTypeExtends")); - return suite; + return buildModelTestSuite(SubstringCompletionTests.class, BYTECODE_DECLARATION_ORDER); } public SubstringCompletionTests(String name) { super(name); } public void setUpSuite() throws Exception { if (COMPLETION_PROJECT == null) { - COMPLETION_PROJECT = setUpJavaProject("Completion"); + COMPLETION_PROJECT = setUpJavaProject("Completion", "1.8", true); } else { - setUpProjectCompliance(COMPLETION_PROJECT, "1.8"); + setUpProjectCompliance(COMPLETION_PROJECT, "1.8", true); } super.setUpSuite(); Hashtable<String, String> options = new Hashtable<>(this.oldOptions); @@ -202,24 +163,24 @@ public void testQualifiedNonStaticField() throws JavaModelException { "/Completion/src/test/Test.java", "package test;"+ "public class Test {\n" + - " int element;\n" + - " int otherElement;\n" + - " long elementCount;\n" + + " int items;\n" + + " int otherItems;\n" + + " long itemsCount;\n" + " void foo() {\n" + - " this.elem\n" + + " this.item\n" + " }\n" + "}\n"); CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); String str = this.workingCopies[0].getSource(); - String completeBehind = "this.elem"; + String completeBehind = "this.item"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "otherElement[FIELD_REF]{otherElement, Ltest.Test;, I, otherElement, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_STATIC + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + - "element[FIELD_REF]{element, Ltest.Test;, I, element, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + + R_CASE + R_NON_STATIC + R_NON_RESTRICTED) + "}\n" + - "elementCount[FIELD_REF]{elementCount, Ltest.Test;, J, elementCount, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_STATIC + R_NON_RESTRICTED) + "}", + "otherItems[FIELD_REF]{otherItems, Ltest.Test;, I, otherItems, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_STATIC + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + + "items[FIELD_REF]{items, Ltest.Test;, I, items, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + + R_CASE + R_NON_STATIC + R_NON_RESTRICTED) + "}\n" + + "itemsCount[FIELD_REF]{itemsCount, Ltest.Test;, J, itemsCount, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_STATIC + R_NON_RESTRICTED) + "}", requestor.getResults()); } @@ -229,25 +190,24 @@ public void testUnqualifiedNonStaticField() throws JavaModelException { "/Completion/src/test/Test.java", "package test;"+ "public class Test {\n" + - " int element;\n" + - " int otherElement;\n" + - " long elementCount;\n" + + " int items;\n" + + " int otherItems;\n" + + " long itemsCount;\n" + " void foo() {\n" + - " elem\n" + + " item\n" + " }\n" + "}\n"); CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); String str = this.workingCopies[0].getSource(); - String completeBehind = "elem"; + String completeBehind = "item"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "ElementType[TYPE_REF]{java.lang.annotation.ElementType, java.lang.annotation, Ljava.lang.annotation.ElementType;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED) + "}\n" + - "otherElement[FIELD_REF]{otherElement, Ltest.Test;, I, otherElement, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_UNQUALIFIED + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + - "element[FIELD_REF]{element, Ltest.Test;, I, element, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" + - "elementCount[FIELD_REF]{elementCount, Ltest.Test;, J, elementCount, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", + "otherItems[FIELD_REF]{otherItems, Ltest.Test;, I, otherItems, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_UNQUALIFIED + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + + "items[FIELD_REF]{items, Ltest.Test;, I, items, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" + + "itemsCount[FIELD_REF]{itemsCount, Ltest.Test;, J, itemsCount, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", requestor.getResults()); } public void testQualifiedStaticField() throws JavaModelException { @@ -256,23 +216,23 @@ public void testQualifiedStaticField() throws JavaModelException { "/Completion/src/test/Test.java", "package test;"+ "public class Test {\n" + - " static int element;\n" + - " int otherElement;\n" + - " static long elementCount;\n" + + " static int items;\n" + + " int otherItems;\n" + + " static long itemsCount;\n" + " void foo() {\n" + - " Test.elem\n" + + " Test.item\n" + " }\n" + "}\n"); CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); String str = this.workingCopies[0].getSource(); - String completeBehind = "Test.elem"; + String completeBehind = "Test.item"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "element[FIELD_REF]{element, Ltest.Test;, I, element, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_RESTRICTED + R_NON_INHERITED) + "}\n" + - "elementCount[FIELD_REF]{elementCount, Ltest.Test;, J, elementCount, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_RESTRICTED + R_NON_INHERITED) + "}", + "items[FIELD_REF]{items, Ltest.Test;, I, items, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_RESTRICTED + R_NON_INHERITED) + "}\n" + + "itemsCount[FIELD_REF]{itemsCount, Ltest.Test;, J, itemsCount, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_NON_RESTRICTED + R_NON_INHERITED) + "}", requestor.getResults()); } public void testUnqualifiedStaticField() throws JavaModelException { @@ -281,25 +241,24 @@ public void testUnqualifiedStaticField() throws JavaModelException { "/Completion/src/test/Test.java", "package test;"+ "public class Test {\n" + - " static int element;\n" + - " int otherElement;\n" + - " static long elementCount;\n" + + " static int items;\n" + + " int otherItems;\n" + + " static long itemsCount;\n" + " void foo() {\n" + - " elem\n" + + " item\n" + " }\n" + "}\n"); CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); String str = this.workingCopies[0].getSource(); - String completeBehind = "elem"; + String completeBehind = "item"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "ElementType[TYPE_REF]{java.lang.annotation.ElementType, java.lang.annotation, Ljava.lang.annotation.ElementType;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED) + "}\n" + - "otherElement[FIELD_REF]{otherElement, Ltest.Test;, I, otherElement, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_UNQUALIFIED + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + - "element[FIELD_REF]{element, Ltest.Test;, I, element, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" + - "elementCount[FIELD_REF]{elementCount, Ltest.Test;, J, elementCount, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", + "otherItems[FIELD_REF]{otherItems, Ltest.Test;, I, otherItems, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_UNQUALIFIED + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + + "items[FIELD_REF]{items, Ltest.Test;, I, items, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" + + "itemsCount[FIELD_REF]{itemsCount, Ltest.Test;, J, itemsCount, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", requestor.getResults()); } public void testLocalVariable() throws JavaModelException { @@ -308,27 +267,26 @@ public void testLocalVariable() throws JavaModelException { "/Completion/src/test/Test.java", "package test;"+ "public class Test {\n" + - " static int element;\n" + - " int otherElement;\n" + - " static long elementCount;\n" + + " static int items;\n" + + " int otherItems;\n" + + " static long itemsCount;\n" + " void foo() {\n" + - " int temporaryElement = 0;\n" + - " elem\n" + + " int temporaryItem = 0;\n" + + " item\n" + " }\n" + "}\n"); CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); String str = this.workingCopies[0].getSource(); - String completeBehind = "elem"; + String completeBehind = "item"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "ElementType[TYPE_REF]{java.lang.annotation.ElementType, java.lang.annotation, Ljava.lang.annotation.ElementType;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED) + "}\n" + - "otherElement[FIELD_REF]{otherElement, Ltest.Test;, I, otherElement, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_UNQUALIFIED + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + - "temporaryElement[LOCAL_VARIABLE_REF]{temporaryElement, null, I, temporaryElement, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_UNQUALIFIED + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + - "element[FIELD_REF]{element, Ltest.Test;, I, element, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" + - "elementCount[FIELD_REF]{elementCount, Ltest.Test;, J, elementCount, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", + "otherItems[FIELD_REF]{otherItems, Ltest.Test;, I, otherItems, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_UNQUALIFIED + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + + "temporaryItem[LOCAL_VARIABLE_REF]{temporaryItem, null, I, temporaryItem, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_UNQUALIFIED + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + + "items[FIELD_REF]{items, Ltest.Test;, I, items, null, " + (R_DEFAULT + R_RESOLVED + R_CASE + R_INTERESTING + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" + + "itemsCount[FIELD_REF]{itemsCount, Ltest.Test;, J, itemsCount, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", requestor.getResults()); } public void testMethodParamVariable() throws JavaModelException { @@ -337,26 +295,25 @@ public void testMethodParamVariable() throws JavaModelException { "/Completion/src/test/Test.java", "package test;"+ "public class Test {\n" + - " static int element;\n" + - " int otherElement;\n" + - " static long elementCount;\n" + - " void foo(int initElement) {\n" + - " elem\n" + + " static int items;\n" + + " int otherItems;\n" + + " static long itemsCount;\n" + + " void foo(int initItems) {\n" + + " item\n" + " }\n" + "}\n"); CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); String str = this.workingCopies[0].getSource(); - String completeBehind = "elem"; + String completeBehind = "item"; int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); assertResults( - "ElementType[TYPE_REF]{java.lang.annotation.ElementType, java.lang.annotation, Ljava.lang.annotation.ElementType;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED) + "}\n" + - "initElement[LOCAL_VARIABLE_REF]{initElement, null, I, initElement, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_UNQUALIFIED + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + - "otherElement[FIELD_REF]{otherElement, Ltest.Test;, I, otherElement, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_UNQUALIFIED + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + - "element[FIELD_REF]{element, Ltest.Test;, I, element, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" + - "elementCount[FIELD_REF]{elementCount, Ltest.Test;, J, elementCount, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", + "initItems[LOCAL_VARIABLE_REF]{initItems, null, I, initItems, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_UNQUALIFIED + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + + "otherItems[FIELD_REF]{otherItems, Ltest.Test;, I, otherItems, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_UNQUALIFIED + R_NON_RESTRICTED + R_SUBSTRING) + "}\n" + + "items[FIELD_REF]{items, Ltest.Test;, I, items, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}\n" + + "itemsCount[FIELD_REF]{itemsCount, Ltest.Test;, J, itemsCount, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_CASE + R_UNQUALIFIED + R_NON_RESTRICTED) + "}", requestor.getResults()); } public void testClassTypeInstantiation() throws JavaModelException { @@ -899,4 +856,257 @@ public void testLocalClassTypeExtends() throws JavaModelException { "FooBar[TYPE_REF]{FooBar, test, LFooBar;, null, null, " + (R_DEFAULT + R_RESOLVED + R_INTERESTING + R_EXPECTED_TYPE + R_NON_RESTRICTED + R_UNQUALIFIED + R_SUBSTRING) + "}", requestor.getResults()); } -}
\ No newline at end of file +public void testBug488441_1() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + String content = "public class Try18 {\n" + + " public void main(String[] args) {\n" + + " \"s\".st\n" + + " }\n" + + "}\n" + + "}\n"; + this.workingCopies[0] = getWorkingCopy( + "/Completion/src/test/Test.java", + content); + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeBehind = ".st"; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_STATIC + R_NON_RESTRICTED; + assertResults( + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (I)I, lastIndexOf, (arg0), "+ (relevance + R_SUBSTRING) +"}\n" + + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (II)I, lastIndexOf, (arg0, arg1), "+ (relevance + R_SUBSTRING) +"}\n" + + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (Ljava.lang.String;)I, lastIndexOf, (arg0), "+ (relevance + R_SUBSTRING) +"}\n" + + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (Ljava.lang.String;I)I, lastIndexOf, (arg0, arg1), "+ (relevance + R_SUBSTRING) +"}\n" + + "replaceFirst[METHOD_REF]{replaceFirst(), Ljava.lang.String;, (Ljava.lang.String;Ljava.lang.String;)Ljava.lang.String;, replaceFirst, (arg0, arg1), "+ (relevance + R_SUBSTRING) +"}\n" + + "substring[METHOD_REF]{substring(), Ljava.lang.String;, (I)Ljava.lang.String;, substring, (arg0), "+ (relevance + R_SUBSTRING) +"}\n" + + "substring[METHOD_REF]{substring(), Ljava.lang.String;, (II)Ljava.lang.String;, substring, (arg0, arg1), "+ (relevance + R_SUBSTRING) +"}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.String;, ()Ljava.lang.String;, toString, null, "+ (relevance + R_SUBSTRING) +"}\n" + + "startsWith[METHOD_REF]{startsWith(), Ljava.lang.String;, (Ljava.lang.String;)Z, startsWith, (arg0), "+ (relevance + R_CASE) +"}\n" + + "startsWith[METHOD_REF]{startsWith(), Ljava.lang.String;, (Ljava.lang.String;I)Z, startsWith, (arg0, arg1), "+ (relevance + R_CASE) +"}", + requestor.getResults()); +} +public void testBug488441_2() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + String content = "public class Try18 {\n" + + " public void main(String[] args) {\n" + + " int i = \"s\".st\n" + + " }\n" + + "}\n"; + this.workingCopies[0] = getWorkingCopy( + "/Completion/src/test/Test.java", + content); + + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeBehind = ".st"; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_STATIC + R_NON_RESTRICTED; + assertResults( + "replaceFirst[METHOD_REF]{replaceFirst(), Ljava.lang.String;, (Ljava.lang.String;Ljava.lang.String;)Ljava.lang.String;, replaceFirst, (arg0, arg1), "+ (relevance + R_SUBSTRING) +"}\n" + + "substring[METHOD_REF]{substring(), Ljava.lang.String;, (I)Ljava.lang.String;, substring, (arg0), "+ (relevance + R_SUBSTRING) +"}\n" + + "substring[METHOD_REF]{substring(), Ljava.lang.String;, (II)Ljava.lang.String;, substring, (arg0, arg1), "+ (relevance + R_SUBSTRING) +"}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.String;, ()Ljava.lang.String;, toString, null, "+ (relevance + R_SUBSTRING) +"}\n" + + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (I)I, lastIndexOf, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (II)I, lastIndexOf, (arg0, arg1), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (Ljava.lang.String;)I, lastIndexOf, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (Ljava.lang.String;I)I, lastIndexOf, (arg0, arg1), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "startsWith[METHOD_REF]{startsWith(), Ljava.lang.String;, (Ljava.lang.String;)Z, startsWith, (arg0), "+ (relevance + R_CASE) +"}\n" + + "startsWith[METHOD_REF]{startsWith(), Ljava.lang.String;, (Ljava.lang.String;I)Z, startsWith, (arg0, arg1), "+ (relevance + R_CASE) +"}", + requestor.getResults()); +} +public void testBug488441_3() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + String content = "public class Try18 {\n" + + " public void main(String[] args) {\n" + + " String s = \"s\".st\n" + + " }\n" + + "}\n"; + this.workingCopies[0] = getWorkingCopy( + "/Completion/src/test/Test.java", + content); + + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeBehind = ".st"; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_STATIC + R_NON_RESTRICTED; + assertResults( + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (I)I, lastIndexOf, (arg0), "+ (relevance + R_SUBSTRING) +"}\n" + + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (II)I, lastIndexOf, (arg0, arg1), "+ (relevance + R_SUBSTRING) +"}\n" + + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (Ljava.lang.String;)I, lastIndexOf, (arg0), "+ (relevance + R_SUBSTRING) +"}\n" + + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (Ljava.lang.String;I)I, lastIndexOf, (arg0, arg1), "+ (relevance + R_SUBSTRING) +"}\n" + + "replaceFirst[METHOD_REF]{replaceFirst(), Ljava.lang.String;, (Ljava.lang.String;Ljava.lang.String;)Ljava.lang.String;, replaceFirst, (arg0, arg1), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "startsWith[METHOD_REF]{startsWith(), Ljava.lang.String;, (Ljava.lang.String;)Z, startsWith, (arg0), "+ (relevance + R_CASE) +"}\n" + + "startsWith[METHOD_REF]{startsWith(), Ljava.lang.String;, (Ljava.lang.String;I)Z, startsWith, (arg0, arg1), "+ (relevance + R_CASE) +"}\n" + + "substring[METHOD_REF]{substring(), Ljava.lang.String;, (I)Ljava.lang.String;, substring, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "substring[METHOD_REF]{substring(), Ljava.lang.String;, (II)Ljava.lang.String;, substring, (arg0, arg1), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.String;, ()Ljava.lang.String;, toString, null, "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}", + requestor.getResults()); +} +public void testBug488441_4() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + String content = "public class Try18 {\n" + + " public void main(String[] args) {\n" + + " boolean s = \"s\".st\n" + + " }\n" + + "}\n"; + this.workingCopies[0] = getWorkingCopy( + "/Completion/src/test/Test.java", + content); + + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeBehind = ".st"; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_STATIC + R_NON_RESTRICTED; + assertResults( + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (I)I, lastIndexOf, (arg0), " + (relevance + R_SUBSTRING) + "}\n" + + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (II)I, lastIndexOf, (arg0, arg1), " + (relevance + R_SUBSTRING) + "}\n" + + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (Ljava.lang.String;)I, lastIndexOf, (arg0), " + (relevance + R_SUBSTRING) + "}\n" + + "lastIndexOf[METHOD_REF]{lastIndexOf(), Ljava.lang.String;, (Ljava.lang.String;I)I, lastIndexOf, (arg0, arg1), " + (relevance + R_SUBSTRING) + "}\n" + + "replaceFirst[METHOD_REF]{replaceFirst(), Ljava.lang.String;, (Ljava.lang.String;Ljava.lang.String;)Ljava.lang.String;, replaceFirst, (arg0, arg1), " + (relevance + R_SUBSTRING) + "}\n" + + "substring[METHOD_REF]{substring(), Ljava.lang.String;, (I)Ljava.lang.String;, substring, (arg0), " + (relevance + R_SUBSTRING) + "}\n" + + "substring[METHOD_REF]{substring(), Ljava.lang.String;, (II)Ljava.lang.String;, substring, (arg0, arg1), " + (relevance + R_SUBSTRING) + "}\n" + + "toString[METHOD_REF]{toString(), Ljava.lang.String;, ()Ljava.lang.String;, toString, null, " + (relevance + R_SUBSTRING) + "}\n" + + "startsWith[METHOD_REF]{startsWith(), Ljava.lang.String;, (Ljava.lang.String;)Z, startsWith, (arg0), " + (relevance + R_EXACT_EXPECTED_TYPE + R_CASE) + "}\n" + + "startsWith[METHOD_REF]{startsWith(), Ljava.lang.String;, (Ljava.lang.String;I)Z, startsWith, (arg0, arg1), " + (relevance + R_EXACT_EXPECTED_TYPE + R_CASE) + "}", + requestor.getResults()); +} +public void testBug488441_5() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "/Completion/src/test/Test.java", + "import java.util.Arrays;\n" + + "public class Try18 {\n" + + " public void main(String[] args) {\n" + + " String msg=\"\";\n" + + " String[] parameters = {\"a\"};\n" + + " System.out.println(msg + Arrays.as);\n" + + " }\n" + + "}\n"); + + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeBehind = ".as"; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED + R_NON_INHERITED; + assertResults( + "asList[METHOD_REF]{asList(), Ljava.util.Arrays;, <T:Ljava.lang.Object;>([TT;)Ljava.util.List<TT;>;, asList, (arg0), "+ (relevance + R_CASE) +"}\n" + + "deepHashCode[METHOD_REF]{deepHashCode(), Ljava.util.Arrays;, ([Ljava.lang.Object;)I, deepHashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([B)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([C)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([D)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([F)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([I)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([J)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([Ljava.lang.Object;)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([S)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([Z)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}", + requestor.getResults()); +} +public void testBug488441_6() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "/Completion/src/test/Test.java", + "import java.util.Arrays;\n" + + "public class Try18 {\n" + + " public void main(String[] args) {\n" + + " String msg=\"\";\n" + + " String[] parameters = {\"a\"};\n" + + " System.out.println(msg + Arrays.aS);\n" + + " }\n" + + "}\n"); + + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeBehind = ".aS"; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED + R_NON_INHERITED; + assertResults( + "asList[METHOD_REF]{asList(), Ljava.util.Arrays;, <T:Ljava.lang.Object;>([TT;)Ljava.util.List<TT;>;, asList, (arg0), "+ (relevance) +"}\n" + + "deepHashCode[METHOD_REF]{deepHashCode(), Ljava.util.Arrays;, ([Ljava.lang.Object;)I, deepHashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([B)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([C)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([D)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([F)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([I)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([J)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([Ljava.lang.Object;)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([S)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([Z)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}", + requestor.getResults()); +} +public void testBug488441_7() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "/Completion/src/test/Test.java", + "import java.util.Arrays;\n" + + "public class Try18 {\n" + + " public void main(String[] args) {\n" + + " String msg=\"\";\n" + + " String[] parameters = {\"a\"};\n" + + " System.out.println(Arrays.as);\n" + + " }\n" + + "}\n"); + + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeBehind = ".as"; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED + R_NON_INHERITED; + assertResults( + "deepHashCode[METHOD_REF]{deepHashCode(), Ljava.util.Arrays;, ([Ljava.lang.Object;)I, deepHashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([B)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([C)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([D)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([F)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([I)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([J)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([Ljava.lang.Object;)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([S)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([Z)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "asList[METHOD_REF]{asList(), Ljava.util.Arrays;, <T:Ljava.lang.Object;>([TT;)Ljava.util.List<TT;>;, asList, (arg0), "+ (relevance + R_EXPECTED_TYPE + R_CASE) +"}", + requestor.getResults()); +} +public void testBug488441_8() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "/Completion/src/test/Test.java", + "import java.util.Arrays;\n" + + "public class Try18 {\n" + + " public void main(String[] args) {\n" + + " String msg=\"\";\n" + + " String[] parameters = {\"a\"};\n" + + " int i = Arrays.as;\n" + + " }\n" + + "}\n"); + + CompletionTestsRequestor2 requestor = new CompletionTestsRequestor2(true); + String str = this.workingCopies[0].getSource(); + String completeBehind = ".as"; + int cursorLocation = str.lastIndexOf(completeBehind) + completeBehind.length(); + this.workingCopies[0].codeComplete(cursorLocation, requestor, this.wcOwner); + + int relevance = R_DEFAULT + R_RESOLVED + R_INTERESTING + R_NON_RESTRICTED + R_NON_INHERITED; + assertResults( + "asList[METHOD_REF]{asList(), Ljava.util.Arrays;, <T:Ljava.lang.Object;>([TT;)Ljava.util.List<TT;>;, asList, (arg0), "+ (relevance + R_CASE) +"}\n" + + "deepHashCode[METHOD_REF]{deepHashCode(), Ljava.util.Arrays;, ([Ljava.lang.Object;)I, deepHashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([B)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([C)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([D)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([F)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([I)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([J)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([Ljava.lang.Object;)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([S)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}\n" + + "hashCode[METHOD_REF]{hashCode(), Ljava.util.Arrays;, ([Z)I, hashCode, (arg0), "+ (relevance + R_SUBSTRING + R_EXACT_EXPECTED_TYPE) +"}", + requestor.getResults()); +} +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SuiteOfTestCases.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SuiteOfTestCases.java index 3917b757f..94385decf 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SuiteOfTestCases.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SuiteOfTestCases.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -14,6 +14,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Set; +import org.eclipse.jdt.internal.core.nd.indexer.Indexer; import org.eclipse.test.internal.performance.PerformanceMeterFactory; import junit.extensions.TestSetup; @@ -29,6 +30,12 @@ import junit.framework.TestSuite; @SuppressWarnings("rawtypes") public class SuiteOfTestCases extends org.eclipse.jdt.core.tests.junit.extension.TestCase { + /** + * Number of milliseconds that a test case can run for before we consider it to be potentially + * deadlocked and dump out a stack trace. Currently set to 5 minutes. + */ + private static final long FROZEN_TEST_TIMEOUT_MS = 1000 * 60 * 5; + /* * A test suite that initialize the test case's fields once, then that copies the values * of these fields into each subsequent test case. @@ -118,12 +125,27 @@ public class SuiteOfTestCases extends org.eclipse.jdt.core.tests.junit.extension * Setup the test suite once before all test cases run. */ public void setUpSuite() throws Exception { + Indexer.getInstance().enableAutomaticIndexing(false); + //Indexer.getInstance().waitForIndex(null); } /** * Tear down the test suite once after all test cases have run. */ public void tearDownSuite() throws Exception { + Indexer.getInstance().enableAutomaticIndexing(true); + } + + @Override + protected void setUp() throws Exception { + FreezeMonitor.expectCompletionIn(FROZEN_TEST_TIMEOUT_MS); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + FreezeMonitor.done(); + super.tearDown(); } /** diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/BTreeExpensiveTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/BTreeExpensiveTests.java new file mode 100644 index 000000000..155726008 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/BTreeExpensiveTests.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2007, 2016 Symbian Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Symbian - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd; + +import junit.framework.Test; + +/** + * Tests which are too expensive to run as part of normal testing, but + * should be run after B-tree related development. + * <p> + * The 'Full Checking' tests perform a full validation of the B-tree + * invariants after each B-tree operation, and so are especially + * expensive and cpu hungry. + */ +public class BTreeExpensiveTests extends BTreeTests { + + public static Test suite() { + return suite(BTreeExpensiveTests.class); + } + + public void testBySortedSetMirror() throws Exception { + sortedMirrorTest(100); + } + + // @Override + @Override + public void testInsertion() throws Exception { + super.testInsertion(); + } + + /* + * N.B. Each of the following tests are quite expensive (i.e. > 10mins each on a 2Ghz machine) + */ + + public void testBySortedSetMirror1682762087() throws Exception { + System.out.println("1682762087 Full Checking"); + trial(1682762087, true); // exposed bugs in 2a,b + } + + public void testBySortedSetMirror322922974() throws Exception { + System.out.println("322922974 Full Checking"); + trial(322922974, true); // exposed bugs in 3b(ii) + } + + public void testBySortedSetMirror_588448152() throws Exception { + System.out.println("-588448152 Full Checking"); + trial(-588448152, true); // exposed root-delete-on-merge problems + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/BTreeTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/BTreeTests.java new file mode 100644 index 000000000..42711e2cc --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/BTreeTests.java @@ -0,0 +1,264 @@ +/******************************************************************************* + * Copyright (c) 2006, 2016 Symbian Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Symbian - Initial implementation + * Markus Schorn (Wind River Systems) + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd; + +import junit.framework.Test; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.tests.nd.util.BaseTestCase; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.BTree; +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.db.IBTreeComparator; +import org.eclipse.jdt.internal.core.nd.db.IBTreeVisitor; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * Test insertion/deletion of records of a mock record type in a B-tree. + * + * @author aferguso + */ +public class BTreeTests extends BaseTestCase { + private static int DEBUG= 0; + protected File dbFile; + protected Nd nd; + protected Database db; + protected BTree btree; + protected int rootRecord; + protected IBTreeComparator comparator; + + public static Test suite() { + return suite(BTreeTests.class); + } + + // setUp is not used since we need to parameterize this method, + // and invoke it multiple times per Junit test + protected void init(int degree) throws Exception { + this.dbFile = File.createTempFile("ndtest", "db"); + this.nd = DatabaseTestUtil.createEmptyNd(getName()); + this.db = this.nd.getDB(); + this.db.setExclusiveLock(); + this.rootRecord = Database.DATA_AREA_OFFSET; + this.comparator = new BTMockRecordComparator(); + this.btree = new BTree(this.nd, this.rootRecord, degree, this.comparator); + } + + // tearDown is not used for the same reason as above + protected void finish() throws Exception { + this.db.close(); + this.dbFile.deleteOnExit(); + } + + + public void testBySortedSetMirrorLite() throws Exception { + sortedMirrorTest(8); + } + + /** + * Test random (but reproducible via known seed) sequences of insertions/deletions + * and use TreeSet as a reference implementation to check behaviour against. + * @throws Exception + */ + protected void sortedMirrorTest(int noTrials) throws Exception { + Random seeder = new Random(90210); + + for (int i = 0; i < noTrials; i++) { + int seed = seeder.nextInt(); + if (DEBUG > 0) + System.out.println("Iteration #" + i); + trial(seed, false); + } + } + + /** + * Test random (but reproducible via known seed) sequence of insertions + * and use TreeSet as a reference implementation to check behaviour against. + * @throws Exception + */ + public void testInsertion() throws Exception { + Random seeder = new Random(); + + for (int i = 0; i < 6; i++) { + int seed = seeder.nextInt(); + if (DEBUG > 0) + System.out.println("Iteration #" + i); + trialImp(seed, false, new Random(seed * 2), 1); + } + } + + /** + * Bug 402177: BTree.insert should return the matching record if the new record was not inserted. + */ + public void testEquivalentRecordInsert_Bug402177() throws Exception { + init(8); + try { + BTMockRecord value1 = new BTMockRecord(this.db, 42); + BTMockRecord value2 = new BTMockRecord(this.db, 42); + + long insert1 = this.btree.insert(value1.getRecord()); + long insert2 = this.btree.insert(value2.getRecord()); + assertEquals(insert1, insert2); + } finally { + finish(); + } + } + + /** + * Insert/Delete a random number of records into/from the B-tree + * @param seed the seed for obtaining the deterministic random testing + * @param checkCorrectnessEachIteration if true, then on every single insertion/deletion check that the B-tree invariants + * still hold + * @throws Exception + */ + protected void trial(int seed, final boolean checkCorrectnessEachIteration) throws Exception { + Random random = new Random(seed); + + // the probabilty that a particular iterations action will be an insertion + double pInsert = Math.min(0.5 + random.nextDouble(), 1); + + trialImp(seed, checkCorrectnessEachIteration, random, pInsert); + } + + private void trialImp(int seed, final boolean checkCorrectnessEachIteration, Random random, + double pInsert) throws Exception { + final int degree = 2 + random.nextInt(11); + final int nIterations = random.nextInt(100000); + final SortedSet<Integer> expected = new TreeSet<>(); + final List<BTMockRecord> history = new ArrayList<>(); + + init(degree); + + if (DEBUG > 0) + System.out.print("\t " + seed + " " + (nIterations/1000) + "K: "); + for (int i = 0; i < nIterations; i++) { + if (random.nextDouble() < pInsert) { + Integer value = new Integer(random.nextInt(Integer.MAX_VALUE)); + boolean newEntry = expected.add(value); + if (newEntry) { + BTMockRecord btValue = new BTMockRecord(this.db, value.intValue()); + history.add(btValue); + if (DEBUG > 1) + System.out.println("Add: " + value + " @ " + btValue.record); + this.btree.insert(btValue.getRecord()); + } + } else { + if (!history.isEmpty()) { + int index = random.nextInt(history.size()); + BTMockRecord btValue = history.get(index); + history.remove(index); + expected.remove(new Integer(btValue.intValue())); + if (DEBUG > 1) + System.out.println("Remove: " + btValue.intValue() + " @ " + btValue.record); + this.btree.delete(btValue.getRecord()); + } + } + if (i % 1000 == 0 && DEBUG > 0) { + System.out.print("."); + } + if (checkCorrectnessEachIteration) { + assertBTreeMatchesSortedSet("[iteration " + i + "] ", this.btree, expected); + assertBTreeInvariantsHold("[iteration " + i + "] "); + } + } + if (DEBUG > 0) + System.out.println(); + + assertBTreeMatchesSortedSet("[Trial end] ", this.btree, expected); + assertBTreeInvariantsHold("[Trial end]"); + + finish(); + } + + public void assertBTreeInvariantsHold(String msg) throws CoreException { + String errorReport = this.btree.getInvariantsErrorReport(); + if (!errorReport.equals("")) { + fail("Invariants do not hold: " + errorReport); + } + } + + public void assertBTreeMatchesSortedSet(final String msg, BTree actual, SortedSet<Integer> expected) throws CoreException { + final Iterator<Integer> i = expected.iterator(); + this.btree.accept(new IBTreeVisitor() { + int k; + @Override + public int compare(long record) { + return 0; + } + + @Override + public boolean visit(long record) { + if (record != 0) { + BTMockRecord btValue = new BTMockRecord(record, BTreeTests.this.db); + if (i.hasNext()) { + Integer exp = i.next(); + assertEquals(msg + " Differ at index: " + this.k, btValue.intValue(), exp.intValue()); + this.k++; + } else { + fail("Sizes different"); + return false; + } + } + return true; + } + }); + } + + private static class BTMockRecord { + public static final int VALUE_PTR = 0; + public static final int RECORD_SIZE = Database.INT_SIZE; + long record; + Database db; + + /** + * Make a new record + */ + public BTMockRecord(Database db, int value) throws CoreException { + this.db = db; + this.record = db.malloc(BTMockRecord.RECORD_SIZE, Database.POOL_MISC); + db.putInt(this.record + VALUE_PTR, value); + } + + /** + * Get an existing record + */ + public BTMockRecord(long record, Database db) { + this.db = db; + this.record = record; + } + + public int intValue() { + return this.db.getInt(this.record); + } + + public long getRecord() { + return this.record; + } + } + + private class BTMockRecordComparator implements IBTreeComparator { + public BTMockRecordComparator() { + } + + @Override + public int compare(Nd ndToCompare, long record1, long record2) { + Database dbToCompare = ndToCompare.getDB(); + return dbToCompare.getInt(record1) - dbToCompare.getInt(record2); + } + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/DatabaseTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/DatabaseTest.java new file mode 100644 index 000000000..9dd4557c4 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/DatabaseTest.java @@ -0,0 +1,333 @@ +/******************************************************************************* + * Copyright (c) 2005, 2016 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - initial API and implementation + * Andrew Ferguson (Symbian) + * Markus Schorn (Wind River Systems) + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd; + +import java.io.File; +import java.util.Random; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.tests.nd.util.BaseTestCase; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.NdNodeTypeRegistry; +import org.eclipse.jdt.internal.core.nd.db.BTree; +import org.eclipse.jdt.internal.core.nd.db.ChunkCache; +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.db.IBTreeComparator; +import org.eclipse.jdt.internal.core.nd.db.IBTreeVisitor; +import org.eclipse.jdt.internal.core.nd.db.IString; +import org.eclipse.jdt.internal.core.nd.db.IndexException; +import org.eclipse.jdt.internal.core.nd.db.ShortString; + +import junit.framework.Test; + +/** + * Tests for the {@link Database} class. + */ +public class DatabaseTest extends BaseTestCase { + // This constant can be used to run the test with very large databases. + // Try, for example, setting it to Integer.MAX_VALUE * 7L; + private static final long TEST_OFFSET = 0; + private Nd nd; + protected Database db; + private static final int CURRENT_VERSION = 10; + + @Override + protected void setUp() throws Exception { + super.setUp(); + String testName = getName(); + NdNodeTypeRegistry<NdNode> registry = new NdNodeTypeRegistry<>(); + this.nd = new Nd(DatabaseTestUtil.getTempDbName(testName), new ChunkCache(), registry, + 0, 100, CURRENT_VERSION); + this.db = this.nd.getDB(); + this.db.setExclusiveLock(); + + // Allocate all database chunks up to TEST_OFFSET. + int count = 0; + for (long offset = 0; offset < TEST_OFFSET;) { + offset = this.db.malloc(Database.MAX_MALLOC_SIZE, Database.POOL_MISC); + if (++count >= 1000) { + this.db.flush(); + count = 0; + } + } + this.db.flush(); + } + + public static Test suite() { + return BaseTestCase.suite(DatabaseTest.class); + } + + @Override + protected void tearDown() throws Exception { + this.db.close(); + if (!this.db.getLocation().delete()) { + this.db.getLocation().deleteOnExit(); + } + this.db= null; + } + + public void testBlockSizeAndFirstBlock() throws Exception { + assertEquals(CURRENT_VERSION, this.db.getVersion()); + + final int realsize = 42; + final int deltas = (realsize + Database.BLOCK_HEADER_SIZE + Database.BLOCK_SIZE_DELTA - 1) / Database.BLOCK_SIZE_DELTA; + final int blocksize = deltas * Database.BLOCK_SIZE_DELTA; + final int freeDeltas= Database.CHUNK_SIZE / Database.BLOCK_SIZE_DELTA - deltas; + + long mem = this.db.malloc(realsize, Database.POOL_MISC); + assertEquals(-blocksize, this.db.getShort(mem - Database.BLOCK_HEADER_SIZE)); + this.db.free(mem, Database.POOL_MISC); + assertEquals(blocksize, this.db.getShort(mem - Database.BLOCK_HEADER_SIZE)); + assertEquals(mem, this.db.getRecPtr((deltas - Database.MIN_BLOCK_DELTAS +1 ) * Database.INT_SIZE)); + assertEquals(mem + blocksize, this.db.getRecPtr((freeDeltas - Database.MIN_BLOCK_DELTAS + 1) * Database.INT_SIZE)); + } + + public void testBug192437() throws Exception { + File tmp= File.createTempFile("readOnlyEmpty", ".db"); + try { + tmp.setReadOnly(); + + /* check opening a readonly file for rw access fails */ + try { + new Database(tmp, ChunkCache.getSharedInstance(), 0, false); + fail("A readonly file should not be openable with write-access"); + } catch (IndexException e) { + // we expect to get a failure here + } + + /* check opening a readonly file for read access does not fail */ + try { + new Database(tmp, ChunkCache.getSharedInstance(), 0, true); + } catch (IndexException e) { + fail("A readonly file should be readable by a permanently readonly database " + e); + } + } finally { + tmp.delete(); // this may be pointless on some platforms + } + } + + public void testFreeBlockLinking() throws Exception { + final int realsize = 42; + final int deltas = (realsize + Database.BLOCK_HEADER_SIZE + Database.BLOCK_SIZE_DELTA - 1) / Database.BLOCK_SIZE_DELTA; + + long mem1 = this.db.malloc(realsize, Database.POOL_MISC); + long mem2 = this.db.malloc(realsize, Database.POOL_MISC); + this.db.free(mem1, Database.POOL_MISC); + this.db.free(mem2, Database.POOL_MISC); + assertEquals(mem2, this.db.getRecPtr((deltas - Database.MIN_BLOCK_DELTAS + 1) * Database.INT_SIZE)); + assertEquals(0, this.db.getRecPtr(mem2)); + assertEquals(mem1, this.db.getRecPtr(mem2 + Database.INT_SIZE)); + assertEquals(mem2, this.db.getRecPtr(mem1)); + assertEquals(0, this.db.getRecPtr(mem1 + Database.INT_SIZE)); + } + + public void testSimpleAllocationLifecycle() throws Exception { + long mem1 = this.db.malloc(42, Database.POOL_MISC); + this.db.free(mem1, Database.POOL_MISC); + long mem2 = this.db.malloc(42, Database.POOL_MISC); + assertEquals(mem2, mem1); + } + + private static class FindVisitor implements IBTreeVisitor { + private Database db; + private String key; + private long address; + + public FindVisitor(Database db, String key) { + this.db = db; + this.key = key; + } + + @Override + public int compare(long toCompare) { + return this.db.getString(this.db.getRecPtr(toCompare + 4)).compare(this.key, true); + } + + @Override + public boolean visit(long toCompare) { + this.address = toCompare; + return false; + } + + public long getRecord() { + return this.address; + } + } + + public void testStringsInBTree() throws Exception { + String[] names = { + "ARLENE", + "BRET", + "CINDY", + "DENNIS", + "EMILY", + "FRANKLIN", + "GERT", + "HARVEY", + "IRENE", + "JOSE", + "KATRINA", + "LEE", + "MARIA", + "NATE", + "OPHELIA", + "PHILIPPE", + "RITA", + "STAN", + "TAMMY", + "VINCE", + "WILMA", + "ALPHA", + "BETA" + }; + + IBTreeComparator comparator = new IBTreeComparator() { + @Override + public int compare(Nd ndToCompare, long record1, long record2) { + IString string1 = DatabaseTest.this.db.getString(DatabaseTest.this.db.getRecPtr(record1 + 4)); + IString string2 = DatabaseTest.this.db.getString(DatabaseTest.this.db.getRecPtr(record2 + 4)); + return string1.compare(string2, true); + } + }; + + BTree btree = new BTree(this.nd, Database.DATA_AREA_OFFSET, comparator); + for (int i = 0; i < names.length; ++i) { + String name = names[i]; + long record = this.db.malloc(8, Database.POOL_MISC); + this.db.putInt(record + 0, i); + IString string = this.db.newString(name); + this.db.putRecPtr(record + 4, string.getRecord()); + btree.insert(record); + } + + for (int i = 0; i < names.length; ++i) { + String name = names[i]; + FindVisitor finder = new FindVisitor(this.db, name); + btree.accept(finder); + long record = finder.getRecord(); + assertTrue(record != 0); + assertEquals(i, this.db.getInt(record)); + IString rname = this.db.getString(this.db.getRecPtr(record + 4)); + assertTrue(rname.equals(name)); + } + } + + private final int GT = 1, LT = -1, EQ = 0; + + public void testShortStringComparison() throws CoreException { + Random r= new Random(90210); + + assertCMP("", this.EQ, "", true); + assertCMP("", this.EQ, "", false); + + doTrials(1000, 1, ShortString.MAX_BYTE_LENGTH / 2, r, true); + doTrials(1000, 1, ShortString.MAX_BYTE_LENGTH / 2, r, false); + doTrials(1000, 1, ShortString.MAX_BYTE_LENGTH, r, true); + doTrials(1000, 1, ShortString.MAX_BYTE_LENGTH, r, false); + + assertCMP("a", this.LT, "b", true); + assertCMP("aa", this.LT, "ab", true); + assertCMP("a", this.EQ, "a", true); + + assertCMP("a", this.GT, "A", true); + assertCMP("aa", this.GT, "aA", true); + assertCMP("a", this.GT, "B", true); + + assertCMP("a", this.EQ, "a", false); + assertCMP("a", this.EQ, "A", false); + } + + public void testLongStringComparison() throws CoreException { + Random r= new Random(314159265); + doTrials(100, ShortString.MAX_BYTE_LENGTH + 1, ShortString.MAX_BYTE_LENGTH * 2, r, true); + doTrials(100, ShortString.MAX_BYTE_LENGTH + 1, ShortString.MAX_BYTE_LENGTH * 2, r, false); + } + + private void doTrials(int n, int min, int max, Random r, boolean caseSensitive) throws CoreException { +// long start = System.currentTimeMillis(); + for (int i= 0; i < n; i++) { + String a = randomString(min, max, r); + String b = randomString(min, max, r); + int expected = caseSensitive ? a.compareTo(b) : a.compareToIgnoreCase(b); + assertCMP(a, expected, b, caseSensitive); + } +// System.out.print("Trials: " + n + " Max length: " + max + " ignoreCase: " + !caseSensitive); +// System.out.println(" Time: " + (System.currentTimeMillis() - start)); + } + + private String randomString(int min, int max, Random r) { + int len = min + r.nextInt(max - min); + return randomString(len, r); + } + + private String randomString(int len, Random r) { + StringBuilder result = new StringBuilder(len); + for (int i= 0; i < len; i++) { + result.append(randomChar(r)); + } + return result.toString(); + } + + private char randomChar(Random r) { + // We only match String.compareToIgnoreCase behavior within this limited range. + return (char) (32 + r.nextInt(40)); + } + + private void assertCMP(String a, int expected, String b, boolean caseSensitive) throws CoreException { + char[] acs = a.toCharArray(); + char[] bcs = b.toCharArray(); + IString aiss = this.db.newString(a); + IString biss = this.db.newString(b); + IString aisc = this.db.newString(acs); + IString bisc = this.db.newString(bcs); + + assertEquals(a.hashCode(), aiss.hashCode()); + assertEquals(a.hashCode(), aisc.hashCode()); + assertEquals(b.hashCode(), biss.hashCode()); + assertEquals(b.hashCode(), bisc.hashCode()); + + assertEquals(aiss, a); + assertEquals(aisc, a); + assertEquals(biss, b); + assertEquals(bisc, b); + + assertSignEquals(expected, aiss.compare(bcs, caseSensitive)); + assertSignEquals(expected, aiss.compare(biss, caseSensitive)); + assertSignEquals(expected, aiss.compare(bisc, caseSensitive)); + assertSignEquals(expected, aiss.compare(b, caseSensitive)); + assertSignEquals(expected, aiss.comparePrefix(bcs, caseSensitive)); + + assertSignEquals(expected, -biss.compare(acs, caseSensitive)); + assertSignEquals(expected, -biss.compare(aiss, caseSensitive)); + assertSignEquals(expected, -biss.compare(aisc, caseSensitive)); + assertSignEquals(expected, -biss.compare(a, caseSensitive)); + assertSignEquals(expected, -biss.comparePrefix(acs, caseSensitive)); + + if (!caseSensitive && expected != 0) { + assertSignEquals(expected, aiss.compareCompatibleWithIgnoreCase(bcs)); + assertSignEquals(expected, aiss.compareCompatibleWithIgnoreCase(biss)); + assertSignEquals(expected, aiss.compareCompatibleWithIgnoreCase(bisc)); + + assertSignEquals(expected, -biss.compareCompatibleWithIgnoreCase(acs)); + assertSignEquals(expected, -biss.compareCompatibleWithIgnoreCase(aiss)); + assertSignEquals(expected, -biss.compareCompatibleWithIgnoreCase(aisc)); + } + } + + private void assertSignEquals(int a, int b) { + a= a < 0 ? -1 : (a > 0 ? 1 : 0); + b= b < 0 ? -1 : (b > 0 ? 1 : 0); + assertEquals(a, b); + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/DatabaseTestUtil.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/DatabaseTestUtil.java new file mode 100644 index 000000000..30040db2b --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/DatabaseTestUtil.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd; + +import java.io.File; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.jdt.core.tests.Activator; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.NdNodeTypeRegistry; +import org.eclipse.jdt.internal.core.nd.db.ChunkCache; + +/** + * + */ +public class DatabaseTestUtil { + + public static IPath getTestDir() { + Plugin plugin = Activator.getInstance(); + + IPath path = plugin.getStateLocation().append("tests/"); + File file = path.toFile(); + if (!file.exists()) + file.mkdir(); + return path; + } + + public static File getTempDbName(String testName) { + return DatabaseTestUtil.getTestDir().append(testName + System.currentTimeMillis() + ".dat").toFile(); + } + + /** + * Creates an empty {@link Nd} with an empty type registry and randomly-named + * database for the given test name + * + * @param testName + * @return the new {@link Nd} + */ + public static Nd createEmptyNd(String testName) { + NdNodeTypeRegistry<NdNode> registry = new NdNodeTypeRegistry<>(); + return new Nd(DatabaseTestUtil.getTempDbName(testName), new ChunkCache(), registry, 0, 0, 0); + } + + public static Nd createEmptyNd(String testName, NdNodeTypeRegistry<NdNode> registry) { + return new Nd(DatabaseTestUtil.getTempDbName(testName), new ChunkCache(), registry, 0, 0, 0); + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/FieldBackPointerTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/FieldBackPointerTest.java new file mode 100644 index 000000000..b2ccfad6b --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/FieldBackPointerTest.java @@ -0,0 +1,379 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +import org.eclipse.jdt.core.tests.nd.util.BaseTestCase; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.NdNodeTypeRegistry; +import org.eclipse.jdt.internal.core.nd.RawGrowableArray; +import org.eclipse.jdt.internal.core.nd.field.FieldInt; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +import junit.framework.Test; + +public class FieldBackPointerTest extends BaseTestCase { + public static class ForwardPointerStruct extends NdNode { + public static final FieldManyToOne<BackPointerStruct> FORWARD; + public static final FieldManyToOne<BackPointerStruct> OWNER; + + @SuppressWarnings("hiding") + public static final StructDef<ForwardPointerStruct> type; + + static { + type = StructDef.create(ForwardPointerStruct.class, NdNode.type); + + FORWARD = FieldManyToOne.create(type, BackPointerStruct.BACK); + OWNER = FieldManyToOne.createOwner(type, BackPointerStruct.OWNED); + type.done(); + } + + public ForwardPointerStruct(Nd nd) { + super(nd); + } + + public ForwardPointerStruct(Nd nd, long record) { + super(nd, record); + } + + public void setBp(BackPointerStruct toSet) { + FORWARD.put(getNd(), this.address, toSet); + } + + public BackPointerStruct getBp() { + return FORWARD.get(getNd(), this.address); + } + + public void setOwner(BackPointerStruct owner) { + OWNER.put(getNd(), this.address, owner); + } + + public BackPointerStruct getOwner() { + return OWNER.get(getNd(), this.address); + } + } + + public static class BackPointerStruct extends NdNode { + public static final FieldOneToMany<ForwardPointerStruct> BACK; + public static final FieldOneToMany<ForwardPointerStruct> OWNED; + public static final FieldInt SOMEINT; + + @SuppressWarnings("hiding") + public static final StructDef<BackPointerStruct> type; + + static { + type = StructDef.create(BackPointerStruct.class, NdNode.type); + + BACK = FieldOneToMany.create(type, ForwardPointerStruct.FORWARD, 2); + OWNED = FieldOneToMany.create(type, ForwardPointerStruct.OWNER, 0); + SOMEINT = type.addInt(); + type.done(); + } + + public BackPointerStruct(Nd nd) { + super(nd); + + // Fill with nonzero values to ensure that "OWNED" doesn't read beyond its boundary + SOMEINT.put(nd, this.address, 0xf0f0f0f0); + } + + public BackPointerStruct(Nd nd, long record) { + super(nd, record); + } + + public void ensureBackPointerCapacity(int capacity) { + BACK.ensureCapacity(getNd(), this.address, capacity); + } + + public int getBackPointerCapacity() { + return BACK.getCapacity(getNd(), this.address); + } + + public List<ForwardPointerStruct> getBackPointers() { + return BACK.asList(getNd(), this.address); + } + + public List<ForwardPointerStruct> getOwned() { + return OWNED.asList(getNd(), this.address); + } + + public int backPointerSize() { + return BACK.size(getNd(), this.address); + } + + public boolean backPointersAreEmpty() { + return BACK.isEmpty(getNd(), this.address); + } + + public boolean ownedPointersAreEmpty() { + return OWNED.isEmpty(getNd(), this.address); + } + + public ForwardPointerStruct getBackPointer(int i) { + return BACK.get(getNd(), this.address, i); + } + } + + ForwardPointerStruct fa; + ForwardPointerStruct fb; + ForwardPointerStruct fc; + ForwardPointerStruct fd; + BackPointerStruct ba; + BackPointerStruct bb; + private Nd nd; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + NdNodeTypeRegistry<NdNode> registry = new NdNodeTypeRegistry<>(); + registry.register(0, BackPointerStruct.type.getFactory()); + registry.register(1, ForwardPointerStruct.type.getFactory()); + this.nd = DatabaseTestUtil.createEmptyNd(getName(), registry); + this.nd.getDB().setExclusiveLock(); + this.ba = new BackPointerStruct(this.nd); + this.bb = new BackPointerStruct(this.nd); + this.fa = new ForwardPointerStruct(this.nd); + this.fb = new ForwardPointerStruct(this.nd); + this.fc = new ForwardPointerStruct(this.nd); + this.fd = new ForwardPointerStruct(this.nd); + } + + public static Test suite() { + return BaseTestCase.suite(FieldBackPointerTest.class); + } + + void assertBackPointers(BackPointerStruct bp, ForwardPointerStruct... fp) { + HashSet<ForwardPointerStruct> backPointers = new HashSet<>(bp.getBackPointers()); + HashSet<ForwardPointerStruct> desired = new HashSet<>(); + + desired.addAll(Arrays.asList(fp)); + assertEquals(desired, backPointers); + } + + public void testWriteFollowedByReadReturnsSameThing() throws Exception { + this.fa.setBp(this.ba); + BackPointerStruct backpointer = this.fa.getBp(); + + assertEquals(this.ba, backpointer); + } + + public void testListWithoutInlineElementsCanBeEmpty() throws Exception { + assertTrue(this.ba.ownedPointersAreEmpty()); + } + + public void testReadNull() throws Exception { + assertEquals(null, this.fa.getBp()); + } + + public void testAssigningTheSamePointerTwiceIsANoop() throws Exception { + this.fa.setBp(this.ba); + + assertBackPointers(this.ba, this.fa); + + // Now do the same thing again + this.fa.setBp(this.ba); + + assertBackPointers(this.ba, this.fa); + } + + public void testAssigningForwardPointerInsertsBackPointer() throws Exception { + this.fa.setBp(this.ba); + + assertEquals(Arrays.asList(this.fa), this.ba.getBackPointers()); + assertEquals(1, this.ba.backPointerSize()); + } + + public void testRemovesInlineElement() throws Exception { + this.fa.setBp(this.ba); + this.fb.setBp(this.ba); + this.fc.setBp(this.ba); + this.fd.setBp(this.ba); + + assertEquals(4, this.ba.backPointerSize()); + this.fb.setBp(null); + assertEquals(3, this.ba.backPointerSize()); + + assertBackPointers(this.ba, this.fa, this.fc, this.fd); + } + + public void testRemovesElementFromGrowableBlock() throws Exception { + this.fa.setBp(this.ba); + this.fb.setBp(this.ba); + this.fc.setBp(this.ba); + this.fd.setBp(this.ba); + + assertEquals(4, this.ba.backPointerSize()); + this.fc.setBp(null); + assertEquals(3, this.ba.backPointerSize()); + + assertBackPointers(this.ba, this.fa, this.fb, this.fd); + } + + public void testDestructingForwardPointerRemovesBackPointer() throws Exception { + this.fa.setBp(this.ba); + this.fb.setBp(this.ba); + this.fc.setBp(this.ba); + + this.fb.delete(); + this.nd.processDeletions(); + + assertBackPointers(this.ba, this.fa, this.fc); + } + + public void testDestructingBackPointerClearsForwardPointers() throws Exception { + this.fa.setBp(this.ba); + this.fb.setBp(this.ba); + this.fc.setBp(this.ba); + + this.ba.delete(); + this.nd.processDeletions(); + + assertEquals(null, this.fa.getBp()); + assertEquals(null, this.fb.getBp()); + assertEquals(null, this.fc.getBp()); + } + + public void testElementsRemainInInsertionOrderIfNoRemovals() throws Exception { + this.fa.setBp(this.ba); + this.fb.setBp(this.ba); + this.fc.setBp(this.ba); + this.fd.setBp(this.ba); + + assertEquals(Arrays.asList(this.fa, this.fb, this.fc, this.fd), this.ba.getBackPointers()); + } + + public void testDeletingOwnerDeletesOwned() throws Exception { + this.fa.setBp(this.ba); + this.fa.setOwner(this.bb); + + this.fb.setBp(this.ba); + this.fb.setOwner(this.bb); + + this.fc.setBp(this.ba); + + this.bb.delete(); + this.nd.processDeletions(); + + assertBackPointers(this.ba, this.fc); + } + + public void testEnsureCapacityDoesNothingIfLessThanInlineElements() throws Exception { + this.ba.ensureBackPointerCapacity(1); + assertEquals(2, this.ba.getBackPointerCapacity()); + } + + public void testEnsureCapacityAllocatesPowersOfTwoPlusInlineSize() throws Exception { + this.ba.ensureBackPointerCapacity(60); + assertEquals(66, this.ba.getBackPointerCapacity()); + } + + public void testEnsureCapacityAllocatesMinimumSize() throws Exception { + this.ba.ensureBackPointerCapacity(3); + assertEquals(4, this.ba.getBackPointerCapacity()); + } + + public void testEnsureCapacityClampsToChunkSize() throws Exception { + this.ba.ensureBackPointerCapacity(RawGrowableArray.getMaxGrowableBlockSize() - 40); + assertEquals(RawGrowableArray.getMaxGrowableBlockSize() + 2, this.ba.getBackPointerCapacity()); + } + + public void testEnsureCapacityGrowsByMultiplesOfMaxBlockSizeOnceMetablockInUse() throws Exception { + int maxBlockSize = RawGrowableArray.getMaxGrowableBlockSize(); + this.ba.ensureBackPointerCapacity(maxBlockSize * 3 - 100); + assertEquals(maxBlockSize * 3 + 2, this.ba.getBackPointerCapacity()); + } + + public void testAdditionsWontReduceCapacity() throws Exception { + int maxBlockSize = RawGrowableArray.getMaxGrowableBlockSize(); + this.ba.ensureBackPointerCapacity(maxBlockSize); + + this.fa.setBp(this.ba); + this.fb.setBp(this.ba); + this.fc.setBp(this.ba); + this.fd.setBp(this.ba); + + assertEquals(maxBlockSize + 2, this.ba.getBackPointerCapacity()); + } + + public void testIsEmpty() throws Exception { + assertTrue(this.ba.backPointersAreEmpty()); + this.fa.setBp(this.ba); + assertFalse(this.ba.backPointersAreEmpty()); + this.fb.setBp(this.ba); + this.fc.setBp(this.ba); + this.fd.setBp(this.ba); + assertFalse(this.ba.backPointersAreEmpty()); + } + + public void testRemovalsReduceCapacity() throws Exception { + int maxBlockSize = RawGrowableArray.getMaxGrowableBlockSize(); + this.ba.ensureBackPointerCapacity(maxBlockSize); + + this.fa.setBp(this.ba); + this.fb.setBp(this.ba); + this.fc.setBp(this.ba); + assertEquals(maxBlockSize + 2, this.ba.getBackPointerCapacity()); + + this.fb.setBp(null); + this.fc.setBp(null); + + assertEquals(2, this.ba.getBackPointerCapacity()); + } + + public void testInsertEnoughToUseMetablock() throws Exception { + // We need enough instances to fill several full blocks since we don't reclaim + // memory until there are two unused blocks. + int numToAllocate = RawGrowableArray.getMaxGrowableBlockSize() * 4 + 1; + + List<ForwardPointerStruct> allocated = new ArrayList<>(); + + for (int count = 0; count < numToAllocate; count++) { + ForwardPointerStruct next = new ForwardPointerStruct(this.nd); + + next.setBp(this.ba); + assertEquals(next, this.ba.getBackPointer(count)); + allocated.add(next); + assertEquals(count + 1, this.ba.backPointerSize()); + } + + assertEquals(allocated.get(numToAllocate - 1), this.ba.getBackPointer(numToAllocate - 1)); + assertEquals(numToAllocate, this.ba.backPointerSize()); + + int correctSize = numToAllocate; + for (ForwardPointerStruct next : allocated) { + next.setBp(null); + assertEquals(--correctSize, this.ba.backPointerSize()); + } + + assertEquals(0, this.ba.backPointerSize()); + assertEquals(2, this.ba.getBackPointerCapacity()); + } + + public void testGrowExistingMetablock() throws Exception { + int blockSize = RawGrowableArray.getMaxGrowableBlockSize(); + + this.ba.ensureBackPointerCapacity(2 * blockSize); + + assertEquals(2 * blockSize + 2, this.ba.getBackPointerCapacity()); + + this.ba.ensureBackPointerCapacity(6 * blockSize); + + assertEquals(6 * blockSize + 2, this.ba.getBackPointerCapacity()); + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/InheritenceTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/InheritenceTests.java new file mode 100644 index 000000000..bb0db5301 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/InheritenceTests.java @@ -0,0 +1,244 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd; + +import org.eclipse.jdt.core.tests.nd.util.BaseTestCase; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.NdNodeTypeRegistry; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +import junit.framework.Test; + +public class InheritenceTests extends BaseTestCase { + /** + * Every other object in this test has a pointer to the deletion detector, so we can detect + * which objects have been deleted by looking for the object in the backpointer list. + */ + public static class AllObjects extends NdNode { + public static final FieldOneToMany<BaseClass> BASE_CLASS_INSTANCES; + public static final FieldOneToMany<Reference> REFERENCE_INSTANCES; + + @SuppressWarnings("hiding") + public static final StructDef<AllObjects> type; + + static { + type = StructDef.create(AllObjects.class, NdNode.type); + + BASE_CLASS_INSTANCES = FieldOneToMany.create(type, BaseClass.DELETION_DETECTOR, 0); + REFERENCE_INSTANCES = FieldOneToMany.create(type, Reference.DELETION_DETECTOR, 0); + type.done(); + } + + public AllObjects(Nd nd, long record) { + super(nd, record); + } + + public AllObjects(Nd nd) { + super(nd); + } + + boolean contains(BaseClass toTest) { + return BASE_CLASS_INSTANCES.asList(getNd(), this.address).contains(toTest); + } + + boolean contains(Reference toTest) { + return REFERENCE_INSTANCES.asList(getNd(), this.address).contains(toTest); + } + } + + public static class BaseClass extends NdNode { + public static final FieldOneToMany<Reference> INCOMING_REFERENCES; + public static final FieldOneToMany<Reference> OWNED_REFERENCES; + public static final FieldManyToOne<AllObjects> DELETION_DETECTOR; + + @SuppressWarnings("hiding") + public static final StructDef<BaseClass> type; + + static { + type = StructDef.create(BaseClass.class, NdNode.type); + + INCOMING_REFERENCES = FieldOneToMany.create(type, Reference.BASE_CLASS_REFERENCE, 0); + OWNED_REFERENCES = FieldOneToMany.create(type, Reference.OWNER, 0); + DELETION_DETECTOR = FieldManyToOne.create(type, AllObjects.BASE_CLASS_INSTANCES); + type.useStandardRefCounting().done(); + } + + public BaseClass(Nd nd, AllObjects deletionDetector) { + super(nd); + + DELETION_DETECTOR.put(nd, this.address, deletionDetector); + } + + public BaseClass(Nd nd, long record) { + super(nd, record); + } + } + + public static class SubClass extends BaseClass { + public static final FieldOneToMany<Reference> MORE_REFERENCES; + + @SuppressWarnings("hiding") + public static final StructDef<SubClass> type; + + static { + type = StructDef.create(SubClass.class, BaseClass.type); + + MORE_REFERENCES = FieldOneToMany.create(type, Reference.SUB_CLASS_REFERENCE, 0); + type.useStandardRefCounting().done(); + } + + public SubClass(Nd nd, long record) { + super(nd, record); + } + + public SubClass(Nd nd, AllObjects deletionDetector) { + super(nd, deletionDetector); + } + } + + public static class Reference extends NdNode { + public static final FieldManyToOne<BaseClass> BASE_CLASS_REFERENCE; + public static final FieldManyToOne<BaseClass> OWNER; + public static final FieldManyToOne<SubClass> SUB_CLASS_REFERENCE; + public static final FieldManyToOne<AllObjects> DELETION_DETECTOR; + + @SuppressWarnings("hiding") + public static StructDef<Reference> type; + + static { + type = StructDef.create(Reference.class, NdNode.type); + + BASE_CLASS_REFERENCE = FieldManyToOne.create(type, BaseClass.INCOMING_REFERENCES); + OWNER = FieldManyToOne.createOwner(type, BaseClass.OWNED_REFERENCES); + SUB_CLASS_REFERENCE = FieldManyToOne.create(type, SubClass.MORE_REFERENCES); + DELETION_DETECTOR = FieldManyToOne.create(type, AllObjects.REFERENCE_INSTANCES); + type.done(); + } + + public Reference(Nd nd, long record) { + super(nd, record); + } + + public Reference(Nd nd, AllObjects deletionDetector) { + super(nd); + + DELETION_DETECTOR.put(nd, this.address, deletionDetector); + } + + public void setBaseClassReference(BaseClass target) { + BASE_CLASS_REFERENCE.put(getNd(), this.address, target); + } + + public void setOwner(BaseClass target) { + OWNER.put(getNd(), this.address, target); + } + + public void setSubClassReference(SubClass target) { + SUB_CLASS_REFERENCE.put(getNd(), this.address, target); + } + } + + AllObjects allObjects; + BaseClass baseClass; + SubClass subClass; + Reference refA; + Reference refB; + Reference refC; + private Nd nd; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + NdNodeTypeRegistry<NdNode> registry = new NdNodeTypeRegistry<>(); + registry.register(0, BaseClass.type.getFactory()); + registry.register(1, SubClass.type.getFactory()); + registry.register(2, Reference.type.getFactory()); + registry.register(3, AllObjects.type.getFactory()); + this.nd = DatabaseTestUtil.createEmptyNd(getName(), registry); + this.nd.getDB().setExclusiveLock(); + + this.allObjects = new AllObjects(this.nd); + this.baseClass = new BaseClass(this.nd, this.allObjects); + this.subClass = new SubClass(this.nd, this.allObjects); + + this.refA = new Reference(this.nd, this.allObjects); + this.refB = new Reference(this.nd, this.allObjects); + this.refC = new Reference(this.nd, this.allObjects); + } + + public static Test suite() { + return BaseTestCase.suite(InheritenceTests.class); + } + + public void testRemovingOnlyRefcountDeletesObject() { + assertTrue(this.allObjects.contains(this.subClass)); + this.refA.setSubClassReference(this.subClass); + this.refA.setSubClassReference(null); + this.nd.processDeletions(); + assertFalse(this.allObjects.contains(this.subClass)); + } + + public void testReferencesToBaseClassIncludedInRefCountA() { + // Test what happens when the subclass reference is removed first. + this.refA.setSubClassReference(this.subClass); + this.refB.setBaseClassReference(this.subClass); + assertTrue(this.allObjects.contains(this.subClass)); + this.refA.setSubClassReference(null); + this.nd.processDeletions(); + assertTrue(this.allObjects.contains(this.subClass)); + this.refB.setBaseClassReference(null); + this.nd.processDeletions(); + assertFalse(this.allObjects.contains(this.subClass)); + } + + public void testReferencesToBaseClassIncludedInRefCountB() { + // Test what happens when the base class reference is removed first. + this.refA.setSubClassReference(this.subClass); + this.refB.setBaseClassReference(this.subClass); + this.nd.processDeletions(); + assertTrue(this.allObjects.contains(this.subClass)); + this.refB.setBaseClassReference(null); + this.nd.processDeletions(); + assertTrue(this.allObjects.contains(this.subClass)); + this.refA.setSubClassReference(null); + this.nd.processDeletions(); + assertFalse(this.allObjects.contains(this.subClass)); + } + + public void testOwnedPointersDontCountTowardsRefCount() { + this.refA.setOwner(this.subClass); + this.nd.processDeletions(); + assertTrue(this.allObjects.contains(this.subClass)); + this.refB.setBaseClassReference(this.subClass); + this.nd.processDeletions(); + assertTrue(this.allObjects.contains(this.subClass)); + assertTrue(this.allObjects.contains(this.refA)); + this.refB.setBaseClassReference(null); + this.nd.processDeletions(); + assertFalse(this.allObjects.contains(this.subClass)); + assertFalse(this.allObjects.contains(this.refA)); + } + + public void testMultipleReferences() { + this.refA.setBaseClassReference(this.subClass); + this.refB.setBaseClassReference(this.subClass); + this.refA.setBaseClassReference(null); + this.nd.processDeletions(); + assertTrue(this.allObjects.contains(this.subClass)); + this.refB.setBaseClassReference(null); + this.nd.processDeletions(); + assertFalse(this.allObjects.contains(this.subClass)); + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/Package.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/Package.java new file mode 100644 index 000000000..7485b5431 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/Package.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd; + +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.JavaCore; + +/** + * @noreference This class is not intended to be referenced by clients + */ +/* package */ class Package { + public static String PLUGIN_ID = JavaCore.PLUGIN_ID; + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public static void log(Throwable e) { + String msg= e.getMessage(); + if (msg == null) { + log("Error", e); //$NON-NLS-1$ + } else { + log("Error: " + msg, e); //$NON-NLS-1$ + } + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public static void log(String message, Throwable e) { + log(createStatus(message, e)); + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public static IStatus createStatus(String msg, Throwable e) { + return new Status(IStatus.ERROR, PLUGIN_ID, msg, e); + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public static IStatus createStatus(String msg) { + return new Status(IStatus.ERROR, PLUGIN_ID, msg); + } + + /** + * @noreference This method is not intended to be referenced by clients. + * + * Returns the appropriate ILog for this package + */ + public static ILog getLog() { + Plugin plugin = JavaCore.getPlugin(); + if (plugin == null) { + return null; + } + return plugin.getLog(); + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public static void log(IStatus status) { + getLog().log(status); + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/RunIndexTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/RunIndexTests.java new file mode 100644 index 000000000..a6c2bbc16 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/RunIndexTests.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.eclipse.jdt.core.tests.junit.extension.TestCase; +import org.eclipse.jdt.core.tests.nd.indexer.IndexerTest; + +import junit.framework.Test; +import junit.framework.TestSuite; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class RunIndexTests extends junit.framework.TestCase { +public RunIndexTests(String name) { + super(name); +} +public static Class[] getAllTestClasses() { + return new Class[] { + BTreeTests.class, + DatabaseTest.class, + FieldBackPointerTest.class, + IndexerTest.class, + InheritenceTests.class, + SearchKeyTests.class + }; +} +public static Test suite() { + TestSuite ts = new TestSuite(RunIndexTests.class.getName()); + + Class[] testClasses = getAllTestClasses(); + // Reset forgotten subsets of tests + TestCase.TESTS_PREFIX = null; + TestCase.TESTS_NAMES = null; + TestCase.TESTS_NUMBERS = null; + TestCase.TESTS_RANGE = null; + TestCase.RUN_ONLY_ID = null; + + for (int i = 0; i < testClasses.length; i++) { + Class testClass = testClasses[i]; + + // call the suite() method and add the resulting suite to the suite + try { + Method suiteMethod = testClass.getDeclaredMethod("suite", new Class[0]); //$NON-NLS-1$ + Test suite = (Test)suiteMethod.invoke(null, new Object[0]); + ts.addTest(suite); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.getTargetException().printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + return ts; +} +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/SearchKeyTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/SearchKeyTests.java new file mode 100644 index 000000000..e48ae79c9 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/SearchKeyTests.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd; + +import org.eclipse.jdt.core.tests.nd.util.BaseTestCase; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.NdNodeTypeRegistry; +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.field.FieldSearchIndex; +import org.eclipse.jdt.internal.core.nd.field.FieldSearchKey; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +import junit.framework.Test; + +public class SearchKeyTests extends BaseTestCase { + private static final String SEARCH_STRING_B = "Yo"; + private static final String SEARCH_STRING_A = "Heyguyswhatshappening"; + private static final String SEARCH_STRING_C = "Shnoogins"; + + public static class TestSearchIndex { + public static final FieldSearchIndex<Element> NICKNAME_INDEX; + public static final FieldSearchIndex<Element> NAME_INDEX; + + public static final StructDef<TestSearchIndex> type; + + static { + type = StructDef.create(TestSearchIndex.class); + NICKNAME_INDEX = FieldSearchIndex.create(type, Element.NICKNAME); + NAME_INDEX = FieldSearchIndex.create(type, Element.NAME); + type.done(); + } + + private final long address; + private Nd nd; + + public TestSearchIndex(Nd dom, long address) { + this.address = address; + this.nd = dom; + } + + public static TestSearchIndex getIndex(Nd nd) { + return new TestSearchIndex(nd, Database.DATA_AREA_OFFSET); + } + + public Element findName(String searchString) { + return NAME_INDEX.findFirst(this.nd, this.address, + FieldSearchIndex.SearchCriteria.create(searchString.toCharArray())); + } + + public Element findNickName(String searchString) { + return NICKNAME_INDEX.findFirst(this.nd, this.address, + FieldSearchIndex.SearchCriteria.create(searchString.toCharArray())); + } + } + + public static class Element extends NdNode { + public static final FieldSearchKey<TestSearchIndex> NAME; + public static final FieldSearchKey<TestSearchIndex> NICKNAME; + + @SuppressWarnings("hiding") + public static StructDef<Element> type; + + static { + type = StructDef.create(Element.class, NdNode.type); + + NAME = FieldSearchKey.create(type, TestSearchIndex.NAME_INDEX); + NICKNAME = FieldSearchKey.create(type, TestSearchIndex.NICKNAME_INDEX); + type.done(); + } + + public Element(Nd nd, long record) { + super(nd, record); + } + + public Element(Nd nd) { + super(nd); + } + + public void setName(String searchStringA) { + NAME.put(getNd(), this.address, searchStringA); + } + + public void setNickName(String searchStringA) { + NICKNAME.put(getNd(), this.address, searchStringA); + } + } + + private Nd nd; + private Element elementA; + private Element elementB; + private TestSearchIndex index; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + NdNodeTypeRegistry<NdNode> registry = new NdNodeTypeRegistry<>(); + registry.register(0, Element.type.getFactory()); + this.nd = DatabaseTestUtil.createEmptyNd(getName(), registry); + this.nd.getDB().setExclusiveLock(); + + this.elementA = new Element(this.nd); + this.elementB = new Element(this.nd); + + this.index = TestSearchIndex.getIndex(this.nd); + } + + public static Test suite() { + return BaseTestCase.suite(SearchKeyTests.class); + } + + public void testSettingKeyCausesInsertionInSearchIndex() { + this.elementA.setName(SEARCH_STRING_A); + this.elementB.setName(SEARCH_STRING_B); + + Element foundElementA = this.index.findName(SEARCH_STRING_A); + Element foundElementB = this.index.findName(SEARCH_STRING_B); + Element foundElementC = this.index.findName(SEARCH_STRING_C); + + assertEquals(this.elementA, foundElementA); + assertEquals(this.elementB, foundElementB); + assertEquals(null, foundElementC); + } + + public void testChangingSearchKeyAffectsIndex() { + this.elementA.setName(SEARCH_STRING_A); + + Element foundElementA = this.index.findName(SEARCH_STRING_A); + Element foundElementB = this.index.findName(SEARCH_STRING_B); + + assertEquals(null, foundElementB); + assertEquals(this.elementA, foundElementA); + + this.elementA.setName(SEARCH_STRING_B); + + foundElementA = this.index.findName(SEARCH_STRING_A); + foundElementB = this.index.findName(SEARCH_STRING_B); + + assertEquals(this.elementA, foundElementB); + assertEquals(null, foundElementA); + } + + public void testDeletingElementRemovesFromIndex() { + this.elementA.setName(SEARCH_STRING_A); + this.elementA.setNickName(SEARCH_STRING_B); + + assertEquals(this.elementA, this.index.findName(SEARCH_STRING_A)); + assertEquals(this.elementA, this.index.findNickName(SEARCH_STRING_B)); + + this.elementA.delete(); + this.nd.processDeletions(); + assertEquals(null, this.index.findName(SEARCH_STRING_A)); + assertEquals(null, this.index.findNickName(SEARCH_STRING_B)); + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/indexer/IndexerTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/indexer/IndexerTest.java new file mode 100644 index 000000000..f378a33bc --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/indexer/IndexerTest.java @@ -0,0 +1,214 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd.indexer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Semaphore; + +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jdt.core.IClassFile; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.IParent; +import org.eclipse.jdt.core.tests.model.AbstractJavaModelTests; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.core.nd.IReader; +import org.eclipse.jdt.internal.core.nd.db.ChunkCache; +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.indexer.IndexTester; +import org.eclipse.jdt.internal.core.nd.indexer.Indexer; +import org.eclipse.jdt.internal.core.nd.java.JavaIndex; +import org.eclipse.jdt.internal.core.nd.java.NdType; +import org.eclipse.jdt.internal.core.nd.java.NdTypeId; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeDescriptor; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory; +import org.eclipse.jdt.internal.core.nd.java.model.IndexBinaryType; + +import junit.framework.Test; + +/** + * Tests for the {@link Database} class. + */ +public class IndexerTest extends AbstractJavaModelTests { + + public IndexerTest(String name) { + super(name); + } + + private static final String PROJECT_NAME = "IndexerTest"; + private static JavaIndex index; + + @Override + protected void setUp() throws Exception { + String testName = getName(); + index = JavaIndexTestUtil.createTempIndex(testName); + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + deleteProject(PROJECT_NAME); + index.getNd().getPath().delete(); + index = null; + super.tearDown(); + } + + public static Test suite() { + return buildModelTestSuite(IndexerTest.class); + } + + /** + * Verifies that if the index fails a read due to call to {@link Thread#interrupt()}, subsequent reads will + * still succeed. + */ + public void testInterruptedException() throws Exception { + createJavaProject(PROJECT_NAME, new String[] {"src"}, new String[] {"JCL18_FULL"}, "bin", "1.8", true); + // Create an index + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + Indexer indexer = new Indexer(index.getNd(), root); + indexer.rescan(SubMonitor.convert(null)); + // Ensure we're starting with an empty page cache by creating a new + // Index accessor object on the same database + JavaIndex testIndex = JavaIndex + .getIndex(JavaIndex.createNd(index.getNd().getDB().getLocation(), new ChunkCache())); + + Semaphore semaphore = new Semaphore(0); + + boolean[] wasInterrupted = new boolean[1]; + Thread newThread = new Thread(() -> { + try (IReader reader = testIndex.getNd().acquireReadLock()) { + Thread.currentThread().interrupt(); + testIndex.findType("Ljava/util/List;".toCharArray()); + } catch (OperationCanceledException e) { + wasInterrupted[0] = true; + } finally { + semaphore.release(); + } + }); + + newThread.start(); + + semaphore.acquire(); + + assertTrue(wasInterrupted[0]); + try (IReader reader = testIndex.getNd().acquireReadLock()) { + NdTypeId type = testIndex.findType("Ljava/util/List;".toCharArray()); + assertNotNull(type); + } + } + + public void testSubclassesOfGenericTypeCanBeFound() throws Exception { + createJavaProject(PROJECT_NAME, new String[] {"src"}, new String[] {"JCL18_FULL"}, "bin", "1.8", true); + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + Indexer indexer = new Indexer(index.getNd(), root); + + indexer.rescan(SubMonitor.convert(null)); + + try (IReader reader = IndexerTest.index.getNd().acquireReadLock()) { + NdTypeId javaUtilList = IndexerTest.index.findType("Ljava/util/List;".toCharArray()); + NdTypeId javaUtilArrayList = IndexerTest.index.findType("Ljava/util/ArrayList;".toCharArray()); + + boolean found = false; + List<NdType> subtypes = javaUtilList.getSubTypes(); + for (NdType next : subtypes) { + if (Objects.equals(next.getTypeId(), javaUtilArrayList)) { + found = true; + } + } + + assertTrue("ArrayList was found as a subtype of List", found); + } + } + + private void collectAllClassFiles(List<? super IClassFile> result, IParent nextRoot) throws CoreException { + for (IJavaElement child : nextRoot.getChildren()) { + int type = child.getElementType(); + + if (type == IJavaElement.CLASS_FILE) { + result.add((IClassFile)child); + } else if (child instanceof IParent) { + IParent parent = (IParent) child; + + collectAllClassFiles(result, parent); + } + } + } + + public void testReadingAllClassesInIndexAreEquivalentToOriginalJarFiles() throws Exception { + IJavaProject javaProject = createJavaProject(PROJECT_NAME, new String[] {"src"}, new String[] {"JCL18_FULL"}, "bin", "1.8", true); + addClassFolder(javaProject, "lib", new String[] { + "p/Outer.java", + "import java.lang.annotation.*;\n" + + "\n" + + "@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE_USE) @interface A {}\n" + + "@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface M {}\n" + + "@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) @interface P {}\n" + + "\n" + + "class Outer {\n" + + " class Middle1 {\n" + + " class Inner {}\n" + + " }\n" + + " static class Middle2 {\n" + + " class Inner {}\n" + + " static class Middle3 {\n" + + " class Inner2{};\n" + + " }\n" + + " }\n" + + " Middle1.@A Inner e1;\n" + + " Middle2.@A Inner e2;\n" + + " Middle2.Middle3.@A Inner2 e3;\n" + + " @M void foo(@P Middle2.Middle3.@A Inner2 e3) {};\n" + + " class Middle4 extends @A Middle1 {}\n" + + "}\n", + }, "1.8"); + + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + Indexer indexer = new Indexer(index.getNd(), root); + + indexer.rescan(SubMonitor.convert(null)); + + boolean foundAtLeastOneClass = false; + SubMonitor subMonitor = SubMonitor.convert(null); + JavaIndex localIndex = IndexerTest.index; + try (IReader reader = localIndex.getNd().acquireReadLock()) { + IPackageFragmentRoot[] roots = javaProject.getAllPackageFragmentRoots(); + subMonitor.setWorkRemaining(roots.length); + for (IPackageFragmentRoot next : roots) { + SubMonitor iterationMon = subMonitor.split(1); + if (next.getKind() == IPackageFragmentRoot.K_BINARY) { + List<IClassFile> result = new ArrayList<>(); + collectAllClassFiles(result, next); + iterationMon.setWorkRemaining(result.size()); + for (IClassFile nextClass : result) { + SubMonitor classMon = iterationMon.split(1); + BinaryTypeDescriptor descriptor = BinaryTypeFactory.createDescriptor(nextClass); + IndexBinaryType indexedBinaryType = (IndexBinaryType)BinaryTypeFactory.readFromIndex(localIndex, descriptor, classMon); + ClassFileReader originalBinaryType = BinaryTypeFactory.rawReadType(descriptor, true); + + if (!indexedBinaryType.exists()) { + throw new IllegalStateException("Unable to find class in index " + new String(descriptor.indexPath)); + } + IndexTester.testType(originalBinaryType, indexedBinaryType); + foundAtLeastOneClass = true; + } + } + } + } + assertTrue("No classes found in the index", foundAtLeastOneClass); + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/indexer/JavaIndexTestUtil.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/indexer/JavaIndexTestUtil.java new file mode 100644 index 000000000..84a1cd7c0 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/indexer/JavaIndexTestUtil.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd.indexer; + +import java.io.File; + +import org.eclipse.jdt.core.tests.nd.DatabaseTestUtil; +import org.eclipse.jdt.internal.core.nd.db.ChunkCache; +import org.eclipse.jdt.internal.core.nd.java.JavaIndex; + +public class JavaIndexTestUtil { + public static JavaIndex createTempIndex(String id) { + File dbName = DatabaseTestUtil.getTempDbName(id); + return JavaIndex.getIndex(JavaIndex.createNd(dbName, new ChunkCache())); + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/util/BaseTestCase.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/util/BaseTestCase.java new file mode 100644 index 000000000..cb99bf2a8 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/util/BaseTestCase.java @@ -0,0 +1,269 @@ +/******************************************************************************* + * Copyright (c) 2006, 2016 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Markus Schorn - initial API and implementation + * Andrew Ferguson (Symbian) + * Sergey Prigogin (Google) + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd.util; + +import junit.framework.AssertionFailedError; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestFailure; +import junit.framework.TestResult; +import junit.framework.TestSuite; + +import org.eclipse.core.resources.IResourceStatus; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.ILogListener; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.NullProgressMonitor; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Deque; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class BaseTestCase extends TestCase { + private static final String DEFAULT_INDEXER_TIMEOUT_SEC = "10"; + private static final String INDEXER_TIMEOUT_PROPERTY = "indexer.timeout"; + /** + * Indexer timeout used by tests. To avoid this timeout expiring during debugging add + * -Dindexer.timeout=some_large_number to VM arguments of the test launch configuration. + */ + protected static final int INDEXER_TIMEOUT_SEC = + Integer.parseInt(System.getProperty(INDEXER_TIMEOUT_PROPERTY, DEFAULT_INDEXER_TIMEOUT_SEC)); + protected static final int INDEXER_TIMEOUT_MILLISEC= INDEXER_TIMEOUT_SEC * 1000; + + private boolean fExpectFailure; + private int fBugNumber; + private int fExpectedLoggedNonOK; + private Deque<File> filesToDeleteOnTearDown= new ArrayDeque<>(); + + public BaseTestCase() { + super(); + } + + public BaseTestCase(String name) { + super(name); + } + + public static NullProgressMonitor npm() { + return new NullProgressMonitor(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + //CModelListener.sSuppressUpdateOfLastRecentlyUsed= true; + } + + @Override + protected void tearDown() throws Exception { + for (File file; (file = this.filesToDeleteOnTearDown.pollLast()) != null;) { + file.delete(); + } + ResourceHelper.cleanUp(); + //TestScannerProvider.clear(); + super.tearDown(); + } + + protected void deleteOnTearDown(File file) { + this.filesToDeleteOnTearDown.add(file); + } + + protected File createTempFile(String prefix, String suffix) throws IOException { + File file = File.createTempFile(prefix, suffix); + this.filesToDeleteOnTearDown.add(file); + return file; + } + + protected File nonExistentTempFile(String prefix, String suffix) { + File file= new File(System.getProperty("java.io.tmpdir"), + prefix + System.currentTimeMillis() + suffix); + this.filesToDeleteOnTearDown.add(file); + return file; + } + + public static TestSuite suite(Class<? extends BaseTestCase> clazz) { + return suite(clazz, null); + } + + protected static TestSuite suite(Class<? extends BaseTestCase> clazz, String failingTestPrefix) { + TestSuite suite= new TestSuite(clazz); + Test failing= getFailingTests(clazz, failingTestPrefix); + if (failing != null) { + suite.addTest(failing); + } + return suite; + } + + private static Test getFailingTests(Class<? extends BaseTestCase> clazz, String prefix) { + TestSuite suite= new TestSuite("Failing Tests"); + HashSet<String> names= new HashSet<>(); + Class<?> superClass= clazz; + while (Test.class.isAssignableFrom(superClass) && !TestCase.class.equals(superClass)) { + Method[] methods= superClass.getDeclaredMethods(); + for (Method method : methods) { + addFailingMethod(suite, method, names, clazz, prefix); + } + superClass= superClass.getSuperclass(); + } + if (suite.countTestCases() == 0) { + return null; + } + return suite; + } + + private static void addFailingMethod(TestSuite suite, Method m, Set<String> names, + Class<? extends BaseTestCase> clazz, String prefix) { + String name = m.getName(); + if (!names.add(name)) { + return; + } + if (name.startsWith("test") || (prefix != null && !name.startsWith(prefix))) { + return; + } + if (name.equals("tearDown") || name.equals("setUp") || name.equals("runBare")) { + return; + } + if (Modifier.isPublic(m.getModifiers())) { + Class<?>[] parameters = m.getParameterTypes(); + Class<?> returnType = m.getReturnType(); + if (parameters.length == 0 && returnType.equals(Void.TYPE)) { + Test test = TestSuite.createTest(clazz, name); + ((BaseTestCase) test).setExpectFailure(0); + suite.addTest(test); + } + } + } + + @Override + public void runBare() throws Throwable { + final List<IStatus> statusLog= Collections.synchronizedList(new ArrayList<>()); + ILogListener logListener= new ILogListener() { + @Override + public void logging(IStatus status, String plugin) { + if (!status.isOK() && status.getSeverity() != IStatus.INFO) { + switch (status.getCode()) { + case IResourceStatus.NOT_FOUND_LOCAL: + case IResourceStatus.NO_LOCATION_LOCAL: + case IResourceStatus.FAILED_READ_LOCAL: + case IResourceStatus.RESOURCE_NOT_LOCAL: + // Logged by the resources plugin. + return; + } + statusLog.add(status); + } + } + }; + final ILog log = Package.getLog(); + if (log != null) { // Iff we don't run as a JUnit Plugin Test. + log.addLogListener(logListener); + } + + Throwable testThrowable= null; + try { + try { + super.runBare(); + } catch (Throwable e) { + testThrowable= e; + } + + if (statusLog.size() != this.fExpectedLoggedNonOK) { + StringBuilder msg= new StringBuilder("Expected number (" + this.fExpectedLoggedNonOK + ") of "); + msg.append("Non-OK status objects in log differs from actual (" + statusLog.size() + ").\n"); + Throwable cause= null; + if (!statusLog.isEmpty()) { + synchronized (statusLog) { + for (IStatus status : statusLog) { + IStatus[] ss= {status}; + ss= status instanceof MultiStatus ? ((MultiStatus) status).getChildren() : ss; + for (IStatus s : ss) { + msg.append("\t" + s.getMessage() + " "); + + Throwable t= s.getException(); + cause= cause != null ? cause : t; + if (t != null) { + msg.append(t.getMessage() != null ? t.getMessage() : t.getClass().getCanonicalName()); + } + + msg.append("\n"); + } + } + } + } + cause= cause != null ? cause : testThrowable; + AssertionFailedError afe= new AssertionFailedError(msg.toString()); + afe.initCause(cause); + throw afe; + } + } finally { + if (log != null) { + log.removeLogListener(logListener); + } + } + + if (testThrowable != null) + throw testThrowable; + } + + @Override + public void run(TestResult result) { + if (!this.fExpectFailure || Boolean.parseBoolean(System.getProperty("SHOW_EXPECTED_FAILURES"))) { + super.run(result); + return; + } + + result.startTest(this); + + TestResult r = new TestResult(); + super.run(r); + if (r.failureCount() == 1) { + TestFailure failure= r.failures().nextElement(); + String msg= failure.exceptionMessage(); + if (msg != null && msg.startsWith("Method \"" + getName() + "\"")) { + result.addFailure(this, new AssertionFailedError(msg)); + } + } else if (r.errorCount() == 0 && r.failureCount() == 0) { + String err = "Unexpected success of " + getName(); + if (this.fBugNumber > 0) { + err += ", bug #" + this.fBugNumber; + } + result.addFailure(this, new AssertionFailedError(err)); + } + + result.endTest(this); + } + + public void setExpectFailure(int bugNumber) { + this.fExpectFailure= true; + this.fBugNumber= bugNumber; + } + + /** + * The last value passed to this method in the body of a testXXX method + * will be used to determine whether or not the presence of non-OK status objects + * in the log should fail the test. If the logged number of non-OK status objects + * differs from the last value passed, the test is failed. If this method is not called + * at all, the expected number defaults to zero. + * @param count the expected number of logged error and warning messages + */ + public void setExpectedNumberOfLoggedNonOKStatusObjects(int count) { + this.fExpectedLoggedNonOK= count; + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/util/Package.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/util/Package.java new file mode 100644 index 000000000..4e2dbbdd3 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/util/Package.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd.util; + +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.JavaCore; + +/** + * @noreference This class is not intended to be referenced by clients + */ +/* package */ class Package { + public static String PLUGIN_ID = JavaCore.PLUGIN_ID; + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public static void log(Throwable e) { + String msg= e.getMessage(); + if (msg == null) { + log("Error", e); //$NON-NLS-1$ + } else { + log("Error: " + msg, e); //$NON-NLS-1$ + } + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public static void log(String message, Throwable e) { + log(createStatus(message, e)); + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public static IStatus createStatus(String msg, Throwable e) { + return new Status(IStatus.ERROR, PLUGIN_ID, msg, e); + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public static IStatus createStatus(String msg) { + return new Status(IStatus.ERROR, PLUGIN_ID, msg); + } + + /** + * @noreference This method is not intended to be referenced by clients. + * + * Returns the appropriate ILog for this package + */ + public static ILog getLog() { + Plugin plugin = JavaCore.getPlugin(); + if (plugin == null) { + return null; + } + return plugin.getLog(); + } + + /** + * @noreference This method is not intended to be referenced by clients. + */ + public static void log(IStatus status) { + getLog().log(status); + } +} diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/util/ResourceHelper.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/util/ResourceHelper.java new file mode 100644 index 000000000..5b2cd89eb --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/nd/util/ResourceHelper.java @@ -0,0 +1,576 @@ +/******************************************************************************* + * Copyright (c) 2009, 2016 Andrew Gvozdev and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Gvozdev - Initial API and implementation + * James Blackburn (Broadcom Corp.) + * Liviu Ionescu - bug 392416 + *******************************************************************************/ +package org.eclipse.jdt.core.tests.nd.util; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.junit.Assert; + +/** + * This class contains utility methods for creating resources + * such as projects, files, folders etc. which are being used + * in test fixture of unit tests. + * + * Some classes with similar idea worth to look at: + * org.eclipse.core.filebuffers.tests.ResourceHelper, + * org.eclipse.cdt.ui.tests.text.ResourceHelper. + */ +public class ResourceHelper { + private final static IProgressMonitor NULL_MONITOR = new NullProgressMonitor(); + private static final int MAX_RETRY= 5; + + private final static Set<String> externalFilesCreated = new HashSet<String>(); + private final static Set<IResource> resourcesCreated = new HashSet<IResource>(); + + /** + * Creates a plain Eclipse project. + * + * @param projectName + * @return the project handle + * @throws CoreException if project could not be created + */ + public static IProject createProject(String projectName) throws CoreException { + IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot(); + IProject project= root.getProject(projectName); + if (!project.exists()) { + project.create(NULL_MONITOR); + } else { + project.refreshLocal(IResource.DEPTH_INFINITE, null); + } + + if (!project.isOpen()) + project.open(NULL_MONITOR); + + resourcesCreated.add(project); + return project; + } + + /** + * Deletes project by name. + * + * @param projectName + * @throws CoreException + */ + public static void deleteProject(String projectName) throws CoreException { + IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot(); + IProject project= root.getProject(projectName); + if (project.exists()) + delete(project); + } + + /** + * Deletes given project with content. + * + * @param project + * @throws CoreException + */ + public static void delete(final IProject project) throws CoreException { + delete(project, true); + } + + /** + * Deletes project. + * + * @param project + * @param deleteContent whether to delete project content + * @throws CoreException + */ + public static void delete(final IProject project, boolean deleteContent) throws CoreException { + for (int i= 0; i < MAX_RETRY; i++) { + try { + project.delete(deleteContent, true, NULL_MONITOR); + i= MAX_RETRY; + } catch (CoreException x) { + if (i == MAX_RETRY - 1) { + Package.log(x.getStatus()); + } + try { + Thread.sleep(1000); // sleep a second + } catch (InterruptedException e) { + } + } + } + } + + /** + * Creates a file with specified content. + * + * @param file - file name. + * @param contents - contents of the file. + * @return file handle. + * @throws CoreException - if the file can't be created. + */ + public static IFile createFile(IFile file, String contents) throws CoreException { + if (contents == null) { + contents= ""; + } + + InputStream inputStream = new ByteArrayInputStream(contents.getBytes()); + file.create(inputStream, true, NULL_MONITOR); + resourcesCreated.add(file); + return file; + } + + /** + * Creates new file from project root with empty content. The filename + * can include relative path as a part of the name but the the path + * has to be present on disk. + * + * @param project - project where to create the file. + * @param name - filename. + * @return file handle. + * @throws CoreException if something goes wrong. + */ + public static IFile createFile(IProject project, String name) throws CoreException { + if (new Path(name).segmentCount() > 1) + createFolder(project, new Path(name).removeLastSegments(1).toString()); + return createFile(project.getFile(name), null); + } + + /** + * Creates new file from workspace root with empty content. The filename + * can include relative path as a part of the name but the the path + * has to be present on disk. + * The intention of the method is to create files which do not belong to any project. + * + * @param name - filename. + * @return full path of the created file. + * + * @throws CoreException... + * @throws IOException... + */ + public static IPath createWorkspaceFile(String name) throws CoreException, IOException { + IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); + IPath fullPath = workspaceRoot.getLocation().append(name); + java.io.File file = new java.io.File(fullPath.toOSString()); + if (!file.exists()) { + boolean result = file.createNewFile(); + Assert.assertTrue(result); + } + Assert.assertTrue(file.exists()); + + externalFilesCreated.add(fullPath.toOSString()); + workspaceRoot.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); + return fullPath; + } + + /** + * Creates new folder from project root. The folder name + * can include relative path as a part of the name. + * Nonexistent parent directories are being created. + * + * @param project - project where to create the folder. + * @param name - folder name. + * @return folder handle. + * @throws CoreException if something goes wrong. + */ + public static IFolder createFolder(IProject project, String name) throws CoreException { + final IPath p = new Path(name); + IContainer folder = project; + for (String seg : p.segments()) { + folder = folder.getFolder(new Path(seg)); + if (!folder.exists()) + ((IFolder)folder).create(true, true, NULL_MONITOR); + } + resourcesCreated.add(folder); + return (IFolder)folder; + } + + /** + * Creates new folder from workspace root. The folder name + * can include relative path as a part of the name. + * Nonexistent parent directories are being created as per {@link File#mkdirs()}. + * The intention of the method is to create folders which do not belong to any project. + * + * @param name - folder name. + * @return absolute location of the folder on the file system. + * @throws IOException if something goes wrong. + */ + public static IPath createWorkspaceFolder(String name) throws CoreException, IOException { + IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); + IPath fullPath = workspaceRoot.getLocation().append(name); + java.io.File folder = new java.io.File(fullPath.toOSString()); + if (!folder.exists()) { + boolean result = folder.mkdirs(); + Assert.assertTrue(result); + } + Assert.assertTrue(folder.exists()); + + externalFilesCreated.add(fullPath.toOSString()); + workspaceRoot.refreshLocal(IResource.DEPTH_INFINITE, NULL_MONITOR); + return fullPath; + } + + /** + * Creates new temporary folder with generated name from workspace root. + * + * @return absolute location of the folder on the file system. + * @throws IOException if something goes wrong. + */ + public static IPath createTemporaryFolder() throws CoreException, IOException { + return ResourceHelper.createWorkspaceFolder("tmp/" + System.currentTimeMillis() + '.' + UUID.randomUUID()); + } + + /** + * Creates new eclipse file-link from project root to file system file. The filename + * can include relative path as a part of the name but the the path + * has to be present on disk. + * + * @param project - project where to create the file. + * @param fileLink - filename of the link being created. + * @param realFile - file on the file system, the target of the link. + * @return file handle. + * @throws CoreException if something goes wrong. + */ + public static IFile createLinkedFile(IProject project, String fileLink, IPath realFile) throws CoreException { + IFile file = project.getFile(fileLink); + file.createLink(realFile, IResource.REPLACE, null); + Assert.assertTrue(file.exists()); + resourcesCreated.add(file); + return file; + } + + /** + * Creates new eclipse file-link from project root to file system file. The filename + * can include relative path as a part of the name but the the path + * has to be present on disk. + * + * @param project - project where to create the file. + * @param fileLink - filename of the link being created. + * @param realFile - file on the file system, the target of the link. + * @return file handle. + * @throws CoreException if something goes wrong. + */ + public static IFile createLinkedFile(IProject project, String fileLink, String realFile) throws CoreException { + return createLinkedFile(project, fileLink, new Path(realFile)); + } + + /** + * Creates new eclipse file-link from project root to EFS file. + * + * @param project - project where to create the file. + * @param fileLink - filename of the link being created. + * @param realFile - file on the EFS file system, the target of the link. + * @return file handle. + * @throws CoreException if something goes wrong. + */ + public static IFile createEfsFile(IProject project, String fileLink, URI realFile) throws CoreException { + IFile file= project.getFile(fileLink); + file.createLink(realFile, IResource.ALLOW_MISSING_LOCAL, NULL_MONITOR); + resourcesCreated.add(file); + return file; + } + + /** + * Creates new eclipse file-link from project root to EFS file. + * + * @param project - project where to create the file. + * @param fileLink - filename of the link being created. + * @param realFile - file on the EFS file system, the target of the link. + * @return file handle. + * @throws CoreException if something goes wrong. + * @throws URISyntaxException if wrong URI syntax + */ + public static IFile createEfsFile(IProject project, String fileLink, String realFile) throws CoreException, URISyntaxException { + return createEfsFile(project,fileLink,new URI(realFile)); + } + + /** + * Creates new eclipse folder-link from project root to file system folder. The folder name + * can include relative path as a part of the name but the the path + * has to be present on disk. + * + * @param project - project where to create the file. + * @param folderLink - name of the link being created. + * @param realFolder - folder on the file system, the target of the link. + * @return file handle. + * @throws CoreException if something goes wrong. + */ + public static IFolder createLinkedFolder(IProject project, String folderLink, IPath realFolder) throws CoreException { + IFolder folder = project.getFolder(folderLink); + folder.createLink(realFolder, IResource.REPLACE | IResource.ALLOW_MISSING_LOCAL, null); + Assert.assertTrue(folder.exists()); + resourcesCreated.add(folder); + return folder; + } + + /** + * Creates new eclipse folder-link from project root to file system folder. The folder name + * can include relative path as a part of the name but the the path + * has to be present on disk. + * + * @param project - project where to create the file. + * @param folderLink - name of the link being created. + * @param realFolder - folder on the file system, the target of the link. + * @return file handle. + * @throws CoreException if something goes wrong. + */ + public static IFolder createLinkedFolder(IProject project, String folderLink, String realFolder) throws CoreException { + return createLinkedFolder(project, folderLink, new Path(realFolder)); + } + + /** + * Creates new eclipse folder-link from project root to EFS folder. + * + * @param project - project where to create the folder. + * @param folderLink - folder name of the link being created. + * @param realFolder - folder on the EFS file system, the target of the link. + * @return folder handle. + * @throws CoreException if something goes wrong. + */ + public static IFolder createEfsFolder(IProject project, String folderLink, URI realFolder) throws CoreException { + IFolder folder= project.getFolder(folderLink); + if (folder.exists()) { + Assert.assertEquals("Folder with the same name but different location already exists", + realFolder, folder.getLocationURI()); + return folder; + } + + folder.createLink(realFolder, IResource.ALLOW_MISSING_LOCAL, new NullProgressMonitor()); + resourcesCreated.add(folder); + return folder; + } + + /** + * Creates new eclipse folder-link from project root to EFS folder. + * + * @param project - project where to create the folder. + * @param folderLink - folder name of the link being created. + * @param realFolder - folder on the EFS file system, the target of the link. + * @return folder handle. + * @throws CoreException if something goes wrong. + * @throws URISyntaxException if wrong URI syntax + */ + public static IFolder createEfsFolder(IProject project, String folderLink, String realFolder) throws CoreException, URISyntaxException { + return createEfsFolder(project,folderLink,new URI(realFolder)); + } + + /** + * Checks if symbolic links are supported on the system. + * Used in particular by method {@link #createSymbolicLink(IPath, IPath)} + * and other flavors to create symbolic links. + * + * Note that Windows links .lnk are not supported here. + * @return {@code true} if symbolic links are suppoted, {@code false} otherwise. + */ + public static boolean isSymbolicLinkSupported() { + return ! Platform.getOS().equals(Platform.OS_WIN32); + } + + /** + * Creates new symbolic file system link from file or folder on project root + * to another file system file. The filename can include relative path + * as a part of the name but the the path has to be present on disk. + * + * @param project - project where to create the file. + * @param linkName - name of the link being created. + * @param realPath - file or folder on the file system, the target of the link. + * @return file handle. + * + * @throws UnsupportedOperationException on Windows where links are not supported. + * @throws IOException... + * @throws CoreException... + */ + public static IResource createSymbolicLink(IProject project, String linkName, IPath realPath) + throws IOException, CoreException, UnsupportedOperationException { + if (!isSymbolicLinkSupported()) { + throw new UnsupportedOperationException("Windows links .lnk are not supported."); + } + + Assert.assertTrue("Path for symbolic link does not exist: [" + realPath.toOSString() + "]", + new File(realPath.toOSString()).exists()); + + IPath linkedPath = project.getLocation().append(linkName); + createSymbolicLink(linkedPath, realPath); + + IResource resource = project.getFile(linkName); + resource.refreshLocal(IResource.DEPTH_ZERO, null); + + if (!resource.exists()) { + resource = project.getFolder(linkName); + resource.refreshLocal(IResource.DEPTH_ZERO, null); + } + Assert.assertTrue("Failed to create resource form symbolic link", resource.exists()); + + externalFilesCreated.add(linkedPath.toOSString()); + ResourcesPlugin.getWorkspace().getRoot().refreshLocal(IResource.DEPTH_INFINITE, NULL_MONITOR); + return resource; + } + + /** + * Creates new symbolic file system link from file or folder to another filesystem file. + * The target path has to be present on disk. + * + * @param linkPath - filesystem path of the link being created. + * @param realPath - file or folder on the file system, the target of the link. + * + * @throws UnsupportedOperationException on Windows where links are not supported. + * @throws IOException if execution of the command fails. + */ + public static void createSymbolicLink(IPath linkPath, IPath realPath) throws IOException { + if (!isSymbolicLinkSupported()) { + throw new UnsupportedOperationException("Windows links .lnk are not supported."); + } + + String command[] = { "ln", "-s", realPath.toOSString(), linkPath.toOSString()}; + Process process = Runtime.getRuntime().exec(command); + + // Wait for up to 2.5s... + for (int i = 0; i < 5; i++) { + try { + Assert.assertTrue("ln process exited with non-zero status", process.waitFor() == 0); + // If exitValue succeeded, then the process has exited successfully. + break; + } catch (InterruptedException e) { + // Clear interrupted state, see Java bug http://bugs.sun.com/view_bug.do?bug_id=6420270 + Thread.interrupted(); + } + // Wait for a 500ms before checking again. + try { Thread.sleep(500); } catch (InterruptedException e) {/*don't care*/} + } + Assert.assertTrue("Symbolic link not created, command=[" + command + "]", linkPath.toFile().exists()); + } + + /** + * Creates new symbolic file system link from file or folder on project root + * to another file system file. The filename can include relative path + * as a part of the name but the the path has to be present on disk. + * + * @param project - project where to create the file. + * @param linkName - name of the link being created. + * @param realPath - file or folder on the file system, the target of the link. + * @return file handle. + * + * @throws UnsupportedOperationException on Windows where links are not supported. + * @throws IOException... + * @throws CoreException... + */ + public static IResource createSymbolicLink(IProject project, String linkName, String realPath) + throws IOException, CoreException, UnsupportedOperationException { + return createSymbolicLink(project, linkName, new Path(realPath)); + } + + /** + * Get contents of file on file-system. + * + * @param fullPath - full path to the file on the file-system. + * @return contents of the file. + * @throws IOException on IO problem. + */ + public static String getContents(IPath fullPath) throws IOException { + FileInputStream stream = new FileInputStream(fullPath.toFile()); + try { + // Avoid using java.nio.channels.FileChannel, + // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4715154 + Reader reader = new BufferedReader(new InputStreamReader(stream, Charset.defaultCharset())); + StringBuilder builder = new StringBuilder(); + char[] buffer = new char[8192]; + int read; + while ((read = reader.read(buffer, 0, buffer.length)) > 0) { + builder.append(buffer, 0, read); + } + return builder.toString(); + } finally { + stream.close(); + } + } + + /** + * Get contents of file on file-system. + * + * @param fullPath - full path to the file on the file-system. + * @return contents of the file. + * @throws IOException on IO problem. + */ + public static String getContents(String fullPath) throws IOException { + return getContents(new Path(fullPath)); + } + + /** + * Clean-up any files created as part of a unit test. + * This method removes *all* Workspace IResources and any external + * files / folders created with the #createWorkspaceFile #createWorkspaceFolder + * methods in this class + */ + public static void cleanUp() throws CoreException, IOException { + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + root.refreshLocal(IResource.DEPTH_INFINITE, NULL_MONITOR); + + // Delete all external files & folders created using ResourceHelper + for (String loc : externalFilesCreated) { + File f = new File(loc); + if (f.exists()) + deleteRecursive(f); + } + externalFilesCreated.clear(); + + // Remove IResources created by this helper + for (IResource r : resourcesCreated) { + if (r.exists()) { + try { + r.delete(true, NULL_MONITOR); + } catch (CoreException e) { + // Ignore + } + } + } + resourcesCreated.clear(); + } + + /** + * Recursively delete a directory / file + * + * For safety this method only deletes files created under the workspace + */ + private static final void deleteRecursive(File f) throws IllegalArgumentException { + // Ensure that the file being deleted is a child of the workspace + // root to prevent anything nasty happening + if (!f.getAbsolutePath().startsWith( + ResourcesPlugin.getWorkspace().getRoot().getLocation().toFile().getAbsolutePath())) { + throw new IllegalArgumentException("File must exist within the workspace!"); + } + + if (f.isDirectory()) { + for (File f1 : f.listFiles()) { + deleteRecursive(f1); + } + } + f.delete(); + } +} diff --git a/org.eclipse.jdt.core/.classpath b/org.eclipse.jdt.core/.classpath index 6add95f90..cfba090bb 100644 --- a/org.eclipse.jdt.core/.classpath +++ b/org.eclipse.jdt.core/.classpath @@ -9,7 +9,7 @@ <classpathentry kind="src" path="formatter"/> <classpathentry kind="src" path="model"/> <classpathentry kind="src" path="search"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/org.eclipse.jdt.core/.options b/org.eclipse.jdt.core/.options index 83d4c300a..c50fb79ce 100644 --- a/org.eclipse.jdt.core/.options +++ b/org.eclipse.jdt.core/.options @@ -47,6 +47,33 @@ org.eclipse.jdt.core/debug/javamodel=false # Reports Java model elements opening/closing org.eclipse.jdt.core/debug/javamodel/cache=false +# Reports changes in the Java classpath and classpath resolution +org.eclipse.jdt.core/debug/javamodel/classpath=false + +# Reports all insertions and removals from the java model cache +org.eclipse.jdt.core/debug/javamodel/insertions=false + +# Records information about the invalid archive cache +org.eclipse.jdt.core/debug/javamodel/invalid_archives=false + +# Prints information about when the indexer runs and what files are being indexed +org.eclipse.jdt.core/debug/index/indexer=false + +# Prints a line whenever a class is added to or removed from the index +org.eclipse.jdt.core/debug/index/insertions=false + +# Prints diagnostic information about index database locks +org.eclipse.jdt.core/debug/index/locks=false + +# Prints statistics about database memory usage +org.eclipse.jdt.core/debug/index/space=false + +# Performs self-testing during indexing by reading back every class and comparing it with the original .class file +org.eclipse.jdt.core/debug/index/selftest=false + +# Prints statistics about indexing time +org.eclipse.jdt.core/debug/index/timing=false + # Reports post actions addition/run org.eclipse.jdt.core/debug/postaction=false diff --git a/org.eclipse.jdt.core/.settings/org.eclipse.jdt.core.prefs b/org.eclipse.jdt.core/.settings/org.eclipse.jdt.core.prefs index c02097a3d..d2ef492bb 100644 --- a/org.eclipse.jdt.core/.settings/org.eclipse.jdt.core.prefs +++ b/org.eclipse.jdt.core/.settings/org.eclipse.jdt.core.prefs @@ -25,9 +25,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -131,7 +131,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.compiler.source=1.8 org.eclipse.jdt.core.formatter.align_type_members_on_columns=false org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 diff --git a/org.eclipse.jdt.core/META-INF/MANIFEST.MF b/org.eclipse.jdt.core/META-INF/MANIFEST.MF index 4bb8b6f57..0e9d5cfe1 100644 --- a/org.eclipse.jdt.core/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.core/META-INF/MANIFEST.MF @@ -45,6 +45,13 @@ Export-Package: org.eclipse.jdt.core, org.eclipse.jdt.internal.core.hierarchy;x-friends:="org.eclipse.objectteams.otdt", org.eclipse.jdt.internal.core.index;x-internal:=true, org.eclipse.jdt.internal.core.jdom;x-internal:=true, + org.eclipse.jdt.internal.core.nd;x-internal:=true, + org.eclipse.jdt.internal.core.nd.db;x-internal:=true, + org.eclipse.jdt.internal.core.nd.field;x-internal:=true, + org.eclipse.jdt.internal.core.nd.indexer;x-internal:=true, + org.eclipse.jdt.internal.core.nd.java;x-internal:=true, + org.eclipse.jdt.internal.core.nd.java.model;x-internal:=true, + org.eclipse.jdt.internal.core.nd.util;x-internal:=true, org.eclipse.jdt.internal.core.search;x-internal:=true, org.eclipse.jdt.internal.core.search.indexing;x-internal:=true, org.eclipse.jdt.internal.core.search.matching;x-internal:=true, @@ -79,7 +86,8 @@ Require-Bundle: org.eclipse.core.resources;bundle-version="[3.3.0,4.0.0)", org.eclipse.core.filesystem;bundle-version="[1.0.0,2.0.0)", org.eclipse.text;bundle-version="[3.1.0,4.0.0)", org.eclipse.team.core;bundle-version="[3.1.0,4.0.0)";resolution:=optional, + com.ibm.icu;bundle-version="54.1.1", org.eclipse.jdt.annotation;bundle-version="[1.1.0,2.0.0)" -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Eclipse-ExtensibleAPI: true Bundle-ActivationPolicy: lazy diff --git a/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/JDTCompilerAdapter.java b/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/JDTCompilerAdapter.java index 73664aef7..c2bdf7136 100644 --- a/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/JDTCompilerAdapter.java +++ b/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/JDTCompilerAdapter.java @@ -305,6 +305,7 @@ public class JDTCompilerAdapter extends DefaultCompilerAdapter { String source = this.attributes.getSource(); if (source != null) { this.customDefaultOptions.put(CompilerOptions.OPTION_Source, source); + this.customDefaultOptions.put(CompilerOptions.OPTION_Compliance, source); } if (compilerArgs != null) { diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJar.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJar.java index 77ad9037c..624583702 100644 --- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJar.java +++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJar.java @@ -27,9 +27,12 @@ import java.util.zip.ZipFile; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationDecorator; import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider; import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; +import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.ExternalAnnotationStatus; import org.eclipse.jdt.internal.compiler.util.ManifestAnalyzer; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.compiler.util.Util; @@ -102,15 +105,20 @@ public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageN return null; // most common case try { - ClassFileReader reader = ClassFileReader.read(this.zipFile, qualifiedBinaryFileName); + IBinaryType reader = ClassFileReader.read(this.zipFile, qualifiedBinaryFileName); if (reader != null) { if (this.annotationPaths != null) { String qualifiedClassName = qualifiedBinaryFileName.substring(0, qualifiedBinaryFileName.length()-SuffixConstants.EXTENSION_CLASS.length()-1); for (String annotationPath : this.annotationPaths) { try { - this.annotationZipFile = reader.setExternalAnnotationProvider(annotationPath, qualifiedClassName, this.annotationZipFile, null); - if (reader.hasAnnotationProvider()) + if (this.annotationZipFile == null) { + this.annotationZipFile = ExternalAnnotationDecorator.getAnnotationZipFile(annotationPath, null); + } + reader = ExternalAnnotationDecorator.create(reader, annotationPath, qualifiedClassName, this.annotationZipFile); + + if (reader.getExternalAnnotationStatus() == ExternalAnnotationStatus.TYPE_IS_ANNOTATED) { break; + } } catch (IOException e) { // don't let error on annotations fail class reading } diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/FileSystem.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/FileSystem.java index b6d9e5f01..7c7dbc3f8 100644 --- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/FileSystem.java +++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/FileSystem.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -24,6 +24,7 @@ import java.util.zip.ZipFile; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationDecorator; import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; @@ -275,9 +276,14 @@ private NameEnvironmentAnswer findClass(String qualifiedTypeName, char[] typeNam for (int i = 0, length = this.classpaths.length; i < length; i++) { Classpath classpathEntry = this.classpaths[i]; if (classpathEntry.hasAnnotationFileFor(qualifiedTypeName)) { + // in case of 'this.annotationsFromClasspath' we indeed search for .eea entries inside the main zipFile of the entry: ZipFile zip = classpathEntry instanceof ClasspathJar ? ((ClasspathJar) classpathEntry).zipFile : null; try { - ((ClassFileReader) answer.getBinaryType()).setExternalAnnotationProvider(classpathEntry.getPath(), qualifiedTypeName, zip, null); + if (zip == null) { + zip = ExternalAnnotationDecorator.getAnnotationZipFile(classpathEntry.getPath(), null); + } + answer.setBinaryType(ExternalAnnotationDecorator.create(answer.getBinaryType(), classpathEntry.getPath(), + qualifiedTypeName, zip)); break; } catch (IOException e) { // ignore broken entry, keep searching diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java index 59b6c3acf..5967f2252 100644 --- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java +++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionEngine.java @@ -18,6 +18,7 @@ package org.eclipse.jdt.internal.codeassist; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.Locale; import java.util.Map; @@ -43,41 +44,171 @@ import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.search.IJavaSearchConstants; -import org.eclipse.jdt.internal.codeassist.complete.*; +import org.eclipse.jdt.internal.codeassist.complete.CompletionNodeDetector; +import org.eclipse.jdt.internal.codeassist.complete.CompletionNodeFound; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnAnnotationOfType; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnArgumentName; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnBranchStatementLabel; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnClassLiteralAccess; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnExplicitConstructorCall; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnFieldName; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnFieldType; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnImportReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadoc; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocAllocationExpression; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocFieldReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocMessageSend; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocParamNameReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocQualifiedTypeReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocSingleTypeReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocTag; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocTypeParamReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnKeyword; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnKeyword3; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnLocalName; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMarkerAnnotationName; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMemberAccess; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMemberValueName; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMessageSend; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMessageSendName; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMethodName; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMethodReturnType; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnPackageReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnParameterizedQualifiedTypeReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedAllocationExpression; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedNameReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedTypeReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnReferenceExpressionName; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleNameReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleTypeReference; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnStringLiteral; +import org.eclipse.jdt.internal.codeassist.complete.CompletionParser; +import org.eclipse.jdt.internal.codeassist.complete.CompletionScanner; +import org.eclipse.jdt.internal.codeassist.complete.InvalidCursorLocation; import org.eclipse.jdt.internal.codeassist.impl.AssistParser; import org.eclipse.jdt.internal.codeassist.impl.Engine; import org.eclipse.jdt.internal.codeassist.impl.Keywords; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; import org.eclipse.jdt.internal.compiler.ExtraFlags; -import org.eclipse.jdt.internal.compiler.ast.*; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration; +import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.Argument; +import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; +import org.eclipse.jdt.internal.compiler.ast.ArrayReference; +import org.eclipse.jdt.internal.compiler.ast.AssertStatement; +import org.eclipse.jdt.internal.compiler.ast.Assignment; +import org.eclipse.jdt.internal.compiler.ast.BinaryExpression; +import org.eclipse.jdt.internal.compiler.ast.CaseStatement; +import org.eclipse.jdt.internal.compiler.ast.CastExpression; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression; +import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.ExpressionContext; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.FieldReference; +import org.eclipse.jdt.internal.compiler.ast.ForStatement; +import org.eclipse.jdt.internal.compiler.ast.IfStatement; +import org.eclipse.jdt.internal.compiler.ast.ImportReference; +import org.eclipse.jdt.internal.compiler.ast.Initializer; +import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression; +import org.eclipse.jdt.internal.compiler.ast.Javadoc; +import org.eclipse.jdt.internal.compiler.ast.JavadocImplicitTypeReference; +import org.eclipse.jdt.internal.compiler.ast.JavadocQualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.JavadocSingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; +import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; +import org.eclipse.jdt.internal.compiler.ast.MessageSend; +import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.NameReference; +import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation; +import org.eclipse.jdt.internal.compiler.ast.OperatorExpression; +import org.eclipse.jdt.internal.compiler.ast.OperatorIds; +import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; +import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression; +import org.eclipse.jdt.internal.compiler.ast.ReturnStatement; +import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; +import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.SuperReference; +import org.eclipse.jdt.internal.compiler.ast.SwitchStatement; +import org.eclipse.jdt.internal.compiler.ast.ThisReference; +import org.eclipse.jdt.internal.compiler.ast.TryStatement; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeParameter; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.eclipse.jdt.internal.compiler.ast.UnaryExpression; +import org.eclipse.jdt.internal.compiler.ast.UnionTypeReference; +import org.eclipse.jdt.internal.compiler.ast.WhileStatement; +import org.eclipse.jdt.internal.compiler.ast.Wildcard; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.eclipse.jdt.internal.compiler.env.*; +import org.eclipse.jdt.internal.compiler.env.AccessRestriction; +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; +import org.eclipse.jdt.internal.compiler.env.INameEnvironment; +import org.eclipse.jdt.internal.compiler.env.ISourceType; +import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; -import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; +import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; +import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; +import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; +import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; +import org.eclipse.jdt.internal.compiler.lookup.ImportBinding; +import org.eclipse.jdt.internal.compiler.lookup.InferenceContext18; +import org.eclipse.jdt.internal.compiler.lookup.InvocationSite; +import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.MethodScope; +import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; +import org.eclipse.jdt.internal.compiler.lookup.ParameterizedMethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; +import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.Scope; +import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TagBits; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; +import org.eclipse.jdt.internal.compiler.lookup.TypeIds; +import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding; +import org.eclipse.jdt.internal.compiler.parser.JavadocTagConstants; import org.eclipse.jdt.internal.compiler.parser.ScannerHelper; import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter; -import org.eclipse.jdt.internal.compiler.parser.JavadocTagConstants; import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; -import org.eclipse.jdt.internal.compiler.util.SimpleSetOfCharArray; -import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.compiler.util.HashtableOfObject; import org.eclipse.jdt.internal.compiler.util.ObjectVector; +import org.eclipse.jdt.internal.compiler.util.SimpleSetOfCharArray; +import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.core.BasicCompilationUnit; +import org.eclipse.jdt.internal.core.BinaryTypeConverter; import org.eclipse.jdt.internal.core.INamingRequestor; import org.eclipse.jdt.internal.core.InternalNamingConventions; import org.eclipse.jdt.internal.core.JavaModelManager; +import org.eclipse.jdt.internal.core.SearchableEnvironment; import org.eclipse.jdt.internal.core.SourceMethod; import org.eclipse.jdt.internal.core.SourceMethodElementInfo; import org.eclipse.jdt.internal.core.SourceType; -import org.eclipse.jdt.internal.core.BinaryTypeConverter; -import org.eclipse.jdt.internal.core.SearchableEnvironment; import org.eclipse.jdt.internal.core.SourceTypeElementInfo; -import org.eclipse.jdt.internal.core.search.matching.JavaSearchNameEnvironment; +import org.eclipse.jdt.internal.core.search.matching.IndexBasedJavaSearchEnvironment; import org.eclipse.jdt.internal.core.util.Messages; import org.eclipse.jdt.internal.core.util.Util; import org.eclipse.objectteams.otdt.core.IOTType; @@ -581,7 +712,7 @@ public final class CompletionEngine CompletionRequestor requestor; CompletionProblemFactory problemFactory; ProblemReporter problemReporter; - private JavaSearchNameEnvironment noCacheNameEnvironment; + private INameEnvironment noCacheNameEnvironment; char[] source; char[] completionToken; char[] qualifiedCompletionToken; @@ -12849,7 +12980,7 @@ public final class CompletionEngine private INameEnvironment getNoCacheNameEnvironment() { if (this.noCacheNameEnvironment == null) { JavaModelManager.getJavaModelManager().cacheZipFiles(this); - this.noCacheNameEnvironment = new JavaSearchNameEnvironment(this.javaProject, this.owner == null ? null : JavaModelManager.getJavaModelManager().getWorkingCopies(this.owner, true/*add primary WCs*/)); + this.noCacheNameEnvironment = IndexBasedJavaSearchEnvironment.create(Collections.singletonList(this.javaProject), this.owner == null ? null : JavaModelManager.getJavaModelManager().getWorkingCopies(this.owner, true/*add primary WCs*/)); } return this.noCacheNameEnvironment; } diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/RelevanceConstants.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/RelevanceConstants.java index 53f1215ab..97f8a5e45 100644 --- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/RelevanceConstants.java +++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/RelevanceConstants.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -21,7 +21,7 @@ public interface RelevanceConstants { * 4. The value of R_DEFAULT is maintained at a positive value such that the sum of all the negative relevance constants * and R_DEFAULT must not be negative. */ - int R_DEFAULT = 5; + int R_DEFAULT = 30; int R_INTERESTING = 5; int R_CASE = 10; int R_CAMEL_CASE = 5; @@ -44,7 +44,7 @@ public interface RelevanceConstants { int R_NAME_FIRST_SUFFIX = 4; int R_NAME_SUFFIX = 3; int R_NAME_LESS_NEW_CHARACTERS = 15; - int R_SUBSTRING = -1; + int R_SUBSTRING = -20; int R_METHOD_OVERIDE = 3; int R_NON_RESTRICTED = 3; int R_TRUE_OR_FALSE = 1; diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/SelectionEngine.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/SelectionEngine.java index 94e796de6..83777f192 100644 --- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/SelectionEngine.java +++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/SelectionEngine.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -17,7 +17,6 @@ import java.util.Iterator; import java.util.Locale; import java.util.Map; -import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.IBuffer; @@ -26,26 +25,76 @@ import org.eclipse.jdt.core.IMember; import org.eclipse.jdt.core.IOpenable; import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.WorkingCopyOwner; -import org.eclipse.jdt.core.compiler.*; +import org.eclipse.jdt.core.compiler.CategorizedProblem; +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.compiler.IProblem; +import org.eclipse.jdt.core.compiler.InvalidInputException; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.TypeNameMatch; import org.eclipse.jdt.core.search.TypeNameMatchRequestor; -import org.eclipse.jdt.internal.codeassist.impl.*; -import org.eclipse.jdt.internal.codeassist.select.*; -import org.eclipse.jdt.internal.compiler.*; +import org.eclipse.jdt.internal.codeassist.impl.AssistParser; +import org.eclipse.jdt.internal.codeassist.impl.Engine; +import org.eclipse.jdt.internal.codeassist.select.SelectionJavadocParser; +import org.eclipse.jdt.internal.codeassist.select.SelectionNodeFound; +import org.eclipse.jdt.internal.codeassist.select.SelectionOnImportReference; +import org.eclipse.jdt.internal.codeassist.select.SelectionOnPackageReference; +import org.eclipse.jdt.internal.codeassist.select.SelectionOnQualifiedTypeReference; +import org.eclipse.jdt.internal.codeassist.select.SelectionOnSingleTypeReference; +import org.eclipse.jdt.internal.codeassist.select.SelectionParser; +import org.eclipse.jdt.internal.compiler.ASTVisitor; +import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Expression.DecapsulationState; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ImportReference; +import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; -import org.eclipse.jdt.internal.compiler.env.*; -import org.eclipse.jdt.internal.compiler.ast.*; -import org.eclipse.jdt.internal.compiler.ast.Expression.DecapsulationState; -import org.eclipse.jdt.internal.compiler.lookup.*; -import org.eclipse.jdt.internal.compiler.parser.*; -import org.eclipse.jdt.internal.compiler.problem.*; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.env.AccessRestriction; +import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; +import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; +import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.ClassScope; +import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; +import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; +import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; +import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.MethodScope; +import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; +import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding; +import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons; +import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding; +import org.eclipse.jdt.internal.compiler.parser.Scanner; +import org.eclipse.jdt.internal.compiler.parser.ScannerHelper; +import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter; +import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; +import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; +import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; +import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.compiler.util.HashtableOfObject; import org.eclipse.jdt.internal.compiler.util.ObjectVector; import org.eclipse.jdt.internal.core.BinaryTypeConverter; @@ -55,6 +104,8 @@ import org.eclipse.jdt.internal.core.SearchableEnvironment; import org.eclipse.jdt.internal.core.SelectionRequestor; import org.eclipse.jdt.internal.core.SourceType; import org.eclipse.jdt.internal.core.SourceTypeElementInfo; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeDescriptor; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory; import org.eclipse.jdt.internal.core.search.BasicSearchEngine; import org.eclipse.jdt.internal.core.search.TypeNameMatchRequestorWrapper; import org.eclipse.jdt.internal.core.util.ASTNodeFinder; @@ -1350,7 +1401,7 @@ public final class SelectionEngine extends Engine implements ISearchRequestor { if(!isValuesOrValueOf && !methodBinding.isSynthetic()) { //{ObjectTeams: retrench enhanced callin signature: /* orig: - TypeBinding[] parameterTypes = methodBinding.original().parameters; + TypeBinding[] parameterTypes = methodBinding.original().parameters; :giro */ TypeBinding[] parameterTypes = methodBinding.original().getSourceParameters(); // SH} @@ -1402,11 +1453,11 @@ public final class SelectionEngine extends Engine implements ISearchRequestor { declaringClass, parsedUnit); //haebor} - this.requestor.acceptMethod( + this.requestor.acceptMethod( //{ObjectTeams /* orig: - declaringClass.qualifiedPackageName(), - declaringClass.qualifiedSourceName(), + declaringClass.qualifiedPackageName(), + declaringClass.qualifiedSourceName(), :giro */ packTypeName.qualifiedPackageName, packTypeName.qualifiedSourceName, @@ -1666,7 +1717,18 @@ public final class SelectionEngine extends Engine implements ISearchRequestor { } } else { // binary type ClassFile classFile = (ClassFile) context.getClassFile(); - ClassFileReader reader = (ClassFileReader) classFile.getBinaryTypeInfo((IFile) classFile.resource(), false/*don't fully initialize so as to keep constant pool (used below)*/); + BinaryTypeDescriptor descriptor = BinaryTypeFactory.createDescriptor(classFile); + ClassFileReader reader = null; + try { + reader = BinaryTypeFactory.rawReadType(descriptor, false/*don't fully initialize so as to keep constant pool (used below)*/); + } catch (ClassFormatException e) { + if (JavaCore.getPlugin().isDebugging()) { + e.printStackTrace(System.err); + } + } + if (reader == null) { + throw classFile.newNotPresentException(); + } CompilationResult result = new CompilationResult(reader.getFileName(), 1, 1, this.compilerOptions.maxProblemsPerUnit); parsedUnit = new CompilationUnitDeclaration(this.parser.problemReporter(), result, 0); HashSetOfCharArrayArray typeNames = new HashSetOfCharArrayArray(); diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java index fb21a1144..d3fc3377e 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java @@ -92,6 +92,7 @@ import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.PolymorphicMethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding; @@ -3098,7 +3099,7 @@ public class ClassFile implements TypeConstants, TypeIds { this.contents[localContentsOffset++] = (byte) (functionalDescriptorIndex >> 8); this.contents[localContentsOffset++] = (byte) functionalDescriptorIndex; - int methodHandleIndex = this.constantPool.literalIndexForMethodHandle(functional.binding.original()); // Speak of " implementation" (erased) version here, adaptations described below. + int methodHandleIndex = this.constantPool.literalIndexForMethodHandle(functional.binding instanceof PolymorphicMethodBinding ? functional.binding : functional.binding.original()); // Speak of " implementation" (erased) version here, adaptations described below. this.contents[localContentsOffset++] = (byte) (methodHandleIndex >> 8); this.contents[localContentsOffset++] = (byte) methodHandleIndex; diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java index b8163091e..ca0453b08 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java @@ -50,8 +50,9 @@ public FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowConte currentScope, flowContext, analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()); - if ((this.resolvedType.tagBits & TagBits.AnnotationNonNull) != 0) { - int nullStatus = assignment.expression.nullStatus(flowInfo, flowContext); + if ((this.resolvedType.tagBits & TagBits.AnnotationNonNull) != 0 || + (this.resolvedType.isFreeTypeVariable() && !assignment.expression.resolvedType.isFreeTypeVariable())) { + int nullStatus = assignment.expression.nullStatus(flowInfo, flowContext); if (nullStatus != FlowInfo.NON_NULL) { currentScope.problemReporter().nullityMismatch(this, assignment.expression.resolvedType, this.resolvedType, nullStatus, currentScope.environment().getNonNullAnnotationName()); } @@ -60,8 +61,8 @@ public FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowConte } public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - this.receiver.checkNPE(currentScope, flowContext, flowInfo, 1); flowInfo = this.receiver.analyseCode(currentScope, flowContext, flowInfo); + this.receiver.checkNPE(currentScope, flowContext, flowInfo, 1); flowInfo = this.position.analyseCode(currentScope, flowContext, flowInfo); this.position.checkNPEbyUnboxing(currentScope, flowContext, flowInfo); // account for potential ArrayIndexOutOfBoundsException: diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java index 9404a33f9..f4d9d991c 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BinaryExpression.java @@ -70,13 +70,13 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()) .unconditionalInits(); } else { - this.left.checkNPE(currentScope, flowContext, flowInfo); flowInfo = this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); + this.left.checkNPE(currentScope, flowContext, flowInfo); if (((this.bits & OperatorMASK) >> OperatorSHIFT) != AND) { flowContext.expireNullCheckedFieldInfo(); } - this.right.checkNPE(currentScope, flowContext, flowInfo); flowInfo = this.right.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); + this.right.checkNPE(currentScope, flowContext, flowInfo); if (((this.bits & OperatorMASK) >> OperatorSHIFT) != AND) { flowContext.expireNullCheckedFieldInfo(); } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CombinedBinaryExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CombinedBinaryExpression.java index 7ff2fec0f..51e284942 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CombinedBinaryExpression.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CombinedBinaryExpression.java @@ -125,26 +125,26 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, } try { BinaryExpression cursor; - if ((cursor = this.referencesTable[0]).resolvedType.id != - TypeIds.T_JavaLangString) { + cursor = this.referencesTable[0]; + flowInfo = cursor.left.analyseCode(currentScope, flowContext, flowInfo). + unconditionalInits(); + if (cursor.resolvedType.id != TypeIds.T_JavaLangString) { cursor.left.checkNPE(currentScope, flowContext, flowInfo); } - flowInfo = cursor.left.analyseCode(currentScope, flowContext, flowInfo). - unconditionalInits(); for (int i = 0, end = this.arity; i < end; i ++) { - if ((cursor = this.referencesTable[i]).resolvedType.id != - TypeIds.T_JavaLangString) { + cursor = this.referencesTable[i]; + flowInfo = cursor.right. + analyseCode(currentScope, flowContext, flowInfo). + unconditionalInits(); + if (cursor.resolvedType.id != TypeIds.T_JavaLangString) { cursor.right.checkNPE(currentScope, flowContext, flowInfo); } - flowInfo = cursor.right. - analyseCode(currentScope, flowContext, flowInfo). - unconditionalInits(); } + flowInfo = this.right.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); if (this.resolvedType.id != TypeIds.T_JavaLangString) { this.right.checkNPE(currentScope, flowContext, flowInfo); } - return this.right.analyseCode(currentScope, flowContext, flowInfo). - unconditionalInits(); + return flowInfo; } finally { // account for exception possibly thrown by arithmetics flowContext.recordAbruptExit(); diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java index 94110e54d..93b2020a3 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java @@ -136,6 +136,9 @@ public FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowConte this.receiver .analyseCode(currentScope, flowContext, flowInfo, !this.binding.isStatic()) .unconditionalInits(); + + this.receiver.checkNPE(currentScope, flowContext, flowInfo); + if (assignment.expression != null) { flowInfo = assignment diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java index 6c18cc851..c590ba9fb 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java @@ -107,9 +107,9 @@ public class ForeachStatement extends Statement { int initialComplaintLevel = (flowInfo.reachMode() & FlowInfo.UNREACHABLE) != 0 ? Statement.COMPLAINED_FAKE_REACHABLE : Statement.NOT_COMPLAINED; // process the element variable and collection - this.collection.checkNPE(currentScope, flowContext, flowInfo, 1); flowInfo = this.elementVariable.analyseCode(this.scope, flowContext, flowInfo); FlowInfo condInfo = this.collection.analyseCode(this.scope, flowContext, flowInfo.copy()); + this.collection.checkNPE(currentScope, flowContext, condInfo.copy(), 1); LocalVariableBinding elementVarBinding = this.elementVariable.binding; // element variable will be assigned when iterating @@ -126,7 +126,7 @@ public class ForeachStatement extends Statement { condInfo.nullInfoLessUnconditionalCopy(); actionInfo.markAsDefinitelyUnknown(elementVarBinding); if (currentScope.compilerOptions().isAnnotationBasedNullAnalysisEnabled) { - int elementNullStatus = FlowInfo.tagBitsToNullStatus(this.collectionElementType.tagBits); + int elementNullStatus = NullAnnotationMatching.nullStatusFromExpressionType(this.collectionElementType); int nullStatus = NullAnnotationMatching.checkAssignment(currentScope, flowContext, elementVarBinding, null, // have no useful flowinfo for element var elementNullStatus, this.collection, this.collectionElementType); if ((elementVarBinding.type.tagBits & TagBits.IsBaseType) == 0) { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java index 2c7667185..d6a97919f 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java @@ -878,7 +878,7 @@ public TypeBinding resolveType(BlockScope scope) { this.genericTypeArguments = new TypeBinding[length]; for (int i = 0; i < length; i++) { TypeReference typeReference = this.typeArguments[i]; - if ((this.genericTypeArguments[i] = typeReference.resolveType(scope, true /* check bounds*/)) == null) { + if ((this.genericTypeArguments[i] = typeReference.resolveType(scope, true /* check bounds*/, Binding.DefaultLocationTypeArgument)) == null) { this.argumentsHaveErrors = true; } if (this.argumentsHaveErrors && typeReference instanceof Wildcard) { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java index ef63d2a0d..0d57976d0 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java @@ -311,7 +311,7 @@ public class NullAnnotationMatching { } } severity = severity.max(s); - if (!severity.isAnyMismatch() && (providedBits & TagBits.AnnotationNonNull) != 0) + if (!severity.isAnyMismatch() && (providedBits & TagBits.AnnotationNullMASK) == TagBits.AnnotationNonNull) okStatus = okNonNullStatus(providedExpression); } if (severity != Severity.MISMATCH && nullStatus != FlowInfo.NULL) { // null value has no details @@ -479,6 +479,20 @@ public class NullAnnotationMatching { return 0; } + /** + * Use only if no suitable flowInfo is available. + */ + public static int nullStatusFromExpressionType(TypeBinding type) { + if (type.isFreeTypeVariable()) + return FlowInfo.FREE_TYPEVARIABLE; + long bits = type.tagBits & TagBits.AnnotationNullMASK; + if (bits == 0) + return FlowInfo.UNKNOWN; + if (bits == TagBits.AnnotationNonNull) + return FlowInfo.NON_NULL; + return FlowInfo.POTENTIALLY_NON_NULL | FlowInfo.POTENTIALLY_NULL; + } + public static long validNullTagBits(long bits) { bits &= TagBits.AnnotationNullMASK; return bits == TagBits.AnnotationNullMASK ? 0 : bits; @@ -725,5 +739,5 @@ public class NullAnnotationMatching { buf.append("Analysis result: severity="+this.severity); buf.append(" nullStatus="+this.nullStatus); return buf.toString(); - } + } } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java index 331bd262b..004ddafbd 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java @@ -55,7 +55,7 @@ import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.CodeStream; import org.eclipse.jdt.internal.compiler.codegen.ConstantPool; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; -import org.eclipse.jdt.internal.compiler.flow.ExceptionHandlingFlowContext; +import org.eclipse.jdt.internal.compiler.flow.FieldInitsFakingFlowContext; import org.eclipse.jdt.internal.compiler.flow.FlowContext; import org.eclipse.jdt.internal.compiler.flow.FlowInfo; import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo; @@ -243,7 +243,7 @@ public class ReferenceExpression extends FunctionalExpression implements IPolyEx IErrorHandlingPolicy oldPolicy = currentScope.problemReporter().switchErrorHandlingPolicy(silentErrorHandlingPolicy); try { implicitLambda.analyseCode(currentScope, - new ExceptionHandlingFlowContext(null, this, Binding.NO_EXCEPTIONS, null, currentScope, FlowInfo.DEAD_END), + new FieldInitsFakingFlowContext(null, this, Binding.NO_EXCEPTIONS, null, currentScope, FlowInfo.DEAD_END), UnconditionalFlowInfo.fakeInitializedFlowInfo(currentScope.outerMostMethodScope().analysisIndex, currentScope.referenceType().maxFieldCount)); } finally { currentScope.problemReporter().switchErrorHandlingPolicy(oldPolicy); @@ -288,7 +288,7 @@ public class ReferenceExpression extends FunctionalExpression implements IPolyEx if (this.binding != null && isMethodReference()) { if (TypeBinding.notEquals(this.binding.declaringClass, this.lhs.resolvedType.erasure())) { if (!this.binding.declaringClass.canBeSeenBy(currentScope)) { - this.binding = new MethodBinding(this.binding, (ReferenceBinding) this.lhs.resolvedType.erasure()); + this.binding = new MethodBinding(this.binding.original(), (ReferenceBinding) this.lhs.resolvedType.erasure()); } } } @@ -415,8 +415,8 @@ public class ReferenceExpression extends FunctionalExpression implements IPolyEx public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { // static methods with receiver value never get here if (this.haveReceiver) { - this.lhs.checkNPE(currentScope, flowContext, flowInfo); this.lhs.analyseCode(currentScope, flowContext, flowInfo, true); + this.lhs.checkNPE(currentScope, flowContext, flowInfo); } else if (isConstructorReference()) { TypeBinding type = this.receiverType.leafComponentType(); if (type.isNestedType() && diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java index ed03863b5..e099594cb 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/UnaryExpression.java @@ -34,18 +34,18 @@ public FlowInfo analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { - this.expression.checkNPE(currentScope, flowContext, flowInfo); if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT) { flowContext.tagBits ^= FlowContext.INSIDE_NEGATION; flowInfo = this.expression. analyseCode(currentScope, flowContext, flowInfo). asNegatedCondition(); flowContext.tagBits ^= FlowContext.INSIDE_NEGATION; - return flowInfo; } else { - return this.expression. + flowInfo = this.expression. analyseCode(currentScope, flowContext, flowInfo); } + this.expression.checkNPE(currentScope, flowContext, flowInfo); + return flowInfo; } public Constant optimizedBooleanConstant() { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/AnnotationInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/AnnotationInfo.java index 908176981..35380982d 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/AnnotationInfo.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/AnnotationInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2012 BEA Systems, Inc. + * Copyright (c) 2005, 2016 BEA Systems, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -377,20 +377,7 @@ private int scanElementValue(int offset) { return currentOffset; } public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append('@'); - buffer.append(this.typename); - if (this.pairs != null) { - buffer.append('('); - buffer.append("\n\t"); //$NON-NLS-1$ - for (int i = 0, len = this.pairs.length; i < len; i++) { - if (i > 0) - buffer.append(",\n\t"); //$NON-NLS-1$ - buffer.append(this.pairs[i]); - } - buffer.append(')'); - } - return buffer.toString(); + return BinaryTypeFormatter.annotationToString(this); } public int hashCode() { final int prime = 31; diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/AnnotationMethodInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/AnnotationMethodInfo.java index 6ddba77d2..1a598744f 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/AnnotationMethodInfo.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/AnnotationMethodInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2009 BEA Systems, Inc. + * Copyright (c) 2005, 2016 BEA Systems, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -93,23 +93,4 @@ AnnotationMethodInfo(MethodInfo methodInfo, Object defaultValue) { public Object getDefaultValue() { return this.defaultValue; } -protected void toStringContent(StringBuffer buffer) { - super.toStringContent(buffer); - if (this.defaultValue != null) { - buffer.append(" default "); //$NON-NLS-1$ - if (this.defaultValue instanceof Object[]) { - buffer.append('{'); - Object[] elements = (Object[]) this.defaultValue; - for (int i = 0, len = elements.length; i < len; i++) { - if (i > 0) - buffer.append(", "); //$NON-NLS-1$ - buffer.append(elements[i]); - } - buffer.append('}'); - } else { - buffer.append(this.defaultValue); - } - buffer.append('\n'); - } -} } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/AnnotationMethodInfoWithAnnotations.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/AnnotationMethodInfoWithAnnotations.java index bd1cce8ad..9ff47d30b 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/AnnotationMethodInfoWithAnnotations.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/AnnotationMethodInfoWithAnnotations.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2007 BEA Systems, Inc. + * Copyright (c) 2005, 2016 BEA Systems, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -34,11 +34,4 @@ protected void reset() { this.annotations[i].reset(); super.reset(); } -protected void toStringContent(StringBuffer buffer) { - super.toStringContent(buffer); - for (int i = 0, l = this.annotations == null ? 0 : this.annotations.length; i < l; i++) { - buffer.append(this.annotations[i]); - buffer.append('\n'); - } -} } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/BinaryTypeFormatter.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/BinaryTypeFormatter.java new file mode 100644 index 000000000..99c51113a --- /dev/null +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/BinaryTypeFormatter.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.compiler.classfmt; + +import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; +import org.eclipse.jdt.internal.compiler.env.IBinaryElementValuePair; +import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; +import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; +import org.eclipse.jdt.internal.compiler.util.Util; + +public class BinaryTypeFormatter { + + public static String annotationToString(IBinaryAnnotation annotation) { + StringBuffer buffer = new StringBuffer(); + buffer.append('@'); + buffer.append(annotation.getTypeName()); + IBinaryElementValuePair[] valuePairs = annotation.getElementValuePairs(); + if (valuePairs != null) { + buffer.append('('); + buffer.append("\n\t"); //$NON-NLS-1$ + for (int i = 0, len = valuePairs.length; i < len; i++) { + if (i > 0) + buffer.append(",\n\t"); //$NON-NLS-1$ + buffer.append(valuePairs[i]); + } + buffer.append(')'); + } + return buffer.toString(); + } + + public static String annotationToString(IBinaryTypeAnnotation typeAnnotation) { + StringBuffer buffer = new StringBuffer(); + buffer.append(typeAnnotation.getAnnotation()); + buffer.append(' '); + // Not fully decoding it here, just including all the information in the string + buffer.append("target_type=").append(typeAnnotation.getTargetType()); //$NON-NLS-1$ + buffer.append(", info=").append(typeAnnotation.getSupertypeIndex()); //$NON-NLS-1$ + buffer.append(", info2=").append(typeAnnotation.getBoundIndex()); //$NON-NLS-1$ + int[] theTypePath = typeAnnotation.getTypePath(); + if (theTypePath != null && theTypePath.length != 0) { + buffer.append(", location=["); //$NON-NLS-1$ + for (int i = 0, max = theTypePath.length; i < max; i += 2) { + if (i > 0) { + buffer.append(", "); //$NON-NLS-1$ + } + switch (theTypePath[i]) { + case 0: + buffer.append("ARRAY"); //$NON-NLS-1$ + break; + case 1: + buffer.append("INNER_TYPE"); //$NON-NLS-1$ + break; + case 2: + buffer.append("WILDCARD"); //$NON-NLS-1$ + break; + case 3: + buffer.append("TYPE_ARGUMENT(").append(theTypePath[i+1]).append(')'); //$NON-NLS-1$ + break; + } + } + buffer.append(']'); + } + return buffer.toString(); + } + + public static String methodToString(IBinaryMethod method) { + StringBuffer result = new StringBuffer(); + methodToStringContent(result, method); + return result.toString(); + } + + public static void methodToStringContent(StringBuffer buffer, IBinaryMethod method) { + int modifiers = method.getModifiers(); + char[] desc = method.getGenericSignature(); + if (desc == null) + desc = method.getMethodDescriptor(); + buffer + .append('{') + .append( + ((modifiers & ClassFileConstants.AccDeprecated) != 0 ? "deprecated " : Util.EMPTY_STRING) //$NON-NLS-1$ + + ((modifiers & 0x0001) == 1 ? "public " : Util.EMPTY_STRING) //$NON-NLS-1$ + + ((modifiers & 0x0002) == 0x0002 ? "private " : Util.EMPTY_STRING) //$NON-NLS-1$ + + ((modifiers & 0x0004) == 0x0004 ? "protected " : Util.EMPTY_STRING) //$NON-NLS-1$ + + ((modifiers & 0x0008) == 0x000008 ? "static " : Util.EMPTY_STRING) //$NON-NLS-1$ + + ((modifiers & 0x0010) == 0x0010 ? "final " : Util.EMPTY_STRING) //$NON-NLS-1$ + + ((modifiers & 0x0040) == 0x0040 ? "bridge " : Util.EMPTY_STRING) //$NON-NLS-1$ + + ((modifiers & 0x0080) == 0x0080 ? "varargs " : Util.EMPTY_STRING)) //$NON-NLS-1$ + .append(method.getSelector()) + .append(desc) + .append('}'); + + Object defaultValue = method.getDefaultValue(); + if (defaultValue != null) { + buffer.append(" default "); //$NON-NLS-1$ + if (defaultValue instanceof Object[]) { + buffer.append('{'); + Object[] elements = (Object[]) defaultValue; + for (int i = 0, len = elements.length; i < len; i++) { + if (i > 0) + buffer.append(", "); //$NON-NLS-1$ + buffer.append(elements[i]); + } + buffer.append('}'); + } else { + buffer.append(defaultValue); + } + buffer.append('\n'); + } + + IBinaryAnnotation[] annotations = method.getAnnotations(); + for (int i = 0, l = annotations == null ? 0 : annotations.length; i < l; i++) { + buffer.append(annotations[i]); + buffer.append('\n'); + } + + int annotatedParameterCount = method.getAnnotatedParametersCount(); + for (int i = 0; i < annotatedParameterCount; i++) { + buffer.append("param" + (i - 1)); //$NON-NLS-1$ + buffer.append('\n'); + IBinaryAnnotation[] infos = method.getParameterAnnotations(i, new char[0]); + for (int j = 0, k = infos == null ? 0 : infos.length; j < k; j++) { + buffer.append(infos[j]); + buffer.append('\n'); + } + } + } + +} diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java index 5b5d2a1b8..526c325cf 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -19,29 +19,31 @@ package org.eclipse.jdt.internal.compiler.classfmt; import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants; import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants; -import org.eclipse.jdt.internal.compiler.env.*; +import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; +import org.eclipse.jdt.internal.compiler.env.IBinaryElementValuePair; +import org.eclipse.jdt.internal.compiler.env.IBinaryField; +import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; +import org.eclipse.jdt.internal.compiler.env.IBinaryNestedType; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; +import org.eclipse.jdt.internal.compiler.env.ITypeAnnotationWalker; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.ExternalAnnotationStatus; import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; import org.eclipse.jdt.internal.compiler.lookup.TagBits; -import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.lookup.TypeIds; -import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.ExternalAnnotationStatus; import org.eclipse.jdt.internal.compiler.util.Util; import org.eclipse.objectteams.otdt.core.compiler.IOTConstants; import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.*; @@ -94,8 +96,6 @@ public class ClassFileReader extends ClassFileStruct implements IBinaryType { private char[][][] missingTypeNames; private int enclosingNameAndTypeIndex; private char[] enclosingMethod; - private ExternalAnnotationProvider annotationProvider; - private ExternalAnnotationStatus externalAnnotationStatus = ExternalAnnotationStatus.NOT_EEA_CONFIGURED; private static String printTypeModifiers(int modifiers) { java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); @@ -528,60 +528,9 @@ public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInit } } -/** Auxiliary interface for {@link #setExternalAnnotationProvider(String,String,ZipFile,ZipFileProducer)}. */ -public interface ZipFileProducer { ZipFile produce() throws IOException; } - -/** - * Create and remember a provider for external annotations using the given basePath, - * which is either a directory holding .eea text files, or a zip file of entries of the same format. - * @param basePath resolved filesystem path of either directory or zip file - * @param qualifiedBinaryTypeName slash-separated type name - * @param zipFile an existing zip file for the same basePath, or null. - * Output: wl be filled with - * @param producer an optional helper to produce the zipFile when needed. - * @return the client provided zip file; - * or else a fresh new zip file, to let clients cache it, if desired; - * or null to signal that basePath is not a zip file, but a directory. - * @throws IOException any unexpected errors during file access. File not found while - * accessing an individual file if basePath is a directory <em>is</em> expected, - * and simply answered with null. If basePath is neither a directory nor a zip file, - * this is unexpected. - */ -public ZipFile setExternalAnnotationProvider(String basePath, String qualifiedBinaryTypeName, ZipFile zipFile, ZipFileProducer producer) throws IOException { - this.externalAnnotationStatus = ExternalAnnotationStatus.NO_EEA_FILE; - String qualifiedBinaryFileName = qualifiedBinaryTypeName + ExternalAnnotationProvider.ANNOTATION_FILE_SUFFIX; - if (zipFile == null) { - File annotationBase = new File(basePath); - if (annotationBase.isDirectory()) { - try { - String filePath = annotationBase.getAbsolutePath()+'/'+qualifiedBinaryFileName; - this.annotationProvider = new ExternalAnnotationProvider(new FileInputStream(filePath), String.valueOf(getName())); - this.externalAnnotationStatus = ExternalAnnotationStatus.TYPE_IS_ANNOTATED; - } catch (FileNotFoundException e) { - // expected, no need to report an error here - } - return null; // no zipFile - } - if (!annotationBase.exists()) - return null; // no zipFile, treat as not-yet-created directory - zipFile = (producer != null ? producer.produce() : new ZipFile(annotationBase)); - } - ZipEntry entry = zipFile.getEntry(qualifiedBinaryFileName); - if (entry != null) { - this.annotationProvider = new ExternalAnnotationProvider(zipFile.getInputStream(entry), String.valueOf(getName())); - this.externalAnnotationStatus = ExternalAnnotationStatus.TYPE_IS_ANNOTATED; - } - return zipFile; -} -public boolean hasAnnotationProvider() { - return this.annotationProvider != null; -} -public void markAsFromSource() { - this.externalAnnotationStatus = ExternalAnnotationStatus.FROM_SOURCE; -} @Override public ExternalAnnotationStatus getExternalAnnotationStatus() { - return this.externalAnnotationStatus; + return ExternalAnnotationStatus.NOT_EEA_CONFIGURED; } /** * Conditionally add external annotations to the mix. @@ -590,23 +539,6 @@ public ExternalAnnotationStatus getExternalAnnotationStatus() { */ @Override public ITypeAnnotationWalker enrichWithExternalAnnotationsFor(ITypeAnnotationWalker walker, Object member, LookupEnvironment environment) { - if (walker == ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER && this.annotationProvider != null) { - if (member == null) { - return this.annotationProvider.forTypeHeader(environment); - } else if (member instanceof IBinaryField) { - IBinaryField field = (IBinaryField) member; - char[] fieldSignature = field.getGenericSignature(); - if (fieldSignature == null) - fieldSignature = field.getTypeName(); - return this.annotationProvider.forField(field.getName(), fieldSignature, environment); - } else if (member instanceof IBinaryMethod) { - IBinaryMethod method = (IBinaryMethod) member; - char[] methodSignature = method.getGenericSignature(); - if (methodSignature == null) - methodSignature = method.getMethodDescriptor(); - return this.annotationProvider.forMethod(method.isConstructor() ? TypeConstants.INIT : method.getSelector(), methodSignature, environment); - } - } return walker; } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ElementValuePairInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ElementValuePairInfo.java index 5a40c0d96..b3a3872bf 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ElementValuePairInfo.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ElementValuePairInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2010 BEA Systems, Inc. + * Copyright (c) 2005, 2016 BEA Systems, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -22,7 +22,7 @@ public class ElementValuePairInfo implements org.eclipse.jdt.internal.compiler.e private char[] name; private Object value; -ElementValuePairInfo(char[] name, Object value) { +public ElementValuePairInfo(char[] name, Object value) { this.name = name; this.value = value; } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationDecorator.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationDecorator.java new file mode 100644 index 000000000..f379a7ecb --- /dev/null +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationDecorator.java @@ -0,0 +1,290 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos <sxenos@gmail.com> (Google) - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.compiler.classfmt; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; +import org.eclipse.jdt.internal.compiler.env.IBinaryField; +import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; +import org.eclipse.jdt.internal.compiler.env.IBinaryNestedType; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; +import org.eclipse.jdt.internal.compiler.env.ITypeAnnotationWalker; +import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.ExternalAnnotationStatus; +import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; + +/** + * A decorator for {@link IBinaryType} that allows external annotations to be attached. This can be used to change the + * result of {@link #enrichWithExternalAnnotationsFor} or {@link #getExternalAnnotationStatus}. + */ +public class ExternalAnnotationDecorator implements IBinaryType { + private IBinaryType inputType; + private ExternalAnnotationProvider annotationProvider; + private boolean isFromSource; + + /** Auxiliary interface for {@link #getAnnotationZipFile(String, ZipFileProducer)}. */ + public interface ZipFileProducer { ZipFile produce() throws IOException; } + + public ExternalAnnotationDecorator(IBinaryType toDecorate, ExternalAnnotationProvider externalAnnotationProvider) { + if (toDecorate == null) { + throw new NullPointerException("toDecorate"); //$NON-NLS-1$ + } + this.inputType = toDecorate; + this.annotationProvider = externalAnnotationProvider; + } + + public ExternalAnnotationDecorator(IBinaryType toDecorate, boolean isFromSource) { + if (toDecorate == null) { + throw new NullPointerException("toDecorate"); //$NON-NLS-1$ + } + this.isFromSource = isFromSource; + this.inputType = toDecorate; + } + + @Override + public char[] getFileName() { + return this.inputType.getFileName(); + } + + @Override + public boolean isBinaryType() { + return this.inputType.isBinaryType(); + } + + @Override + public IBinaryAnnotation[] getAnnotations() { + return this.inputType.getAnnotations(); + } + + @Override + public IBinaryTypeAnnotation[] getTypeAnnotations() { + return this.inputType.getTypeAnnotations(); + } + + @Override + public char[] getEnclosingMethod() { + return this.inputType.getEnclosingMethod(); + } + + @Override + public char[] getEnclosingTypeName() { + return this.inputType.getEnclosingTypeName(); + } + + @Override + public IBinaryField[] getFields() { + return this.inputType.getFields(); + } + + @Override + public char[] getGenericSignature() { + return this.inputType.getGenericSignature(); + } + + @Override + public char[][] getInterfaceNames() { + return this.inputType.getInterfaceNames(); + } + + @Override + public IBinaryNestedType[] getMemberTypes() { + return this.inputType.getMemberTypes(); + } + + @Override + public IBinaryMethod[] getMethods() { + return this.inputType.getMethods(); + } + + @Override + public char[][][] getMissingTypeNames() { + return this.inputType.getMissingTypeNames(); + } + + @Override + public char[] getName() { + return this.inputType.getName(); + } + + @Override + public char[] getSourceName() { + return this.inputType.getSourceName(); + } + + @Override + public char[] getSuperclassName() { + return this.inputType.getSuperclassName(); + } + + @Override + public long getTagBits() { + return this.inputType.getTagBits(); + } + + @Override + public boolean isAnonymous() { + return this.inputType.isAnonymous(); + } + + @Override + public boolean isLocal() { + return this.inputType.isLocal(); + } + + @Override + public boolean isMember() { + return this.inputType.isMember(); + } + + @Override + public char[] sourceFileName() { + return this.inputType.sourceFileName(); + } + + @Override + public int getModifiers() { + return this.inputType.getModifiers(); + } + + /** + * Returns the zip file containing external annotations, if any. Returns null if there are no external annotations + * or if the basePath refers to a directory. + * + * @param basePath + * resolved filesystem path of either directory or zip file + * @param producer + * an optional helper to produce the zipFile when needed. + * @return the client provided zip file; or else a fresh new zip file, to let clients cache it, if desired; or null + * to signal that basePath is not a zip file, but a directory. + * @throws IOException + * any unexpected errors during file access. File not found while accessing an individual file if + * basePath is a directory <em>is</em> expected, and simply answered with null. If basePath is neither a + * directory nor a zip file, this is unexpected. + */ + public static ZipFile getAnnotationZipFile(String basePath, ZipFileProducer producer) throws IOException { + File annotationBase = new File(basePath); + if (!annotationBase.isFile()) { + return null; + } + return (producer != null ? producer.produce() : new ZipFile(annotationBase)); + } + + /** + * Creates an external annotation provider for external annotations using the given basePath, which is either a + * directory holding .eea text files, or a zip file of entries of the same format. + * + * @param basePath + * resolved filesystem path of either directory or zip file + * @param qualifiedBinaryTypeName + * slash-separated type name + * @param zipFile + * an existing zip file for the same basePath, or null. + * @return the annotation provider or null if there are no external annotations. + * @throws IOException + * any unexpected errors during file access. File not found while accessing an individual file if + * basePath is a directory <em>is</em> expected, and simply answered with null. If basePath is neither a + * directory nor a zip file, this is unexpected. + */ + public static ExternalAnnotationProvider externalAnnotationProvider(String basePath, String qualifiedBinaryTypeName, + ZipFile zipFile) throws IOException { + String qualifiedBinaryFileName = qualifiedBinaryTypeName + ExternalAnnotationProvider.ANNOTATION_FILE_SUFFIX; + if (zipFile == null) { + File annotationBase = new File(basePath); + if (annotationBase.isDirectory()) { + String filePath = annotationBase.getAbsolutePath() + '/' + qualifiedBinaryFileName; + try { + return new ExternalAnnotationProvider(new FileInputStream(filePath), qualifiedBinaryTypeName); + } catch (FileNotFoundException e) { + // Expected, no need to report an error here + return null; + } + } + } else { + ZipEntry entry = zipFile.getEntry(qualifiedBinaryFileName); + if (entry != null) { + return new ExternalAnnotationProvider(zipFile.getInputStream(entry), qualifiedBinaryTypeName); + } + } + return null; + } + + /** + * Possibly wrap the provided binary type in a ClassWithExternalAnnotations to which a fresh provider for external + * annotations is associated. This provider is constructed using the given basePath, which is either a directory + * holding .eea text files, or a zip file of entries of the same format. If no such provider could be constructed, + * then the original binary type is returned unchanged. + * + * @param toDecorate + * the binary type to wrap, if needed + * @param basePath + * resolved filesystem path of either directory or zip file + * @param qualifiedBinaryTypeName + * slash-separated type name + * @param zipFile + * an existing zip file for the same basePath, or null. + * @return either a fresh ClassWithExternalAnnotations or the original binary type unchanged. + * @throws IOException + * any unexpected errors during file access. File not found while accessing an individual file if + * basePath is a directory <em>is</em> expected, and simply handled by not setting up an external + * annotation provider. If basePath is neither a directory nor a zip file, this is unexpected, resulting + * in an exception. + */ + public static IBinaryType create(IBinaryType toDecorate, String basePath, + String qualifiedBinaryTypeName, ZipFile zipFile) throws IOException { + ExternalAnnotationProvider externalAnnotationProvider = externalAnnotationProvider(basePath, qualifiedBinaryTypeName, zipFile); + if (externalAnnotationProvider == null) + return toDecorate; + return new ExternalAnnotationDecorator(toDecorate, externalAnnotationProvider); + } + + @Override + public ITypeAnnotationWalker enrichWithExternalAnnotationsFor(ITypeAnnotationWalker walker, Object member, + LookupEnvironment environment) { + if (walker == ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER && this.annotationProvider != null) { + if (member == null) { + return this.annotationProvider.forTypeHeader(environment); + } else if (member instanceof IBinaryField) { + IBinaryField field = (IBinaryField) member; + char[] fieldSignature = field.getGenericSignature(); + if (fieldSignature == null) + fieldSignature = field.getTypeName(); + return this.annotationProvider.forField(field.getName(), fieldSignature, environment); + } else if (member instanceof IBinaryMethod) { + IBinaryMethod method = (IBinaryMethod) member; + char[] methodSignature = method.getGenericSignature(); + if (methodSignature == null) + methodSignature = method.getMethodDescriptor(); + return this.annotationProvider.forMethod( + method.isConstructor() ? TypeConstants.INIT : method.getSelector(), methodSignature, + environment); + } + } + return walker; + } + + @Override + public ExternalAnnotationStatus getExternalAnnotationStatus() { + if (this.annotationProvider == null) { + if (this.isFromSource) { + return ExternalAnnotationStatus.FROM_SOURCE; + } + return ExternalAnnotationStatus.NO_EEA_FILE; + } + return ExternalAnnotationStatus.TYPE_IS_ANNOTATED; + } +} diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfo.java index 4e2724da5..6033dc886 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfo.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -36,7 +36,7 @@ import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; -import org.eclipse.jdt.internal.compiler.util.Util; +import org.eclipse.jdt.internal.core.nd.java.JavaNames; import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.AbstractAttribute; import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.AnchorListAttribute; import org.eclipse.objectteams.otdt.internal.core.compiler.bytecode.CopyInheritanceSourceAttribute; @@ -326,6 +326,7 @@ public int compareTo(Object o) { if (result != 0) return result; return new String(getMethodDescriptor()).compareTo(new String(otherMethod.getMethodDescriptor())); } +@Override public boolean equals(Object o) { if (!(o instanceof MethodInfo)) { return false; @@ -334,6 +335,7 @@ public boolean equals(Object o) { return CharOperation.equals(getSelector(), otherMethod.getSelector()) && CharOperation.equals(getMethodDescriptor(), otherMethod.getMethodDescriptor()); } +@Override public int hashCode() { return CharOperation.hashCode(getSelector()) + CharOperation.hashCode(getMethodDescriptor()); } @@ -457,16 +459,14 @@ protected void initialize() { * @return boolean */ public boolean isClinit() { - char[] selector = getSelector(); - return selector[0] == '<' && selector.length == 8; // Can only match <clinit> + return JavaNames.isClinit(getSelector()); } /** * Answer true if the method is a constructor, false otherwise. * @return boolean */ public boolean isConstructor() { - char[] selector = getSelector(); - return selector[0] == '<' && selector.length == 6; // Can only match <init> + return JavaNames.isConstructor(getSelector()); } /** * Return true if the field is a synthetic method, false otherwise. @@ -599,6 +599,7 @@ public int sizeInBytes() { return this.attributeBytes; } +@Override public String toString() { StringBuffer buffer = new StringBuffer(); toString(buffer); @@ -609,24 +610,7 @@ void toString(StringBuffer buffer) { toStringContent(buffer); } protected void toStringContent(StringBuffer buffer) { - int modifiers = getModifiers(); - char[] desc = getGenericSignature(); - if (desc == null) - desc = getMethodDescriptor(); - buffer - .append('{') - .append( - ((modifiers & ClassFileConstants.AccDeprecated) != 0 ? "deprecated " : Util.EMPTY_STRING) //$NON-NLS-1$ - + ((modifiers & 0x0001) == 1 ? "public " : Util.EMPTY_STRING) //$NON-NLS-1$ - + ((modifiers & 0x0002) == 0x0002 ? "private " : Util.EMPTY_STRING) //$NON-NLS-1$ - + ((modifiers & 0x0004) == 0x0004 ? "protected " : Util.EMPTY_STRING) //$NON-NLS-1$ - + ((modifiers & 0x0008) == 0x000008 ? "static " : Util.EMPTY_STRING) //$NON-NLS-1$ - + ((modifiers & 0x0010) == 0x0010 ? "final " : Util.EMPTY_STRING) //$NON-NLS-1$ - + ((modifiers & 0x0040) == 0x0040 ? "bridge " : Util.EMPTY_STRING) //$NON-NLS-1$ - + ((modifiers & 0x0080) == 0x0080 ? "varargs " : Util.EMPTY_STRING)) //$NON-NLS-1$ - .append(getSelector()) - .append(desc) - .append('}'); + BinaryTypeFormatter.methodToStringContent(buffer, this); } private void readCodeAttribute() { int attributesCount = u2At(6); diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfoWithAnnotations.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfoWithAnnotations.java index 4db5b95d4..c2621f470 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfoWithAnnotations.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfoWithAnnotations.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2007 BEA Systems, Inc. + * Copyright (c) 2005, 2016 BEA Systems, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -43,11 +43,4 @@ protected void reset() { this.annotations[i].reset(); super.reset(); } -protected void toStringContent(StringBuffer buffer) { - super.toStringContent(buffer); - for (int i = 0, l = this.annotations == null ? 0 : this.annotations.length; i < l; i++) { - buffer.append(this.annotations[i]); - buffer.append('\n'); - } -} } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfoWithParameterAnnotations.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfoWithParameterAnnotations.java index d29206f80..3fa81eb45 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfoWithParameterAnnotations.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfoWithParameterAnnotations.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2015 BEA Systems, Inc. + * Copyright (c) 2005, 2016 BEA Systems, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -61,16 +61,4 @@ protected void reset() { } super.reset(); } -protected void toStringContent(StringBuffer buffer) { - super.toStringContent(buffer); - for (int i = 0, l = this.parameterAnnotations == null ? 0 : this.parameterAnnotations.length; i < l; i++) { - buffer.append("param" + (i - 1)); //$NON-NLS-1$ - buffer.append('\n'); - AnnotationInfo[] infos = this.parameterAnnotations[i]; - for (int j = 0, k = infos == null ? 0 : infos.length; j < k; j++) { - buffer.append(infos[j]); - buffer.append('\n'); - } - } -} } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfoWithTypeAnnotations.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfoWithTypeAnnotations.java index 54c75dcff..d2f7db600 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfoWithTypeAnnotations.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/MethodInfoWithTypeAnnotations.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013 GoPivotal, Inc. All Rights Reserved. + * Copyright (c) 2016 GoPivotal, Inc. All Rights Reserved. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -36,12 +36,4 @@ protected void reset() { } super.reset(); } -protected void toStringContent(StringBuffer buffer) { - super.toStringContent(buffer); - buffer.append("type annotations = \n");//$NON-NLS-1$ - for (int i = 0, l = this.typeAnnotations == null ? 0 : this.typeAnnotations.length; i < l; i++) { - buffer.append(this.typeAnnotations[i].toString()); - buffer.append('\n'); - } -} } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/TypeAnnotationInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/TypeAnnotationInfo.java index f58de40a1..1472b3cc7 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/TypeAnnotationInfo.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/TypeAnnotationInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013 GoPivotal, Inc. All Rights Reserved. + * Copyright (c) 2016 GoPivotal, Inc. All Rights Reserved. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -123,40 +123,9 @@ protected void reset() { } public String toString() { - StringBuffer buffer = new StringBuffer(); - buffer.append(this.annotation); - buffer.append(' '); - // Not fully decoding it here, just including all the information in the string - buffer.append("target_type=").append(this.targetType); //$NON-NLS-1$ - buffer.append(", info=").append(this.info); //$NON-NLS-1$ - buffer.append(", info2=").append(this.info2); //$NON-NLS-1$ - if (this.typePath != NO_TYPE_PATH) { - buffer.append(", location=["); //$NON-NLS-1$ - for (int i = 0, max = this.typePath.length; i < max; i += 2) { - if (i > 0) { - buffer.append(", "); //$NON-NLS-1$ - } - switch (this.typePath[i]) { - case 0: - buffer.append("ARRAY"); //$NON-NLS-1$ - break; - case 1: - buffer.append("INNER_TYPE"); //$NON-NLS-1$ - break; - case 2: - buffer.append("WILDCARD"); //$NON-NLS-1$ - break; - case 3: - buffer.append("TYPE_ARGUMENT(").append(this.typePath[i+1]).append(')'); //$NON-NLS-1$ - break; - } - } - buffer.append(']'); - } - return buffer.toString(); + return BinaryTypeFormatter.annotationToString(this); } - public int getTargetType() { return this.targetType; } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java index f49d64ced..f7709c443 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java @@ -2716,6 +2716,8 @@ public void generateSyntheticBodyForDeserializeLambda(SyntheticMethodBinding met methodKind = ClassFileConstants.MethodHandleRefKindInvokeSpecial; } else if (mb.isConstructor()) { methodKind = ClassFileConstants.MethodHandleRefKindNewInvokeSpecial; + } else if (mb.declaringClass.isInterface()) { + methodKind = ClassFileConstants.MethodHandleRefKindInvokeInterface; } else { methodKind = ClassFileConstants.MethodHandleRefKindInvokeVirtual; } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/NameEnvironmentAnswer.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/NameEnvironmentAnswer.java index bc79c2f51..03b856352 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/NameEnvironmentAnswer.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/NameEnvironmentAnswer.java @@ -34,15 +34,44 @@ public class NameEnvironmentAnswer { this.accessRestriction = accessRestriction; this.externalAnnotationPath = externalAnnotationPath; } + + @Override + public String toString() { + String baseString = ""; //$NON-NLS-1$ + if (this.binaryType != null) { + char[] fileNameChars = this.binaryType.getFileName(); + String fileName = fileNameChars == null ? "" : new String(fileNameChars); //$NON-NLS-1$ + baseString = "IBinaryType " + fileName; //$NON-NLS-1$ + } + if (this.compilationUnit != null) { + baseString = "ICompilationUnit " + this.compilationUnit.toString(); //$NON-NLS-1$ + } + if (this.sourceTypes != null) { + baseString = this.sourceTypes.toString(); + } + if (this.accessRestriction != null) { + baseString += " " + this.accessRestriction.toString(); //$NON-NLS-1$ + } + if (this.externalAnnotationPath != null) { + baseString += " extPath=" + this.externalAnnotationPath.toString(); //$NON-NLS-1$ + } + return baseString; + } + /** * Returns the associated access restriction, or null if none. */ public AccessRestriction getAccessRestriction() { return this.accessRestriction; } + + public void setBinaryType(IBinaryType newType) { + this.binaryType = newType; + } + /** - * Answer the resolved binary form for the type or null if the - * receiver represents a compilation unit or source type. + * Answer the resolved binary form for the type or null if the receiver represents a compilation unit or source + * type. */ public IBinaryType getBinaryType() { return this.binaryType; diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionInferenceFlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionInferenceFlowContext.java index a94d4d06d..172e28ecf 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionInferenceFlowContext.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ExceptionInferenceFlowContext.java @@ -19,7 +19,7 @@ import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; * try statements, exception handlers, etc... */ -public class ExceptionInferenceFlowContext extends ExceptionHandlingFlowContext { +public class ExceptionInferenceFlowContext extends FieldInitsFakingFlowContext { public ExceptionInferenceFlowContext( FlowContext parent, ASTNode associatedNode, diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FieldInitsFakingFlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FieldInitsFakingFlowContext.java new file mode 100644 index 000000000..35e1b94ec --- /dev/null +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FieldInitsFakingFlowContext.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2016 Till Brychcy and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Till Brychcy - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.compiler.flow; + +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; + +/** + * For instances of this class, + * {@link FlowContext#getInitsForFinalBlankInitializationCheck(org.eclipse.jdt.internal.compiler.lookup.TypeBinding, FlowInfo)} + * will returns a {@link FlowInfo#DEAD_END}, which for which + * {@link FlowInfo#isDefinitelyAssigned(org.eclipse.jdt.internal.compiler.lookup.FieldBinding)} returns true for all + * fields. + */ + +public class FieldInitsFakingFlowContext extends ExceptionHandlingFlowContext { + public FieldInitsFakingFlowContext( + FlowContext parent, + ASTNode associatedNode, + ReferenceBinding[] handledExceptions, + FlowContext initializationParent, + BlockScope scope, + UnconditionalFlowInfo flowInfo) { + super(parent, associatedNode, handledExceptions, initializationParent, scope, flowInfo); +} +}
\ No newline at end of file diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java index e75997d01..ce65992f2 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java @@ -514,6 +514,9 @@ public FlowInfo getInitsForFinalBlankInitializationCheck(TypeBinding declaringTy inits = initializationContext.initsBeforeContext; current = initializationContext.initializationParent; } else if (current instanceof ExceptionHandlingFlowContext) { + if(current instanceof FieldInitsFakingFlowContext) { + return FlowInfo.DEAD_END; // isDefinitelyAssigned will return true for all fields + } ExceptionHandlingFlowContext exceptionContext = (ExceptionHandlingFlowContext) current; current = exceptionContext.initializationParent == null ? exceptionContext.parent : exceptionContext.initializationParent; } else { @@ -521,7 +524,7 @@ public FlowInfo getInitsForFinalBlankInitializationCheck(TypeBinding declaringTy } } while (current != null); // not found - return null; + throw new IllegalStateException(declaringType.debugName()); } /* diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java index 0cc4f1125..11a78f3bf 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java @@ -1648,8 +1648,7 @@ public MethodBinding[] getMethods(char[] selector, int suggestedParameterLength) int length = end - start + 1; int count = 0; for (int i = start; i <= end; i++) { - int len = this.methods[i].parameters.length; - if (len <= suggestedParameterLength || (this.methods[i].isVarargs() && len == suggestedParameterLength + 1)) + if (this.methods[i].doesParameterLengthMatch(suggestedParameterLength)) count++; } if (count == 0) { @@ -1662,8 +1661,7 @@ public MethodBinding[] getMethods(char[] selector, int suggestedParameterLength) MethodBinding[] result = new MethodBinding[count]; // iterate methods to resolve them for (int i = start, index = 0; i <= end; i++) { - int len = this.methods[i].parameters.length; - if (len <= suggestedParameterLength || (this.methods[i].isVarargs() && len == suggestedParameterLength + 1)) + if (this.methods[i].doesParameterLengthMatch(suggestedParameterLength)) result[index++] = resolveTypesFor(this.methods[i]); } return result; @@ -1671,6 +1669,7 @@ public MethodBinding[] getMethods(char[] selector, int suggestedParameterLength) } return Binding.NO_METHODS; } + public boolean hasMemberTypes() { if (!isPrototype()) return this.prototype.hasMemberTypes(); @@ -2126,7 +2125,9 @@ private void scanMethodForNullAnnotation(IBinaryMethod method, MethodBinding met } } if (useNullTypeAnnotations && this.externalAnnotationStatus.isPotentiallyUnannotatedLib()) { - if (methodBinding.returnType.hasNullTypeAnnotations()) { + if (methodBinding.returnType.hasNullTypeAnnotations() + || (methodBinding.tagBits & TagBits.AnnotationNullMASK) != 0 + || methodBinding.parameterNonNullness != null) { this.externalAnnotationStatus = ExternalAnnotationStatus.TYPE_IS_ANNOTATED; } else { for (TypeBinding parameter : parameters) { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java index a00415836..b8ebe8398 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java @@ -849,19 +849,27 @@ class BoundSet { // α = S and T <: α imply ⟨T <: S⟩ InferenceVariable alpha = boundS.left; TypeBinding s = boundS.right; - if (TypeBinding.equalsEquals(alpha,boundT.left)) - return ConstraintTypeFormula.create(s, boundT.right, boundT.relation, boundT.isSoft||boundS.isSoft); - if (TypeBinding.equalsEquals(alpha, boundT.right)) - return ConstraintTypeFormula.create(boundT.right, s, boundT.relation, boundT.isSoft||boundS.isSoft); + if (TypeBinding.equalsEquals(alpha, boundT.left)) { + TypeBinding t = boundT.right; + return ConstraintTypeFormula.create(s, t, boundT.relation, boundT.isSoft||boundS.isSoft); + } + if (TypeBinding.equalsEquals(alpha, boundT.right)) { + TypeBinding t = boundT.left; + return ConstraintTypeFormula.create(t, s, boundT.relation, boundT.isSoft||boundS.isSoft); + } if (boundS.right instanceof InferenceVariable) { // reverse: alpha = (InferenceVariable) boundS.right; s = boundS.left; - if (TypeBinding.equalsEquals(alpha, boundT.left)) - return ConstraintTypeFormula.create(s, boundT.right, boundT.relation, boundT.isSoft||boundS.isSoft); - if (TypeBinding.equalsEquals(alpha, boundT.right)) - return ConstraintTypeFormula.create(boundT.right, s, boundT.relation, boundT.isSoft||boundS.isSoft); + if (TypeBinding.equalsEquals(alpha, boundT.left)) { + TypeBinding t = boundT.right; + return ConstraintTypeFormula.create(s, t, boundT.relation, boundT.isSoft||boundS.isSoft); + } + if (TypeBinding.equalsEquals(alpha, boundT.right)) { + TypeBinding t = boundT.left; + return ConstraintTypeFormula.create(t, s, boundT.relation, boundT.isSoft||boundS.isSoft); + } } // α = U and S <: T imply ⟨S[α:=U] <: T[α:=U]⟩ diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CaptureBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CaptureBinding.java index 59baf7916..d9d49ee8a 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CaptureBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CaptureBinding.java @@ -147,6 +147,7 @@ public class CaptureBinding extends TypeVariableBinding { * e.g. given X<U, V extends X<U, V>>, capture(X<E,?>) = X<E,capture>, where capture extends X<E,capture> */ public void initializeBounds(Scope scope, ParameterizedTypeBinding capturedParameterizedType) { + boolean is18plus = scope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_8; TypeVariableBinding wildcardVariable = this.wildcard.typeVariable(); if (wildcardVariable == null) { // error resilience when capturing Zork<?> @@ -155,7 +156,9 @@ public class CaptureBinding extends TypeVariableBinding { switch (this.wildcard.boundKind) { case Wildcard.EXTENDS : // still need to capture bound supertype as well so as not to expose wildcards to the outside (111208) - TypeBinding capturedWildcardBound = originalWildcardBound.capture(scope, this.start, this.end); + TypeBinding capturedWildcardBound = is18plus + ? originalWildcardBound // as spec'd + : originalWildcardBound.capture(scope, this.start, this.end); // for compatibility with old behavior at 1.7- if (originalWildcardBound.isInterface()) { this.setSuperClass(scope.getJavaLangObject()); this.setSuperInterfaces(new ReferenceBinding[] { (ReferenceBinding) capturedWildcardBound }); @@ -207,7 +210,9 @@ public class CaptureBinding extends TypeVariableBinding { switch (this.wildcard.boundKind) { case Wildcard.EXTENDS : // still need to capture bound supertype as well so as not to expose wildcards to the outside (111208) - TypeBinding capturedWildcardBound = originalWildcardBound.capture(scope, this.start, this.end); + TypeBinding capturedWildcardBound = is18plus + ? originalWildcardBound // as spec'd + : originalWildcardBound.capture(scope, this.start, this.end); // for compatibility with old behavior at 1.7- //{ObjectTeams: is the bound a role type requiring wrapping? if (capturedWildcardBound.isRole()) capturedWildcardBound = RoleTypeCreator.maybeWrapUnqualifiedRoleType(scope, capturedWildcardBound.enclosingType(), capturedWildcardBound, scope.methodScope().referenceMethod(), scope.problemReporter()); diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java index 7ce55780b..f1a3933f4 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java @@ -108,6 +108,9 @@ class ConstraintTypeFormula extends ConstraintFormula { if (this.left.kind() != Binding.WILDCARD_TYPE) { return ConstraintTypeFormula.create(this.left, this.right, SAME, this.isSoft); } else { + // TODO: speculative addition: + if (this.right instanceof InferenceVariable) + return new TypeBound((InferenceVariable) this.right, this.left, SAME, this.isSoft); return FALSE; } } else { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java index ad486632d..4d5d04e01 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java @@ -15,6 +15,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Argument; @@ -202,11 +203,15 @@ public class ImplicitNullAnnotationVerifier { private void collectOverriddenMethods(MethodBinding original, char[] selector, int suggestedParameterLength, ReferenceBinding superType, Set ifcsSeen, List result) { - MethodBinding [] ifcMethods = superType.getMethods(selector, suggestedParameterLength); + MethodBinding [] ifcMethods = superType.unResolvedMethods(); int length = ifcMethods.length; boolean added = false; for (int i=0; i<length; i++) { MethodBinding currentMethod = ifcMethods[i]; + if (!CharOperation.equals(selector, currentMethod.selector)) + continue; + if (!currentMethod.doesParameterLengthMatch(suggestedParameterLength)) + continue; if (currentMethod.isStatic()) continue; if (MethodVerifier.doesMethodOverride(original, currentMethod, this.environment)) { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java index 6451ae5b9..e3995ce89 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java @@ -1880,7 +1880,7 @@ public TypeBinding getTypeFromTypeSignature(SignatureWrapper wrapper, TypeVariab // type must be a ReferenceBinding at this point, cannot be a BaseTypeBinding or ArrayTypeBinding ReferenceBinding actualType = (ReferenceBinding) type; if (actualType instanceof UnresolvedReferenceBinding) - if (CharOperation.indexOf('$', actualType.compoundName[actualType.compoundName.length - 1]) > 0) + if (actualType.depth() > 0) actualType = (ReferenceBinding) BinaryTypeBinding.resolveType(actualType, this, false /* no raw conversion */); // must resolve member types before asking for enclosingType ReferenceBinding actualEnclosing = actualType.enclosingType(); diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java index e8b11194c..a52c0376f 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java @@ -1922,4 +1922,8 @@ public boolean redeclaresPublicObjectMethod(Scope scope) { public boolean isVoidMethod() { return this.returnType == TypeBinding.VOID; } +public boolean doesParameterLengthMatch(int suggestedParameterLength) { + int len = this.parameters.length; + return len <= suggestedParameterLength || (isVarargs() && len == suggestedParameterLength + 1); +} } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java index 35727fec4..fd2072aa8 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodScope.java @@ -250,6 +250,7 @@ private void checkAndSetModifiersForMethod(final MethodBinding methodBinding) { problemReporter().illegalModifierForAnnotationMember((AbstractMethodDeclaration) this.referenceContext); else problemReporter().illegalModifierForInterfaceMethod((AbstractMethodDeclaration) this.referenceContext, isJDK18orGreater); + methodBinding.modifiers &= (expectedModifiers | ~ExtraCompilerModifiers.AccJustFlag); } return; } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/PackageBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/PackageBinding.java index 31928d696..41307d3d6 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/PackageBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/PackageBinding.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2016 IBM Corporation and others. + * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -207,20 +207,7 @@ public Binding getTypeOrPackage(char[] name) { if (packageBinding != null && packageBinding != LookupEnvironment.TheNotFoundPackage) { return packageBinding; } - - if (packageBinding == null) { // have not looked for it before - if ((packageBinding = findPackage(name)) != null) { - return packageBinding; - } - if (referenceBinding != null && referenceBinding != LookupEnvironment.TheNotFoundType) { - return referenceBinding; // found cached missing type - check if package conflict - } - addNotFoundPackage(name); - } - if (referenceBinding == null) { // have not looked for it before - //This call (to askForType) should be the last option to call, because the call is very expensive regarding performance - // (a search for secondary types may get triggered which requires to parse all classes of a package). if ((referenceBinding = this.environment.askForType(this, name)) != null) { if (referenceBinding.isNestedType()) { return new ProblemReferenceBinding(new char[][]{name}, referenceBinding, ProblemReasons.InternalNameProvided); @@ -233,6 +220,16 @@ public Binding getTypeOrPackage(char[] name) { addNotFoundType(name); } + if (packageBinding == null) { // have not looked for it before + if ((packageBinding = findPackage(name)) != null) { + return packageBinding; + } + if (referenceBinding != null && referenceBinding != LookupEnvironment.TheNotFoundType) { + return referenceBinding; // found cached missing type - check if package conflict + } + addNotFoundPackage(name); + } + return null; } public final boolean isViewedAsDeprecated() { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SignatureWrapper.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SignatureWrapper.java index c76163a02..a967cf282 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SignatureWrapper.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SignatureWrapper.java @@ -192,6 +192,15 @@ public class SignatureWrapper { return CharOperation.subarray(this.signature, this.start, this.signature.length); } public String toString() { + if (this.start >= 0 && this.start <= this.signature.length) { + return new String(CharOperation.subarray(this.signature, 0, this.start)) + " ^ " //$NON-NLS-1$ + + new String(CharOperation.subarray(this.signature, this.start, this.signature.length)); + } + return new String(this.signature) + " @ " + this.start; //$NON-NLS-1$ } + + public char charAtStart() { + return this.signature[this.start]; + } } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java index 3a8231a07..e059649a2 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java @@ -182,9 +182,9 @@ public class SourceTypeBinding extends ReferenceBinding { private int nullnessDefaultInitialized = 0; // 0: nothing; 1: type; 2: package private int lambdaOrdinal = 0; private ReferenceBinding containerAnnotationType = null; + + public ExternalAnnotationProvider externalAnnotationProvider; - public ExternalAnnotationProvider externalAnnotationProvider;
-
public SourceTypeBinding(char[][] compoundName, PackageBinding fPackage, ClassScope scope) { //{ObjectTeams: // share model from TypeDeclaration: super(scope.referenceContext.getModel()); @@ -2430,7 +2430,7 @@ public MethodBinding resolveTypesFor(MethodBinding method) { } public MethodBinding resolveTypesFor(MethodBinding method, boolean fromSynthetic) { // SH} - + if (!isPrototype()) return this.prototype.resolveTypesFor(method); diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedReferenceBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedReferenceBinding.java index 6cbccb6cd..6d95fe90f 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedReferenceBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedReferenceBinding.java @@ -75,8 +75,9 @@ public String debugName() { } public int depth() { // we don't yet have our enclosing types wired, but we know the nesting depth from our compoundName: + // (NOTE: this an upper bound, because class names may contain '$') int last = this.compoundName.length-1; - return CharOperation.occurencesOf('$', this.compoundName[last]); + return CharOperation.occurencesOf('$', this.compoundName[last], 1); // leading '$' must be part of the class name, so start at 1. } public boolean hasTypeBit(int bit) { // shouldn't happen since we are not called before analyseCode(), but play safe: diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredInitializer.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredInitializer.java index 09290e1b0..fdc72b2c9 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredInitializer.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredInitializer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -62,9 +62,11 @@ public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValu this.foundOpeningBrace = true; this.bracketBalance++; } - this.initializerBody = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue); + if (this.initializerBody == null) { + return this.initializerBody = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue); + } if (nestedBlockDeclaration.sourceEnd == 0) return this.initializerBody; - return this; + return this.initializerBody.add(nestedBlockDeclaration, bracketBalanceValue, true); } /* * Record a field declaration (act like inside method body) @@ -109,18 +111,26 @@ public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanc return this.parent.add(localDeclaration, bracketBalanceValue); } /* method body should have been created */ - Block block = new Block(0); - block.sourceStart = ((Initializer)this.fieldDeclaration).sourceStart; - RecoveredElement element = this.add(block, 1); - if (this.initializerBody != null) { - this.initializerBody.attachPendingModifiers( + if (this.initializerBody == null) { + Block block = new Block(0); + block.sourceStart = ((Initializer)this.fieldDeclaration).sourceStart; + RecoveredElement element = this.add(block, 1); + if (this.bracketBalance > 0){ + for (int i = 0; i < this.bracketBalance - 1; i++){ + element = element.add(new Block(0), 1); + } + this.bracketBalance = 1; + } + return element.add(localDeclaration, bracketBalanceValue); + } + this.initializerBody.attachPendingModifiers( this.pendingAnnotations, this.pendingAnnotationCount, this.pendingModifiers, this.pendingModifersSourceStart); - } resetPendingModifiers(); - return element.add(localDeclaration, bracketBalanceValue); + + return this.initializerBody.add(localDeclaration, bracketBalanceValue, true); } /* * Record a statement - regular method should have been created a block body diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredMethod.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredMethod.java index 7dd7b548a..7e79ea1c3 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredMethod.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/RecoveredMethod.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -99,7 +99,11 @@ public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValu this.bracketBalance++; } - this.methodBody = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue); + if (this.methodBody != null) { + this.methodBody.addBlockStatement(new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue)); + } else { + this.methodBody = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue); + } if (nestedBlockDeclaration.sourceEnd == 0) return this.methodBody; return this; } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index 827613aef..8e7d0973a 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -4,7 +4,7 @@ * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html - * + * * Contributors: * IBM Corporation - initial API and implementation * Benjamin Muskalla - Contribution for bug 239066 @@ -10090,7 +10090,11 @@ public void nullityMismatch(Expression expression, TypeBinding providedType, Typ nullityMismatchSpecdNullable(expression, requiredType, annotationName); return; } - nullityMismatchPotentiallyNull(expression, requiredType, annotationName); + if (expression instanceof ArrayReference && expression.resolvedType.isFreeTypeVariable()) { + nullityMismatchingTypeAnnotation(expression, providedType, requiredType, NullAnnotationMatching.NULL_ANNOTATIONS_MISMATCH); + return; + } + nullityMismatchPotentiallyNull(expression, requiredType, annotationName); return; } if (this.options.usesNullTypeAnnotations()) diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java index 96540d09d..a5a11f3e6 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/LineBreaksPreparator.java @@ -289,18 +289,14 @@ public class LineBreaksPreparator extends ASTVisitor { if (node.getBody() == null) return true; - if (node.isConstructor()) { - handleBracedCode(node.getBody(), null, this.options.brace_position_for_constructor_declaration, - this.options.indent_statements_compare_to_body, - this.options.insert_new_line_in_empty_method_body); - } else { - handleBracedCode(node.getBody(), null, this.options.brace_position_for_method_declaration, - this.options.indent_statements_compare_to_body, - this.options.insert_new_line_in_empty_method_body); - Token openBrace = this.tm.firstTokenIn(node.getBody(), TokenNameLBRACE); - if (openBrace.getLineBreaksAfter() > 0) // if not, these are empty braces - openBrace.putLineBreaksAfter(this.options.blank_lines_at_beginning_of_method_body + 1); - } + String bracePosition = node.isConstructor() ? this.options.brace_position_for_constructor_declaration + : this.options.brace_position_for_method_declaration; + handleBracedCode(node.getBody(), null, bracePosition, + this.options.indent_statements_compare_to_body, + this.options.insert_new_line_in_empty_method_body); + Token openBrace = this.tm.firstTokenIn(node.getBody(), TokenNameLBRACE); + if (openBrace.getLineBreaksAfter() > 0) // if not, these are empty braces + openBrace.putLineBreaksAfter(this.options.blank_lines_at_beginning_of_method_body + 1); return true; } @@ -448,10 +444,6 @@ public class LineBreaksPreparator extends ASTVisitor { @Override public boolean visit(NormalAnnotation node) { handleAnnotation(node); - - int lParen = this.tm.firstIndexAfter(node.getTypeName(), TokenNameLPAREN); - int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN); - handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_annotation); return true; } @@ -551,6 +543,12 @@ public class LineBreaksPreparator extends ASTVisitor { } if (breakAfter) this.tm.lastTokenIn(node, -1).breakAfter(); + + if (!(node instanceof MarkerAnnotation)) { + int lParen = this.tm.firstIndexAfter(node.getTypeName(), TokenNameLPAREN); + int rParen = this.tm.lastIndexIn(node, TokenNameRPAREN); + handleParenthesesPositions(lParen, rParen, this.options.parenthesis_positions_in_annotation); + } } @Override @@ -819,7 +817,7 @@ public class LineBreaksPreparator extends ASTVisitor { //$FALL-THROUGH$ case DefaultCodeFormatterConstants.SEPARATE_LINES: case DefaultCodeFormatterConstants.PRESERVE_POSITIONS: - boolean always = positionsSetting != DefaultCodeFormatterConstants.PRESERVE_POSITIONS; + boolean always = !positionsSetting.equals(DefaultCodeFormatterConstants.PRESERVE_POSITIONS); Token afterOpening = this.tm.get(openingParenIndex + 1); if (always || this.tm.countLineBreaksBetween(this.tm.get(openingParenIndex), afterOpening) > 0) { afterOpening.setWrapPolicy( diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java index 978380355..bd12cf787 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapExecutor.java @@ -122,6 +122,7 @@ public class WrapExecutor { */ public int analyzeLine(int startIndex, int indent) { Token startToken = WrapExecutor.this.tm.get(startIndex); + assert startToken.getLineBreaksBefore() > 0; this.counter = WrapExecutor.this.tm.toIndent(indent, startToken.isWrappable()); this.lineIndent = indent; this.firstPotentialWrap = -1; @@ -170,11 +171,11 @@ public class WrapExecutor { if (this.lineExceeded && this.firstPotentialWrap >= 0) { return false; } - if (!token.isNextLineOnWrap()) - token.setIndent(this.lineIndent); + token.setIndent(this.lineIndent); - boolean isLineEnd = getLineBreaksAfter() > 0 || getNext() == null; - assert !(token.isNextLineOnWrap() && !isLineEnd); + boolean isLineEnd = getLineBreaksAfter() > 0 || getNext() == null + || (getNext().isNextLineOnWrap() && WrapExecutor.this.tm + .get(WrapExecutor.this.tm.findFirstTokenInLine(index)).isWrappable()); return !isLineEnd; } diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java index f745dd8f2..d59106deb 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/linewrap/WrapPreparator.java @@ -586,13 +586,18 @@ public class WrapPreparator extends ASTVisitor { prepareElementsList(expressions, TokenNameCOMMA, TokenNameLBRACE); handleWrap(this.options.alignment_for_expressions_in_array_initializer, node); } + int openingBraceIndex = this.tm.firstIndexIn(node, TokenNameLBRACE); + Token openingBrace = this.tm.get(openingBraceIndex); + if (openingBrace.isNextLineOnWrap() && openingBrace.getWrapPolicy() == null && openingBraceIndex > 0) { + // add fake wrap policy to make sure the brace indentation is right + openingBrace.setWrapPolicy(new WrapPolicy(WrapMode.DISABLED, openingBraceIndex - 1, 0)); + } if (!this.options.join_wrapped_lines && !this.options.insert_new_line_before_closing_brace_in_array_initializer) { // if there is a line break before the closing brace, formatter should treat it as a valid wrap to preserve int closingBraceIndex = this.tm.lastIndexIn(node, TokenNameRBRACE); Token closingBrace = this.tm.get(closingBraceIndex); if (this.tm.countLineBreaksBetween(this.tm.get(closingBraceIndex - 1), closingBrace) == 1) { - int openingBraceIndex = this.tm.firstIndexIn(node, TokenNameLBRACE); closingBrace.setWrapPolicy(new WrapPolicy(WrapMode.WHERE_NECESSARY, openingBraceIndex, closingBraceIndex, 0, this.currentDepth, 1, true, false)); } @@ -899,7 +904,7 @@ public class WrapPreparator extends ASTVisitor { if (policy == null) return; - setTokenWrapPolicy(this.wrapIndexes.get(0), policy, true); + setTokenWrapPolicy(0, policy, true); boolean wrapPreceedingComments = !(parentNode instanceof InfixExpression) || !this.options.wrap_before_binary_operator; @@ -907,7 +912,7 @@ public class WrapPreparator extends ASTVisitor { penalty = this.wrapPenalties.size() > i ? this.wrapPenalties.get(i) : 1; if (penalty != policy.penaltyMultiplier || i == 1) policy = getWrapPolicy(wrappingOption, penalty, false, parentNode); - setTokenWrapPolicy(this.wrapIndexes.get(i), policy, wrapPreceedingComments); + setTokenWrapPolicy(i, policy, wrapPreceedingComments); } boolean forceWrap = (wrappingOption & Alignment.M_FORCE) != 0; @@ -939,7 +944,8 @@ public class WrapPreparator extends ASTVisitor { } } - private void setTokenWrapPolicy(int index, WrapPolicy policy, boolean wrapPreceedingComments) { + private void setTokenWrapPolicy(int wrapIndexesIndex, WrapPolicy policy, boolean wrapPreceedingComments) { + int index = this.wrapIndexes.get(wrapIndexesIndex); if (wrapPreceedingComments) { for (int i = index - 1; i >= 0; i--) { Token previous = this.tm.get(i); @@ -950,6 +956,7 @@ public class WrapPreparator extends ASTVisitor { if (previous.getLineBreaksBefore() > 0) previous.setWrapPolicy(policy); } + this.wrapIndexes.set(wrapIndexesIndex, index); } Token token = this.tm.get(index); @@ -972,6 +979,8 @@ public class WrapPreparator extends ASTVisitor { } else if (parentNode instanceof EnumDeclaration) { // special behavior for compatibility with legacy formatter extraIndent = ((wrappingOption & Alignment.M_INDENT_BY_ONE) != 0) ? 2 : 1; + if (!this.options.indent_body_declarations_compare_to_enum_declaration_header) + extraIndent--; isAlreadyWrapped = isFirst; } else if (parentNode instanceof IfStatement) { extraIndent = 1; diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IOpenable.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IOpenable.java index 070206105..515a8ffda 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IOpenable.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IOpenable.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2008 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.jdt.core; +import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; /** @@ -46,7 +47,7 @@ public interface IOpenable { * Closes this element and its buffer (if any). * Closing an element which is not open has no effect. * - * <p>Note: although {@link #close} is exposed in the API, clients are + * <p>Note: Although {@link #close} is exposed in the API, clients are * not expected to open and close elements - the Java model does this automatically * as elements are accessed. * @@ -114,6 +115,13 @@ boolean hasUnsavedChanges() throws JavaModelException; boolean isConsistent() throws JavaModelException; /** * Returns whether this openable is open. This is a handle-only method. + * + * <p>Note: This method doesn't tell whether an {@link IJavaProject}'s {@link IJavaProject#getProject() getProject()} is open. + * It is <b>not</b> equivalent to {@link IProject#isOpen()}!</p> + * + * <p>Note: Although {@link #isOpen} is exposed in the API, clients + * rarely have a need to rely on this internal state of the Java model.</p> + * @return true if this openable is open, false otherwise */ boolean isOpen(); @@ -142,7 +150,7 @@ void makeConsistent(IProgressMonitor progress) throws JavaModelException; * Opens this element and all parent elements that are not already open. * For compilation units, a buffer is opened on the contents of the underlying resource. * - * <p>Note: although {@link #open} is exposed in the API, clients are + * <p>Note: Although {@link #open} is exposed in the API, clients are * not expected to open and close elements - the Java model does this automatically * as elements are accessed. * diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IType.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IType.java index 5ee63a6ad..f2156427f 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IType.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IType.java @@ -36,6 +36,11 @@ import org.eclipse.core.runtime.IProgressMonitor; * <code>IMethod</code>, <code>IInitializer</code> and <code>IType</code>. * The children are listed in the order in which they appear in the source or class file. * </p> + * <p> + * Caveat: The {@link #getChildren() children} of a {@link #isBinary() binary} type include + * nested types. However, the {@link #getParent() parent} of such a nested binary type is + * <em>not</em> the enclosing type, but that nested type's {@link IClassFile}! + * </p> * * @noimplement This interface is not intended to be implemented by clients. */ diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java index 84bd065e9..5eef53310 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java @@ -157,6 +157,7 @@ import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.core.*; import org.eclipse.jdt.internal.core.builder.JavaBuilder; import org.eclipse.jdt.internal.core.builder.State; +import org.eclipse.jdt.internal.core.nd.indexer.Indexer; import org.eclipse.jdt.internal.core.util.MementoTokenizer; import org.eclipse.jdt.internal.core.util.Messages; import org.eclipse.jdt.internal.core.util.Util; @@ -5877,5 +5878,6 @@ public final class JavaCore extends Plugin { super.start(context); JavaModelManager.registerDebugOptionsListener(context); JavaModelManager.getJavaModelManager().startup(); + Indexer.getInstance().rescanAll(); } } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/CompilationParticipant.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/CompilationParticipant.java index c40c1392e..34ae1a2cc 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/CompilationParticipant.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/CompilationParticipant.java @@ -101,8 +101,11 @@ public void cleanStarting(IJavaProject project) { * given project should return <code>false</code> for that project. * </p><p> * Note: In {@link org.eclipse.jdt.core.WorkingCopyOwner#newWorkingCopy(String, org.eclipse.jdt.core.IClasspathEntry[], org.eclipse.core.runtime.IProgressMonitor) - * special cases}, the project may be closed and not exist. Participants typically return false for projects that are - * !{@link IJavaProject#isOpen()}. + * special cases}, the project may be closed and not exist. Participants typically return false when the + * underlying project is closed. I.e. when the following check returns false: + * <pre> + * javaProject.getProject().isOpen(); + * </pre> * </p> * @param project the project to participate in * @return whether this participant is active for a given project diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java index 6d15fb255..dad83972d 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java @@ -294,7 +294,7 @@ public final class ExternalAnnotationUtil { newContent.append('\n'); continue; } - if (!Character.isJavaIdentifierStart(line.charAt(0))) { + if (!Character.isJavaIdentifierStart(line.charAt(0)) && line.charAt(0) != '<') { newContent.append(line).append('\n'); continue; } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java index f895be97f..978704c96 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClassFile.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClassFile.java index 000c46382..9871f47cd 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClassFile.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClassFile.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -37,11 +37,15 @@ import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.*; import org.eclipse.jdt.core.compiler.IProblem; -import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationDecorator; +import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider; import org.eclipse.jdt.internal.compiler.env.IBinaryType; -import org.eclipse.jdt.internal.compiler.env.IDependent; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; +import org.eclipse.jdt.internal.core.nd.java.JavaNames; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeDescriptor; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; import org.eclipse.jdt.internal.core.util.MementoTokenizer; import org.eclipse.jdt.internal.core.util.Util; import org.eclipse.objectteams.otdt.core.OTModelManager; @@ -93,8 +97,9 @@ public ICompilationUnit becomeWorkingCopy(IProblemRequestor problemRequestor, Wo * @see Openable * @see Signature */ +@Override protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException { - IBinaryType typeInfo = getBinaryTypeInfo((IFile) underlyingResource); + IBinaryType typeInfo = getBinaryTypeInfo(); if (typeInfo == null) { // The structure of a class file is unknown if a class file format errors occurred //during the creation of the diet class file representative of this ClassFile. @@ -124,6 +129,7 @@ protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, * @see ICodeAssist#codeComplete(int, ICompletionRequestor) * @deprecated */ +@Deprecated public void codeComplete(int offset, ICompletionRequestor requestor) throws JavaModelException { codeComplete(offset, requestor, DefaultWorkingCopyOwner.PRIMARY); } @@ -131,6 +137,7 @@ public void codeComplete(int offset, ICompletionRequestor requestor) throws Java * @see ICodeAssist#codeComplete(int, ICompletionRequestor, WorkingCopyOwner) * @deprecated */ +@Deprecated public void codeComplete(int offset, ICompletionRequestor requestor, WorkingCopyOwner owner) throws JavaModelException { if (requestor == null) { throw new IllegalArgumentException("Completion requestor cannot be null"); //$NON-NLS-1$ @@ -197,9 +204,11 @@ public IJavaElement[] codeSelect(int offset, int length, WorkingCopyOwner owner) /** * Returns a new element info for this element. */ +@Override protected Object createElementInfo() { return new ClassFileInfo(); } +@Override public boolean equals(Object o) { if (!(o instanceof ClassFile)) return false; ClassFile other = (ClassFile) o; @@ -227,7 +236,7 @@ public boolean existsUsingJarTypeCache() { return false; } try { - info = getJarBinaryTypeInfo((PackageFragment) getParent(), true/*fully initialize so as to not keep a reference to the byte array*/); + info = getJarBinaryTypeInfo(); } catch (CoreException e) { // leave info null } catch (IOException e) { @@ -277,6 +286,7 @@ public IType findPrimaryType() { } return null; } +@Override public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelException { return getType().getAttachedJavadoc(monitor); } @@ -292,40 +302,26 @@ public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelExcep * @exception JavaModelException when the IFile resource or JAR is not available * or when this class file is not present in the JAR */ -public IBinaryType getBinaryTypeInfo(IFile file) throws JavaModelException { - return getBinaryTypeInfo(file, true/*fully initialize so as to not keep a reference to the byte array*/); -} -public IBinaryType getBinaryTypeInfo(IFile file, boolean fullyInitialize) throws JavaModelException { - JavaElement pkg = (JavaElement) getParent(); - if (pkg instanceof JarPackageFragment) { - try { - IBinaryType info = getJarBinaryTypeInfo((PackageFragment) pkg, fullyInitialize); - if (info == null) { - throw newNotPresentException(); - } - return info; - } catch (ClassFormatException cfe) { - //the structure remains unknown - if (JavaCore.getPlugin().isDebugging()) { - cfe.printStackTrace(System.err); - } - return null; - } catch (IOException ioe) { - throw new JavaModelException(ioe, IJavaModelStatusConstants.IO_EXCEPTION); - } catch (CoreException e) { - if (e instanceof JavaModelException) { - throw (JavaModelException)e; - } else { - throw new JavaModelException(e); - } +public IBinaryType getBinaryTypeInfo() throws JavaModelException { + try { + IBinaryType info = getJarBinaryTypeInfo(); + if (info == null) { + throw newNotPresentException(); } - } else { - byte[] contents = Util.getResourceContentsAsByteArray(file); - try { - return new ClassFileReader(contents, file.getFullPath().toString().toCharArray(), fullyInitialize); - } catch (ClassFormatException cfe) { - //the structure remains unknown - return null; + return info; + } catch (ClassFormatException cfe) { + //the structure remains unknown + if (JavaCore.getPlugin().isDebugging()) { + cfe.printStackTrace(System.err); + } + return null; + } catch (IOException ioe) { + throw new JavaModelException(ioe, IJavaModelStatusConstants.IO_EXCEPTION); + } catch (CoreException e) { + if (e instanceof JavaModelException) { + throw (JavaModelException)e; + } else { + throw new JavaModelException(e); } } } @@ -359,44 +355,58 @@ public byte[] getBytes() throws JavaModelException { return Util.getResourceContentsAsByteArray(file); } } -private IBinaryType getJarBinaryTypeInfo(PackageFragment pkg, boolean fullyInitialize) throws CoreException, IOException, ClassFormatException { - JarPackageFragmentRoot root = (JarPackageFragmentRoot) pkg.getParent(); - ZipFile zip = null; - ZipFile annotationZip = null; - try { - zip = root.getJar(); - String entryName = Util.concatWith(pkg.names, getElementName(), '/'); - ZipEntry ze = zip.getEntry(entryName); - if (ze != null) { - byte contents[] = org.eclipse.jdt.internal.compiler.util.Util.getZipEntryByteContent(ze, zip); - String fileName = root.getHandleIdentifier() + IDependent.JAR_FILE_ENTRY_SEPARATOR + entryName; - ClassFileReader reader = new ClassFileReader(contents, fileName.toCharArray(), fullyInitialize); - if (root.getKind() == IPackageFragmentRoot.K_BINARY) { - JavaProject javaProject = (JavaProject) getAncestor(IJavaElement.JAVA_PROJECT); - IClasspathEntry entry = javaProject.getClasspathEntryFor(getPath()); - if (entry != null) { - IProject project = javaProject.getProject(); - IPath externalAnnotationPath = ClasspathEntry.getExternalAnnotationPath(entry, project, false); // unresolved for use in ExternalAnnotationTracker - if (externalAnnotationPath != null) { - setupExternalAnnotationProvider(project, externalAnnotationPath, annotationZip, reader, - entryName.substring(0, entryName.length() - SuffixConstants.SUFFIX_CLASS.length)); - } else if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { - reader.markAsFromSource(); - } + +public String getName() { + return this.name; +} + +private IBinaryType getJarBinaryTypeInfo() throws CoreException, IOException, ClassFormatException { + BinaryTypeDescriptor descriptor = BinaryTypeFactory.createDescriptor(this); + + if (descriptor == null) { + return null; + } + + IBinaryType result = BinaryTypeFactory.readType(descriptor, null); + + if (result == null) { + return null; + } + + // TODO(sxenos): setup the external annotation provider if the IBinaryType came from the index + // TODO(sxenos): the old code always passed null as the third argument to setupExternalAnnotationProvider, + // but this looks like a bug. I've preserved it for now but we need to figure out what was supposed to go + // there. + PackageFragment pkg = (PackageFragment) getParent(); + IJavaElement grandparent = pkg.getParent(); + if (grandparent instanceof JarPackageFragmentRoot) { + JarPackageFragmentRoot root = (JarPackageFragmentRoot) grandparent; + + if (root.getKind() == IPackageFragmentRoot.K_BINARY) { + JavaProject javaProject = (JavaProject) getAncestor(IJavaElement.JAVA_PROJECT); + IClasspathEntry entry = javaProject.getClasspathEntryFor(getPath()); + if (entry != null) { + String entryName = new String(CharArrayUtils.concat( + JavaNames.fieldDescriptorToBinaryName(descriptor.fieldDescriptor), SuffixConstants.SUFFIX_CLASS)); + IProject project = javaProject.getProject(); + IPath externalAnnotationPath = ClasspathEntry.getExternalAnnotationPath(entry, project, false); // unresolved for use in ExternalAnnotationTracker + if (externalAnnotationPath != null) { + result = setupExternalAnnotationProvider(project, externalAnnotationPath, null, result, + entryName.substring(0, entryName.length() - SuffixConstants.SUFFIX_CLASS.length)); + } else if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { + result = new ExternalAnnotationDecorator(result, true); } } - return reader; } - } finally { - JavaModelManager.getJavaModelManager().closeZipFile(zip); - JavaModelManager.getJavaModelManager().closeZipFile(annotationZip); } - return null; + + return result; } -private void setupExternalAnnotationProvider(IProject project, final IPath externalAnnotationPath, - ZipFile annotationZip, ClassFileReader reader, final String typeName) +private IBinaryType setupExternalAnnotationProvider(IProject project, final IPath externalAnnotationPath, + ZipFile annotationZip, IBinaryType reader, final String typeName) { + IBinaryType result = reader; // try resolve path within the workspace: IWorkspaceRoot root = project.getWorkspace().getRoot(); IResource resource; @@ -410,26 +420,32 @@ private void setupExternalAnnotationProvider(IProject project, final IPath exter String resolvedPath; if (resource.exists()) { if (resource.isVirtual()) { - Util.log(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, + Util.log(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, "Virtual resource "+externalAnnotationPath+" cannot be used as annotationpath for project "+project.getName())); //$NON-NLS-1$ //$NON-NLS-2$ - return; + return reader; } resolvedPath = resource.getLocation().toString(); // workspace lookup succeeded -> resolve it } else { resolvedPath = externalAnnotationPath.toString(); // not in workspace, use as is } try { - annotationZip = reader.setExternalAnnotationProvider(resolvedPath, typeName, annotationZip, new ClassFileReader.ZipFileProducer() { - @Override public ZipFile produce() throws IOException { - try { - return JavaModelManager.getJavaModelManager().getZipFile(externalAnnotationPath); // use (absolute, but) unresolved path here - } catch (CoreException e) { - throw new IOException("Failed to read annotation file for "+typeName+" from "+externalAnnotationPath.toString(), e); //$NON-NLS-1$ //$NON-NLS-2$ - } - }}); + if (annotationZip == null) { + annotationZip = ExternalAnnotationDecorator.getAnnotationZipFile(resolvedPath, new ExternalAnnotationDecorator.ZipFileProducer() { + @Override public ZipFile produce() throws IOException { + try { + return JavaModelManager.getJavaModelManager().getZipFile(externalAnnotationPath); // use (absolute, but) unresolved path here + } catch (CoreException e) { + throw new IOException("Failed to read annotation file for "+typeName+" from "+externalAnnotationPath.toString(), e); //$NON-NLS-1$ //$NON-NLS-2$ + } + }}); + } + + ExternalAnnotationProvider annotationProvider = ExternalAnnotationDecorator + .externalAnnotationProvider(resolvedPath, typeName, annotationZip); + result = new ExternalAnnotationDecorator(reader, annotationProvider); } catch (IOException e) { Util.log(e); - return; + return result; } if (annotationZip == null) { // Additional change listening for individual types only when annotations are in individual files. @@ -437,6 +453,7 @@ private void setupExternalAnnotationProvider(IProject project, final IPath exter this.externalAnnotationBase = externalAnnotationPath; // remember so we can unregister later ExternalAnnotationTracker.registerClassFile(externalAnnotationPath, new Path(typeName), this); } + return result; } void closeAndRemoveFromJarTypeCache() throws JavaModelException { super.close(); @@ -451,6 +468,7 @@ public void close() throws JavaModelException { } super.close(); } +@Override public IBuffer getBuffer() throws JavaModelException { IStatus status = validateClassFile(); if (status.isOK()) { @@ -468,6 +486,7 @@ public IBuffer getBuffer() throws JavaModelException { /** * @see IMember */ +@Override public IClassFile getClassFile() { return this; } @@ -483,6 +502,7 @@ public ITypeRoot getTypeRoot() { * * @see IJavaElement */ +@Override public IResource getCorrespondingResource() throws JavaModelException { IPackageFragmentRoot root= (IPackageFragmentRoot)getParent().getParent(); if (root.isArchive()) { @@ -554,6 +574,7 @@ public IJavaElement getElementAtConsideringSibling(int position) throws JavaMode return null; } } +@Override public String getElementName() { return this.name + SuffixConstants.SUFFIX_STRING_class; } @@ -566,6 +587,7 @@ public int getElementType() { /* * @see JavaElement */ +@Override public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner) { switch (token.charAt(0)) { case JEM_TYPE: @@ -579,6 +601,7 @@ public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, /** * @see JavaElement#getHandleMemento() */ +@Override protected char getHandleMementoDelimiter() { return JavaElement.JEM_CLASSFILE; } @@ -596,6 +619,7 @@ public IPath getPath() { /* * @see IJavaElement */ +@Override public IResource resource(PackageFragmentRoot root) { return ((IContainer) ((Openable) this.parent).resource(root)).getFile(new Path(getElementName())); } @@ -707,15 +731,18 @@ public ICompilationUnit getWorkingCopy(WorkingCopyOwner owner, IProgressMonitor * @see IClassFile * @deprecated */ +@Deprecated public IJavaElement getWorkingCopy(IProgressMonitor monitor, org.eclipse.jdt.core.IBufferFactory factory) throws JavaModelException { return getWorkingCopy(BufferFactoryWrapper.create(factory), monitor); } /** * @see Openable */ +@Override protected boolean hasBuffer() { return true; } +@Override public int hashCode() { return Util.combineHashCodes(this.name.hashCode(), this.parent.hashCode()); } @@ -734,6 +761,7 @@ public boolean isInterface() throws JavaModelException { /** * Returns true - class files are always read only. */ +@Override public boolean isReadOnly() { return true; } @@ -756,6 +784,7 @@ private IStatus validateClassFile() { * * @see Openable */ +@Override protected IBuffer openBuffer(IProgressMonitor pm, Object info) throws JavaModelException { // Check the cache for the top-level type first IType outerMostEnclosingType = getOuterMostEnclosingType(); @@ -896,6 +925,7 @@ public static char[] translatedName(char[] name) { * @see ICodeAssist#codeComplete(int, ICodeCompletionRequestor) * @deprecated - should use codeComplete(int, ICompletionRequestor) instead */ +@Deprecated public void codeComplete(int offset, final org.eclipse.jdt.core.ICodeCompletionRequestor requestor) throws JavaModelException { if (requestor == null){ @@ -951,6 +981,7 @@ public void codeComplete(int offset, final org.eclipse.jdt.core.ICodeCompletionR }); } +@Override protected IStatus validateExistence(IResource underlyingResource) { // check whether the class file can be opened IStatus status = validateClassFile(); diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessingState.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessingState.java index 1854aeea6..a8e3b27d0 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessingState.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessingState.java @@ -24,13 +24,16 @@ import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; import org.eclipse.jdt.core.*; import org.eclipse.jdt.internal.core.JavaModelManager.PerProjectInfo; +import org.eclipse.jdt.internal.core.nd.indexer.Indexer; +import org.eclipse.jdt.internal.core.nd.indexer.IndexerEvent; +import org.eclipse.jdt.internal.core.nd.java.JavaIndex; import org.eclipse.jdt.internal.core.util.Util; /** * Keep the global states used during Java element delta processing. */ @SuppressWarnings({ "rawtypes", "unchecked" }) -public class DeltaProcessingState implements IResourceChangeListener { +public class DeltaProcessingState implements IResourceChangeListener, Indexer.Listener { /* * Collection of listeners for Java element deltas @@ -643,4 +646,15 @@ public class DeltaProcessingState implements IResourceChangeListener { } } + @Override + public void consume(IndexerEvent event) { + if (JavaIndex.isEnabled()) { + DeltaProcessor processor = getDeltaProcessor(); + JavaElementDelta delta = (JavaElementDelta) event.getDelta(); + delta.ignoreFromTests = true; + processor.notifyAndFire(delta); + this.deltaProcessors.set(null); + } + } + } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java index 18f5b752a..0e2c59896 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java @@ -502,8 +502,24 @@ public class DeltaProcessor { break; case IResource.FOLDER: - if (delta.getKind() == IResourceDelta.CHANGED) { // look for .jar file change to update classpath - children = delta.getAffectedChildren(); + switch (delta.getKind()) { + case IResourceDelta.ADDED: + case IResourceDelta.REMOVED: + // Close the containing package fragment root to reset its cached children. + // See http://bugs.eclipse.org/500714 + IPackageFragmentRoot root = findContainingPackageFragmentRoot(resource); + if (root != null && root.isOpen()) { + try { + root.close(); + } catch (JavaModelException e) { + Util.log(e); + } + } + break; + + case IResourceDelta.CHANGED: // look for .jar file change to update classpath + children = delta.getAffectedChildren(); + break; } break; case IResource.FILE : @@ -548,6 +564,27 @@ public class DeltaProcessor { } } + private IPackageFragmentRoot findContainingPackageFragmentRoot(IResource resource) { + IProject project = resource.getProject(); + if (JavaProject.hasJavaNature(project)) { + IJavaProject javaProject = JavaCore.create(project); + try { + IPath path = resource.getProjectRelativePath(); + IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots(); + for (IPackageFragmentRoot root : roots) { + IResource rootResource = root.getUnderlyingResource(); + if (rootResource != null && !resource.equals(rootResource) && + rootResource.getProjectRelativePath().isPrefixOf(path)) { + return root; + } + } + } catch (JavaModelException e) { + Util.log(e); + } + } + return null; + } + private void checkExternalFolderChange(IProject project, JavaProject javaProject) { ClasspathChange change = this.state.getClasspathChange(project); this.state.addExternalFolderChange(javaProject, change == null ? null : change.oldResolvedClasspath); @@ -1020,6 +1057,9 @@ public class DeltaProcessor { if (VERBOSE){ System.out.println("- External JAR CHANGED, affecting root: "+root.getElementName()); //$NON-NLS-1$ } + // TODO(sxenos): this is causing each change event for an external jar file to be fired twice. + // We need to preserve the clearing of cached information in the jar but defer the actual firing of + // the event until after the indexer has processed the jar. contentChanged(root); deltaContainsModifiedJar = true; hasDelta = true; @@ -1908,7 +1948,7 @@ public class DeltaProcessor { * caches and their dependents */ public void resetProjectCaches() { - if (this.projectCachesToReset.size() == 0) + if (this.projectCachesToReset.isEmpty()) return; JavaModelManager.getJavaModelManager().resetJarTypeCache(); @@ -2064,14 +2104,7 @@ public class DeltaProcessor { this.sourceElementParserCache = null; // don't hold onto parser longer than necessary startDeltas(); } - IElementChangedListener[] listeners; - int listenerCount; - synchronized (this.state) { - listeners = this.state.elementChangedListeners; - listenerCount = this.state.elementChangedListenerCount; - } - notifyTypeHierarchies(listeners, listenerCount); - fire(null, ElementChangedEvent.POST_CHANGE); + notifyAndFire(null); } finally { // workaround for bug 15168 circular errors not reported this.state.resetOldJavaProjectNames(); @@ -2180,6 +2213,17 @@ public class DeltaProcessor { } } + public void notifyAndFire(IJavaElementDelta delta) { + IElementChangedListener[] listeners; + int listenerCount; + synchronized (this.state) { + listeners = this.state.elementChangedListeners; + listenerCount = this.state.elementChangedListenerCount; + } + notifyTypeHierarchies(listeners, listenerCount); + fire(delta, ElementChangedEvent.POST_CHANGE); + } + /* * Returns the root info for the given path. Look in the old roots table if kind is REMOVED. */ diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaElementDelta.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaElementDelta.java index a7beb47bc..86abfafba 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaElementDelta.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaElementDelta.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -82,6 +82,8 @@ public class JavaElementDelta extends SimpleDelta implements IJavaElementDelta { */ Map<Key, Integer> childIndex; + public boolean ignoreFromTests = false; + /** * The delta key */ diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelCache.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelCache.java index c2ad7c360..5c6ca12da 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelCache.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelCache.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -28,6 +28,7 @@ import org.eclipse.objectteams.otdt.internal.core.OTType; @SuppressWarnings({"rawtypes", "unchecked"}) public class JavaModelCache { public static boolean VERBOSE = false; + public static boolean DEBUG_CACHE_INSERTIONS = false; public static final int DEFAULT_PROJECT_SIZE = 5; // average 25552 bytes per project. public static final int DEFAULT_ROOT_SIZE = 50; // average 2590 bytes per root -> maximum size : 25900*BASE_VALUE bytes @@ -224,6 +225,9 @@ protected Object peekAtInfo(IJavaElement element) { * Remember the info for the element. */ protected void putInfo(IJavaElement element, Object info) { + if (DEBUG_CACHE_INSERTIONS) { + System.out.println(Thread.currentThread() + " cache putInfo (" + getElementType(element) + " " + element.toString() + ", " + info + ")"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ + } switch (element.getElementType()) { case IJavaElement.JAVA_MODEL: this.modelInfo = info; @@ -248,10 +252,39 @@ protected void putInfo(IJavaElement element, Object info) { this.childrenCache.put(element, info); } } + +public static String getElementType(IJavaElement element) { + String elementType; + switch (element.getElementType()) { + case IJavaElement.JAVA_PROJECT: + elementType = "project"; //$NON-NLS-1$ + break; + case IJavaElement.PACKAGE_FRAGMENT_ROOT: + elementType = "root"; //$NON-NLS-1$ + break; + case IJavaElement.PACKAGE_FRAGMENT: + elementType = "package"; //$NON-NLS-1$ + break; + case IJavaElement.CLASS_FILE: + elementType = "class file"; //$NON-NLS-1$ + break; + case IJavaElement.COMPILATION_UNIT: + elementType = "compilation unit"; //$NON-NLS-1$ + break; + default: + elementType = "element"; //$NON-NLS-1$ + } + return elementType; +} + /** * Removes the info of the element from the cache. */ protected void removeInfo(JavaElement element) { + if (DEBUG_CACHE_INSERTIONS) { + String elementToString = element.toString(); + System.out.println(Thread.currentThread() + " cache removeInfo " + getElementType(element) + " " + elementToString); //$NON-NLS-1$//$NON-NLS-2$ + } switch (element.getElementType()) { case IJavaElement.JAVA_MODEL: this.modelInfo = null; diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java index cdb4157ef..8b4feaa1a 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java @@ -130,6 +130,8 @@ import org.eclipse.jdt.internal.core.builder.JavaBuilder; import org.eclipse.jdt.internal.core.dom.SourceRangeVerifier; import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEventStore; import org.eclipse.jdt.internal.core.hierarchy.TypeHierarchy; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.indexer.Indexer; import org.eclipse.jdt.internal.core.search.AbstractSearchScope; import org.eclipse.jdt.internal.core.search.BasicSearchEngine; import org.eclipse.jdt.internal.core.search.IRestrictedAccessTypeRequestor; @@ -176,6 +178,14 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis private static final String EXTERNAL_FILES_CACHE = "externalFilesCache"; //$NON-NLS-1$ private static final String ASSUMED_EXTERNAL_FILES_CACHE = "assumedExternalFilesCache"; //$NON-NLS-1$ + public static enum ArchiveValidity { + BAD_FORMAT, UNABLE_TO_READ, VALID; + + public boolean isValid() { + return this == VALID; + } + } + /** * Define a zip cache object. */ @@ -333,8 +343,11 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis private static final String INDEX_MANAGER_DEBUG = JavaCore.PLUGIN_ID + "/debug/indexmanager" ; //$NON-NLS-1$ private static final String INDEX_MANAGER_ADVANCED_DEBUG = JavaCore.PLUGIN_ID + "/debug/indexmanager/advanced" ; //$NON-NLS-1$ private static final String COMPILER_DEBUG = JavaCore.PLUGIN_ID + "/debug/compiler" ; //$NON-NLS-1$ + private static final String JAVAMODEL_CLASSPATH = JavaCore.PLUGIN_ID + "/debug/javamodel/classpath" ; //$NON-NLS-1$ private static final String JAVAMODEL_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel" ; //$NON-NLS-1$ + private static final String JAVAMODEL_INVALID_ARCHIVES = JavaCore.PLUGIN_ID + "/debug/javamodel/invalid_archives" ; //$NON-NLS-1$ private static final String JAVAMODELCACHE_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel/cache" ; //$NON-NLS-1$ + private static final String JAVAMODELCACHE_INSERTIONS_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel/insertions" ; //$NON-NLS-1$ private static final String CP_RESOLVE_DEBUG = JavaCore.PLUGIN_ID + "/debug/cpresolution" ; //$NON-NLS-1$ private static final String CP_RESOLVE_ADVANCED_DEBUG = JavaCore.PLUGIN_ID + "/debug/cpresolution/advanced" ; //$NON-NLS-1$ private static final String CP_RESOLVE_FAILURE_DEBUG = JavaCore.PLUGIN_ID + "/debug/cpresolution/failure" ; //$NON-NLS-1$ @@ -354,6 +367,12 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis private static final String SEARCH_DEBUG = JavaCore.PLUGIN_ID + "/debug/search" ; //$NON-NLS-1$ private static final String SOURCE_MAPPER_DEBUG_VERBOSE = JavaCore.PLUGIN_ID + "/debug/sourcemapper" ; //$NON-NLS-1$ private static final String FORMATTER_DEBUG = JavaCore.PLUGIN_ID + "/debug/formatter" ; //$NON-NLS-1$ + private static final String INDEX_INDEXER_DEBUG = JavaCore.PLUGIN_ID + "/debug/index/indexer" ; //$NON-NLS-1$ + private static final String INDEX_INDEXER_INSERTIONS = JavaCore.PLUGIN_ID + "/debug/index/insertions" ; //$NON-NLS-1$ + private static final String INDEX_INDEXER_SELFTEST = JavaCore.PLUGIN_ID + "/debug/index/selftest" ; //$NON-NLS-1$ + private static final String INDEX_LOCKS_DEBUG = JavaCore.PLUGIN_ID + "/debug/index/locks" ; //$NON-NLS-1$ + private static final String INDEX_INDEXER_SPACE = JavaCore.PLUGIN_ID + "/debug/index/space" ; //$NON-NLS-1$ + private static final String INDEX_INDEXER_TIMING = JavaCore.PLUGIN_ID + "/debug/index/timing" ; //$NON-NLS-1$ public static final String COMPLETION_PERF = JavaCore.PLUGIN_ID + "/perf/completion" ; //$NON-NLS-1$ public static final String SELECTION_PERF = JavaCore.PLUGIN_ID + "/perf/selection" ; //$NON-NLS-1$ @@ -1290,6 +1309,16 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis } private ClasspathChange setClasspath(IClasspathEntry[] newRawClasspath, IClasspathEntry[] referencedEntries, IPath newOutputLocation, IJavaModelStatus newRawClasspathStatus, IClasspathEntry[] newResolvedClasspath, Map newRootPathToRawEntries, Map newRootPathToResolvedEntries, IJavaModelStatus newUnresolvedEntryStatus, boolean addClasspathChange) { + if (DEBUG_CLASSPATH) { + System.out.println("Setting resolved classpath for " + this.project.getFullPath()); //$NON-NLS-1$ + if (newResolvedClasspath == null) { + System.out.println("New classpath = null"); //$NON-NLS-1$ + } else { + for (IClasspathEntry next : newResolvedClasspath) { + System.out.println(" " + next); //$NON-NLS-1$ + } + } + } ClasspathChange classpathChange = addClasspathChange ? addClasspathChange() : null; if (referencedEntries != null) this.referencedEntries = referencedEntries; @@ -1510,6 +1539,8 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis } public static boolean VERBOSE = false; + public static boolean DEBUG_CLASSPATH = false; + public static boolean DEBUG_INVALID_ARCHIVES = false; public static boolean CP_RESOLVE_VERBOSE = false; public static boolean CP_RESOLVE_VERBOSE_ADVANCED = false; public static boolean CP_RESOLVE_VERBOSE_FAILURE = false; @@ -1531,10 +1562,29 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis // The amount of time from when an invalid archive is first sensed until that state is considered stale. private static long INVALID_ARCHIVE_TTL_MILLISECONDS = 2 * 60 * 1000; + private static class InvalidArchiveInfo { + /** + * Time at which this entry will be removed from the invalid archive list. + */ + final long evictionTimestamp; + + /** + * Reason the entry was added to the invalid archive list. + */ + final ArchiveValidity reason; + + InvalidArchiveInfo(long evictionTimestamp, ArchiveValidity reason) { + this.evictionTimestamp = evictionTimestamp; + this.reason = reason; + } + } + /* * A map of IPaths for jars that are known to be invalid (such as not being in a valid/known format), to an eviction timestamp. + * Synchronize on invalidArchivesMutex before accessing. */ - private Map<IPath, Long> invalidArchives; + private final Map<IPath, InvalidArchiveInfo> invalidArchives = new HashMap<IPath, InvalidArchiveInfo>(); + private final Object invalidArchivesMutex = new Object(); /* * A set of IPaths for files that are known to be external to the workspace. @@ -1698,12 +1748,13 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis this.nonChainingJars.add(path); } - public void addInvalidArchive(IPath path) { - // unlikely to be null - if (this.invalidArchives == null) { - this.invalidArchives = Collections.synchronizedMap(new HashMap()); + public void addInvalidArchive(IPath path, ArchiveValidity reason) { + if (DEBUG_INVALID_ARCHIVES) { + System.out.println("Invalid JAR cache: adding " + path + ", reason: " + reason); //$NON-NLS-1$//$NON-NLS-2$ + } + synchronized (this.invalidArchivesMutex) { + this.invalidArchives.put(path, new InvalidArchiveInfo(System.currentTimeMillis() + INVALID_ARCHIVE_TTL_MILLISECONDS, reason)); } - this.invalidArchives.put(path, System.currentTimeMillis() + INVALID_ARCHIVE_TTL_MILLISECONDS); } /** @@ -1773,8 +1824,11 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis TypeHierarchy.DEBUG = debug && options.getBooleanOption(HIERARCHY_DEBUG, false); JobManager.VERBOSE = debug && options.getBooleanOption(INDEX_MANAGER_DEBUG, false); IndexManager.DEBUG = debug && options.getBooleanOption(INDEX_MANAGER_ADVANCED_DEBUG, false); + JavaModelManager.DEBUG_CLASSPATH = debug && options.getBooleanOption(JAVAMODEL_CLASSPATH, false); + JavaModelManager.DEBUG_INVALID_ARCHIVES = debug && options.getBooleanOption(JAVAMODEL_INVALID_ARCHIVES, false); JavaModelManager.VERBOSE = debug && options.getBooleanOption(JAVAMODEL_DEBUG, false); JavaModelCache.VERBOSE = debug && options.getBooleanOption(JAVAMODELCACHE_DEBUG, false); + JavaModelCache.DEBUG_CACHE_INSERTIONS = debug && options.getBooleanOption(JAVAMODELCACHE_INSERTIONS_DEBUG, false); JavaModelOperation.POST_ACTION_VERBOSE = debug && options.getBooleanOption(POST_ACTION_DEBUG, false); NameLookup.VERBOSE = debug && options.getBooleanOption(RESOLUTION_DEBUG, false); BasicSearchEngine.VERBOSE = debug && options.getBooleanOption(SEARCH_DEBUG, false); @@ -1782,6 +1836,12 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis JavaModelManager.ZIP_ACCESS_VERBOSE = debug && options.getBooleanOption(ZIP_ACCESS_DEBUG, false); SourceMapper.VERBOSE = debug && options.getBooleanOption(SOURCE_MAPPER_DEBUG_VERBOSE, false); DefaultCodeFormatter.DEBUG = debug && options.getBooleanOption(FORMATTER_DEBUG, false); + Indexer.DEBUG = debug && options.getBooleanOption(INDEX_INDEXER_DEBUG, false); + Indexer.DEBUG_INSERTIONS = debug && options.getBooleanOption(INDEX_INDEXER_INSERTIONS, false); + Indexer.DEBUG_ALLOCATIONS = debug && options.getBooleanOption(INDEX_INDEXER_SPACE, false); + Indexer.DEBUG_TIMING = debug && options.getBooleanOption(INDEX_INDEXER_TIMING, false); + Indexer.DEBUG_SELFTEST = debug && options.getBooleanOption(INDEX_INDEXER_SELFTEST, false); + Nd.sDEBUG_LOCKS = debug && options.getBooleanOption(INDEX_LOCKS_DEBUG, false); // configure performance options if(PerformanceStats.ENABLED) { @@ -2702,9 +2762,7 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis } public void verifyArchiveContent(IPath path) throws CoreException { - if (isInvalidArchive(path)) { - throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, new ZipException())); - } + throwExceptionIfArchiveInvalid(path); ZipFile file = getZipFile(path); closeZipFile(file); } @@ -2724,16 +2782,47 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis return getZipFile(path, true); } + /** + * For use in the JDT unit tests only. Used for testing error handling. Causes an + * {@link IOException} to be thrown in {@link #getZipFile} whenever it attempts to + * read a zip file. + * + * @noreference This field is not intended to be referenced by clients. + */ + public static boolean throwIoExceptionsInGetZipFile = false; + private ZipFile getZipFile(IPath path, boolean checkInvalidArchiveCache) throws CoreException { - if (checkInvalidArchiveCache && isInvalidArchive(path)) - throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, new ZipException())); - + if (checkInvalidArchiveCache) { + throwExceptionIfArchiveInvalid(path); + } ZipCache zipCache; ZipFile zipFile; if ((zipCache = (ZipCache)this.zipFiles.get()) != null && (zipFile = zipCache.getCache(path)) != null) { return zipFile; } + File localFile = getLocalFile(path); + + try { + if (ZIP_ACCESS_VERBOSE) { + System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.getZipFile(IPath)] Creating ZipFile on " + localFile ); //$NON-NLS-1$ //$NON-NLS-2$ + } + if (throwIoExceptionsInGetZipFile) { + throw new IOException(); + } + zipFile = new ZipFile(localFile); + if (zipCache != null) { + zipCache.setCache(path, zipFile); + } + return zipFile; + } catch (IOException e) { + ArchiveValidity reason = (e instanceof ZipException) ? ArchiveValidity.BAD_FORMAT : ArchiveValidity.UNABLE_TO_READ; + addInvalidArchive(path, reason); + throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, e)); + } + } + + public static File getLocalFile(IPath path) throws CoreException { File localFile = null; IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IResource file = root.findMember(path); @@ -2750,19 +2839,19 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis // external resource -> it is ok to use toFile() localFile= path.toFile(); } + return localFile; + } - try { - if (ZIP_ACCESS_VERBOSE) { - System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.getZipFile(IPath)] Creating ZipFile on " + localFile ); //$NON-NLS-1$ //$NON-NLS-2$ - } - zipFile = new ZipFile(localFile); - if (zipCache != null) { - zipCache.setCache(path, zipFile); + private void throwExceptionIfArchiveInvalid(IPath path) throws CoreException { + ArchiveValidity validity = getArchiveValidity(path); + if (!validity.isValid()) { + IOException reason; + if (validity == ArchiveValidity.BAD_FORMAT) { + reason = new ZipException(); + } else { + reason = new IOException(); } - return zipFile; - } catch (IOException e) { - addInvalidArchive(path); - throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, e)); + throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, reason)); } } @@ -3186,31 +3275,36 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis return this.nonChainingJars != null && this.nonChainingJars.contains(path); } - public boolean isInvalidArchive(IPath path) { - if (this.invalidArchives == null) - return false; - Long evictionTime = this.invalidArchives.get(path); - if (evictionTime == null) - return false; + public ArchiveValidity getArchiveValidity(IPath path) { + InvalidArchiveInfo invalidArchiveInfo; + synchronized (this.invalidArchivesMutex) { + invalidArchiveInfo = this.invalidArchives.get(path); + } + if (invalidArchiveInfo == null) + return ArchiveValidity.VALID; long now = System.currentTimeMillis(); // If the TTL for this cache entry has expired, directly check whether the archive is still invalid. // If it transitioned to being valid, remove it from the cache and force an update to project caches. - if (now > evictionTime) { + if (now > invalidArchiveInfo.evictionTimestamp) { try { getZipFile(path, false); removeFromInvalidArchiveCache(path); - return false; } catch (CoreException e) { // Archive is still invalid, fall through to reporting it is invalid. } + // Retry the test from the start, now that we have an up-to-date result + return getArchiveValidity(path); } - return true; + return invalidArchiveInfo.reason; } public void removeFromInvalidArchiveCache(IPath path) { - if (this.invalidArchives != null) { + synchronized(this.invalidArchivesMutex) { if (this.invalidArchives.remove(path) != null) { + if (DEBUG_INVALID_ARCHIVES) { + System.out.println("Invalid JAR cache: removed " + path); //$NON-NLS-1$ + } try { // Bug 455042: Force an update of the JavaProjectElementInfo project caches. for (IJavaProject project : getJavaModel().getJavaProjects()) { @@ -3985,26 +4079,7 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis boolean wasVerbose = false; try { if (JavaModelCache.VERBOSE) { - String elementType; - switch (element.getElementType()) { - case IJavaElement.JAVA_PROJECT: - elementType = "project"; //$NON-NLS-1$ - break; - case IJavaElement.PACKAGE_FRAGMENT_ROOT: - elementType = "root"; //$NON-NLS-1$ - break; - case IJavaElement.PACKAGE_FRAGMENT: - elementType = "package"; //$NON-NLS-1$ - break; - case IJavaElement.CLASS_FILE: - elementType = "class file"; //$NON-NLS-1$ - break; - case IJavaElement.COMPILATION_UNIT: - elementType = "compilation unit"; //$NON-NLS-1$ - break; - default: - elementType = "element"; //$NON-NLS-1$ - } + String elementType = JavaModelCache.getElementType(element); System.out.println(Thread.currentThread() + " CLOSING "+ elementType + " " + element.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$ wasVerbose = true; JavaModelCache.VERBOSE = false; @@ -4085,8 +4160,16 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis public void resetClasspathListCache() { if (this.nonChainingJars != null) this.nonChainingJars.clear(); - if (this.invalidArchives != null) + if (DEBUG_INVALID_ARCHIVES) { + synchronized(this.invalidArchivesMutex) { + if (!this.invalidArchives.isEmpty()) { + System.out.println("Invalid JAR cache: clearing cache"); //$NON-NLS-1$ + } + } + } + synchronized(this.invalidArchivesMutex) { this.invalidArchives.clear(); + } if (this.externalFiles != null) this.externalFiles.clear(); if (this.assumedExternalFiles != null) @@ -5163,6 +5246,8 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_REFRESH); + Indexer.getInstance().addListener(this.deltaState); + // listen to resource changes affecting external annotations ExternalAnnotationTracker.start(workspace); diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocConstants.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocConstants.java index ed53500c3..e52d2e190 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocConstants.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocConstants.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2014 IBM Corporation and others. + * Copyright (c) 2005, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -14,7 +14,9 @@ public interface JavadocConstants { String ANCHOR_PREFIX_END = "\""; //$NON-NLS-1$ char[] ANCHOR_PREFIX_START = "<A NAME=\"".toCharArray(); //$NON-NLS-1$ - int ANCHOR_PREFIX_START_LENGHT = ANCHOR_PREFIX_START.length; + char[] ANCHOR_PREFIX_START_2 = "<A ID=\"".toCharArray(); //$NON-NLS-1$ + int ANCHOR_PREFIX_START_LENGTH = ANCHOR_PREFIX_START.length; + int ANCHOR_PREFIX_START2_LENGTH = ANCHOR_PREFIX_START_2.length; char[] ANCHOR_SUFFIX = "</A>".toCharArray(); //$NON-NLS-1$ int ANCHOR_SUFFIX_LENGTH = JavadocConstants.ANCHOR_SUFFIX.length; char[] CONSTRUCTOR_DETAIL = "<!-- ========= CONSTRUCTOR DETAIL ======== -->".toCharArray(); //$NON-NLS-1$ diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocContents.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocContents.java index 02341d5b1..e29b96de1 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocContents.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocContents.java @@ -185,14 +185,15 @@ public class JavadocContents { } int fromIndex = this.tempLastAnchorFoundIndex; - int index; + int[] index; // check each next unknown anchor locations - while ((index = CharOperation.indexOf(JavadocConstants.ANCHOR_PREFIX_START, this.content, false, fromIndex)) != -1 && (index < indexOfSectionBottom || indexOfSectionBottom == -1)) { - fromIndex = index + 1; - - int anchorEndStart = index + JavadocConstants.ANCHOR_PREFIX_START_LENGHT; - + index = getAnchorIndex(fromIndex); + while (index[0] != -1 && (index[0] < indexOfSectionBottom || indexOfSectionBottom == -1)) { + fromIndex = index[0] + 1; + + int anchorEndStart = index[0] + index[1]; + this.tempLastAnchorFoundIndex = anchorEndStart; if (CharOperation.prefixEquals(anchor, this.content, false, anchorEndStart)) { @@ -204,11 +205,25 @@ public class JavadocContents { this.tempAnchorIndexes[this.tempAnchorIndexesCount++] = anchorEndStart; } + index = getAnchorIndex(fromIndex); } return null; } - + private int[] getAnchorIndex(int fromIndex) { + int index = CharOperation.indexOf(JavadocConstants.ANCHOR_PREFIX_START, this.content, false, fromIndex); + if (index != -1) { + return new int[]{index, JavadocConstants.ANCHOR_PREFIX_START_LENGTH}; + } + if (index == -1) { + index = CharOperation.indexOf(JavadocConstants.ANCHOR_PREFIX_START_2, this.content, false, fromIndex); + } + if (index == -1) { + return new int[]{-1, -1}; + } else { + return new int[]{index, JavadocConstants.ANCHOR_PREFIX_START2_LENGTH}; + } + } private int[] computeChildRange(int anchorEndStart, char[] anchor, int indexOfBottom) { int[] range = null; @@ -218,7 +233,7 @@ public class JavadocContents { int indexOfEndLink = CharOperation.indexOf(JavadocConstants.ANCHOR_SUFFIX, this.content, false, anchorEndStart + anchor.length); if (indexOfEndLink != -1) { // try to find the next anchor - int indexOfNextElement = CharOperation.indexOf(JavadocConstants.ANCHOR_PREFIX_START, this.content, false, indexOfEndLink); + int indexOfNextElement = getAnchorIndex(indexOfEndLink)[0]; int javadocStart = indexOfEndLink + JavadocConstants.ANCHOR_SUFFIX_LENGTH; int javadocEnd = indexOfNextElement == -1 ? indexOfBottom : Math.min(indexOfNextElement, indexOfBottom); diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathDirectory.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathDirectory.java index 72917d571..fa1e11d10 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathDirectory.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathDirectory.java @@ -15,10 +15,10 @@ import java.util.zip.ZipFile; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; - -import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationDecorator; import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; @@ -107,7 +107,7 @@ public boolean equals(Object o) { public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPackageName, String qualifiedBinaryFileName) { if (!doesFileExist(binaryFileName, qualifiedPackageName, qualifiedBinaryFileName)) return null; // most common case - ClassFileReader reader = null; + IBinaryType reader = null; try { reader = Util.newClassFileReader(this.binaryFolder.getFile(new Path(qualifiedBinaryFileName))); } catch (CoreException e) { @@ -121,7 +121,12 @@ public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPa String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0, qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length); if (this.externalAnnotationPath != null) { try { - this.annotationZipFile = reader.setExternalAnnotationProvider(this.externalAnnotationPath, fileNameWithoutExtension, this.annotationZipFile, null); + if (this.annotationZipFile == null) { + this.annotationZipFile = ExternalAnnotationDecorator + .getAnnotationZipFile(this.externalAnnotationPath, null); + } + reader = ExternalAnnotationDecorator.create(reader, this.externalAnnotationPath, + fileNameWithoutExtension, this.annotationZipFile); } catch (IOException e) { // don't let error on annotations fail class reading } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java index 1bda5fb27..657567583 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -13,22 +13,27 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core.builder; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.*; +import java.io.File; +import java.io.IOException; +import java.util.Date; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationDecorator; import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; import org.eclipse.jdt.internal.compiler.util.SimpleSet; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.core.util.Util; -import java.io.*; -import java.util.*; -import java.util.zip.*; - @SuppressWarnings("rawtypes") public class ClasspathJar extends ClasspathLocation { @@ -165,12 +170,18 @@ public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPa if (!isPackage(qualifiedPackageName)) return null; // most common case try { - ClassFileReader reader = ClassFileReader.read(this.zipFile, qualifiedBinaryFileName); + IBinaryType reader = ClassFileReader.read(this.zipFile, qualifiedBinaryFileName); if (reader != null) { String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0, qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length); if (this.externalAnnotationPath != null) { try { - this.annotationZipFile = reader.setExternalAnnotationProvider(this.externalAnnotationPath, fileNameWithoutExtension, this.annotationZipFile, null); + if (this.annotationZipFile == null) { + this.annotationZipFile = ExternalAnnotationDecorator + .getAnnotationZipFile(this.externalAnnotationPath, null); + } + + reader = ExternalAnnotationDecorator.create(reader, this.externalAnnotationPath, + fileNameWithoutExtension, this.annotationZipFile); } catch (IOException e) { // don't let error on annotations fail class reading } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/BindingMap.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/BindingMap.java new file mode 100644 index 000000000..3070193d8 --- /dev/null +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/BindingMap.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.hierarchy; + +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeIds; + +/** + * Maps a {@link TypeBinding} onto values. Two {@link TypeBinding}s are considered equivalent + * if their IDs are the same or if they have TypeIds.NoId and they are identical objects. + * <p> + * Takes into account the fact that a ReferenceBinding may have its ID change from NoId + * to a real ID at any time without notice. (This is a behavior that was observed in + * TypeHierarchyTests.testAnonymousType01 -- if type IDs could be made invariant then it + * would be possible to implement a more efficient map that never needs to perform an + * exhaustive search.) + */ +public class BindingMap<V> { + private Map<TypeBinding, V> identityMap = new IdentityHashMap<>(); + private Object[] mapIdToValue = new Object[0]; + private Set<TypeBinding> bindingsWithoutAnId = new HashSet<>(); + + public void put(TypeBinding key, V value) { + this.identityMap.put(key, value); + if (key.id != TypeIds.NoId) { + int targetId = key.id; + insertIntoIdMap(targetId, value); + } else { + this.bindingsWithoutAnId.add(key); + } + } + + @SuppressWarnings("unchecked") + public V get(TypeBinding key) { + // Check if we can find this binding by identity + V value = this.identityMap.get(key); + if (value != null) { + return value; + } + int targetId = key.id; + if (targetId != TypeIds.NoId) { + // Check if we can find this binding by value + if (targetId < this.mapIdToValue.length) { + value = (V)this.mapIdToValue[targetId]; + } + if (value != null) { + return value; + } + + // Check if there are any bindings that previously had no ID that have + // subsequently been assigned one. + for (Iterator<TypeBinding> bindingIter = this.bindingsWithoutAnId.iterator(); bindingIter.hasNext();) { + TypeBinding nextBinding = bindingIter.next(); + + if (nextBinding.id != TypeIds.NoId) { + insertIntoIdMap(nextBinding.id, this.identityMap.get(nextBinding)); + bindingIter.remove(); + } + } + + // Now look again to see if this binding can be found + if (targetId < this.mapIdToValue.length) { + value = (V)this.mapIdToValue[targetId]; + } + } + + return value; + } + + private void insertIntoIdMap(int targetId, V value) { + int requiredSize = targetId + 1; + if (this.mapIdToValue.length < requiredSize) { + int newSize = requiredSize * 2; + Object[] newArray = new Object[newSize]; + System.arraycopy(this.mapIdToValue, 0, newArray, 0, this.mapIdToValue.length); + this.mapIdToValue = newArray; + } + this.mapIdToValue[targetId] = value; + } + + public void clear() { + this.identityMap.clear(); + this.bindingsWithoutAnId.clear(); + this.mapIdToValue = new Object[0]; + } +} diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBinaryType.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBinaryType.java index 932c0494f..0427355e3 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBinaryType.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBinaryType.java @@ -53,6 +53,23 @@ public HierarchyBinaryType(int modifiers, char[] qualification, char[] sourceNam this.typeParameterSignatures = typeParameterSignatures; CharOperation.replace(this.name, '.', '/'); } + +public HierarchyBinaryType(int modifiers, char[] binaryName, char[] sourceName, char[] enclosingTypeBinaryName, char[][] typeParameterSignatures) { + this.modifiers = modifiers; + this.sourceName = sourceName; + this.name = binaryName; + this.enclosingTypeName = enclosingTypeBinaryName; + this.typeParameterSignatures = typeParameterSignatures; + + if (typeParameterSignatures != null) { + for (char[] next : typeParameterSignatures) { + if (next == null) { + throw new IllegalArgumentException("Parameter's type signature must not be null"); //$NON-NLS-1$ + } + } + } +} + /** * @see org.eclipse.jdt.internal.compiler.env.IBinaryType */ @@ -197,6 +214,7 @@ public boolean isMember() { return false; // index did not record this information (since unused for hierarchies) } + public void recordSuperType(char[] superTypeName, char[] superQualification, char superClassOrInterface){ // index encoding of p.A$B was B/p.A$, rebuild the proper name @@ -215,17 +233,25 @@ public void recordSuperType(char[] superTypeName, char[] superQualification, cha if (TypeDeclaration.kind(this.modifiers) == TypeDeclaration.INTERFACE_DECL) return; char[] encodedName = CharOperation.concat(superQualification, superTypeName, '/'); CharOperation.replace(encodedName, '.', '/'); - this.superclass = encodedName; + recordSuperclass(encodedName); } else { char[] encodedName = CharOperation.concat(superQualification, superTypeName, '/'); CharOperation.replace(encodedName, '.', '/'); - if (this.superInterfaces == NoInterface){ - this.superInterfaces = new char[][] { encodedName }; - } else { - int length = this.superInterfaces.length; - System.arraycopy(this.superInterfaces, 0, this.superInterfaces = new char[length+1][], 0, length); - this.superInterfaces[length] = encodedName; - } + recordInterface(encodedName); + } +} + +public void recordSuperclass(char[] binaryName) { + this.superclass = binaryName; +} + +public void recordInterface(char[] binaryName) { + if (this.superInterfaces == NoInterface){ + this.superInterfaces = new char[][] { binaryName }; + } else { + int length = this.superInterfaces.length; + System.arraycopy(this.superInterfaces, 0, this.superInterfaces = new char[length+1][], 0, length); + this.superInterfaces[length] = binaryName; } } @@ -235,6 +261,7 @@ public void recordSuperType(char[] superTypeName, char[] superQualification, cha public char[] sourceFileName() { return null; } +@Override public String toString() { StringBuffer buffer = new StringBuffer(); if (this.modifiers == ClassFileConstants.AccPublic) { diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBuilder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBuilder.java index 41fb754a1..46fd72d2e 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBuilder.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBuilder.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -18,15 +18,25 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.IGenericType; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; -import org.eclipse.jdt.internal.core.*; +import org.eclipse.jdt.internal.core.ClassFile; +import org.eclipse.jdt.internal.core.JavaElement; +import org.eclipse.jdt.internal.core.JavaProject; +import org.eclipse.jdt.internal.core.NameLookup; +import org.eclipse.jdt.internal.core.Openable; +import org.eclipse.jdt.internal.core.ResolvedBinaryType; +import org.eclipse.jdt.internal.core.SearchableEnvironment; +import org.eclipse.jdt.internal.core.SourceTypeElementInfo; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory; import org.eclipse.jdt.internal.core.util.ResourceCompilationUnit; import org.eclipse.jdt.internal.core.util.Util; @@ -280,6 +290,7 @@ public abstract class HierarchyBuilder { protected ICompilationUnit createCompilationUnitFromPath(Openable handle, IFile file) { final char[] elementName = handle.getElementName().toCharArray(); return new ResourceCompilationUnit(file) { + @Override public char[] getFileName() { return elementName; } @@ -316,33 +327,17 @@ protected IBinaryType createInfoFromClassFile(Openable handle, IResource file) { * Create a type info from the given class file in a jar and adds it to the given list of infos. */ protected IBinaryType createInfoFromClassFileInJar(Openable classFile) { - PackageFragment pkg = (PackageFragment) classFile.getParent(); - String classFilePath = Util.concatWith(pkg.names, classFile.getElementName(), '/'); - IBinaryType info = null; - java.util.zip.ZipFile zipFile = null; + IClassFile cf = (IClassFile)classFile; + IBinaryType info; try { - zipFile = ((JarPackageFragmentRoot)pkg.getParent()).getJar(); - info = org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader.read( - zipFile, - classFilePath); - } catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException e) { - if (TypeHierarchy.DEBUG) { - e.printStackTrace(); - } - return null; - } catch (java.io.IOException e) { + info = BinaryTypeFactory.create(cf, null); + } catch (JavaModelException | ClassFormatException e) { if (TypeHierarchy.DEBUG) { e.printStackTrace(); } return null; - } catch (CoreException e) { - if (TypeHierarchy.DEBUG) { - e.printStackTrace(); - } - return null; - } finally { - JavaModelManager.getJavaModelManager().closeZipFile(zipFile); } + this.infoToHandle.put(info, classFile); return info; } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyResolver.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyResolver.java index b57e2d5ec..d9a99ff85 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyResolver.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyResolver.java @@ -87,6 +87,7 @@ public class HierarchyResolver implements ITypeRequestor { private CompilerOptions options; HierarchyBuilder builder; private ReferenceBinding[] typeBindings; + private BindingMap<IGenericType> bindingMap = new BindingMap<>(); private int typeIndex; private IGenericType[] typeModels; @@ -230,10 +231,9 @@ private IType findSuperClass(IGenericType type, ReferenceBinding typeBinding) { } } } - for (int t = this.typeIndex; t >= 0; t--) { - if (TypeBinding.equalsEquals(this.typeBindings[t], superBinding)) { - return this.builder.getHandle(this.typeModels[t], superBinding); - } + IGenericType typeModel = this.bindingMap.get(superBinding); + if (typeModel != null) { + return this.builder.getHandle(typeModel, superBinding); } } return null; @@ -336,13 +336,12 @@ private IType[] findSuperInterfaces(IGenericType type, ReferenceBinding typeBind // ensure that the binding corresponds to the interface defined by the user if (CharOperation.equals(simpleName, interfaceBinding.sourceName)) { bindingIndex++; - for (int t = this.typeIndex; t >= 0; t--) { - if (TypeBinding.equalsEquals(this.typeBindings[t], interfaceBinding)) { - IType handle = this.builder.getHandle(this.typeModels[t], interfaceBinding); - if (handle != null) { - superinterfaces[index++] = handle; - continue next; - } + IGenericType genericType = this.bindingMap.get(interfaceBinding); + if (genericType != null) { + IType handle = this.builder.getHandle(genericType, interfaceBinding); + if (handle != null) { + superinterfaces[index++] = handle; + continue next; } } } @@ -438,6 +437,7 @@ private void remember(IGenericType suppliedType, ReferenceBinding typeBinding) { } this.typeModels[this.typeIndex] = suppliedType; this.typeBindings[this.typeIndex] = typeBinding; + this.bindingMap.put(typeBinding, suppliedType); } private void remember(IType type, ReferenceBinding typeBinding) { //{ObjectTeams: for phantom roles avoid hitting the JME (phantom has no info) but proceed into else as to record what we have @@ -740,6 +740,7 @@ private void reset(){ this.typeIndex = -1; this.typeModels = new IGenericType[5]; this.typeBindings = new ReferenceBinding[5]; + this.bindingMap.clear(); } /** @@ -1062,6 +1063,7 @@ private void setEnvironment(LookupEnvironment lookupEnvironment, HierarchyBuilde this.typeIndex = -1; this.typeModels = new IGenericType[5]; this.typeBindings = new ReferenceBinding[5]; + this.bindingMap.clear(); } /* diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/IndexBasedHierarchyBuilder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/IndexBasedHierarchyBuilder.java index 4c9d1e7f4..a2342ebc1 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/IndexBasedHierarchyBuilder.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/IndexBasedHierarchyBuilder.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core.hierarchy; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -17,9 +18,12 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; +import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubMonitor; @@ -50,9 +54,19 @@ import org.eclipse.jdt.internal.core.Member; import org.eclipse.jdt.internal.core.Openable; import org.eclipse.jdt.internal.core.PackageFragment; import org.eclipse.jdt.internal.core.SearchableEnvironment; +import org.eclipse.jdt.internal.core.nd.IReader; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.indexer.Indexer; +import org.eclipse.jdt.internal.core.nd.java.JavaIndex; +import org.eclipse.jdt.internal.core.nd.java.JavaNames; +import org.eclipse.jdt.internal.core.nd.java.NdType; +import org.eclipse.jdt.internal.core.nd.java.NdTypeId; +import org.eclipse.jdt.internal.core.nd.java.NdTypeInterface; +import org.eclipse.jdt.internal.core.nd.java.NdTypeSignature; import org.eclipse.jdt.internal.core.search.IndexQueryRequestor; import org.eclipse.jdt.internal.core.search.JavaSearchParticipant; import org.eclipse.jdt.internal.core.search.SubTypeSearchJob; +import org.eclipse.jdt.internal.core.search.UnindexedSearchScope; import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants; import org.eclipse.jdt.internal.core.search.indexing.IndexManager; import org.eclipse.jdt.internal.core.search.matching.MatchLocator; @@ -467,7 +481,103 @@ public static void searchAllPossibleSubTypes( int waitingPolicy, // WaitUntilReadyToSearch | ForceImmediateSearch | CancelIfNotReadyToSearch final IProgressMonitor monitor) { - SubMonitor subMonitor = SubMonitor.convert(monitor); + if (JavaIndex.isEnabled()) { + SubMonitor subMonitor = SubMonitor.convert(monitor, 2); + newSearchAllPossibleSubTypes(type, scope, binariesFromIndexMatches, pathRequestor, waitingPolicy, + subMonitor.split(1)); + legacySearchAllPossibleSubTypes(type, UnindexedSearchScope.filterEntriesCoveredByTheNewIndex(scope), + binariesFromIndexMatches, pathRequestor, waitingPolicy, subMonitor.split(1)); + } else { + legacySearchAllPossibleSubTypes(type, scope, binariesFromIndexMatches, pathRequestor, waitingPolicy, + monitor); + } +} + +private static void newSearchAllPossibleSubTypes(IType type, IJavaSearchScope scope2, Map binariesFromIndexMatches2, + IPathRequestor pathRequestor, int waitingPolicy, IProgressMonitor progressMonitor) { + SubMonitor subMonitor = SubMonitor.convert(progressMonitor, 2); + JavaIndex index = JavaIndex.getIndex(); + + Indexer.getInstance().waitForIndex(waitingPolicy, subMonitor.split(1)); + + Nd nd = index.getNd(); + char[] fieldDefinition = JavaNames.fullyQualifiedNameToFieldDescriptor(type.getFullyQualifiedName().toCharArray()); + + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + + try (IReader reader = nd.acquireReadLock()) { + NdTypeId foundType = index.findType(fieldDefinition); + + if (foundType == null) { + return; + } + + ArrayDeque<NdType> typesToVisit = new ArrayDeque<>(); + Set<NdType> discoveredTypes = new HashSet<>(); + typesToVisit.addAll(foundType.getTypes()); + discoveredTypes.addAll(typesToVisit); + + while (!typesToVisit.isEmpty()) { + NdType nextType = typesToVisit.removeFirst(); + NdTypeId typeId = nextType.getTypeId(); + + String typePath = new String(JavaNames.getIndexPathFor(nextType, root)); + if (!scope2.encloses(typePath)) { + continue; + } + + subMonitor.setWorkRemaining(Math.max(typesToVisit.size(), 3000)).split(1); + + boolean isLocalClass = nextType.isLocal() || nextType.isAnonymous(); + pathRequestor.acceptPath(typePath, isLocalClass); + + HierarchyBinaryType binaryType = (HierarchyBinaryType)binariesFromIndexMatches2.get(typePath); + if (binaryType == null) { + binaryType = createBinaryTypeFrom(nextType); + binariesFromIndexMatches2.put(typePath, binaryType); + } + + for (NdType subType : typeId.getSubTypes()) { + if (discoveredTypes.add(subType)) { + typesToVisit.add(subType); + } + } + } + } +} + +private static HierarchyBinaryType createBinaryTypeFrom(NdType type) { + char[] enclosingTypeName = null; + NdTypeSignature enclosingType = type.getDeclaringType(); + if (enclosingType != null) { + enclosingTypeName = enclosingType.getRawType().getBinaryName(); + } + char[][] typeParameters = type.getTypeParameterSignatures(); + NdTypeId typeId = type.getTypeId(); + HierarchyBinaryType result = new HierarchyBinaryType(type.getModifiers(), typeId.getBinaryName(), + type.getSourceName(), enclosingTypeName, typeParameters.length == 0 ? null : typeParameters); + + NdTypeSignature superClass = type.getSuperclass(); + if (superClass != null) { + result.recordSuperclass(superClass.getRawType().getBinaryName()); + } + + for (NdTypeInterface interf : type.getInterfaces()) { + result.recordInterface(interf.getInterface().getRawType().getBinaryName()); + } + return result; +} + +private static void legacySearchAllPossibleSubTypes( + IType type, + IJavaSearchScope scope, + final Map binariesFromIndexMatches, + final IPathRequestor pathRequestor, + int waitingPolicy, // WaitUntilReadyToSearch | ForceImmediateSearch | CancelIfNotReadyToSearch + final IProgressMonitor progressMonitor) { + + SubMonitor subMonitor = SubMonitor.convert(progressMonitor, 100); + /* embed constructs inside arrays so as to pass them to (inner) collector */ final Queue queue = new Queue(); final HashtableOfObject foundSuperNames = new HashtableOfObject(5); diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/AbstractTypeFactory.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/AbstractTypeFactory.java new file mode 100644 index 000000000..957e7cf45 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/AbstractTypeFactory.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import org.eclipse.jdt.internal.core.nd.field.StructDef.DeletionSemantics; + +public abstract class AbstractTypeFactory<T> implements ITypeFactory<T> { + @Override + public void destructFields(Nd dom, long address) { + // No nested fields by default + } + + @Override + public void destruct(Nd dom, long address) { + // Nothing to destruct by default + } + + @Override + public boolean hasDestructor() { + return false; + } + + @Override + public boolean isReadyForDeletion(Nd dom, long address) { + return false; + } + + @Override + public DeletionSemantics getDeletionSemantics() { + return DeletionSemantics.EXPLICIT; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/DatabaseRef.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/DatabaseRef.java new file mode 100644 index 000000000..044fe241d --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/DatabaseRef.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import java.util.function.Supplier; + +/** + * Holds a reference to a database entity that may be retained across read locks. In normal circumstances, it + * is unsafe to retain a database address after a read lock is released since the object pointed to at that + * address may have been deleted in the meantime. This class addresses this problem by remembering both the + * address itself and enough information to determine whether that address is invalid and search for an + * equivalent object if the original is lost. + */ +public class DatabaseRef<T extends NdNode> implements Supplier<T> { + private final Nd nd; + private T lastResult; + private long writeCounter; + private final Supplier<T> searchFunction; + + /** + * Constructs a new {@link DatabaseRef} that will search for its target using the given search function. + */ + public DatabaseRef(Nd nd, Supplier<T> searchFunction) { + this.nd = nd; + this.searchFunction = searchFunction; + this.writeCounter = -1; + } + + /** + * Constructs a new {@link DatabaseRef} that will search for its target using the given search function. + */ + public DatabaseRef(Nd nd, Supplier<T> searchFunction, T initialResult) { + this.nd = nd; + this.searchFunction = searchFunction; + this.lastResult = initialResult; + this.writeCounter = this.nd.getWriteNumber(); + } + + /** + * Returns the referenced object or null if the object is no longer present in the database. + */ + public T get() { + long ndWriteNumber = this.nd.getWriteNumber(); + if (this.writeCounter == ndWriteNumber) { + return this.lastResult; + } + + T result = this.searchFunction.get(); + this.writeCounter = ndWriteNumber; + this.lastResult = result; + return result; + } + + public Nd getNd() { + return this.nd; + } + + /** + * Acquires a read lock. Callers must invoke close() on the result when done. + */ + public IReader lock() { + return this.nd.acquireReadLock(); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/IDestructable.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/IDestructable.java new file mode 100644 index 000000000..5bc78e047 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/IDestructable.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +/** + * This mix-in interface is implemented by database objects that require a custom + * destruction step. + */ +public interface IDestructable { + /** + * Intended to be implemented by objects which require a custom destruction step. + * This should normally not be invoked by clients, since custom destruction is just + * one step in tearing down an object. The normal way to tear down an object is + * {@link NdNode#delete} + * <p> + * If you are writing code that must run as part of delete (or are implementing part + * of the destruct method on a custom ITypeFactory)the correct steps to destructing + * an object are: + * <ul> + * <li>Invoke this destruct method (which serves the same purpose as the user-implemented + * portion of a C++ destructor)</li> + * <li>Invoke ITypeFactory.destructFields to destruct its fields (which serves the same + * purpose as the compiler-implemented portion of a C++ destructor)</li> + * <li>Invoke Database.free on its address to free up memory allocated for the object + * itself. (Which serves the same purpose as the memory deallocation step in + * the C++ delete operator)</li> + * </ul> + * <p> + * Normally, first two steps are performed together as part of ITypeFactory.destruct + */ + void destruct(); +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/IIndexFileLocation.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/IIndexFileLocation.java new file mode 100644 index 000000000..8a805d042 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/IIndexFileLocation.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import java.net.URI; + +/** + * Files in the index are (conceptually) partitioned into workspace and non-workspace (external) files. Two index file + * locations are considered equal if their URIs are equal. + * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IIndexFileLocation { + /** + * Returns the URI of the indexed file (non-{@code null}). + */ + public URI getURI(); + + /** + * Returns the workspace relative path of the file in the index or {@code null} if the file is not in the workspace. + */ + public String getFullPath(); +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/INdNode.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/INdNode.java new file mode 100644 index 000000000..ef24eb6fb --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/INdNode.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import org.eclipse.jdt.internal.core.nd.db.IndexException; + +/** + * Interface for all nodes that can be visited by a {@link INdVisitor}. + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface INdNode { + + /** + * Visits the children of this node. + */ + public void accept(INdVisitor visitor) throws IndexException; +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/INdVisitor.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/INdVisitor.java new file mode 100644 index 000000000..034e23302 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/INdVisitor.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import org.eclipse.core.runtime.CoreException; + +public interface INdVisitor { + + /** + * Walk the nodes in a {@link Nd}. Return true to visit the children of + * this node, or false to skip to the next sibling of this node. + * Throw CoreException to stop the visit. + * + * @param node being visited + * @return whether to visit children + */ + public boolean visit(INdNode node) throws CoreException; + + /** + * All children have been visited, about to go back to the parent. + * + * @param node that has just completed visitation + * @throws CoreException + */ + public void leave(INdNode node) throws CoreException; + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/IReader.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/IReader.java new file mode 100644 index 000000000..3e1c3211b --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/IReader.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +public interface IReader extends AutoCloseable { + @Override + void close(); +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/ITypeFactory.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/ITypeFactory.java new file mode 100644 index 000000000..e387a4059 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/ITypeFactory.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import org.eclipse.jdt.internal.core.nd.field.StructDef.DeletionSemantics; + +// TODO(sxenos): rename this to something like "StructDescriptor" -- it's more than a factory and the word +// type is overloaded in JDT. +public interface ITypeFactory<T> { + /** + * Invokes the delete method on all the fields of the object, and calls deleteFields on the superclass' type (if + * any). Does not perform any higher-level cleanup operations. This is only intended to be called from the + * deleteFields methods of a subtype or the delete method of this class. + * <p> + * When destructing a type with a superclass, the correct destruction behavior is: + * <ul> + * <li>External code invokes the delete method on ITypeFactory + * <li>The ITypeFactory.delete method calls an instance method on the class (typically called T#delete()), which + * performs high-level deletion operations (if any). + * <li>T.delete also calls T.super.delete() (if any) + * <li>ITypeFactory.delete calls ITypeFactory.deleteFields, which performs low-level deletion operations on the + * fields, then calls ITypeFactory.deleteFields on the base type. + * </ul> + */ + void destructFields(Nd dom, long address); + + T create(Nd dom, long address); + + /** + * Invokes any cleanup code for this object. In particular, it deallocates any memory allocated by the type's + * fields. Does not free the memory at address, though. This is used for both objects which were allocated their own + * memory block and objects which are embedded as fields within a larger object. If the object was given its own + * memory block, it is the caller's responsibility to invoke free after calling this method. + */ + void destruct(Nd dom, long address); + + /** + * If this returns false, the delete and deleteFields methods both always do nothing. + */ + boolean hasDestructor(); + + int getRecordSize(); + + Class<?> getElementClass(); + + /** + * Returns true if this object is orphaned. If the object is refcounted, this means the refcount is 0. If + * the object is deleted via an owner pointer, this means the owner pointer is null. + */ + boolean isReadyForDeletion(Nd dom, long address); + + /** + * Returns the deletion semantics used for this object. + */ + DeletionSemantics getDeletionSemantics(); +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/IndexFileLocation.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/IndexFileLocation.java new file mode 100644 index 000000000..e3112146b --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/IndexFileLocation.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import java.net.URI; + +/** + * An implementation of IIndexFileLocation. + */ +public class IndexFileLocation implements IIndexFileLocation { + private final URI uri; + private final String fullPath; + + public IndexFileLocation(URI uri, String fullPath) { + if (uri == null) + throw new IllegalArgumentException(); + this.uri = uri; + this.fullPath = fullPath; + } + + @Override + public String getFullPath() { + return this.fullPath; + } + + @Override + public URI getURI() { + return this.uri; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof IIndexFileLocation) { + return this.uri.equals(((IIndexFileLocation) obj).getURI()); + } + return false; + } + + @Override + public int hashCode() { + return this.uri.hashCode(); + } + + @Override + public String toString() { + if (this.fullPath == null) { + return this.uri.toString(); + } + return this.fullPath.toString() + " (" + this.uri.toString() + ')'; //$NON-NLS-1$ + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/LongArray.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/LongArray.java new file mode 100644 index 000000000..11d28f8d3 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/LongArray.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +/** + * Represents an array of long. + */ +public class LongArray { + private static final int MIN_CAPACITY = 8; + private long[] contents; + private int size; + + long get(int index) { + if (index >= this.size) { + throw new ArrayIndexOutOfBoundsException(index); + } + + return this.contents[index]; + } + + long removeLast() { + return this.contents[--this.size]; + } + + void addLast(long toAdd) { + ensureCapacity(this.size + 1); + this.contents[this.size++] = toAdd; + } + + private void ensureCapacity(int capacity) { + if (this.contents == null) { + this.contents = new long[Math.max(MIN_CAPACITY, capacity)]; + } + + if (this.contents.length >= capacity) { + return; + } + + int newSize = capacity * 2; + long[] newContents = new long[newSize]; + + System.arraycopy(this.contents, 0, newContents, 0, this.contents.length); + this.contents = newContents; + } + + int size() { + return this.size; + } + + public boolean isEmpty() { + return this.size == 0; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/Nd.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/Nd.java new file mode 100644 index 000000000..fb02f1319 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/Nd.java @@ -0,0 +1,602 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.jdt.internal.core.nd.db.ChunkCache; +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.db.IndexException; + +/** + * Network Database for storing semantic information. + */ +public class Nd { + private static final int CANCELLATION_CHECK_INTERVAL = 500; + private static final int BLOCKED_WRITE_LOCK_OUTPUT_INTERVAL = 30000; + private static final int LONG_WRITE_LOCK_REPORT_THRESHOLD = 1000; + private static final int LONG_READ_LOCK_WAIT_REPORT_THRESHOLD = 1000; + public static boolean sDEBUG_LOCKS= false; + public static boolean DEBUG_DUPLICATE_DELETIONS = false; + + private final int currentVersion; + private final int maxVersion; + private final int minVersion; + + public static int version(int major, int minor) { + return (major << 16) + minor; + } + + /** + * Returns the version that shall be used when creating new databases. + */ + public int getDefaultVersion() { + return this.currentVersion; + } + + public boolean isSupportedVersion(int vers) { + return vers >= this.minVersion && vers <= this.maxVersion; + } + + public int getMinSupportedVersion() { + return this.minVersion; + } + + public int getMaxSupportedVersion() { + return this.maxVersion; + } + + public static String versionString(int version) { + final int major= version >> 16; + final int minor= version & 0xffff; + return "" + major + '.' + minor; //$NON-NLS-1$ + } + + // Local caches + protected Database db; + private File fPath; + private final HashMap<Object, Object> fResultCache = new HashMap<>(); + + private final NdNodeTypeRegistry<NdNode> fNodeTypeRegistry; + private HashMap<Long, Object> pendingDeletions = new HashMap<>(); + + private IReader fReader = new IReader() { + @Override + public void close() { + releaseReadLock(); + } + }; + + /** + * This long is incremented every time a change is written to the database. Can be used to determine if the database + * has changed. + */ + private long fWriteNumber; + + public Nd(File dbPath, NdNodeTypeRegistry<NdNode> nodeTypes, int minVersion, int maxVersion, + int currentVersion) throws IndexException { + this(dbPath, ChunkCache.getSharedInstance(), nodeTypes, minVersion, maxVersion, currentVersion); + } + + public Nd(File dbPath, ChunkCache chunkCache, NdNodeTypeRegistry<NdNode> nodeTypes, int minVersion, + int maxVersion, int currentVersion) throws IndexException { + this.currentVersion = currentVersion; + this.maxVersion = maxVersion; + this.minVersion = minVersion; + this.fNodeTypeRegistry = nodeTypes; + loadDatabase(dbPath, chunkCache); + if (sDEBUG_LOCKS) { + this.fLockDebugging = new HashMap<>(); + System.out.println("Debugging database Locks"); //$NON-NLS-1$ + } + } + + public File getPath() { + return this.fPath; + } + + public long getWriteNumber() { + return this.fWriteNumber; + } + + public void scheduleDeletion(long addressOfNodeToDelete) { + if (this.pendingDeletions.containsKey(addressOfNodeToDelete)) { + logDoubleDeletion(addressOfNodeToDelete); + return; + } + + Object data = Boolean.TRUE; + if (DEBUG_DUPLICATE_DELETIONS) { + data = new RuntimeException(); + } + this.pendingDeletions.put(addressOfNodeToDelete, data); + } + + protected void logDoubleDeletion(long addressOfNodeToDelete) { + // Sometimes an object can be scheduled for deletion twice, if it is created and then discarded shortly + // afterward during indexing. This may indicate an inefficiency in the indexer but is not necessarily + // a bug. + // If you're debugging issues related to duplicate deletions, set DEBUG_DUPLICATE_DELETIONS to true + Package.log("Database object queued for deletion twice", new RuntimeException()); //$NON-NLS-1$ + Object earlierData = this.pendingDeletions.get(addressOfNodeToDelete); + if (earlierData instanceof RuntimeException) { + RuntimeException exception = (RuntimeException) earlierData; + + Package.log("Data associated with earlier deletion stack was:", exception); //$NON-NLS-1$ + } + } + + /** + * Synchronously processes all pending deletions + */ + public void processDeletions() { + while (!this.pendingDeletions.isEmpty()) { + long next = this.pendingDeletions.keySet().iterator().next(); + + deleteIfUnreferenced(next); + + this.pendingDeletions.remove(next); + } + } + + /** + * Returns whether this {@link Nd} can never be written to. Writable subclasses should return false. + */ + protected boolean isPermanentlyReadOnly() { + return false; + } + + private void loadDatabase(File dbPath, ChunkCache cache) throws IndexException { + this.fPath= dbPath; + final boolean lockDB= this.db == null || this.lockCount != 0; + + clearCaches(); + this.db = new Database(this.fPath, cache, getDefaultVersion(), isPermanentlyReadOnly()); + + this.db.setLocked(lockDB); + if (!isSupportedVersion()) { + Package.log("Index database is uses an unsupported version " + this.db.getVersion() //$NON-NLS-1$ + + " Deleting and recreating.", null); //$NON-NLS-1$ + this.db.close(); + this.fPath.delete(); + this.db = new Database(this.fPath, cache, getDefaultVersion(), isPermanentlyReadOnly()); + this.db.setLocked(lockDB); + } + this.fWriteNumber = this.db.getLong(Database.WRITE_NUMBER_OFFSET); + this.db.setLocked(this.lockCount != 0); + } + + public Database getDB() { + return this.db; + } + + // Read-write lock rules. Readers don't conflict with other readers, + // Writers conflict with readers, and everyone conflicts with writers. + private final Object mutex = new Object(); + private int lockCount; + private int waitingReaders; + private long lastWriteAccess= 0; + //private long lastReadAccess= 0; + private long timeWriteLockAcquired; + + public IReader acquireReadLock() { + try { + long t = sDEBUG_LOCKS ? System.nanoTime() : 0; + synchronized (this.mutex) { + ++this.waitingReaders; + try { + while (this.lockCount < 0) + this.mutex.wait(); + } finally { + --this.waitingReaders; + } + ++this.lockCount; + this.db.setLocked(true); + + if (sDEBUG_LOCKS) { + t = (System.nanoTime() - t) / 1000000; + if (t >= LONG_READ_LOCK_WAIT_REPORT_THRESHOLD) { + System.out.println("Acquired index read lock after " + t + " ms wait."); //$NON-NLS-1$//$NON-NLS-2$ + } + incReadLock(this.fLockDebugging); + } + return this.fReader; + } + } catch (InterruptedException e) { + throw new OperationCanceledException(); + } + } + + public void releaseReadLock() { + synchronized (this.mutex) { + assert this.lockCount > 0: "No lock to release"; //$NON-NLS-1$ + if (sDEBUG_LOCKS) { + decReadLock(this.fLockDebugging); + } + + //this.lastReadAccess= System.currentTimeMillis(); + if (this.lockCount > 0) + --this.lockCount; + this.mutex.notifyAll(); + this.db.setLocked(this.lockCount != 0); + } + // A lock release probably means that some AST is going away. The result cache has to be + // cleared since it may contain objects belonging to the AST that is going away. A failure + // to release an AST object would cause a memory leak since the whole AST would remain + // pinned to memory. + // TODO(sprigogin): It would be more efficient to replace the global result cache with + // separate caches for each AST. + //clearResultCache(); + } + + /** + * Acquire a write lock on this {@link Nd}. Blocks until any existing read/write locks are released. + * @throws OperationCanceledException + * @throws IllegalStateException if this {@link Nd} is not writable + */ + public void acquireWriteLock(IProgressMonitor monitor) { + try { + acquireWriteLock(0, monitor); + } catch (InterruptedException e) { + throw new OperationCanceledException(); + } + } + + /** + * Acquire a write lock on this {@link Nd}, giving up the specified number of read locks first. Blocks + * until any existing read/write locks are released. + * @throws InterruptedException + * @throws IllegalStateException if this {@link Nd} is not writable + */ + public void acquireWriteLock(int giveupReadLocks, IProgressMonitor monitor) throws InterruptedException { + assert !isPermanentlyReadOnly(); + synchronized (this.mutex) { + if (sDEBUG_LOCKS) { + incWriteLock(giveupReadLocks); + } + + if (giveupReadLocks > 0) { + // give up on read locks + assert this.lockCount >= giveupReadLocks: "Not enough locks to release"; //$NON-NLS-1$ + if (this.lockCount < giveupReadLocks) { + giveupReadLocks= this.lockCount; + } + } else { + giveupReadLocks= 0; + } + + // Let the readers go first + long start= sDEBUG_LOCKS ? System.currentTimeMillis() : 0; + while (this.lockCount > giveupReadLocks || this.waitingReaders > 0) { + this.mutex.wait(CANCELLATION_CHECK_INTERVAL); + if (monitor != null && monitor.isCanceled()) { + throw new OperationCanceledException(); + } + if (sDEBUG_LOCKS) { + start = reportBlockedWriteLock(start, giveupReadLocks); + } + } + this.lockCount= -1; + if (sDEBUG_LOCKS) + this.timeWriteLockAcquired = System.currentTimeMillis(); + this.db.setExclusiveLock(); + } + } + + public final void releaseWriteLock() { + releaseWriteLock(0, true); + } + + @SuppressWarnings("nls") + public void releaseWriteLock(int establishReadLocks, boolean flush) { + boolean wasInterrupted = false; + // When all locks are released we can clear the result cache. + if (establishReadLocks == 0) { + processDeletions(); + this.db.putLong(Database.WRITE_NUMBER_OFFSET, ++this.fWriteNumber); + clearResultCache(); + } + try { + wasInterrupted = this.db.giveUpExclusiveLock(flush) || wasInterrupted; + } catch (IndexException e) { + Package.log(e); + } + assert this.lockCount == -1; + this.lastWriteAccess= System.currentTimeMillis(); + synchronized (this.mutex) { + if (sDEBUG_LOCKS) { + long timeHeld = this.lastWriteAccess - this.timeWriteLockAcquired; + if (timeHeld >= LONG_WRITE_LOCK_REPORT_THRESHOLD) { + System.out.println("Index write lock held for " + timeHeld + " ms"); + } + decWriteLock(establishReadLocks); + } + + if (this.lockCount < 0) + this.lockCount= establishReadLocks; + this.mutex.notifyAll(); + this.db.setLocked(this.lockCount != 0); + } + + if (wasInterrupted) { + throw new OperationCanceledException(); + } + } + + public boolean hasWaitingReaders() { + synchronized (this.mutex) { + return this.waitingReaders > 0; + } + } + + public long getLastWriteAccess() { + return this.lastWriteAccess; + } + + public boolean isSupportedVersion() throws IndexException { + final int version = this.db.getVersion(); + return version >= this.minVersion && version <= this.maxVersion; + } + + public void close() throws IndexException { + this.db.close(); + clearCaches(); + } + + private void clearCaches() { +// fileIndex= null; +// tagIndex = null; +// indexOfDefectiveFiles= null; +// indexOfFiledWithUnresolvedIncludes= null; +// fLinkageIDCache.clear(); + clearResultCache(); + } + + public void clearResultCache() { + synchronized (this.fResultCache) { + this.fResultCache.clear(); + } + } + + public Object getCachedResult(Object key) { + synchronized (this.fResultCache) { + return this.fResultCache.get(key); + } + } + + public void putCachedResult(Object key, Object result) { + putCachedResult(key, result, true); + } + + public Object putCachedResult(Object key, Object result, boolean replace) { + synchronized (this.fResultCache) { + Object old= this.fResultCache.put(key, result); + if (old != null && !replace) { + this.fResultCache.put(key, old); + return old; + } + return result; + } + } + + public void removeCachedResult(Object key) { + synchronized (this.fResultCache) { + this.fResultCache.remove(key); + } + } + + // For debugging lock issues + static class DebugLockInfo { + int fReadLocks; + int fWriteLocks; + List<StackTraceElement[]> fTraces= new ArrayList<>(); + + public int addTrace() { + this.fTraces.add(Thread.currentThread().getStackTrace()); + return this.fTraces.size(); + } + + @SuppressWarnings("nls") + public void write(String threadName) { + System.out.println("Thread: '" + threadName + "': " + this.fReadLocks + " readlocks, " + this.fWriteLocks + " writelocks"); + for (StackTraceElement[] trace : this.fTraces) { + System.out.println(" Stacktrace:"); + for (StackTraceElement ste : trace) { + System.out.println(" " + ste); + } + } + } + + public void inc(DebugLockInfo val) { + this.fReadLocks+= val.fReadLocks; + this.fWriteLocks+= val.fWriteLocks; + this.fTraces.addAll(val.fTraces); + } + } + + // For debugging lock issues + private Map<Thread, DebugLockInfo> fLockDebugging; + + // For debugging lock issues + private static DebugLockInfo getLockInfo(Map<Thread, DebugLockInfo> lockDebugging) { + assert sDEBUG_LOCKS; + + Thread key = Thread.currentThread(); + DebugLockInfo result= lockDebugging.get(key); + if (result == null) { + result= new DebugLockInfo(); + lockDebugging.put(key, result); + } + return result; + } + + // For debugging lock issues + static void incReadLock(Map<Thread, DebugLockInfo> lockDebugging) { + DebugLockInfo info = getLockInfo(lockDebugging); + info.fReadLocks++; + if (info.addTrace() > 10) { + outputReadLocks(lockDebugging); + } + } + + // For debugging lock issues + @SuppressWarnings("nls") + static void decReadLock(Map<Thread, DebugLockInfo> lockDebugging) throws AssertionError { + DebugLockInfo info = getLockInfo(lockDebugging); + if (info.fReadLocks <= 0) { + outputReadLocks(lockDebugging); + throw new AssertionError("Superfluous releaseReadLock"); + } + if (info.fWriteLocks != 0) { + outputReadLocks(lockDebugging); + throw new AssertionError("Releasing readlock while holding write lock"); + } + if (--info.fReadLocks == 0) { + lockDebugging.remove(Thread.currentThread()); + } else { + info.addTrace(); + } + } + + // For debugging lock issues + @SuppressWarnings("nls") + private void incWriteLock(int giveupReadLocks) throws AssertionError { + DebugLockInfo info = getLockInfo(this.fLockDebugging); + if (info.fReadLocks != giveupReadLocks) { + outputReadLocks(this.fLockDebugging); + throw new AssertionError("write lock with " + giveupReadLocks + " readlocks, expected " + info.fReadLocks); + } + if (info.fWriteLocks != 0) + throw new AssertionError("Duplicate write lock"); + info.fWriteLocks++; + } + + // For debugging lock issues + private void decWriteLock(int establishReadLocks) throws AssertionError { + DebugLockInfo info = getLockInfo(this.fLockDebugging); + if (info.fReadLocks != establishReadLocks) + throw new AssertionError("release write lock with " + establishReadLocks + " readlocks, expected " + info.fReadLocks); //$NON-NLS-1$ //$NON-NLS-2$ + if (info.fWriteLocks != 1) + throw new AssertionError("Wrong release write lock"); //$NON-NLS-1$ + info.fWriteLocks= 0; + if (info.fReadLocks == 0) { + this.fLockDebugging.remove(Thread.currentThread()); + } + } + + // For debugging lock issues + @SuppressWarnings("nls") + private long reportBlockedWriteLock(long start, int giveupReadLocks) { + long now= System.currentTimeMillis(); + if (now >= start + BLOCKED_WRITE_LOCK_OUTPUT_INTERVAL) { + System.out.println(); + System.out.println("Blocked writeLock"); + System.out.println(" lockcount= " + this.lockCount + ", giveupReadLocks=" + giveupReadLocks + ", waitingReaders=" + this.waitingReaders); + outputReadLocks(this.fLockDebugging); + start= now; + } + return start; + } + + // For debugging lock issues + @SuppressWarnings("nls") + private static void outputReadLocks(Map<Thread, DebugLockInfo> lockDebugging) { + System.out.println("--------------------- Lock Debugging -------------------------"); + for (Thread th: lockDebugging.keySet()) { + DebugLockInfo info = lockDebugging.get(th); + info.write(th.getName()); + } + System.out.println("---------------------------------------------------------------"); + } + + // For debugging lock issues + public void adjustThreadForReadLock(Map<Thread, DebugLockInfo> lockDebugging) { + for (Thread th : lockDebugging.keySet()) { + DebugLockInfo val= lockDebugging.get(th); + if (val.fReadLocks > 0) { + DebugLockInfo myval= this.fLockDebugging.get(th); + if (myval == null) { + myval= new DebugLockInfo(); + this.fLockDebugging.put(th, myval); + } + myval.inc(val); + for (int i = 0; i < val.fReadLocks; i++) { + decReadLock(this.fLockDebugging); + } + } + } + } + + public NdNode getNode(long address, short nodeType) throws IndexException { + return this.fNodeTypeRegistry.createNode(this, address, nodeType); + } + + public <T extends NdNode> ITypeFactory<T> getTypeFactory(short nodeType) { + return this.fNodeTypeRegistry.getTypeFactory(nodeType); + } + + /** + * Returns the type ID for the given class + */ + public short getNodeType(Class<? extends NdNode> toQuery) { + return this.fNodeTypeRegistry.getTypeForClass(toQuery); + } + + private void deleteIfUnreferenced(long address) { + if (address == 0) { + return; + } + short nodeType = NdNode.NODE_TYPE.get(this, address); + + // Look up the type + ITypeFactory<? extends NdNode> factory1 = getTypeFactory(nodeType); + + if (factory1.isReadyForDeletion(this, address)) { + // Call its destructor + factory1.destruct(this, address); + + // Free up its memory + getDB().free(address, (short)(Database.POOL_FIRST_NODE_TYPE + nodeType)); + } + } + + public void delete(long address) { + if (address == 0) { + return; + } + short nodeType = NdNode.NODE_TYPE.get(this, address); + + // Look up the type + ITypeFactory<? extends NdNode> factory1 = getTypeFactory(nodeType); + + // Call its destructor + factory1.destruct(this, address); + + // Free up its memory + getDB().free(address, (short)(Database.POOL_FIRST_NODE_TYPE + nodeType)); + + // If this node was in the list of pending deletions, remove it since it's now been deleted + if (this.pendingDeletions.containsKey(address)) { + logDoubleDeletion(address); + this.pendingDeletions.remove(address); + } + } + + public NdNodeTypeRegistry<NdNode> getTypeRegistry() { + return this.fNodeTypeRegistry; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/NdLinkedList.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/NdLinkedList.java new file mode 100644 index 000000000..cfce2080e --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/NdLinkedList.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import org.eclipse.jdt.internal.core.nd.db.IndexException; + +public final class NdLinkedList<T> { + private final NdRawLinkedList rawList; + final ITypeFactory<T> elementFactory; + + public static interface ILinkedListVisitor<T> { + public void visit(T record, short metadataBits, int index) throws IndexException; + } + + public NdLinkedList(Nd nd, long address, ITypeFactory<T> elementFactory, int recordsInFirstBlock, + int recordsInSubsequentBlocks) { + this(nd, address, elementFactory, recordsInFirstBlock, recordsInSubsequentBlocks, 0); + } + + public NdLinkedList(Nd nd, long address, ITypeFactory<T> elementFactory, int recordsInFirstBlock, + int recordsInSubsequentBlocks, int metadataBitsPerElement) { + this.rawList = new NdRawLinkedList(nd, address, elementFactory.getRecordSize(), recordsInFirstBlock, + recordsInSubsequentBlocks, metadataBitsPerElement); + this.elementFactory = elementFactory; + } + + /** + * Computes the size of this list. This is an O(n) operation. + * + * @return the size of this list + * @throws IndexException + */ + public int size() throws IndexException { + return this.rawList.size(); + } + + public T addMember(short metadataBits) throws IndexException { + long address = this.rawList.addMember(metadataBits); + + return this.elementFactory.create(this.rawList.getNd(), address); + } + + public void accept(final ILinkedListVisitor<T> visitor) throws IndexException { + final NdRawLinkedList localRawList = this.rawList; + final ITypeFactory<T> localElementFactory = this.elementFactory; + localRawList.accept(new NdRawLinkedList.ILinkedListVisitor() { + @Override + public void visit(long address, short metadataBits, int index) throws IndexException { + visitor.visit(localElementFactory.create(localRawList.getNd(), + address), metadataBits, index); + } + }); + } + + public static <T> ITypeFactory<NdLinkedList<T>> getFactoryFor( + final ITypeFactory<T> elementFactory, final int recordsInFirstBlock, final int recordsInSubsequentBlocks) { + return getFactoryFor(elementFactory, recordsInSubsequentBlocks, 0); + } + + public static <T> ITypeFactory<NdLinkedList<T>> getFactoryFor( + final ITypeFactory<T> elementFactory, final int recordsInFirstBlock, final int recordsInSubsequentBlocks, + final int metadataBitsPerElement) { + + return new AbstractTypeFactory<NdLinkedList<T>>() { + public NdLinkedList<T> create(Nd dom, long address) { + return new NdLinkedList<T>(dom, address, elementFactory, recordsInFirstBlock, recordsInSubsequentBlocks, metadataBitsPerElement); + } + + @Override + public int getRecordSize() { + return NdRawLinkedList.recordSize(elementFactory.getRecordSize(), recordsInFirstBlock, + metadataBitsPerElement); + } + + @Override + public Class<?> getElementClass() { + return NdLinkedList.class; + } + + @Override + public boolean hasDestructor() { + return true; + } + + @Override + public void destructFields(Nd dom, long address) { + create(dom, address).destruct(); + } + + @Override + public void destruct(Nd dom, long address) { + destructFields(dom, address); + } + }; + } + + /** + * + */ + protected void destruct() { + if (this.elementFactory.hasDestructor()) { + final Nd nd = this.rawList.getNd(); + this.rawList.accept(new NdRawLinkedList.ILinkedListVisitor() { + @Override + public void visit(long address, short metadataBits, int index) throws IndexException { + NdLinkedList.this.elementFactory.destruct(nd, address); + } + }); + } + this.rawList.destruct(); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/NdNode.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/NdNode.java new file mode 100644 index 000000000..67d039b35 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/NdNode.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.db.IndexException; +import org.eclipse.jdt.internal.core.nd.field.FieldShort; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +/** + * This is a basic node in the network database. + */ +public abstract class NdNode implements IDestructable { + public static final FieldShort NODE_TYPE; + + public static final StructDef<NdNode> type; + + static { + type = StructDef.create(NdNode.class); + NODE_TYPE = type.addShort(); + type.done(); + } + + public final long address; + private Nd nd; + + public static long addressOf(NdNode nullable) { + if (nullable == null) { + return 0; + } + return nullable.address; + } + + /** + * Load a node from the specified address in the given database. Return null if a node cannot + * be loaded. + * + * @param nd The {@link Nd} from which to load the node. + * @param address The address of the node in the given {@link Nd}. + * @return The {@link NdNode} at the specified location or null if a node cannot be loaded. + * @When there is a problem reading the given {@link Nd}'s Database + */ + public static NdNode load(Nd nd, long address) { + if (address == 0) { + return null; + } + + return nd.getNode(address, NODE_TYPE.get(nd, address)); + } + + @SuppressWarnings("unchecked") + public static <T extends NdNode> T load(Nd nd, long address, Class<T> clazz) { + if (address == 0) { + return null; + } + + NdNode result = nd.getNode(address, NODE_TYPE.get(nd, address)); + + if (!clazz.isAssignableFrom(result.getClass())) { + throw new IndexException("Found wrong data type at address " + address + ". Expected a subclass of " + //$NON-NLS-1$//$NON-NLS-2$ + clazz + " but found " + result.getClass()); //$NON-NLS-1$ + } + + return (T)result; + } + + /** + * Invokes the destructor on this node and frees up its memory + */ + public final void delete() { + getNd().delete(this.address); + } + + protected NdNode(Nd nd, long address) { + this.nd = nd; + this.address = address; + } + + protected NdNode(Nd nd) { + Database db = nd.getDB(); + this.nd = nd; + + short nodeType = nd.getNodeType(getClass()); + ITypeFactory<? extends NdNode> factory1 = nd.getTypeFactory(nodeType); + + this.address = db.malloc(factory1.getRecordSize(), (short)(Database.POOL_FIRST_NODE_TYPE + nodeType)); + + NODE_TYPE.put(nd, this.address, nodeType); + } + + protected Database getDB() { + return this.nd.getDB(); + } + + public Nd getNd() { + return this.nd; + } + + /** + * Return a value to uniquely identify the node within the factory that is responsible for loading + * instances of this node from the {@link Nd}. + * <b> + */ + public short getNodeType() { + return this.nd.getNodeType(getClass()); + } + + public final long getAddress() { + return this.address; + } + + public final long getBindingID() { + return this.address; + } + + @Override + public final boolean equals(Object obj) { + if (obj == this) + return true; + if (obj instanceof NdNode) { + NdNode other = (NdNode) obj; + return getNd() == other.getNd() && this.address == other.address; + } + + return super.equals(obj); + } + + @Override + public final int hashCode() { + return (int) (this.address >> Database.BLOCK_SIZE_DELTA_BITS); + } + + public void accept(INdVisitor visitor) { + // No children here. + } + + /** + * Return an value to globally identify the given node within the given linkage. This value + * can be used for comparison with other {@link NdNode}s. + */ + public static int getNodeId(int linkageID, int nodeType) { + return (linkageID << 16) | (nodeType & 0xffff); + } + + /** + * Convenience method for fetching a byte from the database. + * @param offset Location of the byte. + * @return a byte from the database. + */ + protected byte getByte(long offset) { + return getDB().getByte(offset); + } + + /** + * Returns the bit at the specified offset in a bit vector. + * @param bitVector Bits. + * @param offset The position of the desired bit. + * @return the bit at the specified offset. + */ + protected static boolean getBit(int bitVector, int offset) { + int mask = 1 << offset; + return (bitVector & mask) != 0; + } + + /** + * Dispose this {@link NdNode}. Subclasses should extend this method to perform any high-level node-specific cleanup. + * This will be invoked prior to disposing the fields. Implementations must invoke their parent's destruct method + * and should not destruct the fields. + * <p> + * If an external object wants to destroy a node, they should invoke {@link NdNode#delete} rather than this + * method. + */ + public void destruct() { + // Nothing to do by default. Subclasses will provide an implementation if necessary. + } + +}
\ No newline at end of file diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/NdNodeTypeRegistry.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/NdNodeTypeRegistry.java new file mode 100644 index 000000000..b76057dd5 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/NdNodeTypeRegistry.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.internal.core.nd.db.IndexException; + +/** + * Maps integer constants onto factories for {@link NdNode} objects. + */ +public class NdNodeTypeRegistry<R> { + private final Map<Short, ITypeFactory<? extends R>> types = new HashMap<>(); + private final BitSet reserved = new BitSet(); + private final Map<Class<?>, Short> registeredClasses = new HashMap<>(); + + /** + * Registers a class to be used with this node type registry. Note that if we ever want to stop registering a type + * name in the future, its fully-qualified class name should be passed to reserve(...) to prevent its hashfrom being + * reused in the future. + */ + public <T extends R> void register(int typeId, ITypeFactory<T> toRegister) { + if ((typeId & 0xFFFF0000) != 0) { + throw new IllegalArgumentException("The typeId " + typeId + " does not fit within a short int"); //$NON-NLS-1$//$NON-NLS-2$ + } + short shortTypeId = (short)typeId; + String fullyQualifiedClassName = toRegister.getElementClass().getName(); + + if (this.types.containsKey(typeId) || this.reserved.get(typeId)) { + throw new IllegalArgumentException( + "The type id " + typeId + " for class " + fullyQualifiedClassName + " is already in use."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + this.types.put(shortTypeId, toRegister); + this.registeredClasses.put(toRegister.getElementClass(), shortTypeId); + } + + /** + * Reserves the given node class name, such that its hash cannot be used by any other node registered with + * "register". If we ever want to unregister a given Class from the type registry, its class name should be reserved + * using this method. Doing so will prevent its type ID from being reused by another future class. + */ + public void reserve(short typeId) { + if (this.types.containsKey(typeId) || this.reserved.get(typeId)) { + throw new IllegalArgumentException("The type ID " + typeId + " is already in use"); //$NON-NLS-1$ //$NON-NLS-2$ + } + this.reserved.set(typeId); + } + + /** + * Returns the class associated with the given type or null if the given type ID is not known + */ + public ITypeFactory<? extends R> getClassForType(short type) { + return this.types.get(type); + } + + public R createNode(Nd nd, long address, short nodeType) throws IndexException { + ITypeFactory<? extends R> typeFactory = this.types.get(nodeType); + + return typeFactory.create(nd, address); + } + + public short getTypeForClass(Class<? extends R> toQuery) { + Short classId = this.registeredClasses.get(toQuery); + + if (classId == null) { + throw new IllegalArgumentException(toQuery.getName() + " was not registered as a node type"); //$NON-NLS-1$ + } + return classId; + } + + @SuppressWarnings("unchecked") + public <T extends R> ITypeFactory<T> getTypeFactory(short nodeType) { + ITypeFactory<T> result = (ITypeFactory<T>) this.types.get(nodeType); + + if (result == null) { + throw new IllegalArgumentException("The node type " + nodeType //$NON-NLS-1$ + + " is not registered with this database"); //$NON-NLS-1$ + } + + return result; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/NdRawLinkedList.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/NdRawLinkedList.java new file mode 100644 index 000000000..969a893c0 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/NdRawLinkedList.java @@ -0,0 +1,271 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.db.IndexException; + +/** + * {@link NdRawLinkedList} stores a list of fixed-sized records. Along with the records themselves, there is also + * a bit field associated with each record which can hold a small number of bits of metadata per record. + * The underlying format is as follows: + * + * <pre> + * Bytes Content + * ---------------- + * 4 Pointer to the next block. If this is 0, this is the last block and it is not yet full. The number of + * elements will be stored at the position where the last element would normally start. If this points back + * to the start of the block, this is the last block and it is full. If this holds any other value, the + * block is full and this points to the next block. + * headerSize Bit field for this block (the bits for each element are tightly packed) + * recordSize The content of the first element in the block + * recordSize The content of the second element in the block + * ... repeated recordsPerBlock times + * recordSize If the block is full, this holds the last + * </pre> + * + * stored in linked blocks where each block is an array of record pointers. Each block contains a pointer to the + * subsequent block, so they can be chained. + * <p> + * The size of the blocks are generally hardcoded. All blocks are the same size except for the first block whose size + * may be configured independently. The size of the first block may be zero, in which case the first "block" is + * simply a pointer to the following block or null. + */ +public class NdRawLinkedList { + private static final int NEXT_MEMBER_BLOCK = 0; + private static final int ELEMENT_START_POSITION = NEXT_MEMBER_BLOCK + Database.PTR_SIZE; + + private final long address; + private final Nd nd; + private final int firstBlockRecordCount; + private final int recordCount; + private final int elementRecordSize; + private final int metadataBitsPerRecord; + + // Derived data. Holds the address for the last block we know about + private long lastKnownBlock; + + public static interface ILinkedListVisitor { + public void visit(long address, short metadataBits, int index) throws IndexException; + } + + /** + * @param nd the Nd object + * @param address pointer to the start of the linked list + * @param recordsPerBlock number of records per block. This is normally a hardcoded value. + */ + public NdRawLinkedList(Nd nd, long address, int elementRecordSize, int firstBlockRecordCount, int recordsPerBlock, + int metadataBitsPerRecord) { + assert(recordsPerBlock > 0); + assert(firstBlockRecordCount >= 0); + this.nd = nd; + this.address = address; + this.firstBlockRecordCount = firstBlockRecordCount; + this.recordCount = recordsPerBlock; + this.elementRecordSize = elementRecordSize; + this.lastKnownBlock = address; + this.metadataBitsPerRecord = metadataBitsPerRecord; + } + + /** + * Returns the record size for a linked list with the given element record size and number of + * records per block + */ + public static int recordSize(int elementRecordSize, int recordsPerBlock, int metadataBitsPerRecord) { + int metadataSize = 0; + + if (metadataBitsPerRecord > 0) { + int metadataRecordsPerShort = 16 / metadataBitsPerRecord; + int numberOfShorts = (recordsPerBlock + metadataRecordsPerShort - 1) / metadataRecordsPerShort; + + metadataSize = 2 * numberOfShorts; + } + + return Database.PTR_SIZE + elementRecordSize * recordsPerBlock + metadataSize; + } + + public Nd getNd() { + return this.nd; + } + + private int getElementsInBlock(long currentRecord, long ptr, int currentRecordCount) throws IndexException { + if (ptr == 0 && currentRecordCount > 0) { + return getDB().getInt(getAddressOfElement(currentRecord, currentRecordCount - 1)); + } + return currentRecordCount; + } + + private Database getDB() { + return this.nd.getDB(); + } + + public long getAddress() { + return this.address; + } + + /** + * Adds a new element to the list and returns the record pointer to the start of the newly-allocated object + * + * @param metadataBits the metadata bits to attach to the new member. Use 0 if this list does not use metadata. + */ + public long addMember(short metadataBits) throws IndexException { + Database db = getDB(); + long current = this.lastKnownBlock; + int thisBlockRecordCount = this.firstBlockRecordCount; + while (true) { + long ptr = db.getRecPtr(current + NEXT_MEMBER_BLOCK); + int elementsInBlock = getElementsInBlock(current, ptr, thisBlockRecordCount); + + // If there's room in this block + if (elementsInBlock < thisBlockRecordCount) { + long positionOfElementCount = getAddressOfElement(current, thisBlockRecordCount - 1); + // If there's only one space left + if (elementsInBlock == thisBlockRecordCount - 1) { + // We use the fact that the next pointer points to itself as a sentinel to indicate that the + // block is full and there are no further blocks + db.putRecPtr(current + NEXT_MEMBER_BLOCK, current); + // Zero out the int we've been using to hold the count of elements + db.putInt(positionOfElementCount, 0); + } else { + // Increment the element count + db.putInt(positionOfElementCount, elementsInBlock + 1); + } + + if (this.metadataBitsPerRecord > 0) { + int metadataMask = (1 << this.metadataBitsPerRecord) - 1; + int metadataRecordsPerShort = this.metadataBitsPerRecord == 0 ? 0 + : (16 / this.metadataBitsPerRecord); + metadataBits &= metadataMask; + + int metadataBitOffset = elementsInBlock % metadataRecordsPerShort; + long metadataStart = getAddressOfMetadata(current, thisBlockRecordCount); + int whichShort = elementsInBlock / metadataRecordsPerShort; + long metadataOffset = metadataStart + 2 * whichShort; + short metadataValue = db.getShort(metadataOffset); + + // Resetting the previous visibility bits of the target member. + metadataValue &= ~(metadataMask << metadataBitOffset * this.metadataBitsPerRecord); + // Setting the new visibility bits of the target member. + metadataValue |= metadataBits << metadataBitOffset * this.metadataBitsPerRecord; + + getDB().putShort(metadataOffset, metadataValue); + } + + this.lastKnownBlock = current; + return getAddressOfElement(current, elementsInBlock); + } else { + // When ptr == current, this is a sentinel indicating that the block is full and there are no + // further blocks. If this is the case, create a new block + if (isLastBlock(current, ptr)) { + current = db.malloc( + recordSize(this.elementRecordSize, this.recordCount, this.metadataBitsPerRecord), Database.POOL_LINKED_LIST); + db.putRecPtr(current + NEXT_MEMBER_BLOCK, current); + } else { + thisBlockRecordCount = this.recordCount; + // Else, there are more blocks following this one so advance + current = ptr; + } + } + } + } + + private long getAddressOfElement(long blockRecordStart, int elementNumber) { + return blockRecordStart + ELEMENT_START_POSITION + elementNumber * this.elementRecordSize; + } + + private long getAddressOfMetadata(long blockRecordStart, int blockRecordCount) { + return getAddressOfElement(blockRecordStart, blockRecordCount); + } + + public void accept(ILinkedListVisitor visitor) throws IndexException { + int count = 0; + Database db = getDB(); + + int blockRecordCount = this.firstBlockRecordCount; + int metadataMask = (1 << this.metadataBitsPerRecord) - 1; + int metadataRecordsPerShort = this.metadataBitsPerRecord == 0 ? 0 : (16 / this.metadataBitsPerRecord); + long current = this.address; + while (true) { + long ptr = db.getRecPtr(current + NEXT_MEMBER_BLOCK); + int elementsInBlock = getElementsInBlock(current, ptr, blockRecordCount); + + long metadataStart = getAddressOfMetadata(current, blockRecordCount); + for (int idx = 0; idx < elementsInBlock; idx++) { + long elementRecord = getAddressOfElement(current, idx); + + short metadataBits = 0; + + if (metadataRecordsPerShort > 0) { + int metadataBitOffset = idx % metadataRecordsPerShort; + int whichShort = idx / metadataRecordsPerShort; + long metadataOffset = metadataStart + 2 * whichShort; + metadataBits = getDB().getShort(metadataOffset); + + metadataBits >>>= metadataBits * metadataBitOffset; + metadataBits &= metadataMask; + } + + visitor.visit(elementRecord, metadataBits, count++); + } + + blockRecordCount = this.recordCount; + + if (isLastBlock(current, ptr)) { + return; + } + + current = ptr; + } + } + + public void destruct() throws IndexException { + Database db = getDB(); + long current = this.address; + while (true) { + long ptr = db.getRecPtr(current + NEXT_MEMBER_BLOCK); + db.free(current, Database.POOL_LINKED_LIST); + + if (isLastBlock(current, ptr)) { + return; + } + + current = ptr; + } + } + + private boolean isLastBlock(long blockAddress, long pointerToNextBlock) { + return pointerToNextBlock == 0 || pointerToNextBlock == blockAddress; + } + + /** + * Returns the number of elements in this list. This is an O(n) operation. + * @throws IndexException + */ + public int size() throws IndexException { + int count = 0; + Database db = getDB(); + int currentRecordCount = this.firstBlockRecordCount; + long current = this.address; + while (true) { + long ptr = db.getRecPtr(current + NEXT_MEMBER_BLOCK); + count += getElementsInBlock(current, ptr, currentRecordCount); + + if (isLastBlock(current, ptr)) { + break; + } + + currentRecordCount = this.recordCount; + current = ptr; + } + + return count; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/Package.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/Package.java new file mode 100644 index 000000000..1782bd48c --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/Package.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.JavaCore; + +/* package */ class Package { + public static String PLUGIN_ID = JavaCore.PLUGIN_ID; + + public static void log(Throwable e) { + String msg = e.getMessage(); + if (msg == null) { + log("Error", e); //$NON-NLS-1$ + } else { + log("Error: " + msg, e); //$NON-NLS-1$ + } + } + + public static void log(String message, Throwable e) { + log(createStatus(message, e)); + } + + public static void logInfo(String message) { + log(createStatus(IStatus.INFO, message, null)); + } + + public static IStatus createStatus(int statusCode, String msg, Throwable e) { + return new Status(statusCode, PLUGIN_ID, msg, e); + } + + public static IStatus createStatus(String msg, Throwable e) { + return new Status(IStatus.ERROR, PLUGIN_ID, msg, e); + } + + public static IStatus createStatus(String msg) { + return new Status(IStatus.ERROR, PLUGIN_ID, msg); + } + + public static void log(IStatus status) { + JavaCore.getPlugin().getLog().log(status); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/Pointer.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/Pointer.java new file mode 100644 index 000000000..bd8a59783 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/Pointer.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import org.eclipse.jdt.internal.core.nd.db.Database; + +/** + * Points to a concrete type, NOT one of its subclasses. This should not be used for node + * pointers, since they are stored as a pointer to the base class. If you want a pointer to + * a node, use a NodeFieldDefinition instead. + */ +public class Pointer<T> { + private final Nd nd; + private final long address; + private ITypeFactory<T> targetFactory; + + public Pointer(Nd nd, long address, ITypeFactory<T> targetFactory) { + this.nd = nd; + this.address = address; + this.targetFactory = targetFactory; + } + + public T get() { + long ptr = this.nd.getDB().getRecPtr(this.address); + + if (ptr == 0) { + return null; + } + + return this.targetFactory.create(this.nd, ptr); + } + + public static <T> ITypeFactory<Pointer<T>> getFactory(final ITypeFactory<T> targetFactory) { + if (NdNode.class.isAssignableFrom(targetFactory.getElementClass())) { + throw new IllegalArgumentException("Don't use Pointer<T> for references to NdNode"); //$NON-NLS-1$ + } + return new AbstractTypeFactory<Pointer<T>>() { + @Override + public Pointer<T> create(Nd dom, long address) { + return new Pointer<T>(dom, address, targetFactory); + } + + @Override + public int getRecordSize() { + return Database.PTR_SIZE; + } + + @Override + public Class<?> getElementClass() { + return Pointer.class; + } + }; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/PrimitiveTypes.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/PrimitiveTypes.java new file mode 100644 index 000000000..018506890 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/PrimitiveTypes.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import org.eclipse.jdt.internal.core.nd.db.Database; + +/** + * Holds type factories for all primitive types known to the Database. + */ +public class PrimitiveTypes { + public static final ITypeFactory<Long> Pointer = new AbstractTypeFactory<Long>() { + @Override + public Long create(Nd dom, long address) { + return dom.getDB().getRecPtr(address); + } + + @Override + public int getRecordSize() { + return Database.PTR_SIZE; + } + + @Override + public Class<?> getElementClass() { + return Long.class; + } + }; + + public static final ITypeFactory<Short> Short = new AbstractTypeFactory<Short>() { + @Override + public Short create(Nd dom, long address) { + return dom.getDB().getShort(address); + } + + @Override + public int getRecordSize() { + return Database.SHORT_SIZE; + } + + @Override + public Class<?> getElementClass() { + return Short.class; + } + }; + + public static final ITypeFactory<Integer> Integer = new AbstractTypeFactory<Integer>() { + @Override + public Integer create(Nd dom, long address) { + return dom.getDB().getInt(address); + } + + @Override + public int getRecordSize() { + return Database.INT_SIZE; + } + + @Override + public Class<?> getElementClass() { + return Integer.class; + } + }; +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/RawGrowableArray.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/RawGrowableArray.java new file mode 100644 index 000000000..df7ca4ee9 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/RawGrowableArray.java @@ -0,0 +1,593 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.db.IndexException; +import org.eclipse.jdt.internal.core.nd.field.FieldInt; +import org.eclipse.jdt.internal.core.nd.field.FieldPointer; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +/** + * Implements a growable array of pointers that supports constant-time insertions and removals. Items are inserted at + * the end of the array, and each insertion hands back a unique identifier that can be used to remove the item quickly + * at a later time. + * <p> + * The memory format contains a header is as follows: + * <p> + * + * <pre> + * Byte Meaning + * -------------------------------------------- + * 0..3 Pointer to the growable block. Null if the number of records <= inlineRecordCount + * 4..7 Record [0] + * 8..11 Record [1] + * ... + * k...k+4 Record [inlineRecordCount-1] + * </pre> + * + * As shown above, the first few records are stored inline with the array. inlineRecordCount is a tunable parameter + * which may be 0. If there are fewer than inlineRecordCount records, there is no growable block and all records are + * stored in the header. Storing the first few records in the header is intended as an optimization for very small + * arrays in the case where small arrays are expected to be a common case. If there are fewer than inlineRecordCount + * records stored in the array, the size of the array is not stored explicitly. It is computed on demand by searching + * for the first null entry among the inline records. + * + * <p> + * The memory format for a growable block is as follows: + * <p> + * + * <pre> + * Byte Meaning + * -------------------------------------------- + * 0..3 Size of the array, including all inline records. This is also the index at which the next entry will + * be inserted + * 4..7 Capacity of this growable block. + * 8..11 Record [n] + * 12..15 Record [n+1] + * ... + * k...k+4 Record [blockSize-1] + * </pre> + * + * <p> + * The growable block itself begins with a 4-byte int holding the size of the array, followed by a 4-byte int holding + * the capacity of the growable block. In the event that the array is larger than + * {@link GrowableBlockHeader#MAX_GROWABLE_SIZE} enough to be using a metablock, there will be multiple growable blocks + * in use. In this case, the size and capacity stored in the metablock is used for the array and the size and capacity + * stored in each growable block will be filled in with 0s. + * <p> + * If capacity <= MAX_BLOCK_SIZE then this is a normal block containing a flat array of record pointers starting from + * the element numbered inlineRecordCount. If capacity > MAX_BLOCK_SIZE then then it is a metablock which holds record + * pointers to separate growable blocks, each of which holds exactly MAX_BLOCK_SIZE elements. + * <p> + * Every time an element is inserted in the array, the add method returns the element's index. Indices can be used to + * remove elements in constant time, but will be reassigned by the RawGrowableArray during removes by swapping the + * removed element with the last element in the array. If the owner of the array is keeping track of indices, it should + * update the relevant indices on remove. + * <p> + * The array itself is tightly packed. When an element is removed, the last element in the array is swapped into its + * location. Anyone keeping track of indices may rely on the fact that they are consecutive integers. + * <p> + * These arrays preserve insertion order until the first call to "remove". If element order matters, you should not + * remove individual elements but should instead destroy and rebuild the entire array. + * <p> + * Element additions and removals run in constant amortized time. + * <p> + * There are a lot of ints and longs used in the implementation of this class. In order to help clarify their function, + * they get the following suffixes: + * <ul> + * <li>index - holds an index into the array + * <li>size - holds a count of the number of indices + * <li>value - holds one of the pointer values inserted into the array + * <li>address - holds a pointer into the database (refers to a full 8-byte long, not the compressed 4-byte version) + * <li>bytes - holds the size (in bytes) of something in the database + * <li>block - holds a block number (in the case where a metablock is in use, the growable blocks are identified by + * block numbers). + * <li>blockCount - holds a number of blocks + * </ul> + */ +public final class RawGrowableArray { + private static final FieldPointer GROWABLE_BLOCK_ADDRESS; + private static final int ARRAY_HEADER_BYTES; + + private static final StructDef<RawGrowableArray> type; + + static { + type = StructDef.createAbstract(RawGrowableArray.class); + GROWABLE_BLOCK_ADDRESS = type.addPointer(); + type.done(); + + ARRAY_HEADER_BYTES = type.size(); + } + + private static final class GrowableBlockHeader { + public static final FieldInt ARRAY_SIZE; + public static final FieldInt ALLOCATED_SIZE; + public static final int GROWABLE_BLOCK_HEADER_BYTES; + public static final int MAX_GROWABLE_SIZE; + + @SuppressWarnings("hiding") + private static final StructDef<GrowableBlockHeader> type; + + static { + type = StructDef.createAbstract(GrowableBlockHeader.class); + + ARRAY_SIZE = type.addInt(); + ALLOCATED_SIZE = type.addInt(); + type.done(); + + GROWABLE_BLOCK_HEADER_BYTES = type.size(); + + MAX_GROWABLE_SIZE = (Database.MAX_MALLOC_SIZE - GROWABLE_BLOCK_HEADER_BYTES) + / Database.PTR_SIZE; + } + } + + private final int inlineSize; + + public RawGrowableArray(int inlineRecords) { + this.inlineSize = inlineRecords; + } + + public static int getMaxGrowableBlockSize() { + return GrowableBlockHeader.MAX_GROWABLE_SIZE; + } + + /** + * Returns the size of the array. + * + * @param address address of the array + * @return the array size, in number elements + */ + public int size(Nd nd, long address) { + Database db = nd.getDB(); + long growableBlockAddress = GROWABLE_BLOCK_ADDRESS.get(nd, address); + + if (growableBlockAddress == 0) { + // If there is no growable block or metablock, then the size is determined by the position of the first + // null pointer among the inline records. + long inlineRecordStartAddress = address + ARRAY_HEADER_BYTES; + for (int index = 0; index < this.inlineSize; index++) { + long nextAddress = inlineRecordStartAddress + index * Database.PTR_SIZE; + + long nextValue = db.getRecPtr(nextAddress); + if (nextValue == 0) { + return index; + } + } + return this.inlineSize; + } + return GrowableBlockHeader.ARRAY_SIZE.get(nd, growableBlockAddress); + } + + /** + * Adds the given value to the array. Returns an index which can be later passed into remove in order to + * remove the element at a later time. + */ + public int add(Nd nd, long address, long value) { + if (value == 0) { + throw new IllegalArgumentException("Null pointers cannot be inserted into " + getClass().getName()); //$NON-NLS-1$ + } + Database db = nd.getDB(); + + int insertionIndex = size(nd, address); + int newSize = insertionIndex + 1; + + ensureCapacity(nd, address, newSize); + long recordAddress = getAddressOfRecord(nd, address, insertionIndex); + db.putRecPtr(recordAddress, value); + setSize(nd, address, newSize); + return insertionIndex; + } + + /** + * Returns the element at the given index (nonzero). The given index must be < size(). + */ + public long get(Nd nd, long address, int index) { + long recordAddress = getAddressOfRecord(nd, address, index); + return nd.getDB().getRecPtr(recordAddress); + } + + /** + * Ensures that the array contains at least enough space allocated to fit the given number of new elements. + */ + public void ensureCapacity(Nd nd, long address, int desiredSize) { + int growableBlockNeededSize = desiredSize - this.inlineSize; + long growableBlockAddress = GROWABLE_BLOCK_ADDRESS.get(nd, address); + int growableBlockCurrentSize = growableBlockAddress == 0 ? 0 + : GrowableBlockHeader.ALLOCATED_SIZE.get(nd, growableBlockAddress); + + // The growable region is already large enough. + if (growableBlockNeededSize <= growableBlockCurrentSize) { + return; + } + + Database db = nd.getDB(); + + int neededBlockSize = getGrowableRegionSizeFor(desiredSize); + if (neededBlockSize > GrowableBlockHeader.MAX_GROWABLE_SIZE) { + // We need a metablock. + long metablockAddress = growableBlockAddress; + + if (!(growableBlockCurrentSize > GrowableBlockHeader.MAX_GROWABLE_SIZE)) { + // We weren't using a metablock previously + int currentSize = size(nd, address); + // Need to convert to using metablocks. + long firstGrowableBlockAddress = resizeBlock(nd, address, GrowableBlockHeader.MAX_GROWABLE_SIZE); + + metablockAddress = db.malloc(computeBlockBytes(GrowableBlockHeader.MAX_GROWABLE_SIZE), Database.POOL_GROWABLE_ARRAY); + GrowableBlockHeader.ARRAY_SIZE.put(nd, metablockAddress, currentSize); + GrowableBlockHeader.ALLOCATED_SIZE.put(nd, metablockAddress, + GrowableBlockHeader.MAX_GROWABLE_SIZE); + + // Link the first block into the metablock. + db.putRecPtr(metablockAddress + GrowableBlockHeader.GROWABLE_BLOCK_HEADER_BYTES, + firstGrowableBlockAddress); + GROWABLE_BLOCK_ADDRESS.put(nd, address, metablockAddress); + } + + // neededBlockSize should always be a multiple of the max block size when metablocks are in use + assert neededBlockSize % GrowableBlockHeader.MAX_GROWABLE_SIZE == 0; + // Create extra growable blocks if necessary. + int requiredBlockCount = neededBlockSize / GrowableBlockHeader.MAX_GROWABLE_SIZE; + int currentAllocatedSize = GrowableBlockHeader.ALLOCATED_SIZE.get(nd, metablockAddress); + assert currentAllocatedSize % GrowableBlockHeader.MAX_GROWABLE_SIZE == 0; + int currentBlockCount = currentAllocatedSize / GrowableBlockHeader.MAX_GROWABLE_SIZE; + + for (int nextBlock = currentBlockCount; nextBlock < requiredBlockCount; nextBlock++) { + long nextBlockAddress = db.malloc(computeBlockBytes(GrowableBlockHeader.MAX_GROWABLE_SIZE), Database.POOL_GROWABLE_ARRAY); + + db.putRecPtr(metablockAddress + GrowableBlockHeader.GROWABLE_BLOCK_HEADER_BYTES + + nextBlock * Database.PTR_SIZE, nextBlockAddress); + } + + GrowableBlockHeader.ALLOCATED_SIZE.put(nd, metablockAddress, neededBlockSize); + } else { + long newBlockAddress = resizeBlock(nd, address, neededBlockSize); + + GROWABLE_BLOCK_ADDRESS.put(nd, address, newBlockAddress); + } + } + + /** + * Allocates a new normal block, copies the contents of the old block to it, and deletes the old block. Should not + * be used if the array is using metablocks. Returns the address of the newly-allocated block. + */ + private long resizeBlock(Nd nd, long address, int newBlockSize) { + Database db = nd.getDB(); + long oldGrowableBlockAddress = GROWABLE_BLOCK_ADDRESS.get(nd, address); + + // Check if the existing block is already exactly the right size + if (oldGrowableBlockAddress != 0) { + if (newBlockSize == 0) { + db.free(oldGrowableBlockAddress, Database.POOL_GROWABLE_ARRAY); + return 0; + } + + int oldAllocatedSize = GrowableBlockHeader.ALLOCATED_SIZE.get(nd, oldGrowableBlockAddress); + if (oldAllocatedSize == newBlockSize) { + return oldGrowableBlockAddress; + } + } + + int arraySize = size(nd, address); + int numToCopySize = Math.min(Math.max(0, arraySize - this.inlineSize), newBlockSize); + long newGrowableBlockAddress = db.malloc(computeBlockBytes(newBlockSize), Database.POOL_GROWABLE_ARRAY); + + if (oldGrowableBlockAddress != 0) { + db.memcpy(newGrowableBlockAddress, oldGrowableBlockAddress, computeBlockBytes(numToCopySize)); + db.free(oldGrowableBlockAddress, Database.POOL_GROWABLE_ARRAY); + } + + GrowableBlockHeader.ARRAY_SIZE.put(nd, newGrowableBlockAddress, arraySize); + GrowableBlockHeader.ALLOCATED_SIZE.put(nd, newGrowableBlockAddress, newBlockSize); + return newGrowableBlockAddress; + } + + private int computeBlockBytes(int size) { + return size * Database.PTR_SIZE + GrowableBlockHeader.GROWABLE_BLOCK_HEADER_BYTES; + } + + /** + * @param size + */ + private void setSize(Nd nd, long address, int size) { + long growableBlockAddress = GROWABLE_BLOCK_ADDRESS.get(nd, address); + + // If we're not using a growable block, we don't explicitly store the size + if (growableBlockAddress == 0) { + return; + } + + GrowableBlockHeader.ARRAY_SIZE.put(nd, growableBlockAddress, size); + } + + /** + * Returns a record address given a record number + */ + private long getAddressOfRecord(Nd nd, long address, int index) { + int growableBlockRelativeIndex = index - this.inlineSize; + + if (growableBlockRelativeIndex >= 0) { + Database db = nd.getDB(); + // This record is located within the growable region + long growableBlockAddress = GROWABLE_BLOCK_ADDRESS.get(nd, address); + int size = size(nd, address); + + // We use reads of 1 past the end of the array to handle insertions. + if (index > size) { + throw new IndexException( + "Record index " + index + " out of range. Array contains " + size + " elements"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + int growableBlockSize = GrowableBlockHeader.ALLOCATED_SIZE.get(nd, growableBlockAddress); + long dataStartAddress = growableBlockAddress + GrowableBlockHeader.GROWABLE_BLOCK_HEADER_BYTES; + + if (growableBlockSize > GrowableBlockHeader.MAX_GROWABLE_SIZE) { + // If this array is so big that it's using a metablock, look up the correct sub-block and use the + // correct address within the sub-block + int blockRelativeIndex = growableBlockRelativeIndex % GrowableBlockHeader.MAX_GROWABLE_SIZE; + int block = growableBlockRelativeIndex / GrowableBlockHeader.MAX_GROWABLE_SIZE; + + dataStartAddress = db.getRecPtr(dataStartAddress + block * Database.PTR_SIZE) + + GrowableBlockHeader.GROWABLE_BLOCK_HEADER_BYTES; + growableBlockRelativeIndex = blockRelativeIndex; + } + + return dataStartAddress + growableBlockRelativeIndex * Database.PTR_SIZE; + } else { + // This record is one of the ones inlined in the header + return address + ARRAY_HEADER_BYTES + index * Database.PTR_SIZE; + } + } + + /** + * Removes an entry from the array, given an element index. If the given index is not the last element + * in the list, the last element will have its index swapped with the removed element. If another element + * was swapped into the position of the removed element, this returns the value of that element. Otherwise, + * it returns 0. + */ + public long remove(Nd nd, long address, int index) { + int currentSize = size(nd, address); + int lastElementIndex = currentSize - 1; + + Database db = nd.getDB(); + if (index > lastElementIndex || index < 0) { + throw new IndexException("Attempt to remove nonexistent element " + index //$NON-NLS-1$ + + " from an array of size " + (lastElementIndex + 1)); //$NON-NLS-1$ + } + + long toRemoveAddress = getAddressOfRecord(nd, address, index); + long returnValue; + // If we're removing the last element + if (index == lastElementIndex) { + returnValue = 0; + // Clear out the removed element + db.putRecPtr(toRemoveAddress, 0); + } else { + long lastElementAddress = getAddressOfRecord(nd, address, lastElementIndex); + long lastElementValue = db.getRecPtr(lastElementAddress); + + // Move the last element into the position occupied by the element being removed (this is a noop if + // removing the last element) + db.putRecPtr(toRemoveAddress, lastElementValue); + + // Clear out the last element + db.putRecPtr(lastElementAddress, 0); + + returnValue = lastElementValue; + } + + // Update the array size + setSize(nd, address, currentSize - 1); + repackIfNecessary(nd, address, currentSize); + + return returnValue; + } + + /** + * Checks if we should reduce the amount of allocated in the growable region, such that the array can hold the given + * number of elements. + * + * @param desiredSize + * the new current size of the array or 0 to free up all memory + */ + private void repackIfNecessary(Nd nd, long address, int desiredSize) { + long growableBlockAddress = GROWABLE_BLOCK_ADDRESS.get(nd, address); + + // If there is no growable block then the array is already as small as we can make it. Nothing to do. + if (growableBlockAddress == 0) { + return; + } + + int desiredGrowableSize = desiredSize - this.inlineSize; + + int currentGrowableSize = GrowableBlockHeader.ALLOCATED_SIZE.get(nd, growableBlockAddress); + int newGrowableSize = getGrowableRegionSizeFor(desiredSize); + + // We only need to repack if the new size is smaller than the old one + if (newGrowableSize >= currentGrowableSize) { + return; + } + + Database db = nd.getDB(); + if (currentGrowableSize > GrowableBlockHeader.MAX_GROWABLE_SIZE) { + // We are currently using a metablock + int desiredBlockCount = (newGrowableSize + GrowableBlockHeader.MAX_GROWABLE_SIZE - 1) + / GrowableBlockHeader.MAX_GROWABLE_SIZE; + int currentBlockCount = currentGrowableSize / GrowableBlockHeader.MAX_GROWABLE_SIZE; + + // Only deallocate memory if either there are either two full unused blocks + // or the desired size is less than or equal to half of a block + 1. We add one to ensure + // that the newly-shrunk array will still be about double the size of the used elements. + boolean needsRepacking = (currentBlockCount - desiredBlockCount > 1) + || (newGrowableSize <= (GrowableBlockHeader.MAX_GROWABLE_SIZE / 2 + 1)); + if (!needsRepacking) { + return; + } + + long metablockRecordsAddress = growableBlockAddress + GrowableBlockHeader.GROWABLE_BLOCK_HEADER_BYTES; + int currentBlock = currentBlockCount; + while (--currentBlock >= desiredBlockCount) { + long nextAddress = metablockRecordsAddress + currentBlock * Database.PTR_SIZE; + long oldBlockAddress = db.getRecPtr(nextAddress); + db.free(oldBlockAddress, Database.POOL_GROWABLE_ARRAY); + db.putRecPtr(nextAddress, 0); + } + + // If we still need to be using a metablock, we're done + if (newGrowableSize > GrowableBlockHeader.MAX_GROWABLE_SIZE) { + // First record the new growable region size + GrowableBlockHeader.ALLOCATED_SIZE.put(nd, growableBlockAddress, newGrowableSize); + return; + } + + // Else we need to stop using a metablock. + // Dispose the metablock and replace it with the first growable block + long firstBlockAddress = db.getRecPtr(metablockRecordsAddress); + int oldSize = GrowableBlockHeader.ARRAY_SIZE.get(nd, growableBlockAddress); + db.free(growableBlockAddress, Database.POOL_GROWABLE_ARRAY); + + GROWABLE_BLOCK_ADDRESS.put(nd, address, firstBlockAddress); + + if (firstBlockAddress != 0) { + currentGrowableSize = GrowableBlockHeader.MAX_GROWABLE_SIZE; + GrowableBlockHeader.ARRAY_SIZE.put(nd, firstBlockAddress, oldSize); + GrowableBlockHeader.ALLOCATED_SIZE.put(nd, firstBlockAddress, + GrowableBlockHeader.MAX_GROWABLE_SIZE); + } + + // Then we'll fall through to the normal (non-metablock) case, which may shrink the size of the last + // growable block further + } + + // If we're not using metablocks, we only resize the growable region once the size of the array shrinks + // such that we're only using 1/4 of it. + if (desiredGrowableSize <= (currentGrowableSize / 4 + 1)) { + long newBlockAddress = resizeBlock(nd, address, newGrowableSize); + + GROWABLE_BLOCK_ADDRESS.put(nd, address, newBlockAddress); + } + } + + /** + * Returns the number of elements that should actually be allocated in the growable region for an array of the given + * size + */ + private int getGrowableRegionSizeFor(int arraySize) { + int growableRegionSize = arraySize - this.inlineSize; + + if (growableRegionSize <= 0) { + return 0; + } + + // Find the next power of two that is equal or greater than the required size. We use inlineSize + // as the minimum growable block size since we tend to assign a large inlineSize to lists with a large + // average number of elements, and these are also the lists that will benefit from a larger initial block size. + int nextGrowableSize = getNextPowerOfTwo(Math.max(growableRegionSize, this.inlineSize)); + + if (nextGrowableSize > GrowableBlockHeader.MAX_GROWABLE_SIZE) { + // If the next power of two is greater than the max block size but the requested size is smaller than it, + // clamp it to the the max block size + if (growableRegionSize <= GrowableBlockHeader.MAX_GROWABLE_SIZE) { + return GrowableBlockHeader.MAX_GROWABLE_SIZE; + } + + // For sizes larger than the max block size, we need to use a metablock. In this case, the allocated size + // will be a multiple of the max block size. + return roundUpToMultipleOf(GrowableBlockHeader.MAX_GROWABLE_SIZE, growableRegionSize); + } + + return nextGrowableSize; + } + + /** + * Returns the largest power of two that is less than or equal to the given integer + */ + private static int getPrevPowerOfTwo(int n) { + n |= (n >> 1); + n |= (n >> 2); + n |= (n >> 4); + n |= (n >> 8); + n |= (n >> 16); + return n - (n >> 1); + } + + /** + * Returns the next power of two that is equal to or greater than the given int. + */ + private static int getNextPowerOfTwo(int toTest) { + int highBit = getPrevPowerOfTwo(toTest); + int nextGrowableSize = highBit; + + if (highBit != toTest) { + assert (nextGrowableSize << 1) != 0; + nextGrowableSize <<= 1; + } + return nextGrowableSize; + } + + /** + * Rounds a value up to the nearest multiple of another value + */ + private static int roundUpToMultipleOf(int unit, int valueToRound) { + int numberOfMetablocks = (valueToRound + unit - 1) / unit; + + return numberOfMetablocks * unit; + } + + /** + * Returns the record size for a RawGrowableSize with the given number of inline records + */ + public int getRecordSize() { + return ARRAY_HEADER_BYTES + Database.PTR_SIZE * this.inlineSize; + } + + public void destruct(Nd nd, long address) { + repackIfNecessary(nd, address, 0); + } + + + /** + * Returns true iff the size of the array is 0 + * + * @param address address of the array + * @return the array size, in number elements + */ + public boolean isEmpty(Nd nd, long address) { + Database db = nd.getDB(); + long growableBlockAddress = GROWABLE_BLOCK_ADDRESS.get(nd, address); + + if (growableBlockAddress == 0) { + if (this.inlineSize == 0) { + return true; + } + // If there is no growable block or metablock, then the size is determined by the position of the first + // null pointer among the inline records. + long firstValue = db.getRecPtr(address + ARRAY_HEADER_BYTES); + + return firstValue == 0; + } + return GrowableBlockHeader.ARRAY_SIZE.get(nd, growableBlockAddress) == 0; + } + + public int getCapacity(Nd nd, long address) { + long growableBlockAddress = GROWABLE_BLOCK_ADDRESS.get(nd, address); + + if (growableBlockAddress == 0) { + return this.inlineSize; + } + + int growableBlockCurrentSize = GrowableBlockHeader.ALLOCATED_SIZE.get(nd, growableBlockAddress); + + return growableBlockCurrentSize + this.inlineSize; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/StreamHasher.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/StreamHasher.java new file mode 100644 index 000000000..ab1642d8d --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/StreamHasher.java @@ -0,0 +1,235 @@ +/******************************************************************************* + * Copyright (c) 2010, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sergey Prigogin (Google) - initial API and implementation + * + * Based on lookup3.c, by Bob Jenkins {@link "http://burtleburtle.net/bob/c/lookup3.c"} + * + * Here is the original comment by Bob Jenkins: + * ------------------------------------------------------------------------------- + * lookup3.c, by Bob Jenkins, May 2006, Public Domain. + * + * These are functions for producing 32-bit hashes for hash table lookup. + * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() + * are externally useful functions. Routines to test the hash are included + * if SELF_TEST is defined. You can use this free for any purpose. It's in + * the public domain. It has no warranty. + * + * You probably want to use hashlittle(). hashlittle() and hashbig() + * hash byte arrays. hashlittle() is is faster than hashbig() on + * little-endian machines. Intel and AMD are little-endian machines. + * On second thought, you probably want hashlittle2(), which is identical to + * hashlittle() except it returns two 32-bit hashes for the price of one. + * You could implement hashbig2() if you wanted but I haven't bothered here. + * + * If you want to find a hash of, say, exactly 7 integers, do + * a = i1; b = i2; c = i3; + * mix(a, b, c); + * a += i4; b += i5; c += i6; + * mix(a, b, c); + * a += i7; + * finalMix(a, b, c); + * then use c as the hash value. If you have a variable length array of + * 4-byte integers to hash, use hashword(). If you have a byte array (like + * a character string), use hashlittle(). If you have several byte arrays, or + * a mix of things, see the comments above hashlittle(). + * + * Why is this so big? I read 12 bytes at a time into 3 4-byte integers, + * then mix those integers. This is fast (you can do a lot more thorough + * mixing with 12*3 instructions on 3 integers than you can with 3 instructions + * on 1 byte), but shoehorning those bytes into integers efficiently is messy. + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd; + +/** + * Computes a 64-bit hash value of a character stream that can be supplied one chunk at a time. + * Usage: + * <pre> + * StreamHasher hasher = new StreamHasher(); + * for (long offset = 0; offset < streamLength; offset += chunkLength) { + * hasher.addChunk(offset, chunkOfCharacters); + * } + * int64 hashValue = hasher.computeHash(); + * </pre> + * + * Based on lookup3.c by Bob Jenkins from {@link "http://burtleburtle.net/bob/c/lookup3.c"} + */ +public final class StreamHasher { + private static final long SEED = 3141592653589793238L; // PI + private static final long EMPTY_STRING_HASH = new StreamHasher().computeHashInternal(); + + long hashedOffset; // Current position in the stream of characters. + int state; // Current position in the stream of characters modulo 6, or -1 after computeHash is called. + int a; + int b; + int c; + char previousCharacter; + + public StreamHasher() { + // Set up the internal state. + this.hashedOffset = 0; + this.state = 0; + this.a = this.b = this.c = (int) SEED; + this.c += SEED >>> 32; + } + + /** + * Adds a chunk of data to the hasher. + * @param chunk Contents of the chunk. + */ + public void addChunk(char[] chunk) { + for (int pos = 0; pos < chunk.length; pos++, this.hashedOffset++) { + char cc = chunk[pos]; + switch (this.state++) { + case -1: + throw new IllegalStateException("addChunk is called after computeHash."); //$NON-NLS-1$ + case 0: + case 2: + case 4: + this.previousCharacter = cc; + break; + case 1: + this.a += this.previousCharacter | (cc << 16); + break; + case 3: + this.b += this.previousCharacter | (cc << 16); + break; + case 5: + this.c += this.previousCharacter | (cc << 16); + mix(); + this.state = 0; + break; + } + } + } + + /** + * Computes and returns the hash value. Must be called once after the last chunk. + * @return The hash value of the character stream. + */ + public long computeHash() { + if (this.state < 0) { + throw new IllegalStateException("computeHash method is called more than once."); //$NON-NLS-1$ + } + return computeHashInternal() ^ EMPTY_STRING_HASH; + } + + private long computeHashInternal() { + switch (this.state) { + case 1: + this.a += this.previousCharacter; + break; + case 3: + this.b += this.previousCharacter; + break; + case 5: + this.c += this.previousCharacter; + break; + } + this.state = -1; // Protect against subsequent calls. + finalMix(); + return (this.c & 0xFFFFFFFFL) | ((long) this.b << 32); + } + + /** + * Computes a 64-bit hash value of a String. The resulting hash value + * is zero if the string is empty. + * + * @param str The string to hash. + * @return The hash value. + */ + public static long hash(String str) { + StreamHasher hasher = new StreamHasher(); + hasher.addChunk(str.toCharArray()); + return hasher.computeHash(); + } + + /** + * Mixes three 32-bit values reversibly. + * + * This is reversible, so any information in a, b, c before mix() is + * still in a, b, c after mix(). + * + * If four pairs of a, b, c inputs are run through mix(), or through + * mix() in reverse, there are at least 32 bits of the output that + * are sometimes the same for one pair and different for another pair. + * This was tested for: + * * pairs that differed by one bit, by two bits, in any combination + * of top bits of a, b, c, or in any combination of bottom bits of + * a, b, c. + * * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + * the output delta to a Gray code (a ^ (a >> 1)) so a string of 1's + * (as is commonly produced by subtraction) look like a single 1-bit + * difference. + * * the base values were pseudo-random, all zero but one bit set, or + * all zero plus a counter that starts at zero. + * + * Some k values for my "a -= c; a ^= Integer.rotateLeft(c, k); c += b;" + * arrangement that satisfy this are + * 4 6 8 16 19 4 + * 9 15 3 18 27 15 + * 14 9 3 7 17 3 + * Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing + * for "differ" defined as + with a one-bit base and a two-bit delta. + * I used http://burtleburtle.net/bob/hash/avalanche.html to choose + * the operations, constants, and arrangements of the variables. + * + * This does not achieve avalanche. There are input bits of a, b, c + * that fail to affect some output bits of a, b, c, especially of a. + * The most thoroughly mixed value is c, but it doesn't really even + * achieve avalanche in c. + * + * This allows some parallelism. Read-after-writes are good at doubling + * the number of bits affected, so the goal of mixing pulls in the opposite + * direction as the goal of parallelism. I did what I could. Rotates + * seem to cost as much as shifts on every machine I could lay my hands + * on, and rotates are much kinder to the top and bottom bits, so I used + * rotates. + */ + private void mix() { + this.a -= this.c; this.a ^= Integer.rotateLeft(this.c, 4); this.c += this.b; + this.b -= this.a; this.b ^= Integer.rotateLeft(this.a, 6); this.a += this.c; + this.c -= this.b; this.c ^= Integer.rotateLeft(this.b, 8); this.b += this.a; + this.a -= this.c; this.a ^= Integer.rotateLeft(this.c, 16); this.c += this.b; + this.b -= this.a; this.b ^= Integer.rotateLeft(this.a, 19); this.a += this.c; + this.c -= this.b; this.c ^= Integer.rotateLeft(this.b, 4); this.b += this.a; + } + + /** + * Final mixing of 3 32-bit values a, b, c into c + * + * Pairs of a, b, c values differing in only a few bits will usually + * produce values of c that look totally different. This was tested for + * * pairs that differed by one bit, by two bits, in any combination + * of top bits of a, b, c, or in any combination of bottom bits of + * a, b, c. + * * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + * the output delta to a Gray code (a ^ (a >> 1)) so a string of 1's (as + * is commonly produced by subtraction) look like a single 1-bit + * difference. + * * the base values were pseudo-random, all zero but one bit set, or + * all zero plus a counter that starts at zero. + * + * These constants passed: + * 14 11 25 16 4 14 24 + * 12 14 25 16 4 14 24 + * and these came close: + * 4 8 15 26 3 22 24 + * 10 8 15 26 3 22 24 + * 11 8 15 26 3 22 24 + */ + private void finalMix() { + this.c ^= this.b; this.c -= Integer.rotateLeft(this.b, 14); + this.a ^= this.c; this.a -= Integer.rotateLeft(this.c, 11); + this.b ^= this.a; this.b -= Integer.rotateLeft(this.a, 25); + this.c ^= this.b; this.c -= Integer.rotateLeft(this.b, 16); + this.a ^= this.c; this.a -= Integer.rotateLeft(this.c, 4); + this.b ^= this.a; this.b -= Integer.rotateLeft(this.a, 14); + this.c ^= this.b; this.c -= Integer.rotateLeft(this.b, 24); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/BTree.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/BTree.java new file mode 100644 index 000000000..0d047882a --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/BTree.java @@ -0,0 +1,773 @@ +/******************************************************************************* + * Copyright (c) 2005, 2016 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX - Initial API and implementation + * Andrew Ferguson (Symbian) - Provide B-tree deletion routine + * Markus Schorn (Wind River Systems) + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +import java.text.MessageFormat; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.internal.core.nd.AbstractTypeFactory; +import org.eclipse.jdt.internal.core.nd.ITypeFactory; +import org.eclipse.jdt.internal.core.nd.Nd; + +/** + * Implements B-Tree search structure. + */ +public class BTree { + private static final int DEFAULT_DEGREE = 8; + // Constants for internal deletion routine (see deleteImp doc). + private static final int DELMODE_NORMAL = 0; + private static final int DELMODE_DELETE_MINIMUM = 1; + private static final int DELMODE_DELETE_MAXIMUM = 2; + + public static final int RECORD_SIZE = Database.PTR_SIZE; + + private final Nd nd; + protected final Database db; + protected final long rootPointer; + + protected final int degree; + protected final int maxRecords; + protected final int maxChildren; + protected final int minRecords; + protected final int offsetChildren; + protected final int medianRecord; + + protected final IBTreeComparator cmp; + + public BTree(Nd nd, long rootPointer, IBTreeComparator cmp) { + this(nd, rootPointer, DEFAULT_DEGREE, cmp); + } + + /** + * Constructor. + * + * @param nd the database containing the btree + * @param rootPointer offset into database of the pointer to the root node + */ + public BTree(Nd nd, long rootPointer, int degree, IBTreeComparator cmp) { + this.nd = nd; + if (degree < 2) + throw new IllegalArgumentException("Illegal degree " + degree + " in tree"); //$NON-NLS-1$ //$NON-NLS-2$ + + this.db = nd.getDB(); + this.rootPointer = rootPointer; + this.cmp = cmp; + + this.degree = degree; + this.minRecords = this.degree - 1; + this.maxRecords = 2*this.degree - 1; + this.maxChildren = 2*this.degree; + this.offsetChildren = this.maxRecords * Database.INT_SIZE; + this.medianRecord = this.degree - 1; + } + + public static ITypeFactory<BTree> getFactory(final IBTreeComparator cmp) { + return getFactory(8, cmp); + } + + public static ITypeFactory<BTree> getFactory(final int degree, final IBTreeComparator cmp) { + return new AbstractTypeFactory<BTree>() { + @Override + public BTree create(Nd dom, long address) { + return new BTree(dom, address, degree, cmp); + } + + @Override + public int getRecordSize() { + return RECORD_SIZE; + } + + @Override + public Class<?> getElementClass() { + return BTree.class; + } + + @Override + public void destruct(Nd dom, long address) { + destructFields(dom, address); + } + + @Override + public void destructFields(Nd dom, long address) { + create(dom, address).destruct(); + } + }; + } + + protected long getRoot() throws IndexException { + return this.db.getRecPtr(this.rootPointer); + } + + protected final void putRecord(Chunk chunk, long node, int index, long record) { + chunk.putRecPtr(node + index * Database.INT_SIZE, record); + } + + protected final long getRecord(Chunk chunk, long node, int index) { + return chunk.getRecPtr(node + index * Database.INT_SIZE); + } + + protected final void putChild(Chunk chunk, long node, int index, long child) { + chunk.putRecPtr(node + this.offsetChildren + index * Database.INT_SIZE, child); + } + + protected final long getChild(Chunk chunk, long node, int index) { + return chunk.getRecPtr(node + this.offsetChildren + index * Database.INT_SIZE); + } + + public void destruct() { + long root = getRoot(); + + if (root == 0) { + return; + } + + deallocateChildren(root); + } + + private void deallocateChildren(long record) { + Chunk chunk = this.db.getChunk(record); + + // Copy all the children pointers to an array of longs so all the reads will happen on the same chunk + // consecutively + long[] children = new long[this.maxRecords + 1]; + + for (int idx = 0; idx < children.length; idx++) { + children[idx] = getChild(chunk, record, idx); + } + + this.db.free(record, Database.POOL_BTREE); + + chunk = null; + + for (long nextChild : children) { + if (nextChild != 0) { + deallocateChildren(nextChild); + } + } + } + + /** + * Inserts the record into the b-tree. We don't insert if the key was already there, + * in which case we return the record that matched. In other cases, we just return + * the record back. + * + * @param record offset of the record + */ + public long insert(long record) throws IndexException { + long root = getRoot(); + + // Is this our first time in. + if (root == 0) { + firstInsert(record); + return record; + } + + return insert(null, 0, 0, root, record); + } + + private long insert(Chunk pChunk, long parent, int iParent, long node, long record) throws IndexException { + Chunk chunk = this.db.getChunk(node); + + // If this node is full (last record isn't null), split it. + if (getRecord(chunk, node, this.maxRecords - 1) != 0) { + long median = getRecord(chunk, node, this.medianRecord); + if (median == record) { + // Found it, never mind. + return median; + } else { + // Split it. + // Create the new node and move the larger records over. + long newnode = allocateNode(); + Chunk newchunk = this.db.getChunk(newnode); + for (int i = 0; i < this.medianRecord; ++i) { + putRecord(newchunk, newnode, i, getRecord(chunk, node, this.medianRecord + 1 + i)); + putRecord(chunk, node, this.medianRecord + 1 + i, 0); + putChild(newchunk, newnode, i, getChild(chunk, node, this.medianRecord + 1 + i)); + putChild(chunk, node, this.medianRecord + 1 + i, 0); + } + putChild(newchunk, newnode, this.medianRecord, getChild(chunk, node, this.maxRecords)); + putChild(chunk, node, this.maxRecords, 0); + + if (parent == 0) { + // Create a new root + parent = allocateNode(); + pChunk = this.db.getChunk(parent); + this.db.putRecPtr(this.rootPointer, parent); + putChild(pChunk, parent, 0, node); + } else { + // Insert the median into the parent. + for (int i = this.maxRecords - 2; i >= iParent; --i) { + long r = getRecord(pChunk, parent, i); + if (r != 0) { + putRecord(pChunk, parent, i + 1, r); + putChild(pChunk, parent, i + 2, getChild(pChunk, parent, i + 1)); + } + } + } + putRecord(pChunk, parent, iParent, median); + putChild(pChunk, parent, iParent + 1, newnode); + + putRecord(chunk, node, this.medianRecord, 0); + + // Set the node to the correct one to follow. + if (this.cmp.compare(this.nd, record, median) > 0) { + node = newnode; + chunk = newchunk; + } + } + } + + // Binary search to find the insert point. + int lower= 0; + int upper= this.maxRecords - 1; + while (lower < upper && getRecord(chunk, node, upper - 1) == 0) { + upper--; + } + + while (lower < upper) { + int middle= (lower + upper) / 2; + long checkRec= getRecord(chunk, node, middle); + if (checkRec == 0) { + upper= middle; + } else { + int compare= this.cmp.compare(this.nd, checkRec, record); + if (compare > 0) { + upper= middle; + } else if (compare < 0) { + lower= middle + 1; + } else { + // Found it, no insert, just return the matched record. + return checkRec; + } + } + } + final int i= lower; + long child = getChild(chunk, node, i); + if (child != 0) { + // Visit the children. + return insert(chunk, node, i, child, record); + } else { + // We are at the leaf, add us in. + // First copy everything after over one. + for (int j = this.maxRecords - 2; j >= i; --j) { + long r = getRecord(chunk, node, j); + if (r != 0) + putRecord(chunk, node, j + 1, r); + } + putRecord(chunk, node, i, record); + return record; + } + } + + private void firstInsert(long record) throws IndexException { + // Create the node and save it as root. + long root = allocateNode(); + this.db.putRecPtr(this.rootPointer, root); + // Put the record in the first slot of the node. + putRecord(this.db.getChunk(root), root, 0, record); + } + + private long allocateNode() throws IndexException { + return this.db.malloc((2 * this.maxRecords + 1) * Database.INT_SIZE, Database.POOL_BTREE); + } + + /** + * Deletes the specified record from the B-tree. + * <p> + * If the specified record is not present then this routine has no effect. + * <p> + * Specifying a record r for which there is another record q existing in the B-tree + * where cmp.compare(r,q)==0 && r!=q will also have no effect + * <p> + * N.B. The record is not deleted itself - its storage is not deallocated. + * The reference to the record in the btree is deleted. + * + * @param record the record to delete + * @throws IndexException + */ + public void delete(long record) throws IndexException { + try { + deleteImp(record, getRoot(), DELMODE_NORMAL); + } catch (BTreeKeyNotFoundException e) { + // Contract of this method is to NO-OP upon this event. + } + } + + private class BTreeKeyNotFoundException extends Exception { + private static final long serialVersionUID = 9065438266175091670L; + public BTreeKeyNotFoundException(String msg) { + super(msg); + } + } + + /** + * Used in implementation of delete routines + */ + private class BTNode { + final long node; + final int keyCount; + final Chunk chunk; + + BTNode(long node) throws IndexException { + this.node = node; + this.chunk = BTree.this.db.getChunk(node); + int i= 0; + while (i < BTree.this.maxRecords && getRecord(this.chunk, node, i) != 0) + i++; + this.keyCount = i; + } + + BTNode getChild(int index) throws IndexException { + if (0 <= index && index < BTree.this.maxChildren) { + long child = BTree.this.getChild(this.chunk, this.node, index); + if (child != 0) + return new BTNode(child); + } + return null; + } + } + + /** + * Implementation for deleting a key/record from the B-tree. + * <p> + * There is no distinction between keys and records. + * <p> + * This implements a single downward pass (with minor exceptions) deletion + * <p> + * @param key the address of the record to delete + * @param nodeRecord a node that (directly or indirectly) contains the specified key/record + * @param mode one of DELMODE_NORMAL, DELMODE_DELETE_MINIMUM, DELMODE_DELETE_MAXIMUM + * where DELMODE_NORMAL: locates the specified key/record using the comparator provided + * DELMODE_DELETE_MINIMUM: locates and deletes the minimum element in the subtree rooted at nodeRecord + * DELMODE_DELETE_MAXIMUM: locates and deletes the maximum element in the subtree rooted at nodeRecord + * @return the address of the record removed from the B-tree + * @throws IndexException + */ + private long deleteImp(long key, long nodeRecord, int mode) + throws IndexException, BTreeKeyNotFoundException { + BTNode node = new BTNode(nodeRecord); + + // Determine index of key in current node, or -1 if its not in this node. + int keyIndexInNode = -1; + if (mode == DELMODE_NORMAL) + for (int i= 0; i < node.keyCount; i++) + if (getRecord(node.chunk, node.node, i) == key) { + keyIndexInNode = i; + break; + } + + if (getChild(node.chunk, node.node, 0) == 0) { + /* Case 1: leaf node containing the key (by method precondition) */ + if (keyIndexInNode != -1) { + nodeContentDelete(node, keyIndexInNode, 1); + return key; + } else { + if (mode == DELMODE_DELETE_MINIMUM) { + long subst = getRecord(node.chunk, node.node, 0); + nodeContentDelete(node, 0, 1); + return subst; + } else if (mode == DELMODE_DELETE_MAXIMUM) { + long subst = getRecord(node.chunk, node.node, node.keyCount - 1); + nodeContentDelete(node, node.keyCount - 1, 1); + return subst; + } + throw new BTreeKeyNotFoundException("Deletion on absent key " + key + ", mode = " + mode); //$NON-NLS-1$//$NON-NLS-2$ + } + } else { + if (keyIndexInNode != -1) { + /* Case 2: non-leaf node which contains the key itself */ + + BTNode succ = node.getChild(keyIndexInNode + 1); + if (succ != null && succ.keyCount > this.minRecords) { + /* Case 2a: Delete key by overwriting it with its successor (which occurs in a leaf node) */ + long subst = deleteImp(-1, succ.node, DELMODE_DELETE_MINIMUM); + putRecord(node.chunk, node.node, keyIndexInNode, subst); + return key; + } + + BTNode pred = node.getChild(keyIndexInNode); + if (pred != null && pred.keyCount > this.minRecords) { + /* Case 2b: Delete key by overwriting it with its predecessor (which occurs in a leaf node) */ + long subst = deleteImp(-1, pred.node, DELMODE_DELETE_MAXIMUM); + putRecord(node.chunk, node.node, keyIndexInNode, subst); + return key; + } + + /* Case 2c: Merge successor and predecessor */ + // assert(pred != null && succ != null); + if (pred != null) { + mergeNodes(succ, node, keyIndexInNode, pred); + return deleteImp(key, pred.node, mode); + } + return key; + } else { + /* Case 3: non-leaf node which does not itself contain the key */ + + /* Determine root of subtree that should contain the key */ + int subtreeIndex; + switch(mode) { + case DELMODE_NORMAL: + subtreeIndex = node.keyCount; + for (int i= 0; i < node.keyCount; i++) + if (this.cmp.compare(this.nd, getRecord(node.chunk, node.node, i), key)>0) { + subtreeIndex = i; + break; + } + break; + case DELMODE_DELETE_MINIMUM: subtreeIndex = 0; break; + case DELMODE_DELETE_MAXIMUM: subtreeIndex = node.keyCount; break; + default: throw new IndexException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, IStatus.OK, "Unknown delete mode " + mode, null)); //$NON-NLS-1$ + } + + BTNode child = node.getChild(subtreeIndex); + if (child == null) { + throw new IndexException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, IStatus.OK, + "BTree integrity error (null child found)", null)); //$NON-NLS-1$ + } + + if (child.keyCount > this.minRecords) { + return deleteImp(key, child.node, mode); + } else { + BTNode sibR = node.getChild(subtreeIndex + 1); + if (sibR != null && sibR.keyCount > this.minRecords) { + /* Case 3a (i): child will underflow upon deletion, take a key from rightSibling */ + long rightKey = getRecord(node.chunk, node.node, subtreeIndex); + long leftmostRightSiblingKey = getRecord(sibR.chunk, sibR.node, 0); + append(child, rightKey, getChild(sibR.chunk, sibR.node, 0)); + nodeContentDelete(sibR, 0, 1); + putRecord(node.chunk, node.node, subtreeIndex, leftmostRightSiblingKey); + return deleteImp(key, child.node, mode); + } + + BTNode sibL = node.getChild(subtreeIndex - 1); + if (sibL != null && sibL.keyCount > this.minRecords) { + /* Case 3a (ii): child will underflow upon deletion, take a key from leftSibling */ + long leftKey = getRecord(node.chunk, node.node, subtreeIndex - 1); + prepend(child, leftKey, getChild(sibL.chunk, sibL.node, sibL.keyCount)); + long rightmostLeftSiblingKey = getRecord(sibL.chunk, sibL.node, sibL.keyCount - 1); + putRecord(sibL.chunk, sibL.node, sibL.keyCount - 1, 0); + putChild(sibL.chunk, sibL.node, sibL.keyCount, 0); + putRecord(node.chunk, node.node, subtreeIndex - 1, rightmostLeftSiblingKey); + return deleteImp(key, child.node, mode); + } + + /* Case 3b (i,ii): leftSibling, child, rightSibling all have minimum number of keys */ + + if (sibL != null) { // merge child into leftSibling + mergeNodes(child, node, subtreeIndex - 1, sibL); + return deleteImp(key, sibL.node, mode); + } + + if (sibR != null) { // merge rightSibling into child + mergeNodes(sibR, node, subtreeIndex, child); + return deleteImp(key, child.node, mode); + } + + throw new BTreeKeyNotFoundException( + MessageFormat.format("Deletion of key not in btree: {0} mode={1}", //$NON-NLS-1$ + new Object[]{new Long(key), new Integer(mode)})); + } + } + } + } + + /** + * Merge node 'src' onto the right side of node 'dst' using node + * 'keyProvider' as the source of the median key. Bounds checking is not + * performed. + * @param src the key to merge into dst + * @param keyProvider the node that provides the median key for the new node + * @param kIndex the index of the key in the node <i>mid</i> which is to become the new node's median key + * @param dst the node which is the basis and result of the merge + */ + public void mergeNodes(BTNode src, BTNode keyProvider, int kIndex, BTNode dst) + throws IndexException { + nodeContentCopy(src, 0, dst, dst.keyCount + 1, src.keyCount + 1); + long midKey = getRecord(keyProvider.chunk, keyProvider.node, kIndex); + putRecord(dst.chunk, dst.node, dst.keyCount, midKey); + long keySucc = kIndex + 1 == this.maxRecords ? 0 : getRecord(keyProvider.chunk, keyProvider.node, kIndex + 1); + this.db.free(getChild(keyProvider.chunk, keyProvider.node, kIndex + 1), Database.POOL_BTREE); + nodeContentDelete(keyProvider, kIndex + 1, 1); + putRecord(keyProvider.chunk, keyProvider.node, kIndex, keySucc); + if (kIndex == 0 && keySucc == 0) { + /* + * The root node is excused from the property that a node must have a least MIN keys + * This means we must special case it at the point when its had all of its keys deleted + * entirely during merge operations (which push one of its keys down as a pivot) + */ + long rootNode = getRoot(); + if (rootNode == keyProvider.node) { + this.db.putRecPtr(this.rootPointer, dst.node); + this.db.free(rootNode, Database.POOL_BTREE); + } + } + } + + /** + * Insert the key and (its predecessor) child at the left side of the specified node. Bounds checking + * is not performed. + * @param node the node to prepend to + * @param key the new leftmost (least) key + * @param child the new leftmost (least) subtree root + */ + private void prepend(BTNode node, long key, long child) { + nodeContentCopy(node, 0, node, 1, node.keyCount + 1); + putRecord(node.chunk, node.node, 0, key); + putChild(node.chunk, node.node, 0, child); + } + + /** + * Insert the key and (its successor) child at the right side of the specified node. Bounds + * checking is not performed. + * @param node + * @param key + * @param child + */ + private void append(BTNode node, long key, long child) { + putRecord(node.chunk, node.node, node.keyCount, key); + putChild(node.chunk, node.node, node.keyCount + 1, child); + } + + /** + * Overwrite a section of the specified node (dst) with the specified section of the source + * node. Bounds checking is not performed. To allow just copying of the final child (which has + * no corresponding key) the routine behaves as though there were a corresponding key existing + * with value zero.<p> + * Copying from a node to itself is permitted. + * @param src the node to read from + * @param srcPos the initial index to read from (inclusive) + * @param dst the node to write to + * @param dstPos the initial index to write to (inclusive) + * @param length the number of (key,(predecessor)child) nodes to write + */ + private void nodeContentCopy(BTNode src, int srcPos, BTNode dst, int dstPos, int length) { + for (int i=length - 1; i >= 0; i--) { // this order is important when src == dst! + int srcIndex = srcPos + i; + int dstIndex = dstPos + i; + + if (srcIndex < src.keyCount + 1) { + long srcChild = getChild(src.chunk, src.node, srcIndex); + putChild(dst.chunk, dst.node, dstIndex, srcChild); + + if (srcIndex < src.keyCount) { + long srcKey = getRecord(src.chunk, src.node, srcIndex); + putRecord(dst.chunk, dst.node, dstIndex, srcKey); + } + } + } + } + + /** + * Delete a section of node content - (key, (predecessor)child) pairs. Bounds checking + * is not performed. To allow deletion of the final child (which has no corresponding key) + * the routine behaves as though there were a corresponding key existing with value zero.<p> + * Content is deleted and remaining content is moved leftward the appropriate amount. + * @param node the node to delete content from + * @param i the start index (inclusive) to delete from + * @param length the length of the sequence to delete + */ + private void nodeContentDelete(BTNode node, int i, int length) { + for (int index= i; index <= this.maxRecords; index++) { + long newKey = (index + length) < node.keyCount ? getRecord(node.chunk, node.node, index + length) : 0; + long newChild = (index + length) < node.keyCount + 1 ? getChild(node.chunk, node.node, index + length) : 0; + if (index < this.maxRecords) { + putRecord(node.chunk, node.node, index, newKey); + } + if (index < this.maxChildren) { + putChild(node.chunk, node.node, index, newChild); + } + } + } + + /** + * Visit all nodes beginning when the visitor comparator + * returns >= 0 until the visitor visit returns falls. + * + * @param visitor + */ + public boolean accept(IBTreeVisitor visitor) throws IndexException { + return accept(this.db.getRecPtr(this.rootPointer), visitor); + } + + private boolean accept(long node, IBTreeVisitor visitor) throws IndexException { + // If found is false, we are still in search mode. + // Once found is true visit everything. + // Return false when ready to quit. + + if (node == 0) { + return true; + } + if (visitor instanceof IBTreeVisitor2) { + ((IBTreeVisitor2) visitor).preNode(node); + } + + try { + Chunk chunk = this.db.getChunk(node); + + // Binary search to find first record greater or equal. + int lower= 0; + int upper= this.maxRecords - 1; + while (lower < upper && getRecord(chunk, node, upper - 1) == 0) { + upper--; + } + while (lower < upper) { + int middle= (lower + upper) / 2; + long checkRec = getRecord(chunk, node, middle); + if (checkRec == 0) { + upper= middle; + } else { + int compare= visitor.compare(checkRec); + if (compare >= 0) { + upper= middle; + } else { + lower= middle + 1; + } + } + } + + // Start with first record greater or equal, reuse comparison results. + int i= lower; + for (; i < this.maxRecords; ++i) { + long record = getRecord(chunk, node, i); + if (record == 0) + break; + + int compare= visitor.compare(record); + if (compare > 0) { + // Start point is to the left. + return accept(getChild(chunk, node, i), visitor); + } else if (compare == 0) { + if (!accept(getChild(chunk, node, i), visitor)) + return false; + if (!visitor.visit(record)) + return false; + } + } + return accept(getChild(chunk, node, i), visitor); + } finally { + if (visitor instanceof IBTreeVisitor2) { + ((IBTreeVisitor2) visitor).postNode(node); + } + } + } + + /* + * TODO: It would be good to move these into IBTreeVisitor and eliminate + * IBTreeVisitor2 if this is acceptable. + */ + private interface IBTreeVisitor2 extends IBTreeVisitor { + void preNode(long node) throws IndexException; + void postNode(long node) throws IndexException; + } + + /** + * Debugging method for checking B-tree invariants + * @return the empty String if B-tree invariants hold, otherwise + * a human readable report + * @throws IndexException + */ + public String getInvariantsErrorReport() throws IndexException { + InvariantsChecker checker = new InvariantsChecker(); + accept(checker); + return checker.isValid() ? "" : checker.getMsg(); //$NON-NLS-1$ + } + + /** + * A B-tree visitor for checking some B-tree invariants. + * Note ordering invariants are not checked here. + */ + private class InvariantsChecker implements IBTreeVisitor2 { + boolean valid = true; + String msg = ""; //$NON-NLS-1$ + Integer leafDepth; + int depth; + + public InvariantsChecker() {} + public String getMsg() { return this.msg; } + public boolean isValid() { return this.valid; } + @Override + public void postNode(long node) throws IndexException { this.depth--; } + @Override + public int compare(long record) throws IndexException { return 0; } + @Override + public boolean visit(long record) throws IndexException { return true; } + + @Override + public void preNode(long node) throws IndexException { + this.depth++; + + // Collect information for checking. + int keyCount = 0; + int indexFirstBlankKey = BTree.this.maxRecords; + int indexLastNonBlankKey = 0; + for (int i= 0; i < BTree.this.maxRecords; i++) { + if (getRecord(BTree.this.db.getChunk(node), node, i) != 0) { + keyCount++; + indexLastNonBlankKey = i; + } else if (indexFirstBlankKey == BTree.this.maxRecords) { + indexFirstBlankKey = i; + } + } + + int childCount = 0; + for (int i= 0; i < BTree.this.maxChildren; i++) { + if (getChild(BTree.this.db.getChunk(node), node, i) != 0) { + childCount++; + } + } + + // Check that non-blank keys are contiguous and blank key terminated. + if (indexFirstBlankKey != indexLastNonBlankKey + 1) { + boolean full = indexFirstBlankKey == BTree.this.maxRecords && indexLastNonBlankKey == BTree.this.maxRecords - 1; + boolean empty = indexFirstBlankKey == 0 && indexLastNonBlankKey == 0; + if (!full && !empty) { + this.valid = false; + this.msg += MessageFormat.format("[{0} blanks inconsistent b={1} nb={2}]", //$NON-NLS-1$ + new Object[] { new Long(node), new Integer(indexFirstBlankKey), + new Integer(indexLastNonBlankKey) }); + } + } + + // Check: Key number constrains child numbers + if (childCount != 0 && childCount != keyCount + 1) { + this.valid = false; + this.msg += MessageFormat.format("[{0} wrong number of children with respect to key count]", //$NON-NLS-1$ + new Object[] { new Long(node) }); + } + + // The root node is excused from the remaining node constraints. + if (node == BTree.this.db.getRecPtr(BTree.this.rootPointer)) { + return; + } + + // Check: Non-root nodes must have a keyCount within a certain range + if (keyCount < BTree.this.minRecords || keyCount > BTree.this.maxRecords) { + this.valid = false; + this.msg += MessageFormat.format("[{0} key count out of range]", new Object[] { new Long(node) }); //$NON-NLS-1$ + } + + // Check: All leaf nodes are at the same depth + if (childCount == 0) { + if (this.leafDepth == null) { + this.leafDepth = new Integer(this.depth); + } + if (this.depth != this.leafDepth.intValue()) { + this.valid = false; + this.msg += "Leaf nodes at differing depths"; //$NON-NLS-1$ + } + } + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/Chunk.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/Chunk.java new file mode 100644 index 000000000..42194839c --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/Chunk.java @@ -0,0 +1,324 @@ +/******************************************************************************* + * Copyright (c) 2005, 2016 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX - Initial API and implementation + * Markus Schorn (Wind River Systems) + * IBM Corporation + * Sergey Prigogin (Google) + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * Caches the content of a piece of the database. + */ +final class Chunk { + final private byte[] fBuffer= new byte[Database.CHUNK_SIZE]; + + final Database fDatabase; + final int fSequenceNumber; + + boolean fCacheHitFlag; + boolean fDirty; + boolean fLocked; // locked chunks must not be released from cache. + int fCacheIndex= -1; + + Chunk(Database db, int sequenceNumber) { + this.fDatabase= db; + this.fSequenceNumber= sequenceNumber; + } + + void read() throws IndexException { + try { + final ByteBuffer buf= ByteBuffer.wrap(this.fBuffer); + this.fDatabase.read(buf, (long) this.fSequenceNumber * Database.CHUNK_SIZE); + } catch (IOException e) { + throw new IndexException(new DBStatus(e)); + } + } + + /** + * Uninterruptable. Returns true iff an attempt was made to interrupt the flush with + * {@link Thread#interrupt()}. + */ + boolean flush() throws IndexException { + boolean wasCanceled = false; + try { + final ByteBuffer buf= ByteBuffer.wrap(this.fBuffer); + wasCanceled = this.fDatabase.write(buf, (long) this.fSequenceNumber * Database.CHUNK_SIZE); + } catch (IOException e) { + throw new IndexException(new DBStatus(e)); + } + this.fDirty= false; + return wasCanceled; + } + + private static int recPtrToIndex(final long offset) { + return (int) (offset & Database.OFFSET_IN_CHUNK_MASK); + } + + public void putByte(final long offset, final byte value) { + assert this.fLocked; + this.fDirty= true; + this.fBuffer[recPtrToIndex(offset)]= value; + } + + public byte getByte(final long offset) { + return this.fBuffer[recPtrToIndex(offset)]; + } + + public byte[] getBytes(final long offset, final int length) { + final byte[] bytes = new byte[length]; + System.arraycopy(this.fBuffer, recPtrToIndex(offset), bytes, 0, length); + return bytes; + } + + public void putBytes(final long offset, final byte[] bytes) { + assert this.fLocked; + this.fDirty= true; + System.arraycopy(bytes, 0, this.fBuffer, recPtrToIndex(offset), bytes.length); + } + + public void putInt(final long offset, final int value) { + assert this.fLocked; + this.fDirty= true; + int idx= recPtrToIndex(offset); + putInt(value, this.fBuffer, idx); + } + + static final void putInt(final int value, final byte[] buffer, int idx) { + buffer[idx]= (byte) (value >> 24); + buffer[++idx]= (byte) (value >> 16); + buffer[++idx]= (byte) (value >> 8); + buffer[++idx]= (byte) (value); + } + + public int getInt(final long offset) { + return getInt(this.fBuffer, recPtrToIndex(offset)); + } + + static final int getInt(final byte[] buffer, int idx) { + return ((buffer[idx] & 0xff) << 24) | + ((buffer[++idx] & 0xff) << 16) | + ((buffer[++idx] & 0xff) << 8) | + ((buffer[++idx] & 0xff) << 0); + } + + /** + * A free Record Pointer is a pointer to a raw block, i.e. the + * pointer is not moved past the BLOCK_HEADER_SIZE. + */ + static int compressFreeRecPtr(final long value) { + // This assert verifies the alignment. We expect the low bits to be clear. + assert (value & (Database.BLOCK_SIZE_DELTA - 1)) == 0; + final int dense = (int) (value >> Database.BLOCK_SIZE_DELTA_BITS); + return dense; + } + + /** + * A free Record Pointer is a pointer to a raw block, + * i.e. the pointer is not moved past the BLOCK_HEADER_SIZE. + */ + static long expandToFreeRecPtr(int value) { + /* + * We need to properly manage the integer that was read. The value will be sign-extended + * so if the most significant bit is set, the resulting long will look negative. By + * masking it with ((long)1 << 32) - 1 we remove all the sign-extended bits and just + * have an unsigned 32-bit value as a long. This gives us one more useful bit in the + * stored record pointers. + */ + long address = value & 0xFFFFFFFFL; + return address << Database.BLOCK_SIZE_DELTA_BITS; + } + + /** + * A Record Pointer is a pointer as returned by Database.malloc(). + * This is a pointer to a block + BLOCK_HEADER_SIZE. + */ + public void putRecPtr(final long offset, final long value) { + assert this.fLocked; + this.fDirty = true; + int idx = recPtrToIndex(offset); + Database.putRecPtr(value, this.fBuffer, idx); + } + + /** + * A free Record Pointer is a pointer to a raw block, + * i.e. the pointer is not moved past the BLOCK_HEADER_SIZE. + */ + public void putFreeRecPtr(final long offset, final long value) { + assert this.fLocked; + this.fDirty = true; + int idx = recPtrToIndex(offset); + putInt(compressFreeRecPtr(value), this.fBuffer, idx); + } + + public long getRecPtr(final long offset) { + final int idx = recPtrToIndex(offset); + return Database.getRecPtr(this.fBuffer, idx); + } + + public long getFreeRecPtr(final long offset) { + final int idx = recPtrToIndex(offset); + int value = getInt(this.fBuffer, idx); + return expandToFreeRecPtr(value); + } + + public void put3ByteUnsignedInt(final long offset, final int value) { + assert this.fLocked; + this.fDirty= true; + int idx= recPtrToIndex(offset); + this.fBuffer[idx]= (byte) (value >> 16); + this.fBuffer[++idx]= (byte) (value >> 8); + this.fBuffer[++idx]= (byte) (value); + } + + public int get3ByteUnsignedInt(final long offset) { + int idx= recPtrToIndex(offset); + return ((this.fBuffer[idx] & 0xff) << 16) | + ((this.fBuffer[++idx] & 0xff) << 8) | + ((this.fBuffer[++idx] & 0xff) << 0); + } + + public void putShort(final long offset, final short value) { + assert this.fLocked; + this.fDirty= true; + int idx= recPtrToIndex(offset); + this.fBuffer[idx]= (byte) (value >> 8); + this.fBuffer[++idx]= (byte) (value); + } + + public short getShort(final long offset) { + int idx= recPtrToIndex(offset); + return (short) (((this.fBuffer[idx] << 8) | (this.fBuffer[++idx] & 0xff))); + } + + public long getLong(final long offset) { + int idx= recPtrToIndex(offset); + return ((((long) this.fBuffer[idx] & 0xff) << 56) | + (((long) this.fBuffer[++idx] & 0xff) << 48) | + (((long) this.fBuffer[++idx] & 0xff) << 40) | + (((long) this.fBuffer[++idx] & 0xff) << 32) | + (((long) this.fBuffer[++idx] & 0xff) << 24) | + (((long) this.fBuffer[++idx] & 0xff) << 16) | + (((long) this.fBuffer[++idx] & 0xff) << 8) | + (((long) this.fBuffer[++idx] & 0xff) << 0)); + } + + public double getDouble(long offset) { + return Double.longBitsToDouble(getLong(offset)); + } + + public float getFloat(long offset) { + return Float.intBitsToFloat(getInt(offset)); + } + + public void putLong(final long offset, final long value) { + assert this.fLocked; + this.fDirty= true; + int idx= recPtrToIndex(offset); + + this.fBuffer[idx]= (byte) (value >> 56); + this.fBuffer[++idx]= (byte) (value >> 48); + this.fBuffer[++idx]= (byte) (value >> 40); + this.fBuffer[++idx]= (byte) (value >> 32); + this.fBuffer[++idx]= (byte) (value >> 24); + this.fBuffer[++idx]= (byte) (value >> 16); + this.fBuffer[++idx]= (byte) (value >> 8); + this.fBuffer[++idx]= (byte) (value); + } + + public void putChar(final long offset, final char value) { + assert this.fLocked; + this.fDirty= true; + int idx= recPtrToIndex(offset); + this.fBuffer[idx]= (byte) (value >> 8); + this.fBuffer[++idx]= (byte) (value); + } + + public void putChars(final long offset, char[] chars, int start, int len) { + assert this.fLocked; + this.fDirty= true; + int idx= recPtrToIndex(offset)-1; + final int end= start + len; + for (int i = start; i < end; i++) { + char value= chars[i]; + this.fBuffer[++idx]= (byte) (value >> 8); + this.fBuffer[++idx]= (byte) (value); + } + } + + public void putCharsAsBytes(final long offset, char[] chars, int start, int len) { + assert this.fLocked; + this.fDirty= true; + int idx= recPtrToIndex(offset)-1; + final int end= start + len; + for (int i = start; i < end; i++) { + char value= chars[i]; + this.fBuffer[++idx]= (byte) (value); + } + } + + public void putDouble(final long offset, double value) { + putLong(offset, Double.doubleToLongBits(value)); + } + + public void putFloat(final long offset, float value) { + putInt(offset, Float.floatToIntBits(value)); + } + + public char getChar(final long offset) { + int idx= recPtrToIndex(offset); + return (char) (((this.fBuffer[idx] << 8) | (this.fBuffer[++idx] & 0xff))); + } + + public void getChars(final long offset, final char[] result, int start, int len) { + final ByteBuffer buf= ByteBuffer.wrap(this.fBuffer); + buf.position(recPtrToIndex(offset)); + buf.asCharBuffer().get(result, start, len); + } + + public void getCharsFromBytes(final long offset, final char[] result, int start, int len) { + final int pos = recPtrToIndex(offset); + for (int i = 0; i < len; i++) { + result[start + i] = (char) (this.fBuffer[pos + i] & 0xff); + } + } + + void clear(final long offset, final int length) { + assert this.fLocked; + this.fDirty= true; + int idx = recPtrToIndex(offset); + final int end = idx + length; + for (; idx < end; idx++) { + this.fBuffer[idx] = 0; + } + } + + void put(final long offset, final byte[] data, final int len) { + put(offset, data, 0, len); + } + + void put(final long offset, final byte[] data, int dataPos, final int len) { + assert this.fLocked; + this.fDirty = true; + int idx = recPtrToIndex(offset); + System.arraycopy(data, dataPos, this.fBuffer, idx, len); + } + + public void get(final long offset, byte[] data) { + get(offset, data, 0, data.length); + } + + public void get(final long offset, byte[] data, int dataPos, int len) { + int idx = recPtrToIndex(offset); + System.arraycopy(this.fBuffer, idx, data, dataPos, len); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/ChunkCache.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/ChunkCache.java new file mode 100644 index 000000000..1cd3736d8 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/ChunkCache.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2007, 2016 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Markus Schorn - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +public final class ChunkCache { + private static ChunkCache sSharedInstance= new ChunkCache(); + + private Chunk[] fPageTable; + private boolean fTableIsFull; + private int fPointer; + + public static ChunkCache getSharedInstance() { + return sSharedInstance; + } + + public ChunkCache() { + this(5 * 1024 * 1024); + } + + public ChunkCache(long maxSize) { + this.fPageTable= new Chunk[computeLength(maxSize)]; + } + + public synchronized void add(Chunk chunk, boolean locked) { + if (locked) { + chunk.fLocked= true; + } + if (chunk.fCacheIndex >= 0) { + chunk.fCacheHitFlag= true; + return; + } + if (this.fTableIsFull) { + evictChunk(); + chunk.fCacheIndex= this.fPointer; + this.fPageTable[this.fPointer]= chunk; + } else { + chunk.fCacheIndex= this.fPointer; + this.fPageTable[this.fPointer]= chunk; + + this.fPointer++; + if (this.fPointer == this.fPageTable.length) { + this.fPointer= 0; + this.fTableIsFull= true; + } + } + } + + /** + * Evicts a chunk from the page table and the chunk table. + * After this method returns, {@link #fPointer} will contain + * the index of the evicted chunk within the page table. + */ + private void evictChunk() { + /* + * Use the CLOCK algorithm to determine which chunk to evict. + * i.e., if the chunk in the current slot of the page table has been + * recently referenced (i.e. the reference flag is set), unset the + * reference flag and move to the next slot. Otherwise, evict the + * chunk in the current slot. + */ + while (true) { + Chunk chunk = this.fPageTable[this.fPointer]; + if (chunk.fCacheHitFlag) { + chunk.fCacheHitFlag= false; + this.fPointer= (this.fPointer + 1) % this.fPageTable.length; + } else { + chunk.fDatabase.releaseChunk(chunk); + chunk.fCacheIndex= -1; + this.fPageTable[this.fPointer] = null; + return; + } + } + } + + public synchronized void remove(Chunk chunk) { + final int idx= chunk.fCacheIndex; + if (idx >= 0) { + if (this.fTableIsFull) { + this.fPointer= this.fPageTable.length-1; + this.fTableIsFull= false; + } else { + this.fPointer--; + } + chunk.fCacheIndex= -1; + final Chunk move= this.fPageTable[this.fPointer]; + this.fPageTable[idx]= move; + move.fCacheIndex= idx; + this.fPageTable[this.fPointer]= null; + } + } + + /** + * Returns the maximum size of the chunk cache in bytes. + */ + public synchronized long getMaxSize() { + return (long) this.fPageTable.length * Database.CHUNK_SIZE; + } + + /** + * Clears the page table and changes it to hold chunks with + * maximum total memory of <code>maxSize</code>. + * @param maxSize the total size of the chunks in bytes. + */ + public synchronized void setMaxSize(long maxSize) { + final int newLength= computeLength(maxSize); + final int oldLength= this.fTableIsFull ? this.fPageTable.length : this.fPointer; + if (newLength > oldLength) { + Chunk[] newTable= new Chunk[newLength]; + System.arraycopy(this.fPageTable, 0, newTable, 0, oldLength); + this.fTableIsFull= false; + this.fPointer= oldLength; + this.fPageTable= newTable; + } else { + for (int i= newLength; i < oldLength; i++) { + final Chunk chunk= this.fPageTable[i]; + chunk.fDatabase.releaseChunk(chunk); + chunk.fCacheIndex= -1; + } + Chunk[] newTable= new Chunk[newLength]; + System.arraycopy(this.fPageTable, 0, newTable, 0, newLength); + this.fTableIsFull= true; + this.fPointer= 0; + this.fPageTable= newTable; + } + } + + private int computeLength(long maxSize) { + long maxLength= Math.min(maxSize / Database.CHUNK_SIZE, Integer.MAX_VALUE); + return Math.max(1, (int) maxLength); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/DBProperties.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/DBProperties.java new file mode 100644 index 000000000..cfa6050bb --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/DBProperties.java @@ -0,0 +1,261 @@ +/******************************************************************************* + * Copyright (c) 2007, 2016 Symbian Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Ferguson (Symbian) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.jdt.internal.core.nd.Nd; + +/** + * DBProperties is a bare-bones implementation of a String->String mapping. It is neither + * a Map or a Properties subclass, because of their more general applications. + */ +public class DBProperties { + static final int PROP_INDEX = 0; + static final int RECORD_SIZE = 4; + + protected BTree index; + protected Database db; + protected long record; + + /** + * Allocate storage for a new DBProperties record in the specified database + */ + public DBProperties(Nd nd) throws IndexException { + Database database = nd.getDB(); + this.record= database.malloc(RECORD_SIZE, Database.POOL_DB_PROPERTIES); + this.index= new BTree(nd, this.record + PROP_INDEX, DBProperty.getComparator()); + this.db= database; + } + + /** + * Creates an object for accessing an existing DBProperties record at the specified location + * of the specified database. + */ + public DBProperties(Nd nd, long record) throws IndexException { + Database database = nd.getDB(); + this.record= record; + this.index= new BTree(nd, record + PROP_INDEX, DBProperty.getComparator()); + this.db= database; + } + + /** + * Reads the named property from this properties storage. + * @param key a case-sensitive identifier for a property, or null + * @return the value associated with the key, or null if either no such property is set, + * or the specified key was null + * @throws IndexException + */ + public String getProperty(String key) throws IndexException { + if (key != null) { + DBProperty existing= DBProperty.search(this.db, this.index, key); + if (existing != null) { + return existing.getValue().getString(); + } + } + return null; + } + + /** + * Reads the named property from this properties storage, returning the default value if there + * is no such property. + * @param key a case-sensitive identifier for a property, or null + * @param defaultValue a value to return in case the specified key was null + * @return the value associated with the key, or the specified default value if either no such + * property is set, or the specified key was null + * @throws IndexException + */ + public String getProperty(String key, String defaultValue) throws IndexException { + String val= getProperty(key); + return (val == null) ? defaultValue : val; + } + + /** + * Returns the Set of property names stored in this object + * @return the Set of property names stored in this object + * @throws IndexException + */ + public Set<String> getKeySet() throws IndexException { + return DBProperty.getKeySet(this.db, this.index); + } + + /** + * Writes the key, value mapping to the properties. If a mapping for the + * same key already exists, it is overwritten. + * @param key a non-null property name + * @param value a value to associate with the key. may not be null. + * @throws IndexException + * @throws NullPointerException if key is null + */ + public void setProperty(String key, String value) throws IndexException { + removeProperty(key); + DBProperty newProperty= new DBProperty(this.db, key, value); + this.index.insert(newProperty.getRecord()); + } + + /** + * Deletes a property from this DBProperties object. + * @param key + * @return whether a property with matching key existed and was removed, or false if the key + * was null + * @throws IndexException + */ + public boolean removeProperty(String key) throws IndexException { + if (key != null) { + DBProperty existing= DBProperty.search(this.db, this.index, key); + if (existing != null) { + this.index.delete(existing.getRecord()); + existing.delete(); + return true; + } + } + return false; + } + + /** + * Deletes all properties, does not delete the record associated with the object itself + * - that is it can be re-populated. + * @throws IndexException + */ + public void clear() throws IndexException { + this.index.accept(new IBTreeVisitor(){ + @Override + public int compare(long address) throws IndexException { + return 0; + } + @Override + public boolean visit(long address) throws IndexException { + new DBProperty(DBProperties.this.db, address).delete(); + return false; // there should never be duplicates + } + }); + } + + /** + * Deletes all properties stored in this object and the record associated with this object + * itself. + * <br><br> + * <b>The behaviour of objects of this class after calling this method is undefined</b> + * @throws IndexException + */ + public void delete() throws IndexException { + clear(); + this.db.free(this.record, Database.POOL_DB_PROPERTIES); + } + + public long getRecord() { + return this.record; + } + + private static class DBProperty { + static final int KEY = 0; + static final int VALUE = 4; + @SuppressWarnings("hiding") + static final int RECORD_SIZE = 8; + + Database db; + long record; + + public long getRecord() { + return this.record; + } + + /** + * Allocates and initializes a record in the specified database for a DBProperty record + * @param db + * @param key a non-null property name + * @param value a non-null property value + * @throws IndexException + */ + DBProperty(Database db, String key, String value) throws IndexException { + assert key != null; + assert value != null; + IString dbkey= db.newString(key); + IString dbvalue= db.newString(value); + this.record= db.malloc(RECORD_SIZE, Database.POOL_DB_PROPERTIES); + db.putRecPtr(this.record + KEY, dbkey.getRecord()); + db.putRecPtr(this.record + VALUE, dbvalue.getRecord()); + this.db= db; + } + + /** + * Returns an object for accessing an existing DBProperty record at the specified location + * in the specified database. + * @param db + * @param record + */ + DBProperty(Database db, long record) { + this.record= record; + this.db= db; + } + + public IString getKey() throws IndexException { + return this.db.getString(this.db.getRecPtr(this.record + KEY)); + } + + public IString getValue() throws IndexException { + return this.db.getString(this.db.getRecPtr(this.record + VALUE)); + } + + public static IBTreeComparator getComparator() { + return new IBTreeComparator() { + @Override + public int compare(Nd nd, long record1, long record2) throws IndexException { + Database db = nd.getDB(); + IString left= db.getString(db.getRecPtr(record1 + KEY)); + IString right= db.getString(db.getRecPtr(record2 + KEY)); + return left.compare(right, true); + } + }; + } + + public static DBProperty search(final Database db, final BTree index, final String key) throws IndexException { + final DBProperty[] result= new DBProperty[1]; + index.accept(new IBTreeVisitor(){ + @Override + public int compare(long record) throws IndexException { + return db.getString(db.getRecPtr(record + KEY)).compare(key, true); + } + + @Override + public boolean visit(long record) throws IndexException { + result[0] = new DBProperty(db, record); + return false; // There should never be duplicates. + } + }); + return result[0]; + } + + public static Set<String> getKeySet(final Database db, final BTree index) throws IndexException { + final Set<String> result= new HashSet<String>(); + index.accept(new IBTreeVisitor(){ + @Override + public int compare(long record) throws IndexException { + return 0; + } + + @Override + public boolean visit(long record) throws IndexException { + result.add(new DBProperty(db, record).getKey().getString()); + return true; // There should never be duplicates. + } + }); + return result; + } + + public void delete() throws IndexException { + this.db.getString(this.db.getRecPtr(this.record + KEY)).delete(); + this.db.getString(this.db.getRecPtr(this.record + VALUE)).delete(); + this.db.free(this.record, Database.POOL_DB_PROPERTIES); + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/DBStatus.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/DBStatus.java new file mode 100644 index 000000000..d881ff203 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/DBStatus.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2005, 2016 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX - Initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +import java.io.IOException; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +public class DBStatus extends Status { + /** + * @param exception + */ + public DBStatus(IOException exception) { + super(IStatus.ERROR, Package.PLUGIN_ID, 0, "IOException", exception); //$NON-NLS-1$ + } + + public DBStatus(String msg) { + super(IStatus.ERROR, Package.PLUGIN_ID, 0, "Error", null); //$NON-NLS-1$ + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/Database.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/Database.java new file mode 100644 index 000000000..95b4b8dfa --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/Database.java @@ -0,0 +1,959 @@ +/******************************************************************************* + * Copyright (c) 2005, 2016 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX - Initial API and implementation + * Symbian - Add some non-javadoc implementation notes + * Markus Schorn (Wind River Systems) + * IBM Corporation + * Sergey Prigogin (Google) + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedByInterruptException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.FileChannel; +import java.util.ArrayList; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; + +import com.ibm.icu.text.MessageFormat; + +/** + * Database encapsulates access to a flat binary format file with a memory-manager-like API for + * obtaining and releasing areas of storage (memory). + */ +/* + * The file encapsulated is divided into Chunks of size CHUNK_SIZE, and a table of contents + * mapping chunk index to chunk address is maintained. Chunk structure exists only conceptually - + * it is not a structure that appears in the file. + * + * ===== The first chunk is used by Database itself for house-keeping purposes and has structure + * + * offset content + * _____________________________ + * 0 | version number + * INT_SIZE | pointer to head of linked list of blocks of size MIN_BLOCK_DELTAS*BLOCK_SIZE_DELTA + * .. | ... + * INT_SIZE * (M + 1) | pointer to head of linked list of blocks of size (M + MIN_BLOCK_DELTAS) * BLOCK_SIZE_DELTA + * WRITE_NUMBER_OFFSET | long integer which is incremented on every write + * MALLOC_STATS_OFFSET | memory usage statistics + * DATA_AREA | The database singletons are stored here and use the remainder of chunk 0 + * + * M = CHUNK_SIZE / BLOCK_SIZE_DELTA - MIN_BLOCK_DELTAS + * + * ===== block structure (for free/unused blocks) + * + * offset content + * _____________________________ + * 0 | size of block (positive indicates an unused block) (2 bytes) + * PREV_OFFSET | pointer to previous block (of same size) (only in free blocks) + * NEXT_OFFSET | pointer to next block (of same size) (only in free blocks) + * ... | unused space + * + *====== block structure (for allocated blocks) + * + * offset content + * _____________________________ + * 0 | size of block (negative indicates the block is in use) (2 bytes) + * 2 | content of the struct + * + */ +public class Database { + public static final int CHAR_SIZE = 2; + public static final int BYTE_SIZE = 1; + public static final int SHORT_SIZE = 2; + public static final int INT_SIZE = 4; + public static final int LONG_SIZE = 8; + public static final int CHUNK_SIZE = 1024 * 4; + public static final int OFFSET_IN_CHUNK_MASK= CHUNK_SIZE - 1; + public static final int BLOCK_HEADER_SIZE = SHORT_SIZE; + + public static final int BLOCK_SIZE_DELTA_BITS = 3; + public static final int BLOCK_SIZE_DELTA= 1 << BLOCK_SIZE_DELTA_BITS; + + // Fields that are only used by free blocks + private static final int BLOCK_PREV_OFFSET = BLOCK_HEADER_SIZE; + private static final int BLOCK_NEXT_OFFSET = BLOCK_HEADER_SIZE + INT_SIZE; + private static final int FREE_BLOCK_HEADER_SIZE = BLOCK_NEXT_OFFSET + INT_SIZE; + + public static final int MIN_BLOCK_DELTAS = (FREE_BLOCK_HEADER_SIZE + BLOCK_SIZE_DELTA - 1) / + BLOCK_SIZE_DELTA; // Must be enough multiples of BLOCK_SIZE_DELTA in order to fit the free block header + public static final int MAX_BLOCK_DELTAS = CHUNK_SIZE / BLOCK_SIZE_DELTA; + public static final int MAX_MALLOC_SIZE = MAX_BLOCK_DELTAS * BLOCK_SIZE_DELTA - BLOCK_HEADER_SIZE; + public static final int PTR_SIZE = 4; // size of a pointer in the database in bytes + public static final int STRING_SIZE = PTR_SIZE; + public static final int FLOAT_SIZE = INT_SIZE; + public static final int DOUBLE_SIZE = LONG_SIZE; + public static final long MAX_DB_SIZE= ((long) 1 << (Integer.SIZE + BLOCK_SIZE_DELTA_BITS)); + + public static final int VERSION_OFFSET = 0; + private static final int MALLOC_TABLE_OFFSET = VERSION_OFFSET + INT_SIZE; + public static final int WRITE_NUMBER_OFFSET = MALLOC_TABLE_OFFSET + + (CHUNK_SIZE / BLOCK_SIZE_DELTA - MIN_BLOCK_DELTAS + 1) * INT_SIZE; + public static final int MALLOC_STATS_OFFSET = WRITE_NUMBER_OFFSET + LONG_SIZE; + public static final int DATA_AREA_OFFSET = MALLOC_STATS_OFFSET + MemoryStats.SIZE; + + // Malloc pool IDs (used for classifying memory allocations and recording statistics about them) + /** Misc pool -- may be used for any purpose that doesn't fit the IDs below. */ + public static final short POOL_MISC = 0x0000; + public static final short POOL_BTREE = 0x0001; + public static final short POOL_DB_PROPERTIES = 0x0002; + public static final short POOL_STRING_LONG = 0x0003; + public static final short POOL_STRING_SHORT = 0x0004; + public static final short POOL_LINKED_LIST = 0x0005; + public static final short POOL_STRING_SET = 0x0006; + public static final short POOL_GROWABLE_ARRAY = 0x0007; + /** Id for the first node type. All node types will record their stats in a pool whose ID is POOL_FIRST_NODE_TYPE + node_id*/ + public static final short POOL_FIRST_NODE_TYPE = 0x0100; + + private final File fLocation; + private final boolean fReadOnly; + private RandomAccessFile fFile; + private boolean fExclusiveLock; // Necessary for any write operation. + private boolean fLocked; // Necessary for any operation. + private boolean fIsMarkedIncomplete; + + private int fVersion; + private final Chunk fHeaderChunk; + private Chunk[] fChunks; + private int fChunksUsed; + private int fChunksAllocated; + private ChunkCache fCache; + + private long malloced; + private long freed; + private long cacheHits; + private long cacheMisses; + + private MemoryStats memoryUsage; + + /** + * Construct a new Database object, creating a backing file if necessary. + * @param location the local file path for the database + * @param cache the cache to be used optimization + * @param version the version number to store in the database (only applicable for new databases) + * @param openReadOnly whether this Database object will ever need writing to + * @throws IndexException + */ + public Database(File location, ChunkCache cache, int version, boolean openReadOnly) throws IndexException { + try { + this.fLocation = location; + this.fReadOnly= openReadOnly; + this.fCache= cache; + openFile(); + + int nChunksOnDisk = (int) (this.fFile.length() / CHUNK_SIZE); + this.fHeaderChunk= new Chunk(this, 0); + this.fHeaderChunk.fLocked= true; // Never makes it into the cache, needed to satisfy assertions. + if (nChunksOnDisk <= 0) { + this.fVersion= version; + this.fChunks= new Chunk[1]; + this.fChunksUsed = this.fChunksAllocated = this.fChunks.length; + } else { + this.fHeaderChunk.read(); + this.fVersion= this.fHeaderChunk.getInt(VERSION_OFFSET); + this.fChunks = new Chunk[nChunksOnDisk]; // chunk[0] is unused. + this.fChunksUsed = this.fChunksAllocated = nChunksOnDisk; + } + } catch (IOException e) { + throw new IndexException(new DBStatus(e)); + } + this.memoryUsage = new MemoryStats(this.fHeaderChunk, MALLOC_STATS_OFFSET); + } + + private static int divideRoundingUp(int num, int den) { + return (num + den - 1) / den; + } + + private void openFile() throws FileNotFoundException { + this.fFile = new RandomAccessFile(this.fLocation, this.fReadOnly ? "r" : "rw"); //$NON-NLS-1$ //$NON-NLS-2$ + } + + void read(ByteBuffer buf, long position) throws IOException { + int retries= 0; + do { + try { + this.fFile.getChannel().read(buf, position); + return; + } catch (ClosedChannelException e) { + // Always reopen the file if possible or subsequent reads will fail. + openFile(); + + // This is the most common type of interruption. If another thread called Thread.interrupt, + // throw an OperationCanceledException. + if (e instanceof ClosedByInterruptException) { + throw new OperationCanceledException(); + } + + // If we've retried too many times, just rethrow the exception. + if (++retries >= 20) { + throw e; + } + + // Otherwise, retry + } + } while (true); + } + + /** + * Attempts to write to the given position in the file. Will retry if interrupted by Thread.interrupt() until, + * the write succeeds. It will return true if any call to Thread.interrupt() was detected. + * + * @return true iff a call to Thread.interrupt() was detected at any point during the operation. + * @throws IOException + */ + boolean write(ByteBuffer buf, long position) throws IOException { + return performUninterruptableWrite(() -> {this.fFile.getChannel().write(buf, position);}); + } + + private static interface IORunnable { + void run() throws IOException; + } + + /** + * Attempts to perform an uninterruptable write operation on the database. Returns true if an attempt was made + * to interrupt it. + * + * @throws IOException + */ + private boolean performUninterruptableWrite(IORunnable runnable) throws IOException { + boolean interrupted = false; + int retries= 0; + while (true) { + try { + runnable.run(); + return interrupted; + } catch (ClosedChannelException e) { + openFile(); + + if (e instanceof ClosedByInterruptException) { + // Retry forever if necessary as long as another thread is calling Thread.interrupt + interrupted = true; + } else { + if (++retries > 20) { + throw e; + } + } + } + } + } + + public void transferTo(FileChannel target) throws IOException { + assert this.fLocked; + final FileChannel from= this.fFile.getChannel(); + long nRead = 0; + long position = 0; + long size = from.size(); + while (position < size) { + nRead = from.transferTo(position, 4096 * 16, target); + if (nRead == 0) { + break; // Should not happen. + } else { + position+= nRead; + } + } + } + + public int getVersion() { + return this.fVersion; + } + + public void setVersion(int version) throws IndexException { + assert this.fExclusiveLock; + this.fHeaderChunk.putInt(VERSION_OFFSET, version); + this.fVersion= version; + } + + /** + * Empty the contents of the Database, make it ready to start again. Interrupting the thread with + * {@link Thread#interrupt()} won't interrupt the write. Returns true iff the thread was interrupted + * with {@link Thread#interrupt()}. + * + * @throws IndexException + */ + public boolean clear(int version) throws IndexException { + assert this.fExclusiveLock; + boolean wasCanceled = false; + removeChunksFromCache(); + + this.fVersion= version; + // Clear the first chunk. + this.fHeaderChunk.clear(0, CHUNK_SIZE); + // Chunks have been removed from the cache, so we may just reset the array of chunks. + this.fChunks = new Chunk[] {null}; + this.fChunksUsed = this.fChunksAllocated = this.fChunks.length; + try { + wasCanceled = this.fHeaderChunk.flush() || wasCanceled; // Zero out header chunk. + wasCanceled = performUninterruptableWrite(() -> { + this.fFile.getChannel().truncate(CHUNK_SIZE); + }) || wasCanceled; + } catch (IOException e) { + Package.log(e); + } + this.malloced = this.freed = 0; + /* + * This is for debugging purposes in order to simulate having a very large Nd database. + * This will set aside the specified number of chunks. + * Nothing uses these chunks so subsequent allocations come after these fillers. + * The special function createNewChunks allocates all of these chunks at once. + * 524288 for a file starting at 2G + * 8388608 for a file starting at 32G + * + */ + long setasideChunks = Long.getLong("org.eclipse.jdt.core.parser.nd.chunks", 0); //$NON-NLS-1$ + if (setasideChunks != 0) { + setVersion(getVersion()); + createNewChunks((int) setasideChunks); + wasCanceled = flush() || wasCanceled; + } + this.memoryUsage.refresh(); + return wasCanceled; + } + + private void removeChunksFromCache() { + synchronized (this.fCache) { + for (int i= 1; i < this.fChunks.length; i++) { + Chunk chunk= this.fChunks[i]; + if (chunk != null) { + this.fCache.remove(chunk); + this.fChunks[i]= null; + } + } + } + } + + /** + * Return the Chunk that contains the given offset. + * @throws IndexException + */ + public Chunk getChunk(long offset) throws IndexException { + assertLocked(); + if (offset < CHUNK_SIZE) { + return this.fHeaderChunk; + } + long long_index = offset / CHUNK_SIZE; + assert long_index < Integer.MAX_VALUE; + + synchronized (this.fCache) { + assert this.fLocked; + final int index = (int) long_index; + if (index < 0 || index >= this.fChunks.length) { + databaseCorruptionDetected(); + } + Chunk chunk= this.fChunks[index]; + if (chunk == null) { + this.cacheMisses++; + chunk = new Chunk(this, index); + chunk.read(); + this.fChunks[index] = chunk; + } else { + this.cacheHits++; + } + this.fCache.add(chunk, this.fExclusiveLock); + return chunk; + } + } + + public void assertLocked() { + if (!this.fLocked) { + throw new IllegalStateException("Database not locked!"); //$NON-NLS-1$ + } + } + + private void databaseCorruptionDetected() throws IndexException { + String msg = MessageFormat.format("Corrupted database: {0}", //$NON-NLS-1$ + new Object[] { this.fLocation.getName() }); + throw new IndexException(new DBStatus(msg)); + } + + /** + * Copies numBytes from source to destination + */ + public void memcpy(long dest, long source, int numBytes) { + assert numBytes >= 0; + assert numBytes <= MAX_MALLOC_SIZE; + // TODO: make use of lower-level System.arrayCopy + for (int count = 0; count < numBytes; count++) { + putByte(dest + count, getByte(source + count)); + } + } + + /** + * Allocate a block out of the database. + */ + public long malloc(final int datasize, final short poolId) throws IndexException { + assert this.fExclusiveLock; + assert datasize >= 0; + assert datasize <= MAX_MALLOC_SIZE; + + int needDeltas= divideRoundingUp(datasize + BLOCK_HEADER_SIZE, BLOCK_SIZE_DELTA); + if (needDeltas < MIN_BLOCK_DELTAS) { + needDeltas= MIN_BLOCK_DELTAS; + } + + // Which block size. + long freeblock = 0; + int useDeltas; + for (useDeltas= needDeltas; useDeltas <= MAX_BLOCK_DELTAS; useDeltas++) { + freeblock = getFirstBlock(useDeltas * BLOCK_SIZE_DELTA); + if (freeblock != 0) + break; + } + + // Get the block. + Chunk chunk; + if (freeblock == 0) { + // Allocate a new chunk. + freeblock= createNewChunk(); + useDeltas = MAX_BLOCK_DELTAS; + chunk = getChunk(freeblock); + } else { + chunk = getChunk(freeblock); + removeBlock(chunk, useDeltas * BLOCK_SIZE_DELTA, freeblock); + } + + final int unusedDeltas = useDeltas - needDeltas; + if (unusedDeltas >= MIN_BLOCK_DELTAS) { + // Add in the unused part of our block. + addBlock(chunk, unusedDeltas * BLOCK_SIZE_DELTA, freeblock + needDeltas * BLOCK_SIZE_DELTA); + useDeltas= needDeltas; + } + + // Make our size negative to show in use. + final int usedSize= useDeltas * BLOCK_SIZE_DELTA; + chunk.putShort(freeblock, (short) -usedSize); + + // Clear out the block, lots of people are expecting this. + chunk.clear(freeblock + BLOCK_HEADER_SIZE, usedSize - BLOCK_HEADER_SIZE); + + this.malloced += usedSize; + long result = freeblock + BLOCK_HEADER_SIZE; + this.memoryUsage.recordMalloc(poolId, usedSize); + return result; + } + + private long createNewChunk() throws IndexException { + assert this.fExclusiveLock; + synchronized (this.fCache) { + final int newChunkIndex = this.fChunksUsed; // fChunks.length; + + final Chunk chunk = new Chunk(this, newChunkIndex); + chunk.fDirty = true; + + if (newChunkIndex >= this.fChunksAllocated) { + int increment = Math.max(1024, this.fChunksAllocated / 20); + Chunk[] newchunks = new Chunk[this.fChunksAllocated + increment]; + System.arraycopy(this.fChunks, 0, newchunks, 0, this.fChunksAllocated); + + this.fChunks = newchunks; + this.fChunksAllocated += increment; + } + this.fChunksUsed += 1; + this.fChunks[newChunkIndex] = chunk; + + this.fCache.add(chunk, true); + long address = (long) newChunkIndex * CHUNK_SIZE; + + /* + * Non-dense pointers are at most 31 bits dense pointers are at most 35 bits Check the sizes here and throw + * an exception if the address is too large. By throwing the IndexException with the special status, the + * indexing operation should be stopped. This is desired since generally, once the max size is exceeded, + * there are lots of errors. + */ + if (address >= MAX_DB_SIZE) { + Object bindings[] = { this.getLocation().getAbsolutePath(), MAX_DB_SIZE }; + throw new IndexException(new Status(IStatus.ERROR, Package.PLUGIN_ID, Package.STATUS_DATABASE_TOO_LARGE, + NLS.bind("Database too large! Address = " + address + ", max size = " + MAX_DB_SIZE, bindings), //$NON-NLS-1$ //$NON-NLS-2$ + null)); + } + return address; + } + } + + /** + * For testing purposes, only. + */ + private long createNewChunks(int numChunks) throws IndexException { + assert this.fExclusiveLock; + synchronized (this.fCache) { + final int oldLen= this.fChunks.length; + Chunk[] newchunks = new Chunk[oldLen + numChunks]; + System.arraycopy(this.fChunks, 0, newchunks, 0, oldLen); + final Chunk chunk= new Chunk(this, oldLen + numChunks - 1); + chunk.fDirty= true; + newchunks[ oldLen + numChunks - 1 ] = chunk; + this.fChunks= newchunks; + this.fCache.add(chunk, true); + this.fChunksAllocated=oldLen + numChunks; + this.fChunksUsed=oldLen + numChunks; + return (long) (oldLen + numChunks - 1) * CHUNK_SIZE; + } + } + + /** + * @param blocksize (must be a multiple of BLOCK_SIZE_DELTA) + */ + private long getFirstBlock(int blocksize) throws IndexException { + assert this.fLocked; + return this.fHeaderChunk.getFreeRecPtr(MALLOC_TABLE_OFFSET + (blocksize / BLOCK_SIZE_DELTA - MIN_BLOCK_DELTAS) * INT_SIZE); + } + + private void setFirstBlock(int blocksize, long block) throws IndexException { + assert this.fExclusiveLock; + this.fHeaderChunk.putFreeRecPtr(MALLOC_TABLE_OFFSET + (blocksize / BLOCK_SIZE_DELTA - MIN_BLOCK_DELTAS) * INT_SIZE, block); + } + + private void removeBlock(Chunk chunk, int blocksize, long block) throws IndexException { + assert this.fExclusiveLock; + long prevblock = chunk.getFreeRecPtr(block + BLOCK_PREV_OFFSET); + long nextblock = chunk.getFreeRecPtr(block + BLOCK_NEXT_OFFSET); + if (prevblock != 0) { + putFreeRecPtr(prevblock + BLOCK_NEXT_OFFSET, nextblock); + } else { // We were the head. + setFirstBlock(blocksize, nextblock); + } + + if (nextblock != 0) + putFreeRecPtr(nextblock + BLOCK_PREV_OFFSET, prevblock); + } + + private void addBlock(Chunk chunk, int blocksize, long block) throws IndexException { + assert this.fExclusiveLock; + // Mark our size + chunk.putShort(block, (short) blocksize); + + // Add us to the head of the list. + long prevfirst = getFirstBlock(blocksize); + chunk.putFreeRecPtr(block + BLOCK_PREV_OFFSET, 0); + chunk.putFreeRecPtr(block + BLOCK_NEXT_OFFSET, prevfirst); + if (prevfirst != 0) + putFreeRecPtr(prevfirst + BLOCK_PREV_OFFSET, block); + setFirstBlock(blocksize, block); + } + + /** + * Free an allocated block. + * + * @param address memory address to be freed + * @param poolId the same ID that was previously passed into malloc when allocating this memory address + */ + public void free(long address, short poolId) throws IndexException { + assert this.fExclusiveLock; + if (address == 0) { + return; + } + // TODO Look for opportunities to merge blocks + long block = address - BLOCK_HEADER_SIZE; + Chunk chunk = getChunk(block); + int blocksize = - chunk.getShort(block); + if (blocksize < 0) { + // Already freed. + throw new IndexException(new Status(IStatus.ERROR, Package.PLUGIN_ID, 0, + "Already freed record " + address, new Exception())); //$NON-NLS-1$ + } + addBlock(chunk, blocksize, block); + this.freed += blocksize; + this.memoryUsage.recordFree(poolId, blocksize); + } + + public void putByte(long offset, byte value) throws IndexException { + getChunk(offset).putByte(offset, value); + } + + public byte getByte(long offset) throws IndexException { + return getChunk(offset).getByte(offset); + } + + public void putInt(long offset, int value) throws IndexException { + getChunk(offset).putInt(offset, value); + } + + public int getInt(long offset) throws IndexException { + return getChunk(offset).getInt(offset); + } + + public void putRecPtr(long offset, long value) throws IndexException { + getChunk(offset).putRecPtr(offset, value); + } + + public long getRecPtr(long offset) throws IndexException { + return getChunk(offset).getRecPtr(offset); + } + + private void putFreeRecPtr(long offset, long value) throws IndexException { + getChunk(offset).putFreeRecPtr(offset, value); + } + + private long getFreeRecPtr(long offset) throws IndexException { + return getChunk(offset).getFreeRecPtr(offset); + } + + public void put3ByteUnsignedInt(long offset, int value) throws IndexException { + getChunk(offset).put3ByteUnsignedInt(offset, value); + } + + public int get3ByteUnsignedInt(long offset) throws IndexException { + return getChunk(offset).get3ByteUnsignedInt(offset); + } + + public void putShort(long offset, short value) throws IndexException { + getChunk(offset).putShort(offset, value); + } + + public short getShort(long offset) throws IndexException { + return getChunk(offset).getShort(offset); + } + + public void putLong(long offset, long value) throws IndexException { + getChunk(offset).putLong(offset, value); + } + + public void putDouble(long offset, double value) throws IndexException { + getChunk(offset).putDouble(offset, value); + } + + public void putFloat(long offset, float value) throws IndexException { + getChunk(offset).putFloat(offset, value); + } + + public long getLong(long offset) throws IndexException { + return getChunk(offset).getLong(offset); + } + + public double getDouble(long offset) throws IndexException { + return getChunk(offset).getDouble(offset); + } + + public float getFloat(long offset) throws IndexException { + return getChunk(offset).getFloat(offset); + } + + public void putChar(long offset, char value) throws IndexException { + getChunk(offset).putChar(offset, value); + } + + public char getChar(long offset) throws IndexException { + return getChunk(offset).getChar(offset); + } + + public void clearBytes(long offset, int byteCount) throws IndexException { + getChunk(offset).clear(offset, byteCount); + } + + public void putBytes(long offset, byte[] data, int len) throws IndexException { + getChunk(offset).put(offset, data, len); + } + + public void putBytes(long offset, byte[] data, int dataPos, int len) throws IndexException { + getChunk(offset).put(offset, data, dataPos, len); + } + + public void getBytes(long offset, byte[] data) throws IndexException { + getChunk(offset).get(offset, data); + } + + public void getBytes(long offset, byte[] data, int dataPos, int len) throws IndexException { + getChunk(offset).get(offset, data, dataPos, len); + } + + public IString newString(String string) throws IndexException { + return newString(string.toCharArray()); + } + + public IString newString(char[] chars) throws IndexException { + int len= chars.length; + int bytelen; + final boolean useBytes = useBytes(chars); + if (useBytes) { + bytelen= len; + } else { + bytelen= 2 * len; + } + + if (bytelen > ShortString.MAX_BYTE_LENGTH) { + return new LongString(this, chars, useBytes); + } else { + return new ShortString(this, chars, useBytes); + } + } + + private boolean useBytes(char[] chars) { + for (char c : chars) { + if ((c & 0xff00) != 0) + return false; + } + return true; + } + + public IString getString(long offset) throws IndexException { + final int l = getInt(offset); + int bytelen= l < 0 ? -l : 2 * l; + if (bytelen > ShortString.MAX_BYTE_LENGTH) { + return new LongString(this, offset); + } + return new ShortString(this, offset); + } + + public long getDatabaseSize() { + return this.fChunksUsed * CHUNK_SIZE; + } + + /** + * For debugging purposes, only. + */ + public void reportFreeBlocks() throws IndexException { + System.out.println("Allocated size: " + getDatabaseSize() + " bytes"); //$NON-NLS-1$ //$NON-NLS-2$ + System.out.println("malloc'ed: " + this.malloced); //$NON-NLS-1$ + System.out.println("free'd: " + this.freed); //$NON-NLS-1$ + System.out.println("wasted: " + (getDatabaseSize() - (this.malloced - this.freed))); //$NON-NLS-1$ + System.out.println("Free blocks"); //$NON-NLS-1$ + for (int bs = MIN_BLOCK_DELTAS*BLOCK_SIZE_DELTA; bs <= CHUNK_SIZE; bs += BLOCK_SIZE_DELTA) { + int count = 0; + long block = getFirstBlock(bs); + while (block != 0) { + ++count; + block = getFreeRecPtr(block + BLOCK_NEXT_OFFSET); + } + if (count != 0) + System.out.println("Block size: " + bs + "=" + count); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + /** + * Closes the database. + * <p> + * The behavior of any further calls to the Database is undefined + * @throws IndexException + */ + public void close() throws IndexException { + assert this.fExclusiveLock; + flush(); + removeChunksFromCache(); + + // Chunks have been removed from the cache, so we are fine. + this.fHeaderChunk.clear(0, CHUNK_SIZE); + this.memoryUsage.refresh(); + this.fHeaderChunk.fDirty= false; + this.fChunks= new Chunk[] { null }; + this.fChunksUsed = this.fChunksAllocated = this.fChunks.length; + try { + this.fFile.close(); + } catch (IOException e) { + throw new IndexException(new DBStatus(e)); + } + } + + /** + * This method is public for testing purposes only. + */ + public File getLocation() { + return this.fLocation; + } + + /** + * Called from any thread via the cache, protected by {@link #fCache}. + */ + void releaseChunk(final Chunk chunk) { + if (!chunk.fLocked) { + this.fChunks[chunk.fSequenceNumber]= null; + } + } + + /** + * Returns the cache used for this database. + * @since 4.0 + */ + public ChunkCache getChunkCache() { + return this.fCache; + } + + /** + * Asserts that database is used by one thread exclusively. This is necessary when doing + * write operations. + */ + public void setExclusiveLock() { + this.fExclusiveLock= true; + this.fLocked= true; + } + + public void setLocked(boolean val) { + this.fLocked= val; + } + + public boolean giveUpExclusiveLock(final boolean flush) throws IndexException { + boolean wasInterrupted = false; + if (this.fExclusiveLock) { + try { + ArrayList<Chunk> dirtyChunks= new ArrayList<>(); + synchronized (this.fCache) { + for (int i= 1; i < this.fChunksUsed; i++) { + Chunk chunk= this.fChunks[i]; + if (chunk != null) { + if (chunk.fCacheIndex < 0) { + // Locked chunk that has been removed from cache. + if (chunk.fDirty) { + dirtyChunks.add(chunk); // Keep in fChunks until it is flushed. + } else { + chunk.fLocked= false; + this.fChunks[i]= null; + } + } else if (chunk.fLocked) { + // Locked chunk, still in cache. + if (chunk.fDirty) { + if (flush) { + dirtyChunks.add(chunk); + } + } else { + chunk.fLocked= false; + } + } else { + assert !chunk.fDirty; // Dirty chunks must be locked. + } + } + } + } + // Also handles header chunk. + wasInterrupted = flushAndUnlockChunks(dirtyChunks, flush) || wasInterrupted; + } finally { + this.fExclusiveLock= false; + } + } + return wasInterrupted; + } + + public boolean flush() throws IndexException { + boolean wasInterrupted = false; + assert this.fLocked; + if (this.fExclusiveLock) { + try { + wasInterrupted = giveUpExclusiveLock(true) || wasInterrupted; + } finally { + setExclusiveLock(); + } + return wasInterrupted; + } + + // Be careful as other readers may access chunks concurrently. + ArrayList<Chunk> dirtyChunks= new ArrayList<>(); + synchronized (this.fCache) { + for (int i= 1; i < this.fChunksUsed ; i++) { + Chunk chunk= this.fChunks[i]; + if (chunk != null && chunk.fDirty) { + dirtyChunks.add(chunk); + } + } + } + + // Also handles header chunk. + return flushAndUnlockChunks(dirtyChunks, true) || wasInterrupted; + } + + /** + * Interrupting the thread with {@link Thread#interrupt()} won't interrupt the write. Returns true iff an attempt + * was made to interrupt the thread with {@link Thread#interrupt()}. + * + * @throws IndexException + */ + private boolean flushAndUnlockChunks(final ArrayList<Chunk> dirtyChunks, boolean isComplete) throws IndexException { + boolean wasInterrupted = false; + assert !Thread.holdsLock(this.fCache); + synchronized (this.fHeaderChunk) { + final boolean haveDirtyChunks = !dirtyChunks.isEmpty(); + if (haveDirtyChunks || this.fHeaderChunk.fDirty) { + wasInterrupted = markFileIncomplete() || wasInterrupted; + } + if (haveDirtyChunks) { + for (Chunk chunk : dirtyChunks) { + if (chunk.fDirty) { + wasInterrupted = chunk.flush() || wasInterrupted; + } + } + + // Only after the chunks are flushed we may unlock and release them. + synchronized (this.fCache) { + for (Chunk chunk : dirtyChunks) { + chunk.fLocked= false; + if (chunk.fCacheIndex < 0) { + this.fChunks[chunk.fSequenceNumber]= null; + } + } + } + } + + if (isComplete) { + if (this.fHeaderChunk.fDirty || this.fIsMarkedIncomplete) { + this.fHeaderChunk.putInt(VERSION_OFFSET, this.fVersion); + wasInterrupted = this.fHeaderChunk.flush() || wasInterrupted; + this.fIsMarkedIncomplete= false; + } + } + } + return wasInterrupted; + } + + private boolean markFileIncomplete() throws IndexException { + boolean wasInterrupted = false; + if (!this.fIsMarkedIncomplete) { + this.fIsMarkedIncomplete= true; + try { + final ByteBuffer buf= ByteBuffer.wrap(new byte[4]); + wasInterrupted = performUninterruptableWrite(() -> this.fFile.getChannel().write(buf, 0)); + } catch (IOException e) { + throw new IndexException(new DBStatus(e)); + } + } + return wasInterrupted; + } + + public void resetCacheCounters() { + this.cacheHits= this.cacheMisses= 0; + } + + public long getCacheHits() { + return this.cacheHits; + } + + public long getCacheMisses() { + return this.cacheMisses; + } + + public long getSizeBytes() throws IOException { + return this.fFile.length(); + } + + /** + * A Record Pointer is a pointer as returned by Database.malloc(). + * This is a pointer to a block + BLOCK_HEADER_SIZE. + */ + public static void putRecPtr(final long value, byte[] buffer, int idx) { + final int denseValue = value == 0 ? 0 : Chunk.compressFreeRecPtr(value - BLOCK_HEADER_SIZE); + Chunk.putInt(denseValue, buffer, idx); + } + + /** + * A Record Pointer is a pointer as returned by Database.malloc(). + * This is a pointer to a block + BLOCK_HEADER_SIZE. + */ + public static long getRecPtr(byte[] buffer, final int idx) { + int value = Chunk.getInt(buffer, idx); + long address = Chunk.expandToFreeRecPtr(value); + return address != 0 ? (address + BLOCK_HEADER_SIZE) : address; + } + + public MemoryStats getMemoryStats() { + return this.memoryUsage; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/EmptyString.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/EmptyString.java new file mode 100644 index 000000000..f32fdb552 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/EmptyString.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +/** + * Represents an empty string. + */ +public class EmptyString implements IString { + + private int compareResult; + private static EmptyString theEmptyString = new EmptyString(); + + private EmptyString() { + this.compareResult = "".compareTo("a"); //$NON-NLS-1$//$NON-NLS-2$ + } + + public static EmptyString create() { + return theEmptyString; + } + + @Override + public long getRecord() { + return 0; + } + + @Override + public int compare(IString string, boolean caseSensitive) { + if (string.length() == 0) { + return 0; + } + return this.compareResult; + } + + @Override + public int compare(String string, boolean caseSensitive) { + if (string.length() == 0) { + return 0; + } + return this.compareResult; + } + + @Override + public int compare(char[] chars, boolean caseSensitive) { + if (chars.length == 0) { + return 0; + } + return this.compareResult; + } + + @Override + public int compareCompatibleWithIgnoreCase(IString string) { + if (string.length() == 0) { + return 0; + } + return this.compareResult; + } + + @Override + public int compareCompatibleWithIgnoreCase(char[] chars) { + if (chars.length == 0) { + return 0; + } + return this.compareResult; + } + + @Override + public int comparePrefix(char[] name, boolean caseSensitive) { + if (name.length == 0) { + return 0; + } + return this.compareResult; + } + + @Override + public char[] getChars() { + return new char[0]; + } + + @Override + public String getString() { + return ""; //$NON-NLS-1$ + } + + @Override + public void delete() { + // Can't be deleted + } + + @Override + public int length() { + return 0; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/IBTreeComparator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/IBTreeComparator.java new file mode 100644 index 000000000..b2ab084cb --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/IBTreeComparator.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +import org.eclipse.jdt.internal.core.nd.Nd; + +public interface IBTreeComparator { + /** + * Compare two records. Used for insert. + */ + public abstract int compare(Nd nd, long record1, long record2); +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/IBTreeVisitor.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/IBTreeVisitor.java new file mode 100644 index 000000000..e0a28238f --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/IBTreeVisitor.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2005, 2016 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX - Initial API and implementation + * Markus Schorn (Wind River Systems) + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +/** + * The visitor visits all records where compare returns 0. + */ +public interface IBTreeVisitor { + /** + * Compare the record against an internally held key. The comparison must be + * compatible with the one used for the btree. + * Used for visiting. + * + * @param record + * @return -1 if record < key, 0 if record == key, 1 if record > key + * @throws IndexException + */ + public abstract int compare(long record) throws IndexException; + + /** + * Visit a given record and return whether to continue or not. + + * @return <code>true</code> to continue the visit, <code>false</code> to abort it. + * @throws IndexException + */ + public abstract boolean visit(long record) throws IndexException; +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/IString.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/IString.java new file mode 100644 index 000000000..84079794c --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/IString.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (c) 2006, 2016 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX - Initial API and implementation + * Andrew Ferguson (Symbian) + * Markus Schorn (Wind River Systems) + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +/** + * Interface for strings stored in the database. There is more than one string + * format. This interface hides that fact. + * + * @author Doug Schaefer + */ +public interface IString { + /** + * Get the offset of this IString record in the Nd + */ + public long getRecord(); + + // strcmp equivalents + /** + * Compare this IString record and the specified IString record + * @param string + * @param caseSensitive whether to compare in a case-sensitive way + * @return <ul><li> -1 if this < string + * <li> 0 if this == string + * <li> 1 if this > string + * </ul> + * @throws IndexException + */ + public int compare(IString string, boolean caseSensitive) throws IndexException; + + /** + * Compare this IString record and the specified String object + * @param string + * @param caseSensitive whether to compare in a case-sensitive way + * @return <ul><li> -1 if this < string + * <li> 0 if this == string + * <li> 1 if this > string + * </ul> + * @throws IndexException + */ + public int compare(String string, boolean caseSensitive) throws IndexException; + + /** + * Compare this IString record and the specified character array + * @param chars + * @param caseSensitive whether to compare in a case-sensitive way + * @return <ul><li> -1 if this < chars + * <li> 0 if this == chars + * <li> 1 if this > chars + * </ul> + * @throws IndexException + */ + public int compare(char[] chars, boolean caseSensitive) throws IndexException; + + /** + * Compare this IString record and the specified IString record in a case sensitive manner + * such that it is compatible with case insensitive comparison. + * @param string + * @return <ul><li> -1 if this < string + * <li> 0 if this == string + * <li> 1 if this > string + * </ul> + * @throws IndexException + */ + public int compareCompatibleWithIgnoreCase(IString string) throws IndexException; + + /** + * Compare this IString record and the specified char array in a case sensitive manner + * such that it is compatible with case insensitive comparison. + * @param chars + * @return <ul><li> -1 if this < string + * <li> 0 if this == string + * <li> 1 if this > string + * </ul> + * @throws IndexException + */ + public int compareCompatibleWithIgnoreCase(char[] chars) throws IndexException; + + /** + * Compare this IString record and the specified character array + * @param name the name to compare to + * @param caseSensitive whether to compare in a case-sensitive way + * @return <ul><li> -1 if this < chars + * <li> 0 if this has a prefix chars + * <li> 1 if this > chars and does not have the prefix + * </ul> + * @throws IndexException + */ + public int comparePrefix(char[] name, boolean caseSensitive) throws IndexException; + + /** + * Get an equivalent character array to this IString record<p> + * <b>N.B. This method can be expensive: compare and equals can be used for + * efficient comparisons</b> + * @return an equivalent character array to this IString record + * @throws IndexException + */ + public char[] getChars() throws IndexException; + + /** + * Get an equivalent String object to this IString record<p> + * <b>N.B. This method can be expensive: compare and equals can be used for + * efficient comparisons</b> + * @return an equivalent String object to this IString record + * @throws IndexException + */ + public String getString() throws IndexException; + + /** + * Free the associated record in the Nd + * @throws IndexException + */ + public void delete() throws IndexException; + + /** + * @return the length of the string + */ + public int length(); +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/IndexException.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/IndexException.java new file mode 100644 index 000000000..f6ecf9a79 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/IndexException.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * This exception indicates corruption in the JDT index database. + */ +public class IndexException extends RuntimeException { + + private IStatus status; + + public IndexException(IStatus status) { + super(status.getMessage()); + this.status = status; + } + + public IndexException(String message) { + this(new Status(IStatus.ERROR, "org.eclipse.jdt.core", message)); //$NON-NLS-1$ + } + + @Override + public synchronized Throwable getCause() { + return this.status.getException(); + } + + /** + * @return the status + */ + public IStatus getStatus() { + return this.status; + } + + private static final long serialVersionUID = -6561893929558916225L; + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/LongString.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/LongString.java new file mode 100644 index 000000000..c78b7f909 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/LongString.java @@ -0,0 +1,245 @@ +/******************************************************************************* + * Copyright (c) 2006, 2016 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX - Initial API and implementation + * Andrew Ferguson (Symbian) + * Markus Schorn (Wind River Systems) + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +/** + * This is for strings that take up more than on chunk. + * The string will need to be broken up into sections and then + * reassembled when necessary. + * + * @author Doug Schaefer + */ +public class LongString implements IString { + private final Database db; + private final long record; + private int hash; + + // Additional fields of first record. + private static final int LENGTH = 0; // Must be first to match ShortString. + private static final int NEXT1 = 4; + private static final int CHARS1 = 8; + + private static final int NUM_CHARS1 = (Database.MAX_MALLOC_SIZE - CHARS1) / 2; + + // Additional fields of subsequent records. + private static final int NEXTN = 0; + private static final int CHARSN = 4; + + private static final int NUM_CHARSN = (Database.MAX_MALLOC_SIZE - CHARSN) / 2; + + public LongString(Database db, long record) { + this.db = db; + this.record = record; + } + + public LongString(Database db, final char[] chars, boolean useBytes) throws IndexException { + final int numChars1 = useBytes ? NUM_CHARS1 * 2 : NUM_CHARS1; + final int numCharsn = useBytes ? NUM_CHARSN * 2 : NUM_CHARSN; + + this.db = db; + this.record = db.malloc(Database.MAX_MALLOC_SIZE, Database.POOL_STRING_LONG); + + // Write the first record. + final int length = chars.length; + db.putInt(this.record, useBytes ? -length : length); + Chunk chunk= db.getChunk(this.record); + + if (useBytes) { + chunk.putCharsAsBytes(this.record + CHARS1, chars, 0, numChars1); + } else { + chunk.putChars(this.record + CHARS1, chars, 0, numChars1); + } + + // Write the subsequent records. + long lastNext = this.record + NEXT1; + int start = numChars1; + while (length - start > numCharsn) { + long nextRecord = db.malloc(Database.MAX_MALLOC_SIZE, Database.POOL_STRING_LONG); + db.putRecPtr(lastNext, nextRecord); + chunk= db.getChunk(nextRecord); + if (useBytes) { + chunk.putCharsAsBytes(nextRecord + CHARSN, chars, start, numCharsn); + } else { + chunk.putChars(nextRecord + CHARSN, chars, start, numCharsn); + } + start += numCharsn; + lastNext = nextRecord + NEXTN; + } + + // Write the last record. + int remaining= length - start; + long nextRecord = db.malloc(CHARSN + (useBytes ? remaining : remaining * 2), Database.POOL_STRING_LONG); + db.putRecPtr(lastNext, nextRecord); + chunk= db.getChunk(nextRecord); + if (useBytes) { + chunk.putCharsAsBytes(nextRecord + CHARSN, chars, start, remaining); + } else { + chunk.putChars(nextRecord + CHARSN, chars, start, remaining); + } + } + + @Override + public long getRecord() { + return this.record; + } + + @Override + public char[] getChars() throws IndexException { + int length = this.db.getInt(this.record + LENGTH); + final boolean useBytes = length < 0; + int numChars1 = NUM_CHARS1; + int numCharsn = NUM_CHARSN; + if (useBytes) { + length= -length; + numChars1 *= 2; + numCharsn *= 2; + } + + final char[] chars = new char[length]; + + // First record + long p = this.record; + Chunk chunk= this.db.getChunk(p); + if (useBytes) { + chunk.getCharsFromBytes(p + CHARS1, chars, 0, numChars1); + } else { + chunk.getChars(p + CHARS1, chars, 0, numChars1); + } + + int start= numChars1; + p= this.record + NEXT1; + + // Other records + while (start < length) { + p = this.db.getRecPtr(p); + int partLen= Math.min(length - start, numCharsn); + chunk= this.db.getChunk(p); + if (useBytes) { + chunk.getCharsFromBytes(p + CHARSN, chars, start, partLen); + } else { + chunk.getChars(p + CHARSN, chars, start, partLen); + } + start += partLen; + p= p + NEXTN; + } + return chars; + } + + @Override + public void delete() throws IndexException { + int length = this.db.getInt(this.record + LENGTH); + final boolean useBytes = length < 0; + int numChars1 = NUM_CHARS1; + int numCharsn = NUM_CHARSN; + if (useBytes) { + length= -length; + numChars1 *= 2; + numCharsn *= 2; + } + long nextRecord = this.db.getRecPtr(this.record + NEXT1); + this.db.free(this.record, Database.POOL_STRING_LONG); + length -= numChars1; + + // Middle records. + while (length > numCharsn) { + length -= numCharsn; + long nextnext = this.db.getRecPtr(nextRecord + NEXTN); + this.db.free(nextRecord, Database.POOL_STRING_LONG); + nextRecord = nextnext; + } + + // Last record. + this.db.free(nextRecord, Database.POOL_STRING_LONG); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + try { + if (obj instanceof LongString) { + LongString lstr = (LongString)obj; + if (this.db == lstr.db && this.record == lstr.record) + return true; + return compare(lstr, true) == 0; + } + if (obj instanceof char[]) { + return compare((char[]) obj, true) == 0; + } + if (obj instanceof String) { + return compare((String) obj, true) == 0; + } + } catch (IndexException e) { + Package.log(e); + } + return false; + } + + /** + * Compatible with {@link String#hashCode()} + */ + @Override + public int hashCode() { + int h = this.hash; + if (h == 0) { + char chars[]; + chars = getChars(); + final int len = chars.length; + for (int i = 0; i < len; i++) { + h = 31 * h + chars[i]; + } + this.hash = h; + } + return h; + } + + @Override + public int compare(IString string, boolean caseSensitive) throws IndexException { + return ShortString.compare(getChars(), string.getChars(), caseSensitive); + } + + @Override + public int compare(String other, boolean caseSensitive) throws IndexException { + return ShortString.compare(getChars(), other.toCharArray(), caseSensitive); + } + + @Override + public int compare(char[] other, boolean caseSensitive) throws IndexException { + return ShortString.compare(getChars(), other, caseSensitive); + } + + @Override + public int compareCompatibleWithIgnoreCase(IString string) throws IndexException { + return ShortString.compareCompatibleWithIgnoreCase(getChars(), string.getChars()); + } + + @Override + public int comparePrefix(char[] other, boolean caseSensitive) throws IndexException { + return ShortString.comparePrefix(getChars(), other, caseSensitive); + } + + @Override + public String getString() throws IndexException { + return new String(getChars()); + } + + @Override + public int compareCompatibleWithIgnoreCase(char[] other) throws IndexException { + return ShortString.compareCompatibleWithIgnoreCase(getChars(), other); + } + + @Override + public int length() { + return this.db.getInt(this.record + LENGTH); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/MemoryStats.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/MemoryStats.java new file mode 100644 index 000000000..8de5777df --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/MemoryStats.java @@ -0,0 +1,232 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.internal.core.nd.ITypeFactory; +import org.eclipse.jdt.internal.core.nd.NdNodeTypeRegistry; + +public class MemoryStats { + public static final int TOTAL_MALLOC_POOLS = 64; + /** The size of the statistics for a single malloc pool */ + public static final int SIZE = TOTAL_MALLOC_POOLS * PoolStats.RECORD_SIZE; + + private Map<Integer, PoolStats> stats = new HashMap<>(); + + public final long address; + private Chunk db; + + public static final class PoolStats { + public static int POOL_ID_OFFSET = 0; + public static int NUM_ALLOCATIONS_OFFSET = POOL_ID_OFFSET + Database.SHORT_SIZE; + public static int TOTAL_SIZE_OFFSET = NUM_ALLOCATIONS_OFFSET + Database.LONG_SIZE; + + public static final int RECORD_SIZE = TOTAL_SIZE_OFFSET + Database.LONG_SIZE; + + short poolId; + long numAllocations; + long totalSize; + long address; + + public PoolStats(Chunk db, long address) { + this.address = address; + this.poolId = db.getShort(POOL_ID_OFFSET + address); + this.numAllocations = db.getLong(NUM_ALLOCATIONS_OFFSET + address); + this.totalSize = db.getLong(TOTAL_SIZE_OFFSET + address); + } + + public void setAllocations(Chunk db, long numAllocations) { + this.numAllocations = numAllocations; + db.putLong(this.address + NUM_ALLOCATIONS_OFFSET, numAllocations); + } + + public void setTotalSize(Chunk db, long totalSize) { + this.totalSize = totalSize; + db.putLong(this.address + TOTAL_SIZE_OFFSET, totalSize); + } + + public void setPoolId(Chunk db, short poolId) { + this.poolId = poolId; + db.putShort(this.address + POOL_ID_OFFSET, poolId); + } + + public long getNumAllocations() { + return this.numAllocations; + } + + public short getPoolId() { + return this.poolId; + } + + public long getTotalSize() { + return this.totalSize; + } + } + + public MemoryStats(Chunk db, long address) { + this.db = db; + this.address = address; + } + + public void printMemoryStats(NdNodeTypeRegistry<?> nodeRegistry) { + StringBuilder builder = new StringBuilder(); + for (PoolStats next : getSortedPools()) { + builder.append(getPoolName(nodeRegistry, next.poolId)); + builder.append(" "); //$NON-NLS-1$ + builder.append(next.numAllocations); + builder.append(" allocations, "); //$NON-NLS-1$ + builder.append(next.totalSize); + builder.append(" bytes\n"); //$NON-NLS-1$ + } + System.out.println(builder.toString()); + } + + private String getPoolName(NdNodeTypeRegistry<?> registry, int poolId) { + switch (poolId) { + case Database.POOL_MISC: return "Miscellaneous"; //$NON-NLS-1$ + case Database.POOL_BTREE: return "B-Trees"; //$NON-NLS-1$ + case Database.POOL_DB_PROPERTIES: return "DB Properties"; //$NON-NLS-1$ + case Database.POOL_STRING_LONG: return "Long Strings"; //$NON-NLS-1$ + case Database.POOL_STRING_SHORT: return "Short Strings"; //$NON-NLS-1$ + case Database.POOL_LINKED_LIST: return "Linked Lists"; //$NON-NLS-1$ + case Database.POOL_STRING_SET: return "String Sets"; //$NON-NLS-1$ + case Database.POOL_GROWABLE_ARRAY: return "Growable Arrays"; //$NON-NLS-1$ + default: + if (poolId >= Database.POOL_FIRST_NODE_TYPE) { + ITypeFactory<?> type = registry.getClassForType((short)(poolId - Database.POOL_FIRST_NODE_TYPE)); + + if (type != null) { + return type.getElementClass().getSimpleName(); + } + } + return "Unknown memory pool " + poolId; //$NON-NLS-1$ + } + } + + public Collection<PoolStats> getPools() { + return this.stats.values(); + } + + public List<PoolStats> getSortedPools() { + List<PoolStats> unsorted = new ArrayList<>(); + unsorted.addAll(getPools()); + Collections.sort(unsorted, new Comparator<PoolStats>() { + @Override + public int compare(PoolStats o1, PoolStats o2) { + return Long.signum(o2.totalSize - o1.totalSize); + } + }); + return unsorted; + } + + public void recordMalloc(short poolId, long size) { + PoolStats toRecord = getPoolStats(poolId); + toRecord.setAllocations(this.db, toRecord.numAllocations + 1); + toRecord.setTotalSize(this.db, toRecord.totalSize + size); + } + + private PoolStats getPoolStats(short poolId) { + if (this.stats.isEmpty()) { + refresh(); + } + PoolStats result = this.stats.get((int)poolId); + if (result == null) { + if (this.stats.size() >= TOTAL_MALLOC_POOLS) { + throw new IndexException("Too many malloc pools. Please increase the size of TOTAL_MALLOC_POOLS."); //$NON-NLS-1$ + } + // Find the insertion position + int idx = 0; + for (;;idx++) { + PoolStats nextPool = readPool(idx); + if (idx > 0 && nextPool.poolId == 0) { + break; + } + if (nextPool.poolId == poolId) { + throw new IllegalStateException("The stats were out of sync with the database."); //$NON-NLS-1$ + } + if (nextPool.poolId > poolId) { + break; + } + } + + // Find the last pool position + int lastIdx = idx; + for (;;lastIdx++) { + PoolStats nextPool = readPool(lastIdx); + if (lastIdx > 0 && nextPool.poolId == 0) { + break; + } + } + + // Shift all the pools to make room + for (int shiftIdx = lastIdx; shiftIdx > idx; shiftIdx--) { + PoolStats writeTo = readPool(shiftIdx); + PoolStats readFrom = readPool(shiftIdx - 1); + + writeTo.setAllocations(this.db, readFrom.numAllocations); + writeTo.setTotalSize(this.db, readFrom.totalSize); + writeTo.setPoolId(this.db, readFrom.poolId); + } + + result = readPool(idx); + result.setAllocations(this.db, 0); + result.setTotalSize(this.db, 0); + result.setPoolId(this.db, poolId); + + refresh(); + + result = this.stats.get((int)poolId); + } + return result; + } + + private List<PoolStats> loadStats() { + List<PoolStats> result = new ArrayList<>(); + for (int idx = 0; idx < TOTAL_MALLOC_POOLS; idx++) { + PoolStats next = readPool(idx); + + if (idx > 0 && next.poolId == 0) { + break; + } + + result.add(next); + } + return result; + } + + public void refresh() { + this.stats.clear(); + + for (PoolStats next : loadStats()) { + this.stats.put((int)next.poolId, next); + } + } + + public PoolStats readPool(int idx) { + return new PoolStats(this.db, this.address + idx * PoolStats.RECORD_SIZE); + } + + public void recordFree(short poolId, long size) { + PoolStats toRecord = getPoolStats(poolId); + if (toRecord.numAllocations <= 0 || toRecord.totalSize < size) { + throw new IndexException("Attempted to free more memory from pool " + poolId + " than was ever allocated"); //$NON-NLS-1$//$NON-NLS-2$ + } + toRecord.setAllocations(this.db, toRecord.numAllocations - 1); + toRecord.setTotalSize(this.db, toRecord.totalSize - size); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/NdStringSet.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/NdStringSet.java new file mode 100644 index 000000000..37bb613dd --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/NdStringSet.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2013, 2016 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Andrew Eidsness - Initial implementation + */ + +package org.eclipse.jdt.internal.core.nd.db; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; + +/** + * A container for storing a set of strings in the Database. The container allows only one instance of each + * string to be stored. + * <p> + * This implementation should only be used when the set is expected to be small. It uses a singly linked list + * for storing strings in Database. Which means that a linear lookup is needed to find strings in the list. An + * in-memory, lazily-loaded, cache is provided so the list will only be fully retrieved once in the lifetime + * of this instance. A BTree will be more efficient for larger sets. + */ +public class NdStringSet { + private final Database db; + + private long ptr; + private long head; + private long loaded; + + // A lazily initialized, in-memory cache that maps a persisted string to its storage record. + private Map<String, Long> lazyCache; + + public NdStringSet(Database db, long ptr) throws CoreException { + this.db = db; + this.ptr = ptr; + + this.head = 0; + this.loaded = 0; + } + + public void clearCaches() { + this.head = 0; + this.loaded = 0; + + if (this.lazyCache != null) + this.lazyCache = null; + } + + private long getHead() throws CoreException { + if (this.head == 0) + this.head = this.db.getRecPtr(this.ptr); + return this.head; + } + + // A simple enum describing the type of the information that is stored in the Database. Each + // enumerator represents a single field in the persistent structure and is able to answer its + // offset in that structure. + private static enum NodeType { + Next, Item, _last; + + // NOTE: All fields are pointers, if that changes then these initializations will need + // to be updated. + public final long offset = ordinal() * Database.PTR_SIZE; + public static final int sizeof = (int) _last.offset; + + /** Return the value of the pointer stored in this field in the given instance. */ + public long get(Database db, long instance) throws CoreException { + return db.getRecPtr(instance + this.offset); + } + + /** Store the given pointer into this field in the given instance. */ + public void put(Database db, long instance, long value) throws CoreException { + db.putRecPtr(instance + this.offset, value); + } + } + + /** + * Adds the given string to the receiving set. May cause the entire list to be loaded from the Database + * while testing for uniqueness. Returns the record of the string that was inserted into the list. + */ + public long add(String str) throws CoreException { + long record = find(str); + if (record != 0) + return record; + + IString string = this.db.newString(str); + record = string.getRecord(); + + long new_node = this.db.malloc(NodeType.sizeof, Database.POOL_STRING_SET); + NodeType.Next.put(this.db, new_node, getHead()); + NodeType.Item.put(this.db, new_node, record); + + if (this.lazyCache == null) + this.lazyCache = new HashMap<String, Long>(); + this.lazyCache.put(str, record); + + // If the Database has already been partially searched, then the loaded pointer will be after the + // head. Since we've already put this new record into the lazy cache, there is no reason to try to + // load it again. We put the new node at the start of the list so that it will be before the loaded + // pointer. + this.head = new_node; + if (this.loaded == 0) + this.loaded = new_node; + this.db.putRecPtr(this.ptr, new_node); + return record; + } + + /** + * Search for the given string in the receiver. This could cause the entire list to be loaded from the + * Database. The results are cached, so the list will only be loaded one time during the lifetime of this + * instance. Returns the record of the String. + */ + public long find(String str) throws CoreException { + if (this.lazyCache != null) { + Long l = this.lazyCache.get(str); + if (l != null) + return l.longValue(); + } + + // if there is nothing in the Database, then there is nothing to load + if (getHead() == 0) + return 0; + + // otherwise prepare the cache for the data that is about to be loaded + if (this.lazyCache == null) + this.lazyCache = new HashMap<String, Long>(); + + // if nothing has been loaded, then start loading with the head node, otherwise continue + // loading from whatever is after the last loaded node + long curr = this.loaded == 0 ? getHead() : NodeType.Next.get(this.db, this.loaded); + while (curr != 0) { + long next = NodeType.Next.get(this.db, curr); + long item = NodeType.Item.get(this.db, curr); + + IString string = this.db.getString(item); + + // put the value into the cache + this.lazyCache.put(string.getString(), Long.valueOf(item)); + + // return immediately if this is the target + if (string.compare(str, true) == 0) + return item; + + // otherwise keep looking + this.loaded = curr; + curr = next; + } + + return 0; + } + + /** + * Return a pointer to the record of the String that was removed. + */ + public long remove(String str) throws CoreException { + if (this.lazyCache != null) + this.lazyCache.remove(str); + + long prev = 0; + long curr = getHead(); + while (curr != 0) { + long next = NodeType.Next.get(this.db, curr); + long item = NodeType.Item.get(this.db, curr); + + IString string = this.db.getString(item); + + if (string.compare(str, true) == 0) { + if (this.head != curr) + NodeType.Next.put(this.db, prev, next); + else { + this.db.putRecPtr(this.ptr, next); + this.head = next; + } + + this.db.free(curr, Database.POOL_STRING_SET); + return item; + } + + prev = curr; + curr = next; + } + + return 0; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/Package.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/Package.java new file mode 100644 index 000000000..b68df3cb2 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/Package.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.JavaCore; + +/** + * This class is not intended to be referenced by clients + */ +/* package */ class Package { + public static String PLUGIN_ID = JavaCore.PLUGIN_ID; + + /** + * Status code for core exception that is thrown if a database grew larger than the supported limit. + */ + public static final int STATUS_DATABASE_TOO_LARGE = 4; + + public static void log(Throwable e) { + String msg= e.getMessage(); + if (msg == null) { + log("Error", e); //$NON-NLS-1$ + } else { + log("Error: " + msg, e); //$NON-NLS-1$ + } + } + + public static void log(String message, Throwable e) { + log(createStatus(message, e)); + } + + public static IStatus createStatus(String msg, Throwable e) { + return new Status(IStatus.ERROR, PLUGIN_ID, msg, e); + } + + public static void log(IStatus status) { + JavaCore.getPlugin().getLog().log(status); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/ShortString.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/ShortString.java new file mode 100644 index 000000000..09992a564 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/ShortString.java @@ -0,0 +1,296 @@ +/******************************************************************************* + * Copyright (c) 2006, 2016 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX - Initial API and implementation + * Andrew Ferguson (Symbian) + * Markus Schorn (Wind River Systems) + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.db; + +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; + +/** + * This is for strings that fit inside a single chunk. + */ +public class ShortString implements IString { + private final Database db; + private final long record; + private int hash; + + private static final int LENGTH = 0; + private static final int CHARS = 4; + + public static final int MAX_BYTE_LENGTH = Database.MAX_MALLOC_SIZE - CHARS; + + public ShortString(Database db, long offset) { + this.db = db; + this.record = offset; + } + + public ShortString(Database db, char[] chars, boolean useBytes) throws IndexException { + final int n = chars.length; + this.db = db; + + this.record = db.malloc(CHARS + (useBytes ? n : 2 * n), Database.POOL_STRING_SHORT); + Chunk chunk = db.getChunk(this.record); + chunk.putInt(this.record + LENGTH, useBytes ? -n : n); + long p = this.record + CHARS; + if (useBytes) { + chunk.putCharsAsBytes(p, chars, 0, n); + } else { + chunk.putChars(p, chars, 0, n); + } + } + + @Override + public long getRecord() { + return this.record; + } + + @Override + public void delete() throws IndexException { + this.db.free(this.record, Database.POOL_STRING_SHORT); + } + + @Override + public char[] getChars() throws IndexException { + final Chunk chunk = this.db.getChunk(this.record); + final int l = chunk.getInt(this.record + LENGTH); + final int length = Math.abs(l); + final char[] chars = new char[length]; + if (l < 0) { + chunk.getCharsFromBytes(this.record + CHARS, chars, 0, length); + } else { + chunk.getChars(this.record + CHARS, chars, 0, length); + } + return chars; + } + + @Override + public String getString() throws IndexException { + return new String(getChars()); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + + try { + if (obj instanceof ShortString) { + ShortString string = (ShortString)obj; + if (this.db == string.db && this.record == string.record) + return true; + + Chunk chunk1 = this.db.getChunk(this.record); + Chunk chunk2 = string.db.getChunk(string.record); + + int n1 = chunk1.getInt(this.record); + int n2 = chunk2.getInt(string.record); + if (n1 != n2) + return false; + + return CharArrayUtils.equals(getChars(), string.getChars()); + } + if (obj instanceof char[]) { + char[] chars = (char[])obj; + + // Make sure size is the same + if (length() != chars.length) + return false; + + return CharArrayUtils.equals(getChars(), chars); + } else if (obj instanceof String) { + String string = (String)obj; + if (length() != string.length()) + return false; + + return CharArrayUtils.equals(getChars(), string.toCharArray()); + } + } catch (IndexException e) { + Package.log(e); + } + return false; + } + + /** + * Compatible with {@link String#hashCode()} + */ + @Override + public int hashCode() { + int h = this.hash; + if (h == 0) { + char chars[]; + chars = getChars(); + final int len = chars.length; + for (int i = 0; i < len; i++) { + h = 31 * h + chars[i]; + } + this.hash = h; + } + return h; + } + + public static int compare(final char[] chars, char[] other, boolean caseSensitive) { + final int n = Math.min(chars.length, other.length); + for (int i = 0; i < n; i++) { + int cmp= compareChars(chars[i], other[i], caseSensitive); + if (cmp != 0) + return cmp; + } + return chars.length - other.length; + } + + @Override + public int compare(char[] other, boolean caseSensitive) throws IndexException { + return compare(getChars(), other, caseSensitive); + } + + @Override + public int compare(IString string, boolean caseSensitive) throws IndexException { + return compare(getChars(), string.getChars(), caseSensitive); + } + + @Override + public int compare(String other, boolean caseSensitive) throws IndexException { + return compare(getChars(), other.toCharArray(), caseSensitive); + } + + @Override + public int compareCompatibleWithIgnoreCase(IString string) throws IndexException { + return compareCompatibleWithIgnoreCase(string.getChars()); + } + + @Override + public int compareCompatibleWithIgnoreCase(char[] other) throws IndexException { + return compareCompatibleWithIgnoreCase(getChars(), other); + } + + public static int compareCompatibleWithIgnoreCase(final char[] chars, char[] other) { + final int n = Math.min(chars.length, other.length); + int sensitiveCmp= 0; + + for (int i = 0; i < n; i++) { + final char c1= chars[i]; + final char c2= other[i]; + if (c1 != c2) { + int cmp= compareChars(c1, c2, false); // insensitive + if (cmp != 0) + return cmp; + + if (sensitiveCmp == 0) { + if (c1 < c2) { + sensitiveCmp= -1; + } else { + sensitiveCmp= 1; + } + } + } + } + int cmp= chars.length - other.length; + if (cmp != 0) + return cmp; + + return sensitiveCmp; + } + + @Override + public int comparePrefix(char[] other, boolean caseSensitive) throws IndexException { + return comparePrefix(getChars(), other, caseSensitive); + } + + public static int comparePrefix(final char[] chars, char[] other, boolean caseSensitive) { + final int n = Math.min(chars.length, other.length); + + for (int i = 0; i < n; i++) { + int cmp= compareChars(chars[i], other[i], caseSensitive); + if (cmp != 0) + return cmp; + } + if (chars.length < other.length) + return -1; + + return 0; + } + + /** + * Compare characters case-sensitively, or case-insensitively. + * + * <b>Limitation</b> This only maps the range a-z,A-Z onto each other + * @param a a character + * @param b a character + * @param caseSensitive whether to compare case-sensitively + * @return + * <ul> + * <li>-1 if a < b + * <li>0 if a == b + * <li>1 if a > b + * </ul> + */ + public static int compareChars(char a, char b, boolean caseSensitive) { + if (caseSensitive) { + if (a < b) + return -1; + if (a > b) + return 1; + } else { + if (a != b) { + a= a >= 'a' && a <='z' ? (char) (a - 32) : a; + b= b >= 'a' && b <='z' ? (char) (b - 32) : b; + if (a < b) + return -1; + if (a > b) + return 1; + } + } + return 0; + } + +/* TODO - this is more correct than the above implementation, but we need to + * benchmark first. + * + * public static int compareChars(char a, char b, boolean caseSensitive) { + if (caseSensitive) { + if (a < b) + return -1; + if (a > b) + return 1; + } else { + if (a != b) { + a = Character.toUpperCase(a); + b = Character.toUpperCase(b); + if (a != b) { + a = Character.toLowerCase(a); + b = Character.toLowerCase(b); + if (a != b) { + if (a < b) + return -1; + if (a > b) + return 1; + } + } + } + } + return 0; + } +*/ + + @Override + public String toString() { + try { + return getString(); + } catch (IndexException e) { + return super.toString(); + } + } + + @Override + public int length() { + return Math.abs(this.db.getInt(this.record + LENGTH)); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/Field.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/Field.java new file mode 100644 index 000000000..bab45d4dd --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/Field.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.ITypeFactory; +import org.eclipse.jdt.internal.core.nd.Nd; + +/** + * Used to represent a single field of an object stored in the database. Objects + * which store themselves in the database should store a set of static final + * FieldDefinitions at the top of their class definition to indicate their memory map. + * This serves as a standard way to document the memory map for such structs, provides + * access to the field offsets, and provides a convenience getter. + * <p> + * There are two ways to use this. Callers can either use the "get" method to access + * the value of the field, or can use the public "offset" attribute to perform the reads + * manually. The get function is more convenient but allocates objects and so should + * probably not be used for frequently-accessed fields or primitive types that would + * end up being autoboxed unnecessarily. + * + * @param <T> + */ +public final class Field<T> implements IField, IDestructableField { + private int offset; + public final ITypeFactory<T> factory; + + public Field(ITypeFactory<T> objectFactory) { + this.factory = objectFactory; + } + + public T get(Nd nd, long address) { + return this.factory.create(nd, address + this.offset); + } + + public boolean hasDestructor() { + return this.factory.hasDestructor(); + } + + @Override + public void destruct(Nd nd, long address) { + this.factory.destruct(nd, address + this.offset); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return this.factory.getRecordSize(); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldByte.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldByte.java new file mode 100644 index 000000000..c7a9ef5e0 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldByte.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.Database; + +/** + * Declares a Nd field of type byte. Can be used in place of {@link Field}<{@link Byte}> in order to + * avoid extra GC overhead. + */ +public class FieldByte implements IField { + private int offset; + + public FieldByte() { + } + + public byte get(Nd nd, long address) { + Database db = nd.getDB(); + return db.getByte(address + this.offset); + } + + public void put(Nd nd, long address, byte newValue) { + nd.getDB().putByte(address + this.offset, newValue); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return Database.BYTE_SIZE; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldChar.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldChar.java new file mode 100644 index 000000000..e4b0e178a --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldChar.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.Database; + +/** + * Declares a Nd field of type char. Can be used in place of {@link Field}<{@link Character}> in order to + * avoid extra GC overhead. + */ +public class FieldChar implements IField { + private int offset; + + public FieldChar() { + } + + public char get(Nd nd, long address) { + Database db = nd.getDB(); + return db.getChar(address + this.offset); + } + + public void put(Nd nd, long address, char newValue) { + nd.getDB().putChar(address + this.offset, newValue); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return Database.CHAR_SIZE; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldDouble.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldDouble.java new file mode 100644 index 000000000..f0932e209 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldDouble.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.Database; + +/** + * Declares a Nd field of type double. Can be used in place of {@link Field}<{@link Double}> in order to + * avoid extra GC overhead. + */ +public class FieldDouble implements IField { + private int offset; + + public FieldDouble() { + } + + public double get(Nd nd, long address) { + Database db = nd.getDB(); + return db.getDouble(address + this.offset); + } + + public void put(Nd nd, long address, double newValue) { + nd.getDB().putDouble(address + this.offset, newValue); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return Database.DOUBLE_SIZE; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldFloat.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldFloat.java new file mode 100644 index 000000000..4ddd09311 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldFloat.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.Database; + +/** + * Declares a Nd field of type float. Can be used in place of {@link Field}<{@link Float}> in order to + * avoid extra GC overhead. + */ +public class FieldFloat implements IField { + private int offset; + + public FieldFloat() { + } + + public float get(Nd nd, long address) { + Database db = nd.getDB(); + return db.getFloat(address + this.offset); + } + + public void put(Nd nd, long address, float newValue) { + nd.getDB().putFloat(address + this.offset, newValue); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return Database.FLOAT_SIZE; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldInt.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldInt.java new file mode 100644 index 000000000..06e9b8a71 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldInt.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.Database; + +/** + * Declares a Nd field of type int. Can be used in place of {@link Field}<{@link Integer}> in order to + * avoid extra GC overhead. + */ +public class FieldInt implements IField { + private int offset; + + public FieldInt() { + } + + public int get(Nd nd, long address) { + Database db = nd.getDB(); + return db.getInt(address + this.offset); + } + + public void put(Nd nd, long address, int newValue) { + nd.getDB().putInt(address + this.offset, newValue); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return Database.INT_SIZE; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldLong.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldLong.java new file mode 100644 index 000000000..6a66ac26f --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldLong.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.Database; + +/** + * Declares a Nd field of type long. Can be used in place of {@link Field}<{@link Long}> in order to + * avoid extra GC overhead. + */ +public class FieldLong implements IField { + private int offset; + + public FieldLong() { + } + + public long get(Nd nd, long address) { + Database db = nd.getDB(); + return db.getLong(address + this.offset); + } + + public void put(Nd nd, long address, long newValue) { + nd.getDB().putLong(address + this.offset, newValue); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return Database.LONG_SIZE; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldManyToOne.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldManyToOne.java new file mode 100644 index 000000000..ed644953d --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldManyToOne.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.ITypeFactory; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; + +/** + * Holds the n side of a n..1 relationship. Declares a Nd field which is a pointer of a NdNode of the specified + * type. {@link FieldManyToOne} forms a one-to-many relationship with {@link FieldOneToMany}. Whenever a + * {@link FieldManyToOne} points to an object, the inverse pointer is automatically inserted into the matching back + * pointer list. + */ +public class FieldManyToOne<T extends NdNode> implements IDestructableField, IField, IRefCountedField { + public final static FieldPointer TARGET; + public final static FieldInt BACKPOINTER_INDEX; + + private int offset; + Class<T> targetType; + final Class<? extends NdNode> localType; + FieldOneToMany<?> backPointer; + @SuppressWarnings("rawtypes") + private final static StructDef<FieldManyToOne> type; + /** + * True iff the other end of this pointer should delete this object when its end of the pointer is cleared. + */ + public final boolean pointsToOwner; + + static { + type = StructDef.createAbstract(FieldManyToOne.class); + TARGET = type.addPointer(); + BACKPOINTER_INDEX = type.addInt(); + type.done(); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private FieldManyToOne(Class<? extends NdNode> localType, FieldOneToMany<?> backPointer, boolean pointsToOwner) { + this.localType = localType; + this.pointsToOwner = pointsToOwner; + + if (backPointer != null) { + if (backPointer.forwardPointer != null && backPointer.forwardPointer != this) { + throw new IllegalArgumentException( + "Attempted to construct a FieldNodePointer referring to a backpointer list that is already in use" //$NON-NLS-1$ + + " by another field"); //$NON-NLS-1$ + } + backPointer.targetType = (Class) localType; + this.targetType = (Class) backPointer.localType; + backPointer.forwardPointer = this; + } + this.backPointer = backPointer; + } + + public static <T extends NdNode, B extends NdNode> FieldManyToOne<T> create(StructDef<B> builder, + FieldOneToMany<B> forwardPointer) { + FieldManyToOne<T> result = new FieldManyToOne<T>(builder.getStructClass(), forwardPointer, false); + builder.add(result); + builder.addDestructableField(result); + return result; + } + + /** + * Creates a many-to-one pointer which points to this object's owner. If the pointer is non-null when the owner is + * deleted, this object will be deleted too. + * + * @param builder the struct to which the field will be added + * @param forwardPointer the field which holds the pointer in the other direction + * @return a newly constructed field + */ + public static <T extends NdNode, B extends NdNode> FieldManyToOne<T> createOwner(StructDef<B> builder, + FieldOneToMany<B> forwardPointer) { + + FieldManyToOne<T> result = new FieldManyToOne<T>(builder.getStructClass(), forwardPointer, true); + builder.add(result); + builder.addDestructableField(result); + builder.addOwnerField(result); + return result; + } + + public T get(Nd nd, long address) { + return NdNode.load(nd, getAddress(nd, address), this.targetType); + } + + public long getAddress(Nd nd, long address) { + return nd.getDB().getRecPtr(address + this.offset); + } + + /** + * Directs this pointer to the given target. Also removes this pointer from the old backpointer list (if any) and + * inserts it into the new backpointer list (if any) + */ + public void put(Nd nd, long address, T value) { + if (value != null) { + put(nd, address, value.address); + } else { + put(nd, address, 0); + } + } + + public void put(Nd nd, long address, long newTargetAddress) { + long fieldStart = address + this.offset; + if (this.backPointer == null) { + throw new IllegalStateException("FieldNodePointer must be associated with a FieldBackPointer"); //$NON-NLS-1$ + } + + long oldTargetAddress = TARGET.get(nd, fieldStart); + if (oldTargetAddress == newTargetAddress) { + return; + } + + detachFromOldTarget(nd, address, oldTargetAddress); + + TARGET.put(nd, fieldStart, newTargetAddress); + if (newTargetAddress != 0) { + // Note that newValue is the address of the backpointer list and record (the address of the struct + // containing the forward pointer) is the value being inserted into the list. + BACKPOINTER_INDEX.put(nd, fieldStart, this.backPointer.add(nd, newTargetAddress, address)); + } else { + if (this.pointsToOwner) { + nd.scheduleDeletion(address); + } + } + } + + protected void detachFromOldTarget(Nd nd, long address, long oldTargetAddress) { + long fieldStart = address + this.offset; + if (oldTargetAddress != 0) { + int oldIndex = BACKPOINTER_INDEX.get(nd, fieldStart); + + this.backPointer.remove(nd, oldTargetAddress, oldIndex); + + short targetTypeId = NdNode.NODE_TYPE.get(nd, oldTargetAddress); + + ITypeFactory<T> typeFactory = nd.getTypeFactory(targetTypeId); + + if (typeFactory.getDeletionSemantics() == StructDef.DeletionSemantics.REFCOUNTED + && typeFactory.isReadyForDeletion(nd, oldTargetAddress)) { + nd.scheduleDeletion(oldTargetAddress); + } + } + } + + /** + * Called when the index of this forward pointer has moved in the backpointer list. Adjusts the index. + * <p> + * Not intended to be called by clients. This is invoked by {@link FieldOneToMany} whenever it reorders elements in + * the array. + */ + void adjustIndex(Nd nd, long address, int index) { + BACKPOINTER_INDEX.put(nd, address + this.offset, index); + } + + @Override + public void destruct(Nd nd, long address) { + long fieldStart = address + this.offset; + long oldTargetAddress = TARGET.get(nd, fieldStart); + detachFromOldTarget(nd, address, oldTargetAddress); + TARGET.put(nd, fieldStart, 0); + } + + void clearedByBackPointer(Nd nd, long address) { + long fieldStart = this.offset + address; + FieldManyToOne.TARGET.put(nd, fieldStart, 0); + FieldManyToOne.BACKPOINTER_INDEX.put(nd, fieldStart, 0); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return type.size(); + } + + @Override + public boolean hasReferences(Nd nd, long address) { + long fieldStart = this.offset + address; + long target = TARGET.get(nd, fieldStart); + return target != 0; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldOneToMany.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldOneToMany.java new file mode 100644 index 000000000..8f95c681f --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldOneToMany.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.RawGrowableArray; + +/** + * Holds the 1 side of a 1..n relationship between two objects. FieldNodePointer and FieldBackPointer fields always go + * together in pairs. + */ +public class FieldOneToMany<T extends NdNode> implements IDestructableField, IRefCountedField, IField { + private int offset; + public Class<T> targetType; + public final Class<? extends NdNode> localType; + private final RawGrowableArray backPointerArray; + FieldManyToOne<?> forwardPointer; + + public interface Visitor<T> { + public void visit(int index, T toVisit); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private FieldOneToMany(Class<? extends NdNode> localType, FieldManyToOne<? extends NdNode> forwardPointer, + int inlineElements) { + this.localType = localType; + + if (forwardPointer != null) { + if (forwardPointer.backPointer != null && forwardPointer.backPointer != this) { + throw new IllegalArgumentException( + "Attempted to construct a FieldBackPointer referring to a forward pointer that is already in use" //$NON-NLS-1$ + + " by another field"); //$NON-NLS-1$ + } + forwardPointer.targetType = (Class)localType; + this.targetType = (Class)forwardPointer.localType; + forwardPointer.backPointer = this; + } + this.forwardPointer = forwardPointer; + this.backPointerArray = new RawGrowableArray(inlineElements); + } + + /** + * Creates a {@link FieldOneToMany} using the given builder. It will hold the many side of a one-to-many + * relationship with nodeType. + * + * @param builder builder that is being used to construct the struct containing this field + * @param forwardPointer field of the model object which holds the one side of this one-to-many relationship + * @param inlineElementCount number of inline elements. If this is nonzero, space for this number elements is + * preallocated and reserved in the header. The first few elements inserted will be stored here. For relationships + * which will usually have more than a certain number of participants, using a small number of inline elements will + * offer a performance improvement. For relationships that will normally be empty, this should be 0. + * @return the newly constructed backpointer field + */ + public static <T extends NdNode, B extends NdNode> FieldOneToMany<T> create(StructDef<B> builder, + FieldManyToOne<B> forwardPointer, int inlineElementCount) { + FieldOneToMany<T> result = new FieldOneToMany<T>(builder.getStructClass(), forwardPointer, + inlineElementCount); + builder.add(result); + builder.addDestructableField(result); + builder.addRefCountedField(result); + return result; + } + + public static <T extends NdNode, B extends NdNode> FieldOneToMany<T> create(StructDef<B> builder, + FieldManyToOne<B> forwardPointer) { + return create(builder, forwardPointer, 0); + } + + public void accept(Nd nd, long address, Visitor<T> visitor) { + int size = size(nd, address); + + for (int idx = 0; idx < size; idx++) { + visitor.visit(idx, get(nd, address, idx)); + } + } + + public List<T> asList(Nd nd, long address) { + final List<T> result = new ArrayList<>(size(nd, address)); + + accept(nd, address, new Visitor<T>() { + @Override + public void visit(int index, T toVisit) { + result.add(toVisit); + } + }); + + return result; + } + + public boolean isEmpty(Nd nd, long address) { + return this.backPointerArray.isEmpty(nd, address + this.offset); + } + + public int size(Nd nd, long address) { + return this.backPointerArray.size(nd, address + this.offset); + } + + public T get(Nd nd, long address, int index) { + long nextPointer = this.backPointerArray.get(nd, address + this.offset, index); + + return NdNode.load(nd, nextPointer, this.targetType); + } + + /** + * Removes the given index from the list. If another element is swapped into the removed element's + * location, that element's index will be updated. The removed element itself will not be modified. The + * caller is responsible for nulling out the pointer and updating its index if necessary. + * <p> + * Not intended to be called by clients. The normal way to remove something from a backpointer list is + * by calling {@link FieldManyToOne#put}, which performs the appropriate removals automatically. + */ + void remove(Nd nd, long address, int index) { + long swappedElement = this.backPointerArray.remove(nd, address + this.offset, index); + + if (swappedElement != 0) { + this.forwardPointer.adjustIndex(nd, swappedElement, index); + } + } + + /** + * Addss the given forward pointer to the list and returns the insertion index. This should not be invoked + * directly by clients. The normal way to insert into a backpointer list is to assign a forward pointer. + */ + int add(Nd nd, long address, long value) { + return this.backPointerArray.add(nd, address + this.offset, value); + } + + /** + * Returns the record size of the back pointer list + */ + public int getRecordSize() { + return this.backPointerArray.getRecordSize(); + } + + public void ensureCapacity(Nd nd, long address, int capacity) { + long arrayAddress = address + this.offset; + this.backPointerArray.ensureCapacity(nd, arrayAddress, capacity); + } + + @Override + public void destruct(Nd nd, long address) { + long arrayAddress = address + this.offset; + int size = size(nd, address); + + boolean isOwner = this.forwardPointer.pointsToOwner; + for (int idx = 0; idx < size; idx++) { + long target = this.backPointerArray.get(nd, arrayAddress, idx); + + this.forwardPointer.clearedByBackPointer(nd, target); + + if (isOwner) { + nd.scheduleDeletion(target); + } + } + + this.backPointerArray.destruct(nd, arrayAddress); + } + + public int getCapacity(Nd nd, long address) { + return this.backPointerArray.getCapacity(nd, address + this.offset); + } + + @Override + public boolean hasReferences(Nd nd, long address) { + // If this field owns the objects it points to, don't treat the incoming pointers as ref counts + if (this.forwardPointer.pointsToOwner) { + return false; + } + return !isEmpty(nd, address); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldOneToOne.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldOneToOne.java new file mode 100644 index 000000000..c1dd22883 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldOneToOne.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.db.Database; + +/** + * Represents a 1-to-0..1 relationship in a Nd database. + */ +public class FieldOneToOne<T extends NdNode> implements IField, IDestructableField, IRefCountedField { + private int offset; + public final Class<T> nodeType; + FieldOneToOne<?> backPointer; + private boolean pointsToOwner; + + /** + * @param nodeType + * @param backPointer + */ + private FieldOneToOne(Class<T> nodeType, FieldOneToOne<?> backPointer, boolean pointsToOwner) { + this.nodeType = nodeType; + + if (backPointer != null) { + if (backPointer.backPointer != null && backPointer.backPointer != this) { + throw new IllegalArgumentException( + "Attempted to construct a FieldOneToOne referring to a backpointer list that is already in use" //$NON-NLS-1$ + + " by another field"); //$NON-NLS-1$ + } + backPointer.backPointer = this; + } + this.backPointer = backPointer; + this.pointsToOwner = pointsToOwner; + } + + public static <T extends NdNode, B extends NdNode> FieldOneToOne<T> create(StructDef<B> builder, + Class<T> nodeType, FieldOneToOne<B> forwardPointer) { + + FieldOneToOne<T> result = new FieldOneToOne<T>(nodeType, forwardPointer, false); + builder.add(result); + builder.addDestructableField(result); + return result; + } + + public static <T extends NdNode, B extends NdNode> FieldOneToOne<T> createOwner(StructDef<B> builder, + Class<T> nodeType, FieldOneToOne<B> forwardPointer) { + + FieldOneToOne<T> result = new FieldOneToOne<T>(nodeType, forwardPointer, true); + builder.add(result); + builder.addDestructableField(result); + builder.addOwnerField(result); + return result; + } + + public T get(Nd nd, long address) { + long ptr = nd.getDB().getRecPtr(address + this.offset); + return NdNode.load(nd, ptr, this.nodeType); + } + + public void put(Nd nd, long address, T target) { + cleanup(nd, address); + nd.getDB().putRecPtr(address + this.offset, target == null ? 0 : target.address); + if (target == null && this.pointsToOwner) { + nd.scheduleDeletion(address); + } + } + + @Override + public void destruct(Nd nd, long address) { + cleanup(nd, address); + } + + private void cleanup(Nd nd, long address) { + Database db = nd.getDB(); + long ptr = db.getRecPtr(address + this.offset); + if (ptr != 0) { + db.putRecPtr(ptr + this.backPointer.offset, 0); + // If we own our target, delete it + if (this.backPointer.pointsToOwner) { + nd.scheduleDeletion(ptr); + } + } + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return Database.PTR_SIZE; + } + + @Override + public boolean hasReferences(Nd nd, long address) { + if (this.pointsToOwner) { + long ptr = nd.getDB().getRecPtr(address + this.offset); + return ptr != 0; + } + return false; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldPointer.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldPointer.java new file mode 100644 index 000000000..fef317623 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldPointer.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.Database; + +public class FieldPointer implements IField { + private int offset; + + public FieldPointer() { + } + + public long get(Nd nd, long address) { + Database db = nd.getDB(); + return db.getRecPtr(address + this.offset); + } + + public void put(Nd nd, long address, long newValue) { + nd.getDB().putRecPtr(address + this.offset, newValue); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return Database.PTR_SIZE; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldSearchIndex.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldSearchIndex.java new file mode 100644 index 000000000..f265fcfcd --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldSearchIndex.java @@ -0,0 +1,298 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.internal.core.nd.ITypeFactory; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.db.BTree; +import org.eclipse.jdt.internal.core.nd.db.IBTreeComparator; +import org.eclipse.jdt.internal.core.nd.db.IBTreeVisitor; +import org.eclipse.jdt.internal.core.nd.db.IString; +import org.eclipse.jdt.internal.core.nd.db.IndexException; + +/** + * Declares a field representing a case-insensitive search tree over elements which are a subtype of NdNode. + */ +public class FieldSearchIndex<T extends NdNode> implements IField, IDestructableField { + private int offset; + private final ITypeFactory<BTree> btreeFactory; + FieldSearchKey<?> searchKey; + private static IResultRank anything = new IResultRank() { + @Override + public long getRank(Nd nd, long address) { + return 1; + } + }; + + public static final class SearchCriteria { + private boolean matchCase = true; + private boolean isPrefix = false; + private char[] searchString; + private short requiredNodeType = -1; + private boolean matchingParentNodeAddress = false; + + private SearchCriteria(char[] searchString) { + this.searchString = searchString; + } + + public static SearchCriteria create(String searchString) { + return create(searchString.toCharArray()); + } + + public static SearchCriteria create(char[] searchString) { + return new SearchCriteria(searchString); + } + + public SearchCriteria requireNodeType(short type) { + this.requiredNodeType = type; + return this; + } + + public SearchCriteria allowAnyNodeType() { + this.requiredNodeType = -1; + return this; + } + + public SearchCriteria matchCase(boolean match) { + this.matchCase = match; + return this; + } + + public SearchCriteria prefix(boolean isPrefixSearch) { + this.isPrefix = isPrefixSearch; + return this; + } +// +// public SearchCriteria requireParentNode(long parentNameAddress) { +// this.requiredParentNodeAddress = parentNameAddress; +// return this; +// } + + public boolean isMatchingParentNodeAddress() { + return this.matchingParentNodeAddress; + } + + public boolean isMatchingCase() { + return this.matchCase; + } + + public boolean isPrefixSearch() { + return this.isPrefix; + } + + public char[] getSearchString() { + return this.searchString; + } +// +// public long getRequiredParentAddress() { +// return this.requiredParentNodeAddress; +// } + + public boolean acceptsNodeType(short nodeType) { + return this.requiredNodeType == -1 || this.requiredNodeType == nodeType; + } + + public boolean requiresSpecificNodeType() { + return this.requiredNodeType != -1; + } + } + + public static interface IResultRank { + public long getRank(Nd nd, long address); + } + + private abstract class SearchCriteriaToBtreeVisitorAdapter implements IBTreeVisitor { + private final SearchCriteria searchCriteria; + private final Nd nd; + + public SearchCriteriaToBtreeVisitorAdapter(SearchCriteria searchCriteria, Nd nd) { + this.searchCriteria = searchCriteria; + this.nd = nd; + } + + @Override + public int compare(long address) throws IndexException { + IString key = FieldSearchIndex.this.searchKey.get(this.nd, address); + + if (this.searchCriteria.isPrefixSearch()) { + return key.comparePrefix(this.searchCriteria.getSearchString(), false); + } else { + return key.compareCompatibleWithIgnoreCase(this.searchCriteria.getSearchString()); + } + } + + @Override + public boolean visit(long address) throws IndexException { + if (this.searchCriteria.requiresSpecificNodeType()) { + short nodeType = NdNode.NODE_TYPE.get(this.nd, address); + + if (!this.searchCriteria.acceptsNodeType(nodeType)) { + return true; + } + } + + IString key = FieldSearchIndex.this.searchKey.get(this.nd, address); + + if (this.searchCriteria.isMatchingCase()) { + if (this.searchCriteria.isPrefixSearch()) { + if (key.comparePrefix(this.searchCriteria.getSearchString(), true) != 0) { + return true; + } + } else { + if (key.compare(this.searchCriteria.getSearchString(), true) != 0) { + return true; + } + } + } + + return acceptResult(address); + } + + protected abstract boolean acceptResult(long address); + } + + private FieldSearchIndex(FieldSearchKey<?> searchKey) { + this.btreeFactory = BTree.getFactory(new IBTreeComparator() { + @Override + public int compare(Nd nd, long record1, long record2) { + IString key1 = FieldSearchIndex.this.searchKey.get(nd, record1); + IString key2 = FieldSearchIndex.this.searchKey.get(nd, record2); + + int cmp = key1.compareCompatibleWithIgnoreCase(key2); + if (cmp == 0) { + cmp = Long.signum(record1 - record2); + } + + return cmp; + } + }); + + if (searchKey != null) { + if (searchKey.searchIndex != null && searchKey.searchIndex != this) { + throw new IllegalArgumentException( + "Attempted to construct a FieldSearchIndex referring to a search key that " //$NON-NLS-1$ + + "is already in use by a different index"); //$NON-NLS-1$ + } + searchKey.searchIndex = this; + } + this.searchKey = searchKey; + } + + public static <T extends NdNode, B> FieldSearchIndex<T> create(StructDef<B> builder, + final FieldSearchKey<B> searchKey) { + + FieldSearchIndex<T> result = new FieldSearchIndex<T>(searchKey); + + builder.add(result); + builder.addDestructableField(result); + + return result; + } + + public BTree get(Nd nd, long address) { + return this.btreeFactory.create(nd, address + this.offset); + } + + @Override + public void destruct(Nd nd, long address) { + this.btreeFactory.destruct(nd, address); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return this.btreeFactory.getRecordSize(); + } + + public T findFirst(final Nd nd, long address, final SearchCriteria searchCriteria) { + return findBest(nd, address, searchCriteria, anything); + } + + @SuppressWarnings("unchecked") + public T findBest(final Nd nd, long address, final SearchCriteria searchCriteria, final IResultRank rankFunction) { + final long[] resultRank = new long[1]; + final long[] result = new long[1]; + get(nd, address).accept(new SearchCriteriaToBtreeVisitorAdapter(searchCriteria, nd) { + @Override + protected boolean acceptResult(long resultAddress) { + long rank = rankFunction.getRank(nd, resultAddress); + if (rank >= resultRank[0]) { + resultRank[0] = rank; + result[0] = resultAddress; + } + return true; + } + }); + + if (result[0] == 0) { + return null; + } + return (T)NdNode.load(nd, result[0]); + } + + public interface Visitor<T> { + boolean visit(T toVisit); + } + + public boolean visitAll(final Nd nd, long address, final SearchCriteria searchCriteria, final Visitor<T> visitor) { + return get(nd, address).accept(new SearchCriteriaToBtreeVisitorAdapter(searchCriteria, nd) { + @SuppressWarnings("unchecked") + @Override + protected boolean acceptResult(long resultAddress) { + return visitor.visit((T)NdNode.load(nd, resultAddress)); + } + }); + } + + public List<T> findAll(final Nd nd, long address, final SearchCriteria searchCriteria) { + final List<T> result = new ArrayList<T>(); + get(nd, address).accept(new SearchCriteriaToBtreeVisitorAdapter(searchCriteria, nd) { + @SuppressWarnings("unchecked") + @Override + protected boolean acceptResult(long resultAddress) { + result.add((T)NdNode.load(nd, resultAddress)); + return true; + } + }); + + return result; + } + + /** + * Returns the entire contents of the index as a single list. + */ + public List<T> asList(final Nd nd, long address) { + final List<T> result = new ArrayList<T>(); + get(nd, address).accept(new IBTreeVisitor() { + @Override + public int compare(long record) { + return 0; + } + + @SuppressWarnings("unchecked") + @Override + public boolean visit(long resultAddress) { + result.add((T)NdNode.load(nd, resultAddress)); + return true; + } + }); + + return result; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldSearchKey.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldSearchKey.java new file mode 100644 index 000000000..1b585cb5d --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldSearchKey.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.db.BTree; +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.db.EmptyString; +import org.eclipse.jdt.internal.core.nd.db.IString; + +/** + * Represents a search key into a global search index. + */ +public class FieldSearchKey<T> implements IField, IDestructableField { + private int offset; + FieldSearchIndex<?> searchIndex; + + private FieldSearchKey(FieldSearchIndex<?> searchIndex) { + if (searchIndex != null) { + if (searchIndex.searchKey != null && searchIndex.searchKey != this) { + throw new IllegalArgumentException( + "Attempted to construct a FieldSearchKey referring to a search index that is " //$NON-NLS-1$ + + "already in use by a different key"); //$NON-NLS-1$ + } + searchIndex.searchKey = this; + } + this.searchIndex = searchIndex; + } + + /** + * Creates a search key attribute in the given struct which stores an entry in the given global search index + */ + public static <T, B extends NdNode> FieldSearchKey<T> create(StructDef<B> builder, + FieldSearchIndex<B> searchIndex) { + FieldSearchKey<T> result = new FieldSearchKey<T>(searchIndex); + + builder.add(result); + builder.addDestructableField(result); + + return result; + } + + public void put(Nd nd, long address, String newString) { + put(nd, address, newString.toCharArray()); + } + + /** + * Sets the value of the key and inserts it into the index if it is not already present + */ + public void put(Nd nd, long address, char[] newString) { + cleanup(nd, address); + + Database db = nd.getDB(); + BTree btree = this.searchIndex.get(nd, Database.DATA_AREA_OFFSET); + db.putRecPtr(address + this.offset, db.newString(newString).getRecord()); + btree.insert(address); + } + + public IString get(Nd nd, long address) { + Database db = nd.getDB(); + long namerec = db.getRecPtr(address + this.offset); + + if (namerec == 0) { + return EmptyString.create(); + } + return db.getString(namerec); + } + + @Override + public void destruct(Nd nd, long address) { + cleanup(nd, address); + } + + private void cleanup(Nd nd, long address) { + boolean isInIndex = isInIndex(nd, address); + + if (isInIndex) { + // Remove this entry from the search index + this.searchIndex.get(nd, Database.DATA_AREA_OFFSET).delete(address); + + get(nd, address).delete(); + nd.getDB().putRecPtr(address + this.offset, 0); + } + } + + /** + * Clears this key and removes it from the search index + */ + public void removeFromIndex(Nd nd, long address) { + cleanup(nd, address); + } + + /** + * Returns true iff this key is currently in the index + */ + public boolean isInIndex(Nd nd, long address) { + long fieldAddress = address + this.offset; + Database db = nd.getDB(); + long namerec = db.getRecPtr(fieldAddress); + + boolean isInIndex = namerec != 0; + return isInIndex; + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return FieldString.RECORD_SIZE; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldShort.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldShort.java new file mode 100644 index 000000000..fe2a56b2a --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldShort.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.Database; + +/** + * Declares a Nd field of type short. Can be used in place of {@link Field}<{@link Short}> in order to + * avoid extra GC overhead. + */ +public class FieldShort implements IField { + private int offset; + + public FieldShort() { + } + + public short get(Nd nd, long address) { + Database db = nd.getDB(); + return db.getShort(address + this.offset); + } + + public void put(Nd nd, long address, short newValue) { + nd.getDB().putShort(address + this.offset, newValue); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return Database.SHORT_SIZE; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldString.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldString.java new file mode 100644 index 000000000..ddd449380 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldString.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.db.EmptyString; +import org.eclipse.jdt.internal.core.nd.db.IString; + +/** + * Declares a Nd field of type string. Can be used in place of {@link Field}<{@link String}> in order to + * avoid extra GC overhead. + */ +public class FieldString implements IDestructableField, IField { + public static final int RECORD_SIZE = Database.STRING_SIZE; + private static final char[] EMPTY_CHAR_ARRAY = new char[0]; + private int offset; + + public FieldString() { + } + + public IString get(Nd nd, long address) { + Database db = nd.getDB(); + long namerec = db.getRecPtr(address + this.offset); + + if (namerec == 0) { + return EmptyString.create(); + } + return db.getString(namerec); + } + + public void put(Nd nd, long address, char[] newString) { + if (newString == null) { + newString = EMPTY_CHAR_ARRAY; + } + final Database db= nd.getDB(); + IString name= get(nd, address); + if (name.compare(newString, true) != 0) { + name.delete(); + if (newString != null && newString.length > 0) { + db.putRecPtr(address + this.offset, db.newString(newString).getRecord()); + } else { + db.putRecPtr(address + this.offset, 0); + } + } + } + + public void put(Nd nd, long address, String newString) { + put(nd, address, newString.toCharArray()); + } + + public void destruct(Nd nd, long address) { + get(nd, address).delete(); + nd.getDB().putRecPtr(address + this.offset, 0); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } + + @Override + public int getRecordSize() { + return RECORD_SIZE; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/IDestructableField.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/IDestructableField.java new file mode 100644 index 000000000..12d216c94 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/IDestructableField.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.Nd; + +public interface IDestructableField { + public void destruct(Nd nd, long address); +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/IField.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/IField.java new file mode 100644 index 000000000..979a0eb7d --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/IField.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +public interface IField { + void setOffset(int offset); + int getRecordSize(); +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/IRefCountedField.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/IRefCountedField.java new file mode 100644 index 000000000..89c27838a --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/IRefCountedField.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import org.eclipse.jdt.internal.core.nd.Nd; + +public interface IRefCountedField { + /** + * Returns true if this field knows of any remaining incoming references to this object. This is + * used by the implementation of {@link FieldManyToOne} to determine whether or not + * a refcounted object should be deleted after a reference is removed. + * <p> + * Implementations should return false if the refcount is 0 or true if the refcount + * is nonzero. + */ + public boolean hasReferences(Nd nd, long address); +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/StructDef.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/StructDef.java new file mode 100644 index 000000000..261e85371 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/StructDef.java @@ -0,0 +1,398 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jdt.internal.core.nd.IDestructable; +import org.eclipse.jdt.internal.core.nd.ITypeFactory; +import org.eclipse.jdt.internal.core.nd.Nd; + +/** + * Defines a data structure that will appear in the database. + * <p> + * There are three mechanisms for deleting a struct from the database: + * <ul> + * <li>Explicit deletion. This happens synchronously via manual calls to Nd.delete. Structs intended for manual + * deletion have refCounted=false and an empty ownerFields. + * <li>Owner pointers. Such structs have one or more outbound pointers to an "owner" object. They are deleted + * asynchronously when the last owner pointer is deleted. The structs have refCounted=false and a nonempty + * ownerFields. + * <li>Refcounting. Such structs are deleted asynchronously when all elements are removed from all of their ManyToOne + * relationships which are not marked as incoming owner pointers. Owner relationships need to be excluded from + * refcounting since they would always create cycles. These structs have refCounted=true. + * </ul> + * <p> + * Structs deleted by refcounting and owner pointers are not intended to inherit from one another, but anything may + * inherit from a struct that uses manual deletion and anything may inherit from a struct that uses the same deletion + * mechanism. + */ +public final class StructDef<T> { + Class<T> clazz; + private StructDef<? super T> superClass; + private List<IField> fields = new ArrayList<>(); + private boolean doneCalled; + private boolean offsetsComputed; + private List<StructDef<? extends T>> subClasses = new ArrayList<>(); + private int size; + List<IDestructableField> destructableFields = new ArrayList<>(); + boolean refCounted; + private List<IRefCountedField> refCountedFields = new ArrayList<>(); + private List<IRefCountedField> ownerFields = new ArrayList<>(); + boolean isAbstract; + private ITypeFactory<T> factory; + protected boolean hasUserDestructor; + private DeletionSemantics deletionSemantics; + + public static enum DeletionSemantics { + EXPLICIT, OWNED, REFCOUNTED + } + + private StructDef(Class<T> clazz) { + this(clazz, null); + } + + private StructDef(Class<T> clazz, StructDef<? super T> superClass) { + this(clazz, superClass, Modifier.isAbstract(clazz.getModifiers())); + } + + private StructDef(Class<T> clazz, StructDef<? super T> superClass, boolean isAbstract) { + this.clazz = clazz; + this.superClass = superClass; + if (this.superClass != null) { + this.superClass.subClasses.add(this); + } + this.isAbstract = isAbstract; + final String fullyQualifiedClassName = clazz.getName(); + + final Constructor<T> constructor; + if (!this.isAbstract) { + try { + constructor = clazz.getConstructor(new Class<?>[] { Nd.class, long.class }); + } catch (NoSuchMethodException | SecurityException e) { + throw new IllegalArgumentException("The node class " + fullyQualifiedClassName //$NON-NLS-1$ + + " does not have an appropriate constructor for it to be used with Nd"); //$NON-NLS-1$ + } + } else { + constructor = null; + } + + this.hasUserDestructor = IDestructable.class.isAssignableFrom(clazz); + + this.factory = new ITypeFactory<T>() { + public T create(Nd dom, long address) { + if (StructDef.this.isAbstract) { + throw new UnsupportedOperationException( + "Attempting to instantiate abstract class" + fullyQualifiedClassName); //$NON-NLS-1$ + } + + try { + return constructor.newInstance(dom, address); + } catch (InvocationTargetException e) { + Throwable target = e.getCause(); + + if (target instanceof RuntimeException) { + throw (RuntimeException) target; + } + + throw new RuntimeException("Error in AutoTypeFactory", e); //$NON-NLS-1$ + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException("Error in AutoTypeFactory", e); //$NON-NLS-1$ + } + } + + public int getRecordSize() { + return StructDef.this.size(); + } + + public boolean hasDestructor() { + return StructDef.this.hasUserDestructor || hasDestructableFields(); + } + + public Class<?> getElementClass() { + return StructDef.this.clazz; + } + + public void destruct(Nd nd, long address) { + checkNotMutable(); + if (StructDef.this.hasUserDestructor) { + IDestructable destructable = (IDestructable)create(nd, address); + destructable.destruct(); + } + destructFields(nd, address); + } + + public void destructFields(Nd dom, long address) { + StructDef.this.destructFields(dom, address); + } + + @Override + public boolean isReadyForDeletion(Nd dom, long address) { + return StructDef.this.isReadyForDeletion(dom, address); + } + + @Override + public DeletionSemantics getDeletionSemantics() { + return StructDef.this.getDeletionSemantics(); + } + }; + } + + public Class<T> getStructClass() { + return this.clazz; + } + + @Override + public String toString() { + return this.clazz.getName(); + } + + public static <T> StructDef<T> createAbstract(Class<T> clazz) { + return new StructDef<T>(clazz, null, true); + } + + public static <T> StructDef<T> createAbstract(Class<T> clazz, StructDef<? super T> superClass) { + return new StructDef<T>(clazz, superClass, true); + } + + public static <T> StructDef<T> create(Class<T> clazz) { + return new StructDef<T>(clazz); + } + + public static <T> StructDef<T> create(Class<T> clazz, StructDef<? super T> superClass) { + return new StructDef<T>(clazz, superClass); + } + + protected boolean isReadyForDeletion(Nd dom, long address) { + List<IRefCountedField> toIterate = Collections.EMPTY_LIST; + switch (this.deletionSemantics) { + case EXPLICIT: return false; + case OWNED: toIterate = this.ownerFields; break; + case REFCOUNTED: toIterate = this.refCountedFields; break; + } + + for (IRefCountedField next : toIterate) { + if (next.hasReferences(dom, address)) { + return false; + } + } + + final StructDef<? super T> localSuperClass = StructDef.this.superClass; + if (localSuperClass != null && localSuperClass.deletionSemantics != DeletionSemantics.EXPLICIT) { + return localSuperClass.isReadyForDeletion(dom, address); + } + return true; + } + + protected boolean hasDestructableFields() { + return (!StructDef.this.destructableFields.isEmpty() || + (StructDef.this.superClass != null && StructDef.this.superClass.hasDestructableFields())); + } + + public DeletionSemantics getDeletionSemantics() { + return this.deletionSemantics; + } + + /** + * Call this once all the fields have been added to the struct definition and it is + * ready to use. + */ + public void done() { + if (this.doneCalled) { + throw new IllegalStateException("May not call done() more than once"); //$NON-NLS-1$ + } + this.doneCalled = true; + + if (this.superClass == null || this.superClass.areOffsetsComputed()) { + computeOffsets(); + } + } + + public void add(IField toAdd) { + checkMutable(); + + this.fields.add(toAdd); + } + + public void addDestructableField(IDestructableField field) { + checkMutable(); + + this.destructableFields.add(field); + } + + public StructDef<T> useStandardRefCounting() { + checkMutable(); + + this.refCounted = true; + return this; + } + + public void addRefCountedField(IRefCountedField result) { + checkMutable(); + + this.refCountedFields.add(result); + } + + public void addOwnerField(IRefCountedField result) { + checkMutable(); + + this.ownerFields.add(result); + } + + public boolean areOffsetsComputed() { + return this.offsetsComputed; + } + + public int size() { + checkNotMutable(); + return this.size; + } + + void checkNotMutable() { + if (!this.offsetsComputed) { + throw new IllegalStateException("Must call done() before using the struct"); //$NON-NLS-1$ + } + } + + private void checkMutable() { + if (this.doneCalled) { + throw new IllegalStateException("May not modify a StructDef after done() has been called"); //$NON-NLS-1$ + } + } + + /** + * Invoked on all StructDef after both {@link #done()} has been called on the struct and + * {@link #computeOffsets()} has been called on their base class. + */ + private void computeOffsets() { + int offset = this.superClass == null ? 0 : this.superClass.size(); + + for (IField next : this.fields) { + next.setOffset(offset); + offset += next.getRecordSize(); + } + + this.size = offset; + if (this.refCounted) { + this.deletionSemantics = DeletionSemantics.REFCOUNTED; + } else { + if (!this.ownerFields.isEmpty()) { + this.deletionSemantics = DeletionSemantics.OWNED; + } else if (this.superClass != null) { + this.deletionSemantics = this.superClass.deletionSemantics; + } else { + this.deletionSemantics = DeletionSemantics.EXPLICIT; + } + } + // Now verify that the deletion semantics of this struct are compatible with the deletion + // semantics of its superclass + if (this.superClass != null && this.deletionSemantics != this.superClass.deletionSemantics) { + if (this.superClass.deletionSemantics != DeletionSemantics.EXPLICIT) { + throw new IllegalStateException("A class (" + this.clazz.getName() + ") that uses " //$NON-NLS-1$//$NON-NLS-2$ + + this.deletionSemantics.toString() + " deletion semantics may not inherit from a class " //$NON-NLS-1$ + + "that uses " + this.superClass.deletionSemantics.toString() + " semantics"); //$NON-NLS-1$//$NON-NLS-2$ + } + } + + this.offsetsComputed = true; + + for (StructDef<? extends T> next : this.subClasses) { + if (next.doneCalled) { + next.computeOffsets(); + } + } + } + + public FieldPointer addPointer() { + FieldPointer result = new FieldPointer(); + add(result); + return result; + } + + public FieldShort addShort() { + FieldShort result = new FieldShort(); + add(result); + return result; + } + + public FieldInt addInt() { + FieldInt result = new FieldInt(); + add(result); + return result; + } + + public FieldLong addLong() { + FieldLong result = new FieldLong(); + add(result); + return result; + } + + public FieldString addString() { + FieldString result = new FieldString(); + add(result); + addDestructableField(result); + return result; + } + + public FieldDouble addDouble() { + FieldDouble result = new FieldDouble(); + add(result); + return result; + } + + public FieldFloat addFloat() { + FieldFloat result = new FieldFloat(); + add(result); + return result; + } + + public FieldByte addByte() { + FieldByte result = new FieldByte(); + add(result); + return result; + } + + public FieldChar addChar() { + FieldChar result = new FieldChar(); + add(result); + return result; + } + + public <F> Field<F> add(ITypeFactory<F> factory1) { + Field<F> result = new Field<>(factory1); + add(result); + if (result.factory.hasDestructor()) { + this.destructableFields.add(result); + } + return result; + } + + public ITypeFactory<T> getFactory() { + return this.factory; + } + + void destructFields(Nd dom, long address) { + for (IDestructableField next : StructDef.this.destructableFields) { + next.destruct(dom, address); + } + + if (this.superClass != null) { + this.superClass.destructFields(dom, address); + } + } + + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/BindingToIndexConverter.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/BindingToIndexConverter.java new file mode 100644 index 000000000..a8788eb9b --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/BindingToIndexConverter.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.indexer; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.dom.IAnnotationBinding; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.IMemberValuePairBinding; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.IPackageBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.java.JavaIndex; +import org.eclipse.jdt.internal.core.nd.java.JavaNames; +import org.eclipse.jdt.internal.core.nd.java.NdResourceFile; +import org.eclipse.jdt.internal.core.nd.java.NdTreeNode; +import org.eclipse.jdt.internal.core.nd.java.NdType; +import org.eclipse.jdt.internal.core.nd.java.NdTypeId; +import org.eclipse.jdt.internal.core.nd.java.NdTypeInterface; + +public class BindingToIndexConverter { + private static final boolean ENABLE_LOGGING = false; + private JavaIndex index; + private NdResourceFile resource; + + public BindingToIndexConverter(NdResourceFile resource) { + this.resource = resource; + this.index = JavaIndex.getIndex(resource.getNd()); + } + + public void addBinding(NdTreeNode parent, IBinding binding, IProgressMonitor monitor) { + switch (binding.getKind()) { + case IBinding.TYPE: + addType((ITypeBinding) binding, monitor); + break; + case IBinding.ANNOTATION: + addAnnotation(parent, (IAnnotationBinding) binding, monitor); + break; + case IBinding.METHOD: + addMethod(parent, (IMethodBinding) binding, monitor); + break; + case IBinding.VARIABLE: + addVariable(parent, (IVariableBinding) binding, monitor); + break; + case IBinding.PACKAGE: + addPackage(parent, (IPackageBinding) binding, monitor); + break; + case IBinding.MEMBER_VALUE_PAIR: + addMemberValuePair(parent, (IMemberValuePairBinding) binding, monitor); + break; + default: + Package.log("Encountered unknown binding type: " + binding.getKind(), null); //$NON-NLS-1$ + } + } + + public void addMemberValuePair(NdTreeNode parent, IMemberValuePairBinding binding, IProgressMonitor monitor) { + logInfo("Adding member value pair: " + binding.getName()); //$NON-NLS-1$ + } + + public void addPackage(NdTreeNode parent, IPackageBinding binding, IProgressMonitor monitor) { + logInfo("Adding package: " + binding.getName()); //$NON-NLS-1$ + } + + public void addVariable(NdTreeNode parent, IVariableBinding binding, IProgressMonitor monitor) { + logInfo("Adding variable: " + binding.getName()); //$NON-NLS-1$ + } + + public void addMethod(NdTreeNode parent, IMethodBinding binding, IProgressMonitor monitor) { + logInfo("Adding method: " + binding.getName()); //$NON-NLS-1$ + } + + public void addAnnotation(NdTreeNode parent, IAnnotationBinding binding, IProgressMonitor monitor) { + logInfo("Adding annotation: " + binding.getName()); //$NON-NLS-1$ + } + + public NdType addType(ITypeBinding binding, IProgressMonitor monitor) { + logInfo("Adding type: " + binding.getBinaryName()); //$NON-NLS-1$ + + NdTypeId name = makeTypeId(binding); + NdType type = name.findTypeByResourceAddress(this.resource.address); + + if (type == null) { + type = new NdType(getNd(), this.resource); + } + + type.setTypeId(name); + + ITypeBinding superclass = binding.getSuperclass(); + + if (superclass != null) { + type.setSuperclass(makeTypeId(superclass)); + } + + for (ITypeBinding next : binding.getInterfaces()) { + new NdTypeInterface(getNd(), type, makeTypeId(next)); + } + + return type; + } + + private void logInfo(String string) { + if (ENABLE_LOGGING) { + Package.logInfo(string); + } + } + + private NdTypeId makeTypeId(ITypeBinding forBinding) { + return this.index.createTypeId(JavaNames.binaryNameToFieldDescriptor(forBinding.getBinaryName().toCharArray())); + } + + private Nd getNd() { + return this.resource.getNd(); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/ClassFileToIndexConverter.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/ClassFileToIndexConverter.java new file mode 100644 index 000000000..afd740ee0 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/ClassFileToIndexConverter.java @@ -0,0 +1,930 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.indexer; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IClassFile; +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants; +import org.eclipse.jdt.internal.compiler.env.ClassSignature; +import org.eclipse.jdt.internal.compiler.env.EnumConstantSignature; +import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; +import org.eclipse.jdt.internal.compiler.env.IBinaryElementValuePair; +import org.eclipse.jdt.internal.compiler.env.IBinaryField; +import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.lookup.SignatureWrapper; +import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; +import org.eclipse.jdt.internal.core.JavaModelManager; +import org.eclipse.jdt.internal.core.Openable; +import org.eclipse.jdt.internal.core.PackageFragment; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.IndexException; +import org.eclipse.jdt.internal.core.nd.java.JavaIndex; +import org.eclipse.jdt.internal.core.nd.java.JavaNames; +import org.eclipse.jdt.internal.core.nd.java.NdAnnotation; +import org.eclipse.jdt.internal.core.nd.java.NdAnnotationInConstant; +import org.eclipse.jdt.internal.core.nd.java.NdAnnotationInMethod; +import org.eclipse.jdt.internal.core.nd.java.NdAnnotationInMethodParameter; +import org.eclipse.jdt.internal.core.nd.java.NdAnnotationInType; +import org.eclipse.jdt.internal.core.nd.java.NdAnnotationInVariable; +import org.eclipse.jdt.internal.core.nd.java.NdAnnotationValuePair; +import org.eclipse.jdt.internal.core.nd.java.NdBinding; +import org.eclipse.jdt.internal.core.nd.java.NdComplexTypeSignature; +import org.eclipse.jdt.internal.core.nd.java.NdConstant; +import org.eclipse.jdt.internal.core.nd.java.NdConstantAnnotation; +import org.eclipse.jdt.internal.core.nd.java.NdConstantArray; +import org.eclipse.jdt.internal.core.nd.java.NdConstantClass; +import org.eclipse.jdt.internal.core.nd.java.NdConstantEnum; +import org.eclipse.jdt.internal.core.nd.java.NdMethod; +import org.eclipse.jdt.internal.core.nd.java.NdMethodException; +import org.eclipse.jdt.internal.core.nd.java.NdMethodId; +import org.eclipse.jdt.internal.core.nd.java.NdMethodParameter; +import org.eclipse.jdt.internal.core.nd.java.NdResourceFile; +import org.eclipse.jdt.internal.core.nd.java.NdType; +import org.eclipse.jdt.internal.core.nd.java.NdTypeAnnotation; +import org.eclipse.jdt.internal.core.nd.java.NdTypeAnnotationInMethod; +import org.eclipse.jdt.internal.core.nd.java.NdTypeAnnotationInType; +import org.eclipse.jdt.internal.core.nd.java.NdTypeAnnotationInVariable; +import org.eclipse.jdt.internal.core.nd.java.NdTypeArgument; +import org.eclipse.jdt.internal.core.nd.java.NdTypeBound; +import org.eclipse.jdt.internal.core.nd.java.NdTypeId; +import org.eclipse.jdt.internal.core.nd.java.NdTypeInterface; +import org.eclipse.jdt.internal.core.nd.java.NdTypeParameter; +import org.eclipse.jdt.internal.core.nd.java.NdTypeSignature; +import org.eclipse.jdt.internal.core.nd.java.NdVariable; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeDescriptor; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; +import org.eclipse.jdt.internal.core.util.CharArrayBuffer; +import org.eclipse.jdt.internal.core.util.Util; + +public final class ClassFileToIndexConverter { + private static final char[] JAVA_LANG_OBJECT_FIELD_DESCRIPTOR = "Ljava/lang/Object;".toCharArray(); //$NON-NLS-1$ + private static final char[] INNER_TYPE_SEPARATOR = new char[] { '$' }; + private static final char[] FIELD_DESCRIPTOR_SUFFIX = new char[] { ';' }; + private static final char[] COMMA = new char[]{','}; + private static final char[][] EMPTY_CHAR_ARRAY_ARRAY = new char[0][]; + private static final boolean ENABLE_LOGGING = false; + private static final char[] EMPTY_CHAR_ARRAY = new char[0]; + private static final char[] PATH_SEPARATOR = new char[]{'/'}; + private static final char[] ARRAY_FIELD_DESCRIPTOR_PREFIX = new char[] { '[' }; + private NdResourceFile resource; + private JavaIndex index; + + public ClassFileToIndexConverter(NdResourceFile resourceFile) { + this.resource = resourceFile; + this.index = JavaIndex.getIndex(resourceFile.getNd()); + } + + private Nd getNd() { + return this.resource.getNd(); + } + + public static IBinaryType getTypeFromClassFile(IClassFile iClassFile, IProgressMonitor monitor) + throws CoreException, ClassFormatException { + BinaryTypeDescriptor descriptor = BinaryTypeFactory.createDescriptor(iClassFile); + return BinaryTypeFactory.rawReadType(descriptor, true); + } + + /** + * Create a type info from the given class file in a jar and adds it to the given list of infos. + * + * @throws CoreException + */ + protected static IBinaryType createInfoFromClassFileInJar(Openable classFile) throws CoreException { + PackageFragment pkg = (PackageFragment) classFile.getParent(); + String classFilePath = Util.concatWith(pkg.names, classFile.getElementName(), '/'); + IBinaryType info = null; + java.util.zip.ZipFile zipFile = null; + try { + zipFile = ((JarPackageFragmentRoot) pkg.getParent()).getJar(); + info = org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader.read(zipFile, classFilePath); + } catch (Exception e) { + throw new CoreException(Package.createStatus("Unable to parse JAR file", e)); //$NON-NLS-1$ + } finally { + JavaModelManager.getJavaModelManager().closeZipFile(zipFile); + } + return info; + } + + /** + * Adds a type to the index, given an input class file and a binary name. Note that the given binary name is + * + * @param binaryType an object used for parsing the .class file itself + * @param fieldDescriptor the name that is used to locate the class, computed from the .class file's name and location. + * In the event that the .class file has been moved, this may differ from the binary name stored in the .class file + * itself, which is why this is received as an argument rather than extracted from the .class file. + * @throws CoreException + */ + public NdType addType(IBinaryType binaryType, char[] fieldDescriptor, IProgressMonitor monitor) throws CoreException { + char[] fieldDescriptorFromClass = JavaNames.binaryNameToFieldDescriptor(binaryType.getName()); + logInfo("adding binary type " + new String(fieldDescriptor)); //$NON-NLS-1$ + + NdTypeId name = createTypeIdFromFieldDescriptor(fieldDescriptor); + NdType type = name.findTypeByResourceAddress(this.resource.address); + + if (type == null) { + type = new NdType(getNd(), this.resource); + } + + IBinaryTypeAnnotation[] typeAnnotations = binaryType.getTypeAnnotations(); + if (typeAnnotations != null) { + for (IBinaryTypeAnnotation typeAnnotation : typeAnnotations) { + NdTypeAnnotationInType annotation = new NdTypeAnnotationInType(getNd(), type); + + initTypeAnnotation(annotation, typeAnnotation); + } + } + + type.setTypeId(name); + + if (!CharArrayUtils.equals(fieldDescriptorFromClass, fieldDescriptor)) { + type.setFieldDescriptorFromClass(fieldDescriptorFromClass); + } + + char[][] interfaces = binaryType.getInterfaceNames(); + if (interfaces == null) { + interfaces = EMPTY_CHAR_ARRAY_ARRAY; + } + + if (binaryType.getGenericSignature() != null) { + type.setFlag(NdType.FLG_GENERIC_SIGNATURE_PRESENT, true); + } + + // Create the default generic signature if the .class file didn't supply one + SignatureWrapper signatureWrapper = GenericSignatures.getGenericSignature(binaryType); + + type.setModifiers(binaryType.getModifiers()); + type.setDeclaringType(createTypeIdFromBinaryName(binaryType.getEnclosingTypeName())); + + readTypeParameters(type, signatureWrapper); + + char[] superclassFieldDescriptor; + char[] superclassBinaryName = binaryType.getSuperclassName(); + if (superclassBinaryName == null) { + superclassFieldDescriptor = JAVA_LANG_OBJECT_FIELD_DESCRIPTOR; + } else { + superclassFieldDescriptor = JavaNames.binaryNameToFieldDescriptor(superclassBinaryName); + } + type.setSuperclass(createTypeSignature(signatureWrapper, superclassFieldDescriptor)); + + short interfaceIdx = 0; + while (signatureWrapper.start < signatureWrapper.signature.length) { + // Note that there may be more interfaces listed in the generic signature than in the interfaces list. + // Although the VM spec doesn't discuss this case specifically, there are .class files in the wild with + // this characteristic. In such cases, we take what's in the generic signature and discard what's in the + // interfaces list. + char[] interfaceSpec = interfaceIdx < interfaces.length ? interfaces[interfaceIdx] : EMPTY_CHAR_ARRAY; + new NdTypeInterface(getNd(), type, + createTypeSignature(signatureWrapper, JavaNames.binaryNameToFieldDescriptor(interfaceSpec))); + interfaceIdx++; + } + + IBinaryAnnotation[] annotations = binaryType.getAnnotations(); + attachAnnotations(type, annotations); + + type.setDeclaringMethod(createMethodId(binaryType.getEnclosingTypeName(), binaryType.getEnclosingMethod())); + + IBinaryField[] fields = binaryType.getFields(); + + if (fields != null) { + for (IBinaryField nextField : fields) { + addField(type, nextField); + } + } + + IBinaryMethod[] methods = binaryType.getMethods(); + + if (methods != null) { + for (IBinaryMethod next : methods) { + addMethod(type, next, binaryType); + } + } + + char[][][] missingTypeNames = binaryType.getMissingTypeNames(); + char[] missingTypeString = getMissingTypeString(missingTypeNames); + + type.setMissingTypeNames(missingTypeString); + type.setSourceFileName(binaryType.sourceFileName()); + type.setAnonymous(binaryType.isAnonymous()); + type.setIsLocal(binaryType.isLocal()); + type.setIsMember(binaryType.isMember()); + type.setTagBits(binaryType.getTagBits()); + type.setSourceNameOverride(binaryType.getSourceName()); + + return type; + } + + private static char[] getMissingTypeString(char[][][] missingTypeNames) { + char[] missingTypeString = null; + if (missingTypeNames != null) { + CharArrayBuffer builder = new CharArrayBuffer(); + for (int typeIdx = 0; typeIdx < missingTypeNames.length; typeIdx++) { + char[][] next = missingTypeNames[typeIdx]; + if (typeIdx != 0) { + builder.append(COMMA); + } + if (next == null) { + continue; + } + for (int segmentIdx = 0; segmentIdx < next.length; segmentIdx++) { + char[] segment = next[segmentIdx]; + if (segment == null) { + continue; + } + if (segmentIdx != 0) { + builder.append(PATH_SEPARATOR); + } + builder.append(segment); + } + } + missingTypeString = builder.getContents(); + } + return missingTypeString; + } + + private void attachAnnotations(NdMethod method, IBinaryAnnotation[] annotations) { + if (annotations != null) { + for (IBinaryAnnotation next : annotations) { + NdAnnotationInMethod annotation = new NdAnnotationInMethod(getNd(), method); + initAnnotation(annotation, next); + } + } + } + + private void attachAnnotations(NdType type, IBinaryAnnotation[] annotations) { + if (annotations != null) { + for (IBinaryAnnotation next : annotations) { + NdAnnotationInType annotation = new NdAnnotationInType(getNd(), type); + initAnnotation(annotation, next); + } + } + } + + private void attachAnnotations(NdVariable variable, IBinaryAnnotation[] annotations) { + if (annotations != null) { + for (IBinaryAnnotation next : annotations) { + NdAnnotationInVariable annotation = new NdAnnotationInVariable(getNd(), variable); + initAnnotation(annotation, next); + } + } + } + + private void attachAnnotations(NdMethodParameter variable, IBinaryAnnotation[] annotations) { + if (annotations != null) { + for (IBinaryAnnotation next : annotations) { + NdAnnotationInMethodParameter annotation = new NdAnnotationInMethodParameter(getNd(), variable); + initAnnotation(annotation, next); + } + } + } + + /** + * Adds the given method to the given type + * + * @throws CoreException + */ + private void addMethod(NdType type, IBinaryMethod next, IBinaryType binaryType) + throws CoreException { + int flags = 0; + NdMethod method = new NdMethod(type); + + attachAnnotations(method, next.getAnnotations()); + + if (next.getGenericSignature() != null) { + flags |= NdMethod.FLG_GENERIC_SIGNATURE_PRESENT; + } + SignatureWrapper signature = GenericSignatures.getGenericSignature(next); + SignatureWrapper descriptor = new SignatureWrapper(next.getMethodDescriptor()); + readTypeParameters(method, signature); + + IBinaryTypeAnnotation[] typeAnnotations = next.getTypeAnnotations(); + if (typeAnnotations != null) { + for (IBinaryTypeAnnotation typeAnnotation : typeAnnotations) { + NdTypeAnnotationInMethod annotation = new NdTypeAnnotationInMethod(getNd(), method); + + initTypeAnnotation(annotation, typeAnnotation); + } + } + + skipChar(signature, '('); + skipChar(descriptor, '('); + + List<char[]> parameterFieldDescriptors = new ArrayList<>(); + while (!descriptor.atEnd()) { + if (descriptor.charAtStart() == ')') { + skipChar(descriptor, ')'); + break; + } + parameterFieldDescriptors.add(readNextFieldDescriptor(descriptor)); + } + + char[][] parameterNames = next.getArgumentNames(); + int numArgumentsInGenericSignature = countMethodArguments(signature); + int numCompilerDefinedParameters = Math.max(0, + parameterFieldDescriptors.size() - numArgumentsInGenericSignature); + + boolean compilerDefinedParametersAreIncludedInSignature = (next.getGenericSignature() == null); + + // If there is no generic signature, then fall back to heuristics based on what we know about + // where the java compiler likes to create compiler-defined arguments + if (compilerDefinedParametersAreIncludedInSignature) { + // Constructors for non-static member types get a compiler-defined first argument + if (binaryType.isMember() + && next.isConstructor() + && ((binaryType.getModifiers() & Modifier.STATIC) == 0) + && parameterFieldDescriptors.size() > 0) { + + numCompilerDefinedParameters = 1; + } else { + numCompilerDefinedParameters = 0; + } + } + + int parameterNameIdx = 0; + int annotatedParametersCount = next.getAnnotatedParametersCount(); + + short descriptorParameterIdx = 0; + char[] binaryTypeName = binaryType.getName(); + while (!signature.atEnd()) { + if (signature.charAtStart() == ')') { + skipChar(signature, ')'); + break; + } + char[] nextFieldDescriptor = parameterFieldDescriptors.get(descriptorParameterIdx); + /** + * True iff this a parameter which is part of the field descriptor but not the generic signature -- that is, + * it is a compiler-defined parameter. + */ + boolean isCompilerDefined = descriptorParameterIdx < numCompilerDefinedParameters; + SignatureWrapper nextFieldSignature = signature; + if (isCompilerDefined && !compilerDefinedParametersAreIncludedInSignature) { + nextFieldSignature = new SignatureWrapper(nextFieldDescriptor); + } + NdMethodParameter parameter = new NdMethodParameter(method, + createTypeSignature(nextFieldSignature, nextFieldDescriptor)); + + parameter.setCompilerDefined(isCompilerDefined); + + if (descriptorParameterIdx < annotatedParametersCount) { + IBinaryAnnotation[] parameterAnnotations = next.getParameterAnnotations(descriptorParameterIdx, + binaryTypeName); + + attachAnnotations(parameter, parameterAnnotations); + } + if (!isCompilerDefined && parameterNames != null && parameterNames.length > parameterNameIdx) { + parameter.setName(parameterNames[parameterNameIdx++]); + } + descriptorParameterIdx++; + } + + skipChar(descriptor, ')'); + char[] nextFieldDescriptor = readNextFieldDescriptor(descriptor); + method.setReturnType(createTypeSignature(signature, nextFieldDescriptor)); + + boolean hasExceptionsInSignature = hasAnotherException(signature); + char[][] exceptionTypes = next.getExceptionTypeNames(); + if (exceptionTypes == null) { + exceptionTypes = CharArrayUtils.EMPTY_ARRAY_OF_CHAR_ARRAYS; + } + int throwsIdx = 0; + if (hasExceptionsInSignature) { + while (hasAnotherException(signature)) { + signature.start++; + new NdMethodException(method, createTypeSignature(signature, + JavaNames.binaryNameToFieldDescriptor(exceptionTypes[throwsIdx]))); + throwsIdx++; + } + } else if (exceptionTypes.length != 0) { + for (;throwsIdx < exceptionTypes.length; throwsIdx++) { + char[] fieldDescriptor = JavaNames.binaryNameToFieldDescriptor(exceptionTypes[throwsIdx]); + SignatureWrapper convertedWrapper = new SignatureWrapper(fieldDescriptor); + new NdMethodException(method, createTypeSignature(convertedWrapper, + JavaNames.binaryNameToFieldDescriptor(exceptionTypes[throwsIdx]))); + } + } + + if (hasExceptionsInSignature) { + flags |= NdMethod.FLG_THROWS_SIGNATURE_PRESENT; + } + + Object defaultValue = next.getDefaultValue(); + if (defaultValue != null) { + method.setDefaultValue(createConstantFromMixedType(defaultValue)); + } + + method.setMethodId(createMethodId(binaryType.getName(), next.getSelector(), next.getMethodDescriptor())); + method.setModifiers(next.getModifiers()); + method.setTagBits(next.getTagBits()); + method.setFlags(flags); + } + + private boolean hasAnotherException(SignatureWrapper signature) { + return !signature.atEnd() && signature.charAtStart() == '^'; + } + + private void skipChar(SignatureWrapper signature, char toSkip) { + if (signature.start < signature.signature.length && signature.charAtStart() == toSkip) { + signature.start++; + } + } + + /** + * Adds the given field to the given type + */ + private void addField(NdType type, IBinaryField nextField) throws CoreException { + NdVariable variable = new NdVariable(type); + + variable.setName(nextField.getName()); + + if (nextField.getGenericSignature() != null) { + variable.setVariableFlag(NdVariable.FLG_GENERIC_SIGNATURE_PRESENT); + } + + attachAnnotations(variable, nextField.getAnnotations()); + + variable.setConstant(NdConstant.create(getNd(), nextField.getConstant())); + variable.setModifiers(nextField.getModifiers()); + SignatureWrapper nextTypeSignature = GenericSignatures.getGenericSignatureFor(nextField); + + IBinaryTypeAnnotation[] typeAnnotations = nextField.getTypeAnnotations(); + if (typeAnnotations != null) { + for (IBinaryTypeAnnotation next : typeAnnotations) { + NdTypeAnnotationInVariable annotation = new NdTypeAnnotationInVariable(getNd(), variable); + + initTypeAnnotation(annotation, next); + } + } + variable.setType(createTypeSignature(nextTypeSignature, nextField.getTypeName())); + variable.setTagBits(nextField.getTagBits()); + + // char[] fieldDescriptor = nextField.getTypeName(); + // // DO NOT SUBMIT: + // IBinaryField bf = IndexBinaryType.createBinaryField(variable); + } + + /** + * Reads and attaches any generic type parameters at the current start position in the given wrapper. Sets + * wrapper.start to the character following the type parameters. + * + * @throws CoreException + */ + private void readTypeParameters(NdBinding type, SignatureWrapper wrapper) + throws CoreException { + char[] genericSignature = wrapper.signature; + if (genericSignature.length == 0 || wrapper.charAtStart() != '<') { + return; + } + + int indexOfClosingBracket = wrapper.skipAngleContents(wrapper.start) - 1; + wrapper.start++; + NdTypeParameter parameter = null; + while (wrapper.start < indexOfClosingBracket) { + int colonPos = CharOperation.indexOf(':', genericSignature, wrapper.start, indexOfClosingBracket); + + if (colonPos > wrapper.start) { + char[] identifier = CharOperation.subarray(genericSignature, wrapper.start, colonPos); + parameter = new NdTypeParameter(type, identifier); + wrapper.start = colonPos + 1; + // The first bound is a class as long as it doesn't start with a double-colon + parameter.setFirstBoundIsClass(wrapper.charAtStart() != ':'); + } + + skipChar(wrapper, ':'); + + NdTypeSignature boundSignature = createTypeSignature(wrapper, JAVA_LANG_OBJECT_FIELD_DESCRIPTOR); + + new NdTypeBound(parameter, boundSignature); + } + + skipChar(wrapper, '>'); + } + + private char[] readNextFieldDescriptor(SignatureWrapper genericSignature) { + int endPosition = findEndOfFieldDescriptor(genericSignature); + + char[] result = CharArrayUtils.subarray(genericSignature.signature, genericSignature.start, endPosition); + genericSignature.start = endPosition; + return result; + } + + private int findEndOfFieldDescriptor(SignatureWrapper genericSignature) { + char[] signature = genericSignature.signature; + + if (signature == null || signature.length == 0) { + return genericSignature.start; + } + int current = genericSignature.start; + while (current < signature.length) { + char firstChar = signature[current]; + switch (firstChar) { + case 'L': + case 'T': { + return CharArrayUtils.indexOf(';', signature, current, signature.length) + 1; + } + case '[': { + current++; + break; + } + case 'V': + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + return current + 1; + default: + throw new IndexException(Package.createStatus("Field descriptor starts with unknown character: " //$NON-NLS-1$ + + genericSignature.toString())); + } + } + return current; + } + + /** + * Given a generic signature which is positioned at the open brace for method arguments, this returns the number of + * method arguments. The start position of the given signature is not modified. + */ + private int countMethodArguments(SignatureWrapper genericSignature) { + SignatureWrapper lookaheadSignature = new SignatureWrapper(genericSignature.signature); + lookaheadSignature.start = genericSignature.start; + skipChar(lookaheadSignature, '('); + int argumentCount = 0; + while (!lookaheadSignature.atEnd() && !(lookaheadSignature.charAtStart() == ')')) { + switch (lookaheadSignature.charAtStart()) { + case 'T': { + // Skip the 'T' prefix + lookaheadSignature.nextWord(); + skipChar(lookaheadSignature, ';'); + argumentCount++; + break; + } + case '[': { + // Skip the '[' prefix + lookaheadSignature.start++; + break; + } + case 'V': + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + argumentCount++; + lookaheadSignature.start++; + break; + case 'L': + for (;;) { + lookaheadSignature.nextWord(); + lookaheadSignature.start = lookaheadSignature.skipAngleContents(lookaheadSignature.start); + char nextChar = lookaheadSignature.charAtStart(); + if (nextChar == ';') { + break; + } + if (nextChar != '.') { + throw new IllegalStateException( + "Unknown char in generic signature " + lookaheadSignature.toString()); //$NON-NLS-1$ + } + } + skipChar(lookaheadSignature, ';'); + argumentCount++; + break; + default: + throw new IllegalStateException("Generic signature starts with unknown character: " //$NON-NLS-1$ + + lookaheadSignature.toString()); + } + } + return argumentCount; + } + + /** + * Reads a type signature from the given {@link SignatureWrapper}, starting at the character pointed to by + * wrapper.start. On return, wrapper.start will point to the first character following the type signature. Returns + * null if given an empty signature or the signature for the void type. + * + * @param genericSignature + * the generic signature to parse + * @param fieldDescriptorIfVariable + * the field descriptor to use if the type is a type variable -- or null if unknown (the field descriptor + * for java.lang.Object will be used) + * @throws CoreException + */ + private NdTypeSignature createTypeSignature(SignatureWrapper genericSignature, char[] fieldDescriptorIfVariable) + throws CoreException { + char[] signature = genericSignature.signature; + + if (signature == null || signature.length == 0) { + return null; + } + + char firstChar = genericSignature.charAtStart(); + switch (firstChar) { + case 'T': { + // Skip the 'T' prefix + genericSignature.start++; + NdComplexTypeSignature typeSignature = new NdComplexTypeSignature(getNd()); + char[] fieldDescriptor = fieldDescriptorIfVariable; + if (fieldDescriptor == null) { + fieldDescriptor = JAVA_LANG_OBJECT_FIELD_DESCRIPTOR; + } + typeSignature.setRawType(createTypeIdFromFieldDescriptor(fieldDescriptor)); + typeSignature.setVariableIdentifier(genericSignature.nextWord()); + // Skip the trailing semicolon + skipChar(genericSignature, ';'); + return typeSignature; + } + case '[': { + // Skip the '[' prefix + genericSignature.start++; + char[] nestedFieldDescriptor = null; + if (fieldDescriptorIfVariable != null && fieldDescriptorIfVariable.length > 0 + && fieldDescriptorIfVariable[0] == '[') { + nestedFieldDescriptor = CharArrayUtils.subarray(fieldDescriptorIfVariable, 1); + } + // Determine the array argument type + NdTypeSignature elementType = createTypeSignature(genericSignature, nestedFieldDescriptor); + char[] computedFieldDescriptor = CharArrayUtils.concat(ARRAY_FIELD_DESCRIPTOR_PREFIX, + elementType.getRawType().getFieldDescriptor().getChars()); + NdTypeId rawType = createTypeIdFromFieldDescriptor(computedFieldDescriptor); + // We encode signatures as though they were a one-argument generic type whose element + // type is the generic argument. + NdComplexTypeSignature typeSignature = new NdComplexTypeSignature(getNd()); + typeSignature.setRawType(rawType); + NdTypeArgument typeArgument = new NdTypeArgument(getNd(), typeSignature); + typeArgument.setType(elementType); + return typeSignature; + } + case 'V': + genericSignature.start++; + return null; + case 'B': + case 'C': + case 'D': + case 'F': + case 'I': + case 'J': + case 'S': + case 'Z': + genericSignature.start++; + return createTypeIdFromFieldDescriptor(new char[] { firstChar }); + case 'L': + return parseClassTypeSignature(null, genericSignature); + case '+': + case '-': + case '*': + throw new CoreException(Package.createStatus("Unexpected wildcard in top-level of generic signature: " //$NON-NLS-1$ + + genericSignature.toString())); + default: + throw new CoreException(Package.createStatus("Generic signature starts with unknown character: " //$NON-NLS-1$ + + genericSignature.toString())); + } + } + + /** + * Parses a ClassTypeSignature (as described in section 4.7.9.1 of the Java VM Specification Java SE 8 Edition). The + * read pointer should be located just after the identifier. The caller is expected to have already read the field + * descriptor for the type. + */ + private NdTypeSignature parseClassTypeSignature(NdComplexTypeSignature parentTypeOrNull, SignatureWrapper wrapper) + throws CoreException { + char[] identifier = wrapper.nextWord(); + char[] fieldDescriptor; + + if (parentTypeOrNull != null) { + fieldDescriptor = CharArrayUtils.concat( + parentTypeOrNull.getRawType().getFieldDescriptorWithoutTrailingSemicolon(), + INNER_TYPE_SEPARATOR, identifier, FIELD_DESCRIPTOR_SUFFIX); + } else { + fieldDescriptor = CharArrayUtils.concat(identifier, FIELD_DESCRIPTOR_SUFFIX); + } + + char[] genericSignature = wrapper.signature; + boolean hasGenericArguments = (genericSignature.length > wrapper.start) + && genericSignature[wrapper.start] == '<'; + boolean isRawTypeWithNestedClass = genericSignature[wrapper.start] == '.'; + NdTypeId rawType = createTypeIdFromFieldDescriptor(fieldDescriptor); + NdTypeSignature result = rawType; + + boolean checkForSemicolon = true; + // Special optimization for signatures with no type annotations, no arrays, and no generic arguments that + // are not an inner type of a class that can't use this optimization. Basically, if there would be no attributes + // set on a NdComplexTypeSignature besides what it picks up from its raw type, we just use the raw type. + if (hasGenericArguments || parentTypeOrNull != null || isRawTypeWithNestedClass) { + NdComplexTypeSignature typeSignature = new NdComplexTypeSignature(getNd()); + typeSignature.setRawType(rawType); + + if (hasGenericArguments) { + wrapper.start++; + while (wrapper.start < genericSignature.length && (genericSignature[wrapper.start] != '>')) { + NdTypeArgument typeArgument = new NdTypeArgument(getNd(), typeSignature); + + switch (genericSignature[wrapper.start]) { + case '+': { + typeArgument.setWildcard(NdTypeArgument.WILDCARD_SUPER); + wrapper.start++; + break; + } + case '-': { + typeArgument.setWildcard(NdTypeArgument.WILDCARD_EXTENDS); + wrapper.start++; + break; + } + case '*': { + typeArgument.setWildcard(NdTypeArgument.WILDCARD_QUESTION); + wrapper.start++; + continue; + } + } + + NdTypeSignature nextSignature = createTypeSignature(wrapper, null); + typeArgument.setType(nextSignature); + } + + skipChar(wrapper, '>'); + } + result = typeSignature; + + if (parentTypeOrNull != null) { + typeSignature.setGenericDeclaringType(parentTypeOrNull); + } + + if (genericSignature[wrapper.start] == '.') { + // Don't check for a semicolon if we hit this branch since the recursive call to parseClassTypeSignature + // will do this + checkForSemicolon = false; + // Identifiers shouldn't start with '.' + skipChar(wrapper, '.'); + result = parseClassTypeSignature(typeSignature, wrapper); + } + } + + if (checkForSemicolon) { + skipChar(wrapper, ';'); + } + + return result; + } + + private NdTypeId createTypeIdFromFieldDescriptor(char[] typeName) { + if (typeName == null) { + return null; + } + return this.index.createTypeId(typeName); + } + + /** + * Creates a method ID given a method selector, method descriptor, and binary type name + */ + private NdMethodId createMethodId(char[] binaryTypeName, char[] methodSelector, char[] methodDescriptor) { + if (methodSelector == null || binaryTypeName == null || methodDescriptor == null) { + return null; + } + + char[] methodId = JavaNames.getMethodId(binaryTypeName, methodSelector, methodDescriptor); + return this.index.createMethodId(methodId); + } + + /** + * Creates a method ID given a method name (which is a method selector followed by a method descriptor. + */ + private NdMethodId createMethodId(char[] binaryTypeName, char[] methodName) { + if (methodName == null || binaryTypeName == null) { + return null; + } + + char[] methodId = JavaNames.getMethodId(binaryTypeName, methodName); + return this.index.createMethodId(methodId); + } + + private void initTypeAnnotation(NdTypeAnnotation annotation, IBinaryTypeAnnotation next) { + int[] typePath = next.getTypePath(); + if (typePath != null && typePath.length > 0) { + byte[] bytePath = new byte[typePath.length]; + for (int idx = 0; idx < bytePath.length; idx++) { + bytePath[idx] = (byte)typePath[idx]; + } + annotation.setPath(bytePath); + } + int targetType = next.getTargetType(); + annotation.setTargetType(targetType); + switch (targetType) { + case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER: + case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER: + annotation.setTargetInfo(next.getTypeParameterIndex()); + break; + case AnnotationTargetTypeConstants.CLASS_EXTENDS: + annotation.setTargetInfo(next.getSupertypeIndex()); + break; + case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER_BOUND: + case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER_BOUND: + annotation.setTargetInfo((byte)next.getTypeParameterIndex(), (byte)next.getBoundIndex()); + break; + case AnnotationTargetTypeConstants.FIELD: + case AnnotationTargetTypeConstants.METHOD_RETURN: + case AnnotationTargetTypeConstants.METHOD_RECEIVER: + break; + case AnnotationTargetTypeConstants.METHOD_FORMAL_PARAMETER : + annotation.setTargetInfo(next.getMethodFormalParameterIndex()); + break; + case AnnotationTargetTypeConstants.THROWS : + annotation.setTargetInfo(next.getThrowsTypeIndex()); + break; + + default: + throw new IllegalStateException("Target type not handled " + targetType); //$NON-NLS-1$ + } + initAnnotation(annotation, next.getAnnotation()); + } + + private void initAnnotation(NdAnnotation annotation, IBinaryAnnotation next) { + annotation.setType(createTypeIdFromBinaryName(next.getTypeName())); + + IBinaryElementValuePair[] pairs = next.getElementValuePairs(); + + if (pairs != null) { + for (IBinaryElementValuePair element : pairs) { + NdAnnotationValuePair nextPair = new NdAnnotationValuePair(annotation, element.getName()); + nextPair.setValue(createConstantFromMixedType(element.getValue())); + } + } + } + + private void logInfo(String string) { + if (ENABLE_LOGGING) { + Package.logInfo(string); + } + } + + private NdTypeId createTypeIdFromBinaryName(char[] binaryName) { + if (binaryName == null) { + return null; + } + + return this.index.createTypeId(JavaNames.binaryNameToFieldDescriptor(binaryName)); + } + + /** + * + * @param value + * accepts all values returned from {@link IBinaryElementValuePair#getValue()} + */ + public NdConstant createConstantFromMixedType(Object value) { + if (value instanceof Constant) { + Constant constant = (Constant) value; + + return NdConstant.create(getNd(), constant); + } else if (value instanceof ClassSignature) { + ClassSignature signature = (ClassSignature) value; + + char[] binaryName = JavaNames.binaryNameToFieldDescriptor(signature.getTypeName()); + NdTypeSignature typeId = this.index.createTypeId(binaryName); + return NdConstantClass.create(getNd(), typeId); + } else if (value instanceof IBinaryAnnotation) { + IBinaryAnnotation binaryAnnotation = (IBinaryAnnotation) value; + + NdAnnotationInConstant annotation = new NdAnnotationInConstant(getNd()); + initAnnotation(annotation, binaryAnnotation); + return NdConstantAnnotation.create(getNd(), annotation); + } else if (value instanceof Object[]) { + NdConstantArray result = new NdConstantArray(getNd()); + Object[] array = (Object[]) value; + + for (Object next : array) { + NdConstant nextConstant = createConstantFromMixedType(next); + nextConstant.setParent(result); + } + return result; + } else if (value instanceof EnumConstantSignature) { + EnumConstantSignature signature = (EnumConstantSignature) value; + + NdConstantEnum result = NdConstantEnum.create(createTypeIdFromBinaryName(signature.getTypeName()), + new String(signature.getEnumConstantName())); + + return result; + } + throw new IllegalStateException("Unknown constant type " + value.getClass().getName()); //$NON-NLS-1$ + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/GenericSignatures.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/GenericSignatures.java new file mode 100644 index 000000000..caca5a27f --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/GenericSignatures.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.indexer; + +import org.eclipse.jdt.internal.compiler.env.IBinaryField; +import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.lookup.SignatureWrapper; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; + +/** + * Contains static factory methods for constructing {@link SignatureWrapper} from various types. + */ +public class GenericSignatures { + private static final char[][] EMPTY_CHAR_ARRAY_ARRAY = new char[0][]; + + public static SignatureWrapper getGenericSignature(IBinaryMethod next) { + char[] signature = next.getGenericSignature(); + if (signature == null) { + signature = next.getMethodDescriptor(); + } + + return new SignatureWrapper(signature); + } + + /** + * Returns the generic signature for the given field. If the field has no generic signature, one is generated + * from the type's field descriptor. + */ + public static SignatureWrapper getGenericSignature(IBinaryType binaryType) { + char[][] interfaces = binaryType.getInterfaceNames(); + if (interfaces == null) { + interfaces = EMPTY_CHAR_ARRAY_ARRAY; + } + char[] genericSignature = binaryType.getGenericSignature(); + if (genericSignature == null) { + int startIndex = binaryType.getSuperclassName() != null ? 3 : 0; + char[][] toCatenate = new char[startIndex + (interfaces.length * 3)][]; + char[] prefix = new char[]{'L'}; + char[] suffix = new char[]{';'}; + + if (binaryType.getSuperclassName() != null) { + toCatenate[0] = prefix; + toCatenate[1] = binaryType.getSuperclassName(); + toCatenate[2] = suffix; + } + + for (int idx = 0; idx < interfaces.length; idx++) { + int catIndex = startIndex + idx * 3; + toCatenate[catIndex] = prefix; + toCatenate[catIndex + 1] = interfaces[idx]; + toCatenate[catIndex + 2] = suffix; + } + + genericSignature = CharArrayUtils.concat(toCatenate); + } + + SignatureWrapper signatureWrapper = new SignatureWrapper(genericSignature); + return signatureWrapper; + } + + /** + * Returns the generic signature for the given field. If the field has no generic signature, one is generated + * from the type's field descriptor. + */ + static SignatureWrapper getGenericSignatureFor(IBinaryField nextField) { + char[] signature = nextField.getGenericSignature(); + if (signature == null) { + signature = nextField.getTypeName(); + } + return new SignatureWrapper(signature); + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/HierarchicalASTVisitor.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/HierarchicalASTVisitor.java new file mode 100644 index 000000000..66f82de0e --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/HierarchicalASTVisitor.java @@ -0,0 +1,1139 @@ +/******************************************************************************* + * Copyright (c) 2000, 2016 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.indexer; + +import org.eclipse.jdt.core.dom.*; + +/** + * + * <p> + * This class provides a convenient behaviour-only extension mechanism for the ASTNode hierarchy. If + * you feel like you would like to add a method to the ASTNode hierarchy (or a subtree of the + * hierarchy), and you want to have different implementations of it at different points in the + * hierarchy, simply create a HierarchicalASTVisitor representing the new method and all its + * implementations, locating each implementation within the right visit(XX) method. If you wanted to + * add a method implementation to abstract class Foo, an ASTNode descendant, put your implementation + * in visit(Foo). This class will provide appropriate dispatch, just as if the method + * implementations had been added to the ASTNode hierarchy. + * </p> + * + * <p> + * <b>Details:<b> + * </p> + * + * <p> + * This class has a visit(XX node) method for every class (concrete or abstract) XX in the ASTNode + * hierarchy. In this class' default implementations of these methods, the method corresponding to a + * given ASTNode descendant class will call (and return the return value of) the visit(YY) method + * for it's superclass YY, with the exception of the visit(ASTNode) method which simply returns + * true, since ASTNode doesn't have a superclass that is within the ASTNode hierarchy. + * </p> + * + * <p> + * Because of this organization, when visit(XX) methods are overridden in a subclass, and the + * visitor is applied to a node, only the most specialized overridden method implementation for the + * node's type will be called, unless this most specialized method calls other visit methods (this + * is discouraged) or, (preferably) calls super.visit(XX node), (the reference type of the parameter + * must be XX) which will invoke this class' implementation of the method, which will, in turn, + * invoke the visit(YY) method corresponding to the superclass, YY. + * </p> + * + * <p> + * Thus, the dispatching behaviour achieved when HierarchicalASTVisitors' visit(XX) methods, + * corresponding to a particular concrete or abstract ASTNode descendant class, are overridden is + * exactly analogous to the dispatching behaviour obtained when method implementations are added to + * the same ASTNode descendant classes. + * </p> + * + */ +/* + * IMPORTANT NOTE: + * + * The structure and behaviour of this class is + * verified reflectively by + * org.eclipse.jdt.ui.tests.core.HierarchicalASTVisitorTest + * + */ +public abstract class HierarchicalASTVisitor extends ASTVisitor { +//TODO: check callers for handling of comments + +//---- Begin ASTNode Hierarchy ------------------------------------- + + /** + * Visits the given AST node. + * <p> + * The default implementation does nothing and return true. Subclasses may reimplement. + * </p> + * + * @param node the node to visit + * @return <code>true</code> if the children of this node should be visited, and + * <code>false</code> if the children of this node should be skipped + */ + public boolean visit(ASTNode node) { + return true; + } + + /** + * End of visit the given AST node. + * <p> + * The default implementation does nothing. Subclasses may reimplement. + * </p> + * + * @param node the node to visit + */ + public void endVisit(ASTNode node) { + // do nothing + } + + @Override + public boolean visit(AnonymousClassDeclaration node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(AnonymousClassDeclaration node) { + endVisit((ASTNode)node); + } + +//---- Begin BodyDeclaration Hierarchy --------------------------- + public boolean visit(BodyDeclaration node) { + return visit((ASTNode)node); + } + + public void endVisit(BodyDeclaration node) { + endVisit((ASTNode)node); + } + + //---- Begin AbstractTypeDeclaration Hierarchy --------------------------- + public boolean visit(AbstractTypeDeclaration node) { + return visit((BodyDeclaration)node); + } + + public void endVisit(AbstractTypeDeclaration node) { + endVisit((BodyDeclaration)node); + } + + @Override + public boolean visit(AnnotationTypeDeclaration node) { + return visit((AbstractTypeDeclaration)node); + } + + @Override + public void endVisit(AnnotationTypeDeclaration node) { + endVisit((AbstractTypeDeclaration)node); + } + + @Override + public boolean visit(EnumDeclaration node) { + return visit((AbstractTypeDeclaration)node); + } + + @Override + public void endVisit(EnumDeclaration node) { + endVisit((AbstractTypeDeclaration)node); + } + + @Override + public boolean visit(TypeDeclaration node) { + return visit((AbstractTypeDeclaration)node); + } + + @Override + public void endVisit(TypeDeclaration node) { + endVisit((AbstractTypeDeclaration)node); + } + + //---- End AbstractTypeDeclaration Hierarchy --------------------------- + + @Override + public boolean visit(AnnotationTypeMemberDeclaration node) { + return visit((BodyDeclaration)node); + } + + @Override + public void endVisit(AnnotationTypeMemberDeclaration node) { + endVisit((BodyDeclaration)node); + } + + @Override + public boolean visit(EnumConstantDeclaration node) { + return visit((BodyDeclaration)node); + } + + @Override + public void endVisit(EnumConstantDeclaration node) { + endVisit((BodyDeclaration)node); + } + + @Override + public boolean visit(FieldDeclaration node) { + return visit((BodyDeclaration)node); + } + + @Override + public void endVisit(FieldDeclaration node) { + endVisit((BodyDeclaration)node); + } + + @Override + public boolean visit(Initializer node) { + return visit((BodyDeclaration)node); + } + + @Override + public void endVisit(Initializer node) { + endVisit((BodyDeclaration)node); + } + + @Override + public boolean visit(MethodDeclaration node) { + return visit((BodyDeclaration)node); + } + + @Override + public void endVisit(MethodDeclaration node) { + endVisit((BodyDeclaration)node); + } + +//---- End BodyDeclaration Hierarchy ----------------------------- + + @Override + public boolean visit(CatchClause node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(CatchClause node) { + endVisit((ASTNode)node); + } + +//---- Begin Comment Hierarchy ---------------------------------- + public boolean visit(Comment node) { + return visit((ASTNode)node); + } + + public void endVisit(Comment node) { + endVisit((ASTNode)node); + } + + @Override + public boolean visit(BlockComment node) { + return visit((Comment)node); + } + + @Override + public void endVisit(BlockComment node) { + endVisit((Comment)node); + } + + @Override + public boolean visit(Javadoc node) { + return visit((Comment)node); + } + + @Override + public void endVisit(Javadoc node) { + endVisit((Comment)node); + } + + @Override + public boolean visit(LineComment node) { + return visit((Comment)node); + } + + @Override + public void endVisit(LineComment node) { + endVisit((Comment)node); + } + +//---- End Comment Hierarchy ----------------------------- + + @Override + public boolean visit(CompilationUnit node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(CompilationUnit node) { + endVisit((ASTNode)node); + } + + @Override + public boolean visit(Dimension node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(Dimension node) { + endVisit((ASTNode)node); + } + +//---- Begin Expression Hierarchy ---------------------------------- + public boolean visit(Expression node) { + return visit((ASTNode)node); + } + + public void endVisit(Expression node) { + endVisit((ASTNode)node); + } + + //---- Begin Annotation Hierarchy ---------------------------------- + public boolean visit(Annotation node) { + return visit((Expression)node); + } + + public void endVisit(Annotation node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(MarkerAnnotation node) { + return visit((Annotation)node); + } + + @Override + public void endVisit(MarkerAnnotation node) { + endVisit((Annotation)node); + } + + @Override + public boolean visit(NormalAnnotation node) { + return visit((Annotation)node); + } + + @Override + public void endVisit(NormalAnnotation node) { + endVisit((Annotation)node); + } + + @Override + public boolean visit(SingleMemberAnnotation node) { + return visit((Annotation)node); + } + + @Override + public void endVisit(SingleMemberAnnotation node) { + endVisit((Annotation)node); + } + + //---- End Annotation Hierarchy ----------------------------- + + @Override + public boolean visit(ArrayAccess node) { + return visit((Expression)node); + } + + @Override + public void endVisit(ArrayAccess node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(ArrayCreation node) { + return visit((Expression)node); + } + + @Override + public void endVisit(ArrayCreation node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(ArrayInitializer node) { + return visit((Expression)node); + } + + @Override + public void endVisit(ArrayInitializer node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(Assignment node) { + return visit((Expression)node); + } + + @Override + public void endVisit(Assignment node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(BooleanLiteral node) { + return visit((Expression)node); + } + + @Override + public void endVisit(BooleanLiteral node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(CastExpression node) { + return visit((Expression)node); + } + + @Override + public void endVisit(CastExpression node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(CharacterLiteral node) { + return visit((Expression)node); + } + + @Override + public void endVisit(CharacterLiteral node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(ClassInstanceCreation node) { + return visit((Expression)node); + } + + @Override + public void endVisit(ClassInstanceCreation node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(ConditionalExpression node) { + return visit((Expression)node); + } + + @Override + public void endVisit(ConditionalExpression node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(FieldAccess node) { + return visit((Expression)node); + } + + @Override + public void endVisit(FieldAccess node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(InfixExpression node) { + return visit((Expression)node); + } + + @Override + public void endVisit(InfixExpression node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(InstanceofExpression node) { + return visit((Expression)node); + } + + @Override + public void endVisit(InstanceofExpression node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(LambdaExpression node) { + return visit((Expression)node); + } + + @Override + public void endVisit(LambdaExpression node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(MethodInvocation node) { + return visit((Expression)node); + } + + @Override + public void endVisit(MethodInvocation node) { + endVisit((Expression)node); + } + + //---- Begin MethodReference Hierarchy ---------------------------------- + public boolean visit(MethodReference node) { + return visit((Expression)node); + } + + public void endVisit(MethodReference node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(CreationReference node) { + return visit((MethodReference)node); + } + + @Override + public void endVisit(CreationReference node) { + endVisit((MethodReference)node); + } + + @Override + public boolean visit(ExpressionMethodReference node) { + return visit((MethodReference)node); + } + + @Override + public void endVisit(ExpressionMethodReference node) { + endVisit((MethodReference)node); + } + + @Override + public boolean visit(SuperMethodReference node) { + return visit((MethodReference)node); + } + + @Override + public void endVisit(SuperMethodReference node) { + endVisit((MethodReference)node); + } + + @Override + public boolean visit(TypeMethodReference node) { + return visit((MethodReference)node); + } + + @Override + public void endVisit(TypeMethodReference node) { + endVisit((MethodReference)node); + } + + //---- End MethodReference Hierarchy ------------------------------------ + + //---- Begin Name Hierarchy ---------------------------------- + public boolean visit(Name node) { + return visit((Expression)node); + } + + public void endVisit(Name node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(QualifiedName node) { + return visit((Name)node); + } + + @Override + public void endVisit(QualifiedName node) { + endVisit((Name)node); + } + + @Override + public boolean visit(SimpleName node) { + return visit((Name)node); + } + + @Override + public void endVisit(SimpleName node) { + endVisit((Name)node); + } + + //---- End Name Hierarchy ------------------------------------ + + @Override + public boolean visit(NullLiteral node) { + return visit((Expression)node); + } + + @Override + public void endVisit(NullLiteral node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(NumberLiteral node) { + return visit((Expression)node); + } + + @Override + public void endVisit(NumberLiteral node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(ParenthesizedExpression node) { + return visit((Expression)node); + } + + @Override + public void endVisit(ParenthesizedExpression node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(PostfixExpression node) { + return visit((Expression)node); + } + + @Override + public void endVisit(PostfixExpression node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(PrefixExpression node) { + return visit((Expression)node); + } + + @Override + public void endVisit(PrefixExpression node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(StringLiteral node) { + return visit((Expression)node); + } + + @Override + public void endVisit(StringLiteral node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(SuperFieldAccess node) { + return visit((Expression)node); + } + + @Override + public void endVisit(SuperFieldAccess node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(SuperMethodInvocation node) { + return visit((Expression)node); + } + + @Override + public void endVisit(SuperMethodInvocation node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(ThisExpression node) { + return visit((Expression)node); + } + + @Override + public void endVisit(ThisExpression node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(TypeLiteral node) { + return visit((Expression)node); + } + + @Override + public void endVisit(TypeLiteral node) { + endVisit((Expression)node); + } + + @Override + public boolean visit(VariableDeclarationExpression node) { + return visit((Expression)node); + } + + @Override + public void endVisit(VariableDeclarationExpression node) { + endVisit((Expression)node); + } + + //---- End Expression Hierarchy ---------------------------------- + + @Override + public boolean visit(ImportDeclaration node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(ImportDeclaration node) { + endVisit((ASTNode)node); + } + + @Override + public boolean visit(MemberRef node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(MemberRef node) { + endVisit((ASTNode)node); + } + + @Override + public boolean visit(MemberValuePair node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(MemberValuePair node) { + endVisit((ASTNode)node); + } + + @Override + public boolean visit(MethodRef node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(MethodRef node) { + endVisit((ASTNode)node); + } + + @Override + public boolean visit(MethodRefParameter node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(MethodRefParameter node) { + endVisit((ASTNode)node); + } + + @Override + public boolean visit(Modifier node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(Modifier node) { + endVisit((ASTNode)node); + } + + @Override + public boolean visit(PackageDeclaration node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(PackageDeclaration node) { + endVisit((ASTNode)node); + } + +//---- Begin Statement Hierarchy --------------------------------- + public boolean visit(Statement node) { + return visit((ASTNode)node); + } + + public void endVisit(Statement node) { + endVisit((ASTNode)node); + } + + + @Override + public boolean visit(AssertStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(AssertStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(Block node) { + return visit((Statement)node); + } + + @Override + public void endVisit(Block node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(BreakStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(BreakStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(ConstructorInvocation node) { + return visit((Statement)node); + } + + @Override + public void endVisit(ConstructorInvocation node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(ContinueStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(ContinueStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(DoStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(DoStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(EmptyStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(EmptyStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(EnhancedForStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(EnhancedForStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(ExpressionStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(ExpressionStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(ForStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(ForStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(IfStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(IfStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(LabeledStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(LabeledStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(ReturnStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(ReturnStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(SuperConstructorInvocation node) { + return visit((Statement)node); + } + + @Override + public void endVisit(SuperConstructorInvocation node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(SwitchCase node) { + return visit((Statement)node); + } + + @Override + public void endVisit(SwitchCase node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(SwitchStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(SwitchStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(SynchronizedStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(SynchronizedStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(ThrowStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(ThrowStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(TryStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(TryStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(TypeDeclarationStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(TypeDeclarationStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(VariableDeclarationStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(VariableDeclarationStatement node) { + endVisit((Statement)node); + } + + @Override + public boolean visit(WhileStatement node) { + return visit((Statement)node); + } + + @Override + public void endVisit(WhileStatement node) { + endVisit((Statement)node); + } + +//---- End Statement Hierarchy ---------------------------------- + + @Override + public boolean visit(TagElement node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(TagElement node) { + endVisit((ASTNode)node); + } + + @Override + public boolean visit(TextElement node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(TextElement node) { + endVisit((ASTNode)node); + } + + +//---- Begin Type Hierarchy -------------------------------------- + public boolean visit(Type node) { + return visit((ASTNode)node); + } + + public void endVisit(Type node) { + endVisit((ASTNode)node); + } + +//---- Begin Annotatable Type Hierarchy -------------------------------------- + public boolean visit(AnnotatableType node) { + return visit((Type)node); + } + + public void endVisit(AnnotatableType node) { + endVisit((Type)node); + } + + @Override + public boolean visit(NameQualifiedType node) { + return visit((AnnotatableType)node); + } + + @Override + public void endVisit(NameQualifiedType node) { + endVisit((AnnotatableType)node); + } + + @Override + public boolean visit(PrimitiveType node) { + return visit((AnnotatableType)node); + } + + @Override + public void endVisit(PrimitiveType node) { + endVisit((AnnotatableType)node); + } + + @Override + public boolean visit(QualifiedType node) { + return visit((AnnotatableType)node); + } + + @Override + public void endVisit(QualifiedType node) { + endVisit((AnnotatableType)node); + } + + @Override + public boolean visit(SimpleType node) { + return visit((AnnotatableType)node); + } + + @Override + public void endVisit(SimpleType node) { + endVisit((AnnotatableType)node); + } + + @Override + public boolean visit(WildcardType node) { + return visit((AnnotatableType)node); + } + + @Override + public void endVisit(WildcardType node) { + endVisit((AnnotatableType)node); + } +//---- End Annotatable Type Hierarchy -------------------------------------- + + @Override + public boolean visit(ArrayType node) { + return visit((Type)node); + } + + @Override + public void endVisit(ArrayType node) { + endVisit((Type)node); + } + + @Override + public boolean visit(IntersectionType node) { + return visit((Type)node); + } + + @Override + public void endVisit(IntersectionType node) { + endVisit((Type)node); + } + + @Override + public boolean visit(ParameterizedType node) { + return visit((Type)node); + } + + @Override + public void endVisit(ParameterizedType node) { + endVisit((Type)node); + } + + @Override + public boolean visit(UnionType node) { + return visit((Type)node); + } + + @Override + public void endVisit(UnionType node) { + endVisit((Type)node); + } + +//---- End Type Hierarchy ---------------------------------------- + + @Override + public boolean visit(TypeParameter node) { + return visit((ASTNode)node); + } + + @Override + public void endVisit(TypeParameter node) { + endVisit((ASTNode)node); + } + + +//---- Begin VariableDeclaration Hierarchy --------------------------- + public boolean visit(VariableDeclaration node) { + return visit((ASTNode)node); + } + + public void endVisit(VariableDeclaration node) { + endVisit((ASTNode)node); + } + + @Override + public boolean visit(SingleVariableDeclaration node) { + return visit((VariableDeclaration)node); + } + + @Override + public void endVisit(SingleVariableDeclaration node) { + endVisit((VariableDeclaration)node); + } + + @Override + public boolean visit(VariableDeclarationFragment node) { + return visit((VariableDeclaration)node); + } + + @Override + public void endVisit(VariableDeclarationFragment node) { + endVisit((VariableDeclaration)node); + } + +//---- End VariableDeclaration Hierarchy ----------------------------- +//---- End ASTNode Hierarchy ----------------------------------------- +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/IndexTester.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/IndexTester.java new file mode 100644 index 000000000..eba1d711b --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/IndexTester.java @@ -0,0 +1,454 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.indexer; + +import java.util.Arrays; +import java.util.Objects; + +import org.eclipse.jdt.internal.compiler.env.ClassSignature; +import org.eclipse.jdt.internal.compiler.env.EnumConstantSignature; +import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; +import org.eclipse.jdt.internal.compiler.env.IBinaryElementValuePair; +import org.eclipse.jdt.internal.compiler.env.IBinaryField; +import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.impl.DoubleConstant; +import org.eclipse.jdt.internal.compiler.impl.FloatConstant; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; + +public class IndexTester { + + private static final class TypeAnnotationWrapper { + private IBinaryTypeAnnotation annotation; + + public TypeAnnotationWrapper(IBinaryTypeAnnotation next) { + this.annotation = next; + } + + @Override + public int hashCode() { + int hashCode; + int[] typePath = this.annotation.getTypePath(); + + hashCode = Arrays.hashCode(typePath); + hashCode = hashCode * 31 + this.annotation.getTargetType(); + hashCode = hashCode * 31 + this.annotation.getTypeParameterIndex(); + return hashCode; + } + + @Override + public String toString() { + return this.annotation.toString(); + } + + @Override + public boolean equals(Object obj) { + if (obj.getClass() != TypeAnnotationWrapper.class) { + return false; + } + + TypeAnnotationWrapper wrapper = (TypeAnnotationWrapper) obj; + IBinaryTypeAnnotation otherAnnotation = wrapper.annotation; + + int[] typePath = this.annotation.getTypePath(); + int[] otherTypePath = otherAnnotation.getTypePath(); + + if (!Arrays.equals(typePath, otherTypePath)) { + return false; + } + + if (this.annotation.getTargetType() != otherAnnotation.getTargetType()) { + return false; + } + + if (this.annotation.getBoundIndex() != otherAnnotation.getBoundIndex()) { + return false; + } + + if (this.annotation.getMethodFormalParameterIndex() != otherAnnotation.getMethodFormalParameterIndex()) { + return false; + } + + if (this.annotation.getSupertypeIndex() != otherAnnotation.getSupertypeIndex()) { + return false; + } + + if (this.annotation.getThrowsTypeIndex() != otherAnnotation.getThrowsTypeIndex()) { + return false; + } + + if (this.annotation.getTypeParameterIndex() != otherAnnotation.getTypeParameterIndex()) { + return false; + } + + return IndexTester.isEqual(this.annotation.getAnnotation(), otherAnnotation.getAnnotation()); + } + } + + public static void testType(IBinaryType expected, IBinaryType actual) { + String contextPrefix = safeString(actual.getName()); + + IBinaryTypeAnnotation[] expectedTypeAnnotations = expected.getTypeAnnotations(); + IBinaryTypeAnnotation[] actualTypeAnnotations = actual.getTypeAnnotations(); + + compareTypeAnnotations(contextPrefix, expectedTypeAnnotations, actualTypeAnnotations); + + IBinaryAnnotation[] expectedBinaryAnnotations = expected.getAnnotations(); + IBinaryAnnotation[] actualBinaryAnnotations = actual.getAnnotations(); + + compareAnnotations(contextPrefix, expectedBinaryAnnotations, actualBinaryAnnotations); + + compareGenericSignatures(contextPrefix + ": The generic signature did not match", //$NON-NLS-1$ + expected.getGenericSignature(), actual.getGenericSignature()); + + assertEquals(contextPrefix + ": The enclosing method name did not match", expected.getEnclosingMethod(), //$NON-NLS-1$ + actual.getEnclosingMethod()); + assertEquals(contextPrefix + ": The enclosing method name did not match", expected.getEnclosingTypeName(), //$NON-NLS-1$ + actual.getEnclosingTypeName()); + + IBinaryField[] expectedFields = expected.getFields(); + IBinaryField[] actualFields = actual.getFields(); + + if (expectedFields != actualFields) { + if (expectedFields == null && actualFields != null) { + throw new IllegalStateException(contextPrefix + "Expected fields was null -- actual fields were not"); //$NON-NLS-1$ + } + if (expectedFields.length != actualFields.length) { + throw new IllegalStateException( + contextPrefix + "The expected and actual number of fields did not match"); //$NON-NLS-1$ + } + + for (int fieldIdx = 0; fieldIdx < actualFields.length; fieldIdx++) { + compareFields(contextPrefix, expectedFields[fieldIdx], actualFields[fieldIdx]); + } + } + + // Commented this out because the "expected" values often appear to be invalid paths when the "actual" + // ones are correct. + assertEquals("The file name did not match", expected.getFileName(), actual.getFileName()); //$NON-NLS-1$ + assertEquals("The interface names did not match", expected.getInterfaceNames(), actual.getInterfaceNames()); //$NON-NLS-1$ + + // Member types are not expected to match during indexing since the index uses discovered cross-references, + // not the member types encoded in the .class file. + // expected.getMemberTypes() != actual.getMemberTypes() + + IBinaryMethod[] expectedMethods = expected.getMethods(); + IBinaryMethod[] actualMethods = actual.getMethods(); + + if (expectedMethods != actualMethods) { + if (expectedMethods == null || actualMethods == null) { + throw new IllegalStateException("One of the method arrays was null"); //$NON-NLS-1$ + } + + if (expectedMethods.length != actualMethods.length) { + throw new IllegalStateException("The number of methods didn't match"); //$NON-NLS-1$ + } + + for (int i = 0; i < actualMethods.length; i++) { + IBinaryMethod actualMethod = actualMethods[i]; + IBinaryMethod expectedMethod = expectedMethods[i]; + + compareMethods(contextPrefix, expectedMethod, actualMethod); + } + } + + assertEquals("The missing type names did not match", expected.getMissingTypeNames(), //$NON-NLS-1$ + actual.getMissingTypeNames()); + assertEquals("The modifiers don't match", expected.getModifiers(), actual.getModifiers()); //$NON-NLS-1$ + assertEquals("The names don't match.", expected.getName(), actual.getName()); //$NON-NLS-1$ + assertEquals("The source name doesn't match", expected.getSourceName(), actual.getSourceName()); //$NON-NLS-1$ + assertEquals("The superclass name doesn't match", expected.getSuperclassName(), actual.getSuperclassName()); //$NON-NLS-1$ + assertEquals("The tag bits don't match.", expected.getTagBits(), actual.getTagBits()); //$NON-NLS-1$ + + compareTypeAnnotations(contextPrefix, expected.getTypeAnnotations(), actual.getTypeAnnotations()); + } + + private static <T> void assertEquals(String message, T o1, T o2) { + if (!isEqual(o1, o2)) { + throw new IllegalStateException(message + ": expected = " + getString(o1) + ", actual = " + getString(o2)); //$NON-NLS-1$//$NON-NLS-2$ + } + } + + private static String getString(Object object) { + if (object instanceof char[]) { + char[] charArray = (char[]) object; + + return new String(charArray); + } + return object.toString(); + } + + static <T> boolean isEqual(T o1, T o2) { + if (o1 == o2) { + return true; + } + + if (o1 == null || o2 == null) { + return false; + } + + if (o1 instanceof ClassSignature) { + if (!(o2 instanceof ClassSignature)) { + return false; + } + + ClassSignature sig1 = (ClassSignature) o1; + ClassSignature sig2 = (ClassSignature) o2; + + return Arrays.equals(sig1.getTypeName(), sig2.getTypeName()); + } + + if (o1 instanceof IBinaryAnnotation) { + IBinaryAnnotation binaryAnnotation = (IBinaryAnnotation) o1; + IBinaryAnnotation otherBinaryAnnotation = (IBinaryAnnotation) o2; + IBinaryElementValuePair[] elementValuePairs = binaryAnnotation.getElementValuePairs(); + IBinaryElementValuePair[] otherElementValuePairs = otherBinaryAnnotation.getElementValuePairs(); + + if (elementValuePairs.length != otherElementValuePairs.length) { + return false; + } + + for (int idx = 0; idx < elementValuePairs.length; idx++) { + IBinaryElementValuePair next = elementValuePairs[idx]; + IBinaryElementValuePair otherNext = otherElementValuePairs[idx]; + + char[] nextName = next.getName(); + char[] otherNextName = otherNext.getName(); + + if (!Arrays.equals(nextName, otherNextName)) { + return false; + } + + if (!isEqual(next.getValue(), otherNext.getValue())) { + return false; + } + } + return true; + } + + if (o1 instanceof IBinaryTypeAnnotation) { + IBinaryTypeAnnotation binaryAnnotation = (IBinaryTypeAnnotation)o1; + IBinaryTypeAnnotation otherBinaryAnnotation = (IBinaryTypeAnnotation)o2; + + return new TypeAnnotationWrapper(binaryAnnotation).equals(new TypeAnnotationWrapper(otherBinaryAnnotation)); + } + + if (o1 instanceof Constant) { + if (!(o2 instanceof Constant)) { + return false; + } + + if (o1 instanceof DoubleConstant && o2 instanceof DoubleConstant) { + DoubleConstant d1 = (DoubleConstant) o1; + DoubleConstant d2 = (DoubleConstant) o2; + + if (Double.isNaN(d1.doubleValue()) && Double.isNaN(d2.doubleValue())) { + return true; + } + } + + if (o1 instanceof FloatConstant && o2 instanceof FloatConstant) { + FloatConstant d1 = (FloatConstant) o1; + FloatConstant d2 = (FloatConstant) o2; + + if (Float.isNaN(d1.floatValue()) && Float.isNaN(d2.floatValue())) { + return true; + } + } + + Constant const1 = (Constant) o1; + Constant const2 = (Constant) o2; + + return const1.hasSameValue(const2); + } + + if (o1 instanceof EnumConstantSignature) { + if (!(o2 instanceof EnumConstantSignature)) { + return false; + } + + EnumConstantSignature enum1 = (EnumConstantSignature) o1; + EnumConstantSignature enum2 = (EnumConstantSignature) o2; + + return Arrays.equals(enum1.getEnumConstantName(), enum2.getEnumConstantName()) + && Arrays.equals(enum1.getTypeName(), enum2.getTypeName()); + } + + if (o1 instanceof char[]) { + char[] c1 = (char[]) o1; + char[] c2 = (char[]) o2; + + return CharArrayUtils.equals(c1, c2); + } + + if (o1 instanceof char[][]) { + char[][] c1 = (char[][]) o1; + char[][] c2 = (char[][]) o2; + + return CharArrayUtils.equals(c1, c2); + } + + if (o1 instanceof char[][][]) { + char[][][] c1 = (char[][][]) o1; + char[][][] c2 = (char[][][]) o2; + + if (c1.length != c2.length) { + return false; + } + + for (int i = 0; i < c1.length; i++) { + if (!isEqual(c1[i], c2[i])) { + return false; + } + } + return true; + } + + if (o1 instanceof Object[]) { + Object[] a1 = (Object[]) o1; + Object[] a2 = (Object[]) o2; + + if (a1.length != a2.length) { + return false; + } + + for (int idx = 0; idx < a1.length; idx++) { + if (!isEqual(a1[idx], a2[idx])) { + return false; + } + } + return true; + } + + return Objects.equals(o1, o2); + } + + private static void compareMethods(String contextPrefix, IBinaryMethod expectedMethod, IBinaryMethod actualMethod) { + contextPrefix = contextPrefix + "." + safeString(expectedMethod.getSelector()); //$NON-NLS-1$ + compareAnnotations(contextPrefix, expectedMethod.getAnnotations(), actualMethod.getAnnotations()); + + assertEquals(contextPrefix + ": The argument names didn't match.", expectedMethod.getArgumentNames(), //$NON-NLS-1$ + actualMethod.getArgumentNames()); + + assertEquals(contextPrefix + ": The default values didn't match.", expectedMethod.getDefaultValue(), //$NON-NLS-1$ + actualMethod.getDefaultValue()); + + assertEquals(contextPrefix + ": The exception type names did not match.", //$NON-NLS-1$ + expectedMethod.getExceptionTypeNames(), actualMethod.getExceptionTypeNames()); + + compareGenericSignatures(contextPrefix + ": The method's generic signature did not match", //$NON-NLS-1$ + expectedMethod.getGenericSignature(), actualMethod.getGenericSignature()); + + assertEquals(contextPrefix + ": The method descriptors did not match.", expectedMethod.getMethodDescriptor(), //$NON-NLS-1$ + actualMethod.getMethodDescriptor()); + assertEquals(contextPrefix + ": The modifiers didn't match.", expectedMethod.getModifiers(), //$NON-NLS-1$ + actualMethod.getModifiers()); + + char[] classFileName = "".toCharArray(); //$NON-NLS-1$ + int minAnnotatedParameters = Math.min(expectedMethod.getAnnotatedParametersCount(), + actualMethod.getAnnotatedParametersCount()); + for (int idx = 0; idx < minAnnotatedParameters; idx++) { + compareAnnotations(contextPrefix, expectedMethod.getParameterAnnotations(idx, classFileName), + actualMethod.getParameterAnnotations(idx, classFileName)); + } + for (int idx = minAnnotatedParameters; idx < expectedMethod.getAnnotatedParametersCount(); idx++) { + compareAnnotations(contextPrefix, new IBinaryAnnotation[0], + expectedMethod.getParameterAnnotations(idx, classFileName)); + } + for (int idx = minAnnotatedParameters; idx < actualMethod.getAnnotatedParametersCount(); idx++) { + compareAnnotations(contextPrefix, new IBinaryAnnotation[0], + actualMethod.getParameterAnnotations(idx, classFileName)); + } + + assertEquals(contextPrefix + ": The selectors did not match", expectedMethod.getSelector(), //$NON-NLS-1$ + actualMethod.getSelector()); + assertEquals(contextPrefix + ": The tag bits did not match", expectedMethod.getTagBits(), //$NON-NLS-1$ + actualMethod.getTagBits()); + + compareTypeAnnotations(contextPrefix, expectedMethod.getTypeAnnotations(), actualMethod.getTypeAnnotations()); + } + + /** + * The index always provides complete generic signatures whereas some or all of the generic signature is optional + * for class files, so the generic signatures are expected to differ in certain situations. + */ + private static void compareGenericSignatures(String message, char[] expected, char[] actual) { + assertEquals(message, expected, actual); + } + + private static void compareTypeAnnotations(String contextPrefix, IBinaryTypeAnnotation[] expectedTypeAnnotations, + IBinaryTypeAnnotation[] actualTypeAnnotations) { + if (expectedTypeAnnotations == null) { + if (actualTypeAnnotations != null) { + throw new IllegalStateException(contextPrefix + ": Expected null for the annotation list but found: " //$NON-NLS-1$ + + actualTypeAnnotations.toString()); + } + return; + } + + assertEquals(contextPrefix + ": The expected and actual number of type annotations did not match", //$NON-NLS-1$ + expectedTypeAnnotations.length, actualTypeAnnotations.length); + + for (int idx = 0; idx < expectedTypeAnnotations.length; idx++) { + assertEquals(contextPrefix + ": Type annotation number " + idx + " did not match", //$NON-NLS-1$//$NON-NLS-2$ + expectedTypeAnnotations[idx], actualTypeAnnotations[idx]); + } + } + + private static void compareAnnotations(String contextPrefix, IBinaryAnnotation[] expectedBinaryAnnotations, + IBinaryAnnotation[] actualBinaryAnnotations) { + if (expectedBinaryAnnotations == null || expectedBinaryAnnotations.length == 0) { + if (actualBinaryAnnotations != null && actualBinaryAnnotations.length != 0) { + throw new IllegalStateException(contextPrefix + ": Expected null for the binary annotations"); //$NON-NLS-1$ + } else { + return; + } + } + if (actualBinaryAnnotations == null) { + throw new IllegalStateException(contextPrefix + ": Actual null for the binary annotations"); //$NON-NLS-1$ + } + if (expectedBinaryAnnotations.length != actualBinaryAnnotations.length) { + throw new IllegalStateException( + contextPrefix + ": The expected and actual number of annotations differed. Expected: " //$NON-NLS-1$ + + expectedBinaryAnnotations.length + ", actual: " + actualBinaryAnnotations.length); //$NON-NLS-1$ + } + + for (int idx = 0; idx < expectedBinaryAnnotations.length; idx++) { + if (!isEqual(expectedBinaryAnnotations[idx], actualBinaryAnnotations[idx])) { + throw new IllegalStateException(contextPrefix + ": An annotation had an unexpected value"); //$NON-NLS-1$ + } + } + } + + private static void compareFields(String contextPrefix, IBinaryField field1, IBinaryField field2) { + contextPrefix = contextPrefix + "." + safeString(field1.getName()); //$NON-NLS-1$ + compareAnnotations(contextPrefix, field1.getAnnotations(), field2.getAnnotations()); + assertEquals(contextPrefix + ": Constants not equal", field1.getConstant(), field2.getConstant()); //$NON-NLS-1$ + compareGenericSignatures(contextPrefix + ": The generic signature did not match", field1.getGenericSignature(), //$NON-NLS-1$ + field2.getGenericSignature()); + assertEquals(contextPrefix + ": The modifiers did not match", field1.getModifiers(), field2.getModifiers()); //$NON-NLS-1$ + assertEquals(contextPrefix + ": The tag bits did not match", field1.getTagBits(), field2.getTagBits()); //$NON-NLS-1$ + assertEquals(contextPrefix + ": The names did not match", field1.getName(), field2.getName()); //$NON-NLS-1$ + + compareTypeAnnotations(contextPrefix, field1.getTypeAnnotations(), field2.getTypeAnnotations()); + assertEquals(contextPrefix + ": The type names did not match", field1.getTypeName(), field2.getTypeName()); //$NON-NLS-1$ + } + + private static String safeString(char[] name) { + if (name == null) { + return "<unnamed>"; //$NON-NLS-1$ + } + return new String(name); + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/Indexer.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/Indexer.java new file mode 100644 index 000000000..b8f497899 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/Indexer.java @@ -0,0 +1,1069 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.indexer; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.ICoreRunnable; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jdt.core.IClassFile; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaElementDelta; +import org.eclipse.jdt.core.IJavaModelStatusConstants; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.IParent; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.env.IDependent; +import org.eclipse.jdt.internal.compiler.util.SuffixConstants; +import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; +import org.eclipse.jdt.internal.core.JavaElementDelta; +import org.eclipse.jdt.internal.core.JavaModel; +import org.eclipse.jdt.internal.core.JavaModelManager; +import org.eclipse.jdt.internal.core.nd.IReader; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.java.FileFingerprint; +import org.eclipse.jdt.internal.core.nd.java.FileFingerprint.FingerprintTestResult; +import org.eclipse.jdt.internal.core.nd.java.JavaIndex; +import org.eclipse.jdt.internal.core.nd.java.JavaNames; +import org.eclipse.jdt.internal.core.nd.java.NdBinding; +import org.eclipse.jdt.internal.core.nd.java.NdResourceFile; +import org.eclipse.jdt.internal.core.nd.java.NdType; +import org.eclipse.jdt.internal.core.nd.java.NdTypeId; +import org.eclipse.jdt.internal.core.nd.java.NdWorkspaceLocation; +import org.eclipse.jdt.internal.core.nd.java.TypeRef; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeDescriptor; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory; +import org.eclipse.jdt.internal.core.nd.java.model.IndexBinaryType; +import org.eclipse.jdt.internal.core.search.processing.IJob; + +public final class Indexer { + private Nd nd; + private IWorkspaceRoot root; + + private static Indexer indexer; + public static boolean DEBUG; + public static boolean DEBUG_ALLOCATIONS; + public static boolean DEBUG_TIMING; + public static boolean DEBUG_INSERTIONS; + public static boolean DEBUG_SELFTEST; + + /** + * True iff automatic reindexing (that is, the {@link #rescanAll()} method) is disabled + * Synchronize on {@link #automaticIndexingMutex} while accessing. + */ + private boolean enableAutomaticIndexing = true; + /** + * True iff any code tried to schedule reindexing while automatic reindexing was disabled. + * Synchronize on {@link #automaticIndexingMutex} while accessing. + */ + private boolean indexerDirtiedWhileDisabled = false; + private final Object automaticIndexingMutex = new Object(); + + /** + * Enable this to index the content of output folders, in cases where that content exists and + * is up-to-date. This is much faster than indexing source files directly. + */ + public static boolean EXPERIMENTAL_INDEX_OUTPUT_FOLDERS; + private static final Object mutex = new Object(); + private static final long MS_TO_NS = 1000000; + + private Object listenersMutex = new Object(); + /** + * Listener list. Copy-on-write. Synchronize on "listenersMutex" before accessing. + */ + private Set<Listener> listeners = Collections.newSetFromMap(new WeakHashMap<Listener, Boolean>()); + + private Job rescanJob = Job.create(Messages.Indexer_updating_index_job_name, new ICoreRunnable() { + @Override + public void run(IProgressMonitor monitor) throws CoreException { + rescan(monitor); + } + }); + + public static interface Listener { + void consume(IndexerEvent event); + } + + public static Indexer getInstance() { + synchronized (mutex) { + if (indexer == null) { + indexer = new Indexer(JavaIndex.getGlobalNd(), ResourcesPlugin.getWorkspace().getRoot()); + } + return indexer; + } + } + + /** + * Enables or disables the "rescanAll" method. When set to false, rescanAll does nothing + * and indexing will only be triggered when invoking {@link #waitForIndex}. + * <p> + * Normally the indexer runs automatically and asynchronously when resource changes occur. + * However, if this variable is set to false the indexer only runs when someone invokes + * {@link #waitForIndex(IProgressMonitor)}. This can be used to eliminate race conditions + * when running the unit tests, since indexing will not occur unless it is triggered + * explicitly. + * <p> + * Synchronize on {@link #automaticIndexingMutex} before accessing. + */ + public void enableAutomaticIndexing(boolean enabled) { + boolean runRescan = false; + synchronized (this.automaticIndexingMutex) { + if (this.enableAutomaticIndexing == enabled) { + return; + } + this.enableAutomaticIndexing = enabled; + if (enabled && this.indexerDirtiedWhileDisabled) { + runRescan = true; + } + } + + if (runRescan) { + // Force a rescan when re-enabling automatic indexing since we may have missed an update + this.rescanJob.schedule(); + } + + if (!enabled) { + // Wait for any existing indexing operations to finish when disabling automatic indexing since + // we only want explicitly-triggered indexing operations to run after the method returns + try { + this.rescanJob.join(0, null); + } catch (OperationCanceledException | InterruptedException e) { + // Don't care + } + } + } + + /** + * Amount of time (milliseconds) unreferenced files are allowed to sit in the index before they are discarded. + * Making this too short will cause some operations (classpath modifications, closing/reopening projects, etc.) + * to become more expensive. Making this too long will waste space in the database. + * <p> + * The value of this is stored in the JDT core preference called "garbageCleanupTimeoutMs". The default value + * is 3 days. + */ + private static long getGarbageCleanupTimeout() { + return Platform.getPreferencesService().getLong(JavaCore.PLUGIN_ID, "garbageCleanupTimeoutMs", //$NON-NLS-1$ + 1000 * 60 * 60 * 24 * 3, + null); + } + + /** + * Amount of time (milliseconds) before we update the "used" timestamp on a file in the index. We don't update + * the timestamps every update since doing so would be unnecessarily inefficient... but if any of the timestamps + * is older than this update period, we refresh it. + */ + private static long getUsageTimestampUpdatePeriod() { + return getGarbageCleanupTimeout() / 4; + } + + public void rescan(IProgressMonitor monitor) throws CoreException { + SubMonitor subMonitor = SubMonitor.convert(monitor, 100); + + synchronized (this.automaticIndexingMutex) { + this.indexerDirtiedWhileDisabled = false; + } + + long startTimeNs = System.nanoTime(); + long currentTimeMs = System.currentTimeMillis(); + if (DEBUG) { + Package.logInfo("Indexer running rescan"); //$NON-NLS-1$ + } + + // Gather all the IPackageFragmentRoots in the workspace + List<IJavaElement> unfilteredIndexables = getAllIndexableObjectsInWorkspace(subMonitor.split(3)); + + int totalIndexables = unfilteredIndexables.size(); + // Remove all duplicate indexables (jars which are referenced by more than one project) + Map<IPath, List<IJavaElement>> allIndexables = removeDuplicatePaths(unfilteredIndexables); + + long startGarbageCollectionNs = System.nanoTime(); + + // Remove all files in the index which aren't referenced in the workspace + int gcFiles = cleanGarbage(currentTimeMs, allIndexables.keySet(), subMonitor.split(4)); + + long startFingerprintTestNs = System.nanoTime(); + + Map<IPath, FingerprintTestResult> fingerprints = testFingerprints(allIndexables.keySet(), subMonitor.split(7)); + Set<IPath> indexablesWithChanges = new HashSet<>(getIndexablesThatHaveChanged(allIndexables.keySet(), fingerprints)); + + long startIndexingNs = System.nanoTime(); + + int classesIndexed = 0; + SubMonitor loopMonitor = subMonitor.split(80).setWorkRemaining(indexablesWithChanges.size()); + for (IPath next : indexablesWithChanges) { + classesIndexed += rescanArchive(currentTimeMs, next, allIndexables.get(next), fingerprints.get(next).getNewFingerprint(), + loopMonitor.split(1)); + } + + long endIndexingNs = System.nanoTime(); + + Map<IPath, List<IJavaElement>> pathsToUpdate = new HashMap<>(); + + for (IPath next : allIndexables.keySet()) { + if (!indexablesWithChanges.contains(next)) { + pathsToUpdate.put(next, allIndexables.get(next)); + continue; + } + } + + updateResourceMappings(pathsToUpdate, subMonitor.split(5)); + + // Flush the database to disk + this.nd.acquireWriteLock(subMonitor.split(4)); + try { + this.nd.getDB().flush(); + } finally { + this.nd.releaseWriteLock(); + } + + fireDelta(indexablesWithChanges, subMonitor.split(1)); + + if (DEBUG) { + Package.logInfo("Rescan finished"); //$NON-NLS-1$ + } + + long endResourceMappingNs = System.nanoTime(); + + long fingerprintTimeMs = (startIndexingNs - startFingerprintTestNs) / MS_TO_NS; + long locateIndexablesTimeMs = (startGarbageCollectionNs - startTimeNs) / MS_TO_NS; + long garbageCollectionMs = (startFingerprintTestNs - startGarbageCollectionNs) / MS_TO_NS; + long indexingTimeMs = (endIndexingNs - startIndexingNs) / MS_TO_NS; + long resourceMappingTimeMs = (endResourceMappingNs - endIndexingNs) / MS_TO_NS; + + double averageGcTimeMs = gcFiles == 0 ? 0 : (double)garbageCollectionMs / (double)gcFiles; + double averageIndexTimeMs = classesIndexed == 0 ? 0 : (double)indexingTimeMs / (double)classesIndexed; + double averageFingerprintTimeMs = allIndexables.size() == 0 ? 0 : (double)fingerprintTimeMs / (double)allIndexables.size(); + double averageResourceMappingMs = pathsToUpdate.size() == 0 ? 0 : (double)resourceMappingTimeMs / (double)pathsToUpdate.size(); + + if (DEBUG_TIMING) { + Package.logInfo( + "Indexing done.\n" //$NON-NLS-1$ + + " Located " + totalIndexables + " indexables in " + locateIndexablesTimeMs + "ms\n" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + " Collected garbage from " + gcFiles + " files in " + garbageCollectionMs + "ms, average time = " + averageGcTimeMs + "ms\n" //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ + + " Tested " + allIndexables.size() + " fingerprints in " + fingerprintTimeMs + "ms, average time = " + averageFingerprintTimeMs + "ms\n" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + + " Indexed " + classesIndexed + " classes in " + indexingTimeMs + "ms, average time = " + averageIndexTimeMs + "ms\n" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + + " Updated " + pathsToUpdate.size() + " paths in " + resourceMappingTimeMs + "ms, average time = " + averageResourceMappingMs + "ms\n"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ + } + + if (DEBUG_ALLOCATIONS) { + try (IReader readLock = this.nd.acquireReadLock()) { + this.nd.getDB().reportFreeBlocks(); + this.nd.getDB().getMemoryStats().printMemoryStats(this.nd.getTypeRegistry()); + } + } + } + + private void fireDelta(Set<IPath> indexablesWithChanges, IProgressMonitor monitor) { + SubMonitor subMonitor = SubMonitor.convert(monitor, 1); + IProject[] projects = this.root.getProjects(); + + List<IProject> projectsToScan = new ArrayList<>(); + + for (IProject next : projects) { + if (next.isOpen()) { + projectsToScan.add(next); + } + } + JavaModel model = JavaModelManager.getJavaModelManager().getJavaModel(); + boolean hasChanges = false; + JavaElementDelta delta = new JavaElementDelta(model); + SubMonitor projectLoopMonitor = subMonitor.split(1).setWorkRemaining(projectsToScan.size()); + for (IProject project : projectsToScan) { + projectLoopMonitor.split(1); + try { + if (project.isOpen() && project.isNatureEnabled(JavaCore.NATURE_ID)) { + IJavaProject javaProject = JavaCore.create(project); + + IPackageFragmentRoot[] roots = javaProject.getAllPackageFragmentRoots(); + + for (IPackageFragmentRoot next : roots) { + if (next.isArchive()) { + IPath location = JavaIndex.getLocationForElement(next); + + if (indexablesWithChanges.contains(location)) { + hasChanges = true; + delta.changed(next, + IJavaElementDelta.F_CONTENT | IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED); + } + } + } + } + } catch (CoreException e) { + Package.log(e); + } + } + + if (hasChanges) { + fireChange(IndexerEvent.createChange(delta)); + } + } + + private void updateResourceMappings(Map<IPath, List<IJavaElement>> pathsToUpdate, IProgressMonitor monitor) { + SubMonitor subMonitor = SubMonitor.convert(monitor, pathsToUpdate.keySet().size()); + + JavaIndex index = JavaIndex.getIndex(this.nd); + + for (Entry<IPath, List<IJavaElement>> entry : pathsToUpdate.entrySet()) { + SubMonitor iterationMonitor = subMonitor.split(1).setWorkRemaining(10); + + this.nd.acquireWriteLock(iterationMonitor.split(1)); + try { + NdResourceFile resourceFile = index.getResourceFile(entry.getKey().toString().toCharArray()); + if (resourceFile == null) { + continue; + } + + attachWorkspaceFilesToResource(entry.getValue(), resourceFile); + } finally { + this.nd.releaseWriteLock(); + } + + } + } + + /** + * Clean up unneeded files here, but only do so if it's been a long time since the file was last referenced. Being + * too eager about removing old files means that operations which temporarily cause a file to become unreferenced + * will run really slowly. also eagerly clean up any partially-indexed files we discover during the scan. That is, + * if we discover a file with a timestamp of 0, it indicates that the indexer or all of Eclipse crashed midway + * through indexing the file. Such garbage should be cleaned up as soon as possible, since it will never be useful. + * + * @param currentTimeMillis timestamp of the time at which the indexing operation started + * @param allIndexables list of all referenced java roots + * @param monitor progress monitor + * @return the number of indexables in the index, prior to garbage collection + */ + private int cleanGarbage(long currentTimeMillis, Collection<IPath> allIndexables, IProgressMonitor monitor) { + JavaIndex index = JavaIndex.getIndex(this.nd); + + int result = 0; + HashSet<IPath> paths = new HashSet<>(); + paths.addAll(allIndexables); + SubMonitor subMonitor = SubMonitor.convert(monitor, 3); + + List<NdResourceFile> garbage = new ArrayList<>(); + List<NdResourceFile> needsUpdate = new ArrayList<>(); + + long usageTimestampUpdatePeriod = getUsageTimestampUpdatePeriod(); + long garbageCleanupTimeout = getGarbageCleanupTimeout(); + // Build up the list of NdResourceFiles that either need to be garbage collected or + // have their read timestamps updated. + try (IReader reader = this.nd.acquireReadLock()) { + List<NdResourceFile> resourceFiles = index.getAllResourceFiles(); + + result = resourceFiles.size(); + SubMonitor testMonitor = subMonitor.split(1).setWorkRemaining(resourceFiles.size()); + for (NdResourceFile next : resourceFiles) { + testMonitor.split(1); + if (!next.isDoneIndexing()) { + garbage.add(next); + } else { + IPath nextPath = new Path(next.getLocation().toString()); + long timeLastUsed = next.getTimeLastUsed(); + long timeSinceLastUsed = currentTimeMillis - timeLastUsed; + + if (paths.contains(nextPath)) { + if (timeSinceLastUsed > usageTimestampUpdatePeriod) { + needsUpdate.add(next); + } + } else { + if (timeSinceLastUsed > garbageCleanupTimeout) { + garbage.add(next); + } + } + } + } + } + + SubMonitor deleteMonitor = subMonitor.split(1).setWorkRemaining(garbage.size()); + for (NdResourceFile next : garbage) { + deleteResource(next, deleteMonitor.split(1)); + } + + SubMonitor updateMonitor = subMonitor.split(1).setWorkRemaining(needsUpdate.size()); + for (NdResourceFile next : needsUpdate) { + this.nd.acquireWriteLock(updateMonitor.split(1)); + try { + if (next.isInIndex()) { + next.setTimeLastUsed(currentTimeMillis); + } + } finally { + this.nd.releaseWriteLock(); + } + } + + return result; + } + + /** + * Performs a non-atomic delete of the given resource file. First, it marks the file as being invalid + * (by clearing out its timestamp). Then it deletes the children of the resource file, one child at a time. + * Once all the children are deleted, the resource itself is deleted. The result on the database is exactly + * the same as if the caller had called toDelete.delete(), but doing it this way ensures that a write lock + * will never be held for a nontrivial amount of time. + */ + protected void deleteResource(NdResourceFile toDelete, IProgressMonitor monitor) { + SubMonitor deletionMonitor = SubMonitor.convert(monitor, 10); + + this.nd.acquireWriteLock(deletionMonitor.split(1)); + try { + if (toDelete.isInIndex()) { + toDelete.markAsInvalid(); + } + } finally { + this.nd.releaseWriteLock(); + } + + for (;;) { + this.nd.acquireWriteLock(deletionMonitor.split(1)); + try { + if (!toDelete.isInIndex()) { + break; + } + + int numChildren = toDelete.getBindingCount(); + deletionMonitor.setWorkRemaining(numChildren + 1); + if (numChildren == 0) { + break; + } + + NdBinding nextDeletion = toDelete.getBinding(numChildren - 1); + if (DEBUG_INSERTIONS) { + if (nextDeletion instanceof NdType) { + NdType type = (NdType)nextDeletion; + Package.logInfo("Deleting " + type.getTypeId().getFieldDescriptor().getString() + " from " //$NON-NLS-1$//$NON-NLS-2$ + + new String(toDelete.getLocation().getString()) + " " + toDelete.address); //$NON-NLS-1$ + } + } + nextDeletion.delete(); + } finally { + this.nd.releaseWriteLock(); + } + } + + this.nd.acquireWriteLock(deletionMonitor.split(1)); + try { + if (toDelete.isInIndex()) { + toDelete.delete(); + } + } finally { + this.nd.releaseWriteLock(); + } + } + + private Map<IPath, List<IJavaElement>> removeDuplicatePaths(List<IJavaElement> allIndexables) { + Map<IPath, List<IJavaElement>> paths = new HashMap<>(); + + HashSet<IPath> workspacePaths = new HashSet<IPath>(); + for (IJavaElement next : allIndexables) { + IPath nextPath = JavaIndex.getLocationForElement(next); + IPath workspacePath = getWorkspacePathForRoot(next); + + List<IJavaElement> value = paths.get(nextPath); + + if (value == null) { + value = new ArrayList<IJavaElement>(); + paths.put(nextPath, value); + } else { + if (workspacePath != null) { + if (workspacePaths.contains(workspacePath)) { + continue; + } + if (!workspacePath.isEmpty()) { + Package.logInfo("Found duplicate workspace path for " + workspacePath.toString()); //$NON-NLS-1$ + } + workspacePaths.add(workspacePath); + } + } + + value.add(next); + } + + return paths; + } + + private IPath getWorkspacePathForRoot(IJavaElement next) { + IResource resource = next.getResource(); + if (resource != null) { + return resource.getFullPath(); + } + return Path.EMPTY; + } + + private Map<IPath, FingerprintTestResult> testFingerprints(Collection<IPath> allIndexables, + IProgressMonitor monitor) throws CoreException { + SubMonitor subMonitor = SubMonitor.convert(monitor, allIndexables.size()); + Map<IPath, FingerprintTestResult> result = new HashMap<>(); + + for (IPath next : allIndexables) { + result.put(next, testForChanges(next, subMonitor.split(1))); + } + + return result; + } + + /** + * Rescans an archive (a jar, zip, or class file on the filesystem). Returns the number of classes indexed. + * @throws JavaModelException + */ + private int rescanArchive(long currentTimeMillis, IPath thePath, List<IJavaElement> elementsMappingOntoLocation, + FileFingerprint fingerprint, IProgressMonitor monitor) throws JavaModelException { + if (elementsMappingOntoLocation.isEmpty()) { + return 0; + } + + IJavaElement element = elementsMappingOntoLocation.get(0); + SubMonitor subMonitor = SubMonitor.convert(monitor, 100); + + String pathString = thePath.toString(); + JavaIndex javaIndex = JavaIndex.getIndex(this.nd); + + File theFile = thePath.toFile(); + if (!(theFile.exists() && theFile.isFile())) { + if (DEBUG) { + Package.log("the file " + pathString + " does not exist", null); //$NON-NLS-1$ //$NON-NLS-2$ + } + return 0; + } + + NdResourceFile resourceFile; + + this.nd.acquireWriteLock(subMonitor.split(5)); + try { + resourceFile = new NdResourceFile(this.nd); + resourceFile.setTimeLastUsed(currentTimeMillis); + resourceFile.setLocation(pathString); + IPackageFragmentRoot packageFragmentRoot = (IPackageFragmentRoot) element + .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); + IPath rootPathString = JavaIndex.getLocationForElement(packageFragmentRoot); + if (!rootPathString.equals(thePath)) { + resourceFile.setPackageFragmentRoot(rootPathString.toString().toCharArray()); + } + attachWorkspaceFilesToResource(elementsMappingOntoLocation, resourceFile); + } finally { + this.nd.releaseWriteLock(); + } + + if (DEBUG) { + Package.logInfo("rescanning " + thePath.toString() + ", " + fingerprint); //$NON-NLS-1$ //$NON-NLS-2$ + } + int result; + try { + result = addElement(resourceFile, element, subMonitor.split(50)); + } catch (JavaModelException e) { + if (DEBUG) { + Package.log("the file " + pathString + " cannot be indexed due to a recoverable error", null); //$NON-NLS-1$ //$NON-NLS-2$ + } + // If this file can't be indexed due to a recoverable error, delete the NdResourceFile entry for it. + this.nd.acquireWriteLock(subMonitor.split(5)); + try { + if (resourceFile.isInIndex()) { + resourceFile.delete(); + } + } finally { + this.nd.releaseWriteLock(); + } + return 0; + } catch (RuntimeException e) { + if (DEBUG) { + Package.log("A RuntimeException occurred while indexing " + pathString, e); //$NON-NLS-1$ + } + throw e; + } + + List<NdResourceFile> allResourcesWithThisPath = Collections.emptyList(); + // Now update the timestamp and delete all older versions of this resource that exist in the index + this.nd.acquireWriteLock(subMonitor.split(1)); + try { + if (resourceFile.isInIndex()) { + resourceFile.setFingerprint(fingerprint); + allResourcesWithThisPath = javaIndex.findResourcesWithPath(pathString); + } + } finally { + this.nd.releaseWriteLock(); + } + + SubMonitor deletionMonitor = subMonitor.split(40).setWorkRemaining(allResourcesWithThisPath.size() - 1); + for (NdResourceFile next : allResourcesWithThisPath) { + if (!next.equals(resourceFile)) { + deleteResource(next, deletionMonitor.split(1)); + } + } + + return result; + } + + private void attachWorkspaceFilesToResource(List<IJavaElement> elementsMappingOntoLocation, + NdResourceFile resourceFile) { + for (IJavaElement next : elementsMappingOntoLocation) { + IResource nextResource = next.getResource(); + if (nextResource != null) { + new NdWorkspaceLocation(this.nd, resourceFile, + nextResource.getFullPath().toString().toCharArray()); + } + } + } + + /** + * Adds an archive to the index, under the given NdResourceFile. + */ + private int addElement(NdResourceFile resourceFile, IJavaElement element, IProgressMonitor monitor) + throws JavaModelException { + SubMonitor subMonitor = SubMonitor.convert(monitor); + + if (element instanceof JarPackageFragmentRoot) { + JarPackageFragmentRoot jarRoot = (JarPackageFragmentRoot) element; + + IPath workspacePath = jarRoot.getPath(); + IPath location = JavaIndex.getLocationForElement(jarRoot); + + int classesIndexed = 0; + try (ZipFile zipFile = new ZipFile(JavaModelManager.getLocalFile(jarRoot.getPath()))) { + // Used for the error-handling unit tests + if (JavaModelManager.throwIoExceptionsInGetZipFile) { + if (DEBUG) { + Package.logInfo("Throwing simulated IOException for error handling test case"); //$NON-NLS-1$ + } + throw new IOException(); + } + subMonitor.setWorkRemaining(zipFile.size()); + + for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements();) { + SubMonitor nextEntry = subMonitor.split(1).setWorkRemaining(2); + ZipEntry member = e.nextElement(); + if (member.isDirectory()) { + continue; + } + nextEntry.split(1); + String fileName = member.getName(); + + boolean classFileName = org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(fileName); + if (classFileName) { + String binaryName = fileName.substring(0, fileName.length() - SuffixConstants.SUFFIX_STRING_class.length()); + char[] fieldDescriptor = JavaNames.binaryNameToFieldDescriptor(binaryName.toCharArray()); + String indexPath = jarRoot.getHandleIdentifier() + IDependent.JAR_FILE_ENTRY_SEPARATOR + binaryName; + BinaryTypeDescriptor descriptor = new BinaryTypeDescriptor(location.toString().toCharArray(), fieldDescriptor, + workspacePath.toString().toCharArray(), indexPath.toCharArray()); + try { + byte[] contents = org.eclipse.jdt.internal.compiler.util.Util.getZipEntryByteContent(member, zipFile); + ClassFileReader classFileReader = new ClassFileReader(contents, descriptor.indexPath, true); + if (addClassToIndex(resourceFile, descriptor.fieldDescriptor, descriptor.indexPath, + classFileReader, nextEntry.split(1))) { + classesIndexed++; + } + } catch (CoreException | ClassFormatException exception) { + Package.log("Unable to index " + descriptor.toString(), exception); //$NON-NLS-1$ + } + } + } + } catch (ZipException e) { + Package.log("The zip file " + jarRoot.getPath() + " was corrupt", e); //$NON-NLS-1$//$NON-NLS-2$ + // Indicates a corrupt zip file. Treat this like an empty zip file. + } catch (IOException ioException) { + throw new JavaModelException(ioException, IJavaModelStatusConstants.IO_EXCEPTION); + } catch (CoreException coreException) { + throw new JavaModelException(coreException); + } + + if (DEBUG && classesIndexed == 0) { + Package.logInfo("The path " + element.getPath() + " contained no class files"); //$NON-NLS-1$ //$NON-NLS-2$ + } + return classesIndexed; + } else if (element instanceof IClassFile) { + IClassFile classFile = (IClassFile)element; + + SubMonitor iterationMonitor = subMonitor.split(1); + BinaryTypeDescriptor descriptor = BinaryTypeFactory.createDescriptor(classFile); + + boolean indexed = false; + try { + ClassFileReader classFileReader = BinaryTypeFactory.rawReadType(descriptor, true); + if (classFileReader != null) { + indexed = addClassToIndex(resourceFile, descriptor.fieldDescriptor, descriptor.indexPath, + classFileReader, iterationMonitor); + } + } catch (CoreException | ClassFormatException e) { + Package.log("Unable to index " + classFile.toString(), e); //$NON-NLS-1$ + } + + return indexed ? 1 : 0; + } else { + Package.logInfo("Unable to index elements of type " + element); //$NON-NLS-1$ + return 0; + } + } + + private boolean addClassToIndex(NdResourceFile resourceFile, char[] fieldDescriptor, char[] indexPath, + ClassFileReader binaryType, IProgressMonitor monitor) throws ClassFormatException, CoreException { + SubMonitor subMonitor = SubMonitor.convert(monitor, 100); + ClassFileToIndexConverter converter = new ClassFileToIndexConverter(resourceFile); + + boolean indexed = false; + this.nd.acquireWriteLock(subMonitor.split(5)); + try { + if (resourceFile.isInIndex()) { + if (DEBUG_INSERTIONS) { + Package.logInfo("Inserting " + new String(fieldDescriptor) + " into " + resourceFile.getLocation().getString() + " " + resourceFile.address); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + } + converter.addType(binaryType, fieldDescriptor, subMonitor.split(45)); + indexed = true; + } + } finally { + this.nd.releaseWriteLock(); + } + + if (DEBUG_SELFTEST && indexed) { + // When this debug flag is on, we test everything written to the index by reading it back immediately after indexing + // and comparing it with the original class file. + JavaIndex index = JavaIndex.getIndex(this.nd); + try (IReader readLock = this.nd.acquireReadLock()) { + NdTypeId typeId = index.findType(fieldDescriptor); + NdType targetType = null; + if (typeId != null) { + List<NdType> implementations = typeId.getTypes(); + for (NdType nextType : implementations) { + NdResourceFile nextResourceFile = nextType.getResourceFile(); + if (nextResourceFile.equals(resourceFile)) { + targetType = nextType; + break; + } + } + } + + if (targetType != null) { + IndexBinaryType actualType = new IndexBinaryType(TypeRef.create(targetType), indexPath); + IndexTester.testType(binaryType, actualType); + } else { + Package.logInfo("Could not find class in index immediately after indexing it: " + new String(indexPath)); //$NON-NLS-1$ + } + } catch (RuntimeException e) { + Package.log("Error during indexing: " + new String(indexPath), e); //$NON-NLS-1$ + } + } + return indexed; + } + + private List<IJavaElement> getAllIndexableObjectsInWorkspace(IProgressMonitor monitor) throws CoreException { + SubMonitor subMonitor = SubMonitor.convert(monitor, 2); + List<IJavaElement> allIndexables = new ArrayList<>(); + IProject[] projects = this.root.getProjects(); + + List<IProject> projectsToScan = new ArrayList<>(); + + for (IProject next : projects) { + if (next.isOpen()) { + projectsToScan.add(next); + } + } + + Set<IPath> scannedPaths = new HashSet<>(); + Set<IResource> resourcesToScan = new HashSet<>(); + SubMonitor projectLoopMonitor = subMonitor.split(1).setWorkRemaining(projectsToScan.size()); + for (IProject project : projectsToScan) { + SubMonitor iterationMonitor = projectLoopMonitor.split(1); + try { + if (project.isOpen() && project.isNatureEnabled(JavaCore.NATURE_ID)) { + IJavaProject javaProject = JavaCore.create(project); + + IClasspathEntry[] entries = javaProject.getRawClasspath(); + + if (EXPERIMENTAL_INDEX_OUTPUT_FOLDERS) { + IPath defaultOutputLocation = javaProject.getOutputLocation(); + for (IClasspathEntry next : entries) { + IPath nextOutputLocation = next.getOutputLocation(); + + if (nextOutputLocation == null) { + nextOutputLocation = defaultOutputLocation; + } + + IResource resource = this.root.findMember(nextOutputLocation); + if (resource != null) { + resourcesToScan.add(resource); + } + } + } + + IPackageFragmentRoot[] projectRoots = javaProject.getAllPackageFragmentRoots(); + SubMonitor rootLoopMonitor = iterationMonitor.setWorkRemaining(projectRoots.length); + for (IPackageFragmentRoot nextRoot : projectRoots) { + rootLoopMonitor.split(1); + if (!nextRoot.exists()) { + continue; + } + IPath filesystemPath = JavaIndex.getLocationForElement(nextRoot); + if (scannedPaths.contains(filesystemPath)) { + continue; + } + scannedPaths.add(filesystemPath); + if (nextRoot.getKind() == IPackageFragmentRoot.K_BINARY) { + if (nextRoot.isArchive()) { + allIndexables.add(nextRoot); + } else { + collectAllClassFiles(allIndexables, nextRoot); + } + } else { + collectAllClassFiles(allIndexables, nextRoot); + } + } + } + } catch (CoreException e) { + Package.log(e); + } + } + + collectAllClassFiles(allIndexables, resourcesToScan, subMonitor.split(1)); + return allIndexables; + } + + private void collectAllClassFiles(List<? super IClassFile> result, Collection<? extends IResource> toScan, + IProgressMonitor monitor) { + SubMonitor subMonitor = SubMonitor.convert(monitor); + + ArrayDeque<IResource> resources = new ArrayDeque<>(); + resources.addAll(toScan); + + while (!resources.isEmpty()) { + subMonitor.setWorkRemaining(Math.max(resources.size(), 3000)).split(1); + IResource next = resources.removeFirst(); + + if (next instanceof IContainer) { + IContainer container = (IContainer)next; + + try { + for (IResource nextChild : container.members()) { + resources.addLast(nextChild); + } + } catch (CoreException e) { + // If an error occurs in one resource, skip it and move on to the next + Package.log(e); + } + } else if (next instanceof IFile) { + IFile file = (IFile) next; + + String extension = file.getFileExtension(); + if (Objects.equals(extension, "class")) { //$NON-NLS-1$ + IJavaElement element = JavaCore.create(file); + + if (element instanceof IClassFile) { + result.add((IClassFile)element); + } + } + } + } + } + + private void collectAllClassFiles(List<? super IClassFile> result, IParent nextRoot) throws CoreException { + for (IJavaElement child : nextRoot.getChildren()) { + try { + int type = child.getElementType(); + if (!child.exists()) { + continue; + } + if (type == IJavaElement.COMPILATION_UNIT) { + continue; + } + + if (type == IJavaElement.CLASS_FILE) { + result.add((IClassFile)child); + } else if (child instanceof IParent) { + IParent parent = (IParent) child; + + collectAllClassFiles(result, parent); + } + } catch (CoreException e) { + // Log exceptions, then continue with the next child + Package.log(e); + } + } + } + + /** + * Given a list of fragment roots, returns the subset of roots that have changed since the last time they were + * indexed. + */ + private List<IPath> getIndexablesThatHaveChanged(Collection<IPath> indexables, + Map<IPath, FingerprintTestResult> fingerprints) { + List<IPath> indexablesWithChanges = new ArrayList<>(); + for (IPath next : indexables) { + FingerprintTestResult testResult = fingerprints.get(next); + + if (!testResult.matches()) { + indexablesWithChanges.add(next); + } + } + return indexablesWithChanges; + } + + private FingerprintTestResult testForChanges(IPath thePath, IProgressMonitor monitor) throws CoreException { + SubMonitor subMonitor = SubMonitor.convert(monitor, 100); + JavaIndex javaIndex = JavaIndex.getIndex(this.nd); + String pathString = thePath.toString(); + + subMonitor.split(50); + NdResourceFile resourceFile = null; + FileFingerprint fingerprint = FileFingerprint.getEmpty(); + this.nd.acquireReadLock(); + try { + resourceFile = javaIndex.getResourceFile(pathString.toCharArray()); + + if (resourceFile != null) { + fingerprint = resourceFile.getFingerprint(); + } + } finally { + this.nd.releaseReadLock(); + } + + FingerprintTestResult result = fingerprint.test(thePath, subMonitor.split(40)); + + // If this file hasn't changed but its timestamp has, write an updated fingerprint to the database + if (resourceFile != null && result.matches() && result.needsNewFingerprint()) { + this.nd.acquireWriteLock(subMonitor.split(10)); + try { + if (resourceFile.isInIndex()) { + if (DEBUG) { + Package.logInfo( + "Writing updated fingerprint for " + thePath + ": " + result.getNewFingerprint()); //$NON-NLS-1$//$NON-NLS-2$ + } + resourceFile.setFingerprint(result.getNewFingerprint()); + } + } finally { + this.nd.releaseWriteLock(); + } + } + return result; + } + + public Indexer(Nd toPopulate, IWorkspaceRoot workspaceRoot) { + this.nd = toPopulate; + this.root = workspaceRoot; + } + + public void rescanAll() { + if (DEBUG) { + Package.logInfo("Scheduling rescanAll now"); //$NON-NLS-1$ + } + synchronized (this.automaticIndexingMutex) { + if (!this.enableAutomaticIndexing) { + if (!this.indexerDirtiedWhileDisabled) { + this.indexerDirtiedWhileDisabled = true; + } + return; + } + } + this.rescanJob.schedule(); + } + + /** + * Adds the given listener. It will be notified when Nd changes. No strong references + * will be retained to the listener. + */ + public void addListener(Listener newListener) { + synchronized (this.listenersMutex) { + Set<Listener> oldListeners = this.listeners; + this.listeners = Collections.newSetFromMap(new WeakHashMap<Listener, Boolean>()); + this.listeners.addAll(oldListeners); + this.listeners.add(newListener); + } + } + + public void removeListener(Listener oldListener) { + synchronized (this.listenersMutex) { + if (!this.listeners.contains(oldListener)) { + return; + } + Set<Listener> oldListeners = this.listeners; + this.listeners = Collections.newSetFromMap(new WeakHashMap<Listener, Boolean>()); + this.listeners.addAll(oldListeners); + this.listeners.remove(oldListener); + } + } + + private void fireChange(IndexerEvent event) { + Set<Listener> localListeners; + synchronized (this.listenersMutex) { + localListeners = this.listeners; + } + + for (Listener next : localListeners) { + next.consume(event); + } + } + + public void waitForIndex(IProgressMonitor monitor) { + try { + boolean shouldRescan = false; + synchronized (this.automaticIndexingMutex) { + if (!this.enableAutomaticIndexing && this.indexerDirtiedWhileDisabled) { + shouldRescan = true; + } + } + if (shouldRescan) { + this.rescanJob.schedule(); + } + this.rescanJob.join(0, monitor); + } catch (InterruptedException e) { + throw new OperationCanceledException(); + } + } + + public void waitForIndex(int waitingPolicy, IProgressMonitor monitor) { + switch (waitingPolicy) { + case IJob.ForceImmediate: { + break; + } + case IJob.CancelIfNotReady: { + if (this.rescanJob.getState() != Job.NONE) { + throw new OperationCanceledException(); + } + break; + } + case IJob.WaitUntilReady: { + waitForIndex(monitor); + break; + } + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/IndexerEvent.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/IndexerEvent.java new file mode 100644 index 000000000..1a29d5d2f --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/IndexerEvent.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.indexer; + +import org.eclipse.jdt.core.IJavaElementDelta; + +public class IndexerEvent { + final IJavaElementDelta delta; + + private IndexerEvent(IJavaElementDelta delta) { + this.delta = delta; + } + + public static IndexerEvent createChange(IJavaElementDelta delta) { + return new IndexerEvent(delta); + } + + public IJavaElementDelta getDelta() { + return this.delta; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/Messages.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/Messages.java new file mode 100644 index 000000000..78b4f9701 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/Messages.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.indexer; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.jdt.internal.core.nd.indexer.messages"; //$NON-NLS-1$ + public static String Indexer_updating_index_job_name; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/Package.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/Package.java new file mode 100644 index 000000000..df19a3c97 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/Package.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.indexer; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.JavaCore; + +/* package */ class Package { + public static String PLUGIN_ID = JavaCore.PLUGIN_ID; + + public static void log(Throwable e) { + String msg = e.getMessage(); + if (msg == null) { + log("Error", e); //$NON-NLS-1$ + } else { + log("Error: " + msg, e); //$NON-NLS-1$ + } + } + + public static void log(String message, Throwable e) { + log(createStatus(message, e)); + } + + public static IStatus createStatus(String msg, Throwable e) { + return new Status(IStatus.ERROR, PLUGIN_ID, msg, e); + } + + public static IStatus createStatus(String msg) { + return new Status(IStatus.ERROR, PLUGIN_ID, msg); + } + + public static void logInfo(String message) { + log(new Status(IStatus.INFO, PLUGIN_ID, message)); + } + + public static void log(IStatus status) { + JavaCore.getPlugin().getLog().log(status); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/messages.properties b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/messages.properties new file mode 100644 index 000000000..f835143ab --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/indexer/messages.properties @@ -0,0 +1 @@ +Indexer_updating_index_job_name=Updating index diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/ClasspathResolver.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/ClasspathResolver.java new file mode 100644 index 000000000..5e5f638b9 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/ClasspathResolver.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +/** + * Used for filtering and disambiguating bindings in the index to match the classpath. + */ +public interface ClasspathResolver { + public static final int NOT_ON_CLASSPATH = -1; + + /** + * Returns the priority of the given resource file on the classpath or {@link #NOT_ON_CLASSPATH} if the given file + * is not onthe classpath. In the event that the same fully-qualified class name is found in multiple resource + * files, the one with the higher priority number is preferred. + */ + int resolve(NdResourceFile sourceOfReference, NdResourceFile toTest); +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/FileFingerprint.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/FileFingerprint.java new file mode 100644 index 000000000..f1b02622f --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/FileFingerprint.java @@ -0,0 +1,250 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileInfo; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jdt.internal.core.nd.StreamHasher; + +public class FileFingerprint { + /** + * Sentinel value for {@link #time} indicating a nonexistent fingerprint. This is used for the timestamp of + * nonexistent files and for the {@link #getEmpty()} singleton. + */ + public static final long NEVER_MODIFIED = 0; + + /** + * Sentinel value for {@link #time} indicating that the timestamp was not recorded as part of the fingerprint. + * This is normally used to indicate that the file's timestamp was so close to the current system time at the time + * the fingerprint was computed that subsequent changes in the file might not be detected. In such cases, timestamps + * are an unreliable method for determining if the file has changed and so are not included as part of the fingerprint. + */ + public static final long UNKNOWN = 1; + + /** + * Worst-case accuracy of filesystem timestamps, among all supported platforms (this is currently 1s on linux, 2s on + * FAT systems). + */ + private static final long WORST_FILESYSTEM_TIMESTAMP_ACCURACY_MS = 2000; + + private long time; + private long hash; + private long size; + + private static final FileFingerprint EMPTY = new FileFingerprint(NEVER_MODIFIED,0,0); + + public static final FileFingerprint getEmpty() { + return EMPTY; + } + + public static final FileFingerprint create(IPath path, IProgressMonitor monitor) throws CoreException { + return getEmpty().test(path, monitor).getNewFingerprint(); + } + + public FileFingerprint(long time, long size, long hash) { + super(); + this.time = time; + this.size = size; + this.hash = hash; + } + + public long getTime() { + return this.time; + } + + public long getHash() { + return this.hash; + } + + public long getSize() { + return this.size; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (this.hash ^ (this.hash >>> 32)); + result = prime * result + (int) (this.size ^ (this.size >>> 32)); + result = prime * result + (int) (this.time ^ (this.time >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + FileFingerprint other = (FileFingerprint) obj; + if (this.hash != other.hash) + return false; + if (this.size != other.size) + return false; + if (this.time != other.time) + return false; + return true; + } + + public static class FingerprintTestResult { + private boolean matches; + private boolean needsNewFingerprint; + private FileFingerprint newFingerprint; + + public FingerprintTestResult(boolean matches, boolean needsNewFingerprint, FileFingerprint newFingerprint) { + super(); + this.matches = matches; + this.newFingerprint = newFingerprint; + this.needsNewFingerprint = needsNewFingerprint; + } + + public boolean needsNewFingerprint() { + return this.needsNewFingerprint; + } + + public boolean matches() { + return this.matches; + } + + public FileFingerprint getNewFingerprint() { + return this.newFingerprint; + } + + @Override + public String toString() { + return "FingerprintTestResult [matches=" + this.matches + ", needsNewFingerprint=" //$NON-NLS-1$//$NON-NLS-2$ + + this.needsNewFingerprint + ", newFingerprint=" + this.newFingerprint + "]"; //$NON-NLS-1$//$NON-NLS-2$ + } + } + + /** + * Compares the given File with the receiver. If the fingerprint matches (ie: the file + */ + public FingerprintTestResult test(IPath path, IProgressMonitor monitor) throws CoreException { + SubMonitor subMonitor = SubMonitor.convert(monitor, 100); + long currentTime = System.currentTimeMillis(); + IFileStore store = EFS.getLocalFileSystem().getStore(path); + IFileInfo fileInfo = store.fetchInfo(); + + long lastModified = fileInfo.getLastModified(); + if (Math.abs(currentTime - lastModified) < WORST_FILESYSTEM_TIMESTAMP_ACCURACY_MS) { + // If the file was modified so recently that it's within our ability to measure it, don't include + // the timestamp as part of the fingerprint. If another change were to happen to the file immediately + // afterward, we might not be able to detect it using the timestamp. + lastModified = UNKNOWN; + } + subMonitor.split(5); + + long fileSize = fileInfo.getLength(); + subMonitor.split(5); + if (lastModified != UNKNOWN && lastModified == this.time && fileSize == this.size) { + return new FingerprintTestResult(true, false, this); + } + + long hashCode; + try { + hashCode = fileSize == 0 ? 0 : computeHashCode(path.toFile(), fileSize, subMonitor.split(90)); + } catch (IOException e) { + throw new CoreException(Package.createStatus("An error occurred computing a hash code", e)); //$NON-NLS-1$ + } + boolean matches = (hashCode == this.hash && fileSize == this.size); + + FileFingerprint newFingerprint = new FileFingerprint(lastModified, fileSize, hashCode); + return new FingerprintTestResult(matches, !equals(newFingerprint), newFingerprint); + } + + private long computeHashCode(File toTest, long fileSize, IProgressMonitor monitor) throws IOException { + final int BUFFER_SIZE = 2048; + char[] charBuffer = new char[BUFFER_SIZE]; + byte[] byteBuffer = new byte[BUFFER_SIZE * 2]; + + SubMonitor subMonitor = SubMonitor.convert(monitor, (int) (fileSize / (BUFFER_SIZE * 2))); + StreamHasher hasher = new StreamHasher(); + try { + InputStream inputStream = new FileInputStream(toTest); + try { + while (true) { + subMonitor.split(1); + int bytesRead = readUntilBufferFull(inputStream, byteBuffer); + + if (bytesRead < byteBuffer.length) { + charBuffer = new char[(bytesRead + 1) / 2]; + copyByteArrayToCharArray(charBuffer, byteBuffer, bytesRead); + hasher.addChunk(charBuffer); + break; + } + + copyByteArrayToCharArray(charBuffer, byteBuffer, bytesRead); + hasher.addChunk(charBuffer); + } + } finally { + inputStream.close(); + } + + } catch (FileNotFoundException e) { + return 0; + } + + return hasher.computeHash(); + } + + private void copyByteArrayToCharArray(char[] charBuffer, byte[] byteBuffer, int bytesToCopy) { + for (int ch = 0; ch < bytesToCopy / 2; ch++) { + char next = (char) (byteBuffer[ch * 2] + byteBuffer[ch * 2 + 1]); + charBuffer[ch] = next; + } + + if (bytesToCopy % 2 != 0) { + charBuffer[bytesToCopy / 2] = (char) byteBuffer[bytesToCopy - 1]; + } + } + + int readUntilBufferFull(InputStream inputStream, byte[] buffer) throws IOException { + int bytesRead = 0; + while (bytesRead < buffer.length) { + int thisRead = inputStream.read(buffer, bytesRead, buffer.length - bytesRead); + + if (thisRead == -1) { + return bytesRead; + } + + bytesRead += thisRead; + } + return bytesRead; + } + + private static String getTimeString(long timestamp) { + if (timestamp == UNKNOWN) { + return "UNKNOWN"; //$NON-NLS-1$ + } else if (timestamp == NEVER_MODIFIED) { + return "NEVER_MODIFIED"; //$NON-NLS-1$ + } + return Long.toString(timestamp); + } + + @Override + public String toString() { + return "FileFingerprint [time=" + getTimeString(this.time) + ", size=" + this.size + ", hash=" + this.hash + "]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ + } +}
\ No newline at end of file diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/IndexFilter.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/IndexFilter.java new file mode 100644 index 000000000..cc6a90d85 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/IndexFilter.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2006, 2016 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Markus Schorn - initial API and implementation + * Andrew Ferguson (Symbian) + * Bryan Wilkinson (QNX) + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.dom.IBinding; + +/** + * Can be subclassed and used for queries in the index. + */ +public class IndexFilter { + public static final IndexFilter ALL = new IndexFilter(); + + /** + * Get an IndexFilter that accepts everything + * + * @return an IndexFilter instance + */ + public static IndexFilter getFilter() { + return new IndexFilter(); + } + + /** + * Determines whether or not a binding is valid. + * + * @param binding the binding being checked for validity + * @return whether or not the binding is valid + * @throws CoreException + */ + public boolean acceptBinding(IBinding binding) throws CoreException { + return true; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaIndex.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaIndex.java new file mode 100644 index 000000000..006aeff10 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaIndex.java @@ -0,0 +1,300 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.io.File; +import java.util.List; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.preferences.IPreferencesService; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.NdNodeTypeRegistry; +import org.eclipse.jdt.internal.core.nd.db.ChunkCache; +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.field.FieldSearchIndex; +import org.eclipse.jdt.internal.core.nd.field.FieldSearchIndex.IResultRank; +import org.eclipse.jdt.internal.core.nd.field.FieldSearchIndex.SearchCriteria; +import org.eclipse.jdt.internal.core.nd.field.StructDef; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; + +public class JavaIndex { + // Version constants + static final int CURRENT_VERSION = Nd.version(1, 37); + static final int MAX_SUPPORTED_VERSION = Nd.version(1, 37); + static final int MIN_SUPPORTED_VERSION = Nd.version(1, 37); + + // Fields for the search header + public static final FieldSearchIndex<NdResourceFile> FILES; + public static final FieldSearchIndex<NdTypeId> SIMPLE_INDEX; + public static final FieldSearchIndex<NdTypeId> TYPES; + public static final FieldSearchIndex<NdMethodId> METHODS; + + public static final StructDef<JavaIndex> type; + + static { + type = StructDef.create(JavaIndex.class); + FILES = FieldSearchIndex.create(type, NdResourceFile.FILENAME); + SIMPLE_INDEX = FieldSearchIndex.create(type, NdTypeId.SIMPLE_NAME); + TYPES = FieldSearchIndex.create(type, NdTypeId.FIELD_DESCRIPTOR); + METHODS = FieldSearchIndex.create(type, NdMethodId.METHOD_NAME); + type.done(); + + // This struct needs to fit within the first database chunk. + assert type.getFactory().getRecordSize() <= Database.CHUNK_SIZE; + } + + private final static class BestResourceFile implements FieldSearchIndex.IResultRank { + public BestResourceFile() { + } + + @Override + public long getRank(Nd resourceFileNd, long resourceFileAddress) { + return NdResourceFile.TIME_LAST_SCANNED.get(resourceFileNd, resourceFileAddress); + } + } + + private static final BestResourceFile bestResourceFile = new BestResourceFile(); + private final long address; + private Nd nd; + private IResultRank anyResult = new IResultRank() { + @Override + public long getRank(Nd dom, long address1) { + return 1; + } + }; + private static Nd globalNd; + private static final String INDEX_FILENAME = "index.db"; //$NON-NLS-1$ + private final static Object ndMutex = new Object(); + + public JavaIndex(Nd dom, long address) { + this.address = address; + this.nd = dom; + } + + /** + * Returns the most-recently-scanned resource file with the given name or null if none + */ + public NdResourceFile getResourceFile(char[] location) { + return FILES.findBest(this.nd, this.address, FieldSearchIndex.SearchCriteria.create(location), + bestResourceFile); + } + + /** + * Returns true iff the given resource file is up-to-date with the filesystem. Returns false + * if the argument is out-of-date with the file system or null. + * + * @param file the index file to look up or null + * @throws CoreException + */ + public boolean isUpToDate(NdResourceFile file) throws CoreException { + if (file != null && file.isDoneIndexing()) { + // TODO(sxenos): It would be much more efficient to mark files as being in one + // of three states: unknown, dirty, or clean. Files would start in the unknown + // state and move into the dirty state when we see them in a java model change + // event. They would move into the clean state after passing this sort of + // fingerprint test... but by caching the state of all tested files (in memory), + // it would eliminate the vast majority of these (slow) fingerprint tests. + + Path locationPath = new Path(file.getLocation().getString()); + if (file.getFingerprint().test(locationPath, null).matches()) { + return true; + } + } + return false; + } + + public List<NdResourceFile> findResourcesWithPath(String thePath) { + return FILES.findAll(this.nd, this.address, FieldSearchIndex.SearchCriteria.create(thePath.toCharArray())); + } + + public List<NdResourceFile> getAllResourceFiles() { + return FILES.asList(this.nd, this.address); + } + + public NdTypeId findType(char[] fieldDescriptor) { + SearchCriteria searchCriteria = SearchCriteria.create(fieldDescriptor); + return TYPES.findBest(this.nd, this.address, searchCriteria, this.anyResult); + } + + public boolean visitFieldDescriptorsStartingWith(char[] fieldDescriptorPrefix, FieldSearchIndex.Visitor<NdTypeId> visitor) { + SearchCriteria searchCriteria = SearchCriteria.create(fieldDescriptorPrefix).prefix(true); + return TYPES.visitAll(this.nd, this.address, searchCriteria, visitor); + } + + /** + * Returns a type ID or creates a new one if it does not exist. The caller must + * attach a reference to it after calling this method or it may leak. + */ + public NdTypeId createTypeId(char[] fieldDescriptor) { + NdTypeId existingType = findType(fieldDescriptor); + + if (existingType != null) { + return existingType; + } + + if (fieldDescriptor.length > 1) { + if (fieldDescriptor[0] == 'L') { + if (fieldDescriptor[fieldDescriptor.length - 1] != ';') { + throw new IllegalStateException(new String(fieldDescriptor) + " is not a valid field descriptor"); //$NON-NLS-1$ + } + } + } + + NdTypeId result = new NdTypeId(this.nd, fieldDescriptor); + if (!CharArrayUtils.equals(result.getFieldDescriptor().getChars(), fieldDescriptor)) { + throw new IllegalStateException("Field descriptor didn't match"); //$NON-NLS-1$ + } + return result; + } + + public Nd getNd() { + return this.nd; + } + + public NdMethodId findMethodId(char[] methodId) { + SearchCriteria searchCriteria = SearchCriteria.create(methodId); + + return METHODS.findBest(this.nd, this.address, searchCriteria, this.anyResult); + } + + public NdMethodId createMethodId(char[] methodId) { + NdMethodId existingMethod = findMethodId(methodId); + + if (existingMethod != null) { + return existingMethod; + } + + return new NdMethodId(this.nd, methodId); + } + + /** + * Returns the absolute filesystem location of the given element or null if none + */ + public static IPath getLocationForElement(IJavaElement next) { + IResource resource = next.getResource(); + + if (resource != null) { + return resource.getLocation() == null ? new Path("") : resource.getLocation(); //$NON-NLS-1$ + } + + return next.getPath(); + } + + public static boolean isEnabled() { + IPreferencesService preferenceService = Platform.getPreferencesService(); + if (preferenceService == null) { + return true; + } + return !preferenceService.getBoolean(JavaCore.PLUGIN_ID, "disableNewJavaIndex", false, //$NON-NLS-1$ + null); + } + + public static Nd createNd(File databaseFile, ChunkCache chunkCache) { + return new Nd(databaseFile, chunkCache, createTypeRegistry(), + MIN_SUPPORTED_VERSION, MAX_SUPPORTED_VERSION, CURRENT_VERSION); + } + + public static Nd getGlobalNd() { + Nd localNd; + synchronized (ndMutex) { + localNd = globalNd; + } + + if (localNd != null) { + return localNd; + } + + localNd = createNd(getDBFile(), ChunkCache.getSharedInstance()); + + synchronized (ndMutex) { + if (globalNd == null) { + globalNd = localNd; + } + return globalNd; + } + } + + public static JavaIndex getIndex(Nd nd) { + return new JavaIndex(nd, Database.DATA_AREA_OFFSET); + } + + public static JavaIndex getIndex() { + return getIndex(getGlobalNd()); + } + + public static int getCurrentVersion() { + return CURRENT_VERSION; + } + + static File getDBFile() { + IPath stateLocation = JavaCore.getPlugin().getStateLocation(); + return stateLocation.append(INDEX_FILENAME).toFile(); + } + + static NdNodeTypeRegistry<NdNode> createTypeRegistry() { + NdNodeTypeRegistry<NdNode> registry = new NdNodeTypeRegistry<>(); + registry.register(0x0001, NdAnnotation.type.getFactory()); + registry.register(0x0004, NdAnnotationInConstant.type.getFactory()); + registry.register(0x0008, NdAnnotationInMethod.type.getFactory()); + registry.register(0x000c, NdAnnotationInMethodParameter.type.getFactory()); + registry.register(0x0010, NdAnnotationInType.type.getFactory()); + registry.register(0x0014, NdAnnotationInVariable.type.getFactory()); + registry.register(0x0020, NdAnnotationValuePair.type.getFactory()); + registry.register(0x0028, NdBinding.type.getFactory()); + registry.register(0x0030, NdComplexTypeSignature.type.getFactory()); + registry.register(0x0038, NdConstant.type.getFactory()); + registry.register(0x0040, NdConstantAnnotation.type.getFactory()); + registry.register(0x0050, NdConstantArray.type.getFactory()); + registry.register(0x0060, NdConstantBoolean.type.getFactory()); + registry.register(0x0070, NdConstantByte.type.getFactory()); + registry.register(0x0080, NdConstantChar.type.getFactory()); + registry.register(0x0090, NdConstantClass.type.getFactory()); + registry.register(0x00A0, NdConstantDouble.type.getFactory()); + registry.register(0x00B0, NdConstantEnum.type.getFactory()); + registry.register(0x00C0, NdConstantFloat.type.getFactory()); + registry.register(0x00D0, NdConstantInt.type.getFactory()); + registry.register(0x00E0, NdConstantLong.type.getFactory()); + registry.register(0x00F0, NdConstantShort.type.getFactory()); + registry.register(0x0100, NdConstantString.type.getFactory()); + registry.register(0x0110, NdMethod.type.getFactory()); + registry.register(0x0120, NdMethodException.type.getFactory()); + registry.register(0x0130, NdMethodId.type.getFactory()); + registry.register(0x0140, NdMethodParameter.type.getFactory()); + registry.register(0x0150, NdResourceFile.type.getFactory()); + registry.register(0x0160, NdTreeNode.type.getFactory()); + registry.register(0x0170, NdType.type.getFactory()); + registry.register(0x0180, NdTypeAnnotation.type.getFactory()); + registry.register(0x0184, NdTypeAnnotationInMethod.type.getFactory()); + registry.register(0x0188, NdTypeAnnotationInType.type.getFactory()); + registry.register(0x018c, NdTypeAnnotationInVariable.type.getFactory()); + registry.register(0x0190, NdTypeArgument.type.getFactory()); + registry.register(0x0194, NdTypeBound.type.getFactory()); + registry.register(0x01A0, NdTypeInterface.type.getFactory()); + registry.register(0x01B0, NdTypeParameter.type.getFactory()); + registry.register(0x01C0, NdTypeSignature.type.getFactory()); + registry.register(0x01D0, NdTypeId.type.getFactory()); + registry.register(0x01E0, NdTypeInterface.type.getFactory()); + registry.register(0x01F0, NdVariable.type.getFactory()); + registry.register(0x0200, NdWorkspaceLocation.type.getFactory()); + return registry; + } + + public void rebuildIndex() { + // TODO: delete and recreate the index + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaNames.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaNames.java new file mode 100644 index 000000000..0211cb89c --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/JavaNames.java @@ -0,0 +1,222 @@ +package org.eclipse.jdt.internal.core.nd.java; +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; +import org.eclipse.jdt.internal.core.util.CharArrayBuffer; + +public class JavaNames { + private static final char[] CLASS_FILE_SUFFIX = ".class".toCharArray(); //$NON-NLS-1$ + public static final char[] FIELD_DESCRIPTOR_PREFIX = new char[] { 'L' }; + private static final char[] FIELD_DESCRIPTOR_SUFFIX = new char[] { ';' }; + private static final char[] METHOD_ID_SEPARATOR = new char[] { '#' }; + private static final char[] JAR_FILE_ENTRY_SEPARATOR = IJavaSearchScope.JAR_FILE_ENTRY_SEPARATOR.toCharArray(); + public static final char[] ARRAY_FIELD_DESCRIPTOR_PREFIX = new char[] { '[' }; + + /** + * Converts a java binary name to a simple name. + */ + public static char[] binaryNameToSimpleName(char[] binaryName) { + int skipIndex = Math.max( + Math.max(CharOperation.lastIndexOf('$', binaryName), CharOperation.lastIndexOf('.', binaryName)), + CharOperation.lastIndexOf('/', binaryName)) + 1; + + return CharArrayUtils.subarray(binaryName, skipIndex); + } + + /** + * Given the binary name of a class, returns the jar-relative path of the class file within that + * jar, including the .class extension. + */ + public static char[] binaryNameToResourceRelativePath(char[] binaryName) { + return CharOperation.concat(binaryName, CLASS_FILE_SUFFIX); + } + + public static char[] fullyQualifiedNameToBinaryName(char[] fullyQualifiedName) { + return CharOperation.replaceOnCopy(fullyQualifiedName, '.', '/'); + } + + public static char[] fullyQualifiedNameToFieldDescriptor(char[] fullyQualifiedName) { + char[] result = CharArrayUtils.concat(FIELD_DESCRIPTOR_PREFIX, fullyQualifiedName, FIELD_DESCRIPTOR_SUFFIX); + CharOperation.replace(result, '.', '/'); + return result; + } + + /** + * Given a NdType, returns its identifier in the form accepted by {@link IJavaSearchScope#encloses(String)} + */ + public static char[] getIndexPathFor(NdType type, IWorkspaceRoot root) { + NdResourceFile resourceFile = type.getResourceFile(); + + char[] binaryName = type.getTypeId().getBinaryName(); + + char[] workspaceLocation = null; + if (root != null) { + workspaceLocation = resourceFile.getAnyOpenWorkspaceLocation(root).toString().toCharArray(); + } + + if (workspaceLocation == null || workspaceLocation.length == 0) { + workspaceLocation = resourceFile.getLocation().getChars(); + } + + return CharArrayUtils.concat(workspaceLocation, JAR_FILE_ENTRY_SEPARATOR, + binaryNameToResourceRelativePath(binaryName)); + } + + /** + * Converts a binary name to a field descriptor (without the trailing ';') + */ + public static char[] binaryNameToFieldDescriptor(char[] binaryName) { + return CharArrayUtils.concat(FIELD_DESCRIPTOR_PREFIX, binaryName, FIELD_DESCRIPTOR_SUFFIX); + } + + /** + * Converts a field descriptor to a simple class name. Returns null if the given field descriptor + * doesn't refer to a class or is badly-formed. + */ + public static char[] fieldDescriptorToSimpleName(char[] fieldDescriptor) { + if (!CharArrayUtils.startsWith(fieldDescriptor, 'L')) { + return null; + } + + if (!CharArrayUtils.endsWith(fieldDescriptor, ';')) { + return null; + } + + int separatorPosition = CharArrayUtils.lastIndexOf('/', fieldDescriptor); + if (separatorPosition == -1) { + separatorPosition = 0; + } + + char[] className = CharArrayUtils.subarray(fieldDescriptor, separatorPosition + 1, fieldDescriptor.length - 1); + return className; + } + + /** + * Converts a field descriptor to a java name. If fullyQualified is true, it returns a fully qualified class name. + * If it is false, it returns a source name. + */ + public static char[] fieldDescriptorToJavaName(char[] fieldDescriptor, boolean fullyQualified) { + int arrayCount = 0; + CharArrayBuffer result = new CharArrayBuffer(); + for(int scanPosition = 0; scanPosition < fieldDescriptor.length; scanPosition++) { + char nextChar = fieldDescriptor[scanPosition]; + + switch (nextChar) { + case 'B' : result.append("byte"); break; //$NON-NLS-1$ + case 'C' : result.append("char"); break; //$NON-NLS-1$ + case 'D' : result.append("double"); break; //$NON-NLS-1$ + case 'F' : result.append("float"); break; //$NON-NLS-1$ + case 'I' : result.append("int"); break; //$NON-NLS-1$ + case 'J' : result.append("long"); break; //$NON-NLS-1$ + case 'L' : { + int end = fieldDescriptor.length - 1; + char[] binaryName = CharArrayUtils.subarray(fieldDescriptor, scanPosition + 1, end); + if (fullyQualified) { + // Modify the binaryName string in-place to change it into a fully qualified name + CharOperation.replace(binaryName, '/', '.'); + result.append(binaryName); + } else { + result.append(binaryNameToSimpleName(binaryName)); + } + scanPosition += binaryName.length; + break; + } + case 'S' : result.append("short"); break; //$NON-NLS-1$ + case 'Z' : result.append("boolean"); break; //$NON-NLS-1$ + case '[' : arrayCount++; break; + } + } + + while (--arrayCount >= 0) { + result.append("[]"); //$NON-NLS-1$ + } + + return CharArrayUtils.notNull(result.getContents()); + } + + public static char[] binaryNameToFullyQualifiedName(char[] binaryName) { + return CharOperation.replaceOnCopy(binaryName, '/', '.'); + } + + /** + * Returns a method id (suitable for constructing a {@link NdMethodId}) given a field descriptor for its parent type + * and a combined method selector and method descriptor for the method + * + * @param parentTypeBinaryName a field descriptor of the sort returned by the other *ToFieldDescriptor methods. + * @param methodSelectorAndDescriptor a method selector and descriptor of the form returned by {@link IBinaryType#getEnclosingMethod()} + * @return a method id suitable for looking up a {@link NdMethodId} + */ + public static char[] getMethodId(char[] parentTypeBinaryName, char[] methodSelectorAndDescriptor) { + return CharArrayUtils.concat(FIELD_DESCRIPTOR_PREFIX, parentTypeBinaryName, METHOD_ID_SEPARATOR, + methodSelectorAndDescriptor); + } + + public static char[] getMethodId(char[] parentTypeBinaryName, char[] methodSelector, char[] methodDescriptor) { + return CharArrayUtils.concat(FIELD_DESCRIPTOR_PREFIX, parentTypeBinaryName, METHOD_ID_SEPARATOR, methodSelector, + methodDescriptor); + } + + /** + * Given a field descriptor, if the field descriptor points to a class this returns the binary name of the class. If + * the field descriptor points to any other type, this returns the empty string. The field descriptor may optionally + * contain a trailing ';'. + * + * @param fieldDescriptor + * @return "" + */ + public static char[] fieldDescriptorToBinaryName(char[] fieldDescriptor) { + if (CharArrayUtils.startsWith(fieldDescriptor, 'L')) { + int end = fieldDescriptor.length - 1; + return CharArrayUtils.subarray(fieldDescriptor, 1, end); + } + return CharArrayUtils.EMPTY_CHAR_ARRAY; + } + + /** + * Given a simple name, this returns the source name for the type. Note that this won't work for classes that + * contain a $ in their source name. + */ + public static char[] simpleNameToSourceName(char[] chars) { + int lastSlash = CharOperation.lastIndexOf('/', chars); + int lastDollar = CharOperation.lastIndexOf('$', chars); + int lastDot = CharOperation.lastIndexOf('.', chars); + int startPosition = Math.max(Math.max(lastSlash, lastDollar), lastDot) + 1; + while (startPosition < chars.length && Character.isDigit(chars[startPosition])) { + startPosition++; + } + return CharArrayUtils.subarray(chars, startPosition); + } + + /** + * Returns true iff the given method selector is a constructor. + */ + public static boolean isConstructor(char[] selector) { + return selector[0] == '<' && selector.length == 6; // Can only match <init> + } + + /** + * Returns true iff the given method selector is clinit. + */ + public static boolean isClinit(char[] selector) { + return selector[0] == '<' && selector.length == 8; // Can only match <clinit> + } + + public static String classFilePathToBinaryName(String classFilePath) { + if (classFilePath.endsWith(".class")) { //$NON-NLS-1$ + return classFilePath.substring(0, classFilePath.length() - 6); + } + return classFilePath; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotation.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotation.java new file mode 100644 index 000000000..9715c100e --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotation.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.List; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public class NdAnnotation extends NdNode { + public static final FieldManyToOne<NdTypeSignature> ANNOTATION_TYPE; + public static final FieldOneToMany<NdAnnotationValuePair> ELEMENT_VALUE_PAIRS; + + @SuppressWarnings("hiding") + public static final StructDef<NdAnnotation> type; + + static { + type = StructDef.create(NdAnnotation.class, NdNode.type); + ANNOTATION_TYPE = FieldManyToOne.create(type, NdTypeSignature.ANNOTATIONS_OF_THIS_TYPE); + ELEMENT_VALUE_PAIRS = FieldOneToMany.create(type, NdAnnotationValuePair.APPLIES_TO); + type.done(); + } + + public NdAnnotation(Nd nd, long address) { + super(nd, address); + } + + public NdAnnotation(Nd nd) { + super(nd); + } + + public NdTypeSignature getType() { + return ANNOTATION_TYPE.get(getNd(), this.address); + } + + public void setType(NdTypeSignature type) { + ANNOTATION_TYPE.put(getNd(), this.address, type); + } + + public List<NdAnnotationValuePair> getElementValuePairs() { + return ELEMENT_VALUE_PAIRS.asList(getNd(), this.address); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInConstant.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInConstant.java new file mode 100644 index 000000000..2328a49e8 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInConstant.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public class NdAnnotationInConstant extends NdAnnotation { + public static final FieldOneToOne<NdConstantAnnotation> OWNER; + + @SuppressWarnings("hiding") + public static final StructDef<NdAnnotationInConstant> type; + + static { + type = StructDef.create(NdAnnotationInConstant.class, NdAnnotation.type); + OWNER = FieldOneToOne.createOwner(type, NdConstantAnnotation.class, NdConstantAnnotation.VALUE); + type.done(); + } + + public NdAnnotationInConstant(Nd nd, long address) { + super(nd, address); + } + + public NdAnnotationInConstant(Nd nd) { + super(nd); + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInMethod.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInMethod.java new file mode 100644 index 000000000..e7e48ebc8 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInMethod.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public class NdAnnotationInMethod extends NdAnnotation { + public static final FieldManyToOne<NdMethod> OWNER; + + @SuppressWarnings("hiding") + public static final StructDef<NdAnnotationInMethod> type; + + static { + type = StructDef.create(NdAnnotationInMethod.class, NdAnnotation.type); + OWNER = FieldManyToOne.createOwner(type, NdMethod.ANNOTATIONS); + type.done(); + } + + public NdAnnotationInMethod(Nd nd, long address) { + super(nd, address); + } + + public NdAnnotationInMethod(Nd nd, NdMethod owner) { + super(nd); + + OWNER.put(getNd(), this.address, owner); + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInMethodParameter.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInMethodParameter.java new file mode 100644 index 000000000..0a4f3fb68 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInMethodParameter.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public class NdAnnotationInMethodParameter extends NdAnnotation { + public static final FieldManyToOne<NdMethodParameter> OWNER; + + @SuppressWarnings("hiding") + public static final StructDef<NdAnnotationInMethodParameter> type; + + static { + type = StructDef.create(NdAnnotationInMethodParameter.class, NdAnnotation.type); + OWNER = FieldManyToOne.createOwner(type, NdMethodParameter.ANNOTATIONS); + type.done(); + } + + public NdAnnotationInMethodParameter(Nd nd, long address) { + super(nd, address); + } + + public NdAnnotationInMethodParameter(Nd nd, NdMethodParameter owner) { + super(nd); + + OWNER.put(getNd(), this.address, owner); + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInType.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInType.java new file mode 100644 index 000000000..c220ed9f0 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInType.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public class NdAnnotationInType extends NdAnnotation { + public static final FieldManyToOne<NdType> OWNER; + + @SuppressWarnings("hiding") + public static final StructDef<NdAnnotationInType> type; + + static { + type = StructDef.create(NdAnnotationInType.class, NdAnnotation.type); + OWNER = FieldManyToOne.createOwner(type, NdType.ANNOTATIONS); + type.done(); + } + + public NdAnnotationInType(Nd nd, long address) { + super(nd, address); + } + + public NdAnnotationInType(Nd nd, NdType owner) { + super(nd); + + OWNER.put(getNd(), this.address, owner); + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInVariable.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInVariable.java new file mode 100644 index 000000000..378b2d44a --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationInVariable.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public class NdAnnotationInVariable extends NdAnnotation { + public static final FieldManyToOne<NdVariable> OWNER; + + @SuppressWarnings("hiding") + public static final StructDef<NdAnnotationInVariable> type; + + static { + type = StructDef.create(NdAnnotationInVariable.class, NdAnnotation.type); + OWNER = FieldManyToOne.createOwner(type, NdVariable.ANNOTATIONS); + type.done(); + } + + public NdAnnotationInVariable(Nd nd, long address) { + super(nd, address); + } + + public NdAnnotationInVariable(Nd nd, NdVariable owner) { + super(nd); + + OWNER.put(getNd(), this.address, owner); + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationValuePair.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationValuePair.java new file mode 100644 index 000000000..f62ceb37b --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdAnnotationValuePair.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.db.IString; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldString; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public class NdAnnotationValuePair extends NdNode { + public static final FieldManyToOne<NdAnnotation> APPLIES_TO; + public static final FieldString NAME; + public static final FieldOneToOne<NdConstant> VALUE; + + @SuppressWarnings("hiding") + public static final StructDef<NdAnnotationValuePair> type; + + static { + type = StructDef.create(NdAnnotationValuePair.class, NdNode.type); + APPLIES_TO = FieldManyToOne.createOwner(type, NdAnnotation.ELEMENT_VALUE_PAIRS); + NAME = type.addString(); + VALUE = FieldOneToOne.create(type, NdConstant.class, NdConstant.PARENT_ANNOTATION_VALUE); + type.done(); + } + + public NdAnnotationValuePair(Nd nd, long address) { + super(nd, address); + } + + public NdAnnotationValuePair(NdAnnotation annotation, char[] name) { + super(annotation.getNd()); + Nd nd = annotation.getNd(); + APPLIES_TO.put(nd, this.address, annotation); + NAME.put(nd, this.address, name); + } + + public NdAnnotation getAnnotation() { + return APPLIES_TO.get(getNd(), this.address); + } + + public IString getName() { + return NAME.get(getNd(), this.address); + } + + public void setName(String name) { + NAME.put(getNd(), this.address, name); + } + + /** + * Returns the value of this annotation or null if none + */ + public NdConstant getValue() { + return VALUE.get(getNd(), this.address); + } + + public void setValue(NdConstant value) { + VALUE.put(getNd(), this.address, value); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdBinding.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdBinding.java new file mode 100644 index 000000000..25938a102 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdBinding.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.field.FieldInt; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.StructDef; +import org.eclipse.jdt.internal.core.util.CharArrayBuffer; + +/** + * Base class for bindings in the {@link Nd}. + */ +public abstract class NdBinding extends NdNode implements IAdaptable { + public static final FieldInt MODIFIERS; + public static final FieldOneToMany<NdTypeParameter> TYPE_PARAMETERS; + public static final FieldManyToOne<NdResourceFile> FILE; + public static final FieldOneToMany<NdVariable> VARIABLES; + + @SuppressWarnings("hiding") + public static final StructDef<NdBinding> type; + + static { + type = StructDef.create(NdBinding.class, NdNode.type); + MODIFIERS = type.addInt(); + TYPE_PARAMETERS = FieldOneToMany.create(type, NdTypeParameter.PARENT); + FILE = FieldManyToOne.createOwner(type, NdResourceFile.ALL_NODES); + VARIABLES = FieldOneToMany.create(type, NdVariable.PARENT); + type.done(); + } + + public NdBinding(Nd nd, long address) { + super(nd, address); + } + + public NdBinding(Nd nd, NdResourceFile resource) { + super(nd); + + FILE.put(nd, this.address, resource); + } + + public List<NdVariable> getVariables() { + return VARIABLES.asList(getNd(), this.address); + } + + /** + * Tests whether this binding has one of the flags defined in {@link Flags} + */ + public boolean hasModifier(int toTest) { + return (MODIFIERS.get(getNd(), this.address) & toTest) != 0; + } + + /** + * Sets the modifiers for this binding (defined in {@link Flags}) + */ + public void setModifiers(int toSet) { + MODIFIERS.put(getNd(), this.address, toSet); + } + + public int getModifiers() { + return MODIFIERS.get(getNd(), this.address); + } + + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Object getAdapter(Class adapter) { + if (adapter.isAssignableFrom(NdBinding.class)) + return this; + + return null; + } + + public final int getBindingConstant() { + return getNodeType(); + } + + public void setFile(NdResourceFile file) { + FILE.put(getNd(), this.address, file); + } + + public NdResourceFile getFile() { + return FILE.get(getNd(), this.address); + } + + public char[][] getTypeParameterSignatures() { + List<NdTypeParameter> parameters = getTypeParameters(); + char[][] result = new char[parameters.size()][]; + + int idx = 0; + for (NdTypeParameter next : parameters) { + char[] nextContents = getSignatureFor(next); + result[idx] = nextContents; + idx++; + } + return result; + } + + private char[] getSignatureFor(NdTypeParameter next) { + CharArrayBuffer nextArray = new CharArrayBuffer(); + next.getSignature(nextArray); + char[] nextContents = nextArray.getContents(); + return nextContents; + } + + public List<NdTypeParameter> getTypeParameters() { + return TYPE_PARAMETERS.asList(getNd(), this.address); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdComplexTypeSignature.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdComplexTypeSignature.java new file mode 100644 index 000000000..b348f4ea7 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdComplexTypeSignature.java @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.IString; +import org.eclipse.jdt.internal.core.nd.db.IndexException; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.FieldString; +import org.eclipse.jdt.internal.core.nd.field.StructDef; +import org.eclipse.jdt.internal.core.util.CharArrayBuffer; + +/** + * Represents a type signature that is anything other than a trivial reference to a concrete + * type. If a type reference includes annotations, generic arguments, wildcards, or is a + * type variable, this object represents it. + * <p> + * Arrays are encoded in a special way. The RAW_TYPE points to a sentinel type called '[' + * and the first type argument holds the array type. + */ +public class NdComplexTypeSignature extends NdTypeSignature { + public static final FieldString VARIABLE_IDENTIFIER; + public static final FieldManyToOne<NdTypeId> RAW_TYPE; + public static final FieldOneToMany<NdTypeArgument> TYPE_ARGUMENTS; + public static final FieldManyToOne<NdComplexTypeSignature> DECLARING_TYPE; + public static final FieldOneToMany<NdComplexTypeSignature> DECLARED_TYPES; + + @SuppressWarnings("hiding") + public static final StructDef<NdComplexTypeSignature> type; + + static { + type = StructDef.create(NdComplexTypeSignature.class, NdTypeSignature.type); + VARIABLE_IDENTIFIER = type.addString(); + RAW_TYPE = FieldManyToOne.create(type, NdTypeId.USED_AS_COMPLEX_TYPE); + TYPE_ARGUMENTS = FieldOneToMany.create(type, NdTypeArgument.PARENT); + DECLARING_TYPE = FieldManyToOne.create(type, null); + DECLARED_TYPES = FieldOneToMany.create(type, DECLARING_TYPE); + + type.useStandardRefCounting().done(); + } + + public NdComplexTypeSignature(Nd nd, long address) { + super(nd, address); + } + + public NdComplexTypeSignature(Nd nd) { + super(nd); + } + + @Override + public NdTypeId getRawType() { + return RAW_TYPE.get(getNd(), this.address); + } + + public void setVariableIdentifier(char[] variableIdentifier) { + VARIABLE_IDENTIFIER.put(getNd(), this.address, variableIdentifier); + } + + /** + * If this type is a type variable, this returns the variable's identifier. + */ + public IString getVariableIdentifier() { + return VARIABLE_IDENTIFIER.get(getNd(), this.address); + } + + public void setRawType(NdTypeId rawType) { + RAW_TYPE.put(getNd(), this.address, rawType); + } + + public void setGenericDeclaringType(NdComplexTypeSignature enclosingType) { + DECLARING_TYPE.put(getNd(), this.address, enclosingType); + } + + /** + * Returns the declaring type (as reported by the type's generic signature). + * Not to be confused with the declaring type as stored in the class file. + * That is stored in {@link NdType#getDeclaringType}. Any class that is + * nested inside another class with generic arguments will have one of + * these. Classes nested inside non-generic classes won't have one of these, + * and neither will non-nested classes. + */ + public NdComplexTypeSignature getGenericDeclaringType() { + return DECLARING_TYPE.get(getNd(), this.address); + } + + @Override + public List<NdTypeArgument> getTypeArguments() { + return TYPE_ARGUMENTS.asList(getNd(), this.address); + } + + @Override + public NdTypeSignature getArrayDimensionType() { + if (isArrayType()) { + long size = TYPE_ARGUMENTS.size(getNd(), this.address); + + if (size != 1) { + throw new IndexException("Array types should have exactly one argument"); //$NON-NLS-1$ + } + + return TYPE_ARGUMENTS.get(getNd(), this.address, 0).getType(); + } + return null; + } + + @Override + public void getSignature(CharArrayBuffer result, boolean includeTrailingSemicolon) { + NdComplexTypeSignature parentSignature = getGenericDeclaringType(); + + if (isTypeVariable()) { + result.append('T'); + result.append(getVariableIdentifier().getChars()); + if (includeTrailingSemicolon) { + result.append(';'); + } + return; + } + + NdTypeSignature arrayDimension = getArrayDimensionType(); + if (arrayDimension != null) { + result.append('['); + arrayDimension.getSignature(result); + return; + } + if (parentSignature != null) { + parentSignature.getSignature(result, false); + result.append('.'); + char[] simpleName = getRawType().getSimpleName().getChars(); + result.append(simpleName); + } else { + result.append(getRawType().getFieldDescriptorWithoutTrailingSemicolon()); + } + + List<NdTypeArgument> arguments = getTypeArguments(); + if (!arguments.isEmpty()) { + result.append('<'); + for (NdTypeArgument next : arguments) { + next.getSignature(result); + } + result.append('>'); + } + if (includeTrailingSemicolon) { + result.append(';'); + } + } + + @Override + public boolean isTypeVariable() { + return getVariableIdentifier().length() != 0; + } + + @Override + public List<NdTypeSignature> getDeclaringTypeChain() { + NdComplexTypeSignature declaringType = getGenericDeclaringType(); + + if (declaringType == null) { + return Collections.singletonList((NdTypeSignature)this); + } + + List<NdTypeSignature> result = new ArrayList<>(); + computeDeclaringTypes(result); + return result; + } + + private void computeDeclaringTypes(List<NdTypeSignature> result) { + NdComplexTypeSignature declaringType = getGenericDeclaringType(); + + if (declaringType != null) { + declaringType.computeDeclaringTypes(result); + } + + result.add(this); + } + + @Override + public boolean isArrayType() { + NdTypeId rawType = getRawType(); + + if (rawType == null) { + return false; + } + + if (rawType.getFieldDescriptor().comparePrefix(JavaNames.ARRAY_FIELD_DESCRIPTOR_PREFIX, true) == 0) { // $NON-NLS-1$ + return true; + } + + return false; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstant.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstant.java new file mode 100644 index 000000000..96e604588 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstant.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.lookup.TypeIds; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public abstract class NdConstant extends NdNode { + // Parent pointers. Only one will be non-null. + // TODO(sxenos): Create something like a union to hold these, to eliminate this + // sparse data + public static final FieldManyToOne<NdConstantArray> PARENT_ARRAY; + public static final FieldOneToOne<NdAnnotationValuePair> PARENT_ANNOTATION_VALUE; + public static final FieldOneToOne<NdVariable> PARENT_VARIABLE; + public static final FieldOneToOne<NdMethod> PARENT_METHOD; + + @SuppressWarnings("hiding") + public static StructDef<NdConstant> type; + + static { + type = StructDef.createAbstract(NdConstant.class, NdNode.type); + PARENT_ARRAY = FieldManyToOne.createOwner(type, NdConstantArray.ELEMENTS); + PARENT_ANNOTATION_VALUE = FieldOneToOne.createOwner(type, NdAnnotationValuePair.class, + NdAnnotationValuePair.VALUE); + PARENT_VARIABLE = FieldOneToOne.createOwner(type, NdVariable.class, NdVariable.CONSTANT); + PARENT_METHOD = FieldOneToOne.createOwner(type, NdMethod.class, NdMethod.DEFAULT_VALUE); + type.done(); + } + + public NdConstant(Nd nd, long address) { + super(nd, address); + } + + protected NdConstant(Nd nd) { + super(nd); + } + + public static NdConstant create(Nd nd, Constant constant) { + if (constant == Constant.NotAConstant) { + return null; + } + + switch (constant.typeID()) { + case TypeIds.T_boolean: + return NdConstantBoolean.create(nd, constant.booleanValue()); + case TypeIds.T_byte: + return NdConstantByte.create(nd, constant.byteValue()); + case TypeIds.T_char: + return NdConstantChar.create(nd, constant.charValue()); + case TypeIds.T_double: + return NdConstantDouble.create(nd, constant.doubleValue()); + case TypeIds.T_float: + return NdConstantFloat.create(nd, constant.floatValue()); + case TypeIds.T_int: + return NdConstantInt.create(nd, constant.intValue()); + case TypeIds.T_long: + return NdConstantLong.create(nd, constant.longValue()); + case TypeIds.T_short: + return NdConstantShort.create(nd, constant.shortValue()); + case TypeIds.T_JavaLangString: + return NdConstantString.create(nd, constant.stringValue()); + default: + throw new IllegalArgumentException("Unknown typeID() " + constant.typeID()); //$NON-NLS-1$ + } + } + + public void setParent(NdConstantArray result) { + PARENT_ARRAY.put(getNd(), this.address, result); + } + + /** + * Returns the {@link Constant} corresponding to the value of this {@link NdConstant} or null if the receiver + * corresponds to a {@link Constant}. + */ + public abstract Constant getConstant(); + + public String toString() { + try { + return getConstant().toString(); + } catch (RuntimeException e) { + // This is called most often from the debugger, so we want to return something meaningful even + // if the code is buggy, the database is corrupt, or we don't have a read lock. + return super.toString(); + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantAnnotation.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantAnnotation.java new file mode 100644 index 000000000..a69e52c4c --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantAnnotation.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public final class NdConstantAnnotation extends NdConstant { + public static final FieldOneToOne<NdAnnotationInConstant> VALUE; + + @SuppressWarnings("hiding") + public static StructDef<NdConstantAnnotation> type; + + static { + type = StructDef.create(NdConstantAnnotation.class, NdConstant.type); + VALUE = FieldOneToOne.create(type, NdAnnotationInConstant.class, NdAnnotationInConstant.OWNER); + type.done(); + } + + public NdConstantAnnotation(Nd nd, long address) { + super(nd, address); + } + + protected NdConstantAnnotation(Nd nd) { + super(nd); + } + + public static NdConstantAnnotation create(Nd nd, NdAnnotationInConstant value) { + NdConstantAnnotation result = new NdConstantAnnotation(nd); + result.setValue(value); + return result; + } + + public void setValue(NdAnnotationInConstant value) { + VALUE.put(getNd(), this.address, value); + } + + public NdAnnotation getValue() { + return VALUE.get(getNd(), this.address); + } + + @Override + public Constant getConstant() { + return null; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantArray.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantArray.java new file mode 100644 index 000000000..b9833ce02 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantArray.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.List; + +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public final class NdConstantArray extends NdConstant { + public static final FieldOneToMany<NdConstant> ELEMENTS; + + @SuppressWarnings("hiding") + public static StructDef<NdConstantArray> type; + + static { + type = StructDef.create(NdConstantArray.class, NdConstant.type); + ELEMENTS = FieldOneToMany.create(type, NdConstant.PARENT_ARRAY, 2); + type.done(); + } + + public NdConstantArray(Nd nd, long address) { + super(nd, address); + } + + public NdConstantArray(Nd nd) { + super(nd); + } + + public List<NdConstant> getValue() { + return ELEMENTS.asList(getNd(), this.address); + } + + @Override + public Constant getConstant() { + return null; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantBoolean.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantBoolean.java new file mode 100644 index 000000000..564f7a79b --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantBoolean.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.impl.BooleanConstant; +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldByte; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public final class NdConstantBoolean extends NdConstant { + public static final FieldByte VALUE; + + @SuppressWarnings("hiding") + public static StructDef<NdConstantBoolean> type; + + static { + type = StructDef.create(NdConstantBoolean.class, NdConstant.type); + VALUE = type.addByte(); + type.done(); + } + + public NdConstantBoolean(Nd nd, long address) { + super(nd, address); + } + + protected NdConstantBoolean(Nd nd) { + super(nd); + } + + public static NdConstantBoolean create(Nd nd, boolean value) { + NdConstantBoolean result = new NdConstantBoolean(nd); + result.setValue(value); + return result; + } + + public void setValue(boolean value) { + VALUE.put(getNd(), this.address, value ? (byte)1 : (byte)0); + } + + public boolean getValue() { + return VALUE.get(getNd(), this.address) != 0; + } + + @Override + public Constant getConstant() { + return BooleanConstant.fromValue(getValue()); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantByte.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantByte.java new file mode 100644 index 000000000..77c99ec56 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantByte.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.impl.ByteConstant; +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldByte; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public final class NdConstantByte extends NdConstant { + public static final FieldByte VALUE; + + @SuppressWarnings("hiding") + public static StructDef<NdConstantByte> type; + + static { + type = StructDef.create(NdConstantByte.class, NdConstant.type); + VALUE = type.addByte(); + type.done(); + } + + public NdConstantByte(Nd nd, long address) { + super(nd, address); + } + + protected NdConstantByte(Nd nd) { + super(nd); + } + + public static NdConstantByte create(Nd nd, byte value) { + NdConstantByte result = new NdConstantByte(nd); + result.setValue(value); + return result; + } + + public void setValue(byte value) { + VALUE.put(getNd(), this.address, value); + } + + public byte getValue() { + return VALUE.get(getNd(), this.address); + } + + @Override + public Constant getConstant() { + return ByteConstant.fromValue(getValue()); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantChar.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantChar.java new file mode 100644 index 000000000..f54975add --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantChar.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.impl.CharConstant; +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldChar; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public final class NdConstantChar extends NdConstant { + public static final FieldChar VALUE; + + @SuppressWarnings("hiding") + public static StructDef<NdConstantChar> type; + + static { + type = StructDef.create(NdConstantChar.class, NdConstant.type); + VALUE = type.addChar(); + type.done(); + } + + public NdConstantChar(Nd nd, long address) { + super(nd, address); + } + + protected NdConstantChar(Nd nd) { + super(nd); + } + + public static NdConstantChar create(Nd nd, char value) { + NdConstantChar result = new NdConstantChar(nd); + result.setValue(value); + return result; + } + + public void setValue(char value) { + VALUE.put(getNd(), this.address, value); + } + + public char getValue() { + return VALUE.get(getNd(), this.address); + } + + @Override + public Constant getConstant() { + return CharConstant.fromValue(getValue()); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantClass.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantClass.java new file mode 100644 index 000000000..fc6fc6c90 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantClass.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public final class NdConstantClass extends NdConstant { + public static final FieldManyToOne<NdTypeSignature> VALUE; + + @SuppressWarnings("hiding") + public static StructDef<NdConstantClass> type; + + static { + type = StructDef.create(NdConstantClass.class, NdConstant.type); + VALUE = FieldManyToOne.create(type, NdTypeSignature.USED_AS_CONSTANT); + type.done(); + } + + public NdConstantClass(Nd nd, long address) { + super(nd, address); + } + + protected NdConstantClass(Nd nd) { + super(nd); + } + + public static NdConstantClass create(Nd nd, NdTypeSignature value) { + NdConstantClass result = new NdConstantClass(nd); + result.setValue(value); + return result; + } + + public void setValue(NdTypeSignature value) { + VALUE.put(getNd(), this.address, value); + } + + public NdTypeSignature getValue() { + return VALUE.get(getNd(), this.address); + } + + @Override + public Constant getConstant() { + return null; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantDouble.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantDouble.java new file mode 100644 index 000000000..56354ba0a --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantDouble.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.impl.DoubleConstant; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldDouble; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public final class NdConstantDouble extends NdConstant { + public static final FieldDouble VALUE; + + @SuppressWarnings("hiding") + public static StructDef<NdConstantDouble> type; + + static { + type = StructDef.create(NdConstantDouble.class, NdConstant.type); + VALUE = type.addDouble(); + type.done(); + } + + public NdConstantDouble(Nd nd, long address) { + super(nd, address); + } + + protected NdConstantDouble(Nd nd) { + super(nd); + } + + public static NdConstantDouble create(Nd nd, double value) { + NdConstantDouble result = new NdConstantDouble(nd); + result.setValue(value); + return result; + } + + public void setValue(double value) { + VALUE.put(getNd(), this.address, value); + } + + public double getValue() { + return VALUE.get(getNd(), this.address); + } + + @Override + public Constant getConstant() { + return DoubleConstant.fromValue(getValue()); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantEnum.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantEnum.java new file mode 100644 index 000000000..c4e9c8e99 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantEnum.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldString; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public final class NdConstantEnum extends NdConstant { + public static final FieldManyToOne<NdTypeSignature> ENUM_TYPE; + public static final FieldString ENUM_VALUE; + + @SuppressWarnings("hiding") + public static StructDef<NdConstantEnum> type; + + static { + type = StructDef.create(NdConstantEnum.class, NdConstant.type); + ENUM_TYPE = FieldManyToOne.create(type, NdTypeSignature.USED_AS_ENUM_CONSTANT); + ENUM_VALUE = type.addString(); + type.done(); + } + + public NdConstantEnum(Nd nd, long address) { + super(nd, address); + } + + protected NdConstantEnum(Nd nd) { + super(nd); + } + + public static NdConstantEnum create(NdTypeSignature enumType, String enumValue) { + NdConstantEnum result = new NdConstantEnum(enumType.getNd()); + result.setEnumType(enumType); + result.setEnumValue(enumValue); + return result; + } + + public void setEnumType(NdTypeSignature enumType) { + ENUM_TYPE.put(getNd(), this.address, enumType); + } + + public void setEnumValue(String enumType) { + ENUM_VALUE.put(getNd(), this.address, enumType); + } + + public NdTypeSignature getType() { + return ENUM_TYPE.get(getNd(), this.address); + } + + public char[] getValue() { + return ENUM_VALUE.get(getNd(), this.address).getChars(); + } + + @Override + public Constant getConstant() { + return null; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantFloat.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantFloat.java new file mode 100644 index 000000000..731405eeb --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantFloat.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.impl.FloatConstant; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldFloat; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public final class NdConstantFloat extends NdConstant { + public static final FieldFloat VALUE; + + @SuppressWarnings("hiding") + public static StructDef<NdConstantFloat> type; + + static { + type = StructDef.create(NdConstantFloat.class, NdConstant.type); + VALUE = type.addFloat(); + type.done(); + } + + public NdConstantFloat(Nd nd, long address) { + super(nd, address); + } + + protected NdConstantFloat(Nd nd) { + super(nd); + } + + public static NdConstantFloat create(Nd nd, float value) { + NdConstantFloat result = new NdConstantFloat(nd); + result.setValue(value); + return result; + } + + public void setValue(float value) { + VALUE.put(getNd(), this.address, value); + } + + public float getValue() { + return VALUE.get(getNd(), this.address); + } + + @Override + public Constant getConstant() { + return FloatConstant.fromValue(getValue()); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantInt.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantInt.java new file mode 100644 index 000000000..f66367500 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantInt.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.impl.IntConstant; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldInt; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public final class NdConstantInt extends NdConstant { + public static final FieldInt VALUE; + + @SuppressWarnings("hiding") + public static StructDef<NdConstantInt> type; + + static { + type = StructDef.create(NdConstantInt.class, NdConstant.type); + VALUE = type.addInt(); + type.done(); + } + + public NdConstantInt(Nd nd, long address) { + super(nd, address); + } + + protected NdConstantInt(Nd nd) { + super(nd); + } + + public static NdConstantInt create(Nd nd, int value) { + NdConstantInt result = new NdConstantInt(nd); + result.setValue(value); + return result; + } + + public void setValue(int value) { + VALUE.put(getNd(), this.address, value); + } + + public int getValue() { + return VALUE.get(getNd(), this.address); + } + + @Override + public Constant getConstant() { + return IntConstant.fromValue(getValue()); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantLong.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantLong.java new file mode 100644 index 000000000..9bed54698 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantLong.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.impl.LongConstant; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldLong; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public final class NdConstantLong extends NdConstant { + public static final FieldLong VALUE; + + @SuppressWarnings("hiding") + public static StructDef<NdConstantLong> type; + + static { + type = StructDef.create(NdConstantLong.class, NdConstant.type); + VALUE = type.addLong(); + type.done(); + } + + public NdConstantLong(Nd nd, long address) { + super(nd, address); + } + + protected NdConstantLong(Nd nd) { + super(nd); + } + + public static NdConstantLong create(Nd nd, long value) { + NdConstantLong result = new NdConstantLong(nd); + result.setValue(value); + return result; + } + + public void setValue(long value) { + VALUE.put(getNd(), this.address, value); + } + + public long getValue() { + return VALUE.get(getNd(), this.address); + } + + @Override + public Constant getConstant() { + return LongConstant.fromValue(getValue()); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantShort.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantShort.java new file mode 100644 index 000000000..3c9fa0f8f --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantShort.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.impl.ShortConstant; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldShort; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public final class NdConstantShort extends NdConstant { + public static final FieldShort VALUE; + + @SuppressWarnings("hiding") + public static StructDef<NdConstantShort> type; + + static { + type = StructDef.create(NdConstantShort.class, NdConstant.type); + VALUE = type.addShort(); + type.done(); + } + + public NdConstantShort(Nd nd, long address) { + super(nd, address); + } + + protected NdConstantShort(Nd nd) { + super(nd); + } + + public static NdConstantShort create(Nd nd, short value) { + NdConstantShort result = new NdConstantShort(nd); + result.setValue(value); + return result; + } + + public void setValue(short value) { + VALUE.put(getNd(), this.address, value); + } + + public short getValue() { + return VALUE.get(getNd(), this.address); + } + + @Override + public Constant getConstant() { + return ShortConstant.fromValue(getValue()); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantString.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantString.java new file mode 100644 index 000000000..4c1aeffbc --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdConstantString.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.impl.StringConstant; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.IString; +import org.eclipse.jdt.internal.core.nd.field.FieldString; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public final class NdConstantString extends NdConstant { + public static final FieldString VALUE; + + @SuppressWarnings("hiding") + public static StructDef<NdConstantString> type; + + static { + type = StructDef.create(NdConstantString.class, NdConstant.type); + VALUE = type.addString(); + type.done(); + } + + public NdConstantString(Nd nd, long address) { + super(nd, address); + } + + protected NdConstantString(Nd nd) { + super(nd); + } + + public static NdConstantString create(Nd nd, String value) { + NdConstantString result = new NdConstantString(nd); + result.setValue(value); + return result; + } + + public void setValue(String value) { + VALUE.put(getNd(), this.address, value); + } + + public IString getValue() { + return VALUE.get(getNd(), this.address); + } + + @Override + public Constant getConstant() { + return StringConstant.fromValue(getValue().getString()); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdMethod.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdMethod.java new file mode 100644 index 000000000..77991be21 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdMethod.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.List; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldLong; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldShort; +import org.eclipse.jdt.internal.core.nd.field.StructDef; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; +import org.eclipse.jdt.internal.core.util.CharArrayBuffer; + +public class NdMethod extends NdBinding { + public static final FieldManyToOne<NdMethodId> METHOD_ID; + public static final FieldShort METHOD_FLAGS; + public static final FieldManyToOne<NdType> PARENT; + public static final FieldOneToMany<NdVariable> DECLARED_VARIABLES; + public static final FieldOneToMany<NdMethodParameter> PARAMETERS; + public static final FieldOneToOne<NdConstant> DEFAULT_VALUE; + public static final FieldOneToMany<NdMethodException> EXCEPTIONS; + public static final FieldManyToOne<NdTypeSignature> RETURN_TYPE; + public static final FieldLong TAG_BITS; + public static final FieldOneToMany<NdAnnotationInMethod> ANNOTATIONS; + public static final FieldOneToMany<NdTypeAnnotationInMethod> TYPE_ANNOTATIONS; + + @SuppressWarnings("hiding") + public static final StructDef<NdMethod> type; + + static { + type = StructDef.create(NdMethod.class, NdBinding.type); + METHOD_ID = FieldManyToOne.create(type, NdMethodId.METHODS); + METHOD_FLAGS = type.addShort(); + PARENT = FieldManyToOne.create(type, NdType.METHODS); + PARAMETERS = FieldOneToMany.create(type, NdMethodParameter.PARENT); + DECLARED_VARIABLES = FieldOneToMany.create(type, NdVariable.DECLARING_METHOD); + DEFAULT_VALUE = FieldOneToOne.create(type, NdConstant.class, NdConstant.PARENT_METHOD); + EXCEPTIONS = FieldOneToMany.create(type, NdMethodException.PARENT); + RETURN_TYPE = FieldManyToOne.create(type, NdTypeSignature.USED_AS_RETURN_TYPE); + TAG_BITS = type.addLong(); + ANNOTATIONS = FieldOneToMany.create(type, NdAnnotationInMethod.OWNER); + TYPE_ANNOTATIONS = FieldOneToMany.create(type, NdTypeAnnotationInMethod.OWNER); + type.done(); + } + + public static final byte FLG_GENERIC_SIGNATURE_PRESENT = 0x0001; + public static final byte FLG_THROWS_SIGNATURE_PRESENT = 0x0002; + + public NdMethod(Nd nd, long address) { + super(nd, address); + } + + public NdMethod(NdType parent) { + super(parent.getNd(), parent.getFile()); + + PARENT.put(getNd(), this.address, parent); + } + + public NdMethodId getMethodId() { + return METHOD_ID.get(getNd(), this.address); + } + + /** + * Returns method parameter names that were not defined by the compiler. + */ + public char[][] getParameterNames() { + List<NdMethodParameter> params = getMethodParameters(); + + // Use index to count the "real" parameters. + int index = 0; + char[][] result = new char[params.size()][]; + for (int idx = 0; idx < result.length; idx++) { + NdMethodParameter param = params.get(idx); + if (!param.isCompilerDefined()) { + result[index] = param.getName().getChars(); + index++; + } + } + return CharArrayUtils.subarray(result, 0, index); + } + + public List<NdMethodParameter> getMethodParameters() { + return PARAMETERS.asList(getNd(), this.address); + } + + public List<NdAnnotationInMethod> getAnnotations() { + return ANNOTATIONS.asList(getNd(), this.address); + } + + public void setDefaultValue(NdConstant value) { + DEFAULT_VALUE.put(getNd(), this.address, value); + } + + public NdConstant getDefaultValue() { + return DEFAULT_VALUE.get(getNd(), this.address); + } + + public void setReturnType(NdTypeSignature createTypeSignature) { + RETURN_TYPE.put(getNd(), this.address, createTypeSignature); + } + + public void setMethodId(NdMethodId methodId) { + METHOD_ID.put(getNd(), this.address, methodId); + } + + public List<NdTypeAnnotationInMethod> getTypeAnnotations() { + return TYPE_ANNOTATIONS.asList(getNd(), this.address); + } + + public List<NdMethodException> getExceptions() { + return EXCEPTIONS.asList(getNd(), this.address); + } + + /** + * Returns the return type for this method or null if the method returns void + */ + public NdTypeSignature getReturnType() { + return RETURN_TYPE.get(getNd(), this.address); + } + + public int getFlags() { + return METHOD_FLAGS.get(getNd(), this.address); + } + + public boolean hasAllFlags(int flags) { + int ourFlags = getFlags(); + + return (ourFlags & flags) == flags; + } + + public void setFlags(int flags) { + METHOD_FLAGS.put(getNd(), this.address, (short) (getFlags() | flags)); + } + + public void setTagBits(long bits) { + TAG_BITS.put(getNd(), this.address, bits); + } + + public long getTagBits() { + return TAG_BITS.get(getNd(), this.address); + } + + public String toString() { + try { + CharArrayBuffer arrayBuffer = new CharArrayBuffer(); + arrayBuffer.append(getMethodId().getSelector()); + getGenericSignature(arrayBuffer, true); + return arrayBuffer.toString(); + } catch (RuntimeException e) { + // This is called most often from the debugger, so we want to return something meaningful even + // if the code is buggy, the database is corrupt, or we don't have a read lock. + return super.toString(); + } + } + + public void getGenericSignature(CharArrayBuffer result, boolean includeExceptions) { + NdTypeParameter.getSignature(result, getTypeParameters()); + + result.append('('); + for (NdMethodParameter next : getMethodParameters()) { + // Compiler-defined arguments don't show up in the generic signature + if (!next.isCompilerDefined()) { + next.getType().getSignature(result); + } + } + result.append(')'); + NdTypeSignature returnType = getReturnType(); + if (returnType == null) { + result.append('V'); + } else { + returnType.getSignature(result); + } + if (includeExceptions) { + List<NdMethodException> exceptions = getExceptions(); + for (NdMethodException next : exceptions) { + result.append('^'); + next.getExceptionType().getSignature(result); + } + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdMethodException.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdMethodException.java new file mode 100644 index 000000000..4c7cfe5a2 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdMethodException.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public class NdMethodException extends NdNode { + + public static final FieldManyToOne<NdMethod> PARENT; + public static final FieldManyToOne<NdTypeSignature> EXCEPTION_TYPE; + + @SuppressWarnings("hiding") + public static StructDef<NdMethodException> type; + + static { + type = StructDef.create(NdMethodException.class, NdNode.type); + PARENT = FieldManyToOne.createOwner(type, NdMethod.EXCEPTIONS); + EXCEPTION_TYPE = FieldManyToOne.create(type, NdTypeSignature.USED_AS_EXCEPTION); + type.done(); + } + + public NdMethodException(Nd nd, long address) { + super(nd, address); + } + + public NdMethodException(NdMethod method, NdTypeSignature createTypeSignature) { + super(method.getNd()); + + PARENT.put(getNd(), this.address, method); + EXCEPTION_TYPE.put(getNd(), this.address, createTypeSignature); + } + + public NdTypeSignature getExceptionType() { + return EXCEPTION_TYPE.get(getNd(), this.address); + } + + public NdMethod getParent() { + return PARENT.get(getNd(), this.address); + } + + public String toString() { + try { + return getExceptionType().toString(); + } catch (RuntimeException e) { + // This is called most often from the debugger, so we want to return something meaningful even + // if the code is buggy, the database is corrupt, or we don't have a read lock. + return super.toString(); + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdMethodId.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdMethodId.java new file mode 100644 index 000000000..f554b8003 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdMethodId.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.List; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.db.IString; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.FieldSearchKey; +import org.eclipse.jdt.internal.core.nd.field.StructDef; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; + +/** + * Represents the fully-qualified signature a method. Holds back-pointers to all the entities that refer to the name, + * along with pointers to all methods that have this fully-qualified name. Note that this isn't the class declaration + * itself. If there are multiple jar files containing a class of the same fully-qualified name, there may also be + * multiple methods with the same method ID. + */ +public class NdMethodId extends NdNode { + public static final FieldSearchKey<JavaIndex> METHOD_NAME; + public static final FieldOneToMany<NdMethod> METHODS; + public static final FieldOneToMany<NdType> DECLARED_TYPES; + + @SuppressWarnings("hiding") + public static final StructDef<NdMethodId> type; + + static { + type = StructDef.create(NdMethodId.class, NdNode.type); + METHOD_NAME = FieldSearchKey.create(type, JavaIndex.METHODS); + METHODS = FieldOneToMany.create(type, NdMethod.METHOD_ID, 2); + DECLARED_TYPES = FieldOneToMany.create(type, NdType.DECLARING_METHOD); + + type.useStandardRefCounting().done(); + } + + public NdMethodId(Nd nd, long address) { + super(nd, address); + } + + /** + * + * @param nd + * @param methodIdentifier a field descriptor for the method type followed by a "#" followed by a method selector + * followed by method descriptor. For example, "Lorg/eclipse/MyClass#foo()Ljava/lang/Object;V" + */ + public NdMethodId(Nd nd, char[] methodIdentifier) { + super(nd); + + METHOD_NAME.put(nd, this.address, methodIdentifier); + } + + public List<NdType> getDeclaredTypes() { + return DECLARED_TYPES.asList(getNd(), this.address); + } + + /** + * Returns the field descriptor for the type (without a trailing ';') followed by a # followed by the method + * selector followed by the method descriptor. For example, "Lorg/eclipse/MyClass#foo()Ljava/lang/Object;V" + */ + public IString getMethodName() { + return METHOD_NAME.get(getNd(), this.address); + } + + public char[] getSelector() { + char[] name = getMethodName().getChars(); + int selectorStart = CharArrayUtils.indexOf('#', name) + 1; + int selectorEnd = CharArrayUtils.indexOf('(', name, selectorStart, name.length); + if (selectorEnd == -1) { + selectorEnd = name.length; + } + return CharArrayUtils.subarray(name, selectorStart, selectorEnd); + } + + public boolean isConstructor() { + return JavaNames.isConstructor(getSelector()); + } + + public char[] getMethodDescriptor() { + char[] name = getMethodName().getChars(); + int descriptorStart = CharArrayUtils.indexOf('(', name, 0, name.length); + return CharArrayUtils.subarray(name, descriptorStart, name.length); + } + + public boolean isClInit() { + return JavaNames.isClinit(getSelector()); + } + + public String toString() { + try { + return new String(getSelector()); + } catch (RuntimeException e) { + // This is called most often from the debugger, so we want to return something meaningful even + // if the code is buggy, the database is corrupt, or we don't have a read lock. + return super.toString(); + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdMethodParameter.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdMethodParameter.java new file mode 100644 index 000000000..bdcd8324d --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdMethodParameter.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.List; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.db.IString; +import org.eclipse.jdt.internal.core.nd.field.FieldByte; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.FieldString; +import org.eclipse.jdt.internal.core.nd.field.StructDef; +import org.eclipse.jdt.internal.core.util.CharArrayBuffer; + +public class NdMethodParameter extends NdNode { + public static final FieldManyToOne<NdMethod> PARENT; + public static final FieldManyToOne<NdTypeSignature> ARGUMENT_TYPE; + public static final FieldString NAME; + public static final FieldOneToMany<NdAnnotationInMethodParameter> ANNOTATIONS; + public static final FieldByte FLAGS; + + private static final byte FLG_COMPILER_DEFINED = 0x01; + + @SuppressWarnings("hiding") + public static StructDef<NdMethodParameter> type; + + static { + type = StructDef.create(NdMethodParameter.class, NdNode.type); + PARENT = FieldManyToOne.create(type, NdMethod.PARAMETERS); + ARGUMENT_TYPE = FieldManyToOne.create(type, NdTypeSignature.USED_AS_METHOD_ARGUMENT); + NAME = type.addString(); + ANNOTATIONS = FieldOneToMany.create(type, NdAnnotationInMethodParameter.OWNER); + FLAGS = type.addByte(); + type.done(); + } + + public NdMethodParameter(Nd nd, long address) { + super(nd, address); + } + + public NdMethodParameter(NdMethod parent, NdTypeSignature argumentType) { + super(parent.getNd()); + + PARENT.put(getNd(), this.address, parent); + ARGUMENT_TYPE.put(getNd(), this.address, argumentType); + } + + public NdTypeSignature getType() { + return ARGUMENT_TYPE.get(getNd(), this.address); + } + + public void setName(char[] name) { + NAME.put(getNd(), this.address, name); + } + + public IString getName() { + return NAME.get(getNd(), this.address); + } + + public List<NdAnnotationInMethodParameter> getAnnotations() { + return ANNOTATIONS.asList(getNd(), this.address); + } + + private void setFlag(byte flagConstant, boolean value) { + int oldFlags = FLAGS.get(getNd(), this.address); + int newFlags = ((oldFlags & ~flagConstant) | (value ? flagConstant : 0)); + FLAGS.put(getNd(), this.address, (byte) newFlags); + } + + private boolean getFlag(byte flagConstant) { + return (FLAGS.get(getNd(), this.address) & flagConstant) != 0; + } + + public void setCompilerDefined(boolean isCompilerDefined) { + setFlag(FLG_COMPILER_DEFINED, isCompilerDefined); + } + + public boolean isCompilerDefined() { + return getFlag(FLG_COMPILER_DEFINED); + } + + public String toString() { + try { + CharArrayBuffer buf = new CharArrayBuffer(); + buf.append(getType().toString()); + buf.append(" "); //$NON-NLS-1$ + buf.append(getName().toString()); + return buf.toString(); + } catch (RuntimeException e) { + // This is called most often from the debugger, so we want to return something meaningful even + // if the code is buggy, the database is corrupt, or we don't have a read lock. + return super.toString(); + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdResourceFile.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdResourceFile.java new file mode 100644 index 000000000..14e2e5362 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdResourceFile.java @@ -0,0 +1,260 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.db.IString; +import org.eclipse.jdt.internal.core.nd.db.IndexException; +import org.eclipse.jdt.internal.core.nd.field.FieldLong; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany.Visitor; +import org.eclipse.jdt.internal.core.nd.field.FieldSearchIndex.IResultRank; +import org.eclipse.jdt.internal.core.nd.field.FieldSearchIndex.SearchCriteria; +import org.eclipse.jdt.internal.core.nd.field.FieldSearchKey; +import org.eclipse.jdt.internal.core.nd.field.FieldString; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +/** + * Represents a source of java classes (such as a .jar or .class file). + */ +public class NdResourceFile extends NdTreeNode { + public static final FieldSearchKey<JavaIndex> FILENAME; + public static final FieldOneToMany<NdBinding> ALL_NODES; + public static final FieldLong TIME_LAST_USED; + public static final FieldLong TIME_LAST_SCANNED; + public static final FieldLong SIZE_LAST_SCANNED; + public static final FieldLong HASHCODE_LAST_SCANNED; + public static final FieldOneToMany<NdWorkspaceLocation> WORKSPACE_MAPPINGS; + public static final FieldString JAVA_ROOT; + + @SuppressWarnings("hiding") + public static final StructDef<NdResourceFile> type; + + static { + type = StructDef.create(NdResourceFile.class, NdTreeNode.type); + FILENAME = FieldSearchKey.create(type, JavaIndex.FILES); + ALL_NODES = FieldOneToMany.create(type, NdBinding.FILE, 16); + TIME_LAST_USED = type.addLong(); + TIME_LAST_SCANNED = type.addLong(); + SIZE_LAST_SCANNED = type.addLong(); + HASHCODE_LAST_SCANNED = type.addLong(); + WORKSPACE_MAPPINGS = FieldOneToMany.create(type, NdWorkspaceLocation.RESOURCE); + JAVA_ROOT = type.addString(); + type.done(); + } + + public NdResourceFile(Nd dom, long address) { + super(dom, address); + } + + public NdResourceFile(Nd nd) { + super(nd, null); + } + + public List<NdTreeNode> getChildren() { + return CHILDREN.asList(this.getNd(), this.address); + } + + /** + * Determines whether this file is still in the index. If a {@link NdResourceFile} instance is retained while the + * database lock is released and reobtained, this method should be invoked to ensure that the {@link NdResourceFile} + * has not been deleted in the meantime. + */ + public boolean isInIndex() { + try { + Nd nd = getNd(); + // In the common case where the resource file was deleted and the memory hasn't yet been reused, + // this will fail. + if (NODE_TYPE.get(nd, this.address) != nd.getNodeType(getClass())) { + return false; + } + + char[] filename = FILENAME.get(getNd(), this.address).getChars(); + + NdResourceFile result = JavaIndex.FILES.findBest(nd, Database.DATA_AREA_OFFSET, + SearchCriteria.create(filename), new IResultRank() { + @Override + public long getRank(Nd testNd, long testAddress) { + if (testAddress == NdResourceFile.this.address) { + return 1; + } + return -1; + } + }); + + return (this.equals(result)); + } catch (IndexException e) { + // Read errors are expected here. It's possible that the resource file has been deleted and something + // new was written to this address, in which case we may be reading random gibberish from the database. + // This is likely to cause an exception. + return false; + } + } + + public List<IPath> getAllWorkspaceLocations() { + final List<IPath> result = new ArrayList<>(); + + WORKSPACE_MAPPINGS.accept(getNd(), this.address, new Visitor<NdWorkspaceLocation>() { + @Override + public void visit(int index, NdWorkspaceLocation toVisit) { + result.add(new Path(toVisit.getPath().getString())); + } + }); + + return result; + } + + public IPath getFirstWorkspaceLocation() { + if (WORKSPACE_MAPPINGS.isEmpty(getNd(), this.address)) { + return Path.EMPTY; + } + + return new Path(WORKSPACE_MAPPINGS.get(getNd(), this.address, 0).getPath().toString()); + } + + public IPath getAnyOpenWorkspaceLocation(IWorkspaceRoot root) { + int numMappings = WORKSPACE_MAPPINGS.size(getNd(), this.address); + + for (int mapping = 0; mapping < numMappings; mapping++) { + NdWorkspaceLocation nextMapping = WORKSPACE_MAPPINGS.get(getNd(), this.address, mapping); + + IPath nextPath = new Path(nextMapping.getPath().getString()); + if (nextPath.isEmpty()) { + continue; + } + + IProject project = root.getProject(nextPath.segment(0)); + if (project.isOpen()) { + return nextPath; + } + } + + return Path.EMPTY; + } + + /** + * Returns a workspace path to this resource if possible and the absolute filesystem location if not. + */ + public IPath getPath() { + IPath workspacePath = getFirstWorkspaceLocation(); + + if (workspacePath.isEmpty()) { + return new Path(getLocation().getString()); + } + + return workspacePath; + } + + public List<NdWorkspaceLocation> getWorkspaceMappings() { + return WORKSPACE_MAPPINGS.asList(getNd(), this.address); + } + + public IString getLocation() { + return FILENAME.get(getNd(), this.address); + } + + public void setLocation(String filename) { + FILENAME.put(getNd(), this.address, filename); + } + + public FileFingerprint getFingerprint() { + return new FileFingerprint( + getTimeLastScanned(), + getSizeLastScanned(), + getHashcodeLastScanned()); + } + + private long getHashcodeLastScanned() { + return HASHCODE_LAST_SCANNED.get(getNd(), this.address); + } + + /** + * Returns true iff the indexer has finished writing the contents of this file to the index. Returns false if + * indexing may still be going on. If this returns false, readers should ignore all contents of this file. + * + * @return true iff the contents of this file are usable + */ + public boolean isDoneIndexing() { + return getTimeLastScanned() != 0; + } + + public long getTimeLastScanned() { + return TIME_LAST_SCANNED.get(getNd(), this.address); + } + + public long getSizeLastScanned() { + return SIZE_LAST_SCANNED.get(getNd(), this.address); + } + + public long getTimeLastUsed() { + return TIME_LAST_USED.get(getNd(), this.address); + } + + public void setTimeLastUsed(long timeLastUsed) { + TIME_LAST_USED.put(getNd(), this.address, timeLastUsed); + } + + public void setFingerprint(FileFingerprint newFingerprint) { + TIME_LAST_SCANNED.put(getNd(), this.address, newFingerprint.getTime()); + HASHCODE_LAST_SCANNED.put(getNd(), this.address, newFingerprint.getHash()); + SIZE_LAST_SCANNED.put(getNd(), this.address, newFingerprint.getSize()); + } + + public void setPackageFragmentRoot(char[] javaRoot) { + JAVA_ROOT.put(getNd(), this.address, javaRoot); + } + + /** + * Returns the absolute path to the java root for this .jar or .class file. If this is a .jar file, it returns its + * own filename. + */ + public IString getPackageFragmentRoot() { + IString javaRoot = JAVA_ROOT.get(getNd(), this.address); + if (javaRoot.length() == 0) { + return getLocation(); + } + return javaRoot; + } + + public void markAsInvalid() { + TIME_LAST_SCANNED.put(getNd(), this.address, 0); + } + + public int getBindingCount() { + return ALL_NODES.size(getNd(), this.address); + } + + public List<NdBinding> getBindings() { + return ALL_NODES.asList(getNd(), this.address); + } + + public NdBinding getBinding(int index) { + return ALL_NODES.get(getNd(), this.address, index); + } + + public String toString() { + try { + return FILENAME.get(getNd(), this.address).toString(); + } catch (RuntimeException e) { + // This is called most often from the debugger, so we want to return something meaningful even + // if the code is buggy, the database is corrupt, or we don't have a read lock. + return super.toString(); + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTreeNode.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTreeNode.java new file mode 100644 index 000000000..d9e33db80 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTreeNode.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.db.IndexException; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +/** + * {@link NdTreeNode} elements form a tree of nodes rooted at a {@link NdResourceFile}. Each node contains a list of + * children which it declares and has a pointer to the most specific node which declares it. + */ +public abstract class NdTreeNode extends NdNode { + public static final FieldManyToOne<NdTreeNode> PARENT; + public static final FieldOneToMany<NdTreeNode> CHILDREN; + + @SuppressWarnings("hiding") + public static final StructDef<NdTreeNode> type; + + static { + type = StructDef.create(NdTreeNode.class, NdNode.type); + PARENT = FieldManyToOne.create(type, null); + CHILDREN = FieldOneToMany.create(type, PARENT, 16); + type.done(); + } + + public NdTreeNode(Nd nd, long address) { + super(nd, address); + } + + protected NdTreeNode(Nd nd, NdTreeNode parent) { + super(nd); + + PARENT.put(nd, this.address, parent == null ? 0 : parent.address); + } + + public int getChildrenCount() { + return CHILDREN.size(getNd(), this.address); + } + + public NdTreeNode getChild(int index) { + return CHILDREN.get(getNd(), this.address, index); + } + + /** + * Returns the closest ancestor of the given type, or null if none. Note that + * this looks for an exact match. It will not return subtypes of the given type. + */ + @SuppressWarnings("unchecked") + public <T extends NdTreeNode> T getAncestorOfType(Class<T> ancestorType) { + long targetType = getNd().getNodeType(ancestorType); + + Nd nd = getNd(); + long current = PARENT.getAddress(nd, this.address); + + while (current != 0) { + short currentType = NODE_TYPE.get(nd, current); + + if (currentType == targetType) { + NdNode result = load(nd, current); + + if (ancestorType.isInstance(result)) { + return (T) result; + } else { + throw new IndexException("The node at address " + current + //$NON-NLS-1$ + " should have been an instance of " + ancestorType.getName() + //$NON-NLS-1$ + " but was an instance of " + result.getClass().getName()); //$NON-NLS-1$ + } + } + + current = PARENT.getAddress(nd, current); + } + + return null; + } + + NdTreeNode getParentNode() { + return PARENT.get(getNd(), this.address); + } + + public NdBinding getParentBinding() throws IndexException { + NdNode parent= getParentNode(); + if (parent instanceof NdBinding) { + return (NdBinding) parent; + } + return null; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdType.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdType.java new file mode 100644 index 000000000..5d2836399 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdType.java @@ -0,0 +1,266 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.internal.core.nd.INdVisitor; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.IString; +import org.eclipse.jdt.internal.core.nd.field.FieldByte; +import org.eclipse.jdt.internal.core.nd.field.FieldLong; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.FieldString; +import org.eclipse.jdt.internal.core.nd.field.StructDef; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; + +public class NdType extends NdBinding { + public static final FieldManyToOne<NdTypeId> TYPENAME; + public static final FieldManyToOne<NdTypeSignature> SUPERCLASS; + public static final FieldOneToMany<NdTypeInterface> INTERFACES; + public static final FieldManyToOne<NdTypeId> DECLARING_TYPE; + public static final FieldManyToOne<NdMethodId> DECLARING_METHOD; + public static final FieldOneToMany<NdMethod> METHODS; + public static final FieldOneToMany<NdTypeAnnotationInType> TYPE_ANNOTATIONS; + public static final FieldOneToMany<NdAnnotationInType> ANNOTATIONS; + public static final FieldString MISSING_TYPE_NAMES; + public static final FieldString SOURCE_FILE_NAME; + public static final FieldString INNER_CLASS_SOURCE_NAME; + public static final FieldByte FLAGS; + public static final FieldLong TAG_BITS; + /** + * Binary name that was recorded in the .class file if different from the binary + * name that was determined by the .class's name and location. This is only set for + * .class files that have been moved to the wrong folder. + */ + public static final FieldString FIELD_DESCRIPTOR_FROM_CLASS; + + @SuppressWarnings("hiding") + public static final StructDef<NdType> type; + + static { + type = StructDef.create(NdType.class, NdBinding.type); + TYPENAME = FieldManyToOne.create(type, NdTypeId.TYPES); + DECLARING_TYPE = FieldManyToOne.create(type, NdTypeId.DECLARED_TYPES); + INTERFACES = FieldOneToMany.create(type, NdTypeInterface.APPLIES_TO); + SUPERCLASS = FieldManyToOne.create(type, NdTypeSignature.SUBCLASSES); + DECLARING_METHOD = FieldManyToOne.create(type, NdMethodId.DECLARED_TYPES); + METHODS = FieldOneToMany.create(type, NdMethod.PARENT, 6); + TYPE_ANNOTATIONS = FieldOneToMany.create(type, NdTypeAnnotationInType.OWNER); + ANNOTATIONS = FieldOneToMany.create(type, NdAnnotationInType.OWNER); + MISSING_TYPE_NAMES = type.addString(); + SOURCE_FILE_NAME = type.addString(); + INNER_CLASS_SOURCE_NAME = type.addString(); + FLAGS = type.addByte(); + TAG_BITS = type.addLong(); + FIELD_DESCRIPTOR_FROM_CLASS = type.addString(); + type.done(); + } + + public static final byte FLG_TYPE_ANONYMOUS = 0x0001; + public static final byte FLG_TYPE_LOCAL = 0x0002; + public static final byte FLG_TYPE_MEMBER = 0x0004; + public static final byte FLG_GENERIC_SIGNATURE_PRESENT = 0x0008; + + public NdType(Nd nd, long address) { + super(nd, address); + } + + public NdType(Nd nd, NdResourceFile resource) { + super(nd, resource); + } + + /** + * Called to populate the cache for the bindings in the class scope. + */ + public void acceptUncached(INdVisitor visitor) throws CoreException { + super.accept(visitor); + } + + public NdTypeId getTypeId() { + return TYPENAME.get(getNd(), this.address); + } + + public void setTypeId(NdTypeId typeId) { + TYPENAME.put(getNd(), this.address, typeId); + } + + /** + * Sets the source name for this type. + */ + public void setSourceNameOverride(char[] sourceName) { + char[] oldSourceName = getSourceName(); + if (!CharArrayUtils.equals(oldSourceName, sourceName)) { + INNER_CLASS_SOURCE_NAME.put(getNd(), this.address, sourceName); + } + } + + public IString getSourceNameOverride() { + return INNER_CLASS_SOURCE_NAME.get(getNd(), this.address); + } + + public long getResourceAddress() { + return FILE.getAddress(getNd(), this.address); + } + + public void setSuperclass(NdTypeSignature superclassTypeName) { + SUPERCLASS.put(getNd(), this.address, superclassTypeName); + } + + public NdTypeSignature getSuperclass() { + return SUPERCLASS.get(getNd(), this.address); + } + + public List<NdTypeInterface> getInterfaces() { + return INTERFACES.asList(getNd(), this.address); + } + + public NdResourceFile getResourceFile() { + return FILE.get(getNd(), this.address); + } + + public void setDeclaringMethod(NdMethodId createMethodId) { + DECLARING_METHOD.put(getNd(), this.address, createMethodId); + } + + /** + * @param createTypeIdFromBinaryName + */ + public void setDeclaringType(NdTypeId createTypeIdFromBinaryName) { + DECLARING_TYPE.put(getNd(), this.address, createTypeIdFromBinaryName); + } + + public NdTypeId getDeclaringType() { + return DECLARING_TYPE.get(getNd(), this.address); + } + + /** + * Sets the missing type names (if any) for this class. The names are encoded in a comma-separated list. + */ + public void setMissingTypeNames(char[] contents) { + MISSING_TYPE_NAMES.put(getNd(), this.address, contents); + } + + /** + * Returns the missing type names as a comma-separated list + */ + public IString getMissingTypeNames() { + return MISSING_TYPE_NAMES.get(getNd(), this.address); + } + + public void setSourceFileName(char[] sourceFileName) { + SOURCE_FILE_NAME.put(getNd(), this.address, sourceFileName); + } + + public IString getSourceFileName() { + return SOURCE_FILE_NAME.get(getNd(), this.address); + } + + public void setAnonymous(boolean anonymous) { + setFlag(FLG_TYPE_ANONYMOUS, anonymous); + } + + public void setIsLocal(boolean local) { + setFlag(FLG_TYPE_LOCAL, local); + } + + public void setIsMember(boolean member) { + setFlag(FLG_TYPE_MEMBER, member); + } + + public boolean isAnonymous() { + return getFlag(FLG_TYPE_ANONYMOUS); + } + + public boolean isLocal() { + return getFlag(FLG_TYPE_LOCAL); + } + + public boolean isMember() { + return getFlag(FLG_TYPE_MEMBER); + } + + public void setFlag(byte flagConstant, boolean value) { + int oldFlags = FLAGS.get(getNd(), this.address); + int newFlags = ((oldFlags & ~flagConstant) | (value ? flagConstant : 0)); + FLAGS.put(getNd(), this.address, (byte)newFlags); + } + + public boolean getFlag(byte flagConstant) { + return (FLAGS.get(getNd(), this.address) & flagConstant) != 0; + } + + public char[] getSourceName() { + IString sourceName = getSourceNameOverride(); + if (sourceName.length() != 0) { + return sourceName.getChars(); + } + char[] simpleName = getTypeId().getSimpleNameCharArray(); + return JavaNames.simpleNameToSourceName(simpleName); + } + + public NdMethodId getDeclaringMethod() { + return DECLARING_METHOD.get(getNd(), this.address); + } + + @Override + public List<NdTypeParameter> getTypeParameters() { + return TYPE_PARAMETERS.asList(getNd(), this.address); + } + + public List<NdTypeAnnotationInType> getTypeAnnotations() { + return TYPE_ANNOTATIONS.asList(getNd(), this.address); + } + + public List<NdAnnotationInType> getAnnotations() { + return ANNOTATIONS.asList(getNd(), this.address); + } + + public List<NdMethod> getMethods() { + return METHODS.asList(getNd(), this.address); + } + + @Override + public String toString() { + try { + return "class " + new String(getSourceName()); //$NON-NLS-1$ + } catch (RuntimeException e) { + return super.toString(); + } + } + + public void setTagBits(long tagBits) { + TAG_BITS.put(getNd(), this.address, tagBits); + } + + public long getTagBits() { + return TAG_BITS.get(getNd(), this.address); + } + + public void setFieldDescriptorFromClass(char[] fieldDescriptorFromClass) { + FIELD_DESCRIPTOR_FROM_CLASS.put(getNd(), this.address, fieldDescriptorFromClass); + } + + /** + * Returns the field descriptor for this type, based on the binary type information stored in the + * .class file itself. Note that this may differ from the field descriptor of this type's typeId in + * the event that the .class file has been moved. + */ + public IString getFieldDescriptor() { + IString descriptorFromClass = FIELD_DESCRIPTOR_FROM_CLASS.get(getNd(), this.address); + if (descriptorFromClass.length() == 0) { + return getTypeId().getFieldDescriptor(); + } + return descriptorFromClass; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeAnnotation.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeAnnotation.java new file mode 100644 index 000000000..e9bfa4a4d --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeAnnotation.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.field.FieldByte; +import org.eclipse.jdt.internal.core.nd.field.FieldPointer; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public class NdTypeAnnotation extends NdAnnotation { + public static final FieldByte TARGET_TYPE; + public static final FieldByte TARGET_ARG0; + public static final FieldByte TARGET_ARG1; + public static final FieldByte PATH_LENGTH; + public static final FieldPointer PATH; + + @SuppressWarnings("hiding") + public static final StructDef<NdTypeAnnotation> type; + + static { + type = StructDef.create(NdTypeAnnotation.class, NdAnnotation.type); + TARGET_TYPE = type.addByte(); + TARGET_ARG0 = type.addByte(); + TARGET_ARG1 = type.addByte(); + PATH_LENGTH = type.addByte(); + PATH = type.addPointer(); + type.done(); + } + + private static final byte[] NO_TYPE_PATH = new byte[0]; + + public NdTypeAnnotation(Nd nd, long address) { + super(nd, address); + } + + public NdTypeAnnotation(Nd nd) { + super(nd); + } + + public void setPath(byte[] path) { + freePath(); + Nd nd = getNd(); + PATH_LENGTH.put(nd, this.address, (byte)path.length); + if (path.length > 0) { + long pathArray = nd.getDB().malloc(path.length, Database.POOL_MISC); + PATH.put(nd, this.address, pathArray); + nd.getDB().putBytes(pathArray, path, path.length); + } + } + + public void setTargetInfo(int arg) { + TARGET_ARG0.put(getNd(), this.address, (byte)((arg >> 8) & 0xff)); + TARGET_ARG1.put(getNd(), this.address, (byte)(arg & 0xff)); + } + + public byte getTargetInfoArg0() { + return TARGET_ARG0.get(getNd(), this.address); + } + + public byte getTargetInfoArg1() { + return TARGET_ARG1.get(getNd(), this.address); + } + + public int getTarget() { + int arg0 = TARGET_ARG0.get(getNd(), this.address) & 0xff; + int arg1 = TARGET_ARG1.get(getNd(), this.address) & 0xff; + int result = (arg0 << 8) | arg1; + return result; + } + + public void setTargetInfo(byte arg0, byte arg1) { + TARGET_ARG0.put(getNd(), this.address, arg0); + TARGET_ARG1.put(getNd(), this.address, arg1); + } + + /** + * @param targetType one of the constants from {@link AnnotationTargetTypeConstants} + */ + public void setTargetType(int targetType) { + TARGET_TYPE.put(getNd(), this.address, (byte)targetType); + } + + /** + * @return one of the constants from {@link AnnotationTargetTypeConstants} + */ + public int getTargetType() { + return TARGET_TYPE.get(getNd(), this.address); + } + + public byte[] getTypePath() { + long pathPointer = PATH.get(getNd(), this.address); + if (pathPointer == 0) { + return NO_TYPE_PATH; + } + int pathLength = PATH_LENGTH.get(getNd(), this.address); + byte[] result = new byte[pathLength]; + getNd().getDB().getBytes(pathPointer, result); + return result; + } + + @Override + public void destruct() { + freePath(); + super.destruct(); + } + + private void freePath() { + Nd nd = getNd(); + long pathPointer = PATH.get(nd, this.address); + nd.getDB().free(pathPointer, Database.POOL_MISC); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeAnnotationInMethod.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeAnnotationInMethod.java new file mode 100644 index 000000000..894a1d549 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeAnnotationInMethod.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public class NdTypeAnnotationInMethod extends NdTypeAnnotation { + public static final FieldManyToOne<NdMethod> OWNER; + + @SuppressWarnings("hiding") + public static final StructDef<NdTypeAnnotationInMethod> type; + + static { + type = StructDef.create(NdTypeAnnotationInMethod.class, NdTypeAnnotation.type); + OWNER = FieldManyToOne.createOwner(type, NdMethod.TYPE_ANNOTATIONS); + type.done(); + } + + public NdTypeAnnotationInMethod(Nd nd, long address) { + super(nd, address); + } + + public NdTypeAnnotationInMethod(Nd nd, NdMethod variable) { + super(nd); + + OWNER.put(getNd(), this.address, variable); + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeAnnotationInType.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeAnnotationInType.java new file mode 100644 index 000000000..7aff10908 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeAnnotationInType.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public class NdTypeAnnotationInType extends NdTypeAnnotation { + public static final FieldManyToOne<NdType> OWNER; + + @SuppressWarnings("hiding") + public static final StructDef<NdTypeAnnotationInType> type; + + static { + type = StructDef.create(NdTypeAnnotationInType.class, NdTypeAnnotation.type); + OWNER = FieldManyToOne.createOwner(type, NdType.TYPE_ANNOTATIONS); + type.done(); + } + + public NdTypeAnnotationInType(Nd nd, long address) { + super(nd, address); + } + + public NdTypeAnnotationInType(Nd nd, NdType type) { + super(nd); + + OWNER.put(getNd(), this.address, type); + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeAnnotationInVariable.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeAnnotationInVariable.java new file mode 100644 index 000000000..eb591fe06 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeAnnotationInVariable.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public class NdTypeAnnotationInVariable extends NdTypeAnnotation { + public static final FieldManyToOne<NdVariable> OWNER; + + @SuppressWarnings("hiding") + public static final StructDef<NdTypeAnnotationInVariable> type; + + static { + type = StructDef.create(NdTypeAnnotationInVariable.class, NdTypeAnnotation.type); + OWNER = FieldManyToOne.createOwner(type, NdVariable.TYPE_ANNOTATIONS); + type.done(); + } + + public NdTypeAnnotationInVariable(Nd nd, long address) { + super(nd, address); + } + + public NdTypeAnnotationInVariable(Nd nd, NdVariable variable) { + super(nd); + + OWNER.put(getNd(), this.address, variable); + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeArgument.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeArgument.java new file mode 100644 index 000000000..37af73ac3 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeArgument.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.field.FieldByte; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; +import org.eclipse.jdt.internal.core.util.CharArrayBuffer; + +public class NdTypeArgument extends NdNode { + public static final FieldManyToOne<NdComplexTypeSignature> PARENT; + public static final FieldManyToOne<NdTypeSignature> TYPE_SIGNATURE; + public static final FieldByte WILDCARD; + + @SuppressWarnings("hiding") + public static final StructDef<NdTypeArgument> type; + + static { + type = StructDef.create(NdTypeArgument.class, NdNode.type); + PARENT = FieldManyToOne.createOwner(type, NdComplexTypeSignature.TYPE_ARGUMENTS); + TYPE_SIGNATURE = FieldManyToOne.create(type, NdTypeSignature.USED_AS_TYPE_ARGUMENT); + WILDCARD = type.addByte(); + type.done(); + } + + public static final int WILDCARD_NONE = 0; + public static final int WILDCARD_EXTENDS = 1; + public static final int WILDCARD_SUPER = 2; + public static final int WILDCARD_QUESTION = 3; + + public NdTypeArgument(Nd nd, long address) { + super(nd, address); + } + + public NdTypeArgument(Nd nd, NdComplexTypeSignature typeSignature) { + super(nd); + + PARENT.put(nd, this.address, typeSignature); + } + + /** + * Sets the wildcard to use, one of the WILDCARD_* constants. + * + * @param wildcard + */ + public void setWildcard(int wildcard) { + WILDCARD.put(getNd(), this.address, (byte) wildcard); + } + + public void setType(NdTypeSignature typeSignature) { + TYPE_SIGNATURE.put(getNd(), this.address, typeSignature); + } + + public int getWildcard() { + return WILDCARD.get(getNd(), this.address); + } + + public NdComplexTypeSignature getParent() { + return PARENT.get(getNd(), this.address); + } + + public NdTypeSignature getType() { + return TYPE_SIGNATURE.get(getNd(), this.address); + } + + public void getSignature(CharArrayBuffer result) { + switch (getWildcard()) { + case NdTypeArgument.WILDCARD_EXTENDS: result.append('-'); break; + case NdTypeArgument.WILDCARD_QUESTION: result.append('*'); return; + case NdTypeArgument.WILDCARD_SUPER: result.append('+'); break; + } + + NdTypeSignature theType = getType(); + if (theType != null) { + theType.getSignature(result); + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeBound.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeBound.java new file mode 100644 index 000000000..c6c827eee --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeBound.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; +import org.eclipse.jdt.internal.core.util.CharArrayBuffer; + +/** + * Represents the bound on a generic parameter (a ClassBound or InterfaceBound in + * the sense of the Java VM spec Java SE 8 Edition, section 4.7.9.1). + */ +public class NdTypeBound extends NdNode { + public static final FieldManyToOne<NdTypeParameter> PARENT; + public static final FieldManyToOne<NdTypeSignature> TYPE; + + @SuppressWarnings("hiding") + public static final StructDef<NdTypeBound> type; + + static { + type = StructDef.create(NdTypeBound.class, NdNode.type); + PARENT = FieldManyToOne.createOwner(type, NdTypeParameter.BOUNDS); + TYPE = FieldManyToOne.create(type, NdTypeSignature.USED_AS_TYPE_BOUND); + + type.done(); + } + + public NdTypeBound(Nd nd, long address) { + super(nd, address); + } + + public NdTypeBound(NdTypeParameter parent, NdTypeSignature signature) { + super(parent.getNd()); + + PARENT.put(getNd(), this.address, parent); + TYPE.put(getNd(), this.address, signature); + } + + public NdTypeParameter getParent() { + return PARENT.get(getNd(), this.address); + } + + public NdTypeSignature getType() { + return TYPE.get(getNd(), this.address); + } + + public void getSignature(CharArrayBuffer result) { + result.append(':'); + getType().getSignature(result); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeId.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeId.java new file mode 100644 index 000000000..a132f97f9 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeId.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.IString; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.FieldSearchKey; +import org.eclipse.jdt.internal.core.nd.field.StructDef; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; +import org.eclipse.jdt.internal.core.util.CharArrayBuffer; + +public class NdTypeId extends NdTypeSignature { + public static final FieldSearchKey<JavaIndex> FIELD_DESCRIPTOR; + public static final FieldSearchKey<JavaIndex> SIMPLE_NAME; + public static final FieldOneToMany<NdType> TYPES; + public static final FieldOneToMany<NdComplexTypeSignature> USED_AS_COMPLEX_TYPE; + public static final FieldOneToMany<NdType> DECLARED_TYPES; + + @SuppressWarnings("hiding") + public static final StructDef<NdTypeId> type; + + private String fName; + + static { + type = StructDef.create(NdTypeId.class, NdTypeSignature.type); + FIELD_DESCRIPTOR = FieldSearchKey.create(type, JavaIndex.TYPES); + SIMPLE_NAME = FieldSearchKey.create(type, JavaIndex.SIMPLE_INDEX); + TYPES = FieldOneToMany.create(type, NdType.TYPENAME, 2); + USED_AS_COMPLEX_TYPE = FieldOneToMany.create(type, NdComplexTypeSignature.RAW_TYPE); + DECLARED_TYPES = FieldOneToMany.create(type, NdType.DECLARING_TYPE); + type.useStandardRefCounting().done(); + } + + public NdTypeId(Nd nd, long address) { + super(nd, address); + } + + public NdTypeId(Nd nd, char[] fieldDescriptor) { + super(nd); + + char[] simpleName = JavaNames.fieldDescriptorToJavaName(fieldDescriptor, false); + FIELD_DESCRIPTOR.put(nd, this.address, fieldDescriptor); + SIMPLE_NAME.put(nd, this.address, simpleName); + } + + @Override + public List<NdType> getSubTypes() { + List<NdType> result = new ArrayList<>(); + result.addAll(super.getSubTypes()); + for (NdComplexTypeSignature next : getComplexTypes()) { + result.addAll(next.getSubTypes()); + } + return result; + } + + public List<NdComplexTypeSignature> getComplexTypes() { + return USED_AS_COMPLEX_TYPE.asList(getNd(), this.address); + } + + public NdType findTypeByResourceAddress(long resourceAddress) { + int size = TYPES.size(getNd(), this.address); + for (int idx = 0; idx < size; idx++) { + NdType next = TYPES.get(getNd(), this.address, idx); + + if (next.getResourceAddress() == resourceAddress) { + return next; + } + } + return null; + } + + public List<NdType> getTypes() { + return TYPES.asList(getNd(), this.address); + } + + /** + * Returns the field descriptor. + */ + public IString getFieldDescriptor() { + return FIELD_DESCRIPTOR.get(getNd(), this.address); + } + + public char[] getFieldDescriptorWithoutTrailingSemicolon() { + char[] fieldDescriptor = getFieldDescriptor().getChars(); + + int end = fieldDescriptor.length; + if (fieldDescriptor.length > 0 && fieldDescriptor[end - 1] == ';') { + end--; + } + + return CharArrayUtils.subarray(fieldDescriptor, 0, end); + } + + public char[] getBinaryName() { + return JavaNames.fieldDescriptorToBinaryName(getFieldDescriptor().getChars()); + } + + public IString getSimpleName() { + return SIMPLE_NAME.get(getNd(), this.address); + } + + public char[] getSimpleNameCharArray() { + if (this.fName == null) { + this.fName= getSimpleName().getString(); + } + return this.fName.toCharArray(); + } + + public boolean hasFieldDescriptor(String name) { + return this.getFieldDescriptor().compare(name, true) == 0; + } + + public boolean hasSimpleName(String name) { + if (this.fName != null) + return this.fName.equals(name); + + return getSimpleName().equals(name); + } + + public void setSimpleName(String name) { + if (Objects.equals(name, this.fName)) { + return; + } + this.fName = name; + SIMPLE_NAME.put(getNd(), this.address, name); + } + + public List<NdType> getDeclaredTypes() { + return DECLARED_TYPES.asList(getNd(), this.address); + } + + @Override + public NdTypeId getRawType() { + return this; + } + + @Override + public void getSignature(CharArrayBuffer result, boolean includeTrailingSemicolon) { + if (includeTrailingSemicolon) { + result.append(getFieldDescriptor().getChars()); + } else { + result.append(getFieldDescriptorWithoutTrailingSemicolon()); + } + } + + @Override + public boolean isTypeVariable() { + return false; + } + + @Override + public List<NdTypeSignature> getDeclaringTypeChain() { + return Collections.singletonList((NdTypeSignature)this); + } + + @Override + public NdTypeSignature getArrayDimensionType() { + return null; + } + + @Override + public List<NdTypeArgument> getTypeArguments() { + return Collections.emptyList(); + } + + @Override + public boolean isArrayType() { + return false; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeInterface.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeInterface.java new file mode 100644 index 000000000..ca8ae778f --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeInterface.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +/** + * Represents one interface implemented by a specific type. This is an intermediate object between a {@link NdType} and + * the {@link NdTypeId}s corresponding to its interfaces, which is necessary in order to implement the many-to-many + * relationship between them. + */ +public class NdTypeInterface extends NdNode { + public static final FieldManyToOne<NdType> APPLIES_TO; + public static final FieldManyToOne<NdTypeSignature> IMPLEMENTS; + + @SuppressWarnings("hiding") + public static StructDef<NdTypeInterface> type; + + static { + type = StructDef.create(NdTypeInterface.class, NdNode.type); + APPLIES_TO = FieldManyToOne.createOwner(type, NdType.INTERFACES); + IMPLEMENTS = FieldManyToOne.create(type, NdTypeSignature.IMPLEMENTATIONS); + type.done(); + } + + public NdTypeInterface(Nd nd, long address) { + super(nd, address); + } + + public NdTypeInterface(Nd nd, NdType targetType, NdTypeSignature makeTypeId) { + super(nd); + + APPLIES_TO.put(nd, this.address, targetType); + IMPLEMENTS.put(nd, this.address, makeTypeId); + } + + public NdType getImplementation() { + return APPLIES_TO.get(getNd(), this.address); + } + + public NdTypeSignature getInterface() { + return IMPLEMENTS.get(getNd(), this.address); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeParameter.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeParameter.java new file mode 100644 index 000000000..fda4676b1 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeParameter.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.List; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.field.FieldByte; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.FieldString; +import org.eclipse.jdt.internal.core.nd.field.StructDef; +import org.eclipse.jdt.internal.core.util.CharArrayBuffer; + +/** + * Represents a TypeParameter, as described in Section 4.7.9.1 of the java VM specification, Java SE 8 edititon. + */ +public class NdTypeParameter extends NdNode { + public static final FieldManyToOne<NdBinding> PARENT; + public static final FieldString IDENTIFIER; + public static final FieldOneToMany<NdTypeBound> BOUNDS; + public static final FieldByte TYPE_PARAMETER_FLAGS; + + public static final byte FLG_FIRST_BOUND_IS_A_CLASS = 0x01; + + @SuppressWarnings("hiding") + public static final StructDef<NdTypeParameter> type; + + static { + type = StructDef.create(NdTypeParameter.class, NdNode.type); + PARENT = FieldManyToOne.createOwner(type, NdBinding.TYPE_PARAMETERS); + IDENTIFIER = type.addString(); + BOUNDS = FieldOneToMany.create(type, NdTypeBound.PARENT); + TYPE_PARAMETER_FLAGS = type.addByte(); + + type.done(); + } + + public NdTypeParameter(Nd nd, long address) { + super(nd, address); + } + + public NdTypeParameter(NdBinding parent, char[] identifier) { + super(parent.getNd()); + + PARENT.put(getNd(), this.address, parent); + IDENTIFIER.put(getNd(), this.address, identifier); + } + + public char[] getIdentifier() { + return IDENTIFIER.get(getNd(), this.address).getChars(); + } + + public void setFirstBoundIsClass(boolean isClass) { + setFlag(FLG_FIRST_BOUND_IS_A_CLASS, isClass); + } + + public boolean isFirstBoundAClass() { + return (TYPE_PARAMETER_FLAGS.get(getNd(), this.address) & FLG_FIRST_BOUND_IS_A_CLASS) != 0; + } + + private void setFlag(byte flag, boolean value) { + byte oldValue = TYPE_PARAMETER_FLAGS.get(getNd(), this.address); + byte newValue; + if (value) { + newValue = (byte) (oldValue | flag); + } else { + newValue = (byte) (oldValue & ~flag); + } + TYPE_PARAMETER_FLAGS.put(getNd(), this.address, newValue); + } + + public List<NdTypeBound> getBounds() { + return BOUNDS.asList(getNd(), this.address); + } + + public void getSignature(CharArrayBuffer result) { + result.append(getIdentifier()); + + List<NdTypeBound> bounds = getBounds(); + + // If none of the bounds are classes and there is at least one bound, then insert a double-colon + // in the type signature. + if (!bounds.isEmpty() && !isFirstBoundAClass()) { + result.append(':'); + } + + for (NdTypeBound next : bounds) { + next.getSignature(result); + } + } + + public static void getSignature(CharArrayBuffer buffer, List<NdTypeParameter> params) { + if (!params.isEmpty()) { + buffer.append('<'); + for (NdTypeParameter next : params) { + next.getSignature(buffer); + } + buffer.append('>'); + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeSignature.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeSignature.java new file mode 100644 index 000000000..207963684 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdTypeSignature.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.StructDef; +import org.eclipse.jdt.internal.core.util.CharArrayBuffer; + +/** + * Corresponds roughly to a JavaTypeSignature, as described in section 4.7.9.1 of the Java VM spec version 4, with the + * addition of annotations and backpointers to locations where the type is used. + * <p> + * Holds back-pointers to all the entities that refer to the name, along with pointers to all classes that have this + * name. Note that this isn't the class declaration itself. The same index can hold multiple jar files, some of which + * may contain classes with the same name. All classes that use this fully-qualified name point to the same + * {@link NdTypeSignature}. + * <p> + * Other entities should refer to a type via its TypeId if there is any possiblity that the type may change based on the + * classpath. It should refer to the type directly if there is no possibility for a type lookup. For example, nested + * classes refer to their enclosing class directly since they live in the same file and there is no possibility for the + * enclosing class to change based on the classpath. Classes refer to their base class via its TypeId since the parent + * class might live in a different jar and need to be resolved on the classpath. + */ +public abstract class NdTypeSignature extends NdNode { + public static final FieldOneToMany<NdType> SUBCLASSES; + public static final FieldOneToMany<NdAnnotation> ANNOTATIONS_OF_THIS_TYPE; + public static final FieldOneToMany<NdTypeInterface> IMPLEMENTATIONS; + public static final FieldOneToMany<NdVariable> VARIABLES_OF_TYPE; + public static final FieldOneToMany<NdConstantClass> USED_AS_CONSTANT; + public static final FieldOneToMany<NdConstantEnum> USED_AS_ENUM_CONSTANT; + public static final FieldOneToMany<NdTypeArgument> USED_AS_TYPE_ARGUMENT; + public static final FieldOneToMany<NdTypeBound> USED_AS_TYPE_BOUND; + public static final FieldOneToMany<NdMethodParameter> USED_AS_METHOD_ARGUMENT; + public static final FieldOneToMany<NdMethodException> USED_AS_EXCEPTION; + public static final FieldOneToMany<NdMethod> USED_AS_RETURN_TYPE; + + @SuppressWarnings("hiding") + public static StructDef<NdTypeSignature> type; + + static { + type = StructDef.createAbstract(NdTypeSignature.class, NdNode.type); + SUBCLASSES = FieldOneToMany.create(type, NdType.SUPERCLASS); + ANNOTATIONS_OF_THIS_TYPE = FieldOneToMany.create(type, NdAnnotation.ANNOTATION_TYPE); + IMPLEMENTATIONS = FieldOneToMany.create(type, NdTypeInterface.IMPLEMENTS); + VARIABLES_OF_TYPE = FieldOneToMany.create(type, NdVariable.TYPE); + USED_AS_CONSTANT = FieldOneToMany.create(type, NdConstantClass.VALUE); + USED_AS_ENUM_CONSTANT = FieldOneToMany.create(type, NdConstantEnum.ENUM_TYPE); + USED_AS_TYPE_ARGUMENT = FieldOneToMany.create(type, NdTypeArgument.TYPE_SIGNATURE); + USED_AS_TYPE_BOUND = FieldOneToMany.create(type, NdTypeBound.TYPE); + USED_AS_METHOD_ARGUMENT = FieldOneToMany.create(type, NdMethodParameter.ARGUMENT_TYPE); + USED_AS_EXCEPTION = FieldOneToMany.create(type, NdMethodException.EXCEPTION_TYPE); + USED_AS_RETURN_TYPE = FieldOneToMany.create(type, NdMethod.RETURN_TYPE); + type.useStandardRefCounting().done(); + } + + public NdTypeSignature(Nd nd, long address) { + super(nd, address); + } + + public NdTypeSignature(Nd nd) { + super(nd); + } + + public List<NdType> getSubclasses() { + return SUBCLASSES.asList(getNd(), this.address); + } + + public List<NdTypeInterface> getImplementations() { + return IMPLEMENTATIONS.asList(getNd(), this.address); + } + + /** + * Returns all subclasses (for classes) and implementations (for interfaces) of this type + */ + public List<NdType> getSubTypes() { + List<NdType> result = new ArrayList<>(); + result.addAll(getSubclasses()); + + for (NdTypeInterface next : getImplementations()) { + result.add(next.getImplementation()); + } + + return result; + } + + /** + * Returns the raw version of this type, if one exists. That is, the version of this type + * without any generic arguments or annotations, which the java runtime sees. Returns null + * of this signature doesn't have a raw type, for example if it is a type variable. + */ + public abstract NdTypeId getRawType(); + + public final void getSignature(CharArrayBuffer result) { + getSignature(result, true); + } + + public abstract void getSignature(CharArrayBuffer result, boolean includeTrailingSemicolon); + + /** + * Returns true iff this is an array type signature (ie: that getArrayDimensionType() will return a non-null + * answer). Note that this only returns true for the type signature that holds the reference to the array dimension + * type. The raw type for that signature will return false, even though it has a field descriptor starting with '['. + * <p> + * In other words: + * + * <pre> + * NdVariable someVariable = getSomeVariableWithAnArrayType() + * System.out.println(someVariable.getType().isArrayType()); // true + * System.out.println(someVariable.getType().getRawType().isArrayType()); // false + * </pre> + */ + public abstract boolean isArrayType(); + + public abstract boolean isTypeVariable(); + + /** + * Returns the chain of declaring generic types. The first element in the chain is a top-level type and the + * receiver is the last element in the chain. + */ + public abstract List<NdTypeSignature> getDeclaringTypeChain(); + + /** + * If the receiver is an array type, it returns the signature of the array's next dimension. Returns null if + * this is not an array type. + */ + public abstract NdTypeSignature getArrayDimensionType(); + + /** + * Returns the type arguments for this type signature, if any. Returns the empty list if none. + */ + public abstract List<NdTypeArgument> getTypeArguments(); + + public String toString() { + try { + CharArrayBuffer result = new CharArrayBuffer(); + getSignature(result); + return result.toString(); + } catch (RuntimeException e) { + // This is called most often from the debugger, so we want to return something meaningful even + // if the code is buggy, the database is corrupt, or we don't have a read lock. + return super.toString(); + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdVariable.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdVariable.java new file mode 100644 index 000000000..e85f805ac --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdVariable.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.List; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.IString; +import org.eclipse.jdt.internal.core.nd.field.FieldByte; +import org.eclipse.jdt.internal.core.nd.field.FieldInt; +import org.eclipse.jdt.internal.core.nd.field.FieldLong; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; +import org.eclipse.jdt.internal.core.nd.field.FieldOneToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldString; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +public class NdVariable extends NdBinding { + public static final FieldManyToOne<NdTypeSignature> TYPE; + public static final FieldInt VARIABLE_ID; + public static final FieldManyToOne<NdMethod> DECLARING_METHOD; + public static final FieldManyToOne<NdBinding> PARENT; + public static final FieldString NAME; + public static final FieldOneToOne<NdConstant> CONSTANT; + public static final FieldLong TAG_BITS; + public static final FieldByte VARIABLE_FLAGS; + public static final FieldOneToMany<NdAnnotationInVariable> ANNOTATIONS; + public static final FieldOneToMany<NdTypeAnnotationInVariable> TYPE_ANNOTATIONS; + + @SuppressWarnings("hiding") + public static StructDef<NdVariable> type; + + public static final byte FLG_GENERIC_SIGNATURE_PRESENT = 0x01; + + static { + type = StructDef.create(NdVariable.class, NdBinding.type); + TYPE = FieldManyToOne.create(type, NdTypeSignature.VARIABLES_OF_TYPE); + VARIABLE_ID = type.addInt(); + DECLARING_METHOD = FieldManyToOne.create(type, NdMethod.DECLARED_VARIABLES); + PARENT = FieldManyToOne.create(type, NdBinding.VARIABLES); + NAME = type.addString(); + CONSTANT = FieldOneToOne.create(type, NdConstant.class, NdConstant.PARENT_VARIABLE); + TAG_BITS = type.addLong(); + VARIABLE_FLAGS = type.addByte(); + ANNOTATIONS = FieldOneToMany.create(type, NdAnnotationInVariable.OWNER); + TYPE_ANNOTATIONS = FieldOneToMany.create(type, NdTypeAnnotationInVariable.OWNER); + type.done(); + } + + public NdVariable(Nd nd, long bindingRecord) { + super(nd, bindingRecord); + } + + public NdVariable(NdBinding parent) { + super(parent.getNd(), parent.getFile()); + + PARENT.put(getNd(), this.address, parent); + } + + public boolean hasVariableFlag(int toTest) { + return (VARIABLE_FLAGS.get(getNd(), this.address) & toTest) != 0; + } + + public void setVariableFlag(byte toSet) { + int newFlags = VARIABLE_FLAGS.get(getNd(), this.address) | toSet; + VARIABLE_FLAGS.put(getNd(), this.address, (byte)newFlags); + } + + public void setName(char[] name) { + NAME.put(getNd(), this.address, name); + } + + public IString getName() { + return NAME.get(getNd(), this.address); + } + + public void setType(NdTypeSignature typeId) { + TYPE.put(getNd(), this.address, typeId); + } + + public void setConstant(NdConstant constant) { + CONSTANT.put(getNd(), this.address, constant); + } + + public NdConstant getConstant() { + return CONSTANT.get(getNd(), this.address); + } + + public NdTypeSignature getType() { + return TYPE.get(getNd(), this.address); + } + + public long getTagBits() { + return TAG_BITS.get(getNd(), this.address); + } + + public void setTagBits(long tagBits) { + TAG_BITS.put(getNd(), this.address, tagBits); + } + + public List<NdTypeAnnotationInVariable> getTypeAnnotations() { + return TYPE_ANNOTATIONS.asList(getNd(), this.address); + } + + public List<NdAnnotationInVariable> getAnnotations() { + return ANNOTATIONS.asList(getNd(), this.address); + } + + public String toString() { + try { + StringBuilder result = new StringBuilder(); + NdTypeSignature localType = getType(); + if (localType != null) { + result.append(localType.toString()); + result.append(" "); //$NON-NLS-1$ + } + IString name = getName(); + if (name != null) { + result.append(name.toString()); + } + NdConstant constant = getConstant(); + if (constant != null) { + result.append(" = "); //$NON-NLS-1$ + result.append(constant.toString()); + } + return result.toString(); + } catch (RuntimeException e) { + // This is called most often from the debugger, so we want to return something meaningful even + // if the code is buggy, the database is corrupt, or we don't have a read lock. + return super.toString(); + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdWorkspaceLocation.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdWorkspaceLocation.java new file mode 100644 index 000000000..8e52b8bfd --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/NdWorkspaceLocation.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.db.IString; +import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; +import org.eclipse.jdt.internal.core.nd.field.FieldString; +import org.eclipse.jdt.internal.core.nd.field.StructDef; + +/** + * Holds a location in the Eclipse workspace where a given resource was found. Note that a given + * resource might be mapped to multiple locations in the workspace. + */ +public class NdWorkspaceLocation extends NdNode { + public static final FieldManyToOne<NdResourceFile> RESOURCE; + public static final FieldString PATH; + + @SuppressWarnings("hiding") + public static final StructDef<NdWorkspaceLocation> type; + + static { + type = StructDef.create(NdWorkspaceLocation.class, NdNode.type); + RESOURCE = FieldManyToOne.createOwner(type, NdResourceFile.WORKSPACE_MAPPINGS); + PATH = type.addString(); + type.done(); + } + + public NdWorkspaceLocation(Nd nd, long address) { + super(nd, address); + } + + public NdWorkspaceLocation(Nd nd, NdResourceFile resource, char[] path) { + super(nd); + + RESOURCE.put(getNd(), this.address, resource); + PATH.put(getNd(), this.address, path); + } + + public IString getPath() { + return PATH.get(getNd(), this.address); + } + + public NdResourceFile getResourceFile() { + return RESOURCE.get(getNd(), this.address); + } + + public String toString() { + try { + return getPath().toString(); + } catch (RuntimeException e) { + // This is called most often from the debugger, so we want to return something meaningful even + // if the code is buggy, the database is corrupt, or we don't have a read lock. + return super.toString(); + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/Package.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/Package.java new file mode 100644 index 000000000..9903317bc --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/Package.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.JavaCore; + +/* package */ class Package { + public static String PLUGIN_ID = JavaCore.PLUGIN_ID; + + public static void log(Throwable e) { + String msg= e.getMessage(); + if (msg == null) { + log("Error", e); //$NON-NLS-1$ + } else { + log("Error: " + msg, e); //$NON-NLS-1$ + } + } + + public static void log(String message, Throwable e) { + log(createStatus(message, e)); + } + + public static IStatus createStatus(String msg, Throwable e) { + return new Status(IStatus.ERROR, PLUGIN_ID, msg, e); + } + + public static IStatus createStatus(String msg) { + return new Status(IStatus.ERROR, PLUGIN_ID, msg); + } + + public static void log(IStatus status) { + JavaCore.getPlugin().getLog().log(status); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/TagTreeReader.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/TagTreeReader.java new file mode 100644 index 000000000..9ad5a6095 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/TagTreeReader.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.Database; +import org.eclipse.jdt.internal.core.nd.db.IndexException; + +public abstract class TagTreeReader { + public static final int[] UNUSED_RESULT = new int[1]; + + public static abstract class TagHandler<T> { + abstract public T read(Nd nd, long address, TagTreeReader reader, int[] bytesRead); + abstract public void write(Nd nd, long address, TagTreeReader reader, T toWrite, int[] bytesWritten); + abstract public int getSize(Nd nd, T object, TagTreeReader reader); + public void destruct(Nd nd, long address, TagTreeReader reader) { + // Nothing to do by default + } + } + + public static abstract class FixedSizeTagHandler<T> extends TagHandler<T> { + protected abstract T read(Nd nd, long address); + protected abstract void write(Nd nd, long address, T value); + protected abstract int getSize(); + protected void destruct(Nd nd, long address) { + // Nothing to do by default + } + + public final T read(Nd nd, long address, TagTreeReader reader, int[] bytesRead) { + bytesRead[0] = getSize(); + return read(nd, address); + } + + @Override + public final void write(Nd nd, long address, TagTreeReader reader, T value, int[] bytesWritten) { + bytesWritten[0] = getSize(); + write(nd, address, value); + } + + @Override + public final int getSize(Nd nd, T object, TagTreeReader reader) { + return getSize(); + } + + @Override + public final void destruct(Nd nd, long address, TagTreeReader reader) { + destruct(nd, address); + } + } + + private TagHandler<?> readers[] = new TagHandler[256]; + private Map<TagHandler<?>, Integer> values = new HashMap<>(); + + public final void add(byte key, TagHandler<?> reader) { + this.readers[key] = reader; + this.values.put(reader, (int) key); + } + + public final Object read(Nd nd, long address) { + return read(nd, address, UNUSED_RESULT); + } + + public final Object read(Nd nd, long address, int[] bytesRead) { + long readAddress = address; + Database db = nd.getDB(); + byte nextByte = db.getByte(address); + readAddress += Database.BYTE_SIZE; + TagHandler<?> reader = this.readers[nextByte]; + if (reader == null) { + throw new IndexException("Found unknown tag with value " + nextByte + " at address " + address); //$NON-NLS-1$//$NON-NLS-2$ + } + + return reader.read(nd, readAddress, this, bytesRead); + } + + protected abstract byte getKeyFor(Object toWrite); + + public final void write(Nd nd, long address, Object toWrite) { + write(nd, address, toWrite, UNUSED_RESULT); + } + + @SuppressWarnings("unchecked") + public final void write(Nd nd, long address, Object toWrite, int[] bytesWritten) { + byte key = getKeyFor(toWrite); + + @SuppressWarnings("rawtypes") + TagHandler handler = this.readers[key]; + + if (handler == null) { + throw new IndexException("Invalid key " + key + " returned from getKeyFor(...)"); //$NON-NLS-1$//$NON-NLS-2$ + } + + handler.write(nd, address, this, toWrite, bytesWritten); + } + + public final void destruct(Nd nd, long address) { + Database db = nd.getDB(); + long readAddress = address; + byte nextByte = db.getByte(readAddress); + readAddress += Database.BYTE_SIZE; + + TagHandler<?> handler = this.readers[nextByte]; + if (handler == null) { + throw new IndexException("Found unknown tag with value " + nextByte + " at address " + address); //$NON-NLS-1$//$NON-NLS-2$ + } + + handler.destruct(nd, readAddress, this); + } + + @SuppressWarnings("unchecked") + public final int getSize(Nd nd, Object toMeasure) { + byte key = getKeyFor(toMeasure); + + @SuppressWarnings("rawtypes") + TagHandler handler = this.readers[key]; + if (handler == null) { + throw new IndexException("Attempted to get size of object " + toMeasure.toString() + " with unknown key " //$NON-NLS-1$//$NON-NLS-2$ + + key); + } + + return handler.getSize(nd, toMeasure, this); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/TypeRef.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/TypeRef.java new file mode 100644 index 000000000..4e65b4b05 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/TypeRef.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java; + +import java.util.List; +import java.util.function.Supplier; + +import org.eclipse.jdt.internal.core.nd.DatabaseRef; +import org.eclipse.jdt.internal.core.nd.IReader; +import org.eclipse.jdt.internal.core.nd.Nd; + +/** + * Holds a reference to an NdType that can be retained while releasing and reacquiring a read lock. + */ +public final class TypeRef implements Supplier<NdType> { + final DatabaseRef<NdType> ref; + final char[] fileName; + final char[] fieldDescriptor; + final TypeSupplier typeSupplier = new TypeSupplier(); + private final class TypeSupplier implements Supplier<NdType> { + public TypeSupplier() { + } + + @Override + public NdType get() { + NdTypeId typeId = JavaIndex.getIndex(TypeRef.this.ref.getNd()).findType(TypeRef.this.fieldDescriptor); + + if (typeId == null) { + return null; + } + + List<NdType> implementations = typeId.getTypes(); + for (NdType next : implementations) { + NdResourceFile nextResourceFile = next.getResourceFile(); + if (nextResourceFile.getLocation().compare(TypeRef.this.fileName, false) == 0) { + if (nextResourceFile.isDoneIndexing()) { + return next; + } + } + } + return null; + } + } + + private TypeRef(NdType type) { + super(); + this.fieldDescriptor = type.getTypeId().getRawType().getFieldDescriptor().getChars(); + this.fileName = type.getResourceFile().getLocation().getChars(); + this.ref = new DatabaseRef<NdType>(type.getNd(), this.typeSupplier, type); + } + + private TypeRef(Nd nd, char[] resourcePath, char[] fieldDescriptor) { + super(); + this.fieldDescriptor = fieldDescriptor; + this.fileName = resourcePath; + this.ref = new DatabaseRef<NdType>(nd, this.typeSupplier); + } + + public char[] getFieldDescriptor() { + return this.fieldDescriptor; + } + + public char[] getFileName() { + return this.fileName; + } + + /** + * Creates a {@link DatabaseRef} to the given {@link NdType}. + */ + public static TypeRef create(NdType type) { + return new TypeRef(type); + } + + /** + * Creates a {@link DatabaseRef} to the {@link NdType} with the given resource path and field descriptor. + */ + public static TypeRef create(Nd nd, char[] resourcePath, char[] fieldDescriptor) { + return new TypeRef(nd, resourcePath, fieldDescriptor); + } + + public IReader lock() { + return this.ref.lock(); + } + + @Override + public NdType get() { + return this.ref.get(); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeDescriptor.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeDescriptor.java new file mode 100644 index 000000000..393537b21 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeDescriptor.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java.model; + +import org.eclipse.jdt.internal.compiler.env.IDependent; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; + +/** + * Holds a lightweight identifier for an IBinaryType, with sufficient information to either read it from + * disk or read it from the index. + */ +public final class BinaryTypeDescriptor { + public final char[] indexPath; + public final char[] fieldDescriptor; + public final char[] location; + public final char[] workspacePath; + + /** + * Constructs a new descriptor + * + * @param location + * location where the archive (.jar or .class) can be found in the local filesystem + * @param fieldDescriptor + * field descriptor for the type (see the JVM specification) + * @param workspacePath + * location where the archive (.jar or class) can be found in the workspace. If it is not in the + * workspace, this is the path where it can be found on the local filesystem. + * @param indexPath + * index path for the new type (workspace-or-local path to jar optionally followed by a | and a relative + * path within the .jar) + */ + public BinaryTypeDescriptor(char[] location, char[] fieldDescriptor, char[] workspacePath, char[] indexPath) { + super(); + this.location = location; + this.fieldDescriptor = fieldDescriptor; + this.indexPath = indexPath; + this.workspacePath = workspacePath; + } + + public boolean isInJarFile() { + return CharArrayUtils.indexOf(IDependent.JAR_FILE_ENTRY_SEPARATOR, this.indexPath) != -1; + } +}
\ No newline at end of file diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeFactory.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeFactory.java new file mode 100644 index 000000000..d6d3981a7 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeFactory.java @@ -0,0 +1,228 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java.model; + +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.IClassFile; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaModelStatusConstants; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.env.IDependent; +import org.eclipse.jdt.internal.compiler.util.SuffixConstants; +import org.eclipse.jdt.internal.core.ClassFile; +import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; +import org.eclipse.jdt.internal.core.JavaModelManager; +import org.eclipse.jdt.internal.core.PackageFragment; +import org.eclipse.jdt.internal.core.nd.IReader; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.db.IndexException; +import org.eclipse.jdt.internal.core.nd.java.JavaIndex; +import org.eclipse.jdt.internal.core.nd.java.JavaNames; +import org.eclipse.jdt.internal.core.nd.java.NdResourceFile; +import org.eclipse.jdt.internal.core.nd.java.NdType; +import org.eclipse.jdt.internal.core.nd.java.TypeRef; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; +import org.eclipse.jdt.internal.core.util.Util; + +public class BinaryTypeFactory { + public static final class NotInIndexException extends Exception { + private static final long serialVersionUID = 2859848007651528256L; + + public NotInIndexException() { + } + } + + private final static char[] PACKAGE_INFO = "package-info".toCharArray(); //$NON-NLS-1$ + + /** + * Returns a descriptor for the given class within the given package fragment, or null if the fragment doesn't have + * a location on the filesystem. + */ + private static BinaryTypeDescriptor createDescriptor(PackageFragment pkg, ClassFile classFile) { + String name = classFile.getName(); + IJavaElement root = pkg.getParent(); + IPath location = JavaIndex.getLocationForElement(root); + String entryName = Util.concatWith(pkg.names, classFile.getElementName(), '/'); + char[] fieldDescriptor = CharArrayUtils.concat(new char[] { 'L' }, + Util.concatWith(pkg.names, name, '/').toCharArray(), new char[] { ';' }); + IPath workspacePath = root.getPath(); + String indexPath; + + if (location == null) { + return null; + } + + if (root instanceof JarPackageFragmentRoot) { + // The old version returned this, but it doesn't conform to the spec on IBinaryType.getFileName(): + indexPath = root.getHandleIdentifier() + IDependent.JAR_FILE_ENTRY_SEPARATOR + entryName; + // Version that conforms to the JavaDoc spec on IBinaryType.getFileName() -- note that this breaks + // InlineMethodTests in the JDT UI project. Need to investigate why before using it. + //indexPath = workspacePath.toString() + IDependent.JAR_FILE_ENTRY_SEPARATOR + entryName; + } else { + location = location.append(entryName); + indexPath = workspacePath.append(entryName).toString(); + workspacePath = classFile.resource().getFullPath(); + } + + return new BinaryTypeDescriptor(location.toString().toCharArray(), fieldDescriptor, + workspacePath.toString().toCharArray(), indexPath.toCharArray()); + } + + public static BinaryTypeDescriptor createDescriptor(IClassFile classFile) { + ClassFile concreteClass = (ClassFile)classFile; + PackageFragment parent = (PackageFragment) classFile.getParent(); + + return createDescriptor(parent, concreteClass); + } + + public static BinaryTypeDescriptor createDescriptor(IType type) { + return createDescriptor(type.getClassFile()); + } + + public static IBinaryType create(IClassFile classFile, IProgressMonitor monitor) throws JavaModelException, ClassFormatException { + BinaryTypeDescriptor descriptor = createDescriptor(classFile); + return readType(descriptor, monitor); + } + + /** + * Reads the given binary type. If the type can be found in the index with a fingerprint that exactly matches + * the file on disk, the type is read from the index. Otherwise the type is read from disk. Returns null if + * no such type exists. + * @throws ClassFormatException + */ + public static IBinaryType readType(BinaryTypeDescriptor descriptor, + IProgressMonitor monitor) throws JavaModelException, ClassFormatException { + + if (JavaIndex.isEnabled()) { + try { + return readFromIndex(JavaIndex.getIndex(), descriptor, monitor); + } catch (NotInIndexException e) { + // fall back to reading the zip file, below + } + } + + return rawReadType(descriptor, true); + } + + /** + * Read the class file from disk, circumventing the index's cache. This should only be used by callers + * that need to read information from the class file which aren't present in the index (such as method bodies). + * + * @return the newly-created ClassFileReader or null if the given class file does not exist. + * @throws ClassFormatException if the class file existed but was corrupt + * @throws JavaModelException if unable to read the class file due to a transient failure + */ + public static ClassFileReader rawReadType(BinaryTypeDescriptor descriptor, boolean fullyInitialize) throws JavaModelException, ClassFormatException { + if (descriptor == null) { + return null; + } + if (descriptor.isInJarFile()) { + ZipFile zip = null; + try { + zip = JavaModelManager.getJavaModelManager().getZipFile(new Path(new String(descriptor.workspacePath))); + char[] entryNameCharArray = CharArrayUtils.concat( + JavaNames.fieldDescriptorToBinaryName(descriptor.fieldDescriptor), SuffixConstants.SUFFIX_class); + String entryName = new String(entryNameCharArray); + ZipEntry ze = zip.getEntry(entryName); + if (ze != null) { + byte contents[]; + try { + contents = org.eclipse.jdt.internal.compiler.util.Util.getZipEntryByteContent(ze, zip); + } catch (IOException ioe) { + throw new JavaModelException(ioe, IJavaModelStatusConstants.IO_EXCEPTION); + } + return new ClassFileReader(contents, descriptor.indexPath, fullyInitialize); + } + } catch (CoreException e) { + throw new JavaModelException(e); + } finally { + JavaModelManager.getJavaModelManager().closeZipFile(zip); + } + } else { + IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(new String(descriptor.workspacePath))); + byte[] contents = Util.getResourceContentsAsByteArray(file); + return new ClassFileReader(contents, file.getFullPath().toString().toCharArray(), fullyInitialize); + } + return null; + } + + /** + * Tries to read the given IBinaryType from the index. The return value is lightweight and may be cached + * with minimal memory cost. Returns an IBinaryType if the type was found in the index and the index + * was up-to-date. Throws a NotInIndexException if the index does not contain an up-to-date cache of the + * requested file. Returns null if the index contains an up-to-date cache of the requested file and it was + * able to determine that the requested class does not exist in that file. + */ + public static IBinaryType readFromIndex(JavaIndex index, BinaryTypeDescriptor descriptor, IProgressMonitor monitor) throws JavaModelException, NotInIndexException { + char[] className = JavaNames.fieldDescriptorToSimpleName(descriptor.fieldDescriptor); + + // If the new index is enabled, check if we have this class file cached in the index already + char[] fieldDescriptor = descriptor.fieldDescriptor; + + if (!CharArrayUtils.equals(PACKAGE_INFO, className)) { + Nd nd = index.getNd(); + + // We don't currently cache package-info files in the index + if (descriptor.location != null) { + // Acquire a read lock on the index + try (IReader lock = nd.acquireReadLock()) { + try { + TypeRef typeRef = TypeRef.create(nd, descriptor.location, fieldDescriptor); + NdType type = typeRef.get(); + + if (type == null) { + // If we couldn't find the type in the index, determine whether the cause is + // that the type is known not to exist or whether the resource just hasn't + // been indexed yet + + NdResourceFile resourceFile = index.getResourceFile(descriptor.location); + if (index.isUpToDate(resourceFile)) { + return null; + } + throw new NotInIndexException(); + } + NdResourceFile resourceFile = type.getResourceFile(); + if (index.isUpToDate(resourceFile)) { + IndexBinaryType result = new IndexBinaryType(typeRef, descriptor.indexPath); + + // We already have the database lock open and have located the element, so we may as + // well prefetch the inexpensive attributes. + result.initSimpleAttributes(); + + return result; + } + throw new NotInIndexException(); + } catch (CoreException e) { + throw new JavaModelException(e); + } + } catch (IndexException e) { + // Index corrupted. Rebuild it. + index.rebuildIndex(); + } + } + } + + throw new NotInIndexException(); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/ITypeAnnotationBuilder.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/ITypeAnnotationBuilder.java new file mode 100644 index 000000000..39e950133 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/ITypeAnnotationBuilder.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java.model; + +import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; +import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; + +public interface ITypeAnnotationBuilder { + ITypeAnnotationBuilder toField(); + ITypeAnnotationBuilder toThrows(int rank); + ITypeAnnotationBuilder toTypeArgument(int rank); + ITypeAnnotationBuilder toMethodParameter(short index); + ITypeAnnotationBuilder toSupertype(short index); + ITypeAnnotationBuilder toTypeParameterBounds(boolean isClassTypeParameter, int parameterRank); + ITypeAnnotationBuilder toTypeBound(short boundIndex); + ITypeAnnotationBuilder toTypeParameter(boolean isClassTypeParameter, int rank); + ITypeAnnotationBuilder toMethodReturn(); + ITypeAnnotationBuilder toReceiver(); + ITypeAnnotationBuilder toWildcardBound(); + ITypeAnnotationBuilder toNextArrayDimension(); + ITypeAnnotationBuilder toNextNestedType(); + + IBinaryTypeAnnotation build(IBinaryAnnotation annotation); +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryField.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryField.java new file mode 100644 index 000000000..3c1f7634c --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryField.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java.model; + +import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; +import org.eclipse.jdt.internal.compiler.env.IBinaryField; +import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; +import org.eclipse.jdt.internal.compiler.impl.Constant; + +public class IndexBinaryField implements IBinaryField { + private int modifiers; + private IBinaryAnnotation[] annotations; + private IBinaryTypeAnnotation[] typeAnnotations; + private Constant constant; + private char[] genericSignature; + private char[] name; + private long tagBits; + private char[] typeName; + + public IndexBinaryField(IBinaryAnnotation[] annotations, Constant constant, char[] genericSignature, int modifiers, + char[] name, long tagBits, IBinaryTypeAnnotation[] typeAnnotations, char[] fieldDescriptor) { + super(); + this.modifiers = modifiers; + this.annotations = annotations; + this.typeAnnotations = typeAnnotations; + this.constant = constant; + this.genericSignature = genericSignature; + this.name = name; + this.tagBits = tagBits; + this.typeName = fieldDescriptor; + } + + @Override + public int getModifiers() { + return this.modifiers; + } + + @Override + public IBinaryAnnotation[] getAnnotations() { + return this.annotations; + } + + @Override + public IBinaryTypeAnnotation[] getTypeAnnotations() { + return this.typeAnnotations; + } + + @Override + public Constant getConstant() { + return this.constant; + } + + @Override + public char[] getGenericSignature() { + return this.genericSignature; + } + + @Override + public char[] getName() { + return this.name; + } + + @Override + public long getTagBits() { + return this.tagBits; + } + + @Override + public char[] getTypeName() { + return this.typeName; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryMethod.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryMethod.java new file mode 100644 index 000000000..5ce3c4cd5 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryMethod.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java.model; + +import org.eclipse.jdt.internal.compiler.classfmt.BinaryTypeFormatter; +import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; +import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; +import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; + +public final class IndexBinaryMethod implements IBinaryMethod { + private int modifiers; + private boolean isConstructor; + private char[][] argumentNames; + private IBinaryAnnotation[] annotations; + private Object defaultValue; + private char[][] exceptionTypeNames; + private char[] genericSignature; + private char[] methodDescriptor; + private IBinaryAnnotation[][] parameterAnnotations; + private char[] selector; + private long tagBits; + private boolean isClInit; + private IBinaryTypeAnnotation[] typeAnnotations; + + public static IndexBinaryMethod create() { + return new IndexBinaryMethod(); + } + + public IndexBinaryMethod setModifiers(int modifiers) { + this.modifiers = modifiers; + return this; + } + + public IndexBinaryMethod setIsConstructor(boolean isConstructor) { + this.isConstructor = isConstructor; + return this; + } + + public IndexBinaryMethod setArgumentNames(char[][] argumentNames) { + this.argumentNames = argumentNames; + return this; + } + + public IndexBinaryMethod setAnnotations(IBinaryAnnotation[] annotations) { + this.annotations = annotations; + return this; + } + + public IndexBinaryMethod setDefaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + return this; + } + + public IndexBinaryMethod setExceptionTypeNames(char[][] exceptionTypeNames) { + this.exceptionTypeNames = exceptionTypeNames; + return this; + } + + public IndexBinaryMethod setGenericSignature(char[] genericSignature) { + this.genericSignature = genericSignature; + return this; + } + + public IndexBinaryMethod setMethodDescriptor(char[] methodDescriptor) { + this.methodDescriptor = methodDescriptor; + return this; + } + + public IndexBinaryMethod setParameterAnnotations(IBinaryAnnotation[][] parameterAnnotations) { + this.parameterAnnotations = parameterAnnotations; + return this; + } + + public IndexBinaryMethod setSelector(char[] selector) { + this.selector = selector; + return this; + } + + public IndexBinaryMethod setTagBits(long tagBits) { + this.tagBits = tagBits; + return this; + } + + public IndexBinaryMethod setIsClInit(boolean isClInit) { + this.isClInit = isClInit; + return this; + } + + public IndexBinaryMethod setTypeAnnotations(IBinaryTypeAnnotation[] typeAnnotations) { + this.typeAnnotations = typeAnnotations; + return this; + } + + @Override + public int getModifiers() { + return this.modifiers; + } + + @Override + public boolean isConstructor() { + return this.isConstructor; + } + + @Override + public char[][] getArgumentNames() { + return this.argumentNames; + } + + @Override + public IBinaryAnnotation[] getAnnotations() { + return this.annotations; + } + + @Override + public Object getDefaultValue() { + return this.defaultValue; + } + + @Override + public char[][] getExceptionTypeNames() { + return this.exceptionTypeNames; + } + + @Override + public char[] getGenericSignature() { + return this.genericSignature; + } + + @Override + public char[] getMethodDescriptor() { + return this.methodDescriptor; + } + + @Override + public IBinaryAnnotation[] getParameterAnnotations(int index, char[] classFileName) { + if (this.parameterAnnotations == null || this.parameterAnnotations.length <= index) { + return null; + } + return this.parameterAnnotations[index]; + } + + @Override + public int getAnnotatedParametersCount() { + if (this.parameterAnnotations == null) { + return 0; + } + return this.parameterAnnotations.length; + } + + @Override + public char[] getSelector() { + return this.selector; + } + + @Override + public long getTagBits() { + return this.tagBits; + } + + @Override + public boolean isClinit() { + return this.isClInit; + } + + @Override + public IBinaryTypeAnnotation[] getTypeAnnotations() { + return this.typeAnnotations; + } + + @Override + public String toString() { + return BinaryTypeFormatter.methodToString(this); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryNestedType.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryNestedType.java new file mode 100644 index 000000000..40d8a5402 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryNestedType.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java.model; + +import org.eclipse.jdt.internal.compiler.env.IBinaryNestedType; + +public class IndexBinaryNestedType implements IBinaryNestedType { + private char[] enclosingTypeName; + private char[] name; + private int modifiers; + + public IndexBinaryNestedType(char[] name, char[] enclosingTypeName, int modifiers) { + super(); + this.name = name; + this.enclosingTypeName = enclosingTypeName; + this.modifiers = modifiers; + } + + @Override + public char[] getEnclosingTypeName() { + return this.enclosingTypeName; + } + + @Override + public int getModifiers() { + return this.modifiers; + } + + @Override + public char[] getName() { + return this.name; + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryType.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryType.java new file mode 100644 index 000000000..aaa7576a2 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryType.java @@ -0,0 +1,672 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java.model; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.classfmt.BinaryTypeFormatter; +import org.eclipse.jdt.internal.compiler.classfmt.ElementValuePairInfo; +import org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants; +import org.eclipse.jdt.internal.compiler.env.ClassSignature; +import org.eclipse.jdt.internal.compiler.env.EnumConstantSignature; +import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; +import org.eclipse.jdt.internal.compiler.env.IBinaryElementValuePair; +import org.eclipse.jdt.internal.compiler.env.IBinaryField; +import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; +import org.eclipse.jdt.internal.compiler.env.IBinaryNestedType; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; +import org.eclipse.jdt.internal.compiler.env.ITypeAnnotationWalker; +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding.ExternalAnnotationStatus; +import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; +import org.eclipse.jdt.internal.core.nd.IReader; +import org.eclipse.jdt.internal.core.nd.db.IString; +import org.eclipse.jdt.internal.core.nd.java.JavaNames; +import org.eclipse.jdt.internal.core.nd.java.NdAnnotation; +import org.eclipse.jdt.internal.core.nd.java.NdAnnotationValuePair; +import org.eclipse.jdt.internal.core.nd.java.NdConstant; +import org.eclipse.jdt.internal.core.nd.java.NdConstantAnnotation; +import org.eclipse.jdt.internal.core.nd.java.NdConstantArray; +import org.eclipse.jdt.internal.core.nd.java.NdConstantClass; +import org.eclipse.jdt.internal.core.nd.java.NdConstantEnum; +import org.eclipse.jdt.internal.core.nd.java.NdMethod; +import org.eclipse.jdt.internal.core.nd.java.NdMethodException; +import org.eclipse.jdt.internal.core.nd.java.NdMethodId; +import org.eclipse.jdt.internal.core.nd.java.NdMethodParameter; +import org.eclipse.jdt.internal.core.nd.java.NdResourceFile; +import org.eclipse.jdt.internal.core.nd.java.NdType; +import org.eclipse.jdt.internal.core.nd.java.NdTypeAnnotation; +import org.eclipse.jdt.internal.core.nd.java.NdTypeId; +import org.eclipse.jdt.internal.core.nd.java.NdTypeInterface; +import org.eclipse.jdt.internal.core.nd.java.NdTypeParameter; +import org.eclipse.jdt.internal.core.nd.java.NdTypeSignature; +import org.eclipse.jdt.internal.core.nd.java.NdVariable; +import org.eclipse.jdt.internal.core.nd.java.TypeRef; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; +import org.eclipse.jdt.internal.core.util.CharArrayBuffer; + +/** + * Implementation of {@link IBinaryType} that reads all its content from the index + */ +public class IndexBinaryType implements IBinaryType { + private final TypeRef typeRef; + + private boolean simpleAttributesInitialized; + private char[] enclosingMethod; + private char[] enclosingType; + private char[] fileName; + private char[] superclassName; + private int modifiers; + private boolean isAnonymous; + private boolean isLocal; + private boolean isMember; + + private long tagBits; + + private char[] binaryTypeName; + + private static final IBinaryAnnotation[] NO_ANNOTATIONS = new IBinaryAnnotation[0]; + private static final int[] NO_PATH = new int[0]; + + public IndexBinaryType(TypeRef type, char[] indexPath) { + this.typeRef = type; + this.fileName = indexPath; + } + + public boolean exists() { + return this.typeRef.get() != null; + } + + @Override + public int getModifiers() { + initSimpleAttributes(); + + return this.modifiers; + } + + @Override + public boolean isBinaryType() { + return true; + } + + @Override + public char[] getFileName() { + return this.fileName; + } + + @Override + public IBinaryAnnotation[] getAnnotations() { + try (IReader rl = this.typeRef.lock()) { + NdType type = this.typeRef.get(); + if (type != null) { + return toAnnotationArray(this.typeRef.get().getAnnotations()); + } else { + return NO_ANNOTATIONS; + } + } + } + + private static IBinaryAnnotation[] toAnnotationArray(List<? extends NdAnnotation> annotations) { + if (annotations.isEmpty()) { + return NO_ANNOTATIONS; + } + IBinaryAnnotation[] result = new IBinaryAnnotation[annotations.size()]; + + for (int idx = 0; idx < result.length; idx++) { + result[idx] = createBinaryAnnotation(annotations.get(idx)); + } + return result; + } + + @Override + public IBinaryTypeAnnotation[] getTypeAnnotations() { + try (IReader rl = this.typeRef.lock()) { + NdType type = this.typeRef.get(); + if (type != null) { + return createBinaryTypeAnnotations(type.getTypeAnnotations()); + } + } + return null; + } + + @Override + public char[] getEnclosingMethod() { + initSimpleAttributes(); + + return this.enclosingMethod; + } + + @Override + public char[] getEnclosingTypeName() { + initSimpleAttributes(); + + return this.enclosingType; + } + + @Override + public IBinaryField[] getFields() { + try (IReader rl = this.typeRef.lock()) { + NdType type = this.typeRef.get(); + if (type != null) { + List<NdVariable> variables = type.getVariables(); + + if (variables.isEmpty()) { + return null; + } + + IBinaryField[] result = new IBinaryField[variables.size()]; + for (int fieldIdx = 0; fieldIdx < variables.size(); fieldIdx++) { + result[fieldIdx] = createBinaryField(variables.get(fieldIdx)); + } + return result; + } else { + return null; + } + } + } + + @Override + public char[] getGenericSignature() { + try (IReader rl = this.typeRef.lock()) { + NdType type = this.typeRef.get(); + if (type != null) { + if (!type.getFlag(NdType.FLG_GENERIC_SIGNATURE_PRESENT)) { + return null; + } + CharArrayBuffer buffer = new CharArrayBuffer(); + NdTypeParameter.getSignature(buffer, type.getTypeParameters()); + NdTypeSignature superclass = type.getSuperclass(); + if (superclass != null) { + superclass.getSignature(buffer); + } + for (NdTypeInterface nextInterface : type.getInterfaces()) { + nextInterface.getInterface().getSignature(buffer); + } + return buffer.getContents(); + } else { + return null; + } + } + } + + @Override + public char[][] getInterfaceNames() { + try (IReader rl = this.typeRef.lock()) { + NdType type = this.typeRef.get(); + if (type != null) { + List<NdTypeInterface> interfaces = type.getInterfaces(); + + if (interfaces.isEmpty()) { + return null; + } + + char[][] result = new char[interfaces.size()][]; + for (int idx = 0; idx < interfaces.size(); idx++) { + NdTypeSignature nextInterface = interfaces.get(idx).getInterface(); + + result[idx] = nextInterface.getRawType().getBinaryName(); + } + return result; + } else { + return null; + } + } + } + + @Override + public IBinaryNestedType[] getMemberTypes() { + try (IReader rl = this.typeRef.lock()) { + NdType type = this.typeRef.get(); + if (type != null) { + List<NdType> declaredTypes = type.getTypeId().getDeclaredTypes(); + if (declaredTypes.isEmpty()) { + return null; + } + + NdResourceFile resFile = type.getResourceFile(); + IString javaRoot = resFile.getPackageFragmentRoot(); + + // Filter out all the declared types which are at different java roots (only keep the ones belonging + // to the same .jar file or to another .class file in the same folder). + List<IBinaryNestedType> result = new ArrayList<>(); + for (NdType next : declaredTypes) { + NdResourceFile nextResFile = next.getResourceFile(); + + if (nextResFile.getPackageFragmentRoot().compare(javaRoot, true) == 0) { + result.add(createBinaryNestedType(next)); + } + } + return result.isEmpty() ? null : result.toArray(new IBinaryNestedType[result.size()]); + } else { + return null; + } + } + } + + private IBinaryNestedType createBinaryNestedType(NdType next) { + return new IndexBinaryNestedType(next.getTypeId().getBinaryName(), next.getDeclaringType().getBinaryName(), + next.getModifiers()); + } + + @Override + public IBinaryMethod[] getMethods() { + try (IReader rl = this.typeRef.lock()) { + NdType type = this.typeRef.get(); + if (type != null) { + List<NdMethod> methods = type.getMethods(); + + if (methods.isEmpty()) { + return null; + } + + IBinaryMethod[] result = new IBinaryMethod[methods.size()]; + for (int idx = 0; idx < result.length; idx++) { + result[idx] = createBinaryMethod(methods.get(idx)); + } + + return result; + } else { + return null; + } + } + } + + @Override + public char[][][] getMissingTypeNames() { + try (IReader rl = this.typeRef.lock()) { + NdType type = this.typeRef.get(); + if (type != null) { + IString string = type.getMissingTypeNames(); + if (string.length() == 0) { + return null; + } + char[] missingTypeNames = string.getChars(); + char[][] paths = CharOperation.splitOn(',', missingTypeNames); + char[][][] result = new char[paths.length][][]; + for (int idx = 0; idx < paths.length; idx++) { + result[idx] = CharOperation.splitOn('/', paths[idx]); + } + return result; + } else { + return null; + } + } + } + + @Override + public char[] getName() { + initSimpleAttributes(); + + return this.binaryTypeName; + } + + @Override + public char[] getSourceName() { + try (IReader rl = this.typeRef.lock()) { + NdType type = this.typeRef.get(); + if (type != null) { + return type.getSourceName(); + } else { + return new char[0]; + } + } + } + + @Override + public char[] getSuperclassName() { + initSimpleAttributes(); + + return this.superclassName; + } + + @Override + public long getTagBits() { + initSimpleAttributes(); + + return this.tagBits; + } + + @Override + public boolean isAnonymous() { + initSimpleAttributes(); + + return this.isAnonymous; + } + + @Override + public boolean isLocal() { + initSimpleAttributes(); + + return this.isLocal; + } + + @Override + public boolean isMember() { + initSimpleAttributes(); + + return this.isMember; + } + + @Override + public char[] sourceFileName() { + try (IReader rl = this.typeRef.lock()) { + NdType type = this.typeRef.get(); + if (type != null) { + char[] result = type.getSourceFileName().getChars(); + if (result.length == 0) { + return null; + } + return result; + } else { + return null; + } + } + } + + @Override + public ITypeAnnotationWalker enrichWithExternalAnnotationsFor(ITypeAnnotationWalker walker, Object member, + LookupEnvironment environment) { + return walker; + } + + private IBinaryMethod createBinaryMethod(NdMethod ndMethod) { + NdMethodId methodId = ndMethod.getMethodId(); + + return IndexBinaryMethod.create().setAnnotations(toAnnotationArray(ndMethod.getAnnotations())) + .setModifiers(ndMethod.getModifiers()).setIsConstructor(methodId.isConstructor()) + .setArgumentNames(getArgumentNames(ndMethod)).setDefaultValue(unpackValue(ndMethod.getDefaultValue())) + .setExceptionTypeNames(getExceptionTypeNames(ndMethod)) + .setGenericSignature(getGenericSignatureFor(ndMethod)) + .setMethodDescriptor(methodId.getMethodDescriptor()) + .setParameterAnnotations(getParameterAnnotations(ndMethod)) + .setSelector(ndMethod.getMethodId().getSelector()).setTagBits(ndMethod.getTagBits()) + .setIsClInit(methodId.isClInit()).setTypeAnnotations(createBinaryTypeAnnotations(ndMethod.getTypeAnnotations())); + } + + private static IBinaryTypeAnnotation[] createBinaryTypeAnnotations(List<? extends NdTypeAnnotation> typeAnnotations) { + if (typeAnnotations.isEmpty()) { + return null; + } + IBinaryTypeAnnotation[] result = new IBinaryTypeAnnotation[typeAnnotations.size()]; + int idx = 0; + for (NdTypeAnnotation next : typeAnnotations) { + IBinaryAnnotation annotation = createBinaryAnnotation(next); + int[] typePath = getTypePath(next.getTypePath()); + int info = 0; + int info2 = 0; + switch (next.getTargetType()) { + case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER: + case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER: + info = next.getTargetInfoArg0(); + break; + case AnnotationTargetTypeConstants.CLASS_EXTENDS: + info = next.getTarget(); + break; + case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER_BOUND: + case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER_BOUND: + info = next.getTargetInfoArg0(); + info2 = next.getTargetInfoArg1(); + break; + case AnnotationTargetTypeConstants.FIELD: + case AnnotationTargetTypeConstants.METHOD_RETURN: + case AnnotationTargetTypeConstants.METHOD_RECEIVER: + break; + case AnnotationTargetTypeConstants.METHOD_FORMAL_PARAMETER : + info = next.getTargetInfoArg0(); + break; + case AnnotationTargetTypeConstants.THROWS : + info = next.getTarget(); + break; + + default: + throw new IllegalStateException("Target type not handled " + next.getTargetType()); //$NON-NLS-1$ + } + result[idx++] = new IndexBinaryTypeAnnotation(next.getTargetType(), info, info2, typePath, annotation); + } + return result; + } + + private static int[] getTypePath(byte[] typePath) { + if (typePath.length == 0) { + return NO_PATH; + } + int[] result = new int[typePath.length]; + for (int idx = 0; idx < typePath.length; idx++) { + result[idx] = typePath[idx]; + } + return result; + } + + private static char[] getGenericSignatureFor(NdMethod method) { + if (!method.hasAllFlags(NdMethod.FLG_GENERIC_SIGNATURE_PRESENT)) { + return null; + } + CharArrayBuffer result = new CharArrayBuffer(); + method.getGenericSignature(result, method.hasAllFlags(NdMethod.FLG_THROWS_SIGNATURE_PRESENT)); + return result.getContents(); + } + + private char[][] getArgumentNames(NdMethod ndMethod) { + // Unlike what its JavaDoc says, IBinaryType returns an empty array if no argument names are available, so + // we replicate this weird undocumented corner case here. + char[][] result = ndMethod.getParameterNames(); + int lastNonEmpty = -1; + for (int idx = 0; idx < result.length; idx++) { + if (result[idx] != null && result[idx].length != 0) { + lastNonEmpty = idx; + } + } + + if (lastNonEmpty != result.length - 1) { + char[][] newResult = new char[lastNonEmpty + 1][]; + System.arraycopy(result, 0, newResult, 0, lastNonEmpty + 1); + return newResult; + } + return result; + } + + private IBinaryAnnotation[][] getParameterAnnotations(NdMethod ndMethod) { + List<NdMethodParameter> parameters = ndMethod.getMethodParameters(); + if (parameters.isEmpty()) { + return null; + } + + IBinaryAnnotation[][] result = new IBinaryAnnotation[parameters.size()][]; + for (int idx = 0; idx < parameters.size(); idx++) { + NdMethodParameter next = parameters.get(idx); + + result[idx] = toAnnotationArray(next.getAnnotations()); + } + + // int newLength = result.length; + // while (newLength > 0 && result[newLength - 1] == null) { + // --newLength; + // } + // + // if (newLength < result.length) { + // if (newLength == 0) { + // return null; + // } + // IBinaryAnnotation[][] newResult = new IBinaryAnnotation[newLength][]; + // System.arraycopy(result, 0, newResult, 0, newLength); + // result = newResult; + // } + + return result; + } + + private char[][] getExceptionTypeNames(NdMethod ndMethod) { + List<NdMethodException> exceptions = ndMethod.getExceptions(); + + // Although the JavaDoc for IBinaryMethod says that the exception list will be null if empty, + // the implementation in MethodInfo returns an empty array rather than null. We copy the + // same behavior here in case something is relying on it. Uncomment the following if the "null" + // version is deemed correct. + + // if (exceptions.isEmpty()) { + // return null; + // } + + char[][] result = new char[exceptions.size()][]; + for (int idx = 0; idx < exceptions.size(); idx++) { + NdMethodException next = exceptions.get(idx); + + result[idx] = next.getExceptionType().getRawType().getBinaryName(); + } + return result; + } + + public static IBinaryField createBinaryField(NdVariable ndVariable) { + char[] name = ndVariable.getName().getChars(); + Constant constant = null; + NdConstant ndConstant = ndVariable.getConstant(); + if (ndConstant != null) { + constant = ndConstant.getConstant(); + } + if (constant == null) { + constant = Constant.NotAConstant; + } + + NdTypeSignature type = ndVariable.getType(); + + IBinaryTypeAnnotation[] typeAnnotationArray = createBinaryTypeAnnotations(ndVariable.getTypeAnnotations()); + + IBinaryAnnotation[] annotations = toAnnotationArray(ndVariable.getAnnotations()); + + CharArrayBuffer signature = new CharArrayBuffer(); + if (ndVariable.hasVariableFlag(NdVariable.FLG_GENERIC_SIGNATURE_PRESENT)) { + type.getSignature(signature); + } + + long tagBits = ndVariable.getTagBits(); + return new IndexBinaryField(annotations, constant, signature.getContents(), ndVariable.getModifiers(), name, + tagBits, typeAnnotationArray, type.getRawType().getFieldDescriptor().getChars()); + } + + public static IBinaryAnnotation createBinaryAnnotation(NdAnnotation ndAnnotation) { + List<NdAnnotationValuePair> elementValuePairs = ndAnnotation.getElementValuePairs(); + + final IBinaryElementValuePair[] resultingPair = new IBinaryElementValuePair[elementValuePairs.size()]; + + for (int idx = 0; idx < elementValuePairs.size(); idx++) { + NdAnnotationValuePair next = elementValuePairs.get(idx); + + resultingPair[idx] = new ElementValuePairInfo(next.getName().getChars(), unpackValue(next.getValue())); + } + + final char[] binaryName = JavaNames.fieldDescriptorToBinaryName( + ndAnnotation.getType().getRawType().getFieldDescriptor().getChars()); + + return new IBinaryAnnotation() { + @Override + public char[] getTypeName() { + return binaryName; + } + + @Override + public IBinaryElementValuePair[] getElementValuePairs() { + return resultingPair; + } + + @Override + public String toString() { + return BinaryTypeFormatter.annotationToString(this); + } + }; + } + + public void initSimpleAttributes() { + if (!this.simpleAttributesInitialized) { + this.simpleAttributesInitialized = true; + + try (IReader rl = this.typeRef.lock()) { + NdType type = this.typeRef.get(); + if (type != null) { + NdMethodId methodId = type.getDeclaringMethod(); + + if (methodId != null) { + char[] methodName = methodId.getMethodName().getChars(); + int startIdx = CharArrayUtils.lastIndexOf('#', methodName); + this.enclosingMethod = CharArrayUtils.subarray(methodName, startIdx + 1); + this.enclosingType = CharArrayUtils.subarray(methodName, 1, startIdx); + } else { + NdTypeId typeId = type.getDeclaringType(); + + if (typeId != null) { + this.enclosingType = typeId.getBinaryName(); + } + } + + this.modifiers = type.getModifiers(); + this.isAnonymous = type.isAnonymous(); + this.isLocal = type.isLocal(); + this.isMember = type.isMember(); + this.tagBits = type.getTagBits(); + + NdTypeSignature superclass = type.getSuperclass(); + if (superclass != null) { + this.superclassName = superclass.getRawType().getBinaryName(); + } else { + this.superclassName = null; + } + + this.binaryTypeName = JavaNames.fieldDescriptorToBinaryName(type.getFieldDescriptor().getChars()); + } else { + this.binaryTypeName = JavaNames.fieldDescriptorToBinaryName(this.typeRef.getFieldDescriptor()); + } + } + } + } + + private static Object unpackValue(NdConstant value) { + if (value == null) { + return null; + } + if (value instanceof NdConstantAnnotation) { + NdConstantAnnotation annotation = (NdConstantAnnotation) value; + + return createBinaryAnnotation(annotation.getValue()); + } + if (value instanceof NdConstantArray) { + NdConstantArray array = (NdConstantArray) value; + + List<NdConstant> arrayContents = array.getValue(); + + Object[] result = new Object[arrayContents.size()]; + for (int idx = 0; idx < arrayContents.size(); idx++) { + result[idx] = unpackValue(arrayContents.get(idx)); + } + return result; + } + if (value instanceof NdConstantEnum) { + NdConstantEnum ndConstantEnum = (NdConstantEnum) value; + + NdTypeSignature signature = ndConstantEnum.getType(); + + return new EnumConstantSignature(signature.getRawType().getBinaryName(), ndConstantEnum.getValue()); + } + if (value instanceof NdConstantClass) { + NdConstantClass constant = (NdConstantClass) value; + + return new ClassSignature(constant.getValue().getRawType().getBinaryName()); + } + + return value.getConstant(); + } + + @Override + public ExternalAnnotationStatus getExternalAnnotationStatus() { + return ExternalAnnotationStatus.NOT_EEA_CONFIGURED; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryTypeAnnotation.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryTypeAnnotation.java new file mode 100644 index 000000000..521e17bd4 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/IndexBinaryTypeAnnotation.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java.model; + +import org.eclipse.jdt.internal.compiler.classfmt.BinaryTypeFormatter; +import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; +import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; + +public class IndexBinaryTypeAnnotation implements IBinaryTypeAnnotation { + private int targetType; + + // info is used in different ways: + // TargetType 0x00: CLASS_TYPE_PARAMETER: type parameter index + // TargetType 0x01: METHOD_TYPE_PARAMETER: type parameter index + // TargetType 0x10: CLASS_EXTENDS: supertype index (-1 = superclass, 0..N superinterface) + // TargetType 0x11: CLASS_TYPE_PARAMETER_BOUND: type parameter index + // TargetType 0x12: METHOD_TYPE_PARAMETER_BOUND: type parameter index + // TargetType 0x16: METHOD_FORMAL_PARAMETER: method formal parameter index + // TargetType 0x17: THROWS: throws type index + private int info; + + // TargetType 0x11: CLASS_TYPE_PARAMETER_BOUND: bound index + // TargetType 0x12: METHOD_TYPE_PARAMETER_BOUND: bound index + private int info2; + + + private int[] typePath; + private IBinaryAnnotation annotation; + + public IndexBinaryTypeAnnotation(int targetType, int info, int info2, int[] typePath, IBinaryAnnotation annotation) { + this.targetType = targetType; + this.info = info; + this.info2 = info2; + this.typePath = typePath; + this.annotation = annotation; + } + + @Override + public IBinaryAnnotation getAnnotation() { + return this.annotation; + } + + @Override + public int getTargetType() { + return this.targetType; + } + + @Override + public int[] getTypePath() { + return this.typePath; + } + + @Override + public int getSupertypeIndex() { + return this.info; + } + + @Override + public int getTypeParameterIndex() { + return this.info; +} + + @Override + public int getBoundIndex() { + return this.info2; + } + + @Override + public int getMethodFormalParameterIndex() { + return this.info; + } + + @Override + public int getThrowsTypeIndex() { + return this.info; + } + + @Override + public String toString() { + return BinaryTypeFormatter.annotationToString(this); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/TypeAnnotationBuilder.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/TypeAnnotationBuilder.java new file mode 100644 index 000000000..0b8d7d4ed --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/TypeAnnotationBuilder.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.java.model; + +import org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants; +import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; +import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; + +public class TypeAnnotationBuilder implements ITypeAnnotationBuilder { + TypeAnnotationBuilder parent; + int kind; + int index; + int length; + int target; + int targetParameter; + int targetParameter2; + + private TypeAnnotationBuilder(TypeAnnotationBuilder parent, int kind, int index, + int length, int nextTarget, int nextTargetParameter, int nextTargetParameter2) { + super(); + this.parent = parent; + this.kind = kind; + this.index = index; + this.length = length; + this.target = nextTarget; + this.targetParameter = nextTargetParameter; + this.targetParameter2 = nextTargetParameter2; + } + + public static TypeAnnotationBuilder create() { + return new TypeAnnotationBuilder(null, 0, 0, 0, -1, -1, -1); + } + + private TypeAnnotationBuilder walk(int nextKind, int nextIndex) { + return new TypeAnnotationBuilder(this, nextKind, nextIndex, this.length+1, this.target, this.targetParameter, this.targetParameter2); + } + + private TypeAnnotationBuilder toTarget(int newTarget) { + return new TypeAnnotationBuilder(this.parent, this.kind, this.index, this.length, newTarget, this.targetParameter, this.targetParameter2); + } + + private TypeAnnotationBuilder toTarget(int newTarget, int parameter) { + return new TypeAnnotationBuilder(this.parent, this.kind, this.index, this.length, newTarget, parameter, this.targetParameter2); + } + + private TypeAnnotationBuilder toTarget2(int parameter) { + return new TypeAnnotationBuilder(this.parent, this.kind, this.index, this.length, this.target, this.targetParameter, parameter); + } + + @Override + public ITypeAnnotationBuilder toField() { + return toTarget(AnnotationTargetTypeConstants.FIELD); + } + + @Override + public ITypeAnnotationBuilder toMethodReturn() { + return toTarget(AnnotationTargetTypeConstants.METHOD_RETURN); + } + + @Override + public ITypeAnnotationBuilder toReceiver() { + return toTarget(AnnotationTargetTypeConstants.METHOD_RECEIVER); + } + + @Override + public ITypeAnnotationBuilder toTypeParameter(boolean isClassTypeParameter, int rank) { + int targetType = isClassTypeParameter ? AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER + : AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER; + return toTarget(targetType, rank); + } + + @Override + public ITypeAnnotationBuilder toTypeParameterBounds(boolean isClassTypeParameter, int parameterRank) { + int targetType = isClassTypeParameter ? + AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER_BOUND : AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER_BOUND; + + return toTarget(targetType, parameterRank); + } + + @Override + public ITypeAnnotationBuilder toTypeBound(short boundIndex) { + return toTarget2(boundIndex); + } + + @Override + public ITypeAnnotationBuilder toSupertype(short superTypeIndex) { + return toTarget(AnnotationTargetTypeConstants.CLASS_EXTENDS, superTypeIndex); + } + + @Override + public ITypeAnnotationBuilder toMethodParameter(short parameterIndex) { + return toTarget(AnnotationTargetTypeConstants.METHOD_FORMAL_PARAMETER, parameterIndex); + } + + @Override + public ITypeAnnotationBuilder toThrows(int rank) { + return toTarget(AnnotationTargetTypeConstants.THROWS, rank); + } + + @Override + public ITypeAnnotationBuilder toTypeArgument(int rank) { + return walk(AnnotationTargetTypeConstants.TYPE_ARGUMENT, rank); + } + + @Override + public ITypeAnnotationBuilder toWildcardBound() { + return walk(AnnotationTargetTypeConstants.WILDCARD_BOUND, 0); + } + + @Override + public ITypeAnnotationBuilder toNextArrayDimension() { + return walk(AnnotationTargetTypeConstants.NEXT_ARRAY_DIMENSION, 0); + } + + @Override + public ITypeAnnotationBuilder toNextNestedType() { + return walk(AnnotationTargetTypeConstants.NEXT_NESTED_TYPE, 0); + } + + @Override + public IBinaryTypeAnnotation build(IBinaryAnnotation annotation) { + return new IndexBinaryTypeAnnotation(this.target, this.targetParameter, this.targetParameter2, getTypePath(), annotation); + } + + private int[] getTypePath() { + if (this.length == 0) { + return IBinaryTypeAnnotation.NO_TYPE_PATH; + } + + int[] result = new int[this.length * 2]; + + TypeAnnotationBuilder next = this; + while (next != null && next.length > 0) { + int writeIdx = (next.length - 1) * 2; + result[writeIdx] = next.kind; + result[writeIdx + 1] = next.index; + next = next.parent; + } + + return result; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/util/CharArrayMap.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/util/CharArrayMap.java new file mode 100644 index 000000000..eed0a5a74 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/util/CharArrayMap.java @@ -0,0 +1,312 @@ +/******************************************************************************* + * Copyright (c) 2007, 2016 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + + +/** + * Provides functionality similar to a Map, with the feature that char arrays + * and sections of char arrays (known as slices) may be used as keys. + * + * This class is useful because small pieces of an existing large char[] buffer + * can be directly used as map keys. This avoids the need to create many String + * objects as would normally be needed as keys in a standard java.util.Map. + * Thus performance is improved in the CDT core. + * + * Most methods are overloaded with two versions, one that uses a + * section of a char[] as the key (a slice), and one that uses + * the entire char[] as the key. + * + * This class is intended as a replacement for CharArrayObjectMap. + * + * ex: + * char[] key = "one two three".toCharArray(); + * map.put(key, 4, 3, new Integer(99)); + * map.get(key, 4, 3); // returns 99 + * map.get("two".toCharArray()); // returns 99 + * + * @author Mike Kucera + * + * @param <V> + */ +public final class CharArrayMap<V> { + + /** + * Wrapper class used as keys in the map. The purpose + * of this class is to provide implementations of + * equals() and hashCode() that operate on array slices. + * + * This class is private so it is assumed that the arguments + * passed to the constructor are legal. + */ + private static final class Key implements Comparable<Key>{ + final char[] buffer; + final int start; + final int length; + + public Key(char[] buffer, int start, int length) { + this.buffer = buffer; + this.length = length; + this.start = start; + } + + /** + * @throws NullPointerException if buffer is null + */ + public Key(char[] buffer) { + this.buffer = buffer; + this.length = buffer.length; // throws NPE + this.start = 0; + } + + @Override + public boolean equals(Object x) { + if(this == x) + return true; + if(!(x instanceof Key)) + return false; + + Key k = (Key) x; + if(this.length != k.length) + return false; + + for(int i = this.start, j = k.start; i < this.length; i++, j++) { + if(this.buffer[i] != k.buffer[j]) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + int result = 17; + for(int i = this.start; i < this.start+this.length; i++) { + result = 37 * result + this.buffer[i]; + } + return result; + } + + @SuppressWarnings("nls") + @Override + public String toString() { + String slice = new String(this.buffer, this.start, this.length); + return "'" + slice + "'@(" + this.start + "," + this.length + ")"; + } + + + @Override + public int compareTo(Key other) { + char[] b1 = this.buffer, b2 = other.buffer; + + for(int i = this.start, j = other.start; i < b1.length && j < b2.length; i++, j++) { + if(b1[i] != b2[j]) + return b1[i] < b2[j] ? -1 : 1; + } + return b1.length - b2.length; + } + } + + + /** + * Used to enforce preconditions. + * Note that the NPE thrown by mutator methods is thrown from the Key constructor. + * + * @throws IndexOutOfBoundsException if boundaries are wrong in any way + */ + private static void checkBoundaries(char[] chars, int start, int length) { + if(start < 0 || length < 0 || start >= chars.length || start + length > chars.length) + throw new IndexOutOfBoundsException("Buffer length: " + chars.length + //$NON-NLS-1$ + ", Start index: " + start + //$NON-NLS-1$ + ", Length: " + length); //$NON-NLS-1$ + } + + + private final Map<Key,V> map; + + + /** + * Constructs an empty CharArrayMap with default initial capacity. + */ + public CharArrayMap() { + this.map = new HashMap<Key,V>(); + } + + + /** + * Static factory method that constructs an empty CharArrayMap with default initial capacity, + * and the map will be kept in ascending key order. + * + * Characters are compared using a strictly numerical comparison; it is not locale-dependent. + */ + public static <V> CharArrayMap<V> createOrderedMap() { + // TreeMap does not have a constructor that takes an initial capacity + return new CharArrayMap<V>(new TreeMap<Key, V>()); + } + + + private CharArrayMap(Map<Key, V> map) { + assert map != null; + this.map = map; + } + + + /** + * Constructs an empty CharArrayMap with the given initial capacity. + * @throws IllegalArgumentException if the initial capacity is negative + */ + public CharArrayMap(int initialCapacity) { + this.map = new HashMap<Key,V>(initialCapacity); + } + + /** + * Creates a new mapping in this map, uses the given array slice as the key. + * If the map previously contained a mapping for this key, the old value is replaced. + * @throws NullPointerException if chars is null + * @throws IndexOutOfBoundsException if the boundaries specified by start and length are out of range + */ + public void put(char[] chars, int start, int length, V value) { + checkBoundaries(chars, start, length); + this.map.put(new Key(chars, start, length), value); + } + + /** + * Creates a new mapping in this map, uses all of the given array as the key. + * If the map previously contained a mapping for this key, the old value is replaced. + * @throws NullPointerException if chars is null + */ + public void put(char[] chars, V value) { + this.map.put(new Key(chars), value); + } + + /** + * Returns the value to which the specified array slice is mapped in this map, + * or null if the map contains no mapping for this key. + * @throws NullPointerException if chars is null + * @throws IndexOutOfBoundsException if the boundaries specified by start and length are out of range + */ + public V get(char[] chars, int start, int length) { + checkBoundaries(chars, start, length); + return this.map.get(new Key(chars, start, length)); + } + + /** + * Returns the value to which the specified array is mapped in this map, + * or null if the map contains no mapping for this key. + * @throws NullPointerException if chars is null + */ + public V get(char[] chars) { + return this.map.get(new Key(chars)); + } + + /** + * Removes the mapping for the given array slice if present. + * Returns the value object that corresponded to the key + * or null if the key was not in the map. + * @throws NullPointerException if chars is null + * @throws IndexOutOfBoundsException if the boundaries specified by start and length are out of range + */ + public V remove(char[] chars, int start, int length) { + checkBoundaries(chars, start, length); + return this.map.remove(new Key(chars, start, length)); + } + + /** + * Removes the mapping for the given array if present. + * Returns the value object that corresponded to the key + * or null if the key was not in the map. + * @throws NullPointerException if chars is null + */ + public V remove(char[] chars) { + return this.map.remove(new Key(chars)); + } + + /** + * Returns true if the given key has a value associated with it in the map. + * @throws NullPointerException if chars is null + * @throws IndexOutOfBoundsException if the boundaries specified by start and length are out of range + */ + public boolean containsKey(char[] chars, int start, int length) { + checkBoundaries(chars, start, length); + return this.map.containsKey(new Key(chars, start, length)); + } + + /** + * Returns true if the given key has a value associated with it in the map. + * @throws NullPointerException if chars is null + */ + public boolean containsKey(char[] chars) { + return this.map.containsKey(new Key(chars)); + } + + /** + * Returns true if the given value is contained in the map. + */ + public boolean containsValue(V value) { + return this.map.containsValue(value); + } + + /** + * Use this in a foreach loop. + */ + public Collection<V> values() { + return this.map.values(); + } + + /** + * Returns the keys stored in the map. + */ + public Collection<char[]> keys() { + Set<Key> keys= this.map.keySet(); + ArrayList<char[]> r= new ArrayList<char[]>(keys.size()); + for (Key key : keys) { + r.add(CharArrayUtils.extract(key.buffer, key.start, key.length)); + } + return r; + } + + /** + * Removes all mappings from the map. + */ + public void clear() { + this.map.clear(); + } + + /** + * Returns the number of mappings. + */ + public int size() { + return this.map.size(); + } + + /** + * Returns true if the map is empty. + */ + public boolean isEmpty() { + return this.map.isEmpty(); + } + + + /** + * Returns a String representation of the map. + */ + @Override + public String toString() { + return this.map.toString(); + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/util/CharArrayUtils.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/util/CharArrayUtils.java new file mode 100644 index 000000000..1a5791eef --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/util/CharArrayUtils.java @@ -0,0 +1,522 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Andrew Ferguson (Symbian) + * Markus Schorn (Wind River Systems) + * Sergey Prigogin (Google) + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.util; + +import java.util.Arrays; + +/** + * A static utility class for char arrays. + */ +public class CharArrayUtils { + /** @since 5.4 */ + public static final char[] EMPTY_CHAR_ARRAY = {}; + public static final char[] EMPTY = EMPTY_CHAR_ARRAY; + /** @since 5.7 */ + public static final char[][] EMPTY_ARRAY_OF_CHAR_ARRAYS = {}; + + private CharArrayUtils() {} + + public static final int hash(char[] str, int start, int length) { + int h = 0; + int end = start + length; + + for (int curr = start; curr < end; ++curr) { + h = 31 * h + str[curr]; + } + + return h; + } + + public static final int hash(char[] str) { + return hash(str, 0, str.length); + } + + public static final boolean equals(char[] str1, char[] str2) { + return Arrays.equals(str1, str2); + } + + public static final boolean equals(char[][] strarr1, char[][] strarr2) { + if (strarr1 == strarr2) { + return true; + } + if (strarr1 == null || strarr2 == null) { + return false; + } + if (strarr1.length != strarr2.length) { + return false; + } + for (int i = 0; i < strarr2.length; i++) { + if (!Arrays.equals(strarr1[i], strarr2[i])) { + return false; + } + } + return true; + } + + /** + * Returns {@code true} if the contents of a character array are the same as contents + * of a string. + * @since 5.4 + */ + public static final boolean equals(char[] str1, String str2) { + int length = str1.length; + if (str2.length() != length) + return false; + + for (int i = 0; i < length; i++) { + if (str1[i] != str2.charAt(i)) + return false; + } + return true; + } + + /** + * Returns true iff the given array contains the given char at the given position + */ + public static final boolean hasCharAt(char toLookFor, int position, char[] toSearch) { + if (toSearch.length <= position) { + return false; + } + + return toSearch[position] == toLookFor; + } + + /** + * Returns {@code true} if the contents of a section of a character array are the same as contents of a string. + * + * @since 5.5 + */ + public static final boolean equals(char[] str1, int start1, int length1, String str2) { + if (length1 != str2.length() || str1.length < length1 + start1) + return false; + for (int i = 0; i < length1; ++i) { + if (str1[start1++] != str2.charAt(i)) + return false; + } + return true; + } + + /** + * Returns {@code true} if a prefix of the character array is the same as contents + * of a string. + * @since 5.4 + */ + public static final boolean startsWith(char[] str1, String str2) { + int len = str2.length(); + if (str1.length < len) + return false; + for (int i = 0; i < len; i++) { + if (str1[i] != str2.charAt(i)) { + return false; + } + } + return true; + } + + /** + * Implements a lexicographical comparator for char arrays. Comparison is done + * on a per char basis, not a code-point basis. + * + * @param str1 the first of the two char arrays to compare + * @param str2 the second of the two char arrays to compare + * @return 0 if str1==str2, -1 if str1 < str2 and 1 if str1 > str2 + */ + /* + * aftodo - we should think about using the Character codepoint static methods + * if we move to Java 5 + */ + public static final int compare(char[] str1, char[] str2) { + if (str1 == str2) + return 0; + + int end= Math.min(str1.length, str2.length); + for (int i = 0; i < end; ++i) { + int diff= str1[i] - str2[i]; + if (diff != 0) + return diff; + } + + return str1.length - str2.length; + } + + /** + * Returns {@code true} if the contents of a section of a character array are the same as + * contents of another character array. + */ + public static final boolean equals(char[] str1, int start1, int length1, char[] str2) { + if (length1 != str2.length || str1.length < length1 + start1) + return false; + if (str1 == str2 && start1 == 0) + return true; + for (int i = 0; i < length1; ++i) { + if (str1[start1++] != str2[i]) + return false; + } + + return true; + } + + public static final boolean equals(char[] str1, int start1, int length1, char[] str2, boolean ignoreCase) { + if (!ignoreCase) + return equals(str1, start1, length1, str2); + + if (length1 != str2.length || str1.length < start1 + length1) + return false; + + for (int i = 0; i < length1; ++i) { + if (Character.toLowerCase(str1[start1++]) != Character.toLowerCase(str2[i])) + return false; + } + return true; + } + + public static final char[] extract(char[] str, int start, int length) { + if (start == 0 && length == str.length) + return str; + + char[] copy = new char[length]; + System.arraycopy(str, start, copy, 0, length); + return copy; + } + + public static final char[] concat(char[] first, char[] second) { + if (first == null) + return second; + if (second == null) + return first; + + int length1 = first.length; + int length2 = second.length; + char[] result = new char[length1 + length2]; + System.arraycopy(first, 0, result, 0, length1); + System.arraycopy(second, 0, result, length1, length2); + return result; + } + + public static final char[] concat(char[] first, char[] second, char[] third) { + if (first == null) + return concat(second, third); + if (second == null) + return concat(first, third); + if (third == null) + return concat(first, second); + + int length1 = first.length; + int length2 = second.length; + int length3 = third.length; + char[] result = new char[length1 + length2 + length3]; + System.arraycopy(first, 0, result, 0, length1); + System.arraycopy(second, 0, result, length1, length2); + System.arraycopy(third, 0, result, length1 + length2, length3); + return result; + } + + public static final char[] concat(char[] first, char[] second, char[] third, char[] fourth) { + if (first == null) + return concat(second, third, fourth); + if (second == null) + return concat(first, third, fourth); + if (third == null) + return concat(first, second, fourth); + if (fourth == null) + return concat(first, second, third); + + int length1 = first.length; + int length2 = second.length; + int length3 = third.length; + int length4 = fourth.length; + char[] result = new char[length1 + length2 + length3 + length4]; + System.arraycopy(first, 0, result, 0, length1); + System.arraycopy(second, 0, result, length1, length2); + System.arraycopy(third, 0, result, length1 + length2, length3); + System.arraycopy(fourth, 0, result, length1 + length2 + length3, length4); + return result; + } + + /** + * Answers a new array which is the concatenation of all the given arrays. + * + * @param toCatenate + * @since 3.12 + */ + public static char[] concat(char[]... toCatenate) { + int totalSize = 0; + for (char[] next: toCatenate) { + totalSize += next.length; + } + + char[] result = new char[totalSize]; + int writeIndex = 0; + for (char[] next: toCatenate) { + if (next == null) { + continue; + } + System.arraycopy(next, 0, result, writeIndex, next.length); + writeIndex += next.length; + } + return result; + } + + public static final char[] replace(char[] array, char[] toBeReplaced, char[] replacementChars) { + int max = array.length; + int replacedLength = toBeReplaced.length; + int replacementLength = replacementChars.length; + + int[] starts = new int[5]; + int occurrenceCount = 0; + + if (!equals(toBeReplaced, replacementChars)) { + next: for (int i = 0; i < max; i++) { + int j = 0; + while (j < replacedLength) { + if (i + j == max) + continue next; + if (array[i + j] != toBeReplaced[j++]) + continue next; + } + if (occurrenceCount == starts.length) { + System.arraycopy(starts, 0, starts = new int[occurrenceCount * 2], 0, + occurrenceCount); + } + starts[occurrenceCount++] = i; + } + } + if (occurrenceCount == 0) + return array; + char[] result = new char[max + occurrenceCount * (replacementLength - replacedLength)]; + int inStart = 0, outStart = 0; + for (int i = 0; i < occurrenceCount; i++) { + int offset = starts[i] - inStart; + System.arraycopy(array, inStart, result, outStart, offset); + inStart += offset; + outStart += offset; + System.arraycopy( + replacementChars, + 0, + result, + outStart, + replacementLength); + inStart += replacedLength; + outStart += replacementLength; + } + System.arraycopy(array, inStart, result, outStart, max - inStart); + return result; + } + + public static final char[][] subarray(char[][] array, int start, int end) { + if (end == -1) + end = array.length; + if (start > end) + return null; + if (start < 0) + return null; + if (end > array.length) + return null; + + char[][] result = new char[end - start][]; + System.arraycopy(array, start, result, 0, end - start); + return result; + } + + public static final char[] subarray(char[] array, int start, int end) { + if (end == -1) + end = array.length; + if (start > end) + return null; + if (start < 0) + return null; + if (end > array.length) + return null; + + char[] result = new char[end - start]; + System.arraycopy(array, start, result, 0, end - start); + return result; + } + + public static final int indexOf(char toBeFound, char[] array) { + for (int i = 0; i < array.length; i++) { + if (toBeFound == array[i]) + return i; + } + return -1; + } + + public static int indexOf(char toBeFound, char[] buffer, int start, int end) { + if (start < 0 || start > buffer.length || end > buffer.length) + return -1; + + for (int i = start; i < end; i++) { + if (toBeFound == buffer[i]) + return i; + } + return -1; + } + + public static final int indexOf(char[] toBeFound, char[] array) { + if (toBeFound.length > array.length) + return -1; + + int j = 0; + for (int i = 0; i < array.length; i++) { + if (toBeFound[j] == array[i]) { + if (++j == toBeFound.length) + return i - j + 1; + } else { + j = 0; + } + } + return -1; + } + + public static final int lastIndexOf(char[] toBeFound, char[] array) { + return lastIndexOf(toBeFound, array, 0); + } + + /** + * @since 5.11 + */ + public static int lastIndexOf(char toBeFound, char[] array) { + return lastIndexOf(toBeFound, array, 0); + } + + /** + * @since 5.11 + */ + public static int lastIndexOf(char toBeFound, char[] array, int fromIndex) { + for (int i = array.length; --i >= fromIndex;) { + if (array[i] == toBeFound) { + return i; + } + } + return -1; + } + + /** + * @since 5.11 + */ + public static int lastIndexOf(char[] toBeFound, char[] array, int fromIndex) { + int i = array.length; + int j = toBeFound.length; + while (true) { + if (--j < 0) + return i; + if (--i < fromIndex) + return -1; + if (toBeFound[j] != array[i]) { + i += toBeFound.length - j - 1; + j = toBeFound.length; + } + } + } + + static final public char[] trim(char[] chars) { + if (chars == null) + return null; + + int length = chars.length; + int start = 0; + while (start < length && chars[start] == ' ') { + start++; + } + if (start == length) + return EMPTY_CHAR_ARRAY; + + int end = length; + while (--end > start && chars[end] == ' ') { + // Nothing to do + } + end++; + if (start == 0 && end == length) + return chars; + return subarray(chars, start, end); + } + + static final public char[] lastSegment(char[] array, char[] separator) { + int pos = lastIndexOf(separator, array); + if (pos < 0) + return array; + return subarray(array, pos + separator.length, array.length); + } + + /** + * @param buff + * @param i + * @param charImage + */ + public static void overWrite(char[] buff, int i, char[] charImage) { + if (buff.length < i + charImage.length) + return; + for (int j = 0; j < charImage.length; j++) { + buff[i + j] = charImage[j]; + } + } + + /** + * Finds an array of chars in an array of arrays of chars. + * + * @return offset where the array was found or {@code -1} + */ + public static int indexOf(final char[] searchFor, final char[][] searchIn) { + for (int i = 0; i < searchIn.length; i++) { + if (equals(searchIn[i], searchFor)) { + return i; + } + } + return -1; + } + + /** + * Converts a {@link StringBuilder} to a character array. + * @since 5.5 + */ + public static char[] extractChars(StringBuilder buf) { + final int len = buf.length(); + if (len == 0) + return EMPTY_CHAR_ARRAY; + char[] result= new char[len]; + buf.getChars(0, len, result, 0); + return result; + } + + public static char[] subarray(char[] inputString, int index) { + if (inputString.length <= index) { + return EMPTY_CHAR_ARRAY; + } + + char[] result = new char[inputString.length - index]; + System.arraycopy(inputString, index, result, 0, result.length); + return result; + } + + public static boolean startsWith(char[] fieldDescriptor, char c) { + return fieldDescriptor.length > 0 && fieldDescriptor[0] == c; + } + + /** + * If the given array is null, returns the empty array. Otherwise, returns the argument. + */ + public static char[] notNull(char[] contents) { + if (contents == null) { + return EMPTY_CHAR_ARRAY; + } + return contents; + } + + public static boolean endsWith(char[] fieldDescriptor, char c) { + if (fieldDescriptor.length == 0) { + return false; + } + return fieldDescriptor[fieldDescriptor.length - 1] == c; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/util/PathMap.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/util/PathMap.java new file mode 100644 index 000000000..f328fe041 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/util/PathMap.java @@ -0,0 +1,225 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.util; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * Maps IPath keys onto values. + */ +public class PathMap<T> { + private static class Node<T> { + int depth; + boolean exists; + T value; + Map<String, Node<T>> children; + + Node(int depth) { + this.depth = depth; + } + + String getSegment(IPath key) { + return key.segment(this.depth); + } + + Node<T> createNode(IPath key) { + if (this.depth == key.segmentCount()) { + this.exists = true; + return this; + } + + String nextSegment = getSegment(key); + Node<T> next = createChild(nextSegment); + return next.createNode(key); + } + + public Node<T> createChild(String nextSegment) { + if (this.children == null) { + this.children = new HashMap<>(); + } + Node<T> next = this.children.get(nextSegment); + if (next == null) { + next = new Node<>(this.depth + 1); + this.children.put(nextSegment, next); + } + return next; + } + + public Node<T> getMostSpecificNode(IPath key) { + if (this.depth == key.segmentCount()) { + return this; + } + String nextSegment = getSegment(key); + + Node<T> child = getChild(nextSegment); + if (child == null) { + return this; + } + Node<T> result = child.getMostSpecificNode(key); + if (result.exists) { + return result; + } else { + return this; + } + } + + Node<T> getChild(String nextSegment) { + if (this.children == null) { + return null; + } + return this.children.get(nextSegment); + } + + public void addAllKeys(Set<IPath> result, IPath parent) { + if (this.exists) { + result.add(parent); + } + + if (this.children == null) { + return; + } + + for (Entry<String, Node<T>> next : this.children.entrySet()) { + String key = next.getKey(); + IPath nextPath = buildChildPath(parent, key); + next.getValue().addAllKeys(result, nextPath); + } + } + + IPath buildChildPath(IPath parent, String key) { + IPath nextPath = parent.append(key); + return nextPath; + } + + public void toString(StringBuilder builder, IPath parentPath) { + if (this.exists) { + builder.append("["); //$NON-NLS-1$ + builder.append(parentPath); + builder.append("] = "); //$NON-NLS-1$ + builder.append(this.value); + builder.append("\n"); //$NON-NLS-1$ + } + if (this.children != null) { + for (Entry<String, Node<T>> next : this.children.entrySet()) { + String key = next.getKey(); + IPath nextPath = buildChildPath(parentPath, key); + next.getValue().toString(builder, nextPath); + } + } + } + } + + private static class DeviceNode<T> extends Node<T> { + Node<T> noDevice = new Node<>(0); + + DeviceNode() { + super(-1); + } + + @Override + String getSegment(IPath key) { + return key.getDevice(); + } + + @Override + public Node<T> createChild(String nextSegment) { + if (nextSegment == null) { + return this.noDevice; + } + return super.createChild(nextSegment); + } + + Node<T> getChild(String nextSegment) { + if (nextSegment == null) { + return this.noDevice; + } + return super.getChild(nextSegment); + } + + @Override + IPath buildChildPath(IPath parent, String key) { + IPath nextPath = Path.EMPTY.append(parent); + nextPath.setDevice(key); + return nextPath; + } + + @Override + public void toString(StringBuilder builder, IPath parentPath) { + this.noDevice.toString(builder, parentPath); + super.toString(builder,parentPath); + } + } + + private Node<T> root = new DeviceNode<T>(); + + /** + * Inserts the given key into the map. + */ + public T put(IPath key, T value) { + Node<T> node = this.root.createNode(key); + T result = node.value; + node.value = value; + return result; + } + + /** + * Returns the value associated with the given key + */ + public T get(IPath key) { + Node<T> node = this.root.getMostSpecificNode(key); + if (!node.exists || node.depth < key.segmentCount()) { + return null; + } + return node.value; + } + + /** + * Returns the value associated with the longest prefix of the given key + * that can be found in the map. + */ + public T getMostSpecific(IPath key) { + Node<T> node = this.root.getMostSpecificNode(key); + if (!node.exists) { + return null; + } + return node.value; + } + + /** + * Returns true iff any key in this map is a prefix of the given path. + */ + public boolean containsPrefixOf(IPath path) { + Node<T> node = this.root.getMostSpecificNode(path); + return node.exists; + } + + public Set<IPath> keySet() { + Set<IPath> result = new HashSet<>(); + + this.root.addAllKeys(result, Path.EMPTY); + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + + this.root.toString(builder, Path.EMPTY); + return builder.toString(); + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/UnindexedSearchScope.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/UnindexedSearchScope.java new file mode 100644 index 000000000..15ab020d3 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/UnindexedSearchScope.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.search; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IPath; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaElementDelta; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.search.IJavaSearchScope; + +public class UnindexedSearchScope extends AbstractSearchScope { + private IJavaSearchScope searchScope; + + private UnindexedSearchScope(IJavaSearchScope scope) { + this.searchScope = scope; + } + + public static IJavaSearchScope filterEntriesCoveredByTheNewIndex(IJavaSearchScope scope) { + return new UnindexedSearchScope(scope); + } + + @Override + public boolean encloses(String resourcePathString) { + int separatorIndex = resourcePathString.indexOf(JAR_FILE_ENTRY_SEPARATOR); + if (separatorIndex != -1) { + // Files within jar files would have been indexed + return false; + } + + // Jar files themselves would have been indexed + if (isJarFile(resourcePathString)) { + return false; + } + + // Consult with the search scope + return this.searchScope.encloses(resourcePathString); + } + + private boolean isJarFile(String possibleJarFile) { + if (possibleJarFile == null) { + return false; + } + return (possibleJarFile.endsWith(".jar") || possibleJarFile.endsWith(".JAR")); //$NON-NLS-1$//$NON-NLS-2$ + } + + @Override + public boolean encloses(IJavaElement element) { + try { + IResource underlyingResource = element.getUnderlyingResource(); + + if (underlyingResource != null && isJarFile(underlyingResource.getName())) { + return false; + } + } catch (JavaModelException e) { + JavaCore.getPlugin().getLog().log(e.getStatus()); + } + return this.searchScope.encloses(element); + } + + @Override + public IPath[] enclosingProjectsAndJars() { + IPath[] unfiltered = this.searchScope.enclosingProjectsAndJars(); + + List<IPath> result = new ArrayList<>(); + + for (IPath next : unfiltered) { + if (isJarFile(next.lastSegment())) { + continue; + } + result.add(next); + } + return result.toArray(new IPath[result.size()]); + } + + @Override + public void processDelta(IJavaElementDelta delta, int eventType) { + if (this.searchScope instanceof AbstractSearchScope) { + AbstractSearchScope inner = (AbstractSearchScope) this.searchScope; + + inner.processDelta(delta, eventType); + } + } + +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java index 1f53509ec..7ce53faf5 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java @@ -10,27 +10,48 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core.search.indexing; -import java.io.*; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.net.URL; -import java.util.*; +import java.util.ArrayList; +import java.util.Locale; +import java.util.Map; import java.util.zip.CRC32; -import org.eclipse.core.resources.*; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; -import org.eclipse.jdt.core.*; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.CharOperation; -import org.eclipse.jdt.core.search.*; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.SearchDocument; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.core.search.SearchParticipant; import org.eclipse.jdt.internal.compiler.ISourceElementRequestor; import org.eclipse.jdt.internal.compiler.SourceElementParser; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; import org.eclipse.jdt.internal.compiler.util.SimpleSet; -import org.eclipse.jdt.internal.core.*; -import org.eclipse.jdt.internal.core.index.*; +import org.eclipse.jdt.internal.core.ClasspathEntry; +import org.eclipse.jdt.internal.core.JavaModel; +import org.eclipse.jdt.internal.core.JavaModelManager; +import org.eclipse.jdt.internal.core.JavaProject; +import org.eclipse.jdt.internal.core.index.DiskIndex; +import org.eclipse.jdt.internal.core.index.FileIndexLocation; +import org.eclipse.jdt.internal.core.index.Index; +import org.eclipse.jdt.internal.core.index.IndexLocation; +import org.eclipse.jdt.internal.core.nd.indexer.Indexer; import org.eclipse.jdt.internal.core.search.BasicSearchEngine; import org.eclipse.jdt.internal.core.search.PatternSearchJob; import org.eclipse.jdt.internal.core.search.processing.IJob; @@ -77,6 +98,8 @@ public class IndexManager extends JobManager implements IIndexConstants { public synchronized void aboutToUpdateIndex(IPath containerPath, Integer newIndexState) { + // TODO(sxenos): Find a more appropriate and more specific place to trigger re-indexing + Indexer.getInstance().rescanAll(); // newIndexState is either UPDATING_STATE or REBUILDING_STATE // must tag the index as inconsistent, in case we exit before the update job is started IndexLocation indexLocation = computeIndexLocation(containerPath); diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexer.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexer.java index 58e3f0eab..451231e59 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexer.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,10 +10,13 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core.search.indexing; +import java.util.Collections; + import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.compiler.CharOperation; @@ -49,7 +52,7 @@ import org.eclipse.jdt.internal.core.JavaModelManager; import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.SourceTypeElementInfo; import org.eclipse.jdt.internal.core.jdom.CompilationUnit; -import org.eclipse.jdt.internal.core.search.matching.JavaSearchNameEnvironment; +import org.eclipse.jdt.internal.core.search.matching.IndexBasedJavaSearchEnvironment; import org.eclipse.jdt.internal.core.search.matching.MethodPattern; import org.eclipse.jdt.internal.core.search.processing.JobManager; import org.eclipse.objectteams.otdt.internal.core.compiler.control.Dependencies; @@ -165,7 +168,7 @@ public class SourceIndexer extends AbstractIndexer implements ITypeRequestor, Su this.cud = this.basicParser.parse(this.compilationUnit, new CompilationResult(this.compilationUnit, 0, 0, this.options.maxProblemsPerUnit)); // Use a non model name environment to avoid locks, monitors and such. - INameEnvironment nameEnvironment = new JavaSearchNameEnvironment(javaProject, JavaModelManager.getJavaModelManager().getWorkingCopies(DefaultWorkingCopyOwner.PRIMARY, true/*add primary WCs*/)); + INameEnvironment nameEnvironment = IndexBasedJavaSearchEnvironment.create(Collections.singletonList((IJavaProject)javaProject), JavaModelManager.getJavaModelManager().getWorkingCopies(DefaultWorkingCopyOwner.PRIMARY, true/*add primary WCs*/)); this.lookupEnvironment = new LookupEnvironment(this, this.options, problemReporter, nameEnvironment); reduceParseTree(this.cud); //{ObjectTeams: need Dependencies configured: diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ClassFileMatchLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ClassFileMatchLocator.java index 6e8e133ca..48e281952 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ClassFileMatchLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/ClassFileMatchLocator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2012 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -18,10 +18,9 @@ import org.eclipse.jdt.core.*; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.search.*; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; -import org.eclipse.jdt.internal.compiler.classfmt.FieldInfo; -import org.eclipse.jdt.internal.compiler.classfmt.MethodInfo; import org.eclipse.jdt.internal.compiler.env.*; import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.core.BinaryType; import org.eclipse.jdt.internal.core.*; import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants; @@ -352,10 +351,10 @@ private void matchAnnotations(SearchPattern pattern, MatchLocator locator, Class } // Look for references in methods annotations - MethodInfo[] methods = (MethodInfo[]) binaryType.getMethods(); + IBinaryMethod[] methods = binaryType.getMethods(); if (methods != null) { for (int i = 0, max = methods.length; i < max; i++) { - MethodInfo method = methods[i]; + IBinaryMethod method = methods[i]; if (checkAnnotations(typeReferencePattern, method.getAnnotations(), method.getTagBits())) { binaryTypeBinding = locator.cacheBinaryType(classFileBinaryType, binaryType); IMethod methodHandle = classFileBinaryType.getMethod( @@ -370,10 +369,10 @@ private void matchAnnotations(SearchPattern pattern, MatchLocator locator, Class } // Look for references in fields annotations - FieldInfo[] fields = (FieldInfo[]) binaryType.getFields(); + IBinaryField[] fields = binaryType.getFields(); if (fields != null) { for (int i = 0, max = fields.length; i < max; i++) { - FieldInfo field = fields[i]; + IBinaryField field = fields[i]; if (checkAnnotations(typeReferencePattern, field.getAnnotations(), field.getTagBits())) { IField fieldHandle = classFileBinaryType.getField(new String(field.getName())); TypeReferenceMatch match = new TypeReferenceMatch(fieldHandle, SearchMatch.A_ACCURATE, -1, 0, false, locator.getParticipant(), locator.currentPossibleMatch.resource); diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IndexBasedJavaSearchEnvironment.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IndexBasedJavaSearchEnvironment.java new file mode 100644 index 000000000..907707501 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/IndexBasedJavaSearchEnvironment.java @@ -0,0 +1,333 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.search.matching; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Platform; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.internal.compiler.env.AccessRestriction; +import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.env.INameEnvironment; +import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; +import org.eclipse.jdt.internal.compiler.util.SuffixConstants; +import org.eclipse.jdt.internal.core.ClasspathEntry; +import org.eclipse.jdt.internal.core.JavaModel; +import org.eclipse.jdt.internal.core.JavaProject; +import org.eclipse.jdt.internal.core.PackageFragmentRoot; +import org.eclipse.jdt.internal.core.builder.ClasspathLocation; +import org.eclipse.jdt.internal.core.nd.IReader; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.field.FieldSearchIndex; +import org.eclipse.jdt.internal.core.nd.java.JavaIndex; +import org.eclipse.jdt.internal.core.nd.java.JavaNames; +import org.eclipse.jdt.internal.core.nd.java.NdResourceFile; +import org.eclipse.jdt.internal.core.nd.java.NdType; +import org.eclipse.jdt.internal.core.nd.java.NdTypeId; +import org.eclipse.jdt.internal.core.nd.java.TypeRef; +import org.eclipse.jdt.internal.core.nd.java.model.IndexBinaryType; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; +import org.eclipse.jdt.internal.core.nd.util.PathMap; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class IndexBasedJavaSearchEnvironment implements INameEnvironment, SuffixConstants { + + private Map<String, ICompilationUnit> workingCopies; + private PathMap<Integer> mapPathsToRoots = new PathMap<>(); + private IPackageFragmentRoot[] roots; + private int sourceEntryPosition; + private List<ClasspathLocation> unindexedEntries = new ArrayList<>(); + + public IndexBasedJavaSearchEnvironment(List<IJavaProject> javaProject, org.eclipse.jdt.core.ICompilationUnit[] copies) { + this.workingCopies = JavaSearchNameEnvironment.getWorkingCopyMap(copies); + + try { + List<IPackageFragmentRoot> localRoots = new ArrayList<>(); + + for (IJavaProject next : javaProject) { + for (IPackageFragmentRoot nextRoot : next.getAllPackageFragmentRoots()) { + IPath path = nextRoot.getPath(); + if (!nextRoot.isArchive()) { + Object target = JavaModel.getTarget(path, true); + if (target != null) { + ClasspathLocation cp; + if (nextRoot.getKind() == IPackageFragmentRoot.K_SOURCE) { + PackageFragmentRoot root = (PackageFragmentRoot)nextRoot; + cp = new ClasspathSourceDirectory((IContainer)target, root.fullExclusionPatternChars(), root.fullInclusionPatternChars()); + this.unindexedEntries.add(cp); + } + } + } + + localRoots.add(nextRoot); + } + } + + this.roots = localRoots.toArray(new IPackageFragmentRoot[0]); + } catch (JavaModelException e) { + this.roots = new IPackageFragmentRoot[0]; + // project doesn't exist + } + + // Build the map of paths onto root indices + int length = this.roots.length; + for (int i = 0; i < length; i++) { + IPath nextPath = JavaIndex.getLocationForElement(this.roots[i]); + this.mapPathsToRoots.put(nextPath, i); + } + + // Locate the position of the first source entry + this.sourceEntryPosition = Integer.MAX_VALUE; + for (int i = 0; i < length; i++) { + IPackageFragmentRoot nextRoot = this.roots[i]; + try { + if (nextRoot.getKind() == IPackageFragmentRoot.K_SOURCE) { + this.sourceEntryPosition = i; + break; + } + } catch (JavaModelException e) { + // project doesn't exist + } + } + } + + public static boolean isEnabled() { + return Platform.getPreferencesService().getBoolean(JavaCore.PLUGIN_ID, "useIndexBasedSearchEnvironment", false, //$NON-NLS-1$ + null); + } + + @Override + public NameEnvironmentAnswer findType(char[][] compoundTypeName) { + char[] binaryName = CharOperation.concatWith(compoundTypeName, '/'); + + int bestEntryPosition = Integer.MAX_VALUE; + NameEnvironmentAnswer result = findClassInUnindexedLocations(new String(binaryName), compoundTypeName[compoundTypeName.length - 1]); + if (result != null) { + bestEntryPosition = this.sourceEntryPosition; + } + + char[] fieldDescriptor = JavaNames.binaryNameToFieldDescriptor(binaryName); + JavaIndex index = JavaIndex.getIndex(); + Nd nd = index.getNd(); + try (IReader lock = nd.acquireReadLock()) { + NdTypeId typeId = index.findType(fieldDescriptor); + + if (typeId != null) { + List<NdType> types = typeId.getTypes(); + for (NdType next : types) { + NdResourceFile resource = next.getFile(); + + IPath path = resource.getPath(); + Integer nextRoot = this.mapPathsToRoots.getMostSpecific(path); + if (nextRoot != null) { + IPackageFragmentRoot root = this.roots[nextRoot]; + + ClasspathEntry classpathEntry = (ClasspathEntry)root.getRawClasspathEntry(); + AccessRuleSet ruleSet = classpathEntry.getAccessRuleSet(); + AccessRestriction accessRestriction = ruleSet == null? null : ruleSet.getViolatedRestriction(binaryName); + TypeRef typeRef = TypeRef.create(next); + String fileName = new String(binaryName) + ".class"; //$NON-NLS-1$ + IBinaryType binaryType = new IndexBinaryType(typeRef, fileName.toCharArray()); + NameEnvironmentAnswer nextAnswer = new NameEnvironmentAnswer(binaryType, accessRestriction); + + boolean useNewAnswer = isBetter(result, bestEntryPosition, nextAnswer, nextRoot); + + if (useNewAnswer) { + bestEntryPosition = nextRoot; + result = nextAnswer; + } + } + } + } + } catch (JavaModelException e) { + // project doesn't exist + } + + return result; + } + + /** + * Search unindexed locations on the classpath for the given class + */ + private NameEnvironmentAnswer findClassInUnindexedLocations(String qualifiedTypeName, char[] typeName) { + String + binaryFileName = null, qBinaryFileName = null, + sourceFileName = null, qSourceFileName = null, + qPackageName = null; + NameEnvironmentAnswer suggestedAnswer = null; + Iterator <ClasspathLocation> iter = this.unindexedEntries.iterator(); + while (iter.hasNext()) { + ClasspathLocation location = iter.next(); + NameEnvironmentAnswer answer; + if (location instanceof ClasspathSourceDirectory) { + if (sourceFileName == null) { + qSourceFileName = qualifiedTypeName; // doesn't include the file extension + sourceFileName = qSourceFileName; + qPackageName = ""; //$NON-NLS-1$ + if (qualifiedTypeName.length() > typeName.length) { + int typeNameStart = qSourceFileName.length() - typeName.length; + qPackageName = qSourceFileName.substring(0, typeNameStart - 1); + sourceFileName = qSourceFileName.substring(typeNameStart); + } + } + org.eclipse.jdt.internal.compiler.env.ICompilationUnit workingCopy = (org.eclipse.jdt.internal.compiler.env.ICompilationUnit) this.workingCopies.get(qualifiedTypeName); + if (workingCopy != null) { + answer = new NameEnvironmentAnswer(workingCopy, null /*no access restriction*/); + } else { + answer = location.findClass( + sourceFileName, // doesn't include the file extension + qPackageName, + qSourceFileName); // doesn't include the file extension + } + } else { + if (binaryFileName == null) { + qBinaryFileName = qualifiedTypeName + SUFFIX_STRING_class; + binaryFileName = qBinaryFileName; + qPackageName = ""; //$NON-NLS-1$ + if (qualifiedTypeName.length() > typeName.length) { + int typeNameStart = qBinaryFileName.length() - typeName.length - 6; // size of ".class" + qPackageName = qBinaryFileName.substring(0, typeNameStart - 1); + binaryFileName = qBinaryFileName.substring(typeNameStart); + } + } + answer = + location.findClass( + binaryFileName, + qPackageName, + qBinaryFileName); + } + if (answer != null) { + if (!answer.ignoreIfBetter()) { + if (answer.isBetter(suggestedAnswer)) + return answer; + } else if (answer.isBetter(suggestedAnswer)) + // remember suggestion and keep looking + suggestedAnswer = answer; + } + } + if (suggestedAnswer != null) + // no better answer was found + return suggestedAnswer; + return null; + } + + public boolean isBetter(NameEnvironmentAnswer currentBest, int currentBestClasspathPosition, + NameEnvironmentAnswer toTest, int toTestClasspathPosition) { + boolean useNewAnswer = false; + + if (currentBest == null) { + useNewAnswer = true; + } else { + if (toTest.isBetter(currentBest)) { + useNewAnswer = true; + } else { + // If neither one is better, use the one with the earlier classpath position + if (!currentBest.isBetter(toTest)) { + useNewAnswer = (toTestClasspathPosition < currentBestClasspathPosition); + } + } + } + return useNewAnswer; + } + + @Override + public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) { + char[][] newArray = new char[packageName.length + 1][]; + for (int idx = 0; idx < packageName.length; idx++) { + newArray[idx] = packageName[idx]; + } + newArray[packageName.length] = typeName; + return findType(newArray); + } + + @Override + public boolean isPackage(char[][] parentPackageName, char[] packageName) { + char[] binaryPackageName = CharOperation.concatWith(parentPackageName, '/'); + final char[] fieldDescriptorPrefix; + + if (parentPackageName == null || parentPackageName.length == 0) { + fieldDescriptorPrefix = CharArrayUtils.concat(JavaNames.FIELD_DESCRIPTOR_PREFIX, packageName, + new char[] { '/' }); + } else { + fieldDescriptorPrefix = CharArrayUtils.concat(JavaNames.FIELD_DESCRIPTOR_PREFIX, binaryPackageName, + new char[] { '/' }, packageName, new char[] { '/' }); + } + + // Search all the types that are a subpackage of the given package name. Return if we find any one of them on + // the classpath of this project. + JavaIndex index = JavaIndex.getIndex(); + Nd nd = index.getNd(); + try (IReader lock = nd.acquireReadLock()) { + return !index.visitFieldDescriptorsStartingWith(fieldDescriptorPrefix, + new FieldSearchIndex.Visitor<NdTypeId>() { + @Override + public boolean visit(NdTypeId typeId) { + //String fd = typeId.getFieldDescriptor().getString(); + // If this is an exact match for the field descriptor prefix we're looking for then + // this class can't be part of the package we're searching for (and, most likely, the + // "package" we're searching for is actually a class name - not a package). + if (typeId.getFieldDescriptor().length() <= fieldDescriptorPrefix.length + 1) { + return true; + } + List<NdType> types = typeId.getTypes(); + for (NdType next : types) { + if (next.isMember() || next.isLocal() || next.isAnonymous()) { + continue; + } + NdResourceFile resource = next.getFile(); + + IPath path = resource.getPath(); + + if (containsPrefixOf(path)) { + // Terminate the search -- we've found a class belonging to the package + // we're searching for. + return false; + } + } + return true; + } + }); + } + } + + boolean containsPrefixOf(IPath path) { + return this.mapPathsToRoots.containsPrefixOf(path); + } + + @Override + public void cleanup() { + // No explicit cleanup required for this class + } + + public static INameEnvironment create(List<IJavaProject> javaProjects, org.eclipse.jdt.core.ICompilationUnit[] copies) { + if (JavaIndex.isEnabled() && isEnabled()) { + return new IndexBasedJavaSearchEnvironment(javaProjects, copies); + } else { + Iterator<IJavaProject> next = javaProjects.iterator(); + JavaSearchNameEnvironment result = new JavaSearchNameEnvironment(next.next(), copies); + + while (next.hasNext()) { + result.addProjectClassPath((JavaProject)next.next()); + } + return result; + } + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java index 9a115722f..0c7de1594 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java @@ -15,11 +15,15 @@ package org.eclipse.jdt.internal.core.search.matching; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.Map; import org.eclipse.core.resources.IContainer; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; -import org.eclipse.jdt.core.*; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageDeclaration; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; @@ -39,19 +43,24 @@ import org.eclipse.jdt.internal.core.util.Util; */ @SuppressWarnings({"rawtypes", "unchecked"}) public class JavaSearchNameEnvironment implements INameEnvironment, SuffixConstants { - + LinkedHashSet<ClasspathLocation> locationSet; /* * A map from the fully qualified slash-separated name of the main type (String) to the working copy */ - HashMap workingCopies; - + Map<String, org.eclipse.jdt.core.ICompilationUnit> workingCopies; + public JavaSearchNameEnvironment(IJavaProject javaProject, org.eclipse.jdt.core.ICompilationUnit[] copies) { this.locationSet = computeClasspathLocations((JavaProject) javaProject); + this.workingCopies = getWorkingCopyMap(copies); +} + +public static Map<String, org.eclipse.jdt.core.ICompilationUnit> getWorkingCopyMap( + org.eclipse.jdt.core.ICompilationUnit[] copies) { + int length = copies == null ? 0 : copies.length; + HashMap<String, org.eclipse.jdt.core.ICompilationUnit> result = new HashMap<>(length); try { - int length = copies == null ? 0 : copies.length; - this.workingCopies = new HashMap(length); if (copies != null) { for (int i = 0; i < length; i++) { org.eclipse.jdt.core.ICompilationUnit workingCopy = copies[i]; @@ -60,12 +69,13 @@ public JavaSearchNameEnvironment(IJavaProject javaProject, org.eclipse.jdt.core. String cuName = workingCopy.getElementName(); String mainTypeName = Util.getNameWithoutJavaLikeExtension(cuName); String qualifiedMainTypeName = pkg.length() == 0 ? mainTypeName : pkg.replace('.', '/') + '/' + mainTypeName; - this.workingCopies.put(qualifiedMainTypeName, workingCopy); + result.put(qualifiedMainTypeName, workingCopy); } } } catch (JavaModelException e) { // working copy doesn't exist: cannot happen } + return result; } public void cleanup() { diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java index 48094d331..1630f54f9 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java @@ -294,11 +294,11 @@ private static HashMap workingCopiesThatCanSeeFocus(org.eclipse.jdt.core.ICompil return result; } -public static ClassFileReader classFileReader(IType type) { +public static IBinaryType classFileReader(IType type) { IClassFile classFile = type.getClassFile(); JavaModelManager manager = JavaModelManager.getJavaModelManager(); if (classFile.isOpen()) - return (ClassFileReader) manager.getInfo(type); + return (IBinaryType)manager.getInfo(type); PackageFragment pkg = (PackageFragment) type.getPackageFragment(); IPackageFragmentRoot root = (IPackageFragmentRoot) pkg.getParent(); @@ -549,7 +549,7 @@ protected IJavaElement createHandle(AbstractMethodDeclaration method, IJavaEleme if (type.isBinary()) { // don't cache the methods of the binary type // fall thru if its a constructor with a synthetic argument... find it the slower way - ClassFileReader reader = classFileReader(type); + IBinaryType reader = classFileReader(type); if (reader != null) { // build arguments names boolean firstIsSynthetic = false; @@ -619,7 +619,7 @@ protected IJavaElement createHandle(AbstractMethodDeclaration method, IJavaEleme * Create binary method handle */ IMethod createBinaryMethodHandle(IType type, char[] methodSelector, char[][] argumentTypeNames) { - ClassFileReader reader = MatchLocator.classFileReader(type); + IBinaryType reader = MatchLocator.classFileReader(type); if (reader != null) { IBinaryMethod[] methods = reader.getMethods(); if (methods != null) { @@ -1271,9 +1271,15 @@ public void initialize(JavaProject project, int possibleMatchSize) throws JavaMo SearchableEnvironment searchableEnvironment = project.newSearchableNameEnvironment(this.workingCopies); - this.nameEnvironment = new JavaSearchNameEnvironment(project, this.workingCopies); - if (this.pattern.focus != null) - ((JavaSearchNameEnvironment) this.nameEnvironment).addProjectClassPath((JavaProject) this.pattern.focus.getJavaProject()); + List<IJavaProject> projects = new ArrayList<>(); + projects.add(project); + if (this.pattern.focus != null) { + IJavaProject focusProject = this.pattern.focus.getJavaProject(); + if (focusProject != project) { + projects.add(focusProject); + } + } + this.nameEnvironment = IndexBasedJavaSearchEnvironment.create(projects, this.workingCopies); // create lookup environment Map map = project.getOptions(true); diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatch.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatch.java index 3cd748ce1..88f084a64 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatch.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/PossibleMatch.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2012 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -15,7 +15,7 @@ import org.eclipse.jdt.core.*; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.search.SearchDocument; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; -import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.core.*; import org.eclipse.jdt.internal.core.util.Util; @@ -135,7 +135,7 @@ private String getSourceFileName() { this.sourceFileName = NO_SOURCE_FILE_NAME; if (this.openable.getSourceMapper() != null) { BinaryType type = (BinaryType) ((ClassFile) this.openable).getType(); - ClassFileReader reader = MatchLocator.classFileReader(type); + IBinaryType reader = MatchLocator.classFileReader(type); if (reader != null) { String fileName = type.sourceFileName(reader); this.sourceFileName = fileName == null ? NO_SOURCE_FILE_NAME : fileName; diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/processing/JobManager.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/processing/JobManager.java index 6351839ee..71bc06163 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/processing/JobManager.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/processing/JobManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2011 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -201,7 +201,7 @@ public abstract class JobManager implements Runnable { case IJob.WaitUntilReady : int totalWork = 1000; - SubMonitor subProgress = subMonitor.setWorkRemaining(10).split(8).setWorkRemaining(totalWork); + SubMonitor waitMonitor = subMonitor.setWorkRemaining(10).split(8).setWorkRemaining(totalWork); // use local variable to avoid potential NPE (see bug 20435 NPE when searching java method // and bug 42760 NullPointerException in JobManager when searching) Thread t = this.processingThread; @@ -218,30 +218,28 @@ public abstract class JobManager implements Runnable { float lastWorked = 0; float totalWorked = 0; while ((awaitingJobsCount = awaitingJobsCount()) > 0) { - if (subProgress.isCanceled() || this.processingThread == null) + if (waitMonitor.isCanceled() || this.processingThread == null) throw new OperationCanceledException(); IJob currentJob = currentJob(); // currentJob can be null when jobs have been added to the queue but job manager is not enabled if (currentJob != null && currentJob != previousJob) { if (VERBOSE) Util.verbose("-> NOT READY - waiting until ready - " + searchJob);//$NON-NLS-1$ - if (subProgress != null) { - String indexing = Messages.bind(Messages.jobmanager_filesToIndex, currentJob.getJobFamily(), Integer.toString(awaitingJobsCount)); - subProgress.subTask(indexing); - // ratio of the amount of work relative to the total work - float ratio = awaitingJobsCount < totalWork ? 1 : ((float) totalWork) / awaitingJobsCount; - if (lastJobsCount > awaitingJobsCount) { - totalWorked += (lastJobsCount - awaitingJobsCount) * ratio; - } else { - // more jobs were added, just increment by the ratio - totalWorked += ratio; - } - if (totalWorked - lastWorked >= 1) { - subProgress.worked((int) (totalWorked - lastWorked)); - lastWorked = totalWorked; - } - lastJobsCount = awaitingJobsCount; + String indexing = Messages.bind(Messages.jobmanager_filesToIndex, currentJob.getJobFamily(), Integer.toString(awaitingJobsCount)); + waitMonitor.subTask(indexing); + // ratio of the amount of work relative to the total work + float ratio = awaitingJobsCount < totalWork ? 1 : ((float) totalWork) / awaitingJobsCount; + if (lastJobsCount > awaitingJobsCount) { + totalWorked += (lastJobsCount - awaitingJobsCount) * ratio; + } else { + // more jobs were added, just increment by the ratio + totalWorked += ratio; } + if (totalWorked - lastWorked >= 1) { + waitMonitor.worked((int) (totalWorked - lastWorked)); + lastWorked = totalWorked; + } + lastJobsCount = awaitingJobsCount; previousJob = currentJob; } try { |