001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2025 microBean™.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
006 * the License. You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
012 * specific language governing permissions and limitations under the License.
013 */
014package org.microbean.clientproxy.bytebuddy;
015
016import java.lang.invoke.MethodHandles;
017import java.lang.invoke.MethodHandles.Lookup;
018
019import java.util.Objects;
020
021import net.bytebuddy.dynamic.DynamicType;
022
023import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
024
025import net.bytebuddy.pool.TypePool;
026
027import org.microbean.construct.Domain;
028
029import org.microbean.reference.AbstractClientProxier;
030import org.microbean.reference.ProxySpecification;
031
032/**
033 * An {@link AbstractClientProxier} that uses <a href="https://bytebuddy.net/#/">Byte Buddy</a> to {@linkplain
034 * #generate(ProxySpecification) generate} {@linkplain org.microbean.reference.ClientProxy client proxies}.
035 *
036 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
037 *
038 * @see BBClientProxyClassGenerator
039 */
040public final class BBClientProxier extends AbstractClientProxier<DynamicType.Unloaded<?>> {
041
042  private final TypeDefinitions tds;
043
044  private final BBClientProxyClassGenerator g;
045
046  private static final Lookup lookup = MethodHandles.lookup(); // or instance variable?
047
048  /**
049   * Creates a new {@link BBClientProxier}.
050   *
051   * @param domain a {@link Domain}; must not be {@code null}
052   *
053   * @exception NullPointerException if any argument is {@code null}
054   *
055   * @see TypeElementTypePool#TypeElementTypePool(Domain)
056   *
057   * @see #BBClientProxier(Domain, TypePool)
058   */
059  public BBClientProxier(final Domain domain) {
060    this(domain, new TypeElementTypePool(domain));
061  }
062
063  /**
064   * Creates a new {@link BBClientProxier}.
065   *
066   * @param domain a {@link Domain}; must not be {@code null}
067   *
068   * @param typePool a {@link TypePool}; must not be {@code null}
069   *
070   * @exception NullPointerException if any argument is {@code null}
071   *
072   * @see TypeDefinitions#TypeDefinitions(TypePool)
073   *
074   * @see BBClientProxyClassGenerator#BBClientProxyClassGenerator(TypePool)
075   *
076   * @see #BBClientProxier(Domain, TypeDefinitions, BBClientProxyClassGenerator)
077   */
078  public BBClientProxier(final Domain domain,
079                         final TypePool typePool) {
080    this(domain, new TypeDefinitions(typePool), new BBClientProxyClassGenerator(typePool));
081  }
082
083  /**
084   * Creates a new {@link BBClientProxier}.
085   *
086   * @param domain a {@link Domain}; must not be {@code null}
087   *
088   * @param tds a {@link TypeDefinitions}; must not be {@code null}
089   *
090   * @param g a {@link BBClientProxyClassGenerator}; must not be {@code null}
091   *
092   * @exception NullPointerException if any argument is {@code null}
093   */
094  public BBClientProxier(final Domain domain,
095                         final TypeDefinitions tds,
096                         final BBClientProxyClassGenerator g) {
097    super(domain);
098    this.tds = Objects.requireNonNull(tds, "tds");
099    this.g = Objects.requireNonNull(g, "g");
100  }
101
102  @Override // AbstractClientProxier<DynamicType.Unloaded<?>>
103  protected final DynamicType.Unloaded<?> generate(final ProxySpecification ps) {
104    return
105      this.g.generate(ps.name(),
106                      this.tds.typeDescription(ps.superclass()),
107                      ps.interfaces().stream().map(this.tds::typeDescriptionGeneric).toList());
108  }
109
110  @Override // AbstractClientProxier<DynamicType.Unloaded<?>>
111  protected final Class<?> clientProxyClass(final DynamicType.Unloaded<?> dtu, final ClassLoader cl)
112    throws ClassNotFoundException {
113    // getTypeName() invoked on a TypeDescription will be its binary name (required by Class#forName(String)):
114    // https://javadoc.io/static/net.bytebuddy/byte-buddy/1.17.3/net/bytebuddy/description/type/TypeDefinition.html#getTypeName--
115    final String binaryName = dtu.getTypeDescription().getSuperClass().asErasure().getTypeName();
116    final Class<?> superclass = Class.forName(binaryName, false, cl);
117    return dtu.load(superclass.getClassLoader(), ClassLoadingStrategy.UsingLookup.of(lookup(superclass))).getLoaded();
118  }
119
120  @Override // AbstractClientProxier<DynamicType.Unloaded<?>>
121  protected final Lookup lookup(final Class<?> c) {
122    return lookup.in(c);
123  }
124
125}