Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added field constant. Clean up for use of defined shape fields.
- Loading branch information
Rafael Winterhalter
committed
Dec 18, 2015
1 parent
4cd8bf4
commit 12bdec6
Showing
4 changed files
with
246 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
139 changes: 139 additions & 0 deletions
139
...buddy-dep/src/main/java/net/bytebuddy/implementation/bytecode/constant/FieldConstant.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package net.bytebuddy.implementation.bytecode.constant; | ||
|
||
import net.bytebuddy.description.field.FieldDescription; | ||
import net.bytebuddy.description.method.MethodDescription; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.implementation.Implementation; | ||
import net.bytebuddy.implementation.bytecode.StackManipulation; | ||
import net.bytebuddy.implementation.bytecode.member.FieldAccess; | ||
import net.bytebuddy.implementation.bytecode.member.MethodInvocation; | ||
import org.objectweb.asm.MethodVisitor; | ||
|
||
import java.lang.reflect.Field; | ||
|
||
import static net.bytebuddy.matcher.ElementMatchers.named; | ||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; | ||
|
||
/** | ||
* Represents a {@link Field} constant for a given type. | ||
*/ | ||
public class FieldConstant implements StackManipulation { | ||
|
||
/** | ||
* The {@link Class#getDeclaredField(String)} method. | ||
*/ | ||
private static final MethodDescription.InDefinedShape GET_DECLARED_FIELD; | ||
|
||
/* | ||
* Looks up the method for finding a class's declared field. | ||
*/ | ||
static { | ||
GET_DECLARED_FIELD = new TypeDescription.ForLoadedType(Class.class).getDeclaredMethods() | ||
.filter(named("getDeclaredField").and(takesArguments(String.class))) | ||
.getOnly(); | ||
} | ||
|
||
/** | ||
* The field to be represent as a {@link Field}. | ||
*/ | ||
private final FieldDescription.InDefinedShape fieldDescription; | ||
|
||
/** | ||
* Creates a new field constant. | ||
* | ||
* @param fieldDescription The field to be represent as a {@link Field}. | ||
*/ | ||
public FieldConstant(FieldDescription.InDefinedShape fieldDescription) { | ||
this.fieldDescription = fieldDescription; | ||
} | ||
|
||
/** | ||
* Retruns a cached version of this field constant. | ||
* | ||
* @return A cached version of this field constant. | ||
*/ | ||
public StackManipulation cached() { | ||
return new Cached(this); | ||
} | ||
|
||
@Override | ||
public boolean isValid() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { | ||
return new Compound( | ||
ClassConstant.of(fieldDescription.getDeclaringType()), | ||
new TextConstant(fieldDescription.getInternalName()), | ||
MethodInvocation.invoke(GET_DECLARED_FIELD) | ||
).apply(methodVisitor, implementationContext); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object other) { | ||
return this == other || !(other == null || getClass() != other.getClass()) | ||
&& fieldDescription.equals(((FieldConstant) other).fieldDescription); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return fieldDescription.hashCode(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "FieldConstant{" + | ||
"fieldDescription=" + fieldDescription + | ||
'}'; | ||
} | ||
|
||
/** | ||
* A cached version of a {@link FieldConstant}. | ||
*/ | ||
protected static class Cached implements StackManipulation { | ||
|
||
/** | ||
* The field constant stack manipulation. | ||
*/ | ||
private final StackManipulation fieldConstant; | ||
|
||
/** | ||
* Creates a new cached version of a field constant. | ||
* | ||
* @param fieldConstant The field constant stack manipulation. | ||
*/ | ||
public Cached(StackManipulation fieldConstant) { | ||
this.fieldConstant = fieldConstant; | ||
} | ||
|
||
@Override | ||
public boolean isValid() { | ||
return fieldConstant.isValid(); | ||
} | ||
|
||
@Override | ||
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) { | ||
return FieldAccess.forField(implementationContext.cache(fieldConstant, new TypeDescription.ForLoadedType(Field.class))) | ||
.getter() | ||
.apply(methodVisitor, implementationContext); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object other) { | ||
return this == other || !(other == null || getClass() != other.getClass()) && fieldConstant.equals(((Cached) other).fieldConstant); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return fieldConstant.hashCode(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "FieldConstant.Cached{" + | ||
"fieldConstant=" + fieldConstant + | ||
'}'; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
94 changes: 94 additions & 0 deletions
94
...y-dep/src/test/java/net/bytebuddy/implementation/bytecode/constant/FieldConstantTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package net.bytebuddy.implementation.bytecode.constant; | ||
|
||
import net.bytebuddy.description.field.FieldDescription; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.implementation.Implementation; | ||
import net.bytebuddy.implementation.bytecode.StackManipulation; | ||
import net.bytebuddy.implementation.bytecode.StackSize; | ||
import net.bytebuddy.test.utility.MockitoRule; | ||
import net.bytebuddy.test.utility.ObjectPropertyAssertion; | ||
import org.junit.Before; | ||
import org.junit.Rule; | ||
import org.junit.Test; | ||
import org.junit.rules.TestRule; | ||
import org.mockito.Mock; | ||
import org.objectweb.asm.MethodVisitor; | ||
import org.objectweb.asm.Opcodes; | ||
import org.objectweb.asm.Type; | ||
|
||
import java.lang.reflect.Field; | ||
|
||
import static org.hamcrest.CoreMatchers.is; | ||
import static org.junit.Assert.assertThat; | ||
import static org.mockito.Mockito.*; | ||
|
||
public class FieldConstantTest { | ||
|
||
private static final String FOO = "foo", BAR = "bar", QUX = "qux", BAZ = "baz"; | ||
|
||
@Rule | ||
public TestRule mockitoRule = new MockitoRule(this); | ||
|
||
@Mock | ||
private FieldDescription.InDefinedShape fieldDescription, cacheField; | ||
|
||
@Mock | ||
private TypeDescription declaringType, cacheDeclaringType, cacheFieldType; | ||
|
||
@Mock | ||
private MethodVisitor methodVisitor; | ||
|
||
@Mock | ||
private Implementation.Context implementationContext; | ||
|
||
@Before | ||
public void setUp() throws Exception { | ||
when(declaringType.getInternalName()).thenReturn(FOO); | ||
when(fieldDescription.getInternalName()).thenReturn(BAR); | ||
when(fieldDescription.getDeclaringType()).thenReturn(declaringType); | ||
when(declaringType.getDescriptor()).thenReturn("L" + QUX + ";"); | ||
when(implementationContext.cache(new FieldConstant(fieldDescription), new TypeDescription.ForLoadedType(Field.class))) | ||
.thenReturn(cacheField); | ||
when(cacheField.getDeclaringType()).thenReturn(cacheDeclaringType); | ||
when(cacheField.isStatic()).thenReturn(true); | ||
when(cacheDeclaringType.getInternalName()).thenReturn(BAZ); | ||
when(cacheField.getName()).thenReturn(FOO + BAR); | ||
when(cacheField.getType()).thenReturn(cacheFieldType); | ||
when(cacheFieldType.getStackSize()).thenReturn(StackSize.SINGLE); | ||
when(cacheField.getInternalName()).thenReturn(FOO + BAR); | ||
when(cacheField.getDescriptor()).thenReturn(QUX + BAZ); | ||
} | ||
|
||
@Test | ||
public void testConstantCreation() throws Exception { | ||
StackManipulation.Size size = new FieldConstant(fieldDescription).apply(methodVisitor, implementationContext); | ||
assertThat(size.getSizeImpact(), is(1)); | ||
assertThat(size.getMaximalSize(), is(2)); | ||
verify(methodVisitor).visitLdcInsn(Type.getObjectType(QUX)); | ||
verify(methodVisitor).visitLdcInsn(BAR); | ||
verify(methodVisitor).visitMethodInsn(Opcodes.INVOKEVIRTUAL, | ||
"java/lang/Class", | ||
"getDeclaredField", | ||
"(Ljava/lang/String;)Ljava/lang/reflect/Field;", | ||
false); | ||
verifyNoMoreInteractions(methodVisitor); | ||
verifyZeroInteractions(implementationContext); | ||
} | ||
|
||
@Test | ||
public void testCached() throws Exception { | ||
StackManipulation.Size size = new FieldConstant(fieldDescription).cached().apply(methodVisitor, implementationContext); | ||
assertThat(size.getSizeImpact(), is(1)); | ||
assertThat(size.getMaximalSize(), is(1)); | ||
verify(implementationContext).cache(new FieldConstant(fieldDescription), new TypeDescription.ForLoadedType(Field.class)); | ||
verifyNoMoreInteractions(implementationContext); | ||
verify(methodVisitor).visitFieldInsn(Opcodes.GETSTATIC, BAZ, FOO + BAR, QUX + BAZ); | ||
verifyNoMoreInteractions(methodVisitor); | ||
} | ||
|
||
@Test | ||
public void testObjectProperties() throws Exception { | ||
ObjectPropertyAssertion.of(FieldConstant.class).apply(); | ||
ObjectPropertyAssertion.of(FieldConstant.Cached.class).apply(); | ||
} | ||
} |