|
939 | 939 |
|
940 | 940 | <li class="md-nav__item"> |
941 | 941 | <a href="#_1" class="md-nav__link"> |
942 | | - 实现跨语言调用的一般流程 |
| 942 | + 接口定义 |
943 | 943 | </a> |
944 | 944 |
|
| 945 | +</li> |
| 946 | + |
| 947 | + <li class="md-nav__item"> |
| 948 | + <a href="#_2" class="md-nav__link"> |
| 949 | + 绑定生成 |
| 950 | + </a> |
| 951 | + |
| 952 | + <nav class="md-nav" aria-label="绑定生成"> |
| 953 | + <ul class="md-nav__list"> |
| 954 | + |
| 955 | + <li class="md-nav__item"> |
| 956 | + <a href="#_3" class="md-nav__link"> |
| 957 | + 参数解析 |
| 958 | + </a> |
| 959 | + |
| 960 | +</li> |
| 961 | + |
| 962 | + <li class="md-nav__item"> |
| 963 | + <a href="#_4" class="md-nav__link"> |
| 964 | + 调用核心生成器 |
| 965 | + </a> |
| 966 | + |
| 967 | + <nav class="md-nav" aria-label="调用核心生成器"> |
| 968 | + <ul class="md-nav__list"> |
| 969 | + |
| 970 | + <li class="md-nav__item"> |
| 971 | + <a href="#c" class="md-nav__link"> |
| 972 | + 获取 C 库定义 |
| 973 | + </a> |
| 974 | + |
| 975 | +</li> |
| 976 | + |
| 977 | + <li class="md-nav__item"> |
| 978 | + <a href="#kotlin-ir" class="md-nav__link"> |
| 979 | + 构建 Kotlin IR 存根 |
| 980 | + </a> |
| 981 | + |
| 982 | +</li> |
| 983 | + |
| 984 | + <li class="md-nav__item"> |
| 985 | + <a href="#_5" class="md-nav__link"> |
| 986 | + 构建桥接代码 |
| 987 | + </a> |
| 988 | + |
| 989 | +</li> |
| 990 | + |
| 991 | + </ul> |
| 992 | + </nav> |
| 993 | + |
| 994 | +</li> |
| 995 | + |
| 996 | + <li class="md-nav__item"> |
| 997 | + <a href="#_6" class="md-nav__link"> |
| 998 | + 原生代码编译 |
| 999 | + </a> |
| 1000 | + |
| 1001 | + <nav class="md-nav" aria-label="原生代码编译"> |
| 1002 | + <ul class="md-nav__list"> |
| 1003 | + |
| 1004 | + <li class="md-nav__item"> |
| 1005 | + <a href="#klib" class="md-nav__link"> |
| 1006 | + 生成 KLIB |
| 1007 | + </a> |
| 1008 | + |
| 1009 | +</li> |
| 1010 | + |
| 1011 | + </ul> |
| 1012 | + </nav> |
| 1013 | + |
| 1014 | +</li> |
| 1015 | + |
| 1016 | + </ul> |
| 1017 | + </nav> |
| 1018 | + |
945 | 1019 | </li> |
946 | 1020 |
|
947 | 1021 | <li class="md-nav__item"> |
|
2499 | 2573 |
|
2500 | 2574 | <li class="md-nav__item"> |
2501 | 2575 | <a href="#_1" class="md-nav__link"> |
2502 | | - 实现跨语言调用的一般流程 |
| 2576 | + 接口定义 |
| 2577 | + </a> |
| 2578 | + |
| 2579 | +</li> |
| 2580 | + |
| 2581 | + <li class="md-nav__item"> |
| 2582 | + <a href="#_2" class="md-nav__link"> |
| 2583 | + 绑定生成 |
| 2584 | + </a> |
| 2585 | + |
| 2586 | + <nav class="md-nav" aria-label="绑定生成"> |
| 2587 | + <ul class="md-nav__list"> |
| 2588 | + |
| 2589 | + <li class="md-nav__item"> |
| 2590 | + <a href="#_3" class="md-nav__link"> |
| 2591 | + 参数解析 |
| 2592 | + </a> |
| 2593 | + |
| 2594 | +</li> |
| 2595 | + |
| 2596 | + <li class="md-nav__item"> |
| 2597 | + <a href="#_4" class="md-nav__link"> |
| 2598 | + 调用核心生成器 |
| 2599 | + </a> |
| 2600 | + |
| 2601 | + <nav class="md-nav" aria-label="调用核心生成器"> |
| 2602 | + <ul class="md-nav__list"> |
| 2603 | + |
| 2604 | + <li class="md-nav__item"> |
| 2605 | + <a href="#c" class="md-nav__link"> |
| 2606 | + 获取 C 库定义 |
2503 | 2607 | </a> |
2504 | 2608 |
|
| 2609 | +</li> |
| 2610 | + |
| 2611 | + <li class="md-nav__item"> |
| 2612 | + <a href="#kotlin-ir" class="md-nav__link"> |
| 2613 | + 构建 Kotlin IR 存根 |
| 2614 | + </a> |
| 2615 | + |
| 2616 | +</li> |
| 2617 | + |
| 2618 | + <li class="md-nav__item"> |
| 2619 | + <a href="#_5" class="md-nav__link"> |
| 2620 | + 构建桥接代码 |
| 2621 | + </a> |
| 2622 | + |
| 2623 | +</li> |
| 2624 | + |
| 2625 | + </ul> |
| 2626 | + </nav> |
| 2627 | + |
| 2628 | +</li> |
| 2629 | + |
| 2630 | + <li class="md-nav__item"> |
| 2631 | + <a href="#_6" class="md-nav__link"> |
| 2632 | + 原生代码编译 |
| 2633 | + </a> |
| 2634 | + |
| 2635 | + <nav class="md-nav" aria-label="原生代码编译"> |
| 2636 | + <ul class="md-nav__list"> |
| 2637 | + |
| 2638 | + <li class="md-nav__item"> |
| 2639 | + <a href="#klib" class="md-nav__link"> |
| 2640 | + 生成 KLIB |
| 2641 | + </a> |
| 2642 | + |
| 2643 | +</li> |
| 2644 | + |
| 2645 | + </ul> |
| 2646 | + </nav> |
| 2647 | + |
| 2648 | +</li> |
| 2649 | + |
| 2650 | + </ul> |
| 2651 | + </nav> |
| 2652 | + |
2505 | 2653 | </li> |
2506 | 2654 |
|
2507 | 2655 | <li class="md-nav__item"> |
|
2530 | 2678 |
|
2531 | 2679 |
|
2532 | 2680 | <h1 id="kotlin-c">Kotlin 调用 C 语言是如何实现的?</h1> |
2533 | | -<h2 id="_1">实现跨语言调用的一般流程</h2> |
2534 | | -<ul> |
2535 | | -<li>接口定义与绑定生成: 定义目标语言的接口描述,生成调用方语言的绑定代码,建立数据类型映射关系</li> |
2536 | | -<li>数据类型转换:实现基础类型与复杂类型的映射转换</li> |
2537 | | -<li>代码生成阶段: 生成跨语言调用的胶水代码</li> |
2538 | | -<li>运行时支持阶段: 内存管理与错误处理等</li> |
2539 | | -</ul> |
| 2681 | +<p>Kotlin 通过 C-interop 实现与 C 语言的互操作性,允许 Kotlin 代码调用 C 函数和使用 C 数据结构。C-interop 是 Kotlin/Native 的一部分,提供了一种机制来生成 Kotlin 代码,以便与 C 库进行交互。</p> |
| 2682 | +<p>C-interop 的工作原理是通过解析 C 头文件,生成相应的 Kotlin 代码和 KLIB(Kotlin Library)文件。这个过程包括以下几个步骤:</p> |
| 2683 | +<ol> |
| 2684 | +<li><strong>接口定义</strong>:使用 <code>.def</code> 文件定义 C 接口,包括头文件、编译选项、链接选项等信息。</li> |
| 2685 | +<li><strong>绑定生成</strong>:C-interop 工具解析 <code>.def</code> 文件,生成 Kotlin 代码和 KLIB 文件。这些 KLIB 文件包含了 C 函数的声明和数据结构的定义,使得 Kotlin 可以调用 C 函数。</li> |
| 2686 | +<li><strong>数据类型转换</strong>:C-interop 处理基础类型(如整数、浮点数等)和复杂类型(如结构体、枚举等)的映射转换。它会生成相应的 Kotlin 类型,以便在 Kotlin 代码中使用 C 数据结构。</li> |
| 2687 | +<li><strong>代码生成</strong>:C-interop 工具生成胶水代码,这些代码在 Kotlin 和 C 之间进行调用和数据传递。它会生成必要的函数调用包装器和属性访问器,以便 Kotlin 可以调用 C 函数。</li> |
| 2688 | +<li><strong>运行时支持</strong>:C-interop 提供了运行时支持,包括内存管理和错误处理等。它确保 Kotlin 代码可以正确地与 C 代码交互,并处理跨语言调用中的内存分配和释放。 </li> |
| 2689 | +</ol> |
| 2690 | +<h2 id="_1">接口定义</h2> |
| 2691 | +<pre><code>headers = hello.h |
| 2692 | + |
| 2693 | +# (For cinterop tool) Path to search for static libraries. |
| 2694 | +# Must be relative to the project root. |
| 2695 | +libraryPaths = src/nativeInterop/cinterop |
| 2696 | + |
| 2697 | +# (For final linker) Options to be embedded in the klib. |
| 2698 | +# Must be relative to the project root. |
| 2699 | +linkerOpts = -Lsrc/nativeInterop/cinterop |
| 2700 | + |
| 2701 | +# The static library file to be included. |
| 2702 | +staticLibraries = libhello.a |
| 2703 | + |
| 2704 | +# (For cinterop tool) Path to search for header files. |
| 2705 | +# Must be relative to the project root. |
| 2706 | +compilerOpts = -Isrc/nativeInterop/cinterop |
| 2707 | +</code></pre> |
| 2708 | +<p>如上所示,<code>.def</code> 文件定义了 C 接口,包括头文件、编译选项、链接选项等信息。C-interop 工具将解析这个文件,并生成相应的 Kotlin 代码和 KLIB 文件。</p> |
| 2709 | +<h2 id="_2">绑定生成</h2> |
| 2710 | +<pre><code>./kotlin-native/dist/bin/run_konan cinterop -def ./compilerTestData/cinterop/hello.def -o compilerTestData/cinterop/Hello -J"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:50015" |
| 2711 | +</code></pre> |
| 2712 | +<p>当我们执行 C-interop 命令时,实际上是调用了以上命令,这个命令会执行以下操作:</p> |
| 2713 | +<h3 id="_3">参数解析</h3> |
| 2714 | +<p>执行以上命令行时,会执行到 mainImpl 函数,该函数负责解析命令行参数,并根据参数调用相应的 C-interop 工具。</p> |
| 2715 | +<pre><code>private fun mainImpl(args: Array<String>, runFromDaemon: Boolean, konancMain: (Array<String>) -> Unit) { |
| 2716 | + val utilityName = args[0] |
| 2717 | + val utilityArgs = args.drop(1).toTypedArray() |
| 2718 | + when (utilityName) { |
| 2719 | + "cinterop" -> { |
| 2720 | + val konancArgs = invokeInterop("native", utilityArgs, runFromDaemon) |
| 2721 | + konancArgs?.let { konancMain(it) } |
| 2722 | + } |
| 2723 | + // ... |
| 2724 | + } |
| 2725 | +} |
| 2726 | +</code></pre> |
| 2727 | +<h3 id="_4">调用核心生成器</h3> |
| 2728 | +<p>在解析完参数后,C-interop 工具会调用核心生成器来处理 C 库的绑定生成。这个过程主要由 <code>processCLib</code> 函数完成。</p> |
| 2729 | +<p><code>processCLib</code> 的主要职责是:获取 C 库的完整定义,调用 LLVM 解析其头文件,生成所有必要的 Kotlin 和 C 的“胶水代码”(stubs),并将其编译打包,为最终生成 KLIB 做好准备。</p> |
| 2730 | +<h4 id="c">获取 C 库定义</h4> |
| 2731 | +<p>在 <code>processCLib</code> 函数中,首先会获取 C 库的定义, 这一步通过调用 LLVM 解析 <code>.def</code> 中指定的头文件来完成的。</p> |
| 2732 | +<pre><code>// nativeIndex 是一个抽象类,它描述了来自C头文件的定义的IR |
| 2733 | +val (nativeIndex, compilation) = buildNativeIndexImpl(library, verbose, allowPrecompiledHeaders = nativeLibsDir != null) |
| 2734 | +</code></pre> |
| 2735 | +<h4 id="kotlin-ir">构建 Kotlin IR 存根</h4> |
| 2736 | +<p>这一步的主要任务是从 C AST 中提取所有声明信息,并将 C 声明转换为 Kotlin IR 存根。</p> |
| 2737 | +<pre><code class="language-kotlin">class StubIrBuilder(private val context: StubIrContext) { |
| 2738 | + fun build(): StubIrBuilderResult { |
| 2739 | + // ... |
| 2740 | + nativeIndex.functions.filter { it.name !in excludedFunctions }.forEach { generateStubsForFunction(it) } |
| 2741 | + // ... |
| 2742 | + return StubIrBuilderResult( |
| 2743 | + stubs, |
| 2744 | + buildingContext.declarationMapper, |
| 2745 | + buildingContext.bridgeComponentsBuilder.build(), |
| 2746 | + buildingContext.wrapperComponentsBuilder.build() |
| 2747 | + ) |
| 2748 | + } |
| 2749 | +} |
| 2750 | + |
| 2751 | +// 生成类型映射和函数签名 |
| 2752 | +fun mirror(declarationMapper: DeclarationMapper, type: Type): TypeMirror = when (type) { |
| 2753 | + is PrimitiveType -> mirrorPrimitiveType(type, declarationMapper) |
| 2754 | + is RecordType -> byRefTypeMirror(declarationMapper.getKotlinClassForPointed(type.decl).type) |
| 2755 | + is EnumType -> // ... |
| 2756 | + is PointerType -> // ... |
| 2757 | + is ArrayType -> // ... |
| 2758 | + is FunctionType -> byRefTypeMirror(KotlinTypes.cFunction.typeWith(getKotlinFunctionType(declarationMapper, type))) |
| 2759 | + is Typedef -> // ... |
| 2760 | + is ObjCPointer -> objCPointerMirror(declarationMapper, type) |
| 2761 | + else -> TODO(type.toString()) |
| 2762 | +} |
| 2763 | +</code></pre> |
| 2764 | +<p>这一步会处理基础类型(如整数、浮点数等)和复杂类型(如结构体、枚举等)的映射转换。它会生成相应的 Kotlin 类型,以便在 Kotlin 代码中使用 C 数据结构</p> |
| 2765 | +<p>这一步生成的内容实现上就是 CInterop 生成的 knm(kotlin native metadata) 文件,生成的内容就是函数声明,如下所示:</p> |
| 2766 | +<pre><code>package hello |
| 2767 | + |
| 2768 | +@kotlinx.cinterop.internal.CCall |
| 2769 | +@kotlinx.cinterop.ExperimentalForeignApi |
| 2770 | +public external fun hello_from_c(timestamp: kotlin.Long): kotlin.Unit { /* compiled code */ } |
| 2771 | +</code></pre> |
| 2772 | +<h4 id="_5">构建桥接代码</h4> |
| 2773 | +<pre><code>class StubIrDriver( |
| 2774 | + private val context: StubIrContext, |
| 2775 | + private val options: DriverOptions |
| 2776 | +) { |
| 2777 | + fun run(): Result { |
| 2778 | + // 生成 Kotlin IR 存根 |
| 2779 | + val builderResult = StubIrBuilder(context).build() |
| 2780 | + // 构建桥接代码 |
| 2781 | + val bridgeBuilderResult = StubIrBridgeBuilder(context, builderResult).build() |
| 2782 | + // 生成 C 存根文件 |
| 2783 | + outCFile.bufferedWriter().use { |
| 2784 | + emitCFile(context, it, entryPoint, bridgeBuilderResult.nativeBridges) |
| 2785 | + } |
| 2786 | + // ... |
| 2787 | + } |
| 2788 | +} |
| 2789 | +</code></pre> |
| 2790 | +<p>这一步的主要作用是生成 Kotlin 和 C 之间的桥接代码并写入 C 存根文件。它会创建函数调用包装器,生成属性访问器,并处理类型转换。</p> |
| 2791 | +<p>生成的内容如下所示,可以看到生成了<code>hello_from_c</code> 函数的调用包装器,后续 Kotlin 代码调用这个函数时,会通过这个包装器来调用 C 函数。</p> |
| 2792 | +<pre><code class="language-c">// __attribute__((always_inline)): 强制内联优化,减少函数调用开销 |
| 2793 | +__attribute__((always_inline)) |
| 2794 | +// 函数名 hello_hello_from_c_wrapper0 由 Kotlin/Native 自动生成 |
| 2795 | +// 参数 long long p0 对应 Kotlin 的 Long 类型 |
| 2796 | +void hello_hello_from_c_wrapper0(long long p0) { |
| 2797 | + hello_from_c(p0); |
| 2798 | +} |
| 2799 | + |
| 2800 | +// 声明函数指针变量,用于存储包装器函数的地址 |
| 2801 | +// __asm("knifunptr_hello0_hello_from_c") 指定汇编符号名,避免命名冲突 |
| 2802 | +const void* knifunptr_hello0_hello_from_c __asm("knifunptr_hello0_hello_from_c"); |
| 2803 | +// 将包装器函数的地址赋值给函数指针变量 |
| 2804 | +// 这样 Kotlin 代码就可以通过这个函数指针安全地调用 C 函数 |
| 2805 | +const void* knifunptr_hello0_hello_from_c = (const void*)&hello_hello_from_c_wrapper0; |
| 2806 | +</code></pre> |
| 2807 | +<h3 id="_6">原生代码编译</h3> |
| 2808 | +<p>上面生成的 C 桥接代码,需要通过 LLVM 编译为 bc 文件。</p> |
| 2809 | +<pre><code>when (flavor) { |
| 2810 | + KotlinPlatform.NATIVE -> { |
| 2811 | + // 编译为 LLVM 位码 (.bc) 文件 |
| 2812 | + val outLib = File(nativeLibsDir, "$libName.bc") |
| 2813 | + val compilerCmd = arrayOf(compiler, *compilerArgs, |
| 2814 | + "-emit-llvm", "-x", library.language.clangLanguageName, |
| 2815 | + "-c", "-", "-o", outLib.absolutePath) |
| 2816 | + runCmd(compilerCmd, verbose, redirectInputFile = File(outCFile.absolutePath)) |
| 2817 | + } |
| 2818 | +} |
| 2819 | +</code></pre> |
| 2820 | +<h4 id="klib">生成 KLIB</h4> |
| 2821 | +<p>最后一步是将生成的 Kotlin IR 存根和桥接代码序列化并写入 KLIB 文件。这一步会将库的元数据、IR Stub、manifest 文件等所有内容,按照 .klib 文件格式的标准目录结构,写入到由 -o 参数指定的输出目录中。</p> |
| 2822 | +<pre><code class="language-kotlin">when (stubIrOutput) { |
| 2823 | + is StubIrDriver.Result.Metadata -> { |
| 2824 | + createInteropLibrary( |
| 2825 | + metadata = stubIrOutput.metadata, // Kotlin 元数据 |
| 2826 | + nativeBitcodeFiles = compiledFiles + nativeOutputPath, // 原生位码 |
| 2827 | + target = tool.target, // 目标平台 |
| 2828 | + moduleName = moduleName, // 模块名 |
| 2829 | + outputPath = outputPath, // 输出路径 |
| 2830 | + manifest = def.manifestAddendProperties, // 清单属性 |
| 2831 | + dependencies = stdlibDependency + imports.requiredLibraries, |
| 2832 | + nopack = nopack, // 是否打包 |
| 2833 | + shortName = cinteropArguments.shortModuleName, |
| 2834 | + staticLibraries = resolveLibraries(staticLibraries, libraryPaths) |
| 2835 | + ) |
| 2836 | + } |
| 2837 | +} |
| 2838 | +</code></pre> |
2540 | 2839 | <h2 id="cinterop">调试 CInterop 过程</h2> |
2541 | | -<pre><code>./kotlin-native/dist/bin/run_konan cinterop -def ./compilerTestData/cinterop/hello.def -o compilerTestData/cinterop/output -J"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:50015" |
| 2840 | +<pre><code>./kotlin-native/dist/bin/run_konan cinterop -def ./compilerTestData/cinterop/hello.def -o compilerTestData/cinterop/Hello -J"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:50015" |
| 2841 | + |
| 2842 | +./kotlin-native/dist/bin/run_konan konanc ./compilerTestData/cinterop/Main.kt -library ./compilerTestData/cinterop/Hello.klib -o ./compilerTestData/cinterop/Hello -J"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:50015" |
2542 | 2843 | </code></pre> |
2543 | 2844 |
|
2544 | 2845 | <hr> |
2545 | 2846 | <div class="md-source-file"> |
2546 | 2847 | <small> |
2547 | 2848 |
|
2548 | 2849 | 最后更新: |
2549 | | - <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date">August 3, 2025</span> |
| 2850 | + <span class="git-revision-date-localized-plugin git-revision-date-localized-plugin-date">August 10, 2025</span> |
2550 | 2851 |
|
2551 | 2852 |
|
2552 | 2853 | </small> |
|
0 commit comments