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.ClassFileVersion;
19 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
20 import net.bytebuddy.description.method.MethodDescription;
21 import net.bytebuddy.description.method.MethodList;
22 import net.bytebuddy.description.type.TypeDefinition;
23 import net.bytebuddy.description.type.TypeDescription;
24 import net.bytebuddy.dynamic.scaffold.MethodGraph;
25 import net.bytebuddy.implementation.Implementation;
26
27 import static net.bytebuddy.matcher.ElementMatchers.hasSignature;
28 import static net.bytebuddy.matcher.ElementMatchers.isVisibleTo;
29
30 /**
31  * An implementation target for creating a subclass of a given type.
32  */

33 @HashCodeAndEqualsPlugin.Enhance
34 public class SubclassImplementationTarget extends Implementation.Target.AbstractBase {
35
36     /**
37      * The origin type identifier to use.
38      */

39     protected final OriginTypeResolver originTypeResolver;
40
41     /**
42      * Creates a new subclass implementation target.
43      *
44      * @param instrumentedType        The instrumented type.
45      * @param methodGraph             A method graph of the instrumented type.
46      * @param defaultMethodInvocation The default method invocation mode to apply.
47      * @param originTypeResolver      A resolver for the origin type.
48      */

49     protected SubclassImplementationTarget(TypeDescription instrumentedType,
50                                            MethodGraph.Linked methodGraph,
51                                            DefaultMethodInvocation defaultMethodInvocation,
52                                            OriginTypeResolver originTypeResolver) {
53         super(instrumentedType, methodGraph, defaultMethodInvocation);
54         this.originTypeResolver = originTypeResolver;
55     }
56
57     /**
58      * {@inheritDoc}
59      */

60     public Implementation.SpecialMethodInvocation invokeSuper(MethodDescription.SignatureToken token) {
61         return token.getName().equals(MethodDescription.CONSTRUCTOR_INTERNAL_NAME)
62                 ? invokeConstructor(token)
63                 : invokeMethod(token);
64     }
65
66     /**
67      * Resolves a special method invocation for a constructor invocation.
68      *
69      * @param token A token describing the constructor to be invoked.
70      * @return A special method invocation for a constructor representing the given method token, if available.
71      */

72     private Implementation.SpecialMethodInvocation invokeConstructor(MethodDescription.SignatureToken token) {
73         TypeDescription.Generic superClass = instrumentedType.getSuperClass();
74         MethodList<?> candidates = superClass == null
75                 ? new MethodList.Empty<MethodDescription.InGenericShape>()
76                 : superClass.getDeclaredMethods().filter(hasSignature(token).and(isVisibleTo(instrumentedType)));
77         return candidates.size() == 1
78                 ? Implementation.SpecialMethodInvocation.Simple.of(candidates.getOnly(), instrumentedType.getSuperClass().asErasure())
79                 : Implementation.SpecialMethodInvocation.Illegal.INSTANCE;
80     }
81
82     /**
83      * Resolves a special method invocation for a non-constructor invocation.
84      *
85      * @param token A token describing the method to be invoked.
86      * @return A special method invocation for a method representing the given method token, if available.
87      */

88     private Implementation.SpecialMethodInvocation invokeMethod(MethodDescription.SignatureToken token) {
89         MethodGraph.Node methodNode = methodGraph.getSuperClassGraph().locate(token);
90         return methodNode.getSort().isUnique()
91                 ? Implementation.SpecialMethodInvocation.Simple.of(methodNode.getRepresentative(), instrumentedType.getSuperClass().asErasure())
92                 : Implementation.SpecialMethodInvocation.Illegal.INSTANCE;
93     }
94
95     /**
96      * {@inheritDoc}
97      */

98     public TypeDefinition getOriginType() {
99         return originTypeResolver.identify(instrumentedType);
100     }
101
102     /**
103      * Responsible for identifying the origin type that an implementation target represents when
104      * {@link Implementation.Target#getOriginType()} is invoked.
105      */

106     public enum OriginTypeResolver {
107
108         /**
109          * Identifies the super type of an instrumented type as the origin class.
110          */

111         SUPER_CLASS {
112             @Override
113             protected TypeDefinition identify(TypeDescription typeDescription) {
114                 return typeDescription.getSuperClass();
115             }
116         },
117
118         /**
119          * Identifies the instrumented type as its own origin type.
120          */

121         LEVEL_TYPE {
122             @Override
123             protected TypeDefinition identify(TypeDescription typeDescription) {
124                 return typeDescription;
125             }
126         };
127
128         /**
129          * Identifies the origin type to a given type description.
130          *
131          * @param typeDescription The type description for which an origin type should be identified.
132          * @return The origin type to the given type description.
133          */

134         protected abstract TypeDefinition identify(TypeDescription typeDescription);
135     }
136
137     /**
138      * A factory for creating a {@link net.bytebuddy.dynamic.scaffold.subclass.SubclassImplementationTarget}.
139      */

140     public enum Factory implements Implementation.Target.Factory {
141
142         /**
143          * A factory creating a subclass implementation target with a {@link OriginTypeResolver#SUPER_CLASS}.
144          */

145         SUPER_CLASS(OriginTypeResolver.SUPER_CLASS),
146
147         /**
148          * A factory creating a subclass implementation target with a {@link OriginTypeResolver#LEVEL_TYPE}.
149          */

150         LEVEL_TYPE(OriginTypeResolver.LEVEL_TYPE);
151
152         /**
153          * The origin type resolver that this factory hands to the created {@link SubclassImplementationTarget}.
154          */

155         private final OriginTypeResolver originTypeResolver;
156
157         /**
158          * Creates a new factory.
159          *
160          * @param originTypeResolver The origin type resolver that this factory hands to the created {@link SubclassImplementationTarget}.
161          */

162         Factory(OriginTypeResolver originTypeResolver) {
163             this.originTypeResolver = originTypeResolver;
164         }
165
166         /**
167          * {@inheritDoc}
168          */

169         public Implementation.Target make(TypeDescription instrumentedType, MethodGraph.Linked methodGraph, ClassFileVersion classFileVersion) {
170             return new SubclassImplementationTarget(instrumentedType, methodGraph, DefaultMethodInvocation.of(classFileVersion), originTypeResolver);
171         }
172     }
173 }
174