interp: expose fset to fix CompileAST issue
The interpreter has its own internal fileset and expects all code to have been parsed using that fileset. If a user creates a fileset, calls `go/parser.Parse*`, then passes the result to `interp.CompileAST`, strange things can happen. The solutions I can see are: 1. Expose the fileset so the user can use it when parsing source. 2. Add the fileset as an option (input to New) so that the user can tell the interpreter to use a specific fileset. 3. Pass the fileset into `CompileAST` There are two ways to implement option 3. One is to add a field to nodes and update every reference to `interp.fset` to use `node.fset`. The other is to add a parameter to every function that references `interp.fset` or calls a function that does. Both of these are significant changes and involve an extra pointer for every node or most function calls. Options 1 and 2 are easy. Option 2 involves adding an option so I went with option 1. I can imagine situations where option 2 could be necessary, but I can open another issue/PR if and when I need that. Fixes #1383
This commit is contained in:
@@ -10,7 +10,8 @@ import (
|
||||
)
|
||||
|
||||
func TestCompileAST(t *testing.T) {
|
||||
file, err := parser.ParseFile(token.NewFileSet(), "_.go", `
|
||||
i := New(Options{})
|
||||
file, err := parser.ParseFile(i.FileSet(), "_.go", `
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
@@ -61,7 +62,6 @@ func TestCompileAST(t *testing.T) {
|
||||
{desc: "expr", node: dFunc.Body.List[0]},
|
||||
}
|
||||
|
||||
i := New(Options{})
|
||||
_ = i.Use(stdlib.Symbols)
|
||||
|
||||
for _, c := range cases {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -1749,3 +1750,36 @@ func TestRestrictedEnv(t *testing.T) {
|
||||
t.Fatal("expected \"\", got " + s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue1383(t *testing.T) {
|
||||
const src = `
|
||||
package main
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello")
|
||||
}
|
||||
`
|
||||
|
||||
interp := interp.New(interp.Options{})
|
||||
err := interp.Use(stdlib.Symbols)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = interp.Eval(`import "fmt"`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ast, err := parser.ParseFile(interp.FileSet(), "_.go", src, parser.DeclarationErrors)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
prog, err := interp.CompileAST(ast)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = interp.Execute(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package interp
|
||||
import (
|
||||
"context"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
@@ -16,6 +17,12 @@ type Program struct {
|
||||
init []*node
|
||||
}
|
||||
|
||||
// FileSet is the fileset that must be used for parsing Go that will be passed
|
||||
// to interp.CompileAST().
|
||||
func (interp *Interpreter) FileSet() *token.FileSet {
|
||||
return interp.fset
|
||||
}
|
||||
|
||||
// Compile parses and compiles a Go code represented as a string.
|
||||
func (interp *Interpreter) Compile(src string) (*Program, error) {
|
||||
return interp.compileSrc(src, "", true)
|
||||
@@ -55,6 +62,9 @@ func (interp *Interpreter) compileSrc(src, name string, inc bool) (*Program, err
|
||||
// CompileAST builds a Program for the given Go code AST. Files and block
|
||||
// statements can be compiled, as can most expressions. Var declaration nodes
|
||||
// cannot be compiled.
|
||||
//
|
||||
// WARNING: The node must have been parsed using interp.FileSet(). Results are
|
||||
// unpredictable otherwise.
|
||||
func (interp *Interpreter) CompileAST(n ast.Node) (*Program, error) {
|
||||
// Convert AST.
|
||||
pkgName, root, err := interp.ast(n)
|
||||
|
||||
Reference in New Issue
Block a user