gofront 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/package.json +10 -38
- package/src/backend/js/expressions.js +2060 -389
- package/src/backend/js/index.js +586 -174
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +936 -1644
- package/src/backend/js/statements.js +1349 -132
- package/src/backend/js/stdlib/index.js +51 -94
- package/src/backend/js/templ.js +67 -118
- package/src/backend/js/type-info.js +436 -0
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +80 -0
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +753 -872
- package/src/backend/wasm/emit-builtins.js +914 -237
- package/src/backend/wasm/emit-exprs.js +1456 -195
- package/src/backend/wasm/emit-maps.js +78 -380
- package/src/backend/wasm/emit-stmts.js +1659 -214
- package/src/backend/wasm/emit.js +614 -88
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +491 -0
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +406 -3195
- package/src/backend/wasm/method-expr.js +392 -0
- package/src/backend/wasm/module-emitter.js +1615 -0
- package/src/backend/wasm/monomorph.js +164 -9
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +249 -0
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -37
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/types.js +260 -27
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +241 -11
- package/src/{test-runner.js → cli/test-runner.js} +188 -122
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +202 -386
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +203 -62
- package/src/{templ-lexer.js → lexer/templ-lexer.js} +61 -18
- package/src/{tokens.js → lexer/tokens.js} +2 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +737 -0
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +318 -0
- package/src/lower/bounds.js +547 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +267 -64
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +33 -19
- package/src/lower/json-codecs.js +1379 -0
- package/src/lower/ownership.js +47 -8
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +150 -0
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +83 -15
- package/src/parser/expressions.js +110 -18
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +38 -22
- package/src/{templ-parser.js → parser/templ.js} +179 -17
- package/src/parser/types.js +70 -9
- package/src/resolver.js +21 -1
- package/src/typechecker/assignability.js +291 -19
- package/src/typechecker/const-fold.js +399 -0
- package/src/typechecker/constants.js +282 -0
- package/src/typechecker/expressions.js +1220 -183
- package/src/typechecker/index.js +659 -92
- package/src/typechecker/init-order.js +426 -0
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +613 -62
- package/src/typechecker/statements.js +212 -20
- package/src/typechecker/stdlib/excluded.js +902 -0
- package/src/typechecker/stdlib/index.js +38 -0
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +11 -2
- package/src/typechecker/types.js +268 -514
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +871 -0
- package/stdlib/internal/jsonrt/enc.go +516 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +658 -0
- package/stdlib/io/io.go +502 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +272 -0
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +500 -0
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +526 -0
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +250 -0
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -46
- package/src/backend/js/stdlib/builder.js +0 -63
- package/src/backend/js/stdlib/bytes.js +0 -122
- package/src/backend/js/stdlib/errors.js +0 -44
- package/src/backend/js/stdlib/fmt.js +0 -178
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -972
- package/src/backend/js/stdlib/maps.js +0 -45
- package/src/backend/js/stdlib/math.js +0 -69
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -24
- package/src/backend/js/stdlib/slices.js +0 -77
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -74
- package/src/backend/js/stdlib/strings.js +0 -222
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -34
- package/src/backend/js/stdlib/time.js +0 -128
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- package/src/backend/wasm/stdlib/bytes.js +0 -5039
- package/src/backend/wasm/stdlib/core.js +0 -8903
- package/src/backend/wasm/stdlib/io.js +0 -2161
- package/src/backend/wasm/stdlib/packages.js +0 -2400
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -1200
- package/src/typechecker/stdlib/core.js +0 -801
- package/src/typechecker/stdlib/extended.js +0 -914
- package/src/typechecker/stdlib.js +0 -22
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
//go:build gofront
|
|
2
|
+
|
|
3
|
+
//gofront:target both
|
|
4
|
+
|
|
5
|
+
// Package sync provides the basic synchronization primitives for a single
|
|
6
|
+
// thread of execution.
|
|
7
|
+
//
|
|
8
|
+
// GoFront has no goroutines, so nothing here ever blocks: a lock that would
|
|
9
|
+
// have to wait for another goroutine can never be released, and reports the
|
|
10
|
+
// deadlock Go's runtime would. Like Go, a Mutex, RWMutex, Once or WaitGroup
|
|
11
|
+
// must not be copied after first use; a copy is an independent value that
|
|
12
|
+
// starts in the state of the original (this is not diagnosed).
|
|
13
|
+
package sync
|
|
14
|
+
|
|
15
|
+
// deadlock is the message of Go's runtime when every goroutine is blocked.
|
|
16
|
+
const deadlock = "fatal error: all goroutines are asleep - deadlock!"
|
|
17
|
+
|
|
18
|
+
// A Locker represents an object that can be locked and unlocked.
|
|
19
|
+
type Locker interface {
|
|
20
|
+
Lock()
|
|
21
|
+
Unlock()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// A Mutex is a mutual exclusion lock. The zero value is an unlocked mutex.
|
|
25
|
+
type Mutex struct {
|
|
26
|
+
locked bool
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Lock locks m. Locking a mutex that is already locked can never succeed
|
|
30
|
+
// without another goroutine, so it reports a deadlock.
|
|
31
|
+
func (m *Mutex) Lock() {
|
|
32
|
+
if m.locked {
|
|
33
|
+
panic(deadlock)
|
|
34
|
+
}
|
|
35
|
+
m.locked = true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// TryLock tries to lock m and reports whether it succeeded.
|
|
39
|
+
func (m *Mutex) TryLock() bool {
|
|
40
|
+
if m.locked {
|
|
41
|
+
return false
|
|
42
|
+
}
|
|
43
|
+
m.locked = true
|
|
44
|
+
return true
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Unlock unlocks m. It is a run-time error if m is not locked on entry.
|
|
48
|
+
func (m *Mutex) Unlock() {
|
|
49
|
+
if !m.locked {
|
|
50
|
+
panic("sync: unlock of unlocked mutex")
|
|
51
|
+
}
|
|
52
|
+
m.locked = false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// A RWMutex is a reader/writer mutual exclusion lock. The zero value is an
|
|
56
|
+
// unlocked mutex.
|
|
57
|
+
type RWMutex struct {
|
|
58
|
+
writer bool
|
|
59
|
+
readers int
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Lock locks rw for writing. It reports a deadlock when rw is already locked
|
|
63
|
+
// for reading or writing.
|
|
64
|
+
func (rw *RWMutex) Lock() {
|
|
65
|
+
if rw.writer || rw.readers > 0 {
|
|
66
|
+
panic(deadlock)
|
|
67
|
+
}
|
|
68
|
+
rw.writer = true
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// TryLock tries to lock rw for writing and reports whether it succeeded.
|
|
72
|
+
func (rw *RWMutex) TryLock() bool {
|
|
73
|
+
if rw.writer || rw.readers > 0 {
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
rw.writer = true
|
|
77
|
+
return true
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Unlock unlocks rw for writing. It is a run-time error if rw is not locked
|
|
81
|
+
// for writing on entry.
|
|
82
|
+
func (rw *RWMutex) Unlock() {
|
|
83
|
+
if !rw.writer {
|
|
84
|
+
panic("sync: Unlock of unlocked RWMutex")
|
|
85
|
+
}
|
|
86
|
+
rw.writer = false
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// RLock locks rw for reading. It reports a deadlock when rw is locked for
|
|
90
|
+
// writing.
|
|
91
|
+
func (rw *RWMutex) RLock() {
|
|
92
|
+
if rw.writer {
|
|
93
|
+
panic(deadlock)
|
|
94
|
+
}
|
|
95
|
+
rw.readers++
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// TryRLock tries to lock rw for reading and reports whether it succeeded.
|
|
99
|
+
func (rw *RWMutex) TryRLock() bool {
|
|
100
|
+
if rw.writer {
|
|
101
|
+
return false
|
|
102
|
+
}
|
|
103
|
+
rw.readers++
|
|
104
|
+
return true
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// RUnlock undoes a single RLock call. It is a run-time error if rw is not
|
|
108
|
+
// locked for reading on entry.
|
|
109
|
+
func (rw *RWMutex) RUnlock() {
|
|
110
|
+
if rw.readers <= 0 {
|
|
111
|
+
panic("sync: RUnlock of unlocked RWMutex")
|
|
112
|
+
}
|
|
113
|
+
rw.readers--
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
type rlocker struct {
|
|
117
|
+
rw *RWMutex
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
func (r *rlocker) Lock() { r.rw.RLock() }
|
|
121
|
+
|
|
122
|
+
func (r *rlocker) Unlock() { r.rw.RUnlock() }
|
|
123
|
+
|
|
124
|
+
// RLocker returns a Locker interface that implements the Lock and Unlock
|
|
125
|
+
// methods by calling rw.RLock and rw.RUnlock.
|
|
126
|
+
func (rw *RWMutex) RLocker() Locker {
|
|
127
|
+
return &rlocker{rw: rw}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Once is an object that will perform exactly one action.
|
|
131
|
+
type Once struct {
|
|
132
|
+
done bool
|
|
133
|
+
m Mutex
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Do calls the function f if and only if Do is being called for the first
|
|
137
|
+
// time for this instance of Once. If f panics, Do considers it to have
|
|
138
|
+
// returned; future calls of Do return without calling f. Calling Do from
|
|
139
|
+
// within f reports a deadlock, as in Go.
|
|
140
|
+
func (o *Once) Do(f func()) {
|
|
141
|
+
if !o.done {
|
|
142
|
+
o.doSlow(f)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
func (o *Once) doSlow(f func()) {
|
|
147
|
+
o.m.Lock()
|
|
148
|
+
defer o.m.Unlock()
|
|
149
|
+
if !o.done {
|
|
150
|
+
defer func() { o.done = true }()
|
|
151
|
+
f()
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// A WaitGroup waits for a collection of goroutines to finish. Without
|
|
156
|
+
// goroutines the counter only moves with Add and Done.
|
|
157
|
+
type WaitGroup struct {
|
|
158
|
+
n int
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Add adds delta, which may be negative, to the WaitGroup counter.
|
|
162
|
+
// If the counter becomes negative, Add panics.
|
|
163
|
+
func (wg *WaitGroup) Add(delta int) {
|
|
164
|
+
n := wg.n + delta
|
|
165
|
+
if n < 0 {
|
|
166
|
+
panic("sync: negative WaitGroup counter")
|
|
167
|
+
}
|
|
168
|
+
wg.n = n
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Done decrements the WaitGroup counter by one.
|
|
172
|
+
func (wg *WaitGroup) Done() {
|
|
173
|
+
wg.Add(-1)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Wait returns when the counter is zero. With a positive counter nothing
|
|
177
|
+
// could ever call Done, so it reports a deadlock.
|
|
178
|
+
func (wg *WaitGroup) Wait() {
|
|
179
|
+
if wg.n > 0 {
|
|
180
|
+
panic(deadlock)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// OnceFunc returns a function that invokes f only once. The returned function
|
|
185
|
+
// may be called concurrently.
|
|
186
|
+
//
|
|
187
|
+
// If f panics, the returned function will panic with the same value on every call.
|
|
188
|
+
func OnceFunc(f func()) func() {
|
|
189
|
+
var (
|
|
190
|
+
once Once
|
|
191
|
+
valid bool
|
|
192
|
+
p any
|
|
193
|
+
)
|
|
194
|
+
return func() {
|
|
195
|
+
once.Do(func() {
|
|
196
|
+
defer func() {
|
|
197
|
+
p = recover()
|
|
198
|
+
if !valid {
|
|
199
|
+
panic(p)
|
|
200
|
+
}
|
|
201
|
+
}()
|
|
202
|
+
f()
|
|
203
|
+
valid = true
|
|
204
|
+
})
|
|
205
|
+
if !valid {
|
|
206
|
+
panic(p)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// OnceValue returns a function that invokes f only once and returns the value
|
|
212
|
+
// returned by f. The returned function may be called concurrently.
|
|
213
|
+
//
|
|
214
|
+
// If f panics, the returned function will panic with the same value on every call.
|
|
215
|
+
func OnceValue[T any](f func() T) func() T {
|
|
216
|
+
var (
|
|
217
|
+
once Once
|
|
218
|
+
valid bool
|
|
219
|
+
p any
|
|
220
|
+
result T
|
|
221
|
+
)
|
|
222
|
+
return func() T {
|
|
223
|
+
once.Do(func() {
|
|
224
|
+
defer func() {
|
|
225
|
+
p = recover()
|
|
226
|
+
if !valid {
|
|
227
|
+
panic(p)
|
|
228
|
+
}
|
|
229
|
+
}()
|
|
230
|
+
result = f()
|
|
231
|
+
valid = true
|
|
232
|
+
})
|
|
233
|
+
if !valid {
|
|
234
|
+
panic(p)
|
|
235
|
+
}
|
|
236
|
+
return result
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// OnceValues returns a function that invokes f only once and returns the values
|
|
241
|
+
// returned by f. The returned function may be called concurrently.
|
|
242
|
+
//
|
|
243
|
+
// If f panics, the returned function will panic with the same value on every call.
|
|
244
|
+
func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
|
|
245
|
+
var (
|
|
246
|
+
once Once
|
|
247
|
+
valid bool
|
|
248
|
+
p any
|
|
249
|
+
r1 T1
|
|
250
|
+
r2 T2
|
|
251
|
+
)
|
|
252
|
+
return func() (T1, T2) {
|
|
253
|
+
once.Do(func() {
|
|
254
|
+
defer func() {
|
|
255
|
+
p = recover()
|
|
256
|
+
if !valid {
|
|
257
|
+
panic(p)
|
|
258
|
+
}
|
|
259
|
+
}()
|
|
260
|
+
r1, r2 = f()
|
|
261
|
+
valid = true
|
|
262
|
+
})
|
|
263
|
+
if !valid {
|
|
264
|
+
panic(p)
|
|
265
|
+
}
|
|
266
|
+
return r1, r2
|
|
267
|
+
}
|
|
268
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
//go:build gofront
|
|
2
|
+
|
|
3
|
+
//gofront:target js
|
|
4
|
+
|
|
5
|
+
package testing
|
|
6
|
+
|
|
7
|
+
// Async tests and subtests, for js packages. The body of an `async func`
|
|
8
|
+
// test returns a promise that the call through testing_call keeps; the
|
|
9
|
+
// driver awaits it before it reports the test.
|
|
10
|
+
|
|
11
|
+
// call calls f(t) and returns what the function returned: a promise when f
|
|
12
|
+
// is async, nil otherwise.
|
|
13
|
+
//
|
|
14
|
+
//gofront:intrinsic testing_call
|
|
15
|
+
func call(f any, t any) any
|
|
16
|
+
|
|
17
|
+
func jsCall(f func(*T), t *T) any {
|
|
18
|
+
return call(f, t)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// settle waits for the async subtests of t, in the order they started, and
|
|
22
|
+
// reports them.
|
|
23
|
+
async func (t *T) settle() {
|
|
24
|
+
for len(t.pending) > 0 {
|
|
25
|
+
p := t.pending[0]
|
|
26
|
+
t.pending = t.pending[1:]
|
|
27
|
+
await p.sub.wait(p.res)
|
|
28
|
+
await p.sub.settle()
|
|
29
|
+
p.sub.finish()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// wait waits for the promise of a test body, recording a rejection as a
|
|
34
|
+
// failure of the test.
|
|
35
|
+
async func (t *T) wait(res any) {
|
|
36
|
+
defer t.Guard()
|
|
37
|
+
await res
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// RunAsync is Run for a js package: the bodies of async tests are awaited,
|
|
41
|
+
// in order, and so are the async subtests they start. TestMain cannot use
|
|
42
|
+
// it.
|
|
43
|
+
async func (m *M) RunAsync() int {
|
|
44
|
+
callHook = jsCall
|
|
45
|
+
tests, benches := m.plan()
|
|
46
|
+
for _, tt := range tests {
|
|
47
|
+
t := &T{common: common{name: tt.Name}}
|
|
48
|
+
t.begin()
|
|
49
|
+
await t.wait(t.invoke(tt.F))
|
|
50
|
+
await t.settle()
|
|
51
|
+
t.finish()
|
|
52
|
+
if t.failed {
|
|
53
|
+
m.failures++
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
callHook = nil
|
|
57
|
+
for _, bb := range benches {
|
|
58
|
+
m.runBenchmark(bb)
|
|
59
|
+
}
|
|
60
|
+
return m.conclude()
|
|
61
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
//go:build gofront
|
|
2
|
+
|
|
3
|
+
//gofront:target both
|
|
4
|
+
|
|
5
|
+
package testing
|
|
6
|
+
|
|
7
|
+
import (
|
|
8
|
+
"fmt"
|
|
9
|
+
"time"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// B is a type passed to Benchmark functions to manage benchmark timing and
|
|
13
|
+
// control the number of iterations.
|
|
14
|
+
type B struct {
|
|
15
|
+
common
|
|
16
|
+
// N is the number of iterations the benchmark function must run.
|
|
17
|
+
N int
|
|
18
|
+
timerOn bool
|
|
19
|
+
timerStart int64
|
|
20
|
+
elapsedNs int64
|
|
21
|
+
bytes int64
|
|
22
|
+
targetNs int64
|
|
23
|
+
|
|
24
|
+
// hasSub is set once b.Run has run a sub-benchmark: the parent then
|
|
25
|
+
// reports no result of its own and is not grown.
|
|
26
|
+
hasSub bool
|
|
27
|
+
|
|
28
|
+
loopStarted bool
|
|
29
|
+
loopIters int
|
|
30
|
+
nextCheck int
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func newB(name string, targetNs int64) *B {
|
|
34
|
+
return &B{
|
|
35
|
+
common: common{name: name},
|
|
36
|
+
N: 1,
|
|
37
|
+
timerOn: true,
|
|
38
|
+
timerStart: nanotime(),
|
|
39
|
+
targetNs: targetNs,
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ResetTimer zeroes the elapsed benchmark time.
|
|
44
|
+
func (b *B) ResetTimer() {
|
|
45
|
+
b.timerStart = nanotime()
|
|
46
|
+
b.elapsedNs = 0
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// StartTimer starts timing a test.
|
|
50
|
+
func (b *B) StartTimer() {
|
|
51
|
+
if !b.timerOn {
|
|
52
|
+
b.timerStart = nanotime()
|
|
53
|
+
b.timerOn = true
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// StopTimer stops timing a test.
|
|
58
|
+
func (b *B) StopTimer() {
|
|
59
|
+
if b.timerOn {
|
|
60
|
+
b.elapsedNs += nanotime() - b.timerStart
|
|
61
|
+
b.timerOn = false
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ReportAllocs enables malloc statistics. They are not collected.
|
|
66
|
+
func (b *B) ReportAllocs() {}
|
|
67
|
+
|
|
68
|
+
// ReportMetric adds a custom metric. It is not recorded.
|
|
69
|
+
func (b *B) ReportMetric(n float64, unit string) {}
|
|
70
|
+
|
|
71
|
+
// SetBytes records the number of bytes processed in a single operation.
|
|
72
|
+
func (b *B) SetBytes(n int64) { b.bytes = n }
|
|
73
|
+
|
|
74
|
+
// Elapsed returns the measured elapsed time of the benchmark.
|
|
75
|
+
func (b *B) Elapsed() time.Duration {
|
|
76
|
+
ns := b.elapsedNs
|
|
77
|
+
if b.timerOn {
|
|
78
|
+
ns += nanotime() - b.timerStart
|
|
79
|
+
}
|
|
80
|
+
return time.Duration(ns)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Loop returns true as long as the benchmark should continue running.
|
|
84
|
+
func (b *B) Loop() bool {
|
|
85
|
+
if !b.loopStarted {
|
|
86
|
+
b.loopStarted = true
|
|
87
|
+
b.ResetTimer()
|
|
88
|
+
if b.targetNs == 0 {
|
|
89
|
+
b.targetNs = 1000000000
|
|
90
|
+
}
|
|
91
|
+
b.loopIters = 0
|
|
92
|
+
b.nextCheck = 1
|
|
93
|
+
}
|
|
94
|
+
if b.failed || b.skipped {
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
b.loopIters++
|
|
98
|
+
b.N = b.loopIters
|
|
99
|
+
if b.loopIters >= b.nextCheck {
|
|
100
|
+
elapsed := nanotime() - b.timerStart
|
|
101
|
+
if elapsed >= b.targetNs || b.loopIters >= 1000000000 {
|
|
102
|
+
// This call starts no iteration.
|
|
103
|
+
b.loopIters--
|
|
104
|
+
b.N = b.loopIters
|
|
105
|
+
b.StopTimer()
|
|
106
|
+
return false
|
|
107
|
+
}
|
|
108
|
+
nsPerOp := max(int64(1), elapsed/int64(b.loopIters))
|
|
109
|
+
remaining := b.targetNs - elapsed
|
|
110
|
+
add := max(int64(1), min(int64(100000), remaining/(nsPerOp*2)))
|
|
111
|
+
b.nextCheck = b.loopIters + int(add)
|
|
112
|
+
}
|
|
113
|
+
return true
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
func (b *B) invoke(f func(*B)) {
|
|
117
|
+
defer b.Guard()
|
|
118
|
+
f(b)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Run runs f as a sub-benchmark of b called name, growing b.N until the run
|
|
122
|
+
// takes the target time, and reports its result. It reports whether f
|
|
123
|
+
// succeeded.
|
|
124
|
+
func (b *B) Run(name string, f func(b *B)) bool {
|
|
125
|
+
full := name
|
|
126
|
+
if b.name != "" {
|
|
127
|
+
full = b.name + "/" + name
|
|
128
|
+
}
|
|
129
|
+
if !selected(full) {
|
|
130
|
+
return true
|
|
131
|
+
}
|
|
132
|
+
b.hasSub = true
|
|
133
|
+
sub := newB(full, b.targetNs)
|
|
134
|
+
sub.parent = &b.common
|
|
135
|
+
sub.begin()
|
|
136
|
+
sub.ResetTimer()
|
|
137
|
+
sub.invoke(f)
|
|
138
|
+
sub.grow(f)
|
|
139
|
+
sub.StopTimer()
|
|
140
|
+
sub.runCleanups()
|
|
141
|
+
if sub.failed {
|
|
142
|
+
// A failure in a regrown run did not reach the parent.
|
|
143
|
+
b.Fail()
|
|
144
|
+
}
|
|
145
|
+
if !sub.onlyParent() {
|
|
146
|
+
sub.reportBenchmark()
|
|
147
|
+
}
|
|
148
|
+
return !sub.failed
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// BenchmarkResult contains the results of a benchmark run.
|
|
152
|
+
type BenchmarkResult struct {
|
|
153
|
+
N int
|
|
154
|
+
T time.Duration
|
|
155
|
+
Bytes int64
|
|
156
|
+
MemAllocs uint64
|
|
157
|
+
MemBytes uint64
|
|
158
|
+
Extra map[string]float64
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// NsPerOp returns the "ns/op" metric.
|
|
162
|
+
func (r BenchmarkResult) NsPerOp() int64 {
|
|
163
|
+
if r.N <= 0 {
|
|
164
|
+
return 0
|
|
165
|
+
}
|
|
166
|
+
return int64(r.T) / int64(r.N)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// AllocsPerOp returns the "allocs/op" metric.
|
|
170
|
+
func (r BenchmarkResult) AllocsPerOp() int64 {
|
|
171
|
+
if r.N <= 0 {
|
|
172
|
+
return 0
|
|
173
|
+
}
|
|
174
|
+
return int64(r.MemAllocs) / int64(r.N)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// AllocedBytesPerOp returns the "B/op" metric.
|
|
178
|
+
func (r BenchmarkResult) AllocedBytesPerOp() int64 {
|
|
179
|
+
if r.N <= 0 {
|
|
180
|
+
return 0
|
|
181
|
+
}
|
|
182
|
+
return int64(r.MemBytes) / int64(r.N)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// MemString returns r.AllocedBytesPerOp and r.AllocsPerOp in the same format
|
|
186
|
+
// as 'go test'.
|
|
187
|
+
func (r BenchmarkResult) MemString() string {
|
|
188
|
+
return fmt.Sprintf("%d B/op\t%d allocs/op", r.AllocedBytesPerOp(), r.AllocsPerOp())
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// String returns a summary of the benchmark results.
|
|
192
|
+
func (r BenchmarkResult) String() string {
|
|
193
|
+
s := fmt.Sprintf("%d\t%d ns/op", r.N, r.NsPerOp())
|
|
194
|
+
if r.Bytes > 0 && r.T > 0 {
|
|
195
|
+
mbPerSec := float64(r.Bytes*int64(r.N)) / (float64(r.T) / 1e9) / (1024 * 1024)
|
|
196
|
+
s += fmt.Sprintf("\t%.2f MB/s", mbPerSec)
|
|
197
|
+
}
|
|
198
|
+
return s
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// grow reruns f with a larger b.N until the run takes the target time. A
|
|
202
|
+
// benchmark that calls b.Loop sizes itself.
|
|
203
|
+
func (b *B) grow(f func(*B)) {
|
|
204
|
+
if b.loopStarted || b.hasSub || b.failed || b.skipped {
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
b.StopTimer()
|
|
208
|
+
b.runCleanups()
|
|
209
|
+
n := 1
|
|
210
|
+
for !b.failed && !b.skipped {
|
|
211
|
+
elapsed := int64(b.Elapsed())
|
|
212
|
+
if elapsed >= b.targetNs || n >= 1000000000 {
|
|
213
|
+
break
|
|
214
|
+
}
|
|
215
|
+
var next int
|
|
216
|
+
if elapsed == 0 {
|
|
217
|
+
next = n * 100
|
|
218
|
+
} else {
|
|
219
|
+
next = max(n+1, min(1000000000, int(float64(n)*(float64(b.targetNs)/float64(elapsed))*1.2)))
|
|
220
|
+
}
|
|
221
|
+
if next <= n {
|
|
222
|
+
break
|
|
223
|
+
}
|
|
224
|
+
n = next
|
|
225
|
+
nb := newB(b.name, b.targetNs)
|
|
226
|
+
nb.N = n
|
|
227
|
+
nb.ResetTimer()
|
|
228
|
+
nb.invoke(f)
|
|
229
|
+
nb.StopTimer()
|
|
230
|
+
nb.runCleanups()
|
|
231
|
+
b.N = nb.N
|
|
232
|
+
b.elapsedNs = nb.elapsedNs
|
|
233
|
+
b.timerOn = false
|
|
234
|
+
b.failed = nb.failed
|
|
235
|
+
b.skipped = nb.skipped
|
|
236
|
+
b.logs = nb.logs
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Benchmark benchmarks a single function.
|
|
241
|
+
func Benchmark(f func(b *B)) BenchmarkResult {
|
|
242
|
+
b := newB("Benchmark", 1000000000)
|
|
243
|
+
b.ResetTimer()
|
|
244
|
+
b.invoke(f)
|
|
245
|
+
b.grow(f)
|
|
246
|
+
b.runCleanups()
|
|
247
|
+
return BenchmarkResult{N: b.N, T: b.Elapsed(), Bytes: b.bytes}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// runBenchmark runs one benchmark of the table and prints its line.
|
|
251
|
+
func (m *M) runBenchmark(bb GofrontBenchmark) {
|
|
252
|
+
if cfg.verbose {
|
|
253
|
+
say(dim("=== RUN ") + bb.Name)
|
|
254
|
+
}
|
|
255
|
+
b := newB(bb.Name, 100000000)
|
|
256
|
+
b.ResetTimer()
|
|
257
|
+
b.invoke(bb.F)
|
|
258
|
+
b.grow(bb.F)
|
|
259
|
+
b.runCleanups()
|
|
260
|
+
if b.failed {
|
|
261
|
+
m.failures++
|
|
262
|
+
}
|
|
263
|
+
if !b.onlyParent() {
|
|
264
|
+
b.reportBenchmark()
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// onlyParent reports whether b just ran sub-benchmarks, which reported their
|
|
269
|
+
// own results, so it has none of its own.
|
|
270
|
+
func (b *B) onlyParent() bool {
|
|
271
|
+
return b.hasSub && !b.failed && !b.skipped
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
func (b *B) reportBenchmark() {
|
|
275
|
+
if b.skipped {
|
|
276
|
+
say("--- " + yellow("SKIP") + ": " + b.name)
|
|
277
|
+
b.printLogs(plain)
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
if b.failed {
|
|
281
|
+
say("--- " + bold(red("FAIL")) + ": " + b.name)
|
|
282
|
+
b.printLogs(red)
|
|
283
|
+
return
|
|
284
|
+
}
|
|
285
|
+
elapsed := int64(b.Elapsed())
|
|
286
|
+
nsPerOp := 0.0
|
|
287
|
+
if b.N > 0 {
|
|
288
|
+
nsPerOp = float64(elapsed) / float64(b.N)
|
|
289
|
+
}
|
|
290
|
+
line := fmt.Sprintf("%-20s\t%8d\t%10.0f ns/op", b.name, b.N, nsPerOp)
|
|
291
|
+
if b.bytes > 0 && elapsed > 0 {
|
|
292
|
+
mbPerSec := float64(b.bytes*int64(b.N)) / (float64(elapsed) / 1e9) / (1024 * 1024)
|
|
293
|
+
line += fmt.Sprintf("\t%8.2f MB/s", mbPerSec)
|
|
294
|
+
}
|
|
295
|
+
say(line)
|
|
296
|
+
b.printLogs(plain)
|
|
297
|
+
}
|