Skip to content

Fix the signature checks for JDK9, where rt.jar is not available.

Eric Bruneton requested to merge fix-signature-check-for-jdk-9 into master

For reference, the ~450kB jdk8-api.jar was generated from the JDK8 ~64MB rt.jar with the following code, which removes all non public or protected members, skips packages which are not needed and will likely be never needed by ASM, and removes all the code from the remaining methods:

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.TypePath;

public class JdkApiBuilder {

  public static void main(String[] args) throws IOException {
    File rtJarFile = new File(args[0]);
    ZipFile rtJarZipFile = new ZipFile(rtJarFile);
    ZipOutputStream jdkApiJar = new ZipOutputStream(new FileOutputStream(args[1]));

    Enumeration<? extends ZipEntry> zipEntries = rtJarZipFile.entries();
    while (zipEntries.hasMoreElements()) {
      ZipEntry zipEntry = zipEntries.nextElement();
      if (zipEntry.isDirectory()) {
        jdkApiJar.putNextEntry(zipEntry);
        continue;
      }
      String name = zipEntry.getName();
      if (name.startsWith("java/")
          && name.endsWith(".class")
          && !(name.startsWith("java/applet")
              || name.startsWith("java/awt")
              || name.startsWith("java/beans")
              || name.startsWith("java/lang/management")
              || name.startsWith("java/math")
              || name.startsWith("java/net")
              || name.startsWith("java/rmi")
              || name.startsWith("java/security")
              || name.startsWith("java/sql")
              || name.startsWith("java/text")
              || name.startsWith("java/time"))) {
        ClassReader classReader = new ClassReader(rtJarZipFile.getInputStream(zipEntry));
        if ((classReader.getAccess() & Opcodes.ACC_PUBLIC) == 0) {
          continue;
        }
        ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        ClassVisitor classVisitor =
            new ClassVisitor(Opcodes.ASM6, classWriter) {

              private boolean skipAnnotation(String descriptor) {
                return !descriptor.startsWith("java/");
              }

              @Override
              public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
                if (skipAnnotation(descriptor)) return null;
                return super.visitAnnotation(descriptor, visible);
              }

              @Override
              public AnnotationVisitor visitTypeAnnotation(
                  int typeRef, TypePath typePath, String descriptor, boolean visible) {
                if (skipAnnotation(descriptor)) return null;
                return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
              }

              @Override
              public void visitAttribute(Attribute attribute) {
                // Remove non standard attributes.
              }

              @Override
              public FieldVisitor visitField(
                  int access, String name, String descriptor, String signature, Object value) {
                return new FieldVisitor(
                    Opcodes.ASM6, super.visitField(access, name, descriptor, signature, value)) {

                  @Override
                  public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
                    if (skipAnnotation(descriptor)) return null;
                    return super.visitAnnotation(descriptor, visible);
                  }

                  @Override
                  public AnnotationVisitor visitTypeAnnotation(
                      int typeRef, TypePath typePath, String descriptor, boolean visible) {
                    if (skipAnnotation(descriptor)) return null;
                    return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
                  }
                };
              }

              @Override
              public MethodVisitor visitMethod(
                  final int access,
                  final String name,
                  final String descriptor,
                  final String signature,
                  final String[] exceptions) {
                if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
                  return null;
                }
                return new MethodVisitor(
                    Opcodes.ASM6,
                    super.visitMethod(access, name, descriptor, signature, exceptions)) {

                  @Override
                  public AnnotationVisitor visitAnnotationDefault() {
                    if (skipAnnotation(descriptor)) return null;
                    return super.visitAnnotationDefault();
                  }

                  @Override
                  public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
                    if (skipAnnotation(descriptor)) return null;
                    return super.visitAnnotation(descriptor, visible);
                  }

                  @Override
                  public AnnotationVisitor visitTypeAnnotation(
                      int typeRef, TypePath typePath, String descriptor, boolean visible) {
                    if (skipAnnotation(descriptor)) return null;
                    return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
                  }

                  @Override
                  public AnnotationVisitor visitParameterAnnotation(
                      int parameter, String descriptor, boolean visible) {
                    if (skipAnnotation(descriptor)) return null;
                    return super.visitParameterAnnotation(parameter, descriptor, visible);
                  }

                  @Override
                  public void visitAttribute(Attribute attribute) {
                    // Remove non standard attributes.
                  }

                  @Override
                  public AnnotationVisitor visitInsnAnnotation(
                      int typeRef, TypePath typePath, String descriptor, boolean visible) {
                    if (skipAnnotation(descriptor)) return null;
                    return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible);
                  }

                  @Override
                  public AnnotationVisitor visitTryCatchAnnotation(
                      int typeRef, TypePath typePath, String descriptor, boolean visible) {
                    if (skipAnnotation(descriptor)) return null;
                    return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible);
                  }

                  @Override
                  public AnnotationVisitor visitLocalVariableAnnotation(
                      int typeRef,
                      TypePath typePath,
                      Label[] start,
                      Label[] end,
                      int[] index,
                      String descriptor,
                      boolean visible) {
                    if (skipAnnotation(descriptor)) return null;
                    return super.visitLocalVariableAnnotation(
                        typeRef, typePath, start, end, index, descriptor, visible);
                  }

                  @Override
                  public void visitEnd() {
                    if ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) == 0) {
                      visitCode();
                      Type returnType = Type.getReturnType(descriptor);
                      switch (returnType.getSort()) {
                        case Type.VOID:
                          visitInsn(Opcodes.RETURN);
                          break;
                        case Type.BOOLEAN:
                        case Type.CHAR:
                        case Type.BYTE:
                        case Type.SHORT:
                        case Type.INT:
                          visitInsn(Opcodes.ICONST_0);
                          visitInsn(Opcodes.IRETURN);
                          break;
                        case Type.FLOAT:
                          visitInsn(Opcodes.FCONST_0);
                          visitInsn(Opcodes.FRETURN);
                          break;
                        case Type.LONG:
                          visitInsn(Opcodes.LCONST_0);
                          visitInsn(Opcodes.LRETURN);
                          break;
                        case Type.DOUBLE:
                          visitInsn(Opcodes.DCONST_0);
                          visitInsn(Opcodes.DRETURN);
                          break;
                        case Type.ARRAY:
                        case Type.OBJECT:
                          visitInsn(Opcodes.ACONST_NULL);
                          visitInsn(Opcodes.ARETURN);
                          break;
                        default:
                          throw new AssertionError();
                      }
                      visitMaxs(0, 0);
                    }
                    super.visitEnd();
                  }
                };
              }
            };
        classReader.accept(classVisitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG);
        byte[] classFile = classWriter.toByteArray();
        jdkApiJar.putNextEntry(new ZipEntry(name));
        jdkApiJar.write(classFile, 0, classFile.length);
        jdkApiJar.closeEntry();
      }
    }
    jdkApiJar.close();
    rtJarZipFile.close();
  }
}

Merge request reports