|
| 1 | +"""A2: proof identities from RelDump relations. id_full(m) = H(il(m), sorted il of the transitive same-assembly callee |
| 2 | +closure, plus external callee closures resolved in the dependency dumps); id_typed(m) = the same over callees whose |
| 3 | +return type is disposable (the callees that can produce the returned object). Bodiless callees contribute a marker.""" |
| 4 | +import json, hashlib, sys, collections, time |
| 5 | +S='/tmp/claude-0/-home-user/8a9da608-aa27-5449-8306-1fae9426f9b9/scratchpad' |
| 6 | +def load(path): |
| 7 | + d=json.load(open(path)); by=collections.defaultdict(list) |
| 8 | + for r in d['records']: by[r['metadata_name']].append(r) |
| 9 | + for t,hsh in d.get('cctors',{}).items(): by[t+'::.cctor'].append({'metadata_name':t+'::.cctor','il_hash':hsh,'callees':[],'external_callees':[],'ret_disposable':True,'ret_is_type_parameter':False,'static_field_types':[]}) |
| 10 | + return d, by |
| 11 | +def closure(name, dumps, typed, seen=None): |
| 12 | + """dumps: list of (by) dicts to resolve names; returns the set of (name, il_hash or 'NOBODY') reached""" |
| 13 | + seen=set() if seen is None else seen |
| 14 | + if name in seen: return seen |
| 15 | + seen.add(name) |
| 16 | + recs=None |
| 17 | + for by in dumps: |
| 18 | + if name in by: recs=by[name]; break |
| 19 | + if not recs: return seen |
| 20 | + for r in recs: |
| 21 | + for c in list(r['callees'])+list(r['external_callees'])+[t+'::.cctor' for t in r.get('static_field_types',[])]: |
| 22 | + if typed: |
| 23 | + crec=None |
| 24 | + for by in dumps: |
| 25 | + if c in by: crec=by[c]; break |
| 26 | + if crec is not None and not any(x['ret_disposable'] or x.get('ret_is_type_parameter') for x in crec): continue |
| 27 | + closure(c, dumps, typed, seen) |
| 28 | + return seen |
| 29 | +def ident(name, dumps, typed): |
| 30 | + cl=closure(name, dumps, typed); parts=[] |
| 31 | + for n in sorted(cl): |
| 32 | + recs=None |
| 33 | + for by in dumps: |
| 34 | + if n in by: recs=by[n]; break |
| 35 | + if recs is None: parts.append(n+'=EXTERNAL_UNRESOLVED'); continue |
| 36 | + parts.append(n+'='+'|'.join(sorted((x['il_hash'] or 'NOBODY') for x in recs))) |
| 37 | + return hashlib.sha256('\n'.join(parts).encode()).hexdigest()[:16], len(cl), sum(1 for p in parts if p.endswith('EXTERNAL_UNRESOLVED') or 'NOBODY' in p) |
| 38 | +out={} |
| 39 | +# --- A1 fixture sensitivity and no-op stability |
| 40 | +A=f'{S}/lab/sc/a1/out' |
| 41 | +_,cv1=load(f'{A}/rel-caller.v1.json'); _,hv1=load(f'{A}/rel-helper.v1.json'); _,hv1b=load(f'{A}/rel-helper.v1b.json'); _,hv2=load(f'{A}/rel-helper.v2.json') |
| 42 | +sens=[] |
| 43 | +for m in ['MakeDirect','MakeOneHop','MakeTwoHop','MakeVirtual','MakeViaProperty','MakeGeneric']: |
| 44 | + n='CallerLib.Caller::'+m |
| 45 | + f1,s1,u1=ident(n,[cv1,hv1],False); f2,s2,u2=ident(n,[cv1,hv2],False); f1b,_,_=ident(n,[cv1,hv1b],False) |
| 46 | + t1,ts1,_=ident(n,[cv1,hv1],True); t2,ts2,_=ident(n,[cv1,hv2],True) |
| 47 | + sens.append({'variant':m,'id_full_v1':f1,'id_full_v2':f2,'full_changes':f1!=f2,'id_full_size':s1,'unresolved_or_bodiless_in_slice':u1,'noop_rebuild_stable':f1==f1b,'id_typed_v1':t1,'id_typed_v2':t2,'typed_changes':t1!=t2,'id_typed_size':ts1}) |
| 48 | +out['a1_sensitivity']=sens |
| 49 | +mv=lambda p: json.load(open(p))['mvid'] |
| 50 | +out['a1_noop_rebuild']={'helper_v1_mvid':mv(f'{A}/rel-helper.v1.json'),'helper_v1b_mvid':mv(f'{A}/rel-helper.v1b.json'),'il_hashes_identical':all((hv1[k][0]['il_hash']==hv1b[k][0]['il_hash']) for k in hv1)} |
| 51 | +# --- SkiaSharp: identity size, TFM stability, fanout, time |
| 52 | +def skia(tag): return load(f'{S}/lab/ct/relh-SkiaSharp@{tag}.json')[1] |
| 53 | +t0=time.time(); tags=['3.119.4@net6.0','4.152.0@net6.0','4.153.1@net6.0','4.153.1@netstandard2.0']; ids={}; sizes={} |
| 54 | +for tag in tags: |
| 55 | + by=skia(tag); ids[tag]={}; sizes[tag]=[] |
| 56 | + for n in by: |
| 57 | + if any(r['ret_disposable'] for r in by[n]): |
| 58 | + f,sz,_=ident(n,[by],False); t,tsz,_=ident(n,[by],True); ids[tag][n]=(f,t); sizes[tag].append((sz,tsz)) |
| 59 | +out['skia_derivation_seconds']=round(time.time()-t0,1) |
| 60 | +rows=['SkiaSharp.SKBitmap::Decode','SkiaSharp.SKBitmap::FromImage','SkiaSharp.SKData::AsStream','SkiaSharp.SKFont::GetTextPath','SkiaSharp.SKPath::ParseSvgPathData','SkiaSharp.SKTypeface::ToFont'] |
| 61 | +def cmp(a,b): |
| 62 | + common=[n for n in ids[a] if n in ids[b]] |
| 63 | + return {'common':len(common),'full_changed':sum(1 for n in common if ids[a][n][0]!=ids[b][n][0]),'typed_changed':sum(1 for n in common if ids[a][n][1]!=ids[b][n][1]), |
| 64 | + 'rows_full_changed':[n for n in rows if n in ids[a] and n in ids[b] and ids[a][n][0]!=ids[b][n][0]],'rows_typed_changed':[n for n in rows if n in ids[a] and n in ids[b] and ids[a][n][1]!=ids[b][n][1]]} |
| 65 | +out['skia_tfm_stability_4.153.1_net6_vs_ns20']=cmp('4.153.1@net6.0','4.153.1@netstandard2.0') |
| 66 | +out['skia_fanout_4.152.0_to_4.153.1']=cmp('4.152.0@net6.0','4.153.1@net6.0') |
| 67 | +out['skia_fanout_3.119.4_to_4.153.1']=cmp('3.119.4@net6.0','4.153.1@net6.0') |
| 68 | +import statistics |
| 69 | +sz=sizes['4.153.1@net6.0']; out['skia_identity_size_4.153.1']={'surface_callables':len(sz),'full_mean':round(statistics.mean(s for s,_ in sz),1),'full_max':max(s for s,_ in sz),'typed_mean':round(statistics.mean(t for _,t in sz),1),'typed_max':max(t for _,t in sz)} |
| 70 | +by=skia('4.153.1@net6.0'); out['skia_row_slices']={n:{'full_size':ident(n,[by],False)[1],'typed_size':ident(n,[by],True)[1],'typed_slice':sorted(closure(n,[by],True))[:12]} for n in rows if n in by} |
| 71 | +json.dump(out,open(f'{S}/lab/sc/a2-results.json','w'),indent=1); print(json.dumps(out,indent=1)[:6000]) |
0 commit comments