11import { AuthenticationProgramStateCommon , binToHex , encodeAuthenticationInstructions } from '@bitauth/libauth' ;
2- import { Artifact , LogEntry , RequireStatement } from '@cashscript/utils' ;
2+ import { Artifact , DebugEntry , DebugFrame , LogEntry , RequireStatement , parseInlineRanges } from '@cashscript/utils' ;
33
44export interface ResolvedFrame {
55 sourceMap : string ;
@@ -8,6 +8,7 @@ export interface ResolvedFrame {
88 ipOffset : number ;
99 requires : readonly RequireStatement [ ] ;
1010 logs : readonly LogEntry [ ] ;
11+ inlineRanges ?: string ;
1112 functionName ?: string ;
1213}
1314
@@ -18,6 +19,7 @@ export const rootFrame = (artifact: Artifact): ResolvedFrame => ({
1819 ipOffset : artifact . constructorInputs . length ,
1920 requires : artifact . debug ?. requires ?? [ ] ,
2021 logs : artifact . debug ?. logs ?? [ ] ,
22+ inlineRanges : artifact . debug ?. inlineRanges ,
2123} ) ;
2224
2325export const getActiveBytecode = ( step : AuthenticationProgramStateCommon ) : string =>
@@ -35,13 +37,73 @@ export const resolveFrame = (
3537
3638 if ( ! frame ) return rootFrame ( artifact ) ;
3739
40+ return resolveDebugFrame ( artifact , frame ) ;
41+ } ;
42+
43+ const resolveDebugFrame = ( artifact : Artifact , frame : DebugFrame ) : ResolvedFrame => ( {
44+ sourceMap : frame . sourceMap ,
45+ source : frame . source ?? artifact . source ,
46+ sourceName : frame . sourceFile ?? `${ artifact . contractName } .cash` ,
47+ ipOffset : 0 , // function bodies have no constructor-arg prefix; their ips start at 0
48+ requires : frame . requires ,
49+ logs : frame . logs ,
50+ inlineRanges : frame . inlineRanges ,
51+ functionName : frame . sourceFile ? frame . name : undefined ,
52+ } ) ;
53+
54+ export interface InlineAttribution {
55+ frame : ResolvedFrame ; // the inlined callable, resolved like any other frame
56+ entry : DebugEntry ; // the callable's own entry (frame-local ip and line)
57+ }
58+
59+ export const resolveInlineAttribution = (
60+ artifact : Artifact ,
61+ containerFrame : ResolvedFrame ,
62+ entry : DebugEntry ,
63+ kind : 'requires' | 'logs' ,
64+ ) : InlineAttribution | undefined => {
65+ const range = parseInlineRanges ( containerFrame . inlineRanges ?? '' )
66+ . find ( ( candidate ) => entry . ip >= candidate . startIp && entry . ip <= candidate . endIp ) ;
67+ if ( ! range ) return undefined ;
68+
69+ const inlinedFrame = artifact . debug ?. functions ?. find ( ( candidate ) => candidate . name === range . frameName ) ;
70+ if ( ! inlinedFrame ) return undefined ;
71+
72+ const frameEntry = findMatchingFrameEntry ( containerFrame [ kind ] , inlinedFrame [ kind ] , range , entry ) ;
73+ if ( ! frameEntry ) return undefined ;
74+
75+ const frame = resolveDebugFrame ( artifact , inlinedFrame ) ;
76+
77+ // The callable may itself contain deeper inlined callables: attribute to the innermost one
78+ return resolveInlineAttribution ( artifact , frame , frameEntry , kind ) ?? { frame, entry : frameEntry } ;
79+ } ;
80+
81+ // A log merged from an inlined callable is attributed to the callable's own source
82+ export const attributeLogEntry = (
83+ artifact : Artifact ,
84+ frame : ResolvedFrame ,
85+ logEntry : LogEntry ,
86+ ) : { logEntry : LogEntry , sourceName : string } => {
87+ const inline = resolveInlineAttribution ( artifact , frame , logEntry , 'logs' ) ;
88+ if ( ! inline ) return { logEntry, sourceName : frame . sourceName } ;
89+
3890 return {
39- sourceMap : frame . sourceMap ,
40- source : frame . source ?? artifact . source ,
41- sourceName : frame . sourceFile ?? `${ artifact . contractName } .cash` ,
42- ipOffset : 0 , // function bodies have no constructor-arg prefix; their ips start at 0
43- requires : frame . requires ,
44- logs : frame . logs ,
45- ...( frame . sourceFile !== undefined ? { functionName : frame . name } : { } ) ,
91+ logEntry : { ...logEntry , line : inline . entry . line } ,
92+ sourceName : inline . frame . sourceName ,
4693 } ;
4794} ;
95+
96+ const findMatchingFrameEntry = (
97+ containerEntries : readonly DebugEntry [ ] ,
98+ frameEntries : readonly DebugEntry [ ] ,
99+ range : { startIp : number , endIp : number } ,
100+ entry : DebugEntry ,
101+ ) : DebugEntry | undefined => {
102+ const entriesInRange = containerEntries . filter ( ( candidate ) => (
103+ candidate . ip >= range . startIp && candidate . ip <= range . endIp
104+ ) ) ;
105+
106+ const position = entriesInRange . indexOf ( entry ) ;
107+ if ( position === - 1 ) return undefined ;
108+ return frameEntries [ position ] ;
109+ } ;
0 commit comments