diff --git a/main.go b/main.go index cd828d1..d9df8d0 100644 --- a/main.go +++ b/main.go @@ -452,12 +452,29 @@ func hashWith(salt, value string) string { // used with reflect.TypeOf or reflect.ValueOf. Since we obfuscate one package // at a time, we only detect those if the type definition and the reflect usage // are both in the same package. -func buildBlacklist(files []*ast.File, info *types.Info, pkg *types.Package) (blacklist []types.Object) { +// +// The blacklist mainly contains named types and their field declarations. +func buildBlacklist(files []*ast.File, info *types.Info, pkg *types.Package) map[types.Object]struct{} { // Keep track of the current syntax tree level. If reflectCallLevel is // non-negative, we are under a reflect call. level := 0 reflectCallLevel := -1 + blacklist := make(map[types.Object]struct{}) + addToBlacklist := func(named *types.Named) { + obj := named.Obj() + if obj == nil || obj.Pkg() != pkg { + return + } + blacklist[obj] = struct{}{} + + strct, _ := named.Underlying().(*types.Struct) + if strct != nil { + for i := 0; i < strct.NumFields(); i++ { + blacklist[strct.Field(i)] = struct{}{} + } + } + } visit := func(node ast.Node) bool { if node == nil { if level == reflectCallLevel { @@ -468,8 +485,9 @@ func buildBlacklist(files []*ast.File, info *types.Info, pkg *types.Package) (bl } if reflectCallLevel >= 0 && level >= reflectCallLevel { expr, _ := node.(ast.Expr) - if obj := objOf(info.TypeOf(expr)); obj != nil && obj.Pkg() == pkg { - blacklist = append(blacklist, obj) + named := namedType(info.TypeOf(expr)) + if named != nil { + addToBlacklist(named) } } level++ @@ -495,7 +513,7 @@ func buildBlacklist(files []*ast.File, info *types.Info, pkg *types.Package) (bl } // transformGo garbles the provided Go syntax node. -func transformGo(file *ast.File, info *types.Info, blacklist []types.Object) *ast.File { +func transformGo(file *ast.File, info *types.Info, blacklist map[types.Object]struct{}) *ast.File { // Remove all comments, minus the "//go:" compiler directives. // The final binary should still not contain comment text, but removing // it helps ensure that (and makes position info less predictable). @@ -538,7 +556,7 @@ func transformGo(file *ast.File, info *types.Info, blacklist []types.Object) *as if vr, ok := obj.(*types.Var); ok && vr.Embedded() { // ObjectOf returns the field for embedded struct // fields, not the type it uses. Use the type. - obj = objOf(obj.Type()) + obj = namedType(obj.Type()).Obj() pkg = obj.Pkg() } @@ -548,11 +566,9 @@ func transformGo(file *ast.File, info *types.Info, blacklist []types.Object) *as return true // could be a Go plugin API } - // TODO: also do this for method receivers - for _, item := range blacklist { - if obj == item { - return true - } + // The object itself is blacklisted, e.g. a type definition. + if _, ok := blacklist[obj]; ok { + return true } // log.Printf("%#v %T", node, obj) @@ -620,15 +636,15 @@ func implementedOutsideGo(obj *types.Func) bool { (obj.Scope() != nil && obj.Scope().Pos() == token.NoPos) } -// objOf tries to obtain the object behind a *types.Named, even if it's behind a -// pointer type. This is useful to obtain "testing.T" from "*testing.T", or to -// obtain the type declaration object from an embedded field. -func objOf(t types.Type) types.Object { +// named tries to obtain the *types.Named behind a type, if there is one. +// This is useful to obtain "testing.T" from "*testing.T", or to obtain the type +// declaration object from an embedded field. +func namedType(t types.Type) *types.Named { switch t := t.(type) { case *types.Named: - return t.Obj() + return t case interface{ Elem() types.Type }: - return objOf(t.Elem()) + return namedType(t.Elem()) default: return nil } @@ -643,7 +659,7 @@ func isTestSignature(sign *types.Signature) bool { if params.Len() != 1 { return false } - obj := objOf(params.At(0).Type()) + obj := namedType(params.At(0).Type()).Obj() return obj != nil && obj.Pkg().Path() == "testing" && obj.Name() == "T" } diff --git a/testdata/scripts/imports.txt b/testdata/scripts/imports.txt index b551dfc..63b800c 100644 --- a/testdata/scripts/imports.txt +++ b/testdata/scripts/imports.txt @@ -23,6 +23,7 @@ package main import ( "fmt" + "reflect" _ "unsafe" "test/main/imported" @@ -36,12 +37,20 @@ func linkedPrintln(a ...interface{}) (n int, err error) func main() { fmt.Println(imported.ImportedVar) fmt.Println(imported.ImportedConst) - imported.ImportedFunc('x') + fmt.Println(imported.ImportedFunc('x')) fmt.Println(imported.ImportedType(3)) fmt.Printf("%T\n", imported.ReflectTypeOf(2)) fmt.Printf("%T\n", imported.ReflectTypeOfIndirect(4)) - fmt.Printf("%#v\n", imported.ReflectValueOfVar) + + v := imported.ReflectValueOfVar + fmt.Printf("%#v\n", v) + method := reflect.ValueOf(&v).MethodByName("ExportedMethodName") + if method.IsValid() { + fmt.Println(method.Call(nil)) + } else { + fmt.Println("method not found") + } linkedPrintln(nil) fmt.Println(quote.Go()) @@ -68,10 +77,14 @@ type ReflectTypeOfIndirect int var _ = reflect.TypeOf(new([]*ReflectTypeOfIndirect)) type ReflectValueOf struct { - Foo int `bar:"baz"` + ExportedField string + + unexportedField string } -var ReflectValueOfVar = ReflectValueOf{Foo: 3} +func (r *ReflectValueOf) ExportedMethodName() string { return "method: "+r.ExportedField } + +var ReflectValueOfVar = ReflectValueOf{ExportedField: "abc"} var _ = reflect.TypeOf(ReflectValueOfVar) @@ -81,9 +94,11 @@ type ImportedType int -- main.stdout -- imported var value imported const value +x 3 imported.ReflectTypeOf imported.ReflectTypeOfIndirect -imported.ReflectValueOf{Foo:3} +imported.ReflectValueOf{ExportedField:"abc", unexportedField:""} +[method: abc] Don't communicate by sharing memory, share memory by communicating.