Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gmxProjMain: generate gameClass.Main() #1814

Merged
merged 7 commits into from
Mar 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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