Skip to content

Commit 173db14

Browse files
author
mateuszrzeszutek
committed
Muzzle should fail on unimplemented abstract methods
1 parent d89ce81 commit 173db14

File tree

13 files changed

+1086
-164
lines changed

13 files changed

+1086
-164
lines changed
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
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+
17+
package io.opentelemetry.javaagent.tooling.muzzle;
18+
19+
import static java.util.Collections.emptyList;
20+
import static java.util.Collections.singleton;
21+
import static net.bytebuddy.description.method.MethodDescription.CONSTRUCTOR_INTERNAL_NAME;
22+
23+
import com.google.common.base.Function;
24+
import com.google.common.base.Objects;
25+
import com.google.common.base.Predicate;
26+
import com.google.common.collect.FluentIterable;
27+
import com.google.common.collect.Iterables;
28+
import io.opentelemetry.javaagent.tooling.muzzle.Reference.Flag;
29+
import java.util.Map;
30+
import net.bytebuddy.description.method.MethodDescription.InDefinedShape;
31+
import net.bytebuddy.description.type.TypeDescription;
32+
import net.bytebuddy.pool.TypePool;
33+
import net.bytebuddy.pool.TypePool.Resolution;
34+
35+
/** This class provides a common interface for {@link Reference} and {@link TypeDescription}. */
36+
public interface HelperReferenceWrapper {
37+
boolean isAbstract();
38+
39+
/**
40+
* @return true if the wrapped type extends any class other than {@link Object} or implements any
41+
* interface.
42+
*/
43+
boolean hasSuperTypes();
44+
45+
/**
46+
* @return An iterable containing the wrapped type's super class (if exists) and implemented
47+
* interfaces.
48+
*/
49+
Iterable<HelperReferenceWrapper> getSuperTypes();
50+
51+
/** @return An iterable with all non-private, non-static methods declared in the wrapped type. */
52+
Iterable<Method> getMethods();
53+
54+
final class Method {
55+
private final boolean isAbstract;
56+
private final String declaringClass;
57+
private final String name;
58+
private final String descriptor;
59+
60+
public Method(boolean isAbstract, String declaringClass, String name, String descriptor) {
61+
this.isAbstract = isAbstract;
62+
this.declaringClass = declaringClass;
63+
this.name = name;
64+
this.descriptor = descriptor;
65+
}
66+
67+
public boolean isAbstract() {
68+
return isAbstract;
69+
}
70+
71+
public String getName() {
72+
return name;
73+
}
74+
75+
public String getDescriptor() {
76+
return descriptor;
77+
}
78+
79+
@Override
80+
public boolean equals(Object o) {
81+
if (this == o) return true;
82+
if (o == null || getClass() != o.getClass()) return false;
83+
Method method = (Method) o;
84+
return Objects.equal(name, method.name) && Objects.equal(descriptor, method.descriptor);
85+
}
86+
87+
@Override
88+
public int hashCode() {
89+
return Objects.hashCode(name, descriptor);
90+
}
91+
92+
@Override
93+
public String toString() {
94+
return declaringClass + "#" + name + descriptor;
95+
}
96+
}
97+
98+
class Factory {
99+
private final TypePool classpathPool;
100+
private final Map<String, Reference> helperReferences;
101+
102+
public Factory(TypePool classpathPool, Map<String, Reference> helperReferences) {
103+
this.classpathPool = classpathPool;
104+
this.helperReferences = helperReferences;
105+
}
106+
107+
public HelperReferenceWrapper create(Reference reference) {
108+
return new ReferenceType(reference);
109+
}
110+
111+
private HelperReferenceWrapper create(String className) {
112+
Resolution resolution = classpathPool.describe(className);
113+
if (resolution.isResolved()) {
114+
return new ClasspathType(resolution.resolve());
115+
} else if (helperReferences.containsKey(className)) {
116+
return new ReferenceType(helperReferences.get(className));
117+
}
118+
throw new IllegalStateException("Missing class " + className);
119+
}
120+
121+
private final class ReferenceType implements HelperReferenceWrapper {
122+
private final Reference reference;
123+
124+
private ReferenceType(Reference reference) {
125+
this.reference = reference;
126+
}
127+
128+
@Override
129+
public boolean isAbstract() {
130+
return reference.getFlags().contains(Flag.ABSTRACT);
131+
}
132+
133+
@Override
134+
public boolean hasSuperTypes() {
135+
return hasActualSuperType() || reference.getInterfaces().size() > 0;
136+
}
137+
138+
// Uses guava iterables to avoid unnecessary collection copying
139+
@Override
140+
public Iterable<HelperReferenceWrapper> getSuperTypes() {
141+
Iterable<HelperReferenceWrapper> superClass = emptyList();
142+
if (hasActualSuperType()) {
143+
superClass = singleton(Factory.this.create(reference.getSuperName()));
144+
}
145+
146+
Iterable<HelperReferenceWrapper> interfaces =
147+
FluentIterable.from(reference.getInterfaces()).transform(toWrapper());
148+
149+
return Iterables.concat(superClass, interfaces);
150+
}
151+
152+
private boolean hasActualSuperType() {
153+
return reference.getSuperName() != null
154+
&& !reference.getSuperName().equals(Object.class.getName());
155+
}
156+
157+
private Function<String, HelperReferenceWrapper> toWrapper() {
158+
return new Function<String, HelperReferenceWrapper>() {
159+
@Override
160+
public HelperReferenceWrapper apply(String interfaceName) {
161+
return Factory.this.create(interfaceName);
162+
}
163+
};
164+
}
165+
166+
// Uses guava iterables to avoid unnecessary collection copying
167+
@Override
168+
public Iterable<Method> getMethods() {
169+
return FluentIterable.from(reference.getMethods())
170+
.filter(isOverrideable())
171+
.transform(toMethod());
172+
}
173+
174+
private Predicate<Reference.Method> isOverrideable() {
175+
return new Predicate<Reference.Method>() {
176+
@Override
177+
public boolean apply(Reference.Method input) {
178+
return !(input.getFlags().contains(Flag.STATIC)
179+
|| input.getFlags().contains(Flag.PRIVATE_OR_HIGHER)
180+
|| CONSTRUCTOR_INTERNAL_NAME.equals(input.getName()));
181+
}
182+
};
183+
}
184+
185+
private Function<Reference.Method, Method> toMethod() {
186+
return new Function<Reference.Method, Method>() {
187+
@Override
188+
public Method apply(Reference.Method method) {
189+
return new Method(
190+
method.getFlags().contains(Flag.ABSTRACT),
191+
reference.getClassName(),
192+
method.getName(),
193+
method.getDescriptor());
194+
}
195+
};
196+
}
197+
}
198+
199+
private static final class ClasspathType implements HelperReferenceWrapper {
200+
private final TypeDescription type;
201+
202+
private ClasspathType(TypeDescription type) {
203+
this.type = type;
204+
}
205+
206+
@Override
207+
public boolean isAbstract() {
208+
return type.isAbstract();
209+
}
210+
211+
@Override
212+
public boolean hasSuperTypes() {
213+
return hasActualSuperType() || type.getInterfaces().size() > 0;
214+
}
215+
216+
private boolean hasActualSuperType() {
217+
return type.getSuperClass() != null
218+
&& !type.getSuperClass().asErasure().equals(TypeDescription.OBJECT);
219+
}
220+
221+
// Uses guava iterables to avoid unnecessary collection copying
222+
@Override
223+
public Iterable<HelperReferenceWrapper> getSuperTypes() {
224+
Iterable<HelperReferenceWrapper> superClass = emptyList();
225+
if (hasActualSuperType()) {
226+
superClass =
227+
singleton(
228+
(HelperReferenceWrapper) new ClasspathType(type.getSuperClass().asErasure()));
229+
}
230+
231+
Iterable<HelperReferenceWrapper> interfaces =
232+
Iterables.transform(type.getInterfaces().asErasures(), toWrapper());
233+
234+
return Iterables.concat(superClass, interfaces);
235+
}
236+
237+
private static Function<TypeDescription, HelperReferenceWrapper> toWrapper() {
238+
return new Function<TypeDescription, HelperReferenceWrapper>() {
239+
@Override
240+
public HelperReferenceWrapper apply(TypeDescription input) {
241+
return new ClasspathType(input);
242+
}
243+
};
244+
}
245+
246+
// Uses guava iterables to avoid unnecessary collection copying
247+
@Override
248+
public Iterable<Method> getMethods() {
249+
return FluentIterable.from(type.getDeclaredMethods())
250+
.filter(isOverrideable())
251+
.transform(toMethod());
252+
}
253+
254+
private static Predicate<InDefinedShape> isOverrideable() {
255+
return new Predicate<InDefinedShape>() {
256+
@Override
257+
public boolean apply(InDefinedShape input) {
258+
return !(input.isStatic() || input.isPrivate() || input.isConstructor());
259+
}
260+
};
261+
}
262+
263+
private Function<InDefinedShape, Method> toMethod() {
264+
return new Function<InDefinedShape, Method>() {
265+
@Override
266+
public Method apply(InDefinedShape method) {
267+
return new Method(
268+
method.isAbstract(),
269+
type.getName(),
270+
method.getInternalName(),
271+
method.getDescriptor());
272+
}
273+
};
274+
}
275+
}
276+
}
277+
}

javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/MuzzleVisitor.java

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -133,28 +133,18 @@ public MethodVisitor visitMethod(
133133
}
134134

135135
public Reference[] generateReferences() {
136-
// track sources we've generated references from to avoid recursion
137-
Set<String> referenceSources = new HashSet<>();
138136
Map<String, Reference> references = new HashMap<>();
139-
Set<String> adviceClassNames = new HashSet<>();
140-
141-
for (String adviceClassName : instrumenter.transformers().values()) {
142-
adviceClassNames.add(adviceClassName);
143-
}
137+
Set<String> adviceClassNames = new HashSet<>(instrumenter.transformers().values());
144138

145139
for (String adviceClass : adviceClassNames) {
146-
if (!referenceSources.contains(adviceClass)) {
147-
referenceSources.add(adviceClass);
148-
for (Map.Entry<String, Reference> entry :
149-
ReferenceCreator.createReferencesFrom(
150-
adviceClass, ReferenceMatcher.class.getClassLoader())
151-
.entrySet()) {
152-
if (references.containsKey(entry.getKey())) {
153-
references.put(
154-
entry.getKey(), references.get(entry.getKey()).merge(entry.getValue()));
155-
} else {
156-
references.put(entry.getKey(), entry.getValue());
157-
}
140+
for (Map.Entry<String, Reference> entry :
141+
ReferenceCreator.createReferencesFrom(
142+
adviceClass, ReferenceMatcher.class.getClassLoader())
143+
.entrySet()) {
144+
if (references.containsKey(entry.getKey())) {
145+
references.put(entry.getKey(), references.get(entry.getKey()).merge(entry.getValue()));
146+
} else {
147+
references.put(entry.getKey(), entry.getValue());
158148
}
159149
}
160150
}

0 commit comments

Comments
 (0)