一种检测在一个程序组件中字段和类的可变性的系统和方法技术方案

技术编号:2854685 阅读:250 留言:0更新日期:2012-04-11 18:40
公开了一种检测在一个任意程序组件中字段和类的可变性的系统和方法,所述程序组件用面向对象的编程语言书写。如果其内存储入一个新值的话,及如果它的任何可到达的变量是可变的话则一个变量被认为是可变的。该系统和方法使用一个静态分析算法,它可以应用于任何软件组件而不是整个程序。该分析将字段和类或者分类为可变的、或者为不可变的。为便利开放世界分析,该算法识别这样的情形,即通过在该组件以外的代码暴露变量于可能的修改,以及这样的情形,即由被分析的代码修改变量。介绍了本分析的一个实现,它集中在检测类变量的可变性,以便避免分离问题。该实现结合过程内和过程间的数据流分析,并被证明是可高度缩放的。实验结果演示出该算法的有效性。(*该技术在2021年保护过期,可自由使用*)

【技术实现步骤摘要】

本专利技术涉及用于计算机程序的面向对象的编程语言领域,尤其是,涉及检测在任意程序组件中的字段和类(class)的可变性(mutability)。
技术介绍
在1995年末被引入时,编程语言Java给因特网带来一场风暴。这一点的主要原因为Java是一种解释性编程语言,这基本上意味着它使用和诸如C或C++编程语言不同的编译/执行范例。以诸如C或C++这样的高级编程语言书写的程序可以由人来读、写、和理解,这些程序需要被翻译成可由实际运行该程序的计算机理解的机器码。这就是编译程序所做的事情。另外,编译程序在翻译代码时同时优化它。编译的最终产品是机器码,从定义上可知是与机器相关的,意味着该代码是特定针对运行它的那种类型计算机的,而不能由不同类型的计算机所理解。这一点的一个简单的例子是为苹果机编译的程序不能在国际商业机器(IBM)兼容PC机上运行。这一点称为“与平台相关”。另一方面,诸如Java这样的解释性编程语言不为特定类型的计算机编译(Java是Sun Microsystems公司的商标)。它们是平台独立的。这一点是通过在编译的程序和特定平台之间放一个中间的Java虚拟机(JVM)实现的。换句话说,当编译一个Java程序时,最终结果不是机器码,而是可由JVM理解的字节码。JVM是与机器相关的,它作为安装到特定机器中JVM的字节码的解释程序。只要该机器安装有JVM就允许Java程序编译和移植到任何机器中。正是这种平台独立性使Java特别适合于因特网。一个计算机一旦安装有JVM,则不管该计算机是苹果机、Wintel PC、Sun、Digital等,通过因特网下载的Java编译字节码程序就能在其上运行。虽然Java通常作为解释性编程语言运行,但是应该注意,它可以静态地或在运行时被优化和编译(亦即适时编译程序)。Java是一种面向对象的编程(OOP)语言。这意味着其焦点是对象,而不是过程(如在C或BASIC中那样)。粗略说,对象包括数据和操作该数据的方法。Java中的编程可以理解为书写不同对象的说明。更特别地说,在OOP中,“类”定义为一种实现特定类型对象的数据和方法集合。类定义“实例(instance)变量”和“类变量”,同时说明类实现的接口和该类的直接“上类(superclass)”。广义讲,类可以理解为总的定义,而对象是一个类的一个“实例”。例如,命名为圆的类可以用半径和原点的位置的变量来定义。一个特定的圆c可以通过用半径和原点位置的特定值调用圆类而具体实现。因为半径和原点位置对圆类的该实例是特定的,因此它们是“实例变量”。与此对照的是,“类变量”是与类整体相关的数据项。例如,值pi=3.14可以是圆类中的类变量。另一个例子是在圆类中定义的变量num_circles,它在每具体实现一个圆时增加1。这些类变量与整个类相关,而不是与一个实例相关,且用静态修正符说明。Java中的类形成一个类层次结构,在这里,一个类可以是对另一个类的“上类”或“下类”。例如,形状可以是圆的上类,而提供操作和画圆类的具体实现的对象的能力的图圆(GraphicCiecle)可以是圆的下类。下类继承它的上类的行为。在Java中,“包(package)”是类的扩大的集合,Java有程序员执行一般任务而用的缺省包。例如,Java.io包具有处理输入和输出的类,Java.net包具有支持网络功能的类,和Java.awt包提供建立图形用户接口组件的类。继续说明Java某些独特的特征,应该注意,Java是一种动态语言。这意味着任何Java类可以在任何时间加载到一个运行的Java解释程序中。然后,这些动态加载的类可以动态具体实现。Java还是一种用于联网的嵌入式语言。使用Java.net包,很容易通过网络访问文件或资源,就像它们是位于本地的文件或资源。因为Java对于联网既是动态的,又是嵌入式的,因此对于Java解释程序来说能够从因特网下载和运行代码。这当万维网流览器下载和运行Java小程序(Java小程序是由已经运行的Java应用程序加载和运行的类)时就是这样。当前,因特网Java小程序是Java的到处存在的应用,但是Java具有能力建立动态使用网络分布式资源的任何类型的程序。因为在可以下载活动代码的系统中所涉及的固有安全风险,Java具有几条恶意代码的防线。首先,不像C或C++,Java没有可以用来访问一个字串或数组边界外存储器的指针。与没有指针相关联,Java禁止对存储器的任何直接访问,从而阻止任何来自那个方向的安全性攻击。第二,Java解释程序对它加载的任何不信任的代码的字节码验证,这将防止来自利用Java解释程序中执行弱点的恶意代码。第三,Java使用一个安全“沙箱(sandbox)模型”,在这里,不可信代码被放在一个“沙箱”中,在这里可以安全地播放,而不对完全的Java环境做任何损害。当一个小程序在沙箱中运行时,对它可以做的事情有很多安全限制。这意味着阻止恶意代码干扰在同一Java环境中运行的其它应用程序,或获得对在底层操作系统或网络中的资源的未授权访问。通过对Java代码挂接数字签名而提供第四层安全。这些数字签名可以以密码安全和不可伪造的方式建立源代码。用户指定一个特定的源代码是否可信,并且,如果从一个可信源得到代码,则接受它并运行。Java的另一特征是其存储器分配和释放的方法。在C或C++中,程序员分配存储器和然后以审慎的方式释放存储器。换句话说,C++程序员在一个对象或方法的开始明确分配存储器来保存数组、变量等,然后当它们不再被使用时明确释放该存储器。与此相反,Java程序员既不分配也不释放存储器。取而代之的是Java使用垃圾收集,它操作如下Java解释程序知道它曾经分配过那些对象。此外,它还知道哪个变量引用哪个对象,和哪个对象引用哪一个另外的对象。因为这一点,它可以知道一个被分配的对象何时不再由任何其它对象或变量引用。当找到这种对象时,它可以被“垃圾收集”安全地破坏。最后,Java使用可在展开时配置的组件、应用程序级的软件单元。当前,有四种组件企业Bean,万维网组件,小程序,和应用程序客户。企业Bean实现商业任务或商业实体。万维网组件,诸如服务小程序,响应请求提供服务。如前面提到的小程序通常在万维网浏览器中执行,但是可以在支持小程序编程模型的其它各种应用程序或设备中执行。应用程序客户是第一层客户程序,它在它自己的Java虚拟机中执行。通过容器给组件提供生命周期管理、安全、展开、和运行时间服务。每一类型容器(企业Java Bean(EJB),万维网,Java服务器页(JSP),服务小程序,小程序,和应用程序客户)还提供各组件专门的服务。从上面对Java的说明已经清楚,Java的一个基本属性是模型中的知识的本地化,它称为“封装(encapsulation)”。因为对象封装数据和实现,因此,一个对象的用户可以视该对象为一个提供服务的黑盒子。可以增加、删除、或改变实例变量和方法,但是只要由该对象提供的服务保持不变,则使用该对象的代码可以继续使用它,无需重写。然而,当一个对象或组件依赖于一个共享变量或对象的状态和另一个组件或对象改变该变量或对象的状态时就会出现问题。在这种情况中,换句话说,共享对象不被封装。这有时被称为分离本文档来自技高网...

【技术保护点】
一种检测一个程序组件中变量、对象、字段、和类的可变性的方法,所述组件用面向对象的编程语言书写,包括步骤:判定在该程序组件中的任何变量是否可以经受第一类型状态修改,所述第一类型状态修改由该程序组件中的至少一种方法进行;执行封装 分析以判定在该程序组件中的任何变量是否可以经受第二类型的状态修改,所述第二类型状态修改由不在该程序组件中的至少一种方法进行;其中,如果在一个变量初始化以后它的状态曾经改变的话则所述变量是可变的,所述变量的状态是它的值连同任何引用的对 象的状态;其中,如果在一个对象初始化以后它的状态曾经改变的话则所述对象是可变的,所述对象的所述状态是所有相关变量的状态的集合;其中,如果对应于一个字段的任何变量是可变的则所述字段是可变的;其中,如果由一个类实现的任何 实例字段是可变的则所述类是可变的。

【技术特征摘要】
US 2000-9-21 09/667,4301.一种检测一个程序组件中变量、对象、字段、和类的可变性的方法,所述组件用面向对象的编程语言书写,包括步骤判定在该程序组件中的任何变量是否可以经受第一类型状态修改,所述第一类型状态修改由该程序组件中的至少一种方法进行;执行封装分析以判定在该程序组件中的任何变量是否可以经受第二类型的状态修改,所述第二类型状态修改由不在该程序组件中的至少一种方法进行;其中,如果在一个变量初始化以后它的状态曾经改变的话则所述变量是可变的,所述变量的状态是它的值连同任何引用的对象的状态;其中,如果在一个对象初始化以后它的状态曾经改变的话则所述对象是可变的,所述对象的所述状态是所有相关变量的状态的集合;其中,如果对应于一个字段的任何变量是可变的则所述字段是可变的;其中,如果由一个类实现的任何实例字段是可变的则所述类是可变的。2.权利要求1所述方法,第一类型状态修改决定步骤包括步骤检测在所述每一变量中保持值的可能的第一类型状态修改;检测由所述每一变量引用的任何对象状态的可能的第一类型状态修改。3.权利要求1所述方法,封装分析步骤包括步骤检测在所述每一变量中保持值的可能的第二类型状态修改;检测由所述每一变量引用的任何对象状态的可能的第二类型状态修改,任何对象状态的可能的第二类型状态修改发生在初始化的点处;检测变量封装的可能的破坏;其中,如果对从一个变量可到达的对象的所有引用都在该程序组件中定义的话则该变量是被封装的;其中,如果在该程序组件中的一个方法引起一个从该变量可到达的可变对象变得可由至少一种不在该程序组件中的方法访问的话则变量封装被破坏。4.权利要求1所述方法,其中,所述方法在Java环境中实现,所述任何实例字段是非静态字段,所述变量是类变量或实例变量,每一所述类变量在它相应的<clinit>方法完成时被初始化,每一所述实例变量在它相应的<init>方法完成时被初始化。5.权利要求1所述方法,进一步包括步骤识别由于检测可变全局变量或对象的分离错误。6.权利要求1所述方法,进一步包括步骤识别这样的字段和对象,它们因为所述被识别的字段和对象不在检测到的可变字段和对象的集合中而可以被判定为是常数。7.一种检测在一个程序组件中类的可变性的方法,所述组件用面向对象的编程语言书写,包括步骤获得一组类,每一所述类被分类为可变的、不可变的、和未决定的其中之一;测试每一未决定的类,所述测试包括子步骤测试被测试的所述未决定的类中的每一字段,所述测试包括子步骤判定相应于所述每一字段的任何变量是否可以经受第一类型的状态修改,所述第一类型状态修改由在所述组件中的至少一种方法进行;执行封装分析以判定对应于所述每一字段的任何变量是否可以经受第二类型的状态修改,所述第二类型状态修改由不在所述组件中的至少一种方法进行;如果未发现可能的第一类型或第二类型状态修改的话则分类所述每一字段为不可变的;如果类可变性信息不足的话则分类所述每一字段为未决定的;否则分类所述每一字段为可变的;如果在所述未决定类中的任何字段是可变的则重新将所述未决定的类分类为可变的;如果在所述未决定类中的所有字段是不可变的则重新将所述未决定的类分类为不可变的;重复所述测试每一未决定的类步骤,直到在重复所述测试步骤后未决定的类的数目和重复所述测试步骤前的未决定的类的数目相同;重新将剩余的未决定的类分类为可变类。8.一种检测在一个程序组件中类的可变性的方法,所述组件用面向对象的编程语言书写,包括步骤获得一组类,每一所述类被分类为可变的、不可变的、和未决定的其中之一;测试每一未决定的类,所述测试包括子步骤测试被测试的所述未决定的类中的每一实例字段,所述测试包括子步骤判定相应于所述每一实例字段的任何变量是否可以经受第一类型的状态修改,所述第一类型状态修改由在所述组件中的至少一种方法进行;执行封装分析以判定对应于所述每一实例字段的任何变量是否可以经受第二类型的状态修改,所述第二类型状态修改由不在所述组件中的至少一种方法进行;如果未发现可能的第一类型或第二类型的状态修改的话则将所述每一实例字段分类为不可变的;如果类可变性信息不足的话则将所述每一实例字段分类为未决定的;否则将所述每一实例字段分类为可变的;如果在所述未决定的类中的任何实例字段是可变的则重新所述未决定的类分类为可变的;如果在所述未决定的类中的所有实例字段是不可变的则重新所述未决定的类分类为不可变的;重复所述测试每一未决定的类步骤,直到在重复所述测试步骤后未决定的类的数目和重复所述测试步骤前的未决定的类的数目相同;重新将剩余的未决定的类分类为可变类。9.权利要求8所述方法,第一类型状态修改判定子步骤包括步骤检测在所述每一变量中保持的值的可能的第一类型状态修改;检测由所述每一变量引用的任何对象的状态的可能的第一类型状态修改;其中,如果在一个对象初始化以后它可以改变的话,则该对象的状态被修改;一个对象的状态是所有相关变量的状态的集合;其中,如果一个变量的状态在所述变量初始化以后曾经改变的话,该变量是可变的,所述变量的状态是它的值连同任何引用的对象的一个状态。10.权利要求8所述方法,执行封装分析子步骤包括步骤检测所述每一变量值的可能的第二类型状态修改;检测由所述每一变量引用的任何对象状态的可能的第二类型修改,任何对象状态的所述可能的第二类型状态修改发生在初始化点;检测变量封装的可能的破坏;其中,如果在一个对象初始化以后它可以改变的话则该对象的状态被修改;一个对象的状态是所有相关变量的状态的集合;其中,如果一个变量的状态在所述变量初始化以后曾经改变的话则该变量是可变的,所述变量的状态是它的值连同任何引用的对象的一个状态;其中,如果对从一个变量可到达的对象的所有引用都在所述组件中定义则该变量是被封装的;其中,如果在该程序组件中的一个方法引起一个从变量可到达的可变对象变得可由至少一个不在所述组件中的方法访问的话则变量封装被破坏。11.权利要求8所述方法,其中,该方法在Java环境中实现,对应于所述每一实例字段的所述每一变量是非静态变量,且每一非静态变量在它相应的<init>方法完成时被初始化。12.权利要求8所述方法,另外包括步骤如果一个对象是一个可变类的实例,则它被识别为是可变的;如果一个对象是一个不可变类的实例,则它被识别为是不可变的;识别这样的字段和对象,它们因为所述被识别的字段和对象不在检测到的可变字段和对象的集合中而可以被决定为是常数。13.权利要求8所述方法,另外包括步骤测试在每一类中的每一未决定类字段的可变性。14.权利要求13所述方法,另外包括步骤识别由于检测到的可变类字段的分离错误。15.权利要求13所述方法,测试在每一类中的每一未决定类字段的可变性的步骤包括子步骤判定对应于所述每一未决定类字段的任何变量是否可以经受第一类型的状态修改;执行封装分析以判定对应于所述每一未决定类字段的任何变量是否可以经受第二类型的状态修改。16.权利要求15所述方法,其中,判定对应于所述每一未决定类字段的任何变量是否可以经受第一类型的状态修改子步骤包括步骤检测在所述每一变量中保持值的可能的第一类型状态修改;检测由所述每一变量引用的任何对象的一个状态的可能的第一类型状态修改;其中,如果在一个对象初始化以后它可以改变的话则该对象的状态被修改;一个对象的状态是所有相关变量的状态的集合;其中,如果一个变量的状态在所述变量初始化以后曾经改变的话则它是可变的,所述变量的状态是它的值连同任何引用的对象的一个状态。17.权利要求15所述方法,其中,执行封装分析以决定相应于所述每一未决定类字段的任何变量是否可以经受第二类型状态修改子步骤包括步骤检测所述每一变量值的可能的第二类型状态修改;检测由所述每一变量引用的任何对象的一个状态的可能的第二类型状态修改,任何对象的一个状态的所述可能的第二类型状态修改发生在初始化点;检测变量封装的可能的破坏;其中,如果在一个对象初始化以后它可以改变的话则该对象的状态被修改;一个对象的状态是所有相关变量的状态的集合;其中,如果一个变量的状态在所述变量初始化以后曾经改变的话则它是可变的,所述变量的状态是它的值连同任何引用的对象的一个状态;其中,如果对从一个变量可到达的对象的所有引用都在所述组件中定义则它是被封装的;其中,如果在该程序组件中的一个方法引起从一个变量可到达的一个可变对象变得可由至少一个不在所述组件中的方法访问的话则变量封装被破坏。18.权利要求13所述方法,其中,该方法在Java环境中实现,对应于所述每一未决定类字段的所述变量是静态变量,且每一静态变量在它的相应的<clinit>方法完成时被初始化。19.一种检测在一个程序组件中类和类变量的可变性的方法,所述组件用面向对象的编程语言书写,包括步骤获得一组类,每一所述类被分类为可变的、不可变的、和未决定的其中之一;测试每一未决定的类,所述测试包括子步骤测试被测试所述未决定类中的每一实例字段的可变性;如果未发现可能的状态或封装分析修改的话则将一个实例字段分类为不可变的;如果类可变性信息不足的话则将一个实例字段分类为未决定的;否则分类一个实例字段为可变的;如果在所述未决定的类中的任何实例字段是可变的则重新将一个未决定的类分类为可变的;如果在所述未决定的类中的所有实例字段是不可变的则重新将所述未决定的类分类为不可变的;重复对每一未决定的类步骤的所述测试,直到在重复所述测试步骤后未决定的类的数目和重复所述测试步骤前的未决定的类的数目相同;重新将剩余的未决定的类分类为可变类;测试每一类中的每一类字段的可变性。20.权利要求19所述方法,其中,测试字段的可变性,无论所述字段是实例字段还是类字段,包括子步骤判定对应于所述被测试字段的任何变量是否可以经受第一类型状态修改,所述第一类型状态修改由在该程序组件中的至少一种方法进行;执行封装分析以判定对应于所述被测试字段的任何变量是否可以经受第二类型的状态修改,所述第二类型状态修改由不在该程序组件中的至少一种方法进行;如果未发现可能的状态修改或封装的破坏的话则将所述被测试字段分类为不可变的;如果类可变性信息不足的话则将所述被测试字段分类为未决定的;否则分类所述被测试字段为可变的。21.权利要求20所述方法,其中,第一类型状态修改判定子步骤包括步骤检测在所述每一变量中保持值的可能的第一类型状态修改;检测由所述每一变量引用任何对象的一个状态的可能的第一类型状态修改;其中,如果在一个对象初始化以后它可以改变的话则该对象的状态被修改;一个对象的状态是所有相关变量的状态的集合;其中,如果一个变量的状态在所述变量初始化以后曾经改变的话则它是可变的,所述变量的状态是它的值连同任何引用的对象的一个状态。22.权利要求20所述方法,其中,执行封装分析子步骤包括步骤检测所述每一变量的值可能的第二类型状态修改;检测由所述每一变量引用任何对象的一个状态的可能的第二类型修改,任何对象的一个状态的所述可能的第二类型状态修改发生在初始化点;检测变量封装的可能的破坏;其中,如果在一个对象初始化以后它可以改变的话则该对象的状态被修改;一个对象的状态是所有相关变量的状态的集合;其中,如果一个变量的状态在所述变量初始化以后曾经改变的话则它是可变的,所述变量的状态是它的值连同任何引用的对象的一个状态;其中,如果对从一个变量可到达的对象的所有引用都在所述组件中定义则它是被封装的;其中,如果在该程序组件中的一个方法引起从一个变量可到达的一个可变对象变得可由至少一个不在所述组件中的方法访问的话则变量封装被破坏。23.权利要求19所述方法,其中,所述方法在Java环境中实现,所述实例字段是非静态字段,一个实例变量在它的相应的<init>方法完成时被初始化,所述类字段是静态字段,一个类变量在它的相应的<clinit>方法完成时被初始化。24.权利要求19所述方法,另外包括步骤如果一个对象是一个可变类的实例,则它被识别为是可变的;如果一个对象是一个不可变类的实例,则它被识别为是不可变的;识别这样的字段和对象,它们因为所述被识别的字段和对象不在检测到的可变字段和对象的集合中而可以被决定为是常数。25.权利要求19所述方法,另外包括识别由于检测到的可变类字段的分离错误的步骤。26.一种设备,用于检测一个程序组件中的变量、对象、字段、和类的可变性,所述组件用面向对象的编程语言书写,包括至少一个内核库和至少一个数据流分析引擎层,用于提供该程序组件的特定抽取;至少一个实用程序模块层,用于使用至少一个数据分析引擎的结果产生基本结果;至少一个可变性子分析模块层,用于产生最后结果;其中,如果在一个变量初始化以后它的状态曾经改变的话则该变量是可变的,一个变量的状态是它的值连同任何引用的对象的状态;其中,如果在一个对象初始化以后它的状态曾经改变的话则该对象是可变的,一个对象的状态是所有相关变量的状态的集合;其中,如果相应于一个字段的任何变量是可变的则该字段是可变的;其中,如果由一个类实现的任何实例字段是可变的则该类是可变的。27.权利要求26所述设备,至少一个内核库和至少一个数据分析引擎层包括一个库,用于通过分析一组分类文件收集和操作关于该程序组件的静态信息,且用于有效构造该程序组件的引用、层次结构、和调用图。28.权利要求26所述设备,至少一个内核库和至少一个数据分析引擎层包括一个库,用于允许用户读分类文件。29.权利要求26所述设备,至少一个内核库和至少一个数据分析引擎层包括一个过程内数据分析引擎,用于迭代计算一条指令对与在一个方法框架上的位置关联的信息的影响,所述方法框架是一个操作数堆栈和一个本地变量数组。30.权利要求26所述设备,至少一个内核库和至少一个数据分析引擎层包括一个过程间数据分析引擎,用于计算一个方法对与在该方法完成时仍然存在的变量关联的信息的影响。31.权利要求26所述设备,至少一个实用程序模块层包括一个类型分析实用程序模块,用于识别为在每一方法中的每一指令和每一框架位置的可能的类型的集合。32.权利要求26所述设备,至少一个实用程序模块层包括一个可到达性分析实用程序模块,用于为每一方法识别退出对象和类变量的一个集合,从所述类变量为引用一个可变对象的每一指令和每一帧位置可到达所述可变对象。33.权利要求26所述设备,至少一个可变性子分析模块层包括一个值修改可变性子分析模块,用于为每一方法识别一组字段,它们的相应实例和类变量可以在所述每一方法内设定。34.权利要求26所述设备,至少一个可变性子分析模块层包括一个对象修改可变性子分析模块,用于为每一方法识别一组引用类型字段和方法参数,所述该组引用类型字段和方法参数引用一个对象,所述对象的状态由所述每一方法修改。35.权利要求26所述设备,至少一个可变性子分析模块层包括一个变量可访问性可变性子分析模块,用于为每一变量识别它的值是否可以由至少一个不在该程序组件内的方法直接修改。36.权利要求26所述设备,至少一个可变性子分析模块层包括一个对象可访问性可变性子分析模块,用于检测每一对象的一个状态的可能的可访问性,通过决定与所述对象关联的每一变量是否被封装进行;其中,如果对从一个变量可到达的对象的所有引用在该程序组件中定义的话则它是被封装的;其中,所述可访问性由不在该程序组件之内的至少一种方法进行。37.权利要求26所述设备,至少一个可变性子分析模块层包括一个封装破坏可变性子分析模块,用于检测封装的可能的破坏;其中,如果对从一个变量可到达的可变对象的所有引用在该程序组件中定义的话则它是被封装的;其中,如果在程序组件内的一个方法引起从该变量可到达的一个可变对象变得对不在该程序组件内的至少一个方法可访问的话则变量封装被破坏。38.一种检测一个程序组件中变量、对象、字段、和类的可变性的...

【专利技术属性】
技术研发人员:L科维德B门德尔森S珀拉特M比伯斯泰恩
申请(专利权)人:国际商业机器公司
类型:发明
国别省市:US[美国]

网友询问留言 已有0条评论
  • 还没有人留言评论。发表了对其他浏览者有用的留言会获得科技券。

1