- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我知道 Java 编译器将泛型类型中的所有类型参数替换为其边界或 Object
如果类型参数在类型删除过程中是无界的。生成的机器字节码将反射(reflect)替换的边界或 Object
.
有没有办法获取生成的机器字节码并将其反编译回包含泛型类型中原始类型参数的 Java 文件?是否存在可以实现此目的的反编译器?或者由于编译过程的性质,这个过程是不可逆的?
最佳答案
您是正确的,在字节码级别,当您定义泛型类型并与之交互时,会丢失很多信息。类型删除对于保持兼容性很好:如果您主要在编译时强制执行类型安全,则在运行时不需要做太多事情,因此您可以将泛型类型减少到它们的“原始”等价物。
这就是关键:编译时验证。如果您想要泛型的灵活性和类型安全性,您的编译器必须非常了解您与之交互的泛型类型。在许多情况下,您没有这些类的源代码,因此它必须从某个地方获取信息。它确实:元数据。内嵌.class
字节码旁边的文件是丰富的信息:编译器需要知道您正在安全地使用泛型库类型的所有信息。那么什么样的泛型信息会被保留下来呢?
类型变量和约束
为了使用泛型类型,编译器需要知道的最基本的事情是类型变量列表。对于任何泛型类型或泛型方法,都会保留类型变量的名称和位置。此外,还包括任何约束(上限或下限)。
通用父类(super class)型签名
有时您会编写一个扩展泛型类或实现泛型接口(interface)的类。如果你写一个 StringList
扩展 ArrayList<String>
,你继承了很多功能。如果有人想使用您的 StringList
如果没有源代码,编译器仅仅知道您扩展了 ArrayList
是不够的。 ;它必须知道你扩展 ArrayList<String>
.这在层次结构中向上传递:它必须知道 ArrayList<>
扩展 AbstractList<>
, 等等。所以这些信息被保留了下来。您的类文件 a 将包含任何泛型父类(super class)型(类或接口(interface))的完整泛型签名。
成员(member)签名
如果编译器不知道字段、方法参数和返回类型的完整泛型类型,则它无法验证您是否正确使用了泛型类型。所以,你猜对了:这些信息被包括在内。如果类成员的任何部分包含泛型类型、通配符或类型变量,则该成员将获取其保存在元数据中的签名信息。
局部变量
没有必要为了使用类型而保留有关局部变量类型的信息。它对调试很有用,但仅此而已。有一些元数据表可用于记录变量的名称和类型,以及它们存在的字节码范围。根据编译器的不同,它们可能会或可能不会默认编写。您可以强制javac
通过传递 -g:vars
发出它们,但我相信默认情况下它们会被省略
调用站点
反编译器的最大问题之一,主要影响方法主体内的泛型推理,是调用泛型方法的调用站点不保留有关类型参数的信息。这给像 Java 8 Streams 这样的 API 带来了巨大的麻烦,在这些 API 中,泛型运算符被链接在一起,每个运算符都接受匿名类型的 lambdas(它们的参数类型可能是逆变的,而返回类型可能是协变的)。这是类型推断的噩梦,但对于碰巧与泛型交互的任何代码来说,这都是一个问题。这种代码不会因为它存在于泛型类型中而变得更难反编译。
这如何影响反编译
像 Procyon 和 CFR 这样的现代 Java 反编译器应该能够相当好地重构泛型类型。如果局部变量元数据可用,结果应该与原始代码非常接近。如果没有,他们将不得不尝试根据数据流分析推断方法主体中的泛型类型参数。本质上,反编译器必须查看哪些数据流入和流出泛型实例,并使用它对数据类型的了解来猜测类型参数。有时效果很好;其他时候,没有那么多(参见之前关于 Java 8 Streams 的评论)。
但是,在 API 级别——类型和成员签名——结果应该是准确的。
警告
严格来说,这里描述的所有元数据都是可选的:它只在编译时(或反编译时)需要。如果有人通过混淆器、优化器或其他一些实用程序运行他们编译的类,所有这些信息都可能被剥离。它不会在运行时产生影响。
tldr;结论
是的,当然可以在类型参数完整的情况下反编译泛型类型和方法。假设存在所需的元数据,正确获取类型和成员签名是“容易”的部分。正确推断泛型实例和方法调用的类型参数是一个棘手的问题,但这对于碰巧与泛型交互的任何代码来说都是一个问题。
如前所述,Procyon 和 CFR 都应该在恢复泛型类型和方法方面做得相当不错。
关于java - 是否可以将 Java 字节码反编译回原始泛型类型参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45990311/
当需要将原始类型转换为字符串时,例如传递给需要字符串的方法时,基本上有两种选择。 以int为例,给出: int i; 我们可以执行以下操作之一: someStringMethod(Integer.to
我有一个位置估计数据库,并且想要计算每月的内核利用率分布。我可以使用 R 中的 adehabitat 包来完成此操作,但我想使用引导数据库中的样本来估计这些值的 95% 置信区间。今天我一直在尝试引导
我希望使用 FTP 编写大型机作业流。为此,我可以通过 FTP 连接到大型机并运行以下命令: QUOTE TYPE E QUOTE SITE FILETYPE=JES PUT myjob.jcl 那么
我是 WPF 的新手。 目前,我正在为名为“LabeledTextbox”的表单元素制作一个用户控件,其中包含一个标签、一个文本框和一个用于错误消息的文本 block 。 当使用代码添加错误消息时,我
我们正在使用 SignalR(原始版本,而不是 Core 版本)并注意到一些无法解释的行为。我们的情况如下: 我们有一个通过 GenericCommand() 方法接受命令的集线器(见下文)。 这些命
使用 requests module 时,有没有办法打印原始 HTTP 请求? 我不只想要标题,我想要请求行、标题和内容打印输出。是否可以看到最终由 HTTP 请求构造的内容? 最佳答案 Since
与直接访问现有本地磁盘或分区的物理磁盘相比,虚拟磁盘为文件存储提供更好的可移植性和效率。VMware有三种不同的磁盘类型:原始磁盘、厚磁盘和精简磁盘,它们各自分配不同的存储空间。 VMware
我有一个用一些颜色着色器等创建的门。 前段时间我拖着门,它问我该怎么办时,我选择了变体。但现在我决定选择创建原始预制件和门颜色,或者着色器变成粉红色。 这是资源中原始预制件和变体的屏幕截图。 粉红色的
我想呈现原始翻译,所以我决定在 Twig 模板中使用“原始”选项。但它不起作用。例子: {{ form_label(form.sfGuardUserProfile.roules_acceptance)
是否可以在sqlite中制作类似的东西? FOREIGN KEY(TypeCode, 'ARawValue', IdServeur) REFERENCES OTHERTABLE(TypeCode, T
这个问题是一个更具体问题的一般版本 asked here .但是,这些答案无法使用。 问题: geoIP数据的原始来源是什么? 许多网站会告诉我我的 IP 在哪里,但它们似乎都在使用来自不到 5 家公
对于Openshift:如何基于Wildfly创建docker镜像? 这是使用的Dockerfile: FROM openshift/wildfly-101-centos7 # Install exa
结果是 127 double middle = 255 / 2 虽然这产生了 127.5 Double middle = 255 / 2 同时这也会产生 127.5 double middle = (
在此处下载带有已编译可执行文件的源代码(大小:161 KB(165,230 字节)):http://www.eyeClaxton.com/download/delphi/ColorSwap.zip 原
以下几行是我需要在 lua 中使用的任意正则表达式。 ['\";=] !^(?:(?:[a-z]{3,10}\s+(?:\w{3,7}?://[\w\-\./]*(?::\d+)?)?/[^?#]*(
这个问题是一个更具体问题的一般版本 asked here .但是,这些答案无法使用。 问题: geoIP数据的原始来源是什么? 许多网站会告诉我我的 IP 在哪里,但它们似乎都在使用来自不到 5 家公
我正在使用GoLang做服务器api,试图管理和回答所发出的请求。使用net/http和github.com/gorilla/mux。 收到请求时,我使用以下结构创建响应: type Response
tl; dr:我认为我的 static_vector 有未定义的行为,但我找不到它。 这个问题是在 Microsoft Visual C++ 17 上。我有这个简单且未完成的 static_vecto
我试图找到原始 Awk (a/k/a One True Awk) 源代码的“历史”版本。我找到了 Kernighan's occasionally-updated site ,它似乎总是链接到最新版本
我在 python 中使用原始 IPv6 套接字时遇到一些问题。我通过以下方式连接: if self._socket != None: # Close out old sock
我是一名优秀的程序员,十分优秀!