• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 方舟字节码函数命名规则
2
3<!--Kit: ArkTS-->
4<!--Subsystem: ArkCompiler-->
5<!--Owner: @huyunhui1; @oh-rgx1; @zmw1-->
6<!--Designer: @ctw-ian; @hufeng20-->
7<!--Tester: @kirl75; @zsw_zhushiwei-->
8<!--Adviser: @foryourself-->
9
10## 概述
11本文介绍字节码文件中[Method](arkts-bytecode-file-format.md#method)的`name_off`字段指向的字符串的命名规则,该命名规则从方舟字节码文件版本`12.0.4.0`开始生效。
12## 入口函数
13模块加载时被执行的函数,名称固定为`func_main_0`。
14## 非入口函数
15其他函数在字节码文件中的名称结构如下:
16```ts
17#前缀#原函数名
18```
19下面的章节将会详细介绍前缀和原函数名。
20### 前缀
21前缀包含函数定义时所在的作用域信息,具体包括以下几个部分:
22* 作用域标签
23* 作用域名称
24* 重名序号
25
26前缀的结构为:
27```ts
28<作用域标签1><作用域名称1>[<重名序号>]<作用域标签2><作用域名称2><[重名序号]>...<作用域标签n><作用域名称n>[<重名序号>]<作用域标签n+1>
29```
30其中<>仅为便于阅读的分割标识,并不包含在实际的前缀中,[]表示可以为空。仅当出现重名作用域时才需要[<重名序号>],即[<重名序号>]可以为空。最后一个作用域标签是本函数所对应的标签。
31
32**作用域标签**
33
34作用域标签表示作用域的类型。作用域和对应的作用域标签如下表所示,其他的作用域不会被记录进函数名中:
35| 作用域 | 作用域标签 | 说明 |
36| --- | --- | --- |
37| class | `~` | `class`关键字定义的作用域。 |
38| 实例函数 | `>` | 类的实例成员函数定义的作用域。 |
39| 静态函数 | `<` | 类的静态成员函数定义的作用域。 |
40| 构造函数 | `=` | 类的构造函数定义的作用域。 |
41| 普通函数 | `*` | 除了以上类型的其它所有函数定义的作用域。 |
42| namespace/module | `&` | `namespace`或`module`关键字定义的作用域。 |
43| enum | `%` | `enum`关键字定义的作用域。 |
44
45**作用域名称**
46
47源代码中定义作用域时所使用的名称。匿名则为空字符串。为了降低字节码体积,方舟编译器会对较长的作用域名称进行优化,此时作用域名称以`@十六进制数字`的形式体现。这个数字代表作用域名称的字符串在一个字符串数组中的索引:在字节码文件中源代码对应的[Class](arkts-bytecode-file-format.md#class)中有一个名为`scopeNames`的[field](arkts-bytecode-file-format.md#field), 这个field的值是指向一个[LiteralArray](arkts-bytecode-file-format.md#literalarray)的偏移,这个LiteralArray存储的是一个字符串数组。十六进制数字就是代表作用域名称在这个数组中的索引。原函数名不会转换为索引。
48例子:
49```ts
50// test1.ts
51function longFuncName() {                  // longFuncName的函数名为"#*#longFuncName",其中"longFuncName"是原函数名,不会转换为索引。
52    function A() { }                       // A的函数名"#*@0*#A",其中"@0"表示在其对应LiteralArray中,索引为0的字符串,此时这个字符串是"longFuncName"。即这个函数原本的名称为"#*longFuncName*#A"
53    function B() { }                       // B的函数名"#*@0*#B"
54}
55```
56
57**重名序号**
58
59如果源码中相同作用域下出现了同名的实体,同名的名称后会加上重名序号,重名序号以`^十六进制数字`的形式表示。出现重名时,第一个不编号(即重名序号为空),从第二个开始编号,编号从`1`开始。
60
61例子:
62```ts
63namespace A {
64    function bar() { }                      // bar的函数名为"#&A*#bar"
65}
66
67namespace A {
68    function foo() { }                      // foo的函数名为"#&A^1*#foo",其中"^1" 为重名序号
69}
70```
71### 原函数名
72原函数名代表函数在源代码中的名字,匿名函数的名称为空字符串。同样的,如果源码中相同作用域下出现了同名的函数,重名的名称后面会加上重名序号(包括匿名函数)。
73
74```ts
75function foo() {};                           // 原函数名为"foo"
76() => { };                                   // 原函数名为""
77() => { };                                   // 原函数名为"^1"
78```
79
80
81**特殊情况**
82
831. 如果匿名函数被赋值给一个变量,该变量名即为函数名。例如:
84    ```ts
85    let a = () => {}                            // 原函数名为"a"
86    ```
872. 如果匿名函数在对象字面量中定义并且被赋值给了一个字面量属性:
88* 如果属性名中不包含`\`和`.`,则其原函数名即为该属性名。
89    ```ts
90    // test2.ts
91    let B = {
92        b : () => {}                            // 原函数名为"b"
93    }
94    ```
95* 如果属性名包含`\`,`.`,为防止二义性,其原函数名会按照匿名函数命名。
96    ```ts
97    // test3.ts
98    let a = {
99        "a.b#c^2": () => {},                     // 原函数名为""
100        "x\\y#": () => {}                       // 原函数名为"^1"
101    }
102    ```
103
104**开发者应尽量避免使用除字母、数字、下划线以外的字符命名函数,以免出现二义性。**
105## 示例
106```ts
107namespace A {                               // namespace在字节码中的函数名为"#&#A"
108    class B {                               // 构造函数在字节码中的函数名为"#&A~B=#B"
109        m() {                               // 函数m在字节码中的函数名为"#&A~B>#m"
110            return () => {}                 // 匿名函数在字节码中的函数名为"#&A~B>m*#"
111        }
112        static s() {}                       // 静态函数s在字节码中的函数名为"#&A~B<#s"
113    }
114    enum E {                                // enum在字节码中的函数名为"#&A%#E"
115
116    }
117}
118```