diff options
author | Eric Bruneton <ebruneton@free.fr> | 2022-07-31 12:21:41 +0000 |
---|---|---|
committer | Eric Bruneton <ebruneton@free.fr> | 2022-07-31 12:21:41 +0000 |
commit | 5921eb2a141f0dcc83c6a5d7dcd5035a30c5edfc (patch) | |
tree | 33c3de7a1f2ea562fa3288a098e505332e174df0 | |
parent | c5b540441b239ebf090292e69e93155d90cf2626 (diff) | |
download | ow2-asm-5921eb2a141f0dcc83c6a5d7dcd5035a30c5edfc.tar.gz |
Generate the module info classes without Bnd. Delete the Bnd plugin.
-rw-r--r-- | build.gradle | 37 | ||||
-rw-r--r-- | settings.gradle | 1 | ||||
-rw-r--r-- | tools/bnd-module-plugin/src/main/java/org/objectweb/asm/tools/ModuleInfoBndPlugin.java | 99 | ||||
-rw-r--r-- | tools/retrofitter/src/main/java/org/objectweb/asm/tools/Retrofitter.java | 336 |
4 files changed, 267 insertions, 206 deletions
diff --git a/build.gradle b/build.gradle index ce6a73b6..26cab3ac 100644 --- a/build.gradle +++ b/build.gradle @@ -60,6 +60,10 @@ subprojects { test { useJUnitPlatform() } ext.provides = [] // The provided java packages, e.g. ['org.objectweb.asm'] ext.requires = [] // The required Gradle projects, e.g. [':asm-test'] + ext.transitiveRequires = { -> + return requires.collect{p -> + project(p).transitiveRequires().plus(project(p).provides[0])}.flatten() + } ext.depends = [] // The external dependencies, e.g. ['junit:junit:4.12'] // Some external dependencies (such as Jacoco) depend transitively on ASM, and // without this rule Gradle can mix ASM jars of different versions (e.g. @@ -85,7 +89,7 @@ project(':asm-analysis') { project(':asm-commons') { description = "Usefull class adapters based on ${parent.description}" provides = ['org.objectweb.asm.commons'] - requires = [':asm', ':asm-tree', ':asm-analysis'] + requires = [':asm', ':asm-tree'] dependencies { testImplementation project(':asm-util') } } @@ -159,16 +163,11 @@ project(':tools') { description = "Tools used to build ${parent.description}" } -project(':tools:bnd-module-plugin') { - description = "bnd plugin to build moduleinfo with ${rootProject.description}" - // TODO: this compiles asm twice (here and in :asm), find a way to avoid this. - sourceSets.main.java.srcDirs += project(':asm').sourceSets.main.java.srcDirs - depends = ['biz.aQute.bnd:biz.aQute.bnd:6.2.0'] -} - project(':tools:retrofitter') { description = "JDK 1.5 class retrofitter based on ${rootProject.description}" - // TODO: this compiles asm thrice (here, above and in :asm). + sourceCompatibility = '1.9' + targetCompatibility = '1.9' + // TODO: this compiles asm twice (here and in :asm). sourceSets.main.java.srcDirs += project(':asm').sourceSets.main.java.srcDirs } @@ -215,7 +214,7 @@ subprojects { // Configure the projects with a non-empty 'provides' property. They must be // checked for code coverage and backward compatibility, retrofited to Java 1.5, -// and packaged with biz.aQute.bnd. +// and packaged with generated module-info classes. configure(subprojects.findAll { it.provides }) { // Code coverage configuration. jacoco.toolVersion = '0.8.8' @@ -229,7 +228,8 @@ configure(subprojects.findAll { it.provides }) { } check.dependsOn jacocoTestCoverageVerification - // Retrofit the code to Java 1.5, in-place, in compileJava.doLast. + // Retrofit the code in-place to Java 1.5 and generate a module-info class + // from the code content, in compileJava.doLast. if (name != 'asm-test') { compileJava.dependsOn ':tools:retrofitter:classes' compileJava.doLast { @@ -237,7 +237,9 @@ configure(subprojects.findAll { it.provides }) { def loader = new URLClassLoader(path.collect {f -> f.toURL()} as URL[]) def retrofitter = loader.loadClass('org.objectweb.asm.tools.Retrofitter').newInstance() - retrofitter.retrofit sourceSets.main.output.classesDirs.singleFile + def classes = sourceSets.main.output.classesDirs.singleFile + retrofitter.retrofit(classes, "${version}") + retrofitter.verify(classes, "${version}", provides, transitiveRequires()) } } @@ -279,18 +281,13 @@ configure(subprojects.findAll { it.provides }) { } } - // Apply the biz.aQute.bnd plugin to package the project as an OSGi bundle, - // with a custom plugin to generate and include a module-info class. Exclude - // the asm-test project (the DefaultPackage class prevents it from being a - // proper bundle). + // Apply the biz.aQute.bnd plugin to package the project as an OSGi bundle. + // Exclude the asm-test project (the DefaultPackage class prevents it from + // being a proper bundle). if (name != 'asm-test') { apply plugin: 'biz.aQute.bnd.builder' - jar.dependsOn ':tools:bnd-module-plugin:jar' jar.manifest.attributes( '-classpath': sourceSets.main.output.classesDirs.asPath, - '-plugin': 'org.objectweb.asm.tools.ModuleInfoBndPlugin;', - '-pluginpath': - project(':tools:bnd-module-plugin').jar.outputs.files.singleFile, '-removeheaders': 'Bnd-LastModified,Build-By,Created-By,Include-Resource,\ Require-Capability,Tool', 'Bundle-License': 'BSD-3-Clause;link=https://asm.ow2.io/LICENSE.txt', diff --git a/settings.gradle b/settings.gradle index a849bc11..bed9a5d9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -36,6 +36,5 @@ include( 'asm-tree', 'asm-util', 'benchmarks', - 'tools:bnd-module-plugin', 'tools:retrofitter') diff --git a/tools/bnd-module-plugin/src/main/java/org/objectweb/asm/tools/ModuleInfoBndPlugin.java b/tools/bnd-module-plugin/src/main/java/org/objectweb/asm/tools/ModuleInfoBndPlugin.java deleted file mode 100644 index ee91bdc1..00000000 --- a/tools/bnd-module-plugin/src/main/java/org/objectweb/asm/tools/ModuleInfoBndPlugin.java +++ /dev/null @@ -1,99 +0,0 @@ -// ASM: a very small and fast Java bytecode manipulation framework -// Copyright (c) 2000-2011 INRIA, France Telecom -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the name of the copyright holders nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -// THE POSSIBILITY OF SUCH DAMAGE. -package org.objectweb.asm.tools; - -import aQute.bnd.header.Attrs; -import aQute.bnd.header.Parameters; -import aQute.bnd.osgi.Analyzer; -import aQute.bnd.osgi.Constants; -import aQute.bnd.osgi.EmbeddedResource; -import aQute.bnd.service.AnalyzerPlugin; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.ModuleVisitor; -import org.objectweb.asm.Opcodes; - -/** - * An biz.aQute.bnd plugin to generate a module-info class from the name, version, requires and - * export properties of the bundle. - * - * @author Remi Forax - */ -public class ModuleInfoBndPlugin implements AnalyzerPlugin { - private static final String MODULE_NAME = "Module-Name"; - private static final String MODULE_VERSION = "Module-Version"; - private static final String MODULE_REQUIRES = "Module-Requires"; - private static final String MODULE_EXPORTS = "Module-Exports"; - - @Override - public boolean analyzeJar(final Analyzer analyzer) throws Exception { - ClassWriter classWriter = new ClassWriter(0); - classWriter.visit(Opcodes.V9, Opcodes.ACC_MODULE, "module-info", null, null, null); - String moduleName = - analyzer.getProperty(MODULE_NAME, analyzer.getProperty(Constants.BUNDLE_SYMBOLICNAME)); - String moduleVersion = - analyzer.getProperty(MODULE_VERSION, analyzer.getProperty(Constants.BUNDLE_VERSION)); - ModuleVisitor moduleVisitor = - classWriter.visitModule(moduleName, Opcodes.ACC_OPEN, moduleVersion); - - String requireModules = analyzer.getProperty(MODULE_REQUIRES); - if (requireModules != null) { - Parameters requireParams = analyzer.parseHeader(requireModules); - for (String requireName : requireParams.keySet()) { - Attrs attrs = requireParams.get(requireName); - boolean isTransitive = attrs.containsKey("transitive"); - boolean isStatic = attrs.containsKey("static"); - moduleVisitor.visitRequire( - requireName, - (isTransitive ? Opcodes.ACC_TRANSITIVE : 0) | (isStatic ? Opcodes.ACC_STATIC_PHASE : 0), - null); - } - } - moduleVisitor.visitRequire("java.base", Opcodes.ACC_MANDATED, null); - - String exportPackages = - analyzer.getProperty(MODULE_EXPORTS, analyzer.getProperty(Constants.EXPORT_PACKAGE)); - if (exportPackages != null) { - Parameters exportParams = analyzer.parseHeader(exportPackages); - for (String packageName : exportParams.keySet()) { - if (packageName.endsWith("*")) { - throw new IllegalStateException("Unsupported wildcard packages " + packageName); - } - moduleVisitor.visitExport(packageName.replace('.', '/'), 0); - } - } - moduleVisitor.visitEnd(); - classWriter.visitEnd(); - - analyzer - .getJar() - .putResource( - "module-info.class", - new EmbeddedResource(classWriter.toByteArray(), System.currentTimeMillis())); - return false; - } -} diff --git a/tools/retrofitter/src/main/java/org/objectweb/asm/tools/Retrofitter.java b/tools/retrofitter/src/main/java/org/objectweb/asm/tools/Retrofitter.java index 2d017ed2..b3d51e63 100644 --- a/tools/retrofitter/src/main/java/org/objectweb/asm/tools/Retrofitter.java +++ b/tools/retrofitter/src/main/java/org/objectweb/asm/tools/Retrofitter.java @@ -27,22 +27,33 @@ // THE POSSIBILITY OF SUCH DAMAGE. package org.objectweb.asm.tools; +import static java.lang.String.format; +import static java.util.stream.Collectors.toSet; + import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; -import java.io.OutputStream; +import java.lang.module.ModuleDescriptor; import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; import java.util.zip.GZIPInputStream; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.ModuleVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; @@ -57,6 +68,12 @@ import org.objectweb.asm.Type; */ public class Retrofitter { + /** The name of the module-info file. */ + private static final String MODULE_INFO = "module-info.class"; + + /** The name of the java.base module. */ + private static final String JAVA_BASE_MODULE = "java.base"; + /** * The fields and methods of the JDK 1.5 API. Each string has the form * "<owner><name><descriptor>". @@ -68,12 +85,158 @@ public class Retrofitter { */ private final HashMap<String, String> jdkHierarchy = new HashMap<>(); + /** The internal names of the packages exported by the retrofitted classes. */ + private final HashSet<String> exports = new HashSet<>(); + + /** The internal names of the packages imported by the retrofitted classes. */ + private final HashSet<String> imports = new HashSet<>(); + + /** + * Transforms the class files in the given directory, in place, in order to make them compatible + * with the JDK 1.5. Also generates a module-info class in this directory, with the given module + * version. + * + * @param args a directory containing compiled classes and the ASM release version. + * @throws IOException if a file can't be read or written. + */ + public static void main(final String[] args) throws IOException { + if (args.length == 2) { + new Retrofitter().retrofit(new File(args[0]), args[1]); + } else { + System.err.println("Usage: Retrofitter <classes directory> <ASM release version>"); // NOPMD + } + } + + /** + * Transforms the class files in the given directory, in place, in order to make them compatible + * with the JDK 1.5. Also generates a module-info class in this directory, with the given module + * version. + * + * @param classesDir a directory containing compiled classes. + * @param version the module-info version. + * @throws IOException if a file can't be read or written. + */ + public void retrofit(final File classesDir, final String version) throws IOException { + for (File classFile : getAllClasses(classesDir, new ArrayList<File>())) { + ClassReader classReader = new ClassReader(Files.newInputStream(classFile.toPath())); + ClassWriter classWriter = new ClassWriter(0); + classReader.accept(new ClassRetrofitter(classWriter), ClassReader.SKIP_FRAMES); + Files.write(classFile.toPath(), classWriter.toByteArray()); + } + generateModuleInfoClass(classesDir, version); + } + /** - * Constructs a new {@link Retrofitter}. + * Verify that the class files in the given directory only use JDK 1.5 APIs, and that a + * module-info class is present with the expected content. * - * @throws IOException if the JDK API description file can't be read. + * @param classesDir a directory containing compiled classes. + * @param expectedVersion the expected module-info version. + * @param expectedExports the expected module-info exported packages. + * @param expectedRequires the expected module-info required modules. + * @throws IOException if a file can't be read. + * @throws IllegalArgumentException if the module-info class does not have the expected content. */ - public Retrofitter() throws IOException { + public void verify( + final File classesDir, + final String expectedVersion, + final List<String> expectedExports, + final List<String> expectedRequires) + throws IOException { + if (jdkApi.isEmpty()) { + readJdkApi(); + } + for (File classFile : getAllClasses(classesDir, new ArrayList<File>())) { + if (!classFile.getName().equals(MODULE_INFO)) { + new ClassReader(Files.newInputStream(classFile.toPath())).accept(new ClassVerifier(), 0); + } + } + verifyModuleInfoClass( + classesDir, + expectedVersion, + new HashSet<String>(expectedExports), + Stream.concat(expectedRequires.stream(), Stream.of(JAVA_BASE_MODULE)).collect(toSet())); + } + + private List<File> getAllClasses(final File file, final List<File> allClasses) + throws IOException { + if (file.isDirectory()) { + File[] children = file.listFiles(); + if (children == null) { + throw new IOException("Unable to read files of " + file); + } + for (File child : children) { + getAllClasses(child, allClasses); + } + } else if (file.getName().endsWith(".class")) { + allClasses.add(file); + } + return allClasses; + } + + private void generateModuleInfoClass(final File dstDir, final String version) throws IOException { + ClassWriter classWriter = new ClassWriter(0); + classWriter.visit(Opcodes.V9, Opcodes.ACC_MODULE, "module-info", null, null, null); + ArrayList<String> moduleNames = new ArrayList<>(); + for (String exportName : exports) { + if (isAsmModule(exportName)) { + moduleNames.add(exportName); + } + } + if (moduleNames.size() != 1) { + throw new IllegalArgumentException("Module name can't be infered from classes"); + } + ModuleVisitor moduleVisitor = + classWriter.visitModule(moduleNames.get(0), Opcodes.ACC_OPEN, version); + + for (String importName : imports) { + if (isAsmModule(importName) && !exports.contains(importName)) { + moduleVisitor.visitRequire(importName.replace('/', '.'), Opcodes.ACC_TRANSITIVE, null); + } + } + moduleVisitor.visitRequire(JAVA_BASE_MODULE, Opcodes.ACC_MANDATED, null); + + for (String exportName : exports) { + moduleVisitor.visitExport(exportName, 0); + } + moduleVisitor.visitEnd(); + classWriter.visitEnd(); + Files.write(Path.of(dstDir.getAbsolutePath(), MODULE_INFO), classWriter.toByteArray()); + } + + private void verifyModuleInfoClass( + final File dstDir, + final String expectedVersion, + final Set<String> expectedExports, + final Set<String> expectedRequires) + throws IOException { + ModuleDescriptor module = + ModuleDescriptor.read(Files.newInputStream(Path.of(dstDir.getAbsolutePath(), MODULE_INFO))); + String version = module.version().map(ModuleDescriptor.Version::toString).orElse(""); + if (!version.equals(expectedVersion)) { + throw new IllegalArgumentException( + format("Wrong module-info version '%s' (expected '%s')", version, expectedVersion)); + } + Set<String> exports = + module.exports().stream().map(ModuleDescriptor.Exports::source).collect(toSet()); + if (!exports.equals(expectedExports)) { + throw new IllegalArgumentException( + format("Wrong module-info exports %s (expected %s)", exports, expectedExports)); + } + Set<String> requires = + module.requires().stream().map(ModuleDescriptor.Requires::name).collect(toSet()); + if (!requires.equals(expectedRequires)) { + throw new IllegalArgumentException( + format("Wrong module-info requires %s (expected %s)", requires, expectedRequires)); + } + } + + private static boolean isAsmModule(final String packageName) { + return packageName.startsWith("org/objectweb/asm") + && !packageName.equals("org/objectweb/asm/signature"); + } + + private void readJdkApi() throws IOException { try (InputStream inputStream = new GZIPInputStream( Retrofitter.class.getClassLoader().getResourceAsStream("jdk1.5.0.12.txt.gz")); @@ -97,56 +260,8 @@ public class Retrofitter { } } - /** - * Transforms the source class file, or if it is a directory, its files (recursively), in place, - * in order to make them compatible with the JDK 1.5. - * - * @param src source file or directory. - * @throws IOException if the source files can't be read or written. - */ - public void retrofit(final File src) throws IOException { - retrofit(src, null); - } - - /** - * Transforms the source class file, or if it is a directory, its files (recursively), either in - * place or into the destination file or directory, in order to make them compatible with the JDK - * 1.5. - * - * @param src source file or directory. - * @param dst optional destination file or directory. - * @throws IOException if the source or destination file can't be read or written. - */ - public void retrofit(final File src, final File dst) throws IOException { - if (src.isDirectory()) { - File[] files = src.listFiles(); - if (files == null) { - throw new IOException("Unable to read files of " + src); - } - for (File file : files) { - retrofit(file, dst == null ? null : new File(dst, file.getName())); - } - } else if (src.getName().endsWith(".class")) { - if (dst == null || !dst.exists() || dst.lastModified() < src.lastModified()) { - ClassReader classReader = new ClassReader(Files.newInputStream(src.toPath())); - ClassWriter classWriter = new ClassWriter(0); - ClassVerifier classVerifier = new ClassVerifier(classWriter); - ClassRetrofitter classRetrofitter = new ClassRetrofitter(classVerifier); - classReader.accept(classRetrofitter, ClassReader.SKIP_FRAMES); - - if (dst != null && !dst.getParentFile().exists() && !dst.getParentFile().mkdirs()) { - throw new IOException("Cannot create directory " + dst.getParentFile()); - } - try (OutputStream outputStream = - Files.newOutputStream((dst == null ? src : dst).toPath())) { - outputStream.write(classWriter.toByteArray()); - } - } - } - } - /** A ClassVisitor that retrofits classes to 1.5 version. */ - static class ClassRetrofitter extends ClassVisitor { + class ClassRetrofitter extends ClassVisitor { public ClassRetrofitter(final ClassVisitor classVisitor) { super(/* latest api =*/ Opcodes.ASM8, classVisitor); @@ -160,26 +275,47 @@ public class Retrofitter { final String signature, final String superName, final String[] interfaces) { + addPackageReferences(Type.getObjectType(name), /* export = */ true); super.visit(Opcodes.V1_5, access, name, signature, superName, interfaces); } @Override + public FieldVisitor visitField( + final int access, + final String name, + final String descriptor, + final String signature, + final Object value) { + addPackageReferences(Type.getType(descriptor), /* export = */ false); + return super.visitField(access, name, descriptor, signature, value); + } + + @Override public MethodVisitor visitMethod( final int access, final String name, final String descriptor, final String signature, final String[] exceptions) { + addPackageReferences(Type.getType(descriptor), /* export = */ false); return new MethodVisitor( api, super.visitMethod(access, name, descriptor, signature, exceptions)) { @Override + public void visitFieldInsn( + final int opcode, final String owner, final String name, final String descriptor) { + addPackageReferences(Type.getType(descriptor), /* export = */ false); + super.visitFieldInsn(opcode, owner, name, descriptor); + } + + @Override public void visitMethodInsn( final int opcode, final String owner, final String name, final String descriptor, final boolean isInterface) { + addPackageReferences(Type.getType(descriptor), /* export = */ false); // Remove the addSuppressed() method calls generated for try-with-resources statements. // This method is not defined in JDK1.5. if (owner.equals("java/lang/Throwable") @@ -190,8 +326,52 @@ public class Retrofitter { super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } } + + @Override + public void visitTypeInsn(final int opcode, final String type) { + addPackageReferences(Type.getObjectType(type), /* export = */ false); + super.visitTypeInsn(opcode, type); + } + + @Override + public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { + addPackageReferences(Type.getType(descriptor), /* export = */ false); + super.visitMultiANewArrayInsn(descriptor, numDimensions); + } + + @Override + public void visitTryCatchBlock( + final Label start, final Label end, final Label handler, final String type) { + if (type != null) { + addPackageReferences(Type.getObjectType(type), /* export = */ false); + } + super.visitTryCatchBlock(start, end, handler, type); + } }; } + + private void addPackageReferences(final Type type, final boolean export) { + switch (type.getSort()) { + case Type.ARRAY: + addPackageReferences(type.getElementType(), export); + break; + case Type.METHOD: + for (Type argumentType : type.getArgumentTypes()) { + addPackageReferences(argumentType, export); + } + addPackageReferences(type.getReturnType(), export); + break; + case Type.OBJECT: + String internalName = type.getInternalName(); + int lastSlashIndex = internalName.lastIndexOf('/'); + if (lastSlashIndex != -1) { + (export ? exports : imports).add(internalName.substring(0, lastSlashIndex)); + } + break; + default: + break; + } + } } /** @@ -199,18 +379,18 @@ public class Retrofitter { */ class ClassVerifier extends ClassVisitor { - /** The name of the visited class. */ + /** The internal name of the visited class. */ String className; /** The name of the currently visited method. */ String currentMethodName; - public ClassVerifier(final ClassVisitor classVisitor) { + public ClassVerifier() { // Make sure use we don't use Java 9 or higher classfile features. // We also want to make sure we don't use Java 6, 7 or 8 classfile // features (invokedynamic), but this can't be done in the same way. // Instead, we use manual checks below. - super(Opcodes.ASM4, classVisitor); + super(Opcodes.ASM4, null); } @Override @@ -222,10 +402,9 @@ public class Retrofitter { final String superName, final String[] interfaces) { if ((version & 0xFFFF) > Opcodes.V1_5) { - throw new IllegalArgumentException("ERROR: " + name + " version is newer than 1.5"); + throw new IllegalArgumentException(format("ERROR: %d version is newer than 1.5", version)); } className = name; - super.visit(version, access, name, signature, superName, interfaces); } @Override @@ -243,7 +422,6 @@ public class Retrofitter { public void visitFieldInsn( final int opcode, final String owner, final String name, final String descriptor) { check(owner, name); - super.visitFieldInsn(opcode, owner, name, descriptor); } @Override @@ -254,7 +432,6 @@ public class Retrofitter { final String descriptor, final boolean isInterface) { check(owner, name + descriptor); - super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); } @Override @@ -263,21 +440,16 @@ public class Retrofitter { int sort = ((Type) value).getSort(); if (sort == Type.METHOD) { throw new IllegalArgumentException( - "ERROR: ldc with a MethodType called in " - + className - + ' ' - + currentMethodName - + " is not available in JDK 1.5"); + format( + "ERROR: ldc with a MethodType called in %s %s is not available in JDK 1.5", + className, currentMethodName)); } } else if (value instanceof Handle) { throw new IllegalArgumentException( - "ERROR: ldc with a MethodHandle called in " - + className - + ' ' - + currentMethodName - + " is not available in JDK 1.5"); + format( + "ERROR: ldc with a MethodHandle called in %s %s is not available in JDK 1.5", + className, currentMethodName)); } - super.visitLdcInsn(value); } @Override @@ -287,11 +459,9 @@ public class Retrofitter { final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments) { throw new IllegalArgumentException( - "ERROR: invokedynamic called in " - + className - + ' ' - + currentMethodName - + " is not available in JDK 1.5"); + format( + "ERROR: invokedynamic called in %s %s is not available in JDK 1.5", + className, currentMethodName)); } }; } @@ -302,7 +472,7 @@ public class Retrofitter { * @param owner A class name. * @param member A field name or a method name and descriptor. */ - void check(final String owner, final String member) { + private void check(final String owner, final String member) { if (owner.startsWith("java/")) { String currentOwner = owner; while (currentOwner != null) { @@ -312,15 +482,9 @@ public class Retrofitter { currentOwner = jdkHierarchy.get(currentOwner); } throw new IllegalArgumentException( - "ERROR: " - + owner - + ' ' - + member - + " called in " - + className - + ' ' - + currentMethodName - + " is not defined in the JDK 1.5 API"); + format( + "ERROR: %s %s called in %s %s is not defined in the JDK 1.5 API", + owner, member, className, currentMethodName)); } } } |