Skip to content

Commit ad9d143

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

File tree

13 files changed

+1087
-164
lines changed

13 files changed

+1087
-164
lines changed
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
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+
if (helperReferences.containsKey(className)) {
113+
return new ReferenceType(helperReferences.get(className));
114+
}
115+
Resolution resolution = classpathPool.describe(className);
116+
if (resolution.isResolved()) {
117+
return new ClasspathType(resolution.resolve());
118+
}
119+
throw new IllegalStateException("Missing class " + className);
120+
}
121+
122+
private final class ReferenceType implements HelperReferenceWrapper {
123+
private final Reference reference;
124+
125+
private ReferenceType(Reference reference) {
126+
this.reference = reference;
127+
}
128+
129+
@Override
130+
public boolean isAbstract() {
131+
return reference.getFlags().contains(Flag.ABSTRACT);
132+
}
133+
134+
@Override
135+
public boolean hasSuperTypes() {
136+
return hasActualSuperType() || reference.getInterfaces().size() > 0;
137+
}
138+
139+
// Uses guava iterables to avoid unnecessary collection copying
140+
@Override
141+
public Iterable<HelperReferenceWrapper> getSuperTypes() {
142+
Iterable<HelperReferenceWrapper> superClass = emptyList();
143+
if (hasActualSuperType()) {
144+
superClass = singleton(Factory.this.create(reference.getSuperName()));
145+
}
146+
147+
Iterable<HelperReferenceWrapper> interfaces =
148+
FluentIterable.from(reference.getInterfaces()).transform(toWrapper());
149+
150+
return Iterables.concat(superClass, interfaces);
151+
}
152+
153+
private boolean hasActualSuperType() {
154+
return reference.getSuperName() != null
155+
&& !reference.getSuperName().equals(Object.class.getName());
156+
}
157+
158+
private Function<String, HelperReferenceWrapper> toWrapper() {
159+
return new Function<String, HelperReferenceWrapper>() {
160+
@Override
161+
public HelperReferenceWrapper apply(String interfaceName) {
162+
return Factory.this.create(interfaceName);
163+
}
164+
};
165+
}
166+
167+
// Uses guava iterables to avoid unnecessary collection copying
168+
@Override
169+
public Iterable<Method> getMethods() {
170+
return FluentIterable.from(reference.getMethods())
171+
.filter(isOverrideable())
172+
.transform(toMethod());
173+
}
174+
175+
private Predicate<Reference.Method> isOverrideable() {
176+
return new Predicate<Reference.Method>() {
177+
@Override
178+
public boolean apply(Reference.Method input) {
179+
return !(input.getFlags().contains(Flag.STATIC)
180+
|| input.getFlags().contains(Flag.PRIVATE_OR_HIGHER)
181+
|| CONSTRUCTOR_INTERNAL_NAME.equals(input.getName()));
182+
}
183+
};
184+
}
185+
186+
private Function<Reference.Method, Method> toMethod() {
187+
return new Function<Reference.Method, Method>() {
188+
@Override
189+
public Method apply(Reference.Method method) {
190+
return new Method(
191+
method.getFlags().contains(Flag.ABSTRACT),
192+
reference.getClassName(),
193+
method.getName(),
194+
method.getDescriptor());
195+
}
196+
};
197+
}
198+
}
199+
200+
private static final class ClasspathType implements HelperReferenceWrapper {
201+
private final TypeDescription type;
202+
203+
private ClasspathType(TypeDescription type) {
204+
this.type = type;
205+
}
206+
207+
@Override
208+
public boolean isAbstract() {
209+
return type.isAbstract();
210+
}
211+
212+
@Override
213+
public boolean hasSuperTypes() {
214+
return hasActualSuperType() || type.getInterfaces().size() > 0;
215+
}
216+
217+
private boolean hasActualSuperType() {
218+
return type.getSuperClass() != null
219+
&& !type.getSuperClass().asErasure().equals(TypeDescription.OBJECT);
220+
}
221+
222+
// Uses guava iterables to avoid unnecessary collection copying
223+
@Override
224+
public Iterable<HelperReferenceWrapper> getSuperTypes() {
225+
Iterable<HelperReferenceWrapper> superClass = emptyList();
226+
if (hasActualSuperType()) {
227+
superClass =
228+
singleton(
229+
(HelperReferenceWrapper) new ClasspathType(type.getSuperClass().asErasure()));
230+
}
231+
232+
Iterable<HelperReferenceWrapper> interfaces =
233+
Iterables.transform(type.getInterfaces().asErasures(), toWrapper());
234+
235+
return Iterables.concat(superClass, interfaces);
236+
}
237+
238+
private static Function<TypeDescription, HelperReferenceWrapper> toWrapper() {
239+
return new Function<TypeDescription, HelperReferenceWrapper>() {
240+
@Override
241+
public HelperReferenceWrapper apply(TypeDescription input) {
242+
return new ClasspathType(input);
243+
}
244+
};
245+
}
246+
247+
// Uses guava iterables to avoid unnecessary collection copying
248+
@Override
249+
public Iterable<Method> getMethods() {
250+
return FluentIterable.from(type.getDeclaredMethods())
251+
.filter(isOverrideable())
252+
.transform(toMethod());
253+
}
254+
255+
private static Predicate<InDefinedShape> isOverrideable() {
256+
return new Predicate<InDefinedShape>() {
257+
@Override
258+
public boolean apply(InDefinedShape input) {
259+
return !(input.isStatic() || input.isPrivate() || input.isConstructor());
260+
}
261+
};
262+
}
263+
264+
private Function<InDefinedShape, Method> toMethod() {
265+
return new Function<InDefinedShape, Method>() {
266+
@Override
267+
public Method apply(InDefinedShape method) {
268+
return new Method(
269+
method.isAbstract(),
270+
type.getName(),
271+
method.getInternalName(),
272+
method.getDescriptor());
273+
}
274+
};
275+
}
276+
}
277+
}
278+
}

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)