Skip to content

Commit

Permalink
Merge pull request #1814 from xushiwei/q
Browse files Browse the repository at this point in the history
gmxProjMain: generate gameClass.Main()
  • Loading branch information
xushiwei authored Mar 10, 2024
2 parents 1523b64 + dbf4347 commit 824bf92
Show file tree
Hide file tree
Showing 8 changed files with 370 additions and 145 deletions.
9 changes: 6 additions & 3 deletions cl/builtin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,12 +252,15 @@ func TestErrParseTypeEmbedName(t *testing.T) {
parseTypeEmbedName(&ast.StructType{})
}

func TestGmxMainFunc(t *testing.T) {
gmxMainFunc(nil, &pkgCtx{
func TestGmxCheckProjs(t *testing.T) {
_, multi := gmxCheckProjs(nil, &pkgCtx{
projs: map[string]*gmxProject{
".a": {}, ".b": {},
},
}, false)
})
if !multi {
t.Fatal("gmxCheckProjs: not multi?")
}
}

func TestNodeInterp(t *testing.T) {
Expand Down
158 changes: 98 additions & 60 deletions cl/classfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package cl
import (
goast "go/ast"
"go/constant"
gotoken "go/token"
"go/types"
"log"
"path/filepath"
Expand All @@ -42,7 +43,7 @@ type gmxClass struct {

type gmxProject struct {
gameClass string // <gmtype>.gmx
game gogen.Ref // Game
game gogen.Ref // Game (project base class)
sprite map[string]gogen.Ref // .spx => Sprite
sptypes []string // <sptype>.spx
scheds []string
Expand Down Expand Up @@ -245,67 +246,108 @@ func genTestFunc(pkg *gogen.Package, name, testType, param, paramType string) {
End()
}

func gmxMainFunc(pkg *gogen.Package, ctx *pkgCtx, noAutoGenMain bool) func() {
var proj *gmxProject
func gmxCheckProjs(pkg *gogen.Package, ctx *pkgCtx) (proj *gmxProject, multi bool) {
for _, v := range ctx.projs {
if v.isTest {
continue
} else if proj != nil {
return nil
multi = true
} else {
proj = v
}
if v.game != nil { // just to make testcase happy
gmxProjMain(pkg, ctx, v)
}
proj = v
}
if proj != nil { // only one project file
scope := pkg.Types.Scope()
var o types.Object
if proj.gameClass != "" {
o = scope.Lookup(proj.gameClass)
if noAutoGenMain && o != nil && hasMethod(o, "MainEntry") {
noAutoGenMain = false
return
}

func gmxProjMain(pkg *gogen.Package, parent *pkgCtx, proj *gmxProject) {
base := proj.game
classType := proj.gameClass
if classType == "" {
classType = base.Name()
proj.gameClass = classType
}

ld := getTypeLoader(parent, parent.syms, token.NoPos, proj.gameClass)
if ld.typ == nil {
ld.typ = func() {
if debugLoad {
log.Println("==> Load > NewType", classType)
}
} else {
o = proj.game
}
if !noAutoGenMain && o != nil {
// new(Game).Main()
// new(Game).Main(workers...)
fn := pkg.NewFunc(nil, "main", nil, nil, false)
return func() {
new := pkg.Builtin().Ref("new")
cb := fn.BodyStart(pkg).Val(new).Val(o).Call(1).MemberVal("Main")

// force remove //line comments for main func
cb.SetComments(nil, false)

sig := cb.Get(-1).Type.(*types.Signature)
narg := gmxMainNarg(sig)
if narg > 0 {
narg = len(proj.sptypes)
for _, spt := range proj.sptypes {
sp := scope.Lookup(spt)
cb.Val(new).Val(sp).Call(1)
}
old, _ := pkg.SetCurFile(defaultGoFile, true)
defer pkg.RestoreCurFile(old)

baseType := base.Type()
if proj.gameIsPtr {
baseType = types.NewPointer(baseType)
}
name := base.Name()
flds := []*types.Var{
types.NewField(token.NoPos, pkg.Types, name, baseType, true),
}
decl := pkg.NewTypeDefs().NewType(classType)
ld.typInit = func() { // decycle
if debugLoad {
log.Println("==> Load > InitType", classType)
}
old, _ := pkg.SetCurFile(defaultGoFile, true)
defer pkg.RestoreCurFile(old)

cb.Call(narg).EndStmt().End()
decl.InitType(pkg, types.NewStruct(flds, nil))
}
parent.tylds = append(parent.tylds, ld)
}
}
return nil
ld.methods = append(ld.methods, func() {
old, _ := pkg.SetCurFile(defaultGoFile, true)
defer pkg.RestoreCurFile(old)
doInitType(ld)

t := pkg.Ref(classType).Type()
recv := types.NewParam(token.NoPos, pkg.Types, "this", types.NewPointer(t))
fn := pkg.NewFunc(recv, "Main", nil, nil, false)

parent.inits = append(parent.inits, func() {
old, _ := pkg.SetCurFile(defaultGoFile, true)
defer pkg.RestoreCurFile(old)

cb := fn.BodyStart(pkg).Typ(base.Type()).MemberVal("Main")

// force remove //line comments for main func
cb.SetComments(nil, false)

sigParams := cb.Get(-1).Type.(*types.Signature).Params()
if _, ok := sigParams.At(0).Type().(*types.Pointer); !ok {
cb.Val(recv) // template recv method
} else {
cb.Val(recv).MemberRef(base.Name()).UnaryOp(gotoken.AND)
}
narg := sigParams.Len()
if narg > 1 {
narg = 1 + len(proj.sptypes)
new := pkg.Builtin().Ref("new")
for _, spt := range proj.sptypes {
sp := pkg.Ref(spt)
cb.Val(new).Val(sp).Call(1)
}
}

cb.Call(narg).EndStmt().End()
})
})
}

func gmxMainNarg(sig *types.Signature) int {
if fex, ok := gogen.CheckFuncEx(sig); ok {
if trm, ok := fex.(*gogen.TyTemplateRecvMethod); ok {
sig = trm.Func.Type().(*types.Signature)
return sig.Params().Len() - 1
func gmxMainFunc(pkg *gogen.Package, proj *gmxProject) func() {
return func() {
if o := pkg.TryRef(proj.gameClass); o != nil {
// new(gameClass).Main()
new := pkg.Builtin().Ref("new")
pkg.NewFunc(nil, "main", nil, nil, false).
BodyStart(pkg).Val(new).Val(o).Call(1).MemberVal("Main").Call(0).EndStmt().End()
}
}
return sig.Params().Len()
}

func hasMethod(o types.Object, name string) bool {
return findMethod(o, name) != nil
}

func findMethod(o types.Object, name string) *types.Func {
Expand Down Expand Up @@ -364,19 +406,6 @@ func astFnClassfname(c *gmxClass) *ast.FuncDecl {
}
}

func astEmptyFunc(entry string) *ast.FuncDecl {
return &ast.FuncDecl{
Name: &ast.Ident{
Name: entry,
},
Type: &ast.FuncType{
Params: &ast.FieldList{},
},
Body: &ast.BlockStmt{},
Shadow: true,
}
}

func astEmptyEntrypoint(f *ast.File) {
var entry = getEntrypoint(f)
var hasEntry bool
Expand All @@ -389,7 +418,16 @@ func astEmptyEntrypoint(f *ast.File) {
}
}
if !hasEntry {
f.Decls = append(f.Decls, astEmptyFunc(entry))
f.Decls = append(f.Decls, &ast.FuncDecl{
Name: &ast.Ident{
Name: entry,
},
Type: &ast.FuncType{
Params: &ast.FieldList{},
},
Body: &ast.BlockStmt{},
Shadow: true,
})
}
}

Expand Down
13 changes: 10 additions & 3 deletions cl/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gogen.Packag
gmx := f.File
if gmx.IsClass && !gmx.IsNormalGox {
if debugLoad {
log.Println("==> File", f.path, "normalGox:", gmx.IsNormalGox)
log.Println("==> ClassFile", f.path)
}
loadClass(ctx, p, f.path, gmx, conf)
}
Expand All @@ -568,6 +568,8 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gogen.Packag
preloadGopFile(p, ctx, f.path, f.File, conf)
}

proj, multi := gmxCheckProjs(p, ctx)

gopSyms := make(map[string]bool) // TODO: remove this map
for name := range ctx.syms {
gopSyms[name] = true
Expand Down Expand Up @@ -599,8 +601,11 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gogen.Packag
loadFile(ctx, f.File)
}
}

if genMain { // make classfile main func if need
gen = gmxMainFunc(p, ctx, conf.NoAutoGenMain)
if proj != nil && !multi { // only one project file
gen = gmxMainFunc(p, proj)
}
}

for _, f := range sfiles {
Expand Down Expand Up @@ -840,11 +845,13 @@ func preloadGopFile(p *gogen.Package, ctx *blockCtx, file string, f *ast.File, c
f.Decls = append(f.Decls, astFnClassfname(c))
}
}

if d := f.ShadowEntry; d != nil {
d.Name.Name = getEntrypoint(f)
} else if f.IsProj && !conf.NoAutoGenMain && inMainPkg(f) {
} else if baseTypeName != "" { // isClass && not isNormalGox
astEmptyEntrypoint(f)
}

preloadFile(p, ctx, f, goFile, !conf.Outline)
if goxTestFile {
parent.inits = append(parent.inits, func() {
Expand Down
Loading

0 comments on commit 824bf92

Please sign in to comment.