• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2
3# Copyright JS Foundation and other contributors, http://js.foundation
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17from __future__ import print_function
18from cmd import Cmd
19from pprint import pprint
20import math
21import socket
22import sys
23import logging
24import time
25import jerry_client_main
26
27from jerry_client_websocket import WebSocket
28from jerry_client_rawpacket import RawPacket
29from jerry_client_tcp import Socket
30
31def write(string):
32    print(string, end='')
33
34class DebuggerPrompt(Cmd):
35    # pylint: disable=too-many-instance-attributes,too-many-arguments
36    def __init__(self, debugger):
37        Cmd.__init__(self)
38        self.debugger = debugger
39        self.stop = False
40        self.quit = False
41
42    def precmd(self, line):
43        self.stop = False
44        if self.debugger.non_interactive:
45            print("%s" % line)
46        return line
47
48    def postcmd(self, stop, line):
49        return self.stop
50
51    def do_quit(self, _):
52        """ Exit JerryScript debugger """
53        self.debugger.quit()
54        self.quit = True
55        self.stop = True
56
57    def do_display(self, args):
58        """ Toggle source code display after breakpoints """
59        if args:
60            line_num = src_check_args(args)
61            if line_num >= 0:
62                self.debugger.display = line_num
63        else:
64            print("Non-negative integer number expected, 0 turns off this function")
65
66    def do_break(self, args):
67        """ Insert breakpoints on the given lines or functions """
68        write(self.debugger.set_break(args))
69    do_b = do_break
70
71    def do_list(self, _):
72        """ Lists the available breakpoints """
73        write(self.debugger.breakpoint_list())
74
75    def do_delete(self, args):
76        """ Delete the given breakpoint, use 'delete all|active|pending' to clear all the given breakpoints """
77        write(self.debugger.delete(args))
78
79    def do_exception(self, args):
80        """ Config the exception handler module """
81        write(self.debugger.exception(args))
82
83    def do_next(self, args):
84        """ Next breakpoint in the same code block """
85        self.stop = True
86        if not args:
87            args = 0
88            self.debugger.next()
89            return
90
91        try:
92            args = int(args)
93            if args <= 0:
94                raise ValueError(args)
95
96            while args > 0:
97                self.debugger.next()
98                time.sleep(0.1)
99
100                while True:
101                    result = self.debugger.process_messages()
102                    res_type = result.get_type()
103
104                    if res_type == result.END:
105                        self.quit = True
106                        return
107                    elif res_type == result.TEXT:
108                        write(result.get_text())
109                    elif res_type == result.PROMPT:
110                        break
111
112                args -= 1
113        except ValueError as val_errno:
114            print("Error: expected a positive integer: %s" % val_errno)
115    do_n = do_next
116
117    def do_step(self, _):
118        """ Next breakpoint, step into functions """
119        self.debugger.step()
120        self.stop = True
121    do_s = do_step
122
123    def do_continue(self, _):
124        """ Continue execution """
125        self.debugger.do_continue()
126        self.stop = True
127        if not self.debugger.non_interactive:
128            print("Press enter to stop JavaScript execution.")
129    do_c = do_continue
130
131    def do_finish(self, _):
132        """ Continue running until the current function returns """
133        self.debugger.finish()
134        self.stop = True
135    do_f = do_finish
136
137    def do_backtrace(self, args):
138        """ Get backtrace data from debugger """
139        write(self.debugger.backtrace(args))
140        self.stop = True
141    do_bt = do_backtrace
142
143    def do_src(self, args):
144        """ Get current source code """
145        if args:
146            line_num = src_check_args(args)
147            if line_num >= 0:
148                write(self.debugger.print_source(line_num, 0))
149        else:
150            write(self.debugger.print_source(0, 0))
151    do_source = do_src
152
153    def do_scroll(self, _):
154        """ Scroll the source up or down """
155        while True:
156            key = sys.stdin.readline()
157            if key == 'w\n':
158                _scroll_direction(self.debugger, "up")
159            elif key == 's\n':
160                _scroll_direction(self.debugger, "down")
161            elif key == 'q\n':
162                break
163            else:
164                print("Invalid key")
165
166    def do_eval(self, args):
167        """ Evaluate JavaScript source code """
168        self.debugger.eval(args)
169        self.stop = True
170    do_e = do_eval
171
172    def do_eval_at(self, args):
173        """ Evaluate JavaScript source code at a scope chain level """
174
175        code = ''
176        index = 0
177        try:
178            args = args.split(" ", 1)
179
180            index = int(args[0])
181
182            if len(args) == 2:
183                code = args[1]
184
185            if index < 0 or index > 65535:
186                raise ValueError("Invalid scope chain index: %d (must be between 0 and 65535)" % index)
187
188        except ValueError as val_errno:
189            print("Error: %s" % (val_errno))
190            return
191
192        self.debugger.eval_at(code, index)
193        self.stop = True
194
195    def do_throw(self, args):
196        """ Throw an exception """
197        self.debugger.throw(args)
198        self.stop = True
199
200    def do_abort(self, args):
201        """ Throw an exception which cannot be caught """
202        self.debugger.abort(args)
203        self.stop = True
204
205    def do_restart(self, _):
206        """ Restart the engine's debug session """
207        self.debugger.restart()
208        self.stop = True
209    do_res = do_restart
210
211    def do_scope(self, _):
212        """ Get lexical environment chain """
213        self.debugger.scope_chain()
214        self.stop = True
215
216    def do_variables(self, args):
217        """ Get scope variables from debugger """
218        write(self.debugger.scope_variables(args))
219        self.stop = True
220
221    def do_memstats(self, _):
222        """ Memory statistics """
223        self.debugger.memstats()
224        self.stop = True
225    do_ms = do_memstats
226
227    def do_dump(self, args):
228        """ Dump all of the debugger data """
229        if args:
230            print("Error: No argument expected")
231        else:
232            pprint(self.debugger.function_list)
233
234    # pylint: disable=invalid-name
235    def do_EOF(self, _):
236        """ Exit JerryScript debugger """
237        print("Unexpected end of input. Connection closed.")
238        self.debugger.quit()
239        self.quit = True
240        self.stop = True
241
242def _scroll_direction(debugger, direction):
243    """ Helper function for do_scroll """
244    debugger.src_offset_diff = int(max(math.floor(debugger.display / 3), 1))
245    if direction == "up":
246        debugger.src_offset -= debugger.src_offset_diff
247    else:
248        debugger.src_offset += debugger.src_offset_diff
249    print(debugger.print_source(debugger.display, debugger.src_offset)['value'])
250
251def src_check_args(args):
252    try:
253        line_num = int(args)
254        if line_num < 0:
255            print("Error: Non-negative integer number expected")
256            return -1
257
258        return line_num
259    except ValueError as val_errno:
260        print("Error: Non-negative integer number expected: %s" % (val_errno))
261        return -1
262
263# pylint: disable=too-many-branches,too-many-locals,too-many-statements
264def main():
265    args = jerry_client_main.arguments_parse()
266
267    channel = None
268    protocol = None
269
270    if args.protocol == "tcp":
271        address = None
272        if ":" not in args.address:
273            address = (args.address, 5001) # use default port
274        else:
275            host, port = args.address.split(":")
276            address = (host, int(port))
277
278        protocol = Socket(address)
279    elif args.protocol == "serial":
280        from jerry_client_serial import Serial
281        protocol = Serial(args.serial_config)
282    else:
283        print("Unsupported transmission protocol")
284        return -1
285
286    if args.channel == "websocket":
287        channel = WebSocket(protocol=protocol)
288    elif args.channel == "rawpacket":
289        channel = RawPacket(protocol=protocol)
290    else:
291        print("Unsupported communication channel")
292        return -1
293
294    debugger = jerry_client_main.JerryDebugger(channel)
295    debugger.non_interactive = args.non_interactive
296
297    logging.debug("Connected to JerryScript")
298
299    prompt = DebuggerPrompt(debugger)
300    prompt.prompt = "(jerry-debugger) "
301
302    if args.color:
303        debugger.set_colors()
304
305    if args.display:
306        debugger.display = args.display
307        prompt.do_display(args.display)
308    else:
309        prompt.stop = False
310
311    if args.exception is not None:
312        prompt.do_exception(str(args.exception))
313
314    if args.client_source:
315        debugger.store_client_sources(args.client_source)
316
317    while True:
318        if prompt.quit:
319            break
320
321        result = debugger.process_messages()
322        res_type = result.get_type()
323
324        if res_type == result.END:
325            break
326        elif res_type == result.PROMPT:
327            prompt.cmdloop()
328        elif res_type == result.TEXT:
329            write(result.get_text())
330        continue
331
332if __name__ == "__main__":
333    try:
334        main()
335    except socket.error as error_msg:
336        ERRNO = error_msg.errno
337        MSG = str(error_msg)
338        if ERRNO == 111:
339            sys.exit("Failed to connect to the JerryScript debugger.")
340        elif ERRNO == 32 or ERRNO == 104:
341            sys.exit("Connection closed.")
342        else:
343            sys.exit("Failed to connect to the JerryScript debugger.\nError: %s" % (MSG))
344