8186236: ModuleInfoExtender should be ASM6 aware
Reviewed-by: ksrini, mchung, sundar
This commit is contained in:
parent
105a51b96d
commit
693e7e8bf3
@ -57,8 +57,6 @@ import java.util.stream.Stream;
|
||||
import jdk.internal.loader.BuiltinClassLoader;
|
||||
import jdk.internal.loader.BootLoader;
|
||||
import jdk.internal.loader.ClassLoaders;
|
||||
import jdk.internal.misc.JavaLangAccess;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.IllegalAccessLogger;
|
||||
import jdk.internal.module.ModuleLoaderMap;
|
||||
import jdk.internal.module.ServicesCatalog;
|
||||
@ -68,6 +66,7 @@ import jdk.internal.org.objectweb.asm.Attribute;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.ModuleVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
@ -1432,7 +1431,7 @@ public final class Module implements AnnotatedElement {
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
|
||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, cw) {
|
||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM6, cw) {
|
||||
@Override
|
||||
public void visit(int version,
|
||||
int access,
|
||||
@ -1458,6 +1457,11 @@ public final class Module implements AnnotatedElement {
|
||||
public void visitAttribute(Attribute attr) {
|
||||
// drop non-annotation attributes
|
||||
}
|
||||
@Override
|
||||
public ModuleVisitor visitModule(String name, int flags, String version) {
|
||||
// drop Module attribute
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
ClassReader cr = new ClassReader(in);
|
||||
|
@ -1,765 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Builder;
|
||||
import java.lang.module.ModuleDescriptor.Requires;
|
||||
import java.lang.module.ModuleDescriptor.Exports;
|
||||
import java.lang.module.ModuleDescriptor.Opens;
|
||||
import java.lang.module.ModuleDescriptor.Provides;
|
||||
import java.lang.module.ModuleDescriptor.Version;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.misc.JavaLangModuleAccess;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.org.objectweb.asm.Attribute;
|
||||
import jdk.internal.org.objectweb.asm.ByteVector;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Label;
|
||||
import static jdk.internal.module.ClassFileConstants.*;
|
||||
|
||||
|
||||
/**
|
||||
* Provides ASM implementations of {@code Attribute} to read and write the
|
||||
* class file attributes in a module-info class file.
|
||||
*/
|
||||
|
||||
public final class ClassFileAttributes {
|
||||
|
||||
private ClassFileAttributes() { }
|
||||
|
||||
/**
|
||||
* Module_attribute {
|
||||
* // See lang-vm.html for details.
|
||||
* }
|
||||
*/
|
||||
public static class ModuleAttribute extends Attribute {
|
||||
private static final JavaLangModuleAccess JLMA
|
||||
= SharedSecrets.getJavaLangModuleAccess();
|
||||
|
||||
private ModuleDescriptor descriptor;
|
||||
private Version replacementVersion;
|
||||
|
||||
public ModuleAttribute(ModuleDescriptor descriptor) {
|
||||
super(MODULE);
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
public ModuleAttribute(Version v) {
|
||||
super(MODULE);
|
||||
this.replacementVersion = v;
|
||||
}
|
||||
|
||||
public ModuleAttribute() {
|
||||
super(MODULE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attribute read(ClassReader cr,
|
||||
int off,
|
||||
int len,
|
||||
char[] buf,
|
||||
int codeOff,
|
||||
Label[] labels)
|
||||
{
|
||||
// module_name (CONSTANT_Module_info)
|
||||
String mn = cr.readModule(off, buf);
|
||||
off += 2;
|
||||
|
||||
// module_flags
|
||||
int module_flags = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
|
||||
Set<ModuleDescriptor.Modifier> modifiers = new HashSet<>();
|
||||
if ((module_flags & ACC_OPEN) != 0)
|
||||
modifiers.add(ModuleDescriptor.Modifier.OPEN);
|
||||
if ((module_flags & ACC_SYNTHETIC) != 0)
|
||||
modifiers.add(ModuleDescriptor.Modifier.SYNTHETIC);
|
||||
if ((module_flags & ACC_MANDATED) != 0)
|
||||
modifiers.add(ModuleDescriptor.Modifier.MANDATED);
|
||||
|
||||
Builder builder = JLMA.newModuleBuilder(mn, false, modifiers);
|
||||
|
||||
// module_version
|
||||
String module_version = cr.readUTF8(off, buf);
|
||||
off += 2;
|
||||
if (replacementVersion != null) {
|
||||
builder.version(replacementVersion);
|
||||
} else if (module_version != null) {
|
||||
builder.version(module_version);
|
||||
}
|
||||
|
||||
// requires_count and requires[requires_count]
|
||||
int requires_count = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
for (int i=0; i<requires_count; i++) {
|
||||
// CONSTANT_Module_info
|
||||
String dn = cr.readModule(off, buf);
|
||||
off += 2;
|
||||
|
||||
// requires_flags
|
||||
int requires_flags = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
Set<Requires.Modifier> mods;
|
||||
if (requires_flags == 0) {
|
||||
mods = Collections.emptySet();
|
||||
} else {
|
||||
mods = new HashSet<>();
|
||||
if ((requires_flags & ACC_TRANSITIVE) != 0)
|
||||
mods.add(Requires.Modifier.TRANSITIVE);
|
||||
if ((requires_flags & ACC_STATIC_PHASE) != 0)
|
||||
mods.add(Requires.Modifier.STATIC);
|
||||
if ((requires_flags & ACC_SYNTHETIC) != 0)
|
||||
mods.add(Requires.Modifier.SYNTHETIC);
|
||||
if ((requires_flags & ACC_MANDATED) != 0)
|
||||
mods.add(Requires.Modifier.MANDATED);
|
||||
}
|
||||
|
||||
// requires_version
|
||||
String requires_version = cr.readUTF8(off, buf);
|
||||
off += 2;
|
||||
if (requires_version == null) {
|
||||
builder.requires(mods, dn);
|
||||
} else {
|
||||
JLMA.requires(builder, mods, dn, requires_version);
|
||||
}
|
||||
}
|
||||
|
||||
// exports_count and exports[exports_count]
|
||||
int exports_count = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
if (exports_count > 0) {
|
||||
for (int i=0; i<exports_count; i++) {
|
||||
// CONSTANT_Package_info
|
||||
String pkg = cr.readPackage(off, buf).replace('/', '.');
|
||||
off += 2;
|
||||
|
||||
int exports_flags = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
Set<Exports.Modifier> mods;
|
||||
if (exports_flags == 0) {
|
||||
mods = Collections.emptySet();
|
||||
} else {
|
||||
mods = new HashSet<>();
|
||||
if ((exports_flags & ACC_SYNTHETIC) != 0)
|
||||
mods.add(Exports.Modifier.SYNTHETIC);
|
||||
if ((exports_flags & ACC_MANDATED) != 0)
|
||||
mods.add(Exports.Modifier.MANDATED);
|
||||
}
|
||||
|
||||
int exports_to_count = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
if (exports_to_count > 0) {
|
||||
Set<String> targets = new HashSet<>();
|
||||
for (int j=0; j<exports_to_count; j++) {
|
||||
String t = cr.readModule(off, buf);
|
||||
off += 2;
|
||||
targets.add(t);
|
||||
}
|
||||
builder.exports(mods, pkg, targets);
|
||||
} else {
|
||||
builder.exports(mods, pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// opens_count and opens[opens_count]
|
||||
int open_count = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
if (open_count > 0) {
|
||||
for (int i=0; i<open_count; i++) {
|
||||
// CONSTANT_Package_info
|
||||
String pkg = cr.readPackage(off, buf).replace('/', '.');
|
||||
off += 2;
|
||||
|
||||
int opens_flags = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
Set<Opens.Modifier> mods;
|
||||
if (opens_flags == 0) {
|
||||
mods = Collections.emptySet();
|
||||
} else {
|
||||
mods = new HashSet<>();
|
||||
if ((opens_flags & ACC_SYNTHETIC) != 0)
|
||||
mods.add(Opens.Modifier.SYNTHETIC);
|
||||
if ((opens_flags & ACC_MANDATED) != 0)
|
||||
mods.add(Opens.Modifier.MANDATED);
|
||||
}
|
||||
|
||||
int opens_to_count = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
if (opens_to_count > 0) {
|
||||
Set<String> targets = new HashSet<>();
|
||||
for (int j=0; j<opens_to_count; j++) {
|
||||
String t = cr.readModule(off, buf);
|
||||
off += 2;
|
||||
targets.add(t);
|
||||
}
|
||||
builder.opens(mods, pkg, targets);
|
||||
} else {
|
||||
builder.opens(mods, pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// uses_count and uses_index[uses_count]
|
||||
int uses_count = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
if (uses_count > 0) {
|
||||
for (int i=0; i<uses_count; i++) {
|
||||
String sn = cr.readClass(off, buf).replace('/', '.');
|
||||
builder.uses(sn);
|
||||
off += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// provides_count and provides[provides_count]
|
||||
int provides_count = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
if (provides_count > 0) {
|
||||
for (int i=0; i<provides_count; i++) {
|
||||
String service = cr.readClass(off, buf).replace('/', '.');
|
||||
off += 2;
|
||||
int with_count = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
List<String> providers = new ArrayList<>();
|
||||
for (int j=0; j<with_count; j++) {
|
||||
String cn = cr.readClass(off, buf).replace('/', '.');
|
||||
off += 2;
|
||||
providers.add(cn);
|
||||
}
|
||||
builder.provides(service, providers);
|
||||
}
|
||||
}
|
||||
|
||||
return new ModuleAttribute(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteVector write(ClassWriter cw,
|
||||
byte[] code,
|
||||
int len,
|
||||
int maxStack,
|
||||
int maxLocals)
|
||||
{
|
||||
assert descriptor != null;
|
||||
ByteVector attr = new ByteVector();
|
||||
|
||||
// module_name
|
||||
String mn = descriptor.name();
|
||||
int module_name_index = cw.newModule(mn);
|
||||
attr.putShort(module_name_index);
|
||||
|
||||
// module_flags
|
||||
Set<ModuleDescriptor.Modifier> modifiers = descriptor.modifiers();
|
||||
int module_flags = 0;
|
||||
if (modifiers.contains(ModuleDescriptor.Modifier.OPEN))
|
||||
module_flags |= ACC_OPEN;
|
||||
if (modifiers.contains(ModuleDescriptor.Modifier.SYNTHETIC))
|
||||
module_flags |= ACC_SYNTHETIC;
|
||||
if (modifiers.contains(ModuleDescriptor.Modifier.MANDATED))
|
||||
module_flags |= ACC_MANDATED;
|
||||
attr.putShort(module_flags);
|
||||
|
||||
// module_version
|
||||
String vs = descriptor.rawVersion().orElse(null);
|
||||
if (vs == null) {
|
||||
attr.putShort(0);
|
||||
} else {
|
||||
int module_version_index = cw.newUTF8(vs);
|
||||
attr.putShort(module_version_index);
|
||||
}
|
||||
|
||||
// requires_count
|
||||
attr.putShort(descriptor.requires().size());
|
||||
|
||||
// requires[requires_count]
|
||||
for (Requires r : descriptor.requires()) {
|
||||
int requires_index = cw.newModule(r.name());
|
||||
attr.putShort(requires_index);
|
||||
|
||||
int requires_flags = 0;
|
||||
if (r.modifiers().contains(Requires.Modifier.TRANSITIVE))
|
||||
requires_flags |= ACC_TRANSITIVE;
|
||||
if (r.modifiers().contains(Requires.Modifier.STATIC))
|
||||
requires_flags |= ACC_STATIC_PHASE;
|
||||
if (r.modifiers().contains(Requires.Modifier.SYNTHETIC))
|
||||
requires_flags |= ACC_SYNTHETIC;
|
||||
if (r.modifiers().contains(Requires.Modifier.MANDATED))
|
||||
requires_flags |= ACC_MANDATED;
|
||||
attr.putShort(requires_flags);
|
||||
|
||||
int requires_version_index;
|
||||
vs = r.rawCompiledVersion().orElse(null);
|
||||
if (vs == null) {
|
||||
requires_version_index = 0;
|
||||
} else {
|
||||
requires_version_index = cw.newUTF8(vs);
|
||||
}
|
||||
attr.putShort(requires_version_index);
|
||||
}
|
||||
|
||||
// exports_count and exports[exports_count];
|
||||
attr.putShort(descriptor.exports().size());
|
||||
for (Exports e : descriptor.exports()) {
|
||||
String pkg = e.source().replace('.', '/');
|
||||
attr.putShort(cw.newPackage(pkg));
|
||||
|
||||
int exports_flags = 0;
|
||||
if (e.modifiers().contains(Exports.Modifier.SYNTHETIC))
|
||||
exports_flags |= ACC_SYNTHETIC;
|
||||
if (e.modifiers().contains(Exports.Modifier.MANDATED))
|
||||
exports_flags |= ACC_MANDATED;
|
||||
attr.putShort(exports_flags);
|
||||
|
||||
if (e.isQualified()) {
|
||||
Set<String> ts = e.targets();
|
||||
attr.putShort(ts.size());
|
||||
ts.forEach(target -> attr.putShort(cw.newModule(target)));
|
||||
} else {
|
||||
attr.putShort(0);
|
||||
}
|
||||
}
|
||||
|
||||
// opens_counts and opens[opens_counts]
|
||||
attr.putShort(descriptor.opens().size());
|
||||
for (Opens obj : descriptor.opens()) {
|
||||
String pkg = obj.source().replace('.', '/');
|
||||
attr.putShort(cw.newPackage(pkg));
|
||||
|
||||
int opens_flags = 0;
|
||||
if (obj.modifiers().contains(Opens.Modifier.SYNTHETIC))
|
||||
opens_flags |= ACC_SYNTHETIC;
|
||||
if (obj.modifiers().contains(Opens.Modifier.MANDATED))
|
||||
opens_flags |= ACC_MANDATED;
|
||||
attr.putShort(opens_flags);
|
||||
|
||||
if (obj.isQualified()) {
|
||||
Set<String> ts = obj.targets();
|
||||
attr.putShort(ts.size());
|
||||
ts.forEach(target -> attr.putShort(cw.newModule(target)));
|
||||
} else {
|
||||
attr.putShort(0);
|
||||
}
|
||||
}
|
||||
|
||||
// uses_count and uses_index[uses_count]
|
||||
if (descriptor.uses().isEmpty()) {
|
||||
attr.putShort(0);
|
||||
} else {
|
||||
attr.putShort(descriptor.uses().size());
|
||||
for (String s : descriptor.uses()) {
|
||||
String service = s.replace('.', '/');
|
||||
int index = cw.newClass(service);
|
||||
attr.putShort(index);
|
||||
}
|
||||
}
|
||||
|
||||
// provides_count and provides[provides_count]
|
||||
if (descriptor.provides().isEmpty()) {
|
||||
attr.putShort(0);
|
||||
} else {
|
||||
attr.putShort(descriptor.provides().size());
|
||||
for (Provides p : descriptor.provides()) {
|
||||
String service = p.service().replace('.', '/');
|
||||
attr.putShort(cw.newClass(service));
|
||||
int with_count = p.providers().size();
|
||||
attr.putShort(with_count);
|
||||
for (String provider : p.providers()) {
|
||||
attr.putShort(cw.newClass(provider.replace('.', '/')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ModulePackages attribute.
|
||||
*
|
||||
* <pre> {@code
|
||||
*
|
||||
* ModulePackages_attribute {
|
||||
* // index to CONSTANT_utf8_info structure in constant pool representing
|
||||
* // the string "ModulePackages"
|
||||
* u2 attribute_name_index;
|
||||
* u4 attribute_length;
|
||||
*
|
||||
* // the number of entries in the packages table
|
||||
* u2 packages_count;
|
||||
* { // index to CONSTANT_Package_info structure with the package name
|
||||
* u2 package_index
|
||||
* } packages[package_count];
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
public static class ModulePackagesAttribute extends Attribute {
|
||||
private final Set<String> packages;
|
||||
|
||||
public ModulePackagesAttribute(Set<String> packages) {
|
||||
super(MODULE_PACKAGES);
|
||||
this.packages = packages;
|
||||
}
|
||||
|
||||
public ModulePackagesAttribute() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attribute read(ClassReader cr,
|
||||
int off,
|
||||
int len,
|
||||
char[] buf,
|
||||
int codeOff,
|
||||
Label[] labels)
|
||||
{
|
||||
// package count
|
||||
int package_count = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
|
||||
// packages
|
||||
Set<String> packages = new HashSet<>();
|
||||
for (int i=0; i<package_count; i++) {
|
||||
String pkg = cr.readPackage(off, buf).replace('/', '.');
|
||||
packages.add(pkg);
|
||||
off += 2;
|
||||
}
|
||||
|
||||
return new ModulePackagesAttribute(packages);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteVector write(ClassWriter cw,
|
||||
byte[] code,
|
||||
int len,
|
||||
int maxStack,
|
||||
int maxLocals)
|
||||
{
|
||||
assert packages != null;
|
||||
|
||||
ByteVector attr = new ByteVector();
|
||||
|
||||
// package_count
|
||||
attr.putShort(packages.size());
|
||||
|
||||
// packages
|
||||
packages.stream()
|
||||
.map(p -> p.replace('.', '/'))
|
||||
.forEach(p -> attr.putShort(cw.newPackage(p)));
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* ModuleMainClass attribute.
|
||||
*
|
||||
* <pre> {@code
|
||||
*
|
||||
* MainClass_attribute {
|
||||
* // index to CONSTANT_utf8_info structure in constant pool representing
|
||||
* // the string "ModuleMainClass"
|
||||
* u2 attribute_name_index;
|
||||
* u4 attribute_length;
|
||||
*
|
||||
* // index to CONSTANT_Class_info structure with the main class name
|
||||
* u2 main_class_index;
|
||||
* }
|
||||
*
|
||||
* } </pre>
|
||||
*/
|
||||
public static class ModuleMainClassAttribute extends Attribute {
|
||||
private final String mainClass;
|
||||
|
||||
public ModuleMainClassAttribute(String mainClass) {
|
||||
super(MODULE_MAIN_CLASS);
|
||||
this.mainClass = mainClass;
|
||||
}
|
||||
|
||||
public ModuleMainClassAttribute() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attribute read(ClassReader cr,
|
||||
int off,
|
||||
int len,
|
||||
char[] buf,
|
||||
int codeOff,
|
||||
Label[] labels)
|
||||
{
|
||||
String value = cr.readClass(off, buf).replace('/', '.');
|
||||
return new ModuleMainClassAttribute(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteVector write(ClassWriter cw,
|
||||
byte[] code,
|
||||
int len,
|
||||
int maxStack,
|
||||
int maxLocals)
|
||||
{
|
||||
ByteVector attr = new ByteVector();
|
||||
int index = cw.newClass(mainClass.replace('.', '/'));
|
||||
attr.putShort(index);
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ModuleTarget attribute.
|
||||
*
|
||||
* <pre> {@code
|
||||
*
|
||||
* TargetPlatform_attribute {
|
||||
* // index to CONSTANT_utf8_info structure in constant pool representing
|
||||
* // the string "ModuleTarget"
|
||||
* u2 attribute_name_index;
|
||||
* u4 attribute_length;
|
||||
*
|
||||
* // index to CONSTANT_utf8_info structure with the target platform
|
||||
* u2 target_platform_index;
|
||||
* }
|
||||
*
|
||||
* } </pre>
|
||||
*/
|
||||
public static class ModuleTargetAttribute extends Attribute {
|
||||
private final String targetPlatform;
|
||||
|
||||
public ModuleTargetAttribute(String targetPlatform) {
|
||||
super(MODULE_TARGET);
|
||||
this.targetPlatform = targetPlatform;
|
||||
}
|
||||
|
||||
public ModuleTargetAttribute() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public String targetPlatform() {
|
||||
return targetPlatform;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attribute read(ClassReader cr,
|
||||
int off,
|
||||
int len,
|
||||
char[] buf,
|
||||
int codeOff,
|
||||
Label[] labels)
|
||||
{
|
||||
|
||||
String targetPlatform = null;
|
||||
|
||||
int target_platform_index = cr.readUnsignedShort(off);
|
||||
if (target_platform_index != 0)
|
||||
targetPlatform = cr.readUTF8(off, buf);
|
||||
off += 2;
|
||||
|
||||
return new ModuleTargetAttribute(targetPlatform);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteVector write(ClassWriter cw,
|
||||
byte[] code,
|
||||
int len,
|
||||
int maxStack,
|
||||
int maxLocals)
|
||||
{
|
||||
ByteVector attr = new ByteVector();
|
||||
|
||||
int target_platform_index = 0;
|
||||
if (targetPlatform != null && targetPlatform.length() > 0)
|
||||
target_platform_index = cw.newUTF8(targetPlatform);
|
||||
attr.putShort(target_platform_index);
|
||||
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ModuleHashes attribute.
|
||||
*
|
||||
* <pre> {@code
|
||||
*
|
||||
* ModuleHashes_attribute {
|
||||
* // index to CONSTANT_utf8_info structure in constant pool representing
|
||||
* // the string "ModuleHashes"
|
||||
* u2 attribute_name_index;
|
||||
* u4 attribute_length;
|
||||
*
|
||||
* // index to CONSTANT_utf8_info structure with algorithm name
|
||||
* u2 algorithm_index;
|
||||
*
|
||||
* // the number of entries in the hashes table
|
||||
* u2 hashes_count;
|
||||
* { u2 module_name_index (index to CONSTANT_Module_info structure)
|
||||
* u2 hash_length;
|
||||
* u1 hash[hash_length];
|
||||
* } hashes[hashes_count];
|
||||
*
|
||||
* } </pre>
|
||||
*/
|
||||
static class ModuleHashesAttribute extends Attribute {
|
||||
private final ModuleHashes hashes;
|
||||
|
||||
ModuleHashesAttribute(ModuleHashes hashes) {
|
||||
super(MODULE_HASHES);
|
||||
this.hashes = hashes;
|
||||
}
|
||||
|
||||
ModuleHashesAttribute() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attribute read(ClassReader cr,
|
||||
int off,
|
||||
int len,
|
||||
char[] buf,
|
||||
int codeOff,
|
||||
Label[] labels)
|
||||
{
|
||||
String algorithm = cr.readUTF8(off, buf);
|
||||
off += 2;
|
||||
|
||||
int hashes_count = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
|
||||
Map<String, byte[]> map = new HashMap<>();
|
||||
for (int i=0; i<hashes_count; i++) {
|
||||
String mn = cr.readModule(off, buf);
|
||||
off += 2;
|
||||
|
||||
int hash_length = cr.readUnsignedShort(off);
|
||||
off += 2;
|
||||
byte[] hash = new byte[hash_length];
|
||||
for (int j=0; j<hash_length; j++) {
|
||||
hash[j] = (byte) (0xff & cr.readByte(off+j));
|
||||
}
|
||||
off += hash_length;
|
||||
|
||||
map.put(mn, hash);
|
||||
}
|
||||
|
||||
ModuleHashes hashes = new ModuleHashes(algorithm, map);
|
||||
|
||||
return new ModuleHashesAttribute(hashes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteVector write(ClassWriter cw,
|
||||
byte[] code,
|
||||
int len,
|
||||
int maxStack,
|
||||
int maxLocals)
|
||||
{
|
||||
ByteVector attr = new ByteVector();
|
||||
|
||||
int index = cw.newUTF8(hashes.algorithm());
|
||||
attr.putShort(index);
|
||||
|
||||
Set<String> names = hashes.names();
|
||||
attr.putShort(names.size());
|
||||
|
||||
for (String mn : names) {
|
||||
byte[] hash = hashes.hashFor(mn);
|
||||
assert hash != null;
|
||||
attr.putShort(cw.newModule(mn));
|
||||
|
||||
attr.putShort(hash.length);
|
||||
for (byte b: hash) {
|
||||
attr.putByte(b);
|
||||
}
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ModuleResolution_attribute {
|
||||
* u2 attribute_name_index; // "ModuleResolution"
|
||||
* u4 attribute_length; // 2
|
||||
* u2 resolution_flags;
|
||||
*
|
||||
* The value of the resolution_flags item is a mask of flags used to denote
|
||||
* properties of module resolution. The flags are as follows:
|
||||
*
|
||||
* // Optional
|
||||
* 0x0001 (DO_NOT_RESOLVE_BY_DEFAULT)
|
||||
*
|
||||
* // At most one of:
|
||||
* 0x0002 (WARN_DEPRECATED)
|
||||
* 0x0004 (WARN_DEPRECATED_FOR_REMOVAL)
|
||||
* 0x0008 (WARN_INCUBATING)
|
||||
*/
|
||||
static class ModuleResolutionAttribute extends Attribute {
|
||||
private final int value;
|
||||
|
||||
ModuleResolutionAttribute() {
|
||||
super(MODULE_RESOLUTION);
|
||||
value = 0;
|
||||
}
|
||||
|
||||
ModuleResolutionAttribute(int value) {
|
||||
super(MODULE_RESOLUTION);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attribute read(ClassReader cr,
|
||||
int off,
|
||||
int len,
|
||||
char[] buf,
|
||||
int codeOff,
|
||||
Label[] labels)
|
||||
{
|
||||
int flags = cr.readUnsignedShort(off);
|
||||
return new ModuleResolutionAttribute(flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ByteVector write(ClassWriter cw,
|
||||
byte[] code,
|
||||
int len,
|
||||
int maxStack,
|
||||
int maxLocals)
|
||||
{
|
||||
ByteVector attr = new ByteVector();
|
||||
attr.putShort(value);
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
}
|
@ -31,18 +31,18 @@ import java.io.OutputStream;
|
||||
import java.lang.module.ModuleDescriptor.Version;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.Attribute;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.ModuleVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
||||
import static jdk.internal.module.ClassFileAttributes.*;
|
||||
import jdk.internal.org.objectweb.asm.commons.ModuleHashesAttribute;
|
||||
import jdk.internal.org.objectweb.asm.commons.ModuleResolutionAttribute;
|
||||
import jdk.internal.org.objectweb.asm.commons.ModuleTargetAttribute;
|
||||
|
||||
/**
|
||||
* Utility class to extend a module-info.class with additional attributes.
|
||||
@ -132,43 +132,6 @@ public final class ModuleInfoExtender {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* A ClassVisitor that supports adding class file attributes. If an
|
||||
* attribute already exists then the first occurrence of the attribute
|
||||
* is replaced.
|
||||
*/
|
||||
private static class AttributeAddingClassVisitor extends ClassVisitor {
|
||||
private Map<String, Attribute> attrs = new HashMap<>();
|
||||
|
||||
AttributeAddingClassVisitor(int api, ClassVisitor cv) {
|
||||
super(api, cv);
|
||||
}
|
||||
|
||||
void addAttribute(Attribute attr) {
|
||||
attrs.put(attr.type, attr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitAttribute(Attribute attr) {
|
||||
String name = attr.type;
|
||||
Attribute replacement = attrs.get(name);
|
||||
if (replacement != null) {
|
||||
attr = replacement;
|
||||
attrs.remove(name);
|
||||
}
|
||||
super.visitAttribute(attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds any remaining attributes that weren't replaced to the
|
||||
* class file.
|
||||
*/
|
||||
void finish() {
|
||||
attrs.values().forEach(a -> super.visitAttribute(a));
|
||||
attrs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the modified module-info.class to the given output stream.
|
||||
* Once this method has been called then the Extender object should
|
||||
@ -185,38 +148,86 @@ public final class ModuleInfoExtender {
|
||||
* be discarded.
|
||||
*/
|
||||
public byte[] toByteArray() throws IOException {
|
||||
ClassWriter cw
|
||||
= new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
|
||||
AttributeAddingClassVisitor cv
|
||||
= new AttributeAddingClassVisitor(Opcodes.ASM5, cw);
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
|
||||
ClassReader cr = new ClassReader(in);
|
||||
|
||||
if (packages != null)
|
||||
cv.addAttribute(new ModulePackagesAttribute(packages));
|
||||
if (mainClass != null)
|
||||
cv.addAttribute(new ModuleMainClassAttribute(mainClass));
|
||||
if (targetPlatform != null)
|
||||
cv.addAttribute(new ModuleTargetAttribute(targetPlatform));
|
||||
if (hashes != null)
|
||||
cv.addAttribute(new ModuleHashesAttribute(hashes));
|
||||
if (moduleResolution != null)
|
||||
cv.addAttribute(new ModuleResolutionAttribute(moduleResolution.value()));
|
||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM6, cw) {
|
||||
@Override
|
||||
public ModuleVisitor visitModule(String name, int flags, String version) {
|
||||
Version v = ModuleInfoExtender.this.version;
|
||||
String vs = (v != null) ? v.toString() : version;
|
||||
ModuleVisitor mv = super.visitModule(name, flags, vs);
|
||||
|
||||
// ModuleMainClass attribute
|
||||
if (mainClass != null) {
|
||||
mv.visitMainClass(mainClass.replace('.', '/'));
|
||||
}
|
||||
|
||||
// ModulePackages attribute
|
||||
if (packages != null) {
|
||||
packages.forEach(pn -> mv.visitPackage(pn.replace('.', '/')));
|
||||
}
|
||||
|
||||
return new ModuleVisitor(Opcodes.ASM6, mv) {
|
||||
public void visitMainClass(String existingMainClass) {
|
||||
// skip main class if there is a new value
|
||||
if (mainClass == null) {
|
||||
super.visitMainClass(existingMainClass);
|
||||
}
|
||||
}
|
||||
public void visitPackage(String existingPackage) {
|
||||
// skip packages if there is a new set of packages
|
||||
if (packages == null) {
|
||||
super.visitPackage(existingPackage);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@Override
|
||||
public void visitAttribute(Attribute attr) {
|
||||
String name = attr.type;
|
||||
// drop existing attributes if there are replacements
|
||||
if (name.equals(ClassFileConstants.MODULE_TARGET)
|
||||
&& targetPlatform != null)
|
||||
return;
|
||||
if (name.equals(ClassFileConstants.MODULE_RESOLUTION)
|
||||
&& moduleResolution != null)
|
||||
return;
|
||||
if (name.equals(ClassFileConstants.MODULE_HASHES)
|
||||
&& hashes != null)
|
||||
return;
|
||||
|
||||
super.visitAttribute(attr);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
List<Attribute> attrs = new ArrayList<>();
|
||||
|
||||
// prototypes of attributes that should be parsed
|
||||
attrs.add(new ModuleAttribute(version));
|
||||
attrs.add(new ModulePackagesAttribute());
|
||||
attrs.add(new ModuleMainClassAttribute());
|
||||
attrs.add(new ModuleTargetAttribute());
|
||||
attrs.add(new ModuleResolutionAttribute());
|
||||
attrs.add(new ModuleHashesAttribute());
|
||||
|
||||
cr.accept(cv, attrs.toArray(new Attribute[0]), 0);
|
||||
|
||||
// add any attributes that didn't replace previous attributes
|
||||
cv.finish();
|
||||
// add ModuleTarget, ModuleResolution and ModuleHashes attributes
|
||||
if (targetPlatform != null) {
|
||||
cw.visitAttribute(new ModuleTargetAttribute(targetPlatform));
|
||||
}
|
||||
if (moduleResolution != null) {
|
||||
int flags = moduleResolution.value();
|
||||
cw.visitAttribute(new ModuleResolutionAttribute(flags));
|
||||
}
|
||||
if (hashes != null) {
|
||||
String algorithm = hashes.algorithm();
|
||||
List<String> names = new ArrayList<>();
|
||||
List<byte[]> values = new ArrayList<>();
|
||||
for (String name : hashes.names()) {
|
||||
names.add(name);
|
||||
values.add(hashes.hashFor(name));
|
||||
}
|
||||
cw.visitAttribute(new ModuleHashesAttribute(algorithm, names, values));
|
||||
}
|
||||
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
@ -28,13 +28,14 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.ModuleVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
||||
import static jdk.internal.module.ClassFileAttributes.*;
|
||||
import static jdk.internal.module.ClassFileConstants.ACC_MODULE;
|
||||
import jdk.internal.org.objectweb.asm.commons.ModuleTargetAttribute;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
* Utility class to write a ModuleDescriptor as a module-info.class.
|
||||
@ -42,6 +43,35 @@ import static jdk.internal.module.ClassFileConstants.ACC_MODULE;
|
||||
|
||||
public final class ModuleInfoWriter {
|
||||
|
||||
private static final Map<ModuleDescriptor.Modifier, Integer>
|
||||
MODULE_MODS_TO_FLAGS = Map.of(
|
||||
ModuleDescriptor.Modifier.OPEN, ACC_OPEN,
|
||||
ModuleDescriptor.Modifier.SYNTHETIC, ACC_SYNTHETIC,
|
||||
ModuleDescriptor.Modifier.MANDATED, ACC_MANDATED
|
||||
);
|
||||
|
||||
private static final Map<ModuleDescriptor.Requires.Modifier, Integer>
|
||||
REQUIRES_MODS_TO_FLAGS = Map.of(
|
||||
ModuleDescriptor.Requires.Modifier.TRANSITIVE, ACC_TRANSITIVE,
|
||||
ModuleDescriptor.Requires.Modifier.STATIC, ACC_STATIC_PHASE,
|
||||
ModuleDescriptor.Requires.Modifier.SYNTHETIC, ACC_SYNTHETIC,
|
||||
ModuleDescriptor.Requires.Modifier.MANDATED, ACC_MANDATED
|
||||
);
|
||||
|
||||
private static final Map<ModuleDescriptor.Exports.Modifier, Integer>
|
||||
EXPORTS_MODS_TO_FLAGS = Map.of(
|
||||
ModuleDescriptor.Exports.Modifier.SYNTHETIC, ACC_SYNTHETIC,
|
||||
ModuleDescriptor.Exports.Modifier.MANDATED, ACC_MANDATED
|
||||
);
|
||||
|
||||
private static final Map<ModuleDescriptor.Opens.Modifier, Integer>
|
||||
OPENS_MODS_TO_FLAGS = Map.of(
|
||||
ModuleDescriptor.Opens.Modifier.SYNTHETIC, ACC_SYNTHETIC,
|
||||
ModuleDescriptor.Opens.Modifier.MANDATED, ACC_MANDATED
|
||||
);
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
private ModuleInfoWriter() { }
|
||||
|
||||
/**
|
||||
@ -50,24 +80,75 @@ public final class ModuleInfoWriter {
|
||||
*/
|
||||
private static byte[] toModuleInfo(ModuleDescriptor md, ModuleTarget target) {
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
cw.visit(Opcodes.V1_9, ACC_MODULE, "module-info", null, null, null);
|
||||
cw.visitAttribute(new ModuleAttribute(md));
|
||||
cw.visit(Opcodes.V9, ACC_MODULE, "module-info", null, null, null);
|
||||
|
||||
// for tests: write the ModulePackages attribute when there are packages
|
||||
// that aren't exported or open
|
||||
int moduleFlags = md.modifiers().stream()
|
||||
.map(MODULE_MODS_TO_FLAGS::get)
|
||||
.reduce(0, (x, y) -> (x | y));
|
||||
String vs = md.rawVersion().orElse(null);
|
||||
ModuleVisitor mv = cw.visitModule(md.name(), moduleFlags, vs);
|
||||
|
||||
// requires
|
||||
for (ModuleDescriptor.Requires r : md.requires()) {
|
||||
int flags = r.modifiers().stream()
|
||||
.map(REQUIRES_MODS_TO_FLAGS::get)
|
||||
.reduce(0, (x, y) -> (x | y));
|
||||
vs = r.rawCompiledVersion().orElse(null);
|
||||
mv.visitRequire(r.name(), flags, vs);
|
||||
}
|
||||
|
||||
// exports
|
||||
for (ModuleDescriptor.Exports e : md.exports()) {
|
||||
int flags = e.modifiers().stream()
|
||||
.map(EXPORTS_MODS_TO_FLAGS::get)
|
||||
.reduce(0, (x, y) -> (x | y));
|
||||
String[] targets = e.targets().toArray(EMPTY_STRING_ARRAY);
|
||||
mv.visitExport(e.source().replace('.', '/'), flags, targets);
|
||||
}
|
||||
|
||||
// opens
|
||||
for (ModuleDescriptor.Opens opens : md.opens()) {
|
||||
int flags = opens.modifiers().stream()
|
||||
.map(OPENS_MODS_TO_FLAGS::get)
|
||||
.reduce(0, (x, y) -> (x | y));
|
||||
String[] targets = opens.targets().toArray(EMPTY_STRING_ARRAY);
|
||||
mv.visitOpen(opens.source().replace('.', '/'), flags, targets);
|
||||
}
|
||||
|
||||
// uses
|
||||
md.uses().stream().map(sn -> sn.replace('.', '/')).forEach(mv::visitUse);
|
||||
|
||||
// provides
|
||||
for (ModuleDescriptor.Provides p : md.provides()) {
|
||||
mv.visitProvide(p.service().replace('.', '/'),
|
||||
p.providers()
|
||||
.stream()
|
||||
.map(pn -> pn.replace('.', '/'))
|
||||
.toArray(String[]::new));
|
||||
}
|
||||
|
||||
// add the ModulePackages attribute when there are packages that aren't
|
||||
// exported or open
|
||||
Stream<String> exported = md.exports().stream()
|
||||
.map(ModuleDescriptor.Exports::source);
|
||||
Stream<String> open = md.opens().stream()
|
||||
.map(ModuleDescriptor.Opens::source);
|
||||
long exportedOrOpen = Stream.concat(exported, open).distinct().count();
|
||||
if (md.packages().size() > exportedOrOpen)
|
||||
cw.visitAttribute(new ModulePackagesAttribute(md.packages()));
|
||||
if (md.packages().size() > exportedOrOpen) {
|
||||
md.packages().stream()
|
||||
.map(pn -> pn.replace('.', '/'))
|
||||
.forEach(mv::visitPackage);
|
||||
}
|
||||
|
||||
// write ModuleMainClass if the module has a main class
|
||||
md.mainClass().ifPresent(mc -> cw.visitAttribute(new ModuleMainClassAttribute(mc)));
|
||||
// ModuleMainClass attribute
|
||||
md.mainClass()
|
||||
.map(mc -> mc.replace('.', '/'))
|
||||
.ifPresent(mv::visitMainClass);
|
||||
|
||||
// write ModuleTarget if there is a target platform
|
||||
if (target != null) {
|
||||
mv.visitEnd();
|
||||
|
||||
// write ModuleTarget attribute if there is a target platform
|
||||
if (target != null && target.targetPlatform().length() > 0) {
|
||||
cw.visitAttribute(new ModuleTargetAttribute(target.targetPlatform()));
|
||||
}
|
||||
|
||||
|
@ -58,8 +58,6 @@ import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jdk.internal.module.Checks;
|
||||
import jdk.internal.module.ClassFileAttributes;
|
||||
import jdk.internal.module.ClassFileConstants;
|
||||
import jdk.internal.module.DefaultRoots;
|
||||
import jdk.internal.module.IllegalAccessMaps;
|
||||
import jdk.internal.module.ModuleHashes;
|
||||
@ -68,13 +66,13 @@ import jdk.internal.module.ModuleInfoExtender;
|
||||
import jdk.internal.module.ModuleReferenceImpl;
|
||||
import jdk.internal.module.ModuleResolution;
|
||||
import jdk.internal.module.ModuleTarget;
|
||||
import jdk.internal.org.objectweb.asm.Attribute;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ModuleVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
import jdk.tools.jlink.internal.ModuleSorter;
|
||||
@ -435,24 +433,25 @@ public final class SystemModulesPlugin implements Plugin {
|
||||
}
|
||||
|
||||
boolean hasModulePackages() throws IOException {
|
||||
Set<String> attrTypes = new HashSet<>();
|
||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM5) {
|
||||
Set<String> packages = new HashSet<>();
|
||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM6) {
|
||||
@Override
|
||||
public void visitAttribute(Attribute attr) {
|
||||
attrTypes.add(attr.type);
|
||||
public ModuleVisitor visitModule(String name,
|
||||
int flags,
|
||||
String version) {
|
||||
return new ModuleVisitor(Opcodes.ASM6) {
|
||||
public void visitPackage(String pn) {
|
||||
packages.add(pn);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// prototype of attributes that should be parsed
|
||||
Attribute[] attrs = new Attribute[] {
|
||||
new ClassFileAttributes.ModulePackagesAttribute()
|
||||
};
|
||||
|
||||
try (InputStream in = getInputStream()) {
|
||||
// parse module-info.class
|
||||
ClassReader cr = new ClassReader(in);
|
||||
cr.accept(cv, attrs, 0);
|
||||
return attrTypes.contains(ClassFileConstants.MODULE_PACKAGES);
|
||||
cr.accept(cv, 0);
|
||||
return packages.size() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user