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