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 nil issue with several Array and Dictionary methods #247

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
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
Loading