Skip to content

feat: improve checking terminate state in try#19130

Merged
alexander-akait merged 2 commits intowebpack:mainfrom
JSerFeng:feat/walk-dead-stmt
Apr 4, 2025
Merged

feat: improve checking terminate state in try#19130
alexander-akait merged 2 commits intowebpack:mainfrom
JSerFeng:feat/walk-dead-stmt

Conversation

@JSerFeng
Copy link
Contributor

@JSerFeng JSerFeng commented Jan 15, 2025

What kind of change does this PR introduce?

Original PR:

Add check for dead control flow

We can skip walking some branch that is ignored by dead control flow. For example

(function() {
  if (true) { return }
  require('./foo')
})()

We can skip resolve foo module. Dead control flow usually caused by DefinePlugin. Users can define a variable as bool literal so that it can be removed at compile time.

After knowing that there is already a implementation for this

Improve terminate state checking in TryStatement.

Every terminate state in TryStatement will not be passed to old scope, because catch clause can handle the error, thus outer statements can continue executing.

We can improve this by checking if we are right under TryStatement instead of if statement or for statement

Did you add tests for your changes?

Does this PR introduce a breaking change?

What needs to be documented once your changes are merged?

@webpack-bot
Copy link
Contributor

For maintainers only:

  • This needs to be documented (issue in webpack/webpack.js.org will be filed when merged)
  • This needs to be backported to webpack 4 (issue will be created when merged)

@JSerFeng JSerFeng marked this pull request as ready for review January 15, 2025 10:15
@alexander-akait
Copy link
Member

alexander-akait commented Feb 4, 2025

@JSerFeng Sorry for delay, I think you are looking for this - #15497

@alexander-akait
Copy link
Member

It is ready and tests, just forgot to merge 😞 We need just rebase and we can merge, also it solve more variants

@JSerFeng
Copy link
Contributor Author

JSerFeng commented Feb 5, 2025

Not the same #15497, this PR can bailout statements if an explicit dead control flow is detected in inner statement.

eg. current behavior is as follows

if (true) {
  return;
} else {
  inner statements
}

outer statements

Currently we only bailout inner statements inside If statement, but I want to skip the outer as well as we know exactly the rest statements is unreachable

@TheLarkInn TheLarkInn requested a review from Copilot February 10, 2025 19:27
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 4 out of 5 changed files in this pull request and generated no comments.

Files not reviewed (1)
  • package.json: Language not supported


it("should not include unused assets", (done) => {
try {
if (true) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure you have a test case for try/finally:

try {
  return;
} finally {
   // This code *will* execute after the 'return'
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JSerFeng can you add a test for this please?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dmichon-msft we have other PR for such stuff(link above), we need to merge it firstly, I will take care about it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But they can union in some places, that’s is why we need to make it step by step

@JSerFeng JSerFeng force-pushed the feat/walk-dead-stmt branch 3 times, most recently from 8d803ea to be78f47 Compare February 14, 2025 10:11
@alexander-akait
Copy link
Member

@JSerFeng Oh, very big sorry, I just noticed that I gave you the wrong link 😞 I think you are looking for #14357? Right?

@alexander-akait
Copy link
Member

I did rebase and also wrote TODO for tomorrow, will finish tomorrow too 😄

@JSerFeng
Copy link
Contributor Author

I think you are looking for #14357? Right?

Oh I see, it's similar

Do I need to do anything to help ?

@alexander-akait
Copy link
Member

No, will finish soon 👍

@alexander-akait
Copy link
Member

@JSerFeng @dmichon-msft I added basic dead control flow check (#14357), but there are some limitations currently:

  • try/catch with if:
function test() {
  try {
    if (true) {
      throw new Error("should throw");
    }
	
    require("fail");
  } catch(e) {
    require('./used');
  }
}

Now it only works then you have return instead throw inside if (without if return and throw works fine), I think we need to improve nested check here, should not be hard, I don't implement it because I don't see this code often - rare case.

  • switch (case):
function test() {
  switch ("production") {
    return require("fail");
  }
  case "production":
    return require("./used");
  default:
    return require("fail");
  }
}

Rare case, if you want you can improve. if/return/throw inside case work fine.

  • We don't have return and throw at top level scope

This limitation because of we need to handle export and module.export after return and throw otherwise our build failed when you have import { func } from "./file.js"; and output throw an error "Can't found func export". Any feedback on this welcome.

Examaple:

function func() {}

throw new Error("test"):

export { func };
  • Did I miss something?

So you can rebase and improve above cases

@JSerFeng JSerFeng force-pushed the feat/walk-dead-stmt branch 2 times, most recently from 240fb4f to ad9aec0 Compare March 5, 2025 07:03
@JSerFeng
Copy link
Contributor Author

JSerFeng commented Mar 5, 2025

Yes, I think the one, try/catch with if can be improved.

We can't change terminate state if inTry because we can't pass that terminate state outside of TryStatement, so I only check if current statement is exactly in TryStatement other than other statement

@JSerFeng JSerFeng changed the title feat: add dead control flow check feat: improve checking terminate state in try Mar 5, 2025
@JSerFeng JSerFeng force-pushed the feat/walk-dead-stmt branch 2 times, most recently from adb82e2 to 19a508b Compare March 5, 2025 08:59
// we meet other statement, we are not directly in try block
break loop;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am afraid it can be slow, can we do it only when necessary?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Especially in very big and nested code

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe we can refactor our logic and break only in statements?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be slow as it will break immediately when meets any statement that is not BlockStatement, it can only be slow if user code like ;{{{{ statement; }}}}

@alexander-akait
Copy link
Member

@JSerFeng Sorry for delay here, I made some improvements (without loop) and fix edge cases with catch/finally, feedback welcome and we can merge

@alexander-akait alexander-akait merged commit 05b39c2 into webpack:main Apr 4, 2025
62 of 63 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants