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.BuiltinClassLoader;
|
||||||
import jdk.internal.loader.BootLoader;
|
import jdk.internal.loader.BootLoader;
|
||||||
import jdk.internal.loader.ClassLoaders;
|
import jdk.internal.loader.ClassLoaders;
|
||||||
import jdk.internal.misc.JavaLangAccess;
|
|
||||||
import jdk.internal.misc.SharedSecrets;
|
|
||||||
import jdk.internal.module.IllegalAccessLogger;
|
import jdk.internal.module.IllegalAccessLogger;
|
||||||
import jdk.internal.module.ModuleLoaderMap;
|
import jdk.internal.module.ModuleLoaderMap;
|
||||||
import jdk.internal.module.ServicesCatalog;
|
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.ClassReader;
|
||||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||||
|
import jdk.internal.org.objectweb.asm.ModuleVisitor;
|
||||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||||
import jdk.internal.reflect.CallerSensitive;
|
import jdk.internal.reflect.CallerSensitive;
|
||||||
import jdk.internal.reflect.Reflection;
|
import jdk.internal.reflect.Reflection;
|
||||||
@ -1432,7 +1431,7 @@ public final class Module implements AnnotatedElement {
|
|||||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||||
+ ClassWriter.COMPUTE_FRAMES);
|
+ ClassWriter.COMPUTE_FRAMES);
|
||||||
|
|
||||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, cw) {
|
ClassVisitor cv = new ClassVisitor(Opcodes.ASM6, cw) {
|
||||||
@Override
|
@Override
|
||||||
public void visit(int version,
|
public void visit(int version,
|
||||||
int access,
|
int access,
|
||||||
@ -1458,6 +1457,11 @@ public final class Module implements AnnotatedElement {
|
|||||||
public void visitAttribute(Attribute attr) {
|
public void visitAttribute(Attribute attr) {
|
||||||
// drop non-annotation attributes
|
// drop non-annotation attributes
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public ModuleVisitor visitModule(String name, int flags, String version) {
|
||||||
|
// drop Module attribute
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ClassReader cr = new ClassReader(in);
|
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.lang.module.ModuleDescriptor.Version;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import jdk.internal.org.objectweb.asm.Attribute;
|
import jdk.internal.org.objectweb.asm.Attribute;
|
||||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||||
|
import jdk.internal.org.objectweb.asm.ModuleVisitor;
|
||||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||||
|
import jdk.internal.org.objectweb.asm.commons.ModuleHashesAttribute;
|
||||||
import static jdk.internal.module.ClassFileAttributes.*;
|
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.
|
* Utility class to extend a module-info.class with additional attributes.
|
||||||
@ -132,43 +132,6 @@ public final class ModuleInfoExtender {
|
|||||||
return this;
|
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.
|
* Outputs the modified module-info.class to the given output stream.
|
||||||
* Once this method has been called then the Extender object should
|
* Once this method has been called then the Extender object should
|
||||||
@ -185,38 +148,86 @@ public final class ModuleInfoExtender {
|
|||||||
* be discarded.
|
* be discarded.
|
||||||
*/
|
*/
|
||||||
public byte[] toByteArray() throws IOException {
|
public byte[] toByteArray() throws IOException {
|
||||||
ClassWriter cw
|
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||||
= new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
+ ClassWriter.COMPUTE_FRAMES);
|
||||||
|
|
||||||
AttributeAddingClassVisitor cv
|
|
||||||
= new AttributeAddingClassVisitor(Opcodes.ASM5, cw);
|
|
||||||
|
|
||||||
ClassReader cr = new ClassReader(in);
|
ClassReader cr = new ClassReader(in);
|
||||||
|
|
||||||
if (packages != null)
|
ClassVisitor cv = new ClassVisitor(Opcodes.ASM6, cw) {
|
||||||
cv.addAttribute(new ModulePackagesAttribute(packages));
|
@Override
|
||||||
if (mainClass != null)
|
public ModuleVisitor visitModule(String name, int flags, String version) {
|
||||||
cv.addAttribute(new ModuleMainClassAttribute(mainClass));
|
Version v = ModuleInfoExtender.this.version;
|
||||||
if (targetPlatform != null)
|
String vs = (v != null) ? v.toString() : version;
|
||||||
cv.addAttribute(new ModuleTargetAttribute(targetPlatform));
|
ModuleVisitor mv = super.visitModule(name, flags, vs);
|
||||||
if (hashes != null)
|
|
||||||
cv.addAttribute(new ModuleHashesAttribute(hashes));
|
// ModuleMainClass attribute
|
||||||
if (moduleResolution != null)
|
if (mainClass != null) {
|
||||||
cv.addAttribute(new ModuleResolutionAttribute(moduleResolution.value()));
|
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<>();
|
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 ModuleTargetAttribute());
|
||||||
|
attrs.add(new ModuleResolutionAttribute());
|
||||||
attrs.add(new ModuleHashesAttribute());
|
attrs.add(new ModuleHashesAttribute());
|
||||||
|
|
||||||
cr.accept(cv, attrs.toArray(new Attribute[0]), 0);
|
cr.accept(cv, attrs.toArray(new Attribute[0]), 0);
|
||||||
|
|
||||||
// add any attributes that didn't replace previous attributes
|
// add ModuleTarget, ModuleResolution and ModuleHashes attributes
|
||||||
cv.finish();
|
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();
|
return cw.toByteArray();
|
||||||
}
|
}
|
||||||
|
@ -28,13 +28,14 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.lang.module.ModuleDescriptor;
|
import java.lang.module.ModuleDescriptor;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||||
|
import jdk.internal.org.objectweb.asm.ModuleVisitor;
|
||||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||||
|
import jdk.internal.org.objectweb.asm.commons.ModuleTargetAttribute;
|
||||||
import static jdk.internal.module.ClassFileAttributes.*;
|
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||||
import static jdk.internal.module.ClassFileConstants.ACC_MODULE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class to write a ModuleDescriptor as a module-info.class.
|
* 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 {
|
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() { }
|
private ModuleInfoWriter() { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,24 +80,75 @@ public final class ModuleInfoWriter {
|
|||||||
*/
|
*/
|
||||||
private static byte[] toModuleInfo(ModuleDescriptor md, ModuleTarget target) {
|
private static byte[] toModuleInfo(ModuleDescriptor md, ModuleTarget target) {
|
||||||
ClassWriter cw = new ClassWriter(0);
|
ClassWriter cw = new ClassWriter(0);
|
||||||
cw.visit(Opcodes.V1_9, ACC_MODULE, "module-info", null, null, null);
|
cw.visit(Opcodes.V9, ACC_MODULE, "module-info", null, null, null);
|
||||||
cw.visitAttribute(new ModuleAttribute(md));
|
|
||||||
|
|
||||||
// for tests: write the ModulePackages attribute when there are packages
|
int moduleFlags = md.modifiers().stream()
|
||||||
// that aren't exported or open
|
.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()
|
Stream<String> exported = md.exports().stream()
|
||||||
.map(ModuleDescriptor.Exports::source);
|
.map(ModuleDescriptor.Exports::source);
|
||||||
Stream<String> open = md.opens().stream()
|
Stream<String> open = md.opens().stream()
|
||||||
.map(ModuleDescriptor.Opens::source);
|
.map(ModuleDescriptor.Opens::source);
|
||||||
long exportedOrOpen = Stream.concat(exported, open).distinct().count();
|
long exportedOrOpen = Stream.concat(exported, open).distinct().count();
|
||||||
if (md.packages().size() > exportedOrOpen)
|
if (md.packages().size() > exportedOrOpen) {
|
||||||
cw.visitAttribute(new ModulePackagesAttribute(md.packages()));
|
md.packages().stream()
|
||||||
|
.map(pn -> pn.replace('.', '/'))
|
||||||
|
.forEach(mv::visitPackage);
|
||||||
|
}
|
||||||
|
|
||||||
// write ModuleMainClass if the module has a main class
|
// ModuleMainClass attribute
|
||||||
md.mainClass().ifPresent(mc -> cw.visitAttribute(new ModuleMainClassAttribute(mc)));
|
md.mainClass()
|
||||||
|
.map(mc -> mc.replace('.', '/'))
|
||||||
|
.ifPresent(mv::visitMainClass);
|
||||||
|
|
||||||
// write ModuleTarget if there is a target platform
|
mv.visitEnd();
|
||||||
if (target != null) {
|
|
||||||
|
// write ModuleTarget attribute if there is a target platform
|
||||||
|
if (target != null && target.targetPlatform().length() > 0) {
|
||||||
cw.visitAttribute(new ModuleTargetAttribute(target.targetPlatform()));
|
cw.visitAttribute(new ModuleTargetAttribute(target.targetPlatform()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,8 +58,6 @@ import java.util.function.Supplier;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import jdk.internal.module.Checks;
|
import jdk.internal.module.Checks;
|
||||||
import jdk.internal.module.ClassFileAttributes;
|
|
||||||
import jdk.internal.module.ClassFileConstants;
|
|
||||||
import jdk.internal.module.DefaultRoots;
|
import jdk.internal.module.DefaultRoots;
|
||||||
import jdk.internal.module.IllegalAccessMaps;
|
import jdk.internal.module.IllegalAccessMaps;
|
||||||
import jdk.internal.module.ModuleHashes;
|
import jdk.internal.module.ModuleHashes;
|
||||||
@ -68,13 +66,13 @@ import jdk.internal.module.ModuleInfoExtender;
|
|||||||
import jdk.internal.module.ModuleReferenceImpl;
|
import jdk.internal.module.ModuleReferenceImpl;
|
||||||
import jdk.internal.module.ModuleResolution;
|
import jdk.internal.module.ModuleResolution;
|
||||||
import jdk.internal.module.ModuleTarget;
|
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.ClassReader;
|
||||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||||
|
import jdk.internal.org.objectweb.asm.ModuleVisitor;
|
||||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||||
|
|
||||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||||
|
|
||||||
import jdk.tools.jlink.internal.ModuleSorter;
|
import jdk.tools.jlink.internal.ModuleSorter;
|
||||||
@ -435,24 +433,25 @@ public final class SystemModulesPlugin implements Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean hasModulePackages() throws IOException {
|
boolean hasModulePackages() throws IOException {
|
||||||
Set<String> attrTypes = new HashSet<>();
|
Set<String> packages = new HashSet<>();
|
||||||
ClassVisitor cv = new ClassVisitor(Opcodes.ASM5) {
|
ClassVisitor cv = new ClassVisitor(Opcodes.ASM6) {
|
||||||
@Override
|
@Override
|
||||||
public void visitAttribute(Attribute attr) {
|
public ModuleVisitor visitModule(String name,
|
||||||
attrTypes.add(attr.type);
|
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()) {
|
try (InputStream in = getInputStream()) {
|
||||||
// parse module-info.class
|
// parse module-info.class
|
||||||
ClassReader cr = new ClassReader(in);
|
ClassReader cr = new ClassReader(in);
|
||||||
cr.accept(cv, attrs, 0);
|
cr.accept(cv, 0);
|
||||||
return attrTypes.contains(ClassFileConstants.MODULE_PACKAGES);
|
return packages.size() > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user