Repository: goplus/gop
Pull request #1525 (2023-11-07 14:37:05)

ServeAndDial: shutdown gop command when client.Close


Patch:
diff --git a/cl/classfile.go b/cl/classfile.go
index 7c6b5f1c..250fe833 100644
--- a/cl/classfile.go
+++ b/cl/classfile.go
@@ -139,17 +139,18 @@ func getStringConst(spx *gox.PkgRef, name string) string {
 	return ""
 }
 
-func getFields(f *ast.File) []ast.Spec {
+func getFields(f *ast.File) (specs []ast.Spec) {
 	for _, decl := range f.Decls {
 		if g, ok := decl.(*ast.GenDecl); ok {
 			if g.Tok == token.VAR {
-				return g.Specs
+				specs, g.Specs = g.Specs, nil
+				return
 			}
 			continue
 		}
 		break
 	}
-	return nil
+	return
 }
 
 func setBodyHandler(ctx *blockCtx) {
diff --git a/cl/compile.go b/cl/compile.go
index 25a34f27..67d4da0f 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -758,34 +758,21 @@ func preloadGopFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, con
 						tags = append(tags, "")
 					}
 				}
-				rec := ctx.recorder()
 				for _, v := range specs {
 					spec := v.(*ast.ValueSpec)
+					var embed bool
+					if spec.Names == nil {
+						embed = true
+						v := parseTypeEmbedName(spec.Type)
+						spec.Names = []*ast.Ident{v}
+					}
 					typ := toType(ctx, spec.Type)
-					tag := toFieldTag(spec.Tag)
-					if len(spec.Names) == 0 {
-						name := parseTypeEmbedName(spec.Type)
-						if chk.chkRedecl(ctx, name.Name, spec.Type.Pos()) {
+					for _, name := range spec.Names {
+						if chk.chkRedecl(ctx, name.Name, name.Pos()) {
 							continue
 						}
-						fld := types.NewField(spec.Type.Pos(), pkg, name.Name, typ, true)
-						if rec != nil {
-							rec.Def(name, fld)
-						}
-						flds = append(flds, fld)
-						tags = append(tags, tag)
-					} else {
-						for _, name := range spec.Names {
-							if chk.chkRedecl(ctx, name.Name, name.Pos()) {
-								continue
-							}
-							fld := types.NewField(name.Pos(), pkg, name.Name, typ, false)
-							if rec != nil {
-								rec.Def(name, fld)
-							}
-							flds = append(flds, fld)
-							tags = append(tags, tag)
-						}
+						flds = append(flds, types.NewField(name.Pos(), pkg, name.Name, typ, embed))
+						tags = append(tags, toFieldTag(spec.Tag))
 					}
 				}
 				decl.InitType(p, types.NewStruct(flds, tags))
@@ -847,10 +834,6 @@ func preloadFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, gopFil
 	goFile := genGoFile(file, gopFile)
 	old, _ := p.SetCurFile(goFile, true)
 	defer p.RestoreCurFile(old)
-	var skipClassFields bool
-	if f.IsClass {
-		skipClassFields = true
-	}
 	for _, decl := range f.Decls {
 		switch d := decl.(type) {
 		case *ast.FuncDecl:
@@ -987,10 +970,6 @@ func preloadFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, gopFil
 					})
 				}
 			case token.VAR:
-				if skipClassFields {
-					skipClassFields = false
-					continue
-				}
 				for _, spec := range d.Specs {
 					vSpec := spec.(*ast.ValueSpec)
 					if debugLoad {
diff --git a/x/jsonrpc2/conn.go b/x/jsonrpc2/conn.go
index 8d3fd93d..0561b8ca 100644
--- a/x/jsonrpc2/conn.go
+++ b/x/jsonrpc2/conn.go
@@ -715,10 +715,6 @@ func (c *Connection) processResult(from interface{}, req *incomingRequest, resul
 	}
 
 	if req.IsCall() {
-		if result == nil && err == nil {
-			err = c.internalErrorf("%#v returned a nil result and nil error for a %q Request that requires a Response", from, req.Method)
-		}
-
 		response, respErr := NewResponse(req.ID, result, err)
 		if debugCall {
 			log.Println("==> processResult", response.ID, string(response.Result), response.Error)
diff --git a/x/jsonrpc2/serve.go b/x/jsonrpc2/serve.go
index 95ce0744..39a98675 100644
--- a/x/jsonrpc2/serve.go
+++ b/x/jsonrpc2/serve.go
@@ -71,13 +71,13 @@ type Server struct {
 // Handler provided by the Binder, and will release its own resources when the
 // connection is broken, but the caller may Close it earlier to stop accepting
 // (or sending) new requests.
-func Dial(ctx context.Context, dialer Dialer, binder Binder) (*Connection, error) {
+func Dial(ctx context.Context, dialer Dialer, binder Binder, onDone func()) (*Connection, error) {
 	// dial a server
 	rwc, err := dialer.Dial(ctx)
 	if err != nil {
 		return nil, err
 	}
-	return newConnection(ctx, rwc, binder, nil), nil
+	return newConnection(ctx, rwc, binder, onDone), nil
 }
 
 // NewServer starts a new server listening for incoming connections and returns
diff --git a/x/jsonrpc2/stdio/client.go b/x/jsonrpc2/stdio/client.go
index 9a8b86cd..72b98968 100644
--- a/x/jsonrpc2/stdio/client.go
+++ b/x/jsonrpc2/stdio/client.go
@@ -88,8 +88,8 @@ func Dialer(in io.ReadCloser, out io.WriteCloser) jsonrpc2.Dialer {
 // Handler provided by the Binder, and will release its own resources when the
 // connection is broken, but the caller may Close it earlier to stop accepting
 // (or sending) new requests.
-func Dial(in io.ReadCloser, out io.WriteCloser, binder jsonrpc2.Binder) (*jsonrpc2.Connection, error) {
-	return jsonrpc2.Dial(context.Background(), Dialer(in, out), binder)
+func Dial(in io.ReadCloser, out io.WriteCloser, binder jsonrpc2.Binder, onDone func()) (*jsonrpc2.Connection, error) {
+	return jsonrpc2.Dial(context.Background(), Dialer(in, out), binder, onDone)
 }
 
 // -----------------------------------------------------------------------------
diff --git a/x/langserver/client.go b/x/langserver/client.go
index fb4cdb9a..a4bda225 100644
--- a/x/langserver/client.go
+++ b/x/langserver/client.go
@@ -25,9 +25,8 @@ import (
 // -----------------------------------------------------------------------------
 
 const (
-	methodGenGo    = "gengo"
-	methodChanged  = "changed"
-	methodShutdown = "shutdown"
+	methodGenGo   = "gengo"
+	methodChanged = "changed"
 )
 
 // -----------------------------------------------------------------------------
@@ -44,11 +43,11 @@ type Client struct {
 
 // Open uses the dialer to make a new connection and returns a client of the LangServer
 // based on the connection.
-func Open(ctx context.Context, dialer Dialer) (ret Client, err error) {
+func Open(ctx context.Context, dialer Dialer, onDone func()) (ret Client, err error) {
 	c, err := jsonrpc2.Dial(ctx, dialer, jsonrpc2.BinderFunc(
 		func(ctx context.Context, c *jsonrpc2.Connection) (ret jsonrpc2.ConnectionOptions) {
 			return
-		}))
+		}), onDone)
 	if err != nil {
 		return
 	}
@@ -72,8 +71,4 @@ func (p Client) Changed(ctx context.Context, files ...string) (err error) {
 	return p.conn.Notify(ctx, methodChanged, files)
 }
 
-func (p Client) Shutdown(ctx context.Context) (err error) {
-	return p.conn.Notify(ctx, methodShutdown, nil)
-}
-
 // -----------------------------------------------------------------------------
diff --git a/x/langserver/serve_dial.go b/x/langserver/serve_dial.go
index 200d467a..3e1d75a7 100644
--- a/x/langserver/serve_dial.go
+++ b/x/langserver/serve_dial.go
@@ -45,7 +45,7 @@ type ServeAndDialConfig struct {
 
 // ServeAndDial executes a command as a LangServer, makes a new connection to it
 // and returns a client of the LangServer based on the connection.
-func ServeAndDial(cmd string, args []string, conf *ServeAndDialConfig) Client {
+func ServeAndDial(conf *ServeAndDialConfig, cmd string, args ...string) Client {
 	if conf == nil {
 		conf = new(ServeAndDialConfig)
 	}
@@ -70,6 +70,7 @@ func ServeAndDial(cmd string, args []string, conf *ServeAndDialConfig) Client {
 	in, w := io.Pipe()
 	r, out := io.Pipe()
 
+	ctx, cancel := context.WithCancel(context.Background())
 	go func() {
 		defer r.Close()
 		defer w.Close()
@@ -80,15 +81,14 @@ func ServeAndDial(cmd string, args []string, conf *ServeAndDialConfig) Client {
 		}
 		defer f.Close()
 
-		cmd := exec.Command(cmd, args...)
+		cmd := exec.CommandContext(ctx, cmd, args...)
 		cmd.Stdin = r
 		cmd.Stdout = w
 		cmd.Stderr = f
 		cmd.Run()
 	}()
 
-	ctx := context.Background()
-	c, err := Open(ctx, stdio.Dialer(in, out))
+	c, err := Open(ctx, stdio.Dialer(in, out), cancel)
 	if err != nil {
 		onErr(err)
 	}
diff --git a/x/langserver/server.go b/x/langserver/server.go
index 22bf1633..d368633c 100644
--- a/x/langserver/server.go
+++ b/x/langserver/server.go
@@ -87,8 +87,6 @@ func (p *handler) Handle(ctx context.Context, req *jsonrpc2.Request) (result int
 			return
 		}
 		err = GenGo(pattern...)
-	case methodShutdown:
-		p.server.Shutdown()
 	}
 	return
 }
diff --git a/x/typesutil/check.go b/x/typesutil/check.go
index ae5947fd..92ee7ddd 100644
--- a/x/typesutil/check.go
+++ b/x/typesutil/check.go
@@ -164,6 +164,7 @@ func (p *Checker) Files(goFiles []*goast.File, gopFiles []*ast.File) (err error)
 			log.Println("typesutil.Check err:", err)
 			log.SingleStack()
 		}
+		return
 	}
 	if len(files) > 0 {
 		scope := pkgTypes.Scope()


Test patch:
diff --git a/cl/compile_spx_test.go b/cl/compile_spx_test.go
index 9d387a78..28e0fd71 100644
--- a/cl/compile_spx_test.go
+++ b/cl/compile_spx_test.go
@@ -303,15 +303,15 @@ func onCloned() {
 
 import "github.com/goplus/gop/cl/internal/spx"
 
+type Game struct {
+	*spx.MyGame
+	Kai Kai
+}
 type Kai struct {
 	spx.Sprite
 	*Game
 	a int
 }
-type Game struct {
-	*spx.MyGame
-	Kai Kai
-}
 
 func (this *Game) onInit() {
 	spx.Gopt_Sprite_Clone__0(this.Kai)
@@ -345,11 +345,6 @@ import (
 	"github.com/goplus/gop/cl/internal/spx"
 )
 
-type Kai struct {
-	spx.Sprite
-	*index
-}
-
 var x float64 = spx.Rand__1(1.2)
 
 type index struct {
@@ -357,6 +352,10 @@ type index struct {
 	Kai Kai
 	t   spx.Sound
 }
+type Kai struct {
+	spx.Sprite
+	*index
+}
 
 func (this *index) MainEntry() {
 	spx.Gopt_MyGame_Run(this, "hzip://open.qiniu.us/weather/res.zip")
@@ -494,14 +493,14 @@ var (
 
 import "github.com/goplus/gop/cl/internal/spx2"
 
-type Kai struct {
-	spx2.Sprite
-	*Game
-}
 type Game struct {
 	spx2.Game
 	Kai Kai
 }
+type Kai struct {
+	spx2.Sprite
+	*Game
+}
 
 func (this *Game) MainEntry() {
 }
@@ -530,14 +529,14 @@ import (
 	"github.com/goplus/gop/cl/internal/spx2"
 )
 
-type Kai struct {
-	spx2.Sprite
-	*Game
-}
 type Game struct {
 	spx2.Game
 	Kai Kai
 }
+type Kai struct {
+	spx2.Sprite
+	*Game
+}
 
 func (this *Game) MainEntry() {
 	fmt.Println("Hi")
@@ -631,15 +630,15 @@ type info struct {
 	x int
 	y int
 }
+type Game struct {
+	*spx.MyGame
+	Kai Kai
+}
 type Kai struct {
 	spx.Sprite
 	*Game
 	a int
 }
-type Game struct {
-	*spx.MyGame
-	Kai Kai
-}
 
 func (this *Game) onInit() {
 	spx.Gopt_Sprite_Clone__0(this.Kai)
diff --git a/x/jsonrpc2/jsonrpc2test/cases/testcase.go b/x/jsonrpc2/jsonrpc2test/cases/testcase.go
index 3a3bb375..886d6537 100644
--- a/x/jsonrpc2/jsonrpc2test/cases/testcase.go
+++ b/x/jsonrpc2/jsonrpc2test/cases/testcase.go
@@ -148,7 +148,7 @@ func Test(t *testing.T, ctx context.Context, listener jsonrpc2.Listener, framer
 						// also run all simple call tests in echo mode
 						(*echo)(call).Invoke(t, ctx, h)
 					}
-				}})
+				}}, nil)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/x/jsonrpc2/stdio/jsonrpc2_test.go b/x/jsonrpc2/stdio/jsonrpc2_test.go
index b8fd7888..1cc9621c 100644
--- a/x/jsonrpc2/stdio/jsonrpc2_test.go
+++ b/x/jsonrpc2/stdio/jsonrpc2_test.go
@@ -25,7 +25,7 @@ func TestDial(t *testing.T) {
 	stdio.Dial(r, w, jsonrpc2.BinderFunc(
 		func(ctx context.Context, c *jsonrpc2.Connection) jsonrpc2.ConnectionOptions {
 			return jsonrpc2.ConnectionOptions{}
-		}))
+		}), nil)
 	if _, err := stdio.Dialer(r, w).Dial(context.Background()); err != stdio.ErrTooManyConnections {
 		t.Fatal("stdio.Dialer:", err)
 	}