/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsapi.cpp
C++ | 2314 lines | 1863 code | 266 blank | 185 comment | 297 complexity | 1b4b4664355e81ed970caba93e4dbf1d MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
- /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sw=4 et tw=78:
- *
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is Mozilla Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
- /*
- * JavaScript API.
- */
- #include "jsstddef.h"
- #include <ctype.h>
- #include <stdarg.h>
- #include <stdlib.h>
- #include <string.h>
- #include "jstypes.h"
- #include "jsarena.h" /* Added by JSIFY */
- #include "jsutil.h" /* Added by JSIFY */
- #include "jsclist.h"
- #include "jsdhash.h"
- #include "jsprf.h"
- #include "jsapi.h"
- #include "jsarray.h"
- #include "jsatom.h"
- #include "jsbool.h"
- #include "jsbuiltins.h"
- #include "jscntxt.h"
- #include "jsversion.h"
- #include "jsdate.h"
- #include "jsdtoa.h"
- #include "jsemit.h"
- #include "jsexn.h"
- #include "jsfun.h"
- #include "jsgc.h"
- #include "jsinterp.h"
- #include "jsiter.h"
- #include "jslock.h"
- #include "jsmath.h"
- #include "jsnum.h"
- #include "json.h"
- #include "jsobj.h"
- #include "jsopcode.h"
- #include "jsparse.h"
- #include "jsregexp.h"
- #include "jsscan.h"
- #include "jsscope.h"
- #include "jsscript.h"
- #include "jsstr.h"
- #include "prmjtime.h"
- #include "jsstaticcheck.h"
- #if !defined JS_THREADSAFE && defined JS_TRACER
- #include "jstracer.h"
- #endif
- #if JS_HAS_FILE_OBJECT
- #include "jsfile.h"
- #endif
- #if JS_HAS_XML_SUPPORT
- #include "jsxml.h"
- #endif
- #ifdef HAVE_VA_LIST_AS_ARRAY
- #define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap))
- #else
- #define JS_ADDRESSOF_VA_LIST(ap) (&(ap))
- #endif
- #if defined(JS_THREADSAFE)
- #define CHECK_REQUEST(cx) \
- JS_ASSERT((cx)->requestDepth || (cx)->thread == (cx)->runtime->gcThread)
- #else
- #define CHECK_REQUEST(cx) ((void)0)
- #endif
- JS_PUBLIC_API(int64)
- JS_Now()
- {
- return PRMJ_Now();
- }
- JS_PUBLIC_API(jsval)
- JS_GetNaNValue(JSContext *cx)
- {
- return DOUBLE_TO_JSVAL(cx->runtime->jsNaN);
- }
- JS_PUBLIC_API(jsval)
- JS_GetNegativeInfinityValue(JSContext *cx)
- {
- return DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity);
- }
- JS_PUBLIC_API(jsval)
- JS_GetPositiveInfinityValue(JSContext *cx)
- {
- return DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity);
- }
- JS_PUBLIC_API(jsval)
- JS_GetEmptyStringValue(JSContext *cx)
- {
- return STRING_TO_JSVAL(cx->runtime->emptyString);
- }
- static JSBool
- TryArgumentFormatter(JSContext *cx, const char **formatp, JSBool fromJS,
- jsval **vpp, va_list *app)
- {
- const char *format;
- JSArgumentFormatMap *map;
- format = *formatp;
- for (map = cx->argumentFormatMap; map; map = map->next) {
- if (!strncmp(format, map->format, map->length)) {
- *formatp = format + map->length;
- return map->formatter(cx, format, fromJS, vpp, app);
- }
- }
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CHAR, format);
- return JS_FALSE;
- }
- JS_PUBLIC_API(JSBool)
- JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format,
- ...)
- {
- va_list ap;
- JSBool ok;
- va_start(ap, format);
- ok = JS_ConvertArgumentsVA(cx, argc, argv, format, ap);
- va_end(ap);
- return ok;
- }
- JS_PUBLIC_API(JSBool)
- JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv,
- const char *format, va_list ap)
- {
- jsval *sp;
- JSBool required;
- char c;
- JSFunction *fun;
- jsdouble d;
- JSString *str;
- JSObject *obj;
- CHECK_REQUEST(cx);
- sp = argv;
- required = JS_TRUE;
- while ((c = *format++) != '\0') {
- if (isspace(c))
- continue;
- if (c == '/') {
- required = JS_FALSE;
- continue;
- }
- if (sp == argv + argc) {
- if (required) {
- fun = js_ValueToFunction(cx, &argv[-2], 0);
- if (fun) {
- char numBuf[12];
- JS_snprintf(numBuf, sizeof numBuf, "%u", argc);
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
- JSMSG_MORE_ARGS_NEEDED,
- JS_GetFunctionName(fun), numBuf,
- (argc == 1) ? "" : "s");
- }
- return JS_FALSE;
- }
- break;
- }
- switch (c) {
- case 'b':
- *va_arg(ap, JSBool *) = js_ValueToBoolean(*sp);
- break;
- case 'c':
- if (!JS_ValueToUint16(cx, *sp, va_arg(ap, uint16 *)))
- return JS_FALSE;
- break;
- case 'i':
- if (!JS_ValueToECMAInt32(cx, *sp, va_arg(ap, int32 *)))
- return JS_FALSE;
- break;
- case 'u':
- if (!JS_ValueToECMAUint32(cx, *sp, va_arg(ap, uint32 *)))
- return JS_FALSE;
- break;
- case 'j':
- if (!JS_ValueToInt32(cx, *sp, va_arg(ap, int32 *)))
- return JS_FALSE;
- break;
- case 'd':
- if (!JS_ValueToNumber(cx, *sp, va_arg(ap, jsdouble *)))
- return JS_FALSE;
- break;
- case 'I':
- if (!JS_ValueToNumber(cx, *sp, &d))
- return JS_FALSE;
- *va_arg(ap, jsdouble *) = js_DoubleToInteger(d);
- break;
- case 's':
- case 'S':
- case 'W':
- str = js_ValueToString(cx, *sp);
- if (!str)
- return JS_FALSE;
- *sp = STRING_TO_JSVAL(str);
- if (c == 's') {
- const char *bytes = js_GetStringBytes(cx, str);
- if (!bytes)
- return JS_FALSE;
- *va_arg(ap, const char **) = bytes;
- } else if (c == 'W') {
- const jschar *chars = js_GetStringChars(cx, str);
- if (!chars)
- return JS_FALSE;
- *va_arg(ap, const jschar **) = chars;
- } else {
- *va_arg(ap, JSString **) = str;
- }
- break;
- case 'o':
- if (!js_ValueToObject(cx, *sp, &obj))
- return JS_FALSE;
- *sp = OBJECT_TO_JSVAL(obj);
- *va_arg(ap, JSObject **) = obj;
- break;
- case 'f':
- obj = js_ValueToFunctionObject(cx, sp, 0);
- if (!obj)
- return JS_FALSE;
- *sp = OBJECT_TO_JSVAL(obj);
- *va_arg(ap, JSFunction **) = (JSFunction *) JS_GetPrivate(cx, obj);
- break;
- case 'v':
- *va_arg(ap, jsval *) = *sp;
- break;
- case '*':
- break;
- default:
- format--;
- if (!TryArgumentFormatter(cx, &format, JS_TRUE, &sp,
- JS_ADDRESSOF_VA_LIST(ap))) {
- return JS_FALSE;
- }
- /* NB: the formatter already updated sp, so we continue here. */
- continue;
- }
- sp++;
- }
- return JS_TRUE;
- }
- JS_PUBLIC_API(jsval *)
- JS_PushArguments(JSContext *cx, void **markp, const char *format, ...)
- {
- va_list ap;
- jsval *argv;
- va_start(ap, format);
- argv = JS_PushArgumentsVA(cx, markp, format, ap);
- va_end(ap);
- return argv;
- }
- JS_PUBLIC_API(jsval *)
- JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap)
- {
- uintN argc;
- jsval *argv, *sp;
- char c;
- const char *cp;
- JSString *str;
- JSFunction *fun;
- JSStackHeader *sh;
- CHECK_REQUEST(cx);
- *markp = NULL;
- argc = 0;
- for (cp = format; (c = *cp) != '\0'; cp++) {
- /*
- * Count non-space non-star characters as individual jsval arguments.
- * This may over-allocate stack, but we'll fix below.
- */
- if (isspace(c) || c == '*')
- continue;
- argc++;
- }
- sp = js_AllocStack(cx, argc, markp);
- if (!sp)
- return NULL;
- argv = sp;
- while ((c = *format++) != '\0') {
- if (isspace(c) || c == '*')
- continue;
- switch (c) {
- case 'b':
- *sp = BOOLEAN_TO_JSVAL((JSBool) va_arg(ap, int));
- break;
- case 'c':
- *sp = INT_TO_JSVAL((uint16) va_arg(ap, unsigned int));
- break;
- case 'i':
- case 'j':
- /*
- * Use JS_New{Double,Number}Value here and in the next two cases,
- * not js_New{Double,Number}InRootedValue, as sp may point to an
- * unrooted location.
- */
- if (!JS_NewNumberValue(cx, (jsdouble) va_arg(ap, int32), sp))
- goto bad;
- break;
- case 'u':
- if (!JS_NewNumberValue(cx, (jsdouble) va_arg(ap, uint32), sp))
- goto bad;
- break;
- case 'd':
- case 'I':
- if (!JS_NewDoubleValue(cx, va_arg(ap, jsdouble), sp))
- goto bad;
- break;
- case 's':
- str = JS_NewStringCopyZ(cx, va_arg(ap, char *));
- if (!str)
- goto bad;
- *sp = STRING_TO_JSVAL(str);
- break;
- case 'W':
- str = JS_NewUCStringCopyZ(cx, va_arg(ap, jschar *));
- if (!str)
- goto bad;
- *sp = STRING_TO_JSVAL(str);
- break;
- case 'S':
- str = va_arg(ap, JSString *);
- *sp = STRING_TO_JSVAL(str);
- break;
- case 'o':
- *sp = OBJECT_TO_JSVAL(va_arg(ap, JSObject *));
- break;
- case 'f':
- fun = va_arg(ap, JSFunction *);
- *sp = fun ? OBJECT_TO_JSVAL(FUN_OBJECT(fun)) : JSVAL_NULL;
- break;
- case 'v':
- *sp = va_arg(ap, jsval);
- break;
- default:
- format--;
- if (!TryArgumentFormatter(cx, &format, JS_FALSE, &sp,
- JS_ADDRESSOF_VA_LIST(ap))) {
- goto bad;
- }
- /* NB: the formatter already updated sp, so we continue here. */
- continue;
- }
- sp++;
- }
- /*
- * We may have overallocated stack due to a multi-character format code
- * handled by a JSArgumentFormatter. Give back that stack space!
- */
- JS_ASSERT(sp <= argv + argc);
- if (sp < argv + argc) {
- /* Return slots not pushed to the current stack arena. */
- cx->stackPool.current->avail = (jsuword)sp;
- /* Reduce the count of slots the GC will scan in this stack segment. */
- sh = cx->stackHeaders;
- JS_ASSERT(JS_STACK_SEGMENT(sh) + sh->nslots == argv + argc);
- sh->nslots -= argc - (sp - argv);
- }
- return argv;
- bad:
- js_FreeStack(cx, *markp);
- return NULL;
- }
- JS_PUBLIC_API(void)
- JS_PopArguments(JSContext *cx, void *mark)
- {
- CHECK_REQUEST(cx);
- js_FreeStack(cx, mark);
- }
- JS_PUBLIC_API(JSBool)
- JS_AddArgumentFormatter(JSContext *cx, const char *format,
- JSArgumentFormatter formatter)
- {
- size_t length;
- JSArgumentFormatMap **mpp, *map;
- length = strlen(format);
- mpp = &cx->argumentFormatMap;
- while ((map = *mpp) != NULL) {
- /* Insert before any shorter string to match before prefixes. */
- if (map->length < length)
- break;
- if (map->length == length && !strcmp(map->format, format))
- goto out;
- mpp = &map->next;
- }
- map = (JSArgumentFormatMap *) JS_malloc(cx, sizeof *map);
- if (!map)
- return JS_FALSE;
- map->format = format;
- map->length = length;
- map->next = *mpp;
- *mpp = map;
- out:
- map->formatter = formatter;
- return JS_TRUE;
- }
- JS_PUBLIC_API(void)
- JS_RemoveArgumentFormatter(JSContext *cx, const char *format)
- {
- size_t length;
- JSArgumentFormatMap **mpp, *map;
- length = strlen(format);
- mpp = &cx->argumentFormatMap;
- while ((map = *mpp) != NULL) {
- if (map->length == length && !strcmp(map->format, format)) {
- *mpp = map->next;
- JS_free(cx, map);
- return;
- }
- mpp = &map->next;
- }
- }
- JS_PUBLIC_API(JSBool)
- JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp)
- {
- JSBool ok;
- JSObject *obj;
- JSString *str;
- jsdouble d, *dp;
- CHECK_REQUEST(cx);
- switch (type) {
- case JSTYPE_VOID:
- *vp = JSVAL_VOID;
- ok = JS_TRUE;
- break;
- case JSTYPE_OBJECT:
- ok = js_ValueToObject(cx, v, &obj);
- if (ok)
- *vp = OBJECT_TO_JSVAL(obj);
- break;
- case JSTYPE_FUNCTION:
- *vp = v;
- obj = js_ValueToFunctionObject(cx, vp, JSV2F_SEARCH_STACK);
- ok = (obj != NULL);
- break;
- case JSTYPE_STRING:
- str = js_ValueToString(cx, v);
- ok = (str != NULL);
- if (ok)
- *vp = STRING_TO_JSVAL(str);
- break;
- case JSTYPE_NUMBER:
- ok = JS_ValueToNumber(cx, v, &d);
- if (ok) {
- dp = js_NewWeaklyRootedDouble(cx, d);
- ok = (dp != NULL);
- if (ok)
- *vp = DOUBLE_TO_JSVAL(dp);
- }
- break;
- case JSTYPE_BOOLEAN:
- *vp = BOOLEAN_TO_JSVAL(js_ValueToBoolean(v));
- return JS_TRUE;
- default: {
- char numBuf[12];
- JS_snprintf(numBuf, sizeof numBuf, "%d", (int)type);
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_TYPE,
- numBuf);
- ok = JS_FALSE;
- break;
- }
- }
- return ok;
- }
- JS_PUBLIC_API(JSBool)
- JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
- {
- CHECK_REQUEST(cx);
- return js_ValueToObject(cx, v, objp);
- }
- JS_PUBLIC_API(JSFunction *)
- JS_ValueToFunction(JSContext *cx, jsval v)
- {
- CHECK_REQUEST(cx);
- return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK);
- }
- JS_PUBLIC_API(JSFunction *)
- JS_ValueToConstructor(JSContext *cx, jsval v)
- {
- CHECK_REQUEST(cx);
- return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK);
- }
- JS_PUBLIC_API(JSString *)
- JS_ValueToString(JSContext *cx, jsval v)
- {
- CHECK_REQUEST(cx);
- return js_ValueToString(cx, v);
- }
- JS_PUBLIC_API(JSString *)
- JS_ValueToSource(JSContext *cx, jsval v)
- {
- CHECK_REQUEST(cx);
- return js_ValueToSource(cx, v);
- }
- JS_PUBLIC_API(JSBool)
- JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)
- {
- JSTempValueRooter tvr;
- CHECK_REQUEST(cx);
- JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr);
- *dp = js_ValueToNumber(cx, &tvr.u.value);
- JS_POP_TEMP_ROOT(cx, &tvr);
- return !JSVAL_IS_NULL(tvr.u.value);
- }
- JS_PUBLIC_API(JSBool)
- JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip)
- {
- JSTempValueRooter tvr;
- CHECK_REQUEST(cx);
- JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr);
- *ip = js_ValueToECMAInt32(cx, &tvr.u.value);
- JS_POP_TEMP_ROOT(cx, &tvr);
- return !JSVAL_IS_NULL(tvr.u.value);
- }
- JS_PUBLIC_API(JSBool)
- JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip)
- {
- JSTempValueRooter tvr;
- CHECK_REQUEST(cx);
- JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr);
- *ip = js_ValueToECMAUint32(cx, &tvr.u.value);
- JS_POP_TEMP_ROOT(cx, &tvr);
- return !JSVAL_IS_NULL(tvr.u.value);
- }
- JS_PUBLIC_API(JSBool)
- JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip)
- {
- JSTempValueRooter tvr;
- CHECK_REQUEST(cx);
- JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr);
- *ip = js_ValueToInt32(cx, &tvr.u.value);
- JS_POP_TEMP_ROOT(cx, &tvr);
- return !JSVAL_IS_NULL(tvr.u.value);
- }
- JS_PUBLIC_API(JSBool)
- JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip)
- {
- JSTempValueRooter tvr;
- CHECK_REQUEST(cx);
- JS_PUSH_SINGLE_TEMP_ROOT(cx, v, &tvr);
- *ip = js_ValueToUint16(cx, &tvr.u.value);
- JS_POP_TEMP_ROOT(cx, &tvr);
- return !JSVAL_IS_NULL(tvr.u.value);
- }
- JS_PUBLIC_API(JSBool)
- JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp)
- {
- CHECK_REQUEST(cx);
- *bp = js_ValueToBoolean(v);
- return JS_TRUE;
- }
- JS_PUBLIC_API(JSType)
- JS_TypeOfValue(JSContext *cx, jsval v)
- {
- JSType type;
- JSObject *obj;
- JSObjectOps *ops;
- JSClass *clasp;
- CHECK_REQUEST(cx);
- if (JSVAL_IS_OBJECT(v)) {
- type = JSTYPE_OBJECT; /* XXXbe JSTYPE_NULL for JS2 */
- obj = JSVAL_TO_OBJECT(v);
- if (obj) {
- JSObject *wrapped;
- wrapped = js_GetWrappedObject(cx, obj);
- if (wrapped)
- obj = wrapped;
- ops = obj->map->ops;
- #if JS_HAS_XML_SUPPORT
- if (ops == &js_XMLObjectOps.base) {
- type = JSTYPE_XML;
- } else
- #endif
- {
- /*
- * ECMA 262, 11.4.3 says that any native object that implements
- * [[Call]] should be of type "function". Note that RegExp and
- * Script are both of type "function" for compatibility with
- * older SpiderMonkeys.
- */
- clasp = OBJ_GET_CLASS(cx, obj);
- if ((ops == &js_ObjectOps)
- ? (clasp->call
- ? clasp == &js_ScriptClass
- : clasp == &js_FunctionClass)
- : ops->call != NULL) {
- type = JSTYPE_FUNCTION;
- } else {
- #ifdef NARCISSUS
- JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
- if (!OBJ_GET_PROPERTY(cx, obj,
- ATOM_TO_JSID(cx->runtime->atomState
- .__call__Atom),
- &v)) {
- JS_ClearPendingException(cx);
- } else if (VALUE_IS_FUNCTION(cx, v)) {
- type = JSTYPE_FUNCTION;
- }
- #endif
- }
- }
- }
- } else if (JSVAL_IS_NUMBER(v)) {
- type = JSTYPE_NUMBER;
- } else if (JSVAL_IS_STRING(v)) {
- type = JSTYPE_STRING;
- } else if (JSVAL_IS_BOOLEAN(v)) {
- type = JSTYPE_BOOLEAN;
- } else {
- type = JSTYPE_VOID;
- }
- return type;
- }
- JS_PUBLIC_API(const char *)
- JS_GetTypeName(JSContext *cx, JSType type)
- {
- if ((uintN)type >= (uintN)JSTYPE_LIMIT)
- return NULL;
- return JS_TYPE_STR(type);
- }
- /************************************************************************/
- /*
- * Has a new runtime ever been created? This flag is used to detect unsafe
- * changes to js_CStringsAreUTF8 after a runtime has been created, and to
- * ensure that "first checks" on runtime creation are run only once.
- */
- #ifdef DEBUG
- static JSBool js_NewRuntimeWasCalled = JS_FALSE;
- #endif
- JS_PUBLIC_API(JSRuntime *)
- JS_NewRuntime(uint32 maxbytes)
- {
- JSRuntime *rt;
- #ifdef DEBUG
- if (!js_NewRuntimeWasCalled) {
- /*
- * This code asserts that the numbers associated with the error names
- * in jsmsg.def are monotonically increasing. It uses values for the
- * error names enumerated in jscntxt.c. It's not a compile-time check
- * but it's better than nothing.
- */
- int errorNumber = 0;
- #define MSG_DEF(name, number, count, exception, format) \
- JS_ASSERT(name == errorNumber++);
- #include "js.msg"
- #undef MSG_DEF
- #define MSG_DEF(name, number, count, exception, format) \
- JS_BEGIN_MACRO \
- uintN numfmtspecs = 0; \
- const char *fmt; \
- for (fmt = format; *fmt != '\0'; fmt++) { \
- if (*fmt == '{' && isdigit(fmt[1])) \
- ++numfmtspecs; \
- } \
- JS_ASSERT(count == numfmtspecs); \
- JS_END_MACRO;
- #include "js.msg"
- #undef MSG_DEF
- js_NewRuntimeWasCalled = JS_TRUE;
- }
- #endif /* DEBUG */
- rt = (JSRuntime *) malloc(sizeof(JSRuntime));
- if (!rt)
- return NULL;
- /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */
- memset(rt, 0, sizeof(JSRuntime));
- JS_INIT_CLIST(&rt->contextList);
- JS_INIT_CLIST(&rt->trapList);
- JS_INIT_CLIST(&rt->watchPointList);
- if (!js_InitDtoa())
- goto bad;
- if (!js_InitGC(rt, maxbytes))
- goto bad;
- if (!js_InitAtomState(rt))
- goto bad;
- if (!js_InitDeflatedStringCache(rt))
- goto bad;
- #ifdef JS_THREADSAFE
- if (!js_InitThreadPrivateIndex(js_ThreadDestructorCB))
- goto bad;
- rt->gcLock = JS_NEW_LOCK();
- if (!rt->gcLock)
- goto bad;
- rt->gcDone = JS_NEW_CONDVAR(rt->gcLock);
- if (!rt->gcDone)
- goto bad;
- rt->requestDone = JS_NEW_CONDVAR(rt->gcLock);
- if (!rt->requestDone)
- goto bad;
- /* this is asymmetric with JS_ShutDown: */
- if (!js_SetupLocks(8, 16))
- goto bad;
- rt->rtLock = JS_NEW_LOCK();
- if (!rt->rtLock)
- goto bad;
- rt->stateChange = JS_NEW_CONDVAR(rt->gcLock);
- if (!rt->stateChange)
- goto bad;
- rt->titleSharingDone = JS_NEW_CONDVAR(rt->gcLock);
- if (!rt->titleSharingDone)
- goto bad;
- rt->titleSharingTodo = NO_TITLE_SHARING_TODO;
- rt->debuggerLock = JS_NEW_LOCK();
- if (!rt->debuggerLock)
- goto bad;
- #endif
- if (!js_InitPropertyTree(rt))
- goto bad;
- #if !defined JS_THREADSAFE && defined JS_TRACER
- js_InitJIT(&rt->traceMonitor);
- #endif
- return rt;
- bad:
- JS_DestroyRuntime(rt);
- return NULL;
- }
- JS_PUBLIC_API(void)
- JS_DestroyRuntime(JSRuntime *rt)
- {
- #ifdef DEBUG
- /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */
- if (!JS_CLIST_IS_EMPTY(&rt->contextList)) {
- JSContext *cx, *iter = NULL;
- uintN cxcount = 0;
- while ((cx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) {
- fprintf(stderr,
- "JS API usage error: found live context at %p\n",
- cx);
- cxcount++;
- }
- fprintf(stderr,
- "JS API usage error: %u context%s left in runtime upon JS_DestroyRuntime.\n",
- cxcount, (cxcount == 1) ? "" : "s");
- }
- #endif
- #if !defined JS_THREADSAFE && defined JS_TRACER
- js_FinishJIT(&rt->traceMonitor);
- #endif
- js_FreeRuntimeScriptState(rt);
- js_FinishAtomState(rt);
- /*
- * Free unit string storage only after all strings have been finalized, so
- * that js_FinalizeString can detect unit strings and avoid calling free
- * on their chars storage.
- */
- js_FinishUnitStrings(rt);
- /*
- * Finish the deflated string cache after the last GC and after
- * calling js_FinishAtomState, which finalizes strings.
- */
- js_FinishDeflatedStringCache(rt);
- js_FinishGC(rt);
- #ifdef JS_THREADSAFE
- if (rt->gcLock)
- JS_DESTROY_LOCK(rt->gcLock);
- if (rt->gcDone)
- JS_DESTROY_CONDVAR(rt->gcDone);
- if (rt->requestDone)
- JS_DESTROY_CONDVAR(rt->requestDone);
- if (rt->rtLock)
- JS_DESTROY_LOCK(rt->rtLock);
- if (rt->stateChange)
- JS_DESTROY_CONDVAR(rt->stateChange);
- if (rt->titleSharingDone)
- JS_DESTROY_CONDVAR(rt->titleSharingDone);
- if (rt->debuggerLock)
- JS_DESTROY_LOCK(rt->debuggerLock);
- #else
- GSN_CACHE_CLEAR(&rt->gsnCache);
- #endif
- js_FinishPropertyTree(rt);
- free(rt);
- }
- JS_PUBLIC_API(void)
- JS_ShutDown(void)
- {
- #ifdef JS_OPMETER
- extern void js_DumpOpMeters();
- js_DumpOpMeters();
- #endif
- js_FinishDtoa();
- #ifdef JS_THREADSAFE
- js_CleanupLocks();
- #endif
- PRMJ_NowShutdown();
- }
- JS_PUBLIC_API(void *)
- JS_GetRuntimePrivate(JSRuntime *rt)
- {
- return rt->data;
- }
- JS_PUBLIC_API(void)
- JS_SetRuntimePrivate(JSRuntime *rt, void *data)
- {
- rt->data = data;
- }
- JS_PUBLIC_API(void)
- JS_BeginRequest(JSContext *cx)
- {
- #ifdef JS_THREADSAFE
- JSRuntime *rt;
- JS_ASSERT(cx->thread->id == js_CurrentThreadId());
- if (!cx->requestDepth) {
- JS_ASSERT(cx->gcLocalFreeLists == &js_GCEmptyFreeListSet);
- /* Wait until the GC is finished. */
- rt = cx->runtime;
- JS_LOCK_GC(rt);
- /* NB: we use cx->thread here, not js_GetCurrentThread(). */
- if (rt->gcThread != cx->thread) {
- while (rt->gcLevel > 0)
- JS_AWAIT_GC_DONE(rt);
- }
- /* Indicate that a request is running. */
- rt->requestCount++;
- cx->requestDepth = 1;
- cx->outstandingRequests++;
- JS_UNLOCK_GC(rt);
- return;
- }
- cx->requestDepth++;
- cx->outstandingRequests++;
- #endif
- }
- JS_PUBLIC_API(void)
- JS_EndRequest(JSContext *cx)
- {
- #ifdef JS_THREADSAFE
- JSRuntime *rt;
- JSTitle *title, **todop;
- JSBool shared;
- CHECK_REQUEST(cx);
- JS_ASSERT(cx->requestDepth > 0);
- JS_ASSERT(cx->outstandingRequests > 0);
- if (cx->requestDepth == 1) {
- /* Lock before clearing to interlock with ClaimScope, in jslock.c. */
- rt = cx->runtime;
- JS_LOCK_GC(rt);
- cx->requestDepth = 0;
- cx->outstandingRequests--;
- /* See whether cx has any single-threaded titles to start sharing. */
- todop = &rt->titleSharingTodo;
- shared = JS_FALSE;
- while ((title = *todop) != NO_TITLE_SHARING_TODO) {
- if (title->ownercx != cx) {
- todop = &title->u.link;
- continue;
- }
- *todop = title->u.link;
- title->u.link = NULL; /* null u.link for sanity ASAP */
- /*
- * If js_DropObjectMap returns null, we held the last ref to scope.
- * The waiting thread(s) must have been killed, after which the GC
- * collected the object that held this scope. Unlikely, because it
- * requires that the GC ran (e.g., from an operation callback)
- * during this request, but possible.
- */
- if (js_DropObjectMap(cx, TITLE_TO_MAP(title), NULL)) {
- js_InitLock(&title->lock);
- title->u.count = 0; /* NULL may not pun as 0 */
- js_FinishSharingTitle(cx, title); /* set ownercx = NULL */
- shared = JS_TRUE;
- }
- }
- if (shared)
- JS_NOTIFY_ALL_CONDVAR(rt->titleSharingDone);
- js_RevokeGCLocalFreeLists(cx);
- /* Give the GC a chance to run if this was the last request running. */
- JS_ASSERT(rt->requestCount > 0);
- rt->requestCount--;
- if (rt->requestCount == 0)
- JS_NOTIFY_REQUEST_DONE(rt);
- JS_UNLOCK_GC(rt);
- return;
- }
- cx->requestDepth--;
- cx->outstandingRequests--;
- #endif
- }
- /* Yield to pending GC operations, regardless of request depth */
- JS_PUBLIC_API(void)
- JS_YieldRequest(JSContext *cx)
- {
- #ifdef JS_THREADSAFE
- JS_ASSERT(cx->thread);
- CHECK_REQUEST(cx);
- JS_ResumeRequest(cx, JS_SuspendRequest(cx));
- #endif
- }
- JS_PUBLIC_API(jsrefcount)
- JS_SuspendRequest(JSContext *cx)
- {
- #ifdef JS_THREADSAFE
- jsrefcount saveDepth = cx->requestDepth;
- while (cx->requestDepth) {
- cx->outstandingRequests++; /* compensate for JS_EndRequest */
- JS_EndRequest(cx);
- }
- return saveDepth;
- #else
- return 0;
- #endif
- }
- JS_PUBLIC_API(void)
- JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth)
- {
- #ifdef JS_THREADSAFE
- JS_ASSERT(!cx->requestDepth);
- while (--saveDepth >= 0) {
- JS_BeginRequest(cx);
- cx->outstandingRequests--; /* compensate for JS_BeginRequest */
- }
- #endif
- }
- JS_PUBLIC_API(void)
- JS_Lock(JSRuntime *rt)
- {
- JS_LOCK_RUNTIME(rt);
- }
- JS_PUBLIC_API(void)
- JS_Unlock(JSRuntime *rt)
- {
- JS_UNLOCK_RUNTIME(rt);
- }
- JS_PUBLIC_API(JSContextCallback)
- JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback)
- {
- JSContextCallback old;
- old = rt->cxCallback;
- rt->cxCallback = cxCallback;
- return old;
- }
- JS_PUBLIC_API(JSContext *)
- JS_NewContext(JSRuntime *rt, size_t stackChunkSize)
- {
- return js_NewContext(rt, stackChunkSize);
- }
- JS_PUBLIC_API(void)
- JS_DestroyContext(JSContext *cx)
- {
- js_DestroyContext(cx, JSDCM_FORCE_GC);
- }
- JS_PUBLIC_API(void)
- JS_DestroyContextNoGC(JSContext *cx)
- {
- js_DestroyContext(cx, JSDCM_NO_GC);
- }
- JS_PUBLIC_API(void)
- JS_DestroyContextMaybeGC(JSContext *cx)
- {
- js_DestroyContext(cx, JSDCM_MAYBE_GC);
- }
- JS_PUBLIC_API(void *)
- JS_GetContextPrivate(JSContext *cx)
- {
- return cx->data;
- }
- JS_PUBLIC_API(void)
- JS_SetContextPrivate(JSContext *cx, void *data)
- {
- cx->data = data;
- }
- JS_PUBLIC_API(JSRuntime *)
- JS_GetRuntime(JSContext *cx)
- {
- return cx->runtime;
- }
- JS_PUBLIC_API(JSContext *)
- JS_ContextIterator(JSRuntime *rt, JSContext **iterp)
- {
- return js_ContextIterator(rt, JS_TRUE, iterp);
- }
- JS_PUBLIC_API(JSVersion)
- JS_GetVersion(JSContext *cx)
- {
- return JSVERSION_NUMBER(cx);
- }
- JS_PUBLIC_API(JSVersion)
- JS_SetVersion(JSContext *cx, JSVersion version)
- {
- JSVersion oldVersion;
- JS_ASSERT(version != JSVERSION_UNKNOWN);
- JS_ASSERT((version & ~JSVERSION_MASK) == 0);
- oldVersion = JSVERSION_NUMBER(cx);
- if (version == oldVersion)
- return oldVersion;
- /* We no longer support 1.4 or below. */
- if (version != JSVERSION_DEFAULT && version <= JSVERSION_1_4)
- return oldVersion;
- cx->version = (cx->version & ~JSVERSION_MASK) | version;
- js_OnVersionChange(cx);
- return oldVersion;
- }
- static struct v2smap {
- JSVersion version;
- const char *string;
- } v2smap[] = {
- {JSVERSION_1_0, "1.0"},
- {JSVERSION_1_1, "1.1"},
- {JSVERSION_1_2, "1.2"},
- {JSVERSION_1_3, "1.3"},
- {JSVERSION_1_4, "1.4"},
- {JSVERSION_ECMA_3, "ECMAv3"},
- {JSVERSION_1_5, "1.5"},
- {JSVERSION_1_6, "1.6"},
- {JSVERSION_1_7, "1.7"},
- {JSVERSION_1_8, "1.8"},
- {JSVERSION_DEFAULT, js_default_str},
- {JSVERSION_UNKNOWN, NULL}, /* must be last, NULL is sentinel */
- };
- JS_PUBLIC_API(const char *)
- JS_VersionToString(JSVersion version)
- {
- int i;
- for (i = 0; v2smap[i].string; i++)
- if (v2smap[i].version == version)
- return v2smap[i].string;
- return "unknown";
- }
- JS_PUBLIC_API(JSVersion)
- JS_StringToVersion(const char *string)
- {
- int i;
- for (i = 0; v2smap[i].string; i++)
- if (strcmp(v2smap[i].string, string) == 0)
- return v2smap[i].version;
- return JSVERSION_UNKNOWN;
- }
- JS_PUBLIC_API(uint32)
- JS_GetOptions(JSContext *cx)
- {
- return cx->options;
- }
- #define SYNC_OPTIONS_TO_VERSION(cx) \
- JS_BEGIN_MACRO \
- if ((cx)->options & JSOPTION_XML) \
- (cx)->version |= JSVERSION_HAS_XML; \
- else \
- (cx)->version &= ~JSVERSION_HAS_XML; \
- JS_END_MACRO
- JS_PUBLIC_API(uint32)
- JS_SetOptions(JSContext *cx, uint32 options)
- {
- uint32 oldopts = cx->options;
- cx->options = options;
- SYNC_OPTIONS_TO_VERSION(cx);
- return oldopts;
- }
- JS_PUBLIC_API(uint32)
- JS_ToggleOptions(JSContext *cx, uint32 options)
- {
- uint32 oldopts = cx->options;
- cx->options ^= options;
- SYNC_OPTIONS_TO_VERSION(cx);
- return oldopts;
- }
- JS_PUBLIC_API(const char *)
- JS_GetImplementationVersion(void)
- {
- return "JavaScript-C 1.8.0 pre-release 1 2007-10-03";
- }
- JS_PUBLIC_API(JSObject *)
- JS_GetGlobalObject(JSContext *cx)
- {
- return cx->globalObject;
- }
- JS_PUBLIC_API(void)
- JS_SetGlobalObject(JSContext *cx, JSObject *obj)
- {
- cx->globalObject = obj;
- #if JS_HAS_XML_SUPPORT
- cx->xmlSettingFlags = 0;
- #endif
- }
- JS_BEGIN_EXTERN_C
- JSObject *
- js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj)
- {
- JSDHashTable *table;
- JSBool resolving;
- JSRuntime *rt;
- JSResolvingKey key;
- JSResolvingEntry *entry;
- JSObject *fun_proto, *obj_proto;
- /* If cx has no global object, use obj so prototypes can be found. */
- if (!cx->globalObject)
- JS_SetGlobalObject(cx, obj);
- /* Record Function and Object in cx->resolvingTable, if we are resolving. */
- table = cx->resolvingTable;
- resolving = (table && table->entryCount);
- rt = cx->runtime;
- key.obj = obj;
- if (resolving) {
- key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]);
- entry = (JSResolvingEntry *)
- JS_DHashTableOperate(table, &key, JS_DHASH_ADD);
- if (entry && entry->key.obj && (entry->flags & JSRESFLAG_LOOKUP)) {
- /* Already resolving Function, record Object too. */
- JS_ASSERT(entry->key.obj == obj);
- key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]);
- entry = (JSResolvingEntry *)
- JS_DHashTableOperate(table, &key, JS_DHASH_ADD);
- }
- if (!entry) {
- JS_ReportOutOfMemory(cx);
- return NULL;
- }
- JS_ASSERT(!entry->key.obj && entry->flags == 0);
- entry->key = key;
- entry->flags = JSRESFLAG_LOOKUP;
- } else {
- key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]);
- if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry))
- return NULL;
- key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]);
- if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
- key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]);
- JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE);
- return NULL;
- }
- table = cx->resolvingTable;
- }
- /* Initialize the function class first so constructors can be made. */
- if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Function),
- &fun_proto)) {
- fun_proto = NULL;
- goto out;
- }
- if (!fun_proto) {
- fun_proto = js_InitFunctionClass(cx, obj);
- if (!fun_proto)
- goto out;
- } else {
- JSObject *ctor;
- ctor = JS_GetConstructor(cx, fun_proto);
- if (!ctor) {
- fun_proto = NULL;
- goto out;
- }
- OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(CLASS_ATOM(cx, Function)),
- OBJECT_TO_JSVAL(ctor), 0, 0, 0, NULL);
- }
- /* Initialize the object class next so Object.prototype works. */
- if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object),
- &obj_proto)) {
- fun_proto = NULL;
- goto out;
- }
- if (!obj_proto)
- obj_proto = js_InitObjectClass(cx, obj);
- if (!obj_proto) {
- fun_proto = NULL;
- goto out;
- }
- /* Function.prototype and the global object delegate to Object.prototype. */
- OBJ_SET_PROTO(cx, fun_proto, obj_proto);
- if (!OBJ_GET_PROTO(cx, obj))
- OBJ_SET_PROTO(cx, obj, obj_proto);
- out:
- /* If resolving, remove the other entry (Object or Function) from table. */
- JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE);
- if (!resolving) {
- /* If not resolving, remove the first entry added above, for Object. */
- JS_ASSERT(key.id == \
- ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]));
- key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]);
- JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE);
- }
- return fun_proto;
- }
- JS_END_EXTERN_C
- JS_PUBLIC_API(JSBool)
- JS_InitStandardClasses(JSContext *cx, JSObject *obj)
- {
- JSAtom *atom;
- CHECK_REQUEST(cx);
- /* Define a top-level property 'undefined' with the undefined value. */
- atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
- if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID,
- NULL, NULL, JSPROP_PERMANENT, NULL)) {
- return JS_FALSE;
- }
- /* Function and Object require cooperative bootstrapping magic. */
- if (!js_InitFunctionAndObjectClasses(cx, obj))
- return JS_FALSE;
- /* Initialize the rest of the standard objects and functions. */
- return js_InitArrayClass(cx, obj) &&
- js_InitBlockClass(cx, obj) &&
- js_InitBooleanClass(cx, obj) &&
- js_InitCallClass(cx, obj) &&
- js_InitExceptionClasses(cx, obj) &&
- js_InitMathClass(cx, obj) &&
- js_InitNumberClass(cx, obj) &&
- js_InitJSONClass(cx, obj) &&
- js_InitRegExpClass(cx, obj) &&
- js_InitStringClass(cx, obj) &&
- js_InitEval(cx, obj) &&
- #if JS_HAS_SCRIPT_OBJECT
- js_InitScriptClass(cx, obj) &&
- #endif
- #if JS_HAS_XML_SUPPORT
- js_InitXMLClasses(cx, obj) &&
- #endif
- #if JS_HAS_FILE_OBJECT
- js_InitFileClass(cx, obj) &&
- #endif
- #if JS_HAS_GENERATORS
- js_InitIteratorClasses(cx, obj) &&
- #endif
- js_InitDateClass(cx, obj);
- }
- #define CLASP(name) (&js_##name##Class)
- #define EXT_CLASP(name) (&js_##name##Class.base)
- #define EAGER_ATOM(name) ATOM_OFFSET(name), NULL
- #define EAGER_CLASS_ATOM(name) CLASS_ATOM_OFFSET(name), NULL
- #define EAGER_ATOM_AND_CLASP(name) EAGER_CLASS_ATOM(name), CLASP(name)
- #define EAGER_ATOM_AND_EXT_CLASP(name) EAGER_CLASS_ATOM(name), EXT_CLASP(name)
- #define LAZY_ATOM(name) ATOM_OFFSET(lazy.name), js_##name##_str
- typedef struct JSStdName {
- JSObjectOp init;
- size_t atomOffset; /* offset of atom pointer in JSAtomState */
- const char *name; /* null if atom is pre-pinned, else name */
- JSClass *clasp;
- } JSStdName;
- static JSAtom *
- StdNameToAtom(JSContext *cx, JSStdName *stdn)
- {
- size_t offset;
- JSAtom *atom;
- const char *name;
- offset = stdn->atomOffset;
- atom = OFFSET_TO_ATOM(cx->runtime, offset);
- if (!atom) {
- name = stdn->name;
- if (name) {
- atom = js_Atomize(cx, name, strlen(name), ATOM_PINNED);
- OFFSET_TO_ATOM(cx->runtime, offset) = atom;
- }
- }
- return atom;
- }
- /*
- * Table of class initializers and their atom offsets in rt->atomState.
- * If you add a "standard" class, remember to update this table.
- */
- static JSStdName standard_class_atoms[] = {
- {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Function)},
- {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Object)},
- {js_InitArrayClass, EAGER_ATOM_AND_CLASP(Array)},
- {js_InitBlockClass, EAGER_ATOM_AND_CLASP(Block)},
- {js_InitBooleanClass, EAGER_ATOM_AND_CLASP(Boolean)},
- {js_InitDateClass, EAGER_ATOM_AND_CLASP(Date)},
- {js_InitMathClass, EAGER_ATOM_AND_CLASP(Math)},
- {js_InitNumberClass, EAGER_ATOM_AND_CLASP(Number)},
- {js_InitStringClass, EAGER_ATOM_AND_CLASP(String)},
- {js_InitCallClass, EAGER_ATOM_AND_CLASP(Call)},
- {js_InitExceptionClasses, EAGER_ATOM_AND_CLASP(Error)},
- {js_InitRegExpClass, EAGER_ATOM_AND_CLASP(RegExp)},
- #if JS_HAS_SCRIPT_OBJECT
- {js_InitScriptClass, EAGER_ATOM_AND_CLASP(Script)},
- #endif
- #if JS_HAS_XML_SUPPORT
- {js_InitXMLClass, EAGER_ATOM_AND_CLASP(XML)},
- {js_InitNamespaceClass, EAGER_ATOM_AND_EXT_CLASP(Namespace)},
- {js_InitQNameClass, EAGER_ATOM_AND_EXT_CLASP(QName)},
- #endif
- #if JS_HAS_FILE_OBJECT
- {js_InitFileClass, EAGER_ATOM_AND_CLASP(File)},
- #endif
- #if JS_HAS_GENERATORS
- {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(StopIteration)},
- #endif
- {js_InitJSONClass, EAGER_ATOM_AND_CLASP(JSON)},
- {NULL, 0, NULL, NULL}
- };
- /*
- * Table of top-level function and constant names and their init functions.
- * If you add a "standard" global function or property, remember to update
- * this table.
- */
- static JSStdName standard_class_names[] = {
- /* ECMA requires that eval be a direct property of the global object. */
- {js_InitEval, EAGER_ATOM(eval), NULL},
- /* Global properties and functions defined by the Number class. */
- {js_InitNumberClass, LAZY_ATOM(NaN), NULL},
- {js_InitNumberClass, LAZY_ATOM(Infinity), NULL},
- {js_InitNumberClass, LAZY_ATOM(isNaN), NULL},
- {js_InitNumberClass, LAZY_ATOM(isFinite), NULL},
- {js_InitNumberClass, LAZY_ATOM(parseFloat), NULL},
- {js_InitNumberClass, LAZY_ATOM(parseInt), NULL},
- /* String global functions. */
- {js_InitStringClass, LAZY_ATOM(escape), NULL},
- {js_InitStringClass, LAZY_ATOM(unescape), NULL},
- {js_InitStringClass, LAZY_ATOM(decodeURI), NULL},
- {js_InitStringClass, LAZY_ATOM(encodeURI), NULL},
- {js_InitStringClass, LAZY_ATOM(decodeURIComponent), NULL},
- {js_InitStringClass, LAZY_ATOM(encodeURIComponent), NULL},
- #if JS_HAS_UNEVAL
- {js_InitStringClass, LAZY_ATOM(uneval), NULL},
- #endif
- /* Exception constructors. */
- {js_InitExceptionClasses, EAGER_CLASS_ATOM(Error), CLASP(Error)},
- {js_InitExceptionClasses, EAGER_CLASS_ATOM(InternalError), CLASP(Error)},
- {js_InitExceptionClasses, EAGER_CLASS_ATOM(EvalError), CLASP(Error)},
- {js_InitExceptionClasses, EAGER_CLASS_ATOM(RangeError), CLASP(Error)},
- {js_InitExceptionClasses, EAGER_CLASS_ATOM(ReferenceError), CLASP(Error)},
- {js_InitExceptionClasses, EAGER_CLASS_ATOM(SyntaxError), CLASP(Error)},
- {js_InitExceptionClasses, EAGER_CLASS_ATOM(TypeError), CLASP(Error)},
- {js_InitExceptionClasses, EAGER_CLASS_ATOM(URIError), CLASP(Error)},
- #if JS_HAS_XML_SUPPORT
- {js_InitAnyNameClass, EAGER_ATOM_AND_CLASP(AnyName)},
- {js_InitAttributeNameClass, EAGER_ATOM_AND_CLASP(AttributeName)},
- {js_InitXMLClass, LAZY_ATOM(XMLList), &js_XMLClass},
- {js_InitXMLClass, LAZY_ATOM(isXMLName), NULL},
- #endif
- #if JS_HAS_GENERATORS
- {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Iterator)},
- {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Generator)},
- #endif
- {NULL, 0, NULL, NULL}
- };
- static JSStdName object_prototype_names[] = {
- /* Object.prototype properties (global delegates to Object.prototype). */
- {js_InitObjectClass, EAGER_ATOM(proto), NULL},
- {js_InitObjectClass, EAGER_ATOM(parent), NULL},
- {js_InitObjectClass, EAGER_ATOM(count), NULL},
- #if JS_HAS_TOSOURCE
- {js_InitObjectClass, EAGER_ATOM(toSource), NULL},
- #endif
- {js_InitObjectClass, EAGER_ATOM(toString), NULL},
- {js_InitObjectClass, EAGER_ATOM(toLocaleString), NULL},
- {js_InitObjectClass, EAGER_ATOM(valueOf), NULL},
- #if JS_HAS_OBJ_WATCHPOINT
- {js_InitObjectClass, LAZY_ATOM(watch), NULL},
- {js_InitObjectClass, LAZY_ATOM(unwatch), NULL},
- #endif
- {js_InitObjectClass, LAZY_ATOM(hasOwnProperty), NULL},
- {js_InitObjectClass, LAZY_ATOM(isPrototypeOf), NULL},
- {js_InitObjectClass, LAZY_ATOM(propertyIsEnumerable), NULL},
- #if JS_HAS_GETTER_SETTER
- {js_InitObjectClass, LAZY_ATOM(defineGetter), NULL},
- {js_InitObjectClass, LAZY_ATOM(defineSetter), NULL},
- {js_InitObjectClass, LAZY_ATOM(lookupGetter), NULL},
- {js_InitObjectClass, LAZY_ATOM(lookupSetter), NULL},
- #endif
- {NULL, 0, NULL, NULL}
- };
- JS_PUBLIC_API(JSBool)
- JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id,
- JSBool *resolved)
- {
- JSString *idstr;
- JSRuntime *rt;
- JSAtom *atom;
- JSStdName *stdnm;
- uintN i;
- CHECK_REQUEST(cx);
- *resolved = JS_FALSE;
- rt = cx->runtime;
- JS_ASSERT(rt->state != JSRTS_DOWN);
- if (rt->state == JSRTS_LANDING || !JSVAL_IS_STRING(id))
- return JS_TRUE;
- idstr = JSVAL_TO_STRING(id);
- /* Check whether we're resolving 'undefined', and define it if so. */
- atom = rt->atomState.typeAtoms[JSTYPE_VOID];
- if (idstr == ATOM_TO_STRING(atom)) {
- *resolved = JS_TRUE;
- return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID,
- NULL, NULL, JSPROP_PERMANENT, NULL);
- }
- /* Try for class constructors/prototypes named by well-known atoms. */
- stdnm = NULL;
- for (i = 0; standard_class_atoms[i].init; i++) {
- atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset);
- if (idstr == ATOM_TO_STRING(atom)) {
- stdnm = &standard_class_atoms[i];
- break;
- }
- }
- if (!stdnm) {
- /* Try less frequently used top-level functions and constants. */
- for (i = 0; standard_class_names[i].init; i++) {
- atom = StdNameToAtom(cx, &standard_class_names[i]);
- if (!atom)
- return JS_FALSE;
- if (idstr == ATOM_TO_STRING(atom)) {
- stdnm = &standard_class_names[i];
- break;
- }
- }
- if (!stdnm && !OBJ_GET_PROTO(cx, obj)) {
- /*
- * Try even less frequently used names delegated from the global
- * object to Object.prototype, but only if the Object class hasn't
- * yet been initialized.
- */
- for (i = 0; object_prototype_names[i].init; i++) {
- atom = StdNameToAtom(cx, &object_prototype_names[i]);
- if (!atom)
- return JS_FALSE;
- if (idstr == ATOM_TO_STRING(atom)) {
- stdnm = &standard_class_names[i];
- break;
- }
- }
- }
- }
- if (stdnm) {
- /*
- * If this standard class is anonymous and obj advertises itself as a
- * global object (in order to reserve slots for standard class object
- * pointers), then we don't want to resolve by name.
- *
- * If inversely, either id does not name a class, or id does not name
- * an anonymous class, or the global does not reserve slots for class
- * objects, then we must call the init hook here.
- */
- if (stdnm->clasp &&
- (stdnm->clasp->flags & JSCLASS_IS_ANONYMOUS) &&
- (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) {
- return JS_TRUE;
- }
- if (!stdnm->init(cx, obj))
- return JS_FALSE;
- *resolved = JS_TRUE;
- }
- return JS_TRUE;
- }
- static JSBool
- AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom)
- {
- JSScopeProperty *sprop;
- JSScope *scope;
- JS_ASSERT(OBJ_IS_NATIVE(obj));
- JS_LOCK_OBJ(cx, obj);
- scope = OBJ_SCOPE(obj);
- sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom));
- JS_UNLOCK_SCOPE(cx, scope);
- return sprop != NULL;
- }
- JS_PUBLIC_API(JSBool)
- JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj)
- {
- JSRuntime *rt;
- JSAtom *atom;
- uintN i;
- CHECK_REQUEST(cx);
- rt = cx->runtime;
- /* Check whether we need to bind 'undefined' and define it if so. */
- atom = rt->atomState.typeAtoms[JSTYPE_VOID];
- if (!AlreadyHasOwnProperty(cx, obj, atom) &&
- !OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID,
- NULL, NULL, JSPROP_PERMANENT, NULL)) {
- return JS_FALSE;
- }
- /* Initialize any classes that have not been resolved yet. */
- for (i = 0; standard_class_atoms[i].init; i++) {
- atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset);
- if (!AlreadyHasOwnProperty(cx, obj, atom) &&
- !standard_class_atoms[i].init(cx, obj)) {
- return JS_FALSE;
- }
- }
- return JS_TRUE;
- }
- static JSIdArray *
- NewIdArray(JSContext *cx, jsint length)
- {
- JSIdArray *ida;
- ida = (JSIdArray *)
- JS_malloc(cx, offsetof(JSIdArray, vector) + length * sizeof(jsval));
- if (ida)
- ida->length = length;
- return ida;
- }
- /*
- * Unlike realloc(3), this function frees ida on failure.
- */
- static JSIdArray *
- SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length)
- {
- JSIdArray *rida;
- rida = (JSIdArray *)
- JS_realloc(cx, ida,
- offsetof(JSIdArray, vector) + length * sizeof(jsval));
- if (!rida)
- JS_DestroyIdArray(cx, ida);
- else
- rida->length = length;
- return rida;
- }
- static JSIdArray *
- AddAtomToArray(JSContext *cx, JSAtom *atom, JSIdArray *ida, jsint *ip)
- {
- jsint i, length;
- i = *ip;
- length = ida->length;
- if (i >= length) {
- ida = SetIdArrayLength(cx, ida, JS_MAX(length * 2, 8));
- if (!ida)
- return NULL;
- JS_ASSERT(i < ida->length);
- }
- ida->vector[i] = ATOM_TO_JSID(atom);
- *ip = i + 1;
- return ida;
- }
- static JSIdArray *
- EnumerateIfResolved(JSContext *cx, JSObject *obj, JSAtom *atom, JSIdArray *ida,
- jsint *ip, JSBool *foundp)
- {
- *foundp = AlreadyHasOwnProperty(cx, obj, atom);
- if (*foundp)
- ida = AddAtomToArray(cx, atom, ida, ip);
- return ida;
- }
- JS_PUBLIC_API(JSIdArray *)
- JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj,
- JSIdArray *ida)
- {
- JSRuntime *rt;
- jsint i, j, k;
- JSAtom *atom;
- JSBool found;
- JSObjectOp init;
- CHECK_REQUEST(cx);
- rt = cx->runtime;
- if (ida) {
- i = ida->length;
- } else {
- ida = NewIdArray(cx, 8);
- if (!ida)
- return NULL;
- i = 0;
- }
- /* Check whether 'undefined' has been resolved and enumerate it if so. */
- atom = rt->atomState.typeAtoms[JSTYPE_VOID];
- ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found);
- if (!ida)
- return NULL;
- /* Enumerate only classes that *have* been resolved. */
- for (j = 0; standard_class_atoms[j].init; j++) {
- atom = OFFSET_TO_ATOM(rt, standard_class_atoms[j].atomOffset);
- ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found);
- if (!ida)
- return NULL;
- if (found) {
- init = standard_class_atoms[j].init;
- for (k = 0; standard_class_names[k].init; k++) {
- if (standard_class_names[k].init == init) {
- atom = StdNameToAtom(cx, &standard_class_names[k]);
- ida = AddAtomToArray(cx, atom, ida, &i);
- if (!ida)
- return NULL;
- }
- }
- if (init == js_InitObjectClass) {
- for (k = 0; object_prototype_names[k].init; k++) {
- atom = StdNameToAtom(cx, &object_prototype_names[k]);
- ida = AddAtomToArray(cx, atom, ida, &i);
- if (!ida)
- return NULL;
- }
- }
- }
- }
- /* Trim to exact length. */
- return SetIdArrayLength(cx, ida, i);
- }
- #undef CLASP
- #undef EAGER_ATOM
- #undef EAGER_CLASS_ATOM
- #undef EAGER_ATOM_CLASP
- #undef LAZY_ATOM
- JS_PUBLIC_API(JSBool)
- JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
- JSObject **objp)
- {
- CHECK_REQUEST(cx);
- return js_GetClassObject(cx, obj, key, objp);
- }
- JS_PUBLIC_API(JSObject *)
- JS_GetScopeChain(JSContext *cx)
- {
- JSStackFrame *fp;
- CHECK_REQUEST(cx);
- fp = cx->fp;
- if (!fp) {
- /*
- * There is no code active on this context. In place of an actual
- * scope chain, use the context's global object, which is set in
- * js_InitFunctionAndObjectClasses, and which represents the default
- * scope chain for the embedding. See also js_FindClassObject.
- *
- * For embeddings that use the inner and outer object hooks, the inner
- * object represents the ultimate global object, with the outer object
- * acting as a stand-in.
- */
- JSObject *obj = cx->globalObject;
- if (!obj) {
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
- return NULL;
- }
- OBJ_TO_INNER_OBJECT(cx, obj);
- return obj;
- }
- return js_GetScopeChain(cx, fp);
- }
- JS_PUBLIC_API(JSObject *)
- JS_GetGlobalForObject(JSContext *cx, JSObject *obj)
- {
- JSObject *parent;
- while ((parent = OBJ_GET_PARENT(cx, obj)) != NULL)
- obj = parent;
- return obj;
- }
- JS_PUBLIC_API(jsval)
- JS_ComputeThis(JSContext *cx, jsval *vp)
- {
- if (!js_ComputeThis(cx, JS_FALSE, vp + 2))
- return JSVAL_NULL;
- return vp[1];
- }
- JS_PUBLIC_API(void *)
- JS_malloc(JSContext *cx, size_t nbytes)
- {
- void *p;
- JS_ASSERT(nbytes != 0);
- JS_COUNT_OPERATION(cx, JSOW_ALLOCATION);
- if (nbytes == 0)
- nbytes = 1;
- p = malloc(nbytes);
- if (!p) {
- JS_ReportOutOfMemory(cx);
- return NULL;
- }
- js_UpdateMallocCounter(cx, nbytes);
- return p;
- }
- JS_PUBLIC_API(void *)
- JS_realloc(JSContext *cx, void *p, size_t nbytes)
- {
- JS_COUNT_OPERATION(cx, JSOW_ALLOCATION);
- p = realloc(p, nbytes);
- if (!p)
- JS_ReportOutOfMemory(cx);
- return p;
- }
- JS_PUBLIC_API(void)
- JS_free(JSContext *cx, void *p)
- {
- if (p)
- free(p);
- }
- JS_PUBLIC_API(char *)
- JS_strdup(JSContext *cx, const char *s)
- {
- size_t n;
- void *p;
- n = strlen(s) + 1;
- p = JS_malloc(cx, n);
- if (!p)
- return NULL;
- return (char *)memcpy(p, s, n);
- }
- JS_PUBLIC_API(jsdouble *)
- JS_NewDouble(JSContext *cx, jsdouble d)
- {
- CHECK_REQUEST(cx);
- return js_NewWeaklyRootedDouble(cx, d);
- }
- JS_PUBLIC_API(JSBool)
- JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval)
- {
- jsdouble *dp;
- CHECK_REQUEST(cx);
- dp = js_NewWeaklyRootedDouble(cx, d);
- if (!dp)
- return JS_FALSE;
- *rval = DOUBLE_TO_JSVAL(dp);
- return JS_TRUE;
- }
- JS_PUBLIC_API(JSBool)
- JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)
- {
- jsint i;
- CHECK_REQUEST(cx);
- if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) {
- *rval = INT_TO_JSVAL(i);
- return JS_TRUE;
- }
- return JS_NewDoubleValue(cx, d, rval);
- }
- #undef JS_AddRoot
- JS_PUBLIC_API(JSBool)
- JS_AddRoot(JSContext *cx, void *rp)
- {
- CHECK_REQUEST(cx);
- return js_AddRoot(cx, rp, NULL);
- }
- JS_PUBLIC_API(JSBool)
- JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name)
- {
- return js_AddRootRT(rt, rp, name);
- }
- JS_PUBLIC_API(JSBool)
- JS_RemoveRoot(JSContext *cx, void *rp)
- {
- CHECK_REQUEST(cx);
- return js_RemoveRoot(cx->runtime, rp);
- }
- JS_PUBLIC_API(JSBool)
- JS_RemoveRootRT(JSRuntime *rt, void *rp)
- {
- return js_RemoveRoot(rt, rp);
- }
- JS_PUBLIC_API(JSBool)
- JS_AddNamedRoot(JSContext *cx, void *rp, const char *name)
- {
- CHECK_REQUEST(cx);
- return js_AddRoot(cx, rp, name);
- }
- JS_PUBLIC_API(void)
- JS_ClearNewbornRoots(JSContext *cx)
- {
- JS_CLEAR_WEAK_ROOTS(&cx->weakRoots);
- }
- JS_PUBLIC_API(JSBool)
- JS_EnterLocalRootScope(JSContext *cx)
- {
- CHECK_REQUEST(cx);
- return js_EnterLocalRootScope(cx);
- }
- JS_PUBLIC_API(void)
- JS_LeaveLocalRootScope(JSContext *cx)
- {
- CHECK_REQUEST(cx);
- js_LeaveLocalRootScope(cx);
- }
- JS_PUBLIC_API(void)
- JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval)
- {
- CHECK_REQUEST(cx);
- js_LeaveLocalRootScopeWithResult(cx, rval);
- }
- JS_PUBLIC_API(void)
- JS_ForgetLocalRoot(JSContext *cx, void *thing)
- {
- CHECK_REQUEST(cx);
- js_ForgetLocalRoot(cx, (jsval) thing);
- }
- #ifdef DEBUG
- JS_PUBLIC_API(void)
- JS_DumpNamedRoots(JSRuntime *rt,
- void (*dump)(const char *name, void *rp, void *data),
- void *data)
- {
- js_DumpNamedRoots(rt, dump, data);
- }
- #endif /* DEBUG */
- JS_PUBLIC_API(uint32)
- JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data)
- {
- return js_MapGCRoots(rt, map, data);
- }
- JS_PUBLIC_API(JSBool)
- JS_LockGCThing(JSContext *cx, void *thing)
- {
- JSBool ok;
- CHECK_REQUEST(cx);
- ok = js_LockGCThingRT(cx->runtime, thing);
- if (!ok)
- JS_ReportOutOfMemory(cx);
- return ok;
- }
- JS_PUBLIC_API(JSBool)
- JS_LockGCThingRT(JSRuntime *rt, void *thing)
- {
- return js_LockGCThingRT(rt, thing);
- }
- JS_PUBLIC_API(JSBool)
- JS_UnlockGCThing(JSContext *cx, void *thing)
- {
- JSBool ok;
- CHECK_REQUEST(cx);
- ok = js_UnlockGCThingRT(cx->runtime, thing);
- if (!ok)
- JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_UNLOCK);
- return ok;
- }
- JS_PUBLIC_API(JSBool)
- JS_UnlockGCThingRT(JSRuntime *rt, void *thing)
- {
- return js_UnlockGCThingRT(rt, thing);
- }
- JS_PUBLIC_API(void)
- JS_SetExtraGCRoots(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
- {
- rt->gcExtraRootsTraceOp = traceOp;
- rt->gcExtraRootsData = data;
- }
- JS_PUBLIC_API(void)
- JS_TraceRuntime(JSTracer *trc)
- {
- JSBool allAtoms = trc->context->runtime->gcKeepAtoms != 0;
- js_TraceRuntime(trc, allAtoms);
- }
- #ifdef DEBUG
- #ifdef HAVE_XPCONNECT
- #include "dump_xpc.h"
- #endif
- JS_PUBLIC_API(void)
- JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc,
- void *thing, uint32 kind, JSBool details)
- {
- const char *name;
- size_t n;
- if (bufsize == 0)
- return;
- switch (kind) {
- case JSTRACE_OBJECT:
- {
- JSObject *obj = (JSObject *)thing;
- JSClass *clasp = STOBJ_GET_CLASS(obj);
- name = clasp->name;
- #ifdef HAVE_XPCONNECT
- if (clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
- jsval privateValue = STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE);
- JS_ASSERT(clasp->flags & JSCLASS_HAS_PRIVATE);
- if (!JSVAL_IS_VOID(privateValue)) {
- void *privateThing = JSVAL_TO_PRIVATE(privateValue);
- const char *xpcClassName = GetXPCObjectClassName(privateThing);
- if (xpcClassName)
- name = xpcClassName;
- }
- }
- #endif
- break;
- }
- case JSTRACE_STRING:
- name = JSSTRING_IS_DEPENDENT((JSString *)thing)
- ? "substring"
- : "string";
- break;
- case JSTRACE_DOUBLE:
- name = "double";
- break;
- #if JS_HAS_XML_SUPPORT
- case JSTRACE_XML:
- name = "xml";
- break;
- #endif
- default:
- JS_ASSERT(0);
- return;
- break;
- }
- n = strlen(name);
- if (n > bufsize - 1)
- n = bufsize - 1;
- memcpy(buf, name, n + 1);
- buf += n;
- bufsize -= n;
- if (details && bufsize > 2) {
- *buf++ = ' ';
- bufsize--;
- switch (kind) {
- case JSTRACE_OBJECT:
- {
- JSObject *obj = (JSObject *)thing;
- JSClass *clasp = STOBJ_GET_CLASS(obj);
- if (clasp == &js_FunctionClass) {
- JSFunction *fun = (JSFunction *)
- JS_GetPrivate(trc->context, obj);
- if (!fun) {
- JS_snprintf(buf, bufsize, "<newborn>");
- } else if (FUN_OBJECT(fun) != obj) {
- JS_snprintf(buf, bufsize, "%p", fun);
- } else {
- if (fun->atom && ATOM_IS_STRING(fun->atom))
- js_PutEscapedString(buf, bufsize,
- ATOM_TO_STRING(fun->atom), 0);
- }
- } else if (clasp->flags & JSCLASS_HAS_PRIVATE) {
- jsval privateValue = STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE);
- void *privateThing = JSVAL_IS_VOID(privateValue)
- ? NULL
- : JSVAL_TO_PRIVATE(privateValue);
- JS_snprintf(buf, bufsize, "%p", privateThing);
- } else {
- JS_snprintf(buf, bufsize, "<no private>");
- }
- break;
- }
- case JSTRACE_STRING:
- js_PutEscapedString(buf, bufsize, (JSString *)thing, 0);
- break;
- case JSTRACE_DOUBLE:
- JS_snprintf(buf, bufsize, "%g", *(jsdouble *)thing);
- break;
- #if JS_HAS_XML_SUPPORT
- case JSTRACE_XML:
- {
- extern const char *js_xml_class_str[];
- JSXML *xml = (JSXML *)thing;
- JS_snprintf(buf, bufsize, "%s", js_xml_class_str[xml->xml_class]);
- break;
- }
- #endif
- default:
- JS_ASSERT(0);
- break;
- }
- }
- buf[bufsize - 1] = '\0';
- }
- typedef struct JSHeapDumpNode JSHeapDumpNode;
- struct JSHeapDumpNode {
- void *thing;
- uint32 kind;
- JSHeapDumpNode *next; /* next sibling */
- JSHeapDumpNode *parent; /* node with the thing that refer to thing
- from this node */
- char edgeName[1]; /* name of the edge from parent->thing
- into thing */
- };
- typedef struct JSDumpingTracer {
- JSTracer base;
- JSDHashTable visited;
- JSBool ok;
- void *startThing;
- void *thingToFind;
- void *thingToIgnore;
- JSHeapDumpNode *parentNode;
- JSHeapDumpNode **lastNodep;
- char buffer[200];
- } JSDumpingTracer;
- static void
- DumpNotify(JSTracer *trc, void *thing, uint32 kind)
- {
- JSDumpingTracer *dtrc;
- JSContext *cx;
- JSDHashEntryStub *entry;
- JSHeapDumpNode *node;
- const char *edgeName;
- size_t edgeNameSize;
- JS_ASSERT(trc->callback == DumpNotify);
- dtrc = (JSDumpingTracer *)trc;
- if (!dtrc->ok || thing == dtrc->thingToIgnore)
- return;
- cx = trc->context;
- /*
- * Check if we have already seen thing unless it is thingToFind to include
- * it to the graph each time we reach it and print all live things that
- * refer to thingToFind.
- *
- * This does not print all possible paths leading to thingToFind since
- * when a thing A refers directly or indirectly to thingToFind and A is
- * present several times in the graph, we will print only the first path
- * leading to A and thingToFind, other ways to reach A will be ignored.
- */
- if (dtrc->thingToFind != thing) {
- /*
- * The startThing check allows to avoid putting startThing into the
- * hash table before tracing startThing in JS_DumpHeap.
- */
- if (thing == dtrc->startThing)
- return;
- entry = (JSDHashEntryStub *)
- JS_DHashTableOperate(&dtrc->visited, thing, JS_DHASH_ADD);
- if (!entry) {
- JS_ReportOutOfMemory(cx);
- dtrc->ok = JS_FALSE;
- return;
- }
- if (entry->key)
- return;
- entry->key = thing;
- }
- if (dtrc->base.debugPrinter) {
- dtrc->base.debugPrinter(trc, dtrc->buffer, sizeof(dtrc->buffer));
- edgeName = dtrc->buffer;
- } else if (dtrc->base.debugPrintIndex != (size_t)-1) {
- JS_snprintf(dtrc->buffer, sizeof(dtrc->buffer), "%s[%lu]",
- (const char *)dtrc->base.debugPrintArg,
- dtrc->base.debugPrintIndex);
- edgeName = dtrc->buffer;
- } else {
- edgeName = (const char*)dtrc->base.debugPrintArg;
- }
- edgeNameSize = strlen(edgeName) + 1;
- node = (JSHeapDumpNode *)
- JS_malloc(cx, offsetof(JSHeapDumpNode, edgeName) + edgeNameSize);
- if (!node) {
- dtrc->ok = JS_FALSE;
- return;
- }
- node->thing = thing;
- node->kind = kind;
- node->next = NULL;
- node->parent = dtrc->parentNode;
- memcpy(node->edgeName, edgeName, edgeNameSize);
- JS_ASSERT(!*dtrc->lastNodep);
- *dtrc->lastNodep = node;
- dtrc->lastNodep = &node->next;
- }
- /* Dump node and the chain that leads to thing it contains. */
- static JSBool
- DumpNode(JSDumpingTracer *dtrc, FILE* fp, JSHeapDumpNode *node)
- {
- JSHeapDumpNode *prev, *following;
- size_t chainLimit;
- JSBool ok;
- enum { MAX_PARENTS_TO_PRINT = 10 };
- JS_PrintTraceThingInfo(dtrc->buffer, sizeof dtrc->buffer,
- &dtrc->base, node->thing, node->kind, JS_TRUE);
- if (fprintf(fp, "%p %-22s via ", node->thing, dtrc->buffer) < 0)
- return JS_FALSE;
- /*
- * We need to print the parent chain in the reverse order. To do it in
- * O(N) time where N is the chain length we first reverse the cha