main 3a10209

objc_runtime_darwin_test.go RawHistoryFolder

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
3
4package objc_test
5
6import (
7	"fmt"
8	"log"
9	"reflect"
10	"structs"
11	"testing"
12
13	"github.com/bnema/purego"
14	"github.com/bnema/purego/objc"
15)
16
17func ExampleRegisterClass_helloworld() {
18	class, err := objc.RegisterClass(
19		"FooObject",
20		objc.GetClass("NSObject"),
21		nil,
22		nil,
23		[]objc.MethodDef{
24			{
25				Cmd: objc.RegisterName("run"),
26				Fn: func(self objc.ID, _cmd objc.SEL) {
27					fmt.Println("Hello World!")
28				},
29			},
30		},
31	)
32	if err != nil {
33		panic(err)
34	}
35
36	object := objc.ID(class).Send(objc.RegisterName("new"))
37	object.Send(objc.RegisterName("run"))
38	// Output: Hello World!
39}
40
41func ExampleRegisterClass() {
42	var class objc.Class
43	var (
44		sel_new    = objc.RegisterName("new")
45		sel_init   = objc.RegisterName("init")
46		sel_setBar = objc.RegisterName("setBar:")
47		sel_bar    = objc.RegisterName("bar")
48
49		BarInit = func(id objc.ID, cmd objc.SEL) objc.ID {
50			return id.SendSuper2(class, cmd)
51		}
52	)
53
54	var err error
55	class, err = objc.RegisterClass(
56		"BarObject",
57		objc.GetClass("NSObject"),
58		[]*objc.Protocol{
59			objc.GetProtocol("NSDelegateWindow"),
60		},
61		[]objc.FieldDef{
62			{
63				Name:      "bar",
64				Type:      reflect.TypeFor[int](),
65				Attribute: objc.ReadWrite,
66			},
67			{
68				Name:      "foo",
69				Type:      reflect.TypeFor[bool](),
70				Attribute: objc.ReadWrite,
71			},
72		},
73		[]objc.MethodDef{
74			{
75				Cmd: sel_init,
76				Fn:  BarInit,
77			},
78		},
79	)
80	if err != nil {
81		panic(err)
82	}
83
84	object := objc.ID(class).Send(sel_new)
85	object.Send(sel_setBar, 123)
86	bar := int(object.Send(sel_bar))
87	fmt.Println(bar)
88	// Output: 123
89}
90
91func ExampleIMP() {
92	imp := objc.NewIMP(func(self objc.ID, _cmd objc.SEL, a3, a4, a5, a6, a7, a8, a9 int) {
93		fmt.Println("IMP:", self, _cmd, a3, a4, a5, a6, a7, a8, a9)
94	})
95
96	purego.SyscallN(uintptr(imp), 105, 567, 9, 2, 3, ^uintptr(4), 4, 8, 9)
97	// Output: IMP: 105 567 9 2 3 -5 4 8 9
98}
99
100// This example shows three-level inheritance with Base <- Child1 <- Child2.
101// SendSuper2 takes Child1, the class defining the method.
102func ExampleID_SendSuper2() {
103	_, err := purego.Dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", purego.RTLD_GLOBAL|purego.RTLD_NOW)
104	if err != nil {
105		panic(err)
106	}
107
108	sel := objc.RegisterName("run")
109	base, err := objc.RegisterClass(
110		"SuperExampleBase",
111		objc.GetClass("NSObject"),
112		nil,
113		nil,
114		[]objc.MethodDef{
115			{
116				Cmd: sel,
117				Fn: func(self objc.ID, cmd objc.SEL) {
118					fmt.Println("Base")
119				},
120			},
121		},
122	)
123	if err != nil {
124		panic(err)
125	}
126
127	var child1 objc.Class
128	child1, err = objc.RegisterClass(
129		"SuperExampleChild1",
130		base,
131		nil,
132		nil,
133		[]objc.MethodDef{
134			{
135				Cmd: sel,
136				Fn: func(self objc.ID, cmd objc.SEL) {
137					// Start lookup in the superclass of Child1.
138					fmt.Print("SendSuper2: ")
139					self.SendSuper2(child1, cmd)
140				},
141			},
142		},
143	)
144	if err != nil {
145		panic(err)
146	}
147
148	// Child2 inherits Child1's implementation of run.
149	child2, err := objc.RegisterClass("SuperExampleChild2", child1, nil, nil, nil)
150	if err != nil {
151		panic(err)
152	}
153
154	for _, class := range []objc.Class{child1, child2} {
155		object := objc.ID(class).Send(objc.RegisterName("new"))
156		defer object.Send(objc.RegisterName("release"))
157		if class == child1 {
158			fmt.Println("Child1 instance")
159		} else {
160			fmt.Println("Child2 instance")
161		}
162		object.Send(sel)
163	}
164	// Output:
165	// Child1 instance
166	// SendSuper2: Base
167	// Child2 instance
168	// SendSuper2: Base
169}
170
171func TestSend(t *testing.T) {
172	// NSNumber comes from Foundation so make sure we have linked to that framework.
173	_, err := purego.Dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", purego.RTLD_GLOBAL|purego.RTLD_NOW)
174	if err != nil {
175		t.Fatal(err)
176	}
177	const double = float64(2.34)
178	// Initialize a NSNumber
179	NSNumber := objc.ID(objc.GetClass("NSNumber")).Send(objc.RegisterName("numberWithDouble:"), double)
180	// Then get that number back using the generic Send function.
181	number := objc.Send[float64](NSNumber, objc.RegisterName("doubleValue"))
182	if double != number {
183		t.Failed()
184	}
185}
186
187func ExampleSend() {
188	type NSRange struct {
189		_               structs.HostLayout
190		Location, Range uint
191	}
192	class_NSString := objc.GetClass("NSString")
193	sel_stringWithUTF8String := objc.RegisterName("stringWithUTF8String:")
194
195	fullString := objc.ID(class_NSString).Send(sel_stringWithUTF8String, "Hello, World!\x00")
196	subString := objc.ID(class_NSString).Send(sel_stringWithUTF8String, "lo, Wor\x00")
197
198	r := objc.Send[NSRange](fullString, objc.RegisterName("rangeOfString:"), subString)
199	fmt.Println(r.Location, r.Range)
200	// Output: 3 7
201}
202
203func ExampleSendSuper2() {
204	super, err := objc.RegisterClass(
205		"SuperObject2",
206		objc.GetClass("NSObject"),
207		nil,
208		nil,
209		[]objc.MethodDef{
210			{
211				Cmd: objc.RegisterName("doSomething"),
212				Fn: func(self objc.ID, _cmd objc.SEL) int {
213					return 16
214				},
215			},
216		},
217	)
218	if err != nil {
219		panic(err)
220	}
221
222	child, err := objc.RegisterClass(
223		"ChildObject2",
224		super,
225		nil,
226		nil,
227		[]objc.MethodDef{
228			{
229				Cmd: objc.RegisterName("doSomething"),
230				Fn: func(self objc.ID, _cmd objc.SEL) int {
231					return 24
232				},
233			},
234		},
235	)
236	if err != nil {
237		panic(err)
238	}
239
240	res := objc.SendSuper2[int](objc.ID(child).Send(objc.RegisterName("new")), child, objc.RegisterName("doSomething"))
241	fmt.Println(res)
242	// Output: 16
243}
244
245func ExampleAllocateProtocol() {
246	var p *objc.Protocol
247	if p = objc.AllocateProtocol("MyCustomProtocol"); p != nil {
248		p.AddMethodDescription(objc.RegisterName("isFoo"), "B16@0:8", true, true)
249		var adoptedProtocol *objc.Protocol
250		adoptedProtocol = objc.GetProtocol("NSObject")
251		if adoptedProtocol == nil {
252			log.Fatalln("protocol 'NSObject' does not exist")
253		}
254		p.AddProtocol(adoptedProtocol)
255		p.AddProperty("accessibilityElement", []objc.PropertyAttribute{
256			{Name: &[]byte("T\x00")[0], Value: &[]byte("B\x00")[0]},
257			{Name: &[]byte("G\x00")[0], Value: &[]byte("isBar\x00")[0]},
258		}, true, true)
259		p.Register()
260	}
261
262	p = objc.GetProtocol("MyCustomProtocol")
263
264	for _, protocol := range p.CopyProtocolList() {
265		fmt.Println(protocol.Name())
266	}
267	for _, property := range p.CopyPropertyList(true, true) {
268		fmt.Println(property.Name(), property.Attributes())
269	}
270	for _, method := range p.CopyMethodDescriptionList(true, true) {
271		fmt.Println(method.Name(), method.Types())
272	}
273
274	// Output:
275	// NSObject
276	// accessibilityElement TB,GisBar
277	// isFoo B16@0:8
278}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close