Java tutorial
/******************************************************************************* * Copyright (c) 2000, 2019 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 - Contributions for * bug 186342 - [compiler][null] Using annotations for null checking * bug 365519 - editorial cleanup after bug 186342 and bug 365387 * bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; import java.util.ArrayList; import java.util.function.Predicate; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.env.IModuleAwareNameEnvironment; import org.eclipse.jdt.internal.compiler.util.HashtableOfPackage; import org.eclipse.jdt.internal.compiler.util.HashtableOfType; public abstract class PackageBinding extends Binding implements TypeConstants { public long tagBits = 0; // See values in the interface TagBits below public char[][] compoundName; PackageBinding parent; ArrayList<SplitPackageBinding> wrappingSplitPackageBindings; public LookupEnvironment environment; /** Types in this map are either uniquely visible in the current module or ProblemReferenceBindings. */ public HashtableOfType knownTypes; /** All visible member packages, i.e. observable packages associated with modules read by the current module. */ HashtableOfPackage<PackageBinding> knownPackages; // code representing the default that has been defined for this package (using @NonNullByDefault) // once initialized it will be one of Binding.{NO_NULL_DEFAULT,NULL_UNSPECIFIED_BY_DEFAULT,NONNULL_BY_DEFAULT} private int defaultNullness = -1; public ModuleBinding enclosingModule; /** Is this package exported from its module? NB: to query this property use {@link #isExported()} to ensure initialization. */ Boolean isExported; protected PackageBinding(char[][] compoundName, LookupEnvironment environment) { // for creating problem package this.compoundName = compoundName; this.environment = environment; } /* Create a normal package. */ public PackageBinding(char[][] compoundName, PackageBinding parent, LookupEnvironment environment, ModuleBinding enclosingModule) { this.compoundName = compoundName; this.parent = parent; this.environment = environment; this.knownTypes = null; // initialized if used... class counts can be very large 300-600 this.knownPackages = new HashtableOfPackage<PackageBinding>(3); // sub-package counts are typically 0-3 if (compoundName != CharOperation.NO_CHAR_CHAR) checkIfNullAnnotationPackage(); if (enclosingModule != null) this.enclosingModule = enclosingModule; else if (parent != null) this.enclosingModule = parent.enclosingModule; // stop-gap for any remaining calls that don't provide an enclosingModule (they should) if (this.enclosingModule == null) throw new IllegalStateException("Package should have an enclosing module"); //$NON-NLS-1$ } protected void addNotFoundPackage(char[] simpleName) { if (!this.environment.suppressImportErrors) this.knownPackages.put(simpleName, LookupEnvironment.TheNotFoundPackage); } private void addNotFoundType(char[] simpleName) { if (this.environment.suppressImportErrors) return; if (this.knownTypes == null) this.knownTypes = new HashtableOfType(25); this.knownTypes.put(simpleName, LookupEnvironment.TheNotFoundType); } /** * Remembers a sub-package. * For a split parent package this will include potentially enriching with siblings, * in which case the enriched (split) binding will be returned. */ PackageBinding addPackage(PackageBinding element, ModuleBinding module) { if ((element.tagBits & TagBits.HasMissingType) == 0) clearMissingTagBit(); this.knownPackages.put(element.compoundName[element.compoundName.length - 1], element); return element; } void addType(ReferenceBinding element) { if ((element.tagBits & TagBits.HasMissingType) == 0) clearMissingTagBit(); if (this.knownTypes == null) this.knownTypes = new HashtableOfType(25); char[] name = element.compoundName[element.compoundName.length - 1]; ReferenceBinding priorType = this.knownTypes.getput(name, element); if (priorType != null && priorType.isUnresolvedType() && !element.isUnresolvedType()) { ((UnresolvedReferenceBinding) priorType).setResolvedType(element, this.environment); } if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) if (element.isAnnotationType() || element instanceof UnresolvedReferenceBinding) // unresolved types don't yet have the modifiers set checkIfNullAnnotationType(element); if (!element.isUnresolvedType() && this.wrappingSplitPackageBindings != null) { for (SplitPackageBinding splitPackageBinding : this.wrappingSplitPackageBindings) { if (splitPackageBinding.knownTypes != null) { ReferenceBinding prior = splitPackageBinding.knownTypes.get(name); if (prior != null && prior.isUnresolvedType() && !element.isUnresolvedType()) { ((UnresolvedReferenceBinding) prior).setResolvedType(element, this.environment); splitPackageBinding.knownTypes.put(name, null); // forces re-checking for conflicts } } } } } ModuleBinding[] getDeclaringModules() { return new ModuleBinding[] { this.enclosingModule }; } void clearMissingTagBit() { PackageBinding current = this; do { current.tagBits &= ~TagBits.HasMissingType; } while ((current = current.parent) != null); } /* * slash separated name * org.eclipse.jdt.core --> org/eclipse/jdt/core */ @Override public char[] computeUniqueKey(boolean isLeaf) { return CharOperation.concatWith(this.compoundName, '/'); } protected PackageBinding findPackage(char[] name, ModuleBinding module) { // delegate to the module to consider the module graph: return module.getVisiblePackage(CharOperation.arrayConcat(this.compoundName, name)); } /* Answer the subpackage named name; ask the oracle for the package if its not in the cache. * Answer null if it could not be resolved. * * NOTE: This should only be used when we know there is NOT a type with the same name. */ PackageBinding getPackage(char[] name, ModuleBinding mod) { PackageBinding binding = getPackage0(name); if (binding != null) { if (binding == LookupEnvironment.TheNotFoundPackage) return null; else return binding; } if ((binding = findPackage(name, mod)) != null) return binding; // not found so remember a problem package binding in the cache for future lookups addNotFoundPackage(name); return null; } /** Answer the subpackage named name if it exists in the cache. * Answer theNotFoundPackage if it could not be resolved the first time * it was looked up, otherwise answer null. * <p> * NOTE: The returned package binding is guaranteed to be complete wrt. SplitPackageBinding, * or, if no complete binding is yet available, we shyly answer null. * </p><p> * NOTE: Senders must convert theNotFoundPackage into a real problem * package if its to returned.</p> */ PackageBinding getPackage0(char[] name) { return this.knownPackages.get(name); } /** Variant (see {@link #getPackage0(char[])}), that may even answer an incompletely * combined package (in the case of SplitPackageBinding). */ PackageBinding getPackage0Any(char[] name) { return this.knownPackages.get(name); } /* Answer the type named name; ask the oracle for the type if its not in the cache. * Answer a NotVisible problem type if the type is not visible from the invocationPackage. * Answer null if it could not be resolved. * * NOTE: This should only be used by source types/scopes which know there is NOT a * package with the same name. */ ReferenceBinding getType(char[] name, ModuleBinding mod) { ReferenceBinding referenceBinding = getType0(name); if (referenceBinding == null) { if ((referenceBinding = this.environment.askForType(this, name, mod)) == null) { // not found so remember a problem type binding in the cache for future lookups addNotFoundType(name); return null; } } if (referenceBinding == LookupEnvironment.TheNotFoundType) return null; referenceBinding = (ReferenceBinding) BinaryTypeBinding.resolveType(referenceBinding, this.environment, false /* no raw conversion for now */); if (referenceBinding.isNestedType()) return new ProblemReferenceBinding(new char[][] { name }, referenceBinding, ProblemReasons.InternalNameProvided); if (!mod.canAccess(this)) return new ProblemReferenceBinding(referenceBinding.compoundName, referenceBinding, ProblemReasons.NotAccessible); // at this point we have only checked accessibility of the package, accessibility of the type will be checked by callers return referenceBinding; } /* Answer the type named name if it exists in the cache. * Answer theNotFoundType if it could not be resolved the first time * it was looked up, otherwise answer null. * * NOTE: Senders must convert theNotFoundType into a real problem * reference type if its to returned. */ ReferenceBinding getType0(char[] name) { if (this.knownTypes == null) return null; return this.knownTypes.get(name); } /** * Test if this package (or any of its incarnations in case of a SplitPackageBinding) has recorded * an actual, resolved type of the given name (based on answers from getType0()). * Useful for clash detection. */ boolean hasType0Any(char[] name) { ReferenceBinding type0 = getType0(name); return type0 != null && type0.isValidBinding() && !(type0 instanceof UnresolvedReferenceBinding); } /* Answer the package or type named name; ask the oracle if it is not in the cache. * Answer null if it could not be resolved. * * When collisions exist between a type name & a package name, answer the type. * Treat the package as if it does not exist... a problem was already reported when the type was defined. * * NOTE: no visibility checks are performed. * THIS SHOULD ONLY BE USED BY SOURCE TYPES/SCOPES. */ public Binding getTypeOrPackage(char[] name, ModuleBinding mod, boolean splitPackageAllowed) { ReferenceBinding problemBinding = null; ReferenceBinding referenceBinding = getType0(name); lookForType0: if (referenceBinding != null && referenceBinding != LookupEnvironment.TheNotFoundType) { referenceBinding = (ReferenceBinding) BinaryTypeBinding.resolveType(referenceBinding, this.environment, false /* no raw conversion for now */); if (referenceBinding.isNestedType()) { return new ProblemReferenceBinding(new char[][] { name }, referenceBinding, ProblemReasons.InternalNameProvided); } boolean isSameModule = (this instanceof SplitPackageBinding) ? referenceBinding.module() == mod : this.enclosingModule == mod; if (!isSameModule && referenceBinding.isValidBinding() && !mod.canAccess(referenceBinding.fPackage)) { problemBinding = new ProblemReferenceBinding(referenceBinding.compoundName, referenceBinding, ProblemReasons.NotAccessible); break lookForType0; } if ((referenceBinding.tagBits & TagBits.HasMissingType) == 0) { return referenceBinding; } // referenceBinding is a MissingType, will return it if no package is found } PackageBinding packageBinding = getPackage0(name); if (packageBinding != null && packageBinding != LookupEnvironment.TheNotFoundPackage) { if (!splitPackageAllowed) { return packageBinding.getVisibleFor(mod, false); } return packageBinding; } lookForType: if (referenceBinding == null && problemBinding == null) { // have not looked for it before if ((referenceBinding = this.environment.askForType(this, name, mod)) != null) { if (referenceBinding.isNestedType()) { return new ProblemReferenceBinding(new char[][] { name }, referenceBinding, ProblemReasons.InternalNameProvided); } if (referenceBinding.isValidBinding() && !mod.canAccess(referenceBinding.fPackage)) { problemBinding = new ProblemReferenceBinding(referenceBinding.compoundName, referenceBinding, ProblemReasons.NotAccessible); break lookForType; } else { return referenceBinding; } } // Since name could not be found, add a problem binding // to the collections so it will be reported as an error next time. addNotFoundType(name); } if (packageBinding == null) { // have not looked for it before if ((packageBinding = findPackage(name, mod)) != null) { if (!splitPackageAllowed) { return packageBinding.getVisibleFor(mod, false); } return packageBinding; } if (referenceBinding != null && referenceBinding != LookupEnvironment.TheNotFoundType) { if (problemBinding != null) return problemBinding; return referenceBinding; // found cached missing type - check if package conflict } addNotFoundPackage(name); } return problemBinding; } public final boolean isViewedAsDeprecated() { if ((this.tagBits & TagBits.DeprecatedAnnotationResolved) == 0) { this.tagBits |= TagBits.DeprecatedAnnotationResolved; if (this.compoundName != CharOperation.NO_CHAR_CHAR) { ReferenceBinding packageInfo = this.getType(TypeConstants.PACKAGE_INFO_NAME, this.enclosingModule); if (packageInfo != null) { packageInfo.initializeDeprecatedAnnotationTagBits(); this.tagBits |= packageInfo.tagBits & TagBits.AllStandardAnnotationsMask; } } } return (this.tagBits & TagBits.AnnotationDeprecated) != 0; } private void initDefaultNullness() { if (this.defaultNullness == -1) { ReferenceBinding packageInfo = getType(TypeConstants.PACKAGE_INFO_NAME, this.enclosingModule); if (packageInfo != null) { packageInfo.getAnnotationTagBits(); if (packageInfo instanceof SourceTypeBinding) { this.defaultNullness = ((SourceTypeBinding) packageInfo).defaultNullness; } else { this.defaultNullness = ((BinaryTypeBinding) packageInfo).defaultNullness; } } else { this.defaultNullness = NO_NULL_DEFAULT; } } } public int getDefaultNullness() { initDefaultNullness(); if (this.defaultNullness == NO_NULL_DEFAULT) return this.enclosingModule.getDefaultNullness(); return this.defaultNullness; } public void setDefaultNullness(int nullness) { this.defaultNullness = nullness; } /** * Find a binding (either this package or its enclosing ModuleBinding) * where 'defaultNullness' matches the given predicate. */ public Binding findDefaultNullnessTarget(Predicate<Integer> predicate) { initDefaultNullness(); if (predicate.test(this.defaultNullness)) return this; if (this.defaultNullness == NO_NULL_DEFAULT) if (predicate.test(this.enclosingModule.getDefaultNullness())) return this.enclosingModule; return null; } /* API * Answer the receiver's binding type from Binding.BindingID. */ @Override public final int kind() { return Binding.PACKAGE; } @Override public int problemId() { if ((this.tagBits & TagBits.HasMissingType) != 0) return ProblemReasons.NotFound; return ProblemReasons.NoError; } void checkIfNullAnnotationPackage() { LookupEnvironment env = this.environment; if (env.globalOptions.isAnnotationBasedNullAnalysisEnabled) { if (isPackageOfQualifiedTypeName(this.compoundName, env.getNullableAnnotationName())) env.nullableAnnotationPackage = this; if (isPackageOfQualifiedTypeName(this.compoundName, env.getNonNullAnnotationName())) env.nonnullAnnotationPackage = this; if (isPackageOfQualifiedTypeName(this.compoundName, env.getNonNullByDefaultAnnotationName())) env.nonnullByDefaultAnnotationPackage = this; } } private boolean isPackageOfQualifiedTypeName(char[][] packageName, char[][] typeName) { int length; if (typeName == null || (length = packageName.length) != typeName.length - 1) return false; for (int i = 0; i < length; i++) if (!CharOperation.equals(packageName[i], typeName[i])) return false; return true; } void checkIfNullAnnotationType(ReferenceBinding type) { // check if type is one of the configured null annotation types // if so mark as a well known type using the corresponding typeBit: if (this.environment.nullableAnnotationPackage == this && CharOperation.equals(type.compoundName, this.environment.getNullableAnnotationName())) { type.typeBits |= TypeIds.BitNullableAnnotation; if (!(type instanceof UnresolvedReferenceBinding)) // unresolved will need to check back for the resolved type this.environment.nullableAnnotationPackage = null; // don't check again } else if (this.environment.nonnullAnnotationPackage == this && CharOperation.equals(type.compoundName, this.environment.getNonNullAnnotationName())) { type.typeBits |= TypeIds.BitNonNullAnnotation; if (!(type instanceof UnresolvedReferenceBinding)) // unresolved will need to check back for the resolved type this.environment.nonnullAnnotationPackage = null; // don't check again } else if (this.environment.nonnullByDefaultAnnotationPackage == this && CharOperation.equals(type.compoundName, this.environment.getNonNullByDefaultAnnotationName())) { type.typeBits |= TypeIds.BitNonNullByDefaultAnnotation; if (!(type instanceof UnresolvedReferenceBinding)) // unresolved will need to check back for the resolved type this.environment.nonnullByDefaultAnnotationPackage = null; // don't check again } else { type.typeBits |= this.environment.getNullAnnotationBit(type.compoundName); } } @Override public char[] readableName() /*java.lang*/ { return CharOperation.concatWith(this.compoundName, '.'); } @Override public String toString() { String str; if (this.compoundName == CharOperation.NO_CHAR_CHAR) { str = "The Default Package"; //$NON-NLS-1$ } else { str = "package " //$NON-NLS-1$ + ((this.compoundName != null) ? CharOperation.toString(this.compoundName) : "UNNAMED"); //$NON-NLS-1$ } if ((this.tagBits & TagBits.HasMissingType) != 0) { str += "[MISSING]"; //$NON-NLS-1$ } return str; } public boolean isDeclaredIn(ModuleBinding moduleBinding) { return this.enclosingModule == moduleBinding; } public boolean subsumes(PackageBinding binding) { return binding == this; } /** * Is this package exported from its module? * Does not consider export restrictions. */ public boolean isExported() { if (this.isExported == null) { if (this.enclosingModule.isAuto) { this.isExported = Boolean.TRUE; } else { this.enclosingModule.getExports(); // ensure resolved and completed if (this.isExported == null) this.isExported = Boolean.FALSE; } } return this.isExported == Boolean.TRUE; } /** * If this package is uniquely visible to 'module' return a plain PackageBinding. * In case of a conflict between a local package and foreign package flag <b>preferLocal</b> * will select the behavior: * <ul> * <li>if {@code true} the plain local package is returned, because this conflict will more * appropriately be reported against the package declaration, not its references.</li> * <li>if {@code false} a conflict local vs. foreign will be treated just like any other conflict, * see next.</li> * </ul> * In case of multiple accessible foreign packages a SplitPackageBinding is returned * to indicate a conflict. */ public PackageBinding getVisibleFor(ModuleBinding module, boolean preferLocal) { return this; } public abstract PlainPackageBinding getIncarnation(ModuleBinding moduleBinding); public boolean hasCompilationUnit(boolean checkCUs) { if (this.knownTypes != null) { for (ReferenceBinding knownType : this.knownTypes.valueTable) { if (knownType != null && knownType != LookupEnvironment.TheNotFoundType && !knownType.isUnresolvedType()) return true; } } if (this.environment.useModuleSystem) { IModuleAwareNameEnvironment moduleEnv = (IModuleAwareNameEnvironment) this.environment.nameEnvironment; return moduleEnv.hasCompilationUnit(this.compoundName, this.enclosingModule.nameForCUCheck(), checkCUs); } return false; } public void addWrappingSplitPackageBinding(SplitPackageBinding splitPackageBinding) { if (this.wrappingSplitPackageBindings == null) { this.wrappingSplitPackageBindings = new ArrayList<>(); } this.wrappingSplitPackageBindings.add(splitPackageBinding); } }