Skip to content

[duplicate-code] Duplicate Code Pattern: tryCallWasmFunction Dual-Path Result Handling #6384

@github-actions

Description

@github-actions

🔍 Duplicate Code Pattern: tryCallWasmFunction Dual-Path Result Handling

Part of duplicate code analysis: #6383

Summary

tryCallWasmFunction in internal/guard/wasm_parse.go contains two execution paths (allocator path and direct-memory fallback path) that share ~30 lines of identical result-handling logic. Any bug fix or protocol change applied to one path must be manually mirrored in the other.

Duplication Details

Pattern: Duplicated WASM call + result-decode block

  • Severity: High
  • Occurrences: 2 (allocator path ~lines 220–251, direct path ~lines 292–333)
  • Locations:
    • internal/guard/wasm_parse.go (allocator path, lines ~220–251)
    • internal/guard/wasm_parse.go (direct memory path, lines ~292–333)

Identical block in both paths:

results, err := fn.Call(ctx,
    uint64(inputPtr),
    uint64(inputSize),
    uint64(outputPtr),
    uint64(outputSize))
if err != nil {
    return nil, 0, fmt.Errorf("WASM function call failed: %w", err)
}

resultLen := int32(results[0])
if resultLen == -2 {
    if requiredSize, ok := mem.ReadUint32Le(outputPtr); ok && requiredSize > 0 {
        return nil, requiredSize, nil
    }
    return nil, 0, nil
}
if resultLen < 0 {
    return nil, 0, fmt.Errorf("WASM function returned error code: %d", resultLen)
}
if resultLen == 0 {
    return []byte{}, 0, nil
}
outputJSON, ok := mem.Read(outputPtr, uint32(resultLen))
if !ok {
    return nil, 0, fmt.Errorf("failed to read output from WASM memory (len=%d)", resultLen)
}
resultCopy := append([]byte(nil), outputJSON...)
return resultCopy, 0, nil

The two paths differ only in how inputPtr and outputPtr are set up (via wasmAlloc vs. direct linear memory layout). The actual call + result handling is character-for-character identical.

Impact Analysis

  • Maintainability: Any change to the buffer-too-small protocol (-2 return code), error code interpretation, or output-copy strategy requires the same edit in two places. The recent callWasmFunction test coverage work explicitly highlighted this function's complexity.
  • Bug Risk: High — a fix applied only to one path silently leaves the other broken. Already seen: the allocator path has a comment // Copy out of WASM linear memory before deferred dealloc runs. while the direct path says // to avoid aliasing with future calls. — slight comment drift indicating the paths are evolving independently.
  • Code Bloat: ~30 duplicated lines, making the 148-line function harder to audit.

Refactoring Recommendations

  1. Extract executeWasmCall helper

    • Create a private function: func executeWasmCall(ctx context.Context, fn api.Function, mem api.Memory, inputPtr, inputSize, outputPtr, outputSize uint32) ([]byte, uint32, error)
    • Move the shared call + result-decode block into this helper
    • Both paths call this helper after setting up their respective pointers
    • Estimated effort: ~1 hour
    • Benefits: single place to maintain buffer protocol logic; easier to test in isolation
  2. Add a dedicated unit test for executeWasmCall

    • The new helper encapsulates all result-code handling; it can be tested directly without full WASM module setup

Implementation Checklist

  • Extract common fn.Call + result-decode logic into executeWasmCall helper
  • Update allocator path to call helper after wasmAlloc
  • Update direct-memory path to call helper after buffer layout
  • Ensure both deferred wasmDealloc calls still run correctly
  • Add/update unit tests for the extracted helper
  • Verify no functionality broken (make test-all)

Parent Issue

See parent analysis report: #6383
Related to #6383

Generated by Duplicate Code Detector · ● 1.2M ·

  • expires on May 31, 2026, 3:40 AM UTC

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions