/* * Copyright 2007 the original author or authors. * * 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 jdave.unfinalizer.internal; import java.io.IOException; import java.io.InputStream; import jdave.Specification; import jdave.junit4.JDaveRunner; import jdave.org.objectweb.asm.ClassAdapter; import jdave.org.objectweb.asm.ClassReader; import jdave.org.objectweb.asm.ClassVisitor; import jdave.org.objectweb.asm.ClassWriter; import jdave.org.objectweb.asm.MethodVisitor; import jdave.org.objectweb.asm.Opcodes; import jdave.unfinalizer.fake.ClassWithFinalMethod; import jdave.unfinalizer.fake.FinalClass; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.runner.RunWith; /** * @author Tuomas Karkkainen */ @RunWith(JDaveRunner.class) public class UnfinalizingClassVisitorDelegatorSpec extends Specification<Void> { public class WhenClassIsFinal { public void isMadeNonFinal() throws IOException { final byte[] nonFinalClass = new UnfinalizingClassVisitorDelegator().transform(getOriginalClassAsByteArray(FinalClass.class)); specify(nonFinalClass, isNotFinal()); } } public class WhenAMethodIsFinal { public void theMethodIsMadeNonFinal() throws IOException { final byte[] classWithoutFinalMethods = new UnfinalizingClassVisitorDelegator() .transform(getOriginalClassAsByteArray(ClassWithFinalMethod.class)); specify(classWithoutFinalMethods, hasNoFinalMethods()); } } public byte[] getOriginalClassAsByteArray(final Class<?> clazz) throws IOException { final InputStream originalClass = ClassLoader.getSystemClassLoader().getResourceAsStream( clazz.getName().replace('.', '/') + ".class"); final byte[] originalClassBytes = new byte[originalClass.available()]; originalClass.read(originalClassBytes); originalClass.close(); return originalClassBytes; } private Matcher<byte[]> isNotFinal() { return new BaseMatcher<byte[]>() { public void describeTo(final Description description) { } public boolean matches(final Object item) { final byte[] classBytes = (byte[]) item; final ClassReader classReader = new ClassReader(classBytes); return (classReader.getAccess() & Opcodes.ACC_FINAL) == 0; } }; } private Matcher<byte[]> hasNoFinalMethods() { return new BaseMatcher<byte[]>() { public boolean matches(final Object item) { final byte[] classWithoutFinalMethods = (byte[]) item; final ClassReader classReader = new ClassReader(classWithoutFinalMethods); final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); final FinalMethodFindingClassAdapter classAdapter = new FinalMethodFindingClassAdapter(writer); classReader.accept(classAdapter, ClassReader.SKIP_FRAMES); return !classAdapter.hasFinalMethod; } public void describeTo(final Description description) { } }; } private static final class FinalMethodFindingClassAdapter extends ClassAdapter { private boolean hasFinalMethod; private FinalMethodFindingClassAdapter(final ClassVisitor classVisitor) { super(classVisitor); } @Override public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { final boolean methodIsFinal = (access & Opcodes.ACC_FINAL) != 0; if (methodIsFinal) { hasFinalMethod = methodIsFinal; } return super.visitMethod(access, name, desc, signature, exceptions); } } }