Subversion Repositories wimsdev

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14283 obado 1
/**
2
 * Debugger support for skulpt module
3
 */
4
 
5
var Sk = Sk || {}; //jshint ignore:line
6
 
7
function hasOwnProperty(obj, prop) {
8
    var proto = obj.constructor.prototype;
9
    return (prop in obj) &&
10
        (!(prop in proto) || proto[prop] !== obj[prop]);
11
}
12
 
13
Sk.Breakpoint = function(filename, lineno, colno) {
14
    this.filename = filename;
15
    this.lineno = lineno;
16
    this.colno = colno;
17
    this.enabled = true;
18
    this.ignore_count = 0;
19
};
20
 
21
Sk.Debugger = function(filename, output_callback) {
22
    this.dbg_breakpoints = {};
23
    this.tmp_breakpoints = {};
24
    this.suspension_stack = [];
25
    this.current_suspension = -1;
26
    this.eval_callback = null;
27
    this.suspension = null;
28
    this.output_callback = output_callback;
29
    this.step_mode = false;
30
    this.filename = filename;
31
};
32
 
33
Sk.Debugger.prototype.print = function(txt) {
34
    if (this.output_callback != null) {
35
        this.output_callback.print(txt);
36
    }
37
};
38
 
39
Sk.Debugger.prototype.get_source_line = function(lineno) {
40
    if (this.output_callback != null) {
41
        return this.output_callback.get_source_line(lineno);
42
    }
43
 
44
    return "";
45
};
46
 
47
Sk.Debugger.prototype.move_up_the_stack = function() {
48
    this.current_suspension = Math.min(this.current_suspension + 1, this.suspension_stack.length - 1);
49
};
50
 
51
Sk.Debugger.prototype.move_down_the_stack = function() {
52
    this.current_suspension = Math.max(this.current_suspension - 1, 0);
53
};
54
 
55
Sk.Debugger.prototype.enable_step_mode = function() {
56
    this.step_mode = true;
57
};
58
 
59
Sk.Debugger.prototype.disable_step_mode = function() {
60
    this.step_mode = false;
61
};
62
 
63
Sk.Debugger.prototype.get_suspension_stack = function() {
64
    return this.suspension_stack;
65
};
66
 
67
Sk.Debugger.prototype.get_active_suspension = function() {
68
    if (this.suspension_stack.length === 0) {
69
        return null;
70
    }
71
 
72
    return this.suspension_stack[this.current_suspension];
73
};
74
 
75
Sk.Debugger.prototype.generate_breakpoint_key = function(filename, lineno, colno) {
76
    var key = filename + "-" + lineno;
77
    return key;
78
};
79
 
80
Sk.Debugger.prototype.check_breakpoints = function(filename, lineno, colno, globals, locals) {
81
    // If Step mode is enabled then ignore breakpoints since we will just break
82
    // at every line.
83
    if (this.step_mode === true) {
84
        return true;
85
    }
86
 
87
    var key = this.generate_breakpoint_key(filename, lineno, colno);
88
    if (hasOwnProperty(this.dbg_breakpoints, key) &&
89
        this.dbg_breakpoints[key].enabled === true) {
90
        var bp = null;
91
        if (hasOwnProperty(this.tmp_breakpoints, key)) {
92
            delete this.dbg_breakpoints[key];
93
            delete this.tmp_breakpoints[key];
94
            return true;
95
        }
96
 
97
        this.dbg_breakpoints[key].ignore_count -= 1;
98
        this.dbg_breakpoints[key].ignore_count = Math.max(0, this.dbg_breakpoints[key].ignore_count);
99
 
100
        bp = this.dbg_breakpoints[key];
101
        if (bp.ignore_count === 0) {
102
            return true;
103
        } else {
104
            return false;
105
        }
106
    }
107
    return false;
108
};
109
 
110
Sk.Debugger.prototype.get_breakpoints_list = function() {
111
    return this.dbg_breakpoints;
112
};
113
 
114
Sk.Debugger.prototype.disable_breakpoint = function(filename, lineno, colno) {
115
    var key = this.generate_breakpoint_key(filename, lineno, colno);
116
 
117
    if (hasOwnProperty(this.dbg_breakpoints, key)) {
118
        this.dbg_breakpoints[key].enabled = false;
119
    }
120
};
121
 
122
Sk.Debugger.prototype.enable_breakpoint = function(filename, lineno, colno) {
123
    var key = this.generate_breakpoint_key(filename, lineno, colno);
124
 
125
    if (hasOwnProperty(this.dbg_breakpoints, key)) {
126
        this.dbg_breakpoints[key].enabled = true;
127
    }
128
};
129
 
130
Sk.Debugger.prototype.clear_breakpoint = function(filename, lineno, colno) {
131
    var key = this.generate_breakpoint_key(filename, lineno, colno);
132
    if (hasOwnProperty(this.dbg_breakpoints, key)) {
133
        delete this.dbg_breakpoints[key];
134
        return null;
135
    } else {
136
        return "Invalid breakpoint specified: " + filename + " line: " + lineno;
137
    }
138
};
139
 
140
Sk.Debugger.prototype.clear_all_breakpoints = function() {
141
    this.dbg_breakpoints = {};
142
    this.tmp_breakpoints = {};
143
};
144
 
145
Sk.Debugger.prototype.set_ignore_count = function(filename, lineno, colno, count) {
146
    var key = this.generate_breakpoint_key(filename, lineno, colno);
147
    if (hasOwnProperty(this.dbg_breakpoints, key)) {
148
        var bp = this.dbg_breakpoints[key];
149
        bp.ignore_count = count;
150
    }
151
};
152
 
153
Sk.Debugger.prototype.set_condition = function(filename, lineno, colno, lhs, cond, rhs) {
154
    var key = this.generate_breakpoint_key(filename, lineno, colno);
155
    var bp;
156
    if (hasOwnProperty(this.dbg_breakpoints, key)) {
157
        // Set a new condition
158
        bp = this.dbg_breakpoints[key];
159
    } else {
160
        bp = new Sk.Breakpoint(filename, lineno, colno);
161
    }
162
 
163
    bp.condition = new Sk.Condition(lhs, cond, rhs);
164
    this.dbg_breakpoints[key] = bp;
165
};
166
 
167
Sk.Debugger.prototype.print_suspension_info = function(suspension) {
168
    var filename = suspension.filename;
169
    var lineno = suspension.lineno;
170
    var colno = suspension.colno;
171
    this.print("Hit Breakpoint at <" + filename + "> at line: " + lineno + " column: " + colno + "\n");
172
    this.print("----------------------------------------------------------------------------------\n");
173
    this.print(" ==> " + this.get_source_line(lineno - 1) + "\n");
174
    this.print("----------------------------------------------------------------------------------\n");
175
};
176
 
177
Sk.Debugger.prototype.set_suspension = function(suspension) {
178
    var parent = null;
179
    if (!hasOwnProperty(suspension, "filename") && suspension.child instanceof Sk.misceval.Suspension) {
180
        suspension = suspension.child;
181
    }
182
 
183
    // Pop the last suspension of the stack if there is more than 0
184
    if (this.suspension_stack.length > 0) {
185
        this.suspension_stack.pop();
186
        this.current_suspension -= 1;
187
    }
188
 
189
    // Unroll the stack to get each suspension.
190
    while (suspension instanceof Sk.misceval.Suspension) {
191
        parent = suspension;
192
        this.suspension_stack.push(parent);
193
        this.current_suspension += 1;
194
        suspension = suspension.child;
195
    }
196
 
197
    suspension = parent;
198
 
199
    this.print_suspension_info(suspension);
200
};
201
 
202
Sk.Debugger.prototype.add_breakpoint = function(filename, lineno, colno, temporary) {
203
    var key = this.generate_breakpoint_key(filename, lineno, colno);
204
    this.dbg_breakpoints[key] = new Sk.Breakpoint(filename, lineno, colno);
205
    if (temporary) {
206
        this.tmp_breakpoints[key] = true;
207
    }
208
};
209
 
210
Sk.Debugger.prototype.suspension_handler = function(susp) {
211
    return new Promise(function(resolve, reject) {
212
        try {
213
            resolve(susp.resume());
214
        } catch(e) {
215
            reject(e);
216
        }
217
    });
218
};
219
 
220
Sk.Debugger.prototype.resume = function() {
221
    // Reset the suspension stack to the topmost
222
    this.current_suspension = this.suspension_stack.length - 1;
223
 
224
    if (this.suspension_stack.length === 0) {
225
        this.print("No running program");
226
    } else {
227
        var promise = this.suspension_handler(this.get_active_suspension());
228
        promise.then(this.success.bind(this), this.error.bind(this));
229
    }
230
};
231
 
232
Sk.Debugger.prototype.pop_suspension_stack = function() {
233
    this.suspension_stack.pop();
234
    this.current_suspension -= 1;
235
};
236
 
237
Sk.Debugger.prototype.success = function(r) {
238
    if (r instanceof Sk.misceval.Suspension) {
239
        this.set_suspension(r);
240
    } else {
241
        if (this.suspension_stack.length > 0) {
242
            // Current suspension needs to be popped of the stack
243
            this.pop_suspension_stack();
244
 
245
            if (this.suspension_stack.length === 0) {
246
                this.print("Program execution complete");
247
                return;
248
            }
249
 
250
            var parent_suspension = this.get_active_suspension();
251
            // The child has completed the execution. So override the child's resume
252
            // so we can continue the execution.
253
            parent_suspension.child.resume = function() {};
254
            this.resume();
255
        } else {
256
            this.print("Program execution complete");
257
        }
258
    }
259
};
260
 
261
Sk.Debugger.prototype.error = function(e) {
262
    this.print("Traceback (most recent call last):");
263
    for (var idx = 0; idx < e.traceback.length; ++idx) {
264
        this.print("  File \"" + e.traceback[idx].filename + "\", line " + e.traceback[idx].lineno + ", in <module>");
265
        var code = this.get_source_line(e.traceback[idx].lineno - 1);
266
        code = code.trim();
267
        code = "    " + code;
268
        this.print(code);
269
    }
270
 
271
    var err_ty = e.constructor.tp$name;
272
    for (idx = 0; idx < e.args.v.length; ++idx) {
273
        this.print(err_ty + ": " + e.args.v[idx].v);
274
    }
275
};
276
 
277
Sk.Debugger.prototype.asyncToPromise = function(suspendablefn, suspHandlers, debugger_obj) {
278
    return new Promise(function(resolve, reject) {
279
        try {
280
            var r = suspendablefn();
281
 
282
            (function handleResponse (r) {
283
                try {
284
                    while (r instanceof Sk.misceval.Suspension) {
285
                        debugger_obj.set_suspension(r);
286
                        return;
287
                    }
288
 
289
                    resolve(r);
290
                } catch(e) {
291
                    reject(e);
292
                }
293
            })(r);
294
 
295
        } catch (e) {
296
            reject(e);
297
        }
298
    });
299
};
300
 
301
Sk.Debugger.prototype.execute = function(suspendablefn, suspHandlers) {
302
    var r = suspendablefn();
303
 
304
    if (r instanceof Sk.misceval.Suspension) {
305
        this.suspensions.concat(r);
306
        this.eval_callback(r);
307
    }
308
};
309
 
310
goog.exportSymbol("Sk.Debugger", Sk.Debugger);