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