Skip to content

Commit e0f10ec

Browse files
Shigeki Ohtsubnoordhuis
Shigeki Ohtsu
authored andcommitted
debugger: correctly handle source with multi-byte characters
1 parent 4e2343c commit e0f10ec

File tree

3 files changed

+212
-9
lines changed

3 files changed

+212
-9
lines changed

lib/_debugger.js

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,31 +99,42 @@ Protocol.prototype.execute = function(d) {
9999

100100
if (endHeaderIndex < 0) break;
101101

102-
var lines = res.raw.slice(0, endHeaderIndex).split('\r\n');
102+
var rawHeader = res.raw.slice(0, endHeaderIndex);
103+
var endHeaderByteIndex = Buffer.byteLength(rawHeader, 'utf8');
104+
var lines = rawHeader.split('\r\n');
103105
for (var i = 0; i < lines.length; i++) {
104106
var kv = lines[i].split(/: +/);
105107
res.headers[kv[0]] = kv[1];
106108
}
107109

108110
this.contentLength = +res.headers['Content-Length'];
109-
this.bodyStartIndex = endHeaderIndex + 4;
111+
this.bodyStartByteIndex = endHeaderByteIndex + 4;
110112

111113
this.state = 'body';
112-
if (res.raw.length - this.bodyStartIndex < this.contentLength) break;
113-
// pass thru
114114

115+
if (Buffer.byteLength(res.raw, 'utf8') - this.bodyStartByteIndex
116+
< this.contentLength) {
117+
break;
118+
}
119+
// pass thru
115120
case 'body':
116-
if (res.raw.length - this.bodyStartIndex >= this.contentLength) {
121+
var resRawByteLength = Buffer.byteLength(res.raw, 'utf8');
122+
123+
if (resRawByteLength - this.bodyStartByteIndex >= this.contentLength) {
124+
var buf = new Buffer(resRawByteLength);
125+
buf.write(res.raw, 0, resRawByteLength, 'utf8');
117126
res.body =
118-
res.raw.slice(this.bodyStartIndex,
119-
this.bodyStartIndex + this.contentLength);
127+
buf.slice(this.bodyStartByteIndex,
128+
this.bodyStartByteIndex
129+
+ this.contentLength).toString('utf8');
120130
// JSON parse body?
121131
res.body = res.body.length ? JSON.parse(res.body) : {};
122132

123133
// Done!
124134
this.onResponse(res);
125135

126-
this._newRes(res.raw.slice(this.bodyStartIndex + this.contentLength));
136+
this._newRes(buf.slice(this.bodyStartByteIndex
137+
+ this.contentLength).toString('utf8'));
127138
}
128139
break;
129140

@@ -138,7 +149,8 @@ Protocol.prototype.serialize = function(req) {
138149
req.type = 'request';
139150
req.seq = this.reqSeq++;
140151
var json = JSON.stringify(req);
141-
return 'Content-Length: ' + json.length + '\r\n\r\n' + json;
152+
return 'Content-Length: ' + Buffer.byteLength(json,'utf8') + '\r\n\r\n'
153+
+ json;
142154
};
143155

144156

test/fixtures/breakpoints_utf8.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
debugger;
2+
function a(x) {
3+
var i = 10;
4+
while (--i != 0);
5+
debugger;
6+
return i;
7+
}
8+
function b() {
9+
return ['こんにち', 'わ'].join(' ');
10+
}
11+
a();
12+
a(1);
13+
b();
14+
b();
15+
16+
17+
18+
setInterval(function() {
19+
}, 5000);
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
23+
var common = require('../common');
24+
var assert = require('assert');
25+
var spawn = require('child_process').spawn;
26+
var debug = require('_debugger');
27+
28+
var script = common.fixturesDir + '/breakpoints_utf8.js';
29+
30+
var child = spawn(process.execPath, ['debug', script]);
31+
32+
var buffer = '';
33+
child.stdout.setEncoding('utf-8');
34+
child.stdout.on('data', function(data) {
35+
data = (buffer + data.toString()).split(/\n/g);
36+
buffer = data.pop();
37+
data.forEach(function(line) {
38+
child.emit('line', line);
39+
});
40+
});
41+
child.stderr.pipe(process.stdout);
42+
43+
var expected = [];
44+
45+
child.on('line', function(line) {
46+
assert.ok(expected.length > 0, 'Got unexpected line: ' + line);
47+
48+
var expectedLine = expected[0].lines.shift();
49+
assert.ok(line.match(expectedLine) !== null, line + ' != ' + expectedLine);
50+
51+
if (expected[0].lines.length === 0) {
52+
var callback = expected[0].callback;
53+
expected.shift();
54+
callback && callback();
55+
}
56+
});
57+
58+
function addTest(input, output) {
59+
function next() {
60+
if (expected.length > 0) {
61+
child.stdin.write(expected[0].input + '\n');
62+
63+
if (!expected[0].lines) {
64+
setTimeout(function() {
65+
var callback = expected[0].callback;
66+
expected.shift();
67+
68+
callback && callback();
69+
}, 50);
70+
}
71+
} else {
72+
finish();
73+
}
74+
};
75+
expected.push({input: input, lines: output, callback: next});
76+
}
77+
78+
// Initial lines
79+
addTest(null, [
80+
/listening on port 5858/,
81+
/connecting... ok/,
82+
/break in .*:1/,
83+
/1/, /2/, /3/
84+
]);
85+
86+
// Next
87+
addTest('n', [
88+
/break in .*:11/,
89+
/9/, /10/, /11/, /12/, /13/
90+
]);
91+
92+
// Watch
93+
addTest('watch("\'x\'")');
94+
95+
// Continue
96+
addTest('c', [
97+
/break in .*:5/,
98+
/Watchers/,
99+
/0:\s+'x' = "x"/,
100+
/()/,
101+
/3/, /4/, /5/, /6/, /7/
102+
]);
103+
104+
// Show watchers
105+
addTest('watchers', [
106+
/0:\s+'x' = "x"/
107+
]);
108+
109+
// Unwatch
110+
addTest('unwatch("\'x\'")');
111+
112+
// Step out
113+
addTest('o', [
114+
/break in .*:12/,
115+
/10/, /11/, /12/, /13/, /14/
116+
]);
117+
118+
// Continue
119+
addTest('c', [
120+
/break in .*:5/,
121+
/3/, /4/, /5/, /6/, /7/
122+
]);
123+
124+
// Set breakpoint by function name
125+
addTest('sb("setInterval()", "!(setInterval.flag++)")', [
126+
/1/, /2/, /3/, /4/, /5/, /6/, /7/, /8/, /9/, /10/
127+
]);
128+
129+
// Continue
130+
addTest('c', [
131+
/break in node.js:\d+/,
132+
/\d/, /\d/, /\d/, /\d/, /\d/
133+
]);
134+
135+
// Continue
136+
addTest('c, bt', [
137+
/Can't request backtrace now/
138+
]);
139+
140+
141+
function finish() {
142+
process.exit(0);
143+
}
144+
145+
function quit() {
146+
if (quit.called) return;
147+
quit.called = true;
148+
child.stdin.write('quit');
149+
}
150+
151+
setTimeout(function() {
152+
var err = 'Timeout';
153+
if (expected.length > 0 && expected[0].lines) {
154+
err = err + '. Expected: ' + expected[0].lines.shift();
155+
}
156+
157+
throw new Error(err);
158+
}, 5000);
159+
160+
process.once('uncaughtException', function(e) {
161+
quit();
162+
console.error(e.toString());
163+
child.kill('SIGKILL');
164+
process.exit(1);
165+
});
166+
167+
process.on('exit', function(code) {
168+
quit();
169+
if (code === 0) {
170+
assert.equal(expected.length, 0);
171+
}
172+
});

0 commit comments

Comments
 (0)