| /******************************************************************************* |
| * Copyright (c) 2000, 2018 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Stephan Herrmann - contribution for Bug 300576 - NPE Computing type hierarchy when compliance doesn't match libraries |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core.hierarchy; |
| |
| /** |
| * This is the public entry point to resolve type hierarchies. |
| * |
| * When requesting additional types from the name environment, the resolver |
| * accepts all forms (binary, source & compilation unit) for additional types. |
| * |
| * Side notes: Binary types already know their resolved supertypes so this |
| * only makes sense for source types. Even though the compiler finds all binary |
| * types to complete the hierarchy of a given source type, is there any reason |
| * why the requestor should be informed that binary type X subclasses Y & |
| * implements I & J? |
| */ |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.jdt.core.IModuleDescription; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.CompilationResult; |
| import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; |
| import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; |
| import org.eclipse.jdt.internal.compiler.IProblemFactory; |
| import org.eclipse.jdt.internal.compiler.ast.*; |
| import org.eclipse.jdt.internal.compiler.ast.LambdaExpression; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.env.AccessRestriction; |
| import org.eclipse.jdt.internal.compiler.env.IBinaryType; |
| import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; |
| import org.eclipse.jdt.internal.compiler.env.IGenericType; |
| import org.eclipse.jdt.internal.compiler.env.INameEnvironment; |
| import org.eclipse.jdt.internal.compiler.env.ISourceType; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.impl.ITypeRequestor; |
| import org.eclipse.jdt.internal.compiler.lookup.*; |
| import org.eclipse.jdt.internal.compiler.parser.Parser; |
| import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter; |
| import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; |
| import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; |
| import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; |
| import org.eclipse.jdt.internal.compiler.util.Messages; |
| import org.eclipse.jdt.internal.core.*; |
| import org.eclipse.jdt.internal.core.util.ASTNodeFinder; |
| import org.eclipse.jdt.internal.core.util.HandleFactory; |
| |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| public class HierarchyResolver implements ITypeRequestor { |
| |
| private ReferenceBinding focusType; |
| private boolean superTypesOnly; |
| private boolean hasMissingSuperClass; |
| LookupEnvironment lookupEnvironment; |
| private CompilerOptions options; |
| HierarchyBuilder builder; |
| private ReferenceBinding[] typeBindings; |
| private BindingMap<IGenericType> bindingMap = new BindingMap<>(); |
| |
| private int typeIndex; |
| private IGenericType[] typeModels; |
| private Parser basicParser; |
| |
| private static final CompilationUnitDeclaration FakeUnit; |
| static { |
| IErrorHandlingPolicy policy = DefaultErrorHandlingPolicies.exitAfterAllProblems(); |
| ProblemReporter problemReporter = new ProblemReporter(policy, new CompilerOptions(), new DefaultProblemFactory()); |
| CompilationResult result = new CompilationResult(CharOperation.NO_CHAR, 0, 0, 0); |
| FakeUnit = new CompilationUnitDeclaration(problemReporter, result, 0); |
| } |
| |
| public HierarchyResolver(INameEnvironment nameEnvironment, Map settings, HierarchyBuilder builder, IProblemFactory problemFactory) { |
| // create a problem handler with the 'exit after all problems' handling policy |
| this.options = new CompilerOptions(settings); |
| IErrorHandlingPolicy policy = DefaultErrorHandlingPolicies.exitAfterAllProblems(); |
| ProblemReporter problemReporter = new ProblemReporter(policy, this.options, problemFactory); |
| |
| LookupEnvironment environment = new LookupEnvironment(this, this.options, problemReporter, nameEnvironment); |
| environment.mayTolerateMissingType = true; |
| setEnvironment(environment, builder); |
| } |
| public HierarchyResolver(LookupEnvironment lookupEnvironment, HierarchyBuilder builder) { |
| setEnvironment(lookupEnvironment, builder); |
| } |
| |
| /** |
| * Add an additional binary type |
| * @param binaryType |
| * @param packageBinding |
| */ |
| @Override |
| public void accept(IBinaryType binaryType, PackageBinding packageBinding, AccessRestriction accessRestriction) { |
| IProgressMonitor progressMonitor = this.builder.hierarchy.progressMonitor; |
| if (progressMonitor != null && progressMonitor.isCanceled()) |
| throw new OperationCanceledException(); |
| |
| BinaryTypeBinding typeBinding = this.lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding, accessRestriction); |
| try { |
| this.remember(binaryType, typeBinding); |
| } catch (AbortCompilation e) { |
| // ignore |
| } |
| } |
| |
| /** |
| * Add an additional compilation unit. |
| * @param sourceUnit |
| */ |
| @Override |
| public void accept(ICompilationUnit sourceUnit, AccessRestriction accessRestriction) { |
| if (CharOperation.equals(TypeConstants.MODULE_INFO_NAME, sourceUnit.getMainTypeName())) { |
| // module is needed for resolving, so parse and build it now: |
| CompilationResult unitResult = new CompilationResult(sourceUnit, 1, 1, this.options.maxProblemsPerUnit); |
| CompilationUnitDeclaration parsedUnit = basicParser().dietParse(sourceUnit, unitResult); |
| this.lookupEnvironment.buildTypeBindings(parsedUnit, accessRestriction); |
| this.lookupEnvironment.completeTypeBindings(parsedUnit, true); // work done inside checkAndSetImports() |
| } else { |
| //System.out.println("Cannot accept compilation units inside the HierarchyResolver."); |
| this.lookupEnvironment.problemReporter.abortDueToInternalError( |
| new StringBuffer(Messages.accept_cannot) |
| .append(sourceUnit.getFileName()) |
| .toString()); |
| } |
| } |
| private Parser basicParser() { |
| if (this.basicParser == null) { |
| ProblemReporter problemReporter = |
| new ProblemReporter( |
| DefaultErrorHandlingPolicies.proceedWithAllProblems(), |
| this.options, |
| new DefaultProblemFactory()); |
| this.basicParser = new Parser(problemReporter, false); |
| this.basicParser.reportOnlyOneSyntaxError = true; |
| } |
| return this.basicParser; |
| } |
| |
| /** |
| * Add additional source types |
| * @param sourceTypes |
| * @param packageBinding |
| */ |
| @Override |
| public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding, AccessRestriction accessRestriction) { |
| IProgressMonitor progressMonitor = this.builder.hierarchy.progressMonitor; |
| if (progressMonitor != null && progressMonitor.isCanceled()) |
| throw new OperationCanceledException(); |
| |
| // find most enclosing type first (needed when explicit askForType(...) is done |
| // with a member type (e.g. p.A$B)) |
| ISourceType sourceType = sourceTypes[0]; |
| while (sourceType.getEnclosingType() != null) |
| sourceType = sourceType.getEnclosingType(); |
| |
| // build corresponding compilation unit |
| CompilationResult result = new CompilationResult(sourceType.getFileName(), 1, 1, this.options.maxProblemsPerUnit); |
| CompilationUnitDeclaration unit = |
| SourceTypeConverter.buildCompilationUnit( |
| new ISourceType[] {sourceType}, // ignore secondary types, to improve laziness |
| SourceTypeConverter.MEMBER_TYPE | (this.lookupEnvironment.globalOptions.sourceLevel >= ClassFileConstants.JDK1_8 ? SourceTypeConverter.METHOD : 0), // need member types |
| // no need for field initialization |
| this.lookupEnvironment.problemReporter, |
| result); |
| |
| // build bindings |
| if (unit != null) { |
| try { |
| LookupEnvironment environment = packageBinding.environment; |
| if (environment == null) |
| environment = this.lookupEnvironment; |
| environment.buildTypeBindings(unit, accessRestriction); |
| |
| org.eclipse.jdt.core.ICompilationUnit cu = ((SourceTypeElementInfo)sourceType).getHandle().getCompilationUnit(); |
| rememberAllTypes(unit, cu, false); |
| |
| environment.completeTypeBindings(unit, true/*build constructor only*/); |
| } catch (AbortCompilation e) { |
| // missing 'java.lang' package: ignore |
| } |
| } |
| } |
| /* |
| * Creates the super class handle of the given type. |
| * Returns null if the type has no super class. |
| * Adds the simple name to the hierarchy missing types if the class is not found and returns null. |
| */ |
| private IType findSuperClass(IGenericType type, ReferenceBinding typeBinding) { |
| ReferenceBinding superBinding = typeBinding.superclass(); |
| |
| if (superBinding != null) { |
| superBinding = (ReferenceBinding) superBinding.erasure(); |
| if (typeBinding.isHierarchyInconsistent()) { |
| if (superBinding.problemId() == ProblemReasons.NotFound) { |
| this.hasMissingSuperClass = true; |
| this.builder.hierarchy.missingTypes.add(new String(superBinding.sourceName)); // note: this could be Map$Entry |
| return null; |
| } else if ((superBinding.id == TypeIds.T_JavaLangObject)) { |
| char[] superclassName; |
| char separator; |
| if (type instanceof IBinaryType) { |
| superclassName = ((IBinaryType)type).getSuperclassName(); |
| separator = '/'; |
| } else if (type instanceof ISourceType) { |
| superclassName = ((ISourceType)type).getSuperclassName(); |
| separator = '.'; |
| } else if (type instanceof HierarchyType) { |
| superclassName = ((HierarchyType)type).superclassName; |
| separator = '.'; |
| } else { |
| return null; |
| } |
| |
| if (superclassName != null) { // check whether subclass of Object due to broken hierarchy (as opposed to explicitly extending it) |
| int lastSeparator = CharOperation.lastIndexOf(separator, superclassName); |
| char[] simpleName = lastSeparator == -1 ? superclassName : CharOperation.subarray(superclassName, lastSeparator+1, superclassName.length); |
| if (!CharOperation.equals(simpleName, TypeConstants.OBJECT)) { |
| this.hasMissingSuperClass = true; |
| this.builder.hierarchy.missingTypes.add(new String(simpleName)); |
| return null; |
| } |
| } |
| } |
| } |
| IGenericType typeModel = this.bindingMap.get(superBinding); |
| if (typeModel != null) { |
| return this.builder.getHandle(typeModel, superBinding); |
| } |
| } |
| return null; |
| } |
| /* |
| * Returns the handles of the super interfaces of the given type. |
| * Adds the simple name to the hierarchy missing types if an interface is not found (but don't put null in the returned array) |
| */ |
| private IType[] findSuperInterfaces(IGenericType type, ReferenceBinding typeBinding) { |
| char[][] superInterfaceNames; |
| char separator; |
| if (type instanceof IBinaryType) { |
| superInterfaceNames = ((IBinaryType)type).getInterfaceNames(); |
| separator = '/'; |
| } else if (type instanceof ISourceType) { |
| ISourceType sourceType = (ISourceType)type; |
| if (sourceType.isAnonymous()) { // if anonymous type |
| if (typeBinding.superInterfaces() != null && typeBinding.superInterfaces().length > 0) { |
| superInterfaceNames = new char[][] {sourceType.getSuperclassName()}; |
| } else { |
| superInterfaceNames = sourceType.getInterfaceNames(); |
| } |
| } else { |
| if (TypeDeclaration.kind(sourceType.getModifiers()) == TypeDeclaration.ANNOTATION_TYPE_DECL) |
| superInterfaceNames = new char[][] {TypeConstants.CharArray_JAVA_LANG_ANNOTATION_ANNOTATION}; |
| else |
| superInterfaceNames = sourceType.getInterfaceNames(); |
| } |
| separator = '.'; |
| } else if (type instanceof HierarchyType) { |
| HierarchyType hierarchyType = (HierarchyType)type; |
| if (hierarchyType.isAnonymous()) { // if anonymous type |
| if (typeBinding.superInterfaces() != null && typeBinding.superInterfaces().length > 0) { |
| superInterfaceNames = new char[][] {hierarchyType.superclassName}; |
| } else { |
| superInterfaceNames = hierarchyType.superInterfaceNames; |
| } |
| } else { |
| superInterfaceNames = hierarchyType.superInterfaceNames; |
| } |
| separator = '.'; |
| } else{ |
| return null; |
| } |
| |
| ReferenceBinding[] interfaceBindings = typeBinding.superInterfaces(); |
| int bindingIndex = 0; |
| int bindingLength = interfaceBindings == null ? 0 : interfaceBindings.length; |
| int length = superInterfaceNames == null ? 0 : superInterfaceNames.length; |
| IType[] superinterfaces = new IType[length]; |
| int index = 0; |
| next : for (int i = 0; i < length; i++) { |
| char[] superInterfaceName = superInterfaceNames[i]; |
| int end = superInterfaceName.length; |
| |
| // find the end of simple name |
| int genericStart = CharOperation.indexOf(Signature.C_GENERIC_START, superInterfaceName); |
| if (genericStart != -1) end = genericStart; |
| |
| // find the start of simple name |
| int lastSeparator = CharOperation.lastIndexOf(separator, superInterfaceName, 0, end); |
| int start = lastSeparator + 1; |
| |
| // case of binary inner type -> take the last part |
| int lastDollar = CharOperation.lastIndexOf('$', superInterfaceName, start); |
| if (lastDollar != -1) start = lastDollar + 1; |
| |
| char[] simpleName = CharOperation.subarray(superInterfaceName, start, end); |
| |
| if (bindingIndex < bindingLength) { |
| ReferenceBinding interfaceBinding = (ReferenceBinding) interfaceBindings[bindingIndex].erasure(); |
| |
| // ensure that the binding corresponds to the interface defined by the user |
| if (CharOperation.equals(simpleName, interfaceBinding.sourceName)) { |
| bindingIndex++; |
| IGenericType genericType = this.bindingMap.get(interfaceBinding); |
| if (genericType != null) { |
| IType handle = this.builder.getHandle(genericType, interfaceBinding); |
| if (handle != null) { |
| superinterfaces[index++] = handle; |
| continue next; |
| } |
| } |
| } |
| } |
| this.builder.hierarchy.missingTypes.add(new String(simpleName)); |
| } |
| if (index != length) |
| System.arraycopy(superinterfaces, 0, superinterfaces = new IType[index], 0, index); |
| return superinterfaces; |
| } |
| /* |
| * For all type bindings that have hierarchy problems, artificially fix their superclass/superInterfaces so that the connection |
| * can be made. |
| */ |
| private void fixSupertypeBindings() { |
| for (int current = this.typeIndex; current >= 0; current--) { |
| ReferenceBinding typeBinding = this.typeBindings[current]; |
| if ((typeBinding.tagBits & TagBits.HierarchyHasProblems) == 0) |
| continue; |
| |
| if (typeBinding instanceof SourceTypeBinding) { |
| if (typeBinding instanceof LocalTypeBinding) { |
| LocalTypeBinding localTypeBinding = (LocalTypeBinding) typeBinding; |
| QualifiedAllocationExpression allocationExpression = localTypeBinding.scope.referenceContext.allocation; |
| TypeReference type; |
| if (allocationExpression != null && (type = allocationExpression.type) != null && type.resolvedType != null) { |
| localTypeBinding.setSuperClass((ReferenceBinding) type.resolvedType); |
| continue; |
| } |
| } |
| ClassScope scope = ((SourceTypeBinding) typeBinding).scope; |
| if (scope != null) { |
| TypeDeclaration typeDeclaration = scope.referenceContext; |
| TypeReference superclassRef = typeDeclaration == null ? null : typeDeclaration.superclass; |
| TypeBinding superclass = superclassRef == null ? null : superclassRef.resolvedType; |
| if (superclass != null) { |
| superclass = superclass.closestMatch(); |
| } |
| if (superclass instanceof ReferenceBinding) { |
| // ensure we are not creating a cycle (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=215681 ) |
| if (!(subTypeOfType((ReferenceBinding) superclass, typeBinding))) { |
| ((SourceTypeBinding) typeBinding).setSuperClass((ReferenceBinding) superclass); |
| } |
| } |
| |
| TypeReference[] superInterfaces = typeDeclaration == null ? null : typeDeclaration.superInterfaces; |
| int length; |
| ReferenceBinding[] interfaceBindings = typeBinding.superInterfaces(); |
| if (superInterfaces != null && (length = superInterfaces.length) > (interfaceBindings == null ? 0 : interfaceBindings.length)) { // check for interfaceBindings being null (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=139689) |
| interfaceBindings = new ReferenceBinding[length]; |
| int index = 0; |
| for (int i = 0; i < length; i++) { |
| TypeBinding superInterface = superInterfaces[i].resolvedType; |
| if (superInterface != null) { |
| superInterface = superInterface.closestMatch(); |
| } |
| if (superInterface instanceof ReferenceBinding) { |
| // ensure we are not creating a cycle (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=215681 ) |
| if (!(subTypeOfType((ReferenceBinding) superInterface, typeBinding))) { |
| interfaceBindings[index++] = (ReferenceBinding) superInterface; |
| } |
| } |
| } |
| if (index < length) |
| System.arraycopy(interfaceBindings, 0, interfaceBindings = new ReferenceBinding[index], 0 , index); |
| ((SourceTypeBinding) typeBinding).setSuperInterfaces(interfaceBindings); |
| } |
| } |
| } else if (typeBinding instanceof BinaryTypeBinding) { |
| try { |
| typeBinding.superclass(); |
| } catch (AbortCompilation e) { |
| // allow subsequent call to superclass() to succeed so that we don't have to catch AbortCompilation everywhere |
| ((BinaryTypeBinding) typeBinding).tagBits &= ~TagBits.HasUnresolvedSuperclass; |
| this.builder.hierarchy.missingTypes.add(new String(typeBinding.superclass().sourceName())); |
| this.hasMissingSuperClass = true; |
| } |
| try { |
| typeBinding.superInterfaces(); |
| } catch (AbortCompilation e) { |
| // allow subsequent call to superInterfaces() to succeed so that we don't have to catch AbortCompilation everywhere |
| ((BinaryTypeBinding) typeBinding).tagBits &= ~TagBits.HasUnresolvedSuperinterfaces; |
| } |
| } |
| } |
| } |
| private void remember(IGenericType suppliedType, ReferenceBinding typeBinding) { |
| if (typeBinding == null) return; |
| |
| if (++this.typeIndex == this.typeModels.length) { |
| System.arraycopy(this.typeModels, 0, this.typeModels = new IGenericType[this.typeIndex * 2], 0, this.typeIndex); |
| System.arraycopy(this.typeBindings, 0, this.typeBindings = new ReferenceBinding[this.typeIndex * 2], 0, this.typeIndex); |
| } |
| this.typeModels[this.typeIndex] = suppliedType; |
| this.typeBindings[this.typeIndex] = typeBinding; |
| this.bindingMap.put(typeBinding, suppliedType); |
| } |
| private void remember(IType type, ReferenceBinding typeBinding) { |
| if (((CompilationUnit)type.getCompilationUnit()).isOpen()) { |
| try { |
| IGenericType genericType = (IGenericType)((JavaElement)type).getElementInfo(); |
| remember(genericType, typeBinding); |
| } catch (JavaModelException e) { |
| // cannot happen since element is open |
| return; |
| } |
| } else { |
| if (typeBinding == null) return; |
| boolean isAnonymous = false; |
| try { |
| isAnonymous = type.isAnonymous(); |
| } catch(JavaModelException jme) { |
| // Ignore |
| } |
| if (typeBinding instanceof SourceTypeBinding) { |
| TypeDeclaration typeDeclaration = ((SourceTypeBinding)typeBinding).scope.referenceType(); |
| |
| // simple super class name |
| char[] superclassName = null; |
| TypeReference superclass; |
| if ((typeDeclaration.bits & ASTNode.IsAnonymousType) != 0) { |
| superclass = typeDeclaration.allocation.type; |
| } else { |
| superclass = typeDeclaration.superclass; |
| } |
| if (superclass != null) { |
| char[][] typeName = superclass.getTypeName(); |
| superclassName = typeName == null ? null : typeName[typeName.length-1]; |
| } |
| |
| // simple super interface names |
| char[][] superInterfaceNames = null; |
| TypeReference[] superInterfaces = typeDeclaration.superInterfaces; |
| if (superInterfaces != null) { |
| int length = superInterfaces.length; |
| superInterfaceNames = new char[length][]; |
| for (int i = 0; i < length; i++) { |
| TypeReference superInterface = superInterfaces[i]; |
| char[][] typeName = superInterface.getTypeName(); |
| superInterfaceNames[i] = typeName[typeName.length-1]; |
| } |
| } |
| |
| HierarchyType hierarchyType = new HierarchyType( |
| type, |
| typeDeclaration.name, |
| typeDeclaration.binding.modifiers, |
| superclassName, |
| superInterfaceNames, |
| isAnonymous); |
| remember(hierarchyType, typeDeclaration.binding); |
| } else { |
| HierarchyType hierarchyType = new HierarchyType( |
| type, |
| typeBinding.sourceName(), |
| typeBinding.modifiers, |
| typeBinding.superclass().sourceName(), |
| new char [][] { typeBinding.superInterfaces()[0].sourceName() }, |
| isAnonymous); |
| remember(hierarchyType, typeBinding); |
| } |
| } |
| |
| } |
| /* |
| * Remembers all type bindings defined in the given parsed unit, adding local/anonymous types if specified. |
| */ |
| private void rememberAllTypes(CompilationUnitDeclaration parsedUnit, org.eclipse.jdt.core.ICompilationUnit cu, boolean includeLocalTypes) { |
| TypeDeclaration[] types = parsedUnit.types; |
| if (types != null) { |
| for (int i = 0, length = types.length; i < length; i++) { |
| TypeDeclaration type = types[i]; |
| rememberWithMemberTypes(type, cu.getType(new String(type.name))); |
| } |
| } |
| if (!includeLocalTypes || (parsedUnit.localTypes == null && parsedUnit.functionalExpressions == null)) |
| return; |
| |
| HandleFactory factory = new HandleFactory(); |
| HashSet existingElements = new HashSet(parsedUnit.localTypeCount + parsedUnit.functionalExpressionsCount); |
| HashMap knownScopes = new HashMap(parsedUnit.localTypeCount + parsedUnit.functionalExpressionsCount); |
| |
| if (parsedUnit.localTypes != null) { |
| for (int i = 0; i < parsedUnit.localTypeCount; i++) { |
| LocalTypeBinding localType = parsedUnit.localTypes[i]; |
| ClassScope classScope = localType.scope; |
| TypeDeclaration typeDecl = classScope.referenceType(); |
| IType typeHandle = (IType)factory.createElement(classScope, cu, existingElements, knownScopes); |
| rememberWithMemberTypes(typeDecl, typeHandle); |
| } |
| } |
| if (parsedUnit.functionalExpressions != null) { |
| for (int i = 0; i < parsedUnit.functionalExpressionsCount; i++) { |
| if (parsedUnit.functionalExpressions[i] instanceof LambdaExpression) { |
| final LambdaExpression expression = (LambdaExpression) parsedUnit.functionalExpressions[i]; |
| if (expression.resolvedType != null && expression.resolvedType.isValidBinding()) { |
| IType typeHandle = (IType)factory.createLambdaTypeElement(expression, cu, existingElements, knownScopes); |
| remember(typeHandle, expression.getTypeBinding()); |
| } |
| } |
| } |
| } |
| } |
| private void rememberWithMemberTypes(TypeDeclaration typeDecl, IType typeHandle) { |
| remember(typeHandle, typeDecl.binding); |
| |
| TypeDeclaration[] memberTypes = typeDecl.memberTypes; |
| if (memberTypes != null) { |
| for (int i = 0, length = memberTypes.length; i < length; i++) { |
| TypeDeclaration memberType = memberTypes[i]; |
| rememberWithMemberTypes(memberType, typeHandle.getType(new String(memberType.name))); |
| } |
| } |
| } |
| /* |
| * Reports the hierarchy from the remembered bindings. |
| * Note that 'binaryTypeBinding' is null if focus type is a source type. |
| */ |
| private void reportHierarchy(IType focus, TypeDeclaration focusLocalType, ReferenceBinding binaryTypeBinding) { |
| |
| // set focus type binding |
| if (focus != null) { |
| if (binaryTypeBinding != null) { |
| // binary type |
| this.focusType = binaryTypeBinding; |
| } else { |
| // source type |
| if (focusLocalType != null) { |
| // anonymous or local type |
| this.focusType = focusLocalType.binding; |
| } else { |
| // top level or member type |
| char[] fullyQualifiedName = focus.getFullyQualifiedName().toCharArray(); |
| setFocusType(CharOperation.splitOn('.', fullyQualifiedName)); |
| } |
| } |
| } |
| |
| // be resilient and fix super type bindings |
| fixSupertypeBindings(); |
| |
| int objectIndex = -1; |
| IProgressMonitor progressMonitor = this.builder.hierarchy.progressMonitor; |
| for (int current = this.typeIndex; current >= 0; current--) { |
| if (progressMonitor != null && progressMonitor.isCanceled()) |
| throw new OperationCanceledException(); |
| |
| ReferenceBinding typeBinding = this.typeBindings[current]; |
| |
| // java.lang.Object treated at the end |
| if (typeBinding.id == TypeIds.T_JavaLangObject) { |
| objectIndex = current; |
| continue; |
| } |
| |
| IGenericType suppliedType = this.typeModels[current]; |
| |
| if (!subOrSuperOfFocus(typeBinding)) { |
| continue; // ignore types outside of hierarchy |
| } |
| |
| IType superclass; |
| if (typeBinding.isInterface()){ // do not connect interfaces to Object |
| superclass = null; |
| } else { |
| superclass = findSuperClass(suppliedType, typeBinding); |
| } |
| IType[] superinterfaces = findSuperInterfaces(suppliedType, typeBinding); |
| |
| this.builder.connect(suppliedType, this.builder.getHandle(suppliedType, typeBinding), superclass, superinterfaces); |
| } |
| // add java.lang.Object only if the super class is not missing |
| if (objectIndex > -1 && (!this.hasMissingSuperClass || this.focusType == null)) { |
| IGenericType objectType = this.typeModels[objectIndex]; |
| this.builder.connect(objectType, this.builder.getHandle(objectType, this.typeBindings[objectIndex]), null, null); |
| } |
| } |
| private void reset(){ |
| this.lookupEnvironment.reset(); |
| |
| this.focusType = null; |
| this.superTypesOnly = false; |
| this.typeIndex = -1; |
| this.typeModels = new IGenericType[5]; |
| this.typeBindings = new ReferenceBinding[5]; |
| this.bindingMap.clear(); |
| } |
| |
| /** |
| * Resolve the supertypes for the supplied source type. |
| * Inform the requestor of the resolved supertypes using: |
| * connect(ISourceType suppliedType, IGenericType superclass, IGenericType[] superinterfaces) |
| * @param suppliedType |
| */ |
| public void resolve(IGenericType suppliedType) { |
| try { |
| if (suppliedType.isBinaryType()) { |
| BinaryTypeBinding binaryTypeBinding = this.lookupEnvironment.cacheBinaryType((IBinaryType) suppliedType, false/*don't need field and method (bug 125067)*/, null /*no access restriction*/); |
| remember(suppliedType, binaryTypeBinding); |
| // We still need to add superclasses and superinterfaces bindings (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=53095) |
| int startIndex = this.typeIndex; |
| for (int i = startIndex; i <= this.typeIndex; i++) { |
| IGenericType igType = this.typeModels[i]; |
| if (igType != null && igType.isBinaryType()) { |
| CompilationUnitDeclaration previousUnitBeingCompleted = this.lookupEnvironment.unitBeingCompleted; |
| // fault in its hierarchy... |
| try { |
| // ensure that unitBeingCompleted is set so that we don't get an AbortCompilation for a missing type |
| // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=213249 ) |
| if (previousUnitBeingCompleted == null) { |
| this.lookupEnvironment.unitBeingCompleted = FakeUnit; |
| } |
| ReferenceBinding typeBinding = this.typeBindings[i]; |
| typeBinding.superclass(); |
| typeBinding.superInterfaces(); |
| } catch (AbortCompilation e) { |
| // classpath problem for this type: ignore |
| } finally { |
| this.lookupEnvironment.unitBeingCompleted = previousUnitBeingCompleted; |
| } |
| } |
| } |
| this.superTypesOnly = true; |
| reportHierarchy(this.builder.getType(), null, binaryTypeBinding); |
| } else { |
| org.eclipse.jdt.core.ICompilationUnit cu = ((SourceTypeElementInfo)suppliedType).getHandle().getCompilationUnit(); |
| if (cu != null) { |
| HashSet localTypes = new HashSet(); |
| localTypes.add(cu.getPath().toString()); |
| this.superTypesOnly = true; |
| resolve(new Openable[] {(Openable)cu}, localTypes, null); |
| } |
| } |
| } catch (AbortCompilation e) { // ignore this exception for now since it typically means we cannot find java.lang.Object |
| } finally { |
| reset(); |
| } |
| } |
| |
| /** |
| * Resolve the supertypes for the types contained in the given openables (ICompilationUnits and/or IClassFiles). |
| * Inform the requestor of the resolved supertypes for each |
| * supplied source type using: |
| * connect(ISourceType suppliedType, IGenericType superclass, IGenericType[] superinterfaces) |
| * |
| * Also inform the requestor of the supertypes of each |
| * additional requested super type which is also a source type |
| * instead of a binary type. |
| * @param openables |
| * @param localTypes |
| * @param monitor |
| */ |
| public void resolve(Openable[] openables, HashSet localTypes, IProgressMonitor monitor) { |
| SubMonitor subMonitor = SubMonitor.convert(monitor, 3); |
| try { |
| int openablesLength = openables.length; |
| CompilationUnitDeclaration[] parsedUnits = new CompilationUnitDeclaration[openablesLength]; |
| boolean[] hasLocalType = new boolean[openablesLength]; |
| org.eclipse.jdt.core.ICompilationUnit[] cus = new org.eclipse.jdt.core.ICompilationUnit[openablesLength]; |
| int unitsIndex = 0; |
| |
| CompilationUnitDeclaration focusUnit = null; |
| ReferenceBinding focusBinaryBinding = null; |
| IType focus = this.builder.getType(); |
| Openable focusOpenable = null; |
| if (focus != null) { |
| if (focus.isBinary()) { |
| focusOpenable = (Openable)focus.getClassFile(); |
| } else { |
| focusOpenable = (Openable)focus.getCompilationUnit(); |
| } |
| } |
| |
| subMonitor.split(1); |
| // build type bindings |
| Parser parser = new Parser(this.lookupEnvironment.problemReporter, true); |
| final boolean isJava8 = this.options.sourceLevel >= ClassFileConstants.JDK1_8; |
| for (int i = 0; i < openablesLength; i++) { |
| Openable openable = openables[i]; |
| if (openable instanceof org.eclipse.jdt.core.ICompilationUnit) { |
| org.eclipse.jdt.core.ICompilationUnit cu = (org.eclipse.jdt.core.ICompilationUnit)openable; |
| |
| // contains a potential subtype as a local or anonymous type? |
| boolean containsLocalType = false; |
| if (localTypes == null) { // case of hierarchy on region |
| containsLocalType = true; |
| } else { |
| IPath path = cu.getPath(); |
| containsLocalType = cu.isWorkingCopy() ? true /* presume conservatively */ : localTypes.contains(path.toString()); |
| } |
| |
| // build parsed unit |
| CompilationUnitDeclaration parsedUnit = null; |
| if (cu.isOpen()) { |
| // create parsed unit from source element infos |
| CompilationResult result = new CompilationResult((ICompilationUnit)cu, i, openablesLength, this.options.maxProblemsPerUnit); |
| SourceTypeElementInfo[] typeInfos = null; |
| try { |
| IType[] topLevelTypes = cu.getTypes(); |
| int topLevelLength = topLevelTypes.length; |
| if (topLevelLength == 0) continue; // empty cu: no need to parse (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=65677) |
| typeInfos = new SourceTypeElementInfo[topLevelLength]; |
| for (int j = 0; j < topLevelLength; j++) { |
| IType topLevelType = topLevelTypes[j]; |
| typeInfos[j] = (SourceTypeElementInfo)((JavaElement)topLevelType).getElementInfo(); |
| } |
| } catch (JavaModelException e) { |
| // types/cu exist since cu is opened |
| } |
| int flags = !containsLocalType |
| ? SourceTypeConverter.MEMBER_TYPE | (isJava8 ? SourceTypeConverter.METHOD : 0) |
| : SourceTypeConverter.FIELD_AND_METHOD | SourceTypeConverter.MEMBER_TYPE | SourceTypeConverter.LOCAL_TYPE; |
| parsedUnit = |
| SourceTypeConverter.buildCompilationUnit( |
| typeInfos, |
| flags, |
| this.lookupEnvironment.problemReporter, |
| result); |
| |
| // We would have got all the necessary local types by now and hence there is no further need |
| // to parse the method bodies. Parser.getMethodBodies, which is called latter in this function, |
| // will not parse the method statements if ASTNode.HasAllMethodBodies is set. |
| if (containsLocalType && parsedUnit != null) parsedUnit.bits |= ASTNode.HasAllMethodBodies; |
| } else { |
| // create parsed unit from file |
| IFile file = (IFile) cu.getResource(); |
| ICompilationUnit sourceUnit = this.builder.createCompilationUnitFromPath(openable, file, findAssociatedModuleName(openable)); |
| CompilationResult unitResult = new CompilationResult(sourceUnit, i, openablesLength, this.options.maxProblemsPerUnit); |
| parsedUnit = parser.dietParse(sourceUnit, unitResult); |
| } |
| |
| if (parsedUnit != null) { |
| hasLocalType[unitsIndex] = containsLocalType; |
| cus[unitsIndex] = cu; |
| parsedUnits[unitsIndex++] = parsedUnit; |
| try { |
| this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/); |
| if (openable.equals(focusOpenable)) { |
| focusUnit = parsedUnit; |
| } |
| } catch (AbortCompilation e) { |
| // classpath problem for this type: ignore |
| } |
| } |
| } else { |
| // cache binary type binding |
| ClassFile classFile = (ClassFile)openable; |
| IBinaryType binaryType = (IBinaryType) JavaModelManager.getJavaModelManager().getInfo(classFile.getType()); |
| if (binaryType == null) { |
| // create binary type from file |
| if (classFile.getPackageFragmentRoot().isArchive()) { |
| binaryType = this.builder.createInfoFromClassFileInJar(classFile); |
| } else { |
| IResource file = classFile.resource(); |
| binaryType = this.builder.createInfoFromClassFile(classFile, file); |
| } |
| } |
| if (binaryType != null) { |
| try { |
| BinaryTypeBinding binaryTypeBinding = this.lookupEnvironment.cacheBinaryType(binaryType, false/*don't need field and method (bug 125067)*/, null /*no access restriction*/); |
| remember(binaryType, binaryTypeBinding); |
| if (openable.equals(focusOpenable)) { |
| focusBinaryBinding = binaryTypeBinding; |
| } |
| } catch (AbortCompilation e) { |
| // classpath problem for this type: ignore |
| } |
| } |
| } |
| } |
| |
| // remember type declaration of focus if local/anonymous early (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=210498) |
| TypeDeclaration focusLocalType = null; |
| if (focus != null && focusBinaryBinding == null && focusUnit != null && ((Member)focus).getOuterMostLocalContext() != null) { |
| focusLocalType = new ASTNodeFinder(focusUnit).findType(focus); |
| } |
| |
| |
| for (int i = 0; i <= this.typeIndex; i++) { |
| IGenericType suppliedType = this.typeModels[i]; |
| if (suppliedType != null && suppliedType.isBinaryType()) { |
| CompilationUnitDeclaration previousUnitBeingCompleted = this.lookupEnvironment.unitBeingCompleted; |
| // fault in its hierarchy... |
| try { |
| // ensure that unitBeingCompleted is set so that we don't get an AbortCompilation for a missing type |
| // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=213249 ) |
| if (previousUnitBeingCompleted == null) { |
| this.lookupEnvironment.unitBeingCompleted = FakeUnit; |
| } |
| ReferenceBinding typeBinding = this.typeBindings[i]; |
| typeBinding.superclass(); |
| typeBinding.superInterfaces(); |
| } catch (AbortCompilation e) { |
| // classpath problem for this type: ignore |
| } finally { |
| this.lookupEnvironment.unitBeingCompleted = previousUnitBeingCompleted; |
| } |
| } |
| } |
| |
| SubMonitor unitLoopMonitor = subMonitor.split(1).setWorkRemaining(unitsIndex); |
| // complete type bindings (i.e. connect super types) |
| for (int i = 0; i < unitsIndex; i++) { |
| unitLoopMonitor.split(1); |
| CompilationUnitDeclaration parsedUnit = parsedUnits[i]; |
| if (parsedUnit != null) { |
| try { |
| if (hasLocalType[i]) { // NB: no-op if method bodies have been already parsed |
| parser.getMethodBodies(parsedUnit); |
| } |
| } catch (AbortCompilation e) { |
| // classpath problem for this type: don't try to resolve (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=49809) |
| hasLocalType[i] = false; |
| } |
| } |
| } |
| // complete type bindings and build fields and methods only for local types |
| // (in this case the constructor is needed when resolving local types) |
| // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=145333) |
| try { |
| SubMonitor completeLoopMonitor = subMonitor.split(1).setWorkRemaining(unitsIndex); |
| this.lookupEnvironment.completeTypeBindings(parsedUnits, hasLocalType, unitsIndex); |
| // remember type bindings |
| for (int i = 0; i < unitsIndex; i++) { |
| completeLoopMonitor.split(1); |
| CompilationUnitDeclaration parsedUnit = parsedUnits[i]; |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=462158 |
| // Certain assist features require type hierarchy even with code with compiler errors. |
| if (parsedUnit != null) { |
| boolean containsLocalType = hasLocalType[i]; |
| if (containsLocalType) { |
| parsedUnit.scope.faultInTypes(); |
| parsedUnit.resolve(); |
| } |
| rememberAllTypes(parsedUnit, cus[i], containsLocalType); |
| } |
| } |
| } catch (AbortCompilation e) { |
| // skip it silently |
| } |
| |
| // if no potential subtype was a real subtype of the binary focus type, no need to go further |
| // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=54043) |
| if (focusBinaryBinding == null && focus != null && focus.isBinary()) { |
| char[] fullyQualifiedName = focus.getFullyQualifiedName().toCharArray(); |
| focusBinaryBinding = this.lookupEnvironment.getCachedType(CharOperation.splitOn('.', fullyQualifiedName)); |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=436155 |
| // When local types are requested, it's likely a type is found from cache without |
| // the hierarchy being resolved completely, so consider that factor too |
| if (focusBinaryBinding == null || |
| (focusBinaryBinding.tagBits & TagBits.HasUnresolvedSuperclass) != 0) |
| return; |
| } |
| |
| reportHierarchy(focus, focusLocalType, focusBinaryBinding); |
| |
| } catch (ClassCastException e){ // work-around for 1GF5W1S - can happen in case duplicates are fed to the hierarchy with binaries hiding sources |
| } catch (AbortCompilation e) { // ignore this exception for now since it typically means we cannot find java.lang.Object |
| if (TypeHierarchy.DEBUG) |
| e.printStackTrace(); |
| } finally { |
| reset(); |
| } |
| } |
| |
| private char[] findAssociatedModuleName(Openable openable) { |
| IModuleDescription module = null; |
| IPackageFragmentRoot root = openable.getPackageFragmentRoot(); |
| try { |
| if (root.getKind() == IPackageFragmentRoot.K_SOURCE) |
| module = root.getJavaProject().getModuleDescription(); // from any root in this project |
| else |
| module = root.getModuleDescription(); |
| } catch (JavaModelException jme) { |
| // ignore, cannot associate to any module |
| } |
| if (module != null) |
| return module.getElementName().toCharArray(); |
| return null; |
| } |
| |
| private void setEnvironment(LookupEnvironment lookupEnvironment, HierarchyBuilder builder) { |
| this.lookupEnvironment = lookupEnvironment; |
| this.builder = builder; |
| |
| this.typeIndex = -1; |
| this.typeModels = new IGenericType[5]; |
| this.typeBindings = new ReferenceBinding[5]; |
| this.bindingMap.clear(); |
| } |
| |
| /* |
| * Set the focus type (i.e. the type that this resolver is computing the hierarch for. |
| * Returns the binding of this focus type or null if it could not be found. |
| */ |
| public ReferenceBinding setFocusType(char[][] compoundName) { |
| if (compoundName == null || this.lookupEnvironment == null) return null; |
| this.focusType = this.lookupEnvironment.getCachedType(compoundName); |
| if (this.focusType == null) { |
| this.focusType = this.lookupEnvironment.askForType(compoundName, this.lookupEnvironment.UnNamedModule); |
| if (this.focusType == null) { |
| int length = compoundName.length; |
| char[] typeName = compoundName[length-1]; |
| int firstDollar = CharOperation.indexOf('$', typeName); |
| if (firstDollar != -1) { |
| compoundName[length-1] = CharOperation.subarray(typeName, 0, firstDollar); |
| this.focusType = this.lookupEnvironment.askForType(compoundName, this.lookupEnvironment.UnNamedModule); |
| if (this.focusType != null) { |
| char[][] memberTypeNames = CharOperation.splitOn('$', typeName, firstDollar+1, typeName.length); |
| for (int i = 0; i < memberTypeNames.length; i++) { |
| this.focusType = this.focusType.getMemberType(memberTypeNames[i]); |
| if (this.focusType == null) |
| return null; |
| } |
| } |
| } |
| } |
| } |
| return this.focusType; |
| } |
| public boolean subOrSuperOfFocus(ReferenceBinding typeBinding) { |
| if (this.focusType == null) return true; // accept all types (case of hierarchy in a region) |
| try { |
| if (subTypeOfType(this.focusType, typeBinding)) return true; |
| if (!this.superTypesOnly && subTypeOfType(typeBinding, this.focusType)) return true; |
| } catch (AbortCompilation e) { |
| // unresolved superclass/superinterface -> ignore |
| } |
| return false; |
| } |
| private boolean subTypeOfType(ReferenceBinding subType, ReferenceBinding typeBinding) { |
| if (typeBinding == null || subType == null) return false; |
| if (TypeBinding.equalsEquals(subType, typeBinding)) return true; |
| ReferenceBinding superclass = subType.superclass(); |
| if (superclass != null) superclass = (ReferenceBinding) superclass.erasure(); |
| // if (superclass != null && superclass.id == TypeIds.T_JavaLangObject && subType.isHierarchyInconsistent()) return false; |
| if (subTypeOfType(superclass, typeBinding)) return true; |
| ReferenceBinding[] superInterfaces = subType.superInterfaces(); |
| if (superInterfaces != null) { |
| for (int i = 0, length = superInterfaces.length; i < length; i++) { |
| ReferenceBinding superInterface = (ReferenceBinding) superInterfaces[i].erasure(); |
| if (subTypeOfType(superInterface, typeBinding)) return true; |
| } |
| } |
| return false; |
| } |
| protected void worked(IProgressMonitor monitor, int work) { |
| if (monitor != null) { |
| if (monitor.isCanceled()) { |
| throw new OperationCanceledException(); |
| } else { |
| monitor.worked(work); |
| } |
| } |
| } |
| } |
| |