程序代码的运行方法和运行装置以及编译方法和编译装置转让专利

申请号 : CN201811194861.2

文献号 : CN111045657B

文献日 :

基本信息:

PDF:

法律信息:

相似专利:

发明人 : 孙渊磊蒋奕陈永健周正兴

申请人 : 华为技术有限公司

摘要 :

本申请提供一种程序代码的编译方法和编译装置以及运行方法和运行装置。该编译方法和编译装置在编译程序代码阶段,为被声明为原生方法的方法生成注册表项,该注册表项中记录用于确定该方法的符号名的信息和空地址。该运行方法和运行装置在运行程序代码阶段,根据被声明为原生方法的方法的符号名和注册表项中记录的用于确定符号名的信息,找到该方法对应的注册表项,并在该对应的注册表项中记录该方法对应的原生方法的地址,从而完成该方法与其对应的原生方法的绑定,以便于后续过程中能够根据该绑定关系调用该方法对应的原生方法。

权利要求 :

1.一种程序代码的运行方法,其特征在于,包括:确定被声明为原生方法的第一方法的符号名和该第一方法对应的原生方法的地址,所述符号名用于标识所述第一方法;

根据所述第一方法的符号名确定与所述第一方法对应的第一注册表项,所述第一注册表项包括第一字段,所述第一字段中记录的信息用于确定所述第一方法的符号名;

在所述第一注册表项包括的第二字段中记录所述第一方法对应的原生方法的地址。

2.根据权利要求1所述的运行方法,其特征在于,所述第一注册表项中的第一字段中记录的是所述第一方法的符号名。

3.根据权利要求1所述的运行方法,其特征在于,所述第一注册表项中的第一字段中记录所述第一方法的符号名相对于所述第一注册表项的地址偏移量,所述第一符号名记录在字符串表中,所述字符串表中记录一个或多个被声明为原生方法的方法的符号名。

4.根据权利要求1至3中任一项所述的运行方法,其特征在于,根据所述第一方法的符号名确定与所述第一方法对应的第一注册表项,包括:计算所述第一方法的符号名的哈希值;

将所述哈希值作为第一索引,确定哈希表中与该第一索引对应的第一哈希表项,所述哈希表中的哈希表项用于记录所述哈希表项对应的注册表项在原生注册表中的索引,所述原生注册表中包括一个或多个注册表项;

将所述原生注册表中与所述第一哈希表项中记录的第二索引对应的注册表项确定为所述第一注册表项。

5.根据权利要求1至3中任一项所述的运行方法,其特征在于,所述运行方法还包括:确定所述第一方法的调用桩,所述调用桩中包括第一标签,第一标签与所述第一方法对应的第一注册表项具有对应关系;

根据所述第一标签和所述对应关系,确定所述第一方法对应的所述第一注册表项;

根据所述第一注册表项包括的第二字段中记录的地址,调用所述第一方法对应的原生方法。

6.一种程序代码的编译方法,其特征在于,包括:确定待编译代码;

为所述待编译代码中被声明为原生方法的第一方法生成所述第一方法对应的第一注册表项,所述第一注册表项包括第一字段和第二字段,所述第一字段中记录的信息用于确定所述第一方法的符号名,所述第二字段用于记录所述第一方法对应的原生方法的地址,所述第一方法的符号名用于标识所述第一方法。

7.根据权利要求6所述的编译方法,其特征在于,所述第一注册表项中的第一字段中记录的是所述第一方法的符号名。

8.根据权利要求6所述的编译方法,其特征在于,所述第一注册表项中的第一字段中记录所述第一方法的符号名相对于所述第一注册表项的地址偏移量,所述第一方法的符号名记录在字符串表中,所述字符串表中记录一个或多个被声明为原生方法的方法的符号名。

9.根据权利要求6至8中任一项所述的编译方法,其特征在于,所述编译方法还包括:计算所述第一方法的符号名的哈希值;

将所述哈希值作为第一索引,确定哈希表中与该第一索引对应的第一哈希表项;

将所述第一注册表项在原生注册表中的第二索引写入所述第一哈希表项中,所述原生注册表中包括一个或多个注册表项。

10.根据权利要求6至8中任一项所述的编译方法,其特征在于,所述编译方法还包括:根据所述第一方法的符号名生成与所述第一注册表项对应的标签,所述标签用于唯一识别所述第一注册表项;

为调用所述第一方法的语句生成调用桩,所述调用桩中包括所述标签。

11.一种程序代码的运行装置,其特征在于,包括:确定模块,用于确定被声明为原生方法的第一方法的符号名和该第一方法对应的原生方法的地址,所述符号名用于标识所述第一方法;

所述确定模块还用于:根据所述第一方法的符号名确定与所述第一方法对应的第一注册表项,所述第一注册表项包括第一字段,所述第一字段中记录的信息用于确定所述第一方法的符号名;

记录模块,用于在所述第一注册表项包括的第二字段中记录所述第一方法对应的原生方法的地址。

12.根据权利要求11所述的运行装置,其特征在于,所述第一注册表项中的第一字段中记录的是所述第一方法的符号名。

13.根据权利要求11所述的运行装置,其特征在于,所述第一注册表项中的第一字段中记录所述第一方法的符号名相对于所述第一注册表项的地址偏移量,所述第一方法的符号名记录在字符串表中,所述字符串表中记录一个或多个被声明为原生方法的方法的符号名。

14.根据权利要求11至13中任一项所述的运行装置,其特征在于,所述确定模块具体用于:

计算所述第一方法的符号名的哈希值;

将所述哈希值作为第一索引,确定哈希表中与该第一索引对应的第一哈希表项,所述哈希表中的哈希表项用于记录所述哈希表项对应的注册表项在原生注册表中的索引,所述原生注册表中包括一个或多个注册表项;

将所述原生注册表中与所述第一哈希表项中记录的第二索引对应的注册表项确定为所述第一注册表项。

15.根据权利要求11至13中任一项所述的运行装置,其特征在于,所述确定模块还用于:

确定所述第一方法的调用桩,所述调用桩中包括第一标签,第一标签与所述第一方法对应的第一注册表项具有对应关系;

根据所述第一标签和所述对应关系,确定所述第一方法对应的所述第一注册表项;

其中,所述运行装置还包括调用模块,用于根据所述第一注册表项包括的第二字段中记录的地址,调用所述第一方法对应的原生方法。

16.一种程序代码的编译装置,其特征在于,包括:确定模块,用于确定待编译代码;

生成模块,用于为所述待编译代码中被声明为原生方法的第一方法生成所述第一方法对应的第一注册表项,所述第一注册表项包括第一字段和第二字段,所述第一字段中记录的信息用于确定所述第一方法的符号名,所述第二字段用于记录所述第一方法对应的原生方法的地址,所述第一方法的符号名用于标识所述第一方法。

17.根据权利要求16所述的编译装置,其特征在于,所述第一注册表项中的第一字段中记录的是所述第一方法的符号名。

18.根据权利要求16所述的编译装置,其特征在于,所述第一注册表项中的第一字段中记录所述第一方法的符号名相对于所述第一注册表项的地址偏移量,所述第一方法的符号名记录在字符串表中,所述字符串表中记录一个或多个被声明为原生方法的方法的符号名。

19.根据权利要求16至18中任一项所述的编译装置,其特征在于,所述生成模块还用于:

计算所述第一方法的符号名的哈希值;

将所述哈希值作为第一索引,确定哈希表中与该第一索引对应的第一哈希表项;

将所述第一注册表项在原生注册表中的第二索引写入所述第一哈希表项中,所述原生注册表中包括一个或多个注册表项。

20.根据权利要求16至18中任一项所述的编译装置,其特征在于,所述生成模块还用于:

根据所述第一方法的符号名生成与所述第一注册表项对应的标签,所述标签用于唯一识别所述第一注册表项;

为调用所述第一方法的语句生成调用桩,所述调用桩中包括所述标签。

21.一种计算机可读存储介质,包括指令,当其在计算机上运行时,使得计算机执行如权利要求1‑10任意一项所述的方法。

说明书 :

程序代码的运行方法和运行装置以及编译方法和编译装置

技术领域

[0001] 本申请涉及程序语言领域,并且更具体地,涉及程序代码的运行方法和运行装置以及编译方法和编译装置。

背景技术

[0002] Java程序中,可以调用通过C或C++等原生语言实现的原生方法(native method),且,这些原生方法符合Java原生接口(Java native interface,JNI)规范。
[0003] 在Java程序调用原生语言实现的原生方法之前,需要先将通过原生语言实现的原生方法与Java程序中声明的原生方法绑定。Java程序中声明的原生方法是指在Java程序中
声明该方法是通过原生方法实现的,在Java程序中不需要实现。
[0004] 如何实现通过原生语言实现的原生方法与Java程序中声明的原生方法之间的绑定,是一个亟待解决的技术问题。

发明内容

[0005] 本申请提供一种程序代码的编译方法和编译装置以及运行方法和运行装置,能够实现通过原生语言实现的原生方法与Java程序中声明的原生方法之间的绑定,以便于Java
程序能够调用原生方法。
[0006] 第一方面、本申请提供了一种程序代码的运行方法。该运行方法包括:确定被声明为原生方法的第一方法的符号名和该第一方法对应的原生方法的地址,所述符号名用于标
识所述第一方法;根据所述第一方法的符号名确定与所述第一方法对应的第一注册表项,
所述第一注册表项包括第一字段,所述第一字段中记录的信息用于确定所述第一方法的符
号名;在所述第一注册表项包括的第二字段中记录所述第一方法对应的原生方法的地址。
[0007] 该运行方法中,将第一方法对应的原生方法的地址记录在已有的注册表项中,并且该地址与第一方法具有对应关系,从而可以使得后续调用第一方法对应的原生方法时,
可以直接根据该注册表项找到第一方法对应的原生方法的地址,即可以调用该原生方法。
[0008] 在一些可能的实现方式中,所述第一注册表项中的第一字段中记录的是所述第一方法的符号名。
[0009] 在一些可能的实现方式中,所述第一注册表项中的第一字段中记录所述第一方法的符号名相对于所述第一注册表项的地址偏移量,所述第一方法的符号名记录在字符串表
中,所述字符串表中记录一个或多个被声明为原生方法的方法的符号名。
[0010] 在一些可能的实现方式中,根据所述第一方法的符号名确定与所述第一方法对应的第一注册表项,包括:计算所述第一方法的符号名的哈希值;将所述哈希值作为第一索
引,确定哈希表中与该第一索引对应的第一哈希表项,所述哈希表中的哈希表项用于记录
所述哈希表项对应的注册表项在原生注册表中的索引,所述原生注册表中包括一个或多个
注册表项;将所述原生注册表中与所述第一哈希表项中记录的第二索引对应的注册表项确
定为所述第一注册表项。
[0011] 在一些可能的实现方式中,所述通信方法还包括:确定所述第一方法的调用桩,所述调用桩中包括第一标签,第一标签与所述第一方法对应的第一注册表项具有对应关系;
根据所述第一标签和所述对应关系,确定所述第一方法对应的所述第一注册表项;根据所
述第一注册表项包括的第二字段中记录的地址,调用所述第一方法对应的原生方法。
[0012] 第二方面,本申请提供了一种程序代码的编译方法。该编译方法包括:确定待编译代码;为所述待编译代码中被声明为原生方法的第一方法生成所述第一方法对应的第一注
册表项,所述第一注册表项包括第一字段和第二字段,所述第一字段中记录的信息用于确
定所述第一方法的符号名,所述第二字段用于记录所述第一方法对应的原生方法的地址,
所述第一方法的符号名用于标识所述第一方法。
[0013] 该编译方法为每个第一方法生成一个注册表项,以便于运行该待编译代码时,可以将第一方法对应的原生方法的地址与第一方法的对应关系记录在该注册表项中。
[0014] 在一些可能的实现方式中,所述第一注册表项中的第一字段中记录的是所述第一方法的符号名。
[0015] 在一些可能的实现方式中,所述第一注册表项中的第一字段中记录所述第一方法的符号名相对于所述第一注册表项的地址偏移量,所述第一方法的符号名记录在字符串表
中,所述字符串表中记录一个或多个被声明为原生方法的方法的符号名。
[0016] 在一些可能的实现方式中,所述编译方法还包括:计算所述第一方法的符号名的哈希值;将所述哈希值作为第一索引,确定哈希表中与该第一索引对应的第一哈希表项;将
所述第一注册表项在原生注册表中的第二索引写入所述第一哈希表项中,所述原生注册表
中包括一个或多个注册表项。
[0017] 在一些可能的实现方式中,所述编译方法还包括:根据所述第一方法的符号名生成与所述第一注册表项对应的标签,所述标签用于唯一识别所述第一注册表项;为调用所
述第一方法的语句生成调用桩,所述调用桩中包括所述标签。
[0018] 第三方面,本申请提供了一种程序代码的运行装置。该运行装置包括:确定模块,用于确定被声明为原生方法的第一方法的符号名和该第一方法对应的原生方法的地址,所
述符号名用于标识所述第一方法;所述确定模块还用于:根据所述第一方法的符号名确定
与所述第一方法对应的第一注册表项,所述第一注册表项包括第一字段,所述第一字段中
记录的信息用于确定所述第一方法的符号名;记录模块,用于在所述第一注册表项包括的
第二字段中记录所述第一方法对应的原生方法的地址。
[0019] 在一些可能的实现方式中,所述第一注册表项中的第一字段中记录的是所述第一方法的符号名。
[0020] 在一些可能的实现方式中,所述第一注册表项中的第一字段中记录所述第一方法的符号名相对于所述第一注册表项的地址偏移量,所述第一方法的符号名记录在字符串表
中,所述字符串表中记录一个或多个被声明为原生方法的方法的符号名。
[0021] 在一些可能的实现方式中所述确定模块具体用于:计算所述第一方法的符号名的哈希值;将所述哈希值作为第一索引,确定哈希表中与该第一索引对应的第一哈希表项,所
述哈希表中的哈希表项用于记录所述哈希表项对应的注册表项在原生注册表中的索引,所
述原生注册表中包括一个或多个注册表项;将所述原生注册表中与所述第一哈希表项中记
录的第二索引对应的注册表项确定为所述第一注册表项。
[0022] 在一些可能的实现方式中,所述确定模块还用于:确定所述第一方法的调用桩,所述调用桩中包括第一标签,第一标签与所述第一方法对应的第一注册表项具有对应关系;
根据所述第一标签和所述对应关系,确定所述第一方法对应的所述第一注册表项;其中,所
述运行装置还包括调用模块,用于根据所述第一注册表项包括的第二字段中记录的地址,
调用所述第一方法对应的原生方法。
[0023] 第三方面中的运行装置包括的各个模块可以通过软件和/或硬件方式实现。
[0024] 例如,在一些可能的实现方式中,该运行装置可以是运行时系统。
[0025] 在一些可能的实现方式中,该运行装置可以包括与存储器耦合的处理器,该处理器用于执行所述存储器中的程序指令。当处理器执行所述存储器中的程序指令时,该处理
器实现第一方面中的确定模块、记录模块和调用模块所执行的操作,从而实现第一方面中
的运行方法。
[0026] 可选地,该运行装置还可以包括所述存储器。
[0027] 在该可能的实现方式中,该运行装置可以是终端设备。这种情况下,该运行装置还可以包括接收器和/或收发器。
[0028] 或者该运行装置可以是芯片,例如可以是能够集成在终端设备中的芯片。这种情况下,该运行装置还可以包括通信接口。
[0029] 第四方面,本申请提供了一种程序代码的编译装置。该编译装置包括:确定模块,用于确定待编译代码;生成模块,用于为所述待编译代码中被声明为原生方法的第一方法
生成所述第一方法对应的第一注册表项,所述第一注册表项包括第一字段和第二字段,所
述第一字段中记录的信息用于确定所述第一方法的符号名,所述第二字段用于记录所述第
一方法对应的原生方法的地址,所述第一方法的符号名用于标识所述第一方法。
[0030] 在一些可能的实现方式中,所述第一注册表项中的第一字段中记录的是所述第一方法的符号名。
[0031] 在一些可能的实现方式中,所述第一注册表项中的第一字段中记录所述第一方法的符号名相对于所述第一注册表项的地址偏移量,所述第一方法的符号名记录在字符串表
中,所述字符串表中记录一个或多个被声明为原生方法的方法的符号名。
[0032] 在一些可能的实现方式中,所述生成模块还用于:计算所述第一方法的符号名的哈希值;将所述哈希值作为第一索引,确定哈希表中与该第一索引对应的第一哈希表项;将
所述第一注册表项在原生注册表中的第二索引写入所述第一哈希表项中,所述原生注册表
中包括一个或多个注册表项。
[0033] 在一些可能的实现方式中,所述生成模块还用于:根据所述第一方法的符号名生成与所述第一注册表项对应的标签,所述标签用于唯一识别所述第一注册表项;为调用所
述第一方法的语句生成调用桩,所述调用桩中包括所述标签。
[0034] 第四方面中的编译装置包括的各个模块可以通过软件和/或硬件方式实现。
[0035] 例如,在一些可能的实现方式中,该编译装置可以是编译器。
[0036] 在一些可能的实现方式中,该编译装置可以包括与存储器耦合的处理器,该处理器用于执行所述存储器中的程序指令。当处理器执行所述存储器中的程序指令时,该处理
器实现第二方面中的确定模块和生成模块所执行的操作,从而实现第二方面中的编译方
法。
[0037] 可选地,该编译装置还可以包括所述存储器。
[0038] 在该可能的实现方式中,该编译装置可以是应用程序的开发设备。或者该编译装置可以是芯片,例如可以是能够集成在开发设备中的芯片。这种情况下,该运行装置还可以
包括通信接口。
[0039] 第五方面,本申请提供了一种计算机可读存储介质。该计算机可读存储介质中存储用于运行装置执行的程序代码。该程序代码包括用于执行第一方面中的运行方法的指
令。
[0040] 第六方面,本申请提供了一种计算机可读存储介质。该计算机可读存储介质中存储用于编译装置执行的程序代码。该程序代码包括用于执行第二方面中的编译方法的指
令。
[0041] 第七方面,本申请提供了一种包含指令的计算机程序产品。当该计算机程序产品在运行装置上运行时,使得该运行装置执行第一方面中的运行方法。
[0042] 第八方面,本申请提供了一种包含指令的计算机程序产品。当该计算机程序产品在编译装置上运行时,使得该编译装置执行第二方面中的编译方法。

附图说明

[0043] 图1是本申请一个实施例的编译方法的示意性流程图;
[0044] 图2是本申请一个实施例的注册方法的示意性流程图;
[0045] 图3是本申请另一个实施例的注册方法的示意性流程图;
[0046] 图4是本申请一个实施例的调用方法的示意性流程图;
[0047] 图5是本申请一个实施例的编译装置的示意性结构图;
[0048] 图6是本申请一个实施例的运行装置的示意性结构图。

具体实施方式

[0049] 下面将结合附图,对本申请中的技术方案进行描述。
[0050] 编译器(compiler),是一种计算机程序,它会将用一种编程语言(可以称为原始语言)写成的源代码,转换成另一种编程语言的代码(目标语言)。
[0051] 具体地说,编译器主要是将便于人编写、阅读、维护的高级计算机语言所写作的源代码程序,翻译为计算机能解读、运行的低阶机器语言的程序,也就是可执行文件。
[0052] 编译器将原始程序(source program)作为输入,翻译产生使用目标语言(target language)的等价程序。
[0053] 原始语言一般为高阶语言(high‑level language),例如Pascal、C、C++、C#、Java等;目标语言可以是汇编语言或目标机器的目标代码(object code),目标机器的目标代码
有时也称作机器代码(machine code)。机器代码可以简称为机器码。
[0054] 运行时系统(run‑time system),又称运行环境(runtime environment),是一种把编译的运行码在目标机器上运行的环境。例如,运行时系统的一种示例为Java运行环境
(java runtime environment,JRE)。
[0055] 运行时系统可以安装在终端设备上。终端设备可以经接入网与一个或多个核心网(core network,CN)进行通信。终端设备可称为接入终端、终端、用户单元、用户站、移动站、
移动台、远方站、远程终端、移动设备、用户终端、无线网络设备、用户代理或用户装置。终端
可以是蜂窝电话、无绳电话、会话启动协议(session initiation protocol,SIP)电话、无
线本地环路(wireless localloop,WLL)站、个人数字处理(personal digital assistant,
PDA)、具有无线通信功能的手持设备、计算设备或连接到无线调制解调器的其它设备、车载
设备、可穿戴设备或物联网、车辆网中的终端设备以及未来网络中的任意形态的终端设备
等。
[0056] 库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库可以分为静态库和动态库,动态库也可以称为共享库。静态库中的所有数据在编译时被整合进目标
代码中;而动态库中的函数在编译时并没有被编译进目标代码中,而是执行到相关函数时
才调用动态库里相应的函数。
[0057] 不同的操作系统中的动态库的后缀名一般不同,动态库文件格式也不同。例如,window操作系统下的动态库的后缀名为DLL,linux操作系统下的动态库的后缀名为so。
[0058] 调用桩(JNI trampoline),也可以称为JNI stub。当使用不兼容的调用约定来连接代码片段时,使用调用桩将调用者的约定转换为被调用者的约定。
[0059] JNI是一种编程框架。JNI使得Java程序可以调用原生方法(native method)和/或原生库(native library),也可以使得Java程序被其他程序调用。
[0060] 原生方法也可以称为本地方法或本地程序或原生函数。例如,针对Java语言编写的方法而言,原生方法可以指用Java语言之外的其它语言编写的。例如,原生方法可以是使
用C、C++或汇编语言等编写的。原生方法可以被编译为基于设备上的硬件和操作系统的代
码或指令。
[0061] 编写原生方法所使用的语言可以称为原生语言或本地语言,例如C、C++或汇编语言等。
[0062] 使用原生语言实现原生方法后,对原生方法进行编译,可以得到原生库。也可以说,原生库中包括原生方法的实现。
[0063] 本申请一个实施例的程序代码的编译方法的示意性流程图如图1所示。应理解,图1示出了该编译方法的步骤或操作,但这些步骤或操作仅是示例,本申请实施例还可以执行
其他操作或者图1中的各个操作的变形。此外,图1中的各个步骤可以按照与图1呈现的不同
的顺序来执行,并且有可能并非要执行图1中的全部操作。
[0064] 本申请实施例的编译方法中可以包括S110和S120。图1所示的编译方法可以由编译器来执行。
[0065] S110,编译器确定待编译代码。例如,该编译器可以是Java代码的编译器,该待编译代码相应为Java代码。
[0066] S120,编译器为该待编译代码中被声明为原生方法的第一方法生成该第一方法对应的注册表项,该第一方法对应的注册表项包括第一字段和第二字段,第一字段中记录的
信息用于确定该第一方法的符号名,第二字段用于记录该第一方法对应的原生方法的地
址。
[0067] 其中,待编译代码中被声明为原生方法的方法称为第一方法;第一方法对应的原生方法可以理解为:该第一方法通过该原生方法来实现。第一方法的符号名用于标识第一
方法。
[0068] 在一些可能的实现方式中,编译器在编译待编译代码时,可以为待编译代码中的每个模块生成一个原生注册表,该原生注册表中可以包括一个或多个注册表项。一个模块
可以是一个类、一个文件,也可以是一个文件包等等。
[0069] 其中,每个原生注册表中包括的注册表项的数量可以由该原生注册表对应的模块中包括的第一方法的数量来决定。例如,该模块的原生注册表中包括的注册表项的数量可
以与该模块中包括的第一方法的数量相等,该原生注册表中的注册表项与该模块中的第一
方法一一对应。
[0070] 应理解,本申请实施例中的注册表项实质上是用于确定第一方法的符号名的信息和第一方法对应的原生方法的地址的对应关系,注册表项也可以用其他名称代替。
[0071] 同理,本申请实施例中的原生注册表实质上多个上述对应关系的集合,该原生注册表也可以用其他名称。
[0072] 第一方法的符号名可以是第一方法所属的模块中能够唯一标识该第一方法的标识。例如,可以根第一方法的方法名和第一方法的签名生成第一方法的符号名。第一方法的
签名可以包括第一方法的参数类型和返回值类型。
[0073] 根据第一方法的方法名和第一方法的签名生成第一方法的符号名时,可以将第一方法所属的类所属的包的名称、第一方法所属的类的类名、第一方法的方法名以及第一方
法的签名依次用预定的符号连接在一起,从而得到第一方法的符号名。
[0074] 例如,第一方法所属的类的类名为“String”,第一方法所属的类所属的包的名称为“java.lang”,第一方法的方法名为“charAt”,第一方法的参数类型为“int”,第一方法的
返回值类型为“char”时,第一方法的符号名可以为“Ljava/lang/String;|charAt|(I)C”。
进一步地,可以用“_2F”表示“/”,用“_3B”表示“;”,用“_7C”表示“|”,用“_28”表示“(”,用“_
29”表示“)”,这样,第一方法的符号名可以为“Ljava_2Flang_2FString_3B_7CcharAt_7C_
28I_29C”。
[0075] 在编译器编待编码译代码时,由于还未能知道第一方法对应的原生方法的地址,因此,编译器为第一方法生成的注册表项中的第二字段可以被初始化为空(null)。
[0076] 此外,编译器编译待编译代码时,还会生成该待编码代码的目标代码。编译器如何将待编译代码变成目标代码的实现方式可以参考现有的方法,此处不再赘述。
[0077] 在一些可能的实现方式中,第一方法的注册表项中的第一字段可以是第一方法的符号名。
[0078] 例如,待编译代码为Java代码,且包括如下内容时,其中,“displayHelloWorld()”被声明为原生方法,即“displayHelloWorld()”为一个第一方法,编译器可以为
“displayHelloWorld( )”生成注册表项,该注册表项中的第一字段记录
“displayHelloWorld()”的符号名,该符号名的生成方法可以参考前面提到的内容,第二
字段记录“null”。
[0079]
[0080] 其中,语句“public  native string  getNativeString();”用于声明“getNativeString()”为原生方法,即告知编译器“getNativeString()”是通过其他语言
(即原生语言)实现的;语句“System.loadLibrary("hello");”用于加载动态库“hello”,动
态库“hello”中包括了“getNativeString()”对应的原生方法的实现,动态库“hello”即为
原生库;语句“new HelloWorld().getNativeString();”用于调用HelloWorld类中的方法
“getNativeString()”。
[0081] 在一些可能的实现方式中,一个模块中的所有第一方法的符号名可以被保存到一个单独的字符串表里。在该字符串表里,所有第一方法的符号名字符串紧挨排布,可以使用
null字符作为每个符号名字符串的结尾。并且,在该模块的原生注册表中的每个注册表项
的第一字段记录该注册表项对应的第一方法的符号名字符串与该注册表项的相对地址,或
者说,第一字段记录该注册表项对应的第一方法的符号名字符串相对于该注册表项的地址
偏移量。该相对地址可以以字节为单位,例如该相对地址可以指该第一方法的符号名占用
的第一个字节相对于该注册表项的首个字节的地址。
[0082] 可选地,每个注册表项的第一字段可以记录该注册表项对应的第一方法的符号名字符串与该字符串表的相对地址,或者说,第一字段记录该注册表项对应的第一方法的符
号名字符串相对于该字符串表的地址偏移量。该相对地址可以以字节为单位,例如该相对
地址可以指该第一方法的符号名占用的第一个字节相对于字符串表的首个字节的地址。
[0083] 其中,第一字段记录注册表项对应的第一方法的符号名字符串相对于注册表项的地址偏移量的方案会更优,因为这种方案不需要知道字符串表的地址。
[0084] 由于相对地址始终可以使用固定长度的字节来记录,即相对地址可以占用固定长度的字节数,例如可以固定占用4个字节,从而可以保证注册表项占用恒定的内存。
[0085] 还以上述Java代码为待编译代码为例。其中,第一方法“getNativeString()”的符号名记录在用于记录该第一方法所属的模块中所有第一方法的符号名的字符串表(可以称
为符号名字符串表)中,并以“null”结尾,第一方法“getNativeString()”的注册表项中的
第一字段中记录该第一方法的符号名相对于该注册表项或该字符串表的地址偏移量,第二
字段中可以记录“null”。
[0086] 在该可能的实现方式中,可以根据注册表项中第一字段中记录的相对地址从符号名字符串表中确定出第一方法的符号名。
[0087] 本申请实施例中,可选地,注册表项的2个字段(即了第一字段和第二字段)可以分开来存储。例如,多个注册表项的第一字段存储在一个数组(为了后续描述方便,称为第一
数组)中,每个注册表项的第一字段对应第一数组中的一个元素;这多个注册表项的第二字
段存储在一个数组(为了后续描述方便,称为第二数组)中,每个注册表项的第二字段对应
第二数组中的一个元素。每个注册表项的第一字段在第一数组中的索引与该注册表项的第
二字段在第二数组中的索引之间具有关联关系。例如,每个注册表项的第一字段在第一数
组中的索引与该注册表项的第二字段在第二数组中的索引相同。
[0088] 这种实施方式下,注册表包括两个数组,一个数组用于记录注册表项的第一字段,另一个数组用于记录注册表项的第二字段。
[0089] 这种实施方式使得查找注册表项第二字段时,仅需要获知第二数组的起始地址、该注册表项的第二字段在第二数组中的索引以及第二字段的长度,并根据这三种信息计算
该注册表项的第二字段的地址,从而降低计算复杂度。
[0090] 结合上面各种可能的实现方式,在一些可能的实现方式中,编译器还可以为原生方法注册表生成哈希表,并计算第一方法的符号名的哈希值,然后找到该哈希表中该哈希
值对应的哈希表项,并在该哈希表项中记录该第一方法的注册表项在原生注册表中的索
引。
[0091] 这样,后续注册第一方法的过程中,可以计算第一方法的符号名的哈希值,并将该哈希值作为索引找到哈希表中对应的哈希表项,读取该哈希表项中记录的索引值,就可以
根据该索引值找到原生注册表中与该第一方法对应的注册表项了。
[0092] 结合上面各种可能的实现方式,在一些可能的实现方式中,编译器还可以根据第一方法的符号名生成与该第一方法的注册表项对应的标签(label),该标签能够唯一识别
该注册表项,并且,编译器为调用第一方法的语句生成调用桩,该调用桩中包括该第一方法
的注册表项对应的标签。
[0093] 这样,后续调用第一方法的过程中,可以根据第一方法的调用桩中的标签快速找到第一方法的注册表项,从而可以快速读取第一方法对应的原生方法的地址,进而可以快
速调用第一方法对应的原生方法。
[0094] 在编译器对待编译代码进行编译后,编译得到的文件可以在运行时系统上进行运行,在运行的过程中,可以注册第一方法和其对应的原生方法的地址之间的绑定关系。
[0095] 图2所示为本申请一个实施例的原生方法的注册方法的示意性流程图。应理解,图2示出了该注册方法的步骤或操作,但这些步骤或操作仅是示例,本申请实施例还可以执行
其他操作或者图2中的各个操作的变形。此外,图2中的各个步骤可以按照与图2呈现的不同
的顺序来执行,并且有可能并非要执行图2中的全部操作。
[0096] 本申请实施例的注册方法中可以包括S210、S220和S230。图2所示的注册方法可以由运行时系统来执行。该注册方法可以在运行时系统加载包括原生方法的库文件之后,在
调用原生方法之前执行。
[0097] 应理解,该注册方法也可以由其他能够执行主体来执行,本申请对此不作限制。
[0098] S210,运行时系统确定被声明为原生方法的第一方法的符号名和该第一方法对应的原生方法的地址。
[0099] 其中,该运行时系统可以是Java运行时系统。
[0100] 该步骤中的各个词语的含义与图1所示的编译方法中的相关词语的含义相同或相似,此处不再赘述。
[0101] S220,运行时系统根据该第一方法的符号名确定与该第一方法对应的第一注册表项,该第一注册表项包括第一字段,该第一字段中记录的信息用于确定该第一方法的符号
名。
[0102] 该步骤中的各个词语的含义与图1所示的编译方法中的相关词语的含义相同或相似,此处不再赘述。
[0103] 该第一注册表中还可以包括第二字段,该第二字段用于记录该第一方法对应的原生方法的地址。
[0104] 通常情况下,如果运行时系统是第一次执行该步骤,则此时确定的注册表项中的第二字段中记录的可以是“null”。
[0105] 在一些可能的实现方式中,第一注册表项中的第一字段中记录的内容与运行时系统在S210中得到的第一方法的符号名相符。也就是说,第一注册表项中的第一字段中记录
的是第一方法的符号名。
[0106] 例如,运行时系统可以将S210中得到的第一方法的符号名依次与原生注册表中的注册表项中的第一字段中的内容作比较,直到找到与该第一方法的符号名相符的注册表
项,该注册表项即为该第一方法对应的第一注册表项。若将该第一方法的符号名与原生注
册表中的所有注册表项中的第一字段均作比较之后,没有找到与第一方法的符号名相符的
注册表项,则说明没有与该第一方法对应的注册表项。
[0107] 该实现方式中,运行时系统运行的目标代码应该是编译器按照图1所示的编译方法中相应的实现方式编译得到的。也就是说,该实现方式中要求编译器为第一方法生成的
注册表项包括的第一字段中记录的应该是第一方法的符号名。
[0108] 在一些可能的实现方式中,第一注册表项中的第一字段中记录该第一方法的符号名相对于第一注册表项的地址偏移量,第一方法的符号名记录在字符串表中,该字符串表
中记录一个或多个被声明为原生方法的方法的符号名。
[0109] 例如,运行时系统可以读取注册表项中的第一字段中记录的地址,然后将第一注册表项的地址与第一字段中记录的地址相加,得到一个目标地址,并在字符串表中找到该
目标地址处记录的符号名,并将该符号名与S210中得到的第一方法的符号名作比较。若比
较结果为一致,则该注册表项为第一方法对应的第一注册表项。
[0110] 若有多个注册表项,则运行时系统依次对这多个注册表项执行上面一段所述的操作,直到找到该第一方法对应的第一注册表项。若运行时系统依次对这多个注册表项执行
上面一段所述的操作之后,还是没有在字符串表中找到第一方法的符号名,则说明没有与
该第一方法对应的第一注册表项。
[0111] 该实现方式中,运行时系统运行的目标代码应该是编译器按照图1所示的编译方法中相应的实现方式编译得到的。也就是说,该实现方式中要求编译器为第一方法生成的
注册表项包括的第一字段中记录的应该是第一方法的符号名相对于该注册表项的地址偏
移量。
[0112] 当注册表项中的第一字段记录的是第一方法的符号名相对于字符串表的地址偏移量时,运行时系统确定第一方法对应的第一注册表项的方法类似,此处不再赘述。
[0113] 结合上述各种可能的实现方式,在一些可能的实现方式中,运行时系统根据该第一方法的符号名确定与该第一方法对应的第一注册表项可以包括:运行时系统计算第一方
法的符号名的哈希值;将该哈希值作为第一索引,确定哈希表中该第一索引对应的第一哈
希表项,该哈希表中的哈希表项用于记录该哈希表项对应的注册表项在原生注册表中的索
引,该原生注册表中包括一个或多个注册表项,该原生注册表中与第一哈希表项中记录的
第二索引对应的注册表项为所述第一注册表项。
[0114] 这样,运行时系统能够更加快速地从原生注册表中确定第一方法对应的第一注册表项。
[0115] S230,运行时系统在第一注册表项包括的第二字段中记录该第一方法对应的原生方法的地址。
[0116] 可选地,在S220中得到第二索引后,可以先检验第二索引的有效性,在第二索引有效的情况下,再执行S230,否则可以不执行S230,从而较少运行出错率。
[0117] 检验第二索引的有效性的一种实现方式包括:读取原生注册表中与第二索引对应的注册表项中的第一字段中的地址,然后根据该地址找到字符串表中对应的符号名,将该
符号名与第一方法的符号名作比较,若一致,这说明第二索引有效,否则第二索引无效。
[0118] 下面结合图3,介绍本申请实施例的注册方法一种的实现方式。图3所示的方法中的各个步骤的具体实现方式可以参考图2中相关的实现方式,此处不再赘述。
[0119] S301,加载原生库。该原生库中包括原生方法的实现,也就是说,加载原生方法的实现。
[0120] 例如,执行“System.loadLibrary("hello");”对应的目标代码,可以加载名为“hello”的动态库。
[0121] S302,判断原生库是否加载成功。若加载成功,则执行S303,否则执行S310。
[0122] S303,判断原生库中是否存在“JNI_OnLoad()”函数。若存在,则执行S304,否则执行S310。
[0123] S304,调用“JNI_OnLoad()”函数。
[0124] S305,调用“JNI::RegisterNatives()”函数。
[0125] 通常情况下,“JNI_OnLoad()”函数中会调用调用“JNI::RegisterNatives()”函数。
[0126] S306,生成被声明为原生方法的方法的符号名,并计算该符号名的哈希值。
[0127] JNI规范规定了JNI接口RegisterNatives()函数的原型,具体如下:
[0128] JNIT RegisterNatives(JNIEnv*env,jclass clazz,const JNINativeMethod*methods,jint nMethods);
[0129] 其中,参数“*env”为JNI接口指针;参数“clazz”为Java类对象;“*methods”为类的原生方法;“nMethods”为类的原生方法数。
[0130] “JNINativeMethod”类型的定义如下:
[0131]
[0132] 其中,name是Java中声明为原生方法的第一方法的名字;fnPtr是函数指针,也就是第一方法在原生语言(例如,C或C++)中对应的原生方法的实现;signature是第一方法的
签名,可以认为是参数和返回值,比如“(LMyJavaClass;)V”表示第一方法的参数是
LMyJavaClass,返回值是void。
[0133] “RegisterNatives()”中的参数“*methods”的定义可以如下:
[0134] static JNINativeMethod methods[]={
[0135] {"getNativeString","()Ljava/lang/String;",(getString)}
[0136] };
[0137] 其中,“getNativeString”为Java类中声明为原生方法的第一方法的方法名;“()Ljava/lang/String;”为第一方法的签名,“()”表示该方法无参数,“Ljava/lang/
String;”表示返回值为Java中的String类型;具体签名规则可以参考JNI规范;
(getString)为第一方法对应的原生方法的方法名,这里强制转换成了函数指针。
[0138] “RegisterNatives()”中传入上述参数“*methods”,表示需要注册名为“getNativeString”的方法,即需要将Java代码中名为“getNativeString”的方法与原生语
言实现的名为“getString”的原生方法绑定。
[0139] “RegisterNatives()”函数可以根据“getNativeString”和"()Ljava/lang/String;"生成符号名,然后计算该符号名的哈希值。
[0140] S307,根据哈希值找到哈希表中对应的哈希表项,获取哈希表项中存储的索引,该索引为注册表项在原生注册表中的索引。
[0141] S308,判断注册表项的索引是否有效。若有效,则执行S309,否则执行S310。
[0142] S309,根据索引确定注册表项,将原生库中的原生方法地址写入到该注册表项中,即完成原生方法的注册。
[0143] 例如,将“(getString)”写入该注册表项中的第二字段中。
[0144] S310,结束。
[0145] 图4所示为本申请一个实施例的原生方法的调用方法的示意性流程图。应理解,图4示出了该调用方法的步骤或操作,但这些步骤或操作仅是示例,本申请实施例还可以执行
其他操作或者图4中的各个操作的变形。此外,图4中的各个步骤可以按照与图4呈现的不同
的顺序来执行,并且有可能并非要执行图4中的全部操作。
[0146] 本申请实施例的调用方法中可以包括S410、S420和S430。图4所示的调用方法可以由运行时系统来执行。例如,运行时系统可以在执行图2所示的注册方法之后执行图4所示
的调用方法。
[0147] 应理解,该调用方法也可以由其他能够执行主体来执行,本申请对此不作限制。
[0148] S410,运行时系统确定被声明为原生方法的第一方法的调用桩,该调用桩中包括第一标签,第一标签与该第一方法对应的第一注册表项具有对应关系,该第一注册表项包
括第一字段和第二字段,第一字段中记录的信息用于确定第一方法的符号名,第二字段记
录该第一方法对应的原生方法的地址。
[0149] 该步骤中的各个词语的含义与图2所示的注册方法中的相关词语的含义相同或相似,此处不再赘述。
[0150] S420,运行时系统根据第一标签确定该第一方法对应的第一注册表项。
[0151] 例如,运行时系统根据第一标签,以及第一标签与第一注册表的对应关系,确定第一注册表。
[0152] S430,运行时系统根据第一注册表项包括的第二字段中记录的地址调用该第一方法对应的原生方法。
[0153] 运行时系统使用该方法可以更快速地调用原生函数。
[0154] 该调用方法要求编译器需要按照图1所示的编译方法中的相关方式来编译程序代码,为了简洁,此处不再赘述。
[0155] 若运行时系统根据上述调用方法调用原生方法失败,例如,根据调用桩中的标签读取的注册表项中记录的原生方法地址为“null”时,可以使用JNI规范规定的“JNI‑sytle”
符号名查找原生库的动态符号表,以调用原生方法。使用JNI规范规定的“JNI‑sytle”符号
名查找原生库的动态符号表,以调用原生方法的具体实现方式可以参考现有技术,此处不
再赘述。
[0156] 本申请还提供一种程序代码的运行方法,该运行方法可以包括图2所描述的注册方法,进一步地,还可以包括图4所描述的调用方法,为了简洁,此处不再赘述。
[0157] 图5是本申请一个实施例的编译装置的示意性结构图。应理解,图5示出的编译装置500仅是示例,本申请实施例的编译装置还可包括其他模块或单元,或者包括与图5中的
各个模块的功能相似的模块,或者并非要包括图5中所有模块。
[0158] 图5所示的编译装置500可以包括确定模块510和生成模块520。其中,编译装置500包括的各个模块可以通过软件和/或硬件方式实现。
[0159] 确定模块510,用于确定待编译代码.
[0160] 生成模块520,用于为所述待编译代码中被声明为原生方法的第一方法生成所述第一方法对应的第一注册表项,所述第一注册表项包括第一字段和第二字段,所述第一字
段中记录的信息用于确定所述第一方法的符号名,所述第二字段用于记录所述第一方法对
应的原生方法的地址,所述第一方法的符号名用于标识所述第一方法。
[0161] 在一些可能的实现方式中,所述第一注册表项中的第一字段中记录的是所述第一方法的符号名。
[0162] 在一些可能的实现方式中,所述第一注册表项中的第一字段中记录所述第一方法的符号名相对于所述第一注册表项的地址偏移量,所述第一方法的符号名记录在字符串表
中,所述字符串表中记录一个或多个被声明为原生方法的方法的符号名。
[0163] 在一些可能的实现方式中,所述生成模块还用于:计算所述第一方法的符号名的哈希值;将所述哈希值作为第一索引,确定哈希表中与该第一索引对应的第一哈希表项;将
所述第一注册表项在原生注册表中的第二索引写入所述第一哈希表项中,所述原生注册表
中包括一个或多个注册表项。
[0164] 在一些可能的实现方式中,所述生成模块还用于:根据所述第一方法的符号名生成与所述第一注册表项对应的标签,所述标签用于唯一识别所述第一注册表项;为调用所
述第一方法的语句生成调用桩,所述调用桩中包括所述标签。
[0165] 编译装置500可以用于执行图1所描述的编译方法中由编译器执行的步骤。为了简洁,此处不再赘述。
[0166] 编译装置500包括的各个模块可以通过软件和/或硬件方式实现。
[0167] 例如,在一些可能的实现方式中,该编译装置500可以是编译器。
[0168] 在另一些可能的实现方式中,该编译装置500可以包括与存储器耦合的处理器,该处理器用于执行所述存储器中的程序指令。当处理器执行所述存储器中的程序指令时,该
处理器实现确定模块510和生成模块520所执行的操作,从而实现图2中描述的编译方法。
[0169] 可选地,该编译装置500还可以包括所述存储器。
[0170] 在该可能的实现方式中,该编译装置可以是应用程序的开发设备。或者该编译装置可以是芯片,例如可以是能够集成在开发设备中的芯片。这种情况下,该运行装置还可以
包括通信接口。
[0171] 图6是本申请一个实施例的编译装置的示意性结构图。应理解,图6示出的编译装置600仅是示例,本申请实施例的编译装置还可包括其他模块或单元,或者包括与图6中的
各个模块的功能相似的模块,或者并非要包括图6中所有模块。
[0172] 图6所示的运行装置600可以包括确定模块610、记录模块620。可选地,图6所示的运行装置还可以包括调用模块630。
[0173] 确定模块610,用于确定被声明为原生方法的第一方法的符号名和该第一方法对应的原生方法的地址,所述符号名用于标识所述第一方法。
[0174] 所述确定模块610还用于:根据所述第一方法的符号名确定与所述第一方法对应的第一注册表项,所述第一注册表项包括第一字段,所述第一字段中记录的信息用于确定
所述第一方法的符号名;记录模块,用于在所述第一注册表项包括的第二字段中记录所述
第一方法对应的原生方法的地址。
[0175] 在一些可能的实现方式中,所述第一注册表项中的第一字段中记录的是所述第一方法的符号名。
[0176] 在一些可能的实现方式中,所述第一注册表项中的第一字段中记录所述第一方法的符号名相对于所述第一注册表项的地址偏移量,所述第一方法的符号名记录在字符串表
中,所述字符串表中记录一个或多个被声明为原生方法的方法的符号名。
[0177] 在一些可能的实现方式中,所述确定模块具体用于:计算所述第一方法的符号名的哈希值;将所述哈希值作为第一索引,确定哈希表中与该第一索引对应的第一哈希表项,
所述哈希表中的哈希表项用于记录所述哈希表项对应的注册表项在原生注册表中的索引,
所述原生注册表中包括一个或多个注册表项;将所述原生注册表中与所述第一哈希表项中
记录的第二索引对应的注册表项确定为所述第一注册表项。
[0178] 在一些可能的实现方式中,所述确定模块还用于:确定所述第一方法的调用桩,所述调用桩中包括第一标签,第一标签与所述第一方法对应的第一注册表项具有对应关系;
根据所述第一标签和所述对应关系,确定所述第一方法对应的所述第一注册表项;其中,所
述运行装置还包括调用模块630,用于根据所述第一注册表项包括的第二字段中记录的地
址,调用所述第一方法对应的原生方法。
[0179] 运行装置600可以用于执行图2以及图3所描述的注册方法中由运行时系统执行的步骤。进一步,运行装置600还可以用于执行图4所描述的调用方法中由运行时系统执行的
步骤。为了简洁,此处不再赘述。
[0180] 运行装置600包括的各个模块可以通过软件和/或硬件方式实现。
[0181] 例如,在一些可能的实现方式中,该运行装置600可以是运行时系统。
[0182] 在一些可能的实现方式中,该运行装置600可以包括与存储器耦合的处理器,该处理器用于执行所述存储器中的程序指令。当处理器执行所述存储器中的程序指令时,该处
理器实现确定模块610、记录模块620和调用模块630所执行的操作,从而实现图2中描述的
运行方法。
[0183] 可选地,该运行装置600还可以包括所述存储器。
[0184] 在该可能的实现方式中,该运行装置600可以是终端设备。这种情况下,该运行装置600还可以包括接收器和/或收发器。
[0185] 或者该运行装置600可以是芯片,例如可以是能够集成在终端设备中的芯片。这种情况下,该运行装置600还可以包括通信接口。
[0186] 本领域普通技术人员可以意识到,结合本文中所公开的实施例描述的各示例的单元及算法步骤,能够以电子硬件、或者计算机软件和电子硬件的结合来实现。这些功能究竟
以硬件还是软件方式来执行,取决于技术方案的特定应用和设计约束条件。专业技术人员
可以对每个特定的应用来使用不同方法来实现所描述的功能,但是这种实现不应认为超出
本申请的范围。
[0187] 所属领域的技术人员可以清楚地了解到,为描述的方便和简洁,上述描述的系统、装置和单元的具体工作过程,可以参考前述方法实施例中的对应过程,在此不再赘述。
[0188] 在本申请所提供的几个实施例中,应该理解到,所揭露的系统、装置和方法,可以通过其它的方式实现。例如,以上所描述的装置实施例仅仅是示意性的,例如,所述单元的
划分,仅仅为一种逻辑功能划分,实际实现时可以有另外的划分方式,例如多个单元或组件
可以结合或者可以集成到另一个系统,或一些特征可以忽略,或不执行。另一点,所显示或
讨论的相互之间的耦合或直接耦合或通信连接可以是通过一些接口,装置或单元的间接耦
合或通信连接,可以是电性,机械或其它的形式。
[0189] 所述作为分离部件说明的单元可以是或者也可以不是物理上分开的,作为单元显示的部件可以是或者也可以不是物理单元,即可以位于一个地方,或者也可以分布到多个
网络单元上。可以根据实际的需要选择其中的部分或者全部单元来实现本实施例方案的目
的。
[0190] 另外,在本申请各个实施例中的各功能单元可以集成在一个处理单元中,也可以是各个单元单独物理存在,也可以两个或两个以上单元集成在一个单元中。
[0191] 应理解,本申请实施例中的处理器可以为中央处理单元(central processing unit,CPU),该处理器还可以是其他通用处理器、数字信号处理器(digital signal 
processor,DSP)、专用集成电路(application specific integrated circuit,ASIC)、现
场可编程门阵列(field programmable gate array,FPGA)或者其他可编程逻辑器件、分立
门或者晶体管逻辑器件、分立硬件组件等。通用处理器可以是微处理器或者该处理器也可
以是任何常规的处理器等。
[0192] 所述功能如果以软件功能单元的形式实现并作为独立的产品销售或使用时,可以存储在一个计算机可读取存储介质中。基于这样的理解,本申请的技术方案本质上或者说
对现有技术做出贡献的部分或者该技术方案的部分可以以软件产品的形式体现出来,该计
算机软件产品存储在一个存储介质中,包括若干指令用以使得一台计算机设备(可以是个
人计算机,服务器,或者网络设备等)执行本申请各个实施例所述方法的全部或部分步骤。
而前述的存储介质包括:U盘、移动硬盘、只读存储器(read‑only memory,ROM)、随机存取存
储器(random access memory,RAM)、磁碟或者光盘等各种可以存储程序代码的介质。
[0193] 以上所述,仅为本申请的具体实施方式,但本申请的保护范围并不局限于此,任何熟悉本技术领域的技术人员在本申请揭露的技术范围内,可轻易想到变化或替换,都应涵
盖在本申请的保护范围之内。因此,本申请的保护范围应以所述权利要求的保护范围为准。