-
Notifications
You must be signed in to change notification settings - Fork 35
/
plugin.go
132 lines (114 loc) · 2.92 KB
/
plugin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package hotswap
import (
"crypto/sha1"
"errors"
"fmt"
"plugin"
"reflect"
"runtime/debug"
"sync"
"time"
"github.com/edwingeng/hotswap/vault"
"go.uber.org/atomic"
)
var (
ErrNotExist = errors.New("symbol does not exist")
)
type PluginFuncs struct {
fOnLoad func(data interface{}) error
fOnInit func(sharedVault *vault.Vault) error
fOnFree func()
fExport func() interface{}
fImport func() interface{}
InvokeFunc func(name string, params ...interface{}) (interface{}, error)
fReloadable func() bool
hotswapLiveFuncs func() map[string]interface{}
hotswapLiveTypes func() map[string]func() interface{}
}
type Plugin struct {
Name string
File string
FileSha1 [sha1.Size]byte
When time.Time
Note string
unchanged bool
P *plugin.Plugin `json:"-"`
PluginFuncs `json:"-"`
Deps []string
Refs *atomic.Int64 `json:"-"`
exported interface{}
reloadable bool
freeOnce *sync.Once
}
func newPlugin() *Plugin {
return &Plugin{
Refs: atomic.NewInt64(1),
freeOnce: &sync.Once{},
}
}
func isNil(v interface{}) bool {
if v == nil {
return true
}
switch vv := reflect.ValueOf(v); vv.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
return vv.IsNil()
default:
return false
}
}
func (pl *Plugin) Lookup(symName string, out interface{}) error {
if symName == "" {
return fmt.Errorf("symName cannot be empty. plugin: %s", pl.Name)
}
if isNil(out) {
return fmt.Errorf("out cannot be nil. plugin: %s, symName: %s", pl.Name, symName)
}
outVal := reflect.ValueOf(out)
if k := outVal.Type().Kind(); k != reflect.Ptr {
return fmt.Errorf("out must be a pointer. plugin: %s, symName: %s", pl.Name, symName)
}
sym, err := pl.P.Lookup(symName)
if err != nil {
return ErrNotExist
}
symVal := reflect.ValueOf(sym)
symTyp := symVal.Type()
ele := outVal.Elem()
eleTyp := ele.Type()
switch {
case symTyp.AssignableTo(eleTyp):
ele.Set(symVal)
return nil
case symTyp.Kind() == reflect.Ptr && symTyp.Elem().AssignableTo(eleTyp):
ele.Set(symVal.Elem())
return nil
default:
return fmt.Errorf("failed to assign %s to out. plugin: %s, symTyp: %s, outTyp: %s",
symName, pl.Name, symTyp.String(), outVal.Type().String())
}
}
func (pl *Plugin) invokeExport() (_ interface{}, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("<hotswap:%s> panic: %+v\n%s", pl.Name, r, debug.Stack())
}
}()
return pl.fExport(), nil
}
func (pl *Plugin) invokeImport() (_ interface{}, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("<hotswap:%s> panic: %+v\n%s", pl.Name, r, debug.Stack())
}
}()
return pl.fImport(), nil
}
func (pl *Plugin) invokeReloadable() (_ bool, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("<hotswap:%s> panic: %+v\n%s", pl.Name, r, debug.Stack())
}
}()
return pl.fReloadable(), nil
}