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