Swift 的 ABI

随着Swift的开源,苹果开发者迎来了新的开端。其实这个是我一直想要表达的,随着对Swift这个语言的深入了解,越发感觉到Swift的魅力。Swift是系统编程语言,现在C语言之所以能统治底层开发的最重要原因是C规范统一的ABI,使其能做到与汇编程序无缝衔接。而Swift的ABI兼容C(并不包括name mangling部分)。基于强大的llvm生成具体平台代码。不仅仅作为Objective-C的翻译。

通过开发Swift,了解到Swift文件的中间编译结果(介于Swift代码合llvm ir)是SIL。SIL我下一章着重介绍。这张暂时把焦点放在Swift的ABI。那么什么是ABI,你一定很陌生吧,那么对于API呢,你是不是明白了什么?

ABI的全称是Application Binary Interface。翻译过来就是应用二进制接口,描述了应用程序和操作系统或其他应用程序之间的低级接口。

ABI涵盖了各个细节:

  • 数据类型的大小、设计和对齐;
  • 调用约定(控制着函数的参数如何传送以及如何接受返回值),例如,是所有的参数都通过栈传递,还是部分参数通过寄存器传递;哪个寄存器用于哪个函数参数;通过栈传递的第一个函数参数是最先push到栈上还是最后
  • 系统调用的编码和一个应用如何向操作系统进行系统调用
  • 以及在一个完整的操作系统ABI中,目标文件的二进制格式、程序库等等。
一个完整的ABI,像Intel二进制兼容标准(iBCS),允许支持它的操作系统上的程序不经修改在其他支持此ABI的操作系统上运行。

ABI不同于应用程序接口(API),API定义了源代码和库之间的接口,因此同样的代码可以在支持这个API的任何系统中编译,然而ABI允许编译好的目标代码在使用兼容ABI的系统中无需改动就能运行。 在Unix风格的操作系统中,存在很多运行在同一硬件平台上互相相关但是不兼容的操作系统(尤其是Intel 80386兼容系统)。有一些努力尝试标准化ABI,以减少销售商将程序移植到其他系统时所需的工作。然而,直到现在还没有很成功的例子,虽然Linux标准化工作组正在为Linux做这方面的努力。

我在上面说了,ABI的各个细节。在这我需要强调一点:在Swift类,如果没有明确标识对象基类则一律继承SwiftObject Objective-C类。这个是Swift的硬性规定。

类型设计

脆弱的结构体和元组设计

结构和元组目前共享相同的设计算法,编译器实现“通用”设计算法。该算法如下:

  • 从0开始,以1对齐增长
  • 遍历字段,在元素的元组,或顺序结构var声明。每个字段:
    • 通过舍入更新字节来对齐字段,字段增长的值至少大于或等于字节,整除的一致性
    • <字段的当前值的大小赋给偏移量/li>
    • 更新字节通过添加字段的字节大小
    • 更新对齐最大字节,和更新对齐的字段
    • 最终的大小和对齐的大小和对齐集合。类型是最后的,步长大小来对齐。
    注意这不同于C或LLVM是正常的设计规则,大小和步幅是不同的,而C设计要求,嵌入式结构的大小是填充其对齐,迅速设计允许外部结构布置内部结构的尾巴填充字段,允许对齐。与C不同,大小结构和元组也允许,没有存储在包含总量。快速编译器发出LLVM包装结构类型与手动填充必要的控制二进制设计。一些例子:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    // LLVM <{ i64, i8 }>
    struct S {
      var x: Int
      var y: UInt8
    }
    
    // LLVM <{ i8, [7 x i8], <{ i64, i8 }>, i8 }>
    struct S2 {
      var x: UInt8
      var s: S
      var y: UInt8
    }
    
    // LLVM <{}>
    struct Empty {}
    
    // LLVM <{ i64, i64 }>
    struct ContainsEmpty {
      var x: Int
      var y: Empty
      var z: Int
    }
    

    类设计

    Swift依赖下面的关于Objective-C运行时的假设,是Objective-C ABI的一部分:

    • 32位平台从未标记指针。ObjC指针类型是nil或一个对象指针。
    • 在x86-64,一个标记指针设置指针的最低位或最高位的指针。因此,这两位都是0当且仅当该值不是一个标记指针。
    • ARM64,标记指针总是设置最高位的指针。
    • 32位平台不会执行任何isa掩饰object_getClass总是相当于*(Class*)object
    • 64位平台上执行isa掩饰只有在运行时才导出的符号uintptr_t objc_debug_isa_class_mask;。如果导出这个符号,object_getClass在一个非标记指针总是相当于(Class)(objc_debug_isa_class_mask & *(uintptr_t*)object)
    • 超类的类对象总是isa字段后立即存储。它的值是nil或一个指向父类的类对象;它从来没有其他设置。
    下面是Swift ABI的一部分:
    • Swift指针不会是标记指针。

    脆弱的Enum设计

    在设计enum类型、ABI试图避免需要额外存储来存储枚举的标签。ABI选择的五个策略基于enum的设计:

    空枚举

    enum在没有case的情况下,枚举是一个空的类型。

    1
    
    enum Empty {} // => empty type
    

    单一类型枚举

    只有一个情况下的枚举,没有必要筛选,和枚举类型相同的设计的情况下的数据类型,可以是空的,如果没有数据类型。

    1
    2
    
    enum EmptyCase { case X }             // => empty type
    enum DataCase { case Y(Int, Double) } // => LLVM <{ i64, double }>
    

    类C枚举

    如果没有一个case有数据类型(一个典型的“c”enum),那么enum是最小整数标记包含的比特数的所有情况。遵循LLVM的数据设计规则整数类型在目标平台上。被分配的case标签的值依次排列声明顺序。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    enum EnumLike2 { // => LLVM i1
      case A         // => i1 0
      case B         // => i1 1
    }
    
    enum EnumLike8 { // => LLVM i3
      case A         // => i3 0
      case B         // => i3 1
      case C         // => i3 2
      case D         // etc.
      case E
      case F
      case G
      case H
    }
    

    辨别值之后,一个用于最后的case成为额外例子的枚举类型。

    单一的对应枚举

    如果枚举数据类型和一个或多个单情况没有数据的情况下(一个“单一的对应枚举”),然后与数据类型使用的数据类型来表示的二进制表示,必要时添加了零位标记。如果数据类型的二进制表示额外的条件,也就是说,有些模式类型的大小和对齐,但没有形成有效的值的类型,它们是用来表示没有数据的情况下,case的提升数值匹配的顺序没有数据情况下按声明顺序。如果类型有空闲位(见多个对应枚举),它们被用来形成额外的条件。然后枚举值表示为一个整数的存储大小的数据类型。额外的条件使用的负载类型不是枚举类型成为额外的条件enum类型本身。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    enum CharOrSectionMarker { => LLVM i32
      case Paragraph            => i32 0x0020_0000
      case Char(UnicodeScalar)  => i32 (zext i21 %Char to i32)
      case Chapter              => i32 0x0020_0001
    }
    
    CharOrSectionMarker.Char('\x00') => i32 0x0000_0000
    CharOrSectionMarker.Char('\u10FFFF') => i32 0x0010_FFFF
    
    enum CharOrSectionMarkerOrFootnoteMarker { => LLVM i32
      case CharOrSectionMarker(CharOrSectionMarker) => i32 %CharOrSectionMarker
      case Asterisk                                 => i32 0x0020_0002
      case Dagger                                   => i32 0x0020_0003
      case DoubleDagger                             => i32 0x0020_0004
    }
    

    如果数据类型没有额外的条件,或者没有足够的额外的条件表示没有数据的情况下,然后添加一个标记位enum的表示。没有数据的标记位设置情况下,然后分配数据区域中的值的枚举声明的顺序。
    1
    2
    3
    4
    5
    6
    7
    8
    
    enum IntOrInfinity { => LLVM <{ i64, i1 }>
      case NegInfinity    => <{ i64, i1 }> {    0, 1 }
      case Int(Int)       => <{ i64, i1 }> { %Int, 0 }
      case PosInfinity    => <{ i64, i1 }> {    1, 1 }
    }
    
    IntOrInfinity.Int(    0) => <{ i64, i1 }> {     0, 0 }
    IntOrInfinity.Int(20721) => <{ i64, i1 }> { 20721, 0 }
    

    多个对应枚举

    如果一个枚举数据类型有多个情况,然后一个标签来区分数据类型是必要的。ABI首先会尝试找到共同闲置比特,即二进制数据类型的表示fixed-zero或忽略的有效值的所有数据类型。标签将被分散到这些闲置比特尽可能多。目前只备用的原始的整数类型,如高位被认为是i21类型。枚举数据表示为一个整数的存储大小的最大数据类型。

    1
    2
    3
    4
    5
    6
    7
    8
    
    enum TerminalChar {             => LLVM i32
      case Plain(UnicodeScalar)     => i32     (zext i21 %Plain     to i32)
      case Bold(UnicodeScalar)      => i32 (or (zext i21 %Bold      to i32), 0x0020_0000)
      case Underline(UnicodeScalar) => i32 (or (zext i21 %Underline to i32), 0x0040_0000)
      case Blink(UnicodeScalar)     => i32 (or (zext i21 %Blink     to i32), 0x0060_0000)
      case Empty                    => i32 0x0080_0000
      case Cursor                   => i32 0x0080_0001
    }
    

    如果没有足够的空闲位包含标签,然后添加额外的比特表示包含标签。标签的值分配给数据情况下按声明顺序。如果有任何数据的情况下,他们被收集在一个共同的标签,并指定数据区域中的值的枚举声明的顺序。
    1
    2
    3
    4
    5
    6
    7
    
    class Bignum {}
    
    enum IntDoubleOrBignum { => LLVM <{ i64, i2 }>
      case Int(Int)           => <{ i64, i2 }> {           %Int,            0 }
      case Double(Double)     => <{ i64, i2 }> { (bitcast  %Double to i64), 1 }
      case Bignum(Bignum)     => <{ i64, i2 }> { (ptrtoint %Bignum to i64), 2 }
    }
    

    存在容器的设计

    值的协议类型,协议组成类型,或“any”类型(protocol<>)提出使用存在容器(所谓的因为这些类型是类型理论中的“生存类型”)。

    存在的不透明容器

    如果没有类限制协议或协议组成类型,存在的容器必须适应值为任意大小和对齐。它使用一个固定大小的缓冲区,这是三个指针大小和pointer-aligned。这个直接包含的值,如果它的大小和对齐都小于或等于固定大小的缓冲区,或包含一个指针指向一个分配属于存在的容器。所包含的值的类型是由其类型标识元数据记录,为所有需要的协议和映射表的一致性。设计好像声明在C结构体如下:

    1
    2
    3
    4
    5
    
    struct OpaqueExistentialContainer {
      void *fixedSizeBuffer[3];
      Metadata *type;
      WitnessTable *witnessTables[NUM_WITNESS_TABLES];
    };
    

    存在的类的容器

    如果一个或多个协议的协议或协议组成类型有一个类约束,然后只能存储在已经存在的容器,并使用一个更有效的表示。类实例总是一个指针的大小,所以不需要分配一个固定大小的缓冲区,和类实例总是有自己的引用类型的元数据,因此不需要单独的元数据记录。声明中的设计就好像下面C结构体:

    1
    2
    3
    4
    
    struct ClassExistentialContainer {
      HeapObject *value;
      WitnessTable *witnessTables[NUM_WITNESS_TABLES];
    };
    

    注意,如果不需要映射表,如“任何类”类型的protocol<>或一个Objective-C协议类型,然后唯一元素设计堆对象的指针。这是ABI兼容的id,Protocol类型在Objective-C中。

    元数据类型

    快速运行时保持元数据记录为每一类型在程序中使用,包括每一个实例化泛型类型的。可以使用这些元数据记录(TODO:映射)调试器工具发现的信息类型。对于非泛型名义类型,这些元数据是由编译器生成的静态记录。对泛型类型的实例,和内在类型如元组、功能、协议等等组成,元数据记录懒洋洋地根据需要运行时创建的。每个类型都有一个独特的元数据记录,两种元数据指针值相等,如果他们的类型相同。
    在下面的设计描述中,抵消了相对于元数据指针作为指针数组的索引。在32位平台上,抵消1意味着一个偏移量的4个字节,在64位平台上,这意味着8个字节的偏移量。

    通用元数据设计

    所有元数据记录共享一个共同的数据头,下面字段:

    • 映射价值表指针引用的vtable函数实现值的语义类型,提供了基本的操作,如分配,复制,销毁的类型。映射表也记录的值大小、对齐、步长等基本属性的类型。映射价值表指针偏移-1元数据的指针,即指针字节所在位置立即在指针的引用地址偏移。
    • 类型字段是一个pointer-sized整数描述元数据描述。这个字段的偏移量为0的元数据的指针。

    结构体元数据

    除了常见的元数据字段设计、结构元数据记录包含以下字段:

    • 名义类型描述符引用偏移量1。
    • 引用父元数据记录存储在偏移2。是一个封闭的名义类型的成员的结构,这是一个封闭类型的元数据的引用。对于高层结构体,这是null。(父指针总是空)
    • 矢量偏移量从3开始。每个字段的结构体,在var声明顺序,字段的偏移字节从一开始的结构是存储为pointer-sized整数倍。
    • 如果结构是通用的,那么通用参数向量始于抵消3 + n,其中n是结构中的字段的数量

    枚举元数据

    除了常见的元数据字段设计,enum记录元数据包含以下字段:

    • 名义类型描述符引用偏移量1。
    • 引用父元数据记录存储在偏移2。是一个封闭的名义类型的成员的结构,这是一个封闭类型的元数据的引用。对于高层结构体,这是null。(父指针总是空)
    • 如果enum是通用的,那么通用参数向量始于偏移地址3。

    元组元数据

    除了常见的元数据字段设计、元组元数据记录包含以下字段:

    • 名义类型描述符引用偏移量1。
    • 标签字符串指针连续以null结尾的标签名称的列表在元组偏移2。每个标签的名字是作为一个以null结尾,UTF-8编码的字符串序列。如果元组没有标签,这是一个空指针。(目前标签字符串指针总是空,独特的标签不考虑元组元数据)
    • 向量的元素开始的偏移地址3,由一个向量type-offset对。第n个元素的元数据类型是指针的偏移地址3+2*n。字节的偏移量从一开始的第n个元素的元组开始的偏移量是3+2*n+1。

    函数元数据

    除了常见的元数据字段设计,函数元数据记录包含以下字段:

    • 参数的数量函数存储在偏移量为1的地址。
    • 结果的引用类型元数据记录存储在偏移量为2。如果函数有多个返回,这引用一个元组元数据记录
    • 参数向量始于偏移地址3,由指针指向的函数的参数元数据记录。

    如果函数需要任何in/out参数,每个参数的元数据记录指针将另外附加,一些被设置如果in/out最低位。由于指针对准,最低位持有这个标签总是空。 如果函数没有in/out参数,将只有一个指针在下列情形的向量:

    • 0参数:一个元组元数据记录为空元组
    • 1参数:第一个也是唯一一个参数的元数据记录
    • > 1的参数:tuple包含参数的元数据记录

    协议元数据

    除了常见的元数据字段设计,协议元数据记录包含以下字段:

    • 设计标志词是储存在偏移为1。这个词的部分描述存在容器设计用于表示值的类型。这个词设计如下:
      • 映射表存储在最低位31位。协议类型的值包含这个数字映射表指针的设计。
      • 类约束条件是储存在31位。这个位设置如果不类容器类型,这意味着结构、枚举、或类值中可以存储类型。如果没有设置,那么只能存储在类值类型,并使用一个更高效的设计类型。
      注意字段pointer-sized,尽管只有最低的32位目前在所有平台上。这些值可以来自协议描述符记录,但预计算会更便利。
    • 协议的协议组成的数量是储存在偏移2。“任何”protocol<> 。对于单一协议 P。协议构成类型protocol<P, Q, ...>,这是协议的数量
    • 协议描述符向量始于偏移地址3。这是内联的协议描述符的指针数组中的每个协议组成,或单一协议为协议类型描述符。“任何”类型,没有协议描述符向量。

    变型的元数据

    除了常见的元数据字段设计、变型的元数据记录包含以下字段:

    • 引用的实例类型的元数据记录变型代表是储存在偏移为1的地址。

    类元数据

    类元数据设计和Objective-C互操作;所有类元数据记录也有效的Objective-C类对象。类元数据指针的值被用作类变型,所以派生类的元数据记录也作为一个有效的为其父类的所有类变型价值。

    • 析构函数指针存储在从元数据指针偏移2的地址,背后的价值关联表。此功能由Swift的析构器调用时的类实例被销毁
      • isa指针指向Objective-C的兼容元类记录村春在偏移量是0的地址
      • 父类指针指向元类数据记录的父类存储在偏移量1的地址,如果是根类,它为null
      • 偏移2和偏移3保留用于Objective-C的运行时使用
      • 该rodata指针存储在偏移4;它指向的类的Objective-C兼容rodata记录。该指针值包括标签。低位始终设置为1,Swift类和始终设置为0的Objective-C类。
      • 类标志是一个32位字段偏移量5
      • 实例地址指针是一个32位的字段后面的这个类的标志。此类指针实例的开始之后此字节数的实例指针。
      • 实例大小是32位的字段后面的这个实例地址指针。这是存储本字节在这种类型的每一个对象的数
      • 实例对准mask是一个16位的字段后面的这个实例的大小。这是一组低位,它们不能在一指针被设置为这个类的一个实例的。
      • 运行时保留字段是一个16位的字段后面的这个实例对齐mask。编译器初始化这个为零。
      • 类对象的大小是32位的字段,运行时保留字段之后。这是存储在类元数据对象的字节的总数。
      • 对象的地址指针是一个32位的字段后面的这个类的对象的大小。这是存储在类元数据对象的字节数。
      • 名义类型描述为最派生类的类型被引用以立即偏移后的类对象的地址点。这是在64位的平台偏移8或在32位平台偏移11。
      • 对于每一个Swift类在类的继承层次结构,从根类订单开始一直到最派生类,以下领域都存在:
      • 首先,参考到母体元数据记录被存储。对于类是一个封闭的名义类型的成员,这是一个引用封闭类型的元数据。对于顶级类,这是空。
      • 如果类是通用的,它的泛型参数向量的内联存储
      • V表被内嵌存储,并包含一个函数指针类的声明顺序的每一个方法的实现。
      • 如果一个类的实例的布局是依赖于它的通用参数,则字段偏置矢量存储直列,含有以字节偏移从实例指针类的每个字段中声明的顺序。 (对于类与固定布局,字段偏移都可以访问静态的全局变量,类似于Objective-C的ivar偏移。)

      请注意,这些字段都存在在继承层次的Objective-C基类。

      通用参数向量

      元数据记录泛型类型的实例包含有关其泛型参数信息。对于类型的每个参数,引用为类型参数的元数据记录存储。在所有类型参数的元数据参考,对于每种类型的参数,如果有该类​​型参数的协议要求,引用的证人表中为每个协议,要求符合储存在声明顺序。
      例如,给定一个泛型类型,在继承的参数<T,U,V>,它的泛型参数的记录将包括引用的元数据记录,T,U型和V,仿佛在C结构布局:

      1
      2
      3
      
      struct GenericParameterVector {
        TypeMetadata *T, *U, *V;
      };
      
      如果再加协议要求的参数,例如, <T: Runcible, U: protocol<Fungible, Ansible>, V> ,那么该类型的泛型参数向量包含这些协议关联表,设计:
      1
      2
      3
      4
      5
      6
      
      struct GenericParameterVector {
        TypeMetadata *T, *U, *V;
        RuncibleWitnessTable *T_Runcible;
        FungibleWitnessTable *U_Fungible;
        AnsibleWitnessTable *U_Ansible;
      };
      

      通用类型描述符

      元数据记录类,结构和枚举类型包含一个指向类型描述符,其中包含有关名义类型的基本信息,如名称,成员和元数据的布局。对于泛型类型,一是名义类型描述符是该类型的所有实例共享。布局如下:

      • 偏移0的存储类型
        • 若为0则是类
        • 结构体为1
        • 枚举是2
      • 错位的名称被引用为空值终止的C字符串量。本名称不包括绑定泛型参数。
      • 以下四个字段依赖于一种通用类型。
        • 对一个类或者结构体来说:
          • 字段的数量被存储在偏移2。这是在元数据记录中的字段偏置向量的长度,如果有的话。
          • 的偏移到字段偏移矢量被存储在偏移3.这是中的偏移在元数据记录中的类型字段偏置向量的指针大小词语。如果没有字段偏置矢量被存储在元数据记录,这是零。
          • 字段名称偏移量4名的顺序对应的字段的领域偏移向量的顺序被引用为C字符串的双空结尾的列表。
          • 该字段类型的访问是一个函数指针偏移5.如果非空,该函数需要一个指针类型的元数据标称类型的实例,并返回一个指针类型的元数据引用的字段类型的数组该实例。的顺序相匹配的领域偏移向量和字段名列表。
        • 对一个枚举来说:
          • 的有效载荷的情况下和有效载荷大小偏移数被存储在偏移2的至少显著24位是有效载荷的情况下的数量,和在类型元数据的有效载荷大小的最显著8位的偏移,如果有的话。
          • 没有效载荷的情况下的数量被存储在偏移3。
          • case名称偏移量4名进行排序,从而有效载荷的情况下是第一位的,其次是无负载情况下被引用为C字符串的双空结尾的列表。在列表中的每个一半,名称的顺序对应于枚举声明的情况下的顺序。
          • case类型的访问是一个函数指针偏移5.如果非空,该函数需要一个指针类型的元数据枚举的一个实例,并返回一个指针类型的元数据的引用为案件的类型数组该实例。的顺序相匹配的情况下,名单中。这个功能类似于字段类型存取器为一个结构,除了还结果中的每个元件的至少显著位如果枚举的情况下是一种间接的情况下设置的。
      • 如果名义类型是通用的,一个指针,在用于形成该类型的实例的元图形存储在偏移6.指针是空的,如果类型是不通用的。
      • 泛型参数描述开始于偏移7。这说明在元数据记录中的泛型参数向量的布局:
        • 通用参数向量的偏移存储在偏移7.这是中的偏移元数据记录内的通用参数向量的指针大小词语。如果该类型不是通用的,这是零。
        • 类型参数的数量存储在偏移8.这个计数包括相关类型的类型参数与协议约束。
        • 类型参数的数目被储存在仅偏移9.本计数包括初级形式类型参数。
        • 对于每个类型的参数n,以下字段中存储:
          • 该类型参数的数目存储在偏移10 + N。这是存储用于在通用参数向量类型参数表指针的数目。
      请注意,没有名义类型说明符的协议或协议类型。请参阅下面的协议描述符描述。

      Protocol描述符

      协议元数据包含描述所需的类型的协议值以符合引用零个,一个或多个协议的描述符。本协议描述的布局要与Objective-C的协议对象是兼容的。设计如下:
      • 一个isa占位符存放在偏移0填充这个字段的Objective-C运行。
      • 错位的名称被引用为空值终止的C字符串偏移量1。
      • 如果协议继承的一个或多个其他协议,一个指针到继承的协议列表存储在偏移2.名单开始的继承协议作为一个指针大小整数的数目,和后跟许多协议描述符指针。如果协议继承了没有其他协议,这个指针为空。
      • 对于ObjC兼容协议,它需要的实例方法存储在偏移3作为ObjC兼容的方法列表。这是原生Swift协议无效。
      • 对于ObjC兼容协议,它需要的类方法都存储在偏移4作为ObjC兼容的方法列表。这是原生Swift协议无效。
      • 对于ObjC兼容协议,其可选实例方法存储在偏移5作为ObjC兼容的方法列表。这是原生Swift协议无效。
      • 对于ObjC兼容协议,其可选类的方法存储在偏移6作为ObjC兼容的方法列表。这是原生Swift协议无效。
      • 对于ObjC兼容协议,它的实例属性存储在偏移7作为ObjC兼容属性列表。这是原生Swift协议无效。
      • 该协议描述符记录的大小存储为一个32位整数偏移量8。这是目前72在64位的平台和40在32位的平台。
      • 标志被存储为尺寸后的32位整数。以下位目前使用的(从最低计数显著零位):
        • 位0是Swift位。坐落在Swift定义和取消的Objective-C中定义的协议所有协议。
        • 第1位是类约束一下。如果该协议不是类的限制,这意味着任何结构,枚举,或类类型可能符合协议设置它。它没有设置,如果只有类能遵守协议。 (该倒的意思是使用Objective-C协议的记录,在该位从未设置的兼容性。Objective-C的协议只能由类效法。)
        • 第2位是映射表位。它设置,如果派遣协议的方法是通过映射表,可以是传递一个额外的参数,以通用的功能或包含在协议类型的存在容器设计完成。这是未设置如果调度是通过objc_msgSend完成,不需要额外的信息,陪符合类型的值。
        • 第31位是由Objective-C运行时设置它已经完成了它的初始化协议纪录。它是未使用的Swift运行。

      重整

      1
      
      mangled-name ::= '_T' global
      
      所有Swift-重整的名称以此前缀开头。

      全局

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      
      global ::= 't' type                    // standalone type (for DWARF)
      global ::= 'M' type                    // type metadata (address point)
                                             // -- type starts with [BCOSTV]
      global ::= 'Mf' type                   // 'full' type metadata (start of object)
      global ::= 'MP' type                   // type metadata pattern
      global ::= 'Ma' type                   // type metadata access function
      global ::= 'ML' type                   // type metadata lazy cache variable
      global ::= 'Mm' type                   // class metaclass
      global ::= 'Mn' nominal-type           // nominal type descriptor
      global ::= 'Mp' protocol               // protocol descriptor
      global ::= 'PA' .*                     // partial application forwarder
      global ::= 'PAo' .*                    // ObjC partial application forwarder
      global ::= 'w' value-witness-kind type // value witness
      global ::= 'WV' type                   // value witness table
      global ::= 'Wo' entity                 // witness table offset
      global ::= 'Wv' directness entity      // field offset
      global ::= 'WP' protocol-conformance   // protocol witness table
      global ::= 'Wa' protocol-conformance   // protocol witness table accessor
      global ::= 'Wl' type protocol-conformance // lazy protocol witness table accessor
      global ::= 'WL' protocol-conformance   // lazy protocol witness table cache variable
      global ::= 'WD' protocol-conformance   // dependent proto witness table generator
      global ::= 'Wd' protocol-conformance   // dependent proto witness table template
      global ::= entity                      // some identifiable thing
      global ::= 'TO' global                 // ObjC-as-swift thunk
      global ::= 'To' global                 // swift-as-ObjC thunk
      global ::= 'TD' global                 // dynamic dispatch thunk
      global ::= 'Td' global                 // direct method reference thunk
      global ::= 'TR' reabstract-signature   // reabstraction thunk helper function
      global ::= 'Tr' reabstract-signature   // reabstraction thunk
      
      global ::= 'TS' specializationinfo '_' mangled-name
      specializationinfo ::= 'g' passid (type protocol-conformance* '_')+            // Generic specialization info.
      specializationinfo ::= 'f' passid (funcspecializationarginfo '_')+             // Function signature specialization kind
      passid ::= integer                                                             // The id of the pass that generated this specialization.
      funcsigspecializationarginfo ::= 'cl' closurename type*                        // Closure specialized with closed over types in argument order.
      funcsigspecializationarginfo ::= 'n'                                           // Unmodified argument
      funcsigspecializationarginfo ::= 'cp' funcsigspecializationconstantproppayload // Constant propagated argument
      funcsigspecializationarginfo ::= 'd'                                           // Dead argument
      funcsigspecializationarginfo ::= 'g' 's'?                                      // Owned => Guaranteed and Exploded if 's' present.
      funcsigspecializationarginfo ::= 's'                                           // Exploded
      funcsigspecializationarginfo ::= 'k'                                           // Exploded
      funcsigspecializationconstantpropinfo ::= 'fr' mangled-name
      funcsigspecializationconstantpropinfo ::= 'g' mangled-name
      funcsigspecializationconstantpropinfo ::= 'i' 64-bit-integer
      funcsigspecializationconstantpropinfo ::= 'fl' float-as-64-bit-integer
      funcsigspecializationconstantpropinfo ::= 'se' stringencoding 'v' md5hash
      
      global ::= 'TV' global                 // vtable override thunk
      global ::= 'TW' protocol-conformance entity
                                             // protocol witness thunk
      entity ::= nominal-type                // named type declaration
      entity ::= static? entity-kind context entity-name
      entity-kind ::= 'F'                    // function (ctor, accessor, etc.)
      entity-kind ::= 'v'                    // variable (let/var)
      entity-kind ::= 'i'                    // subscript ('i'ndex) itself (not the individual accessors)
      entity-kind ::= 'I'                    // initializer
      entity-name ::= decl-name type         // named declaration
      entity-name ::= 'A' index              // default argument generator
      entity-name ::= 'a' addressor-kind decl-name type     // mutable addressor
      entity-name ::= 'C' type               // allocating constructor
      entity-name ::= 'c' type               // non-allocating constructor
      entity-name ::= 'D'                    // deallocating destructor; untyped
      entity-name ::= 'd'                    // non-deallocating destructor; untyped
      entity-name ::= 'g' decl-name type     // getter
      entity-name ::= 'i'                    // non-local variable initializer
      entity-name ::= 'l' addressor-kind decl-name type     // non-mutable addressor
      entity-name ::= 'm' decl-name type     // materializeForSet
      entity-name ::= 's' decl-name type     // setter
      entity-name ::= 'U' index type         // explicit anonymous closure expression
      entity-name ::= 'u' index type         // implicit anonymous closure
      entity-name ::= 'w' decl-name type     // willSet
      entity-name ::= 'W' decl-name type     // didSet
      static ::= 'Z'                         // entity is a static member of a type
      decl-name ::= identifier
      decl-name ::= local-decl-name
      decl-name ::= private-decl-name
      local-decl-name ::= 'L' index identifier  // locally-discriminated declaration
      private-decl-name ::= 'P' identifier identifier  // file-discriminated declaration
      reabstract-signature ::= ('G' generic-signature)? type type
      addressor-kind ::= 'u'                 // unsafe addressor (no owner)
      addressor-kind ::= 'O'                 // owning addressor (non-native owner)
      addressor-kind ::= 'o'                 // owning addressor (native owner)
      addressor-kind ::= 'p'                 // pinning addressor (native owner)
      
      实体开始于entity nominal-type-kind ([COPV]),去取代 ([Ss]) entity-kind ([FIiv])entity-name开头[AaCcDggis]decl-name。一个decl-name开头[LP]或标识符([0-9oX])。 一个上下文开始于任一实体,一个扩展(其开头[EE]),或者一个模块,它可能是一个标识符([0-9oX])或模块的取代([SS])。 全局的修正开始于[MTWw]。 如果部分应用程序转发器是一个静态符号,它的名字将开始与序列_TPA_随后转发的目的地的错位符号名称。 一个普通的专业化的mangling由一个头,指定用于专门的泛型函数的类型和不合格,其次是原来的非专业化的通用符号的全面重整名称。 在一中的第一标识符是表示原始声明来自文件的字符串。应考虑的包围模块内是唯一的。第二标识符是实体的名称。 并非所有的声明标志着民营声明中,将使用压延;如果实体的上下文是足以唯一标识该实体中,简单的标识符形式是优选的。 在的类型始终是非多态<实现了一套功能型>类型。

      直接和间接的符号

      1
      2
      
      directness ::= 'd'                         // direct
      directness ::= 'i'                         // indirect
      
      直接符号解析直接对象的地址。间接符号解析为一个指向对象的地址。他们是不同的manglings做某一类的错误显而易见。 讨论补偿时的术语稍微超载。直接抵消解析为一个变量持有真正的偏移。间接偏移解析为一个变量保持的偏移被应用到输入的元数据,以获得真实的地址偏移。 (偏移量是必需的变量时,被一个弹性的结构,当物体的布局可能依赖于通用的参数中访问谎言的对象,这些偏移量必须保持在元数据中,间接磁场偏移访问泛型类型的字段时,因此,需要地方元数据本身具有未知的布局。)

      声明上下文

      1
      2
      3
      4
      5
      6
      7
      8
      
      context ::= module
      context ::= extension
      context ::= entity
      module ::= substitution                    // other substitution
      module ::= identifier                      // module name
      module ::= known-module                    // abbreviation
      extension ::= 'E' module entity
      extension ::= 'e' module generic-signature entity
      
      这些manglings确定封闭的上下文中,一个实体被声明,如它的封闭模块,功​​能,或名义类型。 一个扩展的mangling时则使用一个实体的声明上下文是一个扩展和被扩展的实体是在不同的模块。在这种情况下,扩展的模块第一错位,随后由实体被扩展。如果延伸和扩展的实体都在同一模块中,普通实体重整是首选。如果扩展受到限制,在延长的限制被损毁,其一般签名。 当重整本地实体的一个构造或析构,该非分配或非重新分配的方式用于内的上下文。

      类型

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      
      type ::= 'Bb'                              // Builtin.BridgeObject
      type ::= 'BB'                              // Builtin.UnsafeValueBuffer
      type ::= 'Bf' natural '_'                  // Builtin.Float<n>
      type ::= 'Bi' natural '_'                  // Builtin.Int<n>
      type ::= 'BO'                              // Builtin.ObjCPointer
      type ::= 'Bo'                              // Builtin.ObjectPointer
      type ::= 'Bp'                              // Builtin.RawPointer
      type ::= 'Bv' natural type                 // Builtin.Vec<n>x<type>
      type ::= 'Bw'                              // Builtin.Word
      type ::= nominal-type
      type ::= associated-type
      type ::= 'a' context identifier            // Type alias (DWARF only)
      type ::= 'b' type type                     // objc block function type
      type ::= 'c' type type                     // C function pointer type
      type ::= 'F' throws-annotation? type type  // function type
      type ::= 'f' throws-annotation? type type  // uncurried function type
      type ::= 'G' type <type>+ '_'              // generic type application
      type ::= 'K' type type                     // @auto_closure function type
      type ::= 'M' type                          // metatype without representation
      type ::= 'XM' metatype-repr type           // metatype with representation
      type ::= 'P' protocol-list '_'             // protocol type
      type ::= 'PM' type                         // existential metatype without representation
      type ::= 'XPM' metatype-repr type          // existential metatype with representation
      type ::= archetype
      type ::= 'R' type                          // inout
      type ::= 'T' tuple-element* '_'            // tuple
      type ::= 't' tuple-element* '_'            // variadic tuple
      type ::= 'Xo' type                         // @unowned type
      type ::= 'Xu' type                         // @unowned(unsafe) type
      type ::= 'Xw' type                         // @weak type
      type ::= 'XF' impl-function-type           // function implementation type
      type ::= 'Xf' type type                    // @thin function type
      nominal-type ::= known-nominal-type
      nominal-type ::= substitution
      nominal-type ::= nominal-type-kind declaration-name
      nominal-type-kind ::= 'C'                  // class
      nominal-type-kind ::= 'O'                  // enum
      nominal-type-kind ::= 'V'                  // struct
      archetype ::= 'Q' index                    // archetype with depth=0, idx=N
      archetype ::= 'Qd' index index             // archetype with depth=M+1, idx=N
      archetype ::= associated-type
      archetype ::= qualified-archetype
      associated-type ::= substitution
      associated-type ::= 'Q' protocol-context     // self type of protocol
      associated-type ::= 'Q' archetype identifier // associated type
      qualified-archetype ::= 'Qq' index context   // archetype+context (DWARF only)
      protocol-context ::= 'P' protocol
      tuple-element ::= identifier? type
      metatype-repr ::= 't'                      // Thin metatype representation
      metatype-repr ::= 'T'                      // Thick metatype representation
      metatype-repr ::= 'o'                      // ObjC metatype representation
      throws-annotation ::= 'z'                  // 'throws' annotation on function types
      
      
      type ::= 'u' generic-signature type        // generic type
      type ::= 'x'                               // generic param, depth=0, idx=0
      type ::= 'q' generic-param-index           // dependent generic parameter
      type ::= 'q' type assoc-type-name          // associated type of non-generic param
      type ::= 'w' generic-param-index assoc-type-name // associated type
      type ::= 'W' generic-param-index assoc-type-name+ '_' // associated type at depth
      
      generic-param-index ::= 'x'                // depth = 0,   idx = 0
      generic-param-index ::= index              // depth = 0,   idx = N+1
      generic-param-index ::= 'd' index index    // depth = M+1, idx = N
      
      从未开始或以数字结束。 从不以下划线开头。 从来没有开始与D。 从来没有开始以Z。 请注意,协议裂伤不同的类型和作为背景。一个协议方面总是由一个单一的协议名称等等轧液没有一个结尾下划线。协议类型可以有并列设置,结束于结尾下划线零个,一个或多个协议的范围。
      1
      2
      
      assoc-type-name ::= ('P' protocol-name)? identifier
      assoc-type-name ::= substitution
      
      相关类型因此使用缩写的mangling当基座通用参数或关联式是由一个单一的规约要求的限制。在这种情况下,相关联的类型可以明确引用的名字单独。如果基有多个符合性限制,那么协议名称是错位的消除歧义。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      
      impl-function-type ::=
        impl-callee-convention impl-function-attribute* generic-signature? '_'
        impl-parameter* '_' impl-result* '_'
      impl-callee-convention ::= 't'              // thin
      impl-callee-convention ::= impl-convention  // thick, callee transferred with given convention
      impl-convention ::= 'a'                     // direct, autoreleased
      impl-convention ::= 'd'                     // direct, no ownership transfer
      impl-convention ::= 'D'                     // direct, no ownership transfer,
                                                  // dependent on 'self' parameter
      impl-convention ::= 'g'                     // direct, guaranteed
      impl-convention ::= 'e'                     // direct, deallocating
      impl-convention ::= 'i'                     // indirect, ownership transfer
      impl-convention ::= 'l'                     // indirect, inout
      impl-convention ::= 'G'                     // indirect, guaranteed
      impl-convention ::= 'o'                     // direct, ownership transfer
      impl-convention ::= 'z' impl-convention     // error result
      impl-function-attribute ::= 'Cb'            // compatible with C block invocation function
      impl-function-attribute ::= 'Cc'            // compatible with C global function
      impl-function-attribute ::= 'Cm'            // compatible with Swift method
      impl-function-attribute ::= 'CO'            // compatible with ObjC method
      impl-function-attribute ::= 'Cw'            // compatible with protocol witness
      impl-function-attribute ::= 'N'             // noreturn
      impl-function-attribute ::= 'G'             // generic
      impl-parameter ::= impl-convention type
      impl-result ::= impl-convention type
      
      在大多数情况下,manglings遵循的正式语言类型的结构。然而,在某些情况下,它是更为有用的函数类型的准确实现细节进行编码。 任何产品必须出现在它们上面指定的顺序:例如:一个不返回的C函数的错位与CCN。 需要注意的是,公约和功能属性的制作并不需要从一个的开始消除歧义。

      泛型

      1
      
      protocol-conformance ::= ('u' generic-signature)? type protocol module
      
      是指一个类型的一致性协议。命名模块包含声明的一致性扩展或类型声明之一。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      
      generic-signature ::= (generic-param-count+)? ('R' requirement*)? 'r'
      generic-param-count ::= 'z'       // zero parameters
      generic-param-count ::= index     // N+1 parameters
      requirement ::= type-param protocol-name // protocol requirement
      requirement ::= type-param type          // base class requirement
                                               // type starts with [CS]
      requirement ::= type-param 'z' type      // 'z'ame-type requirement
      
      // Special type mangling for type params that saves the initial 'q' on
      // generic params
      type-param ::= generic-param-index       // generic parameter
      type-param ::= 'w' generic-param-index assoc-type-name // associated type
      type-param ::= 'W' generic-param-index assoc-type-name+ '_'
      
      一个通用的签名首先描述在每个深度的签名,随后要求的通用参数的数量。作为一个特殊的情况下,没有generic-param-count表示在最外深度单个通用参数:
      1
      2
      
      urFq_q_                           // <T_0_0> T_0_0 -> T_0_0
      u_0_rFq_qd_0_                     // <T_0_0><T_1_0, T_1_1> T_0_0 -> T_1_1
      

      映射值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      
      value-witness-kind ::= 'al'           // allocateBuffer
      value-witness-kind ::= 'ca'           // assignWithCopy
      value-witness-kind ::= 'ta'           // assignWithTake
      value-witness-kind ::= 'de'           // deallocateBuffer
      value-witness-kind ::= 'xx'           // destroy
      value-witness-kind ::= 'XX'           // destroyBuffer
      value-witness-kind ::= 'Xx'           // destroyArray
      value-witness-kind ::= 'CP'           // initializeBufferWithCopyOfBuffer
      value-witness-kind ::= 'Cp'           // initializeBufferWithCopy
      value-witness-kind ::= 'cp'           // initializeWithCopy
      value-witness-kind ::= 'TK'           // initializeBufferWithTakeOfBuffer
      value-witness-kind ::= 'Tk'           // initializeBufferWithTake
      value-witness-kind ::= 'tk'           // initializeWithTake
      value-witness-kind ::= 'pr'           // projectBuffer
      value-witness-kind ::= 'xs'           // storeExtraInhabitant
      value-witness-kind ::= 'xg'           // getExtraInhabitantIndex
      value-witness-kind ::= 'Cc'           // initializeArrayWithCopy
      value-witness-kind ::= 'Tt'           // initializeArrayWithTakeFrontToBack
      value-witness-kind ::= 'tT'           // initializeArrayWithTakeBackToFront
      value-witness-kind ::= 'ug'           // getEnumTag
      value-witness-kind ::= 'up'           // destructiveProjectEnumData
      value-witness-kind ::= 'ui'           // destructiveInjectEnumTag
      <value-witness-kind> differentiates the kinds of value witness functions for a type.
      
      Identifiers
      
      identifier ::= natural identifier-start-char identifier-char*
      identifier ::= 'o' operator-fixity natural operator-char+
      
      operator-fixity ::= 'p'                    // prefix operator
      operator-fixity ::= 'P'                    // postfix operator
      operator-fixity ::= 'i'                    // infix operator
      
      operator-char ::= 'a'                      // & 'and'
      operator-char ::= 'c'                      // @ 'commercial at'
      operator-char ::= 'd'                      // / 'divide'
      operator-char ::= 'e'                      // = 'equals'
      operator-char ::= 'g'                      // > 'greater'
      operator-char ::= 'l'                      // < 'less'
      operator-char ::= 'm'                      // * 'multiply'
      operator-char ::= 'n'                      // ! 'not'
      operator-char ::= 'o'                      // | 'or'
      operator-char ::= 'p'                      // + 'plus'
      operator-char ::= 'q'                      // ? 'question'
      operator-char ::= 'r'                      // % 'remainder'
      operator-char ::= 's'                      // - 'subtract'
      operator-char ::= 't'                      // ~ 'tilde'
      operator-char ::= 'x'                      // ^ 'xor'
      operator-char ::= 'z'                      // . 'zperiod'
      
      是运行长度编码:自然表示的字符如何跟进。操作字符映射到字母字符给出。在两种情况下可以一个标识符以数字开头,所以有与运行长度没有歧义。
      1
      2
      
      identifier ::= 'X' natural identifier-start-char identifier-char*
      identifier ::= 'X' 'o' operator-fixity natural identifier-char*
      
      包含非ASCII字符都使用在RFC 3492中指定Punycode算法编码,与该_用作编码定界符的修改和大写字母A至J标识符被用来代替数字0至9中的编码字符集。是混淆然后由一个X后跟编码串的游程长度和所述编码的字符串本身。例如,标识符vergüenza被错位到X12vergenza_JFa。 (在标准的Punycode编码是vergenza-95A) 包含非ASCII字符,运营商首先映射ASCII运营字符的字母作为纯ASCII运营商名称错位,然后Punycode码编码取代字符串。是混淆然后由XO后跟的固定性,该编码串的运行长度,以及编码的字符串本身。例如,管道符“+”的错位,以Xoi7p_qcaDc(p_qcaDc是的替代字符串“P»编码)。

      代替

      1
      
      substitution ::= 'S' index
      
      是一个向后引用到先前错位实体。该重整算法维护实体的映射来替代指标,因为它运行。时,可以通过取代(的模块,名义类型或协议)来表示一个实体是错位,一个取代是第一所期望的是取代的地图,并且如果存在,该实体使用相关替代索引错位。否则,该实体通常错位,且随后添加到替代地图,并与下一个可利用的取代索引相关联。 例如,在重整函数类型(zim.zang.zung,zim.zang.zung,zim.zippity) - > zim.zang.zoo(与模块ZIM和类zim.zang),重复上下文ZIM,以星。臧和zim.zang.zung将使用替代被错位的第一次后,被截断。第一个参数类型将在长形裂伤,CC3zim4zang4zung,并在这样做,ZIM将获得替代S_,zim.zang将获得替代S0_,并zim.zang.zung将收购S1_。第二个参数是一样的第一和裂伤将利用其替换,CS1_。第三个参数类型将裂伤使用替代ZIM,CS_7zippity。 (它也获得替代S2_这将被使用,如果再次错位。)结果类型将裂伤使用替代zim.zang,CS0_zoo(并获得替代S3_)。全功能型因而轧液作为fTCC3zim4zang4zungCS1_CS_7zippity_CS0_zoo。
      1
      
      substitution ::= 's'
      
      在特殊的替换s被用于Swift标准库模块。

      预定义替换

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      
      known-module ::= 's'                       // Swift
      known-module ::= 'SC'                      // C
      known-module ::= 'So'                      // Objective-C
      known-nominal-type ::= 'Sa'                // Swift.Array
      known-nominal-type ::= 'Sb'                // Swift.Bool
      known-nominal-type ::= 'Sc'                // Swift.UnicodeScalar
      known-nominal-type ::= 'Sd'                // Swift.Float64
      known-nominal-type ::= 'Sf'                // Swift.Float32
      known-nominal-type ::= 'Si'                // Swift.Int
      known-nominal-type ::= 'SP'                // Swift.UnsafePointer
      known-nominal-type ::= 'Sp'                // Swift.UnsafeMutablePointer
      known-nominal-type ::= 'SQ'                // Swift.ImplicitlyUnwrappedOptional
      known-nominal-type ::= 'Sq'                // Swift.Optional
      known-nominal-type ::= 'SR'                // Swift.UnsafeBufferPointer
      known-nominal-type ::= 'Sr'                // Swift.UnsafeMutableBufferPointer
      known-nominal-type ::= 'SS'                // Swift.String
      known-nominal-type ::= 'Su'                // Swift.UInt
      
      以及 是内置的替换对于某些常见的实体。像任何其他的替代,他们都以’S’。 Objective-C的模块作为上下文重整Objective-C类为的

      索引

      1
      2
      3
      
      index ::= '_'                              // 0
      index ::= natural '_'                      // N+1
      natural ::= [0-9]+
      
      是一个生产环境中不能在一个数字结尾的数字编码;它的编码数量较少的优化。

Comments