protected boolean writeClassFileCheck(IFile file, String fileName, byte[] newBytes)
     throws CoreException {
   try {
     byte[] oldBytes = Util.getResourceContentsAsByteArray(file);
     notEqual:
     if (newBytes.length == oldBytes.length) {
       for (int i = newBytes.length; --i >= 0; ) if (newBytes[i] != oldBytes[i]) break notEqual;
       return false; // bytes are identical so skip them
     }
     URI location = file.getLocationURI();
     if (location == null) return false; // unable to determine location of this class file
     String filePath = location.getSchemeSpecificPart();
     ClassFileReader reader = new ClassFileReader(oldBytes, filePath.toCharArray());
     // ignore local types since they're only visible inside a single method
     if (!(reader.isLocal() || reader.isAnonymous()) && reader.hasStructuralChanges(newBytes)) {
       if (JavaBuilder.DEBUG)
         System.out.println("Type has structural changes " + fileName); // $NON-NLS-1$
       addDependentsOf(new Path(fileName), true);
       this.newState.wasStructurallyChanged(fileName);
     }
   } catch (ClassFormatException e) {
     addDependentsOf(new Path(fileName), true);
     this.newState.wasStructurallyChanged(fileName);
   }
   return true;
 }
 /**
  * Create a method to initialise the annotation class
  *
  * @param aClass the class this method belongs to
  * @param constantPool for the class
  * @param memRef the member reference corresponding to this method
  * @param objectInitIndex an index into the constant pool for a method reference to
  *     java.lang.Object.<init>
  * @param aFields
  * @param aMethods
  * @return the created method
  */
 static RVMMethod createAnnotationInit(
     TypeReference aClass,
     int[] constantPool,
     MemberReference memRef,
     int objectInitIndex,
     RVMField[] aFields,
     RVMMethod[] aMethods,
     int[] defaultConstants) {
   byte[] bytecode = new byte[6 + (defaultConstants.length * 7)];
   bytecode[0] = (byte) JBC_aload_0; // stack[0] = this
   bytecode[1] = (byte) JBC_aload_1; // stack[1] = instanceof RVMAnnotation
   bytecode[2] = (byte) JBC_invokespecial;
   bytecode[3] = (byte) (objectInitIndex >>> 8);
   bytecode[4] = (byte) objectInitIndex;
   for (int i = 0, j = 0; i < aMethods.length; i++) {
     Object value = aMethods[i].getAnnotationDefault();
     if (value != null) {
       bytecode[(j * 7) + 5 + 0] = (byte) JBC_aload_0; // stack[0] = this
       byte literalType = ClassFileReader.getLiteralDescription(constantPool, defaultConstants[j]);
       if (literalType != CP_LONG && literalType != CP_DOUBLE) {
         bytecode[(j * 7) + 5 + 1] = (byte) JBC_ldc_w; // stack[1] = value
       } else {
         bytecode[(j * 7) + 5 + 1] = (byte) JBC_ldc2_w; // stack[1&2] = value
       }
       bytecode[(j * 7) + 5 + 2] = (byte) (defaultConstants[j] >>> 8);
       bytecode[(j * 7) + 5 + 3] = (byte) defaultConstants[j];
       bytecode[(j * 7) + 5 + 4] = (byte) JBC_putfield;
       bytecode[(j * 7) + 5 + 5] = (byte) (i >>> 8);
       bytecode[(j * 7) + 5 + 6] = (byte) i;
       j++;
     }
   }
   bytecode[bytecode.length - 1] = (byte) JBC_return;
   return new NormalMethod(
       aClass,
       memRef,
       (short) (ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC),
       null,
       (short) 2,
       (short) 3,
       bytecode,
       null,
       null,
       null,
       constantPool,
       null,
       null,
       null,
       null);
 }
  @BeforeClass
  public static void findDependencies() throws Exception {
    Path path = Paths.get("target/classes");
    Archive archive = new Archive(path, ClassFileReader.newInstance(path)) {};
    Finder finder = Dependencies.getClassDependencyFinder();

    archive
        .reader()
        .getClassFiles()
        .forEach(
            classFile ->
                StreamSupport.stream(finder.findDependencies(classFile).spliterator(), false)
                    .filter(dependency -> !isAnnotation(dependency))
                    .filter(dependency -> !self(dependency))
                    .forEach(
                        dependency ->
                            packageDependencies
                                .computeIfAbsent(
                                    dependency.getOrigin().getPackageName(), key -> new TreeSet<>())
                                .add(dependency.getTarget().getPackageName())));
  }
 /**
  * Create a method for reflectively invoking this method
  *
  * @param reflectionClass the class this method will belong to
  * @param constantPool for the class
  * @param memRef the member reference corresponding to this method
  * @return the created method
  */
 RVMMethod createReflectionMethod(
     TypeReference reflectionClass, int[] constantPool, MethodReference memRef) {
   TypeReference[] parameters = getParameterTypes();
   int numParams = parameters.length;
   byte[] bytecodes;
   boolean interfaceCall = false;
   int curBC = 0;
   if (!isStatic()) {
     if (!getDeclaringClass().isInterface()) {
       // virtual call
       bytecodes = new byte[8 * numParams + 8];
     } else {
       // interface call
       bytecodes = new byte[8 * numParams + 10];
       interfaceCall = true;
     }
     bytecodes[curBC] = JBC_aload_1;
     curBC++;
   } else {
     // static call
     bytecodes = new byte[8 * numParams + 7];
   }
   for (int i = 0; i < numParams; i++) {
     if (parameters[i].isVoidType()) {
       bytecodes[curBC] =
           bytecodes[curBC + 1] =
               bytecodes[curBC + 2] =
                   bytecodes[curBC + 3] =
                       bytecodes[curBC + 4] =
                           bytecodes[curBC + 5] =
                               bytecodes[curBC + 6] = bytecodes[curBC + 7] = (byte) JBC_nop;
       continue;
     }
     bytecodes[curBC] = (byte) JBC_aload_2;
     bytecodes[curBC + 1] = (byte) JBC_sipush;
     bytecodes[curBC + 2] = (byte) (i >>> 8);
     bytecodes[curBC + 3] = (byte) i;
     bytecodes[curBC + 4] = (byte) JBC_aaload;
     if (!parameters[i].isPrimitiveType()) {
       bytecodes[curBC + 5] = (byte) JBC_checkcast;
       if (VM.VerifyAssertions) VM._assert(parameters[i].getId() != 0);
       constantPool[i + 1] = ClassFileReader.packCPEntry(CP_CLASS, parameters[i].getId());
       bytecodes[curBC + 6] = (byte) ((i + 1) >>> 8);
       bytecodes[curBC + 7] = (byte) (i + 1);
     } else if (parameters[i].isWordLikeType()) {
       bytecodes[curBC + 5] = bytecodes[curBC + 6] = bytecodes[curBC + 7] = (byte) JBC_nop;
     } else {
       bytecodes[curBC + 5] = (byte) JBC_invokestatic;
       MemberReference unboxMethod;
       if (parameters[i].isBooleanType()) {
         unboxMethod =
             MethodReference.findOrCreate(
                 baseReflectionClass,
                 Atom.findOrCreateUnicodeAtom("unboxAsBoolean"),
                 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)Z"));
       } else if (parameters[i].isByteType()) {
         unboxMethod =
             MethodReference.findOrCreate(
                 baseReflectionClass,
                 Atom.findOrCreateUnicodeAtom("unboxAsByte"),
                 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)B"));
       } else if (parameters[i].isShortType()) {
         unboxMethod =
             MethodReference.findOrCreate(
                 baseReflectionClass,
                 Atom.findOrCreateUnicodeAtom("unboxAsShort"),
                 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)S"));
       } else if (parameters[i].isCharType()) {
         unboxMethod =
             MethodReference.findOrCreate(
                 baseReflectionClass,
                 Atom.findOrCreateUnicodeAtom("unboxAsChar"),
                 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)C"));
       } else if (parameters[i].isIntType()) {
         unboxMethod =
             MethodReference.findOrCreate(
                 baseReflectionClass,
                 Atom.findOrCreateUnicodeAtom("unboxAsInt"),
                 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)I"));
       } else if (parameters[i].isLongType()) {
         unboxMethod =
             MethodReference.findOrCreate(
                 baseReflectionClass,
                 Atom.findOrCreateUnicodeAtom("unboxAsLong"),
                 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)J"));
       } else if (parameters[i].isFloatType()) {
         unboxMethod =
             MethodReference.findOrCreate(
                 baseReflectionClass,
                 Atom.findOrCreateUnicodeAtom("unboxAsFloat"),
                 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)F"));
       } else {
         if (VM.VerifyAssertions) VM._assert(parameters[i].isDoubleType());
         unboxMethod =
             MethodReference.findOrCreate(
                 baseReflectionClass,
                 Atom.findOrCreateUnicodeAtom("unboxAsDouble"),
                 Atom.findOrCreateUnicodeAtom("(Ljava/lang/Object;)D"));
       }
       constantPool[i + 1] = ClassFileReader.packCPEntry(CP_MEMBER, unboxMethod.getId());
       bytecodes[curBC + 6] = (byte) ((i + 1) >>> 8);
       bytecodes[curBC + 7] = (byte) (i + 1);
     }
     curBC += 8;
   }
   if (isStatic()) {
     bytecodes[curBC] = (byte) JBC_invokestatic;
   } else if (isObjectInitializer() || isPrivate()) {
     bytecodes[curBC] = (byte) JBC_invokespecial;
   } else if (interfaceCall) {
     bytecodes[curBC] = (byte) JBC_invokeinterface;
   } else {
     bytecodes[curBC] = (byte) JBC_invokevirtual;
   }
   constantPool[numParams + 1] = ClassFileReader.packCPEntry(CP_MEMBER, getId());
   bytecodes[curBC + 1] = (byte) ((numParams + 1) >>> 8);
   bytecodes[curBC + 2] = (byte) (numParams + 1);
   if (interfaceCall) {
     // invokeinterface bytecodes are historically longer than others
     curBC += 2;
   }
   TypeReference returnType = getReturnType();
   if (!returnType.isPrimitiveType() || returnType.isWordLikeType()) {
     bytecodes[curBC + 3] = (byte) JBC_nop;
     bytecodes[curBC + 4] = (byte) JBC_nop;
     bytecodes[curBC + 5] = (byte) JBC_nop;
   } else if (returnType.isVoidType()) {
     bytecodes[curBC + 3] = (byte) JBC_aconst_null;
     bytecodes[curBC + 4] = (byte) JBC_nop;
     bytecodes[curBC + 5] = (byte) JBC_nop;
   } else {
     MemberReference boxMethod;
     if (returnType.isBooleanType()) {
       boxMethod =
           MethodReference.findOrCreate(
               baseReflectionClass,
               Atom.findOrCreateUnicodeAtom("boxAsBoolean"),
               Atom.findOrCreateUnicodeAtom("(Z)Ljava/lang/Object;"));
     } else if (returnType.isByteType()) {
       boxMethod =
           MethodReference.findOrCreate(
               baseReflectionClass,
               Atom.findOrCreateUnicodeAtom("boxAsByte"),
               Atom.findOrCreateUnicodeAtom("(B)Ljava/lang/Object;"));
     } else if (returnType.isShortType()) {
       boxMethod =
           MethodReference.findOrCreate(
               baseReflectionClass,
               Atom.findOrCreateUnicodeAtom("boxAsShort"),
               Atom.findOrCreateUnicodeAtom("(S)Ljava/lang/Object;"));
     } else if (returnType.isCharType()) {
       boxMethod =
           MethodReference.findOrCreate(
               baseReflectionClass,
               Atom.findOrCreateUnicodeAtom("boxAsChar"),
               Atom.findOrCreateUnicodeAtom("(C)Ljava/lang/Object;"));
     } else if (returnType.isIntType()) {
       boxMethod =
           MethodReference.findOrCreate(
               baseReflectionClass,
               Atom.findOrCreateUnicodeAtom("boxAsInt"),
               Atom.findOrCreateUnicodeAtom("(I)Ljava/lang/Object;"));
     } else if (returnType.isLongType()) {
       boxMethod =
           MethodReference.findOrCreate(
               baseReflectionClass,
               Atom.findOrCreateUnicodeAtom("boxAsLong"),
               Atom.findOrCreateUnicodeAtom("(J)Ljava/lang/Object;"));
     } else if (returnType.isFloatType()) {
       boxMethod =
           MethodReference.findOrCreate(
               baseReflectionClass,
               Atom.findOrCreateUnicodeAtom("boxAsFloat"),
               Atom.findOrCreateUnicodeAtom("(F)Ljava/lang/Object;"));
     } else {
       if (VM.VerifyAssertions) VM._assert(returnType.isDoubleType());
       boxMethod =
           MethodReference.findOrCreate(
               baseReflectionClass,
               Atom.findOrCreateUnicodeAtom("boxAsDouble"),
               Atom.findOrCreateUnicodeAtom("(D)Ljava/lang/Object;"));
     }
     constantPool[numParams + 2] = ClassFileReader.packCPEntry(CP_MEMBER, boxMethod.getId());
     bytecodes[curBC + 3] = (byte) JBC_invokestatic;
     bytecodes[curBC + 4] = (byte) ((numParams + 2) >>> 8);
     bytecodes[curBC + 5] = (byte) (numParams + 2);
   }
   bytecodes[curBC + 6] = (byte) JBC_areturn;
   return new NormalMethod(
       reflectionClass,
       memRef,
       (short) (ACC_PUBLIC | ACC_FINAL | ACC_SYNTHETIC),
       null,
       (short) 3,
       (short) (getParameterWords() + 2),
       bytecodes,
       null,
       null,
       null,
       constantPool,
       null,
       null,
       null,
       null);
 }
  /**
   * Called from {@link ClassFileReader#readClass(TypeReference,DataInputStream)} to create an
   * instance of a RVMMethod by reading the relevant data from the argument bytecode stream.
   *
   * @param declaringClass the TypeReference of the class being loaded
   * @param constantPool the constantPool of the RVMClass object that's being constructed
   * @param memRef the canonical memberReference for this member.
   * @param modifiers modifiers associated with this member.
   * @param input the DataInputStream to read the method's attributes from
   */
  static RVMMethod readMethod(
      TypeReference declaringClass,
      int[] constantPool,
      MemberReference memRef,
      short modifiers,
      DataInputStream input)
      throws IOException {
    short tmp_localWords = 0;
    short tmp_operandWords = 0;
    byte[] tmp_bytecodes = null;
    ExceptionHandlerMap tmp_exceptionHandlerMap = null;
    TypeReference[] tmp_exceptionTypes = null;
    int[] tmp_lineNumberMap = null;
    LocalVariableTable tmp_localVariableTable = null;
    Atom tmp_signature = null;
    RVMAnnotation[] annotations = null;
    RVMAnnotation[][] parameterAnnotations = null;
    Object tmp_annotationDefault = null;

    // Read the attributes
    for (int i = 0, n = input.readUnsignedShort(); i < n; i++) {
      Atom attName = ClassFileReader.getUtf(constantPool, input.readUnsignedShort());
      int attLength = input.readInt();

      // Only bother to interpret non-boring Method attributes
      if (attName == RVMClassLoader.codeAttributeName) {
        tmp_operandWords = input.readShort();
        tmp_localWords = input.readShort();
        tmp_bytecodes = new byte[input.readInt()];
        input.readFully(tmp_bytecodes);
        tmp_exceptionHandlerMap = ExceptionHandlerMap.readExceptionHandlerMap(input, constantPool);

        // Read the attributes portion of the code attribute
        for (int j = 0, n2 = input.readUnsignedShort(); j < n2; j++) {
          attName = ClassFileReader.getUtf(constantPool, input.readUnsignedShort());
          attLength = input.readInt();

          if (attName == RVMClassLoader.lineNumberTableAttributeName) {
            int cnt = input.readUnsignedShort();
            if (cnt != 0) {
              tmp_lineNumberMap = new int[cnt];
              for (int k = 0; k < cnt; k++) {
                int startPC = input.readUnsignedShort();
                int lineNumber = input.readUnsignedShort();
                tmp_lineNumberMap[k] = (lineNumber << BITS_IN_SHORT) | startPC;
              }
            }
          } else if (attName == RVMClassLoader.localVariableTableAttributeName) {
            tmp_localVariableTable = LocalVariableTable.readLocalVariableTable(input, constantPool);
          } else {
            // All other entries in the attribute portion of the code attribute are boring.
            int skippedAmount = input.skipBytes(attLength);
            if (skippedAmount != attLength) {
              throw new IOException("Unexpected short skip");
            }
          }
        }
      } else if (attName == RVMClassLoader.exceptionsAttributeName) {
        int cnt = input.readUnsignedShort();
        if (cnt != 0) {
          tmp_exceptionTypes = new TypeReference[cnt];
          for (int j = 0, m = tmp_exceptionTypes.length; j < m; ++j) {
            tmp_exceptionTypes[j] =
                ClassFileReader.getTypeRef(constantPool, input.readUnsignedShort());
          }
        }
      } else if (attName == RVMClassLoader.syntheticAttributeName) {
        modifiers |= ACC_SYNTHETIC;
      } else if (attName == RVMClassLoader.signatureAttributeName) {
        tmp_signature = ClassFileReader.getUtf(constantPool, input.readUnsignedShort());
      } else if (attName == RVMClassLoader.runtimeVisibleAnnotationsAttributeName) {
        annotations =
            AnnotatedElement.readAnnotations(constantPool, input, declaringClass.getClassLoader());
      } else if (attName == RVMClassLoader.runtimeVisibleParameterAnnotationsAttributeName) {
        int numParameters = input.readByte() & 0xFF;
        parameterAnnotations = new RVMAnnotation[numParameters][];
        for (int a = 0; a < numParameters; ++a) {
          parameterAnnotations[a] =
              AnnotatedElement.readAnnotations(
                  constantPool, input, declaringClass.getClassLoader());
        }
      } else if (attName == RVMClassLoader.annotationDefaultAttributeName) {
        try {
          tmp_annotationDefault =
              RVMAnnotation.readValue(
                  memRef.asMethodReference().getReturnType(),
                  constantPool,
                  input,
                  declaringClass.getClassLoader());
        } catch (ClassNotFoundException e) {
          throw new Error(e);
        }
      } else {
        // all other method attributes are boring
        int skippedAmount = input.skipBytes(attLength);
        if (skippedAmount != attLength) {
          throw new IOException("Unexpected short skip");
        }
      }
    }
    RVMMethod method;
    if ((modifiers & ACC_NATIVE) != 0) {
      method =
          new NativeMethod(
              declaringClass,
              memRef,
              modifiers,
              tmp_exceptionTypes,
              tmp_signature,
              annotations,
              parameterAnnotations,
              tmp_annotationDefault);
    } else if ((modifiers & ACC_ABSTRACT) != 0) {
      method =
          new AbstractMethod(
              declaringClass,
              memRef,
              modifiers,
              tmp_exceptionTypes,
              tmp_signature,
              annotations,
              parameterAnnotations,
              tmp_annotationDefault);

    } else {
      method =
          new NormalMethod(
              declaringClass,
              memRef,
              modifiers,
              tmp_exceptionTypes,
              tmp_localWords,
              tmp_operandWords,
              tmp_bytecodes,
              tmp_exceptionHandlerMap,
              tmp_lineNumberMap,
              tmp_localVariableTable,
              constantPool,
              tmp_signature,
              annotations,
              parameterAnnotations,
              tmp_annotationDefault);
    }
    return method;
  }