Commit c2227c10 authored by Hugo Moreau's avatar Hugo Moreau

Blackbox using state vector

Blackboxed functions are now able to handle global variables, by using
G2PState as a reference.

 * main.go,
   tools/blackbox.go,
   transform/checktype.go, transform/global.go,
   transform/globaltostate.go, transform/localvariableassignments.go,
   transform/postglobal.go : Here.
parent a8cafbfd
Pipeline #26461 passed with stage
in 2 minutes and 32 seconds
......@@ -71,12 +71,12 @@ func variableNamesToC(variableNames []string) []ast.Expr {
// Compiles the input file `(path, src)` and writes it to `file`.
func compileTo(filepath string, src []byte, file *os.File,
goroutines []*cspinfo.Goroutine, rers *string, output string) (string, error) {
goroutines []*cspinfo.Goroutine, rers *string, output string) (string, error, *transform.Meta) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, filepath, src, 0)
if err != nil {
return "", err
return "", err, nil
}
var config types.Config
......@@ -100,6 +100,7 @@ func compileTo(filepath string, src []byte, file *os.File,
meta.Create("LineCounter", g.Name)
}
localvariable := &transform.LocalVariableAssignments{}
res := transform.ApplyAll(f, meta, []transform.Transform{
// &transform.Debug{}, // only for debuging a special transformation
&transform.Alive{},
......@@ -116,7 +117,7 @@ func compileTo(filepath string, src []byte, file *os.File,
&transform.RoutineAssignments{
Functions: goroutines,
},
&transform.LocalVariableAssignments{},
localvariable,
&transform.AfterChannel{},
&cfg.Transform{},
&transform.RoutineCounter{
......@@ -125,7 +126,7 @@ func compileTo(filepath string, src []byte, file *os.File,
&transform.DoubleBrackets{},
&transform.Format{},
})
globalObj = localvariable.GlobalBlackbox
var channels = meta.GetChans()
// Generate all the cases of the main switch case.
......@@ -306,7 +307,7 @@ func compileTo(filepath string, src []byte, file *os.File,
astutil.AddImport(fset, res.(*ast.File), path.Join(output, "structs"))
astutil.AddImport(fset, res.(*ast.File), "C")
return f.Name.Name, format.Node(file, fset, res)
return f.Name.Name, format.Node(file, fset, res), meta
}
// Copies files over and format them according to the template data.
......@@ -451,24 +452,21 @@ func treatGlobal(src []byte) ([]byte, bool) {
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, "", src, 0)
if err != nil {
fmt.Println(os.Stderr, err.Error())
os.Exit(1)
}
meta := transform.NewMeta(fset, info)
preglobal := &transform.PreGlobal{}
transform.Apply(node, meta, preglobal)
globalVar = preglobal.GlobalVar
if len(preglobal.GlobalVar) == 0 {
return src, false
}
setter, getter := &transform.Global{Setter: true, GlobalVar: preglobal.GlobalVar},
&transform.Global{Setter: false, GlobalVar: preglobal.GlobalVar}
res := transform.ApplyAll(node, meta, []transform.Transform{setter, getter})
res = transform.Apply(res, meta, &transform.PostGlobal{
FirstFunc: setter.FirstFunc,
FuncsGetter: getter.Funcs,
FuncsSetter: setter.Funcs,
GetterToAdd: getter.FuncCallToAdd,
SetterToAdd: setter.FuncCallToAdd,
})
global := &transform.Global{GlobalVar: preglobal.GlobalVar, ToIgnore: toIgnore}
res := transform.Apply(node, meta, global)
res = transform.Apply(node, meta, &transform.PostGlobal{GlobalBlackbox: global.GlobalBlackbox})
tmpFile, err := ioutil.TempFile("", "*_go2pins.go")
if err != nil {
panic("couldn't create go2pins temp file")
......@@ -487,6 +485,32 @@ func treatGlobal(src []byte) ([]byte, bool) {
return src, true
}
func insertGlobalInBlackbox(meta *transform.Meta, filename string) {
src, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, "", src, 0)
if err != nil {
fmt.Println(os.Stderr, err.Error())
os.Exit(1)
}
transform.Apply(node, meta, &transform.GlobalToState{GlobalVar: globalVar, GlobalBlackbox: globalObj})
file, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
fmt.Println(os.Stderr, err.Error())
os.Exit(2)
}
defer file.Close()
err = format.Node(file, fset, node)
if err != nil {
fmt.Println(os.Stderr, err.Error())
os.Exit(2)
}
}
type packageTemplate struct {
PackagePath string
PackageName string
......@@ -494,6 +518,8 @@ type packageTemplate struct {
}
var toIgnore []string
var globalObj map[string]*ast.Object
var globalVar []string
var (
astprint = flag.Bool("print-ast", false, "Print AST of <file>")
......@@ -601,8 +627,10 @@ func main() {
os.Exit(1)
}
var global bool = false
if len(toIgnore) != 0 {
a, bb := tools.RewriteBlackbox(string(src), toIgnore, *output)
src, global = treatGlobal(src)
a, bb := tools.RewriteBlackbox(string(src), toIgnore, global, *output)
if err = os.MkdirAll(*output+"/blackbox", 0777); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
......@@ -640,11 +668,14 @@ func main() {
}
// Compile and write the package to the output file.
packageName, err := compileTo(basePackagePath, src, f, goroutines, rers, *output)
packageName, err, meta := compileTo(basePackagePath, src, f, goroutines, rers, *output)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
if global {
insertGlobalInBlackbox(meta, path.Join(*output, "blackbox/blackbox.go"))
}
tools.FormatArray(path.Join(*output, "structs/structs.go"), "var G2PVariableNames =")
......
......@@ -24,6 +24,7 @@ import (
"go/parser"
"go/printer"
"go/token"
"golang.org/x/tools/go/ast/astutil"
"log"
"path"
"strconv"
......@@ -43,7 +44,7 @@ func find(a []string, x string) bool {
// the orignal file to put them (visibility public) in the Blackbox file
// Nonetheles problem can occur when a blackbox function call a non
// blackbox one
func RewriteBlackbox(inputfile string, funs []string, outputdir string) (string, string) {
func RewriteBlackbox(inputfile string, funs []string, global bool, outputdir string) (string, string) {
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, "src.go", inputfile, 0)
if err != nil {
......@@ -137,6 +138,7 @@ func RewriteBlackbox(inputfile string, funs []string, outputdir string) (string,
bb_ast.Name.Name = "blackbox"
bb_ast.Decls = toBB
astutil.AddImport(fset, bb_ast, path.Join(outputdir, "structs"))
printer.Fprint(&buf, fset, bb_ast)
bb := buf.String()
......
......@@ -104,9 +104,6 @@ func (t *TypeChecker) Pre(meta *Meta, v *Visitor) bool {
case *ast.SwitchStmt:
panic("Switch are not supported.")
case *ast.ValueSpec:
if t.Blackbox && t.currentFunction == nil {
panic("No support for Blackbox and global variables.")
}
for i := 0; i < len(node.Values); i++ {
switch value := node.Values[i].(type) {
case *ast.BasicLit:
......
......@@ -3,7 +3,6 @@ package transform
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/ast/astutil"
)
/*
......@@ -34,32 +33,25 @@ func foo(G2P_a_set_ptr func(int), G2P_a_get_ptr func() int) {
type Global struct {
currentFunction *ast.Object
FirstFunc string
funcArgToAdd map[string]*ast.Field
FuncCallToAdd map[string][]*ast.Ident
Funcs []*ast.FuncDecl
funcsVar map[string][]string
GlobalVar []string
Setter bool
GlobalBlackbox map[string]bool
ToIgnore []string
}
// Init all Global struct's map at the begining of transform
func (t *Global) init() {
t.funcsVar = make(map[string][]string)
t.funcArgToAdd = make(map[string]*ast.Field)
t.FuncCallToAdd = make(map[string][]*ast.Ident)
t.GlobalBlackbox = make(map[string]bool)
}
// When transform encounter an *ast.FuncDecl, handleFuncDecl will
// register current function and register local variables in the appropriate
// map.
func (t *Global) handleFuncDecl(node *ast.FuncDecl) {
if t.FirstFunc == "" {
t.FirstFunc = node.Name.Obj.Name
}
t.currentFunction = node.Name.Obj
t.GlobalBlackbox[node.Name.Name] = false
t.funcsVar[node.Name.Name] = []string{}
t.FuncCallToAdd[node.Name.Name] = []*ast.Ident{}
for i := 0; i < len(node.Type.Params.List); i++ {
for j := 0; j < len(node.Type.Params.List[i].Names); j++ {
t.funcsVar[t.currentFunction.Name] = append(
......@@ -70,7 +62,7 @@ func (t *Global) handleFuncDecl(node *ast.FuncDecl) {
}
// When transform encounter an *ast.ValueSpec, handleValueSpec will
// register global variables or local variables in their appropriate map
// register local variables.
func (t *Global) handleValueSpec(node *ast.ValueSpec) {
if t.currentFunction != nil {
for i := 0; i < len(node.Names); i++ {
......@@ -81,129 +73,22 @@ func (t *Global) handleValueSpec(node *ast.ValueSpec) {
}
}
// When transform encounter an *ast.AssignStmt, reguralizeAssign will
// reguralize all Stmt different from ASSIGN and DEFINE, in order to simplify
// Getter or Setter uses.
func (t *Global) reguralizeAssign(node *ast.AssignStmt) {
if tok := TokenAssignLess(node.Tok); tok != token.ASSIGN {
switch lhs := node.Lhs[0].(type) {
case *ast.Ident:
if find(t.GlobalVar, lhs.Name) && !find(t.funcsVar[t.currentFunction.Name], lhs.Name) {
node.Tok = token.ASSIGN
node.Rhs[0] = &ast.BinaryExpr{
X: lhs,
Op: tok,
Y: node.Rhs[0],
}
}
}
}
}
// Encoutering a similar *ast.Ident as global variables, Getter or Setter
// will be invoked to replace accordingly to the wanted action.
// Return the identifier of the correct function Getter or Setter.
func (t *Global) createIdent(c *astutil.Cursor, lhs *ast.Ident, i int, node *ast.AssignStmt) (*ast.Ident, int) {
var ident *ast.Ident
if t.Setter {
ident = ast.NewIdent("G2P_" + lhs.Name + "_set_ptr")
expr := &ast.ExprStmt{
X: &ast.CallExpr{
Fun: ident,
Args: []ast.Expr{node.Rhs[i]},
},
}
if len(node.Lhs) > 1 {
c.InsertBefore(expr)
copy(node.Lhs[i:], node.Lhs[i+1:])
node.Lhs = node.Lhs[:len(node.Lhs)-1]
copy(node.Rhs[i:], node.Rhs[i+1:])
node.Rhs = node.Rhs[:len(node.Rhs)-1]
i = -1
} else {
c.Replace(expr)
}
} else {
ident = ast.NewIdent("G2P_" + lhs.Name + "_get_ptr")
c.Replace(&ast.CallExpr{
Fun: ident,
})
}
return ident, i
}
// createArg is called at same time as createIdent, it will generate the
// identifier for the current function call.
func (t *Global) createArg(name, argtype string, ident *ast.Ident) *ast.Ident {
funcident := ast.NewIdent("G2P_" + name)
t.FuncCallToAdd[t.currentFunction.Name] = append(
t.FuncCallToAdd[t.currentFunction.Name],
funcident)
if _, ok := t.funcArgToAdd[t.currentFunction.Name]; ok {
t.funcArgToAdd[t.currentFunction.Name].Names = append(
t.funcArgToAdd[t.currentFunction.Name].Names,
ident)
} else {
t.funcArgToAdd[t.currentFunction.Name] = &ast.Field{
Names: []*ast.Ident{ident},
Type: ast.NewIdent(argtype),
}
}
return funcident
}
// Called with the two previous functions, createFunc will generate
// the function body according to the createArg identifier.
func (t *Global) createFunc(node, funcident *ast.Ident) *ast.FuncDecl {
res := &ast.FuncDecl{
Name: funcident,
Type: &ast.FuncType{},
Body: &ast.BlockStmt{
List: []ast.Stmt{},
},
}
if t.Setter {
value := ast.NewIdent("value")
res.Type.Params = &ast.FieldList{
List: []*ast.Field{
&ast.Field{
Names: []*ast.Ident{value},
Type: ast.NewIdent("int"),
},
},
}
res.Body.List = append(res.Body.List, &ast.AssignStmt{
Lhs: []ast.Expr{node},
Tok: token.ASSIGN,
Rhs: []ast.Expr{value},
})
} else {
res.Type.Results = &ast.FieldList{
List: []*ast.Field{
&ast.Field{
Type: ast.NewIdent("int"),
},
},
}
res.Body.List = append(res.Body.List,
&ast.ReturnStmt{
Results: []ast.Expr{
node,
},
})
}
return res
}
// addArg will add in the current function Parameters List, the new
// Getter or Setter created
func addArg(node *ast.File, funcs map[string]*ast.Field) {
// TODO: <20-02-21, iRyukizo> //
func addState(node *ast.File, funcs map[string]bool) {
for _, d := range node.Decls {
if f, ok := d.(*ast.FuncDecl); ok {
if val, ok2 := funcs[f.Name.Name]; ok2 {
f.Type.Params.List = append(f.Type.Params.List, val)
if funcs[f.Name.Name] {
list := []*ast.Field{
&ast.Field{
Names: []*ast.Ident{ast.NewIdent("G2PState")},
Type: &ast.SelectorExpr{
X: ast.NewIdent("*structs"),
Sel: ast.NewIdent("G2PStateType"),
},
},
}
list = append(list, f.Type.Params.List...)
f.Type.Params.List = list
}
}
}
......@@ -235,40 +120,13 @@ func (t *Global) Pre(meta *Meta, v *Visitor) bool {
t.funcsVar[t.currentFunction.Name] = append(t.funcsVar[t.currentFunction.Name], lhs.Name)
}
}
} else if t.Setter {
t.reguralizeAssign(node)
for i := 0; i < len(node.Lhs); i++ {
switch lhs := node.Lhs[i].(type) {
case *ast.Ident:
if find(t.GlobalVar, lhs.Name) && !find(t.funcsVar[t.currentFunction.Name], lhs.Name) {
var ident *ast.Ident
ident, i = t.createIdent(c, lhs, i, node)
if findIdent(t.funcArgToAdd[t.currentFunction.Name], ident.Name) {
continue
}
funcident := t.createArg(lhs.Name+"_set", "func(int)", ident)
if findFuncIdent(t.Funcs, funcident.Name) {
return true
}
t.Funcs = append(t.Funcs, t.createFunc(lhs, funcident))
}
}
}
}
case *ast.Ident:
if t.currentFunction == nil || t.currentFunction.Name == "main" {
return true
}
if !t.Setter && find(t.GlobalVar, node.Name) && !find(t.funcsVar[t.currentFunction.Name], node.Name) {
ident, _ := t.createIdent(c, node, -1, nil)
if findIdent(t.funcArgToAdd[t.currentFunction.Name], ident.Name) {
return true
}
funcident := t.createArg(node.Name+"_get", "func() int", ident)
if findFuncIdent(t.Funcs, funcident.Name) {
return true
}
t.Funcs = append(t.Funcs, t.createFunc(node, funcident))
if find(t.GlobalVar, node.Name) && !find(t.funcsVar[t.currentFunction.Name], node.Name) && find(t.ToIgnore, t.currentFunction.Name) {
t.GlobalBlackbox[t.currentFunction.Name] = true
}
}
return true
......@@ -279,7 +137,7 @@ func (t *Global) Post(meta *Meta, v *Visitor) bool {
c := v.Cursor()
switch node := c.Node().(type) {
case *ast.File:
addArg(node, t.funcArgToAdd)
addState(node, t.GlobalBlackbox)
}
return true
}
......@@ -292,27 +150,3 @@ func find(a []string, x string) bool {
}
return false
}
func findIdent(a *ast.Field, x string) bool {
if a == nil {
return false
}
for _, val := range a.Names {
if x == val.Name {
return true
}
}
return false
}
func findFuncIdent(a []*ast.FuncDecl, x string) bool {
if a == nil {
return false
}
for _, val := range a {
if x == val.Name.Name {
return true
}
}
return false
}
package transform
import (
"go/ast"
"go/token"
)
type GlobalToState struct {
currentFunction *ast.Object
funcsVar map[string][]string
GlobalBlackbox map[string]*ast.Object
GlobalVar []string
}
// Pre traversal applies the transformation
func (t *GlobalToState) Pre(meta *Meta, v *Visitor) bool {
c := v.Cursor()
switch node := c.Node().(type) {
case *ast.File:
t.funcsVar = make(map[string][]string)
case *ast.FuncDecl:
t.currentFunction = node.Name.Obj
t.funcsVar[node.Name.Name] = []string{}
for i := 0; i < len(node.Type.Params.List); i++ {
for j := 0; j < len(node.Type.Params.List[i].Names); j++ {
t.funcsVar[t.currentFunction.Name] = append(
t.funcsVar[t.currentFunction.Name],
node.Type.Params.List[i].Names[j].Name)
}
}
case *ast.GenDecl:
switch c.Parent().(type) {
case *ast.File:
t.currentFunction = nil
}
case *ast.ValueSpec:
if t.currentFunction != nil {
for i := 0; i < len(node.Names); i++ {
t.funcsVar[t.currentFunction.Name] = append(
t.funcsVar[t.currentFunction.Name],
node.Names[i].Name)
}
}
case *ast.AssignStmt:
if node.Tok == token.DEFINE {
for i := 0; i < len(node.Lhs); i++ {
switch lhs := node.Lhs[i].(type) {
case *ast.Ident:
t.funcsVar[t.currentFunction.Name] = append(t.funcsVar[t.currentFunction.Name], lhs.Name)
}
}
}
case *ast.Ident:
if find(t.GlobalVar, node.Name) && !find(t.funcsVar[t.currentFunction.Name], node.Name) {
c.Replace(meta.Get(t.GlobalBlackbox[node.Name]))
}
}
return true
}
func (t *GlobalToState) Post(meta *Meta, v *Visitor) bool {
return true
}
......@@ -56,12 +56,15 @@ G2PState[7] = 0
type LocalVariableAssignments struct {
currentFunction *ast.Object
globalToMain []ast.Stmt
GlobalBlackbox map[string]*ast.Object
}
// Pre traversal applies the transformation.
func (t *LocalVariableAssignments) Pre(meta *Meta, v *Visitor) bool {
c := v.Cursor()
switch node := c.Node().(type) {
case *ast.File:
t.GlobalBlackbox = make(map[string]*ast.Object)
case *ast.FuncDecl:
t.currentFunction = node.Name.Obj
case *ast.GenDecl:
......@@ -74,6 +77,7 @@ func (t *LocalVariableAssignments) Pre(meta *Meta, v *Visitor) bool {
for i := 0; i < len(node.Names); i++ {
obj := node.Names[i].Obj
meta.Add(obj, v.Name())
t.GlobalBlackbox[obj.Name] = obj
if node.Values != nil {
t.globalToMain = append(t.globalToMain, meta.Set(obj, node.Values[i]))
}
......
......@@ -4,83 +4,21 @@ import (
"go/ast"
)
/*
The PostGlobal transform will occurs directly after the Global transform,
it will add the correct Getters and Setter definitions. At the same time,
it will add all the correct arguments to all modified function call.
Transform:
Before:
```
var a = 0
func foo(G2P_a_set_ptr func(int), G2P_a_get_ptr func() int) {
G2P_a_set_ptr(G2P_a_get_ptr() + 42)
}
func main {
foo()
}
```
After:
```
var a = 0
func G2P_a_set(value int) {
a = value
}
func G2P_a_get() int {
return a
}
func foo(G2P_a_set_ptr func(int), G2P_a_get_ptr func() int) {
G2P_a_set_ptr(G2P_a_get_ptr() + 42)
}
func main {
foo(G2P_a_set, G2P_a_get)
}
```
*/
type PostGlobal struct {
FirstFunc string
FuncsGetter []*ast.FuncDecl
FuncsSetter []*ast.FuncDecl
GetterToAdd map[string][]*ast.Ident
SetterToAdd map[string][]*ast.Ident
GlobalBlackbox map[string]bool
}
// injectArgs will add to all function call, the correct Getter and Setter
// obtained with the Global transform.
func injectArgs(node *ast.CallExpr, fun *ast.Ident, funcCall map[string][]*ast.Ident) {
if funcCall == nil {
// injectArgs will add to all function call, the state of vector
func injectArgs(node *ast.CallExpr, fun *ast.Ident, globalBlackbox map[string]bool) {
if globalBlackbox == nil {
return
}
if val, ok := funcCall[fun.Name]; ok {