{
  "schemaVersion": "0.11.0",
  "canonical": "https://www.pystone.net/notes/webgl-wasm-loading-usage/",
  "atlas": "https://www.pystone.net/?node=webgl-wasm-loading-usage#knowledge-atlas",
  "markdown": "https://www.pystone.net/notes/webgl-wasm-loading-usage.md",
  "context": "https://www.pystone.net/notes/webgl-wasm-loading-usage.context.json",
  "knowledgeVersion": "224c990773de.5fa8af6e39fa",
  "build": {
    "siteCommit": "224c990773de166d23a886306577dd90379529ce",
    "notesCommit": "5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a",
    "builtAt": "1970-01-01T00:00:00.000Z",
    "version": "224c990773de.5fa8af6e39fa"
  },
  "id": "note:webgl-wasm-loading-usage",
  "slug": "webgl-wasm-loading-usage",
  "title": "WebGL应用-Wasm加载使用原理",
  "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加载使用原理",
  "contentRole": "unspecified",
  "isMoc": false,
  "mocRecognition": "none",
  "generated": false,
  "attribution": "unspecified",
  "domain": "10-计算机、信息技术与工程",
  "tags": [],
  "mocs": [],
  "contentHash": "357df8ecde88a43bbf5383454103005ddb138bc1dd493c4f4725069565902b7e",
  "assets": [],
  "headings": [
    {
      "depth": 1,
      "text": "WebGL应用-Wasm加载使用原理",
      "anchor": "webgl应用-wasm加载使用原理",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#webgl%E5%BA%94%E7%94%A8-wasm%E5%8A%A0%E8%BD%BD%E4%BD%BF%E7%94%A8%E5%8E%9F%E7%90%86"
    },
    {
      "depth": 2,
      "text": "整体框架",
      "anchor": "整体框架",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#%E6%95%B4%E4%BD%93%E6%A1%86%E6%9E%B6"
    },
    {
      "depth": 2,
      "text": "代码流程",
      "anchor": "代码流程",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#%E4%BB%A3%E7%A0%81%E6%B5%81%E7%A8%8B"
    },
    {
      "depth": 3,
      "text": "0. 全局准备",
      "anchor": "0-全局准备",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#0-%E5%85%A8%E5%B1%80%E5%87%86%E5%A4%87"
    },
    {
      "depth": 3,
      "text": "1. 确定 .wasm 的 URL",
      "anchor": "1-确定-wasm-的-url",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#1-%E7%A1%AE%E5%AE%9A-wasm-%E7%9A%84-url"
    },
    {
      "depth": 3,
      "text": "2. 读取二进制",
      "anchor": "2-读取二进制",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#2-%E8%AF%BB%E5%8F%96%E4%BA%8C%E8%BF%9B%E5%88%B6"
    },
    {
      "depth": 3,
      "text": "3. createWasm() 主入口",
      "anchor": "3-createwasm-主入口",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#3-createwasm-%E4%B8%BB%E5%85%A5%E5%8F%A3"
    },
    {
      "depth": 3,
      "text": "4. instantiateAsync()：决定 “流式” 还是 “整包”",
      "anchor": "4-instantiateasync决定-流式-还是-整包",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#4-instantiateasync%E5%86%B3%E5%AE%9A-%E6%B5%81%E5%BC%8F-%E8%BF%98%E6%98%AF-%E6%95%B4%E5%8C%85"
    },
    {
      "depth": 3,
      "text": "5. instantiateArrayBuffer() 真正编译",
      "anchor": "5-instantiatearraybuffer-真正编译",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#5-instantiatearraybuffer-%E7%9C%9F%E6%AD%A3%E7%BC%96%E8%AF%91"
    },
    {
      "depth": 3,
      "text": "6. 依赖计数 & ready Promise",
      "anchor": "6-依赖计数-ready-promise",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#6-%E4%BE%9D%E8%B5%96%E8%AE%A1%E6%95%B0-ready-promise"
    },
    {
      "depth": 3,
      "text": "7. 内存、表、HEAP 视图初始化",
      "anchor": "7-内存表heap-视图初始化",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#7-%E5%86%85%E5%AD%98%E8%A1%A8heap-%E8%A7%86%E5%9B%BE%E5%88%9D%E5%A7%8B%E5%8C%96"
    },
    {
      "depth": 3,
      "text": "8. 静态构造 & 运行时就绪",
      "anchor": "8-静态构造-运行时就绪",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#8-%E9%9D%99%E6%80%81%E6%9E%84%E9%80%A0-%E8%BF%90%E8%A1%8C%E6%97%B6%E5%B0%B1%E7%BB%AA"
    },
    {
      "depth": 2,
      "text": "需要再补充的三个环节",
      "anchor": "需要再补充的三个环节",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#%E9%9C%80%E8%A6%81%E5%86%8D%E8%A1%A5%E5%85%85%E7%9A%84%E4%B8%89%E4%B8%AA%E7%8E%AF%E8%8A%82"
    },
    {
      "depth": 3,
      "text": "1.1 生命周期钩子 preRun / onRuntimeInitialized / postRun",
      "anchor": "11-生命周期钩子-prerun-onruntimeinitialized-postrun",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#11-%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E9%92%A9%E5%AD%90-prerun-onruntimeinitialized-postrun"
    },
    {
      "depth": 3,
      "text": "1.2 内存增长与 emscriptennotifymemorygrowth",
      "anchor": "12-内存增长与-emscriptennotifymemorygrowth",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#12-%E5%86%85%E5%AD%98%E5%A2%9E%E9%95%BF%E4%B8%8E-emscriptennotifymemorygrowth"
    },
    {
      "depth": 3,
      "text": "1.3 异常捕获与 abort() 路径",
      "anchor": "13-异常捕获与-abort-路径",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#13-%E5%BC%82%E5%B8%B8%E6%8D%95%E8%8E%B7%E4%B8%8E-abort-%E8%B7%AF%E5%BE%84"
    },
    {
      "depth": 2,
      "text": "加载方式",
      "anchor": "加载方式",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#%E5%8A%A0%E8%BD%BD%E6%96%B9%E5%BC%8F"
    },
    {
      "depth": 3,
      "text": "使用者侧的最简代码（浏览器 / Creator 预览）",
      "anchor": "使用者侧的最简代码浏览器-creator-预览",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#%E4%BD%BF%E7%94%A8%E8%80%85%E4%BE%A7%E7%9A%84%E6%9C%80%E7%AE%80%E4%BB%A3%E7%A0%81%E6%B5%8F%E8%A7%88%E5%99%A8-creator-%E9%A2%84%E8%A7%88"
    },
    {
      "depth": 2,
      "text": "资源定位 & 实例化阶段",
      "anchor": "资源定位-实例化阶段",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#%E8%B5%84%E6%BA%90%E5%AE%9A%E4%BD%8D-%E5%AE%9E%E4%BE%8B%E5%8C%96%E9%98%B6%E6%AE%B5"
    },
    {
      "depth": 3,
      "text": "locateFile —— 改写 .wasm 真正的 URL",
      "anchor": "locatefile-改写-wasm-真正的-url",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#locatefile-%E6%94%B9%E5%86%99-wasm-%E7%9C%9F%E6%AD%A3%E7%9A%84-url"
    },
    {
      "depth": 3,
      "text": "instantiateWasm(imports, successCallback) —— 完全接管实例化",
      "anchor": "instantiatewasmimports-successcallback-完全接管实例化",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#instantiatewasmimports-successcallback-%E5%AE%8C%E5%85%A8%E6%8E%A5%E7%AE%A1%E5%AE%9E%E4%BE%8B%E5%8C%96"
    },
    {
      "depth": 3,
      "text": "Module.wasmBinary —— 预塞 Uint8Array 切断网络请求",
      "anchor": "modulewasmbinary-预塞-uint8array-切断网络请求",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#modulewasmbinary-%E9%A2%84%E5%A1%9E-uint8array-%E5%88%87%E6%96%AD%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82"
    },
    {
      "depth": 3,
      "text": "Module.readBinary —— Node/Electron 里同步读文件",
      "anchor": "modulereadbinary-nodeelectron-里同步读文件",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#modulereadbinary-nodeelectron-%E9%87%8C%E5%90%8C%E6%AD%A5%E8%AF%BB%E6%96%87%E4%BB%B6"
    },
    {
      "depth": 2,
      "text": "运行时行为",
      "anchor": "运行时行为",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#%E8%BF%90%E8%A1%8C%E6%97%B6%E8%A1%8C%E4%B8%BA"
    },
    {
      "depth": 3,
      "text": "noExitRuntime —— 允许 / 禁止退出",
      "anchor": "noexitruntime-允许-禁止退出",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#noexitruntime-%E5%85%81%E8%AE%B8-%E7%A6%81%E6%AD%A2%E9%80%80%E5%87%BA"
    },
    {
      "depth": 3,
      "text": "preRun / postRun —— 追加任务队列",
      "anchor": "prerun-postrun-追加任务队列",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#prerun-postrun-%E8%BF%BD%E5%8A%A0%E4%BB%BB%E5%8A%A1%E9%98%9F%E5%88%97"
    },
    {
      "depth": 3,
      "text": "onRuntimeInitialized —— “等价 ready” 同步钩子",
      "anchor": "onruntimeinitialized-等价-ready-同步钩子",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#onruntimeinitialized-%E7%AD%89%E4%BB%B7-ready-%E5%90%8C%E6%AD%A5%E9%92%A9%E5%AD%90"
    },
    {
      "depth": 3,
      "text": "自定义日志 & 报错",
      "anchor": "自定义日志-报错",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%97%A5%E5%BF%97-%E6%8A%A5%E9%94%99"
    },
    {
      "depth": 2,
      "text": "内存与线程",
      "anchor": "内存与线程",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#%E5%86%85%E5%AD%98%E4%B8%8E%E7%BA%BF%E7%A8%8B"
    },
    {
      "depth": 3,
      "text": "INITIALMEMORY（编译期）+ Module.TOTALMEMORY（运行时旧接口）",
      "anchor": "initialmemory编译期-moduletotalmemory运行时旧接口",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#initialmemory%E7%BC%96%E8%AF%91%E6%9C%9F-moduletotalmemory%E8%BF%90%E8%A1%8C%E6%97%B6%E6%97%A7%E6%8E%A5%E5%8F%A3"
    },
    {
      "depth": 3,
      "text": "allowMemoryGrowth + 运行时增长",
      "anchor": "allowmemorygrowth-运行时增长",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#allowmemorygrowth-%E8%BF%90%E8%A1%8C%E6%97%B6%E5%A2%9E%E9%95%BF"
    },
    {
      "depth": 2,
      "text": "定制文件系统 / 虚拟包",
      "anchor": "定制文件系统-虚拟包",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#%E5%AE%9A%E5%88%B6%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F-%E8%99%9A%E6%8B%9F%E5%8C%85"
    },
    {
      "depth": 3,
      "text": "在 preRun 中安装文件",
      "anchor": "在-prerun-中安装文件",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#%E5%9C%A8-prerun-%E4%B8%AD%E5%AE%89%E8%A3%85%E6%96%87%E4%BB%B6"
    },
    {
      "depth": 2,
      "text": "Cocos 覆写的过程",
      "anchor": "cocos-覆写的过程",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#cocos-%E8%A6%86%E5%86%99%E7%9A%84%E8%BF%87%E7%A8%8B"
    },
    {
      "depth": 2,
      "text": "wasm 插件配置方案",
      "anchor": "wasm-插件配置方案",
      "citation": "https://www.pystone.net/notes/webgl-wasm-loading-usage/#wasm-%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE%E6%96%B9%E6%A1%88"
    }
  ],
  "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加载使用原理”导航项",
      "citation": "https://www.pystone.net/notes/game-graphics-and-runtime/#webgl"
    }
  ],
  "contentMarkdown": "# WebGL应用-Wasm加载使用原理\n\n```bash\nemcc  my_test_lib.cpp          ^\n      -O0 ^\n      -sWASM=1                      ^\n      -sMODULARIZE=1                ^\n      -sEXPORT_NAME=createMyLib     ^\n      -sENVIRONMENT=web             ^\n      -sEXPORTED_RUNTIME_METHODS=\"['cwrap']\" ^\n      -o wasm/my_test_lib.js\n\n```\n\n使用该方式编译C++代码,会生成wasm和对应的js glue\njs glue 负责对wasm进行加载, 接口导出, 外部接口导入等工作.\n加载该js脚本, 即可完成对wasm的加载\n\n## 整体框架\n\n```js\nvar createMyLib = (() => {\n  var _scriptDir = typeof document !== 'undefined' && document.currentScript\n                   ? document.currentScript.src\n                   : undefined;\n  return (function (createMyLib) { … })(/* 参数留空 */);\n})();\n```\n\n**IIFE** (`(() => { … })()`)\n\n立即执行并返回 _一个工厂函数_，赋给全局变量 `createMyLib` （编译时可设置该函数变量名， 不重要）\n\n该工厂函数定义如下, 功能: 创建Module, 返回ready\n```js\nfunction(createMyLib) {\n  createMyLib = createMyLib || {};\n\n...\nvar Module = Object.assign({}, createMyLib);\n...\n\n  return createMyLib.ready;\n}\n\n```\n\nModule.ready 定义如下:\n```js\nModule['ready'] = new Promise(function(resolve, reject) {\n  readyPromiseResolve = resolve;\n  readyPromiseReject = reject;\n});\n```\n\n```js\n  function doRun() {\n    // run may have just been called through dependencies being fulfilled just in this very frame,\n    // or while the async setStatus time below was happening\n    if (calledRun) return;\n    calledRun = true;\n    Module['calledRun'] = true;\n\n    if (ABORT) return;\n\n    initRuntime();\n\n    readyPromiseResolve(Module);\n    if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized']();\n\n    assert(!Module['_main'], 'compiled without a main, but one is present. if you added it from JS, use Module[\"onRuntimeInitialized\"]');\n\n    postRun();\n  }\n```\n\ndoRun的时候, 将 Module Resolve .\n\n工厂函数的导出方式:\n\nemscripten 默认生成:\n```js\nif (typeof exports === 'object' && typeof module === 'object')\n  module.exports = createMyLib;\nelse if (typeof define === 'function' && define['amd'])\n  define([], function() { return createMyLib; });\nelse if (typeof exports === 'object')\n  exports[\"createMyLib\"] = createMyLib;\n```\n\n\nChatGPT认为:\n当你在资源管理器里勾上 **`Is Plugin`**（或在 3.8+ 默认走 ESBuild）—— Creator 构建管线会在文件尾部插入一行\n\n```js\nexport default createMyLib;\n```\n\n因此调用方式是：\n```js\nconst createMyLib = (await import('./wasm/my_test_lib.js')).default;\n//             └─────────────── default 导出就是那行 export default\n\nconst { default: createMyLib } = await import('./wasm/my_test_lib.js');\n\n```\n\n\n## 代码流程\n\n下面按**执行先后顺序**把你截出的 *Emscripten runtime* 代码拆成 8 个阶段，并在关键行插入一段「**发生了什么 → 为什么要这么做**」的说明。跟着阅读即可形成一份笔记。\n\n---\n\n### 0. 全局准备\n\n```js\nvar wasmBinary;                               // 若用户自行塞入 ArrayBuffer\nif (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];\n\nvar noExitRuntime = Module['noExitRuntime'] || true;\n\nif (typeof WebAssembly != 'object')\n  abort('no native wasm support detected');\n```\n\n| 作用              | 解释                                                            |\n| --------------- | ------------------------------------------------------------- |\n| `wasmBinary`    | 如果调用者在 `<script>` 前提前设定 `Module.wasmBinary`，这里保存下来，后面就不用网络请求。 |\n| `noExitRuntime` | 缺省改为 `true`（Web 场景一般不退出进程）。                                   |\n| WebAssembly 检测  | 早期浏览器没有 Wasm 就直接 `abort()`。                                   |\n\n---\n\n### 1. **确定 .wasm 的 URL**\n\n```js\nvar wasmBinaryFile = 'my_test_lib.wasm';\nif (!isDataURI(wasmBinaryFile)) wasmBinaryFile = locateFile(wasmBinaryFile);\n```\n\n* **默认文件名**来自 `-o xxx.js` ⇒ 同名 `xxx.wasm`\n* `locateFile` 可以被外部覆写——\n\n  ```js\n  createMyLib({ locateFile: (f) => 'src/wasm/' + f })\n  ```\n\n  ——让运行时找到真实路径（Creator 构建后常用）。\n\n---\n\n### 2. **读取二进制**\n\n```js\nfunction getBinary(file) { … }\nfunction getBinaryPromise() {\n  if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) {\n    if (typeof fetch == 'function') {\n      return fetch(wasmBinaryFile, {credentials:'same-origin'})\n             .then(r => r.arrayBuffer())\n             .catch(() => getBinary(wasmBinaryFile));\n    }\n  }\n  return Promise.resolve().then(() => getBinary(wasmBinaryFile));\n}\n```\n\n| 路径                                         | 细节                             |\n| ------------------------------------------ | ------------------------------ |\n| ① `Module.wasmBinary` 已给 → 直接 `Uint8Array` | 零网络请求（适合 SINGLE\\_FILE 嵌入或手动内联） |\n| ② 浏览器/Worker 支持 `fetch` → **异步获取**         | 若服务器 MIME 错或 file://，落回 ③      |\n| ③ `readBinary` (Node `fs.readFileSync`)    | Node、本地壳                       |\n\n---\n\n### 3. **createWasm() 主入口**\n\n```js\nfunction createWasm() {\n  // 3-1 生成 import object\n  var info = {\n    env:  asmLibraryArg,\n    wasi_snapshot_preview1: asmLibraryArg\n  };\n\n  // 3-2 定义 receiveInstance：拿到 exports → 挂到 Module\n  function receiveInstance(instance, module) {\n    var exports = instance.exports;\n    Module['asm'] = exports;\n\n    wasmMemory = exports['memory'];\n    updateGlobalBufferAndViews(wasmMemory.buffer);\n\n    wasmTable  = exports['__indirect_function_table'];\n\n    addOnInit(exports['__wasm_call_ctors']);   // 调静态构造\n    removeRunDependency('wasm-instantiate');\n  }\n\n  addRunDependency('wasm-instantiate');        // 阻塞 Module.ready\n\n  // 3-3 如果用户覆写 Module.instantiateWasm，则走用户自定义\n  if (Module['instantiateWasm']) {\n    try {\n      var exports = Module['instantiateWasm'](info, receiveInstance);\n      return exports;                          // 覆写需要同步返回 exports\n    } catch(e) { err('… failed …'); return false; }\n  }\n\n  // 3-4 否则走内部异步流程\n  instantiateAsync().catch(readyPromiseReject);\n\n  return {};   // 立即返回空对象；真正的 exports 稍后填充\n}\n```\n\n---\n\n### 4. **instantiateAsync()：决定 “流式” 还是 “整包”**\n\n```js\nfunction instantiateAsync() {\n  if (!wasmBinary && WebAssembly.instantiateStreaming && fetch && !isDataURI) {\n    // 4-A 现代浏览器首选：边下边编译\n    return fetch(wasmBinaryFile, {credentials:'same-origin'})\n      .then(response =>\n        WebAssembly.instantiateStreaming(response, info)\n          .then(receiveInstantiationResult,\n                reason => {        // MIME 不对或 CDN 阻断\n                  err('streaming compile failed: '+reason);\n                  return instantiateArrayBuffer(receiveInstantiationResult);\n                }));\n  } else {\n    // 4-B 其它环境 fallback：先拿 ArrayBuffer 再 instantiate\n    return instantiateArrayBuffer(receiveInstantiationResult);\n  }\n}\n```\n\n---\n\n### 5. **instantiateArrayBuffer() 真正编译**\n\n```js\nfunction instantiateArrayBuffer(receiver) {\n  return getBinaryPromise()                 // 读 bytes\n    .then(binary => WebAssembly.instantiate(binary, info))\n    .then(instance => instance)             // 兼容链\n    .then(receiver, err => abort(err));\n}\n```\n\n*成品 `{module, instance}` 传给 `receiveInstantiationResult` → `receiveInstance` → **填充 Module.asm**。*\n\n---\n\n### 6. **依赖计数 & ready Promise**\n\n```js\nvar runDependencies = 0;\nfunction addRunDependency(id)   { runDependencies++; … }\nfunction removeRunDependency(id){ runDependencies--; … }\n\nModule['ready'] = new Promise((resolve, reject) => {\n  readyPromiseResolve = resolve;\n  readyPromiseReject  = reject;\n});\n```\n\n* 当 `removeRunDependency('wasm-instantiate')` 使计数归零时 → `readyPromiseResolve(Module)`。\n\n调用者就能：\n\n```ts\nconst Module = await createMyLib();   // 等同于 await Module.ready\n```\n\n---\n\n### 7. **内存、表、HEAP 视图初始化**\n\n```js\nwasmMemory = exports['memory'];               // SharedArrayBuffer\nupdateGlobalBufferAndViews(wasmMemory.buffer); // HEAP8/16/32/… 视图\n\nwasmTable  = exports['__indirect_function_table'];\n```\n\n* **HEAPU8\\[i] = …** 这样的 JS 视图现在才开始指向真正的线性内存。\n* 导出函数地址表 `wasmTable` 也绑定好，供间接调用（函数指针）使用。\n\n---\n\n### 8. **静态构造 & 运行时就绪**\n\n```js\naddOnInit(exports['__wasm_call_ctors']);  // C/C++ static constructors\n```\n\n1. `_GLOBAL__sub_I_xx` 在这一步执行。\n2. `Module.ready` Promise resolve；用户代码可以安全调用 `Module.cwrap('Multiply', …)`。\n\n\n## 需要再补充的三个环节\n\n### 1.1 生命周期钩子 *preRun / onRuntimeInitialized / postRun*\n\n```js\nModule.preRun                // 运行前：可 push(cb) 做自定义内存填充、FS 装载\nModule.onRuntimeInitialized  // createWasm 完成，静态构造也跑完；等价于 Module.ready.then\nModule.postRun               // run() 返回后触发；可做收尾清理\n```\n\n这些数组或函数在 **receiveInstance → addOnInit → run()** 之前/之后被依次调用，用来插入用户代码而不改 generated JS。\n\n---\n\n### 1.2 内存增长与 `emscripten_notify_memory_growth`\n\n* 如果在 C++ 侧调用 `emscripten_resize_heap()`，运行时会\n\n  ```javascript\n  wasmMemory.grow(pages);\n  updateGlobalBufferAndViews(...);   // 再次刷新 HEAP* 视图\n  Module['asm']['emscripten_notify_memory_growth'](...)\n  ```\n* 你的脚本若持有指向旧 `HEAPU8.buffer` 的 TypedArray，增长后必须重新获取。\n\n---\n\n### 1.3 异常捕获与 `abort()` 路径\n\n* `abort(reason)` 会\n\n  1. 触发 `Module['onAbort'] && onAbort(reason)`\n  2. `readyPromiseReject(reason)` – 让 `await createMyLib()` 直接抛错\n* 在小游戏或 Creator 里可覆写 `Module.onAbort = msg => alert(msg)` 以便调试。\n\n## 加载方式\n### 使用者侧的最简代码（浏览器 / Creator 预览）\n\n```ts\nconst { default: createMyLib } =\n   await import('./wasm/my_test_lib.js');\n\nconst Module = await createMyLib({\n  locateFile: f => new URL(`./${f}`, import.meta.url).href\n});\n\nconst mul = Module.cwrap('Multiply', 'number', ['number','number']);\nconsole.log(mul(6, 7)); // 42\n```\n\n> 在 **小游戏运行时**，只需在对象中再加一个 `instantiateWasm(imports, cb)`，里头调用 `CCWebAssembly.instantiate(…); cb(instance,module)` 即可。\n\n\nwasm被加载实例化的过程可以被定制， 定制方法是：\n\n1. 提前创建 `window.Module = { … }`\n2. 调用工厂：\n```ts\ncreateMyLib({ key:value, … });\n```\n\n   这两种都会在 `createWasm()` 执行前被合并进内部 Module。\n3. **对已实例化后才能调用的接口**（如 `Module.asm`、`HEAPU8`）\n   应挂在 `onRuntimeInitialized` / `Module.ready.then`.\n\n\n可覆写的过程包括：\n\n## 资源定位 & 实例化阶段\n\n### `locateFile` —— 改写 .wasm 真正的 URL\n\n```ts\n// 目录结构：build/web-mobile/src/wasm/xxx\nconst { default: createMyLib } = await import('./wasm/my_test_lib.js');\n\nconst Module = await createMyLib({\n  locateFile (filename: string, scriptDir: string) {\n    // filename 是 my_test_lib.wasm / my_test_lib.data\n    // scriptDir  是 my_test_lib.js 所在目录\n    return scriptDir + '/sub/' + filename;   // 指向自定义子目录\n  }\n});\n```\n\n### `instantiateWasm(imports, successCallback)` —— 完全接管实例化\n\nGlue代码：\n```js\nif (Module['instantiateWasm']) {\n\ttry {\n\t  var exports = Module['instantiateWasm'](info, receiveInstance);\n\t  return exports;\n\t} catch(e) {\n\t  err('Module.instantiateWasm callback failed with error: ' + e);\n\t  return false;\n\t}\n}\n\n// If instantiation fails, reject the module ready promise.\ninstantiateAsync().catch(readyPromiseReject);\n```\n\n```ts\n// 微信小游戏：必须走 CCWebAssembly\nconst Module = await createMyLib({\n  locateFile : (f) => new URL(`./${f}`, import.meta.url).href,\n  instantiateWasm (imports: any, success: any) {\n    // @ts-ignore\n    CCWebAssembly.instantiate(\n      new URL('./my_test_lib.wasm', import.meta.url).href,\n      imports\n    ).then(res => success(res.instance, res.module));\n    return {};            // 告诉 runtime “我已经开始实例化了”\n  }\n});\n```\n\n\n### `Module.wasmBinary` —— 预塞 Uint8Array 切断网络请求\nGlue原始代码:\n```js\nvar wasmBinary;\nif (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];legacyModuleProp('wasmBinary', 'wasmBinary');\n```\n\n```html\n<script>\n  // 已经提前用 XHR/FS 读到二进制\n  Module = {\n    wasmBinary: myArrayBuffer,   // 或 new Uint8Array(ab)\n  };\n</script>\n<script src=\"my_test_lib.js\"></script>\n```\n\n### `Module.readBinary` —— Node/Electron 里同步读文件\n\n```js\nglobal.Module = {\n  readBinary: path => require('fs').readFileSync(path, null).buffer\n};\nrequire('./my_test_lib.js');    // 同步实例化\n```\n\n---\n\n## 运行时行为\n\n### `noExitRuntime` —— 允许 / 禁止退出\n\n```js\nModule = {\n  noExitRuntime: false   // 调用 main 返回后允许 runtime 彻底退出\n}\n```\n\n### `preRun / postRun` —— 追加任务队列\n\n```js\nModule.preRun  = [ () => console.log('preRun1') ];\nModule.postRun = (Module.postRun || []);\nModule.postRun.push(() => console.log('cleanup done'));\n```\n\n> 放数组则顺序执行；放函数则当作数组的单元素。\n\n### `onRuntimeInitialized` —— “等价 ready” 同步钩子\n\n```ts\nconst { default: createMyLib } = await import('./wasm/my_test_lib.js');\n\nconst mod = await createMyLib({\n  onRuntimeInitialized () {\n    // 这里已经可安全访问 cwrap\n    const add = mod.cwrap('Add', 'number', ['number','number']);\n    console.log(add(1, 2));\n  }\n});\n```\n\n### 自定义日志 & 报错\n\n```js\nModule.print     = txt => console.log('[WASM]', txt);\nModule.printErr  = txt => console.warn('[ERR]', txt);\nModule.onAbort   = reason => {\n  reportCrashToServer(reason);\n};\n```\n\n## 内存与线程\n\n### `INITIAL_MEMORY`（编译期）+ `Module.TOTAL_MEMORY`（运行时旧接口）\n\n```bash\nemcc foo.cpp -s INITIAL_MEMORY=64MB -o foo.js\n```\n\n若没重新编译只能用旧接口：\n\n```js\nModule.TOTAL_MEMORY = 64 * 1024 * 1024;\n```\n\n> 必须在加载脚本前写入；否则已创建的 `WebAssembly.Memory` 容量无法改变。\n\n### `allowMemoryGrowth` + 运行时增长\n\n```bash\nemcc foo.cpp -s ALLOW_MEMORY_GROWTH=1 -o foo.js\n```\n\n```js\nModule = { allowMemoryGrowth: true };\n```\n\n在 C++ 侧：\n\n```cpp\nemscripten_resize_heap(128 * 1024 * 1024);   // 增至 128 MiB\n```\n\nJS 侧会触发 `emscripten_notify_memory_growth()`，\n`updateGlobalBufferAndViews()` 自动刷新 `HEAP*` 视图——无需手动介入。\n\n---\n\n## 定制文件系统 / 虚拟包\n\n### 在 `preRun` 中安装文件\n\n```js\nModule.preRun = Module.preRun || [];\nModule.preRun.push(function () {\n  // 把浏览器 File 对象挂到 /input.bin\n  FS.writeFile('/input.bin', new Uint8Array(fileArrayBuffer));\n});\n```\n\n游戏启动后就能在 C++ 里 `fopen(\"/input.bin\",\"rb\")`.\n\n## Cocos 覆写的过程\n\n\n## wasm 插件配置方案\n"
}
