Chrome DevTools 和 VSCode Node 条件断点执行差异分析
·
这篇笔记主要是在讨论:Chrome DevTools 和 VSCode + Node.js 在执行“条件断点”时,对多语句条件表达式的取值方式不一样。
通过 console.log 和 hong.push 这两类测试可以看出:
- Chrome 条件断点更像是执行完整段代码后,取最后一个表达式的结果来判断是否断住;
- VSCode + Node.js 条件断点更像是在用户输入的条件前面加了
return,所以会取第一个表达式的结果,后面的语句可能不会继续执行; - 这个差异会影响带副作用的条件表达式,比如
hong.push(1233)是否真的执行、是否记录数据、以及最终是否触发断点。
后面的插桩内容,是为了从 VSCode 的 js-debug 传给调试器的 condition 字段中,验证这个现象背后的实现原因。
- 测试方案
// 条件断点
// 谷歌浏览器中
console.log(11111111111111); // 不会断住 会打印
console.log(11111111111111);true; // 会断住 会打印
true;console.log(11111111111111); // 不会断住 会打印
hong.push(1233); // 会断住 会记录
hong.push(1233);false; // 不会断住 会记录
false;hong.push(1233); // 会断住 会记录
// Nodejs + vscode
console.log(11111111111111); // 不会断住 会打印
console.log(11111111111111);true; // 不会断住 会打印
true;console.log(11111111111111); // 会断住 会打印
hong.push(1233); // 会断住 会记录
hong.push(1233);false; // 会断住 会记录
false;hong.push(1233); // 不会断住 不会记录
- 得出结果
// 第一个 console.log 和 hong.push
// 两个语句
console.log // 返回是 undefined
hong.push // 返回的是数组长度,转成条件判断时是真
// Chrome:用“最后一个表达式”的值当结果
// VSCode Node:用“第一个表达式”的值当结果
也就是说,同样写多个语句时,两个地方对“条件表达式结果”的取值方式不一样:
// Chrome 条件断点
console.log(111); true;
// 先执行 console.log,再取最后的 true,所以会断住
true; console.log(111);
// 最后一个表达式是 console.log 的返回值 undefined,所以不会断住
// VSCode + Node 条件断点
console.log(111); true;
// 取第一个表达式 console.log 的返回值 undefined,所以不会断住
true; console.log(111);
// 取第一个表达式 true,所以会断住
hong.push(1233) 这个例子容易看出来,是因为 push 本身有副作用,会修改数组;并且它的返回值是新数组长度,比如 1、2,在条件判断里会被当成真。
所以:
hong.push(1233); false;
- 在 Chrome 里会执行
push,但是最后结果是false,所以不会断住; - 在 VSCode + Node 里会执行
push,并且第一个表达式hong.push(1233)的返回值为真,所以会断住。
再看:
false; hong.push(1233);
-
在 Chrome 里会执行到
push,最后结果为真,所以会断住; -
在 VSCode + Node 里第一个表达式就是
false,后面的hong.push(1233)不会继续作为条件主体执行,所以不会断住,也不会记录。 -
vscode+nodejs插桩
// Ctrl+Shift+P → 输入 “Developer: Toggle Developer Tools”
// Microsoft VS Code\resources\app\extensions\ms-vscode.js-debug\src\extension.js
async _setAny(e,t){if(!this.isEnabled)return;let n={state:0,args:t,deadletter:!1};console.log('breakpoint', JSON.stringify(t));n.done=(async()=>{let i=RO(t)?await e.cdp().Debugger.setBreakpoint(t)
// console.log('breakpoint', JSON.stringify(t));
// 加入了这个代码,但是这个代码是用 vscode中的开发者工具才可以显示的
// {"urlRegex":"[fF][iI][lL][eE]:\\/\\/\\/[cC]:\\/[uU][sS][eE][rR][sS]\\/[rR][aA][nN][hH][oO][nN][gG][fF][eE][iI][xX][uU][eE]\\/[dD][eE][sS][kK][tT][oO][pP]\\/[wW][oO][rR][kK]0407\\/[wW][oO][rR][kK]\\/[nN][eE][wW][aA][sS][tT]\\/[sS][tT][uU][dD][yY]\\/[cC][aA][sS][eE]1\\/[cC][oO][dD][eE]\\.[jJ][sS]($|\\?)|[cC]:\\\\[uU][sS][eE][rR][sS]\\\\[rR][aA][nN][hH][oO][nN][gG][fF][eE][iI][xX][uU][eE]\\\\[dD][eE][sS][kK][tT][oO][pP]\\\\[wW][oO][rR][kK]0407\\\\[wW][oO][rR][kK]\\\\[nN][eE][wW][aA][sS][tT]\\\\[sS][tT][uU][dD][yY]\\\\[cC][aA][sS][eE]1\\\\[cC][oO][dD][eE]\\.[jJ][sS]($|\\?)","condition":"(()=>{try{return hong.push(123);false;}catch(e){console.error(`Breakpoint condition error: ${e.message||e}`);return false}})()","lineNumber":17,"columnNumber":0}
// urlRegex -> [fF][iI][lL][eE]:\\/\\/\\/[cC]:\\/[uU][sS][eE][rR][sS]\\/[rR][aA][nN][hH][oO][nN][gG][fF][eE][iI][xX][uU][eE]\\/[dD][eE][sS][kK][tT][oO][pP]\\/[wW][oO][rR][kK]0407\\/[wW][oO][rR][kK]\\/[nN][eE][wW][aA][sS][tT]\\/[sS][tT][uU][dD][yY]\\/[cC][aA][sS][eE]1\\/[cC][oO][dD][eE]\\.[jJ][sS]($|\\?)|[cC]:\\\\[uU][sS][eE][rR][sS]\\\\[rR][aA][nN][hH][oO][nN][gG][fF][eE][iI][xX][uU][eE]\\\\[dD][eE][sS][kK][tT][oO][pP]\\\\[wW][oO][rR][kK]0407\\\\[wW][oO][rR][kK]\\\\[nN][eE][wW][aA][sS][tT]\\\\[sS][tT][uU][dD][yY]\\\\[cC][aA][sS][eE]1\\\\[cC][oO][dD][eE]\\.[jJ][sS]($|\\?)
// 转移后 file:///C:/Users/ranhongfeixue/Desktop/work0407/work/newast/study/case1/code.js
这里最关键的是 condition 字段:
condition: "(()=>{try{return hong.push(123);false;}catch(e){...;return false}})()"
VSCode 的 js-debug 会把条件断点包装成一个立即执行函数,并且在用户输入的条件前面加上 return:
return hong.push(123); false;
这样 JS 执行到第一个表达式后就直接 return 了,后面的 false 不会再执行。这就解释了为什么 VSCode + Node 更像是取“第一个表达式”的结果。
对应到前面的测试:
// 用户输入
false; hong.push(1233);
// VSCode 传给调试器的大概效果
return false; hong.push(1233);
因为 return false 已经结束函数,所以 hong.push(1233) 不会执行,也就不会记录。
// 用户输入
hong.push(1233); false;
// VSCode 传给调试器的大概效果
return hong.push(1233); false;
因为 hong.push(1233) 先执行并返回数组长度,所以会记录,而且条件结果为真,会断住。

更多推荐


所有评论(0)