
org.osjava.jardiff.Tools Maven / Gradle / Ivy
/**
* Copyright 2012-2014 Julien Eluard and contributors
* This project includes software developed by Julien Eluard: https://github.com/jeluard/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.osjava.jardiff;
import org.objectweb.asm.Opcodes;
/**
* A set of Tools which do not belong anywhere else in the API at this time.
* This is nasty, but for now, useful.
*
* @author Antony Riley
*/
public final class Tools
{
/**
* Private constructor so this class can't be instantiated.
*/
private Tools() {
/* empty */
}
/**
* Get the java class name given an internal class name.
* This method currently replaces all instances of $ and / with . this
* may not be according to the java language spec, and will almost
* certainly fail for some inner classes.
*
* @param internalName The internal name of the class.
* @return The java class name.
*/
public static final String getClassName(String internalName) {
final StringBuffer ret = new StringBuffer(internalName.length());
for (int i = 0; i < internalName.length(); i++) {
final char ch = internalName.charAt(i);
switch (ch) {
case '$':
case '/':
ret.append('.');
break;
default:
ret.append(ch);
}
}
return ret.toString();
}
private static boolean has(final int value, final int mask) {
return (value & mask) != 0;
}
private static boolean not(final int value, final int mask) {
return (value & mask) == 0;
}
/**
* @deprecated Use {@link #isClassAccessChange(int, int)}.
*/
public static boolean isAccessChange(int oldAccess, int newAccess) {
return isClassAccessChange(oldAccess, newAccess);
}
/**
* Returns whether a class's newAccess is incompatible with oldAccess
* following Java Language Specification, Java SE 7 Edition:
*
* - 13.4.1 abstract Classes
* - If a class that was not declared abstract is changed to be declared abstract,
* then pre-existing binaries that attempt to create new instances of that class
* will throw either an InstantiationError at link time,
* or (if a reflective method is used) an InstantiationException at run time.
* Such changes break backward compatibility!
* - Changing a class that is declared abstract to no longer be declared abstract
* does not break compatibility with pre-existing binaries.
*
* - 13.4.2 final Classes
* - If a class that was not declared final is changed to be declared final,
* then a VerifyError is thrown if a binary of a pre-existing subclass of this class is loaded,
* because final classes can have no subclasses.
* Such changes break functional backward compatibility!
* - Changing a class that is declared final to no longer be declared final
* does not break compatibility with pre-existing binaries.
*
*
*
* @param oldAccess
* @param newAccess
* @return
*/
public static boolean isClassAccessChange(final int oldAccess, final int newAccess) {
if ( not(oldAccess, Opcodes.ACC_ABSTRACT) && has(newAccess, Opcodes.ACC_ABSTRACT) ) {
return true; // 13.4.1 #1
} else if ( not(oldAccess, Opcodes.ACC_FINAL) && has(newAccess, Opcodes.ACC_FINAL) ) {
return true; // 13.4.2 #1
} else {
final int compatibleChanges = Opcodes.ACC_ABSTRACT | // 13.4.1 #2
Opcodes.ACC_FINAL ; // 13.4.2 #2
// FIXME Opcodes.ACC_VOLATILE ?
final int oldAccess2 = oldAccess & ~compatibleChanges;
final int newAccess2 = newAccess & ~compatibleChanges;
return oldAccess2 != newAccess2;
}
}
/**
* Returns whether a field's newAccess is incompatible with oldAccess
* following Java Language Specification, Java SE 7 Edition:
*
* - 13.4.9 final Fields and Constants
* - If a field that was not declared final is changed to be declared final,
* then it can break compatibility with pre-existing binaries that attempt to assign new values to the field.
* - Deleting the keyword final or changing the value to which a non-final field is initialized
* does not break compatibility with existing binaries.
* - If a field is a constant variable (§4.12.4),
* then deleting the keyword final or changing its value
* will not break compatibility with pre-existing binaries by causing them not to run,
* but they will not see any new value for the usage of the field unless they are recompiled.
* This is true even if the usage itself is not a compile-time constant expression (§15.28).
* Such changes break functional backward compatibility!
*
* - 13.4.10 static Fields
* - If a field that is not declared private was not declared static
* and is changed to be declared static, or vice versa,
* then a linkage error, specifically an IncompatibleClassChangeError,
* will result if the field is used by a pre-existing binary which expected a field of the other kind.
* Such changes break backward compatibility!
*
* - 13.4.11. transient Fields
* - Adding or deleting a transient modifier of a field
* does not break compatibility with pre-existing binaries.
*
* - 13.4.11 volatile Fields (JLS 1.0)
* - If a field that is not declared private was not declared volatile
* and is changed to be declared volatile, or vice versa, then a linkage time error,
* specifically an IncompatibleClassChangeError, may result if the field is used
* by a preexisting binary that expected a field of the opposite volatility.
* Such changes break backward compatibility!
*
*
*
* @param oldAccess
* @param newAccess
* @return
*/
public static boolean isFieldAccessChange(final int oldAccess, final int newAccess) {
if ( not(oldAccess, Opcodes.ACC_FINAL) && has(newAccess, Opcodes.ACC_FINAL) ) {
return true; // 13.4.9 #1
} else {
final int compatibleChanges = Opcodes.ACC_FINAL | // 13.4.9 #2
Opcodes.ACC_TRANSIENT; // 13.4.11 #1
final int oldAccess2 = oldAccess & ~compatibleChanges;
final int newAccess2 = newAccess & ~compatibleChanges;
return oldAccess2 != newAccess2;
}
}
/**
* Returns whether a method's newAccess is incompatible with oldAccess
* following Java Language Specification, Java SE 7 Edition:
*
* - 13.4.16 abstract Methods
* - Changing a method that is declared abstract to no longer be declared abstract
* does not break compatibility with pre-existing binaries.
* - Changing a method that is not declared abstract to be declared abstract
* will break compatibility with pre-existing binaries that previously invoked the method, causing an AbstractMethodError.
*
* - 13.4.17 final
* - Changing a method that is declared final to no longer be declared final
* does not break compatibility with pre-existing binaries.
* - Changing an instance method that is not declared final to be declared final
* may break compatibility with existing binaries that depend on the ability to override the method.
* - Changing a class (static) method that is not declared final to be declared final
* does not break compatibility with existing binaries, because the method could not have been overridden.
*
* - 13.4.18 native Methods
* - Adding or deleting a native modifier of a method
* does not break compatibility with pre-existing binaries.
*
* - 13.4.19 static Methods
* - If a method that is not declared private is also declared static (that is, a class method)
* and is changed to not be declared static (that is, to an instance method), or vice versa,
* then compatibility with pre-existing binaries may be broken, resulting in a linkage time error,
* namely an IncompatibleClassChangeError, if these methods are used by the pre-existing binaries.
* Such changes break functional backward compatibility!
*
* - 13.4.20 synchronized Methods
* - Adding or deleting a synchronized modifier of a method
* does not break compatibility with pre-existing binaries.
*
* - 13.4.21 Method and Constructor Throws
* - Changes to the throws clause of methods or constructors
* do not break compatibility with pre-existing binaries; these clauses are checked only at compile time.
*
*
*
* @param oldAccess
* @param newAccess
* @return
*/
public static boolean isMethodAccessChange(final int oldAccess, final int newAccess) {
if ( not(oldAccess, Opcodes.ACC_ABSTRACT) && has(newAccess, Opcodes.ACC_ABSTRACT) ) {
return true; // 13.4.16 #2
} else if ( not(oldAccess, Opcodes.ACC_FINAL) && not(oldAccess, Opcodes.ACC_STATIC) &&
has(newAccess, Opcodes.ACC_FINAL) ) {
return true; // 13.4.17 #2 excluding and #3
} else {
final int compatibleChanges = Opcodes.ACC_ABSTRACT | // 13.4.16 #1
Opcodes.ACC_FINAL | // 13.4.17 #1
Opcodes.ACC_NATIVE | // 13.4.18 #1
Opcodes.ACC_SYNCHRONIZED; // 13.4.20 #1
final int oldAccess2 = oldAccess & ~compatibleChanges;
final int newAccess2 = newAccess & ~compatibleChanges;
return oldAccess2 != newAccess2;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy