forked from unixpickle/gobfuscate
-
Notifications
You must be signed in to change notification settings - Fork 0
/
const_to_var.go
115 lines (101 loc) · 2.11 KB
/
const_to_var.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package main
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
"sort"
"strings"
)
func stringConstsToVar(path string) error {
contents, err := ioutil.ReadFile(path)
if err != nil {
return err
}
set := token.NewFileSet()
file, err := parser.ParseFile(set, path, contents, 0)
if err != nil {
// If the file is invalid, we do nothing.
return nil
}
ctv := &constToVar{}
for _, decl := range file.Decls {
ast.Walk(ctv, decl)
}
sort.Sort(ctv)
var resBuf bytes.Buffer
var lastIdx int
for _, decl := range ctv.Decls {
start := int(decl.Pos() - 1)
end := int(decl.End() - 1)
resBuf.Write(contents[lastIdx:start])
declData := contents[start:end]
varData := strings.Replace(string(declData), "const", "var", 1)
resBuf.WriteString(varData)
lastIdx = end
}
resBuf.Write(contents[lastIdx:])
return ioutil.WriteFile(path, resBuf.Bytes(), 0755)
}
type constToVar struct {
Decls []*ast.GenDecl
}
func (c *constToVar) Visit(n ast.Node) ast.Visitor {
if decl, ok := n.(*ast.GenDecl); ok {
if decl.Tok == token.CONST {
if constOnlyHasStrings(decl) {
c.Decls = append(c.Decls, decl)
}
}
}
return c
}
func (c *constToVar) Len() int {
return len(c.Decls)
}
func (c *constToVar) Swap(i, j int) {
c.Decls[i], c.Decls[j] = c.Decls[j], c.Decls[i]
}
func (c *constToVar) Less(i, j int) bool {
return c.Decls[i].Pos() < c.Decls[j].Pos()
}
func constOnlyHasStrings(decl *ast.GenDecl) bool {
for _, spec := range decl.Specs {
if cs, ok := spec.(*ast.ValueSpec); ok {
if !specIsString(cs) {
return false
}
}
}
return true
}
func specIsString(v *ast.ValueSpec) bool {
if v.Type != nil {
s, ok := v.Type.(fmt.Stringer)
if ok && s.String() == "string" {
return true
}
}
if len(v.Values) != 1 {
return false
}
return exprIsString(v.Values[0])
}
func exprIsString(e ast.Expr) bool {
switch e := e.(type) {
case *ast.BasicLit:
if e.Kind == token.STRING {
return true
}
case *ast.BinaryExpr:
if e.Op == token.ADD {
return exprIsString(e.X) || exprIsString(e.Y)
}
return false
case *ast.ParenExpr:
return exprIsString(e.X)
}
return false
}