{
  "schemaVersion": "0.11.0",
  "canonical": "https://www.pystone.net/notes/webgl-wasm-js-interop-principles/",
  "atlas": "https://www.pystone.net/?node=webgl-wasm-js-interop-principles#knowledge-atlas",
  "markdown": "https://www.pystone.net/notes/webgl-wasm-js-interop-principles.md",
  "context": "https://www.pystone.net/notes/webgl-wasm-js-interop-principles.context.json",
  "knowledgeVersion": "224c990773de.5fa8af6e39fa",
  "build": {
    "siteCommit": "224c990773de166d23a886306577dd90379529ce",
    "notesCommit": "5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a",
    "builtAt": "1970-01-01T00:00:00.000Z",
    "version": "224c990773de.5fa8af6e39fa"
  },
  "id": "note:webgl-wasm-js-interop-principles",
  "slug": "webgl-wasm-js-interop-principles",
  "title": "WebGL应用-wasm与javascript互操作原理浅谈",
  "type": "note",
  "visibility": "public",
  "idStability": "rename-stable",
  "author": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "publisher": {
    "name": "Perrin Yong",
    "profile": "https://www.pystone.net/profile/"
  },
  "aliases": [],
  "summary": "WebGL应用 wasm与javascript互操作原理浅谈",
  "contentRole": "unspecified",
  "isMoc": false,
  "mocRecognition": "none",
  "generated": false,
  "attribution": "unspecified",
  "domain": "10-计算机、信息技术与工程",
  "tags": [],
  "mocs": [],
  "contentHash": "ef6ef3285c49da744c194defe6b0c21cd4702953f8f39e97f90df0b780281b17",
  "assets": [],
  "headings": [
    {
      "depth": 1,
      "text": "WebGL应用-wasm与javascript互操作原理浅谈",
      "anchor": "webgl应用-wasm与javascript互操作原理浅谈",
      "citation": "https://www.pystone.net/notes/webgl-wasm-js-interop-principles/#webgl%E5%BA%94%E7%94%A8-wasm%E4%B8%8Ejavascript%E4%BA%92%E6%93%8D%E4%BD%9C%E5%8E%9F%E7%90%86%E6%B5%85%E8%B0%88"
    },
    {
      "depth": 3,
      "text": "为什么 DllImport(\"Internal\") 能在 Unity WebGL 里“拨号”到 JavaScript？",
      "anchor": "为什么-dllimportinternal-能在-unity-webgl-里拨号到-javascript",
      "citation": "https://www.pystone.net/notes/webgl-wasm-js-interop-principles/#%E4%B8%BA%E4%BB%80%E4%B9%88-dllimportinternal-%E8%83%BD%E5%9C%A8-unity-webgl-%E9%87%8C%E6%8B%A8%E5%8F%B7%E5%88%B0-javascript"
    },
    {
      "depth": 2,
      "text": "1 编译期：DllImport(\"Internal\") → Wasm import",
      "anchor": "1-编译期dllimportinternal-wasm-import",
      "citation": "https://www.pystone.net/notes/webgl-wasm-js-interop-principles/#1-%E7%BC%96%E8%AF%91%E6%9C%9Fdllimportinternal-wasm-import"
    },
    {
      "depth": 2,
      "text": "2 运行期：Glue JS 把符号“填空”",
      "anchor": "2-运行期glue-js-把符号填空",
      "citation": "https://www.pystone.net/notes/webgl-wasm-js-interop-principles/#2-%E8%BF%90%E8%A1%8C%E6%9C%9Fglue-js-%E6%8A%8A%E7%AC%A6%E5%8F%B7%E5%A1%AB%E7%A9%BA"
    },
    {
      "depth": 2,
      "text": "3 调用链 & 数据封送",
      "anchor": "3-调用链-数据封送",
      "citation": "https://www.pystone.net/notes/webgl-wasm-js-interop-principles/#3-%E8%B0%83%E7%94%A8%E9%93%BE-%E6%95%B0%E6%8D%AE%E5%B0%81%E9%80%81"
    },
    {
      "depth": 2,
      "text": "4 与 “Android SO / iOS .a P/Invoke” 的异同",
      "anchor": "4-与-android-so-ios-a-pinvoke-的异同",
      "citation": "https://www.pystone.net/notes/webgl-wasm-js-interop-principles/#4-%E4%B8%8E-android-so-ios-a-pinvoke-%E7%9A%84%E5%BC%82%E5%90%8C"
    },
    {
      "depth": 2,
      "text": "5 写一个最小 Demo（完整往返）",
      "anchor": "5-写一个最小-demo完整往返",
      "citation": "https://www.pystone.net/notes/webgl-wasm-js-interop-principles/#5-%E5%86%99%E4%B8%80%E4%B8%AA%E6%9C%80%E5%B0%8F-demo%E5%AE%8C%E6%95%B4%E5%BE%80%E8%BF%94"
    }
  ],
  "claims": [],
  "outgoing": [],
  "incoming": [
    {
      "id": "note:game-graphics-and-runtime",
      "title": "游戏图形与运行时",
      "url": "https://www.pystone.net/notes/game-graphics-and-runtime/",
      "atlas": "https://www.pystone.net/?node=game-graphics-and-runtime#knowledge-atlas",
      "label": "游戏图形与运行时",
      "origin": "explicit",
      "humanReviewed": true,
      "context": "WebGL中的“WebGL应用-wasm与javascript互操作原理浅谈”导航项",
      "citation": "https://www.pystone.net/notes/game-graphics-and-runtime/#webgl"
    }
  ],
  "contentMarkdown": "# WebGL应用-wasm与javascript互操作原理浅谈\n\n﻿# WebGL应用-wasm与javascript互操作原理浅谈\n\n### 为什么 `DllImport(\"__Internal\")` 能在 Unity WebGL 里“拨号”到 JavaScript？\n\n> **核心一句话**\n> 在 WebGL 构建中，Unity 的 C# → C++ → WebAssembly 流程把\n\n\n```csharp\n[DllImport(\"__Internal\")] void JsAlert(string msg);\n```\n\n>\n> 变成 **Wasm 里的一个“导入符号”**；Emscripten 期待“宿主环境（= 浏览器的 Glue JS）”提供同名函数。于是调用链是 **Wasm → JS Glue → 浏览器 API**。下面分解整条链：\n\n---\n\n## 1 编译期：`DllImport(\"__Internal\")` → Wasm import\n\n| 步骤                            | 发生了什么？                                                                                                              |\n| ----------------------------- | ------------------------------------------------------------------------------------------------------------------- |\n| **① IL2CPP**                  | 把 C# 方法声明翻译成 C++ `extern \"C\" void JsAlert(const char*);`                                                            |\n| **② Emscripten (Clang/LLVM)** | 把这行 `extern` 当成 **未定义符号**；在 `.wasm` 里生成 **import section** 记录：`module=\"env\", field=\"JsAlert\"` ([Emscripten][1]) |\n| **③ 链接**                      | `wasm-ld` 合并对象 → `unity.wasm`，导入仍悬空，等待运行时注入                                                                         |\n\n---\n\n## 2 运行期：Glue JS 把符号“填空”\n\n**Unity WebGL** 会把你在 `Plugins/*.jslib` 或 `index.html/<script>` 里写的 JS 函数合并进 **`*.framework.js`**。\n示例 `Plugins/browser.jslib`:\n\n```javascript\nmergeInto(LibraryManager.library, {\n  JsAlert: function (ptr) {\n    alert(UTF8ToString(ptr));  // Emscripten helper 解析 Wasm 字符串\n  }\n});\n```\n\n生成的 Glue JS 在启动时执行：\n\n```javascript\nModule[\"asmLibraryArg\"] = {\n  \"JsAlert\": js_library_function_ptr,\n  // …其它系统调用…\n};\n```\n\n浏览器随后调用\n\n```javascript\nWebAssembly.instantiateStreaming(fetch(\"unity.wasm\"), {\n  env: Module.asmLibraryArg     // ← 解决导入\n});\n```\n\n至此 **`JsAlert` 地址被注入到 Wasm 实例**，`DllImport` 调用可以跳转。([Emscripten][2])\n\n---\n\n## 3 调用链 & 数据封送\n\n```text\nC# (IL2CPP) → Wasm 导入桩 → JS Glue → 浏览器 API\n```\n\n* **指针/数组/字符串**\n  *Wasm 只有线性内存*；Emscripten帮你生成 `UTF8ToString(ptr) / stringToUTF8()` 等函数，把 C# `string`（UTF‑16）在编译期转成 UTF‑8 字节，并把地址传给 JS。\n* **同步 / 异步**\n  Wasm 调 JS 函数本质是一次 **直接函数指针调用**（无跨线程），耗时几十纳秒，比 Android JNI / iOS Objective‑C Bridge 还轻。\n  若 JS 侧想回调 C#，可用 `Module.ccall('ManagedFunc', null, ['number'], [123]);`。\n\n---\n\n## 4 与 “Android SO / iOS .a P/Invoke” 的异同\n\n| 特征      | 传统移动原生插件                       | WebGL (P/Invoke to JS)                   |\n| ------- | ------------------------------ | ---------------------------------------- |\n| 目标平台    | ARM/ARM64 机器码                  | Wasm → JS → 浏览器 API                      |\n| 绑定方式    | `<__Internal>` ↔  `.so/.a` 符号表 | Wasm import ↔ JS function name           |\n| 调用成本    | 需要 JNI / Obj‑C trampoline      | 同线程函数指针跳转                                |\n| 可调用 API | 系统 NDK / SDK                   | 任意 Web API (`fetch`, `WebGL`, `WebRTC`…) |\n| 对象寿命    | 真实指针/句柄                        | 线性内存指针 + JS 对象（TypedArray/Number）        |\n\n---\n\n## 5 写一个最小 Demo（完整往返）\n\n```javascript\n// Assets/Plugins/WebGL/alert.jslib\nmergeInto(LibraryManager.library, {\n  JsAlert: function (ptr) {\n    alert(\"Wasm says: \" + UTF8ToString(ptr));\n  },\n  JsAdd: function (a, b) {\n    return a + b;\n  }\n});\n```\n\n```csharp\npublic class TestCall : MonoBehaviour {\n#if UNITY_WEBGL && !UNITY_EDITOR\n  [DllImport(\"__Internal\")] private static extern void JsAlert(string msg);\n  [DllImport(\"__Internal\")] private static extern int  JsAdd(int a, int b);\n#endif\n\n  void Start() {\n#if UNITY_WEBGL && !UNITY_EDITOR\n    JsAlert(\"Hello WebGL!\");          // 弹窗\n    Debug.Log(\"2+3=\" + JsAdd(2,3));   // Console 打印 5\n#endif\n  }\n}\n```\n\n* **构建 WebGL** → 浏览器加载 → 弹窗 & Console 输出。\n* 同理，可在 JS Glue 里调用 `UnityLoader.sendMessage()` 或 `Module.ccall` 回调 C#。\n\n\n[1]: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html?utm_source=chatgpt.com \"Interacting with code — Emscripten 4.0.9-git (dev) documentation\"\n[2]: https://emscripten.org/docs/api_reference/module.html?utm_source=chatgpt.com \"Module object — Emscripten 4.0.9-git (dev) documentation\"\n"
}
