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,346 @@
1
+ package main
2
+
3
+ // Branch-site wrapping and the neutral-IR front end (see src/diffgenome/dependence.py).
4
+ // Both read positions from the ORIGINAL parse, before any rewriting, so a site id is the
5
+ // same whether it is computed here at instrumentation time or by the facts pass.
6
+
7
+ import (
8
+ "crypto/sha256"
9
+ "encoding/hex"
10
+ "fmt"
11
+ "go/ast"
12
+ "go/token"
13
+ )
14
+
15
+ // siteID must stay identical to diffgenome.sites.site_id.
16
+ func siteID(rel string, fset *token.FileSet, cond ast.Expr) (string, []int) {
17
+ a, b := fset.Position(cond.Pos()), fset.Position(cond.End())
18
+ span := []int{a.Line, a.Column, b.Line, b.Column}
19
+ h := sha256.Sum256([]byte(fmt.Sprintf("%s:%d:%d:%d:%d", rel, span[0], span[1], span[2], span[3])))
20
+ return "br:" + hex.EncodeToString(h[:])[:12], span
21
+ }
22
+
23
+ // wrapBranches rewrites every `if cond {` in repo code to `if dg.Branch("<site>", cond) {`.
24
+ // Ids are computed before the rewrite, from original positions.
25
+ func (fc *fileCtx) wrapBranches(f *ast.File) {
26
+ type pending struct {
27
+ s *ast.IfStmt
28
+ id string
29
+ }
30
+ var todo []pending
31
+ ast.Inspect(f, func(n ast.Node) bool {
32
+ if s, ok := n.(*ast.IfStmt); ok && s.Cond != nil {
33
+ id, _ := siteID(fc.rel, fc.fset, s.Cond)
34
+ todo = append(todo, pending{s, id})
35
+ }
36
+ return true
37
+ })
38
+ for _, p := range todo {
39
+ p.s.Cond = &ast.CallExpr{Fun: sel("dg", "Branch"), Args: []ast.Expr{strLit(p.id), p.s.Cond}}
40
+ fc.changed = true
41
+ }
42
+ }
43
+
44
+ // ---- neutral IR
45
+
46
+ type irFn = map[string]any
47
+
48
+ func (fc *fileCtx) lowerFuncs(f *ast.File) []irFn {
49
+ var out []irFn
50
+ for _, d := range f.Decls {
51
+ fd, ok := d.(*ast.FuncDecl)
52
+ if !ok || fd.Body == nil {
53
+ continue
54
+ }
55
+ qual := fd.Name.Name
56
+ var params []string
57
+ if fd.Recv != nil && len(fd.Recv.List) > 0 {
58
+ if rt := receiverType(fd.Recv.List[0].Type); rt != "" {
59
+ qual = rt + "." + fd.Name.Name
60
+ }
61
+ for _, n := range fd.Recv.List[0].Names {
62
+ params = append(params, n.Name)
63
+ }
64
+ }
65
+ if fd.Type.Params != nil {
66
+ for _, fl := range fd.Type.Params.List {
67
+ for _, n := range fl.Names {
68
+ params = append(params, n.Name)
69
+ }
70
+ }
71
+ }
72
+ if params == nil {
73
+ params = []string{}
74
+ }
75
+ out = append(out, irFn{"symbol": fc.symbolFor(qual), "file": fc.rel, "params": params, "body": fc.lowerBlock(fd.Body.List)})
76
+ }
77
+ return out
78
+ }
79
+
80
+ func (fc *fileCtx) line(n ast.Node) int { return fc.fset.Position(n.Pos()).Line }
81
+
82
+ // path returns the dotted access path of an ident/selector chain, or "".
83
+ func path(e ast.Expr) string {
84
+ switch x := e.(type) {
85
+ case *ast.Ident:
86
+ return x.Name
87
+ case *ast.SelectorExpr:
88
+ if b := path(x.X); b != "" {
89
+ return b + "." + x.Sel.Name
90
+ }
91
+ case *ast.ParenExpr:
92
+ return path(x.X)
93
+ case *ast.StarExpr:
94
+ return path(x.X)
95
+ }
96
+ return ""
97
+ }
98
+
99
+ type exprFacts struct {
100
+ uses []string
101
+ calls []map[string]any
102
+ }
103
+
104
+ func (fc *fileCtx) expr(e ast.Expr) exprFacts {
105
+ var ef exprFacts
106
+ seen := map[string]bool{}
107
+ add := func(p string) {
108
+ if p != "" && p != "_" && p != "nil" && p != "true" && p != "false" && !seen[p] {
109
+ seen[p] = true
110
+ ef.uses = append(ef.uses, p)
111
+ }
112
+ }
113
+ var walk func(e ast.Expr)
114
+ walk = func(e ast.Expr) {
115
+ switch x := e.(type) {
116
+ case nil:
117
+ case *ast.Ident, *ast.SelectorExpr:
118
+ if p := path(x); p != "" {
119
+ add(p)
120
+ return
121
+ }
122
+ if s, ok := x.(*ast.SelectorExpr); ok {
123
+ walk(s.X)
124
+ }
125
+ case *ast.CallExpr:
126
+ if s, ok := x.Fun.(*ast.SelectorExpr); ok {
127
+ if p := path(s.X); p != "" {
128
+ add(p)
129
+ } else {
130
+ walk(s.X)
131
+ }
132
+ }
133
+ args := [][]string{}
134
+ addr := []string{}
135
+ for _, a := range x.Args {
136
+ sub := fc.expr(a)
137
+ if len(sub.uses) == 0 {
138
+ sub.uses = []string{"?"}
139
+ }
140
+ args = append(args, sub.uses)
141
+ for _, u := range sub.uses {
142
+ if u != "?" {
143
+ add(u)
144
+ }
145
+ }
146
+ ef.calls = append(ef.calls, sub.calls...)
147
+ if ue, ok := a.(*ast.UnaryExpr); ok && ue.Op == token.AND {
148
+ if id, ok := ue.X.(*ast.Ident); ok {
149
+ addr = append(addr, id.Name)
150
+ }
151
+ }
152
+ }
153
+ ef.calls = append(ef.calls, map[string]any{"callee": exprString(x.Fun), "args": args, "line": fc.line(x), "addr": addr})
154
+ case *ast.UnaryExpr:
155
+ walk(x.X)
156
+ case *ast.BinaryExpr:
157
+ walk(x.X)
158
+ walk(x.Y)
159
+ case *ast.ParenExpr:
160
+ walk(x.X)
161
+ case *ast.StarExpr:
162
+ walk(x.X)
163
+ case *ast.IndexExpr:
164
+ walk(x.X)
165
+ walk(x.Index)
166
+ case *ast.TypeAssertExpr:
167
+ walk(x.X)
168
+ case *ast.CompositeLit:
169
+ for _, el := range x.Elts {
170
+ if kv, ok := el.(*ast.KeyValueExpr); ok {
171
+ walk(kv.Value)
172
+ } else {
173
+ walk(el)
174
+ }
175
+ }
176
+ case *ast.KeyValueExpr:
177
+ walk(x.Value)
178
+ case *ast.SliceExpr:
179
+ walk(x.X)
180
+ case *ast.FuncLit, *ast.BasicLit:
181
+ }
182
+ }
183
+ walk(e)
184
+ if ef.uses == nil {
185
+ ef.uses = []string{}
186
+ }
187
+ if ef.calls == nil {
188
+ ef.calls = []map[string]any{}
189
+ }
190
+ return ef
191
+ }
192
+
193
+ func (fc *fileCtx) exprs(es []ast.Expr) exprFacts {
194
+ var out exprFacts
195
+ out.uses, out.calls = []string{}, []map[string]any{}
196
+ for _, e := range es {
197
+ f := fc.expr(e)
198
+ out.uses = append(out.uses, f.uses...)
199
+ out.calls = append(out.calls, f.calls...)
200
+ }
201
+ return out
202
+ }
203
+
204
+ func (fc *fileCtx) lowerBlock(stmts []ast.Stmt) []map[string]any {
205
+ out := []map[string]any{}
206
+ for _, s := range stmts {
207
+ out = append(out, fc.lowerStmt(s)...)
208
+ }
209
+ return out
210
+ }
211
+
212
+ func (fc *fileCtx) lowerStmt(s ast.Stmt) []map[string]any {
213
+ line := fc.line(s)
214
+ switch x := s.(type) {
215
+ case *ast.AssignStmt:
216
+ f := fc.exprs(x.Rhs)
217
+ defs, stores := []string{}, []string{}
218
+ for _, l := range x.Lhs {
219
+ if id, ok := l.(*ast.Ident); ok {
220
+ defs = append(defs, id.Name) // "_" kept: it holds a result position
221
+ } else if p := path(l); p != "" {
222
+ stores = append(stores, p)
223
+ } else if ix, ok := l.(*ast.IndexExpr); ok {
224
+ stores = append(stores, path(ix.X)+"[]")
225
+ } else {
226
+ stores = append(stores, "?")
227
+ }
228
+ }
229
+ if x.Tok != token.ASSIGN && x.Tok != token.DEFINE { // op-assign reads its target
230
+ f.uses = append(f.uses, defs...)
231
+ f.uses = append(f.uses, stores...)
232
+ }
233
+ rhs := x.Rhs[0]
234
+ for {
235
+ if ta, ok := rhs.(*ast.TypeAssertExpr); ok {
236
+ rhs = ta.X
237
+ } else if pe, ok := rhs.(*ast.ParenExpr); ok {
238
+ rhs = pe.X
239
+ } else {
240
+ break
241
+ }
242
+ }
243
+ _, isCall := rhs.(*ast.CallExpr)
244
+ return []map[string]any{{"k": "assign", "line": line, "defs": defs, "stores": stores, "uses": f.uses, "calls": f.calls, "value_is_call": len(x.Rhs) == 1 && isCall}}
245
+ case *ast.IncDecStmt:
246
+ p := path(x.X)
247
+ if id, ok := x.X.(*ast.Ident); ok {
248
+ return []map[string]any{{"k": "assign", "line": line, "defs": []string{id.Name}, "stores": []string{}, "uses": []string{p}, "calls": []map[string]any{}}}
249
+ }
250
+ return []map[string]any{{"k": "assign", "line": line, "defs": []string{}, "stores": []string{p}, "uses": []string{p}, "calls": []map[string]any{}}}
251
+ case *ast.DeclStmt:
252
+ out := []map[string]any{}
253
+ if gd, ok := x.Decl.(*ast.GenDecl); ok {
254
+ for _, sp := range gd.Specs {
255
+ if vs, ok := sp.(*ast.ValueSpec); ok {
256
+ defs := []string{}
257
+ for _, n := range vs.Names {
258
+ defs = append(defs, n.Name)
259
+ }
260
+ f := fc.exprs(vs.Values)
261
+ out = append(out, map[string]any{"k": "assign", "line": line, "defs": defs, "stores": []string{}, "uses": f.uses, "calls": f.calls})
262
+ }
263
+ }
264
+ }
265
+ return out
266
+ case *ast.ExprStmt:
267
+ f := fc.expr(x.X)
268
+ if c, ok := x.X.(*ast.CallExpr); ok {
269
+ if id, ok := c.Fun.(*ast.Ident); ok && id.Name == "panic" {
270
+ return []map[string]any{{"k": "raise", "line": line, "uses": f.uses, "calls": f.calls}}
271
+ }
272
+ }
273
+ return []map[string]any{{"k": "expr", "line": line, "uses": f.uses, "calls": f.calls}}
274
+ case *ast.IfStmt:
275
+ out := []map[string]any{}
276
+ if x.Init != nil {
277
+ out = append(out, fc.lowerStmt(x.Init)...)
278
+ }
279
+ id, span := siteID(fc.rel, fc.fset, x.Cond)
280
+ f := fc.expr(x.Cond)
281
+ var els []map[string]any
282
+ switch e := x.Else.(type) {
283
+ case *ast.BlockStmt:
284
+ els = fc.lowerBlock(e.List)
285
+ case *ast.IfStmt:
286
+ els = fc.lowerStmt(e)
287
+ default:
288
+ els = []map[string]any{}
289
+ }
290
+ out = append(out, map[string]any{"k": "if", "line": line, "site": id, "span": span, "pred": exprString(x.Cond),
291
+ "uses": f.uses, "calls": f.calls, "then": fc.lowerBlock(x.Body.List), "else": els})
292
+ return out
293
+ case *ast.ReturnStmt:
294
+ f := fc.exprs(x.Results)
295
+ return []map[string]any{{"k": "return", "line": line, "uses": f.uses, "calls": f.calls}}
296
+ case *ast.BlockStmt:
297
+ return fc.lowerBlock(x.List)
298
+ case *ast.LabeledStmt:
299
+ return fc.lowerStmt(x.Stmt)
300
+ case *ast.ForStmt:
301
+ f := fc.expr(x.Cond)
302
+ body := []map[string]any{}
303
+ if x.Init != nil {
304
+ body = append(body, fc.lowerStmt(x.Init)...)
305
+ }
306
+ body = append(body, fc.lowerBlock(x.Body.List)...)
307
+ return []map[string]any{{"k": "loop", "line": line, "uses": f.uses, "calls": f.calls, "defs": []string{}, "body": body}}
308
+ case *ast.RangeStmt:
309
+ f := fc.expr(x.X)
310
+ defs := []string{}
311
+ for _, e := range []ast.Expr{x.Key, x.Value} {
312
+ if id, ok := e.(*ast.Ident); ok && id.Name != "_" {
313
+ defs = append(defs, id.Name)
314
+ }
315
+ }
316
+ return []map[string]any{{"k": "loop", "line": line, "uses": f.uses, "calls": f.calls, "defs": defs, "body": fc.lowerBlock(x.Body.List)}}
317
+ case *ast.BranchStmt:
318
+ return []map[string]any{{"k": "break", "line": line}}
319
+ case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt:
320
+ blocks := [][]map[string]any{}
321
+ var body *ast.BlockStmt
322
+ reason := "switch"
323
+ switch y := x.(type) {
324
+ case *ast.SwitchStmt:
325
+ body = y.Body
326
+ case *ast.TypeSwitchStmt:
327
+ body, reason = y.Body, "type-switch"
328
+ case *ast.SelectStmt:
329
+ body, reason = y.Body, "select"
330
+ }
331
+ for _, c := range body.List {
332
+ switch cc := c.(type) {
333
+ case *ast.CaseClause:
334
+ blocks = append(blocks, fc.lowerBlock(cc.Body))
335
+ case *ast.CommClause:
336
+ blocks = append(blocks, fc.lowerBlock(cc.Body))
337
+ }
338
+ }
339
+ return []map[string]any{{"k": "opaque", "line": line, "reason": reason, "blocks": blocks}}
340
+ case *ast.DeferStmt:
341
+ return []map[string]any{{"k": "opaque", "line": line, "reason": "defer"}}
342
+ case *ast.GoStmt:
343
+ return []map[string]any{{"k": "opaque", "line": line, "reason": "go"}}
344
+ }
345
+ return []map[string]any{{"k": "opaque", "line": line, "reason": fmt.Sprintf("%T", s)}}
346
+ }