
io.opentelemetry.javaagent.tooling.field.RealFieldInjector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of opentelemetry-javaagent-tooling Show documentation
Show all versions of opentelemetry-javaagent-tooling Show documentation
Instrumentation of Java libraries using OpenTelemetry.
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.tooling.field;
import static io.opentelemetry.javaagent.tooling.field.GeneratedVirtualFieldNames.getRealFieldName;
import static io.opentelemetry.javaagent.tooling.field.GeneratedVirtualFieldNames.getRealGetterName;
import static io.opentelemetry.javaagent.tooling.field.GeneratedVirtualFieldNames.getRealSetterName;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.opentelemetry.javaagent.bootstrap.VirtualFieldInstalledMarker;
import io.opentelemetry.javaagent.extension.instrumentation.internal.AsmApi;
import io.opentelemetry.javaagent.tooling.Utils;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldList;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.pool.TypePool;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
final class RealFieldInjector implements AsmVisitorWrapper {
private static final String INSTALLED_FIELDS_MARKER_CLASS_NAME =
Utils.getInternalName(VirtualFieldInstalledMarker.class);
private final FieldAccessorInterfaces fieldAccessorInterfaces;
private final String typeName;
private final String fieldTypeName;
RealFieldInjector(
FieldAccessorInterfaces fieldAccessorInterfaces, String typeName, String fieldTypeName) {
this.fieldAccessorInterfaces = fieldAccessorInterfaces;
this.typeName = typeName;
this.fieldTypeName = fieldTypeName;
}
@Override
public int mergeWriter(int flags) {
return flags | ClassWriter.COMPUTE_MAXS;
}
@Override
@CanIgnoreReturnValue
public int mergeReader(int flags) {
return flags;
}
@Override
public ClassVisitor wrap(
TypeDescription instrumentedType,
ClassVisitor classVisitor,
Implementation.Context implementationContext,
TypePool typePool,
FieldList fields,
MethodList> methods,
int writerFlags,
int readerFlags) {
return new ClassVisitor(AsmApi.VERSION, classVisitor) {
// We are using Object class name instead of fieldTypeName here because this gets
// injected onto the bootstrap class loader where context class may be unavailable
private final TypeDescription fieldType = TypeDescription.ForLoadedType.of(Object.class);
private final String fieldName = getRealFieldName(typeName, fieldTypeName);
private final String getterMethodName = getRealGetterName(typeName, fieldTypeName);
private final String setterMethodName = getRealSetterName(typeName, fieldTypeName);
private final TypeDescription interfaceType =
fieldAccessorInterfaces.find(typeName, fieldTypeName);
private boolean foundField = false;
private boolean foundGetter = false;
private boolean foundSetter = false;
@Override
public void visit(
int version,
int access,
String name,
String signature,
String superName,
String[] interfaces) {
if (interfaces == null) {
interfaces = new String[] {};
}
Set set = new LinkedHashSet<>(Arrays.asList(interfaces));
set.add(INSTALLED_FIELDS_MARKER_CLASS_NAME);
set.add(interfaceType.getInternalName());
super.visit(version, access, name, signature, superName, set.toArray(new String[] {}));
}
@Override
public FieldVisitor visitField(
int access, String name, String descriptor, String signature, Object value) {
if (name.equals(fieldName)) {
foundField = true;
}
return super.visitField(access, name, descriptor, signature, value);
}
@Override
public MethodVisitor visitMethod(
int access, String name, String descriptor, String signature, String[] exceptions) {
if (name.equals(getterMethodName)) {
foundGetter = true;
}
if (name.equals(setterMethodName)) {
foundSetter = true;
}
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
@Override
public void visitEnd() {
// Checking only for field existence is not enough as libraries like CGLIB only copy
// public/protected methods and not fields (neither public nor private ones) when
// they enhance a class.
// For this reason we check separately for the field and for the two accessors.
if (!foundField) {
cv.visitField(
// Field should be transient to avoid being serialized with the object.
Opcodes.ACC_PRIVATE
| Opcodes.ACC_VOLATILE
| Opcodes.ACC_TRANSIENT
| Opcodes.ACC_SYNTHETIC,
fieldName,
fieldType.getDescriptor(),
null,
null);
}
if (!foundGetter) {
addGetter();
}
if (!foundSetter) {
addSetter();
}
super.visitEnd();
}
// just 'standard' getter implementation
private void addGetter() {
MethodVisitor mv = getAccessorMethodVisitor(getterMethodName);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitFieldInsn(
Opcodes.GETFIELD,
instrumentedType.getInternalName(),
fieldName,
fieldType.getDescriptor());
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
// just 'standard' setter implementation
private void addSetter() {
MethodVisitor mv = getAccessorMethodVisitor(setterMethodName);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitVarInsn(Opcodes.ALOAD, 1);
mv.visitFieldInsn(
Opcodes.PUTFIELD,
instrumentedType.getInternalName(),
fieldName,
fieldType.getDescriptor());
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
private MethodVisitor getAccessorMethodVisitor(String methodName) {
return cv.visitMethod(
Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC,
methodName,
Utils.getMethodDefinition(interfaceType, methodName).getDescriptor(),
null,
null);
}
};
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy