Skip to content

Commit

Permalink
Adding missing javadoc and tests for the new module-related API.
Browse files Browse the repository at this point in the history
  • Loading branch information
raphw committed Jun 1, 2016
1 parent 1e083a5 commit 407dbde
Show file tree
Hide file tree
Showing 8 changed files with 435 additions and 27 deletions.

Large diffs are not rendered by default.

Expand Up @@ -629,6 +629,12 @@ public static <T extends NamedElement> ElementMatcher.Junction<T> nameMatches(St
return new NameMatcher<T>(new StringMatcher(regex, StringMatcher.Mode.MATCHES));
}

/**
* Matches a {@link NamedElement.WithOptionalName} for having an explicit name.
*
* @param <T> The type of the matched object.
* @return An element matcher that checks if the matched optionally named element has an explicit name.
*/
public static <T extends NamedElement.WithOptionalName> ElementMatcher.Junction<T> isNamed() {
return new IsNamedMatcher<T>();
}
Expand Down Expand Up @@ -1533,7 +1539,7 @@ public static <T extends TypeDescription> ElementMatcher.Junction<T> isSuperType
* Matches any type description that declares a super type that matches the provided matcher.
*
* @param matcher The type to be checked for being a super type of the matched type.
* @param <T> The type of the matched object.
* @param <T> The type of the matched object.
* @return A matcher that matches any type description that declares a super type that matches the provided matcher.
*/
public static <T extends TypeDescription> ElementMatcher.Junction<T> hasSuperType(ElementMatcher<? super TypeDescription> matcher) {
Expand All @@ -1544,7 +1550,7 @@ public static <T extends TypeDescription> ElementMatcher.Junction<T> hasSuperTyp
* Matches any type description that declares a super type that matches the provided matcher.
*
* @param matcher The type to be checked for being a super type of the matched type.
* @param <T> The type of the matched object.
* @param <T> The type of the matched object.
* @return A matcher that matches any type description that declares a super type that matches the provided matcher.
*/
public static <T extends TypeDescription> ElementMatcher.Junction<T> hasGenericSuperType(ElementMatcher<? super TypeDescription.Generic> matcher) {
Expand Down
142 changes: 136 additions & 6 deletions byte-buddy-dep/src/main/java/net/bytebuddy/utility/JavaModule.java
Expand Up @@ -8,12 +8,24 @@
import java.security.AccessController;
import java.security.PrivilegedAction;

/**
* Type-safe representation of a {@code java.lang.reflect.Module}. On platforms that do not support the module API, modules are represented by {@code null}.
*/
public class JavaModule implements NamedElement.WithOptionalName, PrivilegedAction<ClassLoader> {

public static JavaModule UNDEFINED = null;
/**
* Canonical representation of a Java module on a JVM that does not support the module API.
*/
public static final JavaModule UNSUPPORTED = null;

/**
* The dispatcher to use for accessing Java modules, if available.
*/
private static final Dispatcher DISPATCHER;

/*
* Extracts the dispatcher for Java modules that is supported by the current JVM.
*/
static {
Dispatcher dispatcher;
try {
Expand All @@ -30,16 +42,37 @@ public class JavaModule implements NamedElement.WithOptionalName, PrivilegedActi
DISPATCHER = dispatcher;
}

/**
* The {@code java.lang.reflect.Module} instance this wrapper represents.
*/
private final Object module;

/**
* Creates a new Java module representation.
*
* @param module The {@code java.lang.reflect.Module} instance this wrapper represents.
*/
protected JavaModule(Object module) {
this.module = module;
}

/**
* Returns a representation of the supplied type's {@code java.lang.reflect.Module} or {@code null} if the current VM does not support modules.
*
* @param type The type for which to describe the module.
* @return A representation of the type's module or {@code null} if the current VM does not support modules.
*/
public static JavaModule ofType(Class<?> type) {
return DISPATCHER.moduleOf(type);
}

/**
* Represents the supplied {@code java.lang.reflect.Module} as an instance of this class and validates that the
* supplied instance really represents a Java {@code Module}.
*
* @param module The module to represent.
* @return A representation of the supplied Java module.
*/
public static JavaModule of(Object module) {
if (!JavaType.MODULE.getTypeStub().isInstance(module)) {
throw new IllegalArgumentException("Not a Java module: " + module);
Expand All @@ -57,6 +90,21 @@ public String getActualName() {
return DISPATCHER.getName(module);
}

/**
* Returns the class loader of this module.
*
* @param accessControlContext The access control context to use for using extracting the class loader.
* @return The class loader of the represented module.
*/
public ClassLoader getClassLoader(AccessControlContext accessControlContext) {
return AccessController.doPrivileged(this, accessControlContext);
}

/**
* Unwraps this instance to a {@code java.lang.reflect.Module}.
*
* @return The represented {@code java.lang.reflect.Module}.
*/
public Object unwrap() {
return module;
}
Expand All @@ -79,35 +127,81 @@ public String toString() {
return module.toString();
}

public ClassLoader getClassLoader(AccessControlContext accessControlContext) {
return AccessController.doPrivileged(this, accessControlContext);
}

@Override
public ClassLoader run() {
return DISPATCHER.getClassLoader(module);
}

/**
* A dispatcher for accessing the {@code java.lang.reflect.Module} API if it is available on the current VM.
*/
protected interface Dispatcher {

/**
* Extracts the Java {@code Module} for the provided class or returns {@code null} if the current VM does not support modules.
*
* @param type The type for which to extract the module.
* @return The class's {@code Module} or {@code null} if the current VM does not support modules.
*/
JavaModule moduleOf(Class<?> type);

/**
* Returns {@code true} if the supplied module is named.
*
* @param module The {@code java.lang.reflect.Module} to check for the existence of a name.
* @return {@code true} if the supplied module is named.
*/
boolean isNamed(Object module);

/**
* Returns the module's name.
*
* @param module The {@code java.lang.reflect.Module} to check for its name.
* @return The module's (implicit or explicit) name.
*/
String getName(Object module);

/**
* Returns the module's class loader.
*
* @param module The {@code java.lang.reflect.Module}
* @return The module's class loader.
*/
ClassLoader getClassLoader(Object module);

/**
* A dispatcher for a VM that does support the {@code java.lang.reflect.Module} API.
*/
class Enabled implements Dispatcher {

/**
* The {@code java.lang.Class#getModule()} method.
*/
private final Method getModule;

/**
* The {@code java.lang.reflect.Module#getClassLoader()} method.
*/
private final Method getClassLoader;

/**
* The {@code java.lang.reflect.Module#isNamed()} method.
*/
private final Method isNamed;

/**
* The {@code java.lang.reflect.Module#getName()} method.
*/
private final Method getName;

/**
* Creates a new enabled dispatcher.
*
* @param getModule The {@code java.lang.Class#getModule()} method.
* @param getClassLoader The {@code java.lang.reflect.Module#getClassLoader()} method.
* @param isNamed The {@code java.lang.reflect.Module#isNamed()} method.
* @param getName The {@code java.lang.reflect.Module#getName()} method.
*/
protected Enabled(Method getModule, Method getClassLoader, Method isNamed, Method getName) {
this.getModule = getModule;
this.getClassLoader = getClassLoader;
Expand Down Expand Up @@ -158,15 +252,51 @@ public String getName(Object module) {
throw new IllegalStateException("Cannot invoke " + getName, exception.getCause());
}
}

@Override
public boolean equals(Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;
Enabled enabled = (Enabled) object;
if (!getModule.equals(enabled.getModule)) return false;
if (!getClassLoader.equals(enabled.getClassLoader)) return false;
if (!isNamed.equals(enabled.isNamed)) return false;
return getName.equals(enabled.getName);
}

@Override
public int hashCode() {
int result = getModule.hashCode();
result = 31 * result + getClassLoader.hashCode();
result = 31 * result + isNamed.hashCode();
result = 31 * result + getName.hashCode();
return result;
}

@Override
public String toString() {
return "JavaModule.Dispatcher.Enabled{" +
"getModule=" + getModule +
", getClassLoader=" + getClassLoader +
", isNamed=" + isNamed +
", getName=" + getName +
'}';
}
}

/**
* A disabled dispatcher for a VM that does not support the {@code java.lang.reflect.Module} API.
*/
enum Disabled implements Dispatcher {

/**
* The singleton instance.
*/
INSTANCE;

@Override
public JavaModule moduleOf(Class<?> type) {
return UNDEFINED;
return UNSUPPORTED;
}

@Override
Expand Down
Expand Up @@ -39,6 +39,9 @@ public enum JavaType {
*/
EXECUTABLE("java.lang.reflect.Executable", Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, AccessibleObject.class, Member.class, GenericDeclaration.class),

/**
* The {@code java.lang.reflect.Module} type.
*/
MODULE("java.lang.reflect.Module", Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, Object.class);

/**
Expand Down
Expand Up @@ -1058,6 +1058,8 @@ public AccessControlContext create() {
return new AccessControlContext(new ProtectionDomain[]{mock(ProtectionDomain.class)});
}
}).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.ExecutingTransformer.Factory.ForJava9CapableVm.class).apply();
ObjectPropertyAssertion.of(AgentBuilder.Default.ExecutingTransformer.Factory.ForLegacyVm.class).apply();
}

public static class Foo {
Expand Down
Expand Up @@ -2,6 +2,7 @@

import net.bytebuddy.description.ByteCodeElement;
import net.bytebuddy.description.ModifierReviewable;
import net.bytebuddy.description.NamedElement;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.method.MethodDescription;
Expand Down Expand Up @@ -466,6 +467,14 @@ public void testNameMatches() throws Exception {
assertThat(ElementMatchers.nameMatches(BAR).matches(byteCodeElement), is(false));
}

@Test
public void testIsNamed() throws Exception {
NamedElement.WithOptionalName namedElement = mock(NamedElement.WithOptionalName.class);
assertThat(ElementMatchers.isNamed().matches(namedElement), is(false));
when(namedElement.isNamed()).thenReturn(true);
assertThat(ElementMatchers.isNamed().matches(namedElement), is(true));
}

@Test
public void testHasDescriptor() throws Exception {
ByteCodeElement byteCodeElement = mock(ByteCodeElement.class);
Expand Down
@@ -0,0 +1,65 @@
package net.bytebuddy.utility;

import net.bytebuddy.test.utility.ObjectPropertyAssertion;
import org.junit.Test;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Iterator;

import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;

public class JavaModuleTest {

@Test(expected = IllegalArgumentException.class)
public void testExtractModule() throws Exception {
JavaModule.of(mock(Object.class));
}

@Test
public void testUnwrap() throws Exception {
Object object = new Object();
JavaModule module = new JavaModule(object);
assertThat(module.unwrap(), sameInstance(object));
}

@Test(expected = IllegalStateException.class)
public void testIsNamedDisabledThrowException() throws Exception {
JavaModule.Dispatcher.Disabled.INSTANCE.isNamed(mock(Object.class));
}

@Test(expected = IllegalStateException.class)
public void testGetNameDisabledThrowException() throws Exception {
JavaModule.Dispatcher.Disabled.INSTANCE.getName(mock(Object.class));
}

@Test(expected = IllegalStateException.class)
public void testGetClassLoaderDisabledThrowException() throws Exception {
JavaModule.Dispatcher.Disabled.INSTANCE.getClassLoader(mock(Object.class));
}

@Test
public void testDisabledModuleIsNull() throws Exception {
assertThat(JavaModule.Dispatcher.Disabled.INSTANCE.moduleOf(Object.class), nullValue(JavaModule.class));
}

@Test
public void testObjectProperties() throws Exception {
ObjectPropertyAssertion.of(JavaModule.class).skipToString().apply();
Object object = new Object();
assertThat(new JavaModule(object).hashCode(), is(object.hashCode()));
assertThat(new JavaModule(object).toString(), is(object.toString()));
final Iterator<Method> iterator = Arrays.asList(Object.class.getDeclaredMethods()).iterator();
ObjectPropertyAssertion.of(JavaModule.Dispatcher.Enabled.class).create(new ObjectPropertyAssertion.Creator<Method>() {
@Override
public Method create() {
return iterator.next();
}
}).apply();
ObjectPropertyAssertion.of(JavaModule.Dispatcher.Disabled.class).apply();
}
}
Expand Up @@ -66,6 +66,14 @@ public void testExecutable() throws Exception {
assertThat(JavaType.EXECUTABLE.getTypeStub().getInterfaces().contains(new TypeDescription.Generic.OfNonGenericType.ForLoadedType(GenericDeclaration.class)), is(true));
}

@Test
public void testModule() throws Exception {
assertThat(JavaType.MODULE.getTypeStub().getName(), is("java.lang.reflect.Module"));
assertThat(JavaType.MODULE.getTypeStub().getModifiers(), is(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL));
assertThat(JavaType.MODULE.getTypeStub().getSuperClass(), is(TypeDescription.Generic.OBJECT));
assertThat(JavaType.MODULE.getTypeStub().getInterfaces().size(), is(0));
}

@Test
@JavaVersionRule.Enforce(7)
public void testJava7Types() throws Exception {
Expand Down

0 comments on commit 407dbde

Please sign in to comment.