content: /*
   * Copyright The OpenTelemetry Authors
   * SPDX-License-Identifier: Apache-2.0
   */
  
  package io.opentelemetry.javaagent.tooling.field;
  
  import static io.opentelemetry.javaagent.tooling.field.GeneratedVirtualFieldNames.getFieldAccessorInterfaceName;
  import static io.opentelemetry.javaagent.tooling.field.GeneratedVirtualFieldNames.getRealGetterName;
  import static io.opentelemetry.javaagent.tooling.field.GeneratedVirtualFieldNames.getRealSetterName;
  
  import io.opentelemetry.javaagent.bootstrap.VirtualFieldAccessorMarker;
  import io.opentelemetry.javaagent.tooling.muzzle.VirtualFieldMappings;
  import java.util.HashMap;
  import java.util.Map;
  import net.bytebuddy.ByteBuddy;
  import net.bytebuddy.description.modifier.SyntheticState;
  import net.bytebuddy.description.modifier.Visibility;
  import net.bytebuddy.description.type.TypeDescription;
  import net.bytebuddy.dynamic.DynamicType;
  
  final class FieldAccessorInterfacesGenerator {
  
    private final ByteBuddy byteBuddy;
  
    FieldAccessorInterfacesGenerator(ByteBuddy byteBuddy) {
      this.byteBuddy = byteBuddy;
    }
  
    FieldAccessorInterfaces generateFieldAccessorInterfaces(
        VirtualFieldMappings virtualFieldMappings) {
      Map<String, DynamicType.Unloaded<?>> fieldAccessorInterfaces =
          new HashMap<>(virtualFieldMappings.size());
      for (Map.Entry<String, String> entry : virtualFieldMappings.entrySet()) {
        DynamicType.Unloaded<?> type = makeFieldAccessorInterface(entry.getKey(), entry.getValue());
        fieldAccessorInterfaces.put(type.getTypeDescription().getName(), type);
      }
      return new FieldAccessorInterfaces(fieldAccessorInterfaces);
    }
  
    /**
     * Generate an interface that provides field accessor methods for given key class name and context
     * class name.
     *
     * @param typeName key class name
     * @param fieldTypeName context class name
     * @return unloaded dynamic type containing generated interface
     */
    private DynamicType.Unloaded<?> makeFieldAccessorInterface(
        String typeName, String fieldTypeName) {
      // 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
      TypeDescription fieldTypeDesc = TypeDescription.ForLoadedType.of(Object.class);
      return byteBuddy
          .makeInterface()
          .merge(SyntheticState.SYNTHETIC)
          .name(getFieldAccessorInterfaceName(typeName, fieldTypeName))
          .implement(VirtualFieldAccessorMarker.class)
          .defineMethod(
              getRealGetterName(typeName, fieldTypeName),
              fieldTypeDesc,
              Visibility.PUBLIC,
              SyntheticState.SYNTHETIC)
          .withoutCode()
          .defineMethod(
              getRealSetterName(typeName, fieldTypeName),
              TypeDescription.ForLoadedType.of(void.class),
              Visibility.PUBLIC,
              SyntheticState.SYNTHETIC)
          .withParameter(fieldTypeDesc, "value")
          .withoutCode()
          .make();
    }
  }
  