Skip to content

Commit e550fae

Browse files
committed
internal: avoid unnecessary call target checks
1 parent 8351c41 commit e550fae

File tree

1 file changed

+66
-72
lines changed

1 file changed

+66
-72
lines changed

internal/js_parser/js_parser.go

Lines changed: 66 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -13159,74 +13159,24 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1315913159
return js_ast.Expr{Loc: expr.Loc, Data: js_ast.EUndefinedShared}, exprOut{}
1316013160
}
1316113161

13162-
// Recognize "require.resolve()" calls
13163-
if couldBeRequireResolve {
13164-
if dot, ok := e.Target.Data.(*js_ast.EDot); ok && dot.Name == "resolve" {
13165-
if id, ok := dot.Target.Data.(*js_ast.EIdentifier); ok && id.Ref == p.requireRef {
13166-
p.ignoreUsage(p.requireRef)
13167-
return p.maybeTransposeIfExprChain(e.Args[0], func(arg js_ast.Expr) js_ast.Expr {
13168-
if str, ok := e.Args[0].Data.(*js_ast.EString); ok {
13169-
// Ignore calls to require.resolve() if the control flow is provably
13170-
// dead here. We don't want to spend time scanning the required files
13171-
// if they will never be used.
13172-
if p.isControlFlowDead {
13173-
return js_ast.Expr{Loc: arg.Loc, Data: js_ast.ENullShared}
13174-
}
13175-
13176-
importRecordIndex := p.addImportRecord(ast.ImportRequireResolve, e.Args[0].Loc, helpers.UTF16ToString(str.Value), nil)
13177-
if p.fnOrArrowDataVisit.tryBodyCount != 0 {
13178-
record := &p.importRecords[importRecordIndex]
13179-
record.Flags |= ast.HandlesImportErrors
13180-
record.ErrorHandlerLoc = p.fnOrArrowDataVisit.tryCatchLoc
13181-
}
13182-
p.importRecordsForCurrentPart = append(p.importRecordsForCurrentPart, importRecordIndex)
13183-
13184-
// Create a new expression to represent the operation
13185-
return js_ast.Expr{Loc: arg.Loc, Data: &js_ast.ERequireResolveString{
13186-
ImportRecordIndex: importRecordIndex,
13187-
}}
13188-
}
13189-
13190-
// Otherwise just return a clone of the "require.resolve()" call
13191-
return js_ast.Expr{Loc: arg.Loc, Data: &js_ast.ECall{
13192-
Target: js_ast.Expr{Loc: e.Target.Loc, Data: &js_ast.EDot{
13193-
Target: p.valueToSubstituteForRequire(dot.Target.Loc),
13194-
Name: dot.Name,
13195-
NameLoc: dot.NameLoc,
13196-
}},
13197-
Args: []js_ast.Expr{arg},
13198-
}}
13199-
}), exprOut{}
13200-
}
13201-
}
13202-
}
13203-
13204-
// Recognize "Object.create()" calls
13205-
if couldBeObjectCreate {
13206-
if dot, ok := e.Target.Data.(*js_ast.EDot); ok && dot.Name == "create" {
13207-
if id, ok := dot.Target.Data.(*js_ast.EIdentifier); ok {
13208-
if symbol := &p.symbols[id.Ref.InnerIndex]; symbol.Kind == js_ast.SymbolUnbound && symbol.OriginalName == "Object" {
13209-
switch e.Args[0].Data.(type) {
13210-
case *js_ast.ENull, *js_ast.EObject:
13211-
// Mark "Object.create(null)" and "Object.create({})" as pure
13212-
e.CanBeUnwrappedIfUnused = true
13213-
}
13214-
}
13215-
}
13216-
}
13217-
}
13218-
1321913162
// "foo(1, ...[2, 3], 4)" => "foo(1, 2, 3, 4)"
1322013163
if p.options.minifySyntax && hasSpread && in.assignTarget == js_ast.AssignTargetNone {
1322113164
e.Args = inlineSpreadsOfArrayLiterals(e.Args)
1322213165
}
1322313166

13224-
// Detect if this is a direct eval. Note that "(1 ? eval : 0)(x)" will
13225-
// become "eval(x)" after we visit the target due to dead code elimination,
13226-
// but that doesn't mean it should become a direct eval.
13227-
if wasIdentifierBeforeVisit {
13228-
if id, ok := e.Target.Data.(*js_ast.EIdentifier); ok {
13229-
if symbol := p.symbols[id.Ref.InnerIndex]; symbol.OriginalName == "eval" {
13167+
switch t := target.Data.(type) {
13168+
case *js_ast.EImportIdentifier:
13169+
// If this function is inlined, allow it to be tree-shaken
13170+
if p.options.minifySyntax && !p.isControlFlowDead {
13171+
p.convertSymbolUseToCall(t.Ref, len(e.Args) == 1)
13172+
}
13173+
13174+
case *js_ast.EIdentifier:
13175+
// Detect if this is a direct eval. Note that "(1 ? eval : 0)(x)" will
13176+
// become "eval(x)" after we visit the target due to dead code elimination,
13177+
// but that doesn't mean it should become a direct eval.
13178+
if wasIdentifierBeforeVisit {
13179+
if symbol := p.symbols[t.Ref.InnerIndex]; symbol.OriginalName == "eval" {
1323013180
e.IsDirectEval = true
1323113181

1323213182
// Pessimistically assume that if this looks like a CommonJS module
@@ -13259,16 +13209,7 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1325913209
}
1326013210
}
1326113211
}
13262-
}
13263-
13264-
switch t := target.Data.(type) {
13265-
case *js_ast.EImportIdentifier:
13266-
// If this function is inlined, allow it to be tree-shaken
13267-
if p.options.minifySyntax && !p.isControlFlowDead {
13268-
p.convertSymbolUseToCall(t.Ref, len(e.Args) == 1)
13269-
}
1327013212

13271-
case *js_ast.EIdentifier:
1327213213
// Copy the call side effect flag over if this is a known target
1327313214
if t.CallCanBeUnwrappedIfUnused {
1327413215
e.CanBeUnwrappedIfUnused = true
@@ -13280,6 +13221,59 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1328013221
}
1328113222

1328213223
case *js_ast.EDot:
13224+
// Recognize "require.resolve()" calls
13225+
if couldBeRequireResolve && t.Name == "resolve" {
13226+
if id, ok := t.Target.Data.(*js_ast.EIdentifier); ok && id.Ref == p.requireRef {
13227+
p.ignoreUsage(p.requireRef)
13228+
return p.maybeTransposeIfExprChain(e.Args[0], func(arg js_ast.Expr) js_ast.Expr {
13229+
if str, ok := e.Args[0].Data.(*js_ast.EString); ok {
13230+
// Ignore calls to require.resolve() if the control flow is provably
13231+
// dead here. We don't want to spend time scanning the required files
13232+
// if they will never be used.
13233+
if p.isControlFlowDead {
13234+
return js_ast.Expr{Loc: arg.Loc, Data: js_ast.ENullShared}
13235+
}
13236+
13237+
importRecordIndex := p.addImportRecord(ast.ImportRequireResolve, e.Args[0].Loc, helpers.UTF16ToString(str.Value), nil)
13238+
if p.fnOrArrowDataVisit.tryBodyCount != 0 {
13239+
record := &p.importRecords[importRecordIndex]
13240+
record.Flags |= ast.HandlesImportErrors
13241+
record.ErrorHandlerLoc = p.fnOrArrowDataVisit.tryCatchLoc
13242+
}
13243+
p.importRecordsForCurrentPart = append(p.importRecordsForCurrentPart, importRecordIndex)
13244+
13245+
// Create a new expression to represent the operation
13246+
return js_ast.Expr{Loc: arg.Loc, Data: &js_ast.ERequireResolveString{
13247+
ImportRecordIndex: importRecordIndex,
13248+
}}
13249+
}
13250+
13251+
// Otherwise just return a clone of the "require.resolve()" call
13252+
return js_ast.Expr{Loc: arg.Loc, Data: &js_ast.ECall{
13253+
Target: js_ast.Expr{Loc: e.Target.Loc, Data: &js_ast.EDot{
13254+
Target: p.valueToSubstituteForRequire(t.Target.Loc),
13255+
Name: t.Name,
13256+
NameLoc: t.NameLoc,
13257+
}},
13258+
Args: []js_ast.Expr{arg},
13259+
}}
13260+
}), exprOut{}
13261+
}
13262+
}
13263+
13264+
// Recognize "Object.create()" calls
13265+
if couldBeObjectCreate && t.Name == "create" {
13266+
if id, ok := t.Target.Data.(*js_ast.EIdentifier); ok {
13267+
if symbol := &p.symbols[id.Ref.InnerIndex]; symbol.Kind == js_ast.SymbolUnbound && symbol.OriginalName == "Object" {
13268+
switch e.Args[0].Data.(type) {
13269+
case *js_ast.ENull, *js_ast.EObject:
13270+
// Mark "Object.create(null)" and "Object.create({})" as pure
13271+
e.CanBeUnwrappedIfUnused = true
13272+
}
13273+
}
13274+
}
13275+
}
13276+
1328313277
// Copy the call side effect flag over if this is a known target
1328413278
if t.CallCanBeUnwrappedIfUnused {
1328513279
e.CanBeUnwrappedIfUnused = true

0 commit comments

Comments
 (0)