Commit a8cafbfd authored by Hugo Moreau's avatar Hugo Moreau

G2P type in a dedicated package

For future global variable access via `Blackbox`, all type generated by
Go2Pins are now stored in a dedicated package `structs`.
All convenient modifications has been done to let Go2Pins in a valid
state.

 * boilerplate/Makefile, boilerplate/main.go,
   boilerplate/structs/structs.go,
   decl/decl.go,
   main.go,
   tools/blackbox.go,
   transform/meta.go : Here.
parent cb1fec85
Pipeline #26438 passed with stage
in 2 minutes and 11 seconds
......@@ -29,14 +29,17 @@ endif
all: main.dve2C main
main:
main: go.mod
env CGO_LDFLAGS=-no-pie go build -o go2pins-mc *.go
main.dve2C: libgo2pins.a
main.dve2C: libgo2pins.a go.mod
go build -o $@ -buildmode=c-shared
libgo2pins.a: go2pins.o
$(AR) cru $@ $^
go.mod:
go mod init {{.Output}}
clean:
$(RM) main.dve2C main.a main.h go2pins.o main
......@@ -25,6 +25,7 @@ package {{.PackageName}}
*/
import "C"
import (
"{{.Output}}/structs"
"flag"
"fmt"
"log"
......@@ -37,26 +38,8 @@ import (
"unsafe"
)
type GoroutineFunc func(G2PStateType) G2PStateType
// A Goroutine contains information related to a goroutine's
// representation inside go2pins
type Goroutine struct {
Status int // index in vector for status variable
LineCounter int // index in vector for line counter variable
Func GoroutineFunc // duplicated function to launch
}
// A Channel contains informations realted to a channel's
// representation inside go2pins
type Channel struct {
State int // index in vector of the beggining of State's array
Value int // index in vector of the beggining of Value's array
Length int // length of the arrays (same for the two)
}
func G2PEntry(src G2PStateType) []G2PStateType {
res := make([]G2PStateType, 0)
func G2PEntry(src structs.G2PStateType) []structs.G2PStateType {
res := make([]structs.G2PStateType, 0)
for _, g := range G2PGoroutines {
if src[g.Status] != 0 { // is goroutine active ?
res = append(res, g.Func(src))
......@@ -65,13 +48,13 @@ func G2PEntry(src G2PStateType) []G2PStateType {
result := append(res, G2PMain(src))
var final []G2PStateType = []G2PStateType{}
var final []structs.G2PStateType = []structs.G2PStateType{}
WAIT_READ := -1
WAIT_WRITE := 1
for _, succ := range result {
all_are_clear := true
var toadd []G2PStateType = []G2PStateType{}
var toadd []structs.G2PStateType = []structs.G2PStateType{}
for _, channel := range G2PChannels {
for pid, idx := 0, channel.State; idx < channel.State+channel.Length; pid, idx = pid+1, idx+2 {
......@@ -86,7 +69,7 @@ func G2PEntry(src G2PStateType) []G2PStateType {
for jdx := channel.State; jdx < channel.State+channel.Length; jdx += 2 {
if succ[jdx] == WAIT_READ {
// build the combination of the 2
var new_succ G2PStateType = succ
var new_succ structs.G2PStateType = succ
// Reset WAIT_READ / WAIT_WRITE operations
new_succ[idx] = 0
......@@ -125,11 +108,11 @@ func G2PEntry(src G2PStateType) []G2PStateType {
}
// Build the result by considering eventually RERS inputs
var final2 []G2PStateType = []G2PStateType{}
var final2 []structs.G2PStateType = []structs.G2PStateType{}
for i, _ := range final {
if G2PRersActive != 0 && src[G2PRersPos] != result[i][G2PRersPos] {
for _, e := range G2PRersValues {
result[i][G2PRersPos] = e
if structs.G2PRersActive != 0 && src[structs.G2PRersPos] != result[i][structs.G2PRersPos] {
for _, e := range structs.G2PRersValues {
result[i][structs.G2PRersPos] = e
final2 = append(final2, result[i])
}
} else {
......@@ -141,23 +124,23 @@ func G2PEntry(src G2PStateType) []G2PStateType {
//export get_state_variable_count
func get_state_variable_count() C.int {
return C.int(G2PStateSize)
return C.int(structs.G2PStateSize)
}
// See https://github.com/golang/go/wiki/cgo#turning-c-arrays-into-go-slices
// for trick used in this function and the next one:
// The pointer is casted to a go array of size (2^30) then sliced to
// the size expected
func g2pCopyToC(arr *G2PStateType, arrC *C.int) {
arrC2 := (*[1 << 30]C.int)(unsafe.Pointer(arrC))[:G2PStateSize:G2PStateSize]
for i := 0; i < G2PStateSize; i++ {
func g2pCopyToC(arr *structs.G2PStateType, arrC *C.int) {
arrC2 := (*[1 << 30]C.int)(unsafe.Pointer(arrC))[:structs.G2PStateSize:structs.G2PStateSize]
for i := 0; i < structs.G2PStateSize; i++ {
arrC2[i] = C.int(arr[i])
}
}
func g2pCopyFromC(arrC *C.int, arr *G2PStateType) {
arrC2 := (*[1 << 30]C.int)(unsafe.Pointer(arrC))[:G2PStateSize:G2PStateSize]
for i := 0; i < G2PStateSize; i++ {
func g2pCopyFromC(arrC *C.int, arr *structs.G2PStateType) {
arrC2 := (*[1 << 30]C.int)(unsafe.Pointer(arrC))[:structs.G2PStateSize:structs.G2PStateSize]
for i := 0; i < structs.G2PStateSize; i++ {
arr[i] = int(arrC2[i])
}
}
......@@ -169,10 +152,10 @@ func get_successors(
cb C.TransitionCB,
arg unsafe.Pointer,
) C.int {
src := G2PStateType{}
src := structs.G2PStateType{}
g2pCopyFromC(srcC, &src)
dsts := G2PEntry(src)
var dstC = (*C.int)(C.malloc(C.size_t(G2PStateSize) * C.sizeof_int))
var dstC = (*C.int)(C.malloc(C.size_t(structs.G2PStateSize) * C.sizeof_int))
for _, dst := range dsts {
g2pCopyToC(&dst, dstC)
C.callTransitionCb(cb, arg, nil, dstC)
......@@ -182,7 +165,7 @@ func get_successors(
//export get_initial_state
func get_initial_state(srcC *C.int) {
src := G2PStateType{0, 0}
src := structs.G2PStateType{0, 0}
g2pCopyToC(&src, srcC)
}
......@@ -191,8 +174,8 @@ var unknownVarName = C.CString("UNKNOWN_VAR")
//export get_state_variable_name
func get_state_variable_name(vC C.int) *C.char {
v := int(vC)
if v < len(G2PVariableNames) {
return G2PVariableNames[v]
if v < len(structs.G2PVariableNames) {
return (*C.char)(structs.G2PVariableNames[v])
}
return unknownVarName
}
......@@ -442,10 +425,10 @@ func main() {
if *list_var {
fmt.Println("State size:", G2PStateSize)
fmt.Println("State size:", structs.G2PStateSize)
fmt.Println("List of variable names:")
for i := 0; i < G2PStateSize; i++ {
fmt.Println(" - " + C.GoString(G2PVariableNames[i]))
for i := 0; i < structs.G2PStateSize; i++ {
fmt.Println(" - " + C.GoString((*C.char)(structs.G2PVariableNames[i])))
}
}
......
package structs
type GoroutineFunc func(G2PStateType) G2PStateType
// A Goroutine contains information related to a goroutine's
// representation inside go2pins
type Goroutine struct {
Status int // index in vector for status variable
LineCounter int // index in vector for line counter variable
Func GoroutineFunc // duplicated function to launch
}
// A Channel contains informations realted to a channel's
// representation inside go2pins
type Channel struct {
State int // index in vector of the beggining of State's array
Value int // index in vector of the beggining of Value's array
Length int // length of the arrays (same for the two)
}
......@@ -109,7 +109,7 @@ func IntArray(name string, arr []int) *ast.GenDecl {
// Goroutine generates a Goroutine struct declaration
func Goroutine(g *cspinfo.Goroutine) *ast.CompositeLit {
return &ast.CompositeLit{
Type: ast.NewIdent("Goroutine"),
Type: ast.NewIdent("structs.Goroutine"),
Elts: []ast.Expr{
&ast.KeyValueExpr{
Key: ast.NewIdent("Status"),
......@@ -139,13 +139,13 @@ func GoroutineArray(name string, arr []*cspinfo.Goroutine) *ast.GenDecl {
for i, g := range arr {
exprs[i] = Goroutine(g)
}
return Array(name, SimpleArrayType("Goroutine"), exprs)
return Array(name, SimpleArrayType("structs.Goroutine"), exprs)
}
// Channel generates a Channel struct declaration
func Channel(c cspinfo.Channel) *ast.CompositeLit {
return &ast.CompositeLit{
Type: ast.NewIdent("Channel"),
Type: ast.NewIdent("structs.Channel"),
Elts: []ast.Expr{
&ast.KeyValueExpr{
Key: ast.NewIdent("State"),
......@@ -171,7 +171,7 @@ func ChannelArray(name string, arr []cspinfo.Channel) *ast.GenDecl {
for i, c := range arr {
exprs[i] = Channel(c)
}
return Array(name, SimpleArrayType("Channel"), exprs)
return Array(name, SimpleArrayType("structs.Channel"), exprs)
}
// Int generates an integer declaration.
......
......@@ -70,10 +70,10 @@ func variableNamesToC(variableNames []string) []ast.Expr {
}
// Compiles the input file `(path, src)` and writes it to `file`.
func compileTo(path string, src []byte, file *os.File,
goroutines []*cspinfo.Goroutine, rers *string) (string, error) {
func compileTo(filepath string, src []byte, file *os.File,
goroutines []*cspinfo.Goroutine, rers *string, output string) (string, error) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, path, src, 0)
f, err := parser.ParseFile(fset, filepath, src, 0)
if err != nil {
return "", err
......@@ -88,7 +88,7 @@ func compileTo(path string, src []byte, file *os.File,
}
// Ensure that our program type checks.
_, err = config.Check(path, fset, []*ast.File{f}, info)
_, err = config.Check(filepath, fset, []*ast.File{f}, info)
// Apply all transformations.
meta := transform.NewMeta(fset, info)
......@@ -211,8 +211,15 @@ func compileTo(path string, src []byte, file *os.File,
// We also need to add declarations for the state size, the type of the state
// itself, the names of the different variables of our program, and finally
// the entry function itself.
structs, err := ioutil.ReadFile(path.Join(output, "structs/structs.go"))
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fset2 := token.NewFileSet()
node, _ := parser.ParseFile(fset2, "", structs, 0)
transform.InjectDecls(
res.(*ast.File),
node,
[]ast.Decl{
// Do we want a support for RERS?
decl.Int(token.CONST, "G2PRersActive", rers_active),
......@@ -224,7 +231,7 @@ func compileTo(path string, src []byte, file *os.File,
// type declaration for state vector
decl.Type(
meta.StateType(),
meta.StructStateType(),
&ast.ArrayType{
Len: ast.NewIdent("G2PStateSize"),
Elt: ast.NewIdent("int"),
......@@ -249,10 +256,15 @@ func compileTo(path string, src []byte, file *os.File,
},
variableNamesToC(meta.GetVariables()),
),
},
)
astutil.AddImport(fset2, node, "C")
transform.InjectDecls(
res.(*ast.File),
[]ast.Decl{
decl.GoroutineArray("G2PGoroutines", goroutines),
decl.ChannelArray("G2PChannels", channels),
// entrypoint to get a successor from a state vector
decl.Function(
"G2PMain",
......@@ -279,6 +291,20 @@ func compileTo(path string, src []byte, file *os.File,
),
},
)
structFile, err := os.OpenFile(path.Join(output, "structs/structs.go"), os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
fmt.Println(os.Stderr, err.Error())
os.Exit(2)
}
defer structFile.Close()
err = format.Node(structFile, fset2, node)
if err != nil {
fmt.Println(os.Stderr, err.Error())
os.Exit(2)
}
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)
}
......@@ -464,6 +490,7 @@ func treatGlobal(src []byte) ([]byte, bool) {
type packageTemplate struct {
PackagePath string
PackageName string
Output string
}
var toIgnore []string
......@@ -516,6 +543,7 @@ func main() {
basePackagePath := path.Base(relPackagePath)
// Check whether the output/ directory already exists.
*output = path.Clean(*output)
if _, err := os.Stat(*output); err == nil {
if !*forceOverwrite {
fmt.Fprintln(os.Stderr, *output+" already exists")
......@@ -533,6 +561,20 @@ func main() {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
// Create the structs package
if err := os.MkdirAll(path.Join(*output, "structs"), 0777); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
// Get project root directory to locate boilerplate/ dir
_, currentPath, _, ok := runtime.Caller(0)
if !ok {
fmt.Fprintln(os.Stderr, "can't get path of current file")
os.Exit(1)
}
go2pinsDir := path.Dir(currentPath)
copyFile(path.Join(go2pinsDir, "boilerplate/structs/structs.go"), path.Join(*output, "structs/structs.go"), nil)
// Create a file in output/ with the same name as the input package.
f, err := os.Create(path.Join(*output, basePackagePath))
......@@ -559,8 +601,6 @@ func main() {
os.Exit(1)
}
src, global := treatGlobal(src)
if len(toIgnore) != 0 {
a, bb := tools.RewriteBlackbox(string(src), toIgnore, *output)
......@@ -581,14 +621,6 @@ func main() {
os.Exit(1)
}
}
if global {
fmt.Println("Global detected, file has been rewrotre in", path.Join(*output, basePackagePath))
if err = ioutil.WriteFile(path.Join(*output, basePackagePath), []byte(src), 0666); err != nil {
fmt.Println(err)
os.Exit(1)
}
os.Exit(2)
}
// Check if all constraints are respected
info := &types.Info{
......@@ -608,21 +640,13 @@ func main() {
}
// Compile and write the package to the output file.
packageName, err := compileTo(basePackagePath, src, f, goroutines, rers)
packageName, err := compileTo(basePackagePath, src, f, goroutines, rers, *output)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
tools.FormatArray(*output+"/"+basePackagePath, "var G2PVariableNames =")
// Get project root directory to locate boilerplate/ dir
_, currentPath, _, ok := runtime.Caller(0)
if !ok {
fmt.Fprintln(os.Stderr, "can't get path of current file")
os.Exit(1)
}
go2pinsDir := path.Dir(currentPath)
tools.FormatArray(path.Join(*output, "structs/structs.go"), "var G2PVariableNames =")
boilerplateFiles := []string{
"Makefile",
......@@ -630,7 +654,7 @@ func main() {
"go2pins.h",
"main.go",
}
data := packageTemplate{basePackagePath, packageName}
data := packageTemplate{basePackagePath, packageName, *output}
for _, filename := range boilerplateFiles {
from := path.Join(go2pinsDir, "boilerplate/"+filename)
to := path.Join(*output, filename)
......
......@@ -25,7 +25,7 @@ import (
"go/printer"
"go/token"
"log"
"os"
"path"
"strconv"
"strings"
)
......@@ -53,31 +53,7 @@ func RewriteBlackbox(inputfile string, funs []string, outputdir string) (string,
var newOrig []ast.Decl // Keep only function that should not be BB
var toBB []ast.Decl // Keep all other functions
//--------------------------------------------------------------------
// FIXME For now blackboxes are only supported in the go2pins
// repository We should investigate a more elegant solution
// that takes in account GOPATH, etc. Looking to goyacc and
// go generate (https://blog.golang.org/generate) seems
// promising.
dirname, _ := os.Getwd()
idx := strings.Index(dirname, "go2pins") // FIXME if multiple go2pins
if idx == -1 {
// FIXME this is a very weak test
panic("Blackbox is only supported in go2pins source directory")
}
import_name := ""
if dirname[idx:] != "go2pins" {
dirname = string(dirname[idx+8:]) + "/"
import_name = "gitlab.lrde.epita.fr/spot/go2pins/" +
dirname + outputdir + "/blackbox"
} else {
import_name = "gitlab.lrde.epita.fr/spot/go2pins/" +
outputdir + "/blackbox"
}
//--------------------------------------------------------------------
import_name := path.Join(outputdir, "blackbox")
already_bb := false
for _, d := range node.Decls {
......
......@@ -97,6 +97,7 @@ type Meta struct {
objs map[*ast.Object]*objectRecord
state *ast.Ident
stateType *ast.Ident
strStateType *ast.Ident
fnCounter *ast.Object
lblCounter *ast.Object
mainFunction *ast.Object
......@@ -113,7 +114,8 @@ func NewMeta(fset *token.FileSet, info *types.Info) *Meta {
info: info,
objs: make(map[*ast.Object]*objectRecord),
state: ast.NewIdent("G2PState"),
stateType: ast.NewIdent("G2PStateType"),
stateType: ast.NewIdent("structs.G2PStateType"),
strStateType: ast.NewIdent("G2PStateType"),
mainFunction: nil,
functions: make(map[*ast.Object]*FunctionRecord),
// The first index is reserved for the main function.
......@@ -158,6 +160,10 @@ func (m *Meta) StateType() *ast.Ident {
return m.stateType
}
func (m *Meta) StructStateType() *ast.Ident {
return m.strStateType
}
// FileSet returns the file set.
func (m *Meta) FileSet() *token.FileSet {
return m.fset
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment