diff --git a/generate/generator.go b/generate/generator.go index 60c12e0f..a20ed97b 100644 --- a/generate/generator.go +++ b/generate/generator.go @@ -4,6 +4,8 @@ import ( "fmt" "log" "strings" + "io" + "os" "github.com/progrium/macdriver/generate/codegen" "github.com/progrium/macdriver/generate/declparse" @@ -42,6 +44,8 @@ func (db *Generator) Generate(platform string, version int, rootDir string, fram Module: *module, PlatformDir: rootDir, } + copySpecialFile(framework) + for _, s := range db.ModuleSymbols(framework) { if ignoreTypes.Contains(s.Name) { continue @@ -67,6 +71,7 @@ func (db *Generator) Generate(platform string, version int, rootDir string, fram PlatformDir: rootDir, } fw.Add(classGen) + removeMethods(classGen) fw.WriteCode() case "Protocol": protocolGen := db.ToProtocolGen(framework, s) @@ -183,3 +188,154 @@ func strIn(slice []string, str string) bool { } return false } + +// used to store information from special.go files +type SkipMethod struct { + ClassName string + Selector string + Note string +} + +// the database of methods that should not be generated +var methodSkipList []SkipMethod +var attemptedRead = false // prevents multiple unnecessary read attempts + +// reads a file called special.go and creates a database of methods to not generate "skip" +func getFilteredSelectors() []SkipMethod { + workingPath, err := os.Getwd() + if err != nil { + fmt.Println("Error:", err) + return nil + } + specialFilePath := workingPath + "/special.go" + specialFile, err := os.ReadFile(specialFilePath) + if err != nil { + return nil + } + + // The special.go file will contain a section that begins with "begin-skip" + // and end with "end-skip". The information between these two indicators + // is used to create a database of methods to not generate. + // The format is "Objective-c class name", "selector", "note to display while generating". + specialFileContents := string(specialFile) + start := strings.Index(specialFileContents, "begin-skip") + 10 + end := strings.Index(specialFileContents, "end-skip") + data := specialFileContents[start:end] + rows := strings.Split(data, "\n") + buffer := make([]SkipMethod, 0) + for _,row := range(rows) { + if row == "" { + continue + } + row = strings.ReplaceAll(row, "\"", "") + columns := strings.Split(row, ",") + newSkipMethod := SkipMethod{} + newSkipMethod.ClassName = strings.TrimSpace(columns[0]) + newSkipMethod.Selector = strings.TrimSpace(columns[1]) + newSkipMethod.Note = strings.TrimSpace(columns[2]) + buffer = append(buffer, newSkipMethod) + } + return buffer +} + +// removes methods that are on the list of methods to skip +func removeMethods(theClass *codegen.Class) { + + if methodSkipList == nil && attemptedRead == true { + return // no need to continue if there is no list + } + + // cache the list + if methodSkipList == nil && attemptedRead == false { + methodSkipList = getFilteredSelectors() + attemptedRead = true + } + + // find and remove the skippable instance methods + methodBuffer := make([]*codegen.Method, len(theClass.InstanceTypeMethods)) + copy(methodBuffer, theClass.InstanceTypeMethods) + for index, method := range(theClass.InstanceTypeMethods) { + for _, skipMethod := range(methodSkipList) { + if skipMethod.ClassName == theClass.String() { + if skipMethod.Selector == method.Selector() { + fmt.Printf("Removing [%s %s] - %s\n", theClass, skipMethod.Selector, skipMethod.Note) + //remove the element from the slice + methodBuffer[index] = nil + } + } + } + } + + // create a new slice with all the skipped instance methods gone + theClass.InstanceTypeMethods = nil + for _, method := range(methodBuffer) { + if method != nil { + theClass.InstanceTypeMethods = append(theClass.InstanceTypeMethods, method) + } + } + + // find and remove the skippable methods + methodBuffer = make([]*codegen.Method, len(theClass.Methods)) + copy(methodBuffer, theClass.Methods) + for index, method := range(theClass.Methods) { + for _, skipMethod := range(methodSkipList) { + if skipMethod.ClassName == theClass.String() { + if skipMethod.Selector == method.Selector() { + fmt.Printf("Removing [%s %s] - %s\n", theClass, skipMethod.Selector, skipMethod.Note) + //remove the element from the slice + methodBuffer[index] = nil + } + } + } + } + + // create a new slice with all the skipped methods gone + theClass.Methods = nil + for _, method := range(methodBuffer) { + if method != nil { + theClass.Methods = append(theClass.Methods, method) + } + } +} + +// Copies the special.go file to the current framework's folder +func copySpecialFile(framework string) { + + // the folder all the frameworks are in is named one of these + platforms := []string { + "macos", + "ios", + "ipados", + "tvos", + "watchos", + "visionos", + } + + workingPath, err := os.Getwd() + if err != nil { + fmt.Println("Error:", err) + return + } + sourcePath := workingPath + "/../../generate/special/" + + // Go thru every platform + for _, platform := range(platforms) { + sourcePath = sourcePath + platform + sourcePath = sourcePath + "/" + framework + sourcePath = sourcePath + "/special.go" + source, err := os.Open(sourcePath) + if err != nil { + continue // platform not available, move on to the next platform + } + + destPath := workingPath + "/special.go" + destination, err := os.Create(destPath) + + // copy the file to the framework's folder + _, err = io.Copy(destination, source) + if err != nil { + fmt.Println("Error copying file:", err) + return + } + } +} diff --git a/generate/special/macos/foundation/special.go b/generate/special/macos/foundation/special.go new file mode 100644 index 00000000..ed08bc02 --- /dev/null +++ b/generate/special/macos/foundation/special.go @@ -0,0 +1,178 @@ +// Description: where methods that can't work when generated are manually implemented. + +package foundation + +import ( + "reflect" + "unsafe" + + "github.com/progrium/macdriver/objc" +) + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Foundation +#import + +// makes a NSMutableDictionary from two NSArrays + void *MakeMutableDictionaryInternal(void *valuesPtr, void *keysPtr) { + NSArray *keysArray = (NSArray *)keysPtr; + NSArray *valuesArray = (NSArray *)valuesPtr; + NSMutableDictionary *mdict = [[NSMutableDictionary alloc] init]; + for(int i = 0; i < [keysArray count]; i++) { + [mdict setObject:[valuesArray objectAtIndex: i] forKey:[keysArray objectAtIndex: i]]; + } + return mdict; + } + +// makes a NSMutableDictionary from two NSArrays +void *MakeDictionaryInternal(void *valuesPtr, void *keysPtr) { + void *mdict = MakeMutableDictionaryInternal(valuesPtr, keysPtr); + return [NSDictionary dictionaryWithDictionary: mdict]; +} +*/ +import "C" + +// tell the generation system not to generate these methods +/* +Note: fields: class selector note +begin-skip +"NSDictionary", "initWithObjectsAndKeys:", "using custom implementation" +"NSDictionary", "dictionaryWithObjectsAndKeys:", "using custom implementation" +"NSMutableDictionary", "initWithObjectsAndKeys:", "using custom implementation" +"NSMutableDictionary", "dictionaryWithObjectsAndKeys:", "using custom implementation" +"NSArray", "arrayWithObjects:", "using custom implementation" +"NSArray", "initWithObjects:", "using custom implementation" +"NSMutableArray", "arrayWithObjects:", "using custom implementation" +"NSMutableArray", "initWithObjects:", "using custom implementation" +end-skip +*/ + +// create an array for values and a array for keys using one array +func createKeyValueArrays(args []objc.IObject) (unsafe.Pointer, unsafe.Pointer) { + keys := make([]objc.IObject, 0) + values := make([]objc.IObject, 0) + for i := 0; i < len(args); i++ { + if i%2 == 0 { + // if last item is nil + if i == len(args)-1 && args[i] == nil { + continue // remove terminating nil + } + values = append(values, args[i]) + } else { + keys = append(keys, args[i]) + } + } + valuesArray := objc.ToNSArray(reflect.ValueOf(values)) + keysArray := objc.ToNSArray(reflect.ValueOf(keys)) + return keysArray, valuesArray +} + +// Uses key-value pairs to make a dictionary +func makeDictionary(args []objc.IObject) Dictionary { + keysArray, valuesArray := createKeyValueArrays(args) + pointer := C.MakeDictionaryInternal(valuesArray, keysArray) + dict := DictionaryFrom(pointer) + return dict +} + +// Uses key-value pairs to make a mutable dictionary +func makeMutableDictionary(args []objc.IObject) MutableDictionary { + keysArray, valuesArray := createKeyValueArrays(args) + pointer := C.MakeMutableDictionaryInternal(valuesArray, keysArray) + mdict := MutableDictionaryFrom(pointer) + return mdict +} + +/** Dictionary class methods **/ + +// implemented here because Go's nil causes an exception to be thrown +func (a_ Dictionary) InitWithObjectsAndKeys(firstObject objc.IObject, args ...objc.IObject) Dictionary { + args = append([]objc.IObject{firstObject}, args...) + dict := makeDictionary(args) + return dict +} + +// implemented here because Go's nil causes an exception to be thrown +func Dictionary_DictionaryWithObjectsAndKeys(firstObject objc.IObject, args ...objc.IObject) Dictionary { + args = append([]objc.IObject{firstObject}, args...) + dict := makeDictionary(args) + return dict +} + +func NewDictionaryWithObjectsAndKeys(firstObject objc.IObject, args ...objc.IObject) Dictionary { + args = append([]objc.IObject{firstObject}, args...) + dict := makeDictionary(args) + return dict +} + +/** MutableDictionary class methods **/ + +// implemented here because Go's nil causes an exception to be thrown +func (a_ MutableDictionary) InitWithObjectsAndKeys(firstObject objc.IObject, args ...objc.IObject) MutableDictionary { + args = append([]objc.IObject{firstObject}, args...) + mdict := makeMutableDictionary(args) + return mdict +} + +// implemented here because Go's nil causes an exception to be thrown +func MutableDictionary_DictionaryWithObjectsAndKeys(firstObject objc.IObject, args ...objc.IObject) MutableDictionary { + args = append([]objc.IObject{firstObject}, args...) + mdict := makeMutableDictionary(args) + return mdict +} + +func NewMutableDictionaryWithObjectsAndKeys(firstObject objc.IObject, args ...objc.IObject) MutableDictionary { + args = append([]objc.IObject{firstObject}, args...) + mdict := makeMutableDictionary(args) + return mdict +} + +/** Array class methods **/ + +// implemented here because only one object appears in the array otherwise +func Array_ArrayWithObjects(firstObject objc.IObject, args ...objc.IObject) Array { + args = append([]objc.IObject{firstObject}, args...) + arrayPtr := objc.ToNSArray(reflect.ValueOf(args)) + newArray := ArrayFrom(arrayPtr) + return newArray +} + +// implemented here because only one object appears in the array otherwise +func (a_ Array) InitWithObjects(firstObj objc.IObject, args ...objc.IObject) Array { + args = append([]objc.IObject{firstObj}, args...) + arrayPtr := objc.ToNSArray(reflect.ValueOf(args)) + newArray := ArrayFrom(arrayPtr) + return newArray +} + +// implemented here because only one object appears in the array otherwise +func NewArrayWithObjects(firstObj objc.IObject, args ...objc.IObject) Array { + args = append([]objc.IObject{firstObj}, args...) + arrayPtr := objc.ToNSArray(reflect.ValueOf(args)) + newArray := ArrayFrom(arrayPtr) + return newArray +} + +/** MutableArray class methods **/ + +// implemented here because only one object appears in the array otherwise +func MutableArray_ArrayWithObjects(firstObj objc.IObject, args ...objc.IObject) MutableArray { + args = append([]objc.IObject{firstObj}, args...) + mArray := MutableArray_ArrayWithArray(args) + return mArray +} + +// implemented here because only one object appears in the array otherwise +func (a_ MutableArray) InitWithObjects(firstObj objc.IObject, args ...objc.IObject) MutableArray { + args = append([]objc.IObject{firstObj}, args...) + mArray := MutableArray_ArrayWithArray(args) + return mArray +} + +// implemented here because only one object appears in the array otherwise +func NewMutableArrayWithObjects(firstObj objc.IObject, args ...objc.IObject) MutableArray { + args = append([]objc.IObject{firstObj}, args...) + mArray := MutableArray_ArrayWithArray(args) + return mArray +} diff --git a/macos/foundation/array.gen.go b/macos/foundation/array.gen.go index a4a56cae..64ebd6e2 100644 --- a/macos/foundation/array.gen.go +++ b/macos/foundation/array.gen.go @@ -150,20 +150,6 @@ func Array_ArrayWithObject(anObject objc.IObject) Array { return ArrayClass.ArrayWithObject(anObject) } -func (a_ Array) InitWithObjects(firstObj objc.IObject, args ...any) Array { - rv := objc.Call[Array](a_, objc.Sel("initWithObjects:"), append([]any{objc.Ptr(firstObj)}, args...)...) - return rv -} - -// Initializes a newly allocated array by placing in it the objects in the argument list. [Full Topic] -// -// [Full Topic]: https://developer.apple.com/documentation/foundation/nsarray/1460068-initwithobjects?language=objc -func NewArrayWithObjects(firstObj objc.IObject, args ...any) Array { - instance := ArrayClass.Alloc().InitWithObjects(firstObj, args...) - instance.Autorelease() - return instance -} - func (ac _ArrayClass) Array() Array { rv := objc.Call[Array](ac, objc.Sel("array")) return rv @@ -202,18 +188,6 @@ func Array_ArrayWithObjectsCount(objects objc.IObject, cnt uint) Array { return ArrayClass.ArrayWithObjectsCount(objects, cnt) } -func (ac _ArrayClass) ArrayWithObjects(firstObj objc.IObject, args ...any) Array { - rv := objc.Call[Array](ac, objc.Sel("arrayWithObjects:"), append([]any{objc.Ptr(firstObj)}, args...)...) - return rv -} - -// Creates and returns an array containing the objects in the argument list. [Full Topic] -// -// [Full Topic]: https://developer.apple.com/documentation/foundation/nsarray/1460145-arraywithobjects?language=objc -func Array_ArrayWithObjects(firstObj objc.IObject, args ...any) Array { - return ArrayClass.ArrayWithObjects(firstObj, args...) -} - func (ac _ArrayClass) Alloc() Array { rv := objc.Call[Array](ac, objc.Sel("alloc")) return rv diff --git a/macos/foundation/dictionary.gen.go b/macos/foundation/dictionary.gen.go index 09550ebc..6fc46532 100644 --- a/macos/foundation/dictionary.gen.go +++ b/macos/foundation/dictionary.gen.go @@ -89,32 +89,6 @@ func NewDictionaryWithDictionary(otherDictionary Dictionary) Dictionary { return instance } -func (dc _DictionaryClass) DictionaryWithObjectsAndKeys(firstObject objc.IObject, args ...any) Dictionary { - rv := objc.Call[Dictionary](dc, objc.Sel("dictionaryWithObjectsAndKeys:"), append([]any{firstObject}, args...)...) - return rv -} - -// Creates a dictionary containing entries constructed from the specified set of values and keys. [Full Topic] -// -// [Full Topic]: https://developer.apple.com/documentation/foundation/nsdictionary/1574181-dictionarywithobjectsandkeys?language=objc -func Dictionary_DictionaryWithObjectsAndKeys(firstObject objc.IObject, args ...any) Dictionary { - return DictionaryClass.DictionaryWithObjectsAndKeys(firstObject, args...) -} - -func (d_ Dictionary) InitWithObjectsAndKeys(firstObject objc.IObject, args ...any) Dictionary { - rv := objc.Call[Dictionary](d_, objc.Sel("initWithObjectsAndKeys:"), append([]any{firstObject}, args...)...) - return rv -} - -// Initializes a newly allocated dictionary with entries constructed from the specified set of values and keys. [Full Topic] -// -// [Full Topic]: https://developer.apple.com/documentation/foundation/nsdictionary/1574190-initwithobjectsandkeys?language=objc -func NewDictionaryWithObjectsAndKeys(firstObject objc.IObject, args ...any) Dictionary { - instance := DictionaryClass.Alloc().InitWithObjectsAndKeys(firstObject, args...) - instance.Autorelease() - return instance -} - func (d_ Dictionary) Init() Dictionary { rv := objc.Call[Dictionary](d_, objc.Sel("init")) return rv diff --git a/macos/foundation/mutable_array.gen.go b/macos/foundation/mutable_array.gen.go index af1fa944..6b1760ca 100644 --- a/macos/foundation/mutable_array.gen.go +++ b/macos/foundation/mutable_array.gen.go @@ -161,20 +161,6 @@ func MutableArray_ArrayWithObject(anObject objc.IObject) MutableArray { return MutableArrayClass.ArrayWithObject(anObject) } -func (m_ MutableArray) InitWithObjects(firstObj objc.IObject, args ...any) MutableArray { - rv := objc.Call[MutableArray](m_, objc.Sel("initWithObjects:"), append([]any{objc.Ptr(firstObj)}, args...)...) - return rv -} - -// Initializes a newly allocated array by placing in it the objects in the argument list. [Full Topic] -// -// [Full Topic]: https://developer.apple.com/documentation/foundation/nsarray/1460068-initwithobjects?language=objc -func NewMutableArrayWithObjects(firstObj objc.IObject, args ...any) MutableArray { - instance := MutableArrayClass.Alloc().InitWithObjects(firstObj, args...) - instance.Autorelease() - return instance -} - func (mc _MutableArrayClass) Array() MutableArray { rv := objc.Call[MutableArray](mc, objc.Sel("array")) return rv @@ -213,18 +199,6 @@ func MutableArray_ArrayWithObjectsCount(objects objc.IObject, cnt uint) MutableA return MutableArrayClass.ArrayWithObjectsCount(objects, cnt) } -func (mc _MutableArrayClass) ArrayWithObjects(firstObj objc.IObject, args ...any) MutableArray { - rv := objc.Call[MutableArray](mc, objc.Sel("arrayWithObjects:"), append([]any{objc.Ptr(firstObj)}, args...)...) - return rv -} - -// Creates and returns an array containing the objects in the argument list. [Full Topic] -// -// [Full Topic]: https://developer.apple.com/documentation/foundation/nsarray/1460145-arraywithobjects?language=objc -func MutableArray_ArrayWithObjects(firstObj objc.IObject, args ...any) MutableArray { - return MutableArrayClass.ArrayWithObjects(firstObj, args...) -} - // Removes from the receiving array the objects in another given array. [Full Topic] // // [Full Topic]: https://developer.apple.com/documentation/foundation/nsmutablearray/1413942-removeobjectsinarray?language=objc diff --git a/macos/foundation/mutable_dictionary.gen.go b/macos/foundation/mutable_dictionary.gen.go index 8f813dce..aab3b1c5 100644 --- a/macos/foundation/mutable_dictionary.gen.go +++ b/macos/foundation/mutable_dictionary.gen.go @@ -150,32 +150,6 @@ func NewMutableDictionaryWithDictionary(otherDictionary Dictionary) MutableDicti return instance } -func (mc _MutableDictionaryClass) DictionaryWithObjectsAndKeys(firstObject objc.IObject, args ...any) MutableDictionary { - rv := objc.Call[MutableDictionary](mc, objc.Sel("dictionaryWithObjectsAndKeys:"), append([]any{firstObject}, args...)...) - return rv -} - -// Creates a dictionary containing entries constructed from the specified set of values and keys. [Full Topic] -// -// [Full Topic]: https://developer.apple.com/documentation/foundation/nsdictionary/1574181-dictionarywithobjectsandkeys?language=objc -func MutableDictionary_DictionaryWithObjectsAndKeys(firstObject objc.IObject, args ...any) MutableDictionary { - return MutableDictionaryClass.DictionaryWithObjectsAndKeys(firstObject, args...) -} - -func (m_ MutableDictionary) InitWithObjectsAndKeys(firstObject objc.IObject, args ...any) MutableDictionary { - rv := objc.Call[MutableDictionary](m_, objc.Sel("initWithObjectsAndKeys:"), append([]any{firstObject}, args...)...) - return rv -} - -// Initializes a newly allocated dictionary with entries constructed from the specified set of values and keys. [Full Topic] -// -// [Full Topic]: https://developer.apple.com/documentation/foundation/nsdictionary/1574190-initwithobjectsandkeys?language=objc -func NewMutableDictionaryWithObjectsAndKeys(firstObject objc.IObject, args ...any) MutableDictionary { - instance := MutableDictionaryClass.Alloc().InitWithObjectsAndKeys(firstObject, args...) - instance.Autorelease() - return instance -} - func (mc _MutableDictionaryClass) DictionaryWithObjectsForKeys(objects []objc.IObject, keys []PCopying) MutableDictionary { rv := objc.Call[MutableDictionary](mc, objc.Sel("dictionaryWithObjects:forKeys:"), objects, keys) return rv