-
-
Notifications
You must be signed in to change notification settings - Fork 154
Open
Description
Discussed in #517
Originally posted by chlunde April 8, 2025
Background: https://appliedgo.net/spotlight/reflection-binary-size/
Kong currently prevents DCE due to the reflection usage.
This is significant when writing CLIs that use SDKs like the AWS SDK:
ls -lh s3ls s3ls.dce
6.3M Apr 7 21:10 s3ls.dce*
21M Apr 7 21:30 s3ls*
Example program:
package main
import (
"context"
"fmt"
"log"
"github.com/alecthomas/kong"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
var _ = kong.Must(&s3ls{})
type s3ls struct {
Status listCmd `cmd:"" help:"List files."`
}
type listCmd struct {
Verbose bool `short:"v" help:"Show bucket objects."`
}
func (s *listCmd) Run(ctx *kong.Context) error {
// Load the SDK's configuration from environment and shared config, and
// create the client with this.
cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
log.Fatalf("failed to load SDK configuration, %v", err)
}
client := s3.NewFromConfig(cfg)
bucketName := "my-bucket" // Replace with your bucket name
// Set the parameters based on the CLI flag inputs.
params := &s3.ListObjectsV2Input{
Bucket: &bucketName,
}
// Create the Paginator for the ListObjectsV2 operation.
p := s3.NewListObjectsV2Paginator(client, params, func(o *s3.ListObjectsV2PaginatorOptions) {})
// Iterate through the S3 object pages, printing each object returned.
var i int
log.Println("Objects:")
for p.HasMorePages() {
i++
// Next Page takes a new context for each page retrieval. This is where
// you could add timeouts or deadlines.
page, err := p.NextPage(context.TODO())
if err != nil {
log.Fatalf("failed to get page %v, %v", i, err)
}
// Log the objects found
for _, obj := range page.Contents {
fmt.Println("Object:", *obj.Key)
}
}
return nil
}
func main() {
ctx := kong.Parse(&s3ls{},
kong.Description("s3ls - A simple S3 client"),
kong.UsageOnError(),
)
ctx.FatalIfErrorf(ctx.Run())
}
With a few hacks, it might be possible to enable DCE. I did not find and example for Provide, but if adapt the code to only allow a specific name like Provide() without a suffix, it should allow for DCE:
diff -ruwbp vendor/github.com/alecthomas/kong/callbacks.go vendor.mod/github.com/alecthomas/kong/callbacks.go
--- vendor/github.com/alecthomas/kong/callbacks.go 2025-04-07 21:20:36
+++ vendor.mod/github.com/alecthomas/kong/callbacks.go 2025-04-07 21:12:41
@@ -117,11 +117,30 @@ func (b bindings) merge(other bindings) bindings {
return b
}
+func getMethodByName(value reflect.Value, name string) reflect.Value {
+ switch name {
+ case "BeforeReset":
+ return value.MethodByName("BeforeReset")
+ case "BeforeResolve":
+ return value.MethodByName("BeforeResolve")
+ case "BeforeApply":
+ return value.MethodByName("BeforeApply")
+ case "AfterApply":
+ return value.MethodByName("AfterApply")
+ case "AfterRun":
+ return value.MethodByName("AfterRun")
+ case "Run":
+ return value.MethodByName("Run")
+ default:
+ panic(fmt.Sprintf("unknown method %s", name))
+ }
+}
+
func getMethod(value reflect.Value, name string) reflect.Value {
- method := value.MethodByName(name)
+ method := getMethodByName(value, name)
if !method.IsValid() {
if value.CanAddr() {
- method = value.Addr().MethodByName(name)
+ method = getMethodByName(value.Addr(), name)
}
}
return method
diff -ruwbp vendor/github.com/alecthomas/kong/context.go vendor.mod/github.com/alecthomas/kong/context.go
--- vendor/github.com/alecthomas/kong/context.go 2025-04-07 21:20:36
+++ vendor.mod/github.com/alecthomas/kong/context.go 2025-04-07 21:10:35
@@ -825,16 +825,17 @@ func (c *Context) RunNode(node *Node, binds ...any) (e
for _, p := range []reflect.Value{p.Target, p.Target.Addr()} {
t := p.Type()
for i := 0; i < p.NumMethod(); i++ {
- methodt := t.Method(i)
- if strings.HasPrefix(methodt.Name, "Provide") {
- method := p.Method(i)
- if err := methodBinds.addProvider(method.Interface(), false /* singleton */); err != nil {
- return fmt.Errorf("%s.%s: %w", t.Name(), methodt.Name, err)
+ _ = t
+ // methodt := t.Method(i) // TODO: improve this
+ // if strings.HasPrefix(methodt.Name, "Provide") {
+ // method := p.Method(i)
+ // if err := methodBinds.addProvider(method.Interface(), false /* singleton */); err != nil {
+ // return fmt.Errorf("%s.%s: %w", t.Name(), methodt.Name, err)
+ // }
+ // }
}
}
}
- }
- }
if method.IsValid() {
methods = append(methods, targetMethod{node, method, methodBinds})
}
```</div>
Metadata
Metadata
Assignees
Labels
No labels