-
Notifications
You must be signed in to change notification settings - Fork 873
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement invokedynamic advice bootstrapping (#9382)
- Loading branch information
Showing
18 changed files
with
997 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
.../main/java/io/opentelemetry/javaagent/customchecks/OtelCanIgnoreReturnValueSuggester.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.customchecks; | ||
|
||
import static com.google.errorprone.matchers.Description.NO_MATCH; | ||
|
||
import com.google.auto.service.AutoService; | ||
import com.google.errorprone.BugPattern; | ||
import com.google.errorprone.VisitorState; | ||
import com.google.errorprone.bugpatterns.BugChecker; | ||
import com.google.errorprone.bugpatterns.checkreturnvalue.CanIgnoreReturnValueSuggester; | ||
import com.google.errorprone.matchers.Description; | ||
import com.sun.source.tree.ClassTree; | ||
import com.sun.source.tree.MethodTree; | ||
import com.sun.source.util.TreePath; | ||
|
||
@AutoService(BugChecker.class) | ||
@BugPattern( | ||
summary = | ||
"Methods with ignorable return values (including methods that always 'return this') should be annotated with @com.google.errorprone.annotations.CanIgnoreReturnValue", | ||
severity = BugPattern.SeverityLevel.WARNING) | ||
public class OtelCanIgnoreReturnValueSuggester extends BugChecker | ||
implements BugChecker.MethodTreeMatcher { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
private final CanIgnoreReturnValueSuggester delegate = new CanIgnoreReturnValueSuggester(); | ||
|
||
@Override | ||
public Description matchMethod(MethodTree methodTree, VisitorState visitorState) { | ||
ClassTree containerClass = findContainingClass(visitorState.getPath()); | ||
if (containerClass.getSimpleName().toString().endsWith("Advice")) { | ||
return NO_MATCH; | ||
} | ||
Description description = delegate.matchMethod(methodTree, visitorState); | ||
if (description == NO_MATCH) { | ||
return description; | ||
} | ||
return describeMatch(methodTree); | ||
} | ||
|
||
private static ClassTree findContainingClass(TreePath path) { | ||
TreePath parent = path.getParentPath(); | ||
while (parent != null && !(parent.getLeaf() instanceof ClassTree)) { | ||
parent = parent.getParentPath(); | ||
} | ||
if (parent == null) { | ||
throw new IllegalStateException( | ||
"Method is expected to be contained in a class, something must be wrong"); | ||
} | ||
ClassTree containerClass = (ClassTree) parent.getLeaf(); | ||
return containerClass; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
...bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/IndyBootstrapDispatcher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.bootstrap; | ||
|
||
import java.lang.invoke.CallSite; | ||
import java.lang.invoke.ConstantCallSite; | ||
import java.lang.invoke.MethodHandle; | ||
import java.lang.invoke.MethodHandles; | ||
import java.lang.invoke.MethodType; | ||
import java.lang.reflect.Array; | ||
|
||
/** | ||
* Contains the bootstrap method for initializing invokedynamic callsites which are added via agent | ||
* instrumentation. | ||
*/ | ||
public class IndyBootstrapDispatcher { | ||
|
||
private static volatile MethodHandle bootstrap; | ||
|
||
private IndyBootstrapDispatcher() {} | ||
|
||
/** | ||
* Initialized the invokedynamic bootstrapping method to which this class will delegate. | ||
* | ||
* @param bootstrapMethod the method to delegate to. Must have the same type as {@link | ||
* #bootstrap}. | ||
*/ | ||
public static void init(MethodHandle bootstrapMethod) { | ||
bootstrap = bootstrapMethod; | ||
} | ||
|
||
public static CallSite bootstrap( | ||
MethodHandles.Lookup lookup, | ||
String adviceMethodName, | ||
MethodType adviceMethodType, | ||
Object... args) { | ||
CallSite callSite = null; | ||
if (bootstrap != null) { | ||
try { | ||
callSite = (CallSite) bootstrap.invoke(lookup, adviceMethodName, adviceMethodType, args); | ||
} catch (Throwable e) { | ||
ExceptionLogger.logSuppressedError("Error bootstrapping indy instruction", e); | ||
} | ||
} | ||
if (callSite == null) { | ||
// The MethodHandle pointing to the Advice could not be created for some reason, | ||
// fallback to a Noop MethodHandle to not crash the application | ||
MethodHandle noop = generateNoopMethodHandle(adviceMethodType); | ||
callSite = new ConstantCallSite(noop); | ||
} | ||
return callSite; | ||
} | ||
|
||
// package visibility for testing | ||
static MethodHandle generateNoopMethodHandle(MethodType methodType) { | ||
Class<?> returnType = methodType.returnType(); | ||
MethodHandle noopNoArg; | ||
if (returnType == void.class) { | ||
noopNoArg = | ||
MethodHandles.constant(Void.class, null).asType(MethodType.methodType(void.class)); | ||
} else { | ||
noopNoArg = MethodHandles.constant(returnType, getDefaultValue(returnType)); | ||
} | ||
return MethodHandles.dropArguments(noopNoArg, 0, methodType.parameterList()); | ||
} | ||
|
||
private static Object getDefaultValue(Class<?> classOrPrimitive) { | ||
if (classOrPrimitive.isPrimitive()) { | ||
// arrays of primitives are initialized with the correct primitive default value (e.g. 0 for | ||
// int.class) | ||
// we use this fact to generate the correct default value reflectively | ||
return Array.get(Array.newInstance(classOrPrimitive, 1), 0); | ||
} else { | ||
return null; // null is the default value for reference types | ||
} | ||
} | ||
} |
57 changes: 57 additions & 0 deletions
57
...strap/src/test/java/io/opentelemetry/javaagent/bootstrap/IndyBootstrapDispatcherTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.bootstrap; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import java.lang.invoke.MethodHandle; | ||
import java.lang.invoke.MethodType; | ||
import org.junit.jupiter.api.Test; | ||
|
||
public class IndyBootstrapDispatcherTest { | ||
|
||
@Test | ||
void testVoidNoopMethodHandle() throws Throwable { | ||
MethodHandle noArg = generateAndCheck(MethodType.methodType(void.class)); | ||
noArg.invokeExact(); | ||
|
||
MethodHandle intArg = generateAndCheck(MethodType.methodType(void.class, int.class)); | ||
intArg.invokeExact(42); | ||
} | ||
|
||
@Test | ||
void testIntNoopMethodHandle() throws Throwable { | ||
MethodHandle noArg = generateAndCheck(MethodType.methodType(int.class)); | ||
assertThat((int) noArg.invokeExact()).isEqualTo(0); | ||
|
||
MethodHandle intArg = generateAndCheck(MethodType.methodType(int.class, int.class)); | ||
assertThat((int) intArg.invokeExact(42)).isEqualTo(0); | ||
} | ||
|
||
@Test | ||
void testBooleanNoopMethodHandle() throws Throwable { | ||
MethodHandle noArg = generateAndCheck(MethodType.methodType(boolean.class)); | ||
assertThat((boolean) noArg.invokeExact()).isEqualTo(false); | ||
|
||
MethodHandle intArg = generateAndCheck(MethodType.methodType(boolean.class, int.class)); | ||
assertThat((boolean) intArg.invokeExact(42)).isEqualTo(false); | ||
} | ||
|
||
@Test | ||
void testReferenceNoopMethodHandle() throws Throwable { | ||
MethodHandle noArg = generateAndCheck(MethodType.methodType(Runnable.class)); | ||
assertThat((Runnable) noArg.invokeExact()).isEqualTo(null); | ||
|
||
MethodHandle intArg = generateAndCheck(MethodType.methodType(Runnable.class, int.class)); | ||
assertThat((Runnable) intArg.invokeExact(42)).isEqualTo(null); | ||
} | ||
|
||
private static MethodHandle generateAndCheck(MethodType type) { | ||
MethodHandle mh = IndyBootstrapDispatcher.generateNoopMethodHandle(type); | ||
assertThat(mh.type()).isEqualTo(type); | ||
return mh; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.