-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathloader.go
152 lines (142 loc) · 3.88 KB
/
loader.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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/**
* @Author: d33pblue
* @Date: 2020-Apr-19
* @Last modified by: d33pblue
* @Last modified time: 2020-May-19
* @Copyright: 2020
*/
package ga
import(
"fmt"
"plugin"
"os/exec"
"go/ast"
"go/token"
"go/parser"
"errors"
"strings"
)
// Problem defines the interface the user has to
// implement to declare his problem.
// Initialize method is called at the beginning, and then
// New has to return an instance of the DNA interface.
type Problem interface {
Initialize(path string)
New()DNA
}
// Checks if the package is "main" in order
// to compile the source as plugin.
func checkPackage(f *ast.File)error{
if f.Name.Name!="main"{
return errors.New("The defined package is not main")
}
return nil
}
// Checks the user does not import forbidden modules.
func checkImport(decl *ast.ImportSpec)error{
var whitelist = []string{"math/rand","io/ioutil",
"encoding/json","github.com/D33pBlue/poe/op",
"github.com/D33pBlue/poe/ga","fmt"}
var name string = decl.Path.Value
if len(name)>0 && name[0]=='"' {
name = name[1:]
}
if len(name)>0 && name[len(name)-1]=='"' {
name = name[:len(name)-1]
}
var found bool = false
for i:=0; !found && i<len(whitelist); i++{
if name==whitelist[i] {
found = true
}
}
if !found{
return errors.New("Imported "+name+", which is not in white list.")
}
return nil
}
// Checks the functions defined by the user.
func checkFunc(decl *ast.FuncDecl)error{
// TODO: check for conditions at least
return nil
}
// Checks the source defined by the user.
func checkDeclarations(f *ast.File)error{
for _,decl := range f.Decls{
switch decl.(type) {
case *ast.GenDecl:
switch fmt.Sprint(decl.(*ast.GenDecl).Tok) {
case "import":
for _,im := range decl.(*ast.GenDecl).Specs{
err := checkImport(im.(*ast.ImportSpec))
if err!=nil{return err}
}
// case "type": fmt.Println("TYPE")
// case "var": fmt.Println("VV")
}
case *ast.FuncDecl:
err := checkFunc(decl.(*ast.FuncDecl))
if err!=nil{return err}
}
}
return nil
}
// Parse and inspect the source defined by the user.
func inspect(dir,name string)error{
fset := token.NewFileSet()
f, err := parser.ParseFile(fset,dir+name+".go",nil, parser.AllErrors)
if err != nil {
return err
}
// ast.Print(fset,f)
err = checkPackage(f)
if err!=nil {return err}
err = checkDeclarations(f)
if err!=nil {return err}
return nil
}
// Compiles a plugin, after checking it.
func compilePlugin(dir,name string)error{
err := inspect(dir,name)
if err!=nil{
return err
}
baseName := dir+name
cmd := exec.Command("go","build","-buildmode=plugin",
"-o",baseName+".so",baseName+".go")
cmdstr := fmt.Sprint("go build -buildmode=plugin -o "+baseName+".so "+baseName+".go")
// fmt.Println("Compiling plugin..")
err = cmd.Run()
if err!=nil{
return errors.New("Error in plugin compilation:\nrun the following command for more details:\n\t"+cmdstr)
}
return nil
}
// Returns a DNA after checking and compiling
// a user-defined plugin.
func LoadGA(path2job,path2data string)(DNA,error){
index := strings.LastIndex(path2job,"/")
plugDir := path2job[:index+1]
plugName := path2job[index+1:]
index = strings.LastIndex(plugName,".")
plugName = plugName[:index]
err := compilePlugin(plugDir,plugName)
if err != nil { return nil,err}
plug, err := plugin.Open(plugDir+plugName+".so")
if err != nil {return nil,err}
definition, err := plug.Lookup("Definition")
if err != nil {return nil,err}
var problem Problem
problem, ok := definition.(Problem)
if !ok {
err = errors.New("The module does not implement Problem interface")
return nil,err
}
problem.Initialize(path2data)
problDna := problem.New()
if problDna==nil{
err = errors.New("The function New defined in the module does not return a valid DNA")
return nil,err
}
return problDna,nil
}