code wiki / _hdl_build / nx_dupfunc_test.nx

nx_dupfunc_test.nx source

↩ module page · 130 lines · 6681 B

1// nx_dupfunc_test.nx -- gate for the function-body duplication detector. 2// 3// THE PROPERTY THAT MATTERS is T1: two identical bodies behind DIFFERENT 4// NAMES must hash the same. Every duplicate this tool found in the real tree 5// has a different name in each copy -- the integer-printing helper appears 6// 198 times as vn, t_putn, gn, dn, wn and tn -- so a detector keyed on names 7// would report a clean tree. Ignoring the name is the whole design. 8// 9// T4 is a regression test for a bug that made the tool useless while 10// reporting success: NishiLang has no break, the body scan escaped by 11// assigning its loop variable, and that destroyed the position it had just 12// found. Every body came out empty, so the scan measured 0 functions across 13// 17000 files and printed verdict=GREEN. ★A detector that reports nothing 14// looks exactly like a clean tree. 15// expect_exit: 0 license_tier: ORIGINAL 16import "nx_syscalls.nx" 17import "nx_gatelib.nx" 18import "nx_gate.nx" 19 20func tw(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 21func tn(v: i64) -> i64 { let b: *u8 = sys_mmap(24); var m: i64 = v; if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } let t: *u8 = sys_mmap(24); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 } while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } var i: i64 = 0; while i < k { b[i] = t[k - 1 - i]; i = i + 1 } sys_write(1, b, k); return 0 } 22func slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 23 24func x_hash(buf: *u8, n: i64) -> i64 { 25 var h: i64 = 1469598103934665603 26 var i: i64 = 0 27 while i < n { h = h ^ (buf[i] as i64); h = h * 1099511628211; i = i + 1 } 28 return h 29} 30 31// The normalisation under test, re-derived independently: strip comments, 32// drop the signature up to the first brace, collapse whitespace runs. 33func x_norm(src: *u8, n: i64, out: *u8) -> i64 { 34 let tmp: *u8 = sys_mmap(8192) 35 let sn: i64 = gl_strip_comments(src, n, tmp) 36 var p: i64 = 0 37 var start: i64 = 0 - 1 38 while p < sn { 39 if start < 0 { if tmp[p] == (123 as u8) { start = p + 1 } } 40 p = p + 1 41 } 42 if start < 0 { start = sn } 43 var w: i64 = 0 44 var q: i64 = start 45 var ws: i64 = 0 46 while q < sn { 47 let c: i64 = tmp[q] as i64 48 var isws: i64 = 0 49 if c == 32 { isws = 1 } 50 if c == 9 { isws = 1 } 51 if c == 10 { isws = 1 } 52 if c == 13 { isws = 1 } 53 if isws == 1 { if ws == 0 { out[w] = 32 as u8; w = w + 1; ws = 1 } } 54 else { out[w] = tmp[q]; w = w + 1; ws = 0 } 55 q = q + 1 56 } 57 return w 58} 59 60func main() -> i64 { 61 var pass: i64 = 0 62 var total: i64 = 0 63 let a: *u8 = sys_mmap(8192) 64 let b: *u8 = sys_mmap(8192) 65 66 // --- T1 ***NAMES MUST BE IGNORED.*** The same body behind two 67 // different names is the shape every real duplicate in this tree 68 // takes. If this fails the tool reports a clean tree forever. --- 69 total = total + 1 70 let f1: *u8 = "func gs_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != 0 { n = n + 1 } return n }" as *u8 71 let f2: *u8 = "func gq_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != 0 { n = n + 1 } return n }" as *u8 72 let n1: i64 = x_norm(f1, slen(f1), a) 73 let n2: i64 = x_norm(f2, slen(f2), b) 74 tw("T1 gs_len vs gq_len (identical body, different name): hashes " as *u8) 75 var ok1: i64 = 1 76 if x_hash(a, n1) != x_hash(b, n2) { ok1 = 0 } 77 if n1 <= 0 { ok1 = 0 } 78 if ok1 == 1 { pass = pass + 1; tw("MATCH: PASS\n" as *u8) } else { tw("DIFFER: FAIL\n" as *u8) } 79 80 // --- T2 NON-VACUITY. A genuinely different body must NOT match, or 81 // the tool would report the entire tree as one duplicate. --- 82 total = total + 1 83 let f3: *u8 = "func gs_len(s: *u8) -> i64 { var n: i64 = 1; while s[n] != 0 { n = n + 2 } return n }" as *u8 84 let n3: i64 = x_norm(f3, slen(f3), b) 85 tw("T2 same name, DIFFERENT body: hashes " as *u8) 86 if x_hash(a, n1) != x_hash(b, n3) { pass = pass + 1; tw("DIFFER: PASS\n" as *u8) } else { tw("MATCH: FAIL\n" as *u8) } 87 88 // --- T3 Comments and indentation are not differences. A copied 89 // function usually keeps the code and rewrites the comment. --- 90 total = total + 1 91 let f4: *u8 = "func other(s: *u8) -> i64 {\n // a completely different explanation\n var n: i64 = 0; while s[n] != 0 { n = n + 1 }\n return n }" as *u8 92 let n4: i64 = x_norm(f4, slen(f4), b) 93 tw("T3 same body, different comment and indentation: hashes " as *u8) 94 if x_hash(a, n1) == x_hash(b, n4) { pass = pass + 1; tw("MATCH: PASS\n" as *u8) } else { tw("DIFFER: FAIL\n" as *u8) } 95 96 // --- T4 ***THE BUG THAT REPORTED SUCCESS WHILE MEASURING NOTHING.*** 97 // The body must be non-empty and must NOT contain the signature. 98 // Both halves matter: empty bodies made the scan return 0 99 // functions, and a body still carrying its signature would put 100 // the name back into the hash and undo T1. --- 101 total = total + 1 102 var ok4: i64 = 1 103 if n1 < 20 { ok4 = 0 } 104 if gl_has_token(a, n1, "gs_len" as *u8, 6) != 0 { ok4 = 0 } 105 if gl_has_token(a, n1, "return" as *u8, 6) != 1 { ok4 = 0 } 106 tw("T4 extracted body is non-empty (" as *u8); tn(n1) 107 tw("B), excludes the signature, and contains the code: " as *u8) 108 if ok4 == 1 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 109 110 // --- T5 A body-less declaration must not crash or masquerade as a 111 // duplicate of every other body-less thing in a meaningful way. --- 112 total = total + 1 113 let f5: *u8 = "func nobody(s: *u8) -> i64" as *u8 114 let n5: i64 = x_norm(f5, slen(f5), b) 115 tw("T5 signature with no body normalises to " as *u8); tn(n5) 116 tw(" bytes (below any sane min-body threshold): " as *u8) 117 if n5 < 20 { pass = pass + 1; tw("PASS\n" as *u8) } else { tw("FAIL\n" as *u8) } 118 119 // --- T6 The hash discriminates on a ONE CHARACTER change, which is the 120 // resolution a body comparison needs to be trustworthy. --- 121 total = total + 1 122 let f6: *u8 = "func gs_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != 0 { n = n + 1 } return m }" as *u8 123 let n6: i64 = x_norm(f6, slen(f6), b) 124 tw("T6 one character changed (return n -> return m): hashes " as *u8) 125 if x_hash(a, n1) != x_hash(b, n6) { pass = pass + 1; tw("DIFFER: PASS\n" as *u8) } else { tw("MATCH: FAIL\n" as *u8) } 126 127 tw("DUPFUNC-GATE passed " as *u8); tn(pass); tw("/" as *u8); tn(total) 128 if pass == total { tw(" verdict=GREEN\n" as *u8); sys_exit(0); return 0 } 129 tw(" verdict=RED\n" as *u8); sys_exit(1); return 1 130}