michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * JavaScript Debugging support - Source Text functions michael@0: */ michael@0: michael@0: #include michael@0: #include "jsd.h" michael@0: #include "jsprf.h" michael@0: michael@0: #ifdef DEBUG michael@0: void JSD_ASSERT_VALID_SOURCE_TEXT(JSDSourceText* jsdsrc) michael@0: { michael@0: MOZ_ASSERT(jsdsrc); michael@0: MOZ_ASSERT(jsdsrc->url); michael@0: } michael@0: #endif michael@0: michael@0: /***************************************************************************/ michael@0: /* XXX add notification */ michael@0: michael@0: static void michael@0: _clearText(JSDContext* jsdc, JSDSourceText* jsdsrc) michael@0: { michael@0: if( jsdsrc->text ) michael@0: free(jsdsrc->text); michael@0: jsdsrc->text = nullptr; michael@0: jsdsrc->textLength = 0; michael@0: jsdsrc->textSpace = 0; michael@0: jsdsrc->status = JSD_SOURCE_CLEARED; michael@0: jsdsrc->dirty = true; michael@0: jsdsrc->alterCount = jsdc->sourceAlterCount++ ; michael@0: jsdsrc->doingEval = false; michael@0: } michael@0: michael@0: static bool michael@0: _appendText(JSDContext* jsdc, JSDSourceText* jsdsrc, michael@0: const char* text, size_t length) michael@0: { michael@0: #define MEMBUF_GROW 1000 michael@0: michael@0: unsigned neededSize = jsdsrc->textLength + length; michael@0: michael@0: if( neededSize > jsdsrc->textSpace ) michael@0: { michael@0: char* newBuf; michael@0: unsigned iNewSize; michael@0: michael@0: /* if this is the first alloc, the req might be all that's needed*/ michael@0: if( ! jsdsrc->textSpace ) michael@0: iNewSize = length; michael@0: else michael@0: iNewSize = (neededSize * 5 / 4) + MEMBUF_GROW; michael@0: michael@0: newBuf = (char*) realloc(jsdsrc->text, iNewSize); michael@0: if( ! newBuf ) michael@0: { michael@0: /* try again with the minimal size really asked for */ michael@0: iNewSize = neededSize; michael@0: newBuf = (char*) realloc(jsdsrc->text, iNewSize); michael@0: if( ! newBuf ) michael@0: { michael@0: /* out of memory */ michael@0: _clearText( jsdc, jsdsrc ); michael@0: jsdsrc->status = JSD_SOURCE_FAILED; michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: jsdsrc->text = newBuf; michael@0: jsdsrc->textSpace = iNewSize; michael@0: } michael@0: michael@0: memcpy(jsdsrc->text + jsdsrc->textLength, text, length); michael@0: jsdsrc->textLength += length; michael@0: return true; michael@0: } michael@0: michael@0: static JSDSourceText* michael@0: _newSource(JSDContext* jsdc, char* url) michael@0: { michael@0: JSDSourceText* jsdsrc = (JSDSourceText*)calloc(1,sizeof(JSDSourceText)); michael@0: if( ! jsdsrc ) michael@0: return nullptr; michael@0: michael@0: jsdsrc->url = url; michael@0: jsdsrc->status = JSD_SOURCE_INITED; michael@0: jsdsrc->dirty = true; michael@0: jsdsrc->alterCount = jsdc->sourceAlterCount++ ; michael@0: michael@0: return jsdsrc; michael@0: } michael@0: michael@0: static void michael@0: _destroySource(JSDContext* jsdc, JSDSourceText* jsdsrc) michael@0: { michael@0: MOZ_ASSERT(nullptr == jsdsrc->text); /* must _clearText() first */ michael@0: free(jsdsrc->url); michael@0: free(jsdsrc); michael@0: } michael@0: michael@0: static void michael@0: _removeSource(JSDContext* jsdc, JSDSourceText* jsdsrc) michael@0: { michael@0: JS_REMOVE_LINK(&jsdsrc->links); michael@0: _clearText(jsdc, jsdsrc); michael@0: _destroySource(jsdc, jsdsrc); michael@0: } michael@0: michael@0: static JSDSourceText* michael@0: _addSource(JSDContext* jsdc, char* url) michael@0: { michael@0: JSDSourceText* jsdsrc = _newSource(jsdc, url); michael@0: if( ! jsdsrc ) michael@0: return nullptr; michael@0: JS_INSERT_LINK(&jsdsrc->links, &jsdc->sources); michael@0: return jsdsrc; michael@0: } michael@0: michael@0: static void michael@0: _moveSourceToRemovedList(JSDContext* jsdc, JSDSourceText* jsdsrc) michael@0: { michael@0: _clearText(jsdc, jsdsrc); michael@0: JS_REMOVE_LINK(&jsdsrc->links); michael@0: JS_INSERT_LINK(&jsdsrc->links, &jsdc->removedSources); michael@0: } michael@0: michael@0: static void michael@0: _removeSourceFromRemovedList( JSDContext* jsdc, JSDSourceText* jsdsrc ) michael@0: { michael@0: JS_REMOVE_LINK(&jsdsrc->links); michael@0: _destroySource( jsdc, jsdsrc ); michael@0: } michael@0: michael@0: static bool michael@0: _isSourceInSourceList(JSDContext* jsdc, JSDSourceText* jsdsrcToFind) michael@0: { michael@0: JSDSourceText *jsdsrc; michael@0: michael@0: for( jsdsrc = (JSDSourceText*)jsdc->sources.next; michael@0: jsdsrc != (JSDSourceText*)&jsdc->sources; michael@0: jsdsrc = (JSDSourceText*)jsdsrc->links.next ) michael@0: { michael@0: if( jsdsrc == jsdsrcToFind ) michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: /* compare strings in a case insensitive manner with a length limit michael@0: */ michael@0: michael@0: static int michael@0: strncasecomp (const char* one, const char * two, int n) michael@0: { michael@0: const char *pA; michael@0: const char *pB; michael@0: michael@0: for(pA=one, pB=two;; pA++, pB++) michael@0: { michael@0: int tmp; michael@0: if (pA == one+n) michael@0: return 0; michael@0: if (!(*pA && *pB)) michael@0: return *pA - *pB; michael@0: tmp = tolower(*pA) - tolower(*pB); michael@0: if (tmp) michael@0: return tmp; michael@0: } michael@0: } michael@0: michael@0: static const char file_url_prefix[] = "file:"; michael@0: #define FILE_URL_PREFIX_LEN (sizeof file_url_prefix - 1) michael@0: michael@0: char* michael@0: jsd_BuildNormalizedURL( const char* url_string ) michael@0: { michael@0: char *new_url_string; michael@0: michael@0: if( ! url_string ) michael@0: return nullptr; michael@0: michael@0: if (!strncasecomp(url_string, file_url_prefix, FILE_URL_PREFIX_LEN) && michael@0: url_string[FILE_URL_PREFIX_LEN + 0] == '/' && michael@0: url_string[FILE_URL_PREFIX_LEN + 1] == '/') { michael@0: new_url_string = JS_smprintf("%s%s", michael@0: file_url_prefix, michael@0: url_string + FILE_URL_PREFIX_LEN + 2); michael@0: } else { michael@0: new_url_string = strdup(url_string); michael@0: } michael@0: return new_url_string; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: void michael@0: jsd_DestroyAllSources( JSDContext* jsdc ) michael@0: { michael@0: JSDSourceText *jsdsrc; michael@0: JSDSourceText *next; michael@0: michael@0: for( jsdsrc = (JSDSourceText*)jsdc->sources.next; michael@0: jsdsrc != (JSDSourceText*)&jsdc->sources; michael@0: jsdsrc = next ) michael@0: { michael@0: next = (JSDSourceText*)jsdsrc->links.next; michael@0: _removeSource( jsdc, jsdsrc ); michael@0: } michael@0: michael@0: for( jsdsrc = (JSDSourceText*)jsdc->removedSources.next; michael@0: jsdsrc != (JSDSourceText*)&jsdc->removedSources; michael@0: jsdsrc = next ) michael@0: { michael@0: next = (JSDSourceText*)jsdsrc->links.next; michael@0: _removeSourceFromRemovedList( jsdc, jsdsrc ); michael@0: } michael@0: michael@0: } michael@0: michael@0: JSDSourceText* michael@0: jsd_IterateSources(JSDContext* jsdc, JSDSourceText **iterp) michael@0: { michael@0: JSDSourceText *jsdsrc = *iterp; michael@0: michael@0: if( !jsdsrc ) michael@0: jsdsrc = (JSDSourceText *)jsdc->sources.next; michael@0: if( jsdsrc == (JSDSourceText *)&jsdc->sources ) michael@0: return nullptr; michael@0: *iterp = (JSDSourceText *)jsdsrc->links.next; michael@0: return jsdsrc; michael@0: } michael@0: michael@0: JSDSourceText* michael@0: jsd_FindSourceForURL(JSDContext* jsdc, const char* url) michael@0: { michael@0: JSDSourceText *jsdsrc; michael@0: michael@0: for( jsdsrc = (JSDSourceText *)jsdc->sources.next; michael@0: jsdsrc != (JSDSourceText *)&jsdc->sources; michael@0: jsdsrc = (JSDSourceText *)jsdsrc->links.next ) michael@0: { michael@0: if( 0 == strcmp(jsdsrc->url, url) ) michael@0: return jsdsrc; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: const char* michael@0: jsd_GetSourceURL(JSDContext* jsdc, JSDSourceText* jsdsrc) michael@0: { michael@0: return jsdsrc->url; michael@0: } michael@0: michael@0: bool michael@0: jsd_GetSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc, michael@0: const char** ppBuf, int* pLen ) michael@0: { michael@0: *ppBuf = jsdsrc->text; michael@0: *pLen = jsdsrc->textLength; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: jsd_ClearSourceText(JSDContext* jsdc, JSDSourceText* jsdsrc) michael@0: { michael@0: if( JSD_SOURCE_INITED != jsdsrc->status && michael@0: JSD_SOURCE_PARTIAL != jsdsrc->status ) michael@0: { michael@0: _clearText(jsdc, jsdsrc); michael@0: } michael@0: } michael@0: michael@0: JSDSourceStatus michael@0: jsd_GetSourceStatus(JSDContext* jsdc, JSDSourceText* jsdsrc) michael@0: { michael@0: return jsdsrc->status; michael@0: } michael@0: michael@0: bool michael@0: jsd_IsSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc) michael@0: { michael@0: return jsdsrc->dirty; michael@0: } michael@0: michael@0: void michael@0: jsd_SetSourceDirty(JSDContext* jsdc, JSDSourceText* jsdsrc, bool dirty) michael@0: { michael@0: jsdsrc->dirty = dirty; michael@0: } michael@0: michael@0: unsigned michael@0: jsd_GetSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc) michael@0: { michael@0: return jsdsrc->alterCount; michael@0: } michael@0: michael@0: unsigned michael@0: jsd_IncrementSourceAlterCount(JSDContext* jsdc, JSDSourceText* jsdsrc) michael@0: { michael@0: return jsdsrc->alterCount = jsdc->sourceAlterCount++; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: #if defined(DEBUG) && 0 michael@0: void DEBUG_ITERATE_SOURCES( JSDContext* jsdc ) michael@0: { michael@0: JSDSourceText* iterp = nullptr; michael@0: JSDSourceText* jsdsrc = nullptr; michael@0: int dummy; michael@0: michael@0: while( nullptr != (jsdsrc = jsd_IterateSources(jsdc, &iterp)) ) michael@0: { michael@0: const char* url; michael@0: const char* text; michael@0: int len; michael@0: bool dirty; michael@0: JSDStreamStatus status; michael@0: bool gotSrc; michael@0: michael@0: url = JSD_GetSourceURL(jsdc, jsdsrc); michael@0: dirty = JSD_IsSourceDirty(jsdc, jsdsrc); michael@0: status = JSD_GetSourceStatus(jsdc, jsdsrc); michael@0: gotSrc = JSD_GetSourceText(jsdc, jsdsrc, &text, &len ); michael@0: michael@0: dummy = 0; /* gives us a line to set breakpoint... */ michael@0: } michael@0: } michael@0: #else michael@0: #define DEBUG_ITERATE_SOURCES(x) ((void)x) michael@0: #endif michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: JSDSourceText* michael@0: jsd_NewSourceText(JSDContext* jsdc, const char* url) michael@0: { michael@0: JSDSourceText* jsdsrc; michael@0: char* new_url_string; michael@0: michael@0: JSD_LOCK_SOURCE_TEXT(jsdc); michael@0: michael@0: new_url_string = jsd_BuildNormalizedURL(url); michael@0: michael@0: if( ! new_url_string ) michael@0: return nullptr; michael@0: michael@0: jsdsrc = jsd_FindSourceForURL(jsdc, new_url_string); michael@0: michael@0: if( jsdsrc ) michael@0: { michael@0: if( jsdsrc->doingEval ) michael@0: { michael@0: free(new_url_string); michael@0: JSD_UNLOCK_SOURCE_TEXT(jsdc); michael@0: return nullptr; michael@0: } michael@0: else michael@0: _moveSourceToRemovedList(jsdc, jsdsrc); michael@0: } michael@0: michael@0: jsdsrc = _addSource( jsdc, new_url_string ); michael@0: michael@0: JSD_UNLOCK_SOURCE_TEXT(jsdc); michael@0: michael@0: return jsdsrc; michael@0: } michael@0: michael@0: JSDSourceText* michael@0: jsd_AppendSourceText(JSDContext* jsdc, michael@0: JSDSourceText* jsdsrc, michael@0: const char* text, /* *not* zero terminated */ michael@0: size_t length, michael@0: JSDSourceStatus status) michael@0: { michael@0: JSD_LOCK_SOURCE_TEXT(jsdc); michael@0: michael@0: if( jsdsrc->doingEval ) michael@0: { michael@0: JSD_UNLOCK_SOURCE_TEXT(jsdc); michael@0: return nullptr; michael@0: } michael@0: michael@0: if( ! _isSourceInSourceList( jsdc, jsdsrc ) ) michael@0: { michael@0: _removeSourceFromRemovedList( jsdc, jsdsrc ); michael@0: JSD_UNLOCK_SOURCE_TEXT(jsdc); michael@0: return nullptr; michael@0: } michael@0: michael@0: if( text && length && ! _appendText( jsdc, jsdsrc, text, length ) ) michael@0: { michael@0: jsdsrc->dirty = true; michael@0: jsdsrc->alterCount = jsdc->sourceAlterCount++ ; michael@0: jsdsrc->status = JSD_SOURCE_FAILED; michael@0: _moveSourceToRemovedList(jsdc, jsdsrc); michael@0: JSD_UNLOCK_SOURCE_TEXT(jsdc); michael@0: return nullptr; michael@0: } michael@0: michael@0: jsdsrc->dirty = true; michael@0: jsdsrc->alterCount = jsdc->sourceAlterCount++ ; michael@0: jsdsrc->status = status; michael@0: DEBUG_ITERATE_SOURCES(jsdc); michael@0: JSD_UNLOCK_SOURCE_TEXT(jsdc); michael@0: return jsdsrc; michael@0: } michael@0: michael@0: JSDSourceText* michael@0: jsd_AppendUCSourceText(JSDContext* jsdc, michael@0: JSDSourceText* jsdsrc, michael@0: const jschar* text, /* *not* zero terminated */ michael@0: size_t length, michael@0: JSDSourceStatus status) michael@0: { michael@0: #define UNICODE_TRUNCATE_BUF_SIZE 1024 michael@0: static char* buf = nullptr; michael@0: int remaining = length; michael@0: michael@0: if(!text || !length) michael@0: return jsd_AppendSourceText(jsdc, jsdsrc, nullptr, 0, status); michael@0: michael@0: JSD_LOCK_SOURCE_TEXT(jsdc); michael@0: if(!buf) michael@0: { michael@0: buf = js_pod_malloc(UNICODE_TRUNCATE_BUF_SIZE); michael@0: if(!buf) michael@0: { michael@0: JSD_UNLOCK_SOURCE_TEXT(jsdc); michael@0: return nullptr; michael@0: } michael@0: } michael@0: while(remaining && jsdsrc) { michael@0: int bytes = (remaining < UNICODE_TRUNCATE_BUF_SIZE) ? remaining : UNICODE_TRUNCATE_BUF_SIZE; michael@0: int i; michael@0: for(i = 0; i < bytes; i++) michael@0: buf[i] = (const char) *(text++); michael@0: jsdsrc = jsd_AppendSourceText(jsdc,jsdsrc, michael@0: buf, bytes, michael@0: JSD_SOURCE_PARTIAL); michael@0: remaining -= bytes; michael@0: } michael@0: if(jsdsrc && status != JSD_SOURCE_PARTIAL) michael@0: jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc, nullptr, 0, status); michael@0: michael@0: JSD_UNLOCK_SOURCE_TEXT(jsdc); michael@0: return jsdsrc; michael@0: } michael@0: michael@0: /* convienence function for adding complete source of url in one call */ michael@0: bool michael@0: jsd_AddFullSourceText(JSDContext* jsdc, michael@0: const char* text, /* *not* zero terminated */ michael@0: size_t length, michael@0: const char* url) michael@0: { michael@0: JSDSourceText* jsdsrc; michael@0: michael@0: JSD_LOCK_SOURCE_TEXT(jsdc); michael@0: michael@0: jsdsrc = jsd_NewSourceText(jsdc, url); michael@0: if( jsdsrc ) michael@0: jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc, michael@0: text, length, JSD_SOURCE_PARTIAL ); michael@0: if( jsdsrc ) michael@0: jsdsrc = jsd_AppendSourceText(jsdc, jsdsrc, michael@0: nullptr, 0, JSD_SOURCE_COMPLETED ); michael@0: michael@0: JSD_UNLOCK_SOURCE_TEXT(jsdc); michael@0: michael@0: return jsdsrc ? true : false; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: void michael@0: jsd_StartingEvalUsingFilename(JSDContext* jsdc, const char* url) michael@0: { michael@0: JSDSourceText* jsdsrc; michael@0: michael@0: /* NOTE: We leave it locked! */ michael@0: JSD_LOCK_SOURCE_TEXT(jsdc); michael@0: michael@0: jsdsrc = jsd_FindSourceForURL(jsdc, url); michael@0: if(jsdsrc) michael@0: { michael@0: #if 0 michael@0: #ifndef JSD_LOWLEVEL_SOURCE michael@0: MOZ_ASSERT(! jsdsrc->doingEval); michael@0: #endif michael@0: #endif michael@0: jsdsrc->doingEval = true; michael@0: } michael@0: } michael@0: michael@0: void michael@0: jsd_FinishedEvalUsingFilename(JSDContext* jsdc, const char* url) michael@0: { michael@0: JSDSourceText* jsdsrc; michael@0: michael@0: /* NOTE: We ASSUME it is locked! */ michael@0: michael@0: jsdsrc = jsd_FindSourceForURL(jsdc, url); michael@0: if(jsdsrc) michael@0: { michael@0: #if 0 michael@0: #ifndef JSD_LOWLEVEL_SOURCE michael@0: /* michael@0: * when using this low level source addition, this jsdsrc might michael@0: * not have existed before the eval, but does exist now (without michael@0: * this flag set!) michael@0: */ michael@0: MOZ_ASSERT(jsdsrc->doingEval); michael@0: #endif michael@0: #endif michael@0: jsdsrc->doingEval = false; michael@0: } michael@0: michael@0: JSD_UNLOCK_SOURCE_TEXT(jsdc); michael@0: }