Skip to content

Commit 10ba29a

Browse files
authored
all: add backward support for Go 1.16 (#13)
1 parent 08b9d79 commit 10ba29a

18 files changed

+350
-16
lines changed

.github/workflows/clipboard.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ jobs:
2121
fail-fast: false
2222
matrix:
2323
os: [ubuntu-latest, macos-latest, windows-latest]
24+
go: [ '1.16.x', '1.17.x' ]
2425
steps:
2526
- name: Install and run dependencies (xvfb libx11-dev)
2627
if: ${{ runner.os == 'Linux' }}
@@ -47,24 +48,24 @@ jobs:
4748
- uses: actions/setup-go@v2
4849
with:
4950
stable: 'false'
50-
go-version: '1.17.x'
51+
go-version: ${{ matrix.go }}
5152

52-
- name: Build
53+
- name: Build (${{ matrix.go }})
5354
run: |
5455
go build -o gclip cmd/gclip/main.go
5556
go build -o gclip-gui cmd/gclip-gui/main.go
5657
57-
- name: Run Tests with CGO_ENABLED=1
58+
- name: Run Tests with CGO_ENABLED=1 (${{ matrix.go }})
5859
if: ${{ runner.os == 'Linux' || runner.os == 'macOS'}}
5960
run: |
6061
CGO_ENABLED=1 go test -v -covermode=atomic .
6162
62-
- name: Run Tests with CGO_ENABLED=0
63+
- name: Run Tests with CGO_ENABLED=0 (${{ matrix.go }})
6364
if: ${{ runner.os == 'Linux' || runner.os == 'macOS'}}
6465
run: |
6566
CGO_ENABLED=0 go test -v -covermode=atomic .
6667
67-
- name: Run Tests on Windows
68+
- name: Run Tests on Windows (${{ matrix.go }})
6869
if: ${{ runner.os == 'Windows'}}
6970
run: |
7071
go test -v -covermode=atomic .

clipboard_android.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Written by Changkun Ou <changkun.de>
66

77
//go:build android
8+
// +build android
89

910
#include <android/log.h>
1011
#include <jni.h>

clipboard_android.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Written by Changkun Ou <changkun.de>
66

77
//go:build android
8+
// +build android
89

910
package clipboard
1011

clipboard_darwin.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Written by Changkun Ou <changkun.de>
66

77
//go:build darwin && !ios
8+
// +build darwin,!ios
89

910
package clipboard
1011

clipboard_darwin.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Written by Changkun Ou <changkun.de>
66

77
//go:build darwin && !ios
8+
// +build darwin,!ios
89

910
// Interact with NSPasteboard using Objective-C
1011
// https://developer.apple.com/documentation/appkit/nspasteboard?language=objc

clipboard_ios.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Written by Changkun Ou <changkun.de>
66

77
//go:build ios
8+
// +build ios
89

910
package clipboard
1011

clipboard_ios.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Written by Changkun Ou <changkun.de>
66

77
//go:build ios
8+
// +build ios
89

910
#import <UIKit/UIKit.h>
1011
#import <MobileCoreServices/MobileCoreServices.h>

clipboard_linux.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Written by Changkun Ou <changkun.de>
66

77
//go:build linux && !android
8+
// +build linux,!android
89

910
#include <stdlib.h>
1011
#include <stdio.h>

clipboard_linux.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Written by Changkun Ou <changkun.de>
66

77
//go:build linux && !android
8+
// +build linux,!android
89

910
package clipboard
1011

@@ -34,9 +35,10 @@ import (
3435
"fmt"
3536
"os"
3637
"runtime"
37-
"runtime/cgo"
3838
"time"
3939
"unsafe"
40+
41+
"golang.design/x/clipboard/internal/cgo"
4042
)
4143

4244
const errmsg = `Failed to initialize the X11 display, and the clipboard package

clipboard_nocgo.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//go:build !windows && !cgo
2+
// +build !windows,!cgo
23

34
package clipboard
45

clipboard_windows.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Written by Changkun Ou <changkun.de>
66

77
//go:build windows
8+
// +build windows
89

910
package clipboard
1011

cmd/gclip-gui/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Written by Changkun Ou <changkun.de>
66

77
//go:build android || ios || linux || darwin || windows
8+
// +build android ios linux darwin windows
89

910
// This is a very basic example for verification purpose that
1011
// demonstrates how the golang.design/x/clipboard can interact

example_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Written by Changkun Ou <changkun.de>
66

77
//go:build cgo
8+
// +build cgo
89

910
package clipboard_test
1011

go.mod

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
module golang.design/x/clipboard
22

3-
go 1.17
3+
go 1.16
44

55
require (
66
golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d
77
golang.org/x/mobile v0.0.0-20210716004757-34ab1303b554
88
)
9-
10-
require (
11-
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect
12-
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
13-
)

internal/cgo/handle.go

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Copyright 2021 The golang.design Initiative Authors.
2+
// All rights reserved. Use of this source code is governed
3+
// by a MIT license that can be found in the LICENSE file.
4+
//
5+
// Written by Changkun Ou <changkun.de>
6+
7+
//go:build !go1.17
8+
// +build !go1.17
9+
10+
// Package cgo is an implementation of golang.org/issue/37033.
11+
//
12+
// See golang.org/cl/294670 for code review discussion.
13+
package cgo
14+
15+
import (
16+
"reflect"
17+
"sync"
18+
)
19+
20+
// Handle provides a safe representation to pass Go values between C and
21+
// Go back and forth. The zero value of a handle is not a valid handle,
22+
// and thus safe to use as a sentinel in C APIs.
23+
//
24+
// The underlying type of Handle may change, but the value is guaranteed
25+
// to fit in an integer type that is large enough to hold the bit pattern
26+
// of any pointer. For instance, on the Go side:
27+
//
28+
// package main
29+
//
30+
// /*
31+
// extern void MyGoPrint(unsigned long long handle);
32+
// void myprint(unsigned long long handle);
33+
// */
34+
// import "C"
35+
// import "runtime/cgo"
36+
//
37+
// //export MyGoPrint
38+
// func MyGoPrint(handle C.ulonglong) {
39+
// h := cgo.Handle(handle)
40+
// val := h.Value().(int)
41+
// println(val)
42+
// h.Delete()
43+
// }
44+
//
45+
// func main() {
46+
// val := 42
47+
// C.myprint(C.ulonglong(cgo.NewHandle(val)))
48+
// // Output: 42
49+
// }
50+
//
51+
// and on the C side:
52+
//
53+
// // A Go function
54+
// extern void MyGoPrint(unsigned long long handle);
55+
//
56+
// // A C function
57+
// void myprint(unsigned long long handle) {
58+
// MyGoPrint(handle);
59+
// }
60+
type Handle uintptr
61+
62+
// NewHandle returns a handle for a given value. If a given value is a
63+
// pointer, slice, map, channel, or function that refers to the same
64+
// object, the returned handle will also be the same. Besides, nil value
65+
// must not be used.
66+
//
67+
// The handle is valid until the program calls Delete on it. The handle
68+
// uses resources, and this package assumes that C code may hold on to
69+
// the handle, so a program must explicitly call Delete when the handle
70+
// is no longer needed.
71+
//
72+
// The intended use is to pass the returned handle to C code, which
73+
// passes it back to Go, which calls Value. See an example in the
74+
// comments of the Handle definition.
75+
func NewHandle(v interface{}) Handle {
76+
var k uintptr
77+
78+
rv := reflect.ValueOf(v)
79+
switch rv.Kind() {
80+
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice,
81+
reflect.Map, reflect.Chan, reflect.Func:
82+
if rv.IsNil() {
83+
panic("cgo: cannot use Handle for nil value")
84+
}
85+
86+
k = rv.Pointer()
87+
default:
88+
// Wrap and turn a value parameter into a pointer. This enables
89+
// us to always store the passing object as a pointer, and helps
90+
// to identify which of whose are initially pointers or values
91+
// when Value is called.
92+
v = &wrap{v}
93+
k = reflect.ValueOf(v).Pointer()
94+
}
95+
96+
// v was escaped to the heap because of reflection. As Go do not have
97+
// a moving GC (and possibly lasts true for a long future), it is
98+
// safe to use its pointer address as the key of the global map at
99+
// this moment. The implementation must be reconsidered if moving GC
100+
// is introduced internally in the runtime.
101+
actual, loaded := m.LoadOrStore(k, v)
102+
if !loaded {
103+
return Handle(k)
104+
}
105+
106+
arv := reflect.ValueOf(actual)
107+
switch arv.Kind() {
108+
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice,
109+
reflect.Map, reflect.Chan, reflect.Func:
110+
// The underlying object of the given Go value already have
111+
// its existing handle.
112+
if arv.Pointer() == k {
113+
return Handle(k)
114+
}
115+
116+
// If the loaded pointer is inconsistent with the new pointer,
117+
// it means the address has been used for different objects
118+
// because of GC and its address is reused for a new Go object,
119+
// meaning that the Handle does not call Delete explicitly when
120+
// the old Go value is not needed. Consider this as a misuse of
121+
// a handle, do panic.
122+
panic("cgo: misuse of a Handle")
123+
default:
124+
panic("cgo: Handle implementation has an internal bug")
125+
}
126+
}
127+
128+
// Delete invalidates a handle. This method must be called when C code no
129+
// longer has a copy of the handle, and the program no longer needs the
130+
// Go value that associated with the handle.
131+
//
132+
// The method panics if the handle is invalid already.
133+
func (h Handle) Delete() {
134+
_, ok := m.LoadAndDelete(uintptr(h))
135+
if !ok {
136+
panic("cgo: misuse of an invalid Handle")
137+
}
138+
}
139+
140+
// Value returns the associated Go value for a valid handle.
141+
//
142+
// The method panics if the handle is invalid already.
143+
func (h Handle) Value() interface{} {
144+
v, ok := m.Load(uintptr(h))
145+
if !ok {
146+
panic("cgo: misuse of an invalid Handle")
147+
}
148+
if wv, ok := v.(*wrap); ok {
149+
return wv.v
150+
}
151+
return v
152+
}
153+
154+
var m = &sync.Map{} // map[uintptr]interface{}
155+
156+
// wrap wraps a Go value.
157+
type wrap struct {
158+
v interface{}
159+
}

internal/cgo/handle_117.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2021 The golang.design Initiative Authors.
2+
// All rights reserved. Use of this source code is governed
3+
// by a MIT license that can be found in the LICENSE file.
4+
//
5+
// Written by Changkun Ou <changkun.de>
6+
7+
//go:build go1.17
8+
// +build go1.17
9+
10+
package cgo
11+
12+
import "runtime/cgo"
13+
14+
type Handle = cgo.Handle
15+
16+
var NewHandle = cgo.NewHandle

0 commit comments

Comments
 (0)