@Override public Size apply( MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { StackManipulation thisReference = MethodVariableAccess.forType(instrumentedType).loadOffset(0); FieldList<?> fieldList = instrumentedType.getDeclaredFields(); StackManipulation[] fieldLoading = new StackManipulation[fieldList.size()]; int index = 0; for (FieldDescription fieldDescription : fieldList) { fieldLoading[index++] = new StackManipulation.Compound( thisReference, FieldAccess.forField(fieldDescription).getter()); } StackManipulation.Size stackSize = new StackManipulation.Compound( new StackManipulation.Compound(fieldLoading), MethodInvocation.invoke(accessorMethod), assigner.assign( accessorMethod.getReturnType().asErasure(), instrumentedMethod.getReturnType().asErasure(), Assigner.Typing.DYNAMIC), MethodReturn.returning(instrumentedMethod.getReturnType().asErasure())) .apply(methodVisitor, implementationContext); return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize()); }
@Override public Size apply( MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { StackManipulation thisReference = MethodVariableAccess.REFERENCE.loadOffset(0); FieldList<?> fieldList = instrumentedType.getDeclaredFields(); StackManipulation[] fieldLoading = new StackManipulation[fieldList.size()]; int index = 0; for (FieldDescription fieldDescription : fieldList) { fieldLoading[index] = new StackManipulation.Compound( thisReference, MethodVariableAccess.forType(fieldDescription.getType().asErasure()) .loadOffset(instrumentedMethod.getParameters().get(index).getOffset()), FieldAccess.forField(fieldDescription).putter()); index++; } StackManipulation.Size stackSize = new StackManipulation.Compound( thisReference, MethodInvocation.invoke(ConstructorCall.INSTANCE.objectTypeDefaultConstructor), new StackManipulation.Compound(fieldLoading), MethodReturn.VOID) .apply(methodVisitor, implementationContext); return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize()); }
/** * Extracts a handle type for invoking the given method via invokespecial. * * @param methodDescription The method for which a handle type should be found. * @return The handle type for the given method. */ protected static HandleType ofSpecial(MethodDescription methodDescription) { if (methodDescription.isStatic() || methodDescription.isAbstract()) { throw new IllegalArgumentException( "Cannot invoke " + methodDescription + " via invokespecial"); } return methodDescription.isConstructor() ? INVOKE_SPECIAL_CONSTRUCTOR : INVOKE_SPECIAL; }
@Test public void testGenericMethodWithoutGenericExceptionTypes() throws Exception { DynamicType.Unloaded<?> unloaded = new ByteBuddy() .redefine(GenericMethod.class) .method(named(BAR)) .intercept(FixedValue.nullValue()) .make(); Class<?> type = unloaded .load(ClassLoadingStrategy.BOOTSTRAP_LOADER, ClassLoadingStrategy.Default.WRAPPER) .getLoaded(); MethodDescription createdMethod = new MethodDescription.ForLoadedMethod(type.getDeclaredMethod(BAR, Object.class)); MethodDescription originalMethod = new MethodDescription.ForLoadedMethod( GenericMethod.class.getDeclaredMethod(BAR, Object.class)); assertThat(createdMethod.getTypeVariables(), is(originalMethod.getTypeVariables())); assertThat(createdMethod.getReturnType(), is(originalMethod.getReturnType())); assertThat( createdMethod.getParameters().getOnly().getType(), is(originalMethod.getParameters().getOnly().getType())); assertThat( createdMethod.getExceptionTypes().getOnly(), is(originalMethod.getExceptionTypes().getOnly())); }
/** * Creates a method handle representation of the given method. * * @param methodDescription The method ro represent. * @return A method handle representing the given method. */ public static MethodHandle of(MethodDescription methodDescription) { return new MethodHandle( HandleType.of(methodDescription), methodDescription.getDeclaringType().asRawType(), methodDescription.getInternalName(), methodDescription.getReturnType().asRawType(), methodDescription.getParameters().asTypeList().asRawTypes()); }
@Test public void testAbstractSuperTypeMethodIsNotInvokable() throws Exception { when(superMethod.isSpecializableFor(superType)).thenReturn(true); when(superMethod.isAbstract()).thenReturn(true); Implementation.SpecialMethodInvocation specialMethodInvocation = implementationTarget.invokeSuper(superMethod, methodLookup); assertThat(specialMethodInvocation.isValid(), is(false)); }
/** * Creates a method attribute appender factory that writes all annotations of a given method, * both the method annotations themselves and all annotations that are defined for every * parameter. * * @param methodDescription The method from which to extract the annotations. * @return A method attribute appender factory for an appender that writes all annotations of * the supplied method. */ public static Factory of(MethodDescription methodDescription) { ParameterList<?> parameters = methodDescription.getParameters(); List<MethodAttributeAppender.Factory> methodAttributeAppenders = new ArrayList<MethodAttributeAppender.Factory>(parameters.size() + 1); methodAttributeAppenders.add(new Explicit(methodDescription.getDeclaredAnnotations())); for (ParameterDescription parameter : parameters) { methodAttributeAppenders.add( new Explicit(parameter.getIndex(), parameter.getDeclaredAnnotations())); } return new Factory.Compound(methodAttributeAppenders); }
/** * Extracts a handle type for invoking the given method. * * @param methodDescription The method for which a handle type should be found. * @return The handle type for the given method. */ protected static HandleType of(MethodDescription methodDescription) { if (methodDescription.isStatic()) { return INVOKE_STATIC; } else if (methodDescription.isPrivate()) { return INVOKE_SPECIAL; } else if (methodDescription.isConstructor()) { return INVOKE_SPECIAL_CONSTRUCTOR; } else if (methodDescription.getDeclaringType().asRawType().isInterface()) { return INVOKE_INTERFACE; } else { return INVOKE_VIRTUAL; } }
/** * Creates a method handle representation of the given method for an explicit special method * invocation of an otherwise virtual method. * * @param methodDescription The method ro represent. * @param typeDescription The type on which the method is to be invoked on as a special method * invocation. * @return A method handle representing the given method as special method invocation. */ public static MethodHandle ofSpecial( MethodDescription methodDescription, TypeDescription typeDescription) { if (!methodDescription.isSpecializableFor(typeDescription)) { throw new IllegalArgumentException( "Cannot specialize " + methodDescription + " for " + typeDescription); } return new MethodHandle( HandleType.ofSpecial(methodDescription), typeDescription, methodDescription.getInternalName(), methodDescription.getReturnType().asRawType(), methodDescription.getParameters().asTypeList().asRawTypes()); }
@Before public void setUp() throws Exception { when(methodDescription.getDeclaringType()).thenReturn(typeDescription); when(methodDescription.getReturnType()).thenReturn(returnType); when(methodDescription.getInternalName()).thenReturn(FOO); when(methodDescription.getDescriptor()).thenReturn(BAZ); when(typeDescription.getInternalName()).thenReturn(BAR); when(typeDescription.getDescriptor()).thenReturn(BAR); when(methodNameTransformer.transform(methodDescription)).thenReturn(QUX); when(otherMethodNameTransformer.transform(methodDescription)).thenReturn(FOO + BAR); when(parameterType.getStackSize()).thenReturn(StackSize.ZERO); ParameterList parameterList = ParameterList.Explicit.latent(methodDescription, Collections.singletonList(parameterType)); when(methodDescription.getParameters()).thenReturn(parameterList); }
@Override public MethodDelegationBinder.ParameterBinding<?> bind( AnnotationDescription.Loadable<FieldValue> annotation, MethodDescription source, ParameterDescription target, Implementation.Target implementationTarget, Assigner assigner) { FieldLocator.Resolution resolution = FieldLocator.of( annotation.getValue(DEFINING_TYPE, TypeDescription.class), implementationTarget.getTypeDescription()) .resolve(annotation.getValue(FIELD_NAME, String.class), source.isStatic()); if (resolution.isResolved()) { StackManipulation stackManipulation = new StackManipulation.Compound( resolution.getFieldDescription().isStatic() ? StackManipulation.Trivial.INSTANCE : MethodVariableAccess.REFERENCE.loadOffset(0), FieldAccess.forField(resolution.getFieldDescription()).getter(), assigner.assign( resolution.getFieldDescription().getType().asErasure(), target.getType().asErasure(), RuntimeType.Verifier.check(target))); return stackManipulation.isValid() ? new MethodDelegationBinder.ParameterBinding.Anonymous(stackManipulation) : MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE; } else { return MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE; } }
@Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && accessorMethod.equals(((MethodCall) other).accessorMethod) && assigner.equals(((MethodCall) other).assigner); }
/** * Creates a linked hash map of field names to their types where each field represents a parameter * of the method. * * @param methodDescription The method to extract into fields. * @return A map of fields in the order they need to be loaded onto the operand stack for invoking * the original method, including a reference to the instance of the instrumented type that is * invoked if applicable. */ private static LinkedHashMap<String, TypeDescription> extractFields( MethodDescription methodDescription) { ParameterList<?> parameters = methodDescription.getParameters(); LinkedHashMap<String, TypeDescription> typeDescriptions = new LinkedHashMap<String, TypeDescription>( (methodDescription.isStatic() ? 0 : 1) + parameters.size()); int currentIndex = 0; if (!methodDescription.isStatic()) { typeDescriptions.put( fieldName(currentIndex++), methodDescription.getDeclaringType().asErasure()); } for (ParameterDescription parameterDescription : parameters) { typeDescriptions.put(fieldName(currentIndex++), parameterDescription.getType().asErasure()); } return typeDescriptions; }
@Override public int hashCode() { int result = handler.hashCode(); result = 31 * result + attributeAppender.hashCode(); result = 31 * result + methodDescription.hashCode(); result = 31 * result + bridgeTypes.hashCode(); return result; }
@Override public int hashCode() { int result = handler.hashCode(); result = 31 * result + attributeAppenderFactory.hashCode(); result = 31 * result + methodDescription.hashCode(); result = 31 * result + typeTokens.hashCode(); return result; }
@Override public void apply( MethodVisitor methodVisitor, MethodDescription methodDescription, AnnotationValueFilter annotationValueFilter) { AnnotationAppender annotationAppender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnMethod(methodVisitor)); annotationAppender = methodDescription .getReturnType() .accept( AnnotationAppender.ForTypeAnnotations.ofMethodReturnType( annotationAppender, annotationValueFilter)); annotationAppender = AnnotationAppender.ForTypeAnnotations.ofTypeVariable( annotationAppender, annotationValueFilter, AnnotationAppender.ForTypeAnnotations.VARIABLE_ON_INVOKEABLE, methodDescription.getTypeVariables()); for (AnnotationDescription annotation : methodDescription.getDeclaredAnnotations()) { annotationAppender = annotationAppender.append(annotation, annotationValueFilter); } for (ParameterDescription parameterDescription : methodDescription.getParameters()) { AnnotationAppender parameterAppender = new AnnotationAppender.Default( new AnnotationAppender.Target.OnMethodParameter( methodVisitor, parameterDescription.getIndex())); parameterAppender = parameterDescription .getType() .accept( AnnotationAppender.ForTypeAnnotations.ofMethodParameterType( parameterAppender, annotationValueFilter, parameterDescription.getIndex())); for (AnnotationDescription annotation : parameterDescription.getDeclaredAnnotations()) { parameterAppender = parameterAppender.append(annotation, annotationValueFilter); } } int exceptionTypeIndex = 0; for (TypeDescription.Generic exceptionType : methodDescription.getExceptionTypes()) { annotationAppender = exceptionType.accept( AnnotationAppender.ForTypeAnnotations.ofExceptionType( annotationAppender, annotationValueFilter, exceptionTypeIndex++)); } }
private void checkMethodSignature(MethodDescription instrumentedMethod) { final String errMessage = "%s must have signature `void copy(java.lang.Object, long)`"; Preconditions.checkArgument( instrumentedMethod.getReturnType().represents(void.class), errMessage, instrumentedMethod); ParameterList parameters = instrumentedMethod.getParameters(); Preconditions.checkArgument(parameters.size() == 2, errMessage, instrumentedMethod); Preconditions.checkArgument( parameters.get(0).getTypeDescription().represents(Object.class), errMessage, instrumentedMethod); Preconditions.checkArgument( parameters.get(1).getTypeDescription().represents(long.class), errMessage, instrumentedMethod); }
@Override public AnnotationAppender.Target make( MethodVisitor methodVisitor, MethodDescription methodDescription) { if (parameterIndex >= methodDescription.getParameters().size()) { throw new IllegalArgumentException( "Method " + methodDescription + " has less then " + parameterIndex + " parameters"); } return new AnnotationAppender.Target.OnMethodParameter(methodVisitor, parameterIndex); }
@Override public boolean equals(Object other) { if (this == other) return true; if (other == null || getClass() != other.getClass()) return false; Entry entry = (Entry) other; return handler.equals(entry.handler) && attributeAppenderFactory.equals(entry.attributeAppenderFactory) && methodDescription.equals(entry.methodDescription) && typeTokens.equals(entry.typeTokens); }
PrecomputedMethodGraph() { LinkedHashMap<MethodDescription.Token, MethodGraph.Node> nodes = new LinkedHashMap<MethodDescription.Token, MethodGraph.Node>(2); MethodDescription callMethod = new TypeDescription.ForLoadedType(Callable.class) .getDeclaredMethods() .filter(named("call")) .getOnly(); nodes.put(callMethod.asToken(), new MethodGraph.Node.Simple(callMethod)); MethodDescription runMethod = new TypeDescription.ForLoadedType(Runnable.class) .getDeclaredMethods() .filter(named("run")) .getOnly(); nodes.put(runMethod.asToken(), new MethodGraph.Node.Simple(runMethod)); MethodGraph methodGraph = new MethodGraph.Simple(nodes); this.methodGraph = new MethodGraph.Linked.Delegation( methodGraph, methodGraph, Collections.<TypeDescription, MethodGraph>emptyMap()); }
@Override public Size apply( MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { if (instrumentedMethod.getReturnType().isPrimitive()) { throw new IllegalStateException("Cannot return null from " + instrumentedMethod); } return new ByteCodeAppender.Simple(NullConstant.INSTANCE, MethodReturn.REFERENCE) .apply(methodVisitor, implementationContext, instrumentedMethod); }
/** * Blueprint method that for applying the actual implementation. * * @param methodVisitor The method visitor to which the implementation is applied to. * @param implementationContext The implementation context for the given implementation. * @param instrumentedMethod The instrumented method that is target of the implementation. * @param fixedValueType A description of the type of the fixed value that is loaded by the {@code * valueLoadingInstruction}. * @param valueLoadingInstruction A stack manipulation that represents the loading of the fixed * value onto the operand stack. * @return A representation of the stack and variable array sized that are required for this * implementation. */ protected ByteCodeAppender.Size apply( MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod, TypeDescription.Generic fixedValueType, StackManipulation valueLoadingInstruction) { StackManipulation assignment = assigner.assign(fixedValueType, instrumentedMethod.getReturnType(), typing); if (!assignment.isValid()) { throw new IllegalArgumentException( "Cannot return value of type " + fixedValueType + " for " + instrumentedMethod); } StackManipulation.Size stackSize = new StackManipulation.Compound( valueLoadingInstruction, assignment, MethodReturn.returning(instrumentedMethod.getReturnType().asErasure())) .apply(methodVisitor, implementationContext); return new ByteCodeAppender.Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize()); }
@Override public MethodDelegationBinder.ParameterBinding<?> bind( AnnotationDescription.Loadable<Origin> annotation, MethodDescription source, ParameterDescription target, Implementation.Target implementationTarget, Assigner assigner) { TypeDescription parameterType = target.getType().asErasure(); if (parameterType.represents(Class.class)) { return new MethodDelegationBinder.ParameterBinding.Anonymous( ClassConstant.of(implementationTarget.getOriginType())); } else if (parameterType.represents(Method.class)) { return new MethodDelegationBinder.ParameterBinding.Anonymous( annotation.loadSilent().cache() ? MethodConstant.forMethod(source.asDefined()).cached() : MethodConstant.forMethod(source.asDefined())); } else if (parameterType.represents(String.class)) { return new MethodDelegationBinder.ParameterBinding.Anonymous( new TextConstant(source.toString())); } else if (parameterType.represents(int.class)) { return new MethodDelegationBinder.ParameterBinding.Anonymous( IntegerConstant.forValue(source.getModifiers())); } else if (parameterType.equals(JavaType.METHOD_HANDLE.getTypeStub())) { return new MethodDelegationBinder.ParameterBinding.Anonymous( MethodHandleConstant.of(source.asDefined())); } else if (parameterType.equals(JavaType.METHOD_TYPE.getTypeStub())) { return new MethodDelegationBinder.ParameterBinding.Anonymous( MethodTypeConstant.of(source.asDefined())); } else { throw new IllegalStateException( "The " + target + " method's " + target.getIndex() + " parameter is annotated with a Origin annotation with an argument not representing a Class," + " Method, String, int, MethodType or MethodHandle type"); } }
@Test public void testSuperConstructorIsInvokable() throws Exception { when(superMethod.isConstructor()).thenReturn(true); Implementation.SpecialMethodInvocation specialMethodInvocation = implementationTarget.invokeSuper(superMethod, methodLookup); assertThat(specialMethodInvocation.isValid(), is(true)); assertThat(specialMethodInvocation.getMethodDescription(), is(superMethodConstructor)); assertThat(specialMethodInvocation.getTypeDescription(), is(superType)); MethodVisitor methodVisitor = mock(MethodVisitor.class); Implementation.Context implementationContext = mock(Implementation.Context.class); StackManipulation.Size size = specialMethodInvocation.apply(methodVisitor, implementationContext); verify(methodVisitor).visitMethodInsn(Opcodes.INVOKESPECIAL, BAR, QUXBAZ, BAZBAR, false); verifyNoMoreInteractions(methodVisitor); verifyZeroInteractions(implementationContext); assertThat(size.getSizeImpact(), is(0)); assertThat(size.getMaximalSize(), is(0)); }
public Size apply( MethodVisitor methodVisitor, Implementation.Context implementationContext, MethodDescription instrumentedMethod) { checkMethodSignature(instrumentedMethod); try { StackManipulation stack = buildStack(); StackManipulation.Size finalStackSize = stack.apply(methodVisitor, implementationContext); return new Size( finalStackSize.getMaximalSize(), instrumentedMethod.getStackSize() + 2); // 2 stack slots for a single local variable } catch (NoSuchMethodException | NoSuchFieldException e) { throw new RuntimeException(e); } }
@Override public int hashCode() { return accessorMethod.hashCode() + 31 * assigner.hashCode(); }
@Override public MethodRegistry.Prepared prepare( InstrumentedType instrumentedType, MethodGraph.Compiler methodGraphCompiler, LatentMethodMatcher methodFilter) { LinkedHashMap<MethodDescription, Prepared.Entry> implementations = new LinkedHashMap<MethodDescription, Prepared.Entry>(); Set<Handler> handlers = new HashSet<Handler>(entries.size()); MethodList<?> helperMethods = instrumentedType.getDeclaredMethods(); for (Entry entry : entries) { if (handlers.add(entry.getHandler())) { instrumentedType = entry.getHandler().prepare(instrumentedType); ElementMatcher<? super MethodDescription> handledMethods = noneOf(helperMethods); helperMethods = instrumentedType.getDeclaredMethods(); for (MethodDescription helperMethod : helperMethods.filter(handledMethods)) { implementations.put(helperMethod, entry.asSupplementaryEntry(helperMethod)); } } } MethodGraph.Linked methodGraph = methodGraphCompiler.compile(instrumentedType); // Casting required for Java 6 compiler. ElementMatcher<? super MethodDescription> relevanceMatcher = (ElementMatcher<? super MethodDescription>) not(anyOf(implementations.keySet())).and(methodFilter.resolve(instrumentedType)); for (MethodGraph.Node node : methodGraph.listNodes()) { MethodDescription methodDescription = node.getRepresentative(); boolean visibilityBridge = instrumentedType.isPublic() && !instrumentedType.isInterface(); if (relevanceMatcher.matches(methodDescription)) { for (Entry entry : entries) { if (entry.resolve(instrumentedType).matches(methodDescription)) { implementations.put( methodDescription, entry.asPreparedEntry( instrumentedType, methodDescription, node.getMethodTypes())); visibilityBridge = false; break; } } } if (visibilityBridge && methodDescription.isPublic() && !(methodDescription.isAbstract() || methodDescription.isFinal()) && !node.getSort().isMadeVisible() && methodDescription.getDeclaringType().asErasure().isPackagePrivate()) { // Visibility bridges are required for public types that inherit a public method from a // package-private type. // Checking the last condition contradicts any method that is defined by the instrumented // type itself. implementations.put( methodDescription, Prepared.Entry.forVisibilityBridge(methodDescription, node.getMethodTypes())); } } MethodDescription typeInitializer = new MethodDescription.Latent.TypeInitializer(instrumentedType); for (Entry entry : entries) { if (entry.resolve(instrumentedType).matches(typeInitializer)) { implementations.put( typeInitializer, entry.asPreparedEntry( instrumentedType, typeInitializer, Collections.<MethodDescription.TypeToken>emptySet())); break; } } return new Prepared( implementations, instrumentedType.getLoadedTypeInitializer(), instrumentedType.getTypeInitializer(), instrumentedType.asErasure(), methodGraph); }
/** * Resolves the type tokens of all bridge methods that are required to be implemented for * this entry. * * @return A set of type tokens representing the bridge methods required for implementing * this type. */ protected Set<MethodDescription.TypeToken> resolveBridgeTypes() { HashSet<MethodDescription.TypeToken> typeTokens = new HashSet<MethodDescription.TypeToken>(this.typeTokens); typeTokens.remove(methodDescription.asTypeToken()); return typeTokens; }
@Before public void setUp() throws Exception { when(target.getReturnType()).thenReturn(targetType); when(targetType.asErasure()).thenReturn(targetType); when(targetType.getStackSize()).thenReturn(StackSize.SINGLE); }
@Override public String transform(MethodDescription methodDescription) { return String.format("%s%s", prefix, methodDescription.getInternalName()); }