Skip to content

Commit ed66d7d

Browse files
authored
Add TypeScript proxy to support JrS debugging w/ Chrome DevTools (#4)
Basic debugging features of step over, step in , step out, continue, stop, add/remove breakpoints, provide backtrace at breakpoint, and evaluate watch expressions are all working. A few low-hanging features still missing: - passthrough of output to CDT console - reporting exceptions - verbosity control Other features of CDT will require enhancement to JrS debugger, e.g.: - scope chain - inspectable eval output (e.g. JSON, not just strings) - memory analysis - profiling Significant level of unit testing provided via jest. Thanks to Martijn The <[email protected]> for initial TS code skeleton, some unit tests, 'step out' feature, and tons of review! JerryScript-DCO-1.0-Signed-off-by: Geoff Gustafson [email protected]
1 parent 8aa06c8 commit ed66d7d

25 files changed

+6562
-5
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1+
dist
12
node_modules
3+
src/coverage
24
yarn-error.log

README.md

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,73 @@
33
[![Build Status](https://travis-ci.org/jerryscript-project/jerryscript-debugger-ts.svg?branch=master)](https://travis-ci.org/jerryscript-project/jerryscript-debugger-ts)
44
[![IRC Channel](https://img.shields.io/badge/chat-on%20freenode-brightgreen.svg)](https://kiwiirc.com/client/irc.freenode.net/#jerryscript)
55

6-
Work in progress...
6+
## Dependencies
7+
8+
- node.js
9+
- `yarn` or `npm` (the steps below use `yarn`)
10+
11+
## Installing
12+
13+
```
14+
$ cd jerryscript-debugger-ts
15+
$ yarn install
16+
```
17+
18+
## Building in watch mode
19+
20+
This will make the TypeScript compiler monitor the source files and re-build
21+
files whenever there is a change.
22+
23+
```
24+
$ cd jerryscript-debugger-ts
25+
$ yarn build:watch
26+
```
27+
28+
## Running tests
29+
30+
```
31+
$ cd jerryscript-debugger-ts
32+
$ yarn test
33+
```
34+
35+
## Running tests in watch mode
36+
37+
This will make the test runner monitor the source files and re-run the tests
38+
whenever there is a change.
39+
40+
```
41+
$ cd jerryscript-debugger-ts
42+
$ yarn test:watch
43+
```
44+
45+
## Running the linter
46+
47+
```
48+
$ cd jerryscript-debugger-ts
49+
$ yarn lint
50+
```
51+
52+
## Using
53+
54+
Build and run JerryScript with the debugger enabled. For example, with the
55+
JerryScript Linux build:
56+
```bash
57+
$ cd jerryscript
58+
$ python tools/build.py --jerry-debugger=on --jerry-libc=off
59+
$ ./build/bin/jerry --start-debug-server --log-level 2 /path/to/source.js
60+
```
61+
62+
Have Chrome running and visit the URL `chrome://inspect`, and click
63+
"Open dedicated DevTools for Node."
64+
65+
Finally, run this project's proxy application:
66+
```bash
67+
$ cd jerryscript-debugger-ts
68+
$ ./jerry-debugger.sh
69+
```
70+
71+
This should connect to the debugger and give focus to the DevTools window to
72+
start debugging.
773

874
## Contributing
975
The project can only accept contributions which are licensed under the [Apache License 2.0](LICENSE) and are signed according to the JerryScript [Developer's Certificate of Origin](DCO.md). For further information please see our [Contribution Guidelines](CONTRIBUTING.md).

jerry-debugger.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/bash
2+
3+
set -e
4+
5+
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
6+
node $DIR/dist/cli/index.js $@

package.json

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,61 @@
22
"name": "@jerryscript/debugger",
33
"version": "0.0.1",
44
"description": "JerryScript Debugger & Chrome DevTools Proxy",
5+
"bin": {
6+
"jerry-debugger": "dist/cli/index.js"
7+
},
58
"scripts": {
6-
"prepare": "echo prepare",
7-
"lint": "echo lint",
8-
"test": "echo test"
9+
"prepare": "tsc",
10+
"build": "tsc",
11+
"build:watch": "tsc --watch",
12+
"lint": "tslint -p .",
13+
"test": "jest",
14+
"test:watch": "jest --watch"
915
},
1016
"repository": "https://github.com/jerryscript-project/jerryscript-debugger-ts/",
1117
"author": "JS Foundation and other contributors",
1218
"license": "Apache-2.0",
13-
"private": false
19+
"private": false,
20+
"dependencies": {
21+
"chrome-remote-debug-protocol": "^1.2.20170721",
22+
"commander": "^2.15.0",
23+
"noice-json-rpc": "^1.1.1",
24+
"uuid": "^3.2.1",
25+
"ws": "^3.3.2"
26+
},
27+
"devDependencies": {
28+
"@types/commander": "^2.12.2",
29+
"@types/jest": "^22.1.2",
30+
"@types/minimist": "^1.2.0",
31+
"@types/node": "^9.4.6",
32+
"@types/uuid": "^3.4.3",
33+
"@types/ws": "^4.0.1",
34+
"jest": "^22.3.0",
35+
"ts-jest": "^22.0.4",
36+
"tslint": "^5.9.1",
37+
"tslint-config-standard": "^7.0.0",
38+
"typescript": "^2.7.1"
39+
},
40+
"jest": {
41+
"transform": {
42+
"^.+\\.tsx?$": "ts-jest"
43+
},
44+
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
45+
"moduleFileExtensions": [
46+
"ts",
47+
"tsx",
48+
"js",
49+
"jsx",
50+
"json",
51+
"node"
52+
],
53+
"globals": {
54+
"ts-jest": {
55+
"enableTsDiagnostics": true
56+
}
57+
},
58+
"rootDir": "src",
59+
"resetMocks": true,
60+
"resetModules": true
61+
}
1462
}

src/cli/__tests__/index.test.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright JS Foundation and other contributors, http://js.foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import { getOptionsFromArgs } from '../cli';
16+
import {
17+
DEFAULT_DEBUGGER_HOST,
18+
DEFAULT_DEBUGGER_PORT,
19+
} from '../../lib/debugger-client';
20+
import {
21+
DEFAULT_SERVER_HOST,
22+
DEFAULT_SERVER_PORT,
23+
} from '../../lib/cdt-proxy';
24+
25+
describe('getOptionsFromArgs', () => {
26+
27+
function getOptionsFromUserArgs(userArgs: string[]) {
28+
return getOptionsFromArgs(['node', 'jerry-debugger.js'].concat(userArgs));
29+
}
30+
31+
it('works without --inspect-brk', () => {
32+
const opt = getOptionsFromUserArgs([]);
33+
expect(opt.proxyAddress.host).toEqual(DEFAULT_SERVER_HOST);
34+
expect(opt.proxyAddress.port).toEqual(DEFAULT_SERVER_PORT);
35+
});
36+
37+
it('parses --inspect-brk with port only', () => {
38+
const opt = getOptionsFromUserArgs(['--inspect-brk=1234']);
39+
expect(opt.proxyAddress.host).toEqual(undefined);
40+
expect(opt.proxyAddress.port).toEqual(1234);
41+
});
42+
43+
it('parses --inspect-brk with no port', () => {
44+
const opt = getOptionsFromUserArgs(['--inspect-brk=10.10.10.10:']);
45+
expect(opt.proxyAddress.host).toEqual('10.10.10.10');
46+
expect(opt.proxyAddress.port).toEqual(undefined);
47+
});
48+
49+
it('parses --inspect-brk with no host', () => {
50+
const opt = getOptionsFromUserArgs(['--inspect-brk=:1234']);
51+
expect(opt.proxyAddress.host).toEqual(undefined);
52+
expect(opt.proxyAddress.port).toEqual(1234);
53+
});
54+
55+
it('parses --inspect-brk with host and port', () => {
56+
const opt = getOptionsFromUserArgs(['--inspect-brk=10.10.10.10:1234']);
57+
expect(opt.proxyAddress.host).toEqual('10.10.10.10');
58+
expect(opt.proxyAddress.port).toEqual(1234);
59+
});
60+
61+
it('works without --jerry-remote', () => {
62+
const opt = getOptionsFromUserArgs([]);
63+
expect(opt.remoteAddress.host).toEqual(DEFAULT_DEBUGGER_HOST);
64+
expect(opt.remoteAddress.port).toEqual(DEFAULT_DEBUGGER_PORT);
65+
});
66+
67+
it('parses --jerry-remote with port only', () => {
68+
const opt = getOptionsFromUserArgs(['--jerry-remote=1234']);
69+
expect(opt.remoteAddress.host).toEqual(undefined);
70+
expect(opt.remoteAddress.port).toEqual(1234);
71+
});
72+
73+
it('parses --jerry-remote with host and port', () => {
74+
const opt = getOptionsFromUserArgs(['--jerry-remote=10.10.10.10:1234']);
75+
expect(opt.remoteAddress.host).toEqual('10.10.10.10');
76+
expect(opt.remoteAddress.port).toEqual(1234);
77+
});
78+
79+
it('verbose defaults to false', () => {
80+
const opt = getOptionsFromUserArgs([]);
81+
expect(opt.verbose).toEqual(false);
82+
});
83+
84+
it('parses verbose flag', () => {
85+
const opt = getOptionsFromUserArgs(['--verbose']);
86+
expect(opt.verbose).toEqual(true);
87+
});
88+
89+
it('parses v alias for verbose', () => {
90+
const opt = getOptionsFromUserArgs(['-v']);
91+
expect(opt.verbose).toEqual(true);
92+
});
93+
94+
it('jsfile defaults to untitled.js', () => {
95+
const opt = getOptionsFromUserArgs([]);
96+
expect(opt.jsfile).toEqual('untitled.js');
97+
});
98+
99+
it('returns client source as jsfile', () => {
100+
const opt = getOptionsFromUserArgs(['foo/bar.js']);
101+
expect(opt.jsfile).toEqual('foo/bar.js');
102+
});
103+
104+
});

src/cli/cli.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright JS Foundation and other contributors, http://js.foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import { CDTController } from '../lib/cdt-controller';
16+
import {
17+
ChromeDevToolsProxyServer,
18+
DEFAULT_SERVER_HOST,
19+
DEFAULT_SERVER_PORT,
20+
} from '../lib/cdt-proxy';
21+
import {
22+
JerryDebuggerClient,
23+
DEFAULT_DEBUGGER_HOST,
24+
DEFAULT_DEBUGGER_PORT,
25+
} from '../lib/debugger-client';
26+
import { Command } from 'commander';
27+
import { JerryDebugProtocolHandler } from '../lib/protocol-handler';
28+
29+
/**
30+
* Converts string of format [host:][port] to an object with host and port,
31+
* each possibly undefined
32+
*/
33+
function getHostAndPort(input: string) {
34+
const hostAndPort = input.split(':');
35+
const portIndex = hostAndPort.length - 1;
36+
const host = hostAndPort[portIndex - 1];
37+
const port = hostAndPort[portIndex];
38+
return {
39+
host: host ? host : undefined,
40+
port: port ? Number(port) : undefined,
41+
};
42+
}
43+
44+
export function getOptionsFromArgs(argv: Array<string>) {
45+
const program = new Command('jerry-debugger');
46+
program
47+
.usage('[options] <script.js ...>')
48+
.option(
49+
'-v, --verbose',
50+
'Enable verbose logging', false)
51+
.option(
52+
'--inspect-brk [[host:]port]',
53+
'Activate Chrome DevTools proxy on host:port',
54+
`${DEFAULT_SERVER_HOST}:${DEFAULT_SERVER_PORT}`)
55+
.option(
56+
'--jerry-remote [[host:]port]',
57+
'Connect to JerryScript on host:port',
58+
`${DEFAULT_DEBUGGER_HOST}:${DEFAULT_DEBUGGER_PORT}`)
59+
.parse(argv);
60+
61+
return {
62+
proxyAddress: getHostAndPort(program.inspectBrk),
63+
remoteAddress: getHostAndPort(program.jerryRemote),
64+
jsfile: program.args[0] || 'untitled.js',
65+
verbose: program.verbose || false,
66+
};
67+
}
68+
69+
export function main(proc: NodeJS.Process) {
70+
const options = getOptionsFromArgs(proc.argv);
71+
72+
const controller = new CDTController();
73+
const jhandler = new JerryDebugProtocolHandler(controller);
74+
const jclient = new JerryDebuggerClient({
75+
delegate: jhandler,
76+
...options.remoteAddress,
77+
});
78+
jhandler.debuggerClient = jclient;
79+
// set this before connecting to the client
80+
controller.protocolHandler = jhandler;
81+
82+
const debuggerUrl = `ws://${jclient.host}:${jclient.port}`;
83+
jclient.connect().then(() => {
84+
console.log(`Connected to debugger at ${debuggerUrl}`);
85+
const proxy = new ChromeDevToolsProxyServer({
86+
delegate: controller,
87+
...options.proxyAddress,
88+
jsfile: options.jsfile,
89+
});
90+
// set this before making further debugger calls
91+
controller.proxyServer = proxy;
92+
console.log(`Proxy listening at ws://${proxy.host}:${proxy.port}`);
93+
}).catch((err) => {
94+
console.log(`Error connecting to debugger at ${debuggerUrl}`);
95+
console.log(err);
96+
});
97+
}

src/cli/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright JS Foundation and other contributors, http://js.foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import { main } from './cli';
16+
17+
main(process);

src/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright JS Foundation and other contributors, http://js.foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// TODO: re-export more things here from lib/ that we want to expose as 'API'
16+
17+
export { ChromeDevToolsProxyServer } from './lib/cdt-proxy';

0 commit comments

Comments
 (0)