1 /******************************************************************************* 2 * Copyright (c) 2008, 2015 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.pde.api.tools.internal.builder; 15 16 import java.text.MessageFormat; 17 import java.util.LinkedList; 18 import java.util.List; 19 20 import org.eclipse.core.runtime.CoreException; 21 import org.eclipse.core.runtime.IProgressMonitor; 22 import org.eclipse.core.runtime.MultiStatus; 23 import org.eclipse.core.runtime.OperationCanceledException; 24 import org.eclipse.core.runtime.SubMonitor; 25 import org.eclipse.pde.api.tools.internal.provisional.ApiDescriptionVisitor; 26 import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; 27 import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations; 28 import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers; 29 import org.eclipse.pde.api.tools.internal.provisional.builder.IApiProblemDetector; 30 import org.eclipse.pde.api.tools.internal.provisional.builder.IReference; 31 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor; 32 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IPackageDescriptor; 33 import org.eclipse.pde.api.tools.internal.provisional.model.ApiTypeContainerVisitor; 34 import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; 35 import org.eclipse.pde.api.tools.internal.provisional.model.IApiType; 36 import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeContainer; 37 import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeRoot; 38 import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem; 39 40 /** 41 * The reference analyzer 42 * 43 * @since 1.1 44 */ 45 public class ReferenceAnalyzer { 46 47 /** 48 * Natural log of 2. 49 */ 50 private static final double LOG2 = Math.log(2); 51 52 /** 53 * Empty result collection. 54 */ 55 private static final IApiProblem[] EMPTY_RESULT = new IApiProblem[0]; 56 /** 57 * No problem detector to use 58 */ 59 private static final IApiProblemDetector[] NO_PROBLEM_DETECTORS = new IApiProblemDetector[0]; 60 61 /** 62 * Visits each class file, extracting references. 63 */ 64 class Visitor extends ApiTypeContainerVisitor { 65 66 private IProgressMonitor fMonitor = null; 67 Visitor(IProgressMonitor monitor)68 public Visitor(IProgressMonitor monitor) { 69 fMonitor = monitor; 70 } 71 72 @Override visitPackage(String packageName)73 public boolean visitPackage(String packageName) { 74 fMonitor.subTask(MessageFormat.format(BuilderMessages.ReferenceAnalyzer_checking_api_used_by, packageName)); 75 return true; 76 } 77 78 @Override endVisitPackage(String packageName)79 public void endVisitPackage(String packageName) { 80 fMonitor.worked(1); 81 } 82 83 @Override visit(String packageName, IApiTypeRoot classFile)84 public void visit(String packageName, IApiTypeRoot classFile) { 85 if (!fMonitor.isCanceled()) { 86 try { 87 IApiType type = classFile.getStructure(); 88 if (type == null) { 89 // do nothing for bad class files 90 return; 91 } 92 // don't process inner/anonymous/local types, this is done 93 // in the extractor 94 if (type.isMemberType() || type.isLocal() || type.isAnonymous()) { 95 return; 96 } 97 List<IReference> references = type.extractReferences(fAllReferenceKinds, null); 98 // keep potential matches 99 for (IReference ref : references) { 100 // compute index of interested problem detectors 101 int index = getLog2(ref.getReferenceKind()); 102 IApiProblemDetector[] detectors = fIndexedDetectors[index]; 103 boolean added = false; 104 if (detectors != null) { 105 for (IApiProblemDetector detector : detectors) { 106 if (detector.considerReference(ref)) { 107 if (!added) { 108 fReferences.add(ref); 109 added = true; 110 } 111 } 112 } 113 } 114 } 115 } catch (CoreException e) { 116 fStatus.add(e.getStatus()); 117 } 118 } 119 } 120 } 121 122 /** 123 * Scan status 124 */ 125 MultiStatus fStatus; 126 127 /** 128 * Bit mask of reference kinds that problem detectors care about. 129 */ 130 int fAllReferenceKinds = 0; 131 132 /** 133 * List of references to consider/resolve. 134 */ 135 List<IReference> fReferences = new LinkedList<>(); 136 137 /** 138 * Problem detectors indexed by the log base 2 of each reference kind they 139 * are interested in. Provides a fast way to hand references off to 140 * interested problem detectors. 141 */ 142 IApiProblemDetector[][] fIndexedDetectors; 143 144 /** 145 * Indexes the problem detectors by the reference kinds they are interested 146 * in. For example, a detector interested in a 147 * {@link org.eclipse.pde.api.tools.internal.provisional.search.ReferenceModifiers#REF_INSTANTIATE} 148 * will be in the 26th index (0x1 << 27, which is 2 ^ 26). Also initializes 149 * the bit mask of all interesting reference kinds. 150 * 151 * @param detectors problem detectors 152 */ indexProblemDetectors(IApiProblemDetector[] detectors)153 void indexProblemDetectors(IApiProblemDetector[] detectors) { 154 fIndexedDetectors = new IApiProblemDetector[32][]; 155 for (IApiProblemDetector detector : detectors) { 156 int kinds = detector.getReferenceKinds(); 157 fAllReferenceKinds |= kinds; 158 int mask = 0x1; 159 for (int bit = 0; bit < 32; bit++) { 160 if ((mask & kinds) > 0) { 161 IApiProblemDetector[] indexed = fIndexedDetectors[bit]; 162 if (indexed == null) { 163 fIndexedDetectors[bit] = new IApiProblemDetector[] { detector }; 164 } else { 165 IApiProblemDetector[] next = new IApiProblemDetector[indexed.length + 1]; 166 System.arraycopy(indexed, 0, next, 0, indexed.length); 167 next[indexed.length] = detector; 168 fIndexedDetectors[bit] = next; 169 } 170 } 171 mask = mask << 1; 172 } 173 } 174 } 175 176 /** 177 * log 2 (x) = ln(x) / ln(2) 178 * 179 * @param bitConstant a single bit constant (0x1 << n) 180 * @return log base 2 of the constant (the power of 2 the constant is equal 181 * to) 182 */ getLog2(int bitConstant)183 int getLog2(int bitConstant) { 184 double logX = Math.log(bitConstant); 185 double pow = logX / LOG2; 186 return (int) Math.round(pow); 187 } 188 189 /** 190 * Scans the given scope extracting all reference information. 191 * 192 * @param scope scope to scan 193 * @param monitor progress monitor 194 * @exception CoreException if the scan fails 195 */ extractReferences(IApiTypeContainer scope, IProgressMonitor monitor)196 void extractReferences(IApiTypeContainer scope, IProgressMonitor monitor) throws CoreException { 197 fStatus = new MultiStatus(ApiPlugin.PLUGIN_ID, 0, BuilderMessages.ReferenceAnalyzer_api_analysis_error, null); 198 String[] packageNames = scope.getPackageNames(); 199 SubMonitor localMonitor = SubMonitor.convert(monitor, packageNames.length); 200 ApiTypeContainerVisitor visitor = new Visitor(localMonitor); 201 long start = System.currentTimeMillis(); 202 try { 203 scope.accept(visitor); 204 } catch (CoreException e) { 205 fStatus.add(e.getStatus()); 206 } 207 long end = System.currentTimeMillis(); 208 if (!fStatus.isOK()) { 209 throw new CoreException(fStatus); 210 } 211 localMonitor.done(); 212 if (ApiPlugin.DEBUG_REFERENCE_ANALYZER) { 213 System.out.println("Reference Analyzer: extracted " + fReferences.size() + " references in " + (end - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 214 } 215 } 216 217 /** 218 * Analyzes the given {@link IApiComponent} within the given 219 * {@link IApiTypeContainer} (scope) and returns a collection of detected 220 * {@link IApiProblem}s or an empty collection, never <code>null</code> 221 * 222 * @param component 223 * @param scope 224 * @param monitor 225 * @return the collection of detected {@link IApiProblem}s or an empty 226 * collection, never <code>null</code> 227 * @throws CoreException 228 */ analyze(IApiComponent component, IApiTypeContainer scope, IProgressMonitor monitor)229 public IApiProblem[] analyze(IApiComponent component, IApiTypeContainer scope, IProgressMonitor monitor) throws CoreException { 230 SubMonitor localMonitor = SubMonitor.convert(monitor, 4); 231 // build problem detectors 232 IApiProblemDetector[] detectors = buildProblemDetectors(component, ProblemDetectorBuilder.K_ALL, localMonitor.split(1)); 233 // analyze 234 try { 235 // 1. extract references 236 localMonitor.subTask(BuilderMessages.ReferenceAnalyzer_analyzing_api_checking_use); 237 extractReferences(scope, localMonitor.split(1)); 238 // 2. resolve problematic references 239 localMonitor.subTask(BuilderMessages.ReferenceAnalyzer_analyzing_api_checking_use); 240 if (fReferences.size() != 0) { 241 ReferenceResolver.resolveReferences(fReferences, localMonitor.split(1)); 242 } 243 // 3. create problems 244 List<IApiProblem> allProblems = new LinkedList<>(); 245 localMonitor.subTask(BuilderMessages.ReferenceAnalyzer_analyzing_api_checking_use); 246 SubMonitor loopMonitor = localMonitor.split(1).setWorkRemaining(detectors.length); 247 for (IApiProblemDetector detector : detectors) { 248 allProblems.addAll(detector.createProblems(loopMonitor.split(1))); 249 } 250 IApiProblem[] array = allProblems.toArray(new IApiProblem[allProblems.size()]); 251 return array; 252 } catch (OperationCanceledException e) { 253 return EMPTY_RESULT; 254 } finally { 255 // clean up 256 fIndexedDetectors = null; 257 fReferences.clear(); 258 } 259 } 260 261 /** 262 * Returns the collection of problem detectors for the given reference kind 263 * 264 * @param referencekind 265 * @return 266 */ getProblemDetectors(int referencekind)267 public IApiProblemDetector[] getProblemDetectors(int referencekind) { 268 if (fIndexedDetectors != null) { 269 int index = getLog2(referencekind); 270 if (index > -1 && index < fIndexedDetectors.length) { 271 IApiProblemDetector[] detectors = fIndexedDetectors[index]; 272 if (detectors != null) { 273 return detectors; 274 } 275 } 276 return NO_PROBLEM_DETECTORS; 277 } 278 return NO_PROBLEM_DETECTORS; 279 } 280 281 /** 282 * Builds problem detectors to use when analyzing the given component. 283 * 284 * @param component component to be analyzed 285 * @param kindmask the kinds of detectors to build. See 286 * {@link ProblemDetectorBuilder} for kinds 287 * @param monitor 288 * 289 * @return problem detectors 290 */ buildProblemDetectors(IApiComponent component, int kindmask, IProgressMonitor monitor)291 public IApiProblemDetector[] buildProblemDetectors(IApiComponent component, int kindmask, IProgressMonitor monitor) { 292 try { 293 long start = System.currentTimeMillis(); 294 IApiComponent[] components = component.getBaseline().getPrerequisiteComponents(new IApiComponent[] { component }); 295 final ProblemDetectorBuilder visitor = new ProblemDetectorBuilder(component, kindmask); 296 SubMonitor loopMonitor = SubMonitor.convert(monitor, components.length); 297 for (IApiComponent componentLoop : components) { 298 SubMonitor iterationMonitor = loopMonitor.split(1); 299 IApiComponent prereq = componentLoop; 300 if (!prereq.equals(component)) { 301 visitor.setOwningComponent(prereq); 302 try { 303 prereq.getApiDescription().accept(visitor, iterationMonitor); 304 } catch (CoreException e) { 305 ApiPlugin.log(e.getStatus()); 306 } 307 } 308 } 309 long end = System.currentTimeMillis(); 310 if (ApiPlugin.DEBUG_REFERENCE_ANALYZER) { 311 System.out.println("Time to build problem detectors: " + (end - start) + "ms"); //$NON-NLS-1$//$NON-NLS-2$ 312 } 313 // add names from the leak component as well 314 ApiDescriptionVisitor nameVisitor = new ApiDescriptionVisitor() { 315 @Override 316 public boolean visitElement(IElementDescriptor element, IApiAnnotations description) { 317 if (element.getElementType() == IElementDescriptor.PACKAGE) { 318 if (VisibilityModifiers.isPrivate(description.getVisibility())) { 319 visitor.addNonApiPackageName(((IPackageDescriptor) element).getName()); 320 } 321 } 322 return false; 323 } 324 }; 325 component.getApiDescription().accept(nameVisitor, null); 326 List<IApiProblemDetector> detectors = visitor.getProblemDetectors(); 327 int size = detectors.size(); 328 if (size == 0) { 329 return NO_PROBLEM_DETECTORS; 330 } 331 IApiProblemDetector[] array = detectors.toArray(new IApiProblemDetector[size]); 332 indexProblemDetectors(array); 333 return array; 334 } catch (CoreException e) { 335 ApiPlugin.log(e); 336 } 337 return NO_PROBLEM_DETECTORS; 338 } 339 } 340