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; }