Upgrade to neon.2 75dbfad0
diff --git a/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/BuildJarIndex.java b/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/BuildJarIndex.java index cd7238a..fcb9685 100644 --- a/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/BuildJarIndex.java +++ b/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/BuildJarIndex.java
@@ -48,7 +48,7 @@ try { JavaIndexer.generateIndexForJar(this.jarPath, this.indexPath); } catch (IOException e) { - throw new BuildException(AntAdapterMessages.getString("buildJarIndex.ioexception.occured", e.getLocalizedMessage())); //$NON-NLS-1$ + throw new BuildException(AntAdapterMessages.getString("buildJarIndex.ioexception.occured", e.getLocalizedMessage()), e); //$NON-NLS-1$ } }
diff --git a/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/CheckDebugAttributes.java b/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/CheckDebugAttributes.java index b6617d7..28488d8 100644 --- a/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/CheckDebugAttributes.java +++ b/org.eclipse.jdt.core/antadapter/org/eclipse/jdt/core/CheckDebugAttributes.java
@@ -60,7 +60,7 @@ try { jarFile = new ZipFile(this.file); } catch (ZipException e) { - throw new BuildException(AntAdapterMessages.getString("checkDebugAttributes.file.argument.must.be.a.classfile.or.a.jarfile")); //$NON-NLS-1$ + throw new BuildException(AntAdapterMessages.getString("checkDebugAttributes.file.argument.must.be.a.classfile.or.a.jarfile"), e); //$NON-NLS-1$ } finally { if (jarFile != null) { jarFile.close(); @@ -78,7 +78,7 @@ getProject().setUserProperty(this.property, "has debug"); //$NON-NLS-1$ } } catch (IOException e) { - throw new BuildException(AntAdapterMessages.getString("checkDebugAttributes.ioexception.occured") + this.file); //$NON-NLS-1$ + throw new BuildException(AntAdapterMessages.getString("checkDebugAttributes.ioexception.occured") + this.file, e); //$NON-NLS-1$ } }
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 c2bdf71..507f55d 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
@@ -84,7 +84,7 @@ } return resultValue; } catch (ClassNotFoundException cnfe) { - throw new BuildException(AntAdapterMessages.getString("ant.jdtadapter.error.cannotFindJDTCompiler")); //$NON-NLS-1$ + throw new BuildException(AntAdapterMessages.getString("ant.jdtadapter.error.cannotFindJDTCompiler"), cnfe); //$NON-NLS-1$ } catch (Exception ex) { throw new BuildException(ex); }
diff --git a/org.eclipse.jdt.core/apt/org/eclipse/jdt/internal/compiler/apt/model/TypesImpl.java b/org.eclipse.jdt.core/apt/org/eclipse/jdt/internal/compiler/apt/model/TypesImpl.java index 6f5c2a5..b0ac625 100644 --- a/org.eclipse.jdt.core/apt/org/eclipse/jdt/internal/compiler/apt/model/TypesImpl.java +++ b/org.eclipse.jdt.core/apt/org/eclipse/jdt/internal/compiler/apt/model/TypesImpl.java
@@ -8,6 +8,7 @@ * Contributors: * Walter Harley - initial API and implementation * IBM Corporation - fix for 342598, 382590 + * Jean-Marie Henaff <jmhenaff@google.com> (Google) - Bug 481555 *******************************************************************************/ package org.eclipse.jdt.internal.compiler.apt.model; @@ -74,62 +75,124 @@ } @Override - public TypeMirror asMemberOf(DeclaredType containing, Element element) { -// throw new UnsupportedOperationException("NYI: TypesImpl.asMemberOf(" + containing + ", " + element + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - ElementImpl elementImpl = (ElementImpl) element; - DeclaredTypeImpl declaredTypeImpl = (DeclaredTypeImpl) containing; - ReferenceBinding referenceBinding = (ReferenceBinding) declaredTypeImpl._binding; - switch(element.getKind()) { - case CONSTRUCTOR : - case METHOD : - MethodBinding methodBinding = (MethodBinding) elementImpl._binding; - while (referenceBinding != null) { - for (MethodBinding method : referenceBinding.methods()) { - if (CharOperation.equals(method.selector, methodBinding.selector) && - (method.original() == methodBinding || method.areParameterErasuresEqual(methodBinding))) { - return this._env.getFactory().newTypeMirror(method); - } - } - referenceBinding = referenceBinding.superclass(); - } - break; - case FIELD : - case ENUM_CONSTANT: - FieldBinding fieldBinding = (FieldBinding) elementImpl._binding; - while (referenceBinding != null) { - for (FieldBinding field : referenceBinding.fields()) { - if (CharOperation.equals(field.name, fieldBinding.name)) { - return this._env.getFactory().newTypeMirror(field); - } - } - referenceBinding = referenceBinding.superclass(); - } - break; - case ENUM : - case ANNOTATION_TYPE : - case INTERFACE : - case CLASS : - ReferenceBinding elementBinding = (ReferenceBinding) elementImpl._binding; - while (referenceBinding != null) { - // If referenceBinding is a ParameterizedTypeBinding, this will return only ParameterizedTypeBindings - // for member types, even if the member happens to be a static nested class. That's probably a bug; - // static nested classes are not parameterized by their outer class. - for (ReferenceBinding memberReferenceBinding : referenceBinding.memberTypes()) { - if (CharOperation.equals(elementBinding.compoundName, memberReferenceBinding.compoundName)) { - return this._env.getFactory().newTypeMirror(memberReferenceBinding); - } - } - referenceBinding = referenceBinding.superclass(); - } - break; - default: - throw new IllegalArgumentException("element " + element + //$NON-NLS-1$ - " has unrecognized element kind " + element.getKind()); //$NON-NLS-1$ - } - throw new IllegalArgumentException("element " + element + //$NON-NLS-1$ - " is not a member of the containing type " + containing + //$NON-NLS-1$ - " nor any of its superclasses"); //$NON-NLS-1$ - } + public TypeMirror asMemberOf(DeclaredType containing, Element element) { + // throw new UnsupportedOperationException("NYI: TypesImpl.asMemberOf(" + // + containing + ", " + element + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + // //$NON-NLS-3$ + ElementImpl elementImpl = (ElementImpl) element; + DeclaredTypeImpl declaredTypeImpl = (DeclaredTypeImpl) containing; + ReferenceBinding referenceBinding = (ReferenceBinding) declaredTypeImpl._binding; + TypeMirror typeMirror; + + switch (element.getKind()) { + case CONSTRUCTOR: + case METHOD: + typeMirror = findMemberInHierarchy(referenceBinding, elementImpl._binding, new MemberInTypeFinder() { + @Override + public TypeMirror find(ReferenceBinding typeBinding, Binding memberBinding) { + MethodBinding methodBinding = ((MethodBinding) memberBinding); + for (MethodBinding method : typeBinding.methods()) { + if (CharOperation.equals(method.selector, methodBinding.selector) + && (method.original() == methodBinding + || method.areParameterErasuresEqual(methodBinding))) { + return TypesImpl.this._env.getFactory().newTypeMirror(method); + } + } + return null; + } + }); + + if (typeMirror != null) { + return typeMirror; + } + break; + case FIELD: + case ENUM_CONSTANT: + typeMirror = findMemberInHierarchy(referenceBinding, elementImpl._binding, new MemberInTypeFinder() { + @Override + public TypeMirror find(ReferenceBinding typeBinding, Binding memberBinding) { + FieldBinding fieldBinding = (FieldBinding) memberBinding; + for (FieldBinding field : typeBinding.fields()) { + if (CharOperation.equals(field.name, fieldBinding.name)) { + return TypesImpl.this._env.getFactory().newTypeMirror(field); + } + } + return null; + } + }); + + if (typeMirror != null) { + return typeMirror; + } + break; + case ENUM: + case ANNOTATION_TYPE: + case INTERFACE: + case CLASS: + + typeMirror = findMemberInHierarchy(referenceBinding, elementImpl._binding, new MemberInTypeFinder() { + @Override + public TypeMirror find(ReferenceBinding typeBinding, Binding memberBinding) { + ReferenceBinding elementBinding = (ReferenceBinding) memberBinding; + // If referenceBinding is a ParameterizedTypeBinding, this + // will return only ParameterizedTypeBindings + // for member types, even if the member happens to be a + // static nested class. That's probably a bug; + // static nested classes are not parameterized by their + // outer class. + for (ReferenceBinding memberReferenceBinding : typeBinding.memberTypes()) { + if (CharOperation.equals(elementBinding.compoundName, memberReferenceBinding.compoundName)) { + return TypesImpl.this._env.getFactory().newTypeMirror(memberReferenceBinding); + } + } + return null; + } + }); + + if (typeMirror != null) { + return typeMirror; + } + break; + default: + throw new IllegalArgumentException("element " + element + //$NON-NLS-1$ + " has unrecognized element kind " + element.getKind()); //$NON-NLS-1$ + } + throw new IllegalArgumentException("element " + element + //$NON-NLS-1$ + " is not a member of the containing type " + containing + //$NON-NLS-1$ + " nor any of its superclasses"); //$NON-NLS-1$ + } + + private static interface MemberInTypeFinder { + TypeMirror find(ReferenceBinding typeBinding, Binding memberBinding); + } + + private TypeMirror findMemberInHierarchy(ReferenceBinding typeBinding, Binding memberBinding, + MemberInTypeFinder finder) { + TypeMirror result = null; + + if (typeBinding == null) { + return null; + } + + result = finder.find(typeBinding, memberBinding); + if (result != null) { + return result; + } + + result = findMemberInHierarchy(typeBinding.superclass(), memberBinding, finder); + if (result != null) { + return result; + } + + for (ReferenceBinding superInterface : typeBinding.superInterfaces()) { + result = findMemberInHierarchy(superInterface, memberBinding, finder); + if (result != null) { + return result; + } + } + + return null; + } @Override public TypeElement boxedClass(PrimitiveType p) {
diff --git a/org.eclipse.jdt.core/apt/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileManager.java b/org.eclipse.jdt.core/apt/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileManager.java index 96bd353..2319696 100644 --- a/org.eclipse.jdt.core/apt/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileManager.java +++ b/org.eclipse.jdt.core/apt/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileManager.java
@@ -452,7 +452,7 @@ } uri2 = new URI(uri.getScheme(), uri.getHost(), path, uri.getFragment()); } catch (URISyntaxException e) { - throw new IllegalArgumentException("invalid sibling");//$NON-NLS-1$ + throw new IllegalArgumentException("invalid sibling", e);//$NON-NLS-1$ } return new EclipseFileObject(className, uri2, kind, this.charset); } else {
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java index e1d1283..2bfd704 100644 --- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java +++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
@@ -1223,9 +1223,9 @@ this.log.println("# " + dateFormat.format(date));//$NON-NLS-1$ } } catch (FileNotFoundException e) { - throw new IllegalArgumentException(this.main.bind("configure.cannotOpenLog", logFileName)); //$NON-NLS-1$ + throw new IllegalArgumentException(this.main.bind("configure.cannotOpenLog", logFileName), e); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { - throw new IllegalArgumentException(this.main.bind("configure.cannotOpenLogInvalidEncoding", logFileName)); //$NON-NLS-1$ + throw new IllegalArgumentException(this.main.bind("configure.cannotOpenLogInvalidEncoding", logFileName), e); //$NON-NLS-1$ } } private void startLoggingExtraProblems(int count) { @@ -1955,7 +1955,7 @@ new InputStreamReader(new ByteArrayInputStream(new byte[0]), customEncoding); } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException( - this.bind("configure.unsupportedEncoding", customEncoding)); //$NON-NLS-1$ + this.bind("configure.unsupportedEncoding", customEncoding), e); //$NON-NLS-1$ } } currentArg = currentArg.substring(0, encodingStart - 1); @@ -2547,7 +2547,7 @@ throw new IllegalArgumentException(this.bind("configure.repetition", currentArg)); //$NON-NLS-1$ } } catch (NumberFormatException e) { - throw new IllegalArgumentException(this.bind("configure.repetition", currentArg)); //$NON-NLS-1$ + throw new IllegalArgumentException(this.bind("configure.repetition", currentArg), e); //$NON-NLS-1$ } mode = DEFAULT; continue; @@ -2559,7 +2559,7 @@ } this.options.put(CompilerOptions.OPTION_MaxProblemPerUnit, currentArg); } catch (NumberFormatException e) { - throw new IllegalArgumentException(this.bind("configure.maxProblems", currentArg)); //$NON-NLS-1$ + throw new IllegalArgumentException(this.bind("configure.maxProblems", currentArg), e); //$NON-NLS-1$ } mode = DEFAULT; continue; @@ -2609,7 +2609,7 @@ new InputStreamReader(new ByteArrayInputStream(new byte[0]), currentArg); } catch (UnsupportedEncodingException e) { throw new IllegalArgumentException( - this.bind("configure.unsupportedEncoding", currentArg)); //$NON-NLS-1$ + this.bind("configure.unsupportedEncoding", currentArg), e); //$NON-NLS-1$ } specifiedEncodings.add(currentArg); this.options.put(CompilerOptions.OPTION_Encoding, currentArg);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java index 7eafeee..23b3817 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.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 @@ -9,9 +9,12 @@ * IBM Corporation - initial API and implementation * Luiz-Otavio Zorzella <zorzella at gmail dot com> - Improve CamelCase algorithm * Gábor Kövesdán - Contribution for Bug 350000 - [content assist] Include non-prefix matches in auto-complete suggestions + * Stefan Xenos <sxenos@gmail.com> (Google) - Bug 501283 - Lots of hash collisions during indexing *******************************************************************************/ package org.eclipse.jdt.core.compiler; +import java.util.Arrays; + import org.eclipse.jdt.internal.compiler.parser.ScannerHelper; /** @@ -2281,19 +2284,9 @@ * * @param array the array for which a hashcode is required * @return the hashcode - * @throws NullPointerException if array is null */ public static final int hashCode(char[] array) { - int length = array.length; - int hash = length == 0 ? 31 : array[0]; - if (length < 8) { - for (int i = length; --i > 0;) - hash = (hash * 31) + array[i]; - } else { - // 8 characters is enough to compute a decent hash code, don't waste time examining every character - for (int i = length - 1, last = i > 16 ? i - 16 : 0; i > last; i -= 2) - hash = (hash * 31) + array[i]; - } + int hash = Arrays.hashCode(array); return hash & 0x7FFFFFFF; }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java index 671edcf..c0d8ebd 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
@@ -1894,6 +1894,12 @@ int IllegalDefaultModifierSpecification = MethodRelated + 1058; /** @since 3.13 */ int CannotInferInvocationType = TypeRelated + 1059; + + /** @since 3.13 */ + int TypeAnnotationAtQualifiedName = Internal + Syntax + 1060; + + /** @since 3.13 */ + int NullAnnotationAtQualifyingType = Internal + Syntax + 1061; /** @since 3.10 */ int GenericInferenceError = 1100; // FIXME: This is just a stop-gap measure, be more specific via https://bugs.eclipse.org/404675 @@ -1901,4 +1907,6 @@ /** @deprecated - problem is no longer generated (implementation issue has been resolved) * @since 3.10 */ int LambdaShapeComputationError = 1101; + /** @since 3.13 */ + int ProblemNotAnalysed = 1102; }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java index 12639ff..5d4fb85 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
@@ -695,8 +695,11 @@ } else { updatedArgumentType = argument.resolveType(scope); } - if (updatedArgumentType != null && updatedArgumentType.kind() != Binding.POLY_TYPE) + if (updatedArgumentType != null && updatedArgumentType.kind() != Binding.POLY_TYPE) { argumentTypes[i] = updatedArgumentType; + if (candidateMethod.isPolymorphic()) + candidateMethod.parameters[i] = updatedArgumentType; + } } } }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java index 9606e2b..9f06279 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
@@ -644,7 +644,7 @@ // no need to check annotation usage if missing return; } - if (! isAnnotationTargetAllowed(repeatingAnnotation, scope, containerAnnotationType, repeatingAnnotation.recipient.kind())) { + if (isAnnotationTargetAllowed(repeatingAnnotation, scope, containerAnnotationType, repeatingAnnotation.recipient.kind()) != AnnotationTargetAllowed.YES) { scope.problemReporter().disallowedTargetForContainerAnnotation(repeatingAnnotation, containerAnnotationType); } } @@ -961,63 +961,71 @@ return this.resolvedType; } - private static boolean isAnnotationTargetAllowed(Binding recipient, BlockScope scope, TypeBinding annotationType, int kind, long metaTagBits) { + public enum AnnotationTargetAllowed { + YES, TYPE_ANNOTATION_ON_QUALIFIED_NAME, NO; + } + + private static AnnotationTargetAllowed isAnnotationTargetAllowed(Binding recipient, BlockScope scope, TypeBinding annotationType, int kind, long metaTagBits) { switch (kind) { case Binding.PACKAGE : if ((metaTagBits & TagBits.AnnotationForPackage) != 0) - return true; + return AnnotationTargetAllowed.YES; else if (scope.compilerOptions().sourceLevel <= ClassFileConstants.JDK1_6) { SourceTypeBinding sourceType = (SourceTypeBinding) recipient; if (CharOperation.equals(sourceType.sourceName, TypeConstants.PACKAGE_INFO_NAME)) - return true; + return AnnotationTargetAllowed.YES; } break; case Binding.TYPE_USE : if ((metaTagBits & TagBits.AnnotationForTypeUse) != 0) { // jsr 308 - return true; + return AnnotationTargetAllowed.YES; } if (scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_8) { // already reported as syntax error; don't report secondary problems - return true; + return AnnotationTargetAllowed.YES; } break; case Binding.TYPE : case Binding.GENERIC_TYPE : if (((ReferenceBinding)recipient).isAnnotationType()) { if ((metaTagBits & (TagBits.AnnotationForAnnotationType | TagBits.AnnotationForType | TagBits.AnnotationForTypeUse)) != 0) - return true; + return AnnotationTargetAllowed.YES; } else if ((metaTagBits & (TagBits.AnnotationForType | TagBits.AnnotationForTypeUse)) != 0) { - return true; + return AnnotationTargetAllowed.YES; } else if ((metaTagBits & TagBits.AnnotationForPackage) != 0) { if (CharOperation.equals(((ReferenceBinding) recipient).sourceName, TypeConstants.PACKAGE_INFO_NAME)) - return true; + return AnnotationTargetAllowed.YES; } break; case Binding.METHOD : MethodBinding methodBinding = (MethodBinding) recipient; if (methodBinding.isConstructor()) { if ((metaTagBits & (TagBits.AnnotationForConstructor | TagBits.AnnotationForTypeUse)) != 0) - return true; + return AnnotationTargetAllowed.YES; } else if ((metaTagBits & TagBits.AnnotationForMethod) != 0) { - return true; + return AnnotationTargetAllowed.YES; } else if ((metaTagBits & TagBits.AnnotationForTypeUse) != 0) { SourceTypeBinding sourceType = (SourceTypeBinding) methodBinding.declaringClass; MethodDeclaration methodDecl = (MethodDeclaration) sourceType.scope.referenceContext.declarationOf(methodBinding); if (isTypeUseCompatible(methodDecl.returnType, scope)) { - return true; + return AnnotationTargetAllowed.YES; + } else { + return AnnotationTargetAllowed.TYPE_ANNOTATION_ON_QUALIFIED_NAME; } } break; case Binding.FIELD : if ((metaTagBits & TagBits.AnnotationForField) != 0) { - return true; + return AnnotationTargetAllowed.YES; } else if ((metaTagBits & TagBits.AnnotationForTypeUse) != 0) { FieldBinding sourceField = (FieldBinding) recipient; SourceTypeBinding sourceType = (SourceTypeBinding) sourceField.declaringClass; FieldDeclaration fieldDeclaration = sourceType.scope.referenceContext.declarationOf(sourceField); if (isTypeUseCompatible(fieldDeclaration.type, scope)) { - return true; + return AnnotationTargetAllowed.YES; + } else { + return AnnotationTargetAllowed.TYPE_ANNOTATION_ON_QUALIFIED_NAME; } } break; @@ -1025,27 +1033,31 @@ LocalVariableBinding localVariableBinding = (LocalVariableBinding) recipient; if ((localVariableBinding.tagBits & TagBits.IsArgument) != 0) { if ((metaTagBits & TagBits.AnnotationForParameter) != 0) { - return true; + return AnnotationTargetAllowed.YES; } else if ((metaTagBits & TagBits.AnnotationForTypeUse) != 0) { if (isTypeUseCompatible(localVariableBinding.declaration.type, scope)) { - return true; + return AnnotationTargetAllowed.YES; + } else { + return AnnotationTargetAllowed.TYPE_ANNOTATION_ON_QUALIFIED_NAME; } } } else if ((annotationType.tagBits & TagBits.AnnotationForLocalVariable) != 0) { - return true; + return AnnotationTargetAllowed.YES; } else if ((metaTagBits & TagBits.AnnotationForTypeUse) != 0) { if (isTypeUseCompatible(localVariableBinding.declaration.type, scope)) { - return true; + return AnnotationTargetAllowed.YES; + } else { + return AnnotationTargetAllowed.TYPE_ANNOTATION_ON_QUALIFIED_NAME; } } break; case Binding.TYPE_PARAMETER : // jsr308 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=391196 if ((metaTagBits & (TagBits.AnnotationForTypeParameter | TagBits.AnnotationForTypeUse)) != 0) { - return true; + return AnnotationTargetAllowed.YES; } } - return false; + return AnnotationTargetAllowed.NO; } public static boolean isAnnotationTargetAllowed(BlockScope scope, TypeBinding annotationType, Binding recipient) { @@ -1053,10 +1065,10 @@ if ((metaTagBits & TagBits.AnnotationTargetMASK) == 0) { return true; } - return isAnnotationTargetAllowed(recipient, scope, annotationType, recipient.kind(), metaTagBits); + return isAnnotationTargetAllowed(recipient, scope, annotationType, recipient.kind(), metaTagBits)==AnnotationTargetAllowed.YES; } - static boolean isAnnotationTargetAllowed(Annotation annotation, BlockScope scope, TypeBinding annotationType, int kind) { + static AnnotationTargetAllowed isAnnotationTargetAllowed(Annotation annotation, BlockScope scope, TypeBinding annotationType, int kind) { long metaTagBits = annotationType.getAnnotationTagBits(); // could be forward reference if ((metaTagBits & TagBits.AnnotationTargetMASK) == 0) { @@ -1064,7 +1076,7 @@ if (kind == Binding.TYPE_PARAMETER || kind == Binding.TYPE_USE) { scope.problemReporter().explitAnnotationTargetRequired(annotation); } - return true; + return AnnotationTargetAllowed.YES; } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=391201 @@ -1091,8 +1103,13 @@ // no need to check annotation usage if missing return; } - if (! isAnnotationTargetAllowed(annotation, scope, annotationType, kind)) { + AnnotationTargetAllowed annotationTargetAllowed = isAnnotationTargetAllowed(annotation, scope, annotationType, kind); + if (annotationTargetAllowed != AnnotationTargetAllowed.YES) { + if(annotationTargetAllowed == AnnotationTargetAllowed.TYPE_ANNOTATION_ON_QUALIFIED_NAME) { + scope.problemReporter().typeAnnotationAtQualifiedName(annotation); + } else { scope.problemReporter().disallowedTargetForAnnotation(annotation); + } if (recipient instanceof TypeBinding) ((TypeBinding)recipient).tagBits &= ~tagBitsToRevert; } @@ -1161,7 +1178,7 @@ continue nextAnnotation; } else { if (annotation.hasNullBit(TypeIds.BitNonNullAnnotation|TypeIds.BitNullableAnnotation)) { - scope.problemReporter().nullAnnotationUnsupportedLocation(annotation); + scope.problemReporter().nullAnnotationAtQualifyingType(annotation); continue nextAnnotation; } }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java index 00b8bea..1c7dafb 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java
@@ -267,6 +267,9 @@ } public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) { + if((this.resolvedType.tagBits & TagBits.AnnotationNonNull) != 0) { + return true; + } checkNPEbyUnboxing(scope, flowContext, flowInfo); return this.expression.checkNPE(scope, flowContext, flowInfo, ttlForFieldCheck); }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java index 895ea19..acb079b 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java
@@ -299,22 +299,28 @@ Constant cst = inits[iToken].constant; if (cst != Constant.NotAConstant && cst.typeID() == TypeIds.T_JavaLangString) { IrritantSet tokenIrritants = CompilerOptions.warningTokenToIrritants(cst.stringValue()); - if (tokenIrritants != null - && !tokenIrritants.areAllSet() // no complaint against @SuppressWarnings("all") - && options.isAnyEnabled(tokenIrritants) // if irritant is effectively enabled - && (foundIrritants[iSuppress] == null || !foundIrritants[iSuppress].isAnySet(tokenIrritants))) { // if irritant had no matching problem - if (unusedWarningTokenIsWarning) { - int start = value.sourceStart, end = value.sourceEnd; - nextSuppress: for (int jSuppress = iSuppress - 1; jSuppress >= 0; jSuppress--) { - long position = this.suppressWarningScopePositions[jSuppress]; - int startSuppress = (int) (position >>> 32); - int endSuppress = (int) position; - if (start < startSuppress) continue nextSuppress; - if (end > endSuppress) continue nextSuppress; - if (this.suppressWarningIrritants[jSuppress].areAllSet()) break pairLoop; // suppress all? + if (tokenIrritants != null) { + if (!tokenIrritants.areAllSet() // no complaint against @SuppressWarnings("all") + && (foundIrritants[iSuppress] == null || !foundIrritants[iSuppress].isAnySet(tokenIrritants))) { // if irritant had no matching problem + if (unusedWarningTokenIsWarning) { + int start = value.sourceStart, end = value.sourceEnd; + nextSuppress: for (int jSuppress = iSuppress - 1; jSuppress >= 0; jSuppress--) { + long position = this.suppressWarningScopePositions[jSuppress]; + int startSuppress = (int) (position >>> 32); + int endSuppress = (int) position; + if (start < startSuppress) continue nextSuppress; + if (end > endSuppress) continue nextSuppress; + if (this.suppressWarningIrritants[jSuppress].areAllSet()) break pairLoop; // suppress all? + } + } + int id = options.getIgnoredIrritant(tokenIrritants); + if (id > 0) { + String key = CompilerOptions.optionKeyFromIrritant(id); + this.scope.problemReporter().problemNotAnalysed(inits[iToken], key); + } else { + this.scope.problemReporter().unusedWarningToken(inits[iToken]); } } - this.scope.problemReporter().unusedWarningToken(inits[iToken]); } } } @@ -323,22 +329,28 @@ Constant cst = value.constant; if (cst != Constant.NotAConstant && cst.typeID() == T_JavaLangString) { IrritantSet tokenIrritants = CompilerOptions.warningTokenToIrritants(cst.stringValue()); - if (tokenIrritants != null - && !tokenIrritants.areAllSet() // no complaint against @SuppressWarnings("all") - && options.isAnyEnabled(tokenIrritants) // if irritant is effectively enabled - && (foundIrritants[iSuppress] == null || !foundIrritants[iSuppress].isAnySet(tokenIrritants))) { // if irritant had no matching problem - if (unusedWarningTokenIsWarning) { - int start = value.sourceStart, end = value.sourceEnd; - nextSuppress: for (int jSuppress = iSuppress - 1; jSuppress >= 0; jSuppress--) { - long position = this.suppressWarningScopePositions[jSuppress]; - int startSuppress = (int) (position >>> 32); - int endSuppress = (int) position; - if (start < startSuppress) continue nextSuppress; - if (end > endSuppress) continue nextSuppress; - if (this.suppressWarningIrritants[jSuppress].areAllSet()) break pairLoop; // suppress all? + if (tokenIrritants != null) { + if (!tokenIrritants.areAllSet() // no complaint against @SuppressWarnings("all") + && (foundIrritants[iSuppress] == null || !foundIrritants[iSuppress].isAnySet(tokenIrritants))) { // if irritant had no matching problem + if (unusedWarningTokenIsWarning) { + int start = value.sourceStart, end = value.sourceEnd; + nextSuppress: for (int jSuppress = iSuppress - 1; jSuppress >= 0; jSuppress--) { + long position = this.suppressWarningScopePositions[jSuppress]; + int startSuppress = (int) (position >>> 32); + int endSuppress = (int) position; + if (start < startSuppress) continue nextSuppress; + if (end > endSuppress) continue nextSuppress; + if (this.suppressWarningIrritants[jSuppress].areAllSet()) break pairLoop; // suppress all? + } + } + int id = options.getIgnoredIrritant(tokenIrritants); + if (id > 0) { + String key = CompilerOptions.optionKeyFromIrritant(id); + this.scope.problemReporter().problemNotAnalysed(value, key); + } else { + this.scope.problemReporter().unusedWarningToken(value); } } - this.scope.problemReporter().unusedWarningToken(value); } } }
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 ff0850e..789c72d 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
@@ -670,13 +670,13 @@ // Answer the signature return type, answers PolyTypeBinding if a poly expression and there is no target type // Base type promotion if (this.constant != Constant.NotAConstant) { - this.constant = Constant.NotAConstant; - long sourceLevel = scope.compilerOptions().sourceLevel; - boolean receiverCast = false; - if (this.receiver instanceof CastExpression) { - this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on - receiverCast = true; - } + this.constant = Constant.NotAConstant; + long sourceLevel = scope.compilerOptions().sourceLevel; + boolean receiverCast = false; + if (this.receiver instanceof CastExpression) { + this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on + receiverCast = true; + } // AspectJ Extension: commenting this out for now. An InterTypeScope has been observed // to have an already resolved receiver // if (this.receiver.resolvedType != null) @@ -689,8 +689,11 @@ this.receiverIsType = this.receiver instanceof NameReference && (((NameReference) this.receiver).bits & Binding.TYPE) != 0; if (receiverCast && this.actualReceiverType != null) { // due to change of declaring class with receiver type, only identity cast should be notified - if (TypeBinding.equalsEquals(((CastExpression)this.receiver).expression.resolvedType, this.actualReceiverType)) { - scope.problemReporter().unnecessaryCast((CastExpression)this.receiver); + TypeBinding resolvedType2 = ((CastExpression)this.receiver).expression.resolvedType; + if (TypeBinding.equalsEquals(resolvedType2, this.actualReceiverType)) { + if (!scope.environment().usesNullTypeAnnotations() || !NullAnnotationMatching.analyse(this.actualReceiverType, resolvedType2, -1).isAnyMismatch()) { + scope.problemReporter().unnecessaryCast((CastExpression)this.receiver); + } } } // resolve type arguments (for generic constructor call)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedTypeReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedTypeReference.java index 537bcbe..b10f83e 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedTypeReference.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedTypeReference.java
@@ -75,8 +75,15 @@ for (int j = 0; j < i; j++) { Annotation[] qualifierAnnot = this.annotations[j]; if (qualifierAnnot != null && qualifierAnnot.length > 0) { - scope.problemReporter().misplacedTypeAnnotations(qualifierAnnot[0], qualifierAnnot[qualifierAnnot.length - 1]); - this.annotations[j] = null; + if (j == 0) { + for (int k = 0; k < qualifierAnnot.length; k++) { + scope.problemReporter().typeAnnotationAtQualifiedName(qualifierAnnot[k]); + } + } else { + scope.problemReporter().misplacedTypeAnnotations(qualifierAnnot[0], + qualifierAnnot[qualifierAnnot.length - 1]); + this.annotations[j] = null; + } } } }
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 004ddaf..3a2af2c 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
@@ -239,7 +239,7 @@ } // Process the lambda, taking care not to double report diagnostics. Don't expect any from resolve, Any from code generation should surface, but not those from flow analysis. - implicitLambda.resolve(currentScope); + implicitLambda.resolveType(currentScope, true); IErrorHandlingPolicy oldPolicy = currentScope.problemReporter().switchErrorHandlingPolicy(silentErrorHandlingPolicy); try { implicitLambda.analyseCode(currentScope,
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java index cf7b55c..fd6da1b 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java
@@ -586,7 +586,8 @@ if (an == NULLABLE || an == NONNULL) start++; } - int end = wrapperWithStart(start).computeEnd(); + SignatureWrapper wrapper1 = wrapperWithStart(start); + int end = wrapper1.skipAngleContents(wrapper1.computeEnd()); return end; }
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 30dbbfc..6e1f66d 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
@@ -2730,7 +2730,7 @@ invoke(Opcodes.OPC_invokevirtual, 1, 1, ConstantPool.JavaLangInvokeSerializedLambdaConstantPoolName, ConstantPool.GetCapturedArg, ConstantPool.GetCapturedArgSignature); - checkcast(isLambda ? mb.declaringClass : ((ReferenceExpression)funcEx).receiverType); + checkcast(mb.declaringClass); sig.append(mb.declaringClass.signature()); } for (int p = 0, max = outerLocalVariables == null ? 0 : outerLocalVariables.length; p < max; p++) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java index 288fb8f..2d5fd51 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.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 @@ -214,7 +214,9 @@ public UnconditionalFlowInfo mergedWith(UnconditionalFlowInfo otherInits) { return unconditionalInits().mergedWith(otherInits); } - +public UnconditionalFlowInfo mergeDefiniteInitsWith(UnconditionalFlowInfo otherInits) { + return unconditionalInits().mergeDefiniteInitsWith(otherInits); +} public UnconditionalFlowInfo nullInfoLessUnconditionalCopy() { return unconditionalInitsWithoutSideEffect(). nullInfoLessUnconditionalCopy();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java index ad63440..67194d5 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java
@@ -538,7 +538,8 @@ unconditionalInits(); // if a variable is only initialized in one branch and not initialized in the other, // then we need to cast a doubt on its initialization in the merged info - mergedInfo.definiteInits &= initsWhenFalse.unconditionalCopy().definiteInits; + mergedInfo.mergeDefiniteInitsWith(initsWhenFalse.unconditionalCopy()); + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=415997, classify unreachability precisely, IsElseStatementUnreachable could be due to null analysis if ((mergedInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0 && (initsWhenFalse.tagBits & FlowInfo.UNREACHABLE) == FlowInfo.UNREACHABLE_BY_NULLANALYSIS) { mergedInfo.tagBits &= ~UNREACHABLE_OR_DEAD; @@ -558,7 +559,7 @@ unconditionalInits(); // if a variable is only initialized in one branch and not initialized in the other, // then we need to cast a doubt on its initialization in the merged info - mergedInfo.definiteInits &= initsWhenTrue.unconditionalCopy().definiteInits; + mergedInfo.mergeDefiniteInitsWith(initsWhenTrue.unconditionalCopy()); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=415997, classify unreachability precisely, IsThenStatementUnreachable could be due to null analysis if ((mergedInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0 && (initsWhenTrue.tagBits & FlowInfo.UNREACHABLE) == FlowInfo.UNREACHABLE_BY_NULLANALYSIS) { mergedInfo.tagBits &= ~UNREACHABLE_OR_DEAD; @@ -615,6 +616,8 @@ abstract public UnconditionalFlowInfo mergedWith( UnconditionalFlowInfo otherInits); +abstract public UnconditionalFlowInfo mergeDefiniteInitsWith(UnconditionalFlowInfo otherInits); + /** * Return a copy of this unconditional flow info, deprived from its null * info. {@link #DEAD_END DEAD_END} is returned unmodified.
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java index 584b45d..916a590 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.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 @@ -2183,7 +2183,54 @@ public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect() { return this; } +public UnconditionalFlowInfo mergeDefiniteInitsWith(UnconditionalFlowInfo otherInits) { + if ((otherInits.tagBits & UNREACHABLE_OR_DEAD) != 0 && this != DEAD_END) { + return this; + } + if ((this.tagBits & UNREACHABLE_OR_DEAD) != 0) { + return (UnconditionalFlowInfo) otherInits.copy(); // make sure otherInits won't be affected + } + // intersection of definitely assigned variables, + this.definiteInits &= otherInits.definiteInits; + if (this.extra != null) { + if (otherInits.extra != null) { + // both sides have extra storage + int i = 0, length, otherLength; + if ((length = this.extra[0].length) < (otherLength = otherInits.extra[0].length)) { + // current storage is shorter -> grow current + for (int j = 0; j < extraLength; j++) { + System.arraycopy(this.extra[j], 0, + (this.extra[j] = new long[otherLength]), 0, length); + } + for (; i < length; i++) { + this.extra[0][i] &= otherInits.extra[0][i]; + } + for (; i < otherLength; i++) { + this.extra[0][i] = otherInits.extra[0][i]; + } + } + else { + // current storage is longer + for (; i < otherLength; i++) { + this.extra[0][i] &= otherInits.extra[0][i]; + } + } + } else { + for (int i = 0; i < this.extra[0].length; i++) { + this.extra[0][i] = 0; + } + } + } + else if (otherInits.extra != null) { + // no storage here, but other has extra storage. + int otherLength = otherInits.extra[0].length; + createExtraSpace(otherLength); + System.arraycopy(otherInits.extra[0], 0, this.extra[0], 0, + otherLength); + } + return this; +} public void resetAssignmentInfo(LocalVariableBinding local) { resetAssignmentInfo(local.id + this.maxFieldCount); }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java index 967230b..3a25dea 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
@@ -614,7 +614,7 @@ case UnqualifiedFieldAccess : return OPTION_ReportUnqualifiedFieldAccess; case UnusedDeclaredThrownException : - return OPTION_ReportUnusedDeclaredThrownExceptionWhenOverriding; + return OPTION_ReportUnusedDeclaredThrownException; case FinallyBlockNotCompleting : return OPTION_ReportFinallyBlockNotCompletingNormally; case InvalidJavadoc : @@ -1277,6 +1277,27 @@ return this.warningThreshold.isAnySet(irritants) || this.errorThreshold.isAnySet(irritants) || this.infoThreshold.isAnySet(irritants); } + /* + * Just return the first irritant id that is set to 'ignored'. + */ + public int getIgnoredIrritant(IrritantSet irritants) { + int[] bits = irritants.getBits(); + for (int i = 0; i < IrritantSet.GROUP_MAX; i++) { + int bit = bits[i]; + for (int b = 0; b < IrritantSet.GROUP_SHIFT; b++) { + int single = bit & (1 << b); + if (single > 0) { + single |= (i << IrritantSet.GROUP_SHIFT); + if (single == MissingNonNullByDefaultAnnotation) + continue; + if (!(this.warningThreshold.isSet(single) || this.errorThreshold.isSet(single) || this.infoThreshold.isSet(single))) { + return single; + } + } + } + } + return 0; + } protected void resetDefaults() { // problem default severities defined on IrritantSet
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java index 54f524d..280a502 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java
@@ -259,7 +259,9 @@ int group = (singleGroupIrritants & GROUP_MASK) >> GROUP_SHIFT; return (this.bits[group] & singleGroupIrritants) != 0; } - + public int[] getBits() { + return this.bits; + } public IrritantSet set(int singleGroupIrritants) { int group = (singleGroupIrritants & GROUP_MASK) >> GROUP_SHIFT; this.bits[group] |= (singleGroupIrritants & ~GROUP_MASK); // erase the group bits
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java index 9b7b03c..17312a2 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java
@@ -15,6 +15,8 @@ package org.eclipse.jdt.internal.compiler.lookup; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.*; @@ -60,6 +62,8 @@ boolean connectingHierarchy; private ArrayList<Invocation> inferredInvocations; + /** Cache of interned inference variables. Access only via {@link InferenceVariable#get(TypeBinding, int, InvocationSite, Scope, ReferenceBinding)}. */ + Map<InferenceVariable.InferenceVarKey, InferenceVariable> uniqueInferenceVariables = new HashMap<>(); public CompilationUnitScope(CompilationUnitDeclaration unit, LookupEnvironment environment) { super(COMPILATION_UNIT_SCOPE, null);
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 f359f9c..2e127a7 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
@@ -292,7 +292,7 @@ this.environment.getNonNullAnnotationName()); break returnType; } else { - scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod, useTypeAnnotations); + scope.problemReporter().cannotImplementIncompatibleNullness(scope.referenceContext(), currentMethod, inheritedMethod, useTypeAnnotations); return; } } @@ -308,7 +308,7 @@ scope.problemReporter().illegalReturnRedefinition(srcMethod, inheritedMethod, this.environment.getNonNullAnnotationName()); else - scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod, useTypeAnnotations); + scope.problemReporter().cannotImplementIncompatibleNullness(scope.referenceContext(), currentMethod, inheritedMethod, useTypeAnnotations); return; } } @@ -400,7 +400,7 @@ inheritedMethod.declaringClass, (inheritedNonNullNess == null) ? null : this.environment.getNullableAnnotationName()); } else { - scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod, false); + scope.problemReporter().cannotImplementIncompatibleNullness(scope.referenceContext(), currentMethod, inheritedMethod, false); } continue; } else if (currentNonNullNess == null) @@ -413,7 +413,7 @@ inheritedMethod.declaringClass, annotationName); } else { - scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod, false); + scope.problemReporter().cannotImplementIncompatibleNullness(scope.referenceContext(), currentMethod, inheritedMethod, false); } continue; } else if (inheritedNonNullNess == Boolean.TRUE) { @@ -438,7 +438,7 @@ if (currentArgument != null) scope.problemReporter().illegalParameterRedefinition(currentArgument, inheritedMethod.declaringClass, inheritedParameter); else - scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod, false); + scope.problemReporter().cannotImplementIncompatibleNullness(scope.referenceContext(), currentMethod, inheritedMethod, false); } } }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java index 07851ae..f30d941 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java
@@ -130,7 +130,6 @@ /** The inference variables for which as solution is sought. */ InferenceVariable[] inferenceVariables; - int nextVarId; /** Constraints that have not yet been reduced and incorporated. */ ConstraintFormula[] initialConstraints; @@ -164,43 +163,12 @@ /** Not per JLS: signal when current is ready to directly merge all bounds from inner. */ private boolean directlyAcceptingInnerBounds = false; - // InferenceVariable interning: - private InferenceVariable[] internedVariables; - - private InferenceVariable getInferenceVariable(TypeBinding typeParameter, int rank, InvocationSite site) { - InferenceContext18 outermostContext = this.environment.currentInferenceContext; - if (outermostContext == null) - outermostContext = this; - int i = 0; - InferenceVariable[] interned = outermostContext.internedVariables; - if (interned == null) { - outermostContext.internedVariables = new InferenceVariable[10]; - } else { - int len = interned.length; - for (i = 0; i < len; i++) { - InferenceVariable var = interned[i]; - if (var == null) - break; - if (var.typeParameter == typeParameter && var.rank == rank && isSameSite(var.site, site)) //$IDENTITY-COMPARISON$ - return var; - } - if (i >= len) - System.arraycopy(interned, 0, outermostContext.internedVariables = new InferenceVariable[len+10], 0, len); - } - boolean differentContext = outermostContext != this; - int id = differentContext ? Math.max(this.nextVarId, outermostContext.nextVarId) : this.nextVarId; - this.nextVarId = id + 1; - if (differentContext) - outermostContext.nextVarId = this.nextVarId; - return outermostContext.internedVariables[i] = new InferenceVariable(typeParameter, rank, id, site, this.environment, this.object); - } - - boolean isSameSite(InvocationSite site1, InvocationSite site2) { + public static boolean isSameSite(InvocationSite site1, InvocationSite site2) { if (site1 == site2) return true; if (site1 == null || site2 == null) return false; - if (site1.sourceStart() == site2.sourceStart() && site1.sourceEnd() == site2.sourceEnd() && site1.toString().equals(site2.toString())) + if (site1.sourceStart() == site2.sourceStart() && site1.sourceEnd() == site2.sourceEnd()) return true; return false; } @@ -336,7 +304,7 @@ } InferenceVariable[] newVariables = new InferenceVariable[len]; for (int i = 0; i < len; i++) - newVariables[i] = getInferenceVariable(typeVariables[i], i, this.currentInvocation); + newVariables[i] = InferenceVariable.get(typeVariables[i], i, this.currentInvocation, this.scope, this.object); addInferenceVariables(newVariables); return newVariables; } @@ -364,7 +332,7 @@ newVariables[i] = (InferenceVariable) typeVariables[i]; // prevent double substitution of an already-substituted inferenceVariable else toAdd[numToAdd++] = - newVariables[i] = getInferenceVariable(typeVariables[i], i, this.currentInvocation); + newVariables[i] = InferenceVariable.get(typeVariables[i], i, this.currentInvocation, this.scope, this.object); } if (numToAdd > 0) { int start = 0; @@ -1565,18 +1533,6 @@ if (!isSameSite(innerCtx.currentInvocation, this.currentInvocation)) innerCtx.outerContext = this; this.usesUncheckedConversion = innerCtx.usesUncheckedConversion; - for (InferenceVariable variable : this.inferenceVariables) - if (!isInterned(variable)) - variable.updateSourceName(this.nextVarId++); - } - - boolean isInterned(InferenceVariable iv) { - if (this.internedVariables != null) - for (int i = 0; i < this.internedVariables.length; i++) { - if (this.internedVariables[i] == iv) //$IDENTITY-COMPARISON$ - return true; - } - return false; } public void resumeSuspendedInference(SuspendedInferenceRecord record) { @@ -1589,8 +1545,6 @@ // move to back, add previous to front: System.arraycopy(this.inferenceVariables, 0, this.inferenceVariables=new InferenceVariable[l1+l2], l2, l1); System.arraycopy(record.inferenceVariables, 0, this.inferenceVariables, 0, l2); - for (int i=l1;i<l1+l2;i++) - this.inferenceVariables[i].updateSourceName(this.nextVarId++); } // replace invocation site & arguments:
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java index 180f770..d482c7a 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceSubstitution.java
@@ -36,7 +36,7 @@ public TypeBinding substitute(Substitution substitution, TypeBinding originalType) { for (int i = 0; i < this.variables.length; i++) { InferenceVariable variable = this.variables[i]; - if (this.site == variable.site && TypeBinding.equalsEquals(getP(i), originalType)) { + if (InferenceContext18.isSameSite(this.site, variable.site) && TypeBinding.equalsEquals(getP(i), originalType)) { if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled && originalType.hasNullTypeAnnotations()) return this.environment.createAnnotatedType(variable.withoutToplevelNullAnnotation(), originalType.getTypeAnnotations()); return variable;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceVariable.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceVariable.java index b999b85..6b78a5f 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceVariable.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceVariable.java
@@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; +import java.util.Map; import java.util.Set; import org.eclipse.jdt.core.compiler.CharOperation; @@ -18,14 +19,74 @@ * Implementation of 18.1.1 in JLS8 */ public class InferenceVariable extends TypeVariableBinding { + + /** Structured key for interning. */ + static class InferenceVarKey { + /*@NonNull*/ TypeBinding typeParameter; + long position; + int rank; + InferenceVarKey(TypeBinding typeParameter, InvocationSite site, int rank) { + this.typeParameter = typeParameter; + this.position = ((long) site.sourceStart() << 32) + site.sourceEnd(); + this.rank = rank; + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (this.position ^ (this.position >>> 32)); + result = prime * result + this.rank; + result = prime * result + this.typeParameter.id; + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof InferenceVarKey)) + return false; + InferenceVarKey other = (InferenceVarKey) obj; + if (this.position != other.position) + return false; + if (this.rank != other.rank) + return false; + if (TypeBinding.notEquals(this.typeParameter, other.typeParameter)) + return false; + return true; + } + } + + /** + * Create or retrieve the inference variable representing the given typeParameter. + * Inference variables are interned to avoid duplication due to lambda copying. + */ + public static InferenceVariable get(TypeBinding typeParameter, int rank, InvocationSite site, Scope scope, ReferenceBinding object) { + Map<InferenceVarKey, InferenceVariable> uniqueInferenceVariables = scope.compilationUnitScope().uniqueInferenceVariables; + InferenceVariable var = null; + InferenceVarKey key = null; + if (site != null && typeParameter != null) { + key = new InferenceVarKey(typeParameter, site, rank); + var = uniqueInferenceVariables.get(key); + } + if (var == null) { + int newVarId = uniqueInferenceVariables.size(); + var = new InferenceVariable(typeParameter, rank, newVarId, site, scope.environment(), object); + if (key != null) + uniqueInferenceVariables.put(key, var); + } + return var; + } + InvocationSite site; TypeBinding typeParameter; long nullHints; // one of TagBits.{AnnotationNonNull,AnnotationNullable} may steer inference into inferring nullness as well; set both bits to request avoidance. private InferenceVariable prototype; - int varId; // this is used for constructing a source name like T#0. NB: varId and sourceName are mutable, to be updated when two InferenceContext18 are integrated. + int varId; // this is used for constructing a source name like T#0. - public InferenceVariable(TypeBinding typeParameter, int parameterRank, int iVarId, InvocationSite site, LookupEnvironment environment, ReferenceBinding object) { + private InferenceVariable(TypeBinding typeParameter, int parameterRank, int iVarId, InvocationSite site, LookupEnvironment environment, ReferenceBinding object) { this(typeParameter, parameterRank, site, CharOperation.concat(typeParameter.shortReadableName(), Integer.toString(iVarId).toCharArray(), '#'), environment, object); @@ -49,15 +110,7 @@ this.superclass = object; this.prototype = this; } - void updateSourceName(int newId) { - int hashPos = CharOperation.indexOf('#', this.sourceName); - this.varId = newId; - this.sourceName = CharOperation.concat( - CharOperation.subarray(this.sourceName, 0, hashPos), - Integer.toString(this.varId).toCharArray(), - '#'); - } - + @Override public TypeBinding clone(TypeBinding enclosingType) { InferenceVariable clone = new InferenceVariable(this.typeParameter, this.rank, this.site, this.sourceName, this.environment, this.superclass); @@ -131,17 +184,18 @@ public int hashCode() { int code = this.typeParameter.hashCode() + 17 * this.rank; - if (this.site != null) - return 31 * code + this.site.hashCode(); - else - return code; + if (this.site != null) { + code = 31 * code + this.site.sourceStart(); + code = 31 * code + this.site.sourceEnd(); + } + return code; } public boolean equals(Object obj) { if (!(obj instanceof InferenceVariable)) return false; InferenceVariable other = (InferenceVariable) obj; return this.rank == other.rank - && this.site == other.site + && InferenceContext18.isSameSite(this.site, other.site) && TypeBinding.equalsEquals(this.typeParameter, other.typeParameter); }
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 67faad1..55cc090 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
@@ -569,12 +569,16 @@ //pre: null annotation analysis is enabled protected void fillInDefaultNonNullness18(AbstractMethodDeclaration sourceMethod, LookupEnvironment env) { + MethodBinding original = original(); + if(original == null) { + return; + } if (hasNonNullDefaultFor(DefaultLocationParameter, true)) { boolean added = false; int length = this.parameters.length; for (int i = 0; i < length; i++) { TypeBinding parameter = this.parameters[i]; - if (!parameter.acceptsNonNullDefault()) + if (!original.parameters[i].acceptsNonNullDefault()) continue; long existing = parameter.tagBits & TagBits.AnnotationNullMASK; if (existing == 0L) { @@ -592,7 +596,7 @@ if (added) this.tagBits |= TagBits.HasParameterAnnotations; } - if (this.returnType != null && hasNonNullDefaultFor(DefaultLocationReturnType, true) && this.returnType.acceptsNonNullDefault()) { + if (original.returnType != null && hasNonNullDefaultFor(DefaultLocationReturnType, true) && original.returnType.acceptsNonNullDefault()) { if ((this.returnType.tagBits & TagBits.AnnotationNullMASK) == 0) { this.returnType = env.createAnnotatedType(this.returnType, new AnnotationBinding[]{env.getNonNullAnnotation()}); } else if (sourceMethod instanceof MethodDeclaration && (this.returnType.tagBits & TagBits.AnnotationNonNull) != 0
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java index aea2423..bbd02d3 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
@@ -60,6 +60,12 @@ */ public static MethodBinding computeCompatibleMethod(MethodBinding originalMethod, TypeBinding[] arguments, Scope scope, InvocationSite invocationSite) { + LookupEnvironment environment = scope.environment(); + if ((originalMethod.tagBits & TagBits.IsNullnessKnown) == 0) { + // ensure nullness of originalMethod is known (but we are not interested in reporting problems against originalMethod) + new ImplicitNullAnnotationVerifier(environment, environment.globalOptions.inheritNullAnnotations) + .checkImplicitNullAnnotations(originalMethod, null/*srcMethod*/, false, scope); + } ParameterizedGenericMethodBinding methodSubstitute; TypeVariableBinding[] typeVariables = originalMethod.typeVariables; TypeBinding[] substitutes = invocationSite.genericTypeArguments(); @@ -72,7 +78,7 @@ // incompatible due to wrong arity return new ProblemMethodBinding(originalMethod, originalMethod.selector, substitutes, ProblemReasons.TypeParameterArityMismatch); } - methodSubstitute = scope.environment().createParameterizedGenericMethod(originalMethod, substitutes); + methodSubstitute = environment.createParameterizedGenericMethod(originalMethod, substitutes); break computeSubstitutes; } // perform type argument inference (15.12.2.7)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java index 40cae44..20639ff 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
@@ -1149,7 +1149,7 @@ else // WildcardBinding, CaptureBinding have no sourceName nameBuffer.append(this.type.readableName()); } - if (this.arguments != null && this.arguments.length > 0) { // empty arguments array happens when PTB has been created just to capture type annotations + if (this.arguments != null && this.arguments.length > 0 && !isRawType()) { // empty arguments array happens when PTB has been created just to capture type annotations nameBuffer.append('<'); for (int i = 0, length = this.arguments.length; i < length; i++) { if (i > 0) nameBuffer.append(','); @@ -1177,7 +1177,7 @@ else // WildcardBinding, CaptureBinding have no sourceName nameBuffer.append(this.type.shortReadableName()); } - if (this.arguments != null && this.arguments.length > 0) { // empty arguments array happens when PTB has been created just to capture type annotations + if (this.arguments != null && this.arguments.length > 0 && !isRawType()) { // empty arguments array happens when PTB has been created just to capture type annotations nameBuffer.append('<'); for (int i = 0, length = this.arguments.length; i < length; i++) { if (i > 0) nameBuffer.append(',');
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java index 8c7c2c7..965f055 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java
@@ -18,7 +18,6 @@ import java.util.HashMap; -import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; import org.eclipse.jdt.internal.compiler.util.Util; @@ -168,19 +167,7 @@ urb = (UnresolvedReferenceBinding) type; ReferenceBinding resolvedType = urb.resolvedType; if (resolvedType != null) { - if(CharOperation.indexOf('$', type.sourceName()) > 0) { - type = this.environment.convertToRawType(resolvedType, false); - } else { - type = resolvedType; - } - } else if (CharOperation.indexOf('$', type.sourceName()) > 0) { - boolean mayTolerateMissingType = this.environment.mayTolerateMissingType; - this.environment.mayTolerateMissingType = true; - try { - type = BinaryTypeBinding.resolveType(type, this.environment, true); // to ensure unique id assignment (when enclosing type is parameterized, inner type is also) - } finally { - this.environment.mayTolerateMissingType = mayTolerateMissingType; - } + type = resolvedType; } } try {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java index 0fe0324..9386b62 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
@@ -984,7 +984,6 @@ TypeBinding [] annotatedTypes = getDerivedTypesForDeferredInitialization(); for (int i = 0, length = annotatedTypes == null ? 0 : annotatedTypes.length; i < length; i++) { TypeVariableBinding annotatedType = (TypeVariableBinding) annotatedTypes[i]; - if (annotatedType.firstBound == null) annotatedType.firstBound = firstBound; } } @@ -1001,7 +1000,6 @@ TypeBinding [] annotatedTypes = getDerivedTypesForDeferredInitialization(); for (int i = 0, length = annotatedTypes == null ? 0 : annotatedTypes.length; i < length; i++) { TypeVariableBinding annotatedType = (TypeVariableBinding) annotatedTypes[i]; - if (annotatedType.superclass == null) annotatedType.superclass = superclass; } } @@ -1016,7 +1014,6 @@ TypeBinding [] annotatedTypes = getDerivedTypesForDeferredInitialization(); for (int i = 0, length = annotatedTypes == null ? 0 : annotatedTypes.length; i < length; i++) { TypeVariableBinding annotatedType = (TypeVariableBinding) annotatedTypes[i]; - if (annotatedType.superInterfaces == null) annotatedType.superInterfaces = superInterfaces; } }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java index 5fecec4..2a1fd28 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/AbstractCommentParser.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2013 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 @@ -172,7 +172,13 @@ } this.lineEnd = (this.linePtr == this.lastLinePtr) ? this.javadocEnd: this.scanner.getLineEnd(this.linePtr) - 1; this.javadocTextEnd = this.javadocEnd - 2; // supposed text end, it will be refined later... - + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206345 + // when parsing tags such as @code and @literal, + // any tag should be discarded and considered as plain text until + // properly closed with closing brace + boolean considerTagAsPlainText = false; + // internal counter for opening braces + int openingBraces = 0; // Loop on each comment character int textEndPosition = -1; while (!this.abort && this.index < this.javadocEnd) { @@ -208,7 +214,20 @@ switch (nextCharacter) { case '@' : // Start tag parsing only if we are on line beginning or at inline tag beginning - if ((!this.lineStarted || previousChar == '{')) { + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206345: ignore all tags when inside @literal or @code tags + if (considerTagAsPlainText) { + // new tag found + if (!this.lineStarted) { + // we may want to report invalid syntax when no closing brace found, + // or when incoherent number of closing braces found + if (openingBraces > 0 && this.reportProblems) { + this.sourceParser.problemReporter().javadocUnterminatedInlineTag(this.inlineTagStart, invalidInlineTagLineEnd); + } + considerTagAsPlainText = false; + this.inlineTagStarted = false; + openingBraces = 0; + } + } else if ((!this.lineStarted || previousChar == '{')) { if (this.inlineTagStarted) { setInlineTagStarted(false); // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53279 @@ -252,6 +271,12 @@ invalidTagLineEnd = this.lineEnd; textEndPosition = this.index; } + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206345 + // dealing with @literal or @code tags: ignore next tags + if (!isFormatterParser && (this.tagValue == TAG_LITERAL_VALUE || this.tagValue == TAG_CODE_VALUE)) { + considerTagAsPlainText = true; + openingBraces++; + } } catch (InvalidInputException e) { consumeToken(); } @@ -284,13 +309,24 @@ if (verifText && this.tagValue == TAG_RETURN_VALUE && this.returnStatement != null) { refreshReturnStatement(); } + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206345: when ignoring tags, only decrement the opening braces counter + if (considerTagAsPlainText) { + invalidInlineTagLineEnd = this.lineEnd; + if (--openingBraces == 0) { + considerTagAsPlainText = false; // re-enable tag validation + } + } if (this.inlineTagStarted) { textEndPosition = this.index - 1; - if (this.lineStarted && this.textStart != -1 && this.textStart < textEndPosition) { - pushText(this.textStart, textEndPosition); + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206345: do not push text yet if ignoring tags + if (!considerTagAsPlainText) { + if (this.lineStarted && this.textStart != -1 && this.textStart < textEndPosition) { + pushText(this.textStart, textEndPosition); + } + refreshInlineTagPosition(previousPosition); } - refreshInlineTagPosition(previousPosition); - if (!isFormatterParser) this.textStart = this.index; + if (!isFormatterParser && !considerTagAsPlainText) + this.textStart = this.index; setInlineTagStarted(false); } else { if (!this.lineStarted) { @@ -304,7 +340,10 @@ if (verifText && this.tagValue == TAG_RETURN_VALUE && this.returnStatement != null) { refreshReturnStatement(); } - if (this.inlineTagStarted) { + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206345: count opening braces when ignoring tags + if (considerTagAsPlainText) { + openingBraces++; + } else if (this.inlineTagStarted) { setInlineTagStarted(false); // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53279 // Cannot have opening brace in inline comment @@ -325,7 +364,8 @@ this.textStart = previousPosition; } this.lineStarted = true; - this.inlineTagStart = previousPosition; + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206345: do not update tag start position when ignoring tags + if (!considerTagAsPlainText) this.inlineTagStart = previousPosition; break; case '*' : // Store the star position as text start while formatting @@ -395,7 +435,8 @@ // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53279 // Cannot leave comment inside inline comment - if (this.inlineTagStarted) { + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=206345: handle unterminated @code or @literal tag + if (this.inlineTagStarted || considerTagAsPlainText) { if (this.reportProblems) { int end = this.javadocTextEnd<invalidInlineTagLineEnd ? this.javadocTextEnd : invalidInlineTagLineEnd; if (this.index >= this.javadocEnd) end = invalidInlineTagLineEnd;
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 12822b2..28f8c44 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
@@ -1594,6 +1594,8 @@ return ProblemSeverities.Warning; case IProblem.IllegalUseOfUnderscoreAsAnIdentifier: return this.underScoreIsLambdaParameter ? ProblemSeverities.Error : ProblemSeverities.Warning; + case IProblem.ProblemNotAnalysed: + return ProblemSeverities.Info; // Not configurable } int irritant = getIrritant(problemID); if (irritant != 0) { @@ -5861,6 +5863,19 @@ severity, annotation.sourceStart, annotation.sourceEnd); } +public void nullAnnotationAtQualifyingType(Annotation annotation) { + String[] arguments = new String[] { + String.valueOf(annotation.resolvedType.readableName()) + }; + String[] shortArguments = new String[] { + String.valueOf(annotation.resolvedType.shortReadableName()) + }; + int severity = ProblemSeverities.Error | ProblemSeverities.Fatal; + handle(IProblem.NullAnnotationAtQualifyingType, + arguments, shortArguments, + severity, + annotation.sourceStart, annotation.sourceEnd); +} public void nullAnnotationUnsupportedLocation(TypeReference type) { int sourceEnd = type.sourceEnd; if (type instanceof ParameterizedSingleTypeReference) { @@ -8952,6 +8967,14 @@ token.sourceStart, token.sourceEnd); } +public void problemNotAnalysed(Expression token, String optionKey) { + this.handle( + IProblem.ProblemNotAnalysed, + optionKey != null ? new String[]{optionKey} : new String[]{}, + new String[] { token.constant.stringValue() }, + token.sourceStart, + token.sourceEnd); +} public void useAssertAsAnIdentifier(int sourceStart, int sourceEnd) { this.handle( IProblem.UseAssertAsAnIdentifier, @@ -9643,11 +9666,11 @@ location.sourceEnd); } -public void cannotImplementIncompatibleNullness(MethodBinding currentMethod, MethodBinding inheritedMethod, boolean showReturn) { +public void cannotImplementIncompatibleNullness(ReferenceContext context, MethodBinding currentMethod, MethodBinding inheritedMethod, boolean showReturn) { int sourceStart = 0, sourceEnd = 0; - if (this.referenceContext instanceof TypeDeclaration) { - sourceStart = ((TypeDeclaration) this.referenceContext).sourceStart; - sourceEnd = ((TypeDeclaration) this.referenceContext).sourceEnd; + if (context instanceof TypeDeclaration) { + sourceStart = ((TypeDeclaration) context).sourceStart; + sourceEnd = ((TypeDeclaration) context).sourceEnd; } String[] problemArguments = { showReturn @@ -10416,6 +10439,10 @@ annotation.sourceStart, annotation.sourceEnd); } +public void typeAnnotationAtQualifiedName(Annotation annotation) { + this.handle(IProblem.TypeAnnotationAtQualifiedName, NoArgument, NoArgument, annotation.sourceStart, + annotation.sourceEnd); + } public void genericInferenceError(String message, InvocationSite invocationSite) { genericInferenceProblem(message, invocationSite, ProblemSeverities.Error); }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties index 4746474..d07d1ee 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
@@ -862,9 +862,12 @@ 1057 = strictfp is not permitted for abstract interface method {0} 1058 = Default methods are allowed only in interfaces. 1059 = Cannot infer type argument(s) for <{0}> {1} +1060 = Illegally placed annotation: type annotations must directly precede the simple name of the type they are meant to affect (or the [] for arrays) +1061 = The nullness annotation ''{0}'' is not applicable at this location, it must be placed directly before the nested type name. 1100 = Problem detected during type inference: {0} - +#1101 is already used up but deprecated +1102 = At least one of the problems in category ''{0}'' is not analysed due to a compiler option being ignored ### ELABORATIONS ## Access restrictions 78592 = The type ''{1}'' is not API (restriction on classpath entry ''{0}'')
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Util.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Util.java index e033637..818bb0b 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Util.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Util.java
@@ -1198,7 +1198,7 @@ count++; } } catch (ArrayIndexOutOfBoundsException e) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException(e); } }
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/AST.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/AST.java index 7757fed..aae7faa 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/AST.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/AST.java
@@ -503,7 +503,7 @@ return (CompilationUnit) result; } catch (IllegalStateException e) { // convert ASTParser's complaints into old form - throw new IllegalArgumentException(); + throw new IllegalArgumentException(e); } } @@ -577,7 +577,7 @@ return (CompilationUnit) result; } catch (IllegalStateException e) { // convert ASTParser's complaints into old form - throw new IllegalArgumentException(); + throw new IllegalArgumentException(e); } } @@ -815,21 +815,19 @@ } catch (NoSuchMethodException e) { // all AST node classes have a Foo(AST) constructor // therefore nodeClass is not legit - throw new IllegalArgumentException(); + throw new IllegalArgumentException(e); } catch (InstantiationException e) { // all concrete AST node classes can be instantiated // therefore nodeClass is not legit - throw new IllegalArgumentException(); + throw new IllegalArgumentException(e); } catch (IllegalAccessException e) { // all AST node classes have an accessible Foo(AST) constructor // therefore nodeClass is not legit - throw new IllegalArgumentException(); + throw new IllegalArgumentException(e); } catch (InvocationTargetException e) { // concrete AST node classes do not die in the constructor // therefore nodeClass is not legit - IllegalArgumentException iae = new IllegalArgumentException(); - iae.initCause(e.getCause()); - throw iae; + throw new IllegalArgumentException(e.getCause()); } }
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTParser.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTParser.java index fa4e478..4452ea6 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTParser.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTParser.java
@@ -299,7 +299,7 @@ throw new IllegalStateException("invalid environment settings"); //$NON-NLS-1$ } } catch (IllegalArgumentException e) { - throw new IllegalStateException("invalid environment settings"); //$NON-NLS-1$ + throw new IllegalStateException("invalid environment settings", e); //$NON-NLS-1$ } return allClasspaths; }
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CharacterLiteral.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CharacterLiteral.java index b4b1da6..512a8d0 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CharacterLiteral.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CharacterLiteral.java
@@ -286,7 +286,7 @@ throw new IllegalArgumentException("illegal character literal");//$NON-NLS-1$ } } catch (InvalidInputException e) { - throw new IllegalArgumentException("illegal character literal");//$NON-NLS-1$ + throw new IllegalArgumentException("illegal character literal", e);//$NON-NLS-1$ } } nextChar = scanner.getNextChar();
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CompilationUnitResolver.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CompilationUnitResolver.java index 15ca187..bbb9ab2 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CompilationUnitResolver.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CompilationUnitResolver.java
@@ -769,7 +769,7 @@ throw new IllegalArgumentException(element + " has an unexpected type"); //$NON-NLS-1$ binaryElementPositions.put(key, i); } catch (JavaModelException e) { - throw new IllegalArgumentException(element + " does not exist"); //$NON-NLS-1$ + throw new IllegalArgumentException(element + " does not exist", e); //$NON-NLS-1$ } } } @@ -792,7 +792,7 @@ try { finder.search(); } catch (JavaModelException e) { - throw new IllegalArgumentException(element + " does not exist"); //$NON-NLS-1$ + throw new IllegalArgumentException(element + " does not exist", e); //$NON-NLS-1$ } this.bindings[index] = finder.foundBinding; }
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DocCommentParser.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DocCommentParser.java index 1136b34..6bb5390 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DocCommentParser.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DocCommentParser.java
@@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2013 IBM Corporation and others. + * 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 @@ -427,6 +427,9 @@ if (length == TAG_CATEGORY_LENGTH && CharOperation.equals(TAG_CATEGORY, tagName)) { this.tagValue = TAG_CATEGORY_VALUE; valid = parseIdentifierTag(false); // TODO (frederic) reconsider parameter value when @category will be significant in spec + } else if (length == TAG_CODE_LENGTH && CharOperation.equals(TAG_CODE, tagName)) { + this.tagValue = TAG_CODE_VALUE; + createTag(); } else { this.tagValue = TAG_OTHERS_VALUE; createTag(); @@ -490,8 +493,11 @@ this.tagValue = TAG_LINK_VALUE; } else if (length == TAG_LINKPLAIN_LENGTH && CharOperation.equals(TAG_LINKPLAIN, tagName)) { this.tagValue = TAG_LINKPLAIN_VALUE; + } else if (length == TAG_LITERAL_LENGTH && CharOperation.equals(TAG_LITERAL, tagName)) { + this.tagValue = TAG_LITERAL_VALUE; } - if (this.tagValue != NO_TAG_VALUE) { + + if (this.tagValue != NO_TAG_VALUE && this.tagValue != TAG_LITERAL_VALUE) { if (this.inlineTagStarted) { valid = parseReference(); } else { @@ -500,7 +506,7 @@ valid = false; } } else { - this.tagValue = TAG_OTHERS_VALUE; + if (this.tagValue == NO_TAG_VALUE) this.tagValue = TAG_OTHERS_VALUE; createTag(); } break;
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/Javadoc.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/Javadoc.java index 85ae7ff..4d4abe5 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/Javadoc.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/Javadoc.java
@@ -264,7 +264,7 @@ throw new IllegalArgumentException(); } } catch (InvalidInputException e) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException(e); } preValueChange(COMMENT_PROPERTY); this.comment = docComment;
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/SimpleName.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/SimpleName.java index 60dab87..fda05fb 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/SimpleName.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/SimpleName.java
@@ -199,9 +199,7 @@ throw new IllegalArgumentException("Invalid identifier : >" + identifier + "<"); //$NON-NLS-1$//$NON-NLS-2$ } } catch (InvalidInputException e) { - IllegalArgumentException iae = new IllegalArgumentException("Invalid identifier : >" + identifier + "<"); //$NON-NLS-1$//$NON-NLS-2$ - iae.initCause(e); - throw iae; + throw new IllegalArgumentException("Invalid identifier : >" + identifier + "<", e); //$NON-NLS-1$//$NON-NLS-2$ } } finally { this.ast.scanner.sourceLevel = sourceLevel;
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/Statement.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/Statement.java index dffc912..60ba518 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/Statement.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/Statement.java
@@ -155,7 +155,7 @@ throw new IllegalArgumentException(); } } catch (InvalidInputException e) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException(e); } } // we do not consider the obsolete comment as a structureal property
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java index fb0571a..7ecfa08 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java
@@ -4346,8 +4346,6 @@ } final void handleException(Throwable e) { - IllegalArgumentException runtimeException= new IllegalArgumentException("Document does not match the AST"); //$NON-NLS-1$ - runtimeException.initCause(e); - throw runtimeException; + throw new IllegalArgumentException("Document does not match the AST", e); //$NON-NLS-1$ } }
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFormatter.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFormatter.java index 2bd730c..efb8893 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFormatter.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFormatter.java
@@ -429,7 +429,7 @@ try { doc.addPosition(POS_CATEGORY, positions[i]); } catch (BadLocationException e) { - throw new IllegalArgumentException("Position outside of string. offset: " + positions[i].offset + ", length: " + positions[i].length + ", string size: " + string.length()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + throw new IllegalArgumentException("Position outside of string. offset: " + positions[i].offset + ", length: " + positions[i].length + ", string size: " + string.length(), e); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ } } }
diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java index adb1566..987a744 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/core/formatter/DefaultCodeFormatterConstants.java
@@ -4408,7 +4408,7 @@ int existingValue = Integer.parseInt(value); return (existingValue & Alignment.M_FORCE) != 0; } catch (NumberFormatException e) { - throw new IllegalArgumentException("Alignment value is not an integer: " + value); //$NON-NLS-1$ + throw new IllegalArgumentException("Alignment value is not an integer: " + value, e); //$NON-NLS-1$ } } @@ -4435,7 +4435,7 @@ return INDENT_DEFAULT; } } catch (NumberFormatException e) { - throw new IllegalArgumentException("Alignment value is not an integer: " + value); //$NON-NLS-1$ + throw new IllegalArgumentException("Alignment value is not an integer: " + value, e); //$NON-NLS-1$ } } @@ -4478,7 +4478,7 @@ return WRAP_NO_SPLIT; } } catch (NumberFormatException e) { - throw new IllegalArgumentException("Alignment value is not an integer: " + value); //$NON-NLS-1$ + throw new IllegalArgumentException("Alignment value is not an integer: " + value, e); //$NON-NLS-1$ } } /** @@ -4504,7 +4504,7 @@ } return String.valueOf(existingValue); } catch (NumberFormatException e) { - throw new IllegalArgumentException("Alignment value is not an integer: " + value); //$NON-NLS-1$ + throw new IllegalArgumentException("Alignment value is not an integer: " + value, e); //$NON-NLS-1$ } } @@ -4547,7 +4547,7 @@ } return String.valueOf(existingValue); } catch (NumberFormatException e) { - throw new IllegalArgumentException("Alignment value is not an integer: " + value); //$NON-NLS-1$ + throw new IllegalArgumentException("Alignment value is not an integer: " + value, e); //$NON-NLS-1$ } } /** @@ -4605,7 +4605,7 @@ } return String.valueOf(existingValue); } catch (NumberFormatException e) { - throw new IllegalArgumentException("Alignment value is not an integer: " + value); //$NON-NLS-1$ + throw new IllegalArgumentException("Alignment value is not an integer: " + value, e); //$NON-NLS-1$ } } }
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 e186a14..6e5aa19 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
@@ -114,19 +114,6 @@ import java.util.Hashtable; import java.util.Map; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IConfigurationElement; -import org.eclipse.core.runtime.IExtension; -import org.eclipse.core.runtime.IExtensionPoint; -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.Plugin; -import org.eclipse.core.runtime.QualifiedName; -import org.eclipse.core.runtime.SubMonitor; -import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; @@ -140,6 +127,19 @@ import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +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.Plugin; +import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.search.IJavaSearchConstants; @@ -150,10 +150,26 @@ import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; -import org.eclipse.jdt.internal.core.*; +import org.eclipse.jdt.internal.core.BatchOperation; +import org.eclipse.jdt.internal.core.BufferManager; +import org.eclipse.jdt.internal.core.ClasspathAccessRule; +import org.eclipse.jdt.internal.core.ClasspathAttribute; +import org.eclipse.jdt.internal.core.ClasspathEntry; +import org.eclipse.jdt.internal.core.ClasspathValidation; +import org.eclipse.jdt.internal.core.CreateTypeHierarchyOperation; +import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner; +import org.eclipse.jdt.internal.core.ExternalFoldersManager; +import org.eclipse.jdt.internal.core.JavaCorePreferenceInitializer; +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.Region; +import org.eclipse.jdt.internal.core.SetContainerOperation; +import org.eclipse.jdt.internal.core.SetVariablesOperation; 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.search.indexing.IndexManager; import org.eclipse.jdt.internal.core.util.MementoTokenizer; import org.eclipse.jdt.internal.core.util.Messages; import org.eclipse.jdt.internal.core.util.Util; @@ -4116,7 +4132,7 @@ // if factory is null, default factory must be used if (factory == null) factory = BufferManager.getDefaultBufferManager().getDefaultBufferFactory(); - return getWorkingCopies(BufferFactoryWrapper.create(factory)); + return getWorkingCopies(org.eclipse.jdt.internal.core.BufferFactoryWrapper.create(factory)); } /** @@ -4181,14 +4197,14 @@ JavaModelManager manager = JavaModelManager.getJavaModelManager(); try { SubMonitor subMonitor = mainMonitor.split(50).setWorkRemaining(100); // 50% of the time is spent in initializing containers and variables - subMonitor.worked(5); // give feedback to the user that something is happening + subMonitor.split(5); // give feedback to the user that something is happening manager.batchContainerInitializationsProgress.initializeAfterLoadMonitor.set(subMonitor); if (manager.forceBatchInitializations(true/*initAfterLoad*/)) { // if no other thread has started the batch container initializations manager.getClasspathContainer(Path.EMPTY, null); // force the batch initialization } else { // else wait for the batch initialization to finish while (manager.batchContainerInitializations == JavaModelManager.BATCH_INITIALIZATION_IN_PROGRESS) { subMonitor.subTask(manager.batchContainerInitializationsProgress.subTaskName); - subMonitor.worked(manager.batchContainerInitializationsProgress.getWorked()); + subMonitor.split(manager.batchContainerInitializationsProgress.getWorked()); synchronized(manager) { try { manager.wait(100); @@ -4264,38 +4280,8 @@ // dummy query for waiting until the indexes are ready mainMonitor.subTask(Messages.javamodel_configuring_searchengine); - SearchEngine engine = new SearchEngine(); - IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); - try { - engine.searchAllTypeNames( - null, - SearchPattern.R_EXACT_MATCH, - "!@$#!@".toCharArray(), //$NON-NLS-1$ - SearchPattern.R_PATTERN_MATCH | SearchPattern.R_CASE_SENSITIVE, - IJavaSearchConstants.CLASS, - scope, - new TypeNameRequestor() { - public void acceptType( - int modifiers, - char[] packageName, - char[] simpleTypeName, - char[][] enclosingTypeNames, - String path) { - // no type to accept - } - }, - // will not activate index query caches if indexes are not ready, since it would take to long - // to wait until indexes are fully rebuild - IJavaSearchConstants.CANCEL_IF_NOT_READY_TO_SEARCH, - mainMonitor.split(47) // 47% of the time is spent in the dummy search - ); - } catch (JavaModelException e) { - // /search failed: ignore - } catch (OperationCanceledException e) { - if (mainMonitor.isCanceled()) - throw e; - // else indexes were not ready: catch the exception so that jars are still refreshed - } + // 47% of the time is spent in the dummy search + updateLegacyIndex(mainMonitor.split(47)); // check if the build state version number has changed since last session // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=98969) @@ -4342,6 +4328,41 @@ } } + private static void updateLegacyIndex(IProgressMonitor monitor) { + SearchEngine engine = new SearchEngine(); + IJavaSearchScope scope = SearchEngine.createWorkspaceScope(); + try { + engine.searchAllTypeNames( + null, + SearchPattern.R_EXACT_MATCH, + "!@$#!@".toCharArray(), //$NON-NLS-1$ + SearchPattern.R_PATTERN_MATCH | SearchPattern.R_CASE_SENSITIVE, + IJavaSearchConstants.CLASS, + scope, + new TypeNameRequestor() { + public void acceptType( + int modifiers, + char[] packageName, + char[] simpleTypeName, + char[][] enclosingTypeNames, + String path) { + // no type to accept + } + }, + // will not activate index query caches if indexes are not ready, since it would take to long + // to wait until indexes are fully rebuild + IJavaSearchConstants.CANCEL_IF_NOT_READY_TO_SEARCH, + monitor + ); + } catch (JavaModelException e) { + // /search failed: ignore + } catch (OperationCanceledException e) { + if (monitor.isCanceled()) + throw e; + // else indexes were not ready: catch the exception so that jars are still refreshed + } + } + /** * Returns whether a given classpath variable is read-only or not. * @@ -5439,6 +5460,22 @@ JavaModelManager.getDeltaState().removePreResourceChangedListener(listener); } + /** + * Deletes and rebuilds the java index. + * + * @param monitor a progress monitor, or <code>null</code> if progress + * reporting and cancellation are not desired + * @throws CoreException + * @since 3.13 + */ + public static void rebuildIndex(IProgressMonitor monitor) throws CoreException { + SubMonitor subMonitor = SubMonitor.convert(monitor, 10); + IndexManager manager = JavaModelManager.getIndexManager(); + manager.deleteIndexFiles(subMonitor.split(1)); + manager.reset(); + Indexer.getInstance().rebuildIndex(subMonitor.split(7)); + updateLegacyIndex(subMonitor.split(2)); + } /**
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/Signature.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/Signature.java index 1846d3f..c0ec076 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/Signature.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/Signature.java
@@ -1483,7 +1483,7 @@ } return count; } catch (ArrayIndexOutOfBoundsException e) { // signature is syntactically incorrect if last character is C_ARRAY - throw new IllegalArgumentException(); + throw new IllegalArgumentException(e); } } @@ -1626,7 +1626,7 @@ count++; } } catch (ArrayIndexOutOfBoundsException e) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException(e); } } @@ -1680,7 +1680,7 @@ i = e + 1; } } catch (ArrayIndexOutOfBoundsException e) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException(e); } } /**
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaElementInfo.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaElementInfo.java index 0250d5d..4de81cb 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaElementInfo.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaElementInfo.java
@@ -28,7 +28,7 @@ return super.clone(); } catch (CloneNotSupportedException e) { - throw new Error(); + throw new Error(e); } } public IJavaElement[] getChildren() {
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 b898a64..c250a8c 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
@@ -27,6 +27,7 @@ import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.StringReader; @@ -173,7 +174,7 @@ private static final String ASSUMED_EXTERNAL_FILES_CACHE = "assumedExternalFilesCache"; //$NON-NLS-1$ public static enum ArchiveValidity { - BAD_FORMAT, UNABLE_TO_READ, VALID; + BAD_FORMAT, UNABLE_TO_READ, FILE_NOT_FOUND, VALID; public boolean isValid() { return this == VALID; @@ -2767,7 +2768,10 @@ * zip/jar, or it must be an absolute workspace relative path if * representing a zip/jar inside the workspace. * - * @exception CoreException If unable to create/open the ZipFile + * @exception CoreException If unable to create/open the ZipFile. The + * cause will be a {@link ZipException} if the file was corrupt, a + * {@link FileNotFoundException} if the file does not exist, or a + * {@link IOException} if we were unable to read the file. */ public ZipFile getZipFile(IPath path) throws CoreException { return getZipFile(path, true); @@ -2782,7 +2786,7 @@ */ public static boolean throwIoExceptionsInGetZipFile = false; - private ZipFile getZipFile(IPath path, boolean checkInvalidArchiveCache) throws CoreException { + public ZipFile getZipFile(IPath path, boolean checkInvalidArchiveCache) throws CoreException { if (checkInvalidArchiveCache) { throwExceptionIfArchiveInvalid(path); } @@ -2807,7 +2811,15 @@ } return zipFile; } catch (IOException e) { - ArchiveValidity reason = (e instanceof ZipException) ? ArchiveValidity.BAD_FORMAT : ArchiveValidity.UNABLE_TO_READ; + ArchiveValidity reason; + + if (e instanceof ZipException) { + reason = ArchiveValidity.BAD_FORMAT; + } else if (e instanceof FileNotFoundException) { + reason = ArchiveValidity.FILE_NOT_FOUND; + } else { + reason = ArchiveValidity.UNABLE_TO_READ; + } addInvalidArchive(path, reason); throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, e)); } @@ -2835,13 +2847,14 @@ 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(); - } + IOException reason; + switch (validity) { + case BAD_FORMAT: reason = new ZipException(); break; + case FILE_NOT_FOUND: reason = new FileNotFoundException(); break; + case UNABLE_TO_READ: reason = new IOException(); break; + default: reason = null; + } + if (reason != null) { throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, reason)); } }
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java index 02e6460..65dd9b5 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
@@ -913,9 +913,9 @@ DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); cpElement = parser.parse(new InputSource(reader)).getDocumentElement(); } catch (SAXException e) { - throw new IOException(Messages.file_badFormat); + throw new IOException(Messages.file_badFormat, e); } catch (ParserConfigurationException e) { - throw new IOException(Messages.file_badFormat); + throw new IOException(Messages.file_badFormat, e); } finally { reader.close(); }
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/UserLibrary.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/UserLibrary.java index 13a38c3..f205707 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/UserLibrary.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/UserLibrary.java
@@ -154,9 +154,9 @@ DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder(); cpElement = parser.parse(new InputSource(reader)).getDocumentElement(); } catch (SAXException e) { - throw new IOException(Messages.file_badFormat); + throw new IOException(Messages.file_badFormat, e); } catch (ParserConfigurationException e) { - throw new IOException(Messages.file_badFormat); + throw new IOException(Messages.file_badFormat, e); } finally { reader.close(); }
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/BatchImageBuilder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/BatchImageBuilder.java index 3097a43..26fb1c4 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/BatchImageBuilder.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/BatchImageBuilder.java
@@ -112,7 +112,11 @@ } ); } - member.delete(IResource.FORCE, null); + try { + member.delete(IResource.FORCE, null); + } catch(CoreException e) { + Util.log(e, "Error occurred while deleting: " + member.getFullPath()); //$NON-NLS-1$ + } } } this.notifier.checkCancel(); @@ -139,7 +143,11 @@ return false; if (!resource.isDerived()) resource.setDerived(true, null); - resource.delete(IResource.FORCE, null); + try { + resource.delete(IResource.FORCE, null); + } catch(CoreException e) { + Util.log(e, "Error occurred while deleting: " + resource.getFullPath()); //$NON-NLS-1$ + } } return false; }
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 index fb02f13..f38304d 100644 --- 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
@@ -169,8 +169,8 @@ 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$ + Package.logInfo("Index database uses the unsupported version " + this.db.getVersion() //$NON-NLS-1$ + + ". Deleting and recreating."); //$NON-NLS-1$ this.db.close(); this.fPath.delete(); this.db = new Database(this.fPath, cache, getDefaultVersion(), isPermanentlyReadOnly()); @@ -599,4 +599,8 @@ public NdNodeTypeRegistry<NdNode> getTypeRegistry() { return this.fNodeTypeRegistry; } + + public void clear(IProgressMonitor monitor) { + getDB().clear(getDefaultVersion()); + } }
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 index df7ca4e..2bd371c 100644 --- 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
@@ -126,7 +126,7 @@ GROWABLE_BLOCK_HEADER_BYTES = type.size(); - MAX_GROWABLE_SIZE = (Database.MAX_MALLOC_SIZE - GROWABLE_BLOCK_HEADER_BYTES) + MAX_GROWABLE_SIZE = (Database.MAX_SINGLE_BLOCK_MALLOC_SIZE - GROWABLE_BLOCK_HEADER_BYTES) / Database.PTR_SIZE; } }
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 index 02060fe..8bb0ed0 100644 --- 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
@@ -46,8 +46,9 @@ * 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 + * FREE_BLOCK_OFFSET | chunk number for the root of the large block free space trie * WRITE_NUMBER_OFFSET | long integer which is incremented on every write - * MALLOC_STATS_OFFSET | memory usage statistics + * 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 @@ -86,21 +87,26 @@ 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 + + // Must be enough multiples of BLOCK_SIZE_DELTA in order to fit the free block header + public static final int MIN_BLOCK_DELTAS = (FREE_BLOCK_HEADER_SIZE + BLOCK_SIZE_DELTA - 1) / BLOCK_SIZE_DELTA; + public static final int MAX_BLOCK_DELTAS = (CHUNK_SIZE - LargeBlock.HEADER_SIZE - LargeBlock.FOOTER_SIZE) + / BLOCK_SIZE_DELTA; + public static final int MAX_SINGLE_BLOCK_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 long MAX_MALLOC_SIZE = MAX_DB_SIZE - LargeBlock.HEADER_SIZE - LargeBlock.FOOTER_SIZE + - CHUNK_SIZE - BLOCK_HEADER_SIZE; + 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 + public static final int MALLOC_TABLE_OFFSET = VERSION_OFFSET + INT_SIZE; + public static final int FREE_BLOCK_OFFSET = MALLOC_TABLE_OFFSET + (CHUNK_SIZE / BLOCK_SIZE_DELTA - MIN_BLOCK_DELTAS + 1) * INT_SIZE; + public static final int WRITE_NUMBER_OFFSET = FREE_BLOCK_OFFSET + PTR_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; @@ -128,7 +134,6 @@ private final Chunk fHeaderChunk; private Chunk[] fChunks; private int fChunksUsed; - private int fChunksAllocated; private ChunkCache fCache; private long malloced; @@ -159,12 +164,12 @@ if (nChunksOnDisk <= 0) { this.fVersion= version; this.fChunks= new Chunk[1]; - this.fChunksUsed = this.fChunksAllocated = this.fChunks.length; + this.fChunksUsed = 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; + this.fChunksUsed = nChunksOnDisk; } } catch (IOException e) { throw new IndexException(new DBStatus(e)); @@ -172,8 +177,8 @@ this.memoryUsage = new MemoryStats(this.fHeaderChunk, MALLOC_STATS_OFFSET); } - private static int divideRoundingUp(int num, int den) { - return (num + den - 1) / den; + private static int divideRoundingUp(long num, int den) { + return (int) ((num + den - 1) / den); } private void openFile() throws FileNotFoundException { @@ -292,7 +297,7 @@ 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; + this.fChunksUsed = this.fChunks.length; try { wasCanceled = this.fHeaderChunk.flush() || wasCanceled; // Zero out header chunk. wasCanceled = performUninterruptableWrite(() -> { @@ -381,7 +386,7 @@ */ public void memcpy(long dest, long source, int numBytes) { assert numBytes >= 0; - assert numBytes <= MAX_MALLOC_SIZE; + assert numBytes <= MAX_SINGLE_BLOCK_MALLOC_SIZE; // TODO: make use of lower-level System.arrayCopy for (int count = 0; count < numBytes; count++) { putByte(dest + count, getByte(source + count)); @@ -391,78 +396,442 @@ /** * Allocate a block out of the database. */ - public long malloc(final int datasize, final short poolId) throws IndexException { + public long malloc(final long 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); + long result; + int usedSize; + if (datasize >= MAX_SINGLE_BLOCK_MALLOC_SIZE) { + int newChunkNum = createLargeBlock(datasize); + usedSize = Math.abs(getBlockHeaderForChunkNum(newChunkNum)) * CHUNK_SIZE; + result = newChunkNum * CHUNK_SIZE + LargeBlock.HEADER_SIZE; + // Note that we identify large blocks by setting their block size to 0. + clearRange(result, usedSize - LargeBlock.HEADER_SIZE - LargeBlock.FOOTER_SIZE); + result = result + BLOCK_HEADER_SIZE; } else { - chunk = getChunk(freeblock); - removeBlock(chunk, useDeltas * BLOCK_SIZE_DELTA, freeblock); + long freeBlock = 0; + int needDeltas = divideRoundingUp(datasize + BLOCK_HEADER_SIZE, BLOCK_SIZE_DELTA); + if (needDeltas < MIN_BLOCK_DELTAS) { + needDeltas = MIN_BLOCK_DELTAS; + } + + // Which block size. + 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 = (long) (createLargeBlock(datasize)) * (long) CHUNK_SIZE + LargeBlock.HEADER_SIZE; + 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. + 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); + result = freeBlock + BLOCK_HEADER_SIZE; } - 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 { + /** + * Clears all the bytes in the given range by setting them to zero. + * + * @param startAddress first address to clear + * @param bytesToClear number of addresses to clear + */ + public void clearRange(long startAddress, int bytesToClear) { + if (bytesToClear == 0) { + return; + } + long endAddress = startAddress + bytesToClear; + assert endAddress <= this.fChunksUsed * CHUNK_SIZE; + int blockNumber = (int) (startAddress / CHUNK_SIZE); + int firstBlockBytesToClear = Math.min((int) (((blockNumber + 1) * CHUNK_SIZE) - startAddress), bytesToClear); + + Chunk firstBlock = getChunk(startAddress); + firstBlock.clear(startAddress, firstBlockBytesToClear); + startAddress += firstBlockBytesToClear; + bytesToClear -= firstBlockBytesToClear; + while (bytesToClear > CHUNK_SIZE) { + Chunk nextBlock = getChunk(startAddress); + nextBlock.clear(startAddress, CHUNK_SIZE); + startAddress += CHUNK_SIZE; + bytesToClear -= CHUNK_SIZE; + } + + if (bytesToClear > 0) { + Chunk nextBlock = getChunk(startAddress); + nextBlock.clear(startAddress, bytesToClear); + } + } + + /** + * Obtains a new block that can fit the given number of bytes (at minimum). Returns the + * chunk number. + * + * @param datasize minimum number of bytes needed + * @return the chunk number + */ + private int createLargeBlock(long datasize) { + final int neededChunks = getChunksNeededForBytes(datasize); + int freeBlockChunkNum = getFreeBlockFromTrie(neededChunks); + final int numChunks; + + if (freeBlockChunkNum == 0) { + final int lastChunkNum = this.fChunksUsed; + + numChunks = neededChunks; + + // Check if the last block in the database is free. If so, unlink and expand it. + int lastBlockSize = getBlockFooterForChunkBefore(lastChunkNum); + if (lastBlockSize > 0) { + int startChunkNum = getFirstChunkOfBlockBefore(lastChunkNum); + + unlinkFreeBlock(startChunkNum); + // Allocate additional new chunks such that the new chunk is large enough to + // handle this allocation. + createNewChunks(neededChunks - lastBlockSize); + freeBlockChunkNum = startChunkNum; + } else { + freeBlockChunkNum = createNewChunks(numChunks); + } + } else { + numChunks = getBlockHeaderForChunkNum(freeBlockChunkNum); + + unlinkFreeBlock(freeBlockChunkNum); + } + + final int resultChunkNum; + if (numChunks > neededChunks) { + // If the chunk we've selected is larger than necessary, split it. We have the + // choice of using either half of the block. In the interest of leaving more + // opportunities of merging large blocks, we leave the unused half of the block + // next to the larger adjacent block. + final long nextBlockChunkNum = freeBlockChunkNum + numChunks; + + final int nextBlockSize = Math.abs(getBlockHeaderForChunkNum(nextBlockChunkNum)); + final int prevBlockSize = Math.abs(getBlockFooterForChunkBefore(freeBlockChunkNum)); + + final int unusedChunks = numChunks - neededChunks; + if (nextBlockSize >= prevBlockSize) { + // Use the start of the block + resultChunkNum = freeBlockChunkNum; + // Return the last half of the block to the free block pool + linkFreeBlockToTrie(freeBlockChunkNum + neededChunks, unusedChunks); + } else { + // Use the end of the block + resultChunkNum = freeBlockChunkNum + neededChunks; + // Return the first half of the block to the free block pool + linkFreeBlockToTrie(freeBlockChunkNum, unusedChunks); + } + } else { + resultChunkNum = freeBlockChunkNum; + } + + // Fill in the header and footer + setBlockHeader(resultChunkNum, -neededChunks); + return resultChunkNum; + } + + /** + * Unlinks a free block (which currently belongs to the free block trie) so that it may + * be reused. + * + * @param freeBlockChunkNum chunk number of the block to be unlinked + */ + private void unlinkFreeBlock(int freeBlockChunkNum) { + long freeBlockAddress = freeBlockChunkNum * CHUNK_SIZE; + int anotherBlockOfSameSize = 0; + int nextBlockChunkNum = getInt(freeBlockAddress + LargeBlock.NEXT_BLOCK_OFFSET); + int prevBlockChunkNum = getInt(freeBlockAddress + LargeBlock.PREV_BLOCK_OFFSET); + // Relink the linked list + if (nextBlockChunkNum != 0) { + anotherBlockOfSameSize = nextBlockChunkNum; + putInt(nextBlockChunkNum * CHUNK_SIZE + LargeBlock.PREV_BLOCK_OFFSET, prevBlockChunkNum); + } + if (prevBlockChunkNum != 0) { + anotherBlockOfSameSize = prevBlockChunkNum; + putInt(prevBlockChunkNum * CHUNK_SIZE + LargeBlock.NEXT_BLOCK_OFFSET, nextBlockChunkNum); + } + + long root = getInt(FREE_BLOCK_OFFSET); + if (root == freeBlockChunkNum) { + putInt(FREE_BLOCK_OFFSET, 0); + } + + int freeBlockSize = getBlockHeaderForChunkNum(freeBlockChunkNum); + int parentChunkNum = getInt(freeBlockAddress + LargeBlock.PARENT_OFFSET); + if (parentChunkNum != 0) { + int currentSize = getBlockHeaderForChunkNum(parentChunkNum); + int difference = currentSize ^ freeBlockSize; + if (difference != 0) { + int firstDifference = LargeBlock.SIZE_OF_SIZE_FIELD * 8 - Integer.numberOfLeadingZeros(difference) - 1; + long locationOfChildPointer = parentChunkNum * CHUNK_SIZE + LargeBlock.CHILD_TABLE_OFFSET + + (firstDifference * INT_SIZE); + putInt(locationOfChildPointer, 0); + } + } + + if (anotherBlockOfSameSize != 0) { + insertChild(parentChunkNum, anotherBlockOfSameSize); + } + + int currentParent = parentChunkNum; + for (int childIdx = 0; childIdx < LargeBlock.ENTRIES_IN_CHILD_TABLE; childIdx++) { + int nextChildChunkNum = getInt(freeBlockAddress + LargeBlock.CHILD_TABLE_OFFSET + (childIdx * INT_SIZE)); + if (nextChildChunkNum != 0) { + insertChild(currentParent, nextChildChunkNum); + // Parent all subsequent children under the child that was most similar to the old parent + if (currentParent == parentChunkNum) { + currentParent = nextChildChunkNum; + } + } + } + + } + + /** + * Returns the chunk number of a free block that contains at least the given number of chunks, or + * 0 if there is no existing contiguous free block containing at least the given number of chunks. + * + * @param numChunks minumum number of chunks desired + * @return the chunk number of a free block containing at least the given number of chunks or 0 + * if there is no existing free block containing that many chunks. + */ + private int getFreeBlockFromTrie(int numChunks) { + int currentChunkNum = getInt(FREE_BLOCK_OFFSET); + + int resultChunkNum = getSmallestChildNoSmallerThan(currentChunkNum, numChunks); + if (resultChunkNum == 0) { + return 0; + } + + // Try not to return the trie node itself if there is a linked list entry available, since unlinking + // something from the linked list is faster than unlinking a trie node. + int nextResultChunkNum = getInt(resultChunkNum * CHUNK_SIZE + LargeBlock.NEXT_BLOCK_OFFSET); + if (nextResultChunkNum != 0) { + return nextResultChunkNum; + } + return resultChunkNum; + } + + /** + * Given the chunk number of a block somewhere in the free space trie, this returns the smallest + * child in the subtree that is no smaller than the given number of chunks. + * + * @param trieNodeChunkNum chunk number of a block in the free space trie + * @param numChunks desired number of chunks + * @return the chunk number of the first chunk in a contiguous free block containing at least the + * given number of chunks + */ + private int getSmallestChildNoSmallerThan(int trieNodeChunkNum, int numChunks) { + if (trieNodeChunkNum == 0) { + return 0; + } + int currentSize = getBlockHeaderForChunkNum(trieNodeChunkNum); + assert (currentSize >= 0); + int difference = currentSize ^ numChunks; + if (difference == 0) { + return trieNodeChunkNum; + } + + int bitMask = Integer.highestOneBit(difference); + int firstDifference = LargeBlock.SIZE_OF_SIZE_FIELD * 8 - Integer.numberOfLeadingZeros(bitMask) - 1; + boolean lookingForSmallerChild = (currentSize > numChunks); + for (int testPosition = firstDifference; testPosition < LargeBlock.ENTRIES_IN_CHILD_TABLE; testPosition++) { + if (((currentSize & bitMask) != 0) == lookingForSmallerChild) { + int nextChildChunkNum = getInt( + trieNodeChunkNum * CHUNK_SIZE + LargeBlock.CHILD_TABLE_OFFSET + (testPosition * PTR_SIZE)); + int childResultChunkNum = getSmallestChildNoSmallerThan(nextChildChunkNum, numChunks); + if (childResultChunkNum != 0) { + return childResultChunkNum; + } + } + bitMask <<= 1; + } + + if (lookingForSmallerChild) { + return trieNodeChunkNum; + } else { + return 0; + } + } + + /** + * Link the given unused block into the free block tries. The block does not need to have + * its header filled in already. + * + * @param freeBlockChunkNum chunk number of the start of the block + * @param numChunks number of chunks in the block + */ + private void linkFreeBlockToTrie(int freeBlockChunkNum, int numChunks) { + setBlockHeader(freeBlockChunkNum, numChunks); + long freeBlockAddress = freeBlockChunkNum * CHUNK_SIZE; + Chunk chunk = getChunk(freeBlockAddress); + chunk.clear(freeBlockAddress + LargeBlock.HEADER_SIZE, + LargeBlock.UNALLOCATED_HEADER_SIZE - LargeBlock.HEADER_SIZE); + + insertChild(getInt(FREE_BLOCK_OFFSET), freeBlockChunkNum); + } + + /** + * Adds the given child block to the given parent subtree of the free space trie. Any existing + * subtree under the given child block will be retained. + * + * @param parentChunkNum root of the existing tree, or 0 if the child is going to be the new root + * @param newChildChunkNum the new child to insert + */ + private void insertChild(int parentChunkNum, int newChildChunkNum) { + if (parentChunkNum == 0) { + putInt(newChildChunkNum * CHUNK_SIZE + LargeBlock.PARENT_OFFSET, parentChunkNum); + putInt(FREE_BLOCK_OFFSET, newChildChunkNum); + return; + } + int numChunks = getBlockHeaderForChunkNum(newChildChunkNum); + for (;;) { + int currentSize = getBlockHeaderForChunkNum(parentChunkNum); + int difference = currentSize ^ numChunks; + if (difference == 0) { + // The newly added item is exactly the same size as this trie node + insertFreeBlockAfter(parentChunkNum, newChildChunkNum); + return; + } + + int firstDifference = LargeBlock.SIZE_OF_SIZE_FIELD * 8 - Integer.numberOfLeadingZeros(difference) - 1; + long locationOfChildPointer = parentChunkNum * CHUNK_SIZE + LargeBlock.CHILD_TABLE_OFFSET + + (firstDifference * INT_SIZE); + int childChunkNum = getInt(locationOfChildPointer); + if (childChunkNum == 0) { + putInt(locationOfChildPointer, newChildChunkNum); + putInt(newChildChunkNum * CHUNK_SIZE + LargeBlock.PARENT_OFFSET, parentChunkNum); + return; + } + parentChunkNum = childChunkNum; + } + } + + /** + * Adds the given block to the linked list of equally-sized free chunks in the free space trie. + * Both chunks must be unused, must be the same size, and the previous chunk must already + * be linked into the free space trie. The newly-added chunk must not have any children. + * + * @param prevChunkNum chunk number of previous block in the existing list + * @param newChunkNum new chunk to be added to the list + */ + private void insertFreeBlockAfter(int prevChunkNum, int newChunkNum) { + long prevChunkAddress = (long) prevChunkNum * CHUNK_SIZE; + int nextChunkNum = getInt(prevChunkAddress + LargeBlock.NEXT_BLOCK_OFFSET); + long nextChunkAddress = (long) nextChunkNum * CHUNK_SIZE; + long newLockAddress = (long) newChunkNum * CHUNK_SIZE; + + putInt(prevChunkAddress + LargeBlock.NEXT_BLOCK_OFFSET, newChunkNum); + if (nextChunkNum != 0) { + putInt(nextChunkAddress + LargeBlock.PREV_BLOCK_OFFSET, newChunkNum); + } + putInt(newLockAddress + LargeBlock.PREV_BLOCK_OFFSET, prevChunkNum); + putInt(newLockAddress + LargeBlock.NEXT_BLOCK_OFFSET, nextChunkNum); + } + + /** + * Returns the chunk number of the chunk at the start of a block, given the + * chunk number of the chunk at the start of the following block. + * + * @param chunkNum the chunk number of the chunk immediately following the + * chunk being queried + * @return the chunk number of the chunk at the start of the previous block + */ + private int getFirstChunkOfBlockBefore(int chunkNum) { + int blockChunks = Math.abs(getBlockFooterForChunkBefore(chunkNum)); + return chunkNum - blockChunks; + } + + /** + * Sets the block header and footer for the given range of chunks which make + * up a contiguous block. + * + * @param firstChunkNum chunk number of the first chunk in the block + * @param headerContent the content of the header. Its magnitude is the number of + * chunks in the block. It is positive if the chunk is free and negative if + * the chunk is in use. + */ + private void setBlockHeader(int firstChunkNum, int headerContent) { + assert headerContent != 0; + assert firstChunkNum < this.fChunksUsed; + int numBlocks = Math.abs(headerContent); + long firstChunkAddress = firstChunkNum * CHUNK_SIZE; + putInt(firstChunkAddress, headerContent); + putInt(firstChunkAddress + (numBlocks * CHUNK_SIZE) - LargeBlock.FOOTER_SIZE, headerContent); + } + + /** + * Returns the size of the block (in number of chunks) starting at the given address. The return value is positive + * if the block is free and negative if the block is allocated. + */ + private int getBlockHeaderForChunkNum(long firstChunkNum) { + if (firstChunkNum >= this.fChunksUsed) { + return 0; + } + return getInt(firstChunkNum * CHUNK_SIZE); + } + + /** + * Returns the size of the block (in number of chunks), given the (non-inclusive) address that the block ends at. + * The return value is positive if the block is free and negative if the block is allocated. + */ + private int getBlockFooterForChunkBefore(int chunkNum) { + if (chunkNum < 2) { + // Don't report the database header as a normal chunk. + return 0; + } + return getInt(chunkNum * CHUNK_SIZE - LargeBlock.FOOTER_SIZE); + } + + private int createNewChunks(int numChunks) throws IndexException { assert this.fExclusiveLock; synchronized (this.fCache) { - final int newChunkIndex = this.fChunksUsed; // fChunks.length; + final int firstChunkIndex = this.fChunksUsed; + final int lastChunkIndex = firstChunkIndex + numChunks - 1; - final Chunk chunk = new Chunk(this, newChunkIndex); - chunk.fDirty = true; + final Chunk lastChunk = new Chunk(this, lastChunkIndex); + lastChunk.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; + if (lastChunkIndex >= this.fChunks.length) { + int increment = Math.max(1024, this.fChunks.length / 20); + int newNumChunks = Math.max(lastChunkIndex + 1, this.fChunks.length + increment); + Chunk[] newChunks = new Chunk[newNumChunks]; + System.arraycopy(this.fChunks, 0, newChunks, 0, this.fChunks.length); + this.fChunks = newChunks; } - this.fChunksUsed += 1; - this.fChunks[newChunkIndex] = chunk; - this.fCache.add(chunk, true); - long address = (long) newChunkIndex * CHUNK_SIZE; + this.fChunksUsed = lastChunkIndex + 1; + this.fChunks[lastChunkIndex] = lastChunk; + this.fCache.add(lastChunk, true); + long result = (long) firstChunkIndex * CHUNK_SIZE; /* * Non-dense pointers are at most 31 bits dense pointers are at most 35 bits Check the sizes here and throw @@ -470,33 +839,15 @@ * 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) { + long endAddress = result + (numChunks * CHUNK_SIZE); + if (endAddress > 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)); + NLS.bind("Database too large! Address = " + endAddress + ", max size = " + MAX_DB_SIZE, //$NON-NLS-1$ //$NON-NLS-2$ + bindings), 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; + return firstChunkIndex; } } @@ -544,26 +895,74 @@ /** * 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 + * @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 blockSize; 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$ + blockSize = -chunk.getShort(block); + // We use a block size of 0 to indicate a large block that fills a range of chunks + if (blockSize == 0) { + int offsetIntoChunk = (int) (address % CHUNK_SIZE); + assert offsetIntoChunk == LargeBlock.HEADER_SIZE + BLOCK_HEADER_SIZE; + // Deallocating a large block + // This was a large block. It uses a sequence of full chunks. + int chunkNum = (int) (address / CHUNK_SIZE); + int numChunks = -getBlockHeaderForChunkNum(chunkNum); + if (numChunks < 0) { + // Already freed. + throw new IndexException(new Status(IStatus.ERROR, Package.PLUGIN_ID, 0, + "Already freed large block " + address, new Exception())); //$NON-NLS-1$ + } + blockSize = numChunks * CHUNK_SIZE; + freeLargeChunk(chunkNum, numChunks); + } else { + // Deallocating a normal block + // TODO Look for opportunities to merge small blocks + 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, (int) blockSize, block); } - addBlock(chunk, blocksize, block); - this.freed += blocksize; - this.memoryUsage.recordFree(poolId, blocksize); + + this.freed += blockSize; + this.memoryUsage.recordFree(poolId, blockSize); + } + + private void freeLargeChunk(int chunkNum, int numChunks) { + assert chunkNum > 0; + assert numChunks > 0; + int prevBlockHeader = getBlockFooterForChunkBefore(chunkNum); + int nextBlockChunkNum = chunkNum + numChunks; + int nextBlockHeader = getBlockHeaderForChunkNum(nextBlockChunkNum); + + // If the previous block is unused, merge with it + if (prevBlockHeader > 0) { + int prevBlockChunkNum = getFirstChunkOfBlockBefore(chunkNum); + + unlinkFreeBlock(prevBlockChunkNum); + chunkNum = prevBlockChunkNum; + numChunks += prevBlockHeader; + } + + // If the next block is unused, merge with it + if (nextBlockHeader > 0) { + unlinkFreeBlock(nextBlockChunkNum); + numChunks += nextBlockHeader; + } + + // Block merging is done. Now reinsert the merged block into the free space trie + linkFreeBlockToTrie(chunkNum, numChunks); } public void putByte(long offset, byte value) throws IndexException { @@ -745,7 +1144,7 @@ this.memoryUsage.refresh(); this.fHeaderChunk.fDirty= false; this.fChunks= new Chunk[] { null }; - this.fChunksUsed = this.fChunksAllocated = this.fChunks.length; + this.fChunksUsed = this.fChunks.length; try { this.fFile.close(); } catch (IOException e) { @@ -931,6 +1330,10 @@ return this.fFile.length(); } + public int getChunkCount() { + return this.fChunksUsed; + } + /** * A Record Pointer is a pointer as returned by Database.malloc(). * This is a pointer to a block + BLOCK_HEADER_SIZE. @@ -953,4 +1356,19 @@ public MemoryStats getMemoryStats() { return this.memoryUsage; } + + /** + * Returns the number of bytes that can fit in the payload of the given number of chunks. + */ + public static long getBytesThatFitInChunks(int numChunks) { + return CHUNK_SIZE * numChunks - LargeBlock.HEADER_SIZE - LargeBlock.FOOTER_SIZE - BLOCK_HEADER_SIZE; + } + + /** + * Returns the number of chunks needed to fit the given number of bytes of payload. + */ + public static int getChunksNeededForBytes(long datasize) { + return divideRoundingUp(datasize + BLOCK_HEADER_SIZE + LargeBlock.HEADER_SIZE + LargeBlock.FOOTER_SIZE, + CHUNK_SIZE); + } }
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/LargeBlock.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/LargeBlock.java new file mode 100644 index 0000000..26af53c --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/db/LargeBlock.java
@@ -0,0 +1,26 @@ +package org.eclipse.jdt.internal.core.nd.db; + +public class LargeBlock { + public static final int SIZE_OFFSET = 0; + public static final int SIZE_OF_SIZE_FIELD = Database.INT_SIZE; + /** + * Size of the header for a large block. The header consists of a int which holds the number of chunks in the block. + * It is negative for an allocated block and positive for an unallocated block. The header is located at the start + * of the large block. + */ + public static final int HEADER_SIZE = Math.max(Database.INT_SIZE, Database.BLOCK_SIZE_DELTA); + + public static final int ENTRIES_IN_CHILD_TABLE = SIZE_OF_SIZE_FIELD * 8; + public static final int CHILD_TABLE_OFFSET = HEADER_SIZE; + public static final int PARENT_OFFSET = CHILD_TABLE_OFFSET + (Database.INT_SIZE * ENTRIES_IN_CHILD_TABLE); + public static final int PREV_BLOCK_OFFSET = PARENT_OFFSET + Database.INT_SIZE; + public static final int NEXT_BLOCK_OFFSET = PREV_BLOCK_OFFSET + Database.INT_SIZE; + + public static final int UNALLOCATED_HEADER_SIZE = NEXT_BLOCK_OFFSET + Database.INT_SIZE; + + /** + * The large block footer is located at the end of the last chunk in the large block. It is an exact copy of the + * header. + */ + public static final int FOOTER_SIZE = HEADER_SIZE; +}
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 index c78b7f9..eb0e48a 100644 --- 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
@@ -29,13 +29,13 @@ private static final int NEXT1 = 4; private static final int CHARS1 = 8; - private static final int NUM_CHARS1 = (Database.MAX_MALLOC_SIZE - CHARS1) / 2; + private static final int NUM_CHARS1 = (Database.MAX_SINGLE_BLOCK_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; + private static final int NUM_CHARSN = (Database.MAX_SINGLE_BLOCK_MALLOC_SIZE - CHARSN) / 2; public LongString(Database db, long record) { this.db = db; @@ -47,7 +47,7 @@ final int numCharsn = useBytes ? NUM_CHARSN * 2 : NUM_CHARSN; this.db = db; - this.record = db.malloc(Database.MAX_MALLOC_SIZE, Database.POOL_STRING_LONG); + this.record = db.malloc(Database.MAX_SINGLE_BLOCK_MALLOC_SIZE, Database.POOL_STRING_LONG); // Write the first record. final int length = chars.length; @@ -64,7 +64,7 @@ long lastNext = this.record + NEXT1; int start = numChars1; while (length - start > numCharsn) { - long nextRecord = db.malloc(Database.MAX_MALLOC_SIZE, Database.POOL_STRING_LONG); + long nextRecord = db.malloc(Database.MAX_SINGLE_BLOCK_MALLOC_SIZE, Database.POOL_STRING_LONG); db.putRecPtr(lastNext, nextRecord); chunk= db.getChunk(nextRecord); if (useBytes) {
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 index 09992a5..eb56c6c 100644 --- 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
@@ -25,7 +25,7 @@ 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 static final int MAX_BYTE_LENGTH = Database.MAX_SINGLE_BLOCK_MALLOC_SIZE - CHARS; public ShortString(Database db, long offset) { this.db = db;
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 index afd740e..d52ce33 100644 --- 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
@@ -16,9 +16,7 @@ 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; @@ -69,8 +67,6 @@ 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; @@ -97,12 +93,6 @@ 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. *
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 index 318a736..2380780 100644 --- 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
@@ -10,7 +10,7 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core.nd.indexer; -import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayDeque; import java.util.ArrayList; @@ -43,6 +43,7 @@ import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.jobs.JobGroup; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaElement; @@ -115,10 +116,16 @@ */ private Set<Listener> listeners = Collections.newSetFromMap(new WeakHashMap<Listener, Boolean>()); + private JobGroup group = new JobGroup(Messages.Indexer_updating_index_job_name, 1, 1); + private Job rescanJob = Job.create(Messages.Indexer_updating_index_job_name, monitor -> { rescan(monitor); }); + private Job rebuildIndexJob = Job.create(Messages.Indexer_updating_index_job_name, monitor -> { + rebuildIndex(monitor); + }); + public static interface Listener { void consume(IndexerEvent event); } @@ -560,14 +567,6 @@ 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)); @@ -589,9 +588,11 @@ if (DEBUG) { Package.logInfo("rescanning " + thePath.toString() + ", " + fingerprint); //$NON-NLS-1$ //$NON-NLS-2$ } - int result; + int result = 0; try { - result = addElement(resourceFile, element, subMonitor.split(50)); + if (fingerprint.fileExists()) { + 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$ @@ -611,6 +612,12 @@ Package.log("A RuntimeException occurred while indexing " + pathString, e); //$NON-NLS-1$ } throw e; + } catch (FileNotFoundException e) { + fingerprint = FileFingerprint.getEmpty(); + } + + if (DEBUG && !fingerprint.fileExists()) { + Package.log("the file " + pathString + " was not indexed because it does not exist", null); //$NON-NLS-1$ //$NON-NLS-2$ } List<NdResourceFile> allResourcesWithThisPath = Collections.emptyList(); @@ -648,9 +655,10 @@ /** * Adds an archive to the index, under the given NdResourceFile. + * @throws FileNotFoundException if the file does not exist */ private int addElement(NdResourceFile resourceFile, IJavaElement element, IProgressMonitor monitor) - throws JavaModelException { + throws JavaModelException, FileNotFoundException { SubMonitor subMonitor = SubMonitor.convert(monitor); if (element instanceof JarPackageFragmentRoot) { @@ -704,6 +712,8 @@ } 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 (FileNotFoundException e) { + throw e; } catch (IOException ioException) { throw new JavaModelException(ioException, IJavaModelStatusConstants.IO_EXCEPTION); } catch (CoreException coreException) { @@ -722,7 +732,7 @@ boolean indexed = false; try { - ClassFileReader classFileReader = BinaryTypeFactory.rawReadType(descriptor, true); + ClassFileReader classFileReader = BinaryTypeFactory.rawReadTypeTestForExists(descriptor, true, false); if (classFileReader != null) { indexed = addClassToIndex(resourceFile, descriptor.fieldDescriptor, descriptor.indexPath, classFileReader, iterationMonitor); @@ -983,6 +993,10 @@ public Indexer(Nd toPopulate, IWorkspaceRoot workspaceRoot) { this.nd = toPopulate; this.root = workspaceRoot; + this.rescanJob.setSystem(true); + this.rescanJob.setJobGroup(this.group); + this.rebuildIndexJob.setSystem(true); + this.rebuildIndexJob.setJobGroup(this.group); } public void rescanAll() { @@ -1070,4 +1084,20 @@ } } } + + public void rebuildIndex(IProgressMonitor monitor) throws CoreException { + SubMonitor subMonitor = SubMonitor.convert(monitor, 100); + + this.nd.acquireWriteLock(subMonitor.split(1)); + try { + this.nd.clear(subMonitor.split(2)); + } finally { + this.nd.releaseWriteLock(); + } + rescan(subMonitor.split(98)); + } + + public void requestRebuildIndex() { + this.rebuildIndexJob.schedule(); + } }
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 index f1b0262..b5d6062 100644 --- 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
@@ -139,6 +139,15 @@ } /** + * Returns true iff the file existed at the time the fingerprint was computed. + * + * @return true iff the file existed at the time the fingerprint was computed. + */ + public boolean fileExists() { + return !equals(EMPTY); + } + + /** * Compares the given File with the receiver. If the fingerprint matches (ie: the file */ public FingerprintTestResult test(IPath path, IProgressMonitor monitor) throws CoreException {
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 index 006aeff..778d5da 100644 --- 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
@@ -34,9 +34,9 @@ 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); + static final int CURRENT_VERSION = Nd.version(1, 38); + static final int MAX_SUPPORTED_VERSION = Nd.version(1, 38); + static final int MIN_SUPPORTED_VERSION = Nd.version(1, 38); // Fields for the search header public static final FieldSearchIndex<NdResourceFile> FILES; @@ -293,8 +293,4 @@ 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/model/BinaryTypeFactory.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/BinaryTypeFactory.java index d6d3981..631c384 100644 --- 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
@@ -10,15 +10,19 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core.nd.java.model; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResourceStatus; 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.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IJavaElement; @@ -37,6 +41,7 @@ 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.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.NdResourceFile; @@ -111,9 +116,8 @@ * no such type exists. * @throws ClassFormatException */ - public static IBinaryType readType(BinaryTypeDescriptor descriptor, - IProgressMonitor monitor) throws JavaModelException, ClassFormatException { - + public static IBinaryType readType(BinaryTypeDescriptor descriptor, IProgressMonitor monitor) throws JavaModelException, ClassFormatException { + if (JavaIndex.isEnabled()) { try { return readFromIndex(JavaIndex.getIndex(), descriptor, monitor); @@ -125,6 +129,14 @@ return rawReadType(descriptor, true); } + public static ClassFileReader rawReadType(BinaryTypeDescriptor descriptor, boolean fullyInitialize) throws JavaModelException, ClassFormatException { + try { + return rawReadTypeTestForExists(descriptor, fullyInitialize, true); + } catch (FileNotFoundException e) { + throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION); + } + } + /** * 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). @@ -132,15 +144,18 @@ * @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 + * @throws FileNotFoundException if the file does not exist */ - public static ClassFileReader rawReadType(BinaryTypeDescriptor descriptor, boolean fullyInitialize) throws JavaModelException, ClassFormatException { + public static ClassFileReader rawReadTypeTestForExists(BinaryTypeDescriptor descriptor, boolean fullyInitialize, + boolean useInvalidArchiveCache) throws JavaModelException, ClassFormatException, FileNotFoundException { if (descriptor == null) { return null; } if (descriptor.isInJarFile()) { ZipFile zip = null; try { - zip = JavaModelManager.getJavaModelManager().getZipFile(new Path(new String(descriptor.workspacePath))); + zip = JavaModelManager.getJavaModelManager().getZipFile(new Path(new String(descriptor.workspacePath)), + useInvalidArchiveCache); char[] entryNameCharArray = CharArrayUtils.concat( JavaNames.fieldDescriptorToBinaryName(descriptor.fieldDescriptor), SuffixConstants.SUFFIX_class); String entryName = new String(entryNameCharArray); @@ -161,7 +176,18 @@ } } else { IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(new String(descriptor.workspacePath))); - byte[] contents = Util.getResourceContentsAsByteArray(file); + byte[] contents; + try (InputStream stream = file.getContents(true)) { + contents = org.eclipse.jdt.internal.compiler.util.Util.getInputStreamAsByteArray(stream, -1); + } catch (CoreException e) { + IStatus status = e.getStatus(); + if (status.getCode() == IResourceStatus.RESOURCE_NOT_FOUND) { + throw new FileNotFoundException(); + } + throw new JavaModelException(e); + } catch (IOException e) { + throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION); + } return new ClassFileReader(contents, file.getFullPath().toString().toCharArray(), fullyInitialize); } return null; @@ -217,8 +243,8 @@ throw new JavaModelException(e); } } catch (IndexException e) { - // Index corrupted. Rebuild it. - index.rebuildIndex(); + Package.log("Index corruption detected. Rebuilding index.", e); //$NON-NLS-1$ + Indexer.getInstance().requestRebuildIndex(); } } }
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/Package.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/Package.java new file mode 100644 index 0000000..1aad354 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/java/model/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.java.model; + +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/search/indexing/IndexManager.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/IndexManager.java index 7ce53fa..dc72697 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
@@ -28,6 +28,7 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.SubMonitor; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; @@ -164,7 +165,7 @@ if (count > 0) removeIndexesState(locations); } - deleteIndexFiles(knownPaths); + deleteIndexFiles(knownPaths, null); } /** * Compute the pre-built index location for a specified URL @@ -212,17 +213,25 @@ } return indexLocation; } -public void deleteIndexFiles() { +/** + * Use {@link #deleteIndexFiles(IProgressMonitor)} + */ +public final void deleteIndexFiles() { + deleteIndexFiles(null); +} +public void deleteIndexFiles(IProgressMonitor monitor) { if (DEBUG) Util.verbose("Deleting index files"); //$NON-NLS-1$ this.savedIndexNamesFile.delete(); // forget saved indexes & delete each index file - deleteIndexFiles(null); + deleteIndexFiles(null, monitor); } -private void deleteIndexFiles(SimpleSet pathsToKeep) { +private void deleteIndexFiles(SimpleSet pathsToKeep, IProgressMonitor monitor) { File[] indexesFiles = getSavedIndexesDirectory().listFiles(); if (indexesFiles == null) return; + SubMonitor subMonitor = SubMonitor.convert(monitor, indexesFiles.length); for (int i = 0, l = indexesFiles.length; i < l; i++) { + subMonitor.split(1); String fileName = indexesFiles[i].getAbsolutePath(); if (pathsToKeep != null && pathsToKeep.includes(new FileIndexLocation(indexesFiles[i]))) continue; String suffix = ".index"; //$NON-NLS-1$ @@ -865,14 +874,16 @@ /** * Flush current state */ -public synchronized void reset() { +public void reset() { super.reset(); - if (this.indexes != null) { - this.indexes = new SimpleLookupTable(); - this.indexStates = null; + synchronized (this) { + if (this.indexes != null) { + this.indexes = new SimpleLookupTable(); + this.indexStates = null; + } + this.indexLocations = new SimpleLookupTable(); + this.javaPluginLocation = null; } - this.indexLocations = new SimpleLookupTable(); - this.javaPluginLocation = null; } /** * Resets the index for a given path.
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 71bc061..7c07d0f 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
@@ -297,22 +297,29 @@ /** * Flush current state */ - public synchronized void reset() { + public void reset() { if (VERBOSE) Util.verbose("Reset"); //$NON-NLS-1$ - if (this.processingThread != null) { + Thread thread; + synchronized (this) { + thread = this.processingThread; + } + + if (thread != null) { discardJobs(null); // discard all jobs } else { - /* initiate background processing */ - this.processingThread = new Thread(this, processName()); - this.processingThread.setDaemon(true); - // less prioritary by default, priority is raised if clients are actively waiting on it - this.processingThread.setPriority(Thread.NORM_PRIORITY-1); - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=296343 - // set the context loader to avoid leaking the current context loader - this.processingThread.setContextClassLoader(this.getClass().getClassLoader()); - this.processingThread.start(); + synchronized (this) { + /* initiate background processing */ + this.processingThread = new Thread(this, processName()); + this.processingThread.setDaemon(true); + // less prioritary by default, priority is raised if clients are actively waiting on it + this.processingThread.setPriority(Thread.NORM_PRIORITY-1); + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=296343 + // set the context loader to avoid leaking the current context loader + this.processingThread.setContextClassLoader(this.getClass().getClassLoader()); + this.processingThread.start(); + } } } /**