Skip to content

Commit 62b907e

Browse files
authored
all: add anonymous function support and fix build behavior (#10)
1 parent 8a6babc commit 62b907e

File tree

3 files changed

+76
-3
lines changed

3 files changed

+76
-3
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ all: clean
55
./$(NAME) -conf configs/config.yaml
66
build:
77
docker build -t $(NAME):$(VERSION) -t $(NAME):latest -f docker/Dockerfile .
8-
up: down
8+
up:
99
docker-compose -f docker/deploy.yml up -d
1010
down:
1111
docker-compose -f docker/deploy.yml down

public/main.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ function build() {
3030
console.log('no changes, do not submit')
3131
return
3232
}
33-
if (!code.includes('func '+funcname)) {
33+
if (!findSSAFunc(code, funcname)) {
3434
setMessageBox('GOFUNCNAME does not exist in your code.', false)
3535
return
3636
}
@@ -67,6 +67,25 @@ function build() {
6767
.catch(res => setMessageBox(res.data.msg, false));
6868
}
6969

70+
const methodPattern = /^\([\w\*]+\)\.\w+$/
71+
const globPattern = /^glob\.\.func\d+(\.\d)*$/
72+
const anonyPattern = /^\w+\.func\d+(\.\d)*$/
73+
74+
function findSSAFunc(code, funcname) {
75+
if (funcname.indexOf('.') != -1) {
76+
if (funcname[0] == '(') {
77+
return methodPattern.test(funcname)
78+
} else if (funcname.startsWith("glob")) {
79+
return globPattern.test(funcname)
80+
} else {
81+
return anonyPattern.test(funcname)
82+
}
83+
}
84+
85+
let funcDclPattern = new RegExp(`func[ \\t]+${funcname}[ \\t]*\\(`)
86+
return funcDclPattern.test(code)
87+
}
88+
7089
function setMessageBox(msg, hide) {
7190
msgbox.innerText = msg
7291
if (hide) {

src/route/api.go

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,50 @@ func BuildSSA(c *gin.Context) {
134134
c.JSON(http.StatusOK, out)
135135
}
136136

137+
/*
138+
According to spec there are two cases we need to handle:
139+
140+
- Function declaration, in the form of 'func f() {...}' , see:
141+
https://golang.org/ref/spec#Function_declarations
142+
143+
- Function literal/anonymous function, in the form of
144+
'myfunc := func() {...}' or 'go func() {...}', see:
145+
https://golang.org/ref/spec#Function_literals
146+
147+
As users can use some tricks like raw string to bypass our check, we
148+
only do check conservatively, which means it is mainly used for
149+
preventing misspell and wrong format.
150+
151+
All cases:
152+
// <i>,<j>,<k> means unique function index in the scope of outer function, see:
153+
https://github.com/golang/go/blob/84162b88324aa7993fe4a8580a2b65c6a7055f88/src/cmd/compile/internal/typecheck/func.go#L182
154+
155+
- func foo() // most common case
156+
- glob..func<i> // global function literal
157+
+ glob..func<i>.<j>.<k>... // inner anonymous function
158+
- foo.func<i> // anonymous function inside function 'foo'
159+
+ foo.func<i>.<j>.<k>...
160+
- (*T).foo() // method expression with explicit receiver, see
161+
https://golang.org/ref/spec#Method_expressions
162+
163+
Note that non-ascii letters are unsupported, as our intention is to dig
164+
into go ssa IR.
165+
*/
137166
func findSSAFunc(code, funcname string) bool {
167+
// The dot character is not allowed to appear in function name.
168+
// See https://golang.org/ref/spec#Identifiers
169+
if strings.IndexByte(funcname, '.') != -1 {
170+
if funcname[0] == '(' {
171+
methodReg := regexp.MustCompile(`^\([\w\*]+\)\.\w+$`)
172+
return methodReg.MatchString(funcname)
173+
} else if strings.HasPrefix(funcname, "glob") {
174+
globReg := regexp.MustCompile(`^glob\.\.func\d+(\.\d)*$`)
175+
return globReg.MatchString(funcname)
176+
} else {
177+
anonyReg := regexp.MustCompile(`^\w+\.func\d+(\.\d)*$`)
178+
return anonyReg.MatchString(funcname)
179+
}
180+
}
138181
// func Foo (
139182
re := regexp.MustCompile(fmt.Sprintf(`func[ \t]+%s[ \t]*\(`, funcname))
140183
return re.FindString(code) != ""
@@ -185,12 +228,23 @@ func initModules(path string) error {
185228
}
186229

187230
func buildSSA(funcname, gcflags, outf, buildf string, isTest bool) error {
188-
var cmd *exec.Cmd
231+
var (
232+
cmd *exec.Cmd
233+
buildDir string
234+
)
235+
236+
// Restrict the ssa.html target to the target ssa build folder.
237+
// See https://github.com/golang-design/ssaplayground/issues/9
238+
buildDir = filepath.Dir(buildf)
239+
outf = filepath.Base(outf)
240+
buildf = filepath.Base(buildf)
241+
189242
if !isTest {
190243
cmd = exec.Command("go", "build", "-mod=readonly", fmt.Sprintf(`-gcflags=%s`, gcflags), "-o", outf, buildf)
191244
} else {
192245
cmd = exec.Command("go", "test", "-mod=readonly", fmt.Sprintf(`-gcflags=%s`, gcflags), buildf)
193246
}
247+
cmd.Dir = buildDir
194248
cmd.Env = append(os.Environ(), fmt.Sprintf("GOSSAFUNC=%s", funcname))
195249
cmd.Stderr = &bytes.Buffer{}
196250
err := cmd.Run()

0 commit comments

Comments
 (0)