Skip to content

engine: a call through a memoize/once wrapper (memoize(() => require('./X'))()) resolves to nothing, because the wrapper's parameter merges every caller and is fan-capped #695

Closed

Description

@swapnilpaliwal-sd

Found on the development corpus (webpack at main after #694: 16 getCompilationHooks MISSED sites in lib/ModuleTemplate.js, lib/MainTemplate.js, lib/ChunkTemplate.js, plus lib/config/target.js:19 and lib/util/serialization.js:41, all through lib/util/memoize.js; 24 files define a getter with it).

const memoize = fn => {
  let cache = false, result = undefined;
  return () => { if (cache) return result; result = fn(); cache = true; fn = undefined; return result; };
};
const getJavascriptModulesPlugin = memoize(() => require("./javascript/JavascriptModulesPlugin"));
getJavascriptModulesPlugin().getCompilationHooks(compilation);   // MISSED: ambient_terminal (result holds only undefined)

The call getJavascriptModulesPlugin() resolves to the inner arrow (known_edge). Its result is result, assigned from fn(), and fn is memoize's parameter, which context-insensitively holds every function ever passed to memoize (60 across webpack). That fan is over the dispatch cap, so fn() is fan_capped, result holds only its undefined initializer, and the getter's call is an ambient terminal. Without the cap it would be a 60-wide superset. Case 38-style probe with one memoize call resolves fine (the fan is 1), so the suite does not see it.

This is the "utility that merges its callers" item of #465, in its most common concrete form: a wrapper whose returned closure's result is the result of calling one of the wrapper's parameters. A per-call-site summary would fix it exactly: when a function h returns a closure c, and c's return value is p() for a parameter p of h (possibly through a variable), then at a site h(g) the site's value is a closure whose call returns what g returns. Representing that needs a value identity per (closure, site), for instance ("func", c) paired with a site-specific return: call_result_of_wrapped(site, k, i) fed into the value of any call whose callee value came from that site's result. once, lazy, debounce-style wrappers are the same shape.

Regression: a case with two memoized getters of different modules called from one file, each resolving to its own module's member and not the other's; webpack MISSED before and after.

Activity

  1. swapnilpaliwal-sd commented on Sep 15, 2026

    @swapnilpaliwal-sd
    ContributorAuthor

    Taking this one (branch fix/695-thunk-wrapper-summary). Plan: a per-call-site summary for the one wrapper shape this is about. A function W whose return value is a closure C that returns the result of calling W's own parameter p (directly, or through a variable assigned from that call) is a thunk wrapper. A call site W(g) then yields a thunk value keyed by that call site, holding g; a call on that thunk keeps its known_edge into C and, beside it, takes the value of calling g, so getJavascriptModulesPlugin().getCompilationHooks(...) reads the module g returns. The fan inside C stays declared (fan_capped); the per-site value is what the caller needs. Validation: suite case with memoize and once over several wrapped functions, webpack's getCompilationHooks sites, and the solve time of webpack and mongoose before and after.

  2. swapnilpaliwal-sd commented on Sep 28, 2026

    @swapnilpaliwal-sd
    ContributorAuthor

    Fixed by #1648 (merged into 0.1.5).

  3. swapnilpaliwal-sd commented on Sep 28, 2026

    @swapnilpaliwal-sd
    ContributorAuthor

    Fixed by #1648 (merged into 0.1.6).

  4. swapnilpaliwal-sd commented on Sep 28, 2026

    @swapnilpaliwal-sd
    ContributorAuthor

    Fixed by #1648 (merged into 0.1.6).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingengineResolution / call-graph engine rulesjavascriptJavaScript

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions