From 8bffca27b4e2d467fc4d067483074bbe1a720c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E5=8F=81=E7=8E=96?= Date: Fri, 30 May 2025 14:21:24 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AD=97=E8=8A=82=E7=A0=81=E7=BC=96=E8=AF=91AI?= =?UTF-8?q?=E6=89=AB=E6=8F=8F=E6=96=87=E6=A1=A3=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 林叁玖 --- .../arkts-utils/arkts-bytecode-file-format.md | 93 +++++++++---------- .../arkts-bytecode-function-name.md | 16 ++-- .../arkts-bytecode-fundamentals.md | 32 +++---- 3 files changed, 70 insertions(+), 71 deletions(-) diff --git a/zh-cn/application-dev/arkts-utils/arkts-bytecode-file-format.md b/zh-cn/application-dev/arkts-utils/arkts-bytecode-file-format.md index 505cb7c6c24..b0a35020801 100644 --- a/zh-cn/application-dev/arkts-utils/arkts-bytecode-file-format.md +++ b/zh-cn/application-dev/arkts-utils/arkts-bytecode-file-format.md @@ -1,5 +1,5 @@ # 方舟字节码文件格式 -本文详细介绍了方舟字节码文件的格式,旨在帮助开发者深入了解构成字节码文件的各个部分,从而指导开发者进行字节码的分析和修改工作。 +方舟字节码文件是ArkTS/TS/JS编译后的二进制产物。本文介绍方舟字节码文件的格式,帮助开发者了解其构成部分,指导字节码的分析和修改。 ## 约束 @@ -27,7 +27,7 @@ | **名称** | **格式** | **说明** | | -------------- | -------------- | ------------------------------------------------------------ | -| `utf16_length` | `uleb128` | 值为`len << 1 \| is_ascii`,其中`len`是字符串在UTF-16编码中的大小,`is_ascii`标记该字符串是否仅包含ASCII字符。 | +| `utf16_length` | `uleb128` | 值为`len << 1 \| is_ascii`,其中`len`是字符串在UTF-16编码中的大小,`is_ascii`标记该字符串是否仅包含ASCII字符,值是0或1。 | | `data` | `uint8_t[]` | 以'\0'结尾的MUTF-8编码字符序列。 | @@ -43,13 +43,13 @@ ## TypeDescriptor -TypeDescriptor是类([Class](#class))名称的格式,由`'L'`、`'_'`、`ClassName`和`';'`组成:`L_ClassName;`。其中,`ClassName`是类的全名,名字中的`'.'`会被替换为`'/'`。 +TypeDescriptor是类([Class](#class))名称的格式,由`'L'`、`'_'`、`ClassName`和`';'`组成,格式为`L_ClassName;`。其中,`ClassName`是类的全名,名字中的`'.'`会被替换为`'/'`。 ## 字节码文件布局 -字节码文件起始于[Header](#header)结构。文件中的所有结构均可以从`Header`出发,直接或间接地访问到。字节码文件中结构的引用方式包括偏移量和索引。偏移量是一个32位长度的值,表示当前结构的起始位置在字节码文件中相对于文件头的偏移,从0开始计算。索引是一个16位长度的值,表示当前结构在索引区域中的位置,此机制将在[IndexSection](#indexsection)章节描述。 +字节码文件起始于[Header](#header)结构。文件中的所有结构均可从`Header`出发,直接或间接地访问。字节码文件中结构的引用方式包括偏移量和索引。偏移量是一个32位长度的值,表示当前结构的起始位置在字节码文件中相对于文件头的距离,从0开始计算。索引是一个16位长度的值,表示当前结构在索引区域中的位置,具体机制将在[IndexSection](#indexsection)章节描述。 -字节码文件中所有的多字节值均采用小端字节序。 +字节码文件中的多字节值采用小端字节序。 ### Header @@ -70,13 +70,12 @@ TypeDescriptor是类([Class](#class))名称的格式,由`'L'`、`'_'`、`C | `num_lnps` | `uint32_t` | [LineNumberProgramIndex](#linenumberprogramindex)结构中元素的数量,即文件中定义的[Line number program](#line-number-program)的数量。 | | `lnp_idx_off` | `uint32_t` | 偏移量,指向[LineNumberProgramIndex](#linenumberprogramindex)。 | | `reserved` | `uint32_t` | 方舟字节码文件内部使用的保留字段。 | -| `reserved` | `uint32_t` | 方舟字节码文件内部使用的保留字段。 | | `num_index_regions` | `uint32_t` | [IndexSection](#indexsection)结构中元素的数量,即文件中[IndexHeader](#indexheader)的数量。 | | `index_section_off` | `uint32_t` | 偏移量,指向[IndexSection](#indexsection)。 | ### Version -字节码版本号由4个部分组成,格式为:`主版本号.次版本号.特性版本号.编译版本号`。 +字节码版本号包含4个部分,格式为:`主版本号.次版本号.特性版本号.编译版本号`。 | **名称** | **格式** | **说明** | | -------------- | -------------- | ---------------------------------------------------------- | @@ -105,9 +104,9 @@ TypeDescriptor是类([Class](#class))名称的格式,由`'L'`、`'_'`、`C | **名称** | **格式** | **说明** | | -------------- | -------------- | ------------------------------------------------------------ | -| `class_idx` | `uint16_t` | 一个指向该方法所从属的类的索引,指向一个在[ClassRegionIndex](#classregionindex)中的位置,该位置的值是一个指向[Class](#class)或[ForeignClass](#foreignclass)的偏移量。 | +| `class_idx` | `uint16_t` | 一个指向该方法所从属的类的索引,指向一个在[ClassRegionIndex](#classregionindex)中的位置,该位置的值是指向[Class](#class)或[ForeignClass](#foreignclass)的偏移量。 | | `reserved` | `uint16_t` | 方舟字节码文件内部使用的保留字段。 | -| `name_off` | `uint32_t` | 一个偏移量,指向[字符串](#字符串),表示方法名称。 | +| `name_off` | `uint32_t` | 一个指向[字符串](#字符串)的偏移量,表示方法名称。 | | `index_data` | `uleb128` | 方法的[MethodIndexData](#methodindexdata)数据。 | > **注意:** @@ -116,14 +115,14 @@ TypeDescriptor是类([Class](#class))名称的格式,由`'L'`、`'_'`、`C ### ClassIndex -ClassIndex结构的作用是通过名称快速地定位到Class的定义。 +ClassIndex结构用于通过名称快速定位Class的定义。 - 对齐方式:4个字节。 - 格式: | **名称** | **格式** | **说明** | | -------------- | -------------- | ------------------------------------------------------------ | -| `offsets` | `uint32_t[]` | 一个数组,数组中每个元素的值是一个指向[Class](#class)的偏移量。数组中的元素根据类的名称进行排序,名称遵循[TypeDescriptor](#typedescriptor)语法。数组长度由[Header](#header)中的`num_classes`指定。 | +| `offsets` | `uint32_t[]` | 一个数组,数组中每个元素的值是指向[Class](#class)的偏移量。数组中的元素根据类的名称排序,名称遵循[TypeDescriptor](#typedescriptor)语法。数组长度由[Header](#header)中的`num_classes`指定。 | ### Class @@ -159,28 +158,28 @@ ClassIndex结构的作用是通过名称快速地定位到Class的定义。 | **名称** | **值** | **数量** | **格式** | **说明** | | -------------- | ------------ | -------------- | -------------- | ------------------------------------------------------------ | -| `NOTHING` | `0x00` | `1` | `none` | 拥有此标记的[TaggedValue](#taggedvalue),是其所在`class_data`的最后一项。 | +| `NOTHING` | `0x00` | `1` | `none` | 拥有此标记的[TaggedValue](#taggedvalue)是其所在`class_data`的最后一项。 | | `SOURCE_LANG` | `0x02` | `0-1 ` | `uint8_t` | 拥有此标记的[TaggedValue](#taggedvalue)的`data`是0,表示源码语言是ArkTS/TS/JS。 | -| `SOURCE_FILE` | `0x07` | `0-1` | `uint32_t`| 拥有此标记的[TaggedValue](#taggedvalue)的`data`是一个偏移量,指向[字符串](#字符串),表示源文件的名称。 | +| `SOURCE_FILE` | `0x07` | `0-1` | `uint32_t`| 拥有此标记的[TaggedValue](#taggedvalue)的`data`是偏移量,指向[字符串](#字符串),表示源文件的名称。 | > **注意:** > -> ClassTag是`class_data`中元素([TaggedValue](#taggedvalue))所具备的标记,表头中的“数量”指的是在某一个[Class](#class)的`class_data`中拥有此标记的元素出现的次数。 +> ClassTag是`class_data`中元素([TaggedValue](#taggedvalue))所具备的标记,表头中的“数量”表示在某个[Class](#class)的`class_data`中拥有此标记的元素出现的次数。 ### Field -描述字节码文件中的字段。 +描述字节码文件的字段。 - 对齐方式:单字节对齐。 - 格式: | **名称** | **格式** | **说明** | | -------------- | -------------- | ------------------------------------------------------------ | -| `class_idx` | `uint16_t` | 一个指向该字段从属的类的索引,指向一个在[ClassRegionIndex](#classregionindex)中的位置,该位置的值是[Type](#type)类型,是一个指向[Class](#class)的偏移量。 | -| `type_idx` | `uint16_t` | 一个指向定义该字段的类型的索引,指向一个在[ClassRegionIndex](#classregionindex)中的位置,该位置的值是[Type](#type)类型。 | +| `class_idx` | `uint16_t` | 指向该字段从属的类的索引,该索引指向[ClassRegionIndex](#classregionindex)中的位置,值为[Type](#type),表示指向[Class](#class)的偏移量。 | +| `type_idx` | `uint16_t` | 指向定义该字段的类型的索引,该索引指向[ClassRegionIndex](#classregionindex)中的位置,值为[Type](#type)。 | | `name_off` | `uint32_t` | 一个偏移量,指向[字符串](#字符串),表示字段的名称。 | | `reserved` | `uleb128` | 方舟字节码文件内部使用的保留字段。 | -| `field_data` | `TaggedValue[]` | 不定长度的数组,数组中每个元素都是[TaggedValue](#taggedvalue)类型,元素的标记是[FieldTag](#fieldtag)类型,数组中的元素按照标记递增排序(`0x00`标记除外)。 | +| `field_data` | `TaggedValue[]` | 不定长度的数组,数组中每个元素都是[TaggedValue](#taggedvalue)类型,元素的标记是[FieldTag](#fieldtag)类型。数组中的元素按照标记递增排序(`0x00`标记除外)。 | > **注意:** > @@ -204,7 +203,7 @@ ClassIndex结构的作用是通过名称快速地定位到Class的定义。 ### Method -描述字节码文件中的方法。 +描述字节码文件的方法。 - 对齐方式:单字节对齐。 - 格式: @@ -223,7 +222,7 @@ ClassIndex结构的作用是通过名称快速地定位到Class的定义。 ### MethodIndexData -MethodIndexData是一个无符号32位整数,划分为3个部分。 +MethodIndexData是一个无符号32位整数,分为3个部分。 | **位** | **名称** | **格式** | **说明** | | ------------ | -------------- | -------------- | ------------------------------------------------------------ | @@ -249,15 +248,15 @@ MethodIndexData是一个无符号32位整数,划分为3个部分。 | **名称** | **值** | **数量** | **格式** | **说明** | | -------------- | ------------ | -------------- | -------------- | ------------------------------------------------------------ | -| `NOTHING` | `0x00` | `1` | `none` | 拥有此标记的[TaggedValue](#taggedvalue),是其所在`method_data`的最后一项。 | -| `CODE` | `0x01` | `0-1 ` | `uint32_t` | 拥有此标记的[TaggedValue](#taggedvalue)的`data`是一个偏移量,指向[Code](#code),表示方法的代码段。 | +| `NOTHING` | `0x00` | `1` | `none` | 拥有此标记的[TaggedValue](#taggedvalue),表示其所在`method_data`的最后一项。 | +| `CODE` | `0x01` | `0-1 ` | `uint32_t` | 拥有此标记的[TaggedValue](#taggedvalue)的`data`是偏移量,指向[Code](#code),表示方法的代码段。 | | `SOURCE_LANG` | `0x02` | `0-1` | `uint8_t` | 拥有此标记的[TaggedValue](#taggedvalue)的`data`是0,表示源码语言是ArkTS/TS/JS。 | -| `DEBUG_INFO` | `0x05` | `0-1` | `uint32_t` | 拥有此标记的[TaggedValue](#taggedvalue)的`data`是一个偏移量,指向[DebugInfo](#debuginfo),表示方法的调试信息。 | -| `ANNOTATION` | `0x06` | `>=0` | `uint32_t` | 拥有此标记的[TaggedValue](#taggedvalue)的`data`是一个偏移量,指向[Annotation](#annotation), 表示方法的注解。 | +| `DEBUG_INFO` | `0x05` | `0-1` | `uint32_t` | 拥有此标记的[TaggedValue](#taggedvalue)的`data`是偏移量,指向[DebugInfo](#debuginfo),表示方法的调试信息。 | +| `ANNOTATION` | `0x06` | `>=0` | `uint32_t` | 拥有此标记的[TaggedValue](#taggedvalue)的`data`是偏移量,指向[Annotation](#annotation),表示方法的注解。 | > **注意:** > -> MethodTag是`method_data`中元素([TaggedValue](#taggedvalue))所具备的标记,表头中的“数量”指的是在某一个[Method](#method)的`method_data`中拥有此标记的元素出现的次数。 +> MethodTag是`method_data`中元素([TaggedValue](#taggedvalue))所具备的标记,表头中的“数量”表示在某个[Method](#method)的`method_data`中拥有此标记的元素出现的次数。 ### Code @@ -270,7 +269,7 @@ MethodIndexData是一个无符号32位整数,划分为3个部分。 | `num_vregs` | `uleb128` | 寄存器的数量,存放入参和默认参数的寄存器不计算在内。 | | `num_args` | `uleb128` | 入参和默认参数的总数量。 | | `code_size` | `uleb128` | 所有指令的总大小,以字节为单位。 | -| `tries_size` | `uleb128` | `try_blocks`数组的长度,即[TryBlock](#tryblock)的数量。 | +| `tries_size` | `uleb128` | `try_blocks`数组的长度。 | | `instructions` | `uint8_t[]` | 所有指令的数组。 | | `try_blocks` | `TryBlock[]` | 一个数组,数组中每一个元素都是TryBlock类型。 | @@ -282,10 +281,10 @@ MethodIndexData是一个无符号32位整数,划分为3个部分。 | **名称** | **格式** | **说明** | | -------------- | -------------- | ------------------------------------------------------------ | -| `start_pc` | `uleb128` | TryBlock的第一条指令距离其所在[Code](#code)的`instructions`的起始位置的偏移量。 | +| `start_pc` | `uleb128` | TryBlock的第一条指令相对于其所在[Code](#code)的`instructions`的起始位置的偏移量。 | | `length` | `uleb128` | TryBlock的大小,以字节为单位。 | | `num_catches` | `uleb128` | 与TryBlock关联的[CatchBlock](#catchblock)的数量,值为1。 | -| `catch_blocks` | `CatchBlock[]` | 与TryBlock关联的CatchBlock的数组,数组中有且仅有一个可以捕获所有类型的异常的CatchBlock。 | +| `catch_blocks` | `CatchBlock[]` | 与TryBlock关联的CatchBlock的数组,数组中包含唯一一个可捕获所有类型异常的CatchBlock。 | ### CatchBlock @@ -296,22 +295,22 @@ MethodIndexData是一个无符号32位整数,划分为3个部分。 | **名称** | **格式** | **说明** | | -------------- | -------------- | ----------------------------------------------- | | `type_idx` | `uleb128` | 值是0,表示此CatchBlock块捕获了所有类型的异常。 | -| `handler_pc` | `uleb128` | 异常处理逻辑的第一条指令的程序计数器。 | +| `handler_pc` | `uleb128` | 异常处理逻辑的起始程序计数器。 | | `code_size` | `uleb128` | 此CatchBlock的大小,以字节为单位。 | ### Annotation -描述一个注解结构。 +说明一个注解结构。 - 对齐方式:单字节对齐 - 格式: | **名称** | **格式** | **说明** | | -------------- | ------------------- | ------------------------------------------------------------ | -| `class_idx` | `uint16_t` | 一个指向当前Annotation所从属的类的索引,指向一个在[ClassRegionIndex](#classregionindex)中的位置,该位置的值是[Type](#type)类型,是一个指向[Class](#class)的偏移量。 | +| `class_idx` | `uint16_t` | 一个指向当前Annotation所从属的类的索引,指向一个在[ClassRegionIndex](#classregionindex)中的位置,该位置的值是[Type](#type)类型,表示指向[Class](#class)的偏移量。 | | `count` | `uint16_t` | `elements`数组的长度。 | | `elements` | AnnotationElement[] | 一个数组,数组的每个元素都是[AnnotationElement](#annotationelement)类型。 | -| `element_types` | `uint8_t[]` | 一个数组,数组的每个元素都是[AnnotationElementTag](#annotationelementtag)类型,用于描述一个AnnotationElement。每个元素在`element_types`数组中的位置和其对应的AnnotationElement在`elements`数组中的位置一致。 | +| `element_types` | `uint8_t[]` | 一个数组,数组的每个元素都是[AnnotationElementTag](#annotationelementtag)类型,描述对应的AnnotationElement。每个元素在`element_types`数组中的位置和其对应的AnnotationElement在`elements`数组中的位置一致。 | > **注意:** > @@ -347,8 +346,8 @@ MethodIndexData是一个无符号32位整数,划分为3个部分。 | **名称** | **格式** | **说明** | | -------------- | -------------- | ------------------------------------------------------------ | -| `name_off` | `uint32_t` | 一个偏移量,指向[字符串](#字符串),表示注解元素的名称。 | -| `value` | `uint32_t` | 注解元素的值,若值的宽度不超过32位,则此处存储值本身。否则,此处存储的值为指向[Value formats](#value-formats)格式的偏移量。 | +| `name_off` | `uint32_t` | 指向[字符串](#字符串)的偏移量,表示注解元素的名称。 | +| `value` | `uint32_t` | 注解元素的值若值的宽度不超过32位,存储值本身。否则,存储的值为指向[Value formats](#value-formats)格式的偏移量。 | ### Value formats @@ -358,8 +357,8 @@ MethodIndexData是一个无符号32位整数,划分为3个部分。 | -------------- | -------------- | ------------------------------------------------------------ | | `INTEGER` | `uint32_t` | 有符号的四字节整数值。 | | `LONG` | `uint64_t` | 有符号的八字节整数值。 | -| `FLOAT` | `uint32_t` | 四字节位模式,向右零扩展,系统会将其解译为 IEEE754 32 位浮点值。 | -| `DOUBLE` | `uint64_t` | 八字节位模式,向右零扩展,系统会将其解译为 IEEE754 64 位浮点值。 | +| `FLOAT` | `uint32_t` | 四字节位模式,向右零扩展,系统会将其解译为IEEE754 32位浮点值。 | +| `DOUBLE` | `uint64_t` | 八字节位模式,向右零扩展,系统会将其解译为IEEE754 64位浮点值。 | | `ID` | `uint32_t` | 四字节位模式,表示文件中某个结构的偏移量。 | @@ -375,7 +374,7 @@ MethodIndexData是一个无符号32位整数,划分为3个部分。 ### DebugInfo -调试信息(DebugInfo)包含方法的程序计数器与源代码中的行列号之间的映射以及有关局部变量的信息。调试信息的格式由[DWARF调试信息格式第3版](https://dwarfstd.org/dwarf3std.html)(见第6.2项)的内容演变形成。基于状态机(State machine)的执行模型对行号程序(Line number program)进行解释,可得到映射和局部变量信息编码。为对不同方法的相同行号程序进行去重,程序中引用的所有常量都被移动到了常量池(Constant pool)中。 +调试信息(DebugInfo)包含方法的程序计数器与源代码中的行列号之间的映射以及有关局部变量的信息。调试信息的格式由[DWARF调试信息格式第3版](https://dwarfstd.org/dwarf3std.html)(见第6.2项)的内容演变形成。基于状态机([State machine](#state-machine))的执行模型对行号程序([Line number program](#line-number-program))进行解释,可得到映射和局部变量信息编码。为对不同方法的相同行号程序进行去重,程序中引用的所有常量都被移动到了常量池([Constant pool](#constant-pool))中。 - 对齐方式:单字节对齐。 - 格式: @@ -391,31 +390,31 @@ MethodIndexData是一个无符号32位整数,划分为3个部分。 #### Constant pool -常量池(Constant pool)是DebugInfo中存放常量的结构。很多方法都具有相似的行号程序,其区别仅在于变量名、变量类型和文件名。为了对这类行号程序进行去重,程序中引用的所有常量都存储在常量池。在解释程序时,状态机维护一个指向常量池的指针。当状态机解释一条需要常量参数的指令时,会从内存常量池指针指向的位置读取值,然后递增指针。 +常量池(Constant pool)是DebugInfo中存放常量的结构。很多方法都具有相似的行号程序,其区别仅在于变量名、变量类型和文件名。为了对这类行号程序进行去重,程序中引用的所有常量都存储在常量池。在状态机解释程序时,状态机维护一个指向常量池的指针。状态机解释需要常量参数的指令时,从常量池指针指向的位置读取值并递增指针。 #### State machine -状态机(State machine)的作用是产生[DebugInfo](#debuginfo)信息。状态机中有以下的寄存器: +状态机(State machine)的作用是产生[DebugInfo](#debuginfo)信息。状态机包含以下寄存器: | **名称** | **初始值** | **说明** | | ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | `address` | 0 | 程序计数器(指向方法的某个指令),只能单调递增。 | | `line` | [DebugInfo](#debuginfo)的属性`line_start`的值 | 无符号整数,对应源码中的行号。所有的行都是从1开始编号,因此寄存器的值不能小于1。 | | `column` | 0 | 无符号整数,对应源码中的列号。 | -| `file` | `class_data`(参见[Class](#class))中`SOURCE_FILE`标记的值,或者0 | 一个偏移量,指向[字符串](#字符串),表示源文件的名称。如果没有文件名信息([Class](#class)中没有`SOURCE_FILE`标记),那么寄存器的值是0。 | -| `source_code` | 0 | 一个偏移量,指向[字符串](#字符串),表示源文件的源码。如果没有源码信息,那么寄存器的值是0。 | +| `file` | `class_data`(参见[Class](#class))中`SOURCE_FILE`标记的值,或者0 | 一个指向[字符串](#字符串)的偏移量,表示源文件的名称。如果该类未包含`SOURCE_FILE`标记(即[Class](#class)中没有`SOURCE_FILE`标记),则该寄存器的值是0。 | +| `source_code` | 0 | 一个指向[字符串](#字符串)的偏移量,表示源文件的源码。如果没有源码信息,则该寄存器的值是0。 | | `constant_pool_ptr` | [DebugInfo](#debuginfo)中常量池的第一个字节的地址 | 指向当前常量值的指针。 | #### Line number program -一个行号程序(Line number program)由指令组成。每条指令都包含一个字节的操作码以及可选参数。根据操作码的不同,参数的值可能被编码在指令中(称为指令参数),或者需要从常量池中获取(称为常量池参数)。 +行号程序(Line number program)由指令组成。每条指令都包含一个字节的操作码以及可选参数。根据操作码的不同,参数的值可能被编码在指令中(称为指令参数),或者需要从常量池中获取(称为常量池参数)。 | **操作码** | **值** | **指令参数** | **常量池参数** | **参数说明** | **说明** | | ----- | ----- | ------- | ---- | ------- | ------ | | `END_SEQUENCE` | `0x00` | | | | 标记行号程序的结束。 | | `ADVANCE_PC` | `0x01` | | `uleb128 addr_diff` | `addr_diff`:`address`寄存器的值待增加的数值。 | `address`寄存器中的值加上`addr_diff`,指向下一个地址,而不生成位置条目。 | | `ADVANCE_LINE` | `0x02` | | `sleb128 line_diff` | `line_diff`:`line`寄存器的值待增加的数值。 | `line`寄存器中的值加上`line_diff`,指向下一个行位置,而不生成位置条目。 | -| `START_LOCAL` | `0x03` | `sleb128 register_num` | `uleb128 name_idx`
`uleb128 type_idx` | `register_num`:将包含局部变量的寄存器。
`name_idx`:一个偏移量,指向[字符串](#字符串),表示变量的名称。
`type_idx`:一个偏移量,指向[字符串](#字符串),表示变量的类型。 | 在当前地址中引入一个带有名称和类型的局部变量。将要包含这个变量的寄存器的编号被编码在指令中。如果寄存器的编号是-1,则意味着这个是累加器寄存器。`name_idx`和`type_idx`的值可能是0,如果是0,则代表着对应的信息是不存在的。 | +| `START_LOCAL` | `0x03` | `sleb128 register_num` | `uleb128 name_idx`
`uleb128 type_idx` | `register_num`:将包含局部变量的寄存器。
`name_idx`:一个偏移量,指向[字符串](#字符串),表示变量的名称。
`type_idx`:一个偏移量,指向[字符串](#字符串),表示变量的类型。 | 在当前地址中引入一个带有名称和类型的局部变量。将要包含这个变量的寄存器的编号被编码在指令中。如果寄存器的编号是-1,则意味着这个寄存器是累加器寄存器。`name_idx`和`type_idx`的值可能是0,如果是0,则代表着对应的信息是不存在的。 | | `START_LOCAL_EXTENDED` | `0x04` | `sleb128 register_num` | `uleb128 name_idx`
`uleb128 type_idx`
`uleb128 sig_idx` | `register_num`:将包含局部变量的寄存器。
`name_idx`:一个偏移量,指向[字符串](#字符串),表示变量的名称。
`type_idx`:一个偏移量,指向[字符串](#字符串),表示变量的类型。
`sig_idx`:一个偏移量,指向[字符串](#字符串),表示变量的签名。 | 在当前地址中引入一个带有名称、类型和签名的局部变量。将要包含这个变量的寄存器的编号被编码在指令中。如果寄存器的编号是-1,则意味着这个是累加器寄存器。`name_idx`、`type_idx`和`sig_idx`的值可能是0,如果是0,则代表着对应的信息是不存在的。 | | `END_LOCAL` | `0x05` | `sleb128 register_num` | | `register_num`:包含局部变量的寄存器。 | 在当前地址将指定寄存器中的局部变量标记为超出范围。寄存器的编号为-1,则意味着是累加器寄存器。 | | `SET_FILE` | `0x09` | | `uleb128 name_idx` | `name_idx`:一个偏移量,指向[字符串](#字符串),表示文件的名称。 | 设置file寄存器的值。`name_idx`的值可能是0,如果是0,则代表着对应的信息是不存在的。 | @@ -424,7 +423,7 @@ MethodIndexData是一个无符号32位整数,划分为3个部分。 | 特殊操作码 | `0x0c..0xff` | | | | 使 `line` 和 `address` 寄存器指向下一个地址,并生成一个位置条目。详情参阅下文中的说明。 | -对于值在`0x0c`和`0xff`(含)之间的特殊操作码,状态机按照以下步骤将`line`和`address`寄存器移动一小部分,然后生成一个新的位置条目(参见[DWARF调试信息格式第3版](https://dwarfstd.org/dwarf3std.html)第6.2.5.1项 Special Opcodes): +对于值在`0x0c`和`0xff`(含)之间的特殊操作码,状态机按照以下步骤将`line`和`address`寄存器移动一小部分,然后生成一个位置条目(参见[DWARF调试信息格式第3版](https://dwarfstd.org/dwarf3std.html)第6.2.5.1项 Special Opcodes): | **步骤序号** | **操作** | **说明** | | ----- | -------------------------------------------------- | ------------------------------------------------------------ | @@ -439,7 +438,7 @@ MethodIndexData是一个无符号32位整数,划分为3个部分。 ### IndexSection -通常情况下,字节码文件的各个结构使用32位偏移量来引用,当一个结构引用另一个结构时,需要在当前结构中记录被引用结构的32位偏移量。为了减小文件体积,字节码文件被分割成多个索引区域(Index region),每个索引区域内的结构使用16位索引。IndexSection结构描述了索引区域的集合。 +通常情况下,字节码文件的各个结构使用32位偏移量来引用。当一个结构引用另一个结构时,需要在当前结构中记录被引用结构的32位偏移量。为减小文件体积,字节码文件被分割成多个索引区域(Index region),每个索引区域内的结构使用16位索引。IndexSection结构描述了索引区域的集合。 - 对齐方式:4个字节。 - 格式: @@ -450,7 +449,7 @@ MethodIndexData是一个无符号32位整数,划分为3个部分。 ### IndexHeader -每个IndexHeader结构描述一个索引区域。每个索引区域都有两类索引:指向[Type](#type)的索引和指向方法、字符串或者字面量数组的索引。 +每个IndexHeader结构描述一个索引区域。每个索引区域包含两类索引:指向[Type](#type)的索引和指向方法、字符串或者字面量数组的索引。 - 对齐方式:4个字节。 - 格式: @@ -481,7 +480,7 @@ ClassRegionIndex结构的作用是允许通过更紧凑的索引,找到对应 ### Type -表示一个基本类型编码或一个指向[Class](#class)的偏移量,是一个32位的值。 +表示基本类型编码或指向[Class](#class)的偏移量,是一个32位的值。 基本类型采用以下方式编码: diff --git a/zh-cn/application-dev/arkts-utils/arkts-bytecode-function-name.md b/zh-cn/application-dev/arkts-utils/arkts-bytecode-function-name.md index 2a838085245..1b716a0f47b 100644 --- a/zh-cn/application-dev/arkts-utils/arkts-bytecode-function-name.md +++ b/zh-cn/application-dev/arkts-utils/arkts-bytecode-function-name.md @@ -1,17 +1,17 @@ # 方舟字节码函数命名规则 ## 概述 -本文介绍字节码文件中[Method](arkts-bytecode-file-format.md#method)的`name_off`字段指向的字符串的命名规则,该规则从方舟字节码文件版本`12.0.4.0`开始生效。 +本文介绍字节码文件中[Method](arkts-bytecode-file-format.md#method)的`name_off`字段的命名规则,该规则从方舟字节码文件版本`12.0.4.0`开始生效。 ## 入口函数 -模块加载时被执行的函数,名称固定为`func_main_0`。 +模块加载时被执行的函数名称固定为`func_main_0`。 ## 非入口函数 其他函数在字节码文件中的名称结构如下: ```ts #前缀#原函数名 ``` -下面的章节将会详细介绍前缀和原函数名。 +下面章节将详细介绍前缀和原函数名。 ### 前缀 -前缀包含函数定义时所在的作用域信息。包含以下几个部分: +前缀包含函数定义时所在的作用域信息,包括以下几个部分: * 作用域标签 * 作用域名称 * 重名序号 @@ -20,7 +20,7 @@ ```ts <作用域标签1><作用域名称1>[<重名序号>]<作用域标签2><作用域名称2><[重名序号]>...<作用域标签n><作用域名称n>[<重名序号>]<作用域标签n+1> ``` -其中<>仅为便于阅读的分割标识,并不包含在实际的前缀中,[]表示可以为空。仅当出现重名作用域时才需要[<重名序号>],即[<重名序号>]可以为空。最后一个作用域标签是本函数所对应的标签。 +其中<>仅为便于阅读的分割标识,并不包含在实际的前缀中,[]表示可以为空。仅当出现重名作用域时,[<重名序号>]才需要填写,否则可以为空。最后一个作用域标签是本函数所对应的标签。 #### 作用域标签 作用域标签表示作用域的类型。作用域和对应的作用域标签如下表所示,其他的作用域不会被记录进函数名中: | 作用域 | 作用域标签 | 说明 | @@ -64,18 +64,18 @@ function foo() {} // 原函数名为"foo" ``` #### 特殊情况 -1. 如果匿名函数定义时被赋值给了一个变量,那么原函数名是变量名。比如下面的例子: +1. 如果匿名函数定义时被赋值给了变量,那么原函数名是变量名。例如下面的示例: ```ts let a = () => {} // 原函数名为"a" ``` -2. 如果匿名函数在对象字面量中定义并且被赋值给了一个字面量属性: +2. 如果匿名函数在对象字面量中定义并被赋值给一个字面量属性: * 如果属性名不包含`\`,`.`,那么它的原函数名则是这个属性名。 ```ts let B = { b : () => {} // 原函数名为"b" } ``` -* 如果属性名包含`\`,`.`,为防止二义性,其原函数名会按照匿名函数命名。 +* 如果属性名包含`\`,`.`,为防止二义性,其原函数名会按匿名函数命名。 ```ts let a = { "a.b#c^2": () => {} // 原函数名为"" diff --git a/zh-cn/application-dev/arkts-utils/arkts-bytecode-fundamentals.md b/zh-cn/application-dev/arkts-utils/arkts-bytecode-fundamentals.md index a7a3a2983ca..c010819661f 100644 --- a/zh-cn/application-dev/arkts-utils/arkts-bytecode-fundamentals.md +++ b/zh-cn/application-dev/arkts-utils/arkts-bytecode-fundamentals.md @@ -2,9 +2,9 @@ ## 总体设计 ### 概述 -方舟字节码(Ark Bytecode),是由方舟编译器编译ArkTS/TS/JS生成的,提供给方舟运行时解释执行的二进制文件。字节码中的主要内容是方舟字节码指令。
-本文旨在介绍方舟字节码指令相关的设计,将在后续章节中对构成指令的重要概念和具体的指令格式及含义进行说明,帮助开发者了解方舟字节码指令,指导开发者进行指令相关的特性开发工作。
-一条方舟字节码指令,由操作码(指令的名称)和指令入参列表组成。操作码包含无前缀的操作码和有前缀的操作码两种情况。寄存器、立即数以及string id/method id/literal id,均可以作为指令的入参,除此之外,部分指令中使用累加器作为默认参数。
+方舟字节码(ArkCompiler Bytecode),是由方舟编译器编译ArkTS/TS/JS生成的,提供给方舟运行时解释执行的二进制文件。字节码中的主要内容是方舟字节码指令。
+本文介绍方舟字节码指令的设计,后续章节将详细说明构成指令的重要概念、具体格式及含义,帮助开发者了解方舟字节码指令,指导开发者进行指令相关特性开发。
+方舟字节码指令,由操作码(指令的名称)和指令入参列表组成。操作码分为无前缀的操作码和有前缀的操作码两种。寄存器、立即数以及string id/method id/literal id均可以作为指令的入参,除此之外,部分指令中使用累加器作为默认参数。
方舟字节码中,除寄存器和累加器之外,还存在**全局变量**、**模块([module](https://262.ecma-international.org/12.0/#sec-ecmascript-language-scripts-and-modules))命名空间和模块变量**、**词法环境和词法变量**、**补丁变量**4种值存储方式。指令可以使用这4种储值位置中的值作为入参。 ### 术语和约束 @@ -25,12 +25,12 @@ #### 约束 * 本文中所有采用代码形式描述的内容均遵循[ArkTS语言规范](../quick-start/introduction-to-arkts.md)。 -* 本文仅适用于版本号为12.0.6.0的方舟字节码(版本号为方舟编译器内部保留字段,开发者无需关注)。 +* 本文仅适用于版本号为12.0.6.0的方舟字节码。 ### 字节码构成 #### 操作码与前缀 -方舟字节码中的操作码通常被编码为一个8位的值,因此至多只能有256个操作码。随着方舟编译器运行时功能的演进,字节码的数量也在逐步增加,已经超过了256个。因此,方舟字节码引入了前缀(prefix),将操作码最大宽度从8位扩展到16位。8位操作码(无前缀的)用于表示频繁出现的指令,16位操作码(有前缀的)用于表示出现频率不高的指令。
-带前缀的操作码为小端法存储的16位值,由8位操作码和8位前缀组成,编码规则为:操作码左移8位,再与前缀相或。 +方舟字节码的操作码通常编码为8位,最多256个。随着方舟编译器运行时功能的演进,字节码的数量已经超过了256个。因此,方舟字节码引入了前缀(prefix),将操作码最大宽度从8位扩展到16位。8位操作码(无前缀的)用于表示频繁出现的指令,16位操作码(有前缀的)用于表示出现频率不高的指令。
+带前缀的操作码是小端法存储的16位值,由8位操作码和8位前缀组成,编码规则为:操作码左移8位,再与前缀进行或运算。 | 前缀操作码 | 助记符 | 描述 | | ---------- | ---------- | ---------- | | 0xfe | throw | 有条件/无条件的throw指令。 | @@ -38,10 +38,10 @@ | 0xfc | deprecated | 方舟编译器不再会产生的指令,仅用于维护运行时兼容性;
本文后续章节中将省略对这些指令的说明。 | | 0xfb | callruntime | 调用运行时方法的指令。 | -前缀操作码的助记符的形式为**前缀助记符.操作码助记符**,例如,wide.stlexvar。stlexvar指令的操作码是0x0d,前缀wide是0xfd,则此带前缀的指令(wide.stlexvar)的操作码是0x0dfd。 +前缀操作码的助记符的形式为**前缀助记符.操作码助记符**,例如,wide.stlexvar。stlexvar指令的操作码是0x0d,前缀wide是0xfd,因此带前缀的指令(wide.stlexvar)的操作码是0x0dfd。 #### 寄存器与累加器 -方舟虚拟机模型基于寄存器,所有的寄存器均是虚拟寄存器。当寄存器中存放原始类型的值时,寄存器的宽度是64位;当寄存器中存放对象类型的值时,寄存器的宽度适应为足够宽,以存放对该对象的引用。
+方舟虚拟机模型基于寄存器,所有寄存器均为虚拟寄存器。当寄存器中存放原始类型的值时,寄存器的宽度是64位;当寄存器中存放对象类型的值时,宽度自适应足够存放对象引用。
方舟字节码中,存在一个名为累加器(accumulator,也简称作acc)的不可见寄存器。acc是许多指令的默认目标寄存器,也是许多指令的默认参数。acc不占用编码宽度,有助于产生更为紧凑的字节码。
示例代码: @@ -64,11 +64,11 @@ function foo(): number { 方舟字节码中部分指令采用常数形式来表示整型数值、双精度浮点型数值、跳转偏移量等数据。这类常数被称为立即数,可以是8位、16位、32位或64位。 #### 方法索引、字符串索引、字面量索引 -方舟字节码中存放着源文件中使用到的所有方法、字符串和字面量数组的偏移量。其中,字面量数组中存放着各种字面量数据,例如整型数字、字符串偏移量和方法偏移量。在方舟字节码指令中,这些方法、字符串以及字面量数组的索引都是16位的,分别被称作方法索引(method id)、字符串索引(string id)以及字面量索引(literal id)。这些索引被编码在指令中,以引用方法、字符串和字面量数组。 +方舟字节码中存放着源文件中使用到的所有方法、字符串和字面量数组的偏移量。字面量数组中包含各种字面量数据,例如整型数字、字符串偏移量和方法偏移量。在方舟字节码指令中,方法、字符串以及字面量数组的索引都是16位的,分别称为方法索引(method id)、字符串索引(string id)以及字面量索引(literal id)。这些索引被编码在指令中,用于引用方法、字符串和字面量数组。 ### 值存储方式 #### 全局变量 -在[Script](https://262.ecma-international.org/12.0/#sec-ecmascript-language-scripts-and-modules)编译模式下,全局变量是一个存储在全局唯一的映射中的变量,其键值为全局变量的名称,值为全局变量的值。全局变量可通过全局(global)相关的指令进行访问。
+在[Script](https://262.ecma-international.org/12.0/#sec-ecmascript-language-scripts-and-modules)编译模式下,全局变量存储在唯一的全局映射中,其键值为全局变量的名称,值为全局变量的值。全局变量可通过全局(global)相关的指令进行访问。
示例代码: ```ts @@ -101,8 +101,8 @@ function foo(): void { #### 模块命名空间和模块变量 源文件中所有使用到的[模块命名空间](https://262.ecma-international.org/12.0/#module-namespace-exotic-object)(module namespace)都会被编译进一个数组中,指令中使用索引来引用一个模块命名空间。例如,指令*getmodulenamespace 0x1*引用了索引*0x1*处的模块命名空间。
源文件中所有使用到的模块变量(module variable)都会被编译进一个数组中,指令中使用索引来引用一个模块变量。例如,指令*stmodulevar 0x1*引用了索引*0x1*处的模块变量。
-在函数中,如果一个模块变量的声明和这个函数在同一个源文件中,则将这个变量称为局部模块变量;否则称为外部模块变量。例如,指令*ldlocalmodulevar*和*ldexternalmodulevar*分别用于加载局部模块变量和外部模块变量。
-产生模块指令的相关场景,包括[import](https://262.ecma-international.org/12.0/#sec-imports)和[export](https://262.ecma-international.org/12.0/#sec-exports),主要场景列举如下: +在函数中,如果模块变量的声明和函数在同一个源文件中,则该变量称为局部模块变量;否则称为外部模块变量。例如,指令*ldlocalmodulevar*和*ldexternalmodulevar*分别用于加载局部模块变量和外部模块变量。
+产生模块指令的相关场景,包括[import](https://262.ecma-international.org/12.0/#sec-imports)和[export](https://262.ecma-international.org/12.0/#sec-exports),主要场景如下: * ```import * as```:module namespace * ```import { }```:module variable * ```export```:local export @@ -149,7 +149,7 @@ a + b + d; 指令*ldlocalmodulevar 0x0*:加载当前局部模块的0号槽位上的值(d),存放到acc中。 #### 词法环境和词法变量 -方舟字节码中,词法环境(lexical environment)可以看作是一个具有多个槽位的数组,每个槽位对应一个词法变量(lexical variable),一个方法中可能会存在多个词法环境。指令中使用词法环境的相对层级编号和槽位索引,来表示一个词法变量。例如,指令*ldlexvar 0x1, 0x2*的含义是:将1个层次外的词法环境的2号槽位上的值存放到acc中。 +方舟字节码中,词法环境(lexical environment)可以看作是一个具有多个槽位的数组,每个槽位对应一个词法变量(lexical variable),一个方法中可能存在多个词法环境。指令中使用词法环境的相对层级编号和槽位索引,来表示一个词法变量。例如,指令*ldlexvar 0x1, 0x2*的含义是:将1个层次外的词法环境的2号槽位上的值存放到acc中。 ``` |xxx|xxx|xxx|xxx| <-- 当前词法环境外的第1个词法环境 ^ @@ -158,7 +158,7 @@ a + b + d; |xxx|xxx|xxx|xxx| <-- 当前词法环境 ``` **注意:**
-lexical相关的逻辑是编译器的内部实现。随着方舟编译器的后续演进,可能会出现新的涉及lexical指令的场景;另一方面,现有的lexical指令的相关场景,也可能会随着需求演进和代码重构,不再涉及产生lexical相关指令。 +lexical相关的逻辑是编译器的内部实现。随着方舟编译器的演进,可能会出现新的涉及lexical指令的场景;另外,现有的lexical指令的相关场景,也可能因需求演进和代码重构,不再涉及产生lexical相关指令。 示例代码: ```ts function foo(): void { @@ -192,7 +192,7 @@ function foo(): void { 指令*ldlexvar 0x0, 0x0*:将0个层次外的词法环境的0号槽位上的值存放到acc中。 #### 共享词法环境 -共享词法环境是一类特殊的词法环境。与一般词法环境的区别在于,共享词法环境中的每个词法变量都是[sendable对象](arkts-sendable.md)。方舟编译器通过共享词法环境实现词法变量在多线程的共享。 +共享词法环境是一类特殊的词法环境。区别于一般词法环境,共享词法环境中的每个词法变量都是[sendable对象](arkts-sendable.md)。方舟编译器通过共享词法环境实现词法变量在多线程的共享。 示例代码: ```ets @@ -232,7 +232,7 @@ label_2: 指令*callruntime.ldsendablevar 0x0, 0x0*:将0个层次外的共享词法环境的0号槽位上的值存放到acc中。 #### 补丁变量 -方舟编译器支持补丁模式的编译,当源文件发生修改时,经过补丁模式编译,生成一个补丁字节码,配合原字节码,完成功能的更新。方舟编译器在补丁模式下编译时,产生的补丁变量会被存放在一个特殊的补丁词法环境中。方舟字节码中使用补丁词法环境上的槽位编号来引用补丁变量。例如,指令*ldpatchvar 0x1*加载的是槽位号为1的补丁变量。
+方舟编译器支持补丁模式的编译,当源文件发生修改时,补丁模式编译生成补丁字节码,配合原字节码,完成功能的更新。方舟编译器在补丁模式下编译时,产生的补丁变量会被存放在特殊的补丁词法环境中。方舟字节码通过补丁词法环境的槽位编号引用补丁变量。例如,指令*ldpatchvar 0x1*加载的是槽位号为1的补丁变量。
示例代码: ```ts -- Gitee