Commit c0f7167d authored by Eric Bruneton's avatar Eric Bruneton

Improve the code quality of the util package.

parent e1cc902d
Pipeline #525 passed with stage
in 10 minutes and 40 seconds
......@@ -26,19 +26,19 @@ import java.util.Map;
import org.objectweb.asm.Label;
/**
* An {@link org.objectweb.asm.Attribute Attribute} that can print the ASM code to create an
* equivalent attribute.
* An {@link org.objectweb.asm.Attribute} that can generate the ASM code to create an equivalent
* attribute.
*
* @author Eugene Kuleshov
*/
public interface ASMifiable {
/**
* Prints the ASM code to create an attribute equal to this attribute.
* Generates the ASM code to create an attribute equal to this attribute.
*
* @param buf a buffer used for printing Java code.
* @param varName name of the variable in a printed code used to store attribute instance.
* @param labelNames map of label instances to their names.
* @param outputBuffer where the generated code must be appended.
* @param visitorVariableName the name of the visitor variable in the produced code.
* @param labelNames the names of the labels in the generated code.
*/
void asmify(StringBuffer buf, String varName, Map<Label, String> labelNames);
void asmify(StringBuffer outputBuffer, String visitorVariableName, Map<Label, String> labelNames);
}
......@@ -38,22 +38,27 @@ import org.objectweb.asm.Type;
*/
public class CheckAnnotationAdapter extends AnnotationVisitor {
private final boolean named;
/**
* Whether the values of the visited annotation are named. AnnotationVisitor instances used for
* annotation default and annotation arrays use unnamed values.
*/
private final boolean useNamedValue;
private boolean end;
/** Whether the {@link #visitEnd} method has been called. */
private boolean visitEndCalled;
public CheckAnnotationAdapter(final AnnotationVisitor av) {
this(av, true);
public CheckAnnotationAdapter(final AnnotationVisitor annotationVisitor) {
this(annotationVisitor, true);
}
CheckAnnotationAdapter(final AnnotationVisitor av, final boolean named) {
super(Opcodes.ASM6, av);
this.named = named;
CheckAnnotationAdapter(final AnnotationVisitor annotationVisitor, final boolean useNamedValues) {
super(Opcodes.ASM6, annotationVisitor);
this.useNamedValue = useNamedValues;
}
@Override
public void visit(final String name, final Object value) {
checkEnd();
checkVisitEndNotCalled();
checkName(name);
if (!(value instanceof Byte
|| value instanceof Boolean
......@@ -75,63 +80,54 @@ public class CheckAnnotationAdapter extends AnnotationVisitor {
|| value instanceof double[])) {
throw new IllegalArgumentException("Invalid annotation value");
}
if (value instanceof Type) {
int sort = ((Type) value).getSort();
if (sort == Type.METHOD) {
throw new IllegalArgumentException("Invalid annotation value");
}
}
if (av != null) {
av.visit(name, value);
if (value instanceof Type && ((Type) value).getSort() == Type.METHOD) {
throw new IllegalArgumentException("Invalid annotation value");
}
super.visit(name, value);
}
@Override
public void visitEnum(final String name, final String desc, final String value) {
checkEnd();
public void visitEnum(final String name, final String descriptor, final String value) {
checkVisitEndNotCalled();
checkName(name);
CheckMethodAdapter.checkDesc(desc, false);
CheckMethodAdapter.checkDescriptor(descriptor, false);
if (value == null) {
throw new IllegalArgumentException("Invalid enum value");
}
if (av != null) {
av.visitEnum(name, desc, value);
}
super.visitEnum(name, descriptor, value);
}
@Override
public AnnotationVisitor visitAnnotation(final String name, final String desc) {
checkEnd();
public AnnotationVisitor visitAnnotation(final String name, final String descriptor) {
checkVisitEndNotCalled();
checkName(name);
CheckMethodAdapter.checkDesc(desc, false);
return new CheckAnnotationAdapter(av == null ? null : av.visitAnnotation(name, desc));
CheckMethodAdapter.checkDescriptor(descriptor, false);
return new CheckAnnotationAdapter(super.visitAnnotation(name, descriptor));
}
@Override
public AnnotationVisitor visitArray(final String name) {
checkEnd();
checkVisitEndNotCalled();
checkName(name);
return new CheckAnnotationAdapter(av == null ? null : av.visitArray(name), false);
return new CheckAnnotationAdapter(super.visitArray(name), false);
}
@Override
public void visitEnd() {
checkEnd();
end = true;
if (av != null) {
av.visitEnd();
}
checkVisitEndNotCalled();
visitEndCalled = true;
super.visitEnd();
}
private void checkEnd() {
if (end) {
throw new IllegalStateException("Cannot call a visit method after visitEnd has been called");
private void checkName(final String name) {
if (useNamedValue && name == null) {
throw new IllegalArgumentException("Annotation value name must not be null");
}
}
private void checkName(final String name) {
if (named && name == null) {
throw new IllegalArgumentException("Annotation value name must not be null");
private void checkVisitEndNotCalled() {
if (visitEndCalled) {
throw new IllegalStateException("Cannot call a visit method after visitEnd has been called");
}
}
}
......@@ -34,20 +34,25 @@ import org.objectweb.asm.Opcodes;
import org.objectweb.asm.TypePath;
import org.objectweb.asm.TypeReference;
/** A {@link FieldVisitor} that checks that its methods are properly used. */
/**
* A {@link FieldVisitor} that checks that its methods are properly used.
*
* @author Eric Bruneton
*/
public class CheckFieldAdapter extends FieldVisitor {
private boolean end;
/** Whether the {@link #visitEnd} method has been called. */
private boolean visitEndCalled;
/**
* Constructs a new {@link CheckFieldAdapter}. <i>Subclasses must not use this constructor</i>.
* Instead, they must use the {@link #CheckFieldAdapter(int, FieldVisitor)} version.
*
* @param fv the field visitor to which this adapter must delegate calls.
* @param fieldVisitor the field visitor to which this adapter must delegate calls.
* @throws IllegalStateException If a subclass calls this constructor.
*/
public CheckFieldAdapter(final FieldVisitor fv) {
this(Opcodes.ASM6, fv);
public CheckFieldAdapter(final FieldVisitor fieldVisitor) {
this(Opcodes.ASM6, fieldVisitor);
if (getClass() != CheckFieldAdapter.class) {
throw new IllegalStateException();
}
......@@ -58,51 +63,52 @@ public class CheckFieldAdapter extends FieldVisitor {
*
* @param api the ASM API version implemented by this visitor. Must be one of {@link
* Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
* @param fv the field visitor to which this adapter must delegate calls.
* @param fieldVisitor the field visitor to which this adapter must delegate calls.
*/
protected CheckFieldAdapter(final int api, final FieldVisitor fv) {
super(api, fv);
protected CheckFieldAdapter(final int api, final FieldVisitor fieldVisitor) {
super(api, fieldVisitor);
}
@Override
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
checkEnd();
CheckMethodAdapter.checkDesc(desc, false);
return new CheckAnnotationAdapter(super.visitAnnotation(desc, visible));
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
checkVisitEndNotCalled();
CheckMethodAdapter.checkDescriptor(descriptor, false);
return new CheckAnnotationAdapter(super.visitAnnotation(descriptor, visible));
}
@Override
public AnnotationVisitor visitTypeAnnotation(
final int typeRef, final TypePath typePath, final String desc, final boolean visible) {
checkEnd();
int sort = typeRef >>> 24;
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) {
checkVisitEndNotCalled();
int sort = new TypeReference(typeRef).getSort();
if (sort != TypeReference.FIELD) {
throw new IllegalArgumentException(
"Invalid type reference sort 0x" + Integer.toHexString(sort));
}
CheckClassAdapter.checkTypeRefAndPath(typeRef, typePath);
CheckMethodAdapter.checkDesc(desc, false);
return new CheckAnnotationAdapter(super.visitTypeAnnotation(typeRef, typePath, desc, visible));
CheckMethodAdapter.checkDescriptor(descriptor, false);
return new CheckAnnotationAdapter(
super.visitTypeAnnotation(typeRef, typePath, descriptor, visible));
}
@Override
public void visitAttribute(final Attribute attr) {
checkEnd();
if (attr == null) {
public void visitAttribute(final Attribute attribute) {
checkVisitEndNotCalled();
if (attribute == null) {
throw new IllegalArgumentException("Invalid attribute (must not be null)");
}
super.visitAttribute(attr);
super.visitAttribute(attribute);
}
@Override
public void visitEnd() {
checkEnd();
end = true;
checkVisitEndNotCalled();
visitEndCalled = true;
super.visitEnd();
}
private void checkEnd() {
if (end) {
private void checkVisitEndNotCalled() {
if (visitEndCalled) {
throw new IllegalStateException("Cannot call a visit method after visitEnd has been called");
}
}
......
......@@ -32,125 +32,161 @@ import java.util.HashSet;
import org.objectweb.asm.ModuleVisitor;
import org.objectweb.asm.Opcodes;
/** @author Remi Forax */
/**
* A {@link ModuleVisitor} that checks that its methods are properly used.
*
* @author Remi Forax
*/
public final class CheckModuleAdapter extends ModuleVisitor {
private boolean end;
/** Whether the visited module is open. */
private final boolean isOpen;
private final HashSet<String> requireNames = new HashSet<String>();
private final HashSet<String> exportNames = new HashSet<String>();
private final HashSet<String> openNames = new HashSet<String>();
private final HashSet<String> useNames = new HashSet<String>();
private final HashSet<String> provideNames = new HashSet<String>();
/** The fully qualified names of the dependencies of the visited module. */
private final HashSet<String> requiredModules = new HashSet<String>();
/** The internal names of the packages exported by the visited module. */
private final HashSet<String> exportedPackages = new HashSet<String>();
/** The internal names of the packages opened by the visited module. */
private final HashSet<String> openedPackages = new HashSet<String>();
/** The internal names of the services used by the visited module. */
private final HashSet<String> usedServices = new HashSet<String>();
/** The internal names of the services provided by the visited module. */
private final HashSet<String> providedServices = new HashSet<String>();
public CheckModuleAdapter(final ModuleVisitor mv, final boolean isOpen) {
super(Opcodes.ASM6, mv);
/** Whether the {@link #visitEnd} method has been called. */
private boolean visitEndCalled;
/**
* Constructs a new {@link CheckModuleAdapter}. <i>Subclasses must not use this constructor</i>.
* Instead, they must use the {@link #CheckModuleAdapter(int, ModuleVisitor, boolean)} version.
*
* @param moduleVisitor the module visitor to which this adapter must delegate calls.
* @param isOpen whether the visited module is open. Open modules have their {@link
* Opcodes#ACC_OPEN} access flag set in {@link org.objectweb.asm.ClassVisitor#visitModule}.
* @throws IllegalStateException If a subclass calls this constructor.
*/
public CheckModuleAdapter(final ModuleVisitor moduleVisitor, final boolean isOpen) {
this(Opcodes.ASM6, moduleVisitor, isOpen);
if (getClass() != CheckModuleAdapter.class) {
throw new IllegalStateException();
}
this.isOpen = isOpen;
}
protected CheckModuleAdapter(final int api, final ModuleVisitor mv, final boolean isOpen) {
super(api, mv);
/**
* Constructs a new {@link CheckModuleAdapter}.
*
* @param api the ASM API version implemented by this visitor. Must be one of {@link
* Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
* @param moduleVisitor the module visitor to which this adapter must delegate calls.
* @param isOpen whether the visited module is open. Open modules have their {@link
* Opcodes#ACC_OPEN} access flag set in {@link org.objectweb.asm.ClassVisitor#visitModule}.
*/
protected CheckModuleAdapter(
final int api, final ModuleVisitor moduleVisitor, final boolean isOpen) {
super(api, moduleVisitor);
this.isOpen = isOpen;
}
@Override
public void visitRequire(String module, int access, String version) {
checkEnd();
if (module == null) {
throw new IllegalArgumentException("require cannot be null");
}
checkDeclared("requires", requireNames, module);
public void visitMainClass(final String mainClass) {
CheckMethodAdapter.checkInternalName(mainClass, "module main class");
super.visitMainClass(mainClass);
}
@Override
public void visitPackage(final String packaze) {
CheckMethodAdapter.checkInternalName(packaze, "module package");
super.visitPackage(packaze);
}
@Override
public void visitRequire(final String module, final int access, final String version) {
checkVisitEndNotCalled();
CheckClassAdapter.checkFullyQualifiedName(module, "required module");
checkNameNotAlreadyDeclared(module, requiredModules, "Modules requires");
CheckClassAdapter.checkAccess(
access,
Opcodes.ACC_STATIC_PHASE
+ Opcodes.ACC_TRANSITIVE
+ Opcodes.ACC_SYNTHETIC
+ Opcodes.ACC_MANDATED);
| Opcodes.ACC_TRANSITIVE
| Opcodes.ACC_SYNTHETIC
| Opcodes.ACC_MANDATED);
super.visitRequire(module, access, version);
}
@Override
public void visitExport(String packaze, int access, String... modules) {
checkEnd();
if (packaze == null) {
throw new IllegalArgumentException("packaze cannot be null");
}
public void visitExport(final String packaze, final int access, final String... modules) {
checkVisitEndNotCalled();
CheckMethodAdapter.checkInternalName(packaze, "package name");
checkDeclared("exports", exportNames, packaze);
CheckClassAdapter.checkAccess(access, Opcodes.ACC_SYNTHETIC + Opcodes.ACC_MANDATED);
checkNameNotAlreadyDeclared(packaze, exportedPackages, "Module exports");
CheckClassAdapter.checkAccess(access, Opcodes.ACC_SYNTHETIC | Opcodes.ACC_MANDATED);
if (modules != null) {
for (int i = 0; i < modules.length; i++) {
if (modules[i] == null) {
throw new IllegalArgumentException("module at index " + i + " cannot be null");
}
for (String module : modules) {
CheckClassAdapter.checkFullyQualifiedName(module, "module export to");
}
}
super.visitExport(packaze, access, modules);
}
@Override
public void visitOpen(String packaze, int access, String... modules) {
checkEnd();
public void visitOpen(final String packaze, final int access, final String... modules) {
checkVisitEndNotCalled();
if (isOpen) {
throw new IllegalArgumentException("an open module can not use open directive");
}
if (packaze == null) {
throw new IllegalArgumentException("packaze cannot be null");
throw new UnsupportedOperationException("An open module can not use open directive");
}
CheckMethodAdapter.checkInternalName(packaze, "package name");
checkDeclared("opens", openNames, packaze);
CheckClassAdapter.checkAccess(access, Opcodes.ACC_SYNTHETIC + Opcodes.ACC_MANDATED);
checkNameNotAlreadyDeclared(packaze, openedPackages, "Module opens");
CheckClassAdapter.checkAccess(access, Opcodes.ACC_SYNTHETIC | Opcodes.ACC_MANDATED);
if (modules != null) {
for (int i = 0; i < modules.length; i++) {
if (modules[i] == null) {
throw new IllegalArgumentException("module at index " + i + " cannot be null");
}
for (String module : modules) {
CheckClassAdapter.checkFullyQualifiedName(module, "module open to");
}
}
super.visitOpen(packaze, access, modules);
}
@Override
public void visitUse(String service) {
checkEnd();
public void visitUse(final String service) {
checkVisitEndNotCalled();
CheckMethodAdapter.checkInternalName(service, "service");
checkDeclared("uses", useNames, service);
checkNameNotAlreadyDeclared(service, usedServices, "Module uses");
super.visitUse(service);
}
@Override
public void visitProvide(String service, String... providers) {
checkEnd();
public void visitProvide(final String service, final String... providers) {
checkVisitEndNotCalled();
CheckMethodAdapter.checkInternalName(service, "service");
checkDeclared("provides", provideNames, service);
checkNameNotAlreadyDeclared(service, providedServices, "Module provides");
if (providers == null || providers.length == 0) {
throw new IllegalArgumentException("providers cannot be null or empty");
throw new IllegalArgumentException("Providers cannot be null or empty");
}
for (int i = 0; i < providers.length; i++) {
CheckMethodAdapter.checkInternalName(providers[i], "provider");
for (String provider : providers) {
CheckMethodAdapter.checkInternalName(provider, "provider");
}
super.visitProvide(service, providers);
}
@Override
public void visitEnd() {
checkEnd();
end = true;
checkVisitEndNotCalled();
visitEndCalled = true;
super.visitEnd();
}
private void checkEnd() {
if (end) {
throw new IllegalStateException("Cannot call a visit method after visitEnd has been called");
private static void checkNameNotAlreadyDeclared(
final String name, final HashSet<String> declaredNames, final String message) {
if (!declaredNames.add(name)) {
throw new IllegalArgumentException(message + " " + name + " already declared");
}
}
private static void checkDeclared(String directive, HashSet<String> names, String name) {
if (!names.add(name)) {
throw new IllegalArgumentException(directive + " " + name + " already declared");
private void checkVisitEndNotCalled() {
if (visitEndCalled) {
throw new IllegalStateException("Cannot call a visit method after visitEnd has been called");
}
}
}
......@@ -26,21 +26,17 @@ import java.util.Map;
import org.objectweb.asm.Label;
/**
* An {@link org.objectweb.asm.Attribute Attribute} that can print a readable representation of
* itself.
*
* <p>Implementations should construct readable output from an attribute data structure. Such
* representation could be used in unit test assertions.
* An {@link org.objectweb.asm.Attribute} that can print a readable representation of itself.
*
* @author Eugene Kuleshov
*/
public interface Textifiable {
/**
* Build a human readable representation of this attribute.
* Generates a human readable representation of this attribute.
*
* @param buf a buffer used for printing Java code.
* @param labelNames map of label instances to their names.
* @param outputBuffer where the human representation of this attribute must be appended.
* @param labelNames the human readable names of the labels.
*/
void textify(StringBuffer buf, Map<Label, String> labelNames);
void textify(StringBuffer outputBuffer, Map<Label, String> labelNames);
}
......@@ -37,46 +37,56 @@ import org.objectweb.asm.Opcodes;
*/
public final class TraceAnnotationVisitor extends AnnotationVisitor {
private final Printer p;
/** The printer to convert the visited annotation into text. */
private final Printer printer;
public TraceAnnotationVisitor(final Printer p) {
this(null, p);
/**
* Constructs a new {@link TraceAnnotationVisitor}.
*
* @param printer the printer to convert the visited annotation into text.
*/
public TraceAnnotationVisitor(final Printer printer) {
this(null, printer);
}
public TraceAnnotationVisitor(final AnnotationVisitor av, final Printer p) {
super(Opcodes.ASM6, av);
this.p = p;
/**
* Constructs a new {@link TraceAnnotationVisitor}.
*
* @param annotationVisitor the annotation visitor to which to delegate calls. May be <tt>null</tt>.
* @param printer the printer to convert the visited annotation into text.
*/
public TraceAnnotationVisitor(final AnnotationVisitor annotationVisitor, final Printer printer) {
super(Opcodes.ASM6, annotationVisitor);
this.printer = printer;
}
@Override
public void visit(final String name, final Object value) {
p.visit(name, value);
printer.visit(name, value);
super.visit(name, value);
}
@Override
public void visitEnum(final String name, final String desc, final String value) {
p.visitEnum(name, desc, value);
super.visitEnum(name, desc, value);
public void visitEnum(final String name, final String descriptor, final String value) {
printer.visitEnum(name, descriptor, value);
super.visitEnum(name, descriptor, value);
}
@Override
public AnnotationVisitor visitAnnotation(final String name, final String desc) {
Printer p = this.p.visitAnnotation(name, desc);
AnnotationVisitor av = this.av == null ? null : this.av.visitAnnotation(name, desc);
return new TraceAnnotationVisitor(av, p);
public AnnotationVisitor visitAnnotation(final String name, final String descriptor) {
Printer annotationPrinter = printer.visitAnnotation(name, descriptor);
return new TraceAnnotationVisitor(super.visitAnnotation(name, descriptor), annotationPrinter);
}
@Override
public AnnotationVisitor visitArray(final String name) {
Printer p = this.p.visitArray(name);
AnnotationVisitor av = this.av == null ? null : this.av.visitArray(name);
return new TraceAnnotationVisitor(av, p);
Printer arrayPrinter = printer.visitArray(name);
return new TraceAnnotationVisitor(super.visitArray(name), arrayPrinter);
}
@Override
public void visitEnd() {
p.visitAnnotationEnd();
printer.visitAnnotationEnd();
super.visitEnd();
}
}