Skip to content

Commit

Permalink
Trying to use InastanceMap
Browse files Browse the repository at this point in the history
  • Loading branch information
grantnelson-wf committed Oct 9, 2024
1 parent 3f4b0fa commit b7ce0cf
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 42 deletions.
6 changes: 3 additions & 3 deletions compiler/decls.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ func (fc *funcContext) newFuncDecl(fun *ast.FuncDecl, inst typeparams.Instance)
o := fc.pkgCtx.Defs[fun.Name].(*types.Func)
d := &Decl{
FullName: o.FullName(),
Blocking: fc.pkgCtx.IsBlocking(o),
Blocking: fc.pkgCtx.IsBlocking(inst),
LinkingName: symbol.New(o),
}

Expand Down Expand Up @@ -355,7 +355,7 @@ func (fc *funcContext) newFuncDecl(fun *ast.FuncDecl, inst typeparams.Instance)
func (fc *funcContext) callInitFunc(init *types.Func) ast.Stmt {
id := fc.newIdentFor(init)
call := &ast.CallExpr{Fun: id}
if fc.pkgCtx.IsBlocking(init) {
if fc.pkgCtx.IsBlocking(typeparams.Instance{Object: init}) {
fc.Blocking[call] = true
}
return &ast.ExprStmt{X: call}
Expand All @@ -379,7 +379,7 @@ func (fc *funcContext) callMainFunc(main *types.Func) ast.Stmt {
},
},
}
if fc.pkgCtx.IsBlocking(main) {
if fc.pkgCtx.IsBlocking(typeparams.Instance{Object: main}) {
fc.Blocking[call] = true
fc.Flattened[ifStmt] = true
}
Expand Down
14 changes: 7 additions & 7 deletions compiler/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (fc *funcContext) nestedFunctionContext(info *analysis.FuncInfo, inst typep
// namedFuncContext creates a new funcContext for a named Go function
// (standalone or method).
func (fc *funcContext) namedFuncContext(inst typeparams.Instance) *funcContext {
info := fc.pkgCtx.FuncDeclInfos[inst.Object.(*types.Func)]
info := fc.pkgCtx.FuncInfo(inst)
c := fc.nestedFunctionContext(info, inst)

return c
Expand All @@ -82,7 +82,7 @@ func (fc *funcContext) namedFuncContext(inst typeparams.Instance) *funcContext {
// go/types doesn't generate *types.Func objects for function literals, we
// generate a synthetic one for it.
func (fc *funcContext) literalFuncContext(fun *ast.FuncLit) *funcContext {
info := fc.pkgCtx.FuncLitInfos[fun]
info := fc.pkgCtx.FuncLitInfo(fun)
sig := fc.pkgCtx.TypeOf(fun).(*types.Signature)
o := types.NewFunc(fun.Pos(), fc.pkgCtx.Pkg, fc.newLitFuncName(), sig)
inst := typeparams.Instance{Object: o}
Expand Down Expand Up @@ -237,7 +237,7 @@ func (fc *funcContext) translateFunctionBody(typ *ast.FuncType, recv *ast.Ident,
}

bodyOutput := string(fc.CatchOutput(1, func() {
if len(fc.Blocking) != 0 {
if fc.HasBlocking() {
fc.pkgCtx.Scopes[body] = fc.pkgCtx.Scopes[typ]
fc.handleEscapingVars(body)
}
Expand Down Expand Up @@ -283,14 +283,14 @@ func (fc *funcContext) translateFunctionBody(typ *ast.FuncType, recv *ast.Ident,
if fc.HasDefer {
fc.localVars = append(fc.localVars, "$deferred")
suffix = " }" + suffix
if len(fc.Blocking) != 0 {
if fc.HasBlocking() {
suffix = " }" + suffix
}
}

localVarDefs := "" // Function-local var declaration at the top.

if len(fc.Blocking) != 0 {
if fc.HasBlocking() {
localVars := append([]string{}, fc.localVars...)
// There are several special variables involved in handling blocking functions:
// $r is sometimes used as a temporary variable to store blocking call result.
Expand All @@ -314,7 +314,7 @@ func (fc *funcContext) translateFunctionBody(typ *ast.FuncType, recv *ast.Ident,
if fc.HasDefer {
prefix = prefix + " var $err = null; try {"
deferSuffix := " } catch(err) { $err = err;"
if len(fc.Blocking) != 0 {
if fc.HasBlocking() {
deferSuffix += " $s = -1;"
}
if fc.resultNames == nil && fc.sig.HasResults() {
Expand All @@ -324,7 +324,7 @@ func (fc *funcContext) translateFunctionBody(typ *ast.FuncType, recv *ast.Ident,
if fc.resultNames != nil {
deferSuffix += fmt.Sprintf(" if (!$curGoroutine.asleep) { return %s; }", fc.translateResults(fc.resultNames))
}
if len(fc.Blocking) != 0 {
if fc.HasBlocking() {
deferSuffix += " if($curGoroutine.asleep) {"
}
suffix = deferSuffix + suffix
Expand Down
59 changes: 38 additions & 21 deletions compiler/internal/analysis/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/gopherjs/gopherjs/compiler/astutil"
"github.com/gopherjs/gopherjs/compiler/internal/typeparams"
"github.com/gopherjs/gopherjs/compiler/typesutil"
)

Expand Down Expand Up @@ -51,8 +52,8 @@ type Info struct {
*types.Info
Pkg *types.Package
HasPointer map[*types.Var]bool
FuncDeclInfos map[*types.Func]*FuncInfo
FuncLitInfos map[*ast.FuncLit]*FuncInfo
funcInstInfos *typeparams.InstanceMap[*FuncInfo]
funcLitInfos map[*ast.FuncLit]*FuncInfo
InitFuncInfo *FuncInfo // Context for package variable initialization.

isImportedBlocking func(*types.Func) bool // For functions from other packages.
Expand All @@ -65,7 +66,7 @@ func (info *Info) newFuncInfo(n ast.Node) *FuncInfo {
Flattened: make(map[ast.Node]bool),
Blocking: make(map[ast.Node]bool),
GotoLabel: make(map[*types.Label]bool),
localNamedCallees: make(map[*types.Func][]astPath),
localInstCallees: new(typeparams.InstanceMap[[]astPath]),
literalFuncCallees: make(map[*ast.FuncLit][]astPath),
}

Expand All @@ -83,10 +84,11 @@ func (info *Info) newFuncInfo(n ast.Node) *FuncInfo {
}

fn := info.Defs[n.Name].(*types.Func)
info.FuncDeclInfos[fn] = funcInfo
inst := typeparams.Instance{Object: fn}
info.funcInstInfos.Set(inst, funcInfo)

case *ast.FuncLit:
info.FuncLitInfos[n] = funcInfo
info.funcLitInfos[n] = funcInfo
}

// And add it to the list of all functions.
Expand All @@ -95,12 +97,23 @@ func (info *Info) newFuncInfo(n ast.Node) *FuncInfo {
return funcInfo
}

func (info *Info) FuncInfo(inst typeparams.Instance) *FuncInfo {
if funInfo := info.funcInstInfos.Get(inst); funInfo != nil {
return funInfo
}
panic(fmt.Errorf(`Info did not have function declaration or instance for %v`, inst))
}

// IsBlocking returns true if the function may contain blocking calls or operations.
func (info *Info) IsBlocking(fun *types.Func) bool {
if funInfo := info.FuncDeclInfos[fun]; funInfo != nil {
return len(funInfo.Blocking) > 0
func (info *Info) IsBlocking(inst typeparams.Instance) bool {
return info.FuncInfo(inst).HasBlocking()
}

func (info *Info) FuncLitInfo(fun *ast.FuncLit) *FuncInfo {
if funInfo := info.funcLitInfos[fun]; funInfo != nil {
return funInfo
}
panic(fmt.Errorf(`info did not have function declaration for %s`, fun.FullName()))
panic(fmt.Errorf(`Info did not have function literal for %v`, fun))
}

// VarsWithInitializers returns a set of package-level variables that have
Expand All @@ -121,8 +134,8 @@ func AnalyzePkg(files []*ast.File, fileSet *token.FileSet, typesInfo *types.Info
Pkg: typesPkg,
HasPointer: make(map[*types.Var]bool),
isImportedBlocking: isBlocking,
FuncDeclInfos: make(map[*types.Func]*FuncInfo),
FuncLitInfos: make(map[*ast.FuncLit]*FuncInfo),
funcInstInfos: new(typeparams.InstanceMap[*FuncInfo]),
funcLitInfos: make(map[*ast.FuncLit]*FuncInfo),
}
info.InitFuncInfo = info.newFuncInfo(nil)

Expand Down Expand Up @@ -155,19 +168,19 @@ func AnalyzePkg(files []*ast.File, fileSet *token.FileSet, typesInfo *types.Info
done := true
for _, caller := range info.allInfos {
// Check calls to named functions and function-typed variables.
for callee, callSites := range caller.localNamedCallees {
if info.IsBlocking(callee) {
for callee, callSites := range caller.localInstCallees {
if info.funcInstInfos[callee].HasBlocking() {
for _, callSite := range callSites {
caller.markBlocking(callSite)
}
delete(caller.localNamedCallees, callee)
delete(caller.localInstCallees, callee)
done = false
}
}

// Check direct calls to function literals.
for callee, callSites := range caller.literalFuncCallees {
if len(info.FuncLitInfos[callee].Blocking) > 0 {
if info.funcLitInfos[callee].HasBlocking() {
for _, callSite := range callSites {
caller.markBlocking(callSite)
}
Expand Down Expand Up @@ -214,7 +227,7 @@ type FuncInfo struct {
returnStmts []astPath
// List of other named functions from the current package this function calls.
// If any of them are blocking, this function will become blocking too.
localNamedCallees map[*types.Func][]astPath
localInstCallees *typeparams.InstanceMap[[]astPath]
// List of function literals directly called from this function (for example:
// `func() { /* do stuff */ }()`). This is distinct from function literals
// assigned to named variables (for example: `doStuff := func() {};
Expand All @@ -226,6 +239,14 @@ type FuncInfo struct {
visitorStack astPath
}

// HasBlocking indicates if this function may block goroutine execution.
//
// For example, a channel operation in a function or a call to another
// possibly blocking function may block the function.
func (fi *FuncInfo) HasBlocking() bool {
return len(fi.Blocking) != 0
}

func (fi *FuncInfo) Visit(node ast.Node) ast.Visitor {
if node == nil {
if len(fi.visitorStack) != 0 {
Expand Down Expand Up @@ -338,15 +359,13 @@ func (fi *FuncInfo) visitCallExpr(n *ast.CallExpr) ast.Visitor {
switch f := astutil.RemoveParens(n.Fun).(type) {
case *ast.Ident:
fi.callToNamedFunc(fi.pkgInfo.Uses[f])

case *ast.SelectorExpr:
if sel := fi.pkgInfo.Selections[f]; sel != nil && typesutil.IsJsObject(sel.Recv()) {
// js.Object methods are known to be non-blocking, but we still must
// check its arguments.
} else {
fi.callToNamedFunc(fi.pkgInfo.Uses[f.Sel])
}

case *ast.FuncLit:
// Collect info about the function literal itself.
ast.Walk(fi, n.Fun)
Expand All @@ -358,7 +377,6 @@ func (fi *FuncInfo) visitCallExpr(n *ast.CallExpr) ast.Visitor {
// Register literal function call site in case it is identified as blocking.
fi.literalFuncCallees[f] = append(fi.literalFuncCallees[f], fi.visitorStack.copy())
return nil // No need to walk under this CallExpr, we already did it manually.

case *ast.IndexExpr:
// Collect info about the instantiated type or function, or index expression.
if astutil.IsTypeExpr(f, fi.pkgInfo.Info) {
Expand Down Expand Up @@ -386,7 +404,6 @@ func (fi *FuncInfo) visitCallExpr(n *ast.CallExpr) ast.Visitor {
// or not, we have to be conservative and assume that function might be blocking.
fi.markBlocking(fi.visitorStack)
}

case *ast.IndexListExpr:
// Collect info about the instantiated type or function.
if astutil.IsTypeExpr(f, fi.pkgInfo.Info) {
Expand All @@ -401,7 +418,6 @@ func (fi *FuncInfo) visitCallExpr(n *ast.CallExpr) ast.Visitor {
fmt.Printf(" >> %[1]d) %[2]T %#[2]v\n", i, index)
}
}

default:
if astutil.IsTypeExpr(f, fi.pkgInfo.Info) {
// This is a type conversion, not a call. Type assertion itself is not
Expand All @@ -428,6 +444,7 @@ func (fi *FuncInfo) callToNamedFunc(callee types.Object) {
}
}
if o.Pkg() != fi.pkgInfo.Pkg {
// TODO: make isImportedBlocking take an instance type.
if fi.pkgInfo.isImportedBlocking(o) {
fi.markBlocking(fi.visitorStack)
}
Expand Down
Loading

0 comments on commit b7ce0cf

Please sign in to comment.