Skip to content

Commit 02e2bcc

Browse files
committed
fix(live-debugger): exclude params from locals in instrumented code
The locals helper passed to $dd_return included function parameters alongside actual local variables, causing the snapshot to duplicate argument values in both the arguments and locals sections. Change exitVars to only collect local variable declarations (not params), and omit the args/locals helpers entirely when they would be empty to reduce instrumented bundle size.
1 parent 519bb2f commit 02e2bcc

2 files changed

Lines changed: 80 additions & 28 deletions

File tree

packages/plugins/live-debugger/src/transform/index.test.ts

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -257,27 +257,33 @@ describe('transformCode', () => {
257257
});
258258

259259
describe('hoisted variable capture', () => {
260-
it('should generate entry and exit helper functions', () => {
260+
it('should generate args and locals helpers', () => {
261261
const result = transformCode({
262262
...BASE_OPTIONS,
263263
code: 'function f(a, b) { const c = 1; return a + b + c; }',
264264
});
265265

266-
// Entry helper captures params only
267266
expect(result.code).toMatch(/\$dd_e\d+ = \(\) => \(\{a, b\}\)/);
268-
// Exit helper captures params + locals
269-
expect(result.code).toMatch(/\$dd_l\d+ = \(\) => \(\{a, b, c\}\)/);
267+
expect(result.code).toMatch(/\$dd_l\d+ = \(\) => \(\{c\}\)/);
270268
});
271269

272-
it('should emit a single shared helper when entry and exit vars are identical', () => {
270+
it('should omit the locals helper when there are no locals', () => {
273271
const result = transformCode({
274272
...BASE_OPTIONS,
275273
code: 'function f(a, b) { return a + b; }',
276274
});
277275

278-
// Only the entry helper should be emitted
279276
expect(result.code).toMatch(/\$dd_e\d+ = \(\) => \(\{a, b\}\)/);
280-
// No exit helper should be present
277+
expect(result.code).not.toMatch(/\$dd_l\d+/);
278+
});
279+
280+
it('should omit both helpers when there are no params and no locals', () => {
281+
const result = transformCode({
282+
...BASE_OPTIONS,
283+
code: 'function f() { return 1; }',
284+
});
285+
286+
expect(result.code).not.toMatch(/\$dd_e\d+/);
281287
expect(result.code).not.toMatch(/\$dd_l\d+/);
282288
});
283289
});
@@ -668,6 +674,43 @@ describe('transformCode', () => {
668674
return lines.map((s) => s.trimStart()).join('\n');
669675
}
670676

677+
it('should produce the expected output for a function with no arguments', () => {
678+
const result = transformCode({
679+
...BASE_OPTIONS,
680+
code: 'function getTime() { return Date.now(); }',
681+
});
682+
683+
expect(normalizeCode(result.code)).toBe(
684+
normalizeCode(
685+
"function getTime() {const $dd_p0 = $dd_probes('src/utils.ts;getTime');",
686+
' try {',
687+
' let $dd_rv0;',
688+
' if ($dd_p0) $dd_entry($dd_p0, this); return ($dd_rv0 = Date.now(), $dd_p0 ? $dd_return($dd_p0, $dd_rv0, this) : $dd_rv0); ',
689+
' } catch(e) { if ($dd_p0) $dd_throw($dd_p0, e, this); throw e; }',
690+
'}',
691+
),
692+
);
693+
});
694+
695+
it('should produce the expected output for a function with no arguments but with locals', () => {
696+
const result = transformCode({
697+
...BASE_OPTIONS,
698+
code: 'function getTime() { const now = Date.now(); return now; }',
699+
});
700+
701+
expect(normalizeCode(result.code)).toBe(
702+
normalizeCode(
703+
"function getTime() {const $dd_p0 = $dd_probes('src/utils.ts;getTime');",
704+
' try {',
705+
' const $dd_l0 = () => ({now});',
706+
' let $dd_rv0;',
707+
' if ($dd_p0) $dd_entry($dd_p0, this); const now = Date.now(); return ($dd_rv0 = now, $dd_p0 ? $dd_return($dd_p0, $dd_rv0, this, undefined, $dd_l0()) : $dd_rv0); ',
708+
' } catch(e) { if ($dd_p0) $dd_throw($dd_p0, e, this); throw e; }',
709+
'}',
710+
),
711+
);
712+
});
713+
671714
it('should produce the expected output for a function with a single return', () => {
672715
const result = transformCode({
673716
...BASE_OPTIONS,
@@ -680,7 +723,7 @@ describe('transformCode', () => {
680723
' const $dd_e0 = () => ({a, b});',
681724
' try {',
682725
' let $dd_rv0;',
683-
' if ($dd_p0) $dd_entry($dd_p0, this, $dd_e0()); return ($dd_rv0 = a + b, $dd_p0 ? $dd_return($dd_p0, $dd_rv0, this, $dd_e0(), $dd_e0()) : $dd_rv0); ',
726+
' if ($dd_p0) $dd_entry($dd_p0, this, $dd_e0()); return ($dd_rv0 = a + b, $dd_p0 ? $dd_return($dd_p0, $dd_rv0, this, $dd_e0()) : $dd_rv0); ',
684727
' } catch(e) { if ($dd_p0) $dd_throw($dd_p0, e, this, $dd_e0()); throw e; }',
685728
'}',
686729
),
@@ -698,7 +741,7 @@ describe('transformCode', () => {
698741
"function add(a, b) {const $dd_p0 = $dd_probes('src/utils.ts;add');",
699742
' const $dd_e0 = () => ({a, b});',
700743
' try {',
701-
' const $dd_l0 = () => ({a, b, sum});',
744+
' const $dd_l0 = () => ({sum});',
702745
' let $dd_rv0;',
703746
' if ($dd_p0) $dd_entry($dd_p0, this, $dd_e0()); const sum = a + b; return ($dd_rv0 = sum, $dd_p0 ? $dd_return($dd_p0, $dd_rv0, this, $dd_e0(), $dd_l0()) : $dd_rv0); ',
704747
' } catch(e) { if ($dd_p0) $dd_throw($dd_p0, e, this, $dd_e0()); throw e; }',
@@ -721,7 +764,7 @@ describe('transformCode', () => {
721764
' try {',
722765
' if ($dd_p0) $dd_entry($dd_p0, this, $dd_e0());',
723766
' const $dd_rv0 = x * 2;',
724-
' if ($dd_p0) $dd_return($dd_p0, $dd_rv0, this, $dd_e0(), $dd_e0());',
767+
' if ($dd_p0) $dd_return($dd_p0, $dd_rv0, this, $dd_e0());',
725768
' return $dd_rv0;',
726769
' } catch(e) { if ($dd_p0) $dd_throw($dd_p0, e, this, $dd_e0()); throw e; }',
727770
'};',
@@ -743,7 +786,7 @@ describe('transformCode', () => {
743786
' try {',
744787
' if ($dd_p0) $dd_entry($dd_p0, this, $dd_e0());',
745788
' const $dd_rv0 = {key: x};',
746-
' if ($dd_p0) $dd_return($dd_p0, $dd_rv0, this, $dd_e0(), $dd_e0());',
789+
' if ($dd_p0) $dd_return($dd_p0, $dd_rv0, this, $dd_e0());',
747790
' return $dd_rv0;',
748791
' } catch(e) { if ($dd_p0) $dd_throw($dd_p0, e, this, $dd_e0()); throw e; }',
749792
'};',
@@ -764,7 +807,7 @@ describe('transformCode', () => {
764807
' try {',
765808
' let $dd_rv0;',
766809
' if ($dd_p0) $dd_entry($dd_p0, this, $dd_e0()); console.log(msg); ',
767-
' if ($dd_p0) $dd_return($dd_p0, undefined, this, $dd_e0(), $dd_e0());',
810+
' if ($dd_p0) $dd_return($dd_p0, undefined, this, $dd_e0());',
768811
' } catch(e) { if ($dd_p0) $dd_throw($dd_p0, e, this, $dd_e0()); throw e; }',
769812
'}',
770813
),
@@ -783,7 +826,7 @@ describe('transformCode', () => {
783826
' const $dd_e0 = () => ({x});',
784827
' try {',
785828
' let $dd_rv0;',
786-
' if ($dd_p0) $dd_entry($dd_p0, this, $dd_e0()); if (x < 0) { return ($dd_rv0 = -x, $dd_p0 ? $dd_return($dd_p0, $dd_rv0, this, $dd_e0(), $dd_e0()) : $dd_rv0); } return ($dd_rv0 = x, $dd_p0 ? $dd_return($dd_p0, $dd_rv0, this, $dd_e0(), $dd_e0()) : $dd_rv0); ',
829+
' if ($dd_p0) $dd_entry($dd_p0, this, $dd_e0()); if (x < 0) { return ($dd_rv0 = -x, $dd_p0 ? $dd_return($dd_p0, $dd_rv0, this, $dd_e0()) : $dd_rv0); } return ($dd_rv0 = x, $dd_p0 ? $dd_return($dd_p0, $dd_rv0, this, $dd_e0()) : $dd_rv0); ',
787830
' } catch(e) { if ($dd_p0) $dd_throw($dd_p0, e, this, $dd_e0()); throw e; }',
788831
'}',
789832
),
@@ -802,8 +845,8 @@ describe('transformCode', () => {
802845
' const $dd_e0 = () => ({x});',
803846
' try {',
804847
' let $dd_rv0;',
805-
' if ($dd_p0) $dd_entry($dd_p0, this, $dd_e0()); if (!x) { if ($dd_p0) $dd_return($dd_p0, undefined, this, $dd_e0(), $dd_e0()); return; } console.log(x); ',
806-
' if ($dd_p0) $dd_return($dd_p0, undefined, this, $dd_e0(), $dd_e0());',
848+
' if ($dd_p0) $dd_entry($dd_p0, this, $dd_e0()); if (!x) { if ($dd_p0) $dd_return($dd_p0, undefined, this, $dd_e0()); return; } console.log(x); ',
849+
' if ($dd_p0) $dd_return($dd_p0, undefined, this, $dd_e0());',
807850
' } catch(e) { if ($dd_p0) $dd_throw($dd_p0, e, this, $dd_e0()); throw e; }',
808851
'}',
809852
),
@@ -832,9 +875,9 @@ describe('transformCode', () => {
832875
' let $dd_rv0;',
833876
' if ($dd_p0) $dd_entry($dd_p0, this, $dd_e0());',
834877
' if (x > 0) {',
835-
' return ($dd_rv0 = 1, $dd_p0 ? $dd_return($dd_p0, $dd_rv0, this, $dd_e0(), $dd_e0()) : $dd_rv0);',
878+
' return ($dd_rv0 = 1, $dd_p0 ? $dd_return($dd_p0, $dd_rv0, this, $dd_e0()) : $dd_rv0);',
836879
' } else {',
837-
' return ($dd_rv0 = -1, $dd_p0 ? $dd_return($dd_p0, $dd_rv0, this, $dd_e0(), $dd_e0()) : $dd_rv0);',
880+
' return ($dd_rv0 = -1, $dd_p0 ? $dd_return($dd_p0, $dd_rv0, this, $dd_e0()) : $dd_rv0);',
838881
' }',
839882
'',
840883
' } catch(e) { if ($dd_p0) $dd_throw($dd_p0, e, this, $dd_e0()); throw e; }',

packages/plugins/live-debugger/src/transform/index.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ export function transformCode(options: TransformOptions): TransformResult {
282282
const idx = probeVarCounter++;
283283
const probeVarName = `$dd_p${idx}`;
284284
const entryVars = getVariableNames(node, true, false, babelTypes);
285-
const exitVars = getVariableNames(node, true, true, babelTypes);
285+
const exitVars = getVariableNames(node, false, true, babelTypes);
286286

287287
const isExpressionBody =
288288
babelTypes.isArrowFunctionExpression(node) &&
@@ -399,16 +399,25 @@ function injectInstrumentation(s: MagicStringType, code: string, target: Functio
399399
const entryVarsList = entryVars.join(', ');
400400
const exitVarsList = exitVars.join(', ');
401401

402-
const shared = entryVarsList === exitVarsList;
403-
const snapshotHelper = shared ? entryHelper : exitHelper;
402+
const hasParams = entryVarsList !== '';
403+
const hasLocals = exitVarsList !== '';
404+
405+
const argsArg = hasParams ? `, ${entryHelper}()` : '';
406+
const returnArgsAndLocals = hasParams
407+
? hasLocals
408+
? `, ${entryHelper}(), ${exitHelper}()`
409+
: `, ${entryHelper}()`
410+
: hasLocals
411+
? `, undefined, ${exitHelper}()`
412+
: '';
404413

405414
// TODO: functionId is not escaped — if it contains a single quote (e.g. quoted method names),
406415
// the generated code will be invalid. Escaping is not currently supported.
407416
const probeDecl = `const ${probeVarName} = $dd_probes('${functionId}');`;
408-
const entryHelperDecl = `const ${entryHelper} = () => ({${entryVarsList}});`;
409-
const exitHelperDecl = shared ? '' : `const ${exitHelper} = () => ({${exitVarsList}});`;
410-
const entryCall = `if (${probeVarName}) $dd_entry(${probeVarName}, this, ${entryHelper}());`;
411-
const catchBlock = `catch(e) { if (${probeVarName}) $dd_throw(${probeVarName}, e, this, ${entryHelper}()); throw e; }`;
417+
const entryHelperDecl = hasParams ? `const ${entryHelper} = () => ({${entryVarsList}});` : '';
418+
const exitHelperDecl = hasLocals ? `const ${exitHelper} = () => ({${exitVarsList}});` : '';
419+
const entryCall = `if (${probeVarName}) $dd_entry(${probeVarName}, this${argsArg});`;
420+
const catchBlock = `catch(e) { if (${probeVarName}) $dd_throw(${probeVarName}, e, this${argsArg}); throw e; }`;
412421

413422
if (isExpressionBody) {
414423
// Arrow expression body: (a) => expr
@@ -447,7 +456,7 @@ function injectInstrumentation(s: MagicStringType, code: string, target: Functio
447456

448457
const suffix = [
449458
';',
450-
`if (${probeVarName}) $dd_return(${probeVarName}, ${rvVarName}, this, ${entryHelper}(), ${snapshotHelper}());`,
459+
`if (${probeVarName}) $dd_return(${probeVarName}, ${rvVarName}, this${returnArgsAndLocals});`,
451460
`return ${rvVarName};`,
452461
`} ${catchBlock}`,
453462
'}',
@@ -472,7 +481,7 @@ function injectInstrumentation(s: MagicStringType, code: string, target: Functio
472481
const postambleParts = [''];
473482
if (target.needsTrailingReturn) {
474483
postambleParts.push(
475-
`if (${probeVarName}) $dd_return(${probeVarName}, undefined, this, ${entryHelper}(), ${snapshotHelper}());`,
484+
`if (${probeVarName}) $dd_return(${probeVarName}, undefined, this${returnArgsAndLocals});`,
476485
);
477486
}
478487
postambleParts.push(`} ${catchBlock}`, '');
@@ -490,13 +499,13 @@ function injectInstrumentation(s: MagicStringType, code: string, target: Functio
490499
s.appendLeft(ret.argStart, `(${rvVarName} = `);
491500
s.appendLeft(
492501
ret.argEnd,
493-
`, ${probeVarName} ? $dd_return(${probeVarName}, ${rvVarName}, this, ${entryHelper}(), ${snapshotHelper}()) : ${rvVarName})`,
502+
`, ${probeVarName} ? $dd_return(${probeVarName}, ${rvVarName}, this${returnArgsAndLocals}) : ${rvVarName})`,
494503
);
495504
} else {
496505
// return; → if (probe) $dd_return(...); return;
497506
s.appendLeft(
498507
ret.start,
499-
`if (${probeVarName}) $dd_return(${probeVarName}, undefined, this, ${entryHelper}(), ${snapshotHelper}()); `,
508+
`if (${probeVarName}) $dd_return(${probeVarName}, undefined, this${returnArgsAndLocals}); `,
500509
);
501510
}
502511
}

0 commit comments

Comments
 (0)