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