-
Notifications
You must be signed in to change notification settings - Fork 1
/
module.go
129 lines (105 loc) · 2.17 KB
/
module.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
package janet
/*
#cgo CFLAGS: -std=c99
#cgo LDFLAGS: -lm -ldl
#include <janet.h>
#include <api.h>
*/
import "C"
import _ "embed"
import (
"context"
"runtime"
"github.com/sasha-s/go-deadlock"
)
//go:embed go-boot.janet
var GO_BOOT_FILE []byte
type Request interface{}
type VM struct {
deadlock.RWMutex
callbacks map[string]*Callback
evaluate C.Janet
requests chan Request
env *Table
}
func initJanet() {
C.janet_init()
}
func deInitJanet() {
C.janet_deinit()
}
func (v *VM) Env() *Table {
return v.env
}
// Wait for code calls and process them.
func (v *VM) poll(ctx context.Context, ready chan bool) {
// All Janet state is thread-local, so we explicitly want to execute
// all Janet code in the same OS thread.
runtime.LockOSThread()
initJanet()
defer deInitJanet()
// Set up the core environment
env := C.janet_core_env(nil)
v.runCodeUnsafe(GO_BOOT_FILE, "go-boot.janet")
// Then store our evaluation function
var evaluate C.Janet
C.janet_resolve(env, C.janet_csymbol(C.CString("go/evaluate")), &evaluate)
C.janet_gcroot(evaluate)
v.evaluate = evaluate
ready <- true
for {
select {
case <-ctx.Done():
return
case req := <-v.requests:
switch req := req.(type) {
case callRequest:
params := req.Params
v.runCode(params, req.Call)
case fiberRequest:
params := req.Params
v.continueFiber(params, req.Fiber, req.In)
case unlockRequest:
req.Value.unroot()
case functionRequest:
params := req.Params
v.runFunction(
params,
req.Function.function,
req.Args,
)
case resolveRequest:
result, err := v.resolveCallback(
req.Type,
req.Out,
)
var wrapped C.Janet
if err != nil {
wrapped = wrapError(err.Error())
} else {
wrapped = C.wrap_result_value(result)
}
go v.runFiber(
req.Params,
req.Fiber,
v.value(wrapped),
)
case unmarshalRequest:
req.errc <- v.unmarshal(
req.source,
req.dest,
)
}
}
}
}
func New(ctx context.Context) (*VM, error) {
vm := &VM{
requests: make(chan Request),
callbacks: make(map[string]*Callback),
}
vmReady := make(chan bool)
go vm.poll(ctx, vmReady)
<-vmReady
return vm, nil
}