CN115495086A - 一种编译方法和编译器、Wasm虚拟机 - Google Patents

一种编译方法和编译器、Wasm虚拟机 Download PDF

Info

Publication number
CN115495086A
CN115495086A CN202211066051.5A CN202211066051A CN115495086A CN 115495086 A CN115495086 A CN 115495086A CN 202211066051 A CN202211066051 A CN 202211066051A CN 115495086 A CN115495086 A CN 115495086A
Authority
CN
China
Prior art keywords
code
class
function
wasm
compiler
Prior art date
Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
Pending
Application number
CN202211066051.5A
Other languages
English (en)
Inventor
周维
曹蓉
Current Assignee (The listed assignees may be inaccurate. Google has not performed a legal analysis and makes no representation or warranty as to the accuracy of the list.)
Ant Blockchain Technology Shanghai Co Ltd
Original Assignee
Ant Blockchain Technology Shanghai Co Ltd
Priority date (The priority date is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the date listed.)
Filing date
Publication date
Application filed by Ant Blockchain Technology Shanghai Co Ltd filed Critical Ant Blockchain Technology Shanghai Co Ltd
Priority to CN202211066051.5A priority Critical patent/CN115495086A/zh
Priority to PCT/CN2022/135270 priority patent/WO2024045379A1/zh
Publication of CN115495086A publication Critical patent/CN115495086A/zh
Pending legal-status Critical Current

Links

Images

Classifications

    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F8/00Arrangements for software engineering
    • G06F8/40Transformation of program code
    • G06F8/41Compilation
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F8/00Arrangements for software engineering
    • G06F8/40Transformation of program code
    • G06F8/41Compilation
    • G06F8/44Encoding
    • G06F8/443Optimisation
    • G06F8/4434Reducing the memory space required by the program code
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F8/00Arrangements for software engineering
    • G06F8/70Software maintenance or management
    • G06F8/73Program documentation
    • GPHYSICS
    • G06COMPUTING; CALCULATING OR COUNTING
    • G06FELECTRIC DIGITAL DATA PROCESSING
    • G06F9/00Arrangements for program control, e.g. control units
    • G06F9/06Arrangements for program control, e.g. control units using stored programs, i.e. using an internal store of processing equipment to receive or retain programs
    • G06F9/44Arrangements for executing specific programs
    • G06F9/455Emulation; Interpretation; Software simulation, e.g. virtualisation or emulation of application or operating system execution engines
    • G06F9/45504Abstract machines for programme code execution, e.g. Java virtual machine [JVM], interpreters, emulators

Landscapes

  • Engineering & Computer Science (AREA)
  • Software Systems (AREA)
  • Theoretical Computer Science (AREA)
  • General Engineering & Computer Science (AREA)
  • Physics & Mathematics (AREA)
  • General Physics & Mathematics (AREA)
  • Library & Information Science (AREA)
  • Devices For Executing Special Programs (AREA)

Abstract

一种对包含反射功能的代码进行编译的方法,包括:编译器从代码的程序入口开始扫描反射功能代码,并通过注解得到所述反射功能代码中用到的类及该类所用到的函数;所述编译器将所述包含反射功能的代码所依赖的类中由所述注解得到的用到的这个类及该类所用到的函数的代码加入到待编译列表;所述编译器对所述待编译列表进行编译,得到wasm字节码。所述方法可以应用于区块链。

Description

一种编译方法和编译器、Wasm虚拟机
技术领域
本说明书实施例属于编译器技术领域,尤其涉及一种编译方法和编译器、Wasm虚拟机。
背景技术
WebAssembly是由W3C社区组开发的开放标准,是一种安全,可移植的低级代码格式,专为高效执行和紧凑表示而设计,可以接近原生的性能运行,并为诸如C、C++、Java、Go等语言提供一个编译目标。WASM虚拟机起初设计的目的是用于解决Web程序日益严峻的性能问题,由于其具有的优越特性,被越来越多的非Web项目所采用,例如替代区块链智能合约执行引擎EVM。
发明内容
本发明的目的在于提供一种编译方法和编译器、Wasm虚拟机,包括:
一种对包含反射功能的代码进行编译的方法,包括:
编译器从代码的程序入口开始扫描反射功能代码,并通过注解得到所述反射功能代码中用到的类及该类所用到的函数;
所述编译器将所述包含反射功能的代码所依赖的类中由所述注解得到的用到的这个类及该类所用到的函数的代码加入到待编译列表;
所述编译器对所述待编译列表进行编译,得到wasm字节码。
一种编译器,包括:
扫描单元,从代码的程序入口开始扫描反射功能代码,并通过注解得到所述反射功能代码中用到的类及该类所用到的函数;
添加单元,将所述包含反射功能的代码所依赖的类中由所述注解得到的用到的这个类及该类所用到的函数的代码加入到待编译列表;
编译单元,对所述待编译列表进行编译,得到wasm字节码。
一种计算机设备,包括:
处理器;
以及存储器,其中存储有程序,其中在所述处理器执行所述程序时,进行以下操作:
从代码的程序入口开始扫描反射功能代码,并通过注解得到所述反射功能代码中用到的类及该类所用到的函数;
将所述包含反射功能的代码所依赖的类中由所述注解得到的用到的这个类及该类所用到的函数的代码加入到待编译列表;
对所述待编译列表进行编译,得到wasm字节码。
一种存储介质,用于存储程序,其中所述程序在被执行时进行以下操作:
从代码的程序入口开始扫描反射功能代码,并通过注解得到所述反射功能代码中用到的类及该类所用到的函数;
将所述包含反射功能的代码所依赖的类中由所述注解得到的用到的这个类及该类所用到的函数的代码加入到待编译列表;
对所述待编译列表进行编译,得到wasm字节码。
通过上述实施例,对于直接或间接依赖的类,通过注解指明的函数才会被一并编译,而这些直接或间接依赖的类中有大量不会在后续被调用的函数则不会经过编译器编译,使得编译器的编译过程可以具有“按需编译”的能力。这样,不仅降低了编译器的复杂度和工作量,还可以大大减少编译结果的体积。而且,较小体积的编译结果,对于加载进wasm虚拟机线性内存中的代码也将大大降低,从而可以提升wasm虚拟机的整体性能。
附图说明
为了更清楚地说明本说明书实施例的技术方案,下面将对实施例描述中所需要使用的附图作简单地介绍,显而易见地,下面描述中的附图仅仅是本说明书中记载的一些实施例,对于本领域普通技术人员来讲,在不付出创造性劳动性的前提下,还可以根据这些附图获得其他的附图。
图1是一实施例中Java程序的编译、执行过程的示意图;
图2是一编译器可以在将Java源代码编译成Wasm文件的过程的流程图;
图3是一实施例中字节码结构和虚拟机模块示意图;
图4是一实施例中线性内存中的表与普通内存中的表的示意图即关系图;
图5是一实施例中采用Java开发,经过编译器编译后得到wasm字节码,wasm字节码在集成有wasm虚拟机的各种平台上运行的流程图;
图6是一实施例中编译方法的流程图。
具体实施方式
为了使本技术领域的人员更好地理解本说明书中的技术方案,下面将结合本说明书实施例中的附图,对本说明书实施例中的技术方案进行清楚、完整地描述,显然,所描述的实施例仅仅是本说明书一部分实施例,而不是全部的实施例。基于本说明书中的实施例,本领域普通技术人员在没有作出创造性劳动前提下所获得的所有其他实施例,都应当属于本说明书保护的范围。
高级计算机语言便于人们编写,阅读交流,维护,机器语言则是计算机能直接解读、运行的。编译器可以将汇编或高级计算机语言源程序(Source program)作为输入,翻译成目标语言(Target language)机器代码的等价程序。源代码一般为高级语言(High-levellanguage),如C、C++等,而目标则是机器语言的目标代码(Object code),有时也称作机器代码(Machine code)。进而,可以由CPU执行这样的机器码(或者称为“微处理器指令”)。这种方式一般称为“编译执行”。
编译执行一般不具有跨平台的可扩展性。由于存在不同厂商、不同品牌和不同代的CPU,而这些不同的CPU支持的指令集很多情况下是不同的,如x86指令集,ARM指令集等,且同一厂商同一品牌但不同代的CPU(如不同代的Intel CPU)支持的指令集也不完全相同,因此,用同样的高级语言编写的同样的程序代码,在不同CPU上被编译器转换出来的机器码可能不同。具体的,编译器在转换高级语言编写的程序代码到机器码的过程中,会结合具体的CPU指令集的特点(如向量指令集等)进行优化以提升程序执行的速度,而此类优化往往与具体的CPU硬件相关。这样,同样的机器码,一个在x86平台上可以运行,但另一个在ARM上就可能无法运行;甚至同样是x86平台,随着时间的推移,指令集也不断丰富和扩展,这就导致不同代的x86平台运行的机器码也有不同。而且,由于执行机器码需要由操作系统内核对CPU进行调度,因此即使是同样的硬件,在不同操作系统下支持运行的机器码也可能不同。
不同于编译执行,还存在一种“解释执行”的程序运行方式。例如对于Java、C#等高级语言而言,此时编译器完成的功能是把源码(SourceCode)编译成通用中间语言的字节码(ByteCode)。
比如Java语言,将Java源代码通过Java的编译器编译成标准的字节码,这里编译器不针对任何实际的硬件处理器的指令集,而是定义了一套抽象的标准指令集。编译成的标准字节码一般无法在硬件CPU上直接运行,因此引入了一个虚拟机,即JVM,JVM运行在特定的硬件处理器上,用以解释和执行编译后的标准字节码。
JVM是Java Virtual Machine(Java虚拟机)的缩写,是一种虚构出来的计算机,往往通过在实际的计算机上仿真模拟各种计算机功能来实现。JVM屏蔽了与具体的硬件平台、操作系统等相关的信息,使Java程序只需要是生成的可在Java虚拟机上运行的标准字节码,就可以在多种平台上不加修改地运行。
Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。这样,只要保证JVM能够正确执行.class文件,就可以运行在诸如Linux、Windows、MacOS等不同的操作系统平台上了。
JVM运行在特定的硬件处理器上,负责针对所运行的特定处理器而进行字节码的解释和执行,并向上屏蔽这些底层的差异,呈现给开发者以标准的开发规范。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。具体的,JVM接收到输入的字节码后,逐句解释其中的每一条指令,并翻译成适合当前机器的机器码来运行,这些过程例如由称为Interpreter的解释器进行解释和执行。这样一来,编写Java程序的开发者不需要考虑编写后的程序代码将运行在哪种硬件平台上。JVM本身的开发是由Java组织的专业开发人员完成,以将JVM适配到不同的处理器架构上。迄今为止,主流的处理器架构只有有限的几种,如X86,ARM,RISC-V,MIPS。专业的开发人员将JVM分别移植到支持这几种特定硬件的平台后,Java程序理论上就可以在所有的机器上运行了。JVM的移植工作通常由Java开发组织专业的人员提供的,这就极大减轻了Java应用开发者的负担。
上述Java程序的编译、执行过程如图1所示。开发者开发的Java源代码一般是以.java作为扩展名。源文件经过编译器编译,生成.class扩展名的文件,这些.class文件即为字节码。字节码指令,也称为opcode。JVM就是靠解析这些opcode和操作数来完成程序的执行的。当使用Java命令运行.class文件的时候,实际上相当于在操作系统中启动了一个JVM进程,并向操作系统申请了一部分内存。这部分内存一般由虚拟机直接进行管理,具体又可以包括方法区、堆区、栈区等。JVM通过翻译这些字节码来执行,具体包括两种执行方式。一种是常见的解释执行,即将opcode+操作数翻译成机器代码后交给操作系统运行,另外一种执行方式就是JIT(Just In Time),也就是即时编译,这种方式会在一定条件下将字节码编译成机器码之后再执行。
解释执行带来了跨平台可移植性,但由于bytecode的执行经历了JVM中间翻译的过程,因此执行效率不如上述编译执行效率高,这种效率的差异有时甚至可达几十倍。
通过多年的发展和积累,Java已经是一门成熟的编程语言。其中一方面即降低编译器编译后的.class文件的大小。为了降低.class文件的大小,从而使Java字节码更易于传播,JVM中集成了大量的依赖库,并提供了标准化的API。例如,开发者开发的Java源代码中包括Person.java和Main.java两个文件,且main.java文件的头部声明导入Person。实际上,Main及其依赖的Person文件,在运行时还会涉及更多依赖的类,例如默认的父类和祖先类等(具体的一个例子例如是间接依赖的字符串类String.class)。如果JVM没有集成大量的依赖库,则在编译过程中需要对person、main及依赖的类一并进行编译,而这样得到的编译后的.class文件较多,整体体积也较大。JVM中集成大量的标准库之后,JVM在执行Java程序的过程中需要通过类加载器从外部加载的.class文件较少,且体积也较小,但是,仍然需要从内部加载依赖的类,例如是通过本地文件或网络加载。另一方面是JVM的动态加载特性。如前所述,JVM在执行java字节码的.class文件时,例如上述例子中的person.class和main.class,除了加载这两个字节码文件外,还需要加载很多依赖的类文件。动态加载特性,是JVM并不将所有的class一次性全部加载到内存中,而是按需加载class。具体的,JVM在使用到尚未被加载的class时,才去加载这个class。JVM的动态加载class特性,使得java程序在运行时可以根据条件来控制加载不同的实现类,从而降低内存的占用。内存的占用量直接影响JVM的执行效率。
Java等语言是使用运行在x86一类的通用硬件指令集的虚拟机,再执行自己的“汇编语言”(例如Java Bytecode)。实际上,Web平台在浏览器上也是采用类似于Java、Python的虚拟机环境,浏览器提供虚拟机环境执行一些JavaScript或者其他脚本语言,从而实现HTML页面的交互行为和一些网页的特定行为等,网页的特定行为例如是嵌入动态文本之类。随着业务需求越来越复杂,前端的开发逻辑也变得越来越复杂,相应的代码量随之变的越来越多,项目的开发周期也越来越长。除了逻辑复杂、代码量大,还有另一个原因是JavaScript这门语言本身的缺陷——JavaScript没有静态变量类型,从而会降低效率。具体的,JavaScript引擎会对JavaScript代码中执行次数较多的函数进行缓存和优化,例如JavaScript引擎将这样的代码编译成机器码后打包并发送到JIT Compiler,由JITCompiler编译为机器码;下次再执行到这个函数时,就会直接执行编译好的机器码。但是由于JavaScript采用的是动态变量,这个变量上一次可能是数组(Array),下一次就可能变成对象(Object)。这样,上一次JIT Compiler所做的优化就失去了作用,下一次又要重新进行优化。
在2015年,出现了WebAssembly(也简写为wasm)。WebAssembly是由W3C社区组开发的开放标准,是一种安全,可移植的低级代码格式,专为高效执行和紧凑表示而设计,可以接近原生的性能运行。WebAssembly是经过编译器编译之后的代码,体积小、起步快,在语法上完全脱离JavaScript,同时具有沙盒化的执行环境。WebAssembly使用静态类型,从而提升了执行效率。此外,WebAssembly将很多编程语言带到了Web中。而且,WebAssembly还进一步简化了一些执行过程,从而也带来执行效率的大幅提升。
WebAssembly是一个可移植、体积小、加载快并且兼容Web的全新格式,可以作为C/C++/Rust/Java等的编译目标。WebAssembly可以看做是Web平台的x86硬件通用指令集,作为一层中间语言,上层对接Java、Python、Rust、C++等,让这些语言都能编译成统一的格式,用于Web平台运行。
例如采用C++语言开发的源文件,一般以.cpp作为扩展名。cpp文件经过编译器编译,可以生成Wasm格式的字节码。类似的,采用Java语言开发的源文件,一般以.java作为扩展名。java文件经过编译器编译,可以生成Wasm格式的字节码。Wasm格式的字节码可以封装在wasc文件中。wasc是合并字节码和ABI(Application Binary Interface,应用程序二进制接口)的文件。根据W3C社区开放标准实现的WebAssembly虚拟机(也称为Wasm虚拟机或Wasm运行环境,是执行WASM字节码的虚拟机运行环境),采用运行时加载Wasm字节码并解释执行的方式实现。
比如要开发一款应用,如果想实现跨平台,例如采用java完成在Linux平台上的开发,用Objective-C实现iOS上的开发,用C#实现在Windows平台的开发...。如果有了wasm,只需要选择任意一门语言,然后编译成wasm,就可以分发到各个平台上。例如图5中所示,采用Java开发,经过编译器编译后可以得到wasm字节码,这个wasm字节码可以在集成有wasm虚拟机的各种平台上运行。
WASM虚拟机起初设计的目的是用于解决Web程序日益严峻的性能问题,由于其具有的优越特性,被越来越多的非Web项目所采用,例如替代区块链中的智能合约执行引擎EVM。
不同高级语言开发的程序,由于这些高级语言的特性不同,这些程序的行为也可能有所不同。例如采用Java语言开发的程序,由于Java语言具有反射机制,因此在由其对应的JVM虚拟机运行时可以实现反射功能。反射机制,也称为反射编程,是指计算机程序在运行时可以访问、检测和修改它本身状态或行为的一种能力。Java编程语言中的反射编程功能是一种常用功能,典型的是可以支持动态执行,而WASM字节码标准不直接支持反射功能。具有反射编程功能的高级语言除了Java外,还包括C#、Python、Go语言等。本申请某些地方主要以Java为例加以说明,当然也适用于C#、Python、Go语言等。
例如在区块链中,开发者开发的智能合约可以提供不同的函数以实现不同功能,后续合约调用者可以动态调用合约中某个或某些函数以实现特定功能。对于不支持反射功能的高级编程语言,一般需要开发者在开发合约时在代码中显式的写好对于调用不同函数时涉及的方法名到方法调用的转换,代码比较繁琐和冗长。对于支持反射功能的高级编程语言,开发者可以在开发合约时在代码中通过反射功能灵活、简便的实现调用不同函数时涉及的方法名到方法调用的转换。
例如在C++这类不支持反射编程的高级语言中,如果要实现动态执行,一般可以通过分支结构的方式根据需求动态执行,比如以下C++程序模拟动态执行不同方法:
Figure BDA0003827567930000071
代码段1
C++合约中的上述代码段1,提供了sum、multiply等函数来供合约调用者发起调用并传入参数。因为某一次合约调用中,合约无法提前知道发起的调用合约交易将调用合约中的具体哪个函数,因此通常采用if分支来匹配发起的合约调用。匹配到后,传入对应的参数以执行函数,并返回结果。这种方式是模拟动态执行的方式,对于合约中函数较多的情况,这部分代码较为繁琐和冗长。
类似功能的代码,例如在Java中可以通过反射机制实现:
Figure BDA0003827567930000072
代码段2:Person.java
Figure BDA0003827567930000073
Figure BDA0003827567930000081
代码段3:Main.java
代码段2的Person.java中定义了三个函数,分别是getSum、getMultiply、hello。前两个函数getSum、getMultiply的输入参数相同,都是两个整型变量a和b;后一个函数hello的输入参数与前两个不同,是一个字符串变量name。
代码段3的Main.java中首先导入了Person,并定义了类main。类main中定了函数main。函数main中,通过第5-6行的method获得方法名、两个整型类型的参数,在第7行对对应方法名的函数进行调用,并输入参数123和234。其中,第5-6行包含反射功能函数,即通过person.class.getMethod(methodName,int.class,int.class)获取对象person所属的类(包括继承自Object类的其它子类)中具有相同函数名和相同入参、出参(或返回类型)的函数(函数名和入参、出参也称为函数签名);在第7行的代码中采用取出的函数完成计算并返回计算结果。通过这样的方式,特别是对于有多个函数的情况,而不必像上述C++代码中模拟动态执行而采用的多条件分支结构将每个函数名进行匹配。
如前所述,上述代码段2和代码段3的java文件经过编译器编译,可以生成Wasm格式的字节码。但是Wasm虚拟机不同于JVM,Wasm虚拟机并没有集成Java/wasm文件运行所需要依赖的大量的库,所以在将java文件编译为Wasm字节码的过程中,需要由编译器将依赖的类一并编译。例如上述代码段3中第1行导入的代码段2中的Person,即在编译得到main的wasm字节码的过程中,需要将依赖的Person类的所有函数都一并编译。
显然的,上述代码段3中第5-6行,从函数的入参数量为2以及入参类型为int来看,只可能调用代码段1中的getSum或getMultiply函数,而不会调用hello这个函数。那么,对于编译过程来说,如果将包括hello函数在内的Person类一并打包,则会使得无用的函数占据较大的文件体积。上面的代码段仅仅是示例,实际情况中,一个类中可能存在若干类似hello函数的函数入参数量、入参类型、出参类型等与动态调用不一致的情况,则按照这种方式,这些类中大量不会在后续被调用的函数也经过编译器编译,则得到的编译结果将占据较大的体积。此外,上述仅仅示出了Person和Main两个类,实际上一般还会存在很多间接调用的类,例如Person类型依赖了String类型(因为Person类型的方法hello的参数和返回类型是String类型)。而String类作为标准类,其中包括有很多方法,但大多时候在一个程序中只会用到其中的一小部分方法。按照这种方式,大量间接依赖的类也会被一并编译,而这些间接依赖的类中有大量不会在后续被调用的函数也经过编译器编译,则得到的编译结果将占据较大的体积。
尽管在一般的编译过程中,可以通过静态分析来确定依赖的类,但是这种方式限于没有采用反射功能代码的情形。因为没有采用反射功能代码的情况下,调用的函数是明确的,编译器根据代码内容进行分析时可以确切的获得所要调用的是哪个函数。但是对于包括反射功能代码的情况,静态分析是无效的,因为静态分析手段无法确定反射功能代码在执行时实际发生调用的是哪个函数。
再者,Wasm虚拟机不具备动态加载的能力,而是需要将依赖的类一次性全部加载进线性内存中。Wasm虚拟机管理线性内存和非线性内存。Wasm虚拟机管理的线性内存具有逻辑地址,而非系统内存中的逻辑地址。Wasm虚拟机通过线性内存实现了至少一部分的沙箱目标。Wasm文件中的内存地址都是0~线性内存容量范围,而不会超过这个线性内存区域,这样就保证Wasm字节码在由虚拟机执行时不会读取到Wasm管理的线性内存之外的内存,即根本读取不到任何外界信息,除非通过主机API(HostAPI)来调用。这样,所有Wasm指令的读写都是访问的线性内存的地址,无法越界,从而实现沙箱目标。
线性内存的占用量,对wasm虚拟机的性能有较大影响。因此,前述在编译过程中将大量不会实际调用的类中的函数一并编译,生成编译后的字节码文件,在wasm虚拟机解释执行这样的字节码文件时,首先会将这些类和函数的元信息加载进Wasm虚拟机管理的线性内存中,从而占用大量线性内存空间,将影响wasm虚拟机的性能。
对于开发者已经采用Java编写好的源代码,其中可能已经包括反射机制。为了能使得Wasm虚拟机能够在执行编译完的Wasm文件时实现反射功能,编译器可以在将Java源代码编译成Wasm文件的过程中执行如图2所示的以下过程:
S110:根据所述源代码中定义第一类型的代码生成所述第一类型和第一类型中第一函数的元信息,并将生成的所述第一类型和第一类型中第一函数的元信息封装在所述Wasm文件中。
例如Java源代码中,可以定义类型(通常也简称为类),如上述Java代码中的ClassPerson{...}。其中,{...}中可以包括成员变量和成员函数。一个Java文件中可以定义多个类,每个类中可以定义多个成员函数。对于每个成员函数,一般可以包括返回类型、函数名称、输入参数等。这些类型可以统称为第一类型,这些成员函数可以统称为第一函数。这里的“第一”可以理解为“第一种”或“第一类”。在定义了类的基础上,可以基于类生成对象。使用类和对象是面向对象编程的主要手段。对象是对客观事物的抽象;类是对对象的抽象。它们的关系是,对象是类的实例,类是对象的模板。
第一类型和第一函数的元信息可以封装在wasm文件中。第一类型和第一函数的元信息至少可以包括第一类型对象的结构和第一函数的结构。因为在Java中皆为对象,类型也是一种特殊的对象,所以对于一个类型这样的特殊对象,其也有所属的类型和字段。后续根据这个第一类型对象可以找到其所属的类型。此外,还可以包括第一类型结构和/或第一类型的字段结构。是否包括第一类型结构、第一类型的字段结构,取决于编译器的编译方案,也可能取决于第一函数中是否有用到第一类型的字段,如第一函数中的实现需要采用第一类型中的某个或某些字段。在一个具体的例子中,第一类型和第一函数的元信息可以包括第一类型对象的结构、第一类型结构、第一类型的字段结构和第一函数的结构等,具体例如如下:
-第一类型对象结构:
--4字节的对象类型的线性内存地址;
--对象的各字段数组的线性内存地址;
-第一类型结构:
--4字节,类型名称字符串的线性内存地址;
--4字节,类型的字段数组的线性内存地址;
--4字节,类型的方法函数数组的线性内存地址;
-第一类型的字段结构:
--4字节,类型的字段数量;
--4字节,字段名称字符串的线性内存地址;
--4字节,字段的返回类型的线性内存地址;
-第一类型的函数结构:
--4字节,类型的方法函数数量;
--4字节,函数在函数表格中的索引;
--4字节,函数名称字符串的线性内存地址;
--4字节,函数返回类型的线性内存地址;
--4字节,函数的参数数量;
--各参数的类型数组的线性内存地址;
......
上述元信息中,前面的“-”表示第一层级,“--”表示第二层级,第二层级从属于其上最近的第一层级。
上述第一类型和第一类型中第一函数的元信息,可以封装在Wasm文件中。
特别的,这些元信息,后续由Wasm虚拟机加载后,可以加载到Wasm虚拟机管理的线性内存中。Wasm虚拟机管理的线性内存具有逻辑地址,而非系统内存中的逻辑地址。这里,这些元信息封装在Wasm文件的过程中,可以确定这些元信息所在的线性内存中的逻辑地址。此外,虚拟机还可以管理非线性内存,即后续所说的普通内存。
Wasm虚拟机通过线性内存实现了至少一部分的沙箱和确定性目标。首先,Wasm文件中的内存地址都是0~线性内存容量范围,而不会超过这个线性内存区域,这样就保证Wasm字节码在由虚拟机执行时不会读取到Wasm管理的线性内存之外的内存,即根本读取不到任何外界信息,除非通过主机API(HostAPI)来调用。这样,所有Wasm指令的读写都是访问的线性内存的地址,无法越界,从而实现沙箱目标。其次,本申请上下文中Wasm文件中类(即类型)的各种元信息在编译时即已确定,特别是本申请上下文中类及类中成员变量、成员函数在线性内存中的逻辑地址也是确定的,则对于区块链来说,不同区块链节点上通过Wasm虚拟机加载相同的合约Wasm文件并执行其中的合约字节码的过程,可以保证类中的各种元信息一致,具体到其中的类及类中成员变量、成员函数在线性内存中的逻辑地址也是一致的(即使基于逻辑地址产生的各种信息也是一致的,不会因为普通内存的随机性而产生不同),即不会因为细小的差别而导致相同合约字节码在不同节点的Wasm虚拟机中执行结果存在不一致,从而实现了确定性目标。
与此相反的,如果不采用Wasm虚拟机而直接执行C++代码,会因为内存随机性而无法一致,不仅是不同节点运行结果不一致,即使同一节点多次执行同样程序也会导致结果不一致。例如根据类定义采用new语句创建一个对象的操作,每次执行时,生成的对象内存地址很可能是不一样的,因为这个内存地址一般是由操作系统根据内存情况随机分配的。如果这个程序逻辑中包括根据这个地址来计算后续的某些内容,就会导致执行结果不一致。再例如,哈希表在部分实现中会根据对象的地址来计算哈希,这也会导致哈希表的保存顺序不一致,如果后续有遍历哈希表的操作,顺序也将不一致。
结合上述Java源代码,第一类型和第一函数的元信息可以如下:
表1、类型结构
Figure BDA0003827567930000111
Figure BDA0003827567930000121
Figure BDA0003827567930000131
需要说明的是,上面的各个4字节,仅仅是为了举例,而并不是限定。
此外,如上面表格中所示,线性内存中还可以存储类型结构中的具体内容,如下表2所示:
表2、类型结构的内容
Figure BDA0003827567930000132
Figure BDA0003827567930000141
可见,表1中左侧的列中某些字段中的地址指向表2中的某些字段。这个映射关系后续详述。需要说明的是,表1中各个字段所在的内存一般是连续的,这样便于在内存中查找同一类型相关的结构和字段;此外,表1中4个块中,至少每个块内的字段是连续的,这样在后续的代码段4中才可以通过指针从起始地址遍历来访问每个字段。而表1中各个字段存储了指向表2中各个字段的地址,即通过在表1中的地址可以找到内存中表2的各个字段,所以表2中的各个字段所在的内存并不要求连续。
具体的,在编译过程中,wasm函数模块(module)如下被处理:
Figure BDA0003827567930000142
代码段4
上述代码段3,意思是取类中函数的名称字符串、返回结果类型、入参类型填入,从而可以填入表2中的各个对应字段,并在表1中填写表2中该类的函数各字段所在线性内存的地址以及参数个数,同时创建该类函数的索引,并将在表3中建立对应索引3的条目,还将该索引填入表1的对应字段中。这样,例如getSum函数放入table中,索引为1,getMultiply函数也放入table中,索引为2。
S120:根据所述源代码中的反射功能代码生成根据运行时的动态参数获取第一函数类型和第一函数内容的第二函数字节码。
在编译器进行编译的过程中,可以加入对源代码中反射功能代码的支持。编译器的编译过程是将java源代码的结构组织成合适的格式,包括编译过程中根据抽象语法树进行词法/语法分析,根据符号表填充符号,注解处理,语义分析和代码生成等,从而最终将源码编码成为Wasm字节码。在这个过程中,编译器在对反射功能代码进行编译时,可以生成对应的根据运行时的动态参数获取第一函数类型和第一函数内容的第二函数字节码。例如,对于上述示例中的代码段3,第5-7行为反射功能代码,对应的字节码为第二函数字节码。
具体的,为了支持反射功能的代码,一般可以提供反射库,其中包括一些支持反射功能的类。在编写源代码的过程中,按照语法规则,开发者可以在类文件的头部导入这个反射库,例如通过import语句导入。在编译器编译源代码时,可以用反射库中的相关语句替换项目文件中的反射功能代码,进而进行如上所述的词法/语法分析,填充符号,注解处理,语义分析和代码生成等过程,从而生成Wasm文件中的字节码。
例如导入的反射库中,包含了对上述第5-7行代码中Class.getMethod()和Method.invoke()的具体实现。这样,在编译过程中,可以对源代码中涉及的反射功能代码即第5-7行的Class.getMethod()和Method.invoke()方法用反射库中对应的具体实现来替换。
提供的反射库中,可以包括Class.getMethod()和Method.invoke()的具体实现。
Class.getMethod()的实现方法例如如下:
Figure BDA0003827567930000151
Figure BDA0003827567930000161
代码段5
上述代码段5是反射库中对Class.getMethod具体实现的伪代码,如前所述,这些代码所在的反射库可以被导入。这样,编译过程中可以用被导入的相关反射函数的代码替换用户编写的Java代码中的调用。上述代码段5中,采用第11行拼接后的函数名在代码段5中获取到的类型的方法对象数组中遍历,直到匹配到名称字符串相同的第一函数,从而可以获得该第一函数在表1中的索引。
Method.invoke()的实现方法例如如下:
Figure BDA0003827567930000162
代码段6
上述代码段6是反射库中对Method.invoke具体实现的伪代码。在上述代码段3中,通过第5行的Class.getMethod()函数来获得名称字符串匹配的第一函数在表1中的索引,具体可以是通过上述p.getClass().getMethod()获得,这个函数的具体实现的如上述在代码段5中的实现。进而,可以执行代码段3中的第7行,即对对应的第一函数发起调用。具体的,在代码段6中,根据输入参数的数量来再次验证对应case的参数数量与在表1中的对应的数量一致的情况下,进行间接(indirect)调用。例如getSum在表1中的索引是1,通过代码段3中第5行可以由getSum字符串在表1中匹配出索引是1,进而可以通过发起调用的getSum函数输入的2个参数再次通过代码段6中的switch语句验证,可以验证得到case2中funcIndex为1且参数也是2个。这样,可以发起对funcIndex为1的函数的间接调用,即在后续表3中通过索引1查找到getSum()函数在后续表4中的起始地址,进而由虚拟机解析表4中对应起始地址的代码后执行。
虚拟机在执行上述编译后的wasm字节码前,可以先加载该wasm字节码。首先,可以通过一个入口函数,例如可以将sum(),1这样的函数和入参匹配到的函数。例如如下代码:
Figure BDA0003827567930000171
代码段7
这样,将sum(),a转换为getProperty()的实现。其中,sum()的入参可以与getProperty()的入参不同,例如这里sum()的入参为一个参数a,而getProperty()的除了被调用对象以及被调用方法名称外,入参为两个参数a、b。根据上述代码,getProperty()的入参的两个参数中的一个参数a即为sum()函数的入参a,而getProperty()的入参的两个参数中的另一参数b可以设定为设定的值,这个值可以是常量也可以是某个全局变量,后者例如是从其它值中读取。结合代码段2中第10-14行定义的实现,可以将sum()转换为getProperty()函数的处理。
Wasm文件执行前,虚拟机先加载所述Wasm文件,并执行如图2所示的以下过程:
S210:创建线性内存区域。
物理内存一般由操作系统来管理,例如负责建立逻辑地址和物理地址之间的映射关系。Wasm虚拟机可以维护一个线性内存区域,这个线性内存区域是操作系统管理的内存中的一部分,并由Wasm管理和控制。具体的,Wasm可以在操作系统管理的内存基础之上再进行一层抽象,得到一个地址例如是从0开始的线性内存区域,并可以根据偏移量来控制对线性内存的访问。如前所述,Wasm虚拟机还可以管理一部分非线性内存,这里称非线性内存为普通内存。
Wasm虚拟机加载Wasm文件后,在执行字节码之前,可以创建线性内存区域。
S220:采用所述Wasm文件中的所述元信息初始化所述线性内存区域中的至少部分内存。
如前所述,Wasm文件包含类型和函数的元信息,字节码。上述Wasm虚拟机加载Wasm文件后,可以创建线性内存区域,进而虚拟机可以采用所述Wasm文件包含的第一类型和第一函数的元信息初始化至少部分线性内存。如上所述,线性内存地址的地址可以从0开始,这个地址在操作系统中可以称为线性内存的基地址;线性内存中的其它地址相当于是相对这个基地址的偏移量。这样,线性内存中的地址a,对应操作系统中的内存地址是线性内存在操作系统中的基地址+线性内存中的偏移量a。Wasm虚拟机通过对操作系统内存进行这样的抽象,利于Wasm虚拟机更好的管理和使用内存。
这样,在wasm字节码执行之前,线性内存就是非空的;wasm字节码指令执行前,代码中的常量、类及函数的元信息等就预先包含在线性内存中,并且在线性内存中的地址是固定的,便于后续Wasm字节码在执行时进行确定性的调用。
此外,如前所述,Wasm虚拟机加载Wasm文件后,还可以创建普通内存区域,进而虚拟机可以采用Wasm文件包含的第一函数字节码和第二函数字节码初始化至少部分普通内存。根据类实例化得到的对象在执行时所调用的函数,在类对应的存储区域中。这个类对应的存储区域,一般位于虚拟机创建的普通内存中。也就是说,类中的函数位于普通内存区域。根据类创建的对象是类的实例化,在执行类中的函数时,需要从普通内存中加载并执行对应的函数,包括第一函数和第二函数。
虚拟机采用第一函数初始化至少部分普通内存后,可以生成两张表,分别是表3的函数表(table)和表4的函数代码。
函数表可以如下表所示:
表3、普通内存中的函数表
Figure BDA0003827567930000181
函数代码可以如下表所示:
表4、普通内存中的函数
Figure BDA0003827567930000191
例如第一函数包括函数1、函数2、函数3......。如上所示,表4中,函数1的代码数据块存储于普通内存中,具有一个虚拟机管理的普通内存中的起始地址,类似的,函数2的代码数据块具有一个普通内存中的起始地址,函数3的代码数据块具有一个普通内存中的起始地址。表3中的函数表可以以简短、规整的格式存储普通内存中各个函数代码的起始地址,例如表3中每行一个32bits的地址。
可见,上述第一类型中的第一函数,可以包括多个函数。为了便于在内存中统一管理第一类型中的函数,可以将表4中每个函数在普通内存中的起始地址填入表3中对应的位置,从而可以由这个函数表统一映射到不同的函数代码。
虚拟机生成表3的过程中,可以获得表3在普通内存中的起始地址。这样,根据表3的起始地址和索引,可以得到对应函数在表4中的起始地址。
综合上述表1、表2、表3、表4,可以构成一个整体的映射表,这个映射表可以如图3所示。其中,表1和表2可以存储在线性内存中,其地址由编译器编译时确定,且是固定不变的;表3和表4存储于普通内存中。表3的函数表中的每一项的值,可以指向表3中对应的函数代码的起始地址。从虚拟机的角度看,可以如图4所示。
S230:解析并执行所述Wasm文件中的字节码,并在执行到第二函数的字节码时,根据所述调用函数的动态参数在所述线性内存区域中基于所述元信息确定调用的第一函数并执行。
Wasm文件中的字节码在加载进虚拟机的过程中,类中的函数也会加载进虚拟机中的普通内存中,如上述所说的普通函数的初始化过程。所述Wasm字节码在运行时,涉及数值计算、内存的读写操作、函数调用等。Wasm字节码操作的内存空间,是在运行前创建的线性内存,而无法直接操作普通内存。普通内存可以由虚拟机来操作,这样可以保证wasm字节码不会对普通内存中的函数字节码进行直接的修改。
虚拟机解析并执行所述Wasm字节码,按照wasm字节码中的逻辑执行。执行到第二函数字节码中的反射功能代码时,可以根据所述调用函数的动态参数动态确定实际调用的函数。具体的,当执行到第二函数的字节码时:
当执行到上述代码段2中第11行,完成函数名的拼接;
当执行到第12行(实际还包括替换后的代码段4的内容),采用第11行拼接后的函数名在虚表中遍历,直到匹配到名称字符串相同的第一函数,从而可以获得该第一函数在表1中的索引;
当执行到代码段2中的第13行(实际还包括替换后的代码段5的内容),即对对应的第一函数发起调用。具体的,在代码段5中,根据输入参数的数量来再次验证对应case的参数数量与在表1中的对应的数量一致的情况下,进行间接(indirect)调用。例如getSum在表1中的索引是1,通过代码段2中第12行(及替换后的代码段4的内容)可以由getSum字符串在表1中匹配出索引是1,进而可以通过发起调用的getSum函数输入的2个参数再次通过代码段5中的switch语句验证,可以验证得到case2中funcIndex为1且参数也是2个。这样,可以发起对funcIndex为1的函数的间接调用,即在后续表3中通过索引1查找到getSum()函数在后续表4中的起始地址,进而解析表4中对应起始地址的代码后执行。
类似的,例如getMultiply在表1中的索引是2,通过代码段2中第12行(及替换后的代码段4的内容)可以由getMultiply字符串在表1中匹配出索引是2,进而可以通过发起调用的getMultiply函数输入的2个参数再次通过代码段5中的switch语句验证,可以验证得到case2中funcIndex为2且参数也是2个。这样,可以发起对funcIndex为2的函数的间接调用,即在后续表3中通过索引2查找到getMultiply()函数在后续表4中的起始地址,进而解析表4中对应起始地址的代码后执行。
上述的例子,可以实现根据所述调用函数的函数名字符串在所述线性内存区域中基于所述元信息确定调用的第一函数并执行。除了上述采用拼接的字符串,还可以是用户输入的字符串,或者是根据整数或者二进制构造得到的字符串。
通过上述实施例,可以在Wasm文件中实现反射功能,这样在Wasm程序运行时实现可以访问、检测以及修改本身状态或行为的能力。特别是对于有多个函数的情况,开发代码时方便开发者在代码中通过反射功能灵活、简便的实现调用不同函数。例如,开发者可以开发包含反射编程功能的Java源代码。其中,反射编程例如是获取某个对象的类型,获取的类型包括哪些字段、哪些方法等。具体的,区块链平台厂商可以提供辅助函数,这些辅助函数例如是位于一个反射库中。所述辅助函数可以包括一些获取类型和函数元信息的API。这个函数库可以提供给开发者,进而开发者在采用高级语言开发代码的过程中可以将这个库函数包括到源代码中,并且在源代码中调用所述函数库中的这类API,从而在源代码中通过这些辅助函数实现获取类型和函数元信息的功能。此外,也可以采用原有的函数库,例如Java中本身包含的提供反射编程功能的函数库,这样开发者在采用Java语言开发源代码的过程中可以引入所述函数库提供的反射编程功能。
前述提到,采用Java语言编辑的代码,开发者编写完后可以生成对应的源文件,一般是.java扩展名的源文件。代码的.java文件,可以经过编译器编译,生成Wasm格式的字节码。Wasm格式的字节码可以封装在wasc文件中。此外,还可能在其它支持反射功能的区块链系统中开发完成Java字节码,例如是.class扩展名的文件,则该Java字节码包含具有反射功能的代码。这样的Java字节码是Java源代码的一种等价程序,因此也可以采用本申请实施例中的编译器对这样的包含反射功能的Java字节码再次进行编译,从而生成Wasm字节码,则生成的Wasm字节码中也具有反射功能,从而在虚拟机执行该Wasm字节码时可以实现反射功能。
此外,如前所述,具有反射编程功能的高级语言除了Java外,还包括C#、Python、Go语言等。而有一些本身并不支持反射机制的编程语言开发的代码,也可以通过本申请提供的反射库、编译器和虚拟机实现反射功能,例如C++等语言。
以下介绍本申请一种对包含反射功能的代码进行编译方法实施例。其中,所述包含反射功能的代码,包括源代码或中间字节码。源代码例如是Java源代码,如.java扩展名的源代码。中间字节码例如是Java字节码,如.class的Java字节码。所述编译器可以集成Java编译工具链。对于Java源代码,可以先将其编译为java字节码,并进行下述方法实施例的过程。
如图6所示,该方法包括:
S610:编译器从代码的程序入口开始扫描反射功能代码,并通过注解得到所述反射功能代码中用到的类及该类所用到的函数。
开发者在开发源代码的过程中,可以采用反射机制,即包含反射功能代码。这样的反射功能代码,如前所述,表示程序执行时的动态性,可以使程序在运行时可以动态检测本身状态或行为。更具体的说,可以根据输入的指令或命令动态调用某个函数,而这个函数在被调用前是不确定的。
虽然不确定,但是开发者可以预期动态调用的函数,限定在一个较小的范围内。例如之前的代码段2和下面的代码段8。仍然如前所述,代码段2的Person中定义了三个函数,分别是getSum、getMultiply、hello。前两个函数getSum、getMultiply的输入参数相同,都是两个整型变量a和b;后一个函数hello的输入参数与前两个不同,是一个字符串变量name。
Figure BDA0003827567930000211
Figure BDA0003827567930000221
代码段8:Main.java
代码段8的Main.java中首先导入了代码段2中的Person,并定义了类main。类main中定了函数main。函数main中,通过第6-7行的method获得方法名、两个整型类型的参数,在第8行对对应方法名的函数进行调用,并输入参数123和234。其中,第6-7行包含反射功能函数,即通过person.class.getMethod(methodName,int.class,int.class)获取对象person所属的类(包括继承自Object类的其它子类)中具有相同函数名和相同入参、出参(或返回类型)的函数(函数名和入参、出参也称为函数签名);在第8行的代码中采用取出的函数完成计算并返回计算结果。
上述代码段8中第6-7行,从函数的入参数量为2以及入参类型为int来看,只可能调用代码段1中的getSum或getMultiply函数,而不会调用hello这个函数。那么,对于编译过程来说,如果将包括hello函数在内的Person类一并打包,则会使得无用的函数占据较大的文件体积。
为此,开发者可以在代码段8的第3行加入注解,例如"@LinkClass..."开始的代码。Java注解的英文为Java annotation,又称为Java标注,是Java编程语言中的一种特殊的代码注释方式。可以通过给Java代码中的类型、字段、方法、构造函数等添加Java注解来描述一些需要的信息,并可以通过Java的反射机制来获取这些类型或者字段或者方法的注解对象,从而运行时获取到这些注解信息。
这里,开发者可以通过第3行的注解来标注后面的类及该类所用到的函数。例如在第3行通过注解指明了Person.class这个类,即Person类,以及这个Person类中所用到的函数为getSum和getMultiply。注解中包括Person类,指明代码段8第1行导入的Person类;注解中Person类中所用到的函数为getSum和getMultiply,意为编译时只需将Person类中的getSum和getMultiply这两个函数的代码进行编译。注解中的Person类中不包括hello,意为编译时不需要将Person类中hello函数的代码进行编译。
编译器的编译过程包括将Java源代码(或Java字节码)的结构组织成合适的格式,包括编译过程中根据抽象语法树进行词法/语法分析,根据符号表填充符号,注解处理,语义分析和代码生成等,从而最终编码为Wasm字节码。在这个过程中,编译器从待编译的代码的程序入口开始扫描,即从函数main开始扫描,扫描main函数内用到的类型,类型中的字段类型,使用到的方法函数,函数内的函数调用信息,构造出程序的使用到的类型列表,以及使用到的各方法函数。
所述编译器从代码的程序入口开始扫描反射功能代码,可以从main开始扫描。扫描到反射功能代码后,对于反射功能代码之前包括注解的,可以获取注解中的内容。Java注解是一种没有函数逻辑的特殊类型,类似Java代码中可以读取到的特殊注释。不同的是,注释的内容在Java编译到字节码后是无法获取到的,但Java注解也就是java annotation,可以在运行时获取到,从而可以获取某个方法头部有哪些注解,注解有哪些属性。编译器可以读取到main法有LinkClass注解,并可以读取到这个注解的属性值。注解一般可以采用@LinkClass或@LinkClasses开头的代码表示。其中,@LinkClass开头的代码可以表示对单一类的注解,@LinkClasses开头的可以表示包括多个LinkClass注解。上述代码段8示出了@LinkClass开头的形式。@LinkClasses开头的形式,即包括多个LinkClass注解。
@LinkClass开头的形式,如代码段8中所示,可以通过后面的属性值(target=Person.class,methods={"getSum","getMultiply"})来指定后续反射功能代码中用到的类和用到的函数。用到的类,如通过target=被引用类型.class的方式来表示。该类中用到的函数,如通过methods={"被引用方法1","被引用方法2",...}的形式表示。代码段8中,通过target=Person.class指明后续反射功能代码中用到的类是Person这个类,通过methods={"getSum","getMultiply"}表示Person这个类中将用到的函数包括getSum和getMultiply这两个函数,而不包括Person类中的其它函数。这些如前所述,可以被编译器读取到。
需要说明的是,上述代码段2中的是自定义的Person类,其中以示例的形式描述了这个类包括3个成员函数。实际上,可以是其它的类,并可以在代码段8的顶部通过import的方式显示的表达,或者隐式的包含,所谓隐式的包含即不通过import的方式包含。
所述其它的类,可以是直接或间接依赖的类,例如标准的String.class类。String.class类中的方法有很多,可能有上百个。通过注解的方式,可以指明用到的其中的方法;对于注解中没有指明的,则可以认为是不会用到的方法。
S620:所述编译器将所述包含反射功能的代码所依赖的类中由所述注解得到的用到的这个类及该类所用到的函数的代码加入到待编译列表。
编译器编译过程中,如S610中所述,可以从程序入口开始进行扫描。通过注解字段“target=...”可以获取类名,并通过注解字段“methods=...”获取函数列表。如果所述函数列表不为空,则可以将该函数列表中的函数分别加入到待编译的列表中。这里的函数,包括依赖库中的函数原型的代码。
S630:编译器对所述待编译列表进行编译,得到wasm字节码。
对于Java,编译器底层例如可以采用TeaVM实现。TeaVM是一个翻译器,可以将JVM字节码翻译成wasm字节码。翻译过程中,TeaVM并不是必须需要Java源代码,而具有.class文件(即java字节码)既可以实现。TeaVM进行翻译的具体过程有成熟的工程实现和公开文件,这里不再赘述。
通过上述实施例,对于直接或间接依赖的类,通过注解指明的函数才会被一并编译,而这些直接或间接依赖的类中有大量不会在后续被调用的函数则不会经过编译器编译,使得编译器的编译过程可以具有“按需编译”的能力。这样,不仅降低了编译器的复杂度和工作量,还可以大大减少编译结果的体积。而且,较小体积的编译结果,对于加载进wasm虚拟机线性内存中的代码也将大大降低,从而可以提升wasm虚拟机的整体性能。
此外,这种方式并不要求wasm虚拟机集成动态加载的能力,对wasm的改动是较小的。当然,本领域技术人员知道,本实施例并不排斥wasm虚拟机具备动态加载能力。
再者,前述S110~S120以及S210~S230的实现过程,不仅可以适用于具有反射编程功能的高级语言,例如Java、C#、Python、Go语言等,还可以适用于原本本身并不支持反射机制的编程语言开发的代码,即也可以通过本申请提供的反射库、编译器和虚拟机实现反射功能,例如C++等语言。进而,本申请上述S610~S630的实施例,也可以适用于通过本申请提供的反射库、编译器和虚拟机实现反射功能的原本不支持反射功能的高级语言,例如C++等语言。
以下介绍本申请一种编译器的实施例,包括:
扫描单元,从代码的程序入口开始扫描反射功能代码,并通过注解得到所述反射功能代码中用到的类及该类所用到的函数;
添加单元,将所述包含反射功能的代码所依赖的类中由所述注解得到的用到的这个类及该类所用到的函数的代码加入到待编译列表;
编译单元,对所述待编译列表进行编译,得到wasm字节码。
以下介绍本申请一种计算机设备实施例,包括:
处理器;
以及存储器,其中存储有程序,其中在所述处理器执行所述程序时,进行以下操作:
从代码的程序入口开始扫描反射功能代码,并通过注解得到所述反射功能代码中用到的类及该类所用到的函数;
将所述包含反射功能的代码所依赖的类中由所述注解得到的用到的这个类及该类所用到的函数的代码加入到待编译列表;
对所述待编译列表进行编译,得到wasm字节码。
以下以实施例介绍本申请的一种存储介质,用于存储程序,其中所述程序在被执行时进行以下操作:
从代码的程序入口开始扫描反射功能代码,并通过注解得到所述反射功能代码中用到的类及该类所用到的函数;
将所述包含反射功能的代码所依赖的类中由所述注解得到的用到的这个类及该类所用到的函数的代码加入到待编译列表;
对所述待编译列表进行编译,得到wasm字节码。
在20世纪90年代,对于一个技术的改进可以很明显地区分是硬件上的改进(例如,对二极管、晶体管、开关等电路结构的改进)还是软件上的改进(对于方法流程的改进)。然而,随着技术的发展,当今的很多方法流程的改进已经可以视为硬件电路结构的直接改进。设计人员几乎都通过将改进的方法流程编程到硬件电路中来得到相应的硬件电路结构。因此,不能说一个方法流程的改进就不能用硬件实体模块来实现。例如,可编程逻辑器件(Programmable Logic Device,PLD)(例如现场可编程门阵列(Field Programmable GateArray,FPGA))就是这样一种集成电路,其逻辑功能由用户对器件编程来确定。由设计人员自行编程来把一个数字系统“集成”在一片PLD上,而不需要请芯片制造厂商来设计和制作专用的集成电路芯片。而且,如今,取代手工地制作集成电路芯片,这种编程也多半改用“逻辑编译器(logic compiler)”软件来实现,它与程序开发撰写时所用的软件编译器相类似,而要编译之前的原始代码也得用特定的编程语言来撰写,此称之为硬件描述语言(Hardware Description Language,HDL),而HDL也并非仅有一种,而是有许多种,如ABEL(Advanced Boolean Expression Language)、AHDL(Altera Hardware DescriptionLanguage)、Confluence、CUPL(Cornell University Programming Language)、HDCal、JHDL(Java Hardware Description Language)、Lava、Lola、MyHDL、PALASM、RHDL(RubyHardware Description Language)等,目前最普遍使用的是VHDL(Very-High-SpeedIntegrated Circuit Hardware Description Language)与Verilog。本领域技术人员也应该清楚,只需要将方法流程用上述几种硬件描述语言稍作逻辑编程并编程到集成电路中,就可以很容易得到实现该逻辑方法流程的硬件电路。
控制器可以按任何适当的方式实现,例如,控制器可以采取例如微处理器或处理器以及存储可由该(微)处理器执行的计算机可读程序代码(例如软件或固件)的计算机可读介质、逻辑门、开关、专用集成电路(Application Specific Integrated Circuit,ASIC)、可编程逻辑控制器和嵌入微控制器的形式,控制器的例子包括但不限于以下微控制器:ARC 625D、Atmel AT91SAM、Microchip PIC18F26K20以及Silicone Labs C8051F320,存储器控制器还可以被实现为存储器的控制逻辑的一部分。本领域技术人员也知道,除了以纯计算机可读程序代码方式实现控制器以外,完全可以通过将方法步骤进行逻辑编程来使得控制器以逻辑门、开关、专用集成电路、可编程逻辑控制器和嵌入微控制器等的形式来实现相同功能。因此这种控制器可以被认为是一种硬件部件,而对其内包括的用于实现各种功能的装置也可以视为硬件部件内的结构。或者甚至,可以将用于实现各种功能的装置视为既可以是实现方法的软件模块又可以是硬件部件内的结构。
上述实施例阐明的系统、装置、模块或单元,具体可以由计算机芯片或实体实现,或者由具有某种功能的产品来实现。一种典型的实现设备为服务器系统。当然,本申请不排除随着未来计算机技术的发展,实现上述实施例功能的计算机例如可以为个人计算机、膝上型计算机、车载人机交互设备、蜂窝电话、相机电话、智能电话、个人数字助理、媒体播放器、导航设备、电子邮件设备、游戏控制台、平板计算机、可穿戴设备或者这些设备中的任何设备的组合。
虽然本说明书一个或多个实施例提供了如实施例或流程图所述的方法操作步骤,但基于常规或者无创造性的手段可以包括更多或者更少的操作步骤。实施例中列举的步骤顺序仅仅为众多步骤执行顺序中的一种方式,不代表唯一的执行顺序。在实际中的装置或终端产品执行时,可以按照实施例或者附图所示的方法顺序执行或者并行执行(例如并行处理器或者多线程处理的环境,甚至为分布式数据处理环境)。术语“包括”、“包含”或者其任何其他变体意在涵盖非排他性的包含,从而使得包括一系列要素的过程、方法、产品或者设备不仅包括那些要素,而且还包括没有明确列出的其他要素,或者是还包括为这种过程、方法、产品或者设备所固有的要素。在没有更多限制的情况下,并不排除在包括所述要素的过程、方法、产品或者设备中还存在另外的相同或等同要素。例如若使用到第一,第二等词语用来表示名称,而并不表示任何特定的顺序。
为了描述的方便,描述以上装置时以功能分为各种模块分别描述。当然,在实施本说明书一个或多个时可以把各模块的功能在同一个或多个软件和/或硬件中实现,也可以将实现同一功能的模块由多个子模块或子单元的组合实现等。以上所描述的装置实施例仅仅是示意性的,例如,所述单元的划分,仅仅为一种逻辑功能划分,实际实现时可以有另外的划分方式,例如多个单元或组件可以结合或者可以集成到另一个系统,或一些特征可以忽略,或不执行。另一点,所显示或讨论的相互之间的耦合或直接耦合或通信连接可以是通过一些接口,装置或单元的间接耦合或通信连接,可以是电性,机械或其它的形式。
本发明是参照根据本发明实施例的方法、装置(系统)、和计算机程序产品的流程图和/或方框图来描述的。应理解可由计算机程序指令实现流程图和/或方框图中的每一流程和/或方框、以及流程图和/或方框图中的流程和/或方框的结合。可提供这些计算机程序指令到通用计算机、专用计算机、嵌入式处理机或其他可编程数据处理设备的处理器以产生一个机器,使得通过计算机或其他可编程数据处理设备的处理器执行的指令产生用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的装置。
这些计算机程序指令也可存储在能引导计算机或其他可编程数据处理设备以特定方式工作的计算机可读存储器中,使得存储在该计算机可读存储器中的指令产生包括指令装置的制造品,该指令装置实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能。
这些计算机程序指令也可装载到计算机或其他可编程数据处理设备上,使得在计算机或其他可编程设备上执行一系列操作步骤以产生计算机实现的处理,从而在计算机或其他可编程设备上执行的指令提供用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的步骤。
在一个典型的配置中,计算设备包括一个或多个处理器(CPU)、输入/输出接口、网络接口和内存。
内存可能包括计算机可读介质中的非永久性存储器,随机存取存储器(RAM)和/或非易失性内存等形式,如只读存储器(ROM)或闪存(flash RAM)。内存是计算机可读介质的示例。
计算机可读介质包括永久性和非永久性、可移动和非可移动媒体可以由任何方法或技术来实现信息存储。信息可以是计算机可读指令、数据结构、程序的模块或其他数据。计算机的存储介质的例子包括,但不限于相变内存(PRAM)、静态随机存取存储器(SRAM)、动态随机存取存储器(DRAM)、其他类型的随机存取存储器(RAM)、只读存储器(ROM)、电可擦除可编程只读存储器(EEPROM)、快闪记忆体或其他内存技术、只读光盘只读存储器(CD-ROM)、数字多功能光盘(DVD)或其他光学存储、磁盒式磁带,磁带磁磁盘存储、石墨烯存储或其他磁性存储设备或任何其他非传输介质,可用于存储可以被计算设备访问的信息。按照本文中的界定,计算机可读介质不包括暂存电脑可读媒体(transitory media),如调制的数据信号和载波。
本领域技术人员应明白,本说明书一个或多个实施例可提供为方法、系统或计算机程序产品。因此,本说明书一个或多个实施例可采用完全硬件实施例、完全软件实施例或结合软件和硬件方面的实施例的形式。而且,本说明书一个或多个实施例可采用在一个或多个其中包含有计算机可用程序代码的计算机可用存储介质(包括但不限于磁盘存储器、CD-ROM、光学存储器等)上实施的计算机程序产品的形式。
本说明书一个或多个实施例可以在由计算机执行的计算机可执行指令的一般上下文中描述,例如程序模块。一般地,程序模块包括执行特定任务或实现特定抽象数据类型的例程、程序、对象、组件、数据结构等等。也可以在分布式计算环境中实践本本说明书一个或多个实施例,在这些分布式计算环境中,由通过通信网络而被连接的远程处理设备来执行任务。在分布式计算环境中,程序模块可以位于包括存储设备在内的本地和远程计算机存储介质中。
本说明书中的各个实施例均采用递进的方式描述,各个实施例之间相同相似的部分互相参见即可,每个实施例重点说明的都是与其他实施例的不同之处。尤其,对于系统实施例而言,由于其基本相似于方法实施例,所以描述的比较简单,相关之处参见方法实施例的部分说明即可。在本说明书的描述中,参考术语“一个实施例”、“一些实施例”、“示例”、“具体示例”、或“一些示例”等的描述意指结合该实施例或示例描述的具体特征、结构、材料或者特点包含于本说明书的至少一个实施例或示例中。在本说明书中,对上述术语的示意性表述不必须针对的是相同的实施例或示例。而且,描述的具体特征、结构、材料或者特点可以在任一个或多个实施例或示例中以合适的方式结合。此外,在不相互矛盾的情况下,本领域的技术人员可以将本说明书中描述的不同实施例或示例以及不同实施例或示例的特征进行结合和组合。
以上所述仅为本说明书一个或多个实施例的实施例而已,并不用于限制本本说明书一个或多个实施例。对于本领域技术人员来说,本说明书一个或多个实施例可以有各种更改和变化。凡在本说明书的精神和原理之内所作的任何修改、等同替换、改进等,均应包含在权利要求范围之内。

Claims (12)

1.一种对包含反射功能的代码进行编译的方法,包括:
编译器从代码的程序入口开始扫描反射功能代码,并通过注解得到所述反射功能代码中用到的类及该类所用到的函数;
所述编译器将所述包含反射功能的代码所依赖的类中由所述注解得到的用到的这个类及该类所用到的函数的代码加入到待编译列表;
所述编译器对所述待编译列表进行编译,得到wasm字节码。
2.如权利要求1所述的方法,所述包含反射功能的代码,包括源代码或中间字节码。
3.如权利要求2所述的方法,所述编译器集成编译工具链,用于将源代码编译为中间字节码。
4.如权利要求1所述的方法,所述注解采用@LinkClass或@LinkClasses开头的代码表示。
5.如权利要求4所述的方法,所述@LinkClass开头的代码通过后面的属性值(target=被引用类型.class,methods={"被引用方法1","被引用方法2",...})来指定后续反射功能代码中用到的类和用到的该类中的函数。
6.如权利要求1所述的方法,所述反射功能的代码所依赖的类,包括显示或隐式的方式包含的依赖的类。
7.如权利要求1所述的方法,所述反射功能的代码所依赖的类,包括直接或间接依赖的类。
8.如权利要求1所述的方法,所述类所用到的函数的代码包括所述函数原型的代码。
9.如权利要求1-8任一方法,所述方法应用于区块链。
10.一种编译器,包括:
扫描单元,从代码的程序入口开始扫描反射功能代码,并通过注解得到所述反射功能代码中用到的类及该类所用到的函数;
添加单元,将所述包含反射功能的代码所依赖的类中由所述注解得到的用到的这个类及该类所用到的函数的代码加入到待编译列表;
编译单元,对所述待编译列表进行编译,得到wasm字节码。
11.一种计算机设备,包括:
处理器;
以及存储器,其中存储有程序,其中在所述处理器执行所述程序时,进行以下操作:
从代码的程序入口开始扫描反射功能代码,并通过注解得到所述反射功能代码中用到的类及该类所用到的函数;
将所述包含反射功能的代码所依赖的类中由所述注解得到的用到的这个类及该类所用到的函数的代码加入到待编译列表;
对所述待编译列表进行编译,得到wasm字节码。
12.一种存储介质,用于存储程序,其中所述程序在被执行时进行以下操作:
从代码的程序入口开始扫描反射功能代码,并通过注解得到所述反射功能代码中用到的类及该类所用到的函数;
将所述包含反射功能的代码所依赖的类中由所述注解得到的用到的这个类及该类所用到的函数的代码加入到待编译列表;
对所述待编译列表进行编译,得到wasm字节码。
CN202211066051.5A 2022-08-31 2022-08-31 一种编译方法和编译器、Wasm虚拟机 Pending CN115495086A (zh)

Priority Applications (2)

Application Number Priority Date Filing Date Title
CN202211066051.5A CN115495086A (zh) 2022-08-31 2022-08-31 一种编译方法和编译器、Wasm虚拟机
PCT/CN2022/135270 WO2024045379A1 (zh) 2022-08-31 2022-11-30 编译方法和编译器、Wasm虚拟机

Applications Claiming Priority (1)

Application Number Priority Date Filing Date Title
CN202211066051.5A CN115495086A (zh) 2022-08-31 2022-08-31 一种编译方法和编译器、Wasm虚拟机

Publications (1)

Publication Number Publication Date
CN115495086A true CN115495086A (zh) 2022-12-20

Family

ID=84468529

Family Applications (1)

Application Number Title Priority Date Filing Date
CN202211066051.5A Pending CN115495086A (zh) 2022-08-31 2022-08-31 一种编译方法和编译器、Wasm虚拟机

Country Status (2)

Country Link
CN (1) CN115495086A (zh)
WO (1) WO2024045379A1 (zh)

Cited By (4)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN116661910A (zh) * 2023-08-01 2023-08-29 北京中电华大电子设计有限责任公司 一种应用调用的方法及装置
CN116680015A (zh) * 2023-08-03 2023-09-01 北京中电华大电子设计有限责任公司 函数调用方法、装置、电子设备及可读存储介质
CN117111904A (zh) * 2023-04-26 2023-11-24 领悦数字信息技术有限公司 用于将Web应用自动转换成无服务器函数的方法和系统
CN117215544A (zh) * 2023-11-09 2023-12-12 中国科学院软件研究所 一种智能合约专用指令集的实现及运行方法

Family Cites Families (6)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
FR2961922B1 (fr) * 2010-06-29 2013-12-13 Flexycore Procede de compilation selective, dispositif et produit programme d'ordinateur correspondant.
CN109597621B (zh) * 2018-08-24 2022-10-14 天津字节跳动科技有限公司 封装Dagger的方法、装置、终端设备及存储介质
CN114064042A (zh) * 2020-07-31 2022-02-18 阿里巴巴集团控股有限公司 一种工程源码编译系统、方法及装置
CN112346778B (zh) * 2020-10-28 2022-02-08 常州微亿智造科技有限公司 一种Java程序编译时自动生成字节码的优化方法
CN113867704A (zh) * 2021-08-31 2021-12-31 五八有限公司 一种代码测试处理方法、装置、电子设备及存储介质
CN114416103A (zh) * 2022-03-14 2022-04-29 Oppo广东移动通信有限公司 代码编译方法及装置、计算机可读介质、电子设备

Cited By (8)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN117111904A (zh) * 2023-04-26 2023-11-24 领悦数字信息技术有限公司 用于将Web应用自动转换成无服务器函数的方法和系统
CN117111904B (zh) * 2023-04-26 2024-05-28 领悦数字信息技术有限公司 用于将Web应用自动转换成无服务器函数的方法和系统
CN116661910A (zh) * 2023-08-01 2023-08-29 北京中电华大电子设计有限责任公司 一种应用调用的方法及装置
CN116661910B (zh) * 2023-08-01 2023-09-22 北京中电华大电子设计有限责任公司 一种应用调用的方法及装置
CN116680015A (zh) * 2023-08-03 2023-09-01 北京中电华大电子设计有限责任公司 函数调用方法、装置、电子设备及可读存储介质
CN116680015B (zh) * 2023-08-03 2023-09-26 北京中电华大电子设计有限责任公司 函数调用方法、装置、电子设备及可读存储介质
CN117215544A (zh) * 2023-11-09 2023-12-12 中国科学院软件研究所 一种智能合约专用指令集的实现及运行方法
CN117215544B (zh) * 2023-11-09 2024-02-09 中国科学院软件研究所 一种智能合约专用指令集的实现及运行方法

Also Published As

Publication number Publication date
WO2024045379A1 (zh) 2024-03-07

Similar Documents

Publication Publication Date Title
CN111770113B (zh) 一种执行智能合约的方法、区块链节点和节点设备
CN115495086A (zh) 一种编译方法和编译器、Wasm虚拟机
TWI536263B (zh) 將作業系統之原始應用程式介面投射至其它程式語言
CN111815310B (zh) 一种执行智能合约的方法、区块链节点和存储介质
WO2024045382A1 (zh) 区块链中实现反射机制
Bolz et al. Back to the future in one week—implementing a Smalltalk VM in PyPy
US11687388B2 (en) Implementing optional specialization when executing code
CN111768184A (zh) 一种执行智能合约的方法及区块链节点
RU2347269C2 (ru) Система и способ декларативного определения и использования подклассов внутри разметки
US11048489B2 (en) Metadata application constraints within a module system based on modular encapsulation
US20180081677A1 (en) Using Annotation Processors Defined By Modules with Annotation Processors Defined By Non-Module Code
US20120023488A1 (en) Methods, systems, and computer program products for processing an excludable addressable entity
US10983771B1 (en) Quality checking inferred types in a set of code
Chen Comparative study of c, c++, c# and java programming languages
Misse-Chanabier et al. Illicium A modular transpilation toolchain from Pharo to C
CN115981652B (zh) 语言互操作方法、装置、存储介质及程序产品
Ramos et al. Implementing Python for DrRacket
US20200241857A1 (en) Methods, systems, and computer program products for processing an excludable addressable entity
CN116932085A (zh) 一种启动WebAssembly程序的方法、计算机设备及存储介质
CN116909652A (zh) 一种启动WebAssembly程序的方法、计算机设备及存储介质
Ziegler WebAssembly Code Generation from Java Bytecode
CN116931947A (zh) 一种优化wasm字节码的方法及执行方法、计算机设备及存储介质
Licker Low-level cross-language post-link optimisation
Lee et al. Object-Oriented Programming
Veiga Light programming language

Legal Events

Date Code Title Description
PB01 Publication
PB01 Publication
SE01 Entry into force of request for substantive examination
SE01 Entry into force of request for substantive examination