/** * Creates a matcher where only overridable or declared methods are matched unless those are ignored. Methods that * are declared by the target type are only matched if they are not ignored. Declared methods that are not found on the * target type are always matched. * * @param ignoredMethods A method matcher that matches any ignored method. * @param originalType The original type of the instrumentation before adding any user methods. * @return A latent method matcher that identifies any method to instrument for a rebasement or redefinition. */ protected static LatentMatcher<MethodDescription> of(LatentMatcher<? super MethodDescription> ignoredMethods, TypeDescription originalType) { ElementMatcher.Junction<MethodDescription> predefinedMethodSignatures = none(); for (MethodDescription methodDescription : originalType.getDeclaredMethods()) { ElementMatcher.Junction<MethodDescription> signature = methodDescription.isConstructor() ? isConstructor() : ElementMatchers.<MethodDescription>named(methodDescription.getName()); signature = signature.and(returns(methodDescription.getReturnType().asErasure())); signature = signature.and(takesArguments(methodDescription.getParameters().asTypeList().asErasures())); predefinedMethodSignatures = predefinedMethodSignatures.or(signature); } return new InliningImplementationMatcher(ignoredMethods, predefinedMethodSignatures); }
/** * {@inheritDoc} */ public AnnotationList getDeclaredAnnotations() { return methodDescription.getDeclaredAnnotations(); }
@Override public boolean equals(Object other) { if (this == other) { return true; } else if (!(other instanceof MethodDescription)) { return false; } MethodDescription methodDescription = (MethodDescription) other; return getInternalName().equals(methodDescription.getInternalName()) && getDeclaringType().equals(methodDescription.getDeclaringType()) && getReturnType().asErasure().equals(methodDescription.getReturnType().asErasure()) && getParameters().asTypeList().asErasures().equals(methodDescription.getParameters().asTypeList().asErasures()); }
/** {@inheritDoc} */ public StackManipulation resolve(Assigner assigner, Assigner.Typing typing, MethodDescription source, MethodDescription target) { return Removal.of(target.isConstructor() ? target.getDeclaringType() : target.getReturnType()); } };
/** * Creates an annotation description for the values that were defined for this builder. * * @return An appropriate annotation description. */ public AnnotationDescription build() { for (MethodDescription methodDescription : annotationType.getDeclaredMethods()) { if (annotationValues.get(methodDescription.getName()) == null && methodDescription.getDefaultValue() == null) { throw new IllegalStateException("No value or default value defined for " + methodDescription.getName()); } } return new Latent(annotationType, annotationValues); } }
/** * {@inheritDoc} */ public MethodDescription transform(TypeDescription instrumentedType, MethodDescription methodDescription) { return new TransformedMethod(instrumentedType, methodDescription.getDeclaringType(), transformer.transform(instrumentedType, methodDescription.asToken(none())), methodDescription.asDefined()); }
@Override public boolean matches(MethodDescription targetMethod) { TypeDescription superClass = targetMethod.getDeclaringType().asErasure(); do { superClass = superClass.getSuperClass().asErasure(); if (declaresMethod(named(targetMethod.getName()) .and(returns(targetMethod.getReturnType().asErasure())) .and(takesArguments(targetMethod.getParameters().asTypeList().asErasures())) .and(extraMethodMatcher)) .matches(superClass)) { return true; } } while (superClassMatcher.matches(superClass)); return false; } }
MethodList<?> candidates; if (opcode == Opcodes.INVOKESPECIAL && internalName.equals(MethodDescription.CONSTRUCTOR_INTERNAL_NAME)) { candidates = resolution.resolve().getDeclaredMethods().filter(strict ? ElementMatchers.<MethodDescription>isConstructor().and(hasDescriptor(descriptor)) : ElementMatchers.<MethodDescription>failSafe(isConstructor().and(hasDescriptor(descriptor)))); } else if (opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.INVOKESPECIAL) { candidates = resolution.resolve().getDeclaredMethods().filter(strict ? ElementMatchers.<MethodDescription>named(internalName).and(hasDescriptor(descriptor)) : ElementMatchers.<MethodDescription>failSafe(named(internalName).and(hasDescriptor(descriptor)))); } else { // Invokevirtual and invokeinterface can represent a private, non-static method from Java 11. candidates = resolution.resolve().getDeclaredMethods().filter(strict ? ElementMatchers.<MethodDescription>isPrivate().and(not(isStatic())).and(named(internalName).and(hasDescriptor(descriptor))) : ElementMatchers.<MethodDescription>failSafe(isPrivate().<MethodDescription>and(not(isStatic())).and(named(internalName).and(hasDescriptor(descriptor))))); if (binding.isBound()) { stackSizeBuffer = Math.max(stackSizeBuffer, binding.make( candidates.getOnly().isStatic() || candidates.getOnly().isConstructor() ? candidates.getOnly().getParameters().asTypeList() : new TypeList.Generic.Explicit(CompoundList.of(resolution.resolve(), candidates.getOnly().getParameters().asTypeList())), candidates.getOnly().isConstructor() ? candidates.getOnly().getDeclaringType().asGenericType() : candidates.getOnly().getReturnType()) .apply(mv, implementationContext).getMaximalSize() - (candidates.getOnly().isConstructor() ? StackSize.SINGLE : candidates.getOnly().getReturnType().getStackSize()).getSize()); if (candidates.getOnly().isConstructor()) { stackSizeBuffer = Math.max(stackSizeBuffer, new StackManipulation.Compound( Duplication.SINGLE.flipOver(TypeDescription.OBJECT),
/** * {@inheritDoc} */ public Compiled compile(TypeDescription instrumentedType) { MethodList<?> targets = new MethodList.Explicit<MethodDescription>(CompoundList.<MethodDescription>of( instrumentedType.getDeclaredMethods().filter(isStatic().or(isPrivate())), methodGraphCompiler.compile(instrumentedType).listNodes().asMethodList()) ).filter(named(name).and(takesArguments(0)).and(not(returns(isPrimitive().or(isArray()))))); if (targets.size() != 1) { throw new IllegalStateException(instrumentedType + " does not define method without arguments with name " + name + ": " + targets); } else if (!targets.getOnly().getReturnType().asErasure().isVisibleTo(instrumentedType)) { throw new IllegalStateException(targets.getOnly() + " is not visible to " + instrumentedType); } else { MethodList<?> candidates = methodGraphCompiler.compile(targets.getOnly().getReturnType(), instrumentedType) .listNodes() .asMethodList() .filter(matcher); List<MethodDelegationBinder.Record> records = new ArrayList<MethodDelegationBinder.Record>(candidates.size()); MethodDelegationBinder methodDelegationBinder = TargetMethodAnnotationDrivenBinder.of(parameterBinders); for (MethodDescription candidate : candidates) { records.add(methodDelegationBinder.compile(candidate)); } return new Compiled.ForMethodReturn(targets.get(0), records); } }
TypeDescription instrumentedType = instrumentedMethod.getDeclaringType().asErasure(); MethodDescription enumConstructor = instrumentedType.getDeclaredMethods() .filter(isConstructor().and(takesArguments(String.class, int.class))) .getOnly(); int ordinal = 0; List<FieldDescription> enumerationFields = new ArrayList<FieldDescription>(values.size()); for (String value : values) { FieldDescription fieldDescription = instrumentedType.getDeclaredFields().filter(named(value)).getOnly(); stackManipulation = new StackManipulation.Compound(stackManipulation, TypeCreation.of(instrumentedType), ArrayFactory.forType(instrumentedType.asGenericType()).withValues(fieldGetters), FieldAccess.forField(instrumentedType.getDeclaredFields().filter(named(ENUM_VALUES)).getOnly()).write() ); return new Size(stackManipulation.apply(methodVisitor, implementationContext).getMaximalSize(), instrumentedMethod.getStackSize());
/** * {@inheritDoc} */ public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { TypeDescription.Generic parameterType = instrumentedMethod.getParameters().get(0).getType(); MethodDescription setterMethod = methodAccessorFactory.registerSetterFor(fieldDescription, MethodAccessorFactory.AccessType.DEFAULT); StackManipulation.Size stackSize = new StackManipulation.Compound( fieldDescription.isStatic() ? StackManipulation.Trivial.INSTANCE : new StackManipulation.Compound( MethodVariableAccess.loadThis(), FieldAccess.forField(typeDescription.getDeclaredFields() .filter((named(AccessorProxy.FIELD_NAME))).getOnly()).read()), MethodVariableAccess.of(parameterType).loadFrom(1), assigner.assign(parameterType, setterMethod.getParameters().get(0).getType(), Assigner.Typing.DYNAMIC), MethodInvocation.invoke(setterMethod), MethodReturn.VOID ).apply(methodVisitor, implementationContext); return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize()); } }
MethodDescription instrumentedMethod) { StackManipulation arrayReference = MethodVariableAccess.REFERENCE.loadFrom(1); StackManipulation[] parameterLoading = new StackManipulation[accessorMethod.getParameters().size()]; int index = 0; for (TypeDescription.Generic parameterType : accessorMethod.getParameters().asTypeList()) { parameterLoading[index] = new StackManipulation.Compound(arrayReference, IntegerConstant.forValue(index), accessorMethod.isStatic() ? Trivial.INSTANCE : new StackManipulation.Compound( MethodVariableAccess.loadThis(), FieldAccess.forField(typeDescription.getDeclaredFields() .filter((named(RedirectionProxy.FIELD_NAME))) .getOnly()).read()), new StackManipulation.Compound(parameterLoading), MethodInvocation.invoke(accessorMethod), assigner.assign(accessorMethod.getReturnType(), instrumentedMethod.getReturnType(), Assigner.Typing.DYNAMIC), MethodReturn.REFERENCE ).apply(methodVisitor, implementationContext); return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
/** * Locates the only method of a type that is compatible to being overridden for invoking the proxy. * * @param typeDescription The type that is being installed. * @return Its only method after validation. */ private static MethodDescription onlyMethod(TypeDescription typeDescription) { if (!typeDescription.isInterface()) { throw new IllegalArgumentException(typeDescription + " is not an interface"); } else if (!typeDescription.getInterfaces().isEmpty()) { throw new IllegalArgumentException(typeDescription + " must not extend other interfaces"); } else if (!typeDescription.isPublic()) { throw new IllegalArgumentException(typeDescription + " is mot public"); } MethodList<?> methodCandidates = typeDescription.getDeclaredMethods().filter(isAbstract()); if (methodCandidates.size() != 1) { throw new IllegalArgumentException(typeDescription + " must declare exactly one abstract method"); } MethodDescription methodDescription = methodCandidates.getOnly(); if (!methodDescription.getReturnType().asErasure().represents(Object.class)) { throw new IllegalArgumentException(methodDescription + " does not return an Object-type"); } else if (methodDescription.getParameters().size() != 1 || !methodDescription.getParameters().getOnly().getType().asErasure().represents(Object.class)) { throw new IllegalArgumentException(methodDescription + " does not take a single Object-typed argument"); } return methodDescription; }
ElementMatcher<? super MethodDescription> relevanceMatcher = (ElementMatcher<? super MethodDescription>) not(anyOf(implementations.keySet())) .and(returns(isVisibleTo(instrumentedType))) .and(hasParameters(whereNone(hasType(not(isVisibleTo(instrumentedType)))))) .and(ignoredMethods.resolve(instrumentedType)); List<MethodDescription> methods = new ArrayList<MethodDescription>(); for (MethodGraph.Node node : methodGraph.listNodes()) { && methodDescription.isPublic() && !(methodDescription.isAbstract() || methodDescription.isFinal()) && methodDescription.getDeclaringType().isPackagePrivate()) { for (Entry entry : entries) { if (entry.resolve(instrumentedType).matches(methodDescription)) { implementations.put(methodDescription, entry.asPreparedEntry(instrumentedType, methodDescription, methodDescription.getVisibility())); break;
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().filter(not(annotationType(nameStartsWith("jdk.internal."))))) { annotationAppender = annotationAppender.append(annotation, annotationValueFilter); for (ParameterDescription parameterDescription : methodDescription.getParameters()) { AnnotationAppender parameterAppender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnMethodParameter(methodVisitor, parameterDescription.getIndex())); for (TypeDescription.Generic exceptionType : methodDescription.getExceptionTypes()) { annotationAppender = exceptionType.accept(AnnotationAppender.ForTypeAnnotations.ofExceptionType(annotationAppender, annotationValueFilter,
@Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor methodVisitor = super.visitMethod(access, name, desc, signature, exceptions); MethodList<?> methodList = typeDescription.getDeclaredMethods().filter((name.equals(MethodDescription.CONSTRUCTOR_INTERNAL_NAME) ? isConstructor() : ElementMatchers.<MethodDescription>named(name)).and(hasDescriptor(desc))); if (methodList.size() == 1 && methodList.getOnly().getParameters().hasExplicitMetaData()) { for (ParameterDescription parameterDescription : methodList.getOnly().getParameters()) { methodVisitor.visitParameter(parameterDescription.getName(), parameterDescription.getModifiers()); } return new MethodParameterStrippingMethodVisitor(methodVisitor); } else { return methodVisitor; } } }
/** * Returns a builder with the additional, given property. * * @param property The name of the property to define. * @param value An explicit description of the annotation value. * @return A builder with the additional, given property. */ public Builder define(String property, AnnotationValue<?, ?> value) { MethodList<?> methodDescriptions = annotationType.getDeclaredMethods().filter(named(property)); if (methodDescriptions.isEmpty()) { throw new IllegalArgumentException(annotationType + " does not define a property named " + property); } else if (!methodDescriptions.getOnly().getReturnType().asErasure().isAnnotationValue(value.resolve())) { throw new IllegalArgumentException(value + " cannot be assigned to " + property); } Map<String, AnnotationValue<?, ?>> annotationValues = new HashMap<String, AnnotationValue<?, ?>>(); annotationValues.putAll(this.annotationValues); if (annotationValues.put(methodDescriptions.getOnly().getName(), value) != null) { throw new IllegalArgumentException("Property already defined: " + property); } return new Builder(annotationType, annotationValues); }
/** * {@inheritDoc} */ public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) { MethodDescription getterMethod = methodAccessorFactory.registerGetterFor(fieldDescription, MethodAccessorFactory.AccessType.DEFAULT); StackManipulation.Size stackSize = new StackManipulation.Compound( fieldDescription.isStatic() ? StackManipulation.Trivial.INSTANCE : new StackManipulation.Compound( MethodVariableAccess.loadThis(), FieldAccess.forField(typeDescription.getDeclaredFields().filter((named(AccessorProxy.FIELD_NAME))).getOnly()).read()), MethodInvocation.invoke(getterMethod), assigner.assign(getterMethod.getReturnType(), instrumentedMethod.getReturnType(), Assigner.Typing.DYNAMIC), MethodReturn.of(instrumentedMethod.getReturnType().asErasure()) ).apply(methodVisitor, implementationContext); return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize()); } }
/** * {@inheritDoc} */ public MethodRegistry inject(TypeDescription instrumentedType, MethodRegistry methodRegistry) { MethodList<?> candidates = instrumentedType.getSuperClass().getDeclaredMethods().filter(isConstructor().and(elementMatcher)); if (candidates.isEmpty()) { throw new IllegalStateException("No possible candidate for super constructor invocation in " + instrumentedType.getSuperClass()); } else if (!candidates.filter(takesArguments(0)).isEmpty()) { candidates = candidates.filter(takesArguments(0)); } else if (candidates.size() > 1) { throw new IllegalStateException("More than one possible super constructor for constructor delegation: " + candidates); } MethodCall methodCall = MethodCall.invoke(candidates.getOnly()); for (TypeDescription typeDescription : candidates.getOnly().getParameters().asTypeList().asErasures()) { methodCall = methodCall.with(typeDescription.getDefaultValue()); } return methodRegistry.append(new LatentMatcher.Resolved<MethodDescription>(isConstructor().and(takesArguments(0))), new MethodRegistry.Handler.ForImplementation(methodCall), methodAttributeAppenderFactory, Transformer.NoOp.<MethodDescription>make()); } }
/** * {@inheritDoc} */ public StackManipulation resolve(TypeDescription targetType, ByteCodeElement target, TypeList.Generic parameters, TypeDescription.Generic result) { MethodDescription methodDescription = methodResolver.resolve(targetType, target, parameters, result); if (!methodDescription.isAccessibleTo(instrumentedType)) { throw new IllegalStateException(instrumentedType + " cannot access " + methodDescription); } TypeList.Generic mapped = methodDescription.isStatic() ? methodDescription.getParameters().asTypeList() : new TypeList.Generic.Explicit(CompoundList.of(methodDescription.getDeclaringType(), methodDescription.getParameters().asTypeList())); if (!methodDescription.getReturnType().asErasure().isAssignableTo(result.asErasure())) { throw new IllegalStateException("Cannot assign return value of " + methodDescription + " to " + result); } else if (mapped.size() != parameters.size()) { throw new IllegalStateException("Cannot invoke " + methodDescription + " on " + parameters); } for (int index = 0; index < mapped.size(); index++) { if (!mapped.get(index).asErasure().isAssignableTo(parameters.get(index).asErasure())) { throw new IllegalStateException("Cannot invoke " + methodDescription + " on " + parameters); } } return methodDescription.isVirtual() ? MethodInvocation.invoke(methodDescription).virtual(mapped.get(THIS_REFERENCE).asErasure()) : MethodInvocation.invoke(methodDescription); }