use of org.objectweb.asm.commons.SerialVersionUIDAdder in project evosuite by EvoSuite.
the class BytecodeInstrumentation method transformBytes.
/**
* <p>
* transformBytes
* </p>
*
* @param className
* a {@link java.lang.String} object.
* @param reader
* a {@link org.objectweb.asm.ClassReader} object.
* @return an array of byte.
*/
public byte[] transformBytes(ClassLoader classLoader, String className, ClassReader reader) {
int readFlags = ClassReader.SKIP_FRAMES;
if (Properties.INSTRUMENTATION_SKIP_DEBUG)
readFlags |= ClassReader.SKIP_DEBUG;
String classNameWithDots = ResourceList.getClassNameFromResourcePath(className);
if (!checkIfCanInstrument(classNameWithDots)) {
throw new RuntimeException("Should not transform a shared class (" + classNameWithDots + ")! Load by parent (JVM) classloader.");
}
TransformationStatistics.reset();
/*
* To use COMPUTE_FRAMES we need to remove JSR commands. Therefore, we
* have a JSRInlinerAdapter in NonTargetClassAdapter as well as
* CFGAdapter.
*/
int asmFlags = ClassWriter.COMPUTE_FRAMES;
ClassWriter writer = new ComputeClassWriter(asmFlags);
ClassVisitor cv = writer;
if (logger.isDebugEnabled()) {
cv = new TraceClassVisitor(cv, new PrintWriter(System.err));
}
if (Properties.RESET_STATIC_FIELDS) {
cv = new StaticAccessClassAdapter(cv, className);
}
if (Properties.PURE_INSPECTORS) {
CheapPurityAnalyzer purityAnalyzer = CheapPurityAnalyzer.getInstance();
cv = new PurityAnalysisClassVisitor(cv, className, purityAnalyzer);
}
if (Properties.MAX_LOOP_ITERATIONS >= 0) {
cv = new LoopCounterClassAdapter(cv);
}
// Apply transformations to class under test and its owned classes
if (DependencyAnalysis.shouldAnalyze(classNameWithDots)) {
logger.debug("Applying target transformation to class " + classNameWithDots);
if (!Properties.TEST_CARVING && Properties.MAKE_ACCESSIBLE) {
cv = new AccessibleClassAdapter(cv, className);
}
cv = new RemoveFinalClassAdapter(cv);
cv = new ExecutionPathClassAdapter(cv, className);
cv = new CFGClassAdapter(classLoader, cv, className);
if (Properties.EXCEPTION_BRANCHES) {
cv = new ExceptionTransformationClassAdapter(cv, className);
}
if (Properties.ERROR_BRANCHES) {
cv = new ErrorConditionClassAdapter(cv, className);
}
} else {
logger.debug("Not applying target transformation");
cv = new NonTargetClassAdapter(cv, className);
if (Properties.MAKE_ACCESSIBLE) {
cv = new AccessibleClassAdapter(cv, className);
}
// to create the CFG first
if (Properties.TT && classNameWithDots.startsWith(Properties.CLASS_PREFIX)) {
cv = new CFGClassAdapter(classLoader, cv, className);
}
}
// Collect constant values for the value pool
cv = new PrimitiveClassAdapter(cv, className);
if (Properties.RESET_STATIC_FIELDS) {
cv = handleStaticReset(className, cv);
}
// Mock instrumentation (eg File and TCP).
if (TestSuiteWriterUtils.needToUseAgent()) {
cv = new MethodCallReplacementClassAdapter(cv, className);
/*
* If the class is serializable, then doing any change (adding hashCode, static reset, etc)
* will change the serialVersionUID if it is not defined in the class.
* Hence, if it is not defined, we have to define it to
* avoid problems in serialising the class, as reading Master will not do instrumentation.
* The serialVersionUID HAS to be the same as the un-instrumented class
*/
if (RuntimeSettings.applyUIDTransformation)
cv = new SerialVersionUIDAdder(cv);
}
// Testability Transformations
if (classNameWithDots.startsWith(Properties.PROJECT_PREFIX) || (!Properties.TARGET_CLASS_PREFIX.isEmpty() && classNameWithDots.startsWith(Properties.TARGET_CLASS_PREFIX)) || shouldTransform(classNameWithDots)) {
ClassNode cn = new AnnotatedClassNode();
reader.accept(cn, readFlags);
logger.info("Starting transformation of " + className);
if (Properties.STRING_REPLACEMENT) {
StringTransformation st = new StringTransformation(cn);
if (isTargetClassName(classNameWithDots) || shouldTransform(classNameWithDots))
cn = st.transform();
}
ComparisonTransformation cmp = new ComparisonTransformation(cn);
if (isTargetClassName(classNameWithDots) || shouldTransform(classNameWithDots)) {
cn = cmp.transform();
ContainerTransformation ct = new ContainerTransformation(cn);
cn = ct.transform();
}
if (shouldTransform(classNameWithDots)) {
logger.info("Testability Transforming " + className);
BooleanTestabilityTransformation tt = new BooleanTestabilityTransformation(cn, classLoader);
try {
cn = tt.transform();
} catch (Throwable t) {
throw new Error(t);
}
logger.info("Testability Transformation done: " + className);
}
// -----
cn.accept(cv);
if (Properties.TEST_CARVING && TransformerUtil.isClassConsideredForInstrumentation(className)) {
return handleCarving(className, writer);
}
} else {
reader.accept(cv, readFlags);
}
return writer.toByteArray();
}
use of org.objectweb.asm.commons.SerialVersionUIDAdder in project atlas by alibaba.
the class TBIncrementalVisitor method instrumentClass.
@Nullable
public static File instrumentClass(int targetApiLevel, @NonNull File inputRootDirectory, @NonNull File inputFile, @NonNull File outputDirectory, @NonNull VisitorBuilder visitorBuilder, @NonNull ILogger logger, InjectErrorListener injectErrorListener, boolean addSerialVersionUID, boolean patchInitMethod, boolean patchEachMethod, boolean supportAddCallSuper, int count) throws IOException {
byte[] classBytes;
String path = FileUtils.relativePath(inputFile, inputRootDirectory);
// the override class is requested.
if (!isClassEligibleForInstantRun(inputFile)) {
if (injectErrorListener != null) {
injectErrorListener.onError(ErrorType.R_CLASS);
}
if (visitorBuilder.getOutputType() == OutputType.INSTRUMENT) {
File outputFile = new File(outputDirectory, path);
Files.createParentDirs(outputFile);
Files.copy(inputFile, outputFile);
return outputFile;
} else {
return null;
}
}
classBytes = Files.toByteArray(inputFile);
ClassReader classReader = new ClassReader(classBytes);
// override the getCommonSuperClass to use the thread context class loader instead of
// the system classloader. This is useful as ASM needs to load classes from the project
// which the system classloader does not have visibility upon.
// TODO: investigate if there is not a simpler way than overriding.
ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_FRAMES) {
@Override
protected String getCommonSuperClass(final String type1, final String type2) {
Class<?> c, d;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
c = Class.forName(type1.replace('/', '.'), false, classLoader);
d = Class.forName(type2.replace('/', '.'), false, classLoader);
} catch (ClassNotFoundException e) {
// This may happen if we're processing class files which reference APIs not
// available on the target device. In this case return a dummy value, since this
// is ignored during dx compilation.
System.err.println("can not find superClass:" + type1 + " or " + type2);
return "instant/run/NoCommonSuperClass";
} catch (Throwable e) {
throw new RuntimeException(type1 + ":" + type2, e);
}
if (c.isAssignableFrom(d)) {
return type1;
}
if (d.isAssignableFrom(c)) {
return type2;
}
if (c.isInterface() || d.isInterface()) {
return "java/lang/Object";
} else {
do {
c = c.getSuperclass();
} while (!c.isAssignableFrom(d));
return c.getName().replace('.', '/');
}
}
};
ClassNode classNode = AsmUtils.readClass(classReader);
boolean hasOtherMethod = false;
if (classNode != null && classNode.methods != null) {
for (Object methodNode : classNode.methods) {
if (methodNode instanceof MethodNode) {
if (!((MethodNode) methodNode).name.equals(ByteCodeUtils.CLASS_INITIALIZER) && !((MethodNode) methodNode).name.equals(ByteCodeUtils.CONSTRUCTOR)) {
hasOtherMethod = true;
}
}
}
}
// when dealing with interface, we just copy the inputFile over without any changes unless
// this is a package private interface.
AccessRight accessRight = AccessRight.fromNodeAccess(classNode.access);
File outputFile = new File(outputDirectory, path);
if (!hasOtherMethod) {
if (visitorBuilder.getOutputType() == OutputType.INSTRUMENT) {
Files.createParentDirs(outputFile);
Files.write(classBytes, outputFile);
return outputFile;
} else {
return null;
}
}
if ((classNode.access & Opcodes.ACC_INTERFACE) != 0) {
if (visitorBuilder.getOutputType() == OutputType.INSTRUMENT) {
// don't change the name of interfaces.
if (injectErrorListener != null) {
injectErrorListener.onError(ErrorType.INTERFACE);
}
Files.createParentDirs(outputFile);
if (accessRight == AccessRight.PACKAGE_PRIVATE) {
classNode.access = classNode.access | Opcodes.ACC_PUBLIC;
classNode.accept(classWriter);
Files.write(classWriter.toByteArray(), outputFile);
} else {
// just copy the input file over, no change.
Files.write(classBytes, outputFile);
}
return outputFile;
} else {
return null;
}
}
Class<?> c, d;
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
c = Class.forName(classNode.name.replace('/', '.'), false, classLoader);
for (Imatcher imatcher : MatcherCreator.getMatchers()) {
d = ((ImplementsMatcher) imatcher).getClazz(classLoader);
if (d.isAssignableFrom(c)) {
if (visitorBuilder.getOutputType() == OutputType.INSTRUMENT) {
// don't change the name of interfaces.
if (injectErrorListener != null) {
injectErrorListener.onError(ErrorType.IMPLEMENTS);
}
Files.createParentDirs(outputFile);
// just copy the input file over, no change.
Files.write(classBytes, outputFile);
return outputFile;
} else {
return null;
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
AsmUtils.DirectoryBasedClassReader directoryClassReader = new AsmUtils.DirectoryBasedClassReader(getBinaryFolder(inputFile, classNode));
// if we are targeting a more recent version than the current device, disable instant run
// for that class.
List<ClassNode> parentsNodes = isClassTargetingNewerPlatform(targetApiLevel, TARGET_API_TYPE, directoryClassReader, classNode, logger) ? ImmutableList.of() : AsmUtils.parseParents(logger, directoryClassReader, classNode, targetApiLevel);
// if we could not determine the parent hierarchy, disable instant run.
if (parentsNodes.isEmpty() || isPackageInstantRunDisabled(inputFile)) {
if (visitorBuilder.getOutputType() == OutputType.INSTRUMENT) {
if (injectErrorListener != null) {
if (parentsNodes.isEmpty()) {
injectErrorListener.onError(ErrorType.NEW_API);
} else {
injectErrorListener.onError(ErrorType.PACKAGE_DISABLED);
}
}
Files.createParentDirs(outputFile);
Files.write(classBytes, outputFile);
return outputFile;
} else {
return null;
}
}
// Map<String, TBIncrementalSupportVisitor.MethodReference> methods = new HashMap<>();
// for (ClassNode pN : parentsNodes) {
// TBIncrementalSupportVisitor.addAllNewMethods(classNode,pN,methods, null);
// }
// if (methods.size() > count) {
// if (visitorBuilder.getOutputType() == OutputType.INSTRUMENT) {
// if (injectErrorListener != null) {
// injectErrorListener.onError(ErrorType.SUB_EXCEEDED);
// }
// Files.createParentDirs(outputFile);
// Files.write(classBytes, outputFile);
// return outputFile;
// } else {
// return null;
// }
// }
outputFile = new File(outputDirectory, visitorBuilder.getMangledRelativeClassFilePath(path));
Files.createParentDirs(outputFile);
IncrementalVisitor visitor = visitorBuilder.build(classNode, parentsNodes, classWriter, logger);
if (visitor instanceof TBIncrementalSupportVisitor) {
((TBIncrementalSupportVisitor) visitor).setPatchInitMethod(patchInitMethod);
((TBIncrementalSupportVisitor) visitor).setSupportEachMethod(patchEachMethod);
((TBIncrementalSupportVisitor) visitor).setSupportAddCallSuper(supportAddCallSuper);
}
if (visitorBuilder.getOutputType() == OutputType.INSTRUMENT) {
/*
* Classes that do not have a serial version unique identifier, will be updated to
* contain one. This is accomplished by using the {@link SerialVersionUIDAdder} class
* visitor that is added when this visitor is created (see the constructor). This way,
* the serialVersionUID is the same for instrumented and non-instrumented classes. All
* classes will have a serialVersionUID, so if some of the classes that is extended
* starts implementing {@link java.io.Serializable}, serialization and deserialization
* will continue to work correctly.
*/
if (addSerialVersionUID) {
classNode.accept(new SerialVersionUIDAdder(visitor));
} else {
classNode.accept(visitor);
}
} else {
classNode.accept(visitor);
}
Files.write(classWriter.toByteArray(), outputFile);
return outputFile;
}
Aggregations