diffgenome 0.1.0__py3-none-any.whl

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.
Files changed (53) hide show
  1. diffgenome/__init__.py +7 -0
  2. diffgenome/__main__.py +240 -0
  3. diffgenome/_collectors/go/dg/dg.go +623 -0
  4. diffgenome/_collectors/go/go.mod +3 -0
  5. diffgenome/_collectors/go/instrument/facts.go +346 -0
  6. diffgenome/_collectors/go/instrument/main.go +484 -0
  7. diffgenome/_collectors/node/instrument.js +289 -0
  8. diffgenome/_collectors/node/jest-setup.js +40 -0
  9. diffgenome/_collectors/node/package-lock.json +35 -0
  10. diffgenome/_collectors/node/package.json +11 -0
  11. diffgenome/_collectors/node/runtime.js +426 -0
  12. diffgenome/ambiguity.py +122 -0
  13. diffgenome/api.py +67 -0
  14. diffgenome/change.py +86 -0
  15. diffgenome/change_artifact.py +310 -0
  16. diffgenome/collect/__init__.py +2 -0
  17. diffgenome/collect/go_test.py +271 -0
  18. diffgenome/collect/node_jest.py +319 -0
  19. diffgenome/collect/py_monitoring.py +985 -0
  20. diffgenome/collect/py_runtime.py +116 -0
  21. diffgenome/collect/py_symbols.py +238 -0
  22. diffgenome/collect/pytest_plugin.py +130 -0
  23. diffgenome/compose.py +469 -0
  24. diffgenome/dependence.py +264 -0
  25. diffgenome/evaluate.py +669 -0
  26. diffgenome/frontends/__init__.py +0 -0
  27. diffgenome/frontends/python_ir.py +335 -0
  28. diffgenome/genome.py +1016 -0
  29. diffgenome/genome_pipeline.py +674 -0
  30. diffgenome/genome_prompt.py +33 -0
  31. diffgenome/genome_state.py +2118 -0
  32. diffgenome/graph.py +426 -0
  33. diffgenome/llm.py +189 -0
  34. diffgenome/model.py +364 -0
  35. diffgenome/mvp.py +398 -0
  36. diffgenome/probe.py +509 -0
  37. diffgenome/projection.py +308 -0
  38. diffgenome/py.typed +0 -0
  39. diffgenome/render.py +118 -0
  40. diffgenome/report.py +363 -0
  41. diffgenome/resolve.py +37 -0
  42. diffgenome/runtime.py +74 -0
  43. diffgenome/runtime_evidence.py +261 -0
  44. diffgenome/sandbox.py +166 -0
  45. diffgenome/serialize.py +96 -0
  46. diffgenome/sites.py +19 -0
  47. diffgenome/static_types.py +69 -0
  48. diffgenome/structure.py +462 -0
  49. diffgenome-0.1.0.dist-info/METADATA +139 -0
  50. diffgenome-0.1.0.dist-info/RECORD +53 -0
  51. diffgenome-0.1.0.dist-info/WHEEL +4 -0
  52. diffgenome-0.1.0.dist-info/entry_points.txt +2 -0
  53. diffgenome-0.1.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,623 @@
1
+ // Package dg is the diffgenome Go runtime: instrumented code reports function entry,
2
+ // exit (results or panic) and test stimuli here, and one Execution (the diffgenome event
3
+ // protocol, JSON) is written per test or subtest. Copied into the workspace copy of the
4
+ // target as <module>/internal/diffgenome/dg so no network or module changes are needed.
5
+ //
6
+ // Attribution: per-goroutine call stacks keyed by goroutine id. Goroutines spawned by
7
+ // instrumented code start with no parent (their calls hang off the stimulus root); spawn
8
+ // causality is recorded as a limitation, not guessed.
9
+ package dg
10
+
11
+ import (
12
+ "bytes"
13
+ "crypto/sha256"
14
+ "encoding/hex"
15
+ "encoding/json"
16
+ "fmt"
17
+ "os"
18
+ "path/filepath"
19
+ "reflect"
20
+ "runtime"
21
+ "sort"
22
+ "strconv"
23
+ "strings"
24
+ "sync"
25
+ )
26
+
27
+ const (
28
+ maxArgs = 8
29
+ digestDepth = 3
30
+ digestWidth = 16
31
+ )
32
+
33
+ // Meta is what the instrumenter knows statically about a function.
34
+ type Meta struct {
35
+ Sym string
36
+ Origin string // "repo" | "test"
37
+ File string
38
+ Line int
39
+ Params []string
40
+ Claim string // for test doubles: the in-repo symbol this method stands in for
41
+ Relation string // how Claim was established, e.g. "mocks-interface:Store"
42
+ Recv any // the receiver, for state facts (nil for functions)
43
+ }
44
+
45
+ type symbol struct {
46
+ ID string `json:"id"`
47
+ Origin string `json:"origin"`
48
+ Location *location `json:"location"`
49
+ }
50
+
51
+ type location struct {
52
+ Path string `json:"path"`
53
+ Line int `json:"line"`
54
+ }
55
+
56
+ type node struct {
57
+ Type string `json:"type"`
58
+ ID int `json:"id"`
59
+ Parent *int `json:"parent"`
60
+ Symbol string `json:"symbol,omitempty"`
61
+ Collector int `json:"collector"`
62
+ Args [][3]string `json:"args"`
63
+ Thread int `json:"thread,omitempty"`
64
+ Outcome string `json:"outcome"`
65
+ Result string `json:"result"`
66
+ // substitution fields (present only on substitution nodes; see MarshalJSON)
67
+ Mechanism string `json:"mechanism,omitempty"`
68
+ Substitute string `json:"substitute,omitempty"`
69
+ Claimed *string `json:"-"`
70
+ Relation string `json:"relation,omitempty"`
71
+ Path []string `json:"-"`
72
+ State [][3]string `json:"-"`
73
+ StateAfter [][3]string `json:"-"`
74
+ }
75
+
76
+ // MarshalJSON emits exactly the protocol's fields for each node type.
77
+ func (n *node) MarshalJSON() ([]byte, error) {
78
+ m := map[string]any{"type": n.Type, "id": n.ID, "parent": n.Parent, "collector": n.Collector,
79
+ "args": n.Args, "outcome": n.Outcome, "result": n.Result, "state": n.State}
80
+ if n.Args == nil {
81
+ m["args"] = [][3]string{}
82
+ }
83
+ if n.State == nil {
84
+ m["state"] = [][3]string{}
85
+ }
86
+ if n.Type == "call" {
87
+ m["symbol"] = n.Symbol
88
+ m["thread"] = n.Thread
89
+ sa := n.StateAfter
90
+ if sa == nil {
91
+ sa = [][3]string{}
92
+ }
93
+ m["state_after"] = sa
94
+ } else {
95
+ m["mechanism"] = n.Mechanism
96
+ m["substitute"] = n.Substitute
97
+ m["claimed_target"] = n.Claimed
98
+ m["relation"] = n.Relation
99
+ p := n.Path
100
+ if p == nil {
101
+ p = []string{}
102
+ }
103
+ m["path"] = p
104
+ }
105
+ return json.Marshal(m)
106
+ }
107
+
108
+ type branchObs struct {
109
+ Site string `json:"site"`
110
+ Outcome bool `json:"outcome"`
111
+ Node int `json:"node"`
112
+ Seq int `json:"seq"`
113
+ }
114
+
115
+ type execution struct {
116
+ ref string
117
+ nodes []*node
118
+ branches []branchObs
119
+ symbols map[string]symbol
120
+ stacks map[int64][]int // goroutine id -> node ids
121
+ goroutine map[int64]int // goroutine id -> thread number
122
+ }
123
+
124
+ // Ctx is returned by Enter and consumed by Exit/Panic.
125
+ type Ctx struct {
126
+ id int
127
+ sub int // substitution wrapper node id, or -1
128
+ gid int64
129
+ exec *execution
130
+ recv any // receiver, for the state view on exit
131
+ }
132
+
133
+ var (
134
+ mu sync.Mutex
135
+ active []*execution
136
+ outDir = os.Getenv("DIFFGENOME_OUT")
137
+ stim = envOr("DIFFGENOME_STIMULUS", "existing_test")
138
+ rev = os.Getenv("DIFFGENOME_REVISION")
139
+ )
140
+
141
+ func envOr(k, d string) string {
142
+ if v := os.Getenv(k); v != "" {
143
+ return v
144
+ }
145
+ return d
146
+ }
147
+
148
+ func goid() int64 {
149
+ var buf [64]byte
150
+ n := runtime.Stack(buf[:], false)
151
+ fields := bytes.Fields(buf[:n])
152
+ if len(fields) >= 2 {
153
+ id, _ := strconv.ParseInt(string(fields[1]), 10, 64)
154
+ return id
155
+ }
156
+ return 0
157
+ }
158
+
159
+ // ----------------------------------------------------------------------------- summaries
160
+
161
+ func shape(v any) string {
162
+ if v == nil {
163
+ return "nil"
164
+ }
165
+ rv := reflect.ValueOf(v)
166
+ switch rv.Kind() {
167
+ case reflect.Slice, reflect.Array, reflect.Map, reflect.String:
168
+ if rv.Kind() == reflect.String {
169
+ return "string"
170
+ }
171
+ return fmt.Sprintf("%s[%d]", rv.Type().String(), rv.Len())
172
+ case reflect.Ptr:
173
+ if rv.IsNil() {
174
+ return rv.Type().String() + "(nil)"
175
+ }
176
+ return rv.Type().String()
177
+ }
178
+ return rv.Type().String()
179
+ }
180
+
181
+ func canonical(v any, depth int) (string, bool) {
182
+ if v == nil {
183
+ return "nil", true
184
+ }
185
+ rv := reflect.ValueOf(v)
186
+ return canonicalValue(rv, depth)
187
+ }
188
+
189
+ func canonicalValue(rv reflect.Value, depth int) (string, bool) {
190
+ switch rv.Kind() {
191
+ case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
192
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
193
+ return fmt.Sprintf("%s:%v", rv.Kind(), rv.Interface()), true
194
+ case reflect.String:
195
+ return "string:" + strconv.Quote(rv.String()), true
196
+ case reflect.Ptr, reflect.Interface:
197
+ if rv.IsNil() {
198
+ return "nil", true
199
+ }
200
+ if depth == 0 {
201
+ return "", false
202
+ }
203
+ return canonicalValue(rv.Elem(), depth-1)
204
+ case reflect.Slice, reflect.Array:
205
+ if depth == 0 || rv.Len() > digestWidth {
206
+ return "", false
207
+ }
208
+ parts := make([]string, 0, rv.Len())
209
+ for i := 0; i < rv.Len(); i++ {
210
+ c, ok := canonicalValue(rv.Index(i), depth-1)
211
+ if !ok {
212
+ return "", false
213
+ }
214
+ parts = append(parts, c)
215
+ }
216
+ return "array[" + strings.Join(parts, ",") + "]", true
217
+ case reflect.Map:
218
+ if depth == 0 || rv.Len() > digestWidth {
219
+ return "", false
220
+ }
221
+ items := make([]string, 0, rv.Len())
222
+ iter := rv.MapRange()
223
+ for iter.Next() {
224
+ k, ok1 := canonicalValue(iter.Key(), depth-1)
225
+ val, ok2 := canonicalValue(iter.Value(), depth-1)
226
+ if !ok1 || !ok2 {
227
+ return "", false
228
+ }
229
+ items = append(items, k+"="+val)
230
+ }
231
+ sort.Strings(items)
232
+ return "map{" + strings.Join(items, ",") + "}", true
233
+ case reflect.Struct:
234
+ if depth == 0 || rv.NumField() > digestWidth {
235
+ return "", false
236
+ }
237
+ t := rv.Type()
238
+ items := make([]string, 0, rv.NumField())
239
+ for i := 0; i < rv.NumField(); i++ {
240
+ f := t.Field(i)
241
+ if !f.IsExported() {
242
+ continue
243
+ }
244
+ c, ok := canonicalValue(rv.Field(i), depth-1)
245
+ if !ok {
246
+ return "", false
247
+ }
248
+ items = append(items, f.Name+"="+c)
249
+ }
250
+ return t.String() + "{" + strings.Join(items, ",") + "}", true
251
+ }
252
+ return "", false
253
+ }
254
+
255
+ func digest(v any) string {
256
+ c, ok := canonical(v, digestDepth)
257
+ if !ok {
258
+ return ""
259
+ }
260
+ h := sha256.Sum256([]byte(c))
261
+ return hex.EncodeToString(h[:])[:16]
262
+ }
263
+
264
+ func bucket(rv reflect.Value) (string, bool) {
265
+ if !rv.IsValid() {
266
+ return "none", true
267
+ }
268
+ switch rv.Kind() {
269
+ case reflect.Bool:
270
+ if rv.Bool() {
271
+ return "bool:true", true
272
+ }
273
+ return "bool:false", true
274
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
275
+ v := rv.Int()
276
+ if v == 0 {
277
+ return "num:zero", true
278
+ } else if v > 0 {
279
+ return "num:pos", true
280
+ }
281
+ return "num:neg", true
282
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
283
+ if rv.Uint() == 0 {
284
+ return "num:zero", true
285
+ }
286
+ return "num:pos", true
287
+ case reflect.Float32, reflect.Float64:
288
+ v := rv.Float()
289
+ if v == 0 {
290
+ return "num:zero", true
291
+ } else if v > 0 {
292
+ return "num:pos", true
293
+ }
294
+ return "num:neg", true
295
+ case reflect.String:
296
+ if rv.Len() == 0 {
297
+ return "str:empty", true
298
+ }
299
+ return "str:nonempty", true
300
+ case reflect.Slice, reflect.Map, reflect.Array:
301
+ n := rv.Len()
302
+ if n == 0 {
303
+ return "coll:empty", true
304
+ } else if n == 1 {
305
+ return "coll:one", true
306
+ }
307
+ return "coll:many", true
308
+ case reflect.Ptr, reflect.Interface:
309
+ if rv.IsNil() {
310
+ return "none", true
311
+ }
312
+ return "obj:" + rv.Elem().Type().String(), true
313
+ case reflect.Struct:
314
+ return "obj:" + rv.Type().String(), true
315
+ case reflect.Func, reflect.Chan:
316
+ return "", false
317
+ }
318
+ return "", false
319
+ }
320
+
321
+ func stateFacts(recv any) [][3]string {
322
+ facts := [][3]string{}
323
+ if recv == nil {
324
+ return facts
325
+ }
326
+ rv := reflect.ValueOf(recv)
327
+ for rv.Kind() == reflect.Ptr {
328
+ if rv.IsNil() {
329
+ return facts
330
+ }
331
+ rv = rv.Elem()
332
+ }
333
+ facts = append(facts, [3]string{"self:type", "obj:" + rv.Type().String(), digest(rv.Type().String())})
334
+ if rv.Kind() != reflect.Struct {
335
+ return facts
336
+ }
337
+ t := rv.Type()
338
+ for i := 0; i < rv.NumField() && i < digestWidth; i++ {
339
+ f := t.Field(i)
340
+ b, ok := bucket(rv.Field(i))
341
+ if !ok {
342
+ continue
343
+ }
344
+ d := ""
345
+ if strings.HasPrefix(b, "obj:") {
346
+ d = digest(b[4:])
347
+ }
348
+ facts = append(facts, [3]string{"self." + f.Name, b, d})
349
+ }
350
+ return facts
351
+ }
352
+
353
+ func argShapes(names []string, values []any) [][3]string {
354
+ out := [][3]string{}
355
+ for i, v := range values {
356
+ if i >= maxArgs {
357
+ break
358
+ }
359
+ name := fmt.Sprintf("arg%d", i)
360
+ if i < len(names) && names[i] != "" {
361
+ name = names[i]
362
+ }
363
+ out = append(out, [3]string{name, shape(v), digest(v)})
364
+ }
365
+ return out
366
+ }
367
+
368
+ // ----------------------------------------------------------------------------- executions
369
+
370
+ func current() *execution {
371
+ if len(active) == 0 {
372
+ return nil
373
+ }
374
+ return active[len(active)-1]
375
+ }
376
+
377
+ func (e *execution) intern(id, origin, file string, line int) {
378
+ if _, ok := e.symbols[id]; ok {
379
+ return
380
+ }
381
+ var loc *location
382
+ if file != "" {
383
+ loc = &location{Path: file, Line: line}
384
+ }
385
+ e.symbols[id] = symbol{ID: id, Origin: origin, Location: loc}
386
+ }
387
+
388
+ func (e *execution) originOf(nodeID int) string {
389
+ n := e.nodes[nodeID]
390
+ if n.Type != "call" {
391
+ return "unknown"
392
+ }
393
+ if s, ok := e.symbols[n.Symbol]; ok {
394
+ return s.Origin
395
+ }
396
+ return "unknown"
397
+ }
398
+
399
+ func (e *execution) thread(gid int64) int {
400
+ if t, ok := e.goroutine[gid]; ok {
401
+ return t
402
+ }
403
+ t := len(e.goroutine)
404
+ e.goroutine[gid] = t
405
+ return t
406
+ }
407
+
408
+ // Begin marks the start of a stimulus (a test or subtest).
409
+ func Begin(ref string) {
410
+ mu.Lock()
411
+ defer mu.Unlock()
412
+ e := &execution{ref: ref, symbols: map[string]symbol{}, stacks: map[int64][]int{}, goroutine: map[int64]int{}}
413
+ sym := "go:" + ref
414
+ e.intern(sym, "test", "", 0)
415
+ e.nodes = append(e.nodes, &node{Type: "call", ID: 0, Parent: nil, Symbol: sym, Args: [][3]string{}, Outcome: "unknown"})
416
+ e.thread(goid())
417
+ active = append(active, e)
418
+ }
419
+
420
+ // End closes the innermost stimulus and writes its Execution.
421
+ func End(failed bool) {
422
+ mu.Lock()
423
+ defer mu.Unlock()
424
+ e := current()
425
+ if e == nil {
426
+ return
427
+ }
428
+ active = active[:len(active)-1]
429
+ if outDir == "" {
430
+ return
431
+ }
432
+ outcome := "passed"
433
+ if failed {
434
+ outcome = "failed"
435
+ }
436
+ syms := make([]symbol, 0, len(e.symbols))
437
+ for _, s := range e.symbols {
438
+ syms = append(syms, s)
439
+ }
440
+ sort.Slice(syms, func(i, j int) bool { return syms[i].ID < syms[j].ID })
441
+ revision := rev
442
+ var revPtr *string
443
+ if revision != "" {
444
+ revPtr = &revision
445
+ }
446
+ idRev := revision
447
+ if idRev == "" {
448
+ idRev = "unknown"
449
+ }
450
+ doc := map[string]any{
451
+ "id": e.ref + "@" + idRev,
452
+ "stimulus": stim,
453
+ "stimulus_ref": e.ref,
454
+ "outcome": outcome,
455
+ "revision": revPtr,
456
+ "collectors": []map[string]string{
457
+ {"name": "go-source-instrumentation", "plane": "symbol", "fidelity": "complete"},
458
+ },
459
+ "symbols": syms,
460
+ "nodes": e.nodes,
461
+ "branches": append([]branchObs{}, e.branches...),
462
+ "diagnostics": [][2]string{{"goroutines", strconv.Itoa(len(e.goroutine))}},
463
+ }
464
+ _ = os.MkdirAll(outDir, 0o755)
465
+ h := sha256.Sum256([]byte(e.ref))
466
+ name := strings.Map(func(r rune) rune {
467
+ if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '.' || r == '-' {
468
+ return r
469
+ }
470
+ return '_'
471
+ }, e.ref)
472
+ if len(name) > 100 {
473
+ name = name[:100]
474
+ }
475
+ data, _ := json.MarshalIndent(doc, "", " ")
476
+ _ = os.WriteFile(filepath.Join(outDir, name+"-"+hex.EncodeToString(h[:])[:12]+".json"), append(data, '\n'), 0o644)
477
+ }
478
+
479
+ // ----------------------------------------------------------------------------- calls
480
+
481
+ // Enter records a call. Test doubles (test-origin functions entered from repo code) get a
482
+ // substitution wrapper carrying the static claim, so they are non-leaf stand-ins.
483
+ func Enter(m Meta, values ...any) *Ctx {
484
+ mu.Lock()
485
+ defer mu.Unlock()
486
+ e := current()
487
+ if e == nil {
488
+ return nil
489
+ }
490
+ gid := goid()
491
+ e.intern(m.Sym, m.Origin, m.File, m.Line)
492
+ args := argShapes(m.Params, values)
493
+ stack := e.stacks[gid]
494
+ parent := 0
495
+ if len(stack) > 0 {
496
+ parent = stack[len(stack)-1]
497
+ }
498
+ sub := -1
499
+ if m.Origin == "test" && e.originOf(parent) == "repo" {
500
+ mech := "fake"
501
+ if strings.Contains(m.Sym, "<anon>") || !strings.Contains(strings.TrimPrefix(m.Sym, "go:"), ".") {
502
+ mech = "stub"
503
+ }
504
+ var claimed *string
505
+ relation := "none"
506
+ if m.Claim != "" {
507
+ c := m.Claim
508
+ claimed = &c
509
+ relation = m.Relation
510
+ e.intern(c, "repo", "", 0)
511
+ }
512
+ p := parent
513
+ n := &node{Type: "substitution", ID: len(e.nodes), Parent: &p, Collector: 0, Mechanism: mech,
514
+ Substitute: m.Sym, Claimed: claimed, Relation: relation, Path: []string{}, Args: args, Outcome: "unknown",
515
+ State: [][3]string{}}
516
+ e.nodes = append(e.nodes, n)
517
+ sub = n.ID
518
+ parent = sub
519
+ }
520
+ p := parent
521
+ n := &node{Type: "call", ID: len(e.nodes), Parent: &p, Symbol: m.Sym, Collector: 0, Args: args,
522
+ Thread: e.thread(gid), Outcome: "unknown", State: stateFacts(m.Recv)}
523
+ e.nodes = append(e.nodes, n)
524
+ e.stacks[gid] = append(stack, n.ID)
525
+ return &Ctx{id: n.ID, sub: sub, gid: gid, exec: e, recv: m.Recv}
526
+ }
527
+
528
+ func settle(e *execution, id int, outcome, result string) {
529
+ if id < 0 || id >= len(e.nodes) {
530
+ return
531
+ }
532
+ n := e.nodes[id]
533
+ if n.Outcome != "unknown" {
534
+ return
535
+ }
536
+ n.Outcome = outcome
537
+ n.Result = result
538
+ }
539
+
540
+ func (e *execution) pop(gid int64, id int) {
541
+ stack := e.stacks[gid]
542
+ for i := len(stack) - 1; i >= 0; i-- {
543
+ if stack[i] == id {
544
+ e.stacks[gid] = stack[:i]
545
+ return
546
+ }
547
+ }
548
+ }
549
+
550
+ // Exit records a normal return. A trailing non-nil error result is the "raised" outcome
551
+ // in Go's convention; other results are digested.
552
+ func Exit(c *Ctx, results ...any) {
553
+ if c == nil {
554
+ return
555
+ }
556
+ mu.Lock()
557
+ defer mu.Unlock()
558
+ outcome := "returned"
559
+ vals := results
560
+ if n := len(results); n > 0 {
561
+ if err, ok := results[n-1].(error); ok && err != nil {
562
+ outcome = "returned-error:go:" + reflect.TypeOf(err).String()
563
+ c.exec.intern("go:"+reflect.TypeOf(err).String(), "external", "", 0)
564
+ vals = results[:n-1]
565
+ } else if results[n-1] == nil {
566
+ if _, isErrSlot := results[n-1].(error); !isErrSlot {
567
+ vals = results
568
+ }
569
+ }
570
+ }
571
+ res := ""
572
+ if outcome == "returned" {
573
+ if len(vals) == 1 {
574
+ res = digest(vals[0])
575
+ } else if len(vals) > 1 {
576
+ res = digest(vals)
577
+ }
578
+ }
579
+ settle(c.exec, c.id, outcome, res)
580
+ if c.sub >= 0 {
581
+ settle(c.exec, c.sub, outcome, res)
582
+ }
583
+ c.exec.nodes[c.id].StateAfter = stateFacts(c.recv)
584
+ c.exec.pop(c.gid, c.id)
585
+ }
586
+
587
+ // Branch records the outcome of the `if` condition at `site` (an id computed by the
588
+ // instrumenter from the condition's span in the ORIGINAL source) and returns it unchanged.
589
+ func Branch(site string, cond bool) bool {
590
+ mu.Lock()
591
+ defer mu.Unlock()
592
+ e := current()
593
+ if e == nil {
594
+ return cond
595
+ }
596
+ stack := e.stacks[goid()]
597
+ node := 0
598
+ if len(stack) > 0 {
599
+ node = stack[len(stack)-1]
600
+ }
601
+ e.branches = append(e.branches, branchObs{Site: site, Outcome: cond, Node: node, Seq: len(e.nodes)})
602
+ return cond
603
+ }
604
+
605
+ // Panic records a panic unwinding through the call.
606
+ func Panic(c *Ctx, r any) {
607
+ if c == nil {
608
+ return
609
+ }
610
+ mu.Lock()
611
+ defer mu.Unlock()
612
+ t := "go:panic"
613
+ if r != nil {
614
+ t = "go:" + reflect.TypeOf(r).String()
615
+ }
616
+ c.exec.intern(t, "external", "", 0)
617
+ settle(c.exec, c.id, "panic:"+t, "")
618
+ if c.sub >= 0 {
619
+ settle(c.exec, c.sub, "panic:"+t, "")
620
+ }
621
+ c.exec.nodes[c.id].StateAfter = stateFacts(c.recv)
622
+ c.exec.pop(c.gid, c.id)
623
+ }
@@ -0,0 +1,3 @@
1
+ module diffgenome.local/go-collector
2
+
3
+ go 1.22