Skip to content
Merged
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
9 changes: 2 additions & 7 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest]
go: ['1.18.x', '1.19.x', '1.20.x', '1.21.x', '1.22.x', '1.23.x', '1.24.x', '1.25.x', '1.26.x']
go: ['1.25.x', '1.26.x']
name: Test with Go ${{ matrix.go }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
defaults:
Expand Down Expand Up @@ -174,21 +174,18 @@ jobs:
go env -u CXX

- name: go test race (no Cgo)
if: ${{ runner.os == 'macOS' && !startsWith(matrix.go, '1.18.') && !startsWith(matrix.go, '1.19.') }}
if: runner.os == 'macOS'
run: |
# -race usually requires Cgo, but macOS is an exception. See https://go.dev/doc/articles/race_detector#Requirements
env CGO_ENABLED=0 go test -race -shuffle=on -v -count=10 ./...

- name: go test race (Cgo)
if: ${{ !startsWith(matrix.go, '1.18.') && !startsWith(matrix.go, '1.19.') }}
run: |
env CGO_ENABLED=1 go test -race -shuffle=on -v -count=10 ./...

minor-arches:
strategy:
matrix:
# Test only the latest two Go versions to save CI time.
# See https://go.dev/doc/devel/release#policy
go: ['1.25.x', '1.26.x']
name: Test with Go ${{ matrix.go }} on Linux minor architectures
runs-on: ubuntu-latest
Expand Down Expand Up @@ -285,8 +282,6 @@ jobs:
strategy:
matrix:
os: ['FreeBSD', 'NetBSD']
# Test only the latest two Go versions to save CI time.
# See https://go.dev/doc/devel/release#policy
go: ['1.25.8', '1.26.1']
name: Test with Go ${{ matrix.go }} on ${{ matrix.os }}
runs-on: ubuntu-22.04
Expand Down
2 changes: 1 addition & 1 deletion callback_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestCallGoFromSharedLib(t *testing.T) {

const want = 10101
cb := purego.NewCallback(goFunc)
for i := 0; i < 10; i++ {
for i := range 10 {
got := callCallback(cb, "a test string")
if got != want {
t.Fatalf("%d: callCallback() got %v want %v", i, got, want)
Expand Down
2 changes: 1 addition & 1 deletion examples/objc/main_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func main() {
[]objc.FieldDef{
{
Name: "bar",
Type: reflect.TypeOf(int(0)),
Type: reflect.TypeFor[int](),
Attribute: objc.ReadWrite,
},
},
Expand Down
5 changes: 2 additions & 3 deletions func.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"unsafe"

"github.com/ebitengine/purego/internal/strings"
"github.com/ebitengine/purego/internal/xreflect"
)

const (
Expand Down Expand Up @@ -155,7 +154,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
// created in NewCallback.
for j := 0; j < arg.NumIn(); j++ {
in := arg.In(j)
if !in.AssignableTo(reflect.TypeOf(CDecl{})) {
if !in.AssignableTo(reflect.TypeFor[CDecl]()) {
continue
}
if j != 0 {
Expand Down Expand Up @@ -303,7 +302,7 @@ func RegisterFunc(fptr any, cfn uintptr) {
}
}
for i, v := range args {
if variadic, ok := xreflect.TypeAssert[[]any](args[i]); ok {
if variadic, ok := reflect.TypeAssert[[]any](args[i]); ok {
if i != len(args)-1 {
panic("purego: can only expand last parameter")
}
Expand Down
2 changes: 1 addition & 1 deletion func_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func TestRegisterFunc_ConcurrentPointerReturn(t *testing.T) {
wg.Add(1)
go func(id int) {
defer wg.Done()
for j := 0; j < 400_000; j++ {
for range 400_000 {
ptr := alloc(5)
if ptr == nil {
continue
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/ebitengine/purego

go 1.18
go 1.25.0
15 changes: 0 additions & 15 deletions internal/xreflect/reflect_go124.go

This file was deleted.

12 changes: 0 additions & 12 deletions internal/xreflect/reflect_go125.go

This file was deleted.

7 changes: 3 additions & 4 deletions objc/objc_block_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"unsafe"

"github.com/ebitengine/purego"
"github.com/ebitengine/purego/internal/xreflect"
)

const (
Expand Down Expand Up @@ -125,7 +124,7 @@ func (*blockCache) encode(typ reflect.Type) *uint8 {
encoding = returnType
}

if typ.NumIn() == 0 || typ.In(0) != reflect.TypeOf(Block(0)) {
if typ.NumIn() == 0 || typ.In(0) != reflect.TypeFor[Block]() {
panic(fmt.Sprintf("objc: A Block implementation must take a Block as its first argument; got %v", typ.String()))
}

Expand Down Expand Up @@ -168,7 +167,7 @@ func (b *blockCache) getLayout(typ reflect.Type) blockLayout {
reflect.MakeFunc(
typ,
func(args []reflect.Value) (results []reflect.Value) {
block, ok := xreflect.TypeAssert[Block](args[0])
block, ok := reflect.TypeAssert[Block](args[0])
if !ok {
panic(fmt.Sprintf("objc: block argument is not a block but %s", args[0].Type().String()))
}
Expand Down Expand Up @@ -263,7 +262,7 @@ func InvokeBlock[T any](block Block, args ...any) (result T, err error) {
callResult := fn.Call(reflectedArgs)

var ok bool
result, ok = xreflect.TypeAssert[T](callResult[0])
result, ok = reflect.TypeAssert[T](callResult[0])
if !ok {
return result, fmt.Errorf("objc: the returned value type %s was not %T", callResult[0].Type().String(), result)
}
Expand Down
37 changes: 13 additions & 24 deletions objc/objc_runtime_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import (
"reflect"
"regexp"
"runtime"
"slices"
stdstrings "strings"
"unicode"
"unsafe"

"github.com/ebitengine/purego"
"github.com/ebitengine/purego/internal/strings"
"github.com/ebitengine/purego/internal/xreflect"
)

// TODO: support try/catch?
Expand Down Expand Up @@ -322,7 +322,7 @@ func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars [
case ReadWrite:
ty := reflect.FuncOf(
[]reflect.Type{
reflect.TypeOf(ID(0)), reflect.TypeOf(SEL(0)), ivar.Type,
reflect.TypeFor[ID](), reflect.TypeFor[SEL](), ivar.Type,
},
nil, false,
)
Expand All @@ -343,7 +343,7 @@ func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars [
// })(unsafe.Pointer(args[0].Interface().(ID)))).v = 123
//
// However, since the type of the variable is unknown reflection is used to actually assign the value
id, ok := xreflect.TypeAssert[ID](args[0])
id, ok := reflect.TypeAssert[ID](args[0])
if !ok {
panic(fmt.Sprintf("objc: id argument is not a ID but %s", args[0].Type().String()))
}
Expand All @@ -358,7 +358,7 @@ func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars [
case ReadOnly:
ty := reflect.FuncOf(
[]reflect.Type{
reflect.TypeOf(ID(0)), reflect.TypeOf(SEL(0)),
reflect.TypeFor[ID](), reflect.TypeFor[SEL](),
},
[]reflect.Type{ivar.Type}, false,
)
Expand All @@ -371,7 +371,7 @@ func RegisterClass(name string, superClass Class, protocols []*Protocol, ivars [
if len(args) != 2 {
panic(fmt.Sprintf("objc: incorrect number of args. expected 2 got %d", len(args)))
}
id, ok := xreflect.TypeAssert[ID](args[0])
id, ok := reflect.TypeAssert[ID](args[0])
if !ok {
panic(fmt.Sprintf("objc: id argument is not a ID but %s", args[0].Type().String()))
}
Expand Down Expand Up @@ -419,11 +419,11 @@ const (
// Source: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100
func encodeType(typ reflect.Type, insidePtr bool) (string, error) {
switch typ {
case reflect.TypeOf(Class(0)):
case reflect.TypeFor[Class]():
return encClass, nil
case reflect.TypeOf(ID(0)), reflect.TypeOf(Block(0)):
case reflect.TypeFor[ID](), reflect.TypeFor[Block]():
return encId, nil
case reflect.TypeOf(SEL(0)):
case reflect.TypeFor[SEL]():
return encSelector, nil
}

Expand Down Expand Up @@ -626,7 +626,7 @@ func (p *Protocol) Register() {
func (p *Protocol) CopyMethodDescriptionList(isRequiredMethod, isInstanceMethod bool) []MethodDescription {
count := uint32(0)
desc := protocol_copyMethodDescriptionList(p, isRequiredMethod, isInstanceMethod, &count)
methods := clone(unsafe.Slice(desc, count))
methods := slices.Clone(unsafe.Slice(desc, count))
free(unsafe.Pointer(desc))
return methods
}
Expand All @@ -635,7 +635,7 @@ func (p *Protocol) CopyMethodDescriptionList(isRequiredMethod, isInstanceMethod
func (p *Protocol) CopyProtocolList() []*Protocol {
count := uint32(0)
desc := protocol_copyProtocolList(p, &count)
protocols := clone(unsafe.Slice(desc, count))
protocols := slices.Clone(unsafe.Slice(desc, count))
free(unsafe.Pointer(desc))
return protocols
}
Expand All @@ -644,7 +644,7 @@ func (p *Protocol) CopyProtocolList() []*Protocol {
func (p *Protocol) CopyPropertyList(isRequiredProperty, isInstanceProperty bool) []Property {
count := uint32(0)
desc := protocol_copyPropertyList2(p, &count, isRequiredProperty, isInstanceProperty)
protocols := clone(unsafe.Slice(desc, count))
protocols := slices.Clone(unsafe.Slice(desc, count))
free(unsafe.Pointer(desc))
return protocols
}
Expand Down Expand Up @@ -691,21 +691,10 @@ func NewIMP(fn any) IMP {
switch {
case ty.NumIn() < 2:
fallthrough
case ty.In(0) != reflect.TypeOf(ID(0)):
case ty.In(0) != reflect.TypeFor[ID]():
fallthrough
case ty.In(1) != reflect.TypeOf(SEL(0)):
case ty.In(1) != reflect.TypeFor[SEL]():
panic("objc: NewIMP must take a (id, SEL) as its first two arguments; got " + ty.String())
}
return IMP(purego.NewCallback(fn))
}

// TODO: remove and use slices.Clone when minimum version for purego is 1.21
func clone[S ~[]E, E any](s S) S {
// Preserve nilness in case it matters.
if s == nil {
return nil
}
// Avoid s[:0:0] as it leads to unwanted liveness when cloning a
// zero-length slice of a large array; see https://go.dev/issue/68488.
return append(S{}, s...)
}
4 changes: 2 additions & 2 deletions objc/objc_runtime_darwin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@ func ExampleRegisterClass() {
[]objc.FieldDef{
{
Name: "bar",
Type: reflect.TypeOf(int(0)),
Type: reflect.TypeFor[int](),
Attribute: objc.ReadWrite,
},
{
Name: "foo",
Type: reflect.TypeOf(false),
Type: reflect.TypeFor[bool](),
Attribute: objc.ReadWrite,
},
},
Expand Down
6 changes: 3 additions & 3 deletions struct_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ func copyStruct8ByteChunks(ptr unsafe.Pointer, size uintptr, addChunk func(uintp
chunk = *(*uintptr)(unsafe.Add(ptr, offset))
} else {
// Read byte-by-byte to avoid reading beyond allocation
for i := uintptr(0); i < remaining; i++ {
for i := range remaining {
b := *(*byte)(unsafe.Add(ptr, offset+i))
chunk |= uintptr(b) << (i * 8)
}
Comment thread
hajimehoshi marked this conversation as resolved.
Expand Down Expand Up @@ -514,7 +514,7 @@ func bundleStackArgs(stackArgs []reflect.Value, addStack func(uintptr)) {
paddingNeeded := uintptr(valAlign) - (currentOffset % uintptr(valAlign))
fields = append(fields, reflect.StructField{
Name: paddingFieldPrefix + strconv.Itoa(fieldIndex),
Type: reflect.ArrayOf(int(paddingNeeded), reflect.TypeOf(byte(0))),
Type: reflect.ArrayOf(int(paddingNeeded), reflect.TypeFor[byte]()),
})
currentOffset += paddingNeeded
fieldIndex++
Expand Down Expand Up @@ -610,7 +610,7 @@ func getCallbackStruct(inType reflect.Type, frame unsafe.Pointer, floatsN *int,

// Pointer on stack (rare: all integer registers exhausted).
if isDarwin {
ptrVal := callbackArgFromStack(frame, *stackSlot, stackByteOffset, reflect.TypeOf(uintptr(0)))
ptrVal := callbackArgFromStack(frame, *stackSlot, stackByteOffset, reflect.TypeFor[uintptr]())
ptr := uintptr(ptrVal.Uint())
return reflect.NewAt(inType, *(*unsafe.Pointer)(unsafe.Pointer(&ptr))).Elem()
}
Expand Down
Loading
Loading