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.dynamic.scaffold.subclass;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.method.MethodDescription;
20 import net.bytebuddy.description.method.MethodList;
21 import net.bytebuddy.description.type.TypeDescription;
22 import net.bytebuddy.dynamic.Transformer;
23 import net.bytebuddy.dynamic.scaffold.MethodRegistry;
24 import net.bytebuddy.implementation.MethodCall;
25 import net.bytebuddy.implementation.SuperMethodCall;
26 import net.bytebuddy.implementation.attribute.MethodAttributeAppender;
27 import net.bytebuddy.matcher.ElementMatcher;
28 import net.bytebuddy.matcher.LatentMatcher;
29 import net.bytebuddy.jar.asm.Opcodes;
30
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.List;
34
35 import static net.bytebuddy.matcher.ElementMatchers.*;
36
37 /**
38 * A constructor strategy is responsible for creating bootstrap constructors for a
39 * {@link SubclassDynamicTypeBuilder}.
40 *
41 * @see net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy.Default
42 */
43 public interface ConstructorStrategy {
44
45 /**
46 * Extracts constructors for a given super type. The extracted constructor signatures will then be imitated by the
47 * created dynamic type.
48 *
49 * @param instrumentedType The type for which the constructors should be created.
50 * @return A list of tokens that describe the constructors that are to be implemented.
51 */
52 List<MethodDescription.Token> extractConstructors(TypeDescription instrumentedType);
53
54 /**
55 * Returns a method registry that is capable of creating byte code for the constructors that were
56 * provided by the
57 * {@link net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy#extractConstructors(TypeDescription)}
58 * method of this instance.
59 *
60 * @param instrumentedType The instrumented type.
61 * @param methodRegistry The original method registry.
62 * @return A method registry that is capable of providing byte code for the constructors that were added by this strategy.
63 */
64 MethodRegistry inject(TypeDescription instrumentedType, MethodRegistry methodRegistry);
65
66 /**
67 * Default implementations of constructor strategies. Any such strategy offers to additionally apply an {@link MethodAttributeAppender.Factory}.
68 */
69 enum Default implements ConstructorStrategy {
70
71 /**
72 * This strategy is adding no constructors such that the instrumented type will by default not have any. This
73 * is legal by Java byte code requirements. However, if no constructor is added manually if this strategy is
74 * applied, the type is not constructable without using JVM non-public functionality.
75 */
76 NO_CONSTRUCTORS {
77 @Override
78 protected List<MethodDescription.Token> doExtractConstructors(TypeDescription superClass) {
79 return Collections.emptyList();
80 }
81
82 @Override
83 protected MethodRegistry doInject(MethodRegistry methodRegistry, MethodAttributeAppender.Factory methodAttributeAppenderFactory) {
84 return methodRegistry;
85 }
86 },
87
88 /**
89 * This strategy is adding a default constructor that calls it's super types default constructor. If no such
90 * constructor is defined by the super class, an {@link IllegalArgumentException} is thrown. Note that the default
91 * constructor needs to be visible to its sub type for this strategy to work. The declared default constructor of
92 * the created class is declared public and without annotations.
93 */
94 DEFAULT_CONSTRUCTOR {
95 @Override
96 protected List<MethodDescription.Token> doExtractConstructors(TypeDescription instrumentedType) {
97 TypeDescription.Generic superClass = instrumentedType.getSuperClass();
98 MethodList<?> defaultConstructors = superClass == null
99 ? new MethodList.Empty<MethodDescription.InGenericShape>()
100 : superClass.getDeclaredMethods().filter(isConstructor().and(takesArguments(0)).<MethodDescription>and(isVisibleTo(instrumentedType)));
101 if (defaultConstructors.size() == 1) {
102 return Collections.singletonList(new MethodDescription.Token(Opcodes.ACC_PUBLIC));
103 } else {
104 throw new IllegalArgumentException(instrumentedType.getSuperClass() + " declares no constructor that is visible to " + instrumentedType);
105 }
106 }
107
108 @Override
109 protected MethodRegistry doInject(MethodRegistry methodRegistry, MethodAttributeAppender.Factory methodAttributeAppenderFactory) {
110 return methodRegistry.append(new LatentMatcher.Resolved<MethodDescription>(isConstructor()),
111 new MethodRegistry.Handler.ForImplementation(SuperMethodCall.INSTANCE),
112 methodAttributeAppenderFactory,
113 Transformer.NoOp.<MethodDescription>make());
114 }
115 },
116
117 /**
118 * This strategy is adding all constructors of the instrumented type's super class where each constructor is
119 * directly invoking its signature-equivalent super class constructor. Only constructors that are visible to the
120 * instrumented type are added, i.e. package-private constructors are only added if the super type is defined
121 * in the same package as the instrumented type and private constructors are always skipped.
122 */
123 IMITATE_SUPER_CLASS {
124 @Override
125 protected List<MethodDescription.Token> doExtractConstructors(TypeDescription instrumentedType) {
126 TypeDescription.Generic superClass = instrumentedType.getSuperClass();
127 return (superClass == null
128 ? new MethodList.Empty<MethodDescription.InGenericShape>()
129 : superClass.getDeclaredMethods().filter(isConstructor().and(isVisibleTo(instrumentedType)))).asTokenList(is(instrumentedType));
130 }
131
132 @Override
133 public MethodRegistry doInject(MethodRegistry methodRegistry, MethodAttributeAppender.Factory methodAttributeAppenderFactory) {
134 return methodRegistry.append(new LatentMatcher.Resolved<MethodDescription>(isConstructor()),
135 new MethodRegistry.Handler.ForImplementation(SuperMethodCall.INSTANCE),
136 methodAttributeAppenderFactory,
137 Transformer.NoOp.<MethodDescription>make());
138 }
139 },
140
141 /**
142 * This strategy is adding all constructors of the instrumented type's super class where each constructor is
143 * directly invoking its signature-equivalent super class constructor. Only {@code public} constructors are
144 * added.
145 */
146 IMITATE_SUPER_CLASS_PUBLIC {
147 @Override
148 protected List<MethodDescription.Token> doExtractConstructors(TypeDescription instrumentedType) {
149 TypeDescription.Generic superClass = instrumentedType.getSuperClass();
150 return (superClass == null
151 ? new MethodList.Empty<MethodDescription.InGenericShape>()
152 : superClass.getDeclaredMethods().filter(isPublic().and(isConstructor()))).asTokenList(is(instrumentedType));
153 }
154
155 @Override
156 public MethodRegistry doInject(MethodRegistry methodRegistry, MethodAttributeAppender.Factory methodAttributeAppenderFactory) {
157 return methodRegistry.append(new LatentMatcher.Resolved<MethodDescription>(isConstructor()),
158 new MethodRegistry.Handler.ForImplementation(SuperMethodCall.INSTANCE),
159 methodAttributeAppenderFactory,
160 Transformer.NoOp.<MethodDescription>make());
161 }
162 },
163
164 /**
165 * This strategy is adding all constructors of the instrumented type's super class where each constructor is
166 * directly invoking its signature-equivalent super class constructor. A constructor is added for any constructor
167 * of the super class that is invokable and is declared as {@code public}.
168 */
169 IMITATE_SUPER_CLASS_OPENING {
170 @Override
171 protected List<MethodDescription.Token> doExtractConstructors(TypeDescription instrumentedType) {
172 TypeDescription.Generic superClass = instrumentedType.getSuperClass();
173 return (superClass == null
174 ? new MethodList.Empty<MethodDescription.InGenericShape>()
175 : superClass.getDeclaredMethods().filter(isConstructor().and(isVisibleTo(instrumentedType)))).asTokenList(is(instrumentedType));
176 }
177
178 @Override
179 public MethodRegistry doInject(MethodRegistry methodRegistry, MethodAttributeAppender.Factory methodAttributeAppenderFactory) {
180 return methodRegistry.append(new LatentMatcher.Resolved<MethodDescription>(isConstructor()),
181 new MethodRegistry.Handler.ForImplementation(SuperMethodCall.INSTANCE),
182 methodAttributeAppenderFactory,
183 Transformer.NoOp.<MethodDescription>make());
184 }
185
186 @Override
187 protected int resolveModifier(int modifiers) {
188 return Opcodes.ACC_PUBLIC;
189 }
190 };
191
192 /**
193 * {@inheritDoc}
194 */
195 public List<MethodDescription.Token> extractConstructors(TypeDescription instrumentedType) {
196 List<MethodDescription.Token> tokens = doExtractConstructors(instrumentedType), stripped = new ArrayList<MethodDescription.Token>(tokens.size());
197 for (MethodDescription.Token token : tokens) {
198 stripped.add(new MethodDescription.Token(token.getName(),
199 resolveModifier(token.getModifiers()),
200 token.getTypeVariableTokens(),
201 token.getReturnType(),
202 token.getParameterTokens(),
203 token.getExceptionTypes(),
204 token.getAnnotations(),
205 token.getDefaultValue(),
206 TypeDescription.Generic.UNDEFINED));
207 }
208 return stripped;
209 }
210
211 /**
212 * Resolves a constructor's modifiers.
213 *
214 * @param modifiers The actual constructor's modifiers.
215 * @return The resolved modifiers.
216 */
217 protected int resolveModifier(int modifiers) {
218 return modifiers;
219 }
220
221 /**
222 * Extracts the relevant method tokens of the instrumented type's constructors.
223 *
224 * @param instrumentedType The type for which to extract the constructors.
225 * @return A list of relevant method tokens.
226 */
227 protected abstract List<MethodDescription.Token> doExtractConstructors(TypeDescription instrumentedType);
228
229 /**
230 * {@inheritDoc}
231 */
232 public MethodRegistry inject(TypeDescription instrumentedType, MethodRegistry methodRegistry) {
233 return doInject(methodRegistry, MethodAttributeAppender.NoOp.INSTANCE);
234 }
235
236 /**
237 * Applies the actual injection with a method attribute appender factory supplied.
238 *
239 * @param methodRegistry The method registry into which to inject the constructors.
240 * @param methodAttributeAppenderFactory The method attribute appender to use.
241 * @return The resulting method registry.
242 */
243 protected abstract MethodRegistry doInject(MethodRegistry methodRegistry, MethodAttributeAppender.Factory methodAttributeAppenderFactory);
244
245 /**
246 * Returns a constructor strategy that supplies the supplied method attribute appender factory.
247 *
248 * @param methodAttributeAppenderFactory The method attribute appender factory to use.
249 * @return A copy of this constructor strategy with the method attribute appender factory applied.
250 */
251 public ConstructorStrategy with(MethodAttributeAppender.Factory methodAttributeAppenderFactory) {
252 return new WithMethodAttributeAppenderFactory(this, methodAttributeAppenderFactory);
253 }
254
255 /**
256 * Applies this constructor strategy while retaining any of the base constructor's annotations.
257 *
258 * @return A copy of this constructor strategy which retains any of the base constructor's annotations.
259 */
260 public ConstructorStrategy withInheritedAnnotations() {
261 return new WithMethodAttributeAppenderFactory(this, MethodAttributeAppender.ForInstrumentedMethod.EXCLUDING_RECEIVER);
262 }
263
264 /**
265 * A wrapper for a default constructor strategy which additionally applies a method attribute appender factory.
266 */
267 @HashCodeAndEqualsPlugin.Enhance
268 protected static class WithMethodAttributeAppenderFactory implements ConstructorStrategy {
269
270 /**
271 * The delegate default constructor strategy.
272 */
273 private final Default delegate;
274
275 /**
276 * The method attribute appender factory to apply.
277 */
278 private final MethodAttributeAppender.Factory methodAttributeAppenderFactory;
279
280 /**
281 * Creates a new wrapper for a default constructor strategy.
282 *
283 * @param delegate The delegate default constructor strategy.
284 * @param methodAttributeAppenderFactory The method attribute appender factory to apply.
285 */
286 protected WithMethodAttributeAppenderFactory(Default delegate, MethodAttributeAppender.Factory methodAttributeAppenderFactory) {
287 this.delegate = delegate;
288 this.methodAttributeAppenderFactory = methodAttributeAppenderFactory;
289 }
290
291 /**
292 * {@inheritDoc}
293 */
294 public List<MethodDescription.Token> extractConstructors(TypeDescription instrumentedType) {
295 return delegate.extractConstructors(instrumentedType);
296 }
297
298 /**
299 * {@inheritDoc}
300 */
301 public MethodRegistry inject(TypeDescription instrumentedType, MethodRegistry methodRegistry) {
302 return delegate.doInject(methodRegistry, methodAttributeAppenderFactory);
303 }
304 }
305 }
306
307 /**
308 * A constructor strategy that creates a default constructor that invokes a super constructor with default arguments.
309 */
310 @HashCodeAndEqualsPlugin.Enhance
311 class ForDefaultConstructor implements ConstructorStrategy {
312
313 /**
314 * A matcher to select a super constructor among possible candidates.
315 */
316 private final ElementMatcher<? super MethodDescription> elementMatcher;
317
318 /**
319 * The method attribute appender factory to apply.
320 */
321 private final MethodAttributeAppender.Factory methodAttributeAppenderFactory;
322
323 /**
324 * Creates a constructor strategy for invoking a super constructor with default arguments.
325 */
326 public ForDefaultConstructor() {
327 this(any());
328 }
329
330 /**
331 * Creates a constructor strategy for invoking a super constructor with default arguments.
332 *
333 * @param elementMatcher A matcher to select a super constructor among possible candidates.
334 */
335 public ForDefaultConstructor(ElementMatcher<? super MethodDescription> elementMatcher) {
336 this(elementMatcher, MethodAttributeAppender.NoOp.INSTANCE);
337 }
338
339 /**
340 * Creates a constructor strategy for invoking a super constructor with default arguments.
341 *
342 * @param methodAttributeAppenderFactory The method attribute appender factory to apply.
343 */
344 public ForDefaultConstructor(MethodAttributeAppender.Factory methodAttributeAppenderFactory) {
345 this(any(), methodAttributeAppenderFactory);
346 }
347
348 /**
349 * Creates a constructor strategy for invoking a super constructor with default arguments.
350 *
351 * @param elementMatcher A matcher to select a super constructor among possible candidates.
352 * @param methodAttributeAppenderFactory The method attribute appender factory to apply.
353 */
354 public ForDefaultConstructor(ElementMatcher<? super MethodDescription> elementMatcher,
355 MethodAttributeAppender.Factory methodAttributeAppenderFactory) {
356 this.elementMatcher = elementMatcher;
357 this.methodAttributeAppenderFactory = methodAttributeAppenderFactory;
358 }
359
360 /**
361 * {@inheritDoc}
362 */
363 public List<MethodDescription.Token> extractConstructors(TypeDescription instrumentedType) {
364 if (instrumentedType.getSuperClass().getDeclaredMethods().filter(isConstructor()).isEmpty()) {
365 throw new IllegalStateException("Cannot define default constructor for class without super class constructor");
366 }
367 return Collections.singletonList(new MethodDescription.Token(Opcodes.ACC_PUBLIC));
368 }
369
370 /**
371 * {@inheritDoc}
372 */
373 public MethodRegistry inject(TypeDescription instrumentedType, MethodRegistry methodRegistry) {
374 MethodList<?> candidates = instrumentedType.getSuperClass().getDeclaredMethods().filter(isConstructor().and(elementMatcher));
375 if (candidates.isEmpty()) {
376 throw new IllegalStateException("No possible candidate for super constructor invocation in " + instrumentedType.getSuperClass());
377 } else if (!candidates.filter(takesArguments(0)).isEmpty()) {
378 candidates = candidates.filter(takesArguments(0));
379 } else if (candidates.size() > 1) {
380 throw new IllegalStateException("More than one possible super constructor for constructor delegation: " + candidates);
381 }
382 MethodCall methodCall = MethodCall.invoke(candidates.getOnly());
383 for (TypeDescription typeDescription : candidates.getOnly().getParameters().asTypeList().asErasures()) {
384 methodCall = methodCall.with(typeDescription.getDefaultValue());
385 }
386 return methodRegistry.append(new LatentMatcher.Resolved<MethodDescription>(isConstructor().and(takesArguments(0))),
387 new MethodRegistry.Handler.ForImplementation(methodCall),
388 methodAttributeAppenderFactory,
389 Transformer.NoOp.<MethodDescription>make());
390 }
391 }
392 }
393