blob: f2807b7da234c6d45e51669c4f7b8c9bc55f200a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016, 2017 IBM Corporation.
*
* 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.classfmt;
import java.util.Arrays;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
import org.eclipse.jdt.internal.compiler.env.IBinaryModule;
import org.eclipse.jdt.internal.compiler.env.IModule;
public class ModuleInfo extends ClassFileStruct implements IBinaryModule {
protected int flags;
protected int requiresCount;
protected int exportsCount;
protected int usesCount;
protected int providesCount;
protected int opensCount;
protected char[] name;
protected char[] version;
protected ModuleReferenceInfo[] requires;
protected PackageExportInfo[] exports;
protected PackageExportInfo[] opens;
char[][] uses;
IModule.IService[] provides;
protected AnnotationInfo[] annotations;
private long tagBits;
@Override
public boolean isOpen() {
return (this.flags & ClassFileConstants.ACC_OPEN) != 0;
}
public int requiresCount() {
return this.requiresCount;
}
public int exportsCount() {
return this.exportsCount;
}
public int usesCount() {
return this.usesCount;
}
public int providesCount() {
return this.providesCount;
}
@Override
public char[] name() {
return this.name;
}
public void setName(char[] name) {
this.name = name;
}
@Override
public IModule.IModuleReference[] requires() {
return this.requires;
}
@Override
public IModule.IPackageExport[] exports() {
return this.exports;
}
@Override
public char[][] uses() {
return this.uses;
}
@Override
public IService[] provides() {
return this.provides;
}
@Override
public IModule.IPackageExport[] opens() {
return this.opens;
}
@Override
public IBinaryAnnotation[] getAnnotations() {
return this.annotations;
}
@Override
public long getTagBits() {
return this.tagBits;
}
/**
* @param classFileBytes byte[]
* @param offsets int[]
* @param offset int
*/
protected ModuleInfo (byte classFileBytes[], int offsets[], int offset) {
super(classFileBytes, offsets, offset);
}
/**
* @param classFileBytes bytes of the enclosing class file
* @param offsets constant pool offsets
* @param offset offset to the "Module" attribute
* @return a module info initialized from the "Module" attribute, which was already detected by the caller
*/
public static ModuleInfo createModule(byte classFileBytes[], int offsets[], int offset) {
ModuleInfo module = new ModuleInfo(classFileBytes, offsets, 0);
module.readModuleAttribute(offset+6);
return module;
}
private void readModuleAttribute(int moduleOffset) {
int utf8Offset;
int name_index = this.constantPoolOffsets[u2At(moduleOffset)];
utf8Offset = this.constantPoolOffsets[u2At(name_index + 1)];
this.name = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
CharOperation.replace(this.name, '/', '.');
moduleOffset += 2;
this.flags = u2At(moduleOffset);
moduleOffset += 2;
int version_index = u2At(moduleOffset);
if (version_index > 0) {
utf8Offset = this.constantPoolOffsets[version_index];
this.version = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
}
moduleOffset += 2;
int count = u2At(moduleOffset);
this.requiresCount = count;
this.requires = new ModuleReferenceInfo[count];
moduleOffset += 2;
for (int i = 0; i < count; i++) {
name_index = this.constantPoolOffsets[u2At(moduleOffset)];
utf8Offset = this.constantPoolOffsets[u2At(name_index + 1)];
char[] requiresNames = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
this.requires[i] = this.new ModuleReferenceInfo();
CharOperation.replace(requiresNames, '/', '.');
this.requires[i].refName = requiresNames;
moduleOffset += 2;
int modifiers = u2At(moduleOffset);
this.requires[i].modifiers = modifiers;
this.requires[i].isTransitive = (ClassFileConstants.ACC_TRANSITIVE & modifiers) != 0; // Access modifier
moduleOffset += 2;
version_index = u2At(moduleOffset);
if (version_index > 0) {
utf8Offset = this.constantPoolOffsets[version_index];
this.requires[i].required_version = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
}
moduleOffset += 2;
}
count = u2At(moduleOffset);
moduleOffset += 2;
this.exportsCount = count;
this.exports = new PackageExportInfo[count];
for (int i = 0; i < count; i++) {
name_index = this.constantPoolOffsets[u2At(moduleOffset)];
utf8Offset = this.constantPoolOffsets[u2At(name_index + 1)];
char[] exported = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
CharOperation.replace(exported, '/', '.');
PackageExportInfo pack = this.new PackageExportInfo();
this.exports[i] = pack;
pack.packageName = exported;
moduleOffset += 2;
pack.modifiers = u2At(moduleOffset);
moduleOffset += 2;
int exportedtoCount = u2At(moduleOffset);
moduleOffset += 2;
if (exportedtoCount > 0) {
pack.exportedTo = new char[exportedtoCount][];
pack.exportedToCount = exportedtoCount;
for(int k = 0; k < exportedtoCount; k++) {
name_index = this.constantPoolOffsets[u2At(moduleOffset)];
utf8Offset = this.constantPoolOffsets[u2At(name_index + 1)];
char[] exportedToName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
CharOperation.replace(exportedToName, '/', '.');
pack.exportedTo[k] = exportedToName;
moduleOffset += 2;
}
}
}
count = u2At(moduleOffset);
moduleOffset += 2;
this.opensCount = count;
this.opens = new PackageExportInfo[count];
for (int i = 0; i < count; i++) {
name_index = this.constantPoolOffsets[u2At(moduleOffset)];
utf8Offset = this.constantPoolOffsets[u2At(name_index + 1)];
char[] exported = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
CharOperation.replace(exported, '/', '.');
PackageExportInfo pack = this.new PackageExportInfo();
this.opens[i] = pack;
pack.packageName = exported;
moduleOffset += 2;
pack.modifiers = u2At(moduleOffset);
moduleOffset += 2;
int exportedtoCount = u2At(moduleOffset);
moduleOffset += 2;
if (exportedtoCount > 0) {
pack.exportedTo = new char[exportedtoCount][];
pack.exportedToCount = exportedtoCount;
for(int k = 0; k < exportedtoCount; k++) {
name_index = this.constantPoolOffsets[u2At(moduleOffset)];
utf8Offset = this.constantPoolOffsets[u2At(name_index + 1)];
char[] exportedToName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
CharOperation.replace(exportedToName, '/', '.');
pack.exportedTo[k] = exportedToName;
moduleOffset += 2;
}
}
}
count = u2At(moduleOffset);
moduleOffset += 2;
this.usesCount = count;
this.uses = new char[count][];
for (int i = 0; i < count; i++) {
int classIndex = this.constantPoolOffsets[u2At(moduleOffset)];
utf8Offset = this.constantPoolOffsets[u2At(classIndex + 1)];
char[] inf = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
CharOperation.replace(inf, '/', '.');
this.uses[i] = inf;
moduleOffset += 2;
}
count = u2At(moduleOffset);
moduleOffset += 2;
this.providesCount = count;
this.provides = new ServiceInfo[count];
for (int i = 0; i < count; i++) {
int classIndex = this.constantPoolOffsets[u2At(moduleOffset)];
utf8Offset = this.constantPoolOffsets[u2At(classIndex + 1)];
char[] inf = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
CharOperation.replace(inf, '/', '.');
ServiceInfo service = this.new ServiceInfo();
this.provides[i] = service;
service.serviceName = inf;
moduleOffset += 2;
int implCount = u2At(moduleOffset);
moduleOffset += 2;
service.with = new char[implCount][];
if (implCount > 0) {
service.with = new char[implCount][];
for(int k = 0; k < implCount; k++) {
classIndex = this.constantPoolOffsets[u2At(moduleOffset)];
utf8Offset = this.constantPoolOffsets[u2At(classIndex + 1)];
char[] implName = utf8At(utf8Offset + 3, u2At(utf8Offset + 1));
CharOperation.replace(implName, '/', '.');
service.with[k] = implName;
moduleOffset += 2;
}
}
}
}
void setAnnotations(AnnotationInfo[] annotationInfos, long tagBits, boolean fullyInitialize) {
this.annotations = annotationInfos;
this.tagBits = tagBits;
if (fullyInitialize) {
for (int i = 0, max = annotationInfos.length; i < max; i++) {
annotationInfos[i].initialize();
}
}
}
class ModuleReferenceInfo implements IModule.IModuleReference {
char[] refName;
boolean isTransitive = false;
int modifiers;
char[] required_version;
@Override
public char[] name() {
return this.refName;
}
@Override
public boolean isTransitive() {
return this.isTransitive;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof IModule.IModuleReference))
return false;
IModule.IModuleReference mod = (IModule.IModuleReference) o;
if (this.modifiers != mod.getModifiers())
return false;
return CharOperation.equals(this.refName, mod.name(), false);
}
@Override
public int hashCode() {
return CharOperation.hashCode(this.refName);
}
@Override
public int getModifiers() {
return this.modifiers;
}
}
class PackageExportInfo implements IModule.IPackageExport {
char[] packageName;
char[][] exportedTo;
int exportedToCount;
int modifiers;
@Override
public char[] name() {
return this.packageName;
}
@Override
public char[][] targets() {
return this.exportedTo;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
toStringContent(buffer);
return buffer.toString();
}
protected void toStringContent(StringBuffer buffer) {
buffer.append(this.packageName);
if (this.exportedToCount > 0) {
buffer.append(" to "); //$NON-NLS-1$
for(int i = 0; i < this.exportedToCount; i++) {
buffer.append(this.exportedTo[i]);
buffer.append(',').append(' ');
}
}
buffer.append(';').append('\n');
}
}
class ServiceInfo implements IModule.IService {
char[] serviceName;
char[][] with;
@Override
public char[] name() {
return this.serviceName;
}
@Override
public char[][] with() {
return this.with;
}
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof IModule))
return false;
IModule mod = (IModule) o;
if (!CharOperation.equals(this.name, mod.name()))
return false;
return Arrays.equals(this.requires, mod.requires());
}
@Override
public int hashCode() {
int result = 17;
int c = CharOperation.hashCode(this.name);
result = 31 * result + c;
c = Arrays.hashCode(this.requires);
result = 31 * result + c;
return result;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer(getClass().getName());
toStringContent(buffer);
return buffer.toString();
}
protected void toStringContent(StringBuffer buffer) {
buffer.append("\nmodule "); //$NON-NLS-1$
buffer.append(this.name).append(' ');
buffer.append('{').append('\n');
if (this.requiresCount > 0) {
for(int i = 0; i < this.requiresCount; i++) {
buffer.append("\trequires "); //$NON-NLS-1$
if (this.requires[i].isTransitive) {
buffer.append(" public "); //$NON-NLS-1$
}
buffer.append(this.requires[i].refName);
buffer.append(';').append('\n');
}
}
if (this.exportsCount > 0) {
buffer.append('\n');
for(int i = 0; i < this.exportsCount; i++) {
buffer.append("\texports "); //$NON-NLS-1$
buffer.append(this.exports[i].toString());
}
}
buffer.append('\n').append('}').toString();
}
}