Skip to content

Commit

Permalink
fix nil issue with several array and dictionary methods
Browse files Browse the repository at this point in the history
  • Loading branch information
programmingkidx committed Jan 27, 2024
1 parent e468a64 commit 80f9c1c
Show file tree
Hide file tree
Showing 6 changed files with 334 additions and 104 deletions.
156 changes: 156 additions & 0 deletions generate/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"log"
"strings"
"io"
"os"

"github.com/progrium/macdriver/generate/codegen"
"github.com/progrium/macdriver/generate/declparse"
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
}
}
}
178 changes: 178 additions & 0 deletions generate/special/macos/foundation/special.go
Original file line number Diff line number Diff line change
@@ -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 <Foundation/Foundation.h>
// 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 {

Check failure on line 72 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-11)

undefined: Dictionary

Check failure on line 72 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-12)

undefined: Dictionary

Check failure on line 72 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-13)

undefined: 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 {

Check failure on line 80 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-11)

undefined: MutableDictionary

Check failure on line 80 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-12)

undefined: MutableDictionary

Check failure on line 80 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-13)

undefined: 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 {

Check failure on line 90 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-11)

undefined: Dictionary

Check failure on line 90 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-12)

undefined: Dictionary

Check failure on line 90 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-13)

undefined: 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 {

Check failure on line 97 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-11)

undefined: Dictionary

Check failure on line 97 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-12)

undefined: Dictionary

Check failure on line 97 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-13)

undefined: Dictionary
args = append([]objc.IObject{firstObject}, args...)
dict := makeDictionary(args)
return dict
}

func NewDictionaryWithObjectsAndKeys(firstObject objc.IObject, args ...objc.IObject) Dictionary {

Check failure on line 103 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-11)

undefined: Dictionary

Check failure on line 103 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-12)

undefined: Dictionary

Check failure on line 103 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-13)

undefined: 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 {

Check failure on line 112 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-11)

undefined: MutableDictionary

Check failure on line 112 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-12)

undefined: MutableDictionary

Check failure on line 112 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-13)

undefined: 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 {

Check failure on line 119 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-11)

undefined: MutableDictionary

Check failure on line 119 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-12)

undefined: MutableDictionary

Check failure on line 119 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-13)

undefined: MutableDictionary
args = append([]objc.IObject{firstObject}, args...)
mdict := makeMutableDictionary(args)
return mdict
}

func NewMutableDictionaryWithObjectsAndKeys(firstObject objc.IObject, args ...objc.IObject) MutableDictionary {

Check failure on line 125 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-11)

undefined: MutableDictionary

Check failure on line 125 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-12)

undefined: MutableDictionary

Check failure on line 125 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-13)

undefined: 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 {

Check failure on line 134 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-11)

undefined: Array

Check failure on line 134 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-12)

undefined: Array

Check failure on line 134 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-13)

undefined: 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 {

Check failure on line 142 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-11)

undefined: Array

Check failure on line 142 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-12)

undefined: Array

Check failure on line 142 in generate/special/macos/foundation/special.go

View workflow job for this annotation

GitHub Actions / test (oldstable, macos-13)

undefined: 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
}
26 changes: 0 additions & 26 deletions macos/foundation/array.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 80f9c1c

Please sign in to comment.