1 /*
2 * Copyright 2014 - 2020 Rafael Winterhalter
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.bytebuddy.implementation.bind.annotation;
17
18 import net.bytebuddy.description.annotation.AnnotationDescription;
19 import net.bytebuddy.description.method.MethodDescription;
20 import net.bytebuddy.description.method.ParameterDescription;
21 import net.bytebuddy.description.type.TypeDescription;
22 import net.bytebuddy.implementation.Implementation;
23 import net.bytebuddy.implementation.bind.MethodDelegationBinder;
24 import net.bytebuddy.implementation.bytecode.StackManipulation;
25 import net.bytebuddy.implementation.bytecode.assign.Assigner;
26 import net.bytebuddy.implementation.bytecode.collection.ArrayFactory;
27 import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
28 import net.bytebuddy.utility.CompoundList;
29
30 import java.lang.annotation.*;
31 import java.util.ArrayList;
32 import java.util.List;
33
34 /**
35 * Parameters that are annotated with this annotation will be assigned a collection (or an array) containing
36 * all arguments of the source method. Currently, this annotation supports the following collection types:
37 * <ul>
38 * <li>Array</li>
39 * </ul>
40 * <p> </p>
41 * By default, this annotation applies a
42 * {@link net.bytebuddy.implementation.bind.annotation.AllArguments.Assignment#STRICT}
43 * assignment of the source method's parameters to the array. This implies that parameters that are not assignable to
44 * the annotated array's component type make the method with this parameter unbindable. To avoid this, you can
45 * use a {@link net.bytebuddy.implementation.bind.annotation.AllArguments.Assignment#SLACK} assignment
46 * which simply skips non-assignable values instead.
47 *
48 * @see net.bytebuddy.implementation.MethodDelegation
49 * @see net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder
50 * @see net.bytebuddy.implementation.bind.annotation.RuntimeType
51 */
52 @Documented
53 @Retention(RetentionPolicy.RUNTIME)
54 @Target(ElementType.PARAMETER)
55 public @interface AllArguments {
56
57 /**
58 * Defines the type of {@link net.bytebuddy.implementation.bind.annotation.AllArguments.Assignment}
59 * type that is applied for filling the annotated array with values.
60 *
61 * @return The assignment handling to be applied for the annotated parameter.
62 */
63 Assignment value() default Assignment.STRICT;
64
65 /**
66 * Determines if the array should contain the instance that defines the intercepted value when intercepting
67 * a non-static method.
68 *
69 * @return {@code true} if the instance on which the intercepted method should be invoked should be
70 * included in the array containing the arguments.
71 */
72 boolean includeSelf() default false;
73
74 /**
75 * A directive for how an {@link net.bytebuddy.implementation.bind.annotation.AllArguments}
76 * annotation on an array is to be interpreted.
77 */
78 enum Assignment {
79
80 /**
81 * A strict assignment attempts to include <b>all</b> parameter values of the source method. If only one of these
82 * parameters is not assignable to the component type of the annotated array, the method is considered as
83 * non-bindable.
84 */
85 STRICT(true),
86
87 /**
88 * Other than a {@link net.bytebuddy.implementation.bind.annotation.AllArguments.Assignment#STRICT}
89 * assignment, a slack assignment simply ignores non-bindable parameters and does not include them in the target
90 * array. In the most extreme case where no source method parameter is assignable to the component type
91 * of the annotated array, the array that is assigned to the target parameter is empty.
92 */
93 SLACK(false);
94
95 /**
96 * Determines if this assignment is strict.
97 */
98 private final boolean strict;
99
100 /**
101 * Creates a new assignment type.
102 *
103 * @param strict {@code true} if this assignment is strict.
104 */
105 Assignment(boolean strict) {
106 this.strict = strict;
107 }
108
109 /**
110 * Returns {@code true} if this assignment is strict.
111 *
112 * @return {@code true} if this assignment is strict.
113 */
114 protected boolean isStrict() {
115 return strict;
116 }
117 }
118
119 /**
120 * A binder for handling the
121 * {@link net.bytebuddy.implementation.bind.annotation.AllArguments}
122 * annotation.
123 *
124 * @see TargetMethodAnnotationDrivenBinder
125 */
126 enum Binder implements TargetMethodAnnotationDrivenBinder.ParameterBinder<AllArguments> {
127
128 /**
129 * The singleton instance.
130 */
131 INSTANCE;
132
133 /**
134 * {@inheritDoc}
135 */
136 public Class<AllArguments> getHandledType() {
137 return AllArguments.class;
138 }
139
140 /**
141 * {@inheritDoc}
142 */
143 public MethodDelegationBinder.ParameterBinding<?> bind(AnnotationDescription.Loadable<AllArguments> annotation,
144 MethodDescription source,
145 ParameterDescription target,
146 Implementation.Target implementationTarget,
147 Assigner assigner,
148 Assigner.Typing typing) {
149 TypeDescription.Generic componentType;
150 if (target.getType().represents(Object.class)) {
151 componentType = TypeDescription.Generic.OBJECT;
152 } else if (target.getType().isArray()) {
153 componentType = target.getType().getComponentType();
154 } else {
155 throw new IllegalStateException("Expected an array type for all argument annotation on " + source);
156 }
157 boolean includeThis = !source.isStatic() && annotation.load().includeSelf();
158 List<StackManipulation> stackManipulations = new ArrayList<StackManipulation>(source.getParameters().size() + (includeThis ? 1 : 0));
159 int offset = source.isStatic() || includeThis ? 0 : 1;
160 for (TypeDescription.Generic sourceParameter : includeThis
161 ? CompoundList.of(implementationTarget.getInstrumentedType().asGenericType(), source.getParameters().asTypeList())
162 : source.getParameters().asTypeList()) {
163 StackManipulation stackManipulation = new StackManipulation.Compound(
164 MethodVariableAccess.of(sourceParameter).loadFrom(offset),
165 assigner.assign(sourceParameter, componentType, typing)
166 );
167 if (stackManipulation.isValid()) {
168 stackManipulations.add(stackManipulation);
169 } else if (annotation.load().value().isStrict()) {
170 return MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
171 }
172 offset += sourceParameter.getStackSize().getSize();
173 }
174 return new MethodDelegationBinder.ParameterBinding.Anonymous(ArrayFactory.forType(componentType).withValues(stackManipulations));
175 }
176 }
177 }
178