防止 Byte Buddy 在实现参数化类型时具体化类型参数

问题描述 投票:0回答:1

这个机会不大,但我之前在这里得到了很多很棒的 Byte Buddy 的建议,所以我希望我能再次幸运。 背景是实验编译器的一个棘手的引导场景,其中编译器需要执行在同一步骤中先前编译的一些代码。 有一个接口可以扩展参数化接口并绑定类型参数:

interface Reals extends Provider<Real>
{
  //...
}

还有一个“实现”,它实际上是另一个带有一堆默认方法的接口。由于我提到的棘手的引导场景,与其他接口没有扩展或实现关系。 Byte Buddy 用于在运行时创建一个实际的实现类,将两者链接在一起并确保一个实际“实现”另一个(因此有所有不寻常的注释):

@Implements("lang.Reals")
@bind interface Reals extends Provider<Object>
{
  //...
}

值得注意的是,该实现不使用

Real
类型参数(同样是引导原因),并且引导过程中所需的功能也不需要
Real
类型。 Byte Buddy 的底层配置是:

ByteBuddy byteBuddy = new ByteBuddy(JAVA_V9)
  . with(TypeValidation.DISABLED)
  . with(MethodGraph.Compiler.Default.forJVMHierarchy)

桥接方法是显式生成的。

所以,在这个冗长的序言之后,当代码针对这个特定场景运行时,我得到以下异常:

java.lang.TypeNotPresentException: Type lang.Real not present
at java.base/sun.reflect.generics.factory.CoreReflectionFactory.makeNamedType(CoreReflectionFactory.java:117)
at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:125)
at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at java.base/sun.reflect.generics.visitor.Reifier.reifyTypeArguments(Reifier.java:68)
at java.base/sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:138)
at java.base/sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
at java.base/sun.reflect.generics.repository.ClassRepository.computeSuperInterfaces(ClassRepository.java:117)
at java.base/sun.reflect.generics.repository.ClassRepository.getSuperInterfaces(ClassRepository.java:95)
at java.base/java.lang.Class.getGenericInterfaces(Class.java:1211)
at net.bytebuddy.description.type.TypeList$Generic$OfLoadedInterfaceTypes$TypeProjection.resolve(TypeList.java:823)
at net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection.accept(TypeDescription.java:6297)
at net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$WithResolvedErasure.resolve(TypeDescription.java:6949)
at net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection.accept(TypeDescription.java:6297)
at net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default.doAnalyze(MethodGraph.java:746)
at net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default.analyze(MethodGraph.java:710)
at net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default.doAnalyze(MethodGraph.java:746)
at net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$Default.compile(MethodGraph.java:668)
at net.bytebuddy.dynamic.scaffold.MethodGraph$Compiler$AbstractBase.compile(MethodGraph.java:519)
at net.bytebuddy.dynamic.scaffold.MethodRegistry$Default.prepare(MethodRegistry.java:472)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.toTypeWriter(SubclassDynamicTypeBuilder.java:212)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.toTypeWriter(SubclassDynamicTypeBuilder.java:203)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$UsingTypeWriter.make(DynamicType.java:4055)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3739)
at pro.projo.internal.rcg.RuntimeCodeGenerationHandler.generateImplementation(RuntimeCodeGenerationHandler.java:208)
at pro.projo.internal.rcg.RuntimeCodeGenerationHandler.lambda$getImplementationOf$0(RuntimeCodeGenerationHandler.java:183)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1740)
at pro.projo.internal.rcg.RuntimeCodeGenerationHandler.getImplementationOf(RuntimeCodeGenerationHandler.java:183)
at pro.projo.Projo.getImplementationClass(Projo.java:496)
at pro.projo.Projo.getImplementationClass(Projo.java:468)
at natives.bootstrap.Bootstrap.bindNativeImplementation(Bootstrap.java:139)

异常消息很准确:类型

Real
确实不存在。它的类文件尚未编写,编译器此时正在创建它。

当 Byte Buddy 尝试解析原始

Reals
接口(
TypeList.java:822
)的接口时,问题就来了:

            protected TypeDescription.Generic resolve() {
                java.lang.reflect.Type[] type = this.type.getGenericInterfaces();

<Real>
类型参数的存在会触发相应类的加载。 Byte Buddy 处理这个问题是绝对正确的,我并不是说这里有任何错误。然而,对于当前的场景,我想用仅调用
getInterfaces()
来替换此代码,这可能不会触发加载
Provider<Real>
的类型参数的尝试。
Reals
提供程序的使用方式是完全通用的,即它只能通过桥接方法调用,而不是通过任何将类型参数替换为实际
Real
类型作为返回类型或参数类型的方法(事实上,只会生成桥接方法,其中包含实际的方法实现,而不是检查和重定向到实现方法)。

长话短说,我正在寻找一种在 Byte Buddy 中自定义此行为的方法,以便我可以在运行时创建代码,将接口视为原始类型,而不是过早强制加载

Real
类型。

到目前为止我所尝试过的(都没有成功,只会导致各种其他异常):

  1. TypeDescription.ForLoadedType
     替换 
    TypeDescription.Latent
  2. (这似乎是在类型解析期间使用反射的原因)
  3. 使用
    TypeDescription.Latent
    的自定义子类覆盖一些导致其他异常的方法
  4. 用尝试解决此问题的自定义方法图编译器实现替换
    MethodGraph.Compiler.Default.forJVMHierarchy
  5. 实现了由方法图编译器使用的
    TypeDescription.Generic.Visitor<Generic>
    ,再次尝试用自定义的替代处理替换一些默认代码
  6. 以上的各种组合

是否有已知的策略可以实现我在这里尝试做的事情?还有其他 Byte Buddy 选项可以帮助实现我在这里尝试做的事情吗?

java generics jvm classloader byte-buddy
1个回答
0
投票

您需要创建一个 TypePool,在其中通过类加载器或作为虚拟描述来解析类型。 TypePools 可以在层次结构中设置,因此您需要创建一个返回虚拟值的基本实现。请注意,加载的类型将通过类加载器机制解析其成员,并且无法返回使用类型池,因此您需要避免描述加载的类型。

© www.soinside.com 2019 - 2024. All rights reserved.