reduce verbosity in the last change

Declare the ast.GenDecl node once,
which allows us to deduplicate and inline code.
resolveImportSpec doesn't need to be separate either.

We also don't need explicit panics for unexpected nils;
we can rely on nil checks inserted by the compiler.

Finally, move the bulk of the code outside of the scope.Names loop,
which unindents most of the logic.

While here, add a few more inline docs.
pull/665/head
Daniel Martí 1 year ago
parent 7c50979899
commit 09a17375e3

@ -950,7 +950,7 @@ func transformCompile(args []string) ([]string, error) {
if flagTiny { if flagTiny {
// strip unneeded runtime code // strip unneeded runtime code
stripRuntime(basename, file) stripRuntime(basename, file)
tf.makeImportsUsed(file) tf.useAllImports(file)
} }
if basename == "symtab.go" { if basename == "symtab.go" {
updateMagicValue(file, magicValue()) updateMagicValue(file, magicValue())
@ -1687,23 +1687,6 @@ func recordedAsNotObfuscated(obj types.Object) bool {
return ok return ok
} }
func (tf *transformer) resolveImportSpec(imp *ast.ImportSpec) *types.Package {
// Simple import has no ast.Ident and is stored in Implicits separately.
obj := tf.info.Implicits[imp]
if obj == nil {
obj = tf.info.Defs[imp.Name] // renamed or dot import
}
if obj == nil {
return nil
}
pkgObj, ok := obj.(*types.PkgName)
if !ok {
panic(fmt.Sprintf("unexpected object type for %s: %v", imp.Path.Value, obj))
}
return pkgObj.Imported()
}
// isSafeForInstanceType returns true if the passed type is safe for var declaration. // isSafeForInstanceType returns true if the passed type is safe for var declaration.
// Unsafe types: generic types and non-method interfaces. // Unsafe types: generic types and non-method interfaces.
func isSafeForInstanceType(typ types.Type) bool { func isSafeForInstanceType(typ types.Type) bool {
@ -1721,93 +1704,71 @@ func isSafeForInstanceType(typ types.Type) bool {
return true return true
} }
func (tf *transformer) makeImportsUsed(file *ast.File) { func (tf *transformer) useAllImports(file *ast.File) {
for _, imp := range file.Imports { for _, imp := range file.Imports {
if imp.Name != nil && imp.Name.Name == "_" { if imp.Name != nil && imp.Name.Name == "_" {
continue continue
} }
pkg := tf.resolveImportSpec(imp) // Simple import has no ast.Ident and is stored in Implicits separately.
if pkg == nil { pkgObj := tf.info.Implicits[imp]
panic(fmt.Sprintf("import %s not found", imp.Path.Value)) if pkgObj == nil {
pkgObj = tf.info.Defs[imp.Name] // renamed or dot import
} }
generated := false pkgScope := pkgObj.(*types.PkgName).Imported().Scope()
scope := pkg.Scope() var nameObj types.Object
for _, name := range scope.Names() { for _, name := range pkgScope.Names() {
if !token.IsExported(name) { if obj := pkgScope.Lookup(name); obj.Exported() && isSafeForInstanceType(obj.Type()) {
continue nameObj = obj
break
} }
obj := scope.Lookup(name)
if obj == nil {
panic(fmt.Sprintf("%s not found in %s", name, imp.Path.Value))
} }
if !isSafeForInstanceType(obj.Type()) { if nameObj == nil {
// A very unlikely situation where there is no suitable declaration for a reference variable
// and almost certainly means that there is another import reference in code.
continue continue
} }
spec := &ast.ValueSpec{Names: []*ast.Ident{ast.NewIdent("_")}}
decl := &ast.GenDecl{Specs: []ast.Spec{spec}}
nameIdent := ast.NewIdent(name) nameIdent := ast.NewIdent(nameObj.Name())
getFullName := func() ast.Expr { var nameExpr ast.Expr
if imp.Name == nil { switch {
return &ast.SelectorExpr{ case imp.Name == nil: // import "pkg/path"
X: ast.NewIdent(pkg.Name()), nameExpr = &ast.SelectorExpr{
X: ast.NewIdent(pkgObj.Name()),
Sel: nameIdent, Sel: nameIdent,
} }
} case imp.Name.Name != ".": // import path2 "pkg/path"
if imp.Name.Name == "." { nameExpr = &ast.SelectorExpr{
return nameIdent
}
return &ast.SelectorExpr{
X: ast.NewIdent(imp.Name.Name), X: ast.NewIdent(imp.Name.Name),
Sel: nameIdent, Sel: nameIdent,
} }
default: // import . "pkg/path"
nameExpr = nameIdent
} }
var decl *ast.GenDecl switch nameObj.(type) {
switch obj.(type) {
case *types.Const: case *types.Const:
// const _ = <value> // const _ = <value>
decl = &ast.GenDecl{ decl.Tok = token.CONST
Tok: token.CONST, spec.Values = []ast.Expr{nameExpr}
Specs: []ast.Spec{&ast.ValueSpec{
Names: []*ast.Ident{ast.NewIdent("_")},
Values: []ast.Expr{getFullName()},
}},
}
case *types.Var, *types.Func: case *types.Var, *types.Func:
// var _ = <value> // var _ = <value>
decl = &ast.GenDecl{ decl.Tok = token.VAR
Tok: token.VAR, spec.Values = []ast.Expr{nameExpr}
Specs: []ast.Spec{&ast.ValueSpec{
Names: []*ast.Ident{ast.NewIdent("_")},
Values: []ast.Expr{getFullName()},
}},
}
case *types.TypeName: case *types.TypeName:
// var _ <type> // var _ <type>
decl = &ast.GenDecl{ decl.Tok = token.VAR
Tok: token.VAR, spec.Type = nameExpr
Specs: []ast.Spec{&ast.ValueSpec{
Names: []*ast.Ident{ast.NewIdent("_")},
Type: getFullName(),
}},
}
default: default:
continue // Skip *types.Builtin and other continue // skip *types.Builtin and others
} }
// Manually add a new variable for correct name obfuscation // Ensure that types.Info.Uses is up to date.
tf.info.Uses[nameIdent] = obj tf.info.Uses[nameIdent] = nameObj
file.Decls = append(file.Decls, decl) file.Decls = append(file.Decls, decl)
generated = true
break
}
if !generated {
// A very unlikely situation where there is no suitable declaration for a reference variable
// and almost certainly means that there is another import reference in code.
log.Printf("generate reference variable for %s failed", imp.Path.Value)
}
} }
} }
@ -1823,7 +1784,7 @@ func (tf *transformer) transformGoFile(file *ast.File) *ast.File {
file = literals.Obfuscate(obfRand, file, tf.info, tf.linkerVariableStrings) file = literals.Obfuscate(obfRand, file, tf.info, tf.linkerVariableStrings)
// some imported constants might not be needed anymore, remove unnecessary imports // some imported constants might not be needed anymore, remove unnecessary imports
tf.makeImportsUsed(file) tf.useAllImports(file)
} }
pre := func(cursor *astutil.Cursor) bool { pre := func(cursor *astutil.Cursor) bool {

Loading…
Cancel
Save