Skip to content

Fix multiple cases of bracket and semicolon mishandling#89

Open
9382 wants to merge 3 commits intomathiasbynens:masterfrom
9382:master
Open

Fix multiple cases of bracket and semicolon mishandling#89
9382 wants to merge 3 commits intomathiasbynens:masterfrom
9382:master

Conversation

@9382
Copy link

@9382 9382 commented Aug 9, 2024

Fix prefix expressions not being bracketed when required

Now code like (nil)() or ("")"" will stop producing invalid syntax

Not sure why the test cases were configured to see 1() or true() as valid, as calling a literal like this is invalid syntax

Based on prefixexp ::= var | functioncall | `(´ exp `)´ (lua 5.1 docs)

Fixes #87
Fixes #48

Fix truncation via parenthesis being removed when it shouldn't be

Fix (f()) being shortened to f(), even though these aren't actually the same (In lua, if a call expression is surrounded by brackets, its result will always be truncated to one value, which can be an important difference)

Solves the first issue of #88

Fix semicolons not being written in potential ambiguous syntax cases

Fixes the case where a = b; ({})(c) (and similar statement combinations) would be written without the semicolon when minified, causing it to become one large statement

The only way to start a statement with anything other than an alphanumeric (+ underscore) character is a call statement that has its base wrapped in brackets (see stat ::= BNF), so if we see this occur we make sure the end of our first statement couldn't be construed as a prefixexp (which I've just considered might encompass every possible ending to a statement)

Not perfect in terms of minification since it just considers what the last character is instead of its context (e.g. do end;({})() is the same with or without the semicolon), but we only pass the resultant string, not the statements involved, so I don't think this can be easily improved

Fully fixes #88

9382 added 3 commits August 9, 2024 21:01
Not sure why the test cases are configured to see 1() or true() as valid, as they aren't

Based on prefixexp ::= var | functioncall | `(´ exp `)´ (lua 5.1 docs)
In lua, f(x) and (f(x)) are different; (f(x)) will always be truncated to one value, while f(x) won't be

This implementation is imperfect in terms of minification, as the brackets will be kept in cases where its not needed
For example, `local x, y = (f()), nil` would not need to keep the brackets, but we do anyways. Still correct code, just not perfectly written

(Also oops, already broke a test case set up just a commit ago)
Guarantee a ; is present when required to avoid accidentally turning 2 statements into one larger statement, e.g. the difference between a=b;(c or d)(e) and a=b(c or d)(e)

Characters checked for are determined via what can constitute a valid functioncall (essentially if the ending of our first statement could be seen as a prefixexp, which would be any var (Name, a[]) or functioncall (a(), a"", a'', a[[]], a{}) or bracketed expression (a)), since a CallStatement is the only way to start a statement with a non-alphanumeric character
@9382 9382 changed the title Fix multiple cases of bracket mishandling Fix multiple cases of bracket and semicolon mishandling Aug 10, 2024
@cohler
Copy link

cohler commented Aug 10, 2024

Wow. That looks great. I will try it out your new commits and let you know how it goes.

I have luamin installed using node, fyi, and I invoke it from the command line in various build scripts that I use which then minify large numbers of files. Because of all the file opening, writing, and closing, the process it is quite slow and occupies 90% or more of my entire project build time.

Is there any way to improve that invocation speed?

@9382
Copy link
Author

9382 commented Aug 10, 2024

I could certainly have a look to see if I could figure out any performance improvements, but I don't often mess with javascript or nodejs, so I can't guarantee I'll be able to improve anything
I will note that, from some minor testing on the existing luamin testcases, it appears the majority of execution time (>85%) might be getting consumed by luaparse instead of luamin. Upgrading luaparse to 0.3.1 would improve that greatly (~40-50% faster), but unfortunately that can't happen without changes in luaparse to bring back an inParens equivilant

@cohler
Copy link

cohler commented Aug 10, 2024

Thanks. I tried out your version with the latest fixes and the ; and () problems seem to be fixed!! In the coming days, I'll test it on a big codebase and let you know if there are any problems. Thank you for your work on this!

return a + b;
}
if (separator == ';' && firstCharB == '(') {
if (
Copy link
Author

@9382 9382 Feb 14, 2025

Choose a reason for hiding this comment

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

Note: To expand further on the last 2 paragraphs in my initial message, this entire if actually ended up being a collection of every valid ending to a lua statement, and so will always return true. There are times where this optimsation is possible (E.g. t={};(a or b)(c) is 2 statements with or without a ;), but we can't be 100% sure from the last character alone it wasn't something like t=f{};(a or b)(c) instead (which would change its meaning with no ;), so we'd need to know the exact statement instead of just the 2 peices of text to be joined, and we just don't provide that right now

bucket3432 added a commit to butterfansubs/aegsc that referenced this pull request Apr 23, 2025
luamin stable incorrectly removes parentheses and semicolons in cases
where their removal changes the behaviour of the code.

Switch to the version from pull request #89, which fixes these issues.
mathiasbynens/luamin#89
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.

INCORRECT handling of () parens and ; semicolon incorrect handling of brackets when combined with array access Indexing string literals

2 participants