Skip to content

Commit

Permalink
JS: parse comma-operator expressions as a list not a tree, fixes #451
Browse files Browse the repository at this point in the history
  • Loading branch information
tdewolff committed Jan 13, 2022
1 parent f2684cf commit e1eff7a
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 28 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ require (
github.com/fsnotify/fsnotify v1.5.1
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2
github.com/spf13/pflag v1.0.5
github.com/tdewolff/parse/v2 v2.5.26
github.com/tdewolff/parse/v2 v2.5.27
github.com/tdewolff/test v1.0.6
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2 h1:JAEbJn3j/FrhdWA9jW8
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/tdewolff/parse/v2 v2.5.26 h1:a/q3lwDCi4GIQ+sSbs4UOHuObhqp8GHAhfqop/zDyQQ=
github.com/tdewolff/parse/v2 v2.5.26/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
github.com/tdewolff/parse/v2 v2.5.27 h1:PL3LzzXaOpmdrknnOlIeO2muIBHAwiKp6TxN1RbU5gI=
github.com/tdewolff/parse/v2 v2.5.27/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
Expand Down
17 changes: 12 additions & 5 deletions js/js.go
Original file line number Diff line number Diff line change
Expand Up @@ -651,10 +651,10 @@ func (m *jsMinifier) minifyArrowFunc(decl *js.ArrowFunc) {
if removeBraces {
list = append(list, returnStmt.Value)
expr := list[0]
for _, right := range list[1:] {
expr = &js.BinaryExpr{Op: js.CommaToken, X: expr, Y: right}
}
if 0 < len(list) {
if 1 < len(list) {
expr = &js.CommaExpr{list}
}
expr = &js.GroupExpr{X: expr}
}
m.expectExpr = expectExprBody
Expand Down Expand Up @@ -896,7 +896,7 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
// convert (a,b)&&c into a,b&&c but not a=(b,c)&&d into a=(b,c&&d)
if prec <= js.OpExpr {
if group, ok := expr.X.(*js.GroupExpr); ok {
if binary, ok := group.X.(*js.BinaryExpr); ok && binary.Op == js.CommaToken && js.OpAnd <= exprPrec(binary.Y) {
if comma, ok := group.X.(*js.CommaExpr); ok && js.OpAnd <= exprPrec(comma.List[len(comma.List)-1]) {
expr.X = group.X
precLeft = js.OpExpr
}
Expand Down Expand Up @@ -1250,7 +1250,7 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
// convert (a,b)?c:d => a,b?c:d
if prec <= js.OpExpr {
if group, ok := expr.Cond.(*js.GroupExpr); ok {
if binary, ok := group.X.(*js.BinaryExpr); ok && binary.Op == js.CommaToken && js.OpCoalesce <= exprPrec(binary.Y) {
if comma, ok := group.X.(*js.CommaExpr); ok && js.OpCoalesce <= exprPrec(comma.List[len(comma.List)-1]) {
expr.Cond = group.X
}
}
Expand Down Expand Up @@ -1302,5 +1302,12 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
m.inFor = false
m.minifyClassDecl(expr)
m.inFor = parentInFor
case *js.CommaExpr:
for i, item := range expr.List {
if i != 0 {
m.write(commaBytes)
}
m.minifyExpr(item, js.OpAssign)
}
}
}
1 change: 1 addition & 0 deletions js/js_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,7 @@ func TestJS(t *testing.T) {
{`a<<!--script`, `a<<! --script`},
{`a<</script>/`, `a<< /script>/`},
{`function f(a,b){a();for(const c of b){const b=0}}`, `function f(a,b){a();for(const c of b){const b=0}}`},
{`return a,b,void 0`, `return a,b`},

// bugs
{`({"":a})`, `({"":a})`}, // go-fuzz
Expand Down
33 changes: 16 additions & 17 deletions js/stmtlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,10 @@ func (m *jsMinifier) optimizeStmt(i js.IStmt) js.IStmt {
YReturn, isReturnElse := ifStmt.Else.(*js.ReturnStmt)
if isReturnBody && isReturnElse {
if XReturn.Value == nil && YReturn.Value == nil {
return &js.ReturnStmt{Value: &js.BinaryExpr{
Op: js.CommaToken,
X: ifStmt.Cond,
Y: &js.UnaryExpr{
Op: js.VoidToken,
X: &js.LiteralExpr{TokenType: js.NumericToken, Data: zeroBytes},
},
}}

return &js.ReturnStmt{Value: commaExpr(ifStmt.Cond, &js.UnaryExpr{
Op: js.VoidToken,
X: &js.LiteralExpr{TokenType: js.NumericToken, Data: zeroBytes},
})}
} else if XReturn.Value != nil && YReturn.Value != nil {
return &js.ReturnStmt{Value: condExpr(ifStmt.Cond, XReturn.Value, YReturn.Value)}
}
Expand Down Expand Up @@ -142,13 +137,13 @@ func (m *jsMinifier) optimizeStmtList(list []js.IStmt, blockType blockType) []js
// merge expression statements with expression, return, and throw statements
if left, ok := list[i-1].(*js.ExprStmt); ok {
if right, ok := list[i].(*js.ExprStmt); ok {
right.Value = &js.BinaryExpr{Op: js.CommaToken, X: left.Value, Y: right.Value}
right.Value = commaExpr(left.Value, right.Value)
j--
} else if returnStmt, ok := list[i].(*js.ReturnStmt); ok && returnStmt.Value != nil {
returnStmt.Value = &js.BinaryExpr{Op: js.CommaToken, X: left.Value, Y: returnStmt.Value}
returnStmt.Value = commaExpr(left.Value, returnStmt.Value)
j--
} else if throwStmt, ok := list[i].(*js.ThrowStmt); ok {
throwStmt.Value = &js.BinaryExpr{Op: js.CommaToken, X: left.Value, Y: throwStmt.Value}
throwStmt.Value = commaExpr(left.Value, throwStmt.Value)
j--
} else if forStmt, ok := list[i].(*js.ForStmt); ok {
if varDecl, ok := forStmt.Init.(*js.VarDecl); ok && len(varDecl.List) == 0 || forStmt.Init == nil {
Expand All @@ -168,13 +163,13 @@ func (m *jsMinifier) optimizeStmtList(list []js.IStmt, blockType blockType) []js
list[i] = &js.ForStmt{Init: left.Value, Cond: whileStmt.Cond, Post: nil, Body: body}
j--
} else if switchStmt, ok := list[i].(*js.SwitchStmt); ok {
switchStmt.Init = &js.BinaryExpr{Op: js.CommaToken, X: left.Value, Y: switchStmt.Init}
switchStmt.Init = commaExpr(left.Value, switchStmt.Init)
j--
} else if withStmt, ok := list[i].(*js.WithStmt); ok {
withStmt.Cond = &js.BinaryExpr{Op: js.CommaToken, X: left.Value, Y: withStmt.Cond}
withStmt.Cond = commaExpr(left.Value, withStmt.Cond)
j--
} else if ifStmt, ok := list[i].(*js.IfStmt); ok {
ifStmt.Cond = &js.BinaryExpr{Op: js.CommaToken, X: left.Value, Y: ifStmt.Cond}
ifStmt.Cond = commaExpr(left.Value, ifStmt.Cond)
j--
}
} else if left, ok := list[i-1].(*js.VarDecl); ok {
Expand Down Expand Up @@ -274,9 +269,13 @@ func (m *jsMinifier) optimizeStmtList(list []js.IStmt, blockType blockType) []js
if returnStmt, ok := list[j-1].(*js.ReturnStmt); ok {
if returnStmt.Value == nil || m.isUndefined(returnStmt.Value) {
j--
} else if binaryExpr, ok := returnStmt.Value.(*js.BinaryExpr); ok && binaryExpr.Op == js.CommaToken && m.isUndefined(binaryExpr.Y) {
} else if commaExpr, ok := returnStmt.Value.(*js.CommaExpr); ok && m.isUndefined(commaExpr.List[len(commaExpr.List)-1]) {
// rewrite function f(){return a,void 0} => function f(){a}
list[j-1] = &js.ExprStmt{Value: binaryExpr.X}
if len(commaExpr.List) == 2 {
list[j-1] = &js.ExprStmt{Value: commaExpr.List[0]}
} else {
commaExpr.List = commaExpr.List[:len(commaExpr.List)-1]
}
}
}
} else if blockType == iterationBlock {
Expand Down
19 changes: 16 additions & 3 deletions js/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,19 @@ func condExpr(cond, x, y js.IExpr) js.IExpr {
}
}

func commaExpr(x, y js.IExpr) js.IExpr {
comma, ok := x.(*js.CommaExpr)
if !ok {
comma = &js.CommaExpr{List: []js.IExpr{x}}
}
if comma2, ok := y.(*js.CommaExpr); ok {
comma.List = append(comma.List, comma2.List...)
} else {
comma.List = append(comma.List, y)
}
return comma
}

func (m *jsMinifier) isEmptyStmt(stmt js.IStmt) bool {
if stmt == nil {
return true
Expand Down Expand Up @@ -323,11 +336,11 @@ func finalExpr(expr js.IExpr) js.IExpr {
if group, ok := expr.(*js.GroupExpr); ok {
expr = group.X
}
if binary, ok := expr.(*js.BinaryExpr); ok && binary.Op == js.CommaToken {
expr = binary.Y
if comma, ok := expr.(*js.CommaExpr); ok {
expr = comma.List[len(comma.List)-1]
}
if binary, ok := expr.(*js.BinaryExpr); ok && binary.Op == js.EqToken {
expr = binary.X
expr = binary.X // return first
}
return expr
}
Expand Down

0 comments on commit e1eff7a

Please sign in to comment.