Theme / v1.6.0

Codegraph

程式碼知識圖譜工具

實戰範例

實戰範例 003:追蹤 Rust WebAssembly 與 JS 跨語言圖譜呼叫鏈

實戰演練如何利用 Codegraph 跨語言邊界探索,穿透 Rust WebAssembly (Wasm) 與 JavaScript 橋接,排查資料溢出 Bug。

實戰背景

在高效能 Web 應用中,為了進行複雜的影像處理或數學運作,我們常將 Rust 寫寫的核心算法編譯為 WebAssembly (Wasm),並透過 wasm-bindgen 自動生成的 JS 膠水代碼(Glue Code)向前端 TypeScript 提供 API。

痛點與問題描述: 前端網頁在進行大量數據統計時,調用 Wasm 的 compute_fft 快速傅立葉變換算法會偶發回傳 NaN(非數值)錯誤,導致前端圖表崩潰。 由於代碼跨越了 JavaScript 執行緒與 Rust 記憶體沙箱(Wasm Linear Memory),傳統的瀏覽器除錯器(Chrome DevTools)在執行到 Wasm 邊界時會退化為彙編指令集(WebAssembly Text Format),完全無法追蹤 Rust 內部的 AST 呼叫鏈。因此,我們需要利用 Codegraph 穿透 Wasm 橋接巨集,串聯起跨語言的完整依賴圖譜以進行排查。


規格定義

本次變更計畫的 ID 為 wasm-fft-fix。我們在 OpenSpec 中定義了以下驗收標準:

# 實戰範例 003:openspec/changes/wasm-fft-fix/specs/wasm-bridge/spec.md

## ADDED/MODIFIED Requirements

### Requirement: Robust Wasm FFT input validation
Rust 端的 `compute_fft` 函式必須(SHALL)防禦性驗證輸入數組。
- 若輸入數組長度不為 2 的冪次方,或包含非數值,必須(SHALL)安全返回錯誤狀態碼或錯誤型別,嚴禁引發 Rust 內部 panic 或返回 `NaN`

#### Scenario: Verify Wasm compilation and execution
- **WHEN** 前端傳送非 2 的冪次長度數組至 Wasm
- **THEN** 橋接層必須(SHALL)安全攔截,前端收到明細的 `ERR_INVALID_LEN` 錯誤碼而非崩潰。

端到端 8 輪 AI 協作紀錄

以下是 You (開發者) 與 AI 助手穿透 Wasm 邊界進行 Debug 的對話歷史。

── 第 1 輪:定位前端 Wasm 呼叫點 ──

  • You:
    /opsx:explore "前端呼叫 Wasm 算法時出現了 NaN 崩潰。請用 codegraph explore 搜尋前端調用 compute_fft 的進入點。"
  • AI:

    好的,已進入探索模式。我將呼叫 codegraph_explore 尋找 TypeScript 前端程式碼中對 compute_fft 的引用。

    CG 呼叫: codegraph_explore(symbol: "compute_fft")
    CG 回傳:

    • 定義位置: src/frontend/charts/FftPanel.ts (第 45 行)
    • 原始碼:
      import { compute_fft } from "wasm-charts";
      
      const updateChart = (data: Float32Array) => {
          const result = compute_fft(data); // 呼叫點
          renderChart(result);
      };

    分析
    前端透過引進 wasm-charts(Wasm 膠水套件)調用了 compute_fft。由於這是一個 Wasm 導出方法,接下來我需要跳過 JS 膠水代碼,直接在 Rust 專案中尋找與此名稱對應的導出定義。


── 第 2 輪:穿透 Wasm-Bindgen 尋找 Rust 定義 ──

  • You:
    使用 codegraph explore 尋找 Rust 專案中導出為 compute_fft 的 #[wasm_bindgen] 標記。
  • AI:

    好的,我將呼叫 codegraph_explore。Codegraph 內置的跨語言引擎能夠感知 Rust 的 wasm_bindgen 屬性,並將其與 JS 端的呼叫進行鏈接。

    CG 呼叫: codegraph_explore(symbol: "#[wasm_bindgen] pub fn compute_fft")
    CG 回傳:

    • 定義檔案: src/wasm/src/fft.rs
    • 原始碼:
      #[wasm_bindgen]
      pub fn compute_fft(data: &[f32]) -> Vec<f32> {
          let mut input = data.to_vec();
          fft_algorithm::run(&mut input); // 呼叫底層 Rust 演算法庫
          input
      }

    分析
    原生實作位於 Rust fft.rs 檔案中。該方法直接調用了 fft_algorithm::run

    我們需要進一步探索 Rust 底層 fft_algorithm::run 方法,看看它在什麼情況下會產生 NaN 錯誤。


── 第 3 輪:追蹤 Rust 演算法庫 ──

  • You:
    使用 codegraph explore 探索 Rust 演算法庫中的 run 方法實作。
  • AI:

    我呼叫 codegraph_explore 深入 Rust 底層演算邏輯。

    CG 呼叫: codegraph_explore(symbol: "fn run")
    CG 回傳:

    • 定義檔案: src/wasm/src/fft_algorithm.rs (第 12 行到第 28 行)
    • 原始碼:
      pub fn run(data: &mut Vec<f32>) {
          let n = data.len();
          // 致命假設:假設長度必定是 2 的冪次方,否則在 log2 計算時會產生無窮值
          let levels = (n as f32).log2() as usize; 
          
          for i in 0..levels {
              // 進行蝴蝶變換運算,若 n 不是 2 的冪,此處的索引運算會引發 NaN 溢出
          }
      }

    診斷分析
    找到了 Bug 根因!在 fft_algorithm.rs 中,程式碼直接執行了 (n as f32).log2()。如果輸入數組長度 n 不是 2 的冪次方(例如傳入了 100 筆數據,而非 64 或 128),log2() 的結果會是一個浮點數,強轉為 usize 後會導致後續的蝴蝶變換索引計算完全錯亂,進而產生 NaN 溢出。

    調查完成,我退出探索模式,為您建立變更計畫。


── 第 4 輪:建立提案 ──

  • You:
    /opsx:propose "修復 Rust Wasm FFT 算法中的長度溢出 Bug,在橋接層限制輸入長度必須為 2 的冪"
  • AI:

    好的,提案 wasm-fft-fix 已成功建立!

    • proposal.md: 說明 Wasm 邊界長度校驗的防禦性設計。
    • specs/wasm-bridge/spec.md: 定義邊界值校驗與明細錯誤碼。
    • tasks.md: 任務如下:
      • 1.1 在 Rust 端 fft.rscompute_fft 中加入長度校驗,若非 2 的冪,傳回帶有錯誤標記的陣列。
      • 1.2 前端 FftPanel.ts 捕獲錯誤標記並給予警告。
      • 1.3 執行 Cargo 測試與 Astro 建置。

    請輸入 /opsx:apply 開始實作。


── 第 5 輪:重構 Rust Wasm 導出函數 (Task 1.1) ──

  • You:
    /opsx:apply
  • AI:

    Implementing: wasm-fft-fix (schema: spec-driven)

    正在處理任務 1.1:在 Rust 導出方法中實作防禦校驗。

    我更新了 src/wasm/src/fft.rs,加入 is_power_of_two 的防禦性檢查。若長度不合規,在第一個元素寫入 -1.0(作為錯誤訊記):

    #[wasm_bindgen]
    pub fn compute_fft(data: &[f32]) -> Vec<f32> {
        let n = data.len();
        if n == 0 || (n & (n - 1)) != 0 {
            // 傳回首元素為 -1.0 的錯誤標誌
            return vec![-1.0];
        }
        let mut input = data.to_vec();
        fft_algorithm::run(&mut input);
        input
    }

    ✓ 任務 1.1 完成!已更新 tasks.md 狀態。


── 第 6 輪:重構前端 TypeScript 捕獲 (Task 1.2) ──

  • You:
    繼續任務 1.2,更新前端 TS 檔案以處理錯誤標籤。
  • AI:

    Implementing: wasm-fft-fix

    正在處理任務 1.2:前端 JS/TS 錯誤處理。

    我已修改了 src/frontend/charts/FftPanel.ts 中的 updateChart 函式:

    const updateChart = (data: Float32Array) => {
        const result = compute_fft(data);
        if (result.length === 1 && result[0] === -1.0) {
            showNotification("ERR_INVALID_LEN", "數據長度必須是 2 的冪次方!");
            return;
        }
        renderChart(result);
    };

    ✓ 任務 1.2 完成!


── 第 7 輪:執行編譯與驗證 (Task 1.3) ──

  • You:
    繼續實作任務 1.3,編譯 Wasm、跑測試並驗證。
  • AI:

    Implementing: wasm-fft-fix

    正在處理任務 1.3:全域編譯與驗證。

    1. Rust 編譯:在 src/wasm/ 下執行 wasm-pack build,編譯無錯誤,成功更新 pkg。
    2. 前端建置:執行 pnpm run build 通過。
    3. 單元測試:傳送長度為 100 的陣列,Wasm 安全攔截並返回 -1.0,前端成功彈出「數據長度必須是 2 的冪次方」通知,無任何 NaN 崩潰!

    已將變更同步至主規格並歸檔。

    Archive Complete

    變更計畫已成功歸檔!


── 第 8 輪:跨語言重構效益總結 ──

  • You:
    總結本次使用 Codegraph 進行跨語言 Wasm 依賴追蹤的優勢。
  • AI:

    跨語言圖譜在此案例中展現出獨特的除錯優勢:

    • 打破語言孤島:Wasm 的 JS 膠水代碼通常是自動生成的混淆代碼,人工根本無法閱讀。Codegraph 能自動識別 wasm_bindgen 與 JS 調用,將 JS 與 Rust 圖譜縫合在一起。
    • 精確追蹤:如果沒有 Codegraph 的引導,James 只能在瀏覽器中看到一堆 WebAssembly 機器碼,需要耗費數天去定位 Rust 端的實作位置。現在僅用了 2 輪對話就找出了 Bug。