Java tutorial
/******************************************************************************* * Copyright (c) 2015, 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 * *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; import static org.eclipse.jdt.internal.compiler.problem.ProblemSeverities.*; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.ClassFile; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope; import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; import org.eclipse.jdt.internal.compiler.lookup.MethodScope; import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding; import org.eclipse.jdt.internal.compiler.lookup.PlainPackageBinding; import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.SourceModuleBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit; import org.eclipse.jdt.internal.compiler.problem.AbortMethod; import org.eclipse.jdt.internal.compiler.problem.AbortType; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.compiler.util.HashtableOfObject; public class ModuleDeclaration extends ASTNode implements ReferenceContext { public ExportsStatement[] exports; public RequiresStatement[] requires; public UsesStatement[] uses; public ProvidesStatement[] services; public OpensStatement[] opens; public Annotation[] annotations; public int exportsCount; public int requiresCount; public int usesCount; public int servicesCount; public int opensCount; public SourceModuleBinding binding; public int declarationSourceStart; public int declarationSourceEnd; public int bodyStart; public int bodyEnd; // doesn't include the trailing comment if any. public int modifiersSourceStart; public BlockScope scope; public char[][] tokens; public char[] moduleName; public long[] sourcePositions; public int modifiers = ClassFileConstants.AccDefault; boolean ignoreFurtherInvestigation; boolean hasResolvedModuleDirectives; boolean hasResolvedPackageDirectives; boolean hasResolvedTypeDirectives; CompilationResult compilationResult; public ModuleDeclaration(CompilationResult compilationResult, char[][] tokens, long[] positions) { this.compilationResult = compilationResult; this.exportsCount = 0; this.requiresCount = 0; this.tokens = tokens; this.moduleName = CharOperation.concatWith(tokens, '.'); this.sourcePositions = positions; this.sourceEnd = (int) (positions[positions.length - 1] & 0x00000000FFFFFFFF); this.sourceStart = (int) (positions[0] >>> 32); } public ModuleBinding setBinding(SourceModuleBinding sourceModuleBinding) { this.binding = sourceModuleBinding; return sourceModuleBinding; } public void checkAndSetModifiers() { int realModifiers = this.modifiers & ExtraCompilerModifiers.AccJustFlag; int expectedModifiers = ClassFileConstants.ACC_OPEN | ClassFileConstants.ACC_SYNTHETIC; if ((realModifiers & ~(expectedModifiers)) != 0) { this.scope.problemReporter().illegalModifierForModule(this); realModifiers &= expectedModifiers; } int effectiveModifiers = ClassFileConstants.AccModule | realModifiers; this.modifiers = this.binding.modifiers = effectiveModifiers; } public boolean isOpen() { return (this.modifiers & ClassFileConstants.ACC_OPEN) != 0; } public void createScope(final Scope parentScope) { this.scope = new MethodScope(parentScope, null, true) { @Override public ProblemReporter problemReporter() { // this method scope has no reference context so we better deletegate to the 'real' cuScope: return parentScope.problemReporter(); } @Override public ReferenceContext referenceContext() { return ModuleDeclaration.this; } @Override public boolean isModuleScope() { return true; } }; } public void generateCode() { if ((this.bits & ASTNode.HasBeenGenerated) != 0) return; this.bits |= ASTNode.HasBeenGenerated; if (this.ignoreFurtherInvestigation) { return; } try { // create the result for a compiled type LookupEnvironment env = this.scope.environment(); ClassFile classFile = env.classFilePool.acquireForModule(this.binding, env.globalOptions); classFile.initializeForModule(this.binding); // finalize the compiled type result classFile.addModuleAttributes(this.binding, this.annotations, this.scope.referenceCompilationUnit()); this.scope.referenceCompilationUnit().compilationResult.record(this.binding.moduleName, classFile); } catch (AbortType e) { if (this.binding == null) return; } } /** Resolve those module directives that relate to modules (requires). */ public void resolveModuleDirectives(CompilationUnitScope cuScope) { if (this.binding == null) { this.ignoreFurtherInvestigation = true; return; } if (this.hasResolvedModuleDirectives) return; this.hasResolvedModuleDirectives = true; Set<ModuleBinding> requiredModules = new HashSet<ModuleBinding>(); Set<ModuleBinding> requiredTransitiveModules = new HashSet<ModuleBinding>(); for (int i = 0; i < this.requiresCount; i++) { RequiresStatement ref = this.requires[i]; if (ref != null && ref.resolve(cuScope) != null) { if (!requiredModules.add(ref.resolvedBinding)) { cuScope.problemReporter().duplicateModuleReference(IProblem.DuplicateRequires, ref.module); } if (ref.isTransitive()) requiredTransitiveModules.add(ref.resolvedBinding); Collection<ModuleBinding> deps = ref.resolvedBinding.dependencyGraphCollector().get(); if (deps.contains(this.binding)) { cuScope.problemReporter().cyclicModuleDependency(this.binding, ref.module); requiredModules.remove(ref.module.binding); } } } this.binding.setRequires(requiredModules.toArray(new ModuleBinding[requiredModules.size()]), requiredTransitiveModules.toArray(new ModuleBinding[requiredTransitiveModules.size()])); // also resolve module references inside package statements ("to"): if (this.exports != null) { for (ExportsStatement exportsStatement : this.exports) { if (exportsStatement.isQualified()) { for (ModuleReference moduleReference : exportsStatement.targets) moduleReference.resolve(cuScope); } } } if (this.opens != null) { for (OpensStatement opensStatement : this.opens) { if (opensStatement.isQualified()) { for (ModuleReference moduleReference : opensStatement.targets) moduleReference.resolve(cuScope); } } } } /** Resolve those module directives that relate to packages (exports, opens). */ public void resolvePackageDirectives(CompilationUnitScope cuScope) { if (this.binding == null) { this.ignoreFurtherInvestigation = true; return; } if (this.hasResolvedPackageDirectives) return; this.hasResolvedPackageDirectives = true; Set<PlainPackageBinding> exportedPkgs = new HashSet<>(); for (int i = 0; i < this.exportsCount; i++) { ExportsStatement ref = this.exports[i]; if (ref != null && ref.resolve(cuScope)) { if (!exportedPkgs.add(ref.resolvedPackage)) { cuScope.problemReporter().invalidPackageReference(IProblem.DuplicateExports, ref); } char[][] targets = null; if (ref.targets != null) { targets = new char[ref.targets.length][]; for (int j = 0; j < targets.length; j++) targets[j] = ref.targets[j].moduleName; } this.binding.addResolvedExport(ref.resolvedPackage, targets); } } HashtableOfObject openedPkgs = new HashtableOfObject(); for (int i = 0; i < this.opensCount; i++) { OpensStatement ref = this.opens[i]; if (isOpen()) { cuScope.problemReporter().invalidOpensStatement(ref, this); } else { if (openedPkgs.containsKey(ref.pkgName)) { cuScope.problemReporter().invalidPackageReference(IProblem.DuplicateOpens, ref); } else { openedPkgs.put(ref.pkgName, ref); ref.resolve(cuScope); } char[][] targets = null; if (ref.targets != null) { targets = new char[ref.targets.length][]; for (int j = 0; j < targets.length; j++) targets[j] = ref.targets[j].moduleName; } this.binding.addResolvedOpens(ref.resolvedPackage, targets); } } } /** Resolve those module directives that relate to types (provides / uses). */ public void resolveTypeDirectives(CompilationUnitScope cuScope) { if (this.binding == null) { this.ignoreFurtherInvestigation = true; return; } if (this.hasResolvedTypeDirectives) return; this.hasResolvedTypeDirectives = true; ASTNode.resolveAnnotations(this.scope, this.annotations, this.binding); Set<TypeBinding> allTypes = new HashSet<TypeBinding>(); for (int i = 0; i < this.usesCount; i++) { TypeBinding serviceBinding = this.uses[i].serviceInterface.resolveType(this.scope); if (serviceBinding != null && serviceBinding.isValidBinding()) { if (!(serviceBinding.isClass() || serviceBinding.isInterface() || serviceBinding.isAnnotationType())) { cuScope.problemReporter().invalidServiceRef(IProblem.InvalidServiceIntfType, this.uses[i].serviceInterface); } if (!allTypes.add(this.uses[i].serviceInterface.resolvedType)) { cuScope.problemReporter().duplicateTypeReference(IProblem.DuplicateUses, this.uses[i].serviceInterface); } } } this.binding.setUses(allTypes.toArray(new TypeBinding[allTypes.size()])); Set<TypeBinding> interfaces = new HashSet<>(); for (int i = 0; i < this.servicesCount; i++) { this.services[i].resolve(this.scope); TypeBinding infBinding = this.services[i].serviceInterface.resolvedType; if (infBinding != null && infBinding.isValidBinding()) { if (!interfaces.add(this.services[i].serviceInterface.resolvedType)) { cuScope.problemReporter().duplicateTypeReference(IProblem.DuplicateServices, this.services[i].serviceInterface); } this.binding.setImplementations(infBinding, this.services[i].getResolvedImplementations()); } } this.binding.setServices(interfaces.toArray(new TypeBinding[interfaces.size()])); } public void analyseCode(CompilationUnitScope skope) { analyseModuleGraph(skope); analyseReferencedPackages(skope); } private void analyseReferencedPackages(CompilationUnitScope skope) { if (this.exports != null) { analyseSomeReferencedPackages(this.exports, skope); } if (this.opens != null) { analyseSomeReferencedPackages(this.opens, skope); } } private void analyseSomeReferencedPackages(PackageVisibilityStatement[] stats, CompilationUnitScope skope) { for (PackageVisibilityStatement stat : stats) { PlainPackageBinding pb = stat.resolvedPackage; if (pb == null) continue; if (pb.hasCompilationUnit(true)) continue; for (ModuleBinding req : this.binding.getAllRequiredModules()) { for (PlainPackageBinding exported : req.getExports()) { if (CharOperation.equals(pb.compoundName, exported.compoundName)) { skope.problemReporter().exportingForeignPackage(stat, req); return; } } } skope.problemReporter().invalidPackageReference(IProblem.PackageDoesNotExistOrIsEmpty, stat); } } public void analyseModuleGraph(CompilationUnitScope skope) { if (this.requires != null) { // collect transitively: Map<String, Set<ModuleBinding>> pack2mods = new HashMap<>(); for (ModuleBinding requiredModule : this.binding.getAllRequiredModules()) { for (PlainPackageBinding exportedPackage : requiredModule.getExports()) { if (this.binding.canAccess(exportedPackage)) { String packName = String.valueOf(exportedPackage.readableName()); Set<ModuleBinding> mods = pack2mods.get(packName); if (mods == null) pack2mods.put(packName, mods = new HashSet<>()); mods.add(requiredModule); } } } // report against the causing requires directives: for (RequiresStatement requiresStat : this.requires) { ModuleBinding requiredModule = requiresStat.resolvedBinding; if (requiredModule != null) { if (requiredModule.isDeprecated()) skope.problemReporter().deprecatedModule(requiresStat.module, requiredModule); analyseOneDependency(requiresStat, requiredModule, skope, pack2mods); if (requiresStat.isTransitive()) { for (ModuleBinding secondLevelModule : requiredModule.getAllRequiredModules()) analyseOneDependency(requiresStat, secondLevelModule, skope, pack2mods); } } } } } private void analyseOneDependency(RequiresStatement requiresStat, ModuleBinding requiredModule, CompilationUnitScope skope, Map<String, Set<ModuleBinding>> pack2mods) { for (PlainPackageBinding pack : requiredModule.getExports()) { Set<ModuleBinding> mods = pack2mods.get(String.valueOf(pack.readableName())); if (mods != null && mods.size() > 1) { CompilerOptions compilerOptions = skope.compilerOptions(); boolean inJdtDebugCompileMode = compilerOptions.enableJdtDebugCompileMode; if (!inJdtDebugCompileMode) { skope.problemReporter().conflictingPackagesFromModules(pack, mods, requiresStat.sourceStart, requiresStat.sourceEnd); } } } } public void traverse(ASTVisitor visitor, CompilationUnitScope unitScope) { visitor.visit(this, unitScope); } public StringBuffer printHeader(int indent, StringBuffer output) { if (this.annotations != null) { for (int i = 0; i < this.annotations.length; i++) { this.annotations[i].print(indent, output); if (i != this.annotations.length - 1) output.append(" "); //$NON-NLS-1$ } output.append('\n'); } if (isOpen()) { output.append("open "); //$NON-NLS-1$ } output.append("module "); //$NON-NLS-1$ output.append(CharOperation.charToString(this.moduleName)); return output; } public StringBuffer printBody(int indent, StringBuffer output) { output.append(" {"); //$NON-NLS-1$ if (this.requires != null) { for (int i = 0; i < this.requiresCount; i++) { output.append('\n'); printIndent(indent + 1, output); this.requires[i].print(0, output); } } if (this.exports != null) { for (int i = 0; i < this.exportsCount; i++) { output.append('\n'); this.exports[i].print(indent + 1, output); } } if (this.opens != null) { for (int i = 0; i < this.opensCount; i++) { output.append('\n'); this.opens[i].print(indent + 1, output); } } if (this.uses != null) { for (int i = 0; i < this.usesCount; i++) { output.append('\n'); this.uses[i].print(indent + 1, output); } } if (this.servicesCount != 0) { for (int i = 0; i < this.servicesCount; i++) { output.append('\n'); this.services[i].print(indent + 1, output); } } output.append('\n'); return printIndent(indent, output).append('}'); } @Override public StringBuffer print(int indent, StringBuffer output) { // printIndent(indent, output); printHeader(0, output); return printBody(indent, output); } @Override public void abort(int abortLevel, CategorizedProblem problem) { switch (abortLevel) { case AbortCompilation: throw new AbortCompilation(this.compilationResult, problem); case AbortCompilationUnit: throw new AbortCompilationUnit(this.compilationResult, problem); case AbortMethod: throw new AbortMethod(this.compilationResult, problem); default: throw new AbortType(this.compilationResult, problem); } } @Override public CompilationResult compilationResult() { return this.compilationResult; } @Override public CompilationUnitDeclaration getCompilationUnitDeclaration() { return this.scope.referenceCompilationUnit(); } @Override public boolean hasErrors() { return this.ignoreFurtherInvestigation; } @Override public void tagAsHavingErrors() { this.ignoreFurtherInvestigation = true; } @Override public void tagAsHavingIgnoredMandatoryErrors(int problemId) { // Nothing to do for this context; } public String getModuleVersion() { if (this.scope != null) { LookupEnvironment env = this.scope.environment().root; return env.moduleVersion; } return null; } }