Gson 中的验证错误 类
VerifyError in Gson classes
我使用以下代码重新设置 class 基线:
DynamicType.Unloaded unloaded = new ByteBuddy().with(TypeValidation.DISABLED).rebase(typeDescription,
ClassFileLocator.Simple.of(className, classBytes,
ClassFileLocator.ForClassLoader.of((ClassLoader) classLoader))).method(
ElementMatchers.isPackagePrivate().and(ElementMatchers.not(ElementMatchers.isAbstract()))).intercept(
MethodDelegation.to(PackagePrivateInterceptor.class)).transform(
MethodTransformer.Simple.withModifiers(Visibility.PUBLIC)).make();
return new ClassPair(unloaded.load((ClassLoader) classLoader,
ClassLoadingStrategy.Default.INJECTION.withProtectionDomain(
classLoader.getClass().getProtectionDomain())).getLoaded(), unloaded.getBytes());
加载时 class class com.google.gson.internal.ConstructorConstructor
它通过构造函数,直到最终到达 com.google.gson.internal.LinkedTreeMap
的 class 初始值设定项。
在初始化期间,我得到一个 VerifyError:
1:24:20.227 [main] ERROR [io.hakansson.dynamicjar.core.main.Bootstrap] - java.lang.RuntimeException: java.lang.VerifyError: Illegal type at constant pool entry 195 in class com.google.gson.internal.LinkedTreeMap
Exception Details:
Location:
com/google/gson/internal/LinkedTreeMap.thenComparing$accessor$vT023QbO(Ljava/util/function/Function;)Ljava/util/Comparator; @2: invokespecial
Reason:
Constant pool index 195 is invalid
Bytecode:
0x0000000: 2a2b b700 c3b0
at io.hakansson.dynamicjar.core.api.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:52)
at io.hakansson.dynamicjar.core.main.Bootstrap.main(Bootstrap.java:42)
Caused by: java.lang.VerifyError: Illegal type at constant pool entry 195 in class com.google.gson.internal.LinkedTreeMap
Exception Details:
Location:
com/google/gson/internal/LinkedTreeMap.thenComparing$accessor$vT023QbO(Ljava/util/function/Function;)Ljava/util/Comparator; @2: invokespecial
Reason:
Constant pool index 195 is invalid
Bytecode:
0x0000000: 2a2b b700 c3b0
at com.google.gson.internal.LinkedTreeMap.classInitializer$oNOjADym(LinkedTreeMap.java:40)
at com.google.gson.internal.LinkedTreeMap.(LinkedTreeMap.java)
at com.google.gson.internal.ConstructorConstructor.construct$original$gt92dwVY(ConstructorConstructor.java:207)
at com.google.gson.internal.ConstructorConstructor.construct$original$gt92dwVY$accessor$lof1omy8(ConstructorConstructor.java)
at com.google.gson.internal.ConstructorConstructor$auxiliary$oB71rVyd.call(Unknown Source)
at io.hakansson.dynamicjar.nestedjarclassloader.PackagePrivateInterceptor.intercept(PackagePrivateInterceptor.java:29)
at com.google.gson.internal.ConstructorConstructor.construct(ConstructorConstructor.java)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:167)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.read$original$ZTjOtCtb(ReflectiveTypeAdapterFactory.java:116)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.read$original$ZTjOtCtb$accessor$WgRdwpwl(ReflectiveTypeAdapterFactory.java)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$auxiliaryHBhnNS.call(Unknown Source)
at io.hakansson.dynamicjar.nestedjarclassloader.PackagePrivateInterceptor.intercept(PackagePrivateInterceptor.java:29)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.read(ReflectiveTypeAdapterFactory.java)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:216)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read$original$bOjIYDn5(TypeAdapterRuntimeTypeWrapper.java:40)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read$original$bOjIYDn5$accessor$hMWEZRZS(TypeAdapterRuntimeTypeWrapper.java)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper$auxiliary$U1tyihKy.call(Unknown Source)
at io.hakansson.dynamicjar.nestedjarclassloader.PackagePrivateInterceptor.intercept(PackagePrivateInterceptor.java:29)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.read$original$ZTjOtCtb(ReflectiveTypeAdapterFactory.java:116)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.read$original$ZTjOtCtb$accessor$WgRdwpwl(ReflectiveTypeAdapterFactory.java)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$auxiliaryHBhnNS.call(Unknown Source)
at io.hakansson.dynamicjar.nestedjarclassloader.PackagePrivateInterceptor.intercept(PackagePrivateInterceptor.java:29)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.read(ReflectiveTypeAdapterFactory.java)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:216)
at com.google.gson.Gson.fromJson(Gson.java:879)
at com.google.gson.Gson.fromJson(Gson.java:817)
作为参考,拦截器基本上只是手动检查包私有访问(因为包私有方法是public)然后调用superCall.call()
。我认为问题不在于此。
有什么想法吗?
编辑:
这是 PackagePrivateInterceptor
:
public class PackagePrivateInterceptor {
@RuntimeType
@BindingPriority(1)
public static Object intercept(@SuperCall Callable<?> superCall, @Origin Class targetClass, @Origin String method) throws
Exception
{
Class callingClass = new InternalSecurityManager().getCallingClass();
String targetPackage = targetClass.getPackage().getName();
if (!callingClass.getPackage().getName().equals(targetPackage)) {
throw new IllegalAccessError(callingClass + " cannot access method " + method + " of Class " + targetClass);
}
//Default:
return superCall.call();
}
private static class InternalSecurityManager extends SecurityManager {
Class getCallingClass() {
Class[] classContext = getClassContext();
for (Class current : classContext) {
if (current.getName().startsWith("java.") ||
current.getName().equals(PackagePrivateInterceptor.class.getName()) ||
current.getName().equals(InternalSecurityManager.class.getName()))
{
continue;
}
return current;
}
throw new IllegalStateException("Failed to find calling Class");
}
}
编辑2:
以下代码不会触发问题:
//Only package-private methods should be proxied.
DynamicType.Unloaded unloaded = new ByteBuddy().with(TypeValidation.DISABLED).rebase(typeDescription,
ClassFileLocator.Simple.of(className, classBytes,
ClassFileLocator.ForClassLoader.of((ClassLoader) classLoader))).method(
ElementMatchers.isPackagePrivate().and(ElementMatchers.not(ElementMatchers.isAbstract()))).intercept(
MethodDelegation.to(PackagePrivateInterceptor.class)).transform(
MethodTransformer.Simple.withModifiers(Visibility.PUBLIC)).make();
DynamicType.Loaded loaded = unloaded.load((ClassLoader) classLoader,
ClassLoadingStrategy.Default.INJECTION.withProtectionDomain(
classLoader.getClass().getProtectionDomain()));
if (className.equals("com.google.gson.internal.LinkedTreeMap"))
System.out.println(DatatypeConverter.printHexBinary(loaded.getBytes()));
return new ClassPair(loaded.getLoaded(), unloaded.getBytes());
但以下内容确实如此:
//All non-private methods should be proxied
//TODO: Actually, the class itself should be made visible and still only package-private methods should be proxied.
DynamicType.Unloaded unloaded = new ByteBuddy().with(TypeValidation.DISABLED).rebase(typeDescription,
ClassFileLocator.Simple.of(className, classBytes,
ClassFileLocator.ForClassLoader.of((ClassLoader) classLoader))).method(
ElementMatchers.not(ElementMatchers.isPrivate()).and(
ElementMatchers.not(ElementMatchers.isAbstract()))).intercept(
MethodDelegation.to(PackagePrivateInterceptor.class)).transform(
MethodTransformer.Simple.withModifiers(Visibility.PUBLIC)).make();
return new ClassPair(unloaded.load((ClassLoader) classLoader,
ClassLoadingStrategy.Default.INJECTION.withProtectionDomain(
classLoader.getClass().getProtectionDomain())).getLoaded(), unloaded.getBytes());
这确实是字节好友的一个bug。问题是您要在 Java 8 VM 上重新设置 Java 6 class,其中 Comparable
接口正在实现多个默认方法。您指示 Byte Buddy 覆盖这些方法,您还指示库从覆盖的实现中调用这些默认方法。这对于 Java 6 class 个文件是不合法的,即使 VM 是 运行 Java 8.
如果我将你的匹配器设置为:
not(isPrivate().or(isAbstract()).or(isDefaultMethod()))
错误消失了。我会在字节好友的未来版本中解决这个问题。
目前 Byte Buddy 或 ASM 均未捕获此错误,后者会产生奇怪的错误消息。
更新:该错误现已在1.4.13版本中解决。
我使用以下代码重新设置 class 基线:
DynamicType.Unloaded unloaded = new ByteBuddy().with(TypeValidation.DISABLED).rebase(typeDescription,
ClassFileLocator.Simple.of(className, classBytes,
ClassFileLocator.ForClassLoader.of((ClassLoader) classLoader))).method(
ElementMatchers.isPackagePrivate().and(ElementMatchers.not(ElementMatchers.isAbstract()))).intercept(
MethodDelegation.to(PackagePrivateInterceptor.class)).transform(
MethodTransformer.Simple.withModifiers(Visibility.PUBLIC)).make();
return new ClassPair(unloaded.load((ClassLoader) classLoader,
ClassLoadingStrategy.Default.INJECTION.withProtectionDomain(
classLoader.getClass().getProtectionDomain())).getLoaded(), unloaded.getBytes());
加载时 class class com.google.gson.internal.ConstructorConstructor
它通过构造函数,直到最终到达 com.google.gson.internal.LinkedTreeMap
的 class 初始值设定项。
在初始化期间,我得到一个 VerifyError:
1:24:20.227 [main] ERROR [io.hakansson.dynamicjar.core.main.Bootstrap] - java.lang.RuntimeException: java.lang.VerifyError: Illegal type at constant pool entry 195 in class com.google.gson.internal.LinkedTreeMap Exception Details: Location: com/google/gson/internal/LinkedTreeMap.thenComparing$accessor$vT023QbO(Ljava/util/function/Function;)Ljava/util/Comparator; @2: invokespecial Reason: Constant pool index 195 is invalid Bytecode: 0x0000000: 2a2b b700 c3b0 at io.hakansson.dynamicjar.core.api.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:52) at io.hakansson.dynamicjar.core.main.Bootstrap.main(Bootstrap.java:42) Caused by: java.lang.VerifyError: Illegal type at constant pool entry 195 in class com.google.gson.internal.LinkedTreeMap Exception Details: Location: com/google/gson/internal/LinkedTreeMap.thenComparing$accessor$vT023QbO(Ljava/util/function/Function;)Ljava/util/Comparator; @2: invokespecial Reason: Constant pool index 195 is invalid Bytecode: 0x0000000: 2a2b b700 c3b0 at com.google.gson.internal.LinkedTreeMap.classInitializer$oNOjADym(LinkedTreeMap.java:40) at com.google.gson.internal.LinkedTreeMap.(LinkedTreeMap.java) at com.google.gson.internal.ConstructorConstructor.construct$original$gt92dwVY(ConstructorConstructor.java:207) at com.google.gson.internal.ConstructorConstructor.construct$original$gt92dwVY$accessor$lof1omy8(ConstructorConstructor.java) at com.google.gson.internal.ConstructorConstructor$auxiliary$oB71rVyd.call(Unknown Source) at io.hakansson.dynamicjar.nestedjarclassloader.PackagePrivateInterceptor.intercept(PackagePrivateInterceptor.java:29) at com.google.gson.internal.ConstructorConstructor.construct(ConstructorConstructor.java) at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:167) at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.read$original$ZTjOtCtb(ReflectiveTypeAdapterFactory.java:116) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.read$original$ZTjOtCtb$accessor$WgRdwpwl(ReflectiveTypeAdapterFactory.java) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$auxiliaryHBhnNS.call(Unknown Source) at io.hakansson.dynamicjar.nestedjarclassloader.PackagePrivateInterceptor.intercept(PackagePrivateInterceptor.java:29) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.read(ReflectiveTypeAdapterFactory.java) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:216) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read$original$bOjIYDn5(TypeAdapterRuntimeTypeWrapper.java:40) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read$original$bOjIYDn5$accessor$hMWEZRZS(TypeAdapterRuntimeTypeWrapper.java) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper$auxiliary$U1tyihKy.call(Unknown Source) at io.hakansson.dynamicjar.nestedjarclassloader.PackagePrivateInterceptor.intercept(PackagePrivateInterceptor.java:29) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java) at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82) at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.read$original$ZTjOtCtb(ReflectiveTypeAdapterFactory.java:116) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.read$original$ZTjOtCtb$accessor$WgRdwpwl(ReflectiveTypeAdapterFactory.java) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$auxiliaryHBhnNS.call(Unknown Source) at io.hakansson.dynamicjar.nestedjarclassloader.PackagePrivateInterceptor.intercept(PackagePrivateInterceptor.java:29) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.read(ReflectiveTypeAdapterFactory.java) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:216) at com.google.gson.Gson.fromJson(Gson.java:879) at com.google.gson.Gson.fromJson(Gson.java:817)
作为参考,拦截器基本上只是手动检查包私有访问(因为包私有方法是public)然后调用superCall.call()
。我认为问题不在于此。
有什么想法吗?
编辑:
这是 PackagePrivateInterceptor
:
public class PackagePrivateInterceptor {
@RuntimeType
@BindingPriority(1)
public static Object intercept(@SuperCall Callable<?> superCall, @Origin Class targetClass, @Origin String method) throws
Exception
{
Class callingClass = new InternalSecurityManager().getCallingClass();
String targetPackage = targetClass.getPackage().getName();
if (!callingClass.getPackage().getName().equals(targetPackage)) {
throw new IllegalAccessError(callingClass + " cannot access method " + method + " of Class " + targetClass);
}
//Default:
return superCall.call();
}
private static class InternalSecurityManager extends SecurityManager {
Class getCallingClass() {
Class[] classContext = getClassContext();
for (Class current : classContext) {
if (current.getName().startsWith("java.") ||
current.getName().equals(PackagePrivateInterceptor.class.getName()) ||
current.getName().equals(InternalSecurityManager.class.getName()))
{
continue;
}
return current;
}
throw new IllegalStateException("Failed to find calling Class");
}
}
编辑2: 以下代码不会触发问题:
//Only package-private methods should be proxied.
DynamicType.Unloaded unloaded = new ByteBuddy().with(TypeValidation.DISABLED).rebase(typeDescription,
ClassFileLocator.Simple.of(className, classBytes,
ClassFileLocator.ForClassLoader.of((ClassLoader) classLoader))).method(
ElementMatchers.isPackagePrivate().and(ElementMatchers.not(ElementMatchers.isAbstract()))).intercept(
MethodDelegation.to(PackagePrivateInterceptor.class)).transform(
MethodTransformer.Simple.withModifiers(Visibility.PUBLIC)).make();
DynamicType.Loaded loaded = unloaded.load((ClassLoader) classLoader,
ClassLoadingStrategy.Default.INJECTION.withProtectionDomain(
classLoader.getClass().getProtectionDomain()));
if (className.equals("com.google.gson.internal.LinkedTreeMap"))
System.out.println(DatatypeConverter.printHexBinary(loaded.getBytes()));
return new ClassPair(loaded.getLoaded(), unloaded.getBytes());
但以下内容确实如此:
//All non-private methods should be proxied
//TODO: Actually, the class itself should be made visible and still only package-private methods should be proxied.
DynamicType.Unloaded unloaded = new ByteBuddy().with(TypeValidation.DISABLED).rebase(typeDescription,
ClassFileLocator.Simple.of(className, classBytes,
ClassFileLocator.ForClassLoader.of((ClassLoader) classLoader))).method(
ElementMatchers.not(ElementMatchers.isPrivate()).and(
ElementMatchers.not(ElementMatchers.isAbstract()))).intercept(
MethodDelegation.to(PackagePrivateInterceptor.class)).transform(
MethodTransformer.Simple.withModifiers(Visibility.PUBLIC)).make();
return new ClassPair(unloaded.load((ClassLoader) classLoader,
ClassLoadingStrategy.Default.INJECTION.withProtectionDomain(
classLoader.getClass().getProtectionDomain())).getLoaded(), unloaded.getBytes());
这确实是字节好友的一个bug。问题是您要在 Java 8 VM 上重新设置 Java 6 class,其中 Comparable
接口正在实现多个默认方法。您指示 Byte Buddy 覆盖这些方法,您还指示库从覆盖的实现中调用这些默认方法。这对于 Java 6 class 个文件是不合法的,即使 VM 是 运行 Java 8.
如果我将你的匹配器设置为:
not(isPrivate().or(isAbstract()).or(isDefaultMethod()))
错误消失了。我会在字节好友的未来版本中解决这个问题。
目前 Byte Buddy 或 ASM 均未捕获此错误,后者会产生奇怪的错误消息。
更新:该错误现已在1.4.13版本中解决。