|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* |
|
8 * JavaScript Debugging support - Source Text functions |
|
9 */ |
|
10 |
|
11 #include <ctype.h> |
|
12 #include "jsd.h" |
|
13 #include "jsprf.h" |
|
14 |
|
15 #ifdef DEBUG |
|
16 void JSD_ASSERT_VALID_SOURCE_TEXT(JSDSourceText* jsdsrc) |
|
17 { |
|
18 MOZ_ASSERT(jsdsrc); |
|
19 MOZ_ASSERT(jsdsrc->url); |
|
20 } |
|
21 #endif |
|
22 |
|
23 /***************************************************************************/ |
|
24 /* XXX add notification */ |
|
25 |
|
26 static void |
|
27 _clearText(JSDContext* jsdc, JSDSourceText* jsdsrc) |
|
28 { |
|
29 if( jsdsrc->text ) |
|
30 free(jsdsrc->text); |
|
31 jsdsrc->text = nullptr; |
|
32 jsdsrc->textLength = 0; |
|
33 jsdsrc->textSpace = 0; |
|
34 jsdsrc->status = JSD_SOURCE_CLEARED; |
|
35 jsdsrc->dirty = true; |
|
36 jsdsrc->alterCount = jsdc->sourceAlterCount++ ; |
|
37 jsdsrc->doingEval = false; |
|
38 } |
|
39 |
|
40 static bool |
|
41 _appendText(JSDContext* jsdc, JSDSourceText* jsdsrc, |
|
42 const char* text, size_t length) |
|
43 { |
|
44 #define MEMBUF_GROW 1000 |
|
45 |
|
46 unsigned neededSize = jsdsrc->textLength + length; |
|
47 |
|
48 if( neededSize > jsdsrc->textSpace ) |
|
49 { |
|
50 char* newBuf; |
|
51 unsigned iNewSize; |
|
52 |
|
53 /* if this is the first alloc, the req might be all that's needed*/ |
|
54 if( ! jsdsrc->textSpace ) |
|
55 iNewSize = length; |
|
56 else |
|
57 iNewSize = (neededSize * 5 / 4) + MEMBUF_GROW; |
|
58 |
|
59 newBuf = (char*) realloc(jsdsrc->text, iNewSize); |
|
60 if( ! newBuf ) |
|
61 { |
|
62 /* try again with the minimal size really asked for */ |
|
63 iNewSize = neededSize; |
|
64 newBuf = (char*) realloc(jsdsrc->text, iNewSize); |
|
65 if( ! newBuf ) |
|
66 { |
|
67 /* out of memory */ |
|
68 _clearText( jsdc, jsdsrc ); |
|
69 jsdsrc->status = JSD_SOURCE_FAILED; |
|
70 return false; |
|
71 } |
|
72 } |
|
73 |
|
74 jsdsrc->text = newBuf; |
|
75 jsdsrc->textSpace = iNewSize; |
|
76 } |
|
77 |
|
78 memcpy(jsdsrc->text + jsdsrc->textLength, text, length); |
|
79 jsdsrc->textLength += length; |
|
80 return true; |
|
81 } |
|
82 |
|
83 static JSDSourceText* |
|
84 _newSource(JSDContext* jsdc, char* url) |
|
85 { |
|
86 JSDSourceText* jsdsrc = (JSDSourceText*)calloc(1,sizeof(JSDSourceText)); |
|
87 if( ! jsdsrc ) |
|
88 return nullptr; |
|
89 |
|
90 jsdsrc->url = url; |
|
91 jsdsrc->status = JSD_SOURCE_INITED; |
|
92 jsdsrc->dirty = true; |
|
93 jsdsrc->alterCount = jsdc->sourceAlterCount++ ; |
|
94 |
|
95 return jsdsrc; |
|
96 } |
|
97 |
|
98 static void |
|
99 _destroySource(JSDContext* jsdc, JSDSourceText* jsdsrc) |
|
100 { |
|
101 MOZ_ASSERT(nullptr == jsdsrc->text); /* must _clearText() first */ |
|
102 free(jsdsrc->url); |
|
103 free(jsdsrc); |
|
104 } |
|
105 |
|
106 static void |
|
107 _removeSource(JSDContext* jsdc, JSDSourceText* jsdsrc) |
|
108 { |
|
109 JS_REMOVE_LINK(&jsdsrc->links); |
|
110 _clearText(jsdc, jsdsrc); |
|
111 _destroySource(jsdc, jsdsrc); |
|
112 } |
|
113 |
|
114 static JSDSourceText* |
|
115 _addSource(JSDContext* jsdc, char* url) |
|
116 { |
|
117 JSDSourceText* jsdsrc = _newSource(jsdc, url); |
|
118 if( ! jsdsrc ) |
|
119 return nullptr; |
|
120 JS_INSERT_LINK(&jsdsrc->links, &jsdc->sources); |
|
121 return jsdsrc; |
|
122 } |
|
123 |
|
124 static void |
|
125 _moveSourceToRemovedList(JSDContext* jsdc, JSDSourceText* jsdsrc) |
|
126 { |
|
127 _clearText(jsdc, jsdsrc); |
|
128 JS_REMOVE_LINK(&jsdsrc->links); |
|
129 JS_INSERT_LINK(&jsdsrc->links, &jsdc->removedSources); |
|
130 } |
|
131 |
|
132 static void |
|
133 _removeSourceFromRemovedList( JSDContext* jsdc, JSDSourceText* jsdsrc ) |
|
134 { |
|
135 JS_REMOVE_LINK(&jsdsrc->links); |
|
136 _destroySource( jsdc, jsdsrc ); |
|
137 } |
|
138 |
|
139 static bool |
|
140 _isSourceInSourceList(JSDContext* jsdc, JSDSourceText* jsdsrcToFind) |
|
141 { |
|
142 JSDSourceText *jsdsrc; |
|
143 |
|
144 for( jsdsrc = (JSDSourceText*)jsdc->sources.next; |
|
145 jsdsrc != (JSDSourceText*)&jsdc->sources; |
|
146 jsdsrc = (JSDSourceText*)jsdsrc->links.next ) |
|
147 { |
|
148 if( jsdsrc == jsdsrcToFind ) |
|
149 return true; |
|
150 } |
|
151 return false; |
|
152 } |
|
153 |
|
154 /* compare strings in a case insensitive manner with a length limit |
|
155 */ |
|
156 |
|
157 static int |
|
158 strncasecomp (const char* one, const char * two, int n) |
|
159 { |
|
160 const char *pA; |
|
161 const char *pB; |
|
162 |
|
163 for(pA=one, pB=two;; pA++, pB++) |
|
164 { |
|
165 int tmp; |
|
166 if (pA == one+n) |
|
167 return 0; |
|
168 if (!(*pA && *pB)) |
|
169 return *pA - *pB; |
|
170 tmp = tolower(*pA) - tolower(*pB); |
|
171 if (tmp) |
|
172 return tmp; |
|
173 } |
|
174 } |
|
175 |
|
176 static const char file_url_prefix[] = "file:"; |
|
177 #define FILE_URL_PREFIX_LEN (sizeof file_url_prefix - 1) |
|
178 |
|
179 char* |
|
180 jsd_BuildNormalizedURL( const char* url_string ) |
|
181 { |
|
182 char *new_url_string; |
|
183 |
|
184 if( ! url_string ) |
|
185 return nullptr; |
|
186 |
|
187 if (!strncasecomp(url_string, file_url_prefix, FILE_URL_PREFIX_LEN) && |
|
188 url_string[FILE_URL_PREFIX_LEN + 0] == '/' && |
|
189 url_string[FILE_URL_PREFIX_LEN + 1] == '/') { |
|
190 new_url_string = JS_smprintf("%s%s", |
|
191 file_url_prefix, |
|
192 url_string + FILE_URL_PREFIX_LEN + 2); |
|
193 } else { |
|
194 new_url_string = strdup(url_string); |
|
195 } |
|
196 return new_url_string; |
|
197 } |
|
198 |
|
199 /***************************************************************************/ |
|
200 |
|
201 void |
|
202 jsd_DestroyAllSources( JSDContext* jsdc ) |
|
203 { |
|
204 JSDSourceText *jsdsrc; |
|
205 JSDSourceText *next; |
|
206 |
|
207 for( jsdsrc = (JSDSourceText*)jsdc->sources.next; |
|
208 jsdsrc != (JSDSourceText*)&jsdc->sources; |
|
209 jsdsrc = next ) |
|
210 { |
|
211 next = (JSDSourceText*)jsdsrc->links.next; |
|
212 _removeSource( jsdc, jsdsrc ); |
|
213 } |
|
214 |
|
215 for( jsdsrc = (JSDSourceText*)jsdc->removedSources.next; |
|
216 jsdsrc != (JSDSourceText*)&jsdc->removedSources; |
|
217 jsdsrc = next ) |
|
218 { |
|
219 next = (JSDSourceText*)jsdsrc->links.next; |
|
220 _removeSourceFromRemovedList( jsdc, jsdsrc ); |
|
221 } |
|
222 |
|
223 } |
|
224 |
|
225 JSDSourceText* |
|
226 jsd_IterateSources(JSDContext* jsdc, JSDSourceText **iterp) |
|
227 { |
|
228 JSDSourceText *jsdsrc = *iterp; |
|
229 |
|
230 if( !jsdsrc ) |
|
231 jsdsrc = (JSDSourceText *)jsdc->sources.next; |
|
232 if( jsdsrc == (JSDSourceText *)&jsdc->sources ) |
|
233 return nullptr; |
|
234 *iterp = (JSDSourceText *)jsdsrc->links.next; |
|
235 return jsdsrc; |
|
236 } |
|
237 |
|
238 JSDSourceText* |
|
239 jsd_FindSourceForURL(JSDContext* jsdc, const char* url) |
|
240 { |
|
241 JSDSourceText *jsdsrc; |
|
242 |
|
243 for( jsdsrc = (JSDSourceText *)jsdc->sources.next; |
|
244 jsdsrc != (JSDSourceText *)&jsdc->sources; |
|
245 jsdsrc = (JSDSourceText *)jsdsrc->links.next ) |
|
246 { |
|
247 if( 0 == strcmp(jsdsrc->url, url) ) |
|
248 return jsdsrc; |
|
249 } |
|
250 return nullptr; |
|
251 } |
|
252 |
|
253 const char* |
|
254 jsd_GetSourceURL(JSDContext* jsdc, JSDSourceText* jsdsrc) |
|
255 { |
|
256 return jsdsrc->url; |
|
257 } |
|
258 |
|
259 bool |
|
260 jsd_GetSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc, |
|
261 const char** ppBuf, int* pLen ) |
|
262 { |
|
263 *ppBuf = jsdsrc->text; |
|
264 *pLen = jsdsrc->textLength; |
|
265 return true; |
|
266 } |
|
267 |
|
268 void |
|
269 jsd_ClearSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc) |
|
270 { |
|
271 if( JSD_SOURCE_INITED != jsdsrc->status && |
|
272 JSD_SOURCE_PARTIAL != jsdsrc->status ) |
|
273 { |
|
274 _clearText(jsdc, jsdsrc); |
|
275 } |
|
276 } |
|
277 |
|
278 JSDSourceStatus |
|
279 jsd_GetSourceStatus(JSDContext* jsdc, JSDSourceText* jsdsrc) |
|
280 { |
|
281 return jsdsrc->status; |
|
282 } |
|
283 |
|
284 bool |
|
285 jsd_IsSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc) |
|
286 { |
|
287 return jsdsrc->dirty; |
|
288 } |
|
289 |
|
290 void |
|
291 jsd_SetSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc, bool dirty) |
|
292 { |
|
293 jsdsrc->dirty = dirty; |
|
294 } |
|
295 |
|
296 unsigned |
|
297 jsd_GetSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc) |
|
298 { |
|
299 return jsdsrc->alterCount; |
|
300 } |
|
301 |
|
302 unsigned |
|
303 jsd_IncrementSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc) |
|
304 { |
|
305 return jsdsrc->alterCount = jsdc->sourceAlterCount++; |
|
306 } |
|
307 |
|
308 /***************************************************************************/ |
|
309 |
|
310 #if defined(DEBUG) && 0 |
|
311 void DEBUG_ITERATE_SOURCES( JSDContext* jsdc ) |
|
312 { |
|
313 JSDSourceText* iterp = nullptr; |
|
314 JSDSourceText* jsdsrc = nullptr; |
|
315 int dummy; |
|
316 |
|
317 while( nullptr != (jsdsrc = jsd_IterateSources(jsdc, &iterp)) ) |
|
318 { |
|
319 const char* url; |
|
320 const char* text; |
|
321 int len; |
|
322 bool dirty; |
|
323 JSDStreamStatus status; |
|
324 bool gotSrc; |
|
325 |
|
326 url = JSD_GetSourceURL(jsdc, jsdsrc); |
|
327 dirty = JSD_IsSourceDirty(jsdc, jsdsrc); |
|
328 status = JSD_GetSourceStatus(jsdc, jsdsrc); |
|
329 gotSrc = JSD_GetSourceText(jsdc, jsdsrc, &text, &len ); |
|
330 |
|
331 dummy = 0; /* gives us a line to set breakpoint... */ |
|
332 } |
|
333 } |
|
334 #else |
|
335 #define DEBUG_ITERATE_SOURCES(x) ((void)x) |
|
336 #endif |
|
337 |
|
338 /***************************************************************************/ |
|
339 |
|
340 JSDSourceText* |
|
341 jsd_NewSourceText(JSDContext* jsdc, const char* url) |
|
342 { |
|
343 JSDSourceText* jsdsrc; |
|
344 char* new_url_string; |
|
345 |
|
346 JSD_LOCK_SOURCE_TEXT(jsdc); |
|
347 |
|
348 new_url_string = jsd_BuildNormalizedURL(url); |
|
349 |
|
350 if( ! new_url_string ) |
|
351 return nullptr; |
|
352 |
|
353 jsdsrc = jsd_FindSourceForURL(jsdc, new_url_string); |
|
354 |
|
355 if( jsdsrc ) |
|
356 { |
|
357 if( jsdsrc->doingEval ) |
|
358 { |
|
359 free(new_url_string); |
|
360 JSD_UNLOCK_SOURCE_TEXT(jsdc); |
|
361 return nullptr; |
|
362 } |
|
363 else |
|
364 _moveSourceToRemovedList(jsdc, jsdsrc); |
|
365 } |
|
366 |
|
367 jsdsrc = _addSource( jsdc, new_url_string ); |
|
368 |
|
369 JSD_UNLOCK_SOURCE_TEXT(jsdc); |
|
370 |
|
371 return jsdsrc; |
|
372 } |
|
373 |
|
374 JSDSourceText* |
|
375 jsd_AppendSourceText(JSDContext* jsdc, |
|
376 JSDSourceText* jsdsrc, |
|
377 const char* text, /* *not* zero terminated */ |
|
378 size_t length, |
|
379 JSDSourceStatus status) |
|
380 { |
|
381 JSD_LOCK_SOURCE_TEXT(jsdc); |
|
382 |
|
383 if( jsdsrc->doingEval ) |
|
384 { |
|
385 JSD_UNLOCK_SOURCE_TEXT(jsdc); |
|
386 return nullptr; |
|
387 } |
|
388 |
|
389 if( ! _isSourceInSourceList( jsdc, jsdsrc ) ) |
|
390 { |
|
391 _removeSourceFromRemovedList( jsdc, jsdsrc ); |
|
392 JSD_UNLOCK_SOURCE_TEXT(jsdc); |
|
393 return nullptr; |
|
394 } |
|
395 |
|
396 if( text && length && ! _appendText( jsdc, jsdsrc, text, length ) ) |
|
397 { |
|
398 jsdsrc->dirty = true; |
|
399 jsdsrc->alterCount = jsdc->sourceAlterCount++ ; |
|
400 jsdsrc->status = JSD_SOURCE_FAILED; |
|
401 _moveSourceToRemovedList(jsdc, jsdsrc); |
|
402 JSD_UNLOCK_SOURCE_TEXT(jsdc); |
|
403 return nullptr; |
|
404 } |
|
405 |
|
406 jsdsrc->dirty = true; |
|
407 jsdsrc->alterCount = jsdc->sourceAlterCount++ ; |
|
408 jsdsrc->status = status; |
|
409 DEBUG_ITERATE_SOURCES(jsdc); |
|
410 JSD_UNLOCK_SOURCE_TEXT(jsdc); |
|
411 return jsdsrc; |
|
412 } |
|
413 |
|
414 JSDSourceText* |
|
415 jsd_AppendUCSourceText(JSDContext* jsdc, |
|
416 JSDSourceText* jsdsrc, |
|
417 const jschar* text, /* *not* zero terminated */ |
|
418 size_t length, |
|
419 JSDSourceStatus status) |
|
420 { |
|
421 #define UNICODE_TRUNCATE_BUF_SIZE 1024 |
|
422 static char* buf = nullptr; |
|
423 int remaining = length; |
|
424 |
|
425 if(!text || !length) |
|
426 return jsd_AppendSourceText(jsdc, jsdsrc, nullptr, 0, status); |
|
427 |
|
428 JSD_LOCK_SOURCE_TEXT(jsdc); |
|
429 if(!buf) |
|
430 { |
|
431 buf = js_pod_malloc<char>(UNICODE_TRUNCATE_BUF_SIZE); |
|
432 if(!buf) |
|
433 { |
|
434 JSD_UNLOCK_SOURCE_TEXT(jsdc); |
|
435 return nullptr; |
|
436 } |
|
437 } |
|
438 while(remaining && jsdsrc) { |
|
439 int bytes = (remaining < UNICODE_TRUNCATE_BUF_SIZE) ? remaining : UNICODE_TRUNCATE_BUF_SIZE; |
|
440 int i; |
|
441 for(i = 0; i < bytes; i++) |
|
442 buf[i] = (const char) *(text++); |
|
443 jsdsrc = jsd_AppendSourceText(jsdc,jsdsrc, |
|
444 buf, bytes, |
|
445 JSD_SOURCE_PARTIAL); |
|
446 remaining -= bytes; |
|
447 } |
|
448 if(jsdsrc && status != JSD_SOURCE_PARTIAL) |
|
449 jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc, nullptr, 0, status); |
|
450 |
|
451 JSD_UNLOCK_SOURCE_TEXT(jsdc); |
|
452 return jsdsrc; |
|
453 } |
|
454 |
|
455 /* convienence function for adding complete source of url in one call */ |
|
456 bool |
|
457 jsd_AddFullSourceText(JSDContext* jsdc, |
|
458 const char* text, /* *not* zero terminated */ |
|
459 size_t length, |
|
460 const char* url) |
|
461 { |
|
462 JSDSourceText* jsdsrc; |
|
463 |
|
464 JSD_LOCK_SOURCE_TEXT(jsdc); |
|
465 |
|
466 jsdsrc = jsd_NewSourceText(jsdc, url); |
|
467 if( jsdsrc ) |
|
468 jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc, |
|
469 text, length, JSD_SOURCE_PARTIAL ); |
|
470 if( jsdsrc ) |
|
471 jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc, |
|
472 nullptr, 0, JSD_SOURCE_COMPLETED ); |
|
473 |
|
474 JSD_UNLOCK_SOURCE_TEXT(jsdc); |
|
475 |
|
476 return jsdsrc ? true : false; |
|
477 } |
|
478 |
|
479 /***************************************************************************/ |
|
480 |
|
481 void |
|
482 jsd_StartingEvalUsingFilename(JSDContext* jsdc, const char* url) |
|
483 { |
|
484 JSDSourceText* jsdsrc; |
|
485 |
|
486 /* NOTE: We leave it locked! */ |
|
487 JSD_LOCK_SOURCE_TEXT(jsdc); |
|
488 |
|
489 jsdsrc = jsd_FindSourceForURL(jsdc, url); |
|
490 if(jsdsrc) |
|
491 { |
|
492 #if 0 |
|
493 #ifndef JSD_LOWLEVEL_SOURCE |
|
494 MOZ_ASSERT(! jsdsrc->doingEval); |
|
495 #endif |
|
496 #endif |
|
497 jsdsrc->doingEval = true; |
|
498 } |
|
499 } |
|
500 |
|
501 void |
|
502 jsd_FinishedEvalUsingFilename(JSDContext* jsdc, const char* url) |
|
503 { |
|
504 JSDSourceText* jsdsrc; |
|
505 |
|
506 /* NOTE: We ASSUME it is locked! */ |
|
507 |
|
508 jsdsrc = jsd_FindSourceForURL(jsdc, url); |
|
509 if(jsdsrc) |
|
510 { |
|
511 #if 0 |
|
512 #ifndef JSD_LOWLEVEL_SOURCE |
|
513 /* |
|
514 * when using this low level source addition, this jsdsrc might |
|
515 * not have existed before the eval, but does exist now (without |
|
516 * this flag set!) |
|
517 */ |
|
518 MOZ_ASSERT(jsdsrc->doingEval); |
|
519 #endif |
|
520 #endif |
|
521 jsdsrc->doingEval = false; |
|
522 } |
|
523 |
|
524 JSD_UNLOCK_SOURCE_TEXT(jsdc); |
|
525 } |