mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-06 22:42:09 +08:00

* Support variadic arguments and slice, pointer types * Fix computation of type namespaces * Improve comments and general formatting * Set default values correctly for composite types * Add templates for bindings Additionally: * fixes generation of tuple return type * improves imports and namespacing in JS mode * general cleanup of generated code * Simplify import list construction * Refactor type generation code Improves support for unknown types (encoded as any) and maps (using Typescript index signatures) * Support slices with pointer elements * Match encoding/json behaviour in struct parser * Update tests and example * Add tests for complex method signatures and json tag parsing * Add test `function_multiple_files` * Attempt looking up idents with missing denotation * Update test data * fix quoted bool field * Test quoted booleans * Delete old parser code * Remove old test data * Update bindgen flags * Makes call by ID the default * Add package loading code * Add static analyser * Temporarily ignore binding generation code * Add complex slice expressions test * Fix variable reference analysis * Unwrap casts to interface types * Complete code comments * Refactor static analyser * Restrict options struct usage * Update tests * Fix method selector sink and source processing * Improve Set API * Add package info collector * Rename analyser package to analyse * Improve template functions * Add index file templates * Add glue code for binding generation * Refactor collection and rendering code * Implement binding generator * Implement global index generation * Improve marshaler and alias handling * Use package path in binding calls by name * Implement model collection and rendering * Fix wrong exit condition in analyser * Fix enum rendering * Generate shortcuts for all packages. * Implement generator tests * Ignore non-pointer bound types * Treat main package specially * Compute stats * Plug new API into generate command * Support all named types * Update JS runtime * Report dual role types * Remove go1.22 syntax * Fix type assertion in TS bindings * encoding/json compliance for arrays and slices * Ignore got files in testdata * Cleanup type rendering mechanism * Update JS runtime * Implement generic models * Add missing field in renderer initialisation * Improve generic creation code * Add generic model test * Add error reporting infrastructure * Support configurable file names * Detect file naming collisions * Print final error report * New shortcut file structure + collision detection * Update test layout and data * Autoconfiguration for analyser tests * Live progress reporting * Update code comments * Fix model doc rendering * Simplify name resolution * Add test for out of tree types * Fix generic creation code * Fix potential collisions between methods and models * Fix generic class alias rendering * Report model discovery in debug mode * Add interface mode for JS * Collect interface method comments * Add interface methods test * Unwrap generic instantiations in method receivers * Fix rendering of nullable types in interface mode * Fix rendering of class aliases * Expose promise cancel method to typescript * Update test data * Update binding example * Fix rendering of aliased quoted type params * Move to strongly typed bindings * Implement lightweight analyser * Update test cases * Update binding example * Add complex instantiation test * Load full dependency tree * Rewrite collector * Update renderer to match new collector * Update generator to match new collector * Update test data * Update binding example * Configure includes and injections by language * Improve system path resolution * Support rich conditions in inject/include directives * Fix error handling in Generator.Generate * Retrieve compiled go file paths from fileset * Do not rely on struct info in struct flattening algorithm * Fix doc comment for findDeclaraion * Fix bugs in embedded field handling * Fix bugs and comments in package collection * Remove useless fields from ServiceInfo * Fix empty line at the beginning of TS indexes * Remove global index and shortcuts * Remove generation tests for individual packages * Enforce lower-case file names * Update test data * Improve error reporting * Update binding example * Reintroduce go1.22 syntax * Improve relative import path computation * Improve alias support * Add alias test * Update test data * Remove no services error * Rename global analyser test * Add workaround and test for bug in typeutil.Map * Update test data * Do not split fully qualified names * Update typeutil package and remove workaround * Unify alias/named type handling * Fix rendering of generic named class aliases * Fix rendering of array types * Minor tweaks and cleanups * Rmove namespaced export construct * Update test data * Update binding example * Break type cycles * Fix typo in comment * Fix creation code for cyclic types * Fix type of variadic params in interface mode * Update test data * Fix bad whitespace * Refactor type assertions inside bound methods * Update test data * Rename field application.Options.Bind to Services * Rename parser package to generator * Update binding example * Update test data * Update generator readme * Add typescript test harness * Move test output to new subfolder * Fix code generation bugs * Use .js extensions in TS mode imports * Update test data * Revert default generator output dir to frontend/bindings * Bump runtime package version * Update templates * Update changelog * Improve newline handling --------- Co-authored-by: Andreas Bichinger <andreas.bichinger@gmail.com>
206 lines
5.3 KiB
Go
206 lines
5.3 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() != 1 || signature.Results().Len() != 1 || tp.Len() != 1 || tp.At(0).Obj() == nil {
|
|
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
|
|
}
|