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

fix to webkit callback needing string or nil value #189

Merged
merged 3 commits into from
Aug 20, 2023
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
19 changes: 14 additions & 5 deletions generate/codegen/gen_protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ type Protocol struct {
Description string
DocURL string

SkipInterface bool
SkipWrapper bool
SkipDelegate bool

init bool
}

Expand Down Expand Up @@ -104,18 +108,23 @@ func (p *Protocol) GoImports() set.Set[string] {
func (p *Protocol) WriteGoCode(w *CodeWriter) {
w.WriteLine("")

// Protocol Interface
p.writeProtocolInterface(w)
if !p.SkipInterface {
p.writeProtocolInterface(w)
}

// Delegate Prototol impl struct
w.WriteLine("")
if strings.Contains(p.Type.GName, "Delegate") {

if strings.Contains(p.Type.GName, "Delegate") && !p.SkipDelegate {
p.writeDelegateStruct(w)
}

// Protocol Wrapper
w.WriteLine("")
p.writeProtocolWrapperStruct(w)

if !p.SkipWrapper {
p.writeProtocolWrapperStruct(w)
}

}

func (p *Protocol) writeProtocolInterface(w *CodeWriter) {
Expand Down
41 changes: 36 additions & 5 deletions generate/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import (

type Generator struct {
*SymbolCache
Platform string
Version int
Framework string
enumCache map[string][]Symbol
genCache map[string]codegen.CodeGen
Platform string
Version int
Framework string
customTypes set.Set[string]
customMethods set.Set[string]
enumCache map[string][]Symbol
genCache map[string]codegen.CodeGen
}

func (db *Generator) Generate(platform string, version int, rootDir string, framework string, ignoreTypes set.Set[string]) {
Expand All @@ -28,6 +30,10 @@ func (db *Generator) Generate(platform string, version int, rootDir string, fram
db.Framework = module.Package
modsym := db.ModuleSymbol(*module)

types, methods := exportedSourceSymbols(fmt.Sprintf("%s/%s/%s_custom.go", rootDir, db.Framework, db.Framework))
db.customTypes = set.New(types...)
db.customMethods = set.New(methods...)

RemoveGeneratedCode(fmt.Sprintf("%s/%s", rootDir, db.Framework))

mw := &codegen.ModuleWriter{
Expand All @@ -45,6 +51,10 @@ func (db *Generator) Generate(platform string, version int, rootDir string, fram
}
switch s.Kind {
case "Class":
if db.customTypes.Contains(modules.TrimPrefix(s.Name)) {
log.Println("skipping class with custom definition", s.Name)
continue
}
classGen := db.ToClassGen(s)
if classGen == nil {
log.Println("skipping class", s.Name)
Expand All @@ -64,6 +74,18 @@ func (db *Generator) Generate(platform string, version int, rootDir string, fram
log.Println("skipping protocol", s.Name)
continue
}
if db.customTypes.Contains("P" + modules.TrimPrefix(s.Name)) {
log.Println("skipping protocol interface with custom definition", s.Name)
protocolGen.SkipInterface = true
}
if db.customTypes.Contains(modules.TrimPrefix(s.Name) + "Wrapper") {
log.Println("skipping protocol wrapper with custom definition", s.Name)
protocolGen.SkipWrapper = true
}
if db.customTypes.Contains(modules.TrimPrefix(s.Name)) {
log.Println("skipping protocol delegate with custom definition", s.Name)
protocolGen.SkipDelegate = true
}
protocolGen.Init()
fw := &codegen.FileWriter{
Name: s.Name,
Expand All @@ -75,10 +97,19 @@ func (db *Generator) Generate(platform string, version int, rootDir string, fram
mw.Protocols = append(mw.Protocols, protocolGen.Type)
case "Enum":
if s.Name == "MTLArgumentAccess" {
// todo: regen metal without this skip to see why
continue
}
if db.customTypes.Contains(modules.TrimPrefix(s.Name)) {
log.Println("skipping enum with custom definition", s.Name)
continue
}
mw.EnumAliases = append(mw.EnumAliases, db.ToEnumInfo(framework, s))
case "Type":
if db.customTypes.Contains(modules.TrimPrefix(s.Name)) {
log.Println("skipping type with custom definition", s.Name)
continue
}
if db.AllowedEnumAlias(s) {
mw.EnumAliases = append(mw.EnumAliases, db.ToEnumInfo(framework, s))
continue
Expand Down
7 changes: 7 additions & 0 deletions generate/members.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ func (db *Generator) Members(fw string, sym Symbol, covariantTypes []string) (pr
//Required: ??,
}

// skip if defined in custom
qualifiedName := fmt.Sprintf("%s#%s", modules.TrimPrefix(sym.Name), gm.GoFuncName())
if db.customMethods.Contains(qualifiedName) {
log.Printf("skipping custom defined method '%s'\n", qualifiedName)
continue
}

// handle name conflicts
sel := gm.Selector()
goSel := gm.ProtocolGoFuncName()
Expand Down
39 changes: 39 additions & 0 deletions generate/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package generate
import (
"bufio"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/fs"
"log"
"os"
Expand Down Expand Up @@ -43,3 +46,39 @@ func FormatCode(dir string) {

fmt.Println(string(out))
}

func exportedSourceSymbols(filename string) (types []string, methods []string) {
fset := token.NewFileSet()
node, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
if err != nil {
return
}

// Traverse the AST and print type names
ast.Inspect(node, func(n ast.Node) bool {
// Check if the node is a type specification
if typeSpec, ok := n.(*ast.TypeSpec); ok && typeSpec.Name.IsExported() {
types = append(types, typeSpec.Name.Name)
}
if funcDecl, ok := n.(*ast.FuncDecl); ok && funcDecl.Recv != nil && len(funcDecl.Recv.List) > 0 {
var recvTypeName string
switch t := funcDecl.Recv.List[0].Type.(type) {
case *ast.StarExpr: // Pointer receiver
if ident, ok := t.X.(*ast.Ident); ok && ident.IsExported() {
recvTypeName = ident.Name
}
case *ast.Ident: // Value receiver
if t.IsExported() {
recvTypeName = t.Name
}
}
if recvTypeName != "" && funcDecl.Name.IsExported() {
methods = append(methods, fmt.Sprintf("%s#%s", recvTypeName, funcDecl.Name.Name))

}
}
return true
})

return
}
4 changes: 2 additions & 2 deletions macos/_examples/menu/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ func setMainMenu(app appkit.Application) {
menu := appkit.Menu_InitWithTitle("main")
app.SetMainMenu(menu)

mainMenuItem := appkit.NewMenuItemWithSelector(" ", " ", objc.Selector{})
mainMenuItem := appkit.NewMenuItemWithSelector("", "", objc.Selector{})
mainMenuMenu := appkit.Menu_InitWithTitle("App")
mainMenuMenu.AddItem(appkit.NewMenuItemWithAction("Hide", "h", func(sender objc.Object) { app.Hide(nil) }))
mainMenuMenu.AddItem(appkit.NewMenuItemWithAction("Quit", "q", func(sender objc.Object) { app.Terminate(nil) }))
mainMenuItem.SetSubmenu(mainMenuMenu)
menu.AddItem(mainMenuItem)

testMenuItem := appkit.NewMenuItemWithSelector(" ", " ", objc.Selector{})
testMenuItem := appkit.NewMenuItemWithSelector("", "", objc.Selector{})
testMenu := appkit.Menu_InitWithTitle("Edit")
testMenu.AddItem(appkit.NewMenuItemWithSelector("Select All", "a", objc.Sel("selectAll:")))
// missing symbol?
Expand Down
4 changes: 4 additions & 0 deletions macos/_examples/webview/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import (
//go:embed assets
var assetsFS embed.FS

func init() {
runtime.LockOSThread()
}

func main() {
app := appkit.ApplicationClass.SharedApplication()
w := appkit.NewWindowWithSize(600, 400)
Expand Down
2 changes: 1 addition & 1 deletion macos/_examples/widgets/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func main() {
tf.SetFrame(rectOf(10, 100, 150, 25))

// label
label := appkit.NewLabel(" ")
label := appkit.NewLabel("")
label.SetFrame(rectOf(170, 100, 150, 25))
w.ContentView().AddSubview(label)
tfd := &appkit.TextFieldDelegate{}
Expand Down
29 changes: 0 additions & 29 deletions macos/webkit/script_message_handler_with_reply.gen.go
Original file line number Diff line number Diff line change
@@ -1,32 +1,3 @@
// AUTO-GENERATED CODE, DO NOT MODIFY

package webkit

import (
"github.com/progrium/macdriver/objc"
)

// An interface for responding to messages from JavaScript code running in a webpage. [Full Topic]
//
// [Full Topic]: https://developer.apple.com/documentation/webkit/wkscriptmessagehandlerwithreply?language=objc
type PScriptMessageHandlerWithReply interface {
// optional
UserContentControllerDidReceiveScriptMessageReplyHandler(userContentController UserContentController, message ScriptMessage, replyHandler func(reply objc.Object, errorMessage string))
HasUserContentControllerDidReceiveScriptMessageReplyHandler() bool
}

// A concrete type wrapper for the [PScriptMessageHandlerWithReply] protocol.
type ScriptMessageHandlerWithReplyWrapper struct {
objc.Object
}

func (s_ ScriptMessageHandlerWithReplyWrapper) HasUserContentControllerDidReceiveScriptMessageReplyHandler() bool {
return s_.RespondsToSelector(objc.Sel("userContentController:didReceiveScriptMessage:replyHandler:"))
}

// Tells the handler that a webpage sent a script message that included a reply. [Full Topic]
//
// [Full Topic]: https://developer.apple.com/documentation/webkit/wkscriptmessagehandlerwithreply/3585111-usercontentcontroller?language=objc
func (s_ ScriptMessageHandlerWithReplyWrapper) UserContentControllerDidReceiveScriptMessageReplyHandler(userContentController IUserContentController, message IScriptMessage, replyHandler func(reply objc.Object, errorMessage string)) {
objc.Call[objc.Void](s_, objc.Sel("userContentController:didReceiveScriptMessage:replyHandler:"), objc.Ptr(userContentController), objc.Ptr(message), replyHandler)
}
37 changes: 33 additions & 4 deletions macos/webkit/webkit_custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,15 @@ func (h *scriptMessageHandlerWithReply) HasUserContentControllerDidReceiveScript
// UserContentControllerDidReceiveScriptMessageReplyHandler implements ScriptMessageHandlerWithReply
func (h *scriptMessageHandlerWithReply) UserContentControllerDidReceiveScriptMessageReplyHandler(
userContentController UserContentController, message ScriptMessage,
replyHandler func(reply objc.Object, errorMessage string)) {
replyHandler func(reply objc.Object, errorMessage *string)) {
message.Retain()
go func() {
defer message.Release()
reply, err := h.handler(message.Body())
var errMsg = foundation.String{} // nil
var errMsg *string
if err != nil {
errMsg = foundation.String_InitWithString(err.Error())
str := err.Error()
errMsg = &str
}
if !reply.IsNil() {
reply.Retain()
Expand All @@ -90,7 +91,7 @@ func (h *scriptMessageHandlerWithReply) UserContentControllerDidReceiveScriptMes
reply.Release()
}
}()
replyHandler(reply, errMsg.String())
replyHandler(reply, errMsg)
})
}()
}
Expand Down Expand Up @@ -222,3 +223,31 @@ func (h *FileSystemURLSchemeHandler) getMime(path string) string {
return "application/octet-stream"
}
}

// the following was custom defined to fix the type for errorMessage in the callback.
// generation would make it a string but we need it to be a *string

// An interface for responding to messages from JavaScript code running in a webpage. [Full Topic]
//
// [Full Topic]: https://developer.apple.com/documentation/webkit/wkscriptmessagehandlerwithreply?language=objc
type PScriptMessageHandlerWithReply interface {
// optional
UserContentControllerDidReceiveScriptMessageReplyHandler(userContentController UserContentController, message ScriptMessage, replyHandler func(reply objc.Object, errorMessage *string))
HasUserContentControllerDidReceiveScriptMessageReplyHandler() bool
}

// A concrete type wrapper for the [PScriptMessageHandlerWithReply] protocol.
type ScriptMessageHandlerWithReplyWrapper struct {
objc.Object
}

func (s_ ScriptMessageHandlerWithReplyWrapper) HasUserContentControllerDidReceiveScriptMessageReplyHandler() bool {
return s_.RespondsToSelector(objc.Sel("userContentController:didReceiveScriptMessage:replyHandler:"))
}

// Tells the handler that a webpage sent a script message that included a reply. [Full Topic]
//
// [Full Topic]: https://developer.apple.com/documentation/webkit/wkscriptmessagehandlerwithreply/3585111-usercontentcontroller?language=objc
func (s_ ScriptMessageHandlerWithReplyWrapper) UserContentControllerDidReceiveScriptMessageReplyHandler(userContentController IUserContentController, message IScriptMessage, replyHandler func(reply objc.Object, errorMessage *string)) {
objc.Call[objc.Void](s_, objc.Sel("userContentController:didReceiveScriptMessage:replyHandler:"), objc.Ptr(userContentController), objc.Ptr(message), replyHandler)
}
21 changes: 14 additions & 7 deletions objc/type_convertion.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,20 @@ func convertToObjcValue(v reflect.Value) unsafe.Pointer {
case reflect.Float64:
cv := float64(v.Float())
return unsafe.Pointer(&cv)
case reflect.UnsafePointer, reflect.Pointer:
case reflect.Pointer:
// allow use of *string
if rt.Elem().Kind() == reflect.String {
if v.IsNil() {
var p unsafe.Pointer = nil
return unsafe.Pointer(&p)
}
sp := ToNSString(v.Elem().String())
return unsafe.Pointer(&sp)
}
// otherwise treat as unsafe pointer
cv := v.UnsafePointer()
return unsafe.Pointer(&cv)
case reflect.UnsafePointer:
cv := v.UnsafePointer()
return unsafe.Pointer(&cv)
case reflect.Interface:
Expand All @@ -330,12 +343,6 @@ func convertToObjcValue(v reflect.Value) unsafe.Pointer {
}
return getStructValuePointer(v)
case reflect.String:
// need some way to have nil NSString values,
// so let's try empty strings are nil
if v.String() == "" {
var p unsafe.Pointer = nil
return unsafe.Pointer(&p)
}
sp := ToNSString(v.String())
return unsafe.Pointer(&sp)
case reflect.Slice:
Expand Down