Skip to content

Conversation

@sadspirit
Copy link

Issue summary

The current setup uses:

"rollup": "^4.27.4"

This Rollup version still includes object tree-shaking, a feature known to be problematic because it can silently remove valid runtime code, resulting in broken builds and hard-to-detect logic errors.

What happens

During bundling, Rollup’s object tree-shaking analyzes and removes code it believes has no side effects on exports.
However, in many real-world cases (especially in physics or math-heavy libraries), it mistakenly strips out essential conditional logic.

This leads to invisible functional corruption — code still runs, but with degraded logic or performance.

Example: Physics engine (Cannon.js)

When building with the physics engine cannon.js, the following original function is part of the SAT (Separating Axis Theorem) algorithm:

ConvexPolyhedron.prototype.testSepAxis = function(axis, hullB, posA, quatA, posB, quatB){
    var hullA = this;
    ConvexPolyhedron.project(hullA, axis, posA, quatA, maxminA);
    ConvexPolyhedron.project(hullB, axis, posB, quatB, maxminB);
    var maxA = maxminA[0];
    var minA = maxminA[1];
    var maxB = maxminB[0];
    var minB = maxminB[1];
    if (maxA < minB || maxB < minA) {
        return false; // Separated — no collision
    }
    var d0 = maxA - minB;
    var d1 = maxB - minA;
    var depth = d0 < d1 ? d0 : d1;
    return depth;
};

This is a performance-critical early-exit check:
if the objects are not intersecting, the function returns early to avoid heavy collision computations.

After Rollup’s tree-shaking, the generated code becomes:

ConvexPolyhedron.prototype.testSepAxis = function(axis, hullB, posA, quatA, posB, quatB){
    var hullA = this;
    ConvexPolyhedron.project(hullA, axis, posA, quatA, maxminA);
    ConvexPolyhedron.project(hullB, axis, posB, quatB, maxminB);
    var maxA = maxminA[0];
    var minA = maxminA[1];
    var maxB = maxminB[0];
    var minB = maxminB[1];
    var d0 = maxA - minB;
    var d1 = maxB - minA;
    var depth = d0 < d1 ? d0 : d1;
    return depth;
};

Notice that the early return (return false) has been removed.
As a result, every frame performs full collision clipping for all object pairs, causing a 3–4× performance regression.

This behavior occurs regardless of variable names or code formatting, since Rollup analyzes the AST and consistently removes such early exits when it deems them “unused.”

Impact

•	Performance degradation: up to 300–400% slower physics computations.
•	Silent corruption: logic remains syntactically valid, but semantically broken.
•	Non-deterministic builds: the issue may affect different parts of the codebase unpredictably.

This issue is not limited to cannon.js; any library using imperative object-oriented logic (e.g., physics, math, or geometry code) may be affected.

Resolution

  • Upgrade Rollup to a version where object tree-shaking is disabled by default.
    The issue is resolved in newer releases after PR #5736.
  • I have also submitted a pull request to update the rollup version in this project.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant