Skip to content

Commit

Permalink
Added tests for modifier transformation and adjustment.
Browse files Browse the repository at this point in the history
  • Loading branch information
raphw committed Jul 14, 2016
1 parent 78ff087 commit 568cb14
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 84 deletions.
Expand Up @@ -154,23 +154,26 @@ public interface TypeDescription extends TypeDefinition, TypeVariableSource {
TypeList getDeclaredTypes(); TypeList getDeclaredTypes();


/** /**
* Returns a description of the enclosing method of this type. * Returns a description of the method that encloses this type. If this method is not enclosed by any type or is
* enclosed by the type initializer, {@code null} is returned by this method.
* *
* @return A description of the enclosing method of this type or {@code null} if there is no such method. * @return A description of the enclosing method of this type or {@code null} if there is no such method.
*/ */
MethodDescription getEnclosingMethod(); MethodDescription getEnclosingMethod();


/** /**
* Returns a description of the enclosing type of this type. * Returns a description of this type's enclosing type if any.
* *
* @return A description of the enclosing type of this type or {@code null} if there is no such type. * @return A description of the enclosing type of this type or {@code null} if there is no such type.
*/ */
TypeDescription getEnclosingType(); TypeDescription getEnclosingType();


/** /**
* Returns the type's actual modifiers as present in the class file. For example, a type cannot be {@code private}. * Returns the type's actual modifiers as present in the class file. For example, a type cannot be {@code private}.
* but it modifiers might reflect this property nevertheless if a class was defined as a private inner class. The * but it modifiers might reflect this property nevertheless if a class was defined as a private inner class. The
* returned modifiers take also into account if the type is marked as {@link Deprecated}. * returned modifiers take also into account if the type is marked as {@link Deprecated}. Anonymous classes that are
* enclosed in a static method or the type initializer are additionally marked as {@code final} as it is also done
* by the Java compiler.
* *
* @param superFlag {@code true} if the modifier's super flag should be set. * @param superFlag {@code true} if the modifier's super flag should be set.
* @return The type's actual modifiers. * @return The type's actual modifiers.
Expand Down Expand Up @@ -6999,6 +7002,12 @@ public int getActualModifiers(boolean superFlag) {
} else { } else {
actualModifiers = actualModifiers & ~Opcodes.ACC_STATIC; actualModifiers = actualModifiers & ~Opcodes.ACC_STATIC;
} }
if (isAnonymousClass()) {
MethodDescription enclosingMethod = getEnclosingMethod();
if (enclosingMethod == null || enclosingMethod.isStatic()) {
actualModifiers |= Opcodes.ACC_FINAL;
}
}
return superFlag ? (actualModifiers | Opcodes.ACC_SUPER) : actualModifiers; return superFlag ? (actualModifiers | Opcodes.ACC_SUPER) : actualModifiers;
} }


Expand Down
Expand Up @@ -3098,9 +3098,6 @@ public void visit(int classFileVersionNumber,
public void visitInnerClass(String internalName, String outerName, String innerName, int modifiers) { public void visitInnerClass(String internalName, String outerName, String innerName, int modifiers) {
if (internalName.equals(instrumentedType.getInternalName())) { if (internalName.equals(instrumentedType.getInternalName())) {
modifiers = instrumentedType.getModifiers(); modifiers = instrumentedType.getModifiers();
if (instrumentedType.isAnonymousClass()) {
modifiers = modifiers | Opcodes.ACC_STATIC;
}
} }
super.visitInnerClass(internalName, outerName, innerName, modifiers); super.visitInnerClass(internalName, outerName, innerName, modifiers);
} }
Expand Down
Expand Up @@ -14,15 +14,11 @@
import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ByteArrayClassLoader;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.dynamic.loading.PackageDefinitionStrategy;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy; import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.FixedValue; import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.StubMethod; import net.bytebuddy.implementation.StubMethod;
import net.bytebuddy.implementation.SuperMethodCall; import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.test.scope.EnclosingType;
import net.bytebuddy.test.utility.ClassFileExtraction;
import net.bytebuddy.test.utility.JavaVersionRule; import net.bytebuddy.test.utility.JavaVersionRule;
import net.bytebuddy.test.utility.ObjectPropertyAssertion; import net.bytebuddy.test.utility.ObjectPropertyAssertion;
import net.bytebuddy.utility.JavaConstant; import net.bytebuddy.utility.JavaConstant;
Expand All @@ -36,8 +32,6 @@
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.AccessController;


import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer; import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer;
import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.named;
Expand Down Expand Up @@ -446,69 +440,6 @@ public void testMethodHandleInLegacyConstantPool() throws Exception {
.make(); .make();
} }


@Test
public void testInnerClassChangeModifierTest() throws Exception {
ClassLoader classLoader = new ByteArrayClassLoader(ClassLoadingStrategy.BOOTSTRAP_LOADER,
ClassFileExtraction.of(EnclosingType.class),
null,
AccessController.getContext(),
ByteArrayClassLoader.PersistenceHandler.LATENT,
PackageDefinitionStrategy.NoOp.INSTANCE);
Class<?> redefined = new ByteBuddy()
.redefine(EnclosingType.INNER)
.visit(new InnerClassValidator.Wrapper(EnclosingType.INNER, Opcodes.ACC_PUBLIC))
.modifiers(Visibility.PUBLIC)
.make()
.load(classLoader, ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
assertThat(redefined.isAnonymousClass(), is(EnclosingType.INNER.isAnonymousClass()));
assertThat(redefined.isLocalClass(), is(EnclosingType.INNER.isLocalClass()));
assertThat(redefined.isMemberClass(), is(EnclosingType.INNER.isMemberClass()));
assertThat(redefined.getModifiers(), is(Modifier.PUBLIC | EnclosingType.INNER.getModifiers()));
}

@Test
public void testAnonymousInnerClassChangeModifierTest() throws Exception {
ClassLoader classLoader = new ByteArrayClassLoader(ClassLoadingStrategy.BOOTSTRAP_LOADER,
ClassFileExtraction.of(EnclosingType.class),
null,
AccessController.getContext(),
ByteArrayClassLoader.PersistenceHandler.LATENT,
PackageDefinitionStrategy.NoOp.INSTANCE);
Class<?> redefined = new ByteBuddy()
.redefine(EnclosingType.ANONYMOUS_INITIALIZER)
.visit(new InnerClassValidator.Wrapper(EnclosingType.ANONYMOUS_INITIALIZER, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC))
.modifiers(Visibility.PUBLIC)
.make()
.load(classLoader, ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
assertThat(redefined.isAnonymousClass(), is(EnclosingType.ANONYMOUS_INITIALIZER.isAnonymousClass()));
assertThat(redefined.isLocalClass(), is(EnclosingType.ANONYMOUS_INITIALIZER.isLocalClass()));
assertThat(redefined.isMemberClass(), is(EnclosingType.ANONYMOUS_INITIALIZER.isMemberClass()));
assertThat(redefined.getModifiers(), is(Modifier.PUBLIC | EnclosingType.ANONYMOUS_INITIALIZER.getModifiers()));
}

@Test
public void testLocalClassChangeModifierTest() throws Exception {
ClassLoader classLoader = new ByteArrayClassLoader(ClassLoadingStrategy.BOOTSTRAP_LOADER,
ClassFileExtraction.of(EnclosingType.class),
null,
AccessController.getContext(),
ByteArrayClassLoader.PersistenceHandler.LATENT,
PackageDefinitionStrategy.NoOp.INSTANCE);
Class<?> redefined = new ByteBuddy()
.redefine(EnclosingType.LOCAL_INITIALIZER)
.modifiers(Visibility.PUBLIC)
.visit(new InnerClassValidator.Wrapper(EnclosingType.LOCAL_INITIALIZER, Opcodes.ACC_PUBLIC))
.make()
.load(classLoader, ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
assertThat(redefined.isAnonymousClass(), is(EnclosingType.LOCAL_INITIALIZER.isAnonymousClass()));
assertThat(redefined.isLocalClass(), is(EnclosingType.LOCAL_INITIALIZER.isLocalClass()));
assertThat(redefined.isMemberClass(), is(EnclosingType.LOCAL_INITIALIZER.isMemberClass()));
assertThat(redefined.getModifiers(), is(Modifier.PUBLIC | EnclosingType.LOCAL_INITIALIZER.getModifiers()));
}

@Test @Test
public void testBridgeNonLegacyType() throws Exception { public void testBridgeNonLegacyType() throws Exception {
Class<?> base = new ByteBuddy(ClassFileVersion.JAVA_V5) Class<?> base = new ByteBuddy(ClassFileVersion.JAVA_V5)
Expand Down
@@ -1,8 +1,132 @@
package net.bytebuddy.dynamic.scaffold; package net.bytebuddy.dynamic.scaffold;


import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.test.scope.EnclosingType;
import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Parameterized; import org.junit.runners.Parameterized;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;

import java.util.Arrays;
import java.util.Collection;


@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public class TypeWriterModifierPreservationTest { public class TypeWriterModifierPreservationTest {

@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][]{
{Object.class},
{String.class},
{EnclosingType.class},
{new EnclosingType().localMethod},
{new EnclosingType().anonymousMethod},
{new EnclosingType().localConstructor},
{new EnclosingType().anonymousConstructor},
{EnclosingType.LOCAL_INITIALIZER},
{EnclosingType.ANONYMOUS_INITIALIZER},
{EnclosingType.LOCAL_METHOD},
{EnclosingType.ANONYMOUS_METHOD},
{EnclosingType.INNER},
{EnclosingType.NESTED},
{EnclosingType.PRIVATE_INNER},
{EnclosingType.PRIVATE_NESTED},
{EnclosingType.PROTECTED_INNER},
{EnclosingType.PROTECTED_NESTED},
{EnclosingType.PACKAGE_INNER},
{EnclosingType.PACKAGE_NESTED},
{EnclosingType.FINAL_NESTED},
{EnclosingType.FINAL_INNER},
{EnclosingType.DEPRECATED}
});
}

private final Class<?> type;

public TypeWriterModifierPreservationTest(Class<?> type) {
this.type = type;
}

@Test
public void testModifiers() throws Exception {
TypeModifierExtractor typeModifierExtractor = new TypeModifierExtractor();
new ClassReader(type.getName()).accept(typeModifierExtractor, 0);
new ByteBuddy()
.redefine(type)
.visit(new TypeValidator.Wrapper(typeModifierExtractor))
.make();
}

private static class TypeModifierExtractor extends ClassVisitor {

private String name;

public int modifiers, inner;

public TypeModifierExtractor() {
super(Opcodes.ASM5);
}

@Override
public void visit(int version, int modifiers, String name, String signature, String superName, String[] interfaceName) {
this.modifiers = modifiers;
this.name = name;
}

@Override
public void visitInnerClass(String name, String outerName, String innerName, int modifiers) {
if (name.equals(this.name)) {
inner = modifiers;
}
}
}

private static class TypeValidator extends ClassVisitor {

private String name;

public final int modifiers, inner;

public TypeValidator(ClassVisitor classVisitor, int modifiers, int inner) {
super(Opcodes.ASM5, classVisitor);
this.modifiers = modifiers;
this.inner = inner;
}

@Override
public void visit(int version, int modifiers, String name, String signature, String superName, String[] interfaceName) {
this.name = name;
if (modifiers != this.modifiers) {
throw new AssertionError("Unexpected modifiers: Observed " + modifiers + " instead of " + this.modifiers);
}
super.visit(version, modifiers, name, signature, superName, interfaceName);
}

@Override
public void visitInnerClass(String name, String outerName, String innerName, int modifiers) {
if (name.equals(this.name) && modifiers != inner) {
throw new AssertionError("Unexpected inner modifiers: Observed " + modifiers + " instead of " + inner);
}
super.visitInnerClass(name, outerName, innerName, modifiers);
}

private static class Wrapper extends AsmVisitorWrapper.AbstractBase {

public final int modifiers, inner;

public Wrapper(TypeModifierExtractor typeModifierExtractor) {
modifiers = typeModifierExtractor.modifiers;
inner = typeModifierExtractor.inner;
}

@Override
public ClassVisitor wrap(TypeDescription instrumentedType, ClassVisitor classVisitor, int writerFlags, int readerFlags) {
return new TypeValidator(classVisitor, modifiers, inner);
}
}
}
} }
Expand Up @@ -29,9 +29,9 @@ public class EnclosingType {


public static final Class<?> LOCAL_INITIALIZER; public static final Class<?> LOCAL_INITIALIZER;


public static final Class<?> ANONYMOUS_METHOD = localStatic(); public static final Class<?> ANONYMOUS_METHOD = anonymousStatic();


public static final Class<?> LOCAL_METHOD = localAnonymous(); public static final Class<?> LOCAL_METHOD = localStatic();


static { static {
ANONYMOUS_INITIALIZER = new Object() { ANONYMOUS_INITIALIZER = new Object() {
Expand All @@ -43,19 +43,19 @@ class Foo {
LOCAL_INITIALIZER = Foo.class; LOCAL_INITIALIZER = Foo.class;
} }


private static Class<?> anonymousStatic() {
return new Object() {
/* empty */
}.getClass();
}

private static Class<?> localStatic() { private static Class<?> localStatic() {
class FooBar { class FooBar {
/* empty */ /* empty */
} }
return FooBar.class; return FooBar.class;
} }


private static Class<?> localAnonymous() {
return new Object() {
/* empty */
}.getClass();
}

public class Bar { public class Bar {
/* empty */ /* empty */
} }
Expand Down

0 comments on commit 568cb14

Please sign in to comment.