2
2
* Debugger support for skulpt module
3
3
*/
4
4
5
- var Sk = Sk || { } ; //jshint ignore:line
5
+ var Sk = window . Sk || { } ; //jshint ignore:line
6
6
7
7
/**
8
8
* Changes :
@@ -22,7 +22,7 @@ var Sk = Sk || {}; //jshint ignore:line
22
22
*/
23
23
24
24
var DEBUG_DEBUGGER = false ;
25
- var debuggerLog = function ( ) {
25
+ var debuggerLog = function ( ) {
26
26
if ( DEBUG_DEBUGGER ) {
27
27
// 1. Convert args to a normal array
28
28
var args = Array . prototype . slice . call ( arguments ) ;
@@ -48,15 +48,15 @@ function debuggerHasOwnProperty(obj, prop) {
48
48
( ! ( prop in proto ) || proto [ prop ] !== obj [ prop ] ) ;
49
49
}
50
50
51
- Sk . Breakpoint = function ( filename , lineno , colno ) {
51
+ Sk . Breakpoint = function ( filename , lineno , colno ) {
52
52
this . filename = filename ;
53
53
this . lineno = lineno ;
54
54
this . colno = colno ;
55
55
this . enabled = true ;
56
56
this . ignore_count = 0 ;
57
57
} ;
58
58
59
- Sk . Debugger = function ( filename , output_callback ) {
59
+ Sk . Debugger = function ( filename , output_callback ) {
60
60
this . dbg_breakpoints = { } ;
61
61
this . tmp_breakpoints = { } ;
62
62
this . suspension_stack = [ ] ;
@@ -66,60 +66,119 @@ Sk.Debugger = function(filename, output_callback) {
66
66
this . output_callback = output_callback ;
67
67
this . step_mode = false ;
68
68
this . filename = filename ;
69
+
70
+ /**
71
+ * Contains the last references of objects that will be retrieved by Skulpt's promises.
72
+ *
73
+ * This is required because the way Skulpt handles classes is so as it stores the reference of
74
+ * the class before calling a method, and then retrieves it after. Since the codecast implementation
75
+ * modify the references each time something is modified into an object, we need to get the updated
76
+ * object, hence the last reference of that object when we want to get the result of the promise.
77
+ *
78
+ * Storing references of objects would disable the garbage collector to clear those objects when necessary,
79
+ * so we also want to clean those references when there are not needed anymore.
80
+ * Since it's possible to have a method of an object that calls another, or itself recursively, we store
81
+ * the number of use of the reference, so we know when we can safely clear the reference.
82
+ *
83
+ * {
84
+ * UUID_OBJ_1 : {
85
+ * reference: The object 1,
86
+ * nb: The number of use
87
+ * },
88
+ * UUID_OBJ_2 : {
89
+ * reference: The object 2,
90
+ * nb: The number of use
91
+ * }
92
+ * }
93
+ *
94
+ * When nb becomes 0, we can remove the reference as we don't need it anymore.
95
+ */
96
+ this . _promise_references = { } ;
97
+ } ;
98
+
99
+ /**
100
+ * Add a reference of an object that will be retrieved later using a Skulpt promise, and which
101
+ * may later have a new reference.
102
+ *
103
+ * @param object The object.
104
+ */
105
+ Sk . Debugger . prototype . registerPromiseReference = function ( object ) {
106
+ if ( object . hasOwnProperty ( '_uuid' ) ) {
107
+ if ( ! this . _promise_references . hasOwnProperty ( object . _uuid ) ) {
108
+ this . _promise_references [ object . _uuid ] = {
109
+ reference : object ,
110
+ nb : 0
111
+ }
112
+ }
113
+
114
+ this . _promise_references [ object . _uuid ] . nb ++ ;
115
+ }
69
116
} ;
70
117
71
- Sk . Debugger . prototype . print = function ( txt ) {
118
+ /**
119
+ * Updates the promise reference of an object if it exists.
120
+ *
121
+ * @param object The new object reference.
122
+ */
123
+ Sk . Debugger . prototype . updatePromiseReference = function ( object ) {
124
+ if ( object . hasOwnProperty ( '_uuid' ) && this . _promise_references . hasOwnProperty ( object . _uuid ) ) {
125
+ this . _promise_references [ object . _uuid ] . reference = object ;
126
+ }
127
+ } ;
128
+
129
+ Sk . Debugger . prototype . print = function ( txt ) {
72
130
if ( this . output_callback != null ) {
73
131
this . output_callback . print ( txt + "\n" ) ;
74
132
}
75
133
} ;
76
134
77
- Sk . Debugger . prototype . get_source_line = function ( lineno ) {
135
+ Sk . Debugger . prototype . get_source_line = function ( lineno ) {
78
136
if ( this . output_callback != null ) {
79
137
return this . output_callback . get_source_line ( lineno ) ;
80
138
}
81
139
82
140
return "" ;
83
141
} ;
84
142
85
- Sk . Debugger . prototype . move_up_the_stack = function ( ) {
143
+ Sk . Debugger . prototype . move_up_the_stack = function ( ) {
86
144
this . current_suspension = Math . min ( this . current_suspension + 1 , this . suspension_stack . length - 1 ) ;
87
145
} ;
88
146
89
- Sk . Debugger . prototype . move_down_the_stack = function ( ) {
147
+ Sk . Debugger . prototype . move_down_the_stack = function ( ) {
90
148
this . current_suspension = Math . max ( this . current_suspension - 1 , 0 ) ;
91
149
} ;
92
150
93
- Sk . Debugger . prototype . enable_step_mode = function ( ) {
151
+ Sk . Debugger . prototype . enable_step_mode = function ( ) {
94
152
this . step_mode = true ;
95
153
} ;
96
154
97
- Sk . Debugger . prototype . disable_step_mode = function ( ) {
155
+ Sk . Debugger . prototype . disable_step_mode = function ( ) {
98
156
this . step_mode = false ;
99
157
} ;
100
158
101
- Sk . Debugger . prototype . get_suspension_stack = function ( ) {
159
+ Sk . Debugger . prototype . get_suspension_stack = function ( ) {
102
160
return this . suspension_stack ;
103
161
} ;
104
162
105
- Sk . Debugger . prototype . get_active_suspension = function ( ) {
163
+ Sk . Debugger . prototype . get_active_suspension = function ( ) {
106
164
if ( this . suspension_stack . length === 0 ) {
107
165
return null ;
108
166
}
109
167
110
168
return this . suspension_stack [ this . current_suspension ] ;
111
169
} ;
112
170
113
- Sk . Debugger . prototype . generate_breakpoint_key = function ( filename , lineno , colno ) {
171
+ Sk . Debugger . prototype . generate_breakpoint_key = function ( filename , lineno , colno ) {
114
172
var key = filename + "-" + lineno ;
115
173
return key ;
116
174
} ;
117
175
118
- Sk . Debugger . prototype . check_breakpoints = function ( filename , lineno , colno , globals , locals ) {
176
+ Sk . Debugger . prototype . check_breakpoints = function ( filename , lineno , colno , globals , locals ) {
119
177
// debuggerLog('check_breakpoints', filename, lineno, colno, globals, locals);
120
178
121
179
// If Step mode is enabled then ignore breakpoints since we will just break
122
180
// at every line.
181
+ return true ;
123
182
if ( this . step_mode === true ) {
124
183
return true ;
125
184
}
@@ -147,27 +206,27 @@ Sk.Debugger.prototype.check_breakpoints = function(filename, lineno, colno, glob
147
206
return false ;
148
207
} ;
149
208
150
- Sk . Debugger . prototype . get_breakpoints_list = function ( ) {
209
+ Sk . Debugger . prototype . get_breakpoints_list = function ( ) {
151
210
return this . dbg_breakpoints ;
152
211
} ;
153
212
154
- Sk . Debugger . prototype . disable_breakpoint = function ( filename , lineno , colno ) {
213
+ Sk . Debugger . prototype . disable_breakpoint = function ( filename , lineno , colno ) {
155
214
var key = this . generate_breakpoint_key ( filename , lineno , colno ) ;
156
215
157
216
if ( debuggerHasOwnProperty ( this . dbg_breakpoints , key ) ) {
158
217
this . dbg_breakpoints [ key ] . enabled = false ;
159
218
}
160
219
} ;
161
220
162
- Sk . Debugger . prototype . enable_breakpoint = function ( filename , lineno , colno ) {
221
+ Sk . Debugger . prototype . enable_breakpoint = function ( filename , lineno , colno ) {
163
222
var key = this . generate_breakpoint_key ( filename , lineno , colno ) ;
164
223
165
224
if ( debuggerHasOwnProperty ( this . dbg_breakpoints , key ) ) {
166
225
this . dbg_breakpoints [ key ] . enabled = true ;
167
226
}
168
227
} ;
169
228
170
- Sk . Debugger . prototype . clear_breakpoint = function ( filename , lineno , colno ) {
229
+ Sk . Debugger . prototype . clear_breakpoint = function ( filename , lineno , colno ) {
171
230
var key = this . generate_breakpoint_key ( filename , lineno , colno ) ;
172
231
if ( debuggerHasOwnProperty ( this . dbg_breakpoints , key ) ) {
173
232
delete this . dbg_breakpoints [ key ] ;
@@ -177,20 +236,20 @@ Sk.Debugger.prototype.clear_breakpoint = function(filename, lineno, colno) {
177
236
}
178
237
} ;
179
238
180
- Sk . Debugger . prototype . clear_all_breakpoints = function ( ) {
239
+ Sk . Debugger . prototype . clear_all_breakpoints = function ( ) {
181
240
this . dbg_breakpoints = { } ;
182
241
this . tmp_breakpoints = { } ;
183
242
} ;
184
243
185
- Sk . Debugger . prototype . set_ignore_count = function ( filename , lineno , colno , count ) {
244
+ Sk . Debugger . prototype . set_ignore_count = function ( filename , lineno , colno , count ) {
186
245
var key = this . generate_breakpoint_key ( filename , lineno , colno ) ;
187
246
if ( debuggerHasOwnProperty ( this . dbg_breakpoints , key ) ) {
188
247
var bp = this . dbg_breakpoints [ key ] ;
189
248
bp . ignore_count = count ;
190
249
}
191
250
} ;
192
251
193
- Sk . Debugger . prototype . set_condition = function ( filename , lineno , colno , lhs , cond , rhs ) {
252
+ Sk . Debugger . prototype . set_condition = function ( filename , lineno , colno , lhs , cond , rhs ) {
194
253
var key = this . generate_breakpoint_key ( filename , lineno , colno ) ;
195
254
var bp ;
196
255
if ( debuggerHasOwnProperty ( this . dbg_breakpoints , key ) ) {
@@ -204,7 +263,7 @@ Sk.Debugger.prototype.set_condition = function(filename, lineno, colno, lhs, con
204
263
this . dbg_breakpoints [ key ] = bp ;
205
264
} ;
206
265
207
- Sk . Debugger . prototype . print_suspension_info = function ( suspension ) {
266
+ Sk . Debugger . prototype . print_suspension_info = function ( suspension ) {
208
267
var filename = suspension . $filename ;
209
268
var lineno = suspension . $lineno ;
210
269
var colno = suspension . $colno ;
@@ -217,7 +276,7 @@ Sk.Debugger.prototype.print_suspension_info = function(suspension) {
217
276
}
218
277
} ;
219
278
220
- Sk . Debugger . prototype . set_suspension = function ( suspension ) {
279
+ Sk . Debugger . prototype . set_suspension = function ( suspension ) {
221
280
debuggerLog ( 'set_suspension' , suspension ) ;
222
281
223
282
var parent = null ;
@@ -244,25 +303,25 @@ Sk.Debugger.prototype.set_suspension = function(suspension) {
244
303
this . print_suspension_info ( suspension ) ;
245
304
} ;
246
305
247
- Sk . Debugger . prototype . add_breakpoint = function ( filename , lineno , colno , temporary ) {
306
+ Sk . Debugger . prototype . add_breakpoint = function ( filename , lineno , colno , temporary ) {
248
307
var key = this . generate_breakpoint_key ( filename , lineno , colno ) ;
249
308
this . dbg_breakpoints [ key ] = new Sk . Breakpoint ( filename , lineno , colno ) ;
250
309
if ( temporary ) {
251
310
this . tmp_breakpoints [ key ] = true ;
252
311
}
253
312
} ;
254
313
255
- Sk . Debugger . prototype . suspension_handler = function ( susp ) {
256
- return new Promise ( function ( resolve , reject ) {
314
+ Sk . Debugger . prototype . suspension_handler = function ( susp ) {
315
+ return new Promise ( function ( resolve , reject ) {
257
316
try {
258
317
resolve ( susp . resume ( ) ) ;
259
- } catch ( e ) {
318
+ } catch ( e ) {
260
319
reject ( e ) ;
261
320
}
262
321
} ) ;
263
322
} ;
264
323
265
- Sk . Debugger . prototype . resume = function ( resolve , reject ) {
324
+ Sk . Debugger . prototype . resume = function ( resolve , reject ) {
266
325
debuggerLog ( 'resume' ) ;
267
326
268
327
// Reset the suspension stack to the topmost
@@ -277,20 +336,47 @@ Sk.Debugger.prototype.resume = function(resolve, reject) {
277
336
} else {
278
337
var promise = this . suspension_handler ( this . get_active_suspension ( ) ) ;
279
338
var self = this ;
280
- promise . then ( function ( value ) {
281
- self . success ( value , resolve , reject ) ;
282
- } , function ( error ) {
283
- self . error ( error , reject ) ;
339
+ promise . then ( function ( value ) {
340
+ if ( value && value . data && value . data . promise ) {
341
+ // If waiting for input, wait that it has resolved too before continuing.
342
+ value . data . promise . then ( ( inputValue ) => {
343
+ // Skulpt is taking the value into the result parameter, so let's put it in !
344
+ value . data . result = inputValue ;
345
+
346
+ self . success ( value , resolve , reject ) ;
347
+ } ) ;
348
+ } else if ( value && value . hasOwnProperty ( '_uuid' ) ) {
349
+ /**
350
+ * In the case the value is a class, its reference may have changed since the
351
+ * creation of the Promise, which is done before the call to a method.
352
+ * We want to get its last reference.
353
+ */
354
+ value = self . _promise_references [ value . _uuid ] . reference ;
355
+ self . _promise_references [ value . _uuid ] . nb -- ;
356
+ if ( self . _promise_references [ value . _uuid ] . nb < 1 ) {
357
+ delete self . _promise_references [ value . _uuid ] ;
358
+ }
359
+
360
+ self . success ( value , resolve , reject ) ;
361
+ } else {
362
+ self . success ( value , resolve , reject ) ;
363
+ }
364
+ } , function ( error ) {
365
+ /**
366
+ * Note : We call resolve and not reject in case of error because resolve throws an Exception
367
+ * and breaks the player which stops when there is an exception.
368
+ */
369
+ self . error ( error , resolve ) ;
284
370
} ) ;
285
371
}
286
372
} ;
287
373
288
- Sk . Debugger . prototype . pop_suspension_stack = function ( ) {
374
+ Sk . Debugger . prototype . pop_suspension_stack = function ( ) {
289
375
this . suspension_stack . pop ( ) ;
290
376
this . current_suspension -= 1 ;
291
377
} ;
292
378
293
- Sk . Debugger . prototype . success = function ( r , resolve , reject ) {
379
+ Sk . Debugger . prototype . success = function ( r , resolve , reject ) {
294
380
debuggerLog ( 'success' , r , resolve ) ;
295
381
296
382
if ( r instanceof Sk . misceval . Suspension ) {
@@ -321,7 +407,7 @@ Sk.Debugger.prototype.success = function(r, resolve, reject) {
321
407
var parent_suspension = this . get_active_suspension ( ) ;
322
408
// The child has completed the execution. So override the child's resume
323
409
// so we can continue the execution.
324
- parent_suspension . child . resume = function ( ) {
410
+ parent_suspension . child . resume = function ( ) {
325
411
return r ;
326
412
} ;
327
413
@@ -338,7 +424,7 @@ Sk.Debugger.prototype.success = function(r, resolve, reject) {
338
424
}
339
425
} ;
340
426
341
- Sk . Debugger . prototype . error = function ( e , reject ) {
427
+ Sk . Debugger . prototype . error = function ( e , reject ) {
342
428
debuggerLog ( 'error' , e ) ;
343
429
344
430
if ( this . output_callback != null ) {
@@ -363,20 +449,20 @@ Sk.Debugger.prototype.error = function(e, reject) {
363
449
}
364
450
} ;
365
451
366
- Sk . Debugger . prototype . asyncToPromise = function ( suspendablefn , suspHandlers , debugger_obj ) {
367
- return new Promise ( function ( resolve , reject ) {
452
+ Sk . Debugger . prototype . asyncToPromise = function ( suspendablefn , suspHandlers , debugger_obj ) {
453
+ return new Promise ( function ( resolve , reject ) {
368
454
try {
369
455
var r = suspendablefn ( ) ;
370
456
371
- ( function handleResponse ( r ) {
457
+ ( function handleResponse ( r ) {
372
458
try {
373
459
while ( r instanceof Sk . misceval . Suspension ) {
374
460
debugger_obj . set_suspension ( r ) ;
375
461
return ;
376
462
}
377
463
378
464
resolve ( r ) ;
379
- } catch ( e ) {
465
+ } catch ( e ) {
380
466
reject ( e ) ;
381
467
}
382
468
} ) ( r ) ;
@@ -387,7 +473,7 @@ Sk.Debugger.prototype.asyncToPromise = function(suspendablefn, suspHandlers, deb
387
473
} ) ;
388
474
} ;
389
475
390
- Sk . Debugger . prototype . execute = function ( suspendablefn , suspHandlers ) {
476
+ Sk . Debugger . prototype . execute = function ( suspendablefn , suspHandlers ) {
391
477
var r = suspendablefn ( ) ;
392
478
393
479
if ( r instanceof Sk . misceval . Suspension ) {
0 commit comments