forked from go-interpreter/ssainterp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ssainterp.go
142 lines (120 loc) · 3.61 KB
/
ssainterp.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
// Parts of this code are:
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ssainterp
import (
"fmt"
"github.com/go-interpreter/ssainterp/interp"
"go/build"
"io"
//"./interp"
"go/types"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)
// Func is the signature of a normal go function
// that can be called from the interpreter.
// TODO: currently the parameters can only reliably contain simple values.
type Func func([]interp.Ivalue) interp.Ivalue
// ExtFunc defines an external go function callable from the interpreter.
type ExtFunc struct {
Nam string // e.g. "main.Foo"
Fun Func
}
// Interpreter defines the datastructure to run an interpreter.
type Interpreter struct {
Context *interp.Context
Panic interface{}
MainPkg *ssa.Package
exts *interp.Externals
}
// Run the interpreter, given some code.
func Run(code string, extFns []ExtFunc, args []string, output io.Writer) (interp *Interpreter, exitCode int, error error) {
ssai := new(Interpreter)
exitCode, err := ssai.run(code, extFns, args, output)
if ssai.Panic != nil {
return nil, -1, fmt.Errorf("panic in Run: %v", ssai.Panic)
}
return ssai, exitCode, err
}
func (ssai *Interpreter) run(code string, extFns []ExtFunc, args []string, output io.Writer) (int, error) {
defer func() {
if r := recover(); r != nil {
ssai.Panic = r
}
}()
conf := loader.Config{
Build: &build.Default,
}
// Parse the input file.
file, err := conf.ParseFile("main.go", code)
if err != nil {
return -1, err // parse error
}
// Create single-file main package.
conf.CreateFromFiles("main", file)
conf.Import("runtime") // always need this for ssa/interp
// Load the main package and its dependencies.
iprog, err := conf.Load()
if err != nil {
return -1, err // type error in some package
}
// Create SSA-form program representation.
prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
var mainPkg *ssa.Package
for _, pkg := range prog.AllPackages() {
if pkg.Pkg.Name() == "main" {
mainPkg = pkg
if mainPkg.Func("main") == nil {
return -1, fmt.Errorf("no func main() in main package")
}
break
}
}
if mainPkg == nil {
return -1, fmt.Errorf("no main package")
}
// Build SSA code for bodies for whole program
prog.Build()
//mainPkg.Func("main").WriteTo(os.Stdout) // list the main func in SSA form for DEBUG
ssai.exts = interp.NewExternals()
// add the callable external functions
for _, ef := range extFns {
ssai.exts.AddExtFunc(ef.Nam, ef.Fun)
}
context, exitCode := interp.Interpret(mainPkg, 0,
&types.StdSizes{
WordSize: 8, // word size in bytes - must be >= 4 (32bits)
MaxAlign: 8, // maximum alignment in bytes - must be >= 1
}, "main.go", args, ssai.exts, output)
if context == nil {
return -1, fmt.Errorf("nil context returned")
}
ssai.Context = context
ssai.MainPkg = mainPkg
return exitCode, nil
}
// Call a function in an already created interpreter.
func (ssai *Interpreter) Call(name string, args []interp.Ivalue) (interp.Ivalue, error) {
if ssai == nil {
return nil, fmt.Errorf("nil *Interpreter")
}
if ssai.Panic != nil {
return nil, fmt.Errorf("prior panic in Interpreter: %v", ssai.Panic)
}
result := ssai.call(name, args)
if ssai.Panic != nil {
return nil, fmt.Errorf("panic in Call: %v", ssai.Panic)
}
return result, nil
}
func (ssai *Interpreter) call(name string, args []interp.Ivalue) interp.Ivalue {
defer func() {
if r := recover(); r != nil {
ssai.Panic = r
}
}()
return ssai.Context.Call(name, args)
}