Subclasses can have new methods in addition to overriding existing

methods (first draft).
This commit is contained in:
Greg 2019-05-24 12:36:01 -04:00
parent 38c7856b89
commit 34fbcec965
3 changed files with 151 additions and 37 deletions

View File

@ -7,7 +7,8 @@ subclasses:
ClassThree: ClassThree:
ClassTwo: ClassTwo:
- geti1 - geti1
- (void)myMethod:int - -(void)myMethod1:(int)a
- +(const NSObject* _Nonnull)myMethod2:(int)a:(NSDictionary<NSString*,NSObject*> *)options
imports: [ simple.h ] imports: [ simple.h ]
frameworks: [ Foundation ] frameworks: [ Foundation ]

View File

@ -32,6 +32,26 @@ func init() {
//TypeName = _TypeName //TypeName = _TypeName
} }
func MethodSignature(s string, n *Node) (string, *Node) {
return ChildOf(NewNode("MethodSignature"),Seq(
Parenthesized(TypeName),
Identifier,
Opt(MethodParameterList),
))(s,n)
}
func MethodParameterList(s string, n *Node) (string, *Node) {
return OneOrMore(MethodParameter)(s,n)
}
func MethodParameter(s string, n *Node) (string, *Node) {
return ChildOf(NewNode("MethodParameter"),Seq(
Lit(":"),
Parenthesized(TypeName),
Identifier,
))(s,n)
}
func _TypeName(s string, n *Node) (string, *Node) { func _TypeName(s string, n *Node) (string, *Node) {
return ChildOf(NewNode("TypeName"),Seq( return ChildOf(NewNode("TypeName"),Seq(
SpecifierQualifierList, SpecifierQualifierList,

View File

@ -23,7 +23,7 @@ type Wrapper struct {
NamedEnums map[string]*Enum NamedEnums map[string]*Enum
AnonEnums []*Enum AnonEnums []*Enum
Delegates map[string]map[string][]string Delegates map[string]map[string][]string
Subclasses map[string]map[string][]string Subclasses map[string]*Subclass
Protocols map[string]*Protocol Protocols map[string]*Protocol
cgoFlags strings.Builder // put cGo directives here cgoFlags strings.Builder // put cGo directives here
@ -49,6 +49,7 @@ func NewWrapper(debug bool) *Wrapper {
NamedEnums: map[string]*Enum{}, NamedEnums: map[string]*Enum{},
AnonEnums: []*Enum{}, AnonEnums: []*Enum{},
Protocols: map[string]*Protocol{}, Protocols: map[string]*Protocol{},
Subclasses: map[string]*Subclass{},
ProcessedTypes: map[string]bool{}, ProcessedTypes: map[string]bool{},
ProcessedClassMethods: map[string]bool{}, ProcessedClassMethods: map[string]bool{},
Vaargs: 16, Vaargs: 16,
@ -98,7 +99,39 @@ func (w *Wrapper) Delegate(ds map[string]map[string][]string) {
} }
func (w *Wrapper) Subclass(ds map[string]map[string][]string) { func (w *Wrapper) Subclass(ds map[string]map[string][]string) {
w.Subclasses = ds for k,v := range ds {
sc := &Subclass{
Overrides: []string{},
NewMethods: []string{},
}
if len(ds) == 0 {
fmt.Printf("No superclass specified for subclass %s\n",k)
os.Exit(-1)
}
if len(ds) > 1 {
fmt.Printf("Multiple inheritance not permitted for subclass %s\n",k)
os.Exit(-1)
}
sc.Name = k
for x,y := range(v) {
sc.Super = x
for _,m := range y {
switch m[0] {
case '-','+':
sc.NewMethods = append(sc.NewMethods,m)
default:
sc.Overrides = append(sc.Overrides,m)
}
}
}
w.Subclasses[sc.Name] = sc
}
}
type Subclass struct {
Name,Super string
Overrides []string
NewMethods []string
} }
type Property struct { type Property struct {
@ -752,13 +785,13 @@ func (w *Wrapper) _processMethod(m *Method,fun bool) {
return return
} }
w.processType(m.Type) w.processType(m.Type)
//w.processType(m.GoClass) //??
gname := m.Name gname := m.Name
gname = strings.ReplaceAll(gname,"_"," ") gname = strings.ReplaceAll(gname,"_"," ")
gname = strings.Title(gname) gname = strings.Title(gname)
gname = strings.ReplaceAll(gname," ","") gname = strings.ReplaceAll(gname," ","")
receiver := "" receiver := ""
cname := m.Name cname := m.Name
fmt.Printf("Method %s (GoClass %s)\n",cname,m.GoClass)
switch { switch {
case !m.ClassMethod: case !m.ClassMethod:
if types.IsGoInterface(m.GoClass) { if types.IsGoInterface(m.GoClass) {
@ -943,16 +976,63 @@ type %s %s
w.goConst.WriteString("\n") w.goConst.WriteString("\n")
} }
func (w *Wrapper) ProcessSubclass(sname string, ps map[string][]string) { func (w *Wrapper) MethodFromSig(sig,class string) *Method {
w._ProcessDelSub(sname,ps,true) ret := &Method{ Parameters: []*Parameter{} }
if len(sig) == 0 {
return ret
}
if sig[0] == '+' {
ret.ClassMethod = true
}
sig = sig[1:]
rem,n := types.MethodSignature(sig,types.NewNode("AST"))
if len(rem) > 0 {
fmt.Printf("Failed to parse method signature %s (%s)\n",sig,rem)
os.Exit(-1)
}
for _,c := range n.Children {
switch c.Kind {
case "TypeName":
tp := types.NewType(c,class)
ret.Type = tp
case "Identifier":
ret.Name = c.Content
case "MethodParameter":
p := &Parameter{}
for _,d := range c.Children {
switch d.Kind {
case "TypeName":
tp := types.NewType(d,class)
p.Type = tp
case "Identifier":
p.Vname = d.Content
}
}
ret.Parameters = append(ret.Parameters,p)
}
}
return ret
}
func (w *Wrapper) ProcessSubclass(sname string, sc *Subclass) {
fmt.Printf("Wrapping %s\n",sname)
types.Wrap(sname)
types.SetSuper(sname,sc.Super)
ps := map[string][]string{}
ps[sc.Super] = sc.Overrides
nms := make([]*Method,len(sc.NewMethods))
for i,sig := range sc.NewMethods {
nms[i] = w.MethodFromSig(sig,sname)
}
w._ProcessDelSub(sname,ps,nms,true)
} }
func (w *Wrapper) ProcessDelegate(dname string, ps map[string][]string) { func (w *Wrapper) ProcessDelegate(dname string, ps map[string][]string) {
w._ProcessDelSub(dname,ps,false) w._ProcessDelSub(dname,ps,nil,false)
} }
//NOTE: The delegate wrapper does not support variadic callback functions. //NOTE: The delegate wrapper does not support variadic callback functions.
func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string,sub bool) { func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string, nms []*Method, sub bool) {
//To create (per delegate): //To create (per delegate):
//1. ObjC interface //1. ObjC interface
//2. ObjC implementation //2. ObjC implementation
@ -972,6 +1052,7 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string,sub bool)
//set up array of methods for this delegate //set up array of methods for this delegate
methods := []*Method{} methods := []*Method{}
sms := 0 // the number of methods that have super-methods
gnames := []string{} // go names for methods gnames := []string{} // go names for methods
pnames := make([]string,len(ps)) pnames := make([]string,len(ps))
var supr string var supr string
@ -998,14 +1079,14 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string,sub bool)
if sup := types.Super(s); w.Interfaces[sup] != nil { if sup := types.Super(s); w.Interfaces[sup] != nil {
addmeths(sup) addmeths(sup)
} }
fmt.Printf("Adding methods for %s\n",s) //fmt.Printf("Adding methods for %s\n",s)
for k,v := range w.Interfaces[s].InstanceMethods { for k,v := range w.Interfaces[s].InstanceMethods {
ms[k] = v ms[k] = v
} }
} }
//for subclasses, add all superclass methods, depth first //for subclasses, add all superclass methods, depth first
addmeths(interf.Name) addmeths(interf.Name)
} else { } else { // not a subclass
proto := w.Protocols[pname] proto := w.Protocols[pname]
if proto == nil { if proto == nil {
fmt.Printf("Failed to find protocol %s for delegate %s\n",pname,dname) fmt.Printf("Failed to find protocol %s for delegate %s\n",pname,dname)
@ -1019,9 +1100,6 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string,sub bool)
//note:we may have capitalized the first character to make a goname, //note:we may have capitalized the first character to make a goname,
//but m.Name is not disambiguated for polymorphics... //but m.Name is not disambiguated for polymorphics...
if !matches(string(m.Name[0])+gname[1:],pats) { if !matches(string(m.Name[0])+gname[1:],pats) {
if sub {
//Add newly defined methods that don't match anything...
}
continue continue
} }
if m.Type.IsFunction() || m.Type.IsFunctionPtr() || m.hasFunctionParam() { if m.Type.IsFunction() || m.Type.IsFunctionPtr() || m.hasFunctionParam() {
@ -1029,12 +1107,20 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string,sub bool)
} }
methods = append(methods,m) methods = append(methods,m)
gnames = append(gnames,gname) gnames = append(gnames,gname)
if sub { sms = len(methods) }
}
}
//add new methods being defined for the subclass
if sub {
for _,m := range nms {
methods = append(methods,m)
gnames = append(gnames,strings.Title(m.Name))
} }
} }
methprotos := make([]string,len(methods)) // objc method prototypes methprotos := make([]string,len(methods)) // objc method prototypes
smethprotos := make([]string,len(methods)) // super method prototypes smethprotos := make([]string,sms) // super method prototypes
sfunprotos := make([]string,len(methods)) // super method prototypes sfunprotos := make([]string,sms) // super method prototypes
gname := strings.Title(dname) // go name for this Delegate gname := strings.Title(dname) // go name for this Delegate
vnames := make([][]string,len(methods)) // objc variable names vnames := make([][]string,len(methods)) // objc variable names
vpnames := make([][]string,len(methods)) // objc parameter:variable names vpnames := make([][]string,len(methods)) // objc parameter:variable names
@ -1046,11 +1132,10 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string,sub bool)
//1. ObjC interface //1. ObjC interface
if sub { if sub {
fmt.Printf("Subclass ") fmt.Printf("Subclass %s <%s>: %d overrides, %d new methods\n",dname,strings.Join(pnames,", "),sms, len(nms))
} else { } else {
fmt.Printf("Delegate ") fmt.Printf("Delegate %s <%s>: %d methods\n",dname,strings.Join(pnames,", "),len(methods))
} }
fmt.Printf("%s <%s>: %d methods\n",dname,strings.Join(pnames,", "),len(methods))
for i,m := range methods { for i,m := range methods {
w.processType(m.Type) w.processType(m.Type)
vnames[i] = make([]string,len(m.Parameters)+1) vnames[i] = make([]string,len(m.Parameters)+1)
@ -1100,13 +1185,17 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string,sub bool)
methprotos[i] = fmt.Sprintf( methprotos[i] = fmt.Sprintf(
`- (%s)%s%s;`,m.Type.Node.CType(),m.Name,parms) `- (%s)%s%s;`,m.Type.Node.CType(),m.Name,parms)
ct := m.Type.Node.CType() ct := m.Type.Node.CType()
smethprotos[i] = fmt.Sprintf( if i < sms {
smethprotos[i] = fmt.Sprintf(
`- (%s)super_%s%s;`,ct,m.Name,parms) `- (%s)super_%s%s;`,ct,m.Name,parms)
}
if ct == "instancetype" { if ct == "instancetype" {
ct = gname + "*" ct = gname + "*"
} }
sfunprotos[i] = fmt.Sprintf( if i < sms {
sfunprotos[i] = fmt.Sprintf(
`%s %s_super_%s(%s);`,ct,dname,m.Name,cparms) `%s %s_super_%s(%s);`,ct,dname,m.Name,cparms)
}
if x := m.Type.GoType(); x == "Void" { if x := m.Type.GoType(); x == "Void" {
grtypes[i] = "" grtypes[i] = ""
} else { } else {
@ -1150,8 +1239,11 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string,sub bool)
sfundecls := make([]string,len(methods)) sfundecls := make([]string,len(methods))
for i,mp := range methprotos { for i,mp := range methprotos {
mp := mp[:len(mp)-1] mp := mp[:len(mp)-1]
smp := smethprotos[i][:len(smethprotos[i])-1] var smp, sfp string
sfp := sfunprotos[i][:len(sfunprotos[i])-1] if sub && i < sms {
smp = smethprotos[i][:len(smethprotos[i])-1]
sfp = sfunprotos[i][:len(sfunprotos[i])-1]
}
var ret string var ret string
if crtypes[i] != "void" { if crtypes[i] != "void" {
ret = "return " ret = "return "
@ -1168,7 +1260,7 @@ func (w *Wrapper) _ProcessDelSub(dname string, ps map[string][]string,sub bool)
%s%s(%s); %s%s(%s);
} }
`,mp,ret,gname + gnames[i],strings.Join(vnames[i],", ")) `,mp,ret,gname + gnames[i],strings.Join(vnames[i],", "))
if sub { if sub && i < sms {
smethdecls[i] = fmt.Sprintf(` smethdecls[i] = fmt.Sprintf(`
%s %s
{ {
@ -1207,10 +1299,8 @@ void*
//4. Go type //4. Go type
gotypes.WriteString(fmt.Sprintf(` gotypes.WriteString(
type %s struct { %s } types.NewTypeFromString(gname,supr).GoInterfaceDecl())
func (o %s) Ptr() unsafe.Pointer { return o.ptr }
`,gname,supr,gname))
//5. Go constructor //5. Go constructor
gocode.WriteString(fmt.Sprintf(` gocode.WriteString(fmt.Sprintf(`
@ -1223,14 +1313,14 @@ func %sAlloc() %s {
//6. Go dispatch database for callbacks //6. Go dispatch database for callbacks
dispitems := make([]string,len(gnames)) dispitems := make([]string,len(gnames))
sdispitems := make([]string,len(gnames)) sdispitems := make([]string,sms)
for i,n := range gnames { for i,n := range gnames {
if !sub { if !sub {
gtypes[i] = gtypes[i][1:] gtypes[i] = gtypes[i][1:]
} }
dispitems[i] = fmt.Sprintf( dispitems[i] = fmt.Sprintf(
` %s func(%s)%s`,n,strings.Join(gtypes[i],", "),grtypes[i]) ` %s func(%s)%s`,n,strings.Join(gtypes[i],", "),grtypes[i])
if sub { if sub && i < sms {
sdispitems[i] = fmt.Sprintf( sdispitems[i] = fmt.Sprintf(
` %s func(%s)%s`,n,strings.Join(gtypes[i][1:],", "),grtypes[i]) ` %s func(%s)%s`,n,strings.Join(gtypes[i][1:],", "),grtypes[i])
} }
@ -1259,7 +1349,7 @@ type %sSupermethods struct {
`,crtypes[i],gname,gnames[i],ctps)) `,crtypes[i],gname,gnames[i],ctps))
//2. Go callback registration functions //2. Go callback registration functions
gocode.WriteString(fmt.Sprintf(` gocode.WriteString(fmt.Sprintf(`
func (d *%s) %sCallback(f func(%s)%s) { func (d %s) %sCallback(f func(%s)%s) {
dispatch := %sLookup[d.Ptr()] dispatch := %sLookup[d.Ptr()]
dispatch.%s = f dispatch.%s = f
%sLookup[d.Ptr()] = dispatch %sLookup[d.Ptr()] = dispatch
@ -1304,16 +1394,19 @@ func (d *%s) %sCallback(f func(%s)%s) {
if cgtypes[i] == "unsafe.Pointer" { if cgtypes[i] == "unsafe.Pointer" {
retn = "return unsafe.Pointer(" retn = "return unsafe.Pointer("
crtype = " unsafe.Pointer" crtype = " unsafe.Pointer"
if types.ShouldWrap(m.Type.GoType()) {
retnparen = ".Ptr()"
}
} else { } else {
retn = "return (" + cgtypes[i] + ")(" retn = "return (" + cgtypes[i] + ")("
crtype = " " + cgtypes[i] crtype = " " + cgtypes[i]
} }
retnparen = ")" retnparen = retnparen + ")"
} }
sdispentries := []string{} sdispentries := make([]string,sms)
for _,n := range gnames { for i,_ := range sdispentries {
sdispentries = append(sdispentries,fmt.Sprintf( sdispentries[i] = fmt.Sprintf(
` self.Super%s`,n)) ` self.Super%s`,gnames[i])
} }
sper := "" sper := ""
if sub && len(gnames) > 0 { if sub && len(gnames) > 0 {
@ -1338,7 +1431,7 @@ func %s%s(%s)%s {
} }
`,gname,gnames[i],gname,gnames[i],strings.Join(earglist,", "),crtype,retdecl,gname,gnames[i],retname,sper,strings.Join(gargconv,"\n"),retn,strings.Join(garglist,", "),retnparen)) `,gname,gnames[i],gname,gnames[i],strings.Join(earglist,", "),crtype,retdecl,gname,gnames[i],retname,sper,strings.Join(gargconv,"\n"),retn,strings.Join(garglist,", "),retnparen))
//4. Go wrapper functions for superclass methods //4. Go wrapper functions for superclass methods
if !sub { continue } // for subclasses only if !sub || i >= sms { continue } // for subclasses only
grtype := m.Type.GoType() grtype := m.Type.GoType()
if grtype == "Void" { if grtype == "Void" {
grtype = "" grtype = ""
@ -1351,7 +1444,7 @@ func %s%s(%s)%s {
} }
if sub { if sub {
gocode.WriteString(fmt.Sprintf(` gocode.WriteString(fmt.Sprintf(`
func (o *%s) Super%s(%s) %s { func (o %s) Super%s(%s) %s {
`,gname,gnames[i],strings.Join(earglist[1:],", "), grtype)) `,gname,gnames[i],strings.Join(earglist[1:],", "), grtype))
ns,tps,_ := w.gpntp(m) ns,tps,_ := w.gpntp(m)
lparm := len(tps)-1 lparm := len(tps)-1