mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-07 05:11:36 +08:00

* plugin handler and lifecycle * rebase * remove reflect s * remove Config and NewPlugin from plugin template * Remove plugin manager, generation of plugin interface * implement http handlers for services remove log trim path prefix wails/services * update plugine example * Misc updates * Ported plugins to services, rewritten example * Added fileserver * Update OnStartup and use a context for the application * Rename PathPrefix to Route. Create docs. * Use service config copy. Add Name to Service Options. Improve service generation. * Use service config copy. Add Name to Service Options. Improve service generation. Update README * Remove rogue db * Update changelog.md --------- Co-authored-by: Lea O'Anthony <lea.anthony@gmail.com>
207 lines
5.5 KiB
Go
207 lines
5.5 KiB
Go
package generator
|
|
|
|
import (
|
|
"fmt"
|
|
"go/token"
|
|
"go/types"
|
|
|
|
"github.com/wailsapp/wails/v3/internal/generator/config"
|
|
"golang.org/x/tools/go/packages"
|
|
)
|
|
|
|
// FindServices scans the given packages for invocations
|
|
// of the NewService function from the Wails application package.
|
|
//
|
|
// Whenever one is found and the type of its unique argument
|
|
// is a valid service type, the corresponding named type object
|
|
// is passed to yield.
|
|
//
|
|
// Results are deduplicated, i.e. yield is called at most once per object.
|
|
//
|
|
// If yield returns false, FindBoundTypes returns immediately.
|
|
func FindServices(pkgs []*packages.Package, systemPaths *config.SystemPaths, logger config.Logger, yield func(*types.TypeName) bool) error {
|
|
type instanceInfo struct {
|
|
args *types.TypeList
|
|
pos token.Position
|
|
}
|
|
|
|
type target struct {
|
|
obj types.Object
|
|
param int
|
|
}
|
|
|
|
type targetInfo struct {
|
|
target
|
|
cause token.Position
|
|
}
|
|
|
|
// instances maps objects (TypeName or Func) to their instance list.
|
|
instances := make(map[types.Object][]instanceInfo)
|
|
|
|
// owner maps type parameter objects to their parent object (TypeName or Func)
|
|
owner := make(map[*types.TypeName]types.Object)
|
|
|
|
// scheduled holds the set of type parameters
|
|
// that have been already scheduled for analysis,
|
|
// for deduplication.
|
|
scheduled := make(map[target]bool)
|
|
|
|
// next lists type parameter objects that have yet to be analysed.
|
|
var next []targetInfo
|
|
|
|
// Initialise instance/owner maps and detect application.NewService.
|
|
for _, pkg := range pkgs {
|
|
for ident, instance := range pkg.TypesInfo.Instances {
|
|
obj := pkg.TypesInfo.Uses[ident]
|
|
|
|
// Add to instance map.
|
|
objInstances, seen := instances[obj]
|
|
instances[obj] = append(objInstances, instanceInfo{
|
|
instance.TypeArgs,
|
|
pkg.Fset.Position(ident.Pos()),
|
|
})
|
|
|
|
if seen {
|
|
continue
|
|
}
|
|
|
|
// Object seen for the first time:
|
|
// add type params to owner map.
|
|
// If applicable, process methods too.
|
|
var tp *types.TypeParamList
|
|
var recv *types.Named
|
|
switch t := obj.Type().(type) {
|
|
case *types.Named:
|
|
tp = t.TypeParams()
|
|
recv = t
|
|
case *types.Signature:
|
|
tp = t.TypeParams()
|
|
default:
|
|
// Instantiated object has unexpected kind:
|
|
// the spec might have changed.
|
|
logger.Warningf(
|
|
"unexpected instantiation for %s: please report this to Wails maintainers",
|
|
types.ObjectString(obj, nil),
|
|
)
|
|
continue
|
|
}
|
|
|
|
// Add type params to owner map.
|
|
for i := range tp.Len() {
|
|
if param := tp.At(i).Obj(); param != nil {
|
|
owner[param] = obj
|
|
}
|
|
}
|
|
|
|
// Process methods.
|
|
if recv != nil && recv.NumMethods() > 0 {
|
|
// Register receiver type params.
|
|
for i := range recv.NumMethods() {
|
|
tp := recv.Method(i).Type().(*types.Signature).RecvTypeParams()
|
|
for j := range tp.Len() {
|
|
if param := tp.At(j).Obj(); param != nil {
|
|
owner[param] = obj
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(next) > 0 {
|
|
// application.NewService has been found already.
|
|
continue
|
|
}
|
|
|
|
fn, ok := obj.(*types.Func)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
// Detect application.NewService
|
|
if fn.Name() == "NewService" && fn.Pkg().Path() == systemPaths.ApplicationPackage {
|
|
// Check signature.
|
|
signature := fn.Type().(*types.Signature)
|
|
if signature.Params().Len() > 2 || signature.Results().Len() != 1 || tp.Len() != 1 || tp.At(0).Obj() == nil {
|
|
logger.Warningf("Param Len: %d, Results Len: %d, tp.Len: %d, tp.At(0).Obj(): %v", signature.Params().Len(), signature.Results().Len(), tp.Len(), tp.At(0).Obj())
|
|
return ErrBadApplicationPackage
|
|
}
|
|
|
|
// Schedule unique type param for analysis.
|
|
tgt := target{obj, 0}
|
|
scheduled[tgt] = true
|
|
next = append(next, targetInfo{target: tgt})
|
|
}
|
|
}
|
|
}
|
|
|
|
// found tracks service types that have been found so far, for deduplication.
|
|
found := make(map[*types.TypeName]bool)
|
|
|
|
// Process targets.
|
|
for len(next) > 0 {
|
|
// Pop one target off the next list.
|
|
tgt := next[len(next)-1]
|
|
next = next[:len(next)-1]
|
|
|
|
// Prepare indirect binding message.
|
|
indirectMsg := ""
|
|
if tgt.cause.IsValid() {
|
|
indirectMsg = fmt.Sprintf(" (indirectly bound at %s)", tgt.cause)
|
|
}
|
|
|
|
for _, instance := range instances[tgt.obj] {
|
|
// Retrieve type argument.
|
|
serviceType := types.Unalias(instance.args.At(tgt.param))
|
|
|
|
var named *types.Named
|
|
|
|
switch t := serviceType.(type) {
|
|
case *types.Named:
|
|
// Process named type.
|
|
named = t.Origin()
|
|
|
|
case *types.TypeParam:
|
|
// Schedule type parameter for analysis.
|
|
newtgt := target{owner[t.Obj()], t.Index()}
|
|
if !scheduled[newtgt] {
|
|
scheduled[newtgt] = true
|
|
|
|
// Retrieve position of call to application.NewService
|
|
// that caused this target to be scheduled.
|
|
cause := tgt.cause
|
|
if !tgt.cause.IsValid() {
|
|
// This _is_ a call to application.NewService.
|
|
cause = instance.pos
|
|
}
|
|
|
|
// Push on next list.
|
|
next = append(next, targetInfo{newtgt, cause})
|
|
}
|
|
continue
|
|
|
|
default:
|
|
logger.Warningf("%s: ignoring anonymous service type %s%s", instance.pos, serviceType, indirectMsg)
|
|
continue
|
|
}
|
|
|
|
// Reject interfaces and generic types.
|
|
if types.IsInterface(named.Underlying()) {
|
|
logger.Warningf("%s: ignoring interface service type %s%s", instance.pos, named, indirectMsg)
|
|
continue
|
|
} else if named.TypeParams() != nil {
|
|
logger.Warningf("%s: ignoring generic service type %s", instance.pos, named, indirectMsg)
|
|
continue
|
|
}
|
|
|
|
// Record and yield type object.
|
|
if !found[named.Obj()] {
|
|
found[named.Obj()] = true
|
|
if !yield(named.Obj()) {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|