Repository: goplus/gop
Pull request #1380 (2023-06-16 01:27:13)

cl: update classfile for gop.mod
implement https://github.com/goplus/gop/issues/1370


Patch:
diff --git a/cl/classfile.go b/cl/classfile.go
index 432e1a2d..c46e5934 100644
--- a/cl/classfile.go
+++ b/cl/classfile.go
@@ -32,9 +32,8 @@ import (
 
 type gmxSettings struct {
 	gameClass  string
-	extSpx     string
 	game       gox.Ref
-	sprite     gox.Ref
+	sprite     map[string]gox.Ref
 	scheds     []string
 	schedStmts []goast.Stmt // nil or len(scheds) == 2 (delayload)
 	pkgImps    []*gox.PkgRef
@@ -61,34 +60,35 @@ func (p *gmxSettings) getScheds(cb *gox.CodeBuilder) []goast.Stmt {
 }
 
 func newGmx(ctx *pkgCtx, pkg *gox.Package, file string, conf *Config) *gmxSettings {
-	_, name := filepath.Split(file)
-	ext := filepath.Ext(name)
-	if idx := strings.Index(name, "."); idx > 0 {
-		name = name[:idx]
-		if name == "main" {
-			name = "_main"
-		}
-	}
+	ext := filepath.Ext(file)
 	gt, ok := conf.LookupClass(ext)
 	if !ok {
 		panic("TODO: class not found")
 	}
+	var name string
+	if gt.Ext == ext {
+		_, name = filepath.Split(file)
+		if idx := strings.Index(name, "."); idx > 0 {
+			name = name[:idx]
+			if name == "main" {
+				name = "_main"
+			}
+		}
+	}
 	pkgPaths := gt.PkgPaths
-	p := &gmxSettings{extSpx: gt.WorkExt, gameClass: name, pkgPaths: pkgPaths}
+	p := &gmxSettings{gameClass: name, pkgPaths: pkgPaths}
 	p.pkgImps = make([]*gox.PkgRef, len(pkgPaths))
 	for i, pkgPath := range pkgPaths {
 		p.pkgImps[i] = pkg.Import(pkgPath)
 	}
 	spx := p.pkgImps[0]
-	p.game, p.gameIsPtr = spxTryRef(spx, "Gop_proj", "Proj")
-	if p.game == nil {
-		p.game, p.gameIsPtr = spxRef(spx, "Gop_game", "Game")
-	}
-	if gt.WorkExt != "" {
-		p.sprite, _ = spxTryRef(spx, "Gop_work", "Work")
-		if p.sprite == nil {
-			p.sprite, _ = spxRef(spx, "Gop_sprite", "Sprite")
-		}
+	if gt.Class != "" {
+		p.game, p.gameIsPtr = spxRef(spx, gt.Class)
+	}
+	p.sprite = make(map[string]types.Object)
+	for _, v := range gt.Works {
+		obj, _ := spxRef(spx, v.Class)
+		p.sprite[v.Ext] = obj
 	}
 	if x := getStringConst(spx, "Gop_sched"); x != "" {
 		p.scheds, p.hasScheds = strings.SplitN(x, ",", 2), true
@@ -113,22 +113,19 @@ func getDefaultClass(file string) string {
 	return name
 }
 
-func spxRef(spx *gox.PkgRef, name, typ string) (obj gox.Ref, isPtr bool) {
-	obj, isPtr = spxTryRef(spx, name, typ)
-	if obj == nil {
-		panic(spx.Path() + "." + name + " not found")
+func spxTryRef(spx *gox.PkgRef, typ string) (obj types.Object, isPtr bool) {
+	if strings.HasPrefix(typ, "*") {
+		typ, isPtr = typ[1:], true
 	}
+	obj = spx.Ref(typ)
 	return
 }
 
-func spxTryRef(spx *gox.PkgRef, name, typ string) (obj gox.Ref, isPtr bool) {
-	if v := getStringConst(spx, name); v != "" {
-		typ = v
-		if strings.HasPrefix(typ, "*") {
-			typ, isPtr = typ[1:], true
-		}
+func spxRef(spx *gox.PkgRef, typ string) (obj gox.Ref, isPtr bool) {
+	obj, isPtr = spxTryRef(spx, typ)
+	if obj == nil {
+		panic(spx.Path() + "." + typ + " not found")
 	}
-	obj = spx.TryRef(typ)
 	return
 }
 
diff --git a/cl/compile.go b/cl/compile.go
index c472b751..d2a93a14 100644
--- a/cl/compile.go
+++ b/cl/compile.go
@@ -22,7 +22,9 @@ import (
 	"go/types"
 	"log"
 	"os"
+	"path/filepath"
 	"reflect"
+	"sort"
 	"strings"
 
 	"github.com/goplus/gop/ast"
@@ -30,7 +32,7 @@ import (
 	"github.com/goplus/gop/token"
 	"github.com/goplus/gox"
 	"github.com/goplus/gox/cpackages"
-	"github.com/goplus/mod/modfile"
+	"github.com/goplus/mod/gopmod"
 	"github.com/qiniu/x/errors"
 )
 
@@ -60,7 +62,7 @@ func SetDebug(flags int) {
 
 // -----------------------------------------------------------------------------
 
-type Class = modfile.Classfile
+type Project = gopmod.Project
 
 // Config of loading Go+ packages.
 type Config struct {
@@ -82,7 +84,7 @@ type Config struct {
 	LookupPub func(pkgPath string) (pubfile string, err error)
 
 	// LookupClass lookups a class by specified file extension.
-	LookupClass func(ext string) (c *Class, ok bool)
+	LookupClass func(ext string) (c *Project, ok bool)
 
 	// An Importer resolves import paths to Packages.
 	Importer types.Importer
@@ -403,6 +405,14 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package,
 			break
 		}
 	}
+	if ctx.gmxSettings == nil {
+		for file, gmx := range files {
+			if gmx.IsClass && filepath.Ext(file) != ".gopx" {
+				ctx.gmxSettings = newGmx(ctx, p, file, conf)
+				break
+			}
+		}
+	}
 	for fpath, f := range files {
 		fileLine := !conf.NoFileLine
 		ctx := &blockCtx{
@@ -412,6 +422,7 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package,
 		}
 		preloadGopFile(p, ctx, fpath, f, conf)
 	}
+
 	for fpath, gof := range pkg.GoFiles {
 		f := fromgo.ASTFile(gof, 0)
 		ctx := &blockCtx{
@@ -420,16 +431,30 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package,
 		}
 		preloadFile(p, ctx, fpath, f, false)
 	}
-	for _, f := range files {
+
+	// sort files
+	type File struct {
+		*ast.File
+		path string
+	}
+	var sfiles []*File
+	for fpath, f := range files {
+		sfiles = append(sfiles, &File{f, fpath})
+	}
+	sort.Slice(sfiles, func(i, j int) bool {
+		return sfiles[i].path < sfiles[j].path
+	})
+
+	for _, f := range sfiles {
 		if f.IsProj {
-			loadFile(ctx, f)
+			loadFile(ctx, f.File)
 			gmxMainFunc(p, ctx)
 			break
 		}
 	}
-	for _, f := range files {
+	for _, f := range sfiles {
 		if !f.IsProj { // only one .gmx file
-			loadFile(ctx, f)
+			loadFile(ctx, f.File)
 		}
 	}
 	for _, ld := range ctx.tylds {
@@ -532,12 +557,12 @@ func preloadGopFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, con
 			baseType = types.NewPointer(baseType)
 		}
 	case f.IsClass:
+		classType = getDefaultClass(file)
 		if parent.gmxSettings != nil {
-			classType = getDefaultClass(file)
-			o := parent.sprite
-			baseTypeName, baseType, spxClass = o.Name(), o.Type(), true
-		} else {
-			classType = getDefaultClass(file)
+			o, ok := parent.sprite[filepath.Ext(file)]
+			if ok {
+				baseTypeName, baseType, spxClass = o.Name(), o.Type(), true
+			}
 		}
 	}
 	if classType != "" {
@@ -570,7 +595,7 @@ func preloadGopFile(p *gox.Package, ctx *blockCtx, file string, f *ast.File, con
 					flds = append(flds, types.NewField(pos, pkg, baseTypeName, baseType, true))
 					chk.chkRedecl(ctx, baseTypeName, pos)
 				}
-				if spxClass {
+				if spxClass && parent.gmxSettings != nil && parent.gameClass != "" {
 					typ := toType(ctx, &ast.StarExpr{X: &ast.Ident{Name: parent.gameClass}})
 					name := getTypeName(typ)
 					if !chk.chkRedecl(ctx, name, pos) {
diff --git a/cl/internal/spx/game.go b/cl/internal/spx/game.go
index 9162dea9..a201d089 100644
--- a/cl/internal/spx/game.go
+++ b/cl/internal/spx/game.go
@@ -18,7 +18,6 @@ package spx
 
 const (
 	GopPackage = true
-	Gop_game   = "*MyGame"
 	Gop_sched  = "Sched,SchedNow"
 )
 
diff --git a/cl/internal/spx2/spx2.go b/cl/internal/spx2/spx2.go
index 8b83f551..12172a7a 100644
--- a/cl/internal/spx2/spx2.go
+++ b/cl/internal/spx2/spx2.go
@@ -32,3 +32,6 @@ type Sprite struct {
 
 func Sched() {
 }
+
+type Sprite2 struct {
+}
diff --git a/cmd/internal/gopget/get.go b/cmd/internal/gopget/get.go
index b1ddf4cc..b4fb4f86 100644
--- a/cmd/internal/gopget/get.go
+++ b/cmd/internal/gopget/get.go
@@ -81,7 +81,7 @@ func get(pkgPath string) {
 
 	pkgMod, err := modload.Load(pkgModRoot, 0)
 	check(err)
-	if pkgMod.Classfile != nil {
+	if pkgMod.Project != nil {
 		mod.AddRegister(pkgModVer.Path)
 		fmt.Fprintf(os.Stderr, "gop get: registered %s\n", pkgModVer.Path)
 	}
diff --git a/go.mod b/go.mod
index fad1c8e7..e141d5e7 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,6 @@ require (
 	github.com/goplus/c2go v0.7.13
 	github.com/goplus/gox v1.11.35
 	github.com/goplus/libc v0.3.13
-	github.com/goplus/mod v0.9.12
+	github.com/goplus/mod v0.10.0
 	github.com/qiniu/x v1.11.9
 )
diff --git a/go.sum b/go.sum
index 0ff584da..19224284 100644
--- a/go.sum
+++ b/go.sum
@@ -11,8 +11,9 @@ github.com/goplus/gox v1.11.35 h1:IZtsYhHyjClJx5IRBbdYXCtiVoaoX4koYyv+/t7rXXA=
 github.com/goplus/gox v1.11.35/go.mod h1:GcmPyIyC2t4Nfam7x6YRpe1IW9rf4nchgRpE19hF4L4=
 github.com/goplus/libc v0.3.13 h1:2yHG8ghezBTK3Da7Fw1lNiitmt+Va+RzwKJ5GuqypCQ=
 github.com/goplus/libc v0.3.13/go.mod h1:xqG4/g3ilKBE/UDn5vkaE7RRQPQPyspj7ecuMuvlQJ8=
-github.com/goplus/mod v0.9.12 h1:CjgBGQIYqUTPGl3MrAS5CICzJwxbIfSa4OlEb141Gs4=
 github.com/goplus/mod v0.9.12/go.mod h1:YoPIowz71rnLLROA4YG0AC8bzDtPRyMaQwgTRLr8ri4=
+github.com/goplus/mod v0.10.0 h1:S9H/dwS4/IMzUX+Bq9Lb3tkPT3tzU6ws+hKeKI+Uo7g=
+github.com/goplus/mod v0.10.0/go.mod h1:cD7VktDd88SM0fPJ9oT3z5r8KTBW+EGi0gA3G8cDKHo=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=


Test patch:
diff --git a/cl/builtin_test.go b/cl/builtin_test.go
index 0b7236f2..6e123871 100644
--- a/cl/builtin_test.go
+++ b/cl/builtin_test.go
@@ -132,7 +132,7 @@ func TestGmxSettings(t *testing.T) {
 	}}, &Config{
 		LookupClass: lookupClassErr,
 	})
-	if e := err.Error(); e != `github.com/goplus/gop/cl/internal/libc.Gop_game not found` {
+	if e := err.Error(); e != `github.com/goplus/gop/cl/internal/libc.Game not found` {
 		t.Fatal("newGmx:", e)
 	}
 }
@@ -146,21 +146,23 @@ func TestSpxLookup(t *testing.T) {
 	spxLookup(nil, "foo")
 }
 
-func lookupClass(ext string) (c *gopmod.Class, ok bool) {
+func lookupClass(ext string) (c *gopmod.Project, ok bool) {
 	switch ext {
 	case ".t2gmx", ".t2spx":
-		return &gopmod.Class{
-			ProjExt: ".t2gmx", WorkExt: ".t2spx",
+		return &gopmod.Project{
+			Ext: ".t2gmx", Class: "Game",
+			Works:    []*gopmod.Class{{Ext: ".t2spx", Class: "Sprite"}},
 			PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx2"}}, true
 	}
 	return
 }
 
-func lookupClassErr(ext string) (c *gopmod.Class, ok bool) {
+func lookupClassErr(ext string) (c *gopmod.Project, ok bool) {
 	switch ext {
 	case ".t2gmx", ".t2spx":
-		return &gopmod.Class{
-			ProjExt: ".t2gmx", WorkExt: ".t2spx",
+		return &gopmod.Project{
+			Ext: ".t2gmx", Class: "Game",
+			Works:    []*gopmod.Class{{Ext: ".t2spx", Class: "Sprite"}},
 			PkgPaths: []string{"github.com/goplus/gop/cl/internal/libc"}}, true
 	}
 	return
diff --git a/cl/compile_spx_test.go b/cl/compile_spx_test.go
index 5cf85672..2a2f62e7 100644
--- a/cl/compile_spx_test.go
+++ b/cl/compile_spx_test.go
@@ -28,15 +28,23 @@ import (
 	"github.com/goplus/mod/gopmod"
 )
 
-func lookupClass(ext string) (c *gopmod.Class, ok bool) {
+func lookupClass(ext string) (c *gopmod.Project, ok bool) {
 	switch ext {
 	case ".tgmx", ".tspx":
-		return &gopmod.Class{
-			ProjExt: ".tgmx", WorkExt: ".tspx",
+		return &gopmod.Project{
+			Ext: ".tgmx", Class: "*MyGame",
+			Works:    []*gopmod.Class{{Ext: ".tspx", Class: "Sprite"}},
 			PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx", "math"}}, true
-	case ".t2gmx", ".t2spx":
-		return &gopmod.Class{
-			ProjExt: ".t2gmx", WorkExt: ".t2spx",
+	case ".t2gmx", ".t2spx", ".t2spx2":
+		return &gopmod.Project{
+			Ext: ".t2gmx", Class: "Game",
+			Works: []*gopmod.Class{{Ext: ".t2spx", Class: "Sprite"},
+				{Ext: ".t2spx2", Class: "Sprite2"}},
+			PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx2"}}, true
+	case ".t3spx", ".t3spx2":
+		return &gopmod.Project{
+			Works: []*gopmod.Class{{Ext: ".t3spx", Class: "Sprite"},
+				{Ext: ".t3spx2", Class: "Sprite2"}},
 			PkgPaths: []string{"github.com/goplus/gop/cl/internal/spx2"}}, true
 	}
 	return
@@ -47,7 +55,7 @@ func spxParserConf() parser.Config {
 		IsClass: func(ext string) (isProj bool, ok bool) {
 			c, ok := lookupClass(ext)
 			if ok {
-				isProj = (c.ProjExt == ext)
+				isProj = (c.Ext == ext)
 			}
 			return
 		},
@@ -389,6 +397,66 @@ type Kai struct {
 func (this *Kai) onMsg(msg string) {
 }
 `, "Game.t2gmx", "Kai.t2spx")
+
+	gopSpxTestEx(t, `
+println("Hi, Sprite2")
+`, `
+func onMsg(msg string) {
+}
+`, `package main
+
+import (
+	fmt "fmt"
+	spx2 "github.com/goplus/gop/cl/internal/spx2"
+)
+
+type Game struct {
+	spx2.Game
+}
+
+func (this *Game) MainEntry() {
+	fmt.Println("Hi, Sprite2")
+}
+func main() {
+	new(Game).Main()
+}
+
+type Kai struct {
+	spx2.Sprite2
+	*Game
+}
+
+func (this *Kai) onMsg(msg string) {
+}
+`, "Game.t2gmx", "Kai.t2spx2")
+
+	gopSpxTestEx(t, `
+println("Hi, Sprite")
+`, `
+func onMsg(msg string) {
+}
+`, `package main
+
+import (
+	fmt "fmt"
+	spx2 "github.com/goplus/gop/cl/internal/spx2"
+)
+
+type Dog struct {
+	spx2.Sprite
+}
+
+func (this *Dog) Main() {
+	fmt.Println("Hi, Sprite")
+}
+
+type Kai struct {
+	spx2.Sprite2
+}
+
+func (this *Kai) onMsg(msg string) {
+}
+`, "Dog.t3spx", "Kai.t3spx2")
 }
 
 func TestSpxMainEntry(t *testing.T) {