Compare commits
	
		
			No commits in common. "acc8cab583f093a07f119f01b5bdbea3f3084bef" and "4aa1211d7346ac96351840e67a020c5fcdbbc953" have entirely different histories.
		
	
	
		
			acc8cab583
			...
			4aa1211d73
		
	
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -16,5 +16,3 @@ examples/gc/ns | |||
| examples/functions/functions | ||||
| examples/functions/ns | ||||
| ns-old | ||||
| examples/strings/strings | ||||
| examples/strings/ns | ||||
|  |  | |||
							
								
								
									
										401
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										401
									
								
								README.md
									
									
									
									
									
								
							|  | @ -62,8 +62,6 @@ pragma [ clang diagnostic ignored "-Wformat-security" ] | |||
| 
 | ||||
| Regular expressions are permitted in the names of classes, functions, | ||||
| protocols and protocol methods, overridden superclass methods, and enums. | ||||
| Since the `NSObject` class is necessary for memory management, NSWrap will | ||||
| automatically include it if it is encountered in an input header file. | ||||
| 
 | ||||
| When invoked, NSWrap creates a subdirectory with the name of the package | ||||
| as specified in `nswrap.yaml` or, by default, `ns` if a package name is not | ||||
|  | @ -133,14 +131,14 @@ For example, `NSString` provides the folowing `compare` methods: | |||
| These are translated into Go as: | ||||
| 
 | ||||
| ```go | ||||
| func (o *NSString) Compare(string *NSString) NSComparisonResult { } | ||||
| func (o NSString) Compare(string NSString) NSComparisonResult { } | ||||
| 
 | ||||
| func (o *NSString) CompareOptions(string *NSString, mask NSStringCompareOptions) NSComparisonResult { } | ||||
| func (o NSString) CompareOptions(string NSString, mask NSStringCompareOptions) NSComparisonResult { } | ||||
| 
 | ||||
| func (o *NSString) CompareOptionsRange(string *NSString, mask NSStringCompareOptions, | ||||
| func (o NSString) CompareOptionsRange(string NSString, mask NSStringCompareOptions, | ||||
| 	rangeOfReceiverToCompare NSRange) NSComparisonResult { } | ||||
| 
 | ||||
| func (o *NSString) CompareOptionsRangeLocale(string *NSString, mask NSStringCompareOptions, | ||||
| func (o NSString) CompareOptionsRangeLocale(string NSString, mask NSStringCompareOptions, | ||||
| 	rangeOfReceiverToCompare NSRange, locale NSObject) NSComparisonResult { } | ||||
| ``` | ||||
| 
 | ||||
|  | @ -158,45 +156,11 @@ fmt.Printf("%s\n",str) | |||
| NSWrap creates a `Char` Go type that is equivalent to a C `char`. A pointer to | ||||
| `Char` in Go code can therefore be used with Objective-C functions and methods | ||||
| that take a `char*` parameter. | ||||
| 
 | ||||
| When NSWrap binds a method that returns `*Char` (and is in garbage collected mode, | ||||
| the default), it first calls `strdup` | ||||
| on the output of the underlying Objective-C method. Therefore, the returned | ||||
| pointer is manually allocated and will need to be freed later from Go. NSWrap | ||||
| creates a | ||||
| `(*Char).Free()` method for use when these pointers are no longer needed. | ||||
| This copying is necessary because the Objective-C runtime will sometimes | ||||
| return pointers to internal objects that are impossible to manage from the | ||||
| Go side. NSWrap aims to cause any internal objects to be deallocated as soon | ||||
| as possible so they do not cause memory leaks. This means that any returned | ||||
| C strings need to be copied and memory managed manually from the Go side. | ||||
| 
 | ||||
| NSWrap provides the helper functions `CharWithGoString` and `CharWithBytes` | ||||
| that take, respectively, Go strings and Go byte arrays (`[]byte`) and return | ||||
| `*Char` in Go. As demonstrated above, NSWrap also provides `String()` | ||||
| methods so that the `*Char` and `*NSString` types implement the `Stringer` | ||||
| Go interface and therefore can be sent directly to functions like `fmt.Printf`. | ||||
| The `String()` method on `*NSString` creates a temporary `*Char` internally | ||||
| but frees it for you before returning. Since methods returning | ||||
| `*Char` return a pointer that needs to be manually freed, it is important | ||||
| to use these properly in order to avoid leaks: | ||||
| 
 | ||||
| ```go | ||||
| nst := ns.NSStringWithGoString("one string") | ||||
| 
 | ||||
| // NO: the next line leaks a *Char (UTF8String) | ||||
| //mygostring := nst.UTF8String().String() | ||||
| 
 | ||||
| // OK: NSWrap creates a temporary *Char and frees it for you: | ||||
| mygostring := nst.String() | ||||
| 
 | ||||
| // ALSO OK: manually free your own temporary *Char: | ||||
| mytmpchar := nst.UTF8String() | ||||
| mygostring = mytmpchar.String() | ||||
| mytmpchar.Free() | ||||
| ``` | ||||
| In most cases it will be more convenient to convert directly to Go strings instead | ||||
| of `*Char`. | ||||
| `*Char` in Go. As demonstrated above, NSWrap also provides a `String()` | ||||
| methods so that the `*Char` and `NSString` types implement the `Stringer` | ||||
| Go interface. | ||||
| 
 | ||||
| ## Working With NSObject and its Descendants | ||||
| 
 | ||||
|  | @ -207,23 +171,14 @@ follows: | |||
| type Id struct { | ||||
| 	ptr unsafe.Pointer | ||||
| } | ||||
| func (o *Id) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr } | ||||
| func (o Id) Ptr() unsafe.Pointer { return o.ptr } | ||||
| 
 | ||||
| type NSObject interface { | ||||
| 	Ptr() unsafe.Pointer | ||||
| } | ||||
| ``` | ||||
| Other object types in Go are structs that directly or indirectly embed `Id` | ||||
| and therefore contain an `unsafe.Pointer` to an Objective-C object, and | ||||
| implement `NSObject` by inheriting the `Ptr()` method. | ||||
| 
 | ||||
| Because of this implementation, you will note that every Objective-C object | ||||
| is represented by at least two pointers -- an underlying pointer to the  | ||||
| Objective-C object | ||||
| in CGo memory (allocated by the Objective-C runtime), as well as a pointer | ||||
| allocated by the Go runtime to an `Id` type, or to another type that directly | ||||
| or indirectly embeds `Id`. This "dual pointer" approach is necessary to ensure | ||||
| that memory management can be made to work correctly (see below for details). | ||||
| and therefore implement `NSObject`. | ||||
| 
 | ||||
| * The NSObject Interface | ||||
| 
 | ||||
|  | @ -235,7 +190,7 @@ embeds `Id` to be used with generic Objective-C functions. For example: | |||
| 
 | ||||
| ```go | ||||
| o1 := ns.NSStringWithGoString("my string") | ||||
| s1 := ns.NSSetWithObjects(o1) | ||||
| s1 := ns.NSSetWithOBjects(o1) | ||||
| a := ns.NSMutableArrayWithObjects(o1,s1) | ||||
| ``` | ||||
| Since `NSString` and `NSSet` in Go both implement the `NSObject` interface, | ||||
|  | @ -249,73 +204,70 @@ implements the required delegate protocol. | |||
| 
 | ||||
| * Inheritance | ||||
| 
 | ||||
| Objective-C allows single inheritance. NSWrap automatically adds | ||||
| inherited methods to classes that are includled in your binding. | ||||
| 
 | ||||
| Types created by NSWrap also "embed" their parent class. For example, top | ||||
| level objects that inherit from `NSObject` in Objective-C | ||||
| Objective-C only provides single inheritance. In Go, this is modeled using | ||||
| embedding. Top level objects that inherit from `NSObject` in Objective-C | ||||
| embed the Go type `Id` and therefore implement the `NSObject` Go interface. | ||||
| Other objects embed their direct superclass. For example: | ||||
| Other objects embed their superclass. For example: | ||||
| 
 | ||||
| ```go | ||||
| type NSArray struct { Id } | ||||
| func (o *NSArray) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr } | ||||
| func (o *Id) NSArray() *NSArray { | ||||
| 	return (*NSArray)(unsafe.Pointer(o)) | ||||
| func (o NSArray) Ptr() unsafe.Pointer { return o.ptr } | ||||
| func (o Id) NSArray() NSArray { | ||||
| 	ret := NSArray{} | ||||
| 	ret.ptr = o.ptr | ||||
| 	return ret | ||||
| } | ||||
| 
 | ||||
| type NSMutableArray struct { NSArray } | ||||
| func (o *NSMutableArray) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr } | ||||
| func (o *Id) NSMutableArray() *NSMutableArray { | ||||
|         return (*NSMutableArray)(unsafe.Pointer(o)) | ||||
| } | ||||
| func (o NSMutableArray) Ptr() unsafe.Pointer { return o.ptr } | ||||
| func (o Id) NSMutableArray() NSMutableArray {...} | ||||
| ``` | ||||
| 
 | ||||
| Observe: | ||||
| ```go | ||||
| b := ns.NSButtonAlloc()        // NSButton > NSControl > NSView > NSResponder > NSObject | ||||
| b.InitWithFrame(ns.NSMakeRect(100,100,200,200)) | ||||
| b.SetTitle(nst("PUSH")) | ||||
| b.InitWithFrame(ns.NSMakeRect(100,100,200,200)) // Method of NSView | ||||
| b.SetTitle(nst("PUSH"))                         // Method of NSButton | ||||
| vw := win.ContentView() | ||||
| vw.AddSubview(&b.NSView)	// Pass the button's embedded NSView | ||||
| vw.AddSubview(b.NSView)				// Pass the button's embedded NSView | ||||
| ``` | ||||
| In Go, `NSButtonAlloc` returns a Go object of type `ns.NSButton`. However, | ||||
| the `initWithFrame` method is defined in AppKit for `NSView`. NSWrap will find this | ||||
| method and add it to the Go `NSButton` type when creating your wrapper because | ||||
| `NSButton` inherits from `NSControl` which inherits from `NSView`. | ||||
| there is no `InitWithFrame` method for receivers of this type. This is | ||||
| not necessary because `NSButton` embeds `NSControl` which in turn embeds | ||||
| `NSView`. The `InitWithFrame` method only needs to be implemented for `NSView` | ||||
| receivers. Go will automatically find the indirectly embedded `NSView` and | ||||
| call the right method. | ||||
| 
 | ||||
| As of this | ||||
| writing, on MacOS 10.13.6, NSWrap binds 115 instance methods for `NSObject`, | ||||
| so things like `Hash()`, `IsEqualTo()`, `ClassName()`, `RespondsToSelector()` | ||||
| Note that, since `InitWithFrame()` is defined only for `NSView` and returns | ||||
| an `NSView` type, | ||||
| the following will not work. Look out for this if you like to chain your | ||||
| `Alloc` and `Init` methods and are getting type errors: | ||||
| 
 | ||||
| ```go | ||||
| //DO NOT DO THIS -- InitWithFrame returns NSView, not NSButton | ||||
| b := ns.NSButtonAlloc().InitWithFrame(ns.MakeRect(100,100,200,200)) | ||||
| ``` | ||||
| 
 | ||||
| Go has no trouble finding embedded methods for your `NSButton` and will | ||||
| happily search up the chain through `NSControl`, `NSView`, `NSResponder` and | ||||
| `NSObject` and all of their associated protocols and categories. As of this | ||||
| writing, on MacOS 10.13.6, NSWrap binds 90 instance methods for `NSObject`, | ||||
| so things like `Hash()`, `IsEqualTo()`, `ClassName()`, `RespondsToSelector` | ||||
| and many many others are available and can be called on any object directly | ||||
| from Go. | ||||
| 
 | ||||
| All objects implement the `NSObject` interface, but from time to time you | ||||
| will encounter a method that takes a parameter of a different type that may | ||||
| not exactly match the type you have. For example, if you want to pass your | ||||
| `NSButton` as a parameter to a method that accepts an `NSView` type, you need | ||||
| to explicitly pass its embedded `NSView` (`&b.NSView` in the example above). | ||||
| This approach is safer than "converting" the button to an `NSView` (see below) | ||||
| because it will only work on objects that directly or indirectly embed an | ||||
| `NSView` Go type. | ||||
| Go does not perform the same type | ||||
| magic when you use variables as function or method parameters. | ||||
| If you want to pass your `NSButton` as a parameter to a method that accepts | ||||
| an `NSView` type, you need to explicitly pass the embedded `NSView` | ||||
| (`b.NSView` in the example above). | ||||
| 
 | ||||
| NSWrap creates a method for `Id` allowing objects to be converted | ||||
| at run-time to any other class. You will need this for Enumerators and | ||||
| functions like `NSArray`'s `GetObjects`, for example, | ||||
| which always return `*Id`. Make | ||||
| at run-time to any other class. You will need this for Enumerators, which | ||||
| always return `Id`. See below under Enumerators for an example, but make | ||||
| sure you know (or test) what type your objects are before converting them. | ||||
| You can implement a version of a Go type switch this way: | ||||
| 
 | ||||
| ```go | ||||
| switch { | ||||
| case o.IsKindOfClass(ns.NSStringClass()): | ||||
|         // do something with o.NSString() | ||||
| case o.IsKindOfClass(ns.NSSetClass()): | ||||
|         // do something with o.NSSet() | ||||
| default: | ||||
|         ... | ||||
| } | ||||
| ``` | ||||
| You can | ||||
| implement a somewhat less convenient version of a Go type switch this way. | ||||
| 
 | ||||
| Because `Id` can be converted to any type, and every object in the Foundation | ||||
| classes inherits from `Id`, it is possible to send any message to any | ||||
|  | @ -362,12 +314,11 @@ functions. | |||
| Pointers to pointers are sometimes passed to Objective-C methods or functions | ||||
| as a way of receiving output from those functions, especially because | ||||
| Objective-C does not allow for multiple return values. In those cases, after | ||||
| the CGo call, the method parameter will be treated as an array of | ||||
| the CGo call, the method parameter will be treated as a nil-terminated array of | ||||
| object pointers that may have been modified by the Objective-C function or | ||||
| method. NSWrap will copy the object pointers back into the input Go slice, up | ||||
| to its capacity (which will never be changed). The input Go slice is then | ||||
| truncated to the appropriate length. If there is no output, the length will | ||||
| be set to 0. | ||||
| truncated to the appropriate length. | ||||
| 
 | ||||
| An example in Core Foundation is the `getObjects:andKeys:count` method for | ||||
| `NSDictionary`: | ||||
|  | @ -378,31 +329,28 @@ An example in Core Foundation is the `getObjects:andKeys:count` method for | |||
|                 ns.NSArrayWithObjects(nst("obj1"),nst("obj2")), | ||||
|                 ns.NSArrayWithObjects(nst("key1"),nst("key2")), | ||||
|         ) | ||||
|         va,ka := make([]*ns.Id,0,5), make([]*ns.Id,0,5)  // length 0, capacity 5 slices | ||||
|         dict.GetObjects(&va,&ka,5) | ||||
| 	// last parameter to GetObjects is the count, must be less than or equal to the input slice capacity | ||||
|         fmt.Printf("Length of va is now %d\n",len(va)) // va and ka slices are now length = 2 | ||||
|         for i,k := range ka { | ||||
|                 fmt.Printf("-- %s -> %s\n",k.NSString(),va[i].NSString()) | ||||
|         os,ks := make([]ns.Id,0,5), make([]ns.Id,0,5)  // length 0, capacity 5 slices | ||||
|         dict.GetObjects(&os,&ks,5) | ||||
| 	// last parameter is the count, must be less than or equal to the input slice capacity | ||||
|         fmt.Printf("Length of os is now %d\n",len(os)) // os and ks slices are now length = 2 | ||||
|         for i,k := range ks { | ||||
|                 fmt.Printf("-- %s -> %s\n",k.NSString(),os[i].NSString()) | ||||
|         } | ||||
| ``` | ||||
| 
 | ||||
| NSWrap will not check the "count" parameter, so the user will always need | ||||
| NSWrap will never check the "count" parameter, so the user will always need | ||||
| to make sure it is less than or equal to the capacity of the input | ||||
| Go slices. | ||||
| 
 | ||||
| Using pointers to pointers is necessary in many Core Foundation situations | ||||
| where you need to get an error message out of a function or method, or in other | ||||
| cases where an Objective-C method wants to provide multiple return values. | ||||
| where you need to get an error message out of a function or method. | ||||
| Here is an example using `[NSString stringWithContentsOfURL...]`: | ||||
| 
 | ||||
| ```go | ||||
|         err := make([]*ns.NSError,1) | ||||
|         n1 = ns.NSStringWithContentsOfURLEncoding(ns.NSURLWithGoString("htttypo://example.com"), 0, &err) | ||||
| 	if len(err) > 0 { | ||||
|         	fmt.Printf("err: %s\n",err[0].LocalizedDescription()) | ||||
|         err := make([]ns.NSError,1) | ||||
|         n1 = ns.NSStringWithContentsOfURLEncoding(ns.NSURLWithGoString("htttypo://example.com"),0,&err) | ||||
|         fmt.Printf("err: %s\n",err[0].LocalizedDescription()) | ||||
| //err: The file couldn’t be opened because URL type htttypo isn’t supported. | ||||
| 	} | ||||
| ``` | ||||
| 
 | ||||
| ## Selectors | ||||
|  | @ -422,17 +370,15 @@ appMenu.AddItemWithTitle( | |||
| ## Enumerators | ||||
| 
 | ||||
| NSWrap provides a `ForIn` method for the `NSEnumerator` type. Call it with a | ||||
| `func(*ns.Id) bool` parameter that returns `true` to continue and `false` to | ||||
| `func(ns.Id) bool` parameter that returns `true` to continue and `false` to | ||||
| stop the enumeration. | ||||
| 
 | ||||
| ```go | ||||
| a := ns.NSArrayWithObjects(o1,o2,o3) | ||||
| i := 0 | ||||
| a.ObjectEnumerator().ForIn(func (o *ns.Id) bool { | ||||
| a.ObjectEnumerator().ForIn(func (o ns.Id) bool { | ||||
| 	switch { | ||||
| 	case o.IsKindOfClass(ns.NSStringClass()): | ||||
| 		fmt.Printf("%d: %s\n", i, o.NSString()) | ||||
| 		i++ | ||||
| 		fmt.Println(o.NSString().UTF8String()) | ||||
| 		return true  // continue enumeration | ||||
| 	default: | ||||
| 		fmt.Println("Unknown class") | ||||
|  | @ -446,7 +392,7 @@ identification. | |||
| 
 | ||||
| ## Enum Definitions | ||||
| 
 | ||||
| NSWrap translates C `enum` values into Go constants. The enums you want are | ||||
| NSWrap translates C `enum` values into Go constants. The enums you need are | ||||
| specified in `nswrap.yaml` by regular expression, which, in the case of named | ||||
| enums, must match the name of the `enum` itself, or in the case of anonymous | ||||
| enums, must match the name of the constant(s) you are looking for as declared | ||||
|  | @ -480,6 +426,70 @@ const _CLOCK_MONOTONIC_RAW  = C._CLOCK_MONOTONIC_RAW | |||
| ... | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ## Memory management | ||||
| 
 | ||||
| Objective-C objects are always allocated and returned from CGo code, and | ||||
| therefore these pointers are not garbage collected by Go. You can use any | ||||
| of the standard Objective-C memory management techniques for those pointers, | ||||
| which seem to work but have not been extensively tested. | ||||
| 
 | ||||
| Since everything inherits methods from `NSObject`, you can call `Retain()`, | ||||
| `Release()` and `Autorelease()` on any object. | ||||
| 
 | ||||
| If the autorelease configuration directive is set to "true", all allocation functions | ||||
| created by NSWrap (i.e. those ending in `Alloc`) will call `autorelease` before they | ||||
| return an object. Alternately, objects can be manually sent an autorelease message. | ||||
| If you are not working in an environment (such as an | ||||
| Application Delegate callback) that provides an autorelease pool, you can | ||||
| create your own: | ||||
| 
 | ||||
| * Work directly with `NSAutoreleasePool` objects | ||||
| 
 | ||||
| ```go | ||||
| swamp := ns.NSAutoreleasePoolAlloc().Init() | ||||
| del := ns.AppDelegateAlloc() | ||||
| //del.Autorelease() // if autorelease: true is not set in nswrap.yaml | ||||
| menu := ns.NSMenuAlloc().InitWithTitle(nst("Main")) | ||||
| //menu.Autorelease() | ||||
| str := ns.NSStringWithGoString("these objects will be automatically deallocated when swamp is drained.") | ||||
| ... | ||||
| swamp.Drain() | ||||
| ``` | ||||
| 
 | ||||
| * ...or use the `AutoreleasePool()` helper function | ||||
| 
 | ||||
| NSWrap provides a helper function that can be passed a `func()` with no | ||||
| parameters or return value. It is conventient to give it an anonymous function | ||||
| and write your code in line, just like you would if you were using an | ||||
| `@autoreleasepool { }` block. | ||||
| 
 | ||||
| ```go | ||||
| ns.AutoreleasePool(func() { | ||||
| 	a := MyObjectAlloc().Init() | ||||
| 	b := MyOtherObjectAlloc().Init() | ||||
| 	... | ||||
| }) | ||||
| ``` | ||||
| 
 | ||||
| You will need to make sure `NSAutoreleasePool` is included in the `classes` | ||||
| directive in your configuration file before working with | ||||
| `NSAutoreleasePool` objects or the `AutoreleasePool` helper function. | ||||
| 
 | ||||
| * Pitfalls | ||||
| 
 | ||||
| Go concurrency does not play well with Objective-C memory management. In particular, | ||||
| an AutoreleasePool needs to be allocated and drained from the same thread, and | ||||
| only objects allocated within that thread will be drained. Objects allocated and | ||||
| autoreleased from a different goroutine in the same thread are at risk of being | ||||
| prematurely drained. Therefore, you should only work with one AutoreleasePool at | ||||
| a time, and only within a thread that is locked to the OS thread | ||||
| (by calling `runtime.LockOSThread()`). If you will be allocating Objective-C objects | ||||
| from multiple goroutines, it is best not to use the `autorelease: true` directive | ||||
| as that will cause all objects to receive an `autorelease` message even if they are | ||||
| created outside the thread where are using your autorelease pool. | ||||
| See `examples/memory` for some basic tests and read the comments to larn what to avoid. | ||||
| 
 | ||||
| ## Delegates | ||||
| 
 | ||||
| The `delegates` directive in `nswrap.yaml` creates a new Objective-C | ||||
|  | @ -521,8 +531,7 @@ registered, | |||
| it will be called with all of its parameters converted to their Go type | ||||
| equivalents. User-defined callbacks are registered by calling a function | ||||
| with the method name in TitleCase + `Callback`, so in the example above, | ||||
| if your delegate was named `del`, you would call | ||||
| `del.CentralManagerDidUpdateStateCallback(...)` with the name of | ||||
| you would call `ns.CentralManagerDidUpdateStateCallback(...)` with the name of | ||||
| your callback function to register to receive notifications when your central | ||||
| manager updates its state. | ||||
| 
 | ||||
|  | @ -538,16 +547,11 @@ func cb(c ns.CBCentralManager) { | |||
| 	... | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	del *ns.CBDelegate // use global variables so these don't get garbage collected | ||||
| 	cm *ns.CBCentralManager | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	... | ||||
| 	del = ns.CBDelegateAlloc() | ||||
| 	del := ns.CBDelegateAlloc() | ||||
| 	del.CentralManagerDidUpdateStateCallback(cb) | ||||
| 	cm = ns.CBCentralManagerAlloc().InitWithDelegateQueue(del,queue) | ||||
| 	cm := ns.CBCentralManagerAlloc().InitWithDelegateQueue(del,queue) | ||||
| ``` | ||||
| 
 | ||||
| When you provide user-defined callback functions, you will need to specify | ||||
|  | @ -558,12 +562,12 @@ messages will point you in the right direction. | |||
| 
 | ||||
| ``` | ||||
| $ go build | ||||
| ./main.go:127:43: cannot use didFinishLaunching (type func(*ns.NSNotification, bool)) as type | ||||
| func(*ns.NSNotification) in argument to del.ApplicationDidFinishLaunchingCallback | ||||
| ./main.go:127:43: cannot use didFinishLaunching (type func(ns.NSNotification, bool)) as type | ||||
| func(ns.NSNotification) in argument to del.ApplicationDidFinishLaunchingCallback | ||||
| ``` | ||||
| In the above example, the build failed because an extra `bool` parameter was | ||||
| included in the callback function. The compiler is telling you that the right | ||||
| type for the callback is `func(*ns.NSNotification)` with no return value. | ||||
| type for the callback is `func(ns.NSNotification)` with no return value. | ||||
| 
 | ||||
| ## Working with AppKit | ||||
| 
 | ||||
|  | @ -572,7 +576,8 @@ Delegate. This allows you to build a Cocoa application entirely in Go. | |||
| 
 | ||||
| Because AppKit uses thread local storage, you will need to make sure all | ||||
| calls into it are done from the main OS thread. This can be a challenge in | ||||
| Go and you will want to make use of `runtime.LockOSThread()`. | ||||
| Go even though runtime.LockOSThread() is supposed to provide | ||||
| this functionality. | ||||
| 
 | ||||
| This is actually a full working Cocoa application: | ||||
| 
 | ||||
|  | @ -613,30 +618,24 @@ import ( | |||
| 	"git.wow.st/gmp/nswrap/examples/app/ns" // point to your own NSWrap output directory | ||||
| ) | ||||
| 
 | ||||
| func didFinishLaunching(n *ns.NSNotification) { | ||||
| func didFinishLaunching(n ns.NSNotification) { | ||||
| 	fmt.Println("Go: did finish launching!") | ||||
| } | ||||
| 
 | ||||
| func shouldTerminate(s *ns.NSApplication) ns.BOOL { | ||||
| func shouldTerminate(s ns.NSApplication) ns.BOOL { | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	a *ns.NSApplication // global vars so these are not garbage collected | ||||
| 	del *ns.AppDelegate | ||||
| 	win *ns.NSWindow | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	runtime.LockOSThread() | ||||
| 	a = ns.NSApplicationSharedApplication() | ||||
| 	a := ns.NSApplicationSharedApplication() | ||||
| 	a.SetActivationPolicy(ns.NSApplicationActivationPolicyRegular) | ||||
| 	del = ns.AppDelegateAlloc() | ||||
| 	del := ns.AppDelegateAlloc() | ||||
| 	del.ApplicationDidFinishLaunchingCallback(didFinishLaunching) | ||||
| 	del.ApplicationShouldTerminateAfterLastWindowClosedCallback(shouldTerminate) | ||||
| 	a.SetDelegate(del) | ||||
| 
 | ||||
| 	win = ns.NSWindowAlloc().InitWithContentRectStyleMask( | ||||
| 	win := ns.NSWindowAlloc().InitWithContentRectStyleMask( | ||||
| 		ns.NSMakeRect(200,200,600,600), | ||||
| 		ns.NSWindowStyleMaskTitled | ns.NSWindowStyleMaskClosable, | ||||
| 		ns.NSBackingStoreBuffered, | ||||
|  | @ -648,7 +647,7 @@ func main() { | |||
| } | ||||
| ``` | ||||
| 
 | ||||
| Pretty simple right? Not really, NSWrap just generated over 39,000 lines of | ||||
| Pretty simple right? Not really, NSWrap just generated almost 15,000 lines of | ||||
| code. See `examples/app` for a slightly more complex example with working | ||||
| menus, visual format-based auto layout, and a custom button class. | ||||
| 
 | ||||
|  | @ -668,7 +667,7 @@ subclasses: | |||
|     yourClass:                  # the superclass to inherit from | ||||
|       - init.*                  # what methods to override | ||||
|       - -(void)hi_there:(int)x  # Objective-C prototype of your new method(s) | ||||
| #       \--the initial hyphen indicates that this is an instance method | ||||
| #       |--this hyphen indicates that this is an instance method | ||||
| ``` | ||||
| 
 | ||||
| In the example above, your new class will be named `myClass` in Objective-C | ||||
|  | @ -729,109 +728,9 @@ Later on you can add your new button to a view and tell Cocoa where to lay | |||
| it out. It's all a little verbose, but that's because for some reason you | ||||
| decided to write Objective-C code in Go. | ||||
| 
 | ||||
| ## Memory management | ||||
| 
 | ||||
| As mentioned above, NSWrap is designed for there to be at least one Go pointer | ||||
| associated with each underlying Objective-C object pointer. | ||||
| Since Objective-C memory | ||||
| is always allocated by the Objective-C runtime, it is not possible for the Go | ||||
| runtime to have visibility into these memory regions or to directly manage memory | ||||
| used by the CGo code. However, Go will keep track of the associated Go pointer | ||||
| that was created the first time the corresponding Objective-C object was passed | ||||
| over to the Go side and an `Id` or other NSWrap struct type was allocated. | ||||
| Because of this, | ||||
| it is possible to hook into the Go garbage collection system in an attempt to | ||||
| manage Objective-C memory strictly from the Go side. When there are no remaining Go | ||||
| pointers to an NSWrap `Id` struct, it will be deallocated by the Go garbage collector | ||||
| and a finalizer will be called that `release`es the corresponding Objective-C | ||||
| object. | ||||
| 
 | ||||
| The memory management rules work as follows: | ||||
| 
 | ||||
| * Objects in Go are represented by pointers to types that implement the `NSObject` | ||||
| interface | ||||
| * NSObject has one method, `Ptr()`, which returns an `unsafe.Pointer` to an  | ||||
| Objective-C object. | ||||
| * All methods that return objects to Go call `retain` except for `new`, `init`, | ||||
| `alloc`, `copy` and `mutableCopy`, which already return retained objects from the | ||||
| Objective-C runtime. | ||||
| * Go wrappers for Objective-C methods call `runtime.SetFinalizer()`, which calls | ||||
| `release` when the associated Go struct is garbage collected. | ||||
| * All Objective-C methods are run inside an `@autoreleasepool {}` block to prevent | ||||
| internal memory leaks within the Objective-C libraries and frameworks. | ||||
| * Objects sent to you in callback functions are not memory managed by Go | ||||
| and must be manually | ||||
| managed using `Retain()` and `Release()` methods if you need to take ownership of them. | ||||
| A rule of thumb is that if you assign such an object to a persistent Go variable for | ||||
| use outside of the callback, call `Retain()`. | ||||
| 
 | ||||
| Because of the linkage with the Go garbage collector described above, there should be | ||||
| no need for any memory management code to be written from the Go side, except in the | ||||
| case mentioned above where your Go delegate receives objects that need to be kept | ||||
| around outside of the callback. | ||||
| 
 | ||||
| Since everything in Objective C inherits methods from `NSObject`, you can call | ||||
| `Retain()`, `Release()` and `Autorelease()` on any object. You can technically bind | ||||
| the `NSAutoreleasePool` class and create and drain instances of it from the Go side, | ||||
| but this is not recommended in the default, garbage collected mode and can run into | ||||
| problems because the Go runtime is inherently multithreaded. See `examples/memory` | ||||
| for an example of manual memory management, which should be possible to do reliably | ||||
| but I'm not sure why you would go through the trouble. | ||||
| 
 | ||||
| NSWrap is doing a number of things behind the scenes to make garbage collection work. | ||||
| As mentioned, | ||||
| all Objective-C methods are called within an `@autorelease {}` block. This is | ||||
| necessary because some foundation classes (notably `NSString`) create internal | ||||
| objects that are `autoreleased` but never returned to the caller. These objects can | ||||
| never be deallocated unless the method in question was called within an autorelease | ||||
| pool. | ||||
| 
 | ||||
| NSWrap assumes you | ||||
| are going to take ownership of every Objective-C object returned by a method, either | ||||
| directly as a return value or through a pointer to a pointer given as a parameter. | ||||
| Therefore, NSWrap calls `retain` on all of these objects before going back to the | ||||
| Go side, unless the object is either `nil` or equivalent to the input object. | ||||
| NSWrap also will not call `retain` on the return values of `init`, `new`, `copy`, | ||||
| `mutableCopy` or `alloc` methods. If you do not want ownership of the object, | ||||
| simply assign it to a local varable and the garbage collector will take care of | ||||
| releasing it. | ||||
| 
 | ||||
| In | ||||
| order for this to work on a pointer to a pointer parameter, NSWrap treats the | ||||
| input | ||||
| parameter as an array with a length specified by either a `range` parameter (of | ||||
| type `NSRange`) or a `count` parameter of an integer type. If there is neither a | ||||
| `range` or `count` parameter, NSWrap assumes the array is length 1. | ||||
| 
 | ||||
| As an example, in Objective-C, if you were to take an object out of an `NSArray` | ||||
| and the array was later deallocated, there is no guarantee that the object you | ||||
| obtained is still around unless you called `retain` on it. This is not necessary | ||||
| with NSWrap, which automatically retains objects returned by methods like | ||||
| `objectAtIndex:` and `getObjects:range` and manages them with the Go garbage | ||||
| collector. | ||||
| 
 | ||||
| The methods described above work for methods that return Objective-C | ||||
| objects, which can be `retain`ed, but not with methods that return other types of | ||||
| pointers such as C strings. NSWrap has a special case for C strings (`*Char` | ||||
| in Go), calling | ||||
| `strdup` on the return value within the `@autoreleasepool` block. This | ||||
| ensures that the string is preserved even if it points to a termporary | ||||
| autoreleased | ||||
| object. Since this behavior results in newly allocated memory, these pointers will | ||||
| need to be freed from Go later on. Since these are pointers to C memory, | ||||
| it is not possible to set a finalizer on these pointers for garbage collection | ||||
| by Go. | ||||
| 
 | ||||
| Note that the Go garbage collector is lazy and will not activate unless your | ||||
| application is running low on heap space. That means in practice that Objective-C | ||||
| objects are going to stick around a lot longer than they might in a pure | ||||
| Objective-C application. If this is an issue, simply run the Go GC manually with | ||||
| `runtime.GC()`. | ||||
| 
 | ||||
| 
 | ||||
| # Limitations | ||||
| 
 | ||||
| ## Blocks and Function Pointers | ||||
| ## Blocks | ||||
| 
 | ||||
| NSWrap does not support methods or functions that take C functions or blocks | ||||
| as parameters or return values. | ||||
|  | @ -843,7 +742,7 @@ and got carried away. | |||
| 
 | ||||
| # Acknowledgements | ||||
| 
 | ||||
| This work was inspired by Maxim Kupriianov's excellent | ||||
| This work was inspired by Maxim's excellent | ||||
| [c-for-go](https://github.com/xlab/c-for-go). Much of the | ||||
| infrastructure was lifted from Elliot Chance's equally excellent | ||||
| [c2go](https://github.com/elliotchance/c2go). Kiyoshi Murata's | ||||
|  |  | |||
|  | @ -21,10 +21,6 @@ func pb2() { | |||
| 	a.Terminate(a) | ||||
| } | ||||
| 
 | ||||
| func db() { | ||||
| 	fmt.Println("button deallocated") | ||||
| } | ||||
| 
 | ||||
| func didFinishLaunching(n *ns.NSNotification) { | ||||
| 	fmt.Println("Go: did finish launching") | ||||
| 	fmt.Printf("Notification: %s\n", n.Name().UTF8String()) | ||||
|  | @ -72,14 +68,12 @@ func didFinishLaunching(n *ns.NSNotification) { | |||
| 
 | ||||
| 	b1.Init() | ||||
| 	b1.PressedCallback(pb1) | ||||
| 	b1.DeallocCallback(db) | ||||
| 	b1.SetAction(ns.Selector("pressed")) | ||||
| 	b1.SetTarget(b1) | ||||
| 	b1.SetTitle(nst("PUSH")) | ||||
| 
 | ||||
| 	b2.Init() | ||||
| 	b2.PressedCallback(pb2) | ||||
| 	b2.DeallocCallback(db) | ||||
| 	b2.SetTarget(b2) | ||||
| 	b2.SetAction(ns.Selector("pressed")) | ||||
| 	b2.SetTitle(nst("QUIT")) | ||||
|  | @ -109,7 +103,6 @@ func didFinishLaunching(n *ns.NSNotification) { | |||
| } | ||||
| 
 | ||||
| func shouldTerminateAfterLastWindowClosed(s *ns.NSApplication) ns.BOOL { | ||||
| 	fmt.Println("Go: should terminate after last window closed") | ||||
| 	return 1 | ||||
| } | ||||
| 
 | ||||
|  | @ -124,7 +117,6 @@ func didBecomeActive(n *ns.NSNotification) { | |||
| 
 | ||||
| var ( | ||||
| 	a   *ns.NSApplication | ||||
| 	del *ns.AppDelegate | ||||
| 	win *ns.NSWindow | ||||
| ) | ||||
| 
 | ||||
|  | @ -135,8 +127,7 @@ func app() { | |||
| 	a.SetActivationPolicy(ns.NSApplicationActivationPolicyRegular) | ||||
| 
 | ||||
| 	// Set up an AppDelegate
 | ||||
| 	// assign it to a global variable so it doesn't get garbage collected
 | ||||
| 	del = ns.AppDelegateAlloc() | ||||
| 	del := ns.AppDelegateAlloc() | ||||
| 	del.ApplicationDidFinishLaunchingCallback(didFinishLaunching) | ||||
| 	del.ApplicationShouldTerminateAfterLastWindowClosedCallback(shouldTerminateAfterLastWindowClosed) | ||||
| 	del.ApplicationWillTerminateCallback(willTerminate) | ||||
|  | @ -158,6 +149,6 @@ func main() { | |||
| 	}() | ||||
| 
 | ||||
| 	// Run our app in an autorelease pool just for fun
 | ||||
| 	go app() | ||||
| 	go ns.Autoreleasepool(app) | ||||
| 	select {} | ||||
| } | ||||
|  |  | |||
|  | @ -41,7 +41,8 @@ subclasses: | |||
|     GButton: | ||||
|         NSButton: | ||||
|             - -(void)pressed | ||||
|             - dealloc | ||||
| 
 | ||||
| frameworks: [ Foundation, AppKit ] | ||||
| pragma: [ clang diagnostic ignored "-Wformat-security" ] | ||||
| #autorelease: true | ||||
| gogc: true | ||||
|  |  | |||
|  | @ -100,12 +100,12 @@ func hr(d *ns.NSData) int { | |||
| func discoverCharacteristics(p *ns.CBPeripheral, s *ns.CBService, e *ns.NSError) { | ||||
| 	fmt.Printf("Did discover characteristics\n") | ||||
| 	uuid := s.UUID() | ||||
| 	fmt.Printf("----%s\n", uuid.UUIDString()) | ||||
| 	fmt.Printf("----%s\n", uuid.UUIDString().UTF8String()) | ||||
| 	if uuid.IsEqualTo(hrm_uuid) { | ||||
| 		s.Characteristics().ObjectEnumerator().ForIn(func(o *ns.Id) bool { | ||||
| 			chr := o.CBCharacteristic() | ||||
| 			chuuid := chr.UUID() | ||||
| 			fmt.Printf("------%s\n", chuuid.UUIDString()) | ||||
| 			fmt.Printf("------%s\n", chuuid.UUIDString().UTF8String()) | ||||
| 			if chuuid.IsEqualTo(hrv_uuid) { | ||||
| 				p.SetNotifyValue(1, chr) | ||||
| 				v := chr.Value() | ||||
|  |  | |||
|  | @ -39,3 +39,4 @@ delegates: | |||
|             - peripheralDidUpdateValueForCharacteristic | ||||
| 
 | ||||
| pragma: [ clang diagnostic ignored "-Wformat-security" ] | ||||
| gogc: true | ||||
|  |  | |||
|  | @ -4,7 +4,6 @@ package main | |||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 
 | ||||
| 	"git.wow.st/gmp/nswrap/examples/foundation/ns" | ||||
| ) | ||||
|  | @ -44,10 +43,8 @@ func main() { | |||
| 	fmt.Printf("i1 = %@\n", i1) | ||||
| 	fmt.Printf("i1.Ptr() = %p\n", i1.Ptr()) | ||||
| 	fmt.Printf("\nNSArray.ObjectEnumerator().ForIn():\n") | ||||
| 	x := 0 | ||||
| 	a.ObjectEnumerator().ForIn(func(o *ns.Id) bool { | ||||
| 		fmt.Printf("%d: %s\n",x,o.NSString()) | ||||
| 		x++ | ||||
| 		fmt.Println(o.NSString()) | ||||
| 		return true | ||||
| 	}) | ||||
| 	fmt.Printf("\nNSSetWithObjectsCount():\n") | ||||
|  | @ -72,7 +69,6 @@ func main() { | |||
| 		} | ||||
| 		return true | ||||
| 	}) | ||||
| 	fmt.Printf("a = %p a.NSArray = %p\n",a,&a.NSArray) | ||||
| 	fmt.Printf("\nNSArrayWithObjects() (length 1)\n") | ||||
| 	a2 = ns.NSArrayWithObjects(n1) | ||||
| 	a2.ObjectEnumerator().ForIn(func(o *ns.Id) bool { | ||||
|  | @ -106,26 +102,19 @@ func main() { | |||
| 		ns.NSArrayWithObjects(nst("obj1"), nst("obj2")), | ||||
| 		ns.NSArrayWithObjects(nst("key1"), nst("key2")), | ||||
| 	) | ||||
| 	oarr := make([]*ns.Id, 0, 5) | ||||
| 	fmt.Printf("Length of oarr is %d\n", len(oarr)) | ||||
| 	karr := make([]*ns.Id, 0, 5) | ||||
| 	os := make([]*ns.Id, 0, 5) | ||||
| 	fmt.Printf("Length of os is %d\n", len(os)) | ||||
| 	ks := make([]*ns.Id, 0, 5) | ||||
| 	fmt.Printf("\nGetObjects()\n") | ||||
| 	d.GetObjects(&oarr, &karr, 4) | ||||
| 	fmt.Printf("Length of oarr is now %d\n", len(oarr)) | ||||
| 	for i, k := range karr { | ||||
| 		fmt.Printf("-- %s -> %s\n", k.NSString(), oarr[i].NSString()) | ||||
| 	d.GetObjects(&os, &ks, 4) | ||||
| 	fmt.Printf("Length of os is now %d\n", len(os)) | ||||
| 	for i, k := range ks { | ||||
| 		fmt.Printf("-- %s -> %s\n", k.NSString(), os[i].NSString()) | ||||
| 	} | ||||
| 	fmt.Printf("\nNSStringWithContentsOfURLEncoding()\n") | ||||
| 	err := make([]*ns.NSError, 1) | ||||
| 	n1 = ns.NSStringWithContentsOfURLEncoding(ns.NSURLWithGoString("http://captive.apple.com"), ns.NSUTF8StringEncoding, &err) | ||||
| 	if len(err) == 0 { | ||||
| 		fmt.Printf("n1 = %s\n",n1) | ||||
| 	} | ||||
| 	n1 = ns.NSStringWithContentsOfURLEncoding(ns.NSURLWithGoString("htttypo://example.com"), ns.NSUTF8StringEncoding, &err) | ||||
| 	if len(err) > 0 { | ||||
| 		fmt.Printf("err[0] = %p -> %p\n",err[0],err[0].Ptr()) | ||||
| 		fmt.Printf("err: %s\n", err[0].LocalizedDescription()) | ||||
| 	} | ||||
| 	n1 = ns.NSStringWithContentsOfURLEncoding(ns.NSURLWithGoString("htttypo://example.com"), 0, &err) | ||||
| 	fmt.Printf("err: %s\n", err[0].LocalizedDescription()) | ||||
| 
 | ||||
| 	fmt.Printf("\nNSStringWithFormat()\n") | ||||
| 	str := ns.NSStringWithFormat(nst("(%@) (%@)\n(%@)\n"), n2, n3, s1) | ||||
|  | @ -140,24 +129,4 @@ func main() { | |||
| 		fmt.Printf("--%s\n",o.NSString()) | ||||
| 		return true | ||||
| 	}) | ||||
| 	dir,e := os.Getwd() | ||||
| 	if e != nil { | ||||
| 		fmt.Printf("Failed to get current working directory. %s\n",err) | ||||
| 		os.Exit(-1) | ||||
| 	} | ||||
| 	path := nst(dir) | ||||
| 	filter := ns.NSArrayWithObjects(nst("ast"),nst("yaml")) | ||||
| 	ost := make([]*ns.NSString,0,1) | ||||
| 	oar:= make([]*ns.NSArray,0,1) | ||||
| 	fmt.Printf("\nCompletePathIntoString()\n") | ||||
| 	i := path.CompletePathIntoString(&ost,0,&oar,filter) | ||||
| 	fmt.Printf("%d matches\n",i) | ||||
| 	if i > 0 { | ||||
| 		fmt.Printf("ost = %s\n",ost[0]) | ||||
| 		fmt.Printf("oar =\n") | ||||
| 		oar[0].ObjectEnumerator().ForIn(func(o *ns.Id) bool { | ||||
| 			fmt.Printf("--%s\n",o.NSString()) | ||||
| 			return true | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ classes: | |||
|     - NSFileManager | ||||
|     - NSObject | ||||
| functions: [ NSMakeRange ] | ||||
| enums: [ P_ALL, CF.*, .*StringEncoding ] | ||||
| enums: [ P_ALL, CF.* ] | ||||
| frameworks: [ Foundation ] | ||||
| pragma: [ clang diagnostic ignored "-Wformat-security" ] | ||||
| vaargs: 32 | ||||
|  |  | |||
|  | @ -103,7 +103,7 @@ func memtest3() { | |||
| 		// check that our string was retained.
 | ||||
| 
 | ||||
| 		s1 := arr.ObjectAtIndex(0) | ||||
| 		gstr := s1.NSString().String() | ||||
| 		gstr := s1.NSString().UTF8String().String() | ||||
| 		_ = gstr | ||||
| 	} | ||||
| } | ||||
|  | @ -122,7 +122,6 @@ func memtest4() { | |||
| 		_ = c1 | ||||
| 		runtime.GC() | ||||
| 		time.Sleep(time.Second/50) | ||||
| 		c1.Free() // you need to manually free UTF8Strings
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -146,78 +145,22 @@ func memtest5() { | |||
| 		u := sub.UTF8String() | ||||
| 		u2 := sub2.UTF8String() | ||||
| 		u3 := sub3.UTF8String() | ||||
| 		time.Sleep(time.Second/50) | ||||
| 		runtime.GC() | ||||
| 		i++ | ||||
| 		u.Free() | ||||
| 		u2.Free() | ||||
| 		u3.Free() | ||||
| 		_ = u | ||||
| 		_ = u2 | ||||
| 		_ = u3 | ||||
| 		time.Sleep(time.Second/50) | ||||
| 		runtime.GC() | ||||
| 		i++ | ||||
| 		//fmt.Printf("loop completed\n")
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func tmpdict(i int) *ns.NSString { | ||||
| 	o1 := ns.NSStringWithGoString(fmt.Sprintf("temp string 1-%d",i)) | ||||
| 	o2 := ns.NSStringWithGoString(fmt.Sprintf("temp string 2-%d",i)) | ||||
| 	k1 := ns.NSStringWithGoString(fmt.Sprintf("temp key 1-%d",i)) | ||||
| 	k2 := ns.NSStringWithGoString(fmt.Sprintf("temp key 2-%d",i)) | ||||
| 	dict := ns.NSDictionaryWithObjectsAndKeys(o1,k1,o2,k2) | ||||
| 	ret := dict.ValueForKey(k1) | ||||
| 	//fmt.Printf("tmpdict(): string = %s\n",ret.NSString())
 | ||||
| 
 | ||||
| 	defer runtime.GC() // o1, o2, k1, k2, and dict can be released after we return
 | ||||
| 	return ret.NSString() // should be retained by NSDictionary.ValueForKey()
 | ||||
| } | ||||
| 
 | ||||
| func tmparr(i int) *ns.NSString { | ||||
| 	o1 := ns.NSStringWithGoString(fmt.Sprintf("temp string 3-%d",i)) | ||||
| 	o2 := ns.NSStringWithGoString(fmt.Sprintf("temp string 4-%d",i)) | ||||
| 	arr := ns.NSArrayWithObjects(o1,o2) | ||||
| 	os := make([]*ns.Id,0,2) | ||||
| 	arr.GetObjects(&os, ns.NSMakeRange(0,2)) | ||||
| 
 | ||||
| 	defer runtime.GC() // collect o1, o2 and arr
 | ||||
| 	return os[1].NSString() // should have been retained by NSArray.GetObjects()
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| func memtest6() { | ||||
| 	fmt.Println("memtest6 started") | ||||
| 	i := 0 | ||||
| 	for { | ||||
| 		s1 := tmpdict(i) | ||||
| 		s2 := tmparr(i) | ||||
| 		time.Sleep(time.Second / 5) | ||||
| 		u1 := s1.String() // make sure s1 and s2 are still available
 | ||||
| 		u2 := s2.String() | ||||
| 		e1 := fmt.Sprintf("temp string 1-%d",i) | ||||
| 		if u1 != e1 { | ||||
| 			fmt.Printf("tmpdict() error: %s != %s\n",u1,e1) | ||||
| 		} | ||||
| 		e2 := fmt.Sprintf("temp string 4-%d",i) | ||||
| 		if u2 != e2 { | ||||
| 			fmt.Printf("tmparr() error: %s != %s\n",u2,e2) | ||||
| 
 | ||||
| 		} | ||||
| 		i++ | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	fmt.Printf("MultiThreaded? %t\n", ns.NSThreadIsMultiThreaded()) | ||||
| 	th := ns.NSThreadNew() | ||||
| 	th.Start() | ||||
| 	fmt.Printf("MultiThreaded? %t\n", ns.NSThreadIsMultiThreaded()) | ||||
| 	go memtest1() | ||||
| 	go memtest2() | ||||
| 	go memtest3() | ||||
| 	go memtest4() | ||||
| 	go memtest5() | ||||
| 	go memtest6() | ||||
| 
 | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			// print a progress indicator
 | ||||
|  |  | |||
|  | @ -5,8 +5,7 @@ classes: | |||
|     - NSArray | ||||
|     - NSMutableArray | ||||
|     - NSString | ||||
|     - NSDictionary | ||||
|     - NSThread | ||||
|     - NSObject | ||||
| subclasses: | ||||
|     MyClass: | ||||
|         NSObject: | ||||
|  | @ -17,4 +16,4 @@ functions: | |||
| frameworks: | ||||
|     - Foundation | ||||
| pragma: [ clang diagnostic ignored "-Wformat-security" ] | ||||
| #nogc: true | ||||
| gogc: true | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| // An example of manual memory management (nogc directive in nswrap.yaml)
 | ||||
| package main | ||||
| 
 | ||||
| import "C" | ||||
|  | @ -39,6 +38,8 @@ func memtest1() { | |||
| 	for { | ||||
| 		pool := ns.NSAutoreleasePoolAlloc().Init() | ||||
| 		o1 := ns.MyClassAlloc() | ||||
| 		//If autorelease: true is set in nswrap.yaml, the manual calls to
 | ||||
| 		//autorelease are not necessary.
 | ||||
| 		o1.Autorelease() | ||||
| 		o1.DeallocCallback(dealloc) | ||||
| 		o1.ReleaseCallback(release) | ||||
|  | @ -132,9 +133,6 @@ func memtest4() { | |||
| 	go memtest1() | ||||
| 	go memtest2() | ||||
| 	go memtest3() | ||||
| 	go memtest1() | ||||
| 	go memtest2() | ||||
| 	go memtest3() | ||||
| } | ||||
| 
 | ||||
| func memtest4a() { | ||||
|  | @ -198,6 +196,9 @@ func main() { | |||
| 	//Within an autorelease pool, do not do anything that can result in a
 | ||||
| 	//switch to a different thread.
 | ||||
| 
 | ||||
| 	//go memtest1()
 | ||||
| 	//go memtest2()
 | ||||
| 	//go memtest3()
 | ||||
| 	go memtest4() | ||||
| 	select {} | ||||
| } | ||||
|  |  | |||
|  | @ -14,4 +14,3 @@ subclasses: | |||
| frameworks: | ||||
|     - Foundation | ||||
| pragma: [ clang diagnostic ignored "-Wformat-security" ] | ||||
| nogc: true | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| package: ClassOne | ||||
| inputfiles: [ ClassOne/simple.h ] | ||||
| classes: | ||||
|     - NSObject | ||||
|     - ClassOne | ||||
|     - ClassTwo | ||||
| subclasses: | ||||
|  | @ -9,7 +8,8 @@ subclasses: | |||
|         ClassTwo: | ||||
|             - geti1 | ||||
|             - -(void)myMethod1:(int)a | ||||
|             - -(const NSObject* _Nonnull)myMethod2:(int)a options:(NSDictionary<NSString*,NSObject*> *)opt | ||||
|             - +(const NSObject* _Nonnull)myMethod2:(int)a options:(NSDictionary<NSString*,NSObject*> *)opt | ||||
| 
 | ||||
| imports: [ simple.h ] | ||||
| frameworks: [ Foundation ] | ||||
| #arc: true | ||||
|  |  | |||
|  | @ -1,157 +0,0 @@ | |||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"runtime" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 	"unsafe" | ||||
| 
 | ||||
| 	"git.wow.st/gmp/nswrap/examples/strings/ns" | ||||
| ) | ||||
| 
 | ||||
| func incr() func(bool) (int, float64) { | ||||
| 	i := 0 | ||||
| 	b := 0.0 | ||||
| 	var mx sync.Mutex | ||||
| 	return func(bad bool) (int, float64) { | ||||
| 		mx.Lock() | ||||
| 		if bad { | ||||
| 			b++ | ||||
| 			defer mx.Unlock() | ||||
| 		} else { | ||||
| 			defer func() { i++; mx.Unlock() }() | ||||
| 		} | ||||
| 		if b == 0 { | ||||
| 			return i, 0.0 | ||||
| 		} else { | ||||
| 			return i, (b/float64(i)) * 100 | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type tracker struct { | ||||
| 	add, drop func(*ns.Id) | ||||
| 	check func() | ||||
| 	i func(bool) (int, float64) | ||||
| } | ||||
| 
 | ||||
| type record struct { | ||||
| 	ptr unsafe.Pointer | ||||
| 	goPtr *ns.Id | ||||
| 	when time.Time | ||||
| } | ||||
| 
 | ||||
| func newTracker() (func(*ns.Id), func(*ns.Id), func()) { | ||||
| 	addch := make(chan *ns.Id) | ||||
| 	dropch := make(chan *ns.Id) | ||||
| 	data := []record{} | ||||
| 	var mux sync.Mutex | ||||
| 
 | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			select { | ||||
| 			case x := <-addch: | ||||
| 				mux.Lock() | ||||
| 					data = append(data,record{ | ||||
| 						x.Ptr(), | ||||
| 						x, | ||||
| 						time.Now(), | ||||
| 					}) | ||||
| 				mux.Unlock() | ||||
| 			case x := <-dropch: | ||||
| 				mux.Lock() | ||||
| 					data = append(data,record{ | ||||
| 						nil, | ||||
| 						x, | ||||
| 						time.Now(), | ||||
| 					}) | ||||
| 				mux.Unlock() | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	add := func(x *ns.Id) { | ||||
| 		addch<- x | ||||
| 	} | ||||
| 	drop := func(x *ns.Id) { | ||||
| 		dropch<- x | ||||
| 	} | ||||
| 	check := func() { | ||||
| 		live := map[unsafe.Pointer]*ns.Id{} | ||||
| 		bad := false | ||||
| 		mux.Lock() | ||||
| 		for _,r := range data { | ||||
| 			if r.ptr != nil { | ||||
| 				if live[r.ptr] != nil { | ||||
| 					fmt.Printf("COLLISION: %p & %p -> %p\n", r.goPtr, live[r.ptr], r.ptr) | ||||
| 					bad = true | ||||
| 				} | ||||
| 				live[r.ptr] = r.goPtr | ||||
| 			} else { | ||||
| 				delete(live,r.ptr) | ||||
| 			} | ||||
| 		} | ||||
| 		fmt.Printf("Checked %d records -- ",len(data)) | ||||
| 		if bad { | ||||
| 			fmt.Printf("failed\n") | ||||
| 		} else { | ||||
| 			fmt.Printf("ok\n") | ||||
| 		} | ||||
| 		mux.Unlock() | ||||
| 	} | ||||
| 	return add,drop,check | ||||
| } | ||||
| 
 | ||||
| func mkstrings(t tracker) { | ||||
| 	for { | ||||
| 		//fmt.Printf("main thread: %t\n",ns.NSThreadIsMainThread())
 | ||||
| 		x,b := t.i(false) | ||||
| 		str := fmt.Sprintf("string %d",x) | ||||
| 		s := ns.NSStringWithGoString(str) | ||||
| 		//t.add(&s.Id)
 | ||||
| 		for j := 0; j < 10; j++ { | ||||
| 			sout := s.String() | ||||
| 			if str != sout { | ||||
| 				_,b = t.i(true) | ||||
| 				fmt.Printf("%3.2f%% -- %d: '%s' '%s'\n", b, x, str, sout) | ||||
| 			} | ||||
| 			time.Sleep(time.Second/1000) | ||||
| 		} | ||||
| 		if x % 1000 == 0 { | ||||
| 			fmt.Printf("%3.2f%% -- %s\n", b, time.Now().Format("03:04:05.000")) | ||||
| 		} | ||||
| 		//t.drop(&s.Id)
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	runtime.GOMAXPROCS(4) | ||||
| 	fmt.Printf("Starting\n") | ||||
| 	//ns.NSThreadNew().Start()
 | ||||
| 	fmt.Printf("multithreaded: %t\n", ns.NSThreadIsMultiThreaded()) | ||||
| 	//pool := ns.NSAutoreleasePoolAlloc()
 | ||||
| 	add, drop, check := newTracker() | ||||
| 	i := tracker{add, drop, check, incr()} | ||||
| 	go mkstrings(i) | ||||
| 	go mkstrings(i) | ||||
| 	go mkstrings(i) | ||||
| 	go mkstrings(i) | ||||
| 	go mkstrings(i) | ||||
| 	go mkstrings(i) | ||||
| 	go mkstrings(i) | ||||
| 	go mkstrings(i) | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			runtime.GC() | ||||
| 			time.Sleep(time.Second/100) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	time.Sleep(time.Second * 600) | ||||
| 	i.check() | ||||
| 
 | ||||
| 	//pool.Drain()
 | ||||
| 	os.Exit(0) | ||||
| } | ||||
|  | @ -1,9 +0,0 @@ | |||
| inputfiles: | ||||
|     - /System/Library/Frameworks/Foundation.framework/Headers/Foundation.h | ||||
| classes: | ||||
|     - NSString | ||||
|     - NSThread | ||||
| frameworks: | ||||
|     - Foundation | ||||
| pragma: [ clang diagnostic ignored "-Wformat-security" ] | ||||
| #nogc: true | ||||
							
								
								
									
										24
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								main.go
									
									
									
									
									
								
							|  | @ -19,12 +19,6 @@ import ( | |||
| var Debug = false | ||||
| var Profile = false | ||||
| 
 | ||||
| //automatically add interfaces if they are found in the input interface
 | ||||
| //declarations
 | ||||
| var autoadd = []string{ | ||||
| 	"NSObject", | ||||
| } | ||||
| 
 | ||||
| type conf struct { | ||||
| 	Positions     bool | ||||
| 	Package       string | ||||
|  | @ -45,7 +39,7 @@ type conf struct { | |||
| 	//Arc flag for debugging only, builds will break
 | ||||
| 	Arc         bool | ||||
| 	Autorelease bool | ||||
| 	Nogc        bool | ||||
| 	Gogc        bool | ||||
| } | ||||
| 
 | ||||
| var Config conf | ||||
|  | @ -257,20 +251,19 @@ func Start() (err error) { | |||
| 	if Config.Positions { | ||||
| 		ast.TrackPositions = true | ||||
| 	} | ||||
| 	wrap.Gogc = true | ||||
| 
 | ||||
| 	if Config.Gogc && Config.Autorelease { | ||||
| 		fmt.Printf("Cannot use Autorelease and Gogc directives at the same time\n") | ||||
| 		os.Exit(-1) | ||||
| 	} | ||||
| 	if Config.Arc { | ||||
| 		wrap.Arc = true | ||||
| 		wrap.Gogc = false | ||||
| 	} | ||||
| 	if Config.Autorelease { | ||||
| 		wrap.Autorelease = true | ||||
| 		wrap.Gogc = false | ||||
| 	} | ||||
| 	if Config.Nogc { | ||||
| 		wrap.Gogc = false | ||||
| 	if Config.Gogc { | ||||
| 		wrap.Gogc = true | ||||
| 	} | ||||
| 
 | ||||
| 	//NOTE: converting in parallel is slower on my system
 | ||||
| 	//nodes := convertLinesToNodesParallel(lines)
 | ||||
| 	nodes := convertLinesToNodes(lines) | ||||
|  | @ -304,9 +297,6 @@ func Start() (err error) { | |||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				if matches(x.Name, autoadd) { | ||||
| 					Config.Classes = append(Config.Classes,x.Name) | ||||
| 				} | ||||
| 			case *ast.ObjCCategoryDecl: | ||||
| 				w.AddCategory(x) | ||||
| 			case *ast.TypedefDecl: | ||||
|  |  | |||
|  | @ -7,8 +7,6 @@ import ( | |||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| var Gogc bool | ||||
| 
 | ||||
| //super is a map recording which class is the parent of each other class
 | ||||
| var super map[string]string | ||||
| 
 | ||||
|  | @ -444,7 +442,7 @@ func GoToC(sname, name string, pnames, snames []string, rtype *Type, ptypes []*T | |||
| 		case TypedefShouldWrap(ptgt) && !pt.Variadic && !fun: | ||||
| 			p = pn + ".Ptr()" | ||||
| 		case snames[i] != "": | ||||
| 			p = "(*unsafe.Pointer)(unsafe.Pointer(&" + snames[i] + "[0]))" | ||||
| 			p = "unsafe.Pointer(&" + snames[i] + "[0])" | ||||
| 		case pt.Variadic: | ||||
| 			p = "unsafe.Pointer(&" + p + ")" | ||||
| 		case pt.IsPointer() && !fun: | ||||
|  | @ -480,13 +478,6 @@ func GoToC(sname, name string, pnames, snames []string, rtype *Type, ptypes []*T | |||
| 		if IsGoInterface(ptgt) { | ||||
| 			ptgt = "Id" | ||||
| 		} | ||||
| 		dogc := "" | ||||
| 		if Gogc { | ||||
| 			dogc = fmt.Sprintf(` | ||||
| 			runtime.SetFinalizer((*%s)[i], func(o *%s) { | ||||
| 				o.Release() | ||||
| 			})`, pnames[i], ptgt) | ||||
| 		} | ||||
| 		ret.WriteString(fmt.Sprintf(` | ||||
| 	(*%s) = (*%s)[:cap(*%s)] | ||||
| 	for i := 0; i < len(*%s); i++ { | ||||
|  | @ -495,10 +486,10 @@ func GoToC(sname, name string, pnames, snames []string, rtype *Type, ptypes []*T | |||
| 			break | ||||
| 		} | ||||
| 		if (*%s)[i] == nil { | ||||
| 			(*%s)[i] = &%s{}%s | ||||
| 			(*%s)[i] = &%s{} | ||||
| 		} | ||||
| 		(*%s)[i].ptr = %s[i] | ||||
| 	}`, pnames[i], pnames[i], pnames[i], pnames[i], sname, pnames[i], pnames[i], pnames[i], pnames[i], ptgt, dogc, pnames[i], sname)) | ||||
| 	}`, pnames[i], pnames[i], pnames[i], pnames[i], sname, pnames[i], pnames[i], pnames[i], pnames[i], ptgt, pnames[i], sname)) | ||||
| 	} | ||||
| 	if rt != "void" { | ||||
| 		cmp := "" | ||||
|  |  | |||
|  | @ -217,7 +217,9 @@ type NSObject interface { | |||
| type NSString struct { Id } | ||||
| func (o *NSString) Ptr() unsafe.Pointer { if o == nil { return nil }; return o.ptr } | ||||
| func (o *Id) NSString() *NSString { | ||||
| 	return (*NSString)(unsafe.Pointer(o)) | ||||
| 	ret := &NSString{} | ||||
| 	ret.ptr = o.ptr | ||||
| 	return ret | ||||
| } | ||||
| `) | ||||
| 	str = "int(void)" | ||||
|  | @ -274,7 +276,7 @@ func (o *Id) NSString() *NSString { | |||
| 	snames := []string{"", "", "", ""} | ||||
| 
 | ||||
| 	chk_gotoc := func(expected string) { | ||||
| 		chk(GoToC("myFun", "myFun", pnames, snames, rtype, ptypes, false, false, false), expected) | ||||
| 		chk(GoToC("myFun", pnames, snames, rtype, ptypes, false, false, false), expected) | ||||
| 	} | ||||
| 
 | ||||
| 	chk_gotoc("") | ||||
|  | @ -296,16 +298,12 @@ func (o *Id) NSString() *NSString { | |||
| 	chk_gotoc( | ||||
| 		`ret := &NSString{} | ||||
| 	ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), (C.int)(p3), unsafe.Pointer(p4))) | ||||
| 	if ret.ptr == nil { return ret } | ||||
| 	if ret.ptr == o.ptr { return (*NSString)(unsafe.Pointer(o)) } | ||||
| 	return ret`) | ||||
| 
 | ||||
| 	rtype = nsop | ||||
| 	chk_gotoc( | ||||
| 		`ret := &Id{} | ||||
| 	ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), (C.int)(p3), unsafe.Pointer(p4))) | ||||
| 	if ret.ptr == nil { return ret } | ||||
| 	if ret.ptr == o.ptr { return (*Id)(unsafe.Pointer(o)) } | ||||
| 	return ret`) | ||||
| 
 | ||||
| 	ptypes[1].Variadic = true | ||||
|  | @ -313,8 +311,6 @@ func (o *Id) NSString() *NSString { | |||
| 	chk_gotoc( | ||||
| 		`ret := &Id{} | ||||
| 	ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), unsafe.Pointer(&p2), (C.int)(p3), unsafe.Pointer(p4))) | ||||
| 	if ret.ptr == nil { return ret } | ||||
| 	if ret.ptr == o.ptr { return (*Id)(unsafe.Pointer(o)) } | ||||
| 	return ret`) | ||||
| 
 | ||||
| 	ptypes[1].Variadic = false | ||||
|  | @ -322,7 +318,7 @@ func (o *Id) NSString() *NSString { | |||
| 	ptypes[1] = nsopp | ||||
| 	chk_gotoc( | ||||
| 		`ret := &Id{} | ||||
| 	ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), (*unsafe.Pointer)(unsafe.Pointer(&p2p[0])), (C.int)(p3), unsafe.Pointer(p4))) | ||||
| 	ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), unsafe.Pointer(&p2p[0]), (C.int)(p3), unsafe.Pointer(p4))) | ||||
| 	(*p2) = (*p2)[:cap(*p2)] | ||||
| 	for i := 0; i < len(*p2); i++ { | ||||
| 		if p2p[i] == nil { | ||||
|  | @ -334,8 +330,6 @@ func (o *Id) NSString() *NSString { | |||
| 		} | ||||
| 		(*p2)[i].ptr = p2p[i] | ||||
| 	} | ||||
| 	if ret.ptr == nil { return ret } | ||||
| 	if ret.ptr == o.ptr { return (*Id)(unsafe.Pointer(o)) } | ||||
| 	return ret`) | ||||
| 	snames[1] = "" | ||||
| 	snames[2] = "p3p" | ||||
|  | @ -343,7 +337,7 @@ func (o *Id) NSString() *NSString { | |||
| 	ptypes[2] = nstpp | ||||
| 	chk_gotoc( | ||||
| 		`ret := &Id{} | ||||
| 	ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), (*unsafe.Pointer)(unsafe.Pointer(&p3p[0])), unsafe.Pointer(p4))) | ||||
| 	ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), unsafe.Pointer(&p3p[0]), unsafe.Pointer(p4))) | ||||
| 	(*p3) = (*p3)[:cap(*p3)] | ||||
| 	for i := 0; i < len(*p3); i++ { | ||||
| 		if p3p[i] == nil { | ||||
|  | @ -355,13 +349,11 @@ func (o *Id) NSString() *NSString { | |||
| 		} | ||||
| 		(*p3)[i].ptr = p3p[i] | ||||
| 	} | ||||
| 	if ret.ptr == nil { return ret } | ||||
| 	if ret.ptr == o.ptr { return (*Id)(unsafe.Pointer(o)) } | ||||
| 	return ret`) | ||||
| 
 | ||||
| 	chk(GoToC("myFun", "myFun", pnames, snames, rtype, ptypes, true, false, false), | ||||
| 	chk(GoToC("myFun", pnames, snames, rtype, ptypes, true, false, false), | ||||
| 		`ret := &Id{} | ||||
| 	ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), (*unsafe.Pointer)(unsafe.Pointer(&p3p[0])), p4)) | ||||
| 	ret.ptr = unsafe.Pointer(C.myFun(p1.Ptr(), p2.Ptr(), unsafe.Pointer(&p3p[0]), p4)) | ||||
| 	(*p3) = (*p3)[:cap(*p3)] | ||||
| 	for i := 0; i < len(*p3); i++ { | ||||
| 		if p3p[i] == nil { | ||||
|  | @ -373,7 +365,5 @@ func (o *Id) NSString() *NSString { | |||
| 		} | ||||
| 		(*p3)[i].ptr = p3p[i] | ||||
| 	} | ||||
| 	if ret.ptr == nil { return ret } | ||||
| 	if ret.ptr == o.ptr { return (*Id)(unsafe.Pointer(o)) } | ||||
| 	return ret`) | ||||
| } | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ var ( | |||
| ) | ||||
| 
 | ||||
| func dbg(f string, xs ...interface{}) { | ||||
| 	if Debug { | ||||
| 	if Debug && false { | ||||
| 		fmt.Printf(f, xs...) | ||||
| 	} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										140
									
								
								wrap/main.go
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								wrap/main.go
									
									
									
									
									
								
							|  | @ -70,7 +70,7 @@ func NewWrapper(debug bool) *Wrapper { | |||
| 	} | ||||
| 	ret.goImports["unsafe"] = true | ||||
| 	if Gogc { | ||||
| 		types.Gogc = true | ||||
| 		ret.goImports["runtime"] = true | ||||
| 	} | ||||
| 	ret.goTypes.WriteString(` | ||||
| type Id struct { | ||||
|  | @ -152,8 +152,6 @@ type Method struct { | |||
| 	Unavailable                  bool | ||||
| } | ||||
| 
 | ||||
| // ShouldFinalize returns true on a method that returns an object that should
 | ||||
| // have a GC finalizer.
 | ||||
| func (m *Method) ShouldFinalize() bool { | ||||
| 	grtype := m.Type.GoType() | ||||
| 	return Gogc && grtype != "NSAutoreleasePool" && | ||||
|  | @ -162,8 +160,6 @@ func (m *Method) ShouldFinalize() bool { | |||
| } | ||||
| 
 | ||||
| // IsRetained returns true if a given instance method returns a retained object.
 | ||||
| // NSWrap will not send a 'retain' message to these objects before returning
 | ||||
| // them to Go.
 | ||||
| func IsRetained(name string) bool { | ||||
| 	return ( | ||||
| 		(len(name) >= 3 && name[:3] == "new") || | ||||
|  | @ -304,14 +300,10 @@ func (w Wrapper) cparamlist(m *Method) (string, string, string) { | |||
| 	} | ||||
| 	for _, p := range m.Parameters { | ||||
| 		var tp string | ||||
| 		gt := p.Type.GoType() | ||||
| 		wp := types.ShouldWrap(gt) | ||||
| 		switch { | ||||
| 		case len(gt) > 2 && gt[:1] == "*" && types.PtrShouldWrap(gt[1:]): | ||||
| 			tp = "void**" | ||||
| 		case wp || p.Type.IsPointer() || p.Type.Variadic: | ||||
| 		wp := types.ShouldWrap(p.Type.GoType()) | ||||
| 		if wp || p.Type.IsPointer() || p.Type.Variadic { | ||||
| 			tp = "void*" | ||||
| 		default: | ||||
| 		} else { | ||||
| 			tp = p.Type.CType() | ||||
| 		} | ||||
| 		ns = append(ns, p.Vname) | ||||
|  | @ -327,28 +319,21 @@ func (w Wrapper) objcparamlist(m *Method) string { | |||
| 	} | ||||
| 	first := true | ||||
| 	ret := []string{} | ||||
| 	pname := "" | ||||
| 	for _, p := range m.Parameters { | ||||
| 		gt := p.Type.GoType() | ||||
| 		if first { | ||||
| 		if first && !p.Type.Variadic { | ||||
| 			ret = append(ret, m.Name+":"+p.Vname) | ||||
| 			first = false | ||||
| 			pname = m.Name | ||||
| 		} else { | ||||
| 			pname = p.Pname | ||||
| 		} | ||||
| 		switch { | ||||
| 		case len(gt) > 2 && gt[:1] == "*" && types.PtrShouldWrap(gt[1:]): | ||||
| 			ret = append(ret, pname+":("+p.Type.Node.CType()+")"+p.Vname) | ||||
| 		case !p.Type.Variadic: | ||||
| 			ret = append(ret, pname+":"+p.Vname) | ||||
| 			first = false | ||||
| 		case p.Type.Variadic: | ||||
| 			str := []string{p.Pname + ", arr[0]"} | ||||
| 			for i := 1; i < w.Vaargs; i++ { | ||||
| 				str = append(str, "arr["+strconv.Itoa(i)+"]") | ||||
| 			if p.Type.Variadic { | ||||
| 				str := []string{p.Pname + ", arr[0]"} | ||||
| 				for i := 1; i < w.Vaargs; i++ { | ||||
| 					str = append(str, "arr["+strconv.Itoa(i)+"]") | ||||
| 				} | ||||
| 				str = append(str, "nil") | ||||
| 				ret = append(ret, strings.Join(str, ", ")) | ||||
| 			} else { | ||||
| 				ret = append(ret, p.Pname+":"+p.Vname) | ||||
| 			} | ||||
| 			str = append(str, "nil") | ||||
| 			ret = append(ret, strings.Join(str, ", ")) | ||||
| 		} | ||||
| 	} | ||||
| 	return strings.Join(ret, " ") | ||||
|  | @ -362,13 +347,11 @@ var goreserved map[string]bool = map[string]bool{ | |||
| 	"len":   true, | ||||
| } | ||||
| 
 | ||||
| func (w *Wrapper) gpntp(m *Method) ([]string, []string, []string, []*types.Type, string) { | ||||
| func (w *Wrapper) gpntp(m *Method) ([]string, []string, []*types.Type, string) { | ||||
| 	ns := []string{} | ||||
| 	pnames := []string{} | ||||
| 	tps := []*types.Type{} | ||||
| 	if !m.ClassMethod { | ||||
| 		ns = append(ns, "o") | ||||
| 		pnames = append(pnames, m.Name) | ||||
| 		tps = append(tps, types.NewTypeFromString(m.Class+"*", "")) | ||||
| 	} | ||||
| 	for i, p := range m.Parameters { | ||||
|  | @ -380,7 +363,6 @@ func (w *Wrapper) gpntp(m *Method) ([]string, []string, []string, []*types.Type, | |||
| 			gname = fmt.Sprintf("p%d",i) | ||||
| 		} | ||||
| 		ns = append(ns, gname) | ||||
| 		pnames = append(pnames, p.Pname) | ||||
| 		tps = append(tps, p.Type) | ||||
| 	} | ||||
| 	w.processTypes(tps) | ||||
|  | @ -412,7 +394,7 @@ func (w *Wrapper) gpntp(m *Method) ([]string, []string, []string, []*types.Type, | |||
| 		} | ||||
| 		ret = append(ret, ns[i]+" "+gt) | ||||
| 	} | ||||
| 	return ns, pnames, snames, tps, strings.Join(ret, ", ") | ||||
| 	return ns, snames, tps, strings.Join(ret, ", ") | ||||
| } | ||||
| 
 | ||||
| type Interface struct { | ||||
|  | @ -901,17 +883,11 @@ func (c *Char) Free() { | |||
| } | ||||
| 
 | ||||
| func (w *Wrapper) StringHelpers() { | ||||
| 	ufree := "" | ||||
| 	if Gogc { | ||||
| 		ufree = "utf8.Free()\n\t" | ||||
| 	} | ||||
| 	w.goHelpers.WriteString(fmt.Sprintf(` | ||||
| 	w.goHelpers.WriteString(` | ||||
| func (o *NSString) String() string { | ||||
| 	utf8 := o.UTF8String() | ||||
| 	ret := utf8.String() | ||||
| 	%sreturn ret | ||||
| 	return o.UTF8String().String() | ||||
| } | ||||
| `,ufree)) | ||||
| `) | ||||
| } | ||||
| 
 | ||||
| func (w *Wrapper) EnumeratorHelpers() { | ||||
|  | @ -1067,7 +1043,7 @@ func (w *Wrapper) _processMethod(m *Method, fun bool) { | |||
| 	} else { | ||||
| 		cmtype = m.Type.CTypeAttrib() | ||||
| 	} | ||||
| 	ns, pnames, snames, tps, gplist := w.gpntp(m) | ||||
| 	ns, snames, tps, gplist := w.gpntp(m) | ||||
| 	if gname == grtype { // avoid name conflicts between methods and types
 | ||||
| 		gname = "Get" + gname | ||||
| 	} | ||||
|  | @ -1155,8 +1131,7 @@ func %s%s(%s) %s { | |||
| }`, cret, cobj, w.objcparamlist(m))) | ||||
| 		} | ||||
| 	default: | ||||
| 		//if Gogc && !m.isVoid() {
 | ||||
| 		if Gogc { | ||||
| 		if Gogc && !m.isVoid() { | ||||
| 			rtn := "" | ||||
| 			if types.PtrShouldWrap(m.Type.GoType()) { | ||||
| 				switch { | ||||
|  | @ -1164,7 +1139,7 @@ func %s%s(%s) %s { | |||
| 					if grtype != "*NSAutoreleasePool" && constructor { | ||||
| 			// retain objects returned by class constructor methods
 | ||||
| 						rtn = ` | ||||
| 		if(ret != nil) { [ret retain]; }` | ||||
| 		[ret retain];` | ||||
| 					} | ||||
| 
 | ||||
| 				// do not retain new, alloc, init and copy methods
 | ||||
|  | @ -1174,48 +1149,19 @@ func %s%s(%s) %s { | |||
| 				// by default, for instance methods, retain
 | ||||
| 				// if returning a new object
 | ||||
| 					rtn = ` | ||||
| 		if (ret != nil && ret != o) { [ret retain]; }` | ||||
| 		if (o != ret) { [ret retain]; }` | ||||
| 				} | ||||
| 			} | ||||
| 			rtns := []string{} | ||||
| 		// for pointers to pointers, assume length 1 unless there is a
 | ||||
| 		// parameter named "range" or "count".
 | ||||
| 			rlength := "i<1" | ||||
| 			for i,n := range pnames { | ||||
| 				vn := strings.ReplaceAll(ns[i],"_","") | ||||
| 				if n == "range" { | ||||
| 					rlength = "i<" + vn + ".length" | ||||
| 				} | ||||
| 				if n == "count" { | ||||
| 					rlength = "i<" + vn | ||||
| 				} | ||||
| 			} | ||||
| 			for i,n := range ns { | ||||
| 				if snames[i] == "" { | ||||
| 					continue | ||||
| 				} | ||||
| 				rtns = append(rtns,fmt.Sprintf(` | ||||
| 		for(int i=0;%s;i++) { | ||||
| 			if(%s[i] == 0) { break; } | ||||
| 			[(id)%s[i] retain]; | ||||
| 		} | ||||
| 	`, rlength, n, n)) | ||||
| 			} | ||||
| 			var retdecl, reteq, retretn, dup1, dup2 string | ||||
| 			if !m.isVoid() { | ||||
| 				retdecl = fmt.Sprintf("%s ret;\n\t", m.Type.CTypeAttrib()) | ||||
| 				reteq = "ret = " | ||||
| 				retretn = "\n\treturn ret;\n" | ||||
| 				if m.Type.CType() == "char*" { | ||||
| 					dup1 = "strdup(" | ||||
| 					dup2 = ")" | ||||
| 				} | ||||
| 			var ar1, ar2 string | ||||
| 			if constructor || true { | ||||
| 				ar1 = "@autoreleasepool {\n\t\t" | ||||
| 				ar2 = "\n	}" | ||||
| 			} | ||||
| 			w.cCode.WriteString(fmt.Sprintf( | ||||
| `	%s@autoreleasepool { | ||||
| 		%s%s[%s %s]%s;%s%s | ||||
| 	}%s | ||||
| }`, retdecl, reteq, dup1, cobj, w.objcparamlist(m), dup2, rtn, strings.Join(rtns,"\n\t"), retretn)) | ||||
| `	%s ret; | ||||
| 	%sret = [%s %s];%s%s | ||||
| 	return ret; | ||||
| }`, m.Type.CTypeAttrib(), ar1, cobj, w.objcparamlist(m), rtn, ar2)) | ||||
| 		} else { | ||||
| 			w.cCode.WriteString(fmt.Sprintf(`	%s[%s %s]; | ||||
| }`, cret, cobj, w.objcparamlist(m))) | ||||
|  | @ -1236,7 +1182,6 @@ func %s%s(%s) %s { | |||
| 			dbg2 = fmt.Sprintf(`fmt.Printf("GC finalizer (%s): release %%p -> %%p\n", o, o.ptr) | ||||
| 		`, cls) | ||||
| 		} | ||||
| 		w.goImports["runtime"] = true | ||||
| 		w.goCode.WriteString(fmt.Sprintf(` | ||||
| func (o *%s) GC() { | ||||
| 	if o.ptr == nil { return } | ||||
|  | @ -1971,7 +1916,7 @@ func %s%s(%s)%s { | |||
| 			gocode.WriteString(fmt.Sprintf(` | ||||
| func (o *%s) Super%s(%s) %s { | ||||
| `, gname, gnames[i], strings.Join(earglist[1:], ", "), grtype)) | ||||
| 			ns, _, snames, tps, _ := w.gpntp(m) | ||||
| 			ns, snames, tps, _ := w.gpntp(m) | ||||
| 			lparm := len(tps) - 1 | ||||
| 			if len(tps) > 0 && tps[lparm].Variadic { | ||||
| 				vn := ns[lparm] | ||||
|  | @ -2212,31 +2157,14 @@ func (w *Wrapper) Wrap(toproc []string) { | |||
| 	for k := range w.goImports { | ||||
| 		imports = append(imports,"\t\"" + k + "\"") | ||||
| 	} | ||||
| 	startThread := "" | ||||
| 	goInit := "" | ||||
| 	if w.Interfaces["NSThread"] != nil { | ||||
| 		startThread = ` | ||||
| void | ||||
| NSWrap_init() { | ||||
| 	[[NSThread new] start]; // put the runtime into multi-threaded mode
 | ||||
| } | ||||
| ` | ||||
| 		goInit = ` | ||||
| func init() { | ||||
| 	C.NSWrap_init() | ||||
| } | ||||
| ` | ||||
| 	} | ||||
| 	of.WriteString(fmt.Sprintf(` | ||||
| %s | ||||
| */ | ||||
| import "C" | ||||
| 
 | ||||
| import ( | ||||
| %s | ||||
| ) | ||||
| %s | ||||
| `,startThread,strings.Join(imports,"\n"),goInit)) | ||||
| `,strings.Join(imports,"\n"))) | ||||
| 	of.WriteString(w.goTypes.String()) | ||||
| 	of.WriteString(w.goConst.String()) | ||||
| 	of.WriteString(w.goHelpers.String()) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user