org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
Eclipse Compiler for Java(TM)
/*******************************************************************************
* Copyright (c) 2015, 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
*
*******************************************************************************/
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.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.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceModuleBinding;
import org.eclipse.jdt.internal.compiler.lookup.SplitPackageBinding;
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 requiredModules = new HashSet();
Set requiredTransitiveModules = new HashSet();
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 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()]));
}
/** 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 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 allTypes = new HashSet();
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 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) {
for (ExportsStatement export : this.exports) {
PackageBinding pb = export.resolvedPackage;
if (pb == null)
continue;
if (pb instanceof SplitPackageBinding)
pb = ((SplitPackageBinding) pb).getIncarnation(this.binding);
if (pb.hasCompilationUnit(true))
continue;
skope.problemReporter().invalidPackageReference(IProblem.PackageDoesNotExistOrIsEmpty, export);
}
}
}
public void analyseModuleGraph(CompilationUnitScope skope) {
if (this.requires != null) {
// collect transitively:
Map> pack2mods = new HashMap<>();
for (ModuleBinding requiredModule : this.binding.getAllRequiredModules()) {
for (PackageBinding exportedPackage : requiredModule.getExports()) {
if (this.binding.canAccess(exportedPackage)) {
String packName = String.valueOf(exportedPackage.readableName());
Set 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> pack2mods)
{
for (PackageBinding pack : requiredModule.getExports()) {
Set mods = pack2mods.get(String.valueOf(pack.readableName()));
if (mods != null && mods.size() > 1)
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;
}
}