Index: ossp-pkg/js/Makefile.in RCS File: /v/ossp/cvs/ossp-pkg/js/Makefile.in,v rcsdiff -q -kk '-r1.15' '-r1.16' -u '/v/ossp/cvs/ossp-pkg/js/Makefile.in,v' 2>/dev/null --- Makefile.in 2006/07/22 20:04:20 1.15 +++ Makefile.in 2006/07/22 20:31:56 1.16 @@ -203,7 +203,7 @@ $(LIB_NAME): $(LIB_OBJS) @$(LIBTOOL) --mode=link $(CC) -o $(LIB_NAME) $(LIB_OBJS) -rpath $(libdir) \ - -version-info `$(SHTOOL) version -l txt -d libtool VERSION` + -version-info 0:0 $(CLI_NAME): $(CLI_OBJS) @$(LIBTOOL) --mode=link $(CC) $(LDFLAGS) $(CLI_LDFLAGS) -o $(CLI_NAME) $(CLI_OBJS) $(LIB_NAME) $(LIBS) $(CLI_LIBS) Index: ossp-pkg/js/src/jsapi.c RCS File: /v/ossp/cvs/ossp-pkg/js/src/jsapi.c,v co -q -kk -p'1.2' '/v/ossp/cvs/ossp-pkg/js/src/jsapi.c,v' | diff -u /dev/null - -L'ossp-pkg/js/src/jsapi.c' 2>/dev/null --- ossp-pkg/js/src/jsapi.c +++ - 2024-05-12 07:43:25.689571712 +0200 @@ -0,0 +1,4823 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=80: + * + * ***** 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 +#include +#include +#include +#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 "jscntxt.h" +#include "jsconfig.h" +#include "jsdate.h" +#include "jsdtoa.h" +#include "jsemit.h" +#include "jsexn.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsmath.h" +#include "jsnum.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" + +#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_PARANOID_REQUEST) && defined(JS_THREADSAFE) +#define CHECK_REQUEST(cx) JS_ASSERT(cx->requestDepth) +#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, +#ifdef OSSP + jsval **vpp, va_list app) +#else + jsval **vpp, va_list *app) +#endif +{ + 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': + if (!js_ValueToBoolean(cx, *sp, va_arg(ap, JSBool *))) + return JS_FALSE; + 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') + *va_arg(ap, char **) = JS_GetStringBytes(str); + else if (c == 'W') + *va_arg(ap, jschar **) = JS_GetStringChars(str); + 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; + *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, +#ifdef OSSP + ap)) { +#else + JS_ADDRESSOF_VA_LIST(ap))) { +#endif + 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': + 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) : JSVAL_NULL; + break; + case 'v': + *sp = va_arg(ap, jsval); + break; + default: + format--; + if (!TryArgumentFormatter(cx, &format, JS_FALSE, &sp, +#ifdef OSSP + ap)) { +#else + JS_ADDRESSOF_VA_LIST(ap))) { +#endif + 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, b; + 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_NewDouble(cx, d, 0); + ok = (dp != NULL); + if (ok) + *vp = DOUBLE_TO_JSVAL(dp); + } + break; + case JSTYPE_BOOLEAN: + ok = js_ValueToBoolean(cx, v, &b); + if (ok) + *vp = BOOLEAN_TO_JSVAL(b); + break; + 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(JSBool) +JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) +{ + CHECK_REQUEST(cx); + return js_ValueToNumber(cx, v, dp); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) +{ + CHECK_REQUEST(cx); + return js_ValueToECMAInt32(cx, v, ip); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) +{ + CHECK_REQUEST(cx); + return js_ValueToECMAUint32(cx, v, ip); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip) +{ + CHECK_REQUEST(cx); + return js_ValueToInt32(cx, v, ip); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip) +{ + CHECK_REQUEST(cx); + return js_ValueToUint16(cx, v, ip); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) +{ + CHECK_REQUEST(cx); + return js_ValueToBoolean(cx, v, bp); +} + +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) { + 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_RegExpClass || clasp == &js_ScriptClass) + : clasp == &js_FunctionClass) + : ops->call != NULL) { + type = JSTYPE_FUNCTION; + } else { +#ifdef NARCISSUS + if (!OBJ_GET_PROPERTY(cx, obj, + ATOM_TO_JSID(cx->runtime->atomState + .callAtom), + &v)) { + JS_ClearPendingException(cx); + } else if (JSVAL_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]; +} + +/************************************************************************/ + +JS_PUBLIC_API(JSRuntime *) +JS_NewRuntime(uint32 maxbytes) +{ + JSRuntime *rt; + +#ifdef DEBUG + JS_BEGIN_MACRO + /* + * 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 compiletime 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 + JS_END_MACRO; +#endif /* DEBUG */ + + if (!js_InitStringGlobals()) + return NULL; + 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_InitGC(rt, maxbytes)) + goto bad; +#ifdef JS_THREADSAFE + 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->setSlotLock = JS_NEW_LOCK(); + if (!rt->setSlotLock) + goto bad; + rt->setSlotDone = JS_NEW_CONDVAR(rt->setSlotLock); + if (!rt->setSlotDone) + goto bad; + rt->scopeSharingDone = JS_NEW_CONDVAR(rt->gcLock); + if (!rt->scopeSharingDone) + goto bad; + rt->scopeSharingTodo = NO_SCOPE_SHARING_TODO; +#endif + rt->propertyCache.empty = JS_TRUE; + if (!js_InitPropertyTree(rt)) + goto bad; + 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) + cxcount++; + fprintf(stderr, +"JS API usage error: %u contexts left in runtime upon JS_DestroyRuntime.\n", + cxcount); + } +#endif + + js_FreeRuntimeScriptState(rt); + js_FinishAtomState(&rt->atomState); + 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->setSlotLock) + JS_DESTROY_LOCK(rt->setSlotLock); + if (rt->setSlotDone) + JS_DESTROY_CONDVAR(rt->setSlotDone); + if (rt->scopeSharingDone) + JS_DESTROY_CONDVAR(rt->scopeSharingDone); +#endif + js_FinishPropertyTree(rt); + free(rt); +} + +JS_PUBLIC_API(void) +JS_ShutDown(void) +{ + JS_ArenaShutDown(); + js_FinishDtoa(); + js_FreeStringGlobals(); +#ifdef JS_THREADSAFE + js_CleanupLocks(); +#endif +} + +JS_PUBLIC_API(void *) +JS_GetRuntimePrivate(JSRuntime *rt) +{ + return rt->data; +} + +JS_PUBLIC_API(void) +JS_SetRuntimePrivate(JSRuntime *rt, void *data) +{ + rt->data = data; +} + +#ifdef JS_THREADSAFE + +JS_PUBLIC_API(void) +JS_BeginRequest(JSContext *cx) +{ + JSRuntime *rt; + + JS_ASSERT(cx->thread); + if (!cx->requestDepth) { + /* Wait until the GC is finished. */ + rt = cx->runtime; + JS_LOCK_GC(rt); + + /* NB: we use cx->thread here, not js_CurrentThreadId(). */ + 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; + JS_UNLOCK_GC(rt); + return; + } + cx->requestDepth++; +} + +JS_PUBLIC_API(void) +JS_EndRequest(JSContext *cx) +{ + JSRuntime *rt; + JSScope *scope, **todop; + uintN nshares; + + CHECK_REQUEST(cx); + JS_ASSERT(cx->requestDepth > 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; + + /* See whether cx has any single-threaded scopes to start sharing. */ + todop = &rt->scopeSharingTodo; + nshares = 0; + while ((scope = *todop) != NO_SCOPE_SHARING_TODO) { + if (scope->ownercx != cx) { + todop = &scope->u.link; + continue; + } + *todop = scope->u.link; + scope->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 a branch callback) during + * this request, but possible. + */ + if (js_DropObjectMap(cx, &scope->map, NULL)) { + js_InitLock(&scope->lock); + scope->u.count = 0; /* NULL may not pun as 0 */ + js_FinishSharingScope(rt, scope); /* set ownercx = NULL */ + nshares++; + } + } + if (nshares) + JS_NOTIFY_ALL_CONDVAR(rt->scopeSharingDone); + + /* 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--; +} + +/* Yield to pending GC operations, regardless of request depth */ +JS_PUBLIC_API(void) +JS_YieldRequest(JSContext *cx) +{ + JSRuntime *rt; + + JS_ASSERT(cx->thread); + CHECK_REQUEST(cx); + + rt = cx->runtime; + JS_LOCK_GC(rt); + JS_ASSERT(rt->requestCount > 0); + rt->requestCount--; + if (rt->requestCount == 0) + JS_NOTIFY_REQUEST_DONE(rt); + JS_UNLOCK_GC(rt); + /* XXXbe give the GC or another request calling it a chance to run here? + Assumes FIFO scheduling */ + JS_LOCK_GC(rt); + rt->requestCount++; + JS_UNLOCK_GC(rt); +} + +JS_PUBLIC_API(jsrefcount) +JS_SuspendRequest(JSContext *cx) +{ + jsrefcount saveDepth = cx->requestDepth; + + while (cx->requestDepth) + JS_EndRequest(cx); + return saveDepth; +} + +JS_PUBLIC_API(void) +JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth) +{ + JS_ASSERT(!cx->requestDepth); + while (--saveDepth >= 0) + JS_BeginRequest(cx); +} + +#endif /* JS_THREADSAFE */ + +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(JSContext *) +JS_NewContext(JSRuntime *rt, size_t stackChunkSize) +{ + return js_NewContext(rt, stackChunkSize); +} + +JS_PUBLIC_API(void) +JS_DestroyContext(JSContext *cx) +{ + js_DestroyContext(cx, JS_FORCE_GC); +} + +JS_PUBLIC_API(void) +JS_DestroyContextNoGC(JSContext *cx) +{ + js_DestroyContext(cx, JS_NO_GC); +} + +JS_PUBLIC_API(void) +JS_DestroyContextMaybeGC(JSContext *cx) +{ + js_DestroyContext(cx, JS_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 cx->version & JSVERSION_MASK; +} + +JS_PUBLIC_API(JSVersion) +JS_SetVersion(JSContext *cx, JSVersion version) +{ + JSVersion oldVersion; + + JS_ASSERT(version != JSVERSION_UNKNOWN); + JS_ASSERT((version & ~JSVERSION_MASK) == 0); + + oldVersion = cx->version & JSVERSION_MASK; + if (version == oldVersion) + 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_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.6 pre-release 1 2006-04-04"; +} + + +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 +} + +static JSObject * +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); + if (resolving) { + rt = cx->runtime; + key.obj = obj; + key.id = ATOM_TO_JSID(rt->atomState.FunctionAtom); + 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.ObjectAtom); + 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; + } + + /* Initialize the function class first so constructors can be made. */ + fun_proto = js_InitFunctionClass(cx, obj); + if (!fun_proto) + goto out; + + /* Initialize the object class next so Object.prototype works. */ + 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. */ + if (resolving) + JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); + return fun_proto; +} + +JS_PUBLIC_API(JSBool) +JS_InitStandardClasses(JSContext *cx, JSObject *obj) +{ + CHECK_REQUEST(cx); + +#if JS_HAS_UNDEFINED +{ + /* Define a top-level property 'undefined' with the undefined value. */ + JSAtom *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; + } +} +#endif + + /* Function and Object require cooperative bootstrapping magic. */ + if (!InitFunctionAndObjectClasses(cx, obj)) + return JS_FALSE; + + /* Initialize the rest of the standard objects and functions. */ + return js_InitArrayClass(cx, obj) && + js_InitBooleanClass(cx, obj) && + js_InitMathClass(cx, obj) && + js_InitNumberClass(cx, obj) && + js_InitStringClass(cx, obj) && +#if JS_HAS_CALL_OBJECT + js_InitCallClass(cx, obj) && +#endif +#if JS_HAS_REGEXPS + js_InitRegExpClass(cx, obj) && +#endif +#if JS_HAS_SCRIPT_OBJECT + js_InitScriptClass(cx, obj) && +#endif +#if JS_HAS_ERROR_EXCEPTIONS + js_InitExceptionClasses(cx, obj) && +#endif +#if JS_HAS_XML_SUPPORT + js_InitXMLClasses(cx, obj) && +#endif +#if JS_HAS_FILE_OBJECT + js_InitFileClass(cx, obj) && +#endif + js_InitDateClass(cx, obj); +} + +#define ATOM_OFFSET(name) offsetof(JSAtomState, name##Atom) +#define OFFSET_TO_ATOM(rt,off) (*(JSAtom **)((char*)&(rt)->atomState + (off))) + +/* + * Table of class initializers and their atom offsets in rt->atomState. + * If you add a "standard" class, remember to update this table. + */ +static struct { + JSObjectOp init; + size_t atomOffset; +} standard_class_atoms[] = { + {InitFunctionAndObjectClasses, ATOM_OFFSET(Function)}, + {InitFunctionAndObjectClasses, ATOM_OFFSET(Object)}, + {js_InitArrayClass, ATOM_OFFSET(Array)}, + {js_InitBooleanClass, ATOM_OFFSET(Boolean)}, + {js_InitDateClass, ATOM_OFFSET(Date)}, + {js_InitMathClass, ATOM_OFFSET(Math)}, + {js_InitNumberClass, ATOM_OFFSET(Number)}, + {js_InitStringClass, ATOM_OFFSET(String)}, +#if JS_HAS_CALL_OBJECT + {js_InitCallClass, ATOM_OFFSET(Call)}, +#endif +#if JS_HAS_ERROR_EXCEPTIONS + {js_InitExceptionClasses, ATOM_OFFSET(Error)}, +#endif +#if JS_HAS_REGEXPS + {js_InitRegExpClass, ATOM_OFFSET(RegExp)}, +#endif +#if JS_HAS_SCRIPT_OBJECT + {js_InitScriptClass, ATOM_OFFSET(Script)}, +#endif +#if JS_HAS_XML_SUPPORT + {js_InitXMLClass, ATOM_OFFSET(XML)}, + {js_InitNamespaceClass, ATOM_OFFSET(Namespace)}, + {js_InitQNameClass, ATOM_OFFSET(QName)}, +#endif +#if JS_HAS_FILE_OBJECT + {js_InitFileClass, ATOM_OFFSET(File)}, +#endif + {NULL, 0} +}; + +/* + * 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. + */ +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 */ +} 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; +} + +#define EAGERLY_PINNED_ATOM(name) ATOM_OFFSET(name), NULL +#define LAZILY_PINNED_ATOM(name) ATOM_OFFSET(lazy.name), js_##name##_str + +static JSStdName standard_class_names[] = { + /* ECMA requires that eval be a direct property of the global object. */ + {js_InitObjectClass, EAGERLY_PINNED_ATOM(eval)}, + + /* Global properties and functions defined by the Number class. */ + {js_InitNumberClass, LAZILY_PINNED_ATOM(NaN)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(Infinity)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(isNaN)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(isFinite)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(parseFloat)}, + {js_InitNumberClass, LAZILY_PINNED_ATOM(parseInt)}, + + /* String global functions. */ + {js_InitStringClass, LAZILY_PINNED_ATOM(escape)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(unescape)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(decodeURI)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(encodeURI)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(decodeURIComponent)}, + {js_InitStringClass, LAZILY_PINNED_ATOM(encodeURIComponent)}, +#if JS_HAS_UNEVAL + {js_InitStringClass, LAZILY_PINNED_ATOM(uneval)}, +#endif + + /* Exception constructors. */ +#if JS_HAS_ERROR_EXCEPTIONS + {js_InitExceptionClasses, EAGERLY_PINNED_ATOM(Error)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(InternalError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(EvalError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(RangeError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(ReferenceError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(SyntaxError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(TypeError)}, + {js_InitExceptionClasses, LAZILY_PINNED_ATOM(URIError)}, +#endif + +#if JS_HAS_XML_SUPPORT + {js_InitAnyNameClass, LAZILY_PINNED_ATOM(AnyName)}, + {js_InitAttributeNameClass, LAZILY_PINNED_ATOM(AttributeName)}, + {js_InitXMLClass, LAZILY_PINNED_ATOM(XMLList)}, + {js_InitXMLClass, LAZILY_PINNED_ATOM(isXMLName)}, +#endif + + {NULL, 0, NULL} +}; + +static JSStdName object_prototype_names[] = { + /* Object.prototype properties (global delegates to Object.prototype). */ + {js_InitObjectClass, EAGERLY_PINNED_ATOM(proto)}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(parent)}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(count)}, +#if JS_HAS_TOSOURCE + {js_InitObjectClass, EAGERLY_PINNED_ATOM(toSource)}, +#endif + {js_InitObjectClass, EAGERLY_PINNED_ATOM(toString)}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(toLocaleString)}, + {js_InitObjectClass, EAGERLY_PINNED_ATOM(valueOf)}, +#if JS_HAS_OBJ_WATCHPOINT + {js_InitObjectClass, LAZILY_PINNED_ATOM(watch)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(unwatch)}, +#endif +#if JS_HAS_NEW_OBJ_METHODS + {js_InitObjectClass, LAZILY_PINNED_ATOM(hasOwnProperty)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(isPrototypeOf)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(propertyIsEnumerable)}, +#endif +#if JS_HAS_GETTER_SETTER + {js_InitObjectClass, LAZILY_PINNED_ATOM(defineGetter)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(defineSetter)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(lookupGetter)}, + {js_InitObjectClass, LAZILY_PINNED_ATOM(lookupSetter)}, +#endif + + {NULL, 0, NULL} +}; + +#undef EAGERLY_PINNED_ATOM +#undef LAZILY_PINNED_ATOM + +JS_PUBLIC_API(JSBool) +JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, + JSBool *resolved) +{ + JSString *idstr; + JSRuntime *rt; + JSAtom *atom; + JSObjectOp init; + uintN i; + + CHECK_REQUEST(cx); + *resolved = JS_FALSE; + + if (!JSVAL_IS_STRING(id)) + return JS_TRUE; + idstr = JSVAL_TO_STRING(id); + rt = cx->runtime; + +#if JS_HAS_UNDEFINED + /* 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); + } +#endif + + /* Try for class constructors/prototypes named by well-known atoms. */ + init = 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)) { + init = standard_class_atoms[i].init; + break; + } + } + + if (!init) { + /* 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)) { + init = standard_class_names[i].init; + break; + } + } + + if (!init && !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)) { + init = standard_class_names[i].init; + break; + } + } + } + } + + if (init) { + if (!init(cx, obj)) + return JS_FALSE; + *resolved = JS_TRUE; + } + return JS_TRUE; +} + +static JSBool +AlreadyHasOwnProperty(JSObject *obj, JSAtom *atom) +{ + JS_ASSERT(OBJ_IS_NATIVE(obj)); + return SCOPE_GET_PROPERTY(OBJ_SCOPE(obj), ATOM_TO_JSID(atom)) != NULL; +} + +JS_PUBLIC_API(JSBool) +JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj) +{ + JSRuntime *rt; + JSAtom *atom; + uintN i; + + CHECK_REQUEST(cx); + rt = cx->runtime; + +#if JS_HAS_UNDEFINED + /* Check whether we need to bind 'undefined' and define it if so. */ + atom = rt->atomState.typeAtoms[JSTYPE_VOID]; + if (!AlreadyHasOwnProperty(obj, atom) && + !OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, + NULL, NULL, JSPROP_PERMANENT, NULL)) { + return JS_FALSE; + } +#endif + + /* 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(obj, atom) && + !standard_class_atoms[i].init(cx, obj)) { + return JS_FALSE; + } + } + + return JS_TRUE; +} + +static JSIdArray * +AddAtomToArray(JSContext *cx, JSAtom *atom, JSIdArray *ida, jsint *ip) +{ + jsint i, length; + + i = *ip; + length = ida->length; + if (i >= length) { + ida = js_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(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 = js_NewIdArray(cx, 8); + if (!ida) + return NULL; + i = 0; + } + +#if JS_HAS_UNDEFINED + /* 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; +#endif + + /* 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 via js_SetIdArrayLength. */ + return js_SetIdArrayLength(cx, ida, i); +} + +#undef ATOM_OFFSET +#undef OFFSET_TO_ATOM + +JS_PUBLIC_API(JSObject *) +JS_GetScopeChain(JSContext *cx) +{ + return cx->fp ? cx->fp->scopeChain : NULL; +} + +JS_PUBLIC_API(void *) +JS_malloc(JSContext *cx, size_t nbytes) +{ + void *p; + + JS_ASSERT(nbytes != 0); + if (nbytes == 0) + nbytes = 1; + cx->runtime->gcMallocBytes += nbytes; + p = malloc(nbytes); + if (!p) + JS_ReportOutOfMemory(cx); + return p; +} + +JS_PUBLIC_API(void *) +JS_realloc(JSContext *cx, void *p, size_t nbytes) +{ + 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_NewDouble(cx, d, 0); +} + +JS_PUBLIC_API(JSBool) +JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval) +{ + CHECK_REQUEST(cx); + return js_NewDoubleValue(cx, d, rval); +} + +JS_PUBLIC_API(JSBool) +JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) +{ + CHECK_REQUEST(cx); + return js_NewNumberValue(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) +{ + uintN i; + + for (i = 0; i < GCX_NTYPES; i++) + cx->newborn[i] = NULL; + cx->lastAtom = NULL; + cx->lastInternalResult = JSVAL_NULL; +} + +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_ForgetLocalRoot(JSContext *cx, void *thing) +{ + CHECK_REQUEST(cx); + js_ForgetLocalRoot(cx, (jsval) thing); +} + +#include "jshash.h" /* Added by JSIFY */ + +#ifdef DEBUG + +typedef struct NamedRootDumpArgs { + void (*dump)(const char *name, void *rp, void *data); + void *data; +} NamedRootDumpArgs; + +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +js_named_root_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, + void *arg) +{ + NamedRootDumpArgs *args = (NamedRootDumpArgs *) arg; + JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; + + if (rhe->name) + args->dump(rhe->name, rhe->root, args->data); + return JS_DHASH_NEXT; +} + +JS_PUBLIC_API(void) +JS_DumpNamedRoots(JSRuntime *rt, + void (*dump)(const char *name, void *rp, void *data), + void *data) +{ + NamedRootDumpArgs args; + + args.dump = dump; + args.data = data; + JS_DHashTableEnumerate(&rt->gcRootsHash, js_named_root_dumper, &args); +} + +#endif /* DEBUG */ + +typedef struct GCRootMapArgs { + JSGCRootMapFun map; + void *data; +} GCRootMapArgs; + +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +js_gcroot_mapper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, + void *arg) +{ + GCRootMapArgs *args = (GCRootMapArgs *) arg; + JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; + intN mapflags; + JSDHashOperator op; + + mapflags = args->map(rhe->root, rhe->name, args->data); + +#if JS_MAP_GCROOT_NEXT == JS_DHASH_NEXT && \ + JS_MAP_GCROOT_STOP == JS_DHASH_STOP && \ + JS_MAP_GCROOT_REMOVE == JS_DHASH_REMOVE + op = (JSDHashOperator)mapflags; +#else + op = JS_DHASH_NEXT; + if (mapflags & JS_MAP_GCROOT_STOP) + op |= JS_DHASH_STOP; + if (mapflags & JS_MAP_GCROOT_REMOVE) + op |= JS_DHASH_REMOVE; +#endif + + return op; +} + +JS_PUBLIC_API(uint32) +JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) +{ + GCRootMapArgs args; + uint32 rv; + + args.map = map; + args.data = data; + JS_LOCK_GC(rt); + rv = JS_DHashTableEnumerate(&rt->gcRootsHash, js_gcroot_mapper, &args); + JS_UNLOCK_GC(rt); + return rv; +} + +JS_PUBLIC_API(JSBool) +JS_LockGCThing(JSContext *cx, void *thing) +{ + JSBool ok; + + CHECK_REQUEST(cx); + ok = js_LockGCThing(cx, thing); + if (!ok) + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_LOCK); + 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_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg) +{ + JS_ASSERT(cx->runtime->gcLevel > 0); +#ifdef JS_THREADSAFE + JS_ASSERT(cx->runtime->gcThread == js_CurrentThreadId()); +#endif + + GC_MARK(cx, thing, name, arg); +} + +JS_PUBLIC_API(void) +JS_GC(JSContext *cx) +{ + /* Don't nuke active arenas if executing or compiling. */ + if (cx->stackPool.current == &cx->stackPool.first) + JS_FinishArenaPool(&cx->stackPool); + if (cx->tempPool.current == &cx->tempPool.first) + JS_FinishArenaPool(&cx->tempPool); + js_ForceGC(cx, 0); +} + +JS_PUBLIC_API(void) +JS_MaybeGC(JSContext *cx) +{ +#ifdef WAY_TOO_MUCH_GC + JS_GC(cx); +#else + JSRuntime *rt; + uint32 bytes, lastBytes; + + rt = cx->runtime; + bytes = rt->gcBytes; + lastBytes = rt->gcLastBytes; + if ((bytes > 8192 && bytes > lastBytes + lastBytes / 2) || + rt->gcMallocBytes > rt->gcMaxMallocBytes) { + /* + * Run the GC if we have half again as many bytes of GC-things as + * the last time we GC'd, or if we have malloc'd more bytes through + * JS_malloc than we were told to allocate by JS_NewRuntime. + */ + JS_GC(cx); + } +#endif +} + +JS_PUBLIC_API(JSGCCallback) +JS_SetGCCallback(JSContext *cx, JSGCCallback cb) +{ + return JS_SetGCCallbackRT(cx->runtime, cb); +} + +JS_PUBLIC_API(JSGCCallback) +JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb) +{ + JSGCCallback oldcb; + + oldcb = rt->gcCallback; + rt->gcCallback = cb; + return oldcb; +} + +JS_PUBLIC_API(JSBool) +JS_IsAboutToBeFinalized(JSContext *cx, void *thing) +{ + JS_ASSERT(thing); + return js_IsAboutToBeFinalized(cx, thing); +} + +JS_PUBLIC_API(void) +JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value) +{ + switch (key) { + case JSGC_MAX_BYTES: + rt->gcMaxBytes = value; + break; + case JSGC_MAX_MALLOC_BYTES: + rt->gcMaxMallocBytes = value; + break; + } +} + +JS_PUBLIC_API(intN) +JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer) +{ + return js_ChangeExternalStringFinalizer(NULL, finalizer); +} + +JS_PUBLIC_API(intN) +JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer) +{ + return js_ChangeExternalStringFinalizer(finalizer, NULL); +} + +JS_PUBLIC_API(JSString *) +JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type) +{ + JSString *str; + + CHECK_REQUEST(cx); + JS_ASSERT(GCX_EXTERNAL_STRING <= type && type < (intN) GCX_NTYPES); + + str = (JSString *) js_NewGCThing(cx, (uintN) type, sizeof(JSString)); + if (!str) + return NULL; + str->length = length; + str->chars = chars; + return str; +} + +JS_PUBLIC_API(intN) +JS_GetExternalStringGCType(JSRuntime *rt, JSString *str) +{ + uint8 type = (uint8) (*js_GetGCThingFlags(str) & GCF_TYPEMASK); + + if (type >= GCX_EXTERNAL_STRING) + return (intN)type; + JS_ASSERT(type == GCX_STRING || type == GCX_MUTABLE_STRING); + return -1; +} + +JS_PUBLIC_API(void) +JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr) +{ +#if JS_STACK_GROWTH_DIRECTION > 0 + if (limitAddr == 0) + limitAddr = (jsuword)-1; +#endif + cx->stackLimit = limitAddr; +} + +/************************************************************************/ + +JS_PUBLIC_API(void) +JS_DestroyIdArray(JSContext *cx, JSIdArray *ida) +{ + JS_free(cx, ida); +} + +JS_PUBLIC_API(JSBool) +JS_ValueToId(JSContext *cx, jsval v, jsid *idp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + if (JSVAL_IS_INT(v)) { + *idp = v; + } else { + atom = js_ValueToStringAtom(cx, v); + if (!atom) + return JS_FALSE; + *idp = ATOM_TO_JSID(atom); + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_IdToValue(JSContext *cx, jsid id, jsval *vp) +{ + CHECK_REQUEST(cx); + *vp = ID_TO_VALUE(id); + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_EnumerateStub(JSContext *cx, JSObject *obj) +{ + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id) +{ + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp) +{ +#if JS_BUG_EAGER_TOSTRING + if (type == JSTYPE_STRING) + return JS_TRUE; +#endif + js_TryValueOf(cx, obj, type, vp); + return JS_TRUE; +} + +JS_PUBLIC_API(void) +JS_FinalizeStub(JSContext *cx, JSObject *obj) +{ +} + +JS_PUBLIC_API(JSObject *) +JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, + JSClass *clasp, JSNative constructor, uintN nargs, + JSPropertySpec *ps, JSFunctionSpec *fs, + JSPropertySpec *static_ps, JSFunctionSpec *static_fs) +{ + JSAtom *atom; + JSObject *proto, *ctor; + JSTempValueRooter tvr; + jsval cval, rval; + JSBool named; + JSFunction *fun; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); + if (!atom) + return NULL; + + /* Create a prototype object for this class. */ + proto = js_NewObject(cx, clasp, parent_proto, obj); + if (!proto) + return NULL; + + /* After this point, control must exit via label bad or out. */ + JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(proto), &tvr); + + if (!constructor) { + /* Lacking a constructor, name the prototype (e.g., Math). */ + named = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), + OBJECT_TO_JSVAL(proto), + NULL, NULL, 0, NULL); + if (!named) + goto bad; + ctor = proto; + } else { + /* Define the constructor function in obj's scope. */ + fun = js_DefineFunction(cx, obj, atom, constructor, nargs, 0); + named = (fun != NULL); + if (!fun) + goto bad; + + /* + * Remember the class this function is a constructor for so that + * we know to create an object of this class when we call the + * constructor. + */ + fun->clasp = clasp; + + /* + * Optionally construct the prototype object, before the class has + * been fully initialized. Allow the ctor to replace proto with a + * different object, as is done for operator new -- and as at least + * XML support requires. + */ + ctor = fun->object; + if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) { + cval = OBJECT_TO_JSVAL(ctor); + if (!js_InternalConstruct(cx, proto, cval, 0, NULL, &rval)) + goto bad; + if (!JSVAL_IS_PRIMITIVE(rval) && JSVAL_TO_OBJECT(rval) != proto) + proto = JSVAL_TO_OBJECT(rval); + } + + /* Connect constructor and prototype by named properties. */ + if (!js_SetClassPrototype(cx, ctor, proto, + JSPROP_READONLY | JSPROP_PERMANENT)) { + goto bad; + } + + /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */ + if (OBJ_GET_CLASS(cx, ctor) == clasp) { + /* XXXMLM - this fails in framesets that are writing over + * themselves! + * JS_ASSERT(!OBJ_GET_PROTO(cx, ctor)); + */ + OBJ_SET_PROTO(cx, ctor, proto); + } + } + + /* Add properties and methods to the prototype and the constructor. */ + if ((ps && !JS_DefineProperties(cx, proto, ps)) || + (fs && !JS_DefineFunctions(cx, proto, fs)) || + (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) || + (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) { + goto bad; + } + +out: + JS_POP_TEMP_ROOT(cx, &tvr); + return proto; + +bad: + if (named) + (void) OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &rval); + proto = NULL; + goto out; +} + +#ifdef JS_THREADSAFE +JS_PUBLIC_API(JSClass *) +JS_GetClass(JSContext *cx, JSObject *obj) +{ + return (JSClass *) + JSVAL_TO_PRIVATE(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_CLASS)); +} +#else +JS_PUBLIC_API(JSClass *) +JS_GetClass(JSObject *obj) +{ + return LOCKED_OBJ_GET_CLASS(obj); +} +#endif + +JS_PUBLIC_API(JSBool) +JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) +{ + JSFunction *fun; + + CHECK_REQUEST(cx); + if (OBJ_GET_CLASS(cx, obj) == clasp) + return JS_TRUE; + if (argv) { + fun = js_ValueToFunction(cx, &argv[-2], 0); + if (fun) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, + clasp->name, JS_GetFunctionName(fun), + OBJ_GET_CLASS(cx, obj)->name); + } + } + return JS_FALSE; +} + +JS_PUBLIC_API(JSBool) +JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) +{ + return js_HasInstance(cx, obj, v, bp); +} + +JS_PUBLIC_API(void *) +JS_GetPrivate(JSContext *cx, JSObject *obj) +{ + jsval v; + + JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); + v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + if (!JSVAL_IS_INT(v)) + return NULL; + return JSVAL_TO_PRIVATE(v); +} + +JS_PUBLIC_API(JSBool) +JS_SetPrivate(JSContext *cx, JSObject *obj, void *data) +{ + JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); + OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(data)); + return JS_TRUE; +} + +JS_PUBLIC_API(void *) +JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, + jsval *argv) +{ + if (!JS_InstanceOf(cx, obj, clasp, argv)) + return NULL; + return JS_GetPrivate(cx, obj); +} + +JS_PUBLIC_API(JSObject *) +JS_GetPrototype(JSContext *cx, JSObject *obj) +{ + JSObject *proto; + + CHECK_REQUEST(cx); + proto = JSVAL_TO_OBJECT(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PROTO)); + + /* Beware ref to dead object (we may be called from obj's finalizer). */ + return proto && proto->map ? proto : NULL; +} + +JS_PUBLIC_API(JSBool) +JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto) +{ + CHECK_REQUEST(cx); + if (obj->map->ops->setProto) + return obj->map->ops->setProto(cx, obj, JSSLOT_PROTO, proto); + OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto)); + return JS_TRUE; +} + +JS_PUBLIC_API(JSObject *) +JS_GetParent(JSContext *cx, JSObject *obj) +{ + JSObject *parent; + + parent = JSVAL_TO_OBJECT(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PARENT)); + + /* Beware ref to dead object (we may be called from obj's finalizer). */ + return parent && parent->map ? parent : NULL; +} + +JS_PUBLIC_API(JSBool) +JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent) +{ + CHECK_REQUEST(cx); + if (obj->map->ops->setParent) + return obj->map->ops->setParent(cx, obj, JSSLOT_PARENT, parent); + OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent)); + return JS_TRUE; +} + +JS_PUBLIC_API(JSObject *) +JS_GetConstructor(JSContext *cx, JSObject *proto) +{ + jsval cval; + + CHECK_REQUEST(cx); + if (!OBJ_GET_PROPERTY(cx, proto, + ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), + &cval)) { + return NULL; + } + if (!JSVAL_IS_FUNCTION(cx, cval)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, + OBJ_GET_CLASS(cx, proto)->name); + return NULL; + } + return JSVAL_TO_OBJECT(cval); +} + +JS_PUBLIC_API(JSBool) +JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp) +{ + JS_ASSERT(((jsid)obj & JSID_TAGMASK) == 0); + *idp = OBJECT_TO_JSID(obj); + return JS_TRUE; +} + +JS_PUBLIC_API(JSObject *) +JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) +{ + CHECK_REQUEST(cx); + if (!clasp) + clasp = &js_ObjectClass; /* default class is Object */ + return js_NewObject(cx, clasp, proto, parent); +} + +JS_PUBLIC_API(JSBool) +JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep) +{ + JSScope *scope; + JSIdArray *ida; + uint32 nslots; + jsval v, *vp, *end; + + if (!OBJ_IS_NATIVE(obj)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_CANT_SEAL_OBJECT, + OBJ_GET_CLASS(cx, obj)->name); + return JS_FALSE; + } + + scope = OBJ_SCOPE(obj); + +#if defined JS_THREADSAFE && defined DEBUG + /* Insist on scope being used exclusively by cx's thread. */ + if (scope->ownercx != cx) { + JS_LOCK_OBJ(cx, obj); + JS_ASSERT(OBJ_SCOPE(obj) == scope); + JS_ASSERT(scope->ownercx == cx); + JS_UNLOCK_SCOPE(cx, scope); + } +#endif + + /* Nothing to do if obj's scope is already sealed. */ + if (SCOPE_IS_SEALED(scope)) + return JS_TRUE; + + /* XXX Enumerate lazy properties now, as they can't be added later. */ + ida = JS_Enumerate(cx, obj); + if (!ida) + return JS_FALSE; + JS_DestroyIdArray(cx, ida); + + /* Ensure that obj has its own, mutable scope, and seal that scope. */ + JS_LOCK_OBJ(cx, obj); + scope = js_GetMutableScope(cx, obj); + if (scope) + SCOPE_SET_SEALED(scope); + JS_UNLOCK_SCOPE(cx, scope); + if (!scope) + return JS_FALSE; + + /* If we are not sealing an entire object graph, we're done. */ + if (!deep) + return JS_TRUE; + + /* Walk obj->slots and if any value is a non-null object, seal it. */ + nslots = JS_MIN(scope->map.freeslot, scope->map.nslots); + for (vp = obj->slots, end = vp + nslots; vp < end; vp++) { + v = *vp; + if (JSVAL_IS_PRIMITIVE(v)) + continue; + if (!JS_SealObject(cx, JSVAL_TO_OBJECT(v), deep)) + return JS_FALSE; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSObject *) +JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent) +{ + CHECK_REQUEST(cx); + if (!clasp) + clasp = &js_ObjectClass; /* default class is Object */ + return js_ConstructObject(cx, clasp, proto, parent, 0, NULL); +} + +JS_PUBLIC_API(JSObject *) +JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto, + JSObject *parent, uintN argc, jsval *argv) +{ + CHECK_REQUEST(cx); + if (!clasp) + clasp = &js_ObjectClass; /* default class is Object */ + return js_ConstructObject(cx, clasp, proto, parent, argc, argv); +} + +static JSBool +DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + uintN flags, intN tinyid) +{ + jsid id; + JSAtom *atom; + + if (attrs & JSPROP_INDEX) { + id = INT_TO_JSID(JS_PTR_TO_INT32(name)); + atom = NULL; + attrs &= ~JSPROP_INDEX; + } else { + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + id = ATOM_TO_JSID(atom); + } + if (flags != 0 && OBJ_IS_NATIVE(obj)) { + return js_DefineNativeProperty(cx, obj, id, value, getter, setter, + attrs, flags, tinyid, NULL); + } + return OBJ_DEFINE_PROPERTY(cx, obj, id, value, getter, setter, attrs, + NULL); +} + +#define AUTO_NAMELEN(s,n) (((n) == (size_t)-1) ? js_strlen(s) : (n)) + +static JSBool +DefineUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs, + uintN flags, intN tinyid) +{ + JSAtom *atom; + + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return JS_FALSE; + if (flags != 0 && OBJ_IS_NATIVE(obj)) { + return js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, + getter, setter, attrs, flags, tinyid, + NULL); + } + return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), value, + getter, setter, attrs, NULL); +} + +JS_PUBLIC_API(JSObject *) +JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, + JSObject *proto, uintN attrs) +{ + JSObject *nobj; + + CHECK_REQUEST(cx); + if (!clasp) + clasp = &js_ObjectClass; /* default class is Object */ + nobj = js_NewObject(cx, clasp, proto, obj); + if (!nobj) + return NULL; + if (!DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, attrs, + 0, 0)) { + cx->newborn[GCX_OBJECT] = NULL; + return NULL; + } + return nobj; +} + +JS_PUBLIC_API(JSBool) +JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds) +{ + JSBool ok; + jsval value; + uintN flags; + + CHECK_REQUEST(cx); + for (ok = JS_TRUE; cds->name; cds++) { + ok = js_NewNumberValue(cx, cds->dval, &value); + if (!ok) + break; + flags = cds->flags; + if (!flags) + flags = JSPROP_READONLY | JSPROP_PERMANENT; + ok = DefineProperty(cx, obj, cds->name, value, NULL, NULL, flags, 0, 0); + if (!ok) + break; + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps) +{ + JSBool ok; + + CHECK_REQUEST(cx); + for (ok = JS_TRUE; ps->name; ps++) { + ok = DefineProperty(cx, obj, ps->name, JSVAL_VOID, + ps->getter, ps->setter, ps->flags, + SPROP_HAS_SHORTID, ps->tinyid); + if (!ok) + break; + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs) +{ + CHECK_REQUEST(cx); + return DefineProperty(cx, obj, name, value, getter, setter, attrs, 0, 0); +} + +JS_PUBLIC_API(JSBool) +JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, + int8 tinyid, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs) +{ + CHECK_REQUEST(cx); + return DefineProperty(cx, obj, name, value, getter, setter, attrs, + SPROP_HAS_SHORTID, tinyid); +} + +static JSBool +LookupProperty(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, + JSProperty **propp) +{ + JSAtom *atom; + + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + return OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), objp, propp); +} + +static JSBool +LookupUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + JSObject **objp, JSProperty **propp) +{ + JSAtom *atom; + + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return JS_FALSE; + return OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), objp, propp); +} + +JS_PUBLIC_API(JSBool) +JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, + const char *alias) +{ + JSObject *obj2; + JSProperty *prop; + JSAtom *atom; + JSBool ok; + JSScopeProperty *sprop; + + CHECK_REQUEST(cx); + if (!LookupProperty(cx, obj, name, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + js_ReportIsNotDefined(cx, name); + return JS_FALSE; + } + if (obj2 != obj || !OBJ_IS_NATIVE(obj)) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS, + alias, name, OBJ_GET_CLASS(cx, obj2)->name); + return JS_FALSE; + } + atom = js_Atomize(cx, alias, strlen(alias), 0); + if (!atom) { + ok = JS_FALSE; + } else { + sprop = (JSScopeProperty *)prop; + ok = (js_AddNativeProperty(cx, obj, ATOM_TO_JSID(atom), + sprop->getter, sprop->setter, sprop->slot, + sprop->attrs, sprop->flags | SPROP_IS_ALIAS, + sprop->shortid) + != NULL); + } + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; +} + +static jsval +LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, JSProperty *prop) +{ + JSScopeProperty *sprop; + jsval rval; + + if (!prop) { + /* XXX bad API: no way to tell "not defined" from "void value" */ + return JSVAL_VOID; + } + if (OBJ_IS_NATIVE(obj2)) { + /* Peek at the native property's slot value, without doing a Get. */ + sprop = (JSScopeProperty *)prop; + rval = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2)) + ? LOCKED_OBJ_GET_SLOT(obj2, sprop->slot) + : JSVAL_TRUE; + } else { + /* XXX bad API: no way to return "defined but value unknown" */ + rval = JSVAL_TRUE; + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + return rval; +} + +static JSBool +GetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, JSPropertyOp *setterp) +{ + JSObject *obj2; + JSProperty *prop; + JSBool ok; + + if (!atom) + return JS_FALSE; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) + return JS_FALSE; + + if (!prop || obj != obj2) { + *attrsp = 0; + *foundp = JS_FALSE; + if (getterp) + *getterp = NULL; + if (setterp) + *setterp = NULL; + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + return JS_TRUE; + } + + *foundp = JS_TRUE; + ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, attrsp); + if (ok && OBJ_IS_NATIVE(obj)) { + JSScopeProperty *sprop = (JSScopeProperty *) prop; + + if (getterp) + *getterp = sprop->getter; + if (setterp) + *setterp = sprop->setter; + } + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; +} + +static JSBool +SetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, + uintN attrs, JSBool *foundp) +{ + JSObject *obj2; + JSProperty *prop; + JSBool ok; + + if (!atom) + return JS_FALSE; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) + return JS_FALSE; + if (!prop || obj != obj2) { + *foundp = JS_FALSE; + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + return JS_TRUE; + } + + *foundp = JS_TRUE; + ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, + uintN *attrsp, JSBool *foundp) +{ + CHECK_REQUEST(cx); + return GetPropertyAttributes(cx, obj, + js_Atomize(cx, name, strlen(name), 0), + attrsp, foundp, NULL, NULL); +} + +JS_PUBLIC_API(JSBool) +JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, + const char *name, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp) +{ + CHECK_REQUEST(cx); + return GetPropertyAttributes(cx, obj, + js_Atomize(cx, name, strlen(name), 0), + attrsp, foundp, getterp, setterp); +} + +JS_PUBLIC_API(JSBool) +JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, + uintN attrs, JSBool *foundp) +{ + CHECK_REQUEST(cx); + return SetPropertyAttributes(cx, obj, + js_Atomize(cx, name, strlen(name), 0), + attrs, foundp); +} + +JS_PUBLIC_API(JSBool) +JS_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = LookupProperty(cx, obj, name, &obj2, &prop); + if (ok) { + *foundp = (prop != NULL); + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = LookupProperty(cx, obj, name, &obj2, &prop); + if (ok) + *vp = LookupResult(cx, obj, obj2, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, + uintN flags, jsval *vp) +{ + JSAtom *atom; + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + ok = OBJ_IS_NATIVE(obj) + ? js_LookupPropertyWithFlags(cx, obj, ATOM_TO_JSID(atom), flags, + &obj2, &prop) + : OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop); + if (ok) + *vp = LookupResult(cx, obj, obj2, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + return OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); +} + +JS_PUBLIC_API(JSBool) +JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, + jsval *vp) +{ + JSAtom *atom; + jsid id; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + id = ATOM_TO_JSID(atom); + +#if JS_HAS_XML_SUPPORT + if (OBJECT_IS_XML(cx, obj)) { + JSXMLObjectOps *ops; + + ops = (JSXMLObjectOps *) obj->map->ops; + obj = ops->getMethod(cx, obj, id, vp); + if (!obj) + return JS_FALSE; + } else +#endif + { + if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) + return JS_FALSE; + } + + *objp = obj; + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + return OBJ_SET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name) +{ + jsval junk; + + CHECK_REQUEST(cx); + return JS_DeleteProperty2(cx, obj, name, &junk); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, + jsval *rval) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + return OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval); +} + +JS_PUBLIC_API(JSBool) +JS_DefineUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs) +{ + CHECK_REQUEST(cx); + return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, + attrs, 0, 0); +} + +JS_PUBLIC_API(JSBool) +JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN *attrsp, JSBool *foundp) +{ + CHECK_REQUEST(cx); + return GetPropertyAttributes(cx, obj, + js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), + attrsp, foundp, NULL, NULL); +} + +JS_PUBLIC_API(JSBool) +JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN *attrsp, JSBool *foundp, + JSPropertyOp *getterp, + JSPropertyOp *setterp) +{ + CHECK_REQUEST(cx); + return GetPropertyAttributes(cx, obj, + js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), + attrsp, foundp, getterp, setterp); +} + +JS_PUBLIC_API(JSBool) +JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + uintN attrs, JSBool *foundp) +{ + CHECK_REQUEST(cx); + return SetPropertyAttributes(cx, obj, + js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), + attrs, foundp); +} + +JS_PUBLIC_API(JSBool) +JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + int8 tinyid, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs) +{ + CHECK_REQUEST(cx); + return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, + attrs, SPROP_HAS_SHORTID, tinyid); +} + +JS_PUBLIC_API(JSBool) +JS_HasUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + JSBool *vp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = LookupUCProperty(cx, obj, name, namelen, &obj2, &prop); + if (ok) { + *vp = (prop != NULL); + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_LookupUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = LookupUCProperty(cx, obj, name, namelen, &obj2, &prop); + if (ok) + *vp = LookupResult(cx, obj, obj2, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_GetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return JS_FALSE; + return OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); +} + +JS_PUBLIC_API(JSBool) +JS_SetUCProperty(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *vp) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return JS_FALSE; + return OBJ_SET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, + jsval *rval) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return JS_FALSE; + return OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval); +} + +JS_PUBLIC_API(JSObject *) +JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector) +{ + CHECK_REQUEST(cx); + /* NB: jsuint cast does ToUint32. */ + return js_NewArrayObject(cx, (jsuint)length, vector); +} + +JS_PUBLIC_API(JSBool) +JS_IsArrayObject(JSContext *cx, JSObject *obj) +{ + return OBJ_GET_CLASS(cx, obj) == &js_ArrayClass; +} + +JS_PUBLIC_API(JSBool) +JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) +{ + CHECK_REQUEST(cx); + return js_GetLengthProperty(cx, obj, lengthp); +} + +JS_PUBLIC_API(JSBool) +JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length) +{ + CHECK_REQUEST(cx); + return js_SetLengthProperty(cx, obj, length); +} + +JS_PUBLIC_API(JSBool) +JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) +{ + CHECK_REQUEST(cx); + return js_HasLengthProperty(cx, obj, lengthp); +} + +JS_PUBLIC_API(JSBool) +JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, + JSPropertyOp getter, JSPropertyOp setter, uintN attrs) +{ + CHECK_REQUEST(cx); + return OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(index), value, + getter, setter, attrs, NULL); +} + +JS_PUBLIC_API(JSBool) +JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias) +{ + JSObject *obj2; + JSProperty *prop; + JSScopeProperty *sprop; + JSBool ok; + + CHECK_REQUEST(cx); + if (!LookupProperty(cx, obj, name, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + js_ReportIsNotDefined(cx, name); + return JS_FALSE; + } + if (obj2 != obj || !OBJ_IS_NATIVE(obj)) { + char numBuf[12]; + OBJ_DROP_PROPERTY(cx, obj2, prop); + JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)alias); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS, + numBuf, name, OBJ_GET_CLASS(cx, obj2)->name); + return JS_FALSE; + } + sprop = (JSScopeProperty *)prop; + ok = (js_AddNativeProperty(cx, obj, INT_TO_JSID(alias), + sprop->getter, sprop->setter, sprop->slot, + sprop->attrs, sprop->flags | SPROP_IS_ALIAS, + sprop->shortid) + != NULL); + OBJ_DROP_PROPERTY(cx, obj, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSID(index), &obj2, &prop); + if (ok) { + *foundp = (prop != NULL); + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) +{ + JSBool ok; + JSObject *obj2; + JSProperty *prop; + + CHECK_REQUEST(cx); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSID(index), &obj2, &prop); + if (ok) + *vp = LookupResult(cx, obj, obj2, prop); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) +{ + CHECK_REQUEST(cx); + return OBJ_GET_PROPERTY(cx, obj, INT_TO_JSID(index), vp); +} + +JS_PUBLIC_API(JSBool) +JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) +{ + CHECK_REQUEST(cx); + return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSID(index), vp); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index) +{ + jsval junk; + + CHECK_REQUEST(cx); + return JS_DeleteElement2(cx, obj, index, &junk); +} + +JS_PUBLIC_API(JSBool) +JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval) +{ + CHECK_REQUEST(cx); + return OBJ_DELETE_PROPERTY(cx, obj, INT_TO_JSID(index), rval); +} + +JS_PUBLIC_API(void) +JS_ClearScope(JSContext *cx, JSObject *obj) +{ + CHECK_REQUEST(cx); + + if (obj->map->ops->clear) + obj->map->ops->clear(cx, obj); +} + +JS_PUBLIC_API(JSIdArray *) +JS_Enumerate(JSContext *cx, JSObject *obj) +{ + jsint i, n; + jsval iter_state, num_properties; + jsid id; + JSIdArray *ida; + jsval *vector; + + CHECK_REQUEST(cx); + + ida = NULL; + iter_state = JSVAL_NULL; + + /* Get the number of properties to enumerate. */ + if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties)) + goto error; + if (!JSVAL_IS_INT(num_properties)) { + JS_ASSERT(0); + goto error; + } + + /* Grow as needed if we don't know the exact amount ahead of time. */ + n = JSVAL_TO_INT(num_properties); + if (n <= 0) + n = 8; + + /* Create an array of jsids large enough to hold all the properties */ + ida = js_NewIdArray(cx, n); + if (!ida) + goto error; + + i = 0; + vector = &ida->vector[0]; + for (;;) { + if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &id)) + goto error; + + /* No more jsid's to enumerate ? */ + if (iter_state == JSVAL_NULL) + break; + + if (i == ida->length) { + ida = js_SetIdArrayLength(cx, ida, ida->length * 2); + if (!ida) + goto error; + vector = &ida->vector[0]; + } + vector[i++] = id; + } + return js_SetIdArrayLength(cx, ida, i); + +error: + if (iter_state != JSVAL_NULL) + OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); + if (ida) + JS_DestroyIdArray(cx, ida); + return NULL; +} + +/* + * XXX reverse iterator for properties, unreverse and meld with jsinterp.c's + * prop_iterator_class somehow... + * + preserve the OBJ_ENUMERATE API while optimizing the native object case + * + native case here uses a JSScopeProperty *, but that iterates in reverse! + * + so we make non-native match, by reverse-iterating after JS_Enumerating + */ +#define JSSLOT_ITER_INDEX (JSSLOT_PRIVATE + 1) + +#if JSSLOT_ITER_INDEX >= JS_INITIAL_NSLOTS +# error "JSSLOT_ITER_INDEX botch!" +#endif + +static void +prop_iter_finalize(JSContext *cx, JSObject *obj) +{ + jsval v; + jsint i; + JSIdArray *ida; + + v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_ITER_INDEX); + if (JSVAL_IS_VOID(v)) + return; + + i = JSVAL_TO_INT(v); + if (i >= 0) { + /* Non-native case: destroy the ida enumerated when obj was created. */ + ida = (JSIdArray *) JS_GetPrivate(cx, obj); + if (ida) + JS_DestroyIdArray(cx, ida); + } +} + +static uint32 +prop_iter_mark(JSContext *cx, JSObject *obj, void *arg) +{ + jsval v; + jsint i, n; + JSScopeProperty *sprop; + JSIdArray *ida; + jsid id; + + v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PRIVATE); + JS_ASSERT(!JSVAL_IS_VOID(v)); + + i = JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_INDEX)); + if (i < 0) { + /* Native case: just mark the next property to visit. */ + sprop = (JSScopeProperty *) JSVAL_TO_PRIVATE(v); + if (sprop) + MARK_SCOPE_PROPERTY(sprop); + } else { + /* Non-native case: mark each id in the JSIdArray private. */ + ida = (JSIdArray *) JSVAL_TO_PRIVATE(v); + for (i = 0, n = ida->length; i < n; i++) { + id = ida->vector[i]; + if (JSID_IS_ATOM(id)) + GC_MARK_ATOM(cx, JSID_TO_ATOM(id), arg); + else if (JSID_IS_OBJECT(id)) + GC_MARK(cx, JSID_TO_OBJECT(id), "id", arg); + } + } + return 0; +} + +static JSClass prop_iter_class = { + "PropertyIterator", + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, prop_iter_finalize, + NULL, NULL, NULL, NULL, + NULL, NULL, prop_iter_mark, NULL +}; + +JS_PUBLIC_API(JSObject *) +JS_NewPropertyIterator(JSContext *cx, JSObject *obj) +{ + JSObject *iterobj; + JSScope *scope; + void *pdata; + jsint index; + JSIdArray *ida; + + CHECK_REQUEST(cx); + iterobj = js_NewObject(cx, &prop_iter_class, NULL, obj); + if (!iterobj) + return NULL; + + if (OBJ_IS_NATIVE(obj)) { + /* Native case: start with the last property in obj's own scope. */ + scope = OBJ_SCOPE(obj); + pdata = (scope->object == obj) ? scope->lastProp : NULL; + index = -1; + } else { + /* Non-native case: enumerate a JSIdArray and keep it via private. */ + ida = JS_Enumerate(cx, obj); + if (!ida) + goto bad; + pdata = ida; + index = ida->length; + } + + if (!JS_SetPrivate(cx, iterobj, pdata)) + goto bad; + OBJ_SET_SLOT(cx, iterobj, JSSLOT_ITER_INDEX, INT_TO_JSVAL(index)); + return iterobj; + +bad: + cx->newborn[GCX_OBJECT] = NULL; + return NULL; +} + +JS_PUBLIC_API(JSBool) +JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) +{ + jsint i; + JSObject *obj; + JSScope *scope; + JSScopeProperty *sprop; + JSIdArray *ida; + + CHECK_REQUEST(cx); + i = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_INDEX)); + if (i < 0) { + /* Native case: private data is a property tree node pointer. */ + obj = OBJ_GET_PARENT(cx, iterobj); + JS_ASSERT(OBJ_IS_NATIVE(obj)); + scope = OBJ_SCOPE(obj); + JS_ASSERT(scope->object == obj); + sprop = (JSScopeProperty *) JS_GetPrivate(cx, iterobj); + + /* + * If the next property mapped by scope in the property tree ancestor + * line is not enumerable, or it's an alias, or one or more properties + * were deleted from the "middle" of the scope-mapped ancestor line + * and the next property was among those deleted, skip it and keep on + * trying to find an enumerable property that is still in scope. + */ + while (sprop && + (!(sprop->attrs & JSPROP_ENUMERATE) || + (sprop->flags & SPROP_IS_ALIAS) || + (SCOPE_HAD_MIDDLE_DELETE(scope) && + !SCOPE_HAS_PROPERTY(scope, sprop)))) { + sprop = sprop->parent; + } + + if (!sprop) { + *idp = JSVAL_VOID; + } else { + if (!JS_SetPrivate(cx, iterobj, sprop->parent)) + return JS_FALSE; + *idp = sprop->id; + } + } else { + /* Non-native case: use the ida enumerated when iterobj was created. */ + ida = (JSIdArray *) JS_GetPrivate(cx, iterobj); + JS_ASSERT(i <= ida->length); + if (i == 0) { + *idp = JSVAL_VOID; + } else { + *idp = ida->vector[--i]; + OBJ_SET_SLOT(cx, iterobj, JSSLOT_ITER_INDEX, INT_TO_JSVAL(i)); + } + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, + jsval *vp, uintN *attrsp) +{ + CHECK_REQUEST(cx); + return OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, attrsp); +} + +JS_PUBLIC_API(JSCheckAccessOp) +JS_SetCheckObjectAccessCallback(JSRuntime *rt, JSCheckAccessOp acb) +{ + JSCheckAccessOp oldacb; + + oldacb = rt->checkObjectAccess; + rt->checkObjectAccess = acb; + return oldacb; +} + +static JSBool +ReservedSlotIndexOK(JSContext *cx, JSObject *obj, JSClass *clasp, + uint32 index, uint32 limit) +{ + /* Check the computed, possibly per-instance, upper bound. */ + if (clasp->reserveSlots) + JS_LOCK_OBJ_VOID(cx, obj, limit += clasp->reserveSlots(cx, obj)); + if (index >= limit) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_RESERVED_SLOT_RANGE); + return JS_FALSE; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp) +{ + JSClass *clasp; + uint32 limit, slot; + + CHECK_REQUEST(cx); + clasp = OBJ_GET_CLASS(cx, obj); + limit = JSCLASS_RESERVED_SLOTS(clasp); + if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) + return JS_FALSE; + slot = JSSLOT_START(clasp) + index; + *vp = OBJ_GET_REQUIRED_SLOT(cx, obj, slot); + return JS_TRUE; +} + +JS_PUBLIC_API(JSBool) +JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v) +{ + JSClass *clasp; + uint32 limit, slot; + + CHECK_REQUEST(cx); + clasp = OBJ_GET_CLASS(cx, obj); + limit = JSCLASS_RESERVED_SLOTS(clasp); + if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) + return JS_FALSE; + slot = JSSLOT_START(clasp) + index; + return OBJ_SET_REQUIRED_SLOT(cx, obj, slot, v); +} + +#ifdef JS_THREADSAFE +JS_PUBLIC_API(jsrefcount) +JS_HoldPrincipals(JSContext *cx, JSPrincipals *principals) +{ + return JS_ATOMIC_INCREMENT(&principals->refcount); +} + +JS_PUBLIC_API(jsrefcount) +JS_DropPrincipals(JSContext *cx, JSPrincipals *principals) +{ + jsrefcount rc = JS_ATOMIC_DECREMENT(&principals->refcount); + if (rc == 0) + principals->destroy(cx, principals); + return rc; +} +#endif + +JS_PUBLIC_API(JSPrincipalsTranscoder) +JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px) +{ + JSPrincipalsTranscoder oldpx; + + oldpx = rt->principalsTranscoder; + rt->principalsTranscoder = px; + return oldpx; +} + +JS_PUBLIC_API(JSObjectPrincipalsFinder) +JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop) +{ + JSObjectPrincipalsFinder oldfop; + + oldfop = rt->findObjectPrincipals; + rt->findObjectPrincipals = fop; + return oldfop; +} + +JS_PUBLIC_API(JSFunction *) +JS_NewFunction(JSContext *cx, JSNative native, uintN nargs, uintN flags, + JSObject *parent, const char *name) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + + if (!name) { + atom = NULL; + } else { + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return NULL; + } + return js_NewFunction(cx, NULL, native, nargs, flags, parent, atom); +} + +JS_PUBLIC_API(JSObject *) +JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) +{ + CHECK_REQUEST(cx); + if (OBJ_GET_CLASS(cx, funobj) != &js_FunctionClass) { + /* Indicate we cannot clone this object. */ + return funobj; + } + return js_CloneFunctionObject(cx, funobj, parent); +} + +JS_PUBLIC_API(JSObject *) +JS_GetFunctionObject(JSFunction *fun) +{ + return fun->object; +} + +JS_PUBLIC_API(const char *) +JS_GetFunctionName(JSFunction *fun) +{ + return fun->atom + ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom)) + : js_anonymous_str; +} + +JS_PUBLIC_API(JSString *) +JS_GetFunctionId(JSFunction *fun) +{ + return fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; +} + +JS_PUBLIC_API(uintN) +JS_GetFunctionFlags(JSFunction *fun) +{ + return fun->flags; +} + +JS_PUBLIC_API(uint16) +JS_GetFunctionArity(JSFunction *fun) +{ + return fun->nargs; +} + +JS_PUBLIC_API(JSBool) +JS_ObjectIsFunction(JSContext *cx, JSObject *obj) +{ + return OBJ_GET_CLASS(cx, obj) == &js_FunctionClass; +} + +JS_STATIC_DLL_CALLBACK(JSBool) +js_generic_native_method_dispatcher(JSContext *cx, JSObject *obj, + uintN argc, jsval *argv, jsval *rval) +{ + jsval fsv; + JSFunctionSpec *fs; + JSObject *tmp; + + if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(argv[-2]), 0, &fsv)) + return JS_FALSE; + fs = (JSFunctionSpec *) JSVAL_TO_PRIVATE(fsv); + + /* + * We know that argv[0] is valid because JS_DefineFunctions, which is our + * only (indirect) referrer, defined us as requiring at least one argument + * (notice how it passes fs->nargs + 1 as the next-to-last argument to + * JS_DefineFunction). + */ + if (JSVAL_IS_PRIMITIVE(argv[0])) { + /* + * Make sure that this is an object or null, as required by the generic + * functions. + */ + if (!js_ValueToObject(cx, argv[0], &tmp)) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(tmp); + } + + /* + * Copy all actual (argc) and required but missing (fs->nargs + 1 - argc) + * args down over our |this| parameter, argv[-1], which is almost always + * the class constructor object, e.g. Array. Then call the corresponding + * prototype native method with our first argument passed as |this|. + */ + memmove(argv - 1, argv, JS_MAX(fs->nargs + 1U, argc) * sizeof(jsval)); + + /* + * Follow Function.prototype.apply and .call by using the global object as + * the 'this' param if no args. + */ + JS_ASSERT(cx->fp->argv == argv); + if (!js_ComputeThis(cx, JSVAL_TO_OBJECT(argv[-1]), cx->fp)) + return JS_FALSE; + + /* + * Protect against argc - 1 underflowing below. By calling js_ComputeThis, + * we made it as if the static was called with one parameter. + */ + if (argc == 0) + argc = 1; + + return fs->call(cx, JSVAL_TO_OBJECT(argv[-1]), argc - 1, argv, rval); +} + +JS_PUBLIC_API(JSBool) +JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) +{ + uintN flags; + JSObject *ctor; + JSFunction *fun; + + CHECK_REQUEST(cx); + ctor = NULL; + for (; fs->name; fs++) { + flags = fs->flags; + + /* + * Define a generic arity N+1 static method for the arity N prototype + * method if flags contains JSFUN_GENERIC_NATIVE. + */ + if (flags & JSFUN_GENERIC_NATIVE) { + if (!ctor) { + ctor = JS_GetConstructor(cx, obj); + if (!ctor) + return JS_FALSE; + } + + flags &= ~JSFUN_GENERIC_NATIVE; + fun = JS_DefineFunction(cx, ctor, fs->name, + js_generic_native_method_dispatcher, + fs->nargs + 1, flags); + if (!fun) + return JS_FALSE; + fun->extra = fs->extra; + + /* + * As jsapi.h notes, fs must point to storage that lives as long + * as fun->object lives. + */ + if (!JS_SetReservedSlot(cx, fun->object, 0, PRIVATE_TO_JSVAL(fs))) + return JS_FALSE; + } + + fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, flags); + if (!fun) + return JS_FALSE; + fun->extra = fs->extra; + } + return JS_TRUE; +} + +JS_PUBLIC_API(JSFunction *) +JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, + uintN nargs, uintN attrs) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return NULL; + return js_DefineFunction(cx, obj, atom, call, nargs, attrs); +} + +JS_PUBLIC_API(JSFunction *) +JS_DefineUCFunction(JSContext *cx, JSObject *obj, + const jschar *name, size_t namelen, JSNative call, + uintN nargs, uintN attrs) +{ + JSAtom *atom; + + atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); + if (!atom) + return NULL; + return js_DefineFunction(cx, obj, atom, call, nargs, attrs); +} + +static JSScript * +CompileTokenStream(JSContext *cx, JSObject *obj, JSTokenStream *ts, + void *tempMark, JSBool *eofp) +{ + JSBool eof; + JSArenaPool codePool, notePool; + JSCodeGenerator cg; + JSScript *script; + + CHECK_REQUEST(cx); + eof = JS_FALSE; + JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode)); + JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote)); + if (!js_InitCodeGenerator(cx, &cg, &codePool, ¬ePool, + ts->filename, ts->lineno, + ts->principals)) { + script = NULL; + } else if (!js_CompileTokenStream(cx, obj, ts, &cg)) { + script = NULL; + eof = (ts->flags & TSF_EOF) != 0; + } else { + script = js_NewScriptFromCG(cx, &cg, NULL); + } + if (eofp) + *eofp = eof; + if (!js_CloseTokenStream(cx, ts)) { + if (script) + js_DestroyScript(cx, script); + script = NULL; + } + cg.tempMark = tempMark; + js_FinishCodeGenerator(cx, &cg); + JS_FinishArenaPool(&codePool); + JS_FinishArenaPool(¬ePool); + return script; +} + +JS_PUBLIC_API(JSScript *) +JS_CompileScript(JSContext *cx, JSObject *obj, + const char *bytes, size_t length, + const char *filename, uintN lineno) +{ + jschar *chars; + JSScript *script; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, length); + if (!chars) + return NULL; + script = JS_CompileUCScript(cx, obj, chars, length, filename, lineno); + JS_free(cx, chars); + return script; +} + +JS_PUBLIC_API(JSScript *) +JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const char *bytes, size_t length, + const char *filename, uintN lineno) +{ + jschar *chars; + JSScript *script; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, length); + if (!chars) + return NULL; + script = JS_CompileUCScriptForPrincipals(cx, obj, principals, + chars, length, filename, lineno); + JS_free(cx, chars); + return script; +} + +JS_PUBLIC_API(JSScript *) +JS_CompileUCScript(JSContext *cx, JSObject *obj, + const jschar *chars, size_t length, + const char *filename, uintN lineno) +{ + CHECK_REQUEST(cx); + return JS_CompileUCScriptForPrincipals(cx, obj, NULL, chars, length, + filename, lineno); +} + +#if JS_HAS_EXCEPTIONS +# define LAST_FRAME_EXCEPTION_CHECK(cx,result) \ + JS_BEGIN_MACRO \ + if (!(result)) \ + js_ReportUncaughtException(cx); \ + JS_END_MACRO +#else +# define LAST_FRAME_EXCEPTION_CHECK(cx,result) /* nothing */ +#endif + +#define LAST_FRAME_CHECKS(cx,result) \ + JS_BEGIN_MACRO \ + if (!(cx)->fp) { \ + (cx)->lastInternalResult = JSVAL_NULL; \ + LAST_FRAME_EXCEPTION_CHECK(cx, result); \ + } \ + JS_END_MACRO + +JS_PUBLIC_API(JSScript *) +JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const jschar *chars, size_t length, + const char *filename, uintN lineno) +{ + void *mark; + JSTokenStream *ts; + JSScript *script; + + CHECK_REQUEST(cx); + mark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals); + if (!ts) + return NULL; + script = CompileTokenStream(cx, obj, ts, mark, NULL); + LAST_FRAME_CHECKS(cx, script); + return script; +} + +JS_PUBLIC_API(JSBool) +JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, + const char *bytes, size_t length) +{ + jschar *chars; + JSBool result; + JSExceptionState *exnState; + void *tempMark; + JSTokenStream *ts; + JSErrorReporter older; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, length); + if (!chars) + return JS_TRUE; + + /* + * Return true on any out-of-memory error, so our caller doesn't try to + * collect more buffered source. + */ + result = JS_TRUE; + exnState = JS_SaveExceptionState(cx); + tempMark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewTokenStream(cx, chars, length, NULL, 0, NULL); + if (ts) { + older = JS_SetErrorReporter(cx, NULL); + if (!js_ParseTokenStream(cx, obj, ts) && + (ts->flags & TSF_UNEXPECTED_EOF)) { + /* + * We ran into an error. If it was because we ran out of source, + * we return false, so our caller will know to try to collect more + * buffered source. + */ + result = JS_FALSE; + } + + JS_SetErrorReporter(cx, older); + js_CloseTokenStream(cx, ts); + JS_ARENA_RELEASE(&cx->tempPool, tempMark); + } + + JS_free(cx, chars); + JS_RestoreExceptionState(cx, exnState); + return result; +} + +JS_PUBLIC_API(JSScript *) +JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename) +{ + void *mark; + JSTokenStream *ts; + JSScript *script; + + CHECK_REQUEST(cx); + mark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewFileTokenStream(cx, filename, stdin); + if (!ts) + return NULL; + script = CompileTokenStream(cx, obj, ts, mark, NULL); + LAST_FRAME_CHECKS(cx, script); + return script; +} + +JS_PUBLIC_API(JSScript *) +JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, + FILE *file) +{ + return JS_CompileFileHandleForPrincipals(cx, obj, filename, file, NULL); +} + +JS_PUBLIC_API(JSScript *) +JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, + const char *filename, FILE *file, + JSPrincipals *principals) +{ + void *mark; + JSTokenStream *ts; + JSScript *script; + + CHECK_REQUEST(cx); + mark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewFileTokenStream(cx, NULL, file); + if (!ts) + return NULL; + ts->filename = filename; + /* XXXshaver js_NewFileTokenStream should do this, because it drops */ + if (principals) { + ts->principals = principals; + JSPRINCIPALS_HOLD(cx, ts->principals); + } + script = CompileTokenStream(cx, obj, ts, mark, NULL); + LAST_FRAME_CHECKS(cx, script); + return script; +} + +JS_PUBLIC_API(JSObject *) +JS_NewScriptObject(JSContext *cx, JSScript *script) +{ + JSObject *obj; + + obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); + if (!obj) + return NULL; + + if (script) { + if (!JS_SetPrivate(cx, obj, script)) + return NULL; + script->object = obj; + } + return obj; +} + +JS_PUBLIC_API(JSObject *) +JS_GetScriptObject(JSScript *script) +{ + return script->object; +} + +JS_PUBLIC_API(void) +JS_DestroyScript(JSContext *cx, JSScript *script) +{ + CHECK_REQUEST(cx); + js_DestroyScript(cx, script); +} + +JS_PUBLIC_API(JSFunction *) +JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, + uintN nargs, const char **argnames, + const char *bytes, size_t length, + const char *filename, uintN lineno) +{ + jschar *chars; + JSFunction *fun; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, length); + if (!chars) + return NULL; + fun = JS_CompileUCFunction(cx, obj, name, nargs, argnames, chars, length, + filename, lineno); + JS_free(cx, chars); + return fun; +} + +JS_PUBLIC_API(JSFunction *) +JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, const char *name, + uintN nargs, const char **argnames, + const char *bytes, size_t length, + const char *filename, uintN lineno) +{ + jschar *chars; + JSFunction *fun; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, length); + if (!chars) + return NULL; + fun = JS_CompileUCFunctionForPrincipals(cx, obj, principals, name, + nargs, argnames, chars, length, + filename, lineno); + JS_free(cx, chars); + return fun; +} + +JS_PUBLIC_API(JSFunction *) +JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, + uintN nargs, const char **argnames, + const jschar *chars, size_t length, + const char *filename, uintN lineno) +{ + CHECK_REQUEST(cx); + return JS_CompileUCFunctionForPrincipals(cx, obj, NULL, name, + nargs, argnames, + chars, length, + filename, lineno); +} + +JS_PUBLIC_API(JSFunction *) +JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, const char *name, + uintN nargs, const char **argnames, + const jschar *chars, size_t length, + const char *filename, uintN lineno) +{ + void *mark; + JSTokenStream *ts; + JSFunction *fun; + JSAtom *funAtom, *argAtom; + uintN i; + + CHECK_REQUEST(cx); + mark = JS_ARENA_MARK(&cx->tempPool); + ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals); + if (!ts) { + fun = NULL; + goto out; + } + if (!name) { + funAtom = NULL; + } else { + funAtom = js_Atomize(cx, name, strlen(name), 0); + if (!funAtom) { + fun = NULL; + goto out; + } + } + fun = js_NewFunction(cx, NULL, NULL, nargs, 0, obj, funAtom); + if (!fun) + goto out; + if (nargs) { + for (i = 0; i < nargs; i++) { + argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0); + if (!argAtom) + break; + if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom), + js_GetArgument, js_SetArgument, + SPROP_INVALID_SLOT, + JSPROP_PERMANENT | JSPROP_SHARED, + SPROP_HAS_SHORTID, i)) { + break; + } + } + if (i < nargs) { + fun = NULL; + goto out; + } + } + if (!js_CompileFunctionBody(cx, ts, fun)) { + fun = NULL; + goto out; + } + if (obj && funAtom) { + if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(funAtom), + OBJECT_TO_JSVAL(fun->object), + NULL, NULL, JSPROP_ENUMERATE, NULL)) { + return NULL; + } + } +out: + if (ts) + js_CloseTokenStream(cx, ts); + JS_ARENA_RELEASE(&cx->tempPool, mark); + LAST_FRAME_CHECKS(cx, fun); + return fun; +} + +JS_PUBLIC_API(JSString *) +JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, + uintN indent) +{ + JSPrinter *jp; + JSString *str; + + CHECK_REQUEST(cx); + jp = js_NewPrinter(cx, name, + indent & ~JS_DONT_PRETTY_PRINT, + !(indent & JS_DONT_PRETTY_PRINT)); + if (!jp) + return NULL; + if (js_DecompileScript(jp, script)) + str = js_GetPrinterOutput(jp); + else + str = NULL; + js_DestroyPrinter(jp); + return str; +} + +JS_PUBLIC_API(JSString *) +JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent) +{ + JSPrinter *jp; + JSString *str; + + CHECK_REQUEST(cx); + jp = js_NewPrinter(cx, JS_GetFunctionName(fun), + indent & ~JS_DONT_PRETTY_PRINT, + !(indent & JS_DONT_PRETTY_PRINT)); + if (!jp) + return NULL; + if (js_DecompileFunction(jp, fun)) + str = js_GetPrinterOutput(jp); + else + str = NULL; + js_DestroyPrinter(jp); + return str; +} + +JS_PUBLIC_API(JSString *) +JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent) +{ + JSPrinter *jp; + JSString *str; + + CHECK_REQUEST(cx); + jp = js_NewPrinter(cx, JS_GetFunctionName(fun), + indent & ~JS_DONT_PRETTY_PRINT, + !(indent & JS_DONT_PRETTY_PRINT)); + if (!jp) + return NULL; + if (js_DecompileFunctionBody(jp, fun)) + str = js_GetPrinterOutput(jp); + else + str = NULL; + js_DestroyPrinter(jp); + return str; +} + +JS_PUBLIC_API(JSBool) +JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval) +{ + JSBool ok; + + CHECK_REQUEST(cx); + ok = js_Execute(cx, obj, script, NULL, 0, rval); + LAST_FRAME_CHECKS(cx, ok); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, + JSExecPart part, jsval *rval) +{ + JSScript tmp; + JSRuntime *rt; + JSBool ok; + + /* Make a temporary copy of the JSScript structure and farble it a bit. */ + tmp = *script; + if (part == JSEXEC_PROLOG) { + tmp.length = PTRDIFF(tmp.main, tmp.code, jsbytecode); + } else { + tmp.length -= PTRDIFF(tmp.main, tmp.code, jsbytecode); + tmp.code = tmp.main; + } + + /* Tell the debugger about our temporary copy of the script structure. */ + rt = cx->runtime; + if (rt->newScriptHook) { + rt->newScriptHook(cx, tmp.filename, tmp.lineno, &tmp, NULL, + rt->newScriptHookData); + } + + /* Execute the farbled struct and tell the debugger to forget about it. */ + ok = JS_ExecuteScript(cx, obj, &tmp, rval); + if (rt->destroyScriptHook) + rt->destroyScriptHook(cx, &tmp, rt->destroyScriptHookData); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_EvaluateScript(JSContext *cx, JSObject *obj, + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval) +{ + jschar *chars; + JSBool ok; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, length); + if (!chars) + return JS_FALSE; + ok = JS_EvaluateUCScript(cx, obj, chars, length, filename, lineno, rval); + JS_free(cx, chars); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const char *bytes, uintN length, + const char *filename, uintN lineno, + jsval *rval) +{ + jschar *chars; + JSBool ok; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, length); + if (!chars) + return JS_FALSE; + ok = JS_EvaluateUCScriptForPrincipals(cx, obj, principals, chars, length, + filename, lineno, rval); + JS_free(cx, chars); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_EvaluateUCScript(JSContext *cx, JSObject *obj, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval) +{ + CHECK_REQUEST(cx); + return JS_EvaluateUCScriptForPrincipals(cx, obj, NULL, chars, length, + filename, lineno, rval); +} + +JS_PUBLIC_API(JSBool) +JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, + JSPrincipals *principals, + const jschar *chars, uintN length, + const char *filename, uintN lineno, + jsval *rval) +{ + uint32 options; + JSScript *script; + JSBool ok; + + CHECK_REQUEST(cx); + options = cx->options; + cx->options = options | JSOPTION_COMPILE_N_GO; + script = JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length, + filename, lineno); + cx->options = options; + if (!script) + return JS_FALSE; + ok = js_Execute(cx, obj, script, NULL, 0, rval); + LAST_FRAME_CHECKS(cx, ok); + JS_DestroyScript(cx, script); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, + jsval *argv, jsval *rval) +{ + JSBool ok; + + CHECK_REQUEST(cx); + ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(fun->object), argc, argv, + rval); + LAST_FRAME_CHECKS(cx, ok); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, + jsval *argv, jsval *rval) +{ + JSBool ok; + jsval fval; + + CHECK_REQUEST(cx); +#if JS_HAS_XML_SUPPORT + if (OBJECT_IS_XML(cx, obj)) { + JSXMLObjectOps *ops; + JSAtom *atom; + + ops = (JSXMLObjectOps *) obj->map->ops; + atom = js_Atomize(cx, name, strlen(name), 0); + if (!atom) + return JS_FALSE; + obj = ops->getMethod(cx, obj, ATOM_TO_JSID(atom), &fval); + if (!obj) + return JS_FALSE; + } else +#endif + if (!JS_GetProperty(cx, obj, name, &fval)) + return JS_FALSE; + ok = js_InternalCall(cx, obj, fval, argc, argv, rval); + LAST_FRAME_CHECKS(cx, ok); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, + jsval *argv, jsval *rval) +{ + JSBool ok; + + CHECK_REQUEST(cx); + ok = js_InternalCall(cx, obj, fval, argc, argv, rval); + LAST_FRAME_CHECKS(cx, ok); + return ok; +} + +JS_PUBLIC_API(JSBranchCallback) +JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb) +{ + JSBranchCallback oldcb; + + oldcb = cx->branchCallback; + cx->branchCallback = cb; + return oldcb; +} + +JS_PUBLIC_API(JSBool) +JS_IsRunning(JSContext *cx) +{ + return cx->fp != NULL; +} + +JS_PUBLIC_API(JSBool) +JS_IsConstructing(JSContext *cx) +{ + return cx->fp && (cx->fp->flags & JSFRAME_CONSTRUCTING); +} + +JS_FRIEND_API(JSBool) +JS_IsAssigning(JSContext *cx) +{ + JSStackFrame *fp; + jsbytecode *pc; + + for (fp = cx->fp; fp && !fp->script; fp = fp->down) + continue; + if (!fp || !(pc = fp->pc)) + return JS_FALSE; + return (js_CodeSpec[*pc].format & JOF_ASSIGNING) != 0; +} + +JS_PUBLIC_API(void) +JS_SetCallReturnValue2(JSContext *cx, jsval v) +{ +#if JS_HAS_LVALUE_RETURN + cx->rval2 = v; + cx->rval2set = JS_TRUE; +#endif +} + +/************************************************************************/ + +JS_PUBLIC_API(JSString *) +JS_NewString(JSContext *cx, char *bytes, size_t length) +{ + jschar *chars; + JSString *str; + + CHECK_REQUEST(cx); + /* Make a Unicode vector from the 8-bit char codes in bytes. */ + chars = js_InflateString(cx, bytes, length); + if (!chars) + return NULL; + + /* Free chars (but not bytes, which caller frees on error) if we fail. */ + str = js_NewString(cx, chars, length, 0); + if (!str) { + JS_free(cx, chars); + return NULL; + } + + /* Hand off bytes to the deflated string cache, if possible. */ + if (!js_SetStringBytes(str, bytes, length)) + JS_free(cx, bytes); + return str; +} + +JS_PUBLIC_API(JSString *) +JS_NewStringCopyN(JSContext *cx, const char *s, size_t n) +{ + jschar *js; + JSString *str; + + CHECK_REQUEST(cx); + js = js_InflateString(cx, s, n); + if (!js) + return NULL; + str = js_NewString(cx, js, n, 0); + if (!str) + JS_free(cx, js); + return str; +} + +JS_PUBLIC_API(JSString *) +JS_NewStringCopyZ(JSContext *cx, const char *s) +{ + size_t n; + jschar *js; + JSString *str; + + CHECK_REQUEST(cx); + if (!s) + return cx->runtime->emptyString; + n = strlen(s); + js = js_InflateString(cx, s, n); + if (!js) + return NULL; + str = js_NewString(cx, js, n, 0); + if (!str) + JS_free(cx, js); + return str; +} + +JS_PUBLIC_API(JSString *) +JS_InternString(JSContext *cx, const char *s) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_Atomize(cx, s, strlen(s), ATOM_INTERNED); + if (!atom) + return NULL; + return ATOM_TO_STRING(atom); +} + +JS_PUBLIC_API(JSString *) +JS_NewUCString(JSContext *cx, jschar *chars, size_t length) +{ + CHECK_REQUEST(cx); + return js_NewString(cx, chars, length, 0); +} + +JS_PUBLIC_API(JSString *) +JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n) +{ + CHECK_REQUEST(cx); + return js_NewStringCopyN(cx, s, n, 0); +} + +JS_PUBLIC_API(JSString *) +JS_NewUCStringCopyZ(JSContext *cx, const jschar *s) +{ + CHECK_REQUEST(cx); + if (!s) + return cx->runtime->emptyString; + return js_NewStringCopyZ(cx, s, 0); +} + +JS_PUBLIC_API(JSString *) +JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length) +{ + JSAtom *atom; + + CHECK_REQUEST(cx); + atom = js_AtomizeChars(cx, s, length, ATOM_INTERNED); + if (!atom) + return NULL; + return ATOM_TO_STRING(atom); +} + +JS_PUBLIC_API(JSString *) +JS_InternUCString(JSContext *cx, const jschar *s) +{ + return JS_InternUCStringN(cx, s, js_strlen(s)); +} + +JS_PUBLIC_API(char *) +JS_GetStringBytes(JSString *str) +{ + char *bytes; + + bytes = js_GetStringBytes(str); + return bytes ? bytes : ""; +} + +JS_PUBLIC_API(jschar *) +JS_GetStringChars(JSString *str) +{ + /* + * API botch (again, shades of JS_GetStringBytes): we have no cx to pass + * to js_UndependString (called by js_GetStringChars) for out-of-memory + * error reports, so js_UndependString passes NULL and suppresses errors. + * If it fails to convert a dependent string into an independent one, our + * caller will not be guaranteed a \u0000 terminator as a backstop. This + * may break some clients who already misbehave on embedded NULs. + * + * The gain of dependent strings, which cure quadratic and cubic growth + * rate bugs in string concatenation, is worth this slight loss in API + * compatibility. + */ + jschar *chars; + + chars = js_GetStringChars(str); + return chars ? chars : JSSTRING_CHARS(str); +} + +JS_PUBLIC_API(size_t) +JS_GetStringLength(JSString *str) +{ + return JSSTRING_LENGTH(str); +} + +JS_PUBLIC_API(intN) +JS_CompareStrings(JSString *str1, JSString *str2) +{ + return js_CompareStrings(str1, str2); +} + +JS_PUBLIC_API(JSString *) +JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length) +{ + CHECK_REQUEST(cx); + return js_NewString(cx, chars, length, GCF_MUTABLE); +} + +JS_PUBLIC_API(JSString *) +JS_NewDependentString(JSContext *cx, JSString *str, size_t start, + size_t length) +{ + CHECK_REQUEST(cx); + return js_NewDependentString(cx, str, start, length, 0); +} + +JS_PUBLIC_API(JSString *) +JS_ConcatStrings(JSContext *cx, JSString *left, JSString *right) +{ + CHECK_REQUEST(cx); + return js_ConcatStrings(cx, left, right); +} + +JS_PUBLIC_API(const jschar *) +JS_UndependString(JSContext *cx, JSString *str) +{ + CHECK_REQUEST(cx); + return js_UndependString(cx, str); +} + +JS_PUBLIC_API(JSBool) +JS_MakeStringImmutable(JSContext *cx, JSString *str) +{ + CHECK_REQUEST(cx); + if (!js_UndependString(cx, str)) + return JS_FALSE; + + *js_GetGCThingFlags(str) &= ~GCF_MUTABLE; + return JS_TRUE; +} + +/************************************************************************/ + +JS_PUBLIC_API(void) +JS_ReportError(JSContext *cx, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + js_ReportErrorVA(cx, JSREPORT_ERROR, format, ap); + va_end(ap); +} + +JS_PUBLIC_API(void) +JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback, + void *userRef, const uintN errorNumber, ...) +{ + va_list ap; + + va_start(ap, errorNumber); + js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, + errorNumber, JS_TRUE, ap); + va_end(ap); +} + +JS_PUBLIC_API(void) +JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback, + void *userRef, const uintN errorNumber, ...) +{ + va_list ap; + + va_start(ap, errorNumber); + js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, + errorNumber, JS_FALSE, ap); + va_end(ap); +} + +JS_PUBLIC_API(JSBool) +JS_ReportWarning(JSContext *cx, const char *format, ...) +{ + va_list ap; + JSBool ok; + + va_start(ap, format); + ok = js_ReportErrorVA(cx, JSREPORT_WARNING, format, ap); + va_end(ap); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_ReportErrorFlagsAndNumber(JSContext *cx, uintN flags, + JSErrorCallback errorCallback, void *userRef, + const uintN errorNumber, ...) +{ + va_list ap; + JSBool ok; + + va_start(ap, errorNumber); + ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef, + errorNumber, JS_TRUE, ap); + va_end(ap); + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags, + JSErrorCallback errorCallback, void *userRef, + const uintN errorNumber, ...) +{ + va_list ap; + JSBool ok; + + va_start(ap, errorNumber); + ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef, + errorNumber, JS_FALSE, ap); + va_end(ap); + return ok; +} + +JS_PUBLIC_API(void) +JS_ReportOutOfMemory(JSContext *cx) +{ + js_ReportOutOfMemory(cx, js_GetErrorMessage); +} + +JS_PUBLIC_API(JSErrorReporter) +JS_SetErrorReporter(JSContext *cx, JSErrorReporter er) +{ + JSErrorReporter older; + + older = cx->errorReporter; + cx->errorReporter = er; + return older; +} + +/************************************************************************/ + +/* + * Regular Expressions. + */ +JS_PUBLIC_API(JSObject *) +JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags) +{ +#if JS_HAS_REGEXPS + jschar *chars; + JSObject *obj; + + CHECK_REQUEST(cx); + chars = js_InflateString(cx, bytes, length); + if (!chars) + return NULL; + obj = js_NewRegExpObject(cx, NULL, chars, length, flags); + JS_free(cx, chars); + return obj; +#else + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_REG_EXPS); + return NULL; +#endif +} + +JS_PUBLIC_API(JSObject *) +JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags) +{ + CHECK_REQUEST(cx); +#if JS_HAS_REGEXPS + return js_NewRegExpObject(cx, NULL, chars, length, flags); +#else + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_REG_EXPS); + return NULL; +#endif +} + +JS_PUBLIC_API(void) +JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline) +{ + JSRegExpStatics *res; + + CHECK_REQUEST(cx); + /* No locking required, cx is thread-private and input must be live. */ + res = &cx->regExpStatics; + res->input = input; + res->multiline = multiline; + cx->runtime->gcPoke = JS_TRUE; +} + +JS_PUBLIC_API(void) +JS_ClearRegExpStatics(JSContext *cx) +{ + JSRegExpStatics *res; + + /* No locking required, cx is thread-private and input must be live. */ + res = &cx->regExpStatics; + res->input = NULL; + res->multiline = JS_FALSE; + res->parenCount = 0; + res->lastMatch = res->lastParen = js_EmptySubString; + res->leftContext = res->rightContext = js_EmptySubString; + cx->runtime->gcPoke = JS_TRUE; +} + +JS_PUBLIC_API(void) +JS_ClearRegExpRoots(JSContext *cx) +{ + JSRegExpStatics *res; + + /* No locking required, cx is thread-private and input must be live. */ + res = &cx->regExpStatics; + res->input = NULL; + cx->runtime->gcPoke = JS_TRUE; +} + +/* TODO: compile, execute, get/set other statics... */ + +/************************************************************************/ + +JS_PUBLIC_API(void) +JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks) +{ + cx->localeCallbacks = callbacks; +} + +JS_PUBLIC_API(JSLocaleCallbacks *) +JS_GetLocaleCallbacks(JSContext *cx) +{ + return cx->localeCallbacks; +} + +/************************************************************************/ + +JS_PUBLIC_API(JSBool) +JS_IsExceptionPending(JSContext *cx) +{ +#if JS_HAS_EXCEPTIONS + return (JSBool) cx->throwing; +#else + return JS_FALSE; +#endif +} + +JS_PUBLIC_API(JSBool) +JS_GetPendingException(JSContext *cx, jsval *vp) +{ +#if JS_HAS_EXCEPTIONS + CHECK_REQUEST(cx); + if (!cx->throwing) + return JS_FALSE; + *vp = cx->exception; + return JS_TRUE; +#else + return JS_FALSE; +#endif +} + +JS_PUBLIC_API(void) +JS_SetPendingException(JSContext *cx, jsval v) +{ + CHECK_REQUEST(cx); +#if JS_HAS_EXCEPTIONS + cx->throwing = JS_TRUE; + cx->exception = v; +#endif +} + +JS_PUBLIC_API(void) +JS_ClearPendingException(JSContext *cx) +{ +#if JS_HAS_EXCEPTIONS + cx->throwing = JS_FALSE; + cx->exception = JSVAL_VOID; +#endif +} + +JS_PUBLIC_API(JSBool) +JS_ReportPendingException(JSContext *cx) +{ +#if JS_HAS_EXCEPTIONS + JSBool save, ok; + + CHECK_REQUEST(cx); + + /* + * Set cx->creatingException to suppress the standard error-to-exception + * conversion done by all {js,JS}_Report* functions except for OOM. The + * cx->creatingException flag was added to suppress recursive divergence + * under js_ErrorToException, but it serves for our purposes here too. + */ + save = cx->creatingException; + cx->creatingException = JS_TRUE; + ok = js_ReportUncaughtException(cx); + cx->creatingException = save; + return ok; +#else + return JS_TRUE; +#endif +} + +#if JS_HAS_EXCEPTIONS +struct JSExceptionState { + JSBool throwing; + jsval exception; +}; +#endif + +JS_PUBLIC_API(JSExceptionState *) +JS_SaveExceptionState(JSContext *cx) +{ +#if JS_HAS_EXCEPTIONS + JSExceptionState *state; + + CHECK_REQUEST(cx); + state = (JSExceptionState *) JS_malloc(cx, sizeof(JSExceptionState)); + if (state) { + state->throwing = JS_GetPendingException(cx, &state->exception); + if (state->throwing && JSVAL_IS_GCTHING(state->exception)) + js_AddRoot(cx, &state->exception, "JSExceptionState.exception"); + } + return state; +#else + return NULL; +#endif +} + +JS_PUBLIC_API(void) +JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state) +{ +#if JS_HAS_EXCEPTIONS + CHECK_REQUEST(cx); + if (state) { + if (state->throwing) + JS_SetPendingException(cx, state->exception); + else + JS_ClearPendingException(cx); + JS_DropExceptionState(cx, state); + } +#endif +} + +JS_PUBLIC_API(void) +JS_DropExceptionState(JSContext *cx, JSExceptionState *state) +{ +#if JS_HAS_EXCEPTIONS + CHECK_REQUEST(cx); + if (state) { + if (state->throwing && JSVAL_IS_GCTHING(state->exception)) + JS_RemoveRoot(cx, &state->exception); + JS_free(cx, state); + } +#endif +} + +JS_PUBLIC_API(JSErrorReport *) +JS_ErrorFromException(JSContext *cx, jsval v) +{ +#if JS_HAS_ERROR_EXCEPTIONS + CHECK_REQUEST(cx); + return js_ErrorFromException(cx, v); +#else + return NULL; +#endif +} + +JS_PUBLIC_API(JSBool) +JS_ThrowReportedError(JSContext *cx, const char *message, + JSErrorReport *reportp) +{ + return js_ErrorToException(cx, message, reportp); +} + +#ifdef JS_THREADSAFE +JS_PUBLIC_API(jsword) +JS_GetContextThread(JSContext *cx) +{ + return cx->thread; +} + +JS_PUBLIC_API(jsword) +JS_SetContextThread(JSContext *cx) +{ + jsword old = cx->thread; + cx->thread = js_CurrentThreadId(); + return old; +} + +JS_PUBLIC_API(jsword) +JS_ClearContextThread(JSContext *cx) +{ + jsword old = cx->thread; + cx->thread = 0; + return old; +} +#endif + +/************************************************************************/ + +#if defined(XP_WIN) +#include +/* + * Initialization routine for the JS DLL... + */ + +/* + * Global Instance handle... + * In Win32 this is the module handle of the DLL. + * + * In Win16 this is the instance handle of the application + * which loaded the DLL. + */ + +#ifdef _WIN32 +BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved) +{ + return TRUE; +} + +#else /* !_WIN32 */ + +int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg, + WORD cbHeapSize, LPSTR lpszCmdLine ) +{ + return TRUE; +} + +BOOL CALLBACK __loadds WEP(BOOL fSystemExit) +{ + return TRUE; +} + +#endif /* !_WIN32 */ +#endif /* XP_WIN */ Index: ossp-pkg/js/src/jsdtoa.c RCS File: /v/ossp/cvs/ossp-pkg/js/src/jsdtoa.c,v co -q -kk -p'1.2' '/v/ossp/cvs/ossp-pkg/js/src/jsdtoa.c,v' | diff -u /dev/null - -L'ossp-pkg/js/src/jsdtoa.c' 2>/dev/null --- ossp-pkg/js/src/jsdtoa.c +++ - 2024-05-12 07:43:25.694809915 +0200 @@ -0,0 +1,3132 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** 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 ***** */ + +/* + * Portable double to alphanumeric string and back converters. + */ +#include "jsstddef.h" +#include "jslibmath.h" +#include "jstypes.h" +#include "jsdtoa.h" +#include "jsprf.h" +#include "jsutil.h" /* Added by JSIFY */ +#include "jspubtd.h" +#include "jsnum.h" + +#ifdef JS_THREADSAFE +#include "prlock.h" +#endif + +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991 by Lucent Technologies. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* Please send bug reports to + David M. Gay + Bell Laboratories, Room 2C-463 + 600 Mountain Avenue + Murray Hill, NJ 07974-0636 + U.S.A. + dmg@bell-labs.com + */ + +/* On a machine with IEEE extended-precision registers, it is + * necessary to specify double-precision (53-bit) rounding precision + * before invoking strtod or dtoa. If the machine uses (the equivalent + * of) Intel 80x87 arithmetic, the call + * _control87(PC_53, MCW_PC); + * does this with many compilers. Whether this or another call is + * appropriate depends on the compiler; for this to work, it may be + * necessary to #include "float.h" or another system-dependent header + * file. + */ + +/* strtod for IEEE-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets err to JS_DTOA_ERANGE or JS_DTOA_ENOMEM). With IEEE + * arithmetic, ties are broken by the IEEE round-even rule. Otherwise + * ties are broken by biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * + * 1. We only require IEEE double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Long int on machines with 32-bit ints and 64-bit longs. + * #define Sudden_Underflow for IEEE-format machines without gradual + * underflow (i.e., that flush to zero on underflow). + * #define No_leftright to omit left-right logic in fast floating-point + * computation of js_dtoa. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define JS_HAVE_LONG_LONG on machines that have a "long long" + * integer type (of >= 64 bits). If long long is available and the name is + * something other than "long long", #define Llong to be the name, + * and if "unsigned Llong" does not work as an unsigned version of + * Llong, #define #ULLong to be the corresponding unsigned type. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) + * if memory is available and otherwise does something you deem + * appropriate. If MALLOC is undefined, malloc will be invoked + * directly -- and assumed always to succeed. + * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making + * memory allocations from a private pool of memory when possible. + * When used, the private pool is PRIVATE_MEM bytes long: 2000 bytes, + * unless #defined to be a different length. This default length + * suffices to get rid of MALLOC calls except for unusual cases, + * such as decimal-to-binary conversion of a very long string of + * digits. + * #define INFNAN_CHECK on IEEE systems to cause strtod to check for + * Infinity and NaN (case insensitively). On some systems (e.g., + * some HP systems), it may be necessary to #define NAN_WORD0 + * appropriately -- to the most significant word of a quiet NaN. + * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) + * #define MULTIPLE_THREADS if the system offers preemptively scheduled + * multiple threads. In this case, you must provide (or suitably + * #define) two locks, acquired by ACQUIRE_DTOA_LOCK() and released + * by RELEASE_DTOA_LOCK(). (The second lock, accessed + * in pow5mult, ensures lazy evaluation of only one copy of high + * powers of 5; omitting this lock would introduce a small + * probability of wasting memory, but would otherwise be harmless.) + * You must also invoke freedtoa(s) to free the value s returned by + * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. + * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that + * avoids underflows on inputs whose result does not underflow. + */ +#ifdef IS_LITTLE_ENDIAN +#define IEEE_8087 +#else +#define IEEE_MC68k +#endif + +#ifndef Long +#define Long int32 +#endif + +#ifndef ULong +#define ULong uint32 +#endif + +#define Bug(errorMessageString) JS_ASSERT(!errorMessageString) + +#include "stdlib.h" +#include "string.h" + +#ifdef MALLOC +extern void *MALLOC(size_t); +#else +#define MALLOC malloc +#endif + +#define Omit_Private_Memory +/* Private memory currently doesn't work with JS_THREADSAFE */ +#ifndef Omit_Private_Memory +#ifndef PRIVATE_MEM +#define PRIVATE_MEM 2000 +#endif +#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) +static double private_mem[PRIVATE_mem], *pmem_next = private_mem; +#endif + +#ifdef Bad_float_h +#undef __STDC__ + +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#define FLT_ROUNDS 1 +#define DBL_MAX 1.7976931348623157e+308 + + + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif + +#else /* ifndef Bad_float_h */ +#include "float.h" +#endif /* Bad_float_h */ + +#ifndef __MATH_H__ +#include "math.h" +#endif + +#ifndef CONST +#define CONST const +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) != 1 +Exactly one of IEEE_8087 or IEEE_MC68k should be defined. +#endif + +#define word0(x) JSDOUBLE_HI32(x) +#define set_word0(x, y) JSDOUBLE_SET_HI32(x, y) +#define word1(x) JSDOUBLE_LO32(x) +#define set_word1(x, y) JSDOUBLE_SET_LO32(x, y) + +#ifdef OSSP +#define Storeinc(a,b,c) (*(a)++ = (b) << 16 | ((c) & 0xffff)) +#else +#define Storeinc(a,b,c) (*(a)++ = (b) << 16 | (c) & 0xffff) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Bias 1023 +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 +#define Infinite(x) (word0(x) == 0x7ff00000) /* sufficient test for here */ +#ifndef NO_IEEE_Scale +#define Avoid_Underflow +#endif + + + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +extern double rnd_prod(double, double), rnd_quot(double, double); +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#ifndef JS_HAVE_LONG_LONG +#undef ULLong +#else /* long long available */ +#ifndef Llong +#define Llong JSInt64 +#endif +#ifndef ULLong +#define ULLong JSUint64 +#endif +#endif /* JS_HAVE_LONG_LONG */ + +#ifdef JS_THREADSAFE +#define MULTIPLE_THREADS +static PRLock *freelist_lock; +#define ACQUIRE_DTOA_LOCK() \ + JS_BEGIN_MACRO \ + if (!initialized) \ + InitDtoa(); \ + PR_Lock(freelist_lock); \ + JS_END_MACRO +#define RELEASE_DTOA_LOCK() PR_Unlock(freelist_lock) +#else +#undef MULTIPLE_THREADS +#define ACQUIRE_DTOA_LOCK() /*nothing*/ +#define RELEASE_DTOA_LOCK() /*nothing*/ +#endif + +#define Kmax 15 + +struct Bigint { + struct Bigint *next; /* Free list link */ + int32 k; /* lg2(maxwds) */ + int32 maxwds; /* Number of words allocated for x */ + int32 sign; /* Zero if positive, 1 if negative. Ignored by most Bigint routines! */ + int32 wds; /* Actual number of words. If value is nonzero, the most significant word must be nonzero. */ + ULong x[1]; /* wds words of number in little endian order */ +}; + +#ifdef ENABLE_OOM_TESTING +/* Out-of-memory testing. Use a good testcase (over and over) and then use + * these routines to cause a memory failure on every possible Balloc allocation, + * to make sure that all out-of-memory paths can be followed. See bug 14044. + */ + +static int allocationNum; /* which allocation is next? */ +static int desiredFailure; /* which allocation should fail? */ + +/** + * js_BigintTestingReset + * + * Call at the beginning of a test run to set the allocation failure position. + * (Set to 0 to just have the engine count allocations without failing.) + */ +JS_PUBLIC_API(void) +js_BigintTestingReset(int newFailure) +{ + allocationNum = 0; + desiredFailure = newFailure; +} + +/** + * js_BigintTestingWhere + * + * Report the current allocation position. This is really only useful when you + * want to learn how many allocations a test run has. + */ +JS_PUBLIC_API(int) +js_BigintTestingWhere() +{ + return allocationNum; +} + + +/* + * So here's what you do: Set up a fantastic test case that exercises the + * elements of the code you wish. Set the failure point at 0 and run the test, + * then get the allocation position. This number is the number of allocations + * your test makes. Now loop from 1 to that number, setting the failure point + * at each loop count, and run the test over and over, causing failures at each + * step. Any memory failure *should* cause a Out-Of-Memory exception; if it + * doesn't, then there's still an error here. + */ +#endif + +typedef struct Bigint Bigint; + +static Bigint *freelist[Kmax+1]; + +/* + * Allocate a Bigint with 2^k words. + * This is not threadsafe. The caller must use thread locks + */ +static Bigint *Balloc(int32 k) +{ + int32 x; + Bigint *rv; +#ifndef Omit_Private_Memory + uint32 len; +#endif + +#ifdef ENABLE_OOM_TESTING + if (++allocationNum == desiredFailure) { + printf("Forced Failing Allocation number %d\n", allocationNum); + return NULL; + } +#endif + + if ((rv = freelist[k]) != NULL) + freelist[k] = rv->next; + if (rv == NULL) { + x = 1 << k; +#ifdef Omit_Private_Memory + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); +#else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); + if (pmem_next - private_mem + len <= PRIVATE_mem) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } + else + rv = (Bigint*)MALLOC(len*sizeof(double)); +#endif + if (!rv) + return NULL; + rv->k = k; + rv->maxwds = x; + } + rv->sign = rv->wds = 0; + return rv; +} + +static void Bfree(Bigint *v) +{ + if (v) { + v->next = freelist[v->k]; + freelist[v->k] = v; + } +} + +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ + y->wds*sizeof(Long) + 2*sizeof(int32)) + +/* Return b*m + a. Deallocate the old b. Both a and m must be between 0 and + * 65535 inclusive. NOTE: old b is deallocated on memory failure. + */ +static Bigint *multadd(Bigint *b, int32 m, int32 a) +{ + int32 i, wds; +#ifdef ULLong + ULong *x; + ULLong carry, y; +#else + ULong carry, *x, y; + ULong xi, z; +#endif + Bigint *b1; + +#ifdef ENABLE_OOM_TESTING + if (++allocationNum == desiredFailure) { + /* Faux allocation, because I'm not getting all of the failure paths + * without it. + */ + printf("Forced Failing Allocation number %d\n", allocationNum); + Bfree(b); + return NULL; + } +#endif + + wds = b->wds; + x = b->x; + i = 0; + carry = a; + do { +#ifdef ULLong + y = *x * (ULLong)m + carry; + carry = y >> 32; + *x++ = (ULong)(y & 0xffffffffUL); +#else + xi = *x; + y = (xi & 0xffff) * m + carry; + z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#endif + } + while(++i < wds); + if (carry) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + if (!b1) { + Bfree(b); + return NULL; + } + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = (ULong)carry; + b->wds = wds; + } + return b; +} + +static Bigint *s2b(CONST char *s, int32 nd0, int32 nd, ULong y9) +{ + Bigint *b; + int32 i, k; + Long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; + b = Balloc(k); + if (!b) + return NULL; + b->x[0] = y9; + b->wds = 1; + + i = 9; + if (9 < nd0) { + s += 9; + do { + b = multadd(b, 10, *s++ - '0'); + if (!b) + return NULL; + } while(++i < nd0); + s++; + } + else + s += 10; + for(; i < nd; i++) { + b = multadd(b, 10, *s++ - '0'); + if (!b) + return NULL; + } + return b; +} + + +/* Return the number (0 through 32) of most significant zero bits in x. */ +static int32 hi0bits(register ULong x) +{ + register int32 k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; +} + + +/* Return the number (0 through 32) of least significant zero bits in y. + * Also shift y to the right past these 0 through 32 zeros so that y's + * least significant bit will be set unless y was originally zero. */ +static int32 lo0bits(ULong *y) +{ + register int32 k; + register ULong x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x & 1) + return 32; + } + *y = x; + return k; +} + +/* Return a new Bigint with the given integer value, which must be nonnegative. */ +static Bigint *i2b(int32 i) +{ + Bigint *b; + + b = Balloc(1); + if (!b) + return NULL; + b->x[0] = i; + b->wds = 1; + return b; +} + +/* Return a newly allocated product of a and b. */ +static Bigint *mult(CONST Bigint *a, CONST Bigint *b) +{ + CONST Bigint *t; + Bigint *c; + int32 k, wa, wb, wc; + ULong y; + ULong *xc, *xc0, *xce; + CONST ULong *x, *xa, *xae, *xb, *xbe; +#ifdef ULLong + ULLong carry, z; +#else + ULong carry, z; + ULong z2; +#endif + + if (a->wds < b->wds) { + t = a; + a = b; + b = t; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k); + if (!c) + return NULL; + for(xc = c->x, xce = xc + wc; xc < xce; xc++) + *xc = 0; + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef ULLong + for(; xb < xbe; xc0++) { + if ((y = *xb++) != 0) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (ULLong)y + *xc + carry; + carry = z >> 32; + *xc++ = (ULong)(z & 0xffffffffUL); + } + while(x < xae); + *xc = (ULong)carry; + } + } +#else + for(; xb < xbe; xb++, xc0++) { + if ((y = *xb & 0xffff) != 0) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } + while(x < xae); + *xc = carry; + } + if ((y = *xb >> 16) != 0) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } + while(x < xae); + *xc = z2; + } + } +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; +} + +/* + * 'p5s' points to a linked list of Bigints that are powers of 5. + * This list grows on demand, and it can only grow: it won't change + * in any other way. So if we read 'p5s' or the 'next' field of + * some Bigint on the list, and it is not NULL, we know it won't + * change to NULL or some other value. Only when the value of + * 'p5s' or 'next' is NULL do we need to acquire the lock and add + * a new Bigint to the list. + */ + +static Bigint *p5s; + +#ifdef JS_THREADSAFE +static PRLock *p5s_lock; +#endif + +/* Return b * 5^k. Deallocate the old b. k must be nonnegative. */ +/* NOTE: old b is deallocated on memory failure. */ +static Bigint *pow5mult(Bigint *b, int32 k) +{ + Bigint *b1, *p5, *p51; + int32 i; + static CONST int32 p05[3] = { 5, 25, 125 }; + + if ((i = k & 3) != 0) { + b = multadd(b, p05[i-1], 0); + if (!b) + return NULL; + } + + if (!(k >>= 2)) + return b; + if (!(p5 = p5s)) { +#ifdef JS_THREADSAFE + /* + * We take great care to not call i2b() and Bfree() + * while holding the lock. + */ + Bigint *wasted_effort = NULL; + p5 = i2b(625); + if (!p5) { + Bfree(b); + return NULL; + } + /* lock and check again */ + PR_Lock(p5s_lock); + if (!p5s) { + /* first time */ + p5s = p5; + p5->next = 0; + } else { + /* some other thread just beat us */ + wasted_effort = p5; + p5 = p5s; + } + PR_Unlock(p5s_lock); + if (wasted_effort) { + Bfree(wasted_effort); + } +#else + /* first time */ + p5 = p5s = i2b(625); + if (!p5) { + Bfree(b); + return NULL; + } + p5->next = 0; +#endif + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5); + Bfree(b); + if (!b1) + return NULL; + b = b1; + } + if (!(k >>= 1)) + break; + if (!(p51 = p5->next)) { +#ifdef JS_THREADSAFE + Bigint *wasted_effort = NULL; + p51 = mult(p5, p5); + if (!p51) { + Bfree(b); + return NULL; + } + PR_Lock(p5s_lock); + if (!p5->next) { + p5->next = p51; + p51->next = 0; + } else { + wasted_effort = p51; + p51 = p5->next; + } + PR_Unlock(p5s_lock); + if (wasted_effort) { + Bfree(wasted_effort); + } +#else + p51 = mult(p5,p5); + if (!p51) { + Bfree(b); + return NULL; + } + p51->next = 0; + p5->next = p51; +#endif + } + p5 = p51; + } + return b; +} + +/* Return b * 2^k. Deallocate the old b. k must be nonnegative. + * NOTE: on memory failure, old b is deallocated. */ +static Bigint *lshift(Bigint *b, int32 k) +{ + int32 i, k1, n, n1; + Bigint *b1; + ULong *x, *x1, *xe, z; + + n = k >> 5; + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1); + if (!b1) + goto done; + x1 = b1->x; + for(i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } + while(x < xe); + if ((*x1 = z) != 0) + ++n1; + } + else do + *x1++ = *x++; + while(x < xe); + b1->wds = n1 - 1; +done: + Bfree(b); + return b1; +} + +/* Return -1, 0, or 1 depending on whether ab, respectively. */ +static int32 cmp(Bigint *a, Bigint *b) +{ + ULong *xa, *xa0, *xb, *xb0; + int32 i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) + Bug("cmp called with a->x[a->wds-1] == 0"); + if (j > 1 && !b->x[j-1]) + Bug("cmp called with b->x[b->wds-1] == 0"); +#endif + if (i -= j) + return i; + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for(;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; +} + +static Bigint *diff(Bigint *a, Bigint *b) +{ + Bigint *c; + int32 i, wa, wb; + ULong *xa, *xae, *xb, *xbe, *xc; +#ifdef ULLong + ULLong borrow, y; +#else + ULong borrow, y; + ULong z; +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + if (!c) + return NULL; + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else + i = 0; + c = Balloc(a->k); + if (!c) + return NULL; + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef ULLong + do { + y = (ULLong)*xa++ - *xb++ - borrow; + borrow = y >> 32 & 1UL; + *xc++ = (ULong)(y & 0xffffffffUL); + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = y >> 32 & 1UL; + *xc++ = (ULong)(y & 0xffffffffUL); + } +#else + do { + y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } + while(xb < xbe); + while(xa < xae) { + y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#endif + while(!*--xc) + wa--; + c->wds = wa; + return c; +} + +/* Return the absolute difference between x and the adjacent greater-magnitude double number (ignoring exponent overflows). */ +static double ulp(double x) +{ + register Long L; + double a = 0; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Sudden_Underflow + if (L > 0) { +#endif + set_word0(a, L); + set_word1(a, 0); +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + set_word0(a, 0x80000 >> L); + set_word1(a, 0); + } + else { + set_word0(a, 0); + L -= Exp_shift; + set_word1(a, L >= 31 ? 1 : 1 << (31 - L)); + } + } +#endif + return a; +} + + +static double b2d(Bigint *a, int32 *e) +{ + ULong *xa, *xa0, w, y, z; + int32 k; + double d = 0; +#define d0 word0(d) +#define d1 word1(d) +#define set_d0(x) set_word0(d, x) +#define set_d1(x) set_word1(d, x) + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) Bug("zero y in b2d"); +#endif + k = hi0bits(y); + *e = 32 - k; + if (k < Ebits) { + set_d0(Exp_1 | y >> (Ebits - k)); + w = xa > xa0 ? *--xa : 0; + set_d1(y << (32-Ebits + k) | w >> (Ebits - k)); + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + set_d0(Exp_1 | y << k | z >> (32 - k)); + y = xa > xa0 ? *--xa : 0; + set_d1(z << k | y >> (32 - k)); + } + else { + set_d0(Exp_1 | y); + set_d1(z); + } + ret_d: +#undef d0 +#undef d1 +#undef set_d0 +#undef set_d1 + return d; +} + + +/* Convert d into the form b*2^e, where b is an odd integer. b is the returned + * Bigint and e is the returned binary exponent. Return the number of significant + * bits in b in bits. d must be finite and nonzero. */ +static Bigint *d2b(double d, int32 *e, int32 *bits) +{ + Bigint *b; + int32 de, i, k; + ULong *x, y, z; +#define d0 word0(d) +#define d1 word1(d) +#define set_d0(x) set_word0(d, x) +#define set_d1(x) set_word1(d, x) + + b = Balloc(1); + if (!b) + return NULL; + x = b->x; + + z = d0 & Frac_mask; + set_d0(d0 & 0x7fffffff); /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int32)(d0 >> Exp_shift); + z |= Exp_msk11; +#else + if ((de = (int32)(d0 >> Exp_shift)) != 0) + z |= Exp_msk1; +#endif + if ((y = d1) != 0) { + if ((k = lo0bits(&y)) != 0) { + x[0] = y | z << (32 - k); + z >>= k; + } + else + x[0] = y; + i = b->wds = (x[1] = z) ? 2 : 1; + } + else { + JS_ASSERT(z); + k = lo0bits(&z); + x[0] = z; + i = b->wds = 1; + k += 32; + } +#ifndef Sudden_Underflow + if (de) { +#endif + *e = de - Bias - (P-1) + k; + *bits = P - k; +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; + *bits = 32*i - hi0bits(x[i-1]); + } +#endif + return b; +} +#undef d0 +#undef d1 +#undef set_d0 +#undef set_d1 + + +static double ratio(Bigint *a, Bigint *b) +{ + double da, db; + int32 k, ka, kb; + + da = b2d(a, &ka); + db = b2d(b, &kb); + k = ka - kb + 32*(a->wds - b->wds); + if (k > 0) + set_word0(da, word0(da) + k*Exp_msk1); + else { + k = -k; + set_word0(db, word0(db) + k*Exp_msk1); + } + return da / db; +} + +static CONST double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +}; + +static CONST double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, +#ifdef Avoid_Underflow + 9007199254740992.e-256 +#else + 1e-256 +#endif + }; +/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ +/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ +#define Scale_Bit 0x10 +#define n_bigtens 5 + + +#ifdef INFNAN_CHECK + +#ifndef NAN_WORD0 +#define NAN_WORD0 0x7ff80000 +#endif + +#ifndef NAN_WORD1 +#define NAN_WORD1 0 +#endif + +static int match(CONST char **sp, char *t) +{ + int c, d; + CONST char *s = *sp; + + while(d = *t++) { + if ((c = *++s) >= 'A' && c <= 'Z') + c += 'a' - 'A'; + if (c != d) + return 0; + } + *sp = s + 1; + return 1; + } +#endif /* INFNAN_CHECK */ + + +#ifdef JS_THREADSAFE +static JSBool initialized = JS_FALSE; + +/* hacked replica of nspr _PR_InitDtoa */ +static void InitDtoa(void) +{ + freelist_lock = PR_NewLock(); + p5s_lock = PR_NewLock(); + initialized = JS_TRUE; +} +#endif + +void js_FinishDtoa(void) +{ + int count; + Bigint *temp; + +#ifdef JS_THREADSAFE + if (initialized == JS_TRUE) { + PR_DestroyLock(freelist_lock); + PR_DestroyLock(p5s_lock); + initialized = JS_FALSE; + } +#endif + + /* clear down the freelist array and p5s */ + + /* static Bigint *freelist[Kmax+1]; */ + for (count = 0; count <= Kmax; count++) { + Bigint **listp = &freelist[count]; + while ((temp = *listp) != NULL) { + *listp = temp->next; + free(temp); + } + freelist[count] = NULL; + } + + /* static Bigint *p5s; */ + while (p5s) { + temp = p5s; + p5s = p5s->next; + free(temp); + } +} + +/* nspr2 watcom bug ifdef omitted */ + +JS_FRIEND_API(double) +JS_strtod(CONST char *s00, char **se, int *err) +{ + int32 scale; + int32 bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, + e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; + CONST char *s, *s0, *s1; + double aadj, aadj1, adj, rv, rv0; + Long L; + ULong y, z; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; + + *err = 0; + + bb = bd = bs = delta = NULL; + sign = nz0 = nz = 0; + rv = 0.; + + /* Locking for Balloc's shared buffers that will be used in this block */ + ACQUIRE_DTOA_LOCK(); + + for(s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + s = s00; + goto ret; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } +break2: + + if (*s == '0') { + nz0 = 1; + while(*++s == '0') ; + if (!*s) + goto ret; + } + s0 = s; + y = z = 0; + for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < 16) + z = 10*z + c - '0'; + nd0 = nd; + if (c == '.') { + c = *++s; + if (!nd) { + for(; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; + if (c -= '0') { + nf += nz; + for(i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 1) + z *= 10; + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 1) + z = 10*z + c; + nz = 0; + } + } + } +dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + s = s00; + goto ret; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while((c = *++s) >= '0' && c <= '9') + L = 10*L + c - '0'; + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (int32)L; + if (esign) + e = -e; + } + else + e = 0; + } + else + s = s00; + } + if (!nd) { + if (!nz && !nz0) { +#ifdef INFNAN_CHECK + /* Check for Nan and Infinity */ + switch(c) { + case 'i': + case 'I': + if (match(&s,"nfinity")) { + word0(rv) = 0x7ff00000; + word1(rv) = 0; + goto ret; + } + break; + case 'n': + case 'N': + if (match(&s, "an")) { + word0(rv) = NAN_WORD0; + word1(rv) = NAN_WORD1; + goto ret; + } + } +#endif /* INFNAN_CHECK */ + s = s00; + } + goto ret; + } + e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + rv = y; + if (k > 9) + rv = tens[k - 9] * rv + z; + bd0 = 0; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT + && FLT_ROUNDS == 1 +#endif + ) { + if (!e) + goto ret; + if (e > 0) { + if (e <= Ten_pmax) { + /* rv = */ rounded_product(rv, tens[e]); + goto ret; + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ + e -= i; + rv *= tens[i]; + /* rv = */ rounded_product(rv, tens[e]); + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { + /* rv = */ rounded_quotient(rv, tens[-e]); + goto ret; + } +#endif + } + e1 += nd - k; + + scale = 0; + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if ((i = e1 & 15) != 0) + rv *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { + ovfl: + *err = JS_DTOA_ERANGE; +#ifdef __STDC__ + rv = HUGE_VAL; +#else + /* Can't trust HUGE_VAL */ + word0(rv) = Exp_mask; + word1(rv) = 0; +#endif + if (bd0) + goto retfree; + goto ret; + } + e1 >>= 4; + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + rv *= bigtens[j]; + /* The last multiplication could overflow. */ + set_word0(rv, word0(rv) - P*Exp_msk1); + rv *= bigtens[j]; + if ((z = word0(rv) & Exp_mask) > Exp_msk1*(DBL_MAX_EXP+Bias-P)) + goto ovfl; + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + set_word0(rv, Big0); + set_word1(rv, Big1); + } + else + set_word0(rv, word0(rv) + P*Exp_msk1); + } + } + else if (e1 < 0) { + e1 = -e1; + if ((i = e1 & 15) != 0) + rv /= tens[i]; + if (e1 &= ~15) { + e1 >>= 4; + if (e1 >= 1 << n_bigtens) + goto undfl; +#ifdef Avoid_Underflow + if (e1 & Scale_Bit) + scale = P; + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + rv *= tinytens[j]; + if (scale && (j = P + 1 - ((word0(rv) & Exp_mask) + >> Exp_shift)) > 0) { + /* scaled rv is denormal; zap j low bits */ + if (j >= 32) { + set_word1(rv, 0); + set_word0(rv, word0(rv) & (0xffffffff << (j-32))); + if (!word0(rv)) + set_word0(rv, 1); + } + else + set_word1(rv, word1(rv) & (0xffffffff << j)); + } +#else + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + rv *= tinytens[j]; + /* The last multiplication could underflow. */ + rv0 = rv; + rv *= tinytens[j]; + if (!rv) { + rv = 2.*rv0; + rv *= tinytens[j]; +#endif + if (!rv) { + undfl: + rv = 0.; + *err = JS_DTOA_ERANGE; + if (bd0) + goto retfree; + goto ret; + } +#ifndef Avoid_Underflow + set_word0(rv, Tiny0); + set_word1(rv, Tiny1); + /* The refinement below will clean + * this approximation up. + */ + } +#endif + } + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bd0 = s2b(s0, nd0, nd, y); + if (!bd0) + goto nomem; + + for(;;) { + bd = Balloc(bd0->k); + if (!bd) + goto nomem; + Bcopy(bd, bd0); + bb = d2b(rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ + if (!bb) + goto nomem; + bs = i2b(1); + if (!bs) + goto nomem; + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; +#ifdef Sudden_Underflow + j = P + 1 - bbbits; +#else +#ifdef Avoid_Underflow + j = bbe - scale; +#else + j = bbe; +#endif + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#endif + bb2 += j; + bd2 += j; +#ifdef Avoid_Underflow + bd2 += scale; +#endif + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5); + if (!bs) + goto nomem; + bb1 = mult(bs, bb); + if (!bb1) + goto nomem; + Bfree(bb); + bb = bb1; + } + if (bb2 > 0) { + bb = lshift(bb, bb2); + if (!bb) + goto nomem; + } + if (bd5 > 0) { + bd = pow5mult(bd, bd5); + if (!bd) + goto nomem; + } + if (bd2 > 0) { + bd = lshift(bd, bd2); + if (!bd) + goto nomem; + } + if (bs2 > 0) { + bs = lshift(bs, bs2); + if (!bs) + goto nomem; + } + delta = diff(bb, bd); + if (!delta) + goto nomem; + dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask +#ifdef Avoid_Underflow + || (word0(rv) & Exp_mask) <= Exp_msk1 + P*Exp_msk1 +#else + || (word0(rv) & Exp_mask) <= Exp_msk1 +#endif + ) { +#ifdef Avoid_Underflow + if (!delta->x[0] && delta->wds == 1) + dsign = 2; +#endif + break; + } + delta = lshift(delta,Log2P); + if (!delta) + goto nomem; + if (cmp(delta, bs) > 0) + goto drop_down; + break; + } + if (i == 0) { + /* exactly half-way between */ + if (dsign) { + if ((word0(rv) & Bndry_mask1) == Bndry_mask1 + && word1(rv) == 0xffffffff) { + /*boundary case -- increment exponent*/ + set_word0(rv, (word0(rv) & Exp_mask) + Exp_msk1); + set_word1(rv, 0); +#ifdef Avoid_Underflow + dsign = 0; +#endif + break; + } + } + else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { +#ifdef Avoid_Underflow + dsign = 2; +#endif + drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow + L = word0(rv) & Exp_mask; + if (L <= Exp_msk1) + goto undfl; + L -= Exp_msk1; +#else + L = (word0(rv) & Exp_mask) - Exp_msk1; +#endif + set_word0(rv, L | Bndry_mask1); + set_word1(rv, 0xffffffff); + break; + } +#ifndef ROUND_BIASED + if (!(word1(rv) & LSB)) + break; +#endif + if (dsign) + rv += ulp(rv); +#ifndef ROUND_BIASED + else { + rv -= ulp(rv); +#ifndef Sudden_Underflow + if (!rv) + goto undfl; +#endif + } +#ifdef Avoid_Underflow + dsign = 1 - dsign; +#endif +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (dsign) + aadj = aadj1 = 1.; + else if (word1(rv) || word0(rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(rv) == Tiny1 && !word0(rv)) + goto undfl; +#endif + aadj = 1.; + aadj1 = -1.; + } + else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) + aadj = 1./FLT_RADIX; + else + aadj *= 0.5; + aadj1 = -aadj; + } + } + else { + aadj *= 0.5; + aadj1 = dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(FLT_ROUNDS) { + case 2: /* towards +infinity */ + aadj1 -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + aadj1 += 0.5; + } +#else + if (FLT_ROUNDS == 0) + aadj1 += 0.5; +#endif + } + y = word0(rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + rv0 = rv; + set_word0(rv, word0(rv) - P*Exp_msk1); + adj = aadj1 * ulp(rv); + rv += adj; + if ((word0(rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(rv0) == Big0 && word1(rv0) == Big1) + goto ovfl; + set_word0(rv, Big0); + set_word1(rv, Big1); + goto cont; + } + else + set_word0(rv, word0(rv) + P*Exp_msk1); + } + else { +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { + rv0 = rv; + set_word0(rv, word0(rv) + P*Exp_msk1); + adj = aadj1 * ulp(rv); + rv += adj; + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) + { + if (word0(rv0) == Tiny0 + && word1(rv0) == Tiny1) + goto undfl; + set_word0(rv, Tiny0); + set_word1(rv, Tiny1); + goto cont; + } + else + set_word0(rv, word0(rv) - P*Exp_msk1); + } + else { + adj = aadj1 * ulp(rv); + rv += adj; + } +#else + /* Compute adj so that the IEEE rounding rules will + * correctly round rv + adj in some half-way cases. + * If rv * ulp(rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ +#ifdef Avoid_Underflow + if (y <= P*Exp_msk1 && aadj > 1.) +#else + if (y <= (P-1)*Exp_msk1 && aadj > 1.) +#endif + { + aadj1 = (double)(int32)(aadj + 0.5); + if (!dsign) + aadj1 = -aadj1; + } +#ifdef Avoid_Underflow + if (scale && y <= P*Exp_msk1) + set_word0(aadj1, word0(aadj1) + (P+1)*Exp_msk1 - y); +#endif + adj = aadj1 * ulp(rv); + rv += adj; +#endif + } + z = word0(rv) & Exp_mask; +#ifdef Avoid_Underflow + if (!scale) +#endif + if (y == z) { + /* Can we stop now? */ + L = (Long)aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) + break; + } + else if (aadj < .4999999/FLT_RADIX) + break; + } + cont: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); + bb = bd = bs = delta = NULL; + } +#ifdef Avoid_Underflow + if (scale) { +#ifdef OSSP + rv0 = 0.; /* calm the compiler warning */ +#endif + set_word0(rv0, Exp_1 - P*Exp_msk1); + set_word1(rv0, 0); + if ((word0(rv) & Exp_mask) <= P*Exp_msk1 + && word1(rv) & 1 + && dsign != 2) { + if (dsign) { +#ifdef Sudden_Underflow + /* rv will be 0, but this would give the */ + /* right result if only rv *= rv0 worked. */ + set_word0(rv, word0(rv) + P*Exp_msk1); + set_word0(rv0, Exp_1 - 2*P*Exp_msk1); +#endif + rv += ulp(rv); + } + else + set_word1(rv, word1(rv) & ~1); + } + rv *= rv0; + } +#endif /* Avoid_Underflow */ +retfree: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); +ret: + RELEASE_DTOA_LOCK(); + if (se) + *se = (char *)s; + return sign ? -rv : rv; + +nomem: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); + *err = JS_DTOA_ENOMEM; + return 0; +} + + +/* Return floor(b/2^k) and set b to be the remainder. The returned quotient must be less than 2^32. */ +static uint32 quorem2(Bigint *b, int32 k) +{ + ULong mask; + ULong result; + ULong *bx, *bxe; + int32 w; + int32 n = k >> 5; + k &= 0x1F; + mask = (1<wds - n; + if (w <= 0) + return 0; + JS_ASSERT(w <= 2); + bx = b->x; + bxe = bx + n; + result = *bxe >> k; + *bxe &= mask; + if (w == 2) { + JS_ASSERT(!(bxe[1] & ~mask)); + if (k) + result |= bxe[1] << (32 - k); + } + n++; + while (!*bxe && bxe != bx) { + n--; + bxe--; + } + b->wds = n; + return result; +} + +/* Return floor(b/S) and set b to be the remainder. As added restrictions, b must not have + * more words than S, the most significant word of S must not start with a 1 bit, and the + * returned quotient must be less than 36. */ +static int32 quorem(Bigint *b, Bigint *S) +{ + int32 n; + ULong *bx, *bxe, q, *sx, *sxe; +#ifdef ULLong + ULLong borrow, carry, y, ys; +#else + ULong borrow, carry, y, ys; + ULong si, z, zs; +#endif + + n = S->wds; + JS_ASSERT(b->wds <= n); + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + JS_ASSERT(*sxe <= 0x7FFFFFFF); + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ + JS_ASSERT(q < 36); + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef ULLong + ys = *sx++ * (ULLong)q + carry; + carry = ys >> 32; + y = *bx - (ys & 0xffffffffUL) - borrow; + borrow = y >> 32 & 1UL; + *bx++ = (ULong)(y & 0xffffffffUL); +#else + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#endif + } + while(sx <= sxe); + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef ULLong + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & 0xffffffffUL) - borrow; + borrow = y >> 32 & 1UL; + *bx++ = (ULong)(y & 0xffffffffUL); +#else + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#endif + } while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return (int32)q; +} + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + +/* Always emits at least one digit. */ +/* If biasUp is set, then rounding in modes 2 and 3 will round away from zero + * when the number is exactly halfway between two representable values. For example, + * rounding 2.5 to zero digits after the decimal point will return 3 and not 2. + * 2.49 will still round to 2, and 2.51 will still round to 3. */ +/* bufsize should be at least 20 for modes 0 and 1. For the other modes, + * bufsize should be two greater than the maximum number of output characters expected. */ +static JSBool +js_dtoa(double d, int mode, JSBool biasUp, int ndigits, + int *decpt, int *sign, char **rve, char *buf, size_t bufsize) +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4-9 should give the same return values as 2-3, i.e., + 4 <= mode <= 9 ==> same return as mode + 2 + (mode & 1). These modes are mainly for + debugging; often they run slower but sometimes + faster than modes 2-3. + 4,5,8,9 ==> left-to-right digit generation. + 6-9 ==> don't try fast floating-point estimate + (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int32 bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + int32 denorm; + ULong x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + double d2, ds, eps; + char *s; + + if (word0(d) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + set_word0(d, word0(d) & ~Sign_bit); /* clear sign bit */ + } + else + *sign = 0; + + if ((word0(d) & Exp_mask) == Exp_mask) { + /* Infinity or NaN */ + *decpt = 9999; + s = !word1(d) && !(word0(d) & Frac_mask) ? "Infinity" : "NaN"; + if ((s[0] == 'I' && bufsize < 9) || (s[0] == 'N' && bufsize < 4)) { + JS_ASSERT(JS_FALSE); +/* JS_SetError(JS_BUFFER_OVERFLOW_ERROR, 0); */ + return JS_FALSE; + } + strcpy(buf, s); + if (rve) { + *rve = buf[3] ? buf + 8 : buf + 3; + JS_ASSERT(**rve == '\0'); + } + return JS_TRUE; + } + + b = NULL; /* initialize for abort protection */ + S = NULL; + mlo = mhi = NULL; + + if (!d) { + no_digits: + *decpt = 1; + if (bufsize < 2) { + JS_ASSERT(JS_FALSE); +/* JS_SetError(JS_BUFFER_OVERFLOW_ERROR, 0); */ + return JS_FALSE; + } + buf[0] = '0'; buf[1] = '\0'; /* copy "0" to buffer */ + if (rve) + *rve = buf + 1; + /* We might have jumped to "no_digits" from below, so we need + * to be sure to free the potentially allocated Bigints to avoid + * memory leaks. */ + Bfree(b); + Bfree(S); + if (mlo != mhi) + Bfree(mlo); + Bfree(mhi); + return JS_TRUE; + } + + b = d2b(d, &be, &bbits); + if (!b) + goto nomem; +#ifdef Sudden_Underflow + i = (int32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if ((i = (int32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) { +#endif + d2 = d; + set_word0(d2, word0(d2) & Frac_mask1); + set_word0(d2, word0(d2) | Exp_11); + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifndef Sudden_Underflow + denorm = 0; + } + else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(d) << (64 - i) | word1(d) >> (i - 32) : word1(d) << (32 - i); + d2 = x; + set_word0(d2, word0(d2) - 31*Exp_msk1); /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + /* At this point d = f*2^i, where 1 <= f < 2. d2 is an approximation of f. */ + ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int32)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (d < tens[k]) + k--; + k_check = 0; + } + /* At this point floor(log10(d)) <= k <= floor(log10(d))+1. + If k_check is zero, we're guaranteed that k = floor(log10(d)). */ + j = bbits - i - 1; + /* At this point d = b/2^j, where b is an odd integer. */ + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + /* At this point d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5), where b is an odd integer, + b2 >= 0, b5 >= 0, s2 >= 0, and s5 >= 0. */ + if (mode < 0 || mode > 9) + mode = 0; + try_quick = 1; + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + ilim = ilim1 = 0; + switch(mode) { + case 0: + case 1: + ilim = ilim1 = -1; + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + /* ilim is the maximum number of significant digits we want, based on k and ndigits. */ + /* ilim1 is the maximum number of significant digits we want, based on k and ndigits, + when it turns out that k was computed too high by one. */ + + /* Ensure space for at least i+1 characters, including trailing null. */ + if (bufsize <= (size_t)i) { + Bfree(b); + JS_ASSERT(JS_FALSE); + return JS_FALSE; + } + s = buf; + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + d2 = d; + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + /* Divide d by 10^k, keeping track of the roundoff error and avoiding overflows. */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + d /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + d /= ds; + } + else if ((j1 = -k) != 0) { + d *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + d *= bigtens[i]; + } + } + /* Check that k was computed correctly. */ + if (k_check && d < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + d *= 10.; + ieps++; + } + /* eps bounds the cumulative error. */ + eps = ieps*d + 7.; + set_word0(eps, word0(eps) - (P-1)*Exp_msk1); + if (ilim == 0) { + S = mhi = 0; + d -= 5.; + if (d > eps) + goto one_digit; + if (d < -eps) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + eps = 0.5/tens[ilim-1] - eps; + for(i = 0;;) { + L = (Long)d; + d -= L; + *s++ = '0' + (char)L; + if (d < eps) + goto ret1; + if (1. - d < eps) + goto bump_up; + if (++i >= ilim) + break; + eps *= 10.; + d *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + eps *= tens[ilim-1]; + for(i = 1;; i++, d *= 10.) { + L = (Long)d; + d -= L; + *s++ = '0' + (char)L; + if (i == ilim) { + if (d > 0.5 + eps) + goto bump_up; + else if (d < 0.5 - eps) { + while(*--s == '0') ; + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = buf; + d = d2; + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || d < 5*ds || (!biasUp && d == 5*ds)) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++) { + L = (Long) (d / ds); + d -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (d < 0) { + L--; + d += ds; + } +#endif + *s++ = '0' + (char)L; + if (i == ilim) { + d += d; + if ((d > ds) || (d == ds && (L & 1 || biasUp))) { + bump_up: + while(*--s == '9') + if (s == buf) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + if (!(d *= 10.)) + break; + } + goto ret1; + } + + m2 = b2; + m5 = b5; + if (leftright) { + if (mode < 2) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif + 1 + P - bbits; + /* i is 1 plus the number of trailing zero bits in d's significand. Thus, + (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 lsb of d)/10^k. */ + } + else { + j = ilim - 1; + if (m5 >= j) + m5 -= j; + else { + s5 += j -= m5; + b5 += j; + m5 = 0; + } + if ((i = ilim) < 0) { + m2 -= i; + i = 0; + } + /* (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 * 10^(1-ilim))/10^k. */ + } + b2 += i; + s2 += i; + mhi = i2b(1); + if (!mhi) + goto nomem; + /* (mhi * 2^m2 * 5^m5) / (2^s2 * 5^s5) = one-half of last printed (when mode >= 2) or + input (when mode < 2) significant digit, divided by 10^k. */ + } + /* We still have d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5). Reduce common factors in + b2, m2, and s2 without changing the equalities. */ + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + + /* Fold b5 into b and m5 into mhi. */ + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + if (!mhi) + goto nomem; + b1 = mult(mhi, b); + if (!b1) + goto nomem; + Bfree(b); + b = b1; + } + if ((j = b5 - m5) != 0) { + b = pow5mult(b, j); + if (!b) + goto nomem; + } + } + else { + b = pow5mult(b, b5); + if (!b) + goto nomem; + } + } + /* Now we have d/10^k = (b * 2^b2) / (2^s2 * 5^s5) and + (mhi * 2^m2) / (2^s2 * 5^s5) = one-half of last printed or input significant digit, divided by 10^k. */ + + S = i2b(1); + if (!S) + goto nomem; + if (s5 > 0) { + S = pow5mult(S, s5); + if (!S) + goto nomem; + } + /* Now we have d/10^k = (b * 2^b2) / (S * 2^s2) and + (mhi * 2^m2) / (S * 2^s2) = one-half of last printed or input significant digit, divided by 10^k. */ + + /* Check for special case that d is a normalized power of 2. */ + spec_case = 0; + if (mode < 2) { + if (!word1(d) && !(word0(d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(d) & (Exp_mask & Exp_mask << 1) +#endif + ) { + /* The special case. Here we want to be within a quarter of the last input + significant digit instead of one half of it when the decimal output string's value is less than d. */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ + if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0) + i = 32 - i; + /* i is the number of leading zero bits in the most significant word of S*2^s2. */ + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } + else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + /* Now S*2^s2 has exactly four leading zero bits in its most significant word. */ + if (b2 > 0) { + b = lshift(b, b2); + if (!b) + goto nomem; + } + if (s2 > 0) { + S = lshift(S, s2); + if (!S) + goto nomem; + } + /* Now we have d/10^k = b/S and + (mhi * 2^m2) / S = maximum acceptable error, divided by 10^k. */ + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (!b) + goto nomem; + if (leftright) { + mhi = multadd(mhi, 10, 0); + if (!mhi) + goto nomem; + } + ilim = ilim1; + } + } + /* At this point 1 <= d/10^k = b/S < 10. */ + + if (ilim <= 0 && mode > 2) { + /* We're doing fixed-mode output and d is less than the minimum nonzero output in this mode. + Output either zero or the minimum nonzero output depending on which is closer to d. */ + if (ilim < 0) + goto no_digits; + S = multadd(S,5,0); + if (!S) + goto nomem; + i = cmp(b,S); + if (i < 0 || (i == 0 && !biasUp)) { + /* Always emit at least one digit. If the number appears to be zero + using the current mode, then emit one '0' digit and set decpt to 1. */ + /*no_digits: + k = -1 - ndigits; + goto ret; */ + goto no_digits; + } + one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) { + mhi = lshift(mhi, m2); + if (!mhi) + goto nomem; + } + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + if (!mhi) + goto nomem; + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + if (!mhi) + goto nomem; + } + /* mlo/S = maximum acceptable error, divided by 10^k, if the output is less than d. */ + /* mhi/S = maximum acceptable error, divided by 10^k, if the output is greater than d. */ + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + /* j is b/S compared with mlo/S. */ + delta = diff(S, mhi); + if (!delta) + goto nomem; + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); + /* j1 is b/S compared with 1 - mhi/S. */ +#ifndef ROUND_BIASED + if (j1 == 0 && !mode && !(word1(d) & 1)) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; + *s++ = (char)dig; + goto ret; + } +#endif + if ((j < 0) || (j == 0 && !mode +#ifndef ROUND_BIASED + && !(word1(d) & 1) +#endif + )) { + if (j1 > 0) { + /* Either dig or dig+1 would work here as the least significant decimal digit. + Use whichever would produce a decimal value closer to d. */ + b = lshift(b, 1); + if (!b) + goto nomem; + j1 = cmp(b, S); + if (((j1 > 0) || (j1 == 0 && (dig & 1 || biasUp))) + && (dig++ == '9')) + goto round_9_up; + } + *s++ = (char)dig; + goto ret; + } + if (j1 > 0) { + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = (char)dig + 1; + goto ret; + } + *s++ = (char)dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (!b) + goto nomem; + if (mlo == mhi) { + mlo = mhi = multadd(mhi, 10, 0); + if (!mhi) + goto nomem; + } + else { + mlo = multadd(mlo, 10, 0); + if (!mlo) + goto nomem; + mhi = multadd(mhi, 10, 0); + if (!mhi) + goto nomem; + } + } + } + else + for(i = 1;; i++) { + *s++ = (char)(dig = quorem(b,S) + '0'); + if (i >= ilim) + break; + b = multadd(b, 10, 0); + if (!b) + goto nomem; + } + + /* Round off last digit */ + + b = lshift(b, 1); + if (!b) + goto nomem; + j = cmp(b, S); + if ((j > 0) || (j == 0 && (dig & 1 || biasUp))) { + roundoff: + while(*--s == '9') + if (s == buf) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { + /* Strip trailing zeros */ + while(*--s == '0') ; + s++; + } + ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + ret1: + Bfree(b); + JS_ASSERT(s < buf + bufsize); + *s = '\0'; + if (rve) + *rve = s; + *decpt = k + 1; + return JS_TRUE; + +nomem: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + Bfree(b); + return JS_FALSE; +} + + +/* Mapping of JSDToStrMode -> js_dtoa mode */ +static const int dtoaModes[] = { + 0, /* DTOSTR_STANDARD */ + 0, /* DTOSTR_STANDARD_EXPONENTIAL, */ + 3, /* DTOSTR_FIXED, */ + 2, /* DTOSTR_EXPONENTIAL, */ + 2}; /* DTOSTR_PRECISION */ + +JS_FRIEND_API(char *) +JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double d) +{ + int decPt; /* Position of decimal point relative to first digit returned by js_dtoa */ + int sign; /* Nonzero if the sign bit was set in d */ + int nDigits; /* Number of significand digits returned by js_dtoa */ + char *numBegin = buffer+2; /* Pointer to the digits returned by js_dtoa; the +2 leaves space for */ + /* the sign and/or decimal point */ + char *numEnd; /* Pointer past the digits returned by js_dtoa */ + JSBool dtoaRet; + + JS_ASSERT(bufferSize >= (size_t)(mode <= DTOSTR_STANDARD_EXPONENTIAL ? DTOSTR_STANDARD_BUFFER_SIZE : + DTOSTR_VARIABLE_BUFFER_SIZE(precision))); + + if (mode == DTOSTR_FIXED && (d >= 1e21 || d <= -1e21)) + mode = DTOSTR_STANDARD; /* Change mode here rather than below because the buffer may not be large enough to hold a large integer. */ + + /* Locking for Balloc's shared buffers */ + ACQUIRE_DTOA_LOCK(); + dtoaRet = js_dtoa(d, dtoaModes[mode], mode >= DTOSTR_FIXED, precision, &decPt, &sign, &numEnd, numBegin, bufferSize-2); + RELEASE_DTOA_LOCK(); + if (!dtoaRet) + return 0; + + nDigits = numEnd - numBegin; + + /* If Infinity, -Infinity, or NaN, return the string regardless of the mode. */ + if (decPt != 9999) { + JSBool exponentialNotation = JS_FALSE; + int minNDigits = 0; /* Minimum number of significand digits required by mode and precision */ + char *p; + char *q; + + switch (mode) { + case DTOSTR_STANDARD: + if (decPt < -5 || decPt > 21) + exponentialNotation = JS_TRUE; + else + minNDigits = decPt; + break; + + case DTOSTR_FIXED: + if (precision >= 0) + minNDigits = decPt + precision; + else + minNDigits = decPt; + break; + + case DTOSTR_EXPONENTIAL: + JS_ASSERT(precision > 0); + minNDigits = precision; + /* Fall through */ + case DTOSTR_STANDARD_EXPONENTIAL: + exponentialNotation = JS_TRUE; + break; + + case DTOSTR_PRECISION: + JS_ASSERT(precision > 0); + minNDigits = precision; + if (decPt < -5 || decPt > precision) + exponentialNotation = JS_TRUE; + break; + } + + /* If the number has fewer than minNDigits, pad it with zeros at the end */ + if (nDigits < minNDigits) { + p = numBegin + minNDigits; + nDigits = minNDigits; + do { + *numEnd++ = '0'; + } while (numEnd != p); + *numEnd = '\0'; + } + + if (exponentialNotation) { + /* Insert a decimal point if more than one significand digit */ + if (nDigits != 1) { + numBegin--; + numBegin[0] = numBegin[1]; + numBegin[1] = '.'; + } + JS_snprintf(numEnd, bufferSize - (numEnd - buffer), "e%+d", decPt-1); + } else if (decPt != nDigits) { + /* Some kind of a fraction in fixed notation */ + JS_ASSERT(decPt <= nDigits); + if (decPt > 0) { + /* dd...dd . dd...dd */ + p = --numBegin; + do { + *p = p[1]; + p++; + } while (--decPt); + *p = '.'; + } else { + /* 0 . 00...00dd...dd */ + p = numEnd; + numEnd += 1 - decPt; + q = numEnd; + JS_ASSERT(numEnd < buffer + bufferSize); + *numEnd = '\0'; + while (p != numBegin) + *--q = *--p; + for (p = numBegin + 1; p != q; p++) + *p = '0'; + *numBegin = '.'; + *--numBegin = '0'; + } + } + } + + /* If negative and neither -0.0 nor NaN, output a leading '-'. */ + if (sign && + !(word0(d) == Sign_bit && word1(d) == 0) && + !((word0(d) & Exp_mask) == Exp_mask && + (word1(d) || (word0(d) & Frac_mask)))) { + *--numBegin = '-'; + } + return numBegin; +} + + +/* Let b = floor(b / divisor), and return the remainder. b must be nonnegative. + * divisor must be between 1 and 65536. + * This function cannot run out of memory. */ +static uint32 +divrem(Bigint *b, uint32 divisor) +{ + int32 n = b->wds; + uint32 remainder = 0; + ULong *bx; + ULong *bp; + + JS_ASSERT(divisor > 0 && divisor <= 65536); + + if (!n) + return 0; /* b is zero */ + bx = b->x; + bp = bx + n; + do { + ULong a = *--bp; + ULong dividend = remainder << 16 | a >> 16; + ULong quotientHi = dividend / divisor; + ULong quotientLo; + + remainder = dividend - quotientHi*divisor; + JS_ASSERT(quotientHi <= 0xFFFF && remainder < divisor); + dividend = remainder << 16 | (a & 0xFFFF); + quotientLo = dividend / divisor; + remainder = dividend - quotientLo*divisor; + JS_ASSERT(quotientLo <= 0xFFFF && remainder < divisor); + *bp = quotientHi << 16 | quotientLo; + } while (bp != bx); + /* Decrease the size of the number if its most significant word is now zero. */ + if (bx[n-1] == 0) + b->wds--; + return remainder; +} + + +/* "-0.0000...(1073 zeros after decimal point)...0001\0" is the longest string that we could produce, + * which occurs when printing -5e-324 in binary. We could compute a better estimate of the size of + * the output string and malloc fewer bytes depending on d and base, but why bother? */ +#define DTOBASESTR_BUFFER_SIZE 1078 +#define BASEDIGIT(digit) ((char)(((digit) >= 10) ? 'a' - 10 + (digit) : '0' + (digit))) + +JS_FRIEND_API(char *) +JS_dtobasestr(int base, double d) +{ + char *buffer; /* The output string */ + char *p; /* Pointer to current position in the buffer */ + char *pInt; /* Pointer to the beginning of the integer part of the string */ + char *q; + uint32 digit; + double di; /* d truncated to an integer */ + double df; /* The fractional part of d */ + + JS_ASSERT(base >= 2 && base <= 36); + + buffer = (char*) malloc(DTOBASESTR_BUFFER_SIZE); + if (buffer) { + p = buffer; + if (d < 0.0 +#if defined(XP_WIN) || defined(XP_OS2) + && !((word0(d) & Exp_mask) == Exp_mask && ((word0(d) & Frac_mask) || word1(d))) /* Visual C++ doesn't know how to compare against NaN */ +#endif + ) { + *p++ = '-'; + d = -d; + } + + /* Check for Infinity and NaN */ + if ((word0(d) & Exp_mask) == Exp_mask) { + strcpy(p, !word1(d) && !(word0(d) & Frac_mask) ? "Infinity" : "NaN"); + return buffer; + } + + /* Locking for Balloc's shared buffers */ + ACQUIRE_DTOA_LOCK(); + + /* Output the integer part of d with the digits in reverse order. */ + pInt = p; + di = fd_floor(d); + if (di <= 4294967295.0) { + uint32 n = (uint32)di; + if (n) + do { + uint32 m = n / base; + digit = n - m*base; + n = m; + JS_ASSERT(digit < (uint32)base); + *p++ = BASEDIGIT(digit); + } while (n); + else *p++ = '0'; + } else { + int32 e; + int32 bits; /* Number of significant bits in di; not used. */ + Bigint *b = d2b(di, &e, &bits); + if (!b) + goto nomem1; + b = lshift(b, e); + if (!b) { + nomem1: + Bfree(b); + return NULL; + } + do { + digit = divrem(b, base); + JS_ASSERT(digit < (uint32)base); + *p++ = BASEDIGIT(digit); + } while (b->wds); + Bfree(b); + } + /* Reverse the digits of the integer part of d. */ + q = p-1; + while (q > pInt) { + char ch = *pInt; + *pInt++ = *q; + *q-- = ch; + } + + df = d - di; + if (df != 0.0) { + /* We have a fraction. */ + int32 e, bbits, s2, done; + Bigint *b, *s, *mlo, *mhi; + + b = s = mlo = mhi = NULL; + + *p++ = '.'; + b = d2b(df, &e, &bbits); + if (!b) { + nomem2: + Bfree(b); + Bfree(s); + if (mlo != mhi) + Bfree(mlo); + Bfree(mhi); + return NULL; + } + JS_ASSERT(e < 0); + /* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */ + + s2 = -(int32)(word0(d) >> Exp_shift1 & Exp_mask>>Exp_shift1); +#ifndef Sudden_Underflow + if (!s2) + s2 = -1; +#endif + s2 += Bias + P; + /* 1/2^s2 = (nextDouble(d) - d)/2 */ + JS_ASSERT(-s2 < e); + mlo = i2b(1); + if (!mlo) + goto nomem2; + mhi = mlo; + if (!word1(d) && !(word0(d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(d) & (Exp_mask & Exp_mask << 1) +#endif + ) { + /* The special case. Here we want to be within a quarter of the last input + significant digit instead of one half of it when the output string's value is less than d. */ + s2 += Log2P; + mhi = i2b(1< df = b/2^s2 > 0; + * (d - prevDouble(d))/2 = mlo/2^s2; + * (nextDouble(d) - d)/2 = mhi/2^s2. */ + + done = JS_FALSE; + do { + int32 j, j1; + Bigint *delta; + + b = multadd(b, base, 0); + if (!b) + goto nomem2; + digit = quorem2(b, s2); + if (mlo == mhi) { + mlo = mhi = multadd(mlo, base, 0); + if (!mhi) + goto nomem2; + } + else { + mlo = multadd(mlo, base, 0); + if (!mlo) + goto nomem2; + mhi = multadd(mhi, base, 0); + if (!mhi) + goto nomem2; + } + + /* Do we yet have the shortest string that will round to d? */ + j = cmp(b, mlo); + /* j is b/2^s2 compared with mlo/2^s2. */ + delta = diff(s, mhi); + if (!delta) + goto nomem2; + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); + /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */ + +#ifndef ROUND_BIASED + if (j1 == 0 && !(word1(d) & 1)) { + if (j > 0) + digit++; + done = JS_TRUE; + } else +#endif + if (j < 0 || (j == 0 +#ifndef ROUND_BIASED + && !(word1(d) & 1) +#endif + )) { + if (j1 > 0) { + /* Either dig or dig+1 would work here as the least significant digit. + Use whichever would produce an output value closer to d. */ + b = lshift(b, 1); + if (!b) + goto nomem2; + j1 = cmp(b, s); + if (j1 > 0) /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output + * such as 3.5 in base 3. */ + digit++; + } + done = JS_TRUE; + } else if (j1 > 0) { + digit++; + done = JS_TRUE; + } + JS_ASSERT(digit < (uint32)base); + *p++ = BASEDIGIT(digit); + } while (!done); + Bfree(b); + Bfree(s); + if (mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + JS_ASSERT(p < buffer + DTOBASESTR_BUFFER_SIZE); + *p = '\0'; + RELEASE_DTOA_LOCK(); + } + return buffer; +} Index: ossp-pkg/js/src/jsinterp.c RCS File: /v/ossp/cvs/ossp-pkg/js/src/jsinterp.c,v co -q -kk -p'1.3' '/v/ossp/cvs/ossp-pkg/js/src/jsinterp.c,v' | diff -u /dev/null - -L'ossp-pkg/js/src/jsinterp.c' 2>/dev/null --- ossp-pkg/js/src/jsinterp.c +++ - 2024-05-12 07:43:25.699978037 +0200 @@ -0,0 +1,5550 @@ +/* -*- 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 bytecode interpreter. + */ +#include "jsstddef.h" +#include +#include +#include +#include "jstypes.h" +#include "jsarena.h" /* Added by JSIFY */ +#include "jsutil.h" /* Added by JSIFY */ +#include "jsprf.h" +#include "jsapi.h" +#include "jsarray.h" +#include "jsatom.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jsconfig.h" +#include "jsdbgapi.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jsinterp.h" +#include "jslock.h" +#include "jsnum.h" +#include "jsobj.h" +#include "jsopcode.h" +#include "jsscope.h" +#include "jsscript.h" +#include "jsstr.h" + +#if JS_HAS_JIT +#include "jsjit.h" +#endif + +#if JS_HAS_XML_SUPPORT +#include "jsxml.h" +#endif + +#ifdef DEBUG +#define ASSERT_CACHE_IS_EMPTY(cache) \ + JS_BEGIN_MACRO \ + JSPropertyCacheEntry *end_, *pce_, entry_; \ + JSPropertyCache *cache_ = (cache); \ + JS_ASSERT(cache_->empty); \ + end_ = &cache_->table[PROPERTY_CACHE_SIZE]; \ + for (pce_ = &cache_->table[0]; pce_ < end_; pce_++) { \ + PCE_LOAD(cache_, pce_, entry_); \ + JS_ASSERT(!PCE_OBJECT(entry_)); \ + JS_ASSERT(!PCE_PROPERTY(entry_)); \ + } \ + JS_END_MACRO +#else +#define ASSERT_CACHE_IS_EMPTY(cache) ((void)0) +#endif + +void +js_FlushPropertyCache(JSContext *cx) +{ + JSPropertyCache *cache; + + cache = &cx->runtime->propertyCache; + if (cache->empty) { + ASSERT_CACHE_IS_EMPTY(cache); + return; + } + memset(cache->table, 0, sizeof cache->table); + cache->empty = JS_TRUE; +#ifdef JS_PROPERTY_CACHE_METERING + cache->flushes++; +#endif +} + +void +js_DisablePropertyCache(JSContext *cx) +{ + JS_ASSERT(!cx->runtime->propertyCache.disabled); + cx->runtime->propertyCache.disabled = JS_TRUE; +} + +void +js_EnablePropertyCache(JSContext *cx) +{ + JS_ASSERT(cx->runtime->propertyCache.disabled); + ASSERT_CACHE_IS_EMPTY(&cx->runtime->propertyCache); + cx->runtime->propertyCache.disabled = JS_FALSE; +} + +/* + * Class for for/in loop property iterator objects. + */ +#define JSSLOT_ITER_STATE JSSLOT_PRIVATE + +static void +prop_iterator_finalize(JSContext *cx, JSObject *obj) +{ + jsval iter_state; + jsval iteratee; + + /* Protect against stillborn iterators. */ + iter_state = obj->slots[JSSLOT_ITER_STATE]; + iteratee = obj->slots[JSSLOT_PARENT]; + if (!JSVAL_IS_NULL(iter_state) && !JSVAL_IS_PRIMITIVE(iteratee)) { + OBJ_ENUMERATE(cx, JSVAL_TO_OBJECT(iteratee), JSENUMERATE_DESTROY, + &iter_state, NULL); + } + js_RemoveRoot(cx->runtime, &obj->slots[JSSLOT_PARENT]); +} + +static JSClass prop_iterator_class = { + "PropertyIterator", + 0, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, prop_iterator_finalize, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +/* + * Stack macros and functions. These all use a local variable, jsval *sp, to + * point to the next free stack slot. SAVE_SP must be called before any call + * to a function that may invoke the interpreter. RESTORE_SP must be called + * only after return from js_Invoke, because only js_Invoke changes fp->sp. + */ +#define PUSH(v) (*sp++ = (v)) +#define POP() (*--sp) +#ifdef DEBUG +#define SAVE_SP(fp) \ + (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase), \ + (fp)->sp = sp) +#else +#define SAVE_SP(fp) ((fp)->sp = sp) +#endif +#define RESTORE_SP(fp) (sp = (fp)->sp) + +/* + * Push the generating bytecode's pc onto the parallel pc stack that runs + * depth slots below the operands. + * + * NB: PUSH_OPND uses sp, depth, and pc from its lexical environment. See + * js_Interpret for these local variables' declarations and uses. + */ +#define PUSH_OPND(v) (sp[-depth] = (jsval)pc, PUSH(v)) +#define STORE_OPND(n,v) (sp[(n)-depth] = (jsval)pc, sp[n] = (v)) +#define POP_OPND() POP() +#define FETCH_OPND(n) (sp[n]) + +/* + * Push the jsdouble d using sp, depth, and pc from the lexical environment. + * Try to convert d to a jsint that fits in a jsval, otherwise GC-alloc space + * for it and push a reference. + */ +#define STORE_NUMBER(cx, n, d) \ + JS_BEGIN_MACRO \ + jsint i_; \ + jsval v_; \ + \ + if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) { \ + v_ = INT_TO_JSVAL(i_); \ + } else { \ + ok = js_NewDoubleValue(cx, d, &v_); \ + if (!ok) \ + goto out; \ + } \ + STORE_OPND(n, v_); \ + JS_END_MACRO + +#define FETCH_NUMBER(cx, n, d) \ + JS_BEGIN_MACRO \ + jsval v_; \ + \ + v_ = FETCH_OPND(n); \ + VALUE_TO_NUMBER(cx, v_, d); \ + JS_END_MACRO + +#define FETCH_INT(cx, n, i) \ + JS_BEGIN_MACRO \ + jsval v_ = FETCH_OPND(n); \ + if (JSVAL_IS_INT(v_)) { \ + i = JSVAL_TO_INT(v_); \ + } else { \ + SAVE_SP(fp); \ + ok = js_ValueToECMAInt32(cx, v_, &i); \ + if (!ok) \ + goto out; \ + } \ + JS_END_MACRO + +#define FETCH_UINT(cx, n, ui) \ + JS_BEGIN_MACRO \ + jsval v_ = FETCH_OPND(n); \ + jsint i_; \ + if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) { \ + ui = (uint32) i_; \ + } else { \ + SAVE_SP(fp); \ + ok = js_ValueToECMAUint32(cx, v_, &ui); \ + if (!ok) \ + goto out; \ + } \ + JS_END_MACRO + +/* + * Optimized conversion macros that test for the desired type in v before + * homing sp and calling a conversion function. + */ +#define VALUE_TO_NUMBER(cx, v, d) \ + JS_BEGIN_MACRO \ + if (JSVAL_IS_INT(v)) { \ + d = (jsdouble)JSVAL_TO_INT(v); \ + } else if (JSVAL_IS_DOUBLE(v)) { \ + d = *JSVAL_TO_DOUBLE(v); \ + } else { \ + SAVE_SP(fp); \ + ok = js_ValueToNumber(cx, v, &d); \ + if (!ok) \ + goto out; \ + } \ + JS_END_MACRO + +#define POP_BOOLEAN(cx, v, b) \ + JS_BEGIN_MACRO \ + v = FETCH_OPND(-1); \ + if (v == JSVAL_NULL) { \ + b = JS_FALSE; \ + } else if (JSVAL_IS_BOOLEAN(v)) { \ + b = JSVAL_TO_BOOLEAN(v); \ + } else { \ + SAVE_SP(fp); \ + ok = js_ValueToBoolean(cx, v, &b); \ + if (!ok) \ + goto out; \ + } \ + sp--; \ + JS_END_MACRO + +#define VALUE_TO_OBJECT(cx, v, obj) \ + JS_BEGIN_MACRO \ + if (!JSVAL_IS_PRIMITIVE(v)) { \ + obj = JSVAL_TO_OBJECT(v); \ + } else { \ + SAVE_SP(fp); \ + obj = js_ValueToNonNullObject(cx, v); \ + if (!obj) { \ + ok = JS_FALSE; \ + goto out; \ + } \ + } \ + JS_END_MACRO + +#define FETCH_OBJECT(cx, n, v, obj) \ + JS_BEGIN_MACRO \ + v = FETCH_OPND(n); \ + VALUE_TO_OBJECT(cx, v, obj); \ + STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \ + JS_END_MACRO + +#if JS_BUG_VOID_TOSTRING +#define CHECK_VOID_TOSTRING(cx, v) \ + if (JSVAL_IS_VOID(v)) { \ + JSString *str_; \ + str_ = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); \ + v = STRING_TO_JSVAL(str_); \ + } +#else +#define CHECK_VOID_TOSTRING(cx, v) ((void)0) +#endif + +#if JS_BUG_EAGER_TOSTRING +#define CHECK_EAGER_TOSTRING(hint) (hint = JSTYPE_STRING) +#else +#define CHECK_EAGER_TOSTRING(hint) ((void)0) +#endif + +#define VALUE_TO_PRIMITIVE(cx, v, hint, vp) \ + JS_BEGIN_MACRO \ + if (JSVAL_IS_PRIMITIVE(v)) { \ + CHECK_VOID_TOSTRING(cx, v); \ + *vp = v; \ + } else { \ + SAVE_SP(fp); \ + CHECK_EAGER_TOSTRING(hint); \ + ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, vp); \ + if (!ok) \ + goto out; \ + } \ + JS_END_MACRO + +JS_FRIEND_API(jsval *) +js_AllocRawStack(JSContext *cx, uintN nslots, void **markp) +{ + jsval *sp; + + if (markp) + *markp = JS_ARENA_MARK(&cx->stackPool); + JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval)); + if (!sp) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_STACK_OVERFLOW, + (cx->fp && cx->fp->fun) + ? JS_GetFunctionName(cx->fp->fun) + : "script"); + } + return sp; +} + +JS_FRIEND_API(void) +js_FreeRawStack(JSContext *cx, void *mark) +{ + JS_ARENA_RELEASE(&cx->stackPool, mark); +} + +JS_FRIEND_API(jsval *) +js_AllocStack(JSContext *cx, uintN nslots, void **markp) +{ + jsval *sp, *vp, *end; + JSArena *a; + JSStackHeader *sh; + JSStackFrame *fp; + + /* Callers don't check for zero nslots: we do to avoid empty segments. */ + if (nslots == 0) { + *markp = NULL; + return JS_ARENA_MARK(&cx->stackPool); + } + + /* Allocate 2 extra slots for the stack segment header we'll likely need. */ + sp = js_AllocRawStack(cx, 2 + nslots, markp); + if (!sp) + return NULL; + + /* Try to avoid another header if we can piggyback on the last segment. */ + a = cx->stackPool.current; + sh = cx->stackHeaders; + if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) { + /* Extend the last stack segment, give back the 2 header slots. */ + sh->nslots += nslots; + a->avail -= 2 * sizeof(jsval); + } else { + /* + * Need a new stack segment, so we must initialize unused slots in the + * current frame. See js_GC, just before marking the "operand" jsvals, + * where we scan from fp->spbase to fp->sp or through fp->script->depth + * (whichever covers fewer slots). + */ + fp = cx->fp; + if (fp && fp->script && fp->spbase) { +#ifdef DEBUG + jsuword depthdiff = fp->script->depth * sizeof(jsval); + JS_ASSERT(JS_UPTRDIFF(fp->sp, fp->spbase) <= depthdiff); + JS_ASSERT(JS_UPTRDIFF(*markp, fp->spbase) >= depthdiff); +#endif + end = fp->spbase + fp->script->depth; + for (vp = fp->sp; vp < end; vp++) + *vp = JSVAL_VOID; + } + + /* Allocate and push a stack segment header from the 2 extra slots. */ + sh = (JSStackHeader *)sp; + sh->nslots = nslots; + sh->down = cx->stackHeaders; + cx->stackHeaders = sh; + sp += 2; + } + + /* + * Store JSVAL_NULL using memset, to let compilers optimize as they see + * fit, in case a caller allocates and pushes GC-things one by one, which + * could nest a last-ditch GC that will scan this segment. + */ + memset(sp, 0, nslots * sizeof(jsval)); + return sp; +} + +JS_FRIEND_API(void) +js_FreeStack(JSContext *cx, void *mark) +{ + JSStackHeader *sh; + jsuword slotdiff; + + /* Check for zero nslots allocation special case. */ + if (!mark) + return; + + /* We can assert because js_FreeStack always balances js_AllocStack. */ + sh = cx->stackHeaders; + JS_ASSERT(sh); + + /* If mark is in the current segment, reduce sh->nslots, else pop sh. */ + slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval); + if (slotdiff < (jsuword)sh->nslots) + sh->nslots = slotdiff; + else + cx->stackHeaders = sh->down; + + /* Release the stackPool space allocated since mark was set. */ + JS_ARENA_RELEASE(&cx->stackPool, mark); +} + +JSBool +js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +JSBool +js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +JSBool +js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +JSBool +js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + return JS_TRUE; +} + +JSBool +js_ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp) +{ + if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) { + /* Some objects (e.g., With) delegate 'this' to another object. */ + thisp = OBJ_THIS_OBJECT(cx, thisp); + if (!thisp) + return JS_FALSE; + + /* Default return value for a constructor is the new object. */ + if (fp->flags & JSFRAME_CONSTRUCTING) + fp->rval = OBJECT_TO_JSVAL(thisp); + } else { + /* + * ECMA requires "the global object", but in the presence of multiple + * top-level objects (windows, frames, or certain layers in the client + * object model), we prefer fun's parent. An example that causes this + * code to run: + * + * // in window w1 + * function f() { return this } + * function g() { return f } + * + * // in window w2 + * var h = w1.g() + * alert(h() == w1) + * + * The alert should display "true". + */ + JS_ASSERT(!(fp->flags & JSFRAME_CONSTRUCTING)); + if (JSVAL_IS_PRIMITIVE(fp->argv[-2]) || + !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fp->argv[-2]))) { + thisp = cx->globalObject; + } else { + jsid id; + jsval v; + uintN attrs; + + /* Walk up the parent chain. */ + thisp = JSVAL_TO_OBJECT(fp->argv[-2]); + id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); + for (;;) { + if (!OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs)) + return JS_FALSE; + if (JSVAL_IS_VOID(v)) + v = OBJ_GET_SLOT(cx, thisp, JSSLOT_PARENT); + JS_ASSERT(JSVAL_TO_OBJECT(v) == OBJ_GET_PARENT(cx, thisp)); + if (JSVAL_IS_NULL(v)) + break; + thisp = JSVAL_TO_OBJECT(v); + } + } + } + fp->thisp = thisp; + fp->argv[-1] = OBJECT_TO_JSVAL(thisp); + return JS_TRUE; +} + +#ifdef DUMP_CALL_TABLE + +#include "jsclist.h" +#include "jshash.h" +#include "jsdtoa.h" + +typedef struct CallKey { + jsval callee; /* callee value */ + const char *filename; /* function filename or null */ + uintN lineno; /* function lineno or 0 */ +} CallKey; + +/* Compensate for typeof null == "object" brain damage. */ +#define JSTYPE_NULL JSTYPE_LIMIT +#define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v)) +#define TYPENAME(t) (((t) == JSTYPE_NULL) ? js_null_str : js_type_str[t]) +#define NTYPEHIST (JSTYPE_LIMIT + 1) + +typedef struct CallValue { + uint32 total; /* total call count */ + uint32 recycled; /* LRU-recycled calls lost */ + uint16 minargc; /* minimum argument count */ + uint16 maxargc; /* maximum argument count */ + struct ArgInfo { + uint32 typeHist[NTYPEHIST]; /* histogram by type */ + JSCList lruList; /* top 10 values LRU list */ + struct ArgValCount { + JSCList lruLink; /* LRU list linkage */ + jsval value; /* recently passed value */ + uint32 count; /* number of times passed */ + char strbuf[112]; /* string conversion buffer */ + } topValCounts[10]; /* top 10 value storage */ + } argInfo[8]; +} CallValue; + +typedef struct CallEntry { + JSHashEntry entry; + CallKey key; + CallValue value; + char name[32]; /* function name copy */ +} CallEntry; + +static void * +AllocCallTable(void *pool, size_t size) +{ + return malloc(size); +} + +static void +FreeCallTable(void *pool, void *item) +{ + free(item); +} + +static JSHashEntry * +AllocCallEntry(void *pool, const void *key) +{ + return (JSHashEntry*) calloc(1, sizeof(CallEntry)); +} + +static void +FreeCallEntry(void *pool, JSHashEntry *he, uintN flag) +{ + JS_ASSERT(flag == HT_FREE_ENTRY); + free(he); +} + +static JSHashAllocOps callTableAllocOps = { + AllocCallTable, FreeCallTable, + AllocCallEntry, FreeCallEntry +}; + +JS_STATIC_DLL_CALLBACK(JSHashNumber) +js_hash_call_key(const void *key) +{ + CallKey *ck = (CallKey *) key; + JSHashNumber hash = (jsuword)ck->callee >> 3; + + if (ck->filename) { + hash = (hash << 4) ^ JS_HashString(ck->filename); + hash = (hash << 4) ^ ck->lineno; + } + return hash; +} + +JS_STATIC_DLL_CALLBACK(intN) +js_compare_call_keys(const void *k1, const void *k2) +{ + CallKey *ck1 = (CallKey *)k1, *ck2 = (CallKey *)k2; + + return ck1->callee == ck2->callee && + ((ck1->filename && ck2->filename) + ? strcmp(ck1->filename, ck2->filename) == 0 + : ck1->filename == ck2->filename) && + ck1->lineno == ck2->lineno; +} + +JSHashTable *js_CallTable; +size_t js_LogCallToSourceLimit; + +JS_STATIC_DLL_CALLBACK(intN) +CallTableDumper(JSHashEntry *he, intN k, void *arg) +{ + CallEntry *ce = (CallEntry *)he; + FILE *fp = (FILE *)arg; + uintN argc, i, n; + struct ArgInfo *ai; + JSType save, type; + JSCList *cl; + struct ArgValCount *avc; + jsval argval; + + if (ce->key.filename) { + /* We're called at the end of the mark phase, so mark our filenames. */ + js_MarkScriptFilename(ce->key.filename); + fprintf(fp, "%s:%u ", ce->key.filename, ce->key.lineno); + } else { + fprintf(fp, "@%p ", (void *) ce->key.callee); + } + + if (ce->name[0]) + fprintf(fp, "name %s ", ce->name); + fprintf(fp, "calls %lu (%lu) argc %u/%u\n", + (unsigned long) ce->value.total, + (unsigned long) ce->value.recycled, + ce->value.minargc, ce->value.maxargc); + + argc = JS_MIN(ce->value.maxargc, 8); + for (i = 0; i < argc; i++) { + ai = &ce->value.argInfo[i]; + + n = 0; + save = -1; + for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) { + if (ai->typeHist[type]) { + save = type; + ++n; + } + } + if (n == 1) { + fprintf(fp, " arg %u type %s: %lu\n", + i, TYPENAME(save), (unsigned long) ai->typeHist[save]); + } else { + fprintf(fp, " arg %u type histogram:\n", i); + for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) { + fprintf(fp, " %9s: %8lu ", + TYPENAME(type), (unsigned long) ai->typeHist[type]); + for (n = (uintN) JS_HOWMANY(ai->typeHist[type], 10); n > 0; --n) + fputc('*', fp); + fputc('\n', fp); + } + } + + fprintf(fp, " arg %u top 10 values:\n", i); + n = 1; + for (cl = ai->lruList.prev; cl != &ai->lruList; cl = cl->prev) { + avc = (struct ArgValCount *)cl; + if (!avc->count) + break; + argval = avc->value; + fprintf(fp, " %9u: %8lu %.*s (%#lx)\n", + n, (unsigned long) avc->count, + sizeof avc->strbuf, avc->strbuf, argval); + ++n; + } + } + + return HT_ENUMERATE_NEXT; +} + +void +js_DumpCallTable(JSContext *cx) +{ + char name[24]; + FILE *fp; + static uintN dumpCount; + + if (!js_CallTable) + return; + + JS_snprintf(name, sizeof name, "/tmp/calltable.dump.%u", dumpCount & 7); + dumpCount++; + fp = fopen(name, "w"); + if (!fp) + return; + + JS_HashTableEnumerateEntries(js_CallTable, CallTableDumper, fp); + fclose(fp); +} + +static void +LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv) +{ + CallKey key; + const char *name, *cstr; + JSFunction *fun; + JSHashNumber keyHash; + JSHashEntry **hep, *he; + CallEntry *ce; + uintN i, j; + jsval argval; + JSType type; + struct ArgInfo *ai; + struct ArgValCount *avc; + JSString *str; + + if (!js_CallTable) { + js_CallTable = JS_NewHashTable(1024, js_hash_call_key, + js_compare_call_keys, NULL, + &callTableAllocOps, NULL); + if (!js_CallTable) + return; + } + + key.callee = callee; + key.filename = NULL; + key.lineno = 0; + name = ""; + if (JSVAL_IS_FUNCTION(cx, callee)) { + fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(callee)); + if (fun->atom) + name = js_AtomToPrintableString(cx, fun->atom); + if (fun->interpreted) { + key.filename = fun->u.script->filename; + key.lineno = fun->u.script->lineno; + } + } + keyHash = js_hash_call_key(&key); + + hep = JS_HashTableRawLookup(js_CallTable, keyHash, &key); + he = *hep; + if (he) { + ce = (CallEntry *) he; + JS_ASSERT(strncmp(ce->name, name, sizeof ce->name) == 0); + } else { + he = JS_HashTableRawAdd(js_CallTable, hep, keyHash, &key, NULL); + if (!he) + return; + ce = (CallEntry *) he; + ce->entry.key = &ce->key; + ce->entry.value = &ce->value; + ce->key = key; + for (i = 0; i < 8; i++) { + ai = &ce->value.argInfo[i]; + JS_INIT_CLIST(&ai->lruList); + for (j = 0; j < 10; j++) + JS_APPEND_LINK(&ai->topValCounts[j].lruLink, &ai->lruList); + } + strncpy(ce->name, name, sizeof ce->name); + } + + ++ce->value.total; + if (ce->value.minargc < argc) + ce->value.minargc = argc; + if (ce->value.maxargc < argc) + ce->value.maxargc = argc; + if (argc > 8) + argc = 8; + for (i = 0; i < argc; i++) { + ai = &ce->value.argInfo[i]; + argval = argv[i]; + type = TYPEOF(cx, argval); + ++ai->typeHist[type]; + + for (j = 0; ; j++) { + if (j == 10) { + avc = (struct ArgValCount *) ai->lruList.next; + ce->value.recycled += avc->count; + avc->value = argval; + avc->count = 1; + break; + } + avc = &ai->topValCounts[j]; + if (avc->value == argval) { + ++avc->count; + break; + } + } + + /* Move avc to the back of the LRU list. */ + JS_REMOVE_LINK(&avc->lruLink); + JS_APPEND_LINK(&avc->lruLink, &ai->lruList); + + str = NULL; + cstr = ""; + switch (TYPEOF(cx, argval)) { + case JSTYPE_VOID: + cstr = js_type_str[JSTYPE_VOID]; + break; + case JSTYPE_NULL: + cstr = js_null_str; + break; + case JSTYPE_BOOLEAN: + cstr = js_boolean_str[JSVAL_TO_BOOLEAN(argval)]; + break; + case JSTYPE_NUMBER: + if (JSVAL_IS_INT(argval)) { + JS_snprintf(avc->strbuf, sizeof avc->strbuf, "%ld", + JSVAL_TO_INT(argval)); + } else { + JS_dtostr(avc->strbuf, sizeof avc->strbuf, DTOSTR_STANDARD, 0, + *JSVAL_TO_DOUBLE(argval)); + } + continue; + case JSTYPE_STRING: + str = js_QuoteString(cx, JSVAL_TO_STRING(argval), (jschar)'"'); + break; + case JSTYPE_FUNCTION: + if (JSVAL_IS_FUNCTION(cx, argval)) { + fun = (JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(argval)); + if (fun && fun->atom) { + str = ATOM_TO_STRING(fun->atom); + break; + } + } + /* FALL THROUGH */ + case JSTYPE_OBJECT: + js_LogCallToSourceLimit = sizeof avc->strbuf; + cx->options |= JSOPTION_LOGCALL_TOSOURCE; + str = js_ValueToSource(cx, argval); + cx->options &= ~JSOPTION_LOGCALL_TOSOURCE; + break; + } + if (str) + cstr = JS_GetStringBytes(str); + strncpy(avc->strbuf, cstr, sizeof avc->strbuf); + } +} + +#endif /* DUMP_CALL_TABLE */ + +/* + * Find a function reference and its 'this' object implicit first parameter + * under argc arguments on cx's stack, and call the function. Push missing + * required arguments, allocate declared local variables, and pop everything + * when done. Then push the return value. + */ +JS_FRIEND_API(JSBool) +js_Invoke(JSContext *cx, uintN argc, uintN flags) +{ + void *mark; + JSStackFrame *fp, frame; + jsval *sp, *newsp, *limit; + jsval *vp, v; + JSObject *funobj, *parent, *thisp; + JSBool ok; + JSClass *clasp; + JSObjectOps *ops; + JSNative native; + JSFunction *fun; + JSScript *script; + uintN nslots, nvars, nalloc, surplus; + JSInterpreterHook hook; + void *hookData; + + /* Mark the top of stack and load frequently-used registers. */ + mark = JS_ARENA_MARK(&cx->stackPool); + fp = cx->fp; + sp = fp->sp; + + /* + * Set vp to the callee value's stack slot (it's where rval goes). + * Once vp is set, control should flow through label out2: to return. + * Set frame.rval early so native class and object ops can throw and + * return false, causing a goto out2 with ok set to false. Also set + * frame.flags to flags so that ComputeThis can test bits in it. + */ + vp = sp - (2 + argc); + v = *vp; + frame.rval = JSVAL_VOID; + frame.flags = flags; + thisp = JSVAL_TO_OBJECT(vp[1]); + + /* + * A callee must be an object reference, unless its |this| parameter + * implements the __noSuchMethod__ method, in which case that method will + * be called like so: + * + * thisp.__noSuchMethod__(id, args) + * + * where id is the name of the method that this invocation attempted to + * call by name, and args is an Array containing this invocation's actual + * parameters. + */ + if (JSVAL_IS_PRIMITIVE(v)) { +#if JS_HAS_NO_SUCH_METHOD + jsid id; + jsbytecode *pc; + jsatomid atomIndex; + JSAtom *atom; + JSObject *argsobj; + JSArena *a; + + if (!fp->script || (flags & JSINVOKE_INTERNAL)) + goto bad; + + /* + * We must ComputeThis here to censor Call objects; performance hit, + * but at least it's idempotent. + * + * Normally, we call ComputeThis after all frame members have been + * set, and in particular, after any revision of the callee value at + * *vp due to clasp->convert (see below). This matters because + * ComputeThis may access *vp via fp->argv[-2], to follow the parent + * chain to a global object to use as the |this| parameter. + * + * Obviously, here in the JSVAL_IS_PRIMITIVE(v) case, there can't be + * any such defaulting of |this| to callee (v, *vp) ancestor. + */ + frame.argv = vp + 2; + ok = js_ComputeThis(cx, thisp, &frame); + if (!ok) + goto out2; + thisp = frame.thisp; + + id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); +#if JS_HAS_XML_SUPPORT + if (OBJECT_IS_XML(cx, thisp)) { + JSXMLObjectOps *ops; + + ops = (JSXMLObjectOps *) thisp->map->ops; + thisp = ops->getMethod(cx, thisp, id, &v); + if (!thisp) { + ok = JS_FALSE; + goto out2; + } + vp[1] = OBJECT_TO_JSVAL(thisp); + } else { +#endif + ok = OBJ_GET_PROPERTY(cx, thisp, id, &v); +#if JS_HAS_XML_SUPPORT + } +#endif + if (!ok) + goto out2; + if (JSVAL_IS_PRIMITIVE(v)) + goto bad; + + pc = (jsbytecode *) vp[-(intN)fp->script->depth]; + switch ((JSOp) *pc) { + case JSOP_NAME: + case JSOP_GETPROP: +#if JS_HAS_XML_SUPPORT + case JSOP_GETMETHOD: +#endif + atomIndex = GET_ATOM_INDEX(pc); + atom = js_GetAtom(cx, &fp->script->atomMap, atomIndex); + argsobj = js_NewArrayObject(cx, argc, vp + 2); + if (!argsobj) { + ok = JS_FALSE; + goto out2; + } + + sp = vp + 4; + if (argc < 2) { + a = cx->stackPool.current; + if ((jsuword)sp > a->limit) { + /* + * Arguments must be contiguous, and must include argv[-1] + * and argv[-2], so allocate more stack, advance sp, and + * set newsp[1] to thisp (vp[1]). The other argv elements + * will be set below, using negative indexing from sp. + */ + newsp = js_AllocRawStack(cx, 4, NULL); + if (!newsp) { + ok = JS_FALSE; + goto out2; + } + newsp[1] = OBJECT_TO_JSVAL(thisp); + sp = newsp + 4; + } else if ((jsuword)sp > a->avail) { + /* + * Inline, optimized version of JS_ARENA_ALLOCATE to claim + * the small number of words not already allocated as part + * of the caller's operand stack. + */ + JS_ArenaCountAllocation(&cx->stackPool, + (jsuword)sp - a->avail); + a->avail = (jsuword)sp; + } + } + + sp[-4] = v; + JS_ASSERT(sp[-3] == OBJECT_TO_JSVAL(thisp)); + sp[-2] = ATOM_KEY(atom); + sp[-1] = OBJECT_TO_JSVAL(argsobj); + fp->sp = sp; + argc = 2; + break; + + default: + goto bad; + } +#else + goto bad; +#endif + } + + funobj = JSVAL_TO_OBJECT(v); + parent = OBJ_GET_PARENT(cx, funobj); + clasp = OBJ_GET_CLASS(cx, funobj); + if (clasp != &js_FunctionClass) { + /* Function is inlined, all other classes use object ops. */ + ops = funobj->map->ops; + + /* + * XXX this makes no sense -- why convert to function if clasp->call? + * XXX better to call that hook without converting + * XXX the only thing that needs fixing is liveconnect + * + * Try converting to function, for closure and API compatibility. + * We attempt the conversion under all circumstances for 1.2, but + * only if there is a call op defined otherwise. + */ + if (JS_VERSION_IS_1_2(cx) || + ((ops == &js_ObjectOps) ? clasp->call : ops->call)) { + ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v); + if (!ok) + goto out2; + + if (JSVAL_IS_FUNCTION(cx, v)) { + /* Make vp refer to funobj to keep it available as argv[-2]. */ + *vp = v; + funobj = JSVAL_TO_OBJECT(v); + parent = OBJ_GET_PARENT(cx, funobj); + goto have_fun; + } + } + fun = NULL; + script = NULL; + nslots = nvars = 0; + + /* Try a call or construct native object op. */ + native = (flags & JSINVOKE_CONSTRUCT) ? ops->construct : ops->call; + if (!native) + goto bad; + } else { +have_fun: + /* Get private data and set derived locals from it. */ + fun = (JSFunction *) JS_GetPrivate(cx, funobj); + if (fun->interpreted) { + native = NULL; + script = fun->u.script; + } else { + native = fun->u.native; + script = NULL; + } + nslots = (fun->nargs > argc) ? fun->nargs - argc : 0; + nslots += fun->extra; + nvars = fun->nvars; + + /* Handle bound method special case. */ + if (fun->flags & JSFUN_BOUND_METHOD) + thisp = parent; + } + + /* Initialize the rest of frame, except for sp (set by SAVE_SP later). */ + frame.varobj = NULL; + frame.callobj = frame.argsobj = NULL; + frame.script = script; + frame.fun = fun; + frame.argc = argc; + frame.argv = sp - argc; + frame.nvars = nvars; + frame.vars = sp; + frame.down = fp; + frame.annotation = NULL; + frame.scopeChain = NULL; /* set below for real, after cx->fp is set */ + frame.pc = NULL; + frame.spbase = NULL; + frame.sharpDepth = 0; + frame.sharpArray = NULL; + frame.dormantNext = NULL; + frame.xmlNamespace = NULL; + + /* Compute the 'this' parameter and store it in frame as frame.thisp. */ + ok = js_ComputeThis(cx, thisp, &frame); + if (!ok) + goto out2; + + /* From here on, control must flow through label out: to return. */ + cx->fp = &frame; + + /* Init these now in case we goto out before first hook call. */ + hook = cx->runtime->callHook; + hookData = NULL; + + /* Check for argument slots required by the function. */ + if (nslots) { + /* All arguments must be contiguous, so we may have to copy actuals. */ + nalloc = nslots; + limit = (jsval *) cx->stackPool.current->limit; + if (sp + nslots > limit) { + /* Hit end of arena: we have to copy argv[-2..(argc+nslots-1)]. */ + nalloc += 2 + argc; + } else { + /* Take advantage of surplus slots in the caller's frame depth. */ + JS_ASSERT((jsval *)mark >= sp); + surplus = (jsval *)mark - sp; + nalloc -= surplus; + } + + /* Check whether we have enough space in the caller's frame. */ + if ((intN)nalloc > 0) { + /* Need space for actuals plus missing formals minus surplus. */ + newsp = js_AllocRawStack(cx, nalloc, NULL); + if (!newsp) { + ok = JS_FALSE; + goto out; + } + + /* If we couldn't allocate contiguous args, copy actuals now. */ + if (newsp != mark) { + JS_ASSERT(sp + nslots > limit); + JS_ASSERT(2 + argc + nslots == nalloc); + *newsp++ = vp[0]; + *newsp++ = vp[1]; + if (argc) + memcpy(newsp, frame.argv, argc * sizeof(jsval)); + frame.argv = newsp; + sp = frame.vars = newsp + argc; + } + } + + /* Advance frame.vars to make room for the missing args. */ + frame.vars += nslots; + + /* Push void to initialize missing args. */ + do { + PUSH(JSVAL_VOID); + } while (--nslots != 0); + } + JS_ASSERT(nslots == 0); + + /* Now allocate stack space for local variables. */ + if (nvars) { + JS_ASSERT((jsval *)cx->stackPool.current->avail >= frame.vars); + surplus = (jsval *)cx->stackPool.current->avail - frame.vars; + if (surplus < nvars) { + newsp = js_AllocRawStack(cx, nvars, NULL); + if (!newsp) { + ok = JS_FALSE; + goto out; + } + if (newsp != sp) { + /* NB: Discontinuity between argv and vars. */ + sp = frame.vars = newsp; + } + } + + /* Push void to initialize local variables. */ + do { + PUSH(JSVAL_VOID); + } while (--nvars != 0); + } + JS_ASSERT(nvars == 0); + + /* Store the current sp in frame before calling fun. */ + SAVE_SP(&frame); + + /* call the hook if present */ + if (hook && (native || script)) + hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->callHookData); + + /* Call the function, either a native method or an interpreted script. */ + if (native) { +#if JS_HAS_LVALUE_RETURN + /* Set by JS_SetCallReturnValue2, used to return reference types. */ + cx->rval2set = JS_FALSE; +#endif + + /* If native, use caller varobj and scopeChain for eval. */ + frame.varobj = fp->varobj; + frame.scopeChain = fp->scopeChain; + ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval); + JS_RUNTIME_METER(cx->runtime, nativeCalls); + } else if (script) { +#ifdef DUMP_CALL_TABLE + LogCall(cx, *vp, argc, frame.argv); +#endif + /* Use parent scope so js_GetCallObject can find the right "Call". */ + frame.scopeChain = parent; + if (fun->flags & JSFUN_HEAVYWEIGHT) { +#if JS_HAS_CALL_OBJECT + /* Scope with a call object parented by the callee's parent. */ + if (!js_GetCallObject(cx, &frame, parent)) { + ok = JS_FALSE; + goto out; + } +#else + /* Bad old code used the function as a proxy for all calls to it. */ + frame.scopeChain = funobj; +#endif + } + ok = js_Interpret(cx, script->code, &v); + } else { + /* fun might be onerror trying to report a syntax error in itself. */ + frame.scopeChain = NULL; + ok = JS_TRUE; + } + +out: + if (hookData) { + hook = cx->runtime->callHook; + if (hook) + hook(cx, &frame, JS_FALSE, &ok, hookData); + } +#if JS_HAS_CALL_OBJECT + /* If frame has a call object, sync values and clear back-pointer. */ + if (frame.callobj) + ok &= js_PutCallObject(cx, &frame); +#endif +#if JS_HAS_ARGS_OBJECT + /* If frame has an arguments object, sync values and clear back-pointer. */ + if (frame.argsobj) + ok &= js_PutArgsObject(cx, &frame); +#endif + + /* Restore cx->fp now that we're done releasing frame objects. */ + cx->fp = fp; + +out2: + /* Pop everything we may have allocated off the stack. */ + JS_ARENA_RELEASE(&cx->stackPool, mark); + + /* Store the return value and restore sp just above it. */ + *vp = frame.rval; + fp->sp = vp + 1; + + /* + * Store the location of the JSOP_CALL or JSOP_EVAL that generated the + * return value, but only if this is an external (compiled from script + * source) call that has stack budget for the generating pc. + */ + if (fp->script && !(flags & JSINVOKE_INTERNAL)) + vp[-(intN)fp->script->depth] = (jsval)fp->pc; + return ok; + +bad: + js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_CONSTRUCT); + ok = JS_FALSE; + goto out2; +} + +JSBool +js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, + uintN argc, jsval *argv, jsval *rval) +{ + JSStackFrame *fp, *oldfp, frame; + jsval *oldsp, *sp; + void *mark; + uintN i; + JSBool ok; + + fp = oldfp = cx->fp; + if (!fp) { + memset(&frame, 0, sizeof frame); + cx->fp = fp = &frame; + } + oldsp = fp->sp; + sp = js_AllocStack(cx, 2 + argc, &mark); + if (!sp) { + ok = JS_FALSE; + goto out; + } + + PUSH(fval); + PUSH(OBJECT_TO_JSVAL(obj)); + for (i = 0; i < argc; i++) + PUSH(argv[i]); + SAVE_SP(fp); + ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL); + if (ok) { + RESTORE_SP(fp); + + /* + * Store *rval in the a scoped local root if a scope is open, else in + * the cx->lastInternalResult pigeon-hole GC root, solely so users of + * js_InternalInvoke and its direct and indirect (js_ValueToString for + * example) callers do not need to manage roots for local, temporary + * references to such results. + */ + *rval = POP_OPND(); + if (JSVAL_IS_GCTHING(*rval)) { + if (cx->localRootStack) { + if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0) + ok = JS_FALSE; + } else { + cx->lastInternalResult = *rval; + } + } + } + + js_FreeStack(cx, mark); +out: + fp->sp = oldsp; + if (oldfp != fp) + cx->fp = oldfp; + + return ok; +} + +JSBool +js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, + JSAccessMode mode, uintN argc, jsval *argv, jsval *rval) +{ + /* + * Check general (not object-ops/class-specific) access from the running + * script to obj.id only if id has a scripted getter or setter that we're + * about to invoke. If we don't check this case, nothing else will -- no + * other native code has the chance to check. + * + * Contrast this non-native (scripted) case with native getter and setter + * accesses, where the native itself must do an access check, if security + * policies requires it. We make a checkAccess or checkObjectAccess call + * back to the embedding program only in those cases where we're not going + * to call an embedding-defined native function, getter, setter, or class + * hook anyway. Where we do call such a native, there's no need for the + * engine to impose a separate access check callback on all embeddings -- + * many embeddings have no security policy at all. + */ + JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE); + if (cx->runtime->checkObjectAccess && + JSVAL_IS_FUNCTION(cx, fval) && + ((JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval)))->interpreted && + !cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, + &fval)) { + return JS_FALSE; + } + + return js_InternalCall(cx, obj, fval, argc, argv, rval); +} + +JSBool +js_Execute(JSContext *cx, JSObject *chain, JSScript *script, + JSStackFrame *down, uintN flags, jsval *result) +{ + JSInterpreterHook hook; + void *hookData, *mark; + JSStackFrame *oldfp, frame; + JSObject *obj, *tmp; + JSBool ok; + + hook = cx->runtime->executeHook; + hookData = mark = NULL; + oldfp = cx->fp; + frame.callobj = frame.argsobj = NULL; + frame.script = script; + if (down) { + /* Propagate arg/var state for eval and the debugger API. */ + frame.varobj = down->varobj; + frame.fun = down->fun; + frame.thisp = down->thisp; + frame.argc = down->argc; + frame.argv = down->argv; + frame.nvars = down->nvars; + frame.vars = down->vars; + frame.annotation = down->annotation; + frame.sharpArray = down->sharpArray; + } else { + obj = chain; + if (cx->options & JSOPTION_VAROBJFIX) { + while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) + obj = tmp; + } + frame.varobj = obj; + frame.fun = NULL; + frame.thisp = chain; + frame.argc = 0; + frame.argv = NULL; + frame.nvars = script->numGlobalVars; + if (frame.nvars) { + frame.vars = js_AllocRawStack(cx, frame.nvars, &mark); + if (!frame.vars) + return JS_FALSE; + memset(frame.vars, 0, frame.nvars * sizeof(jsval)); + } else { + frame.vars = NULL; + } + frame.annotation = NULL; + frame.sharpArray = NULL; + } + frame.rval = JSVAL_VOID; + frame.down = down; + frame.scopeChain = chain; + frame.pc = NULL; + frame.sp = oldfp ? oldfp->sp : NULL; + frame.spbase = NULL; + frame.sharpDepth = 0; + frame.flags = flags; + frame.dormantNext = NULL; + frame.xmlNamespace = NULL; + + /* + * Here we wrap the call to js_Interpret with code to (conditionally) + * save and restore the old stack frame chain into a chain of 'dormant' + * frame chains. Since we are replacing cx->fp, we were running into + * the problem that if GC was called under this frame, some of the GC + * things associated with the old frame chain (available here only in + * the C variable 'oldfp') were not rooted and were being collected. + * + * So, now we preserve the links to these 'dormant' frame chains in cx + * before calling js_Interpret and cleanup afterwards. The GC walks + * these dormant chains and marks objects in the same way that it marks + * objects in the primary cx->fp chain. + */ + if (oldfp && oldfp != down) { + JS_ASSERT(!oldfp->dormantNext); + oldfp->dormantNext = cx->dormantFrameChain; + cx->dormantFrameChain = oldfp; + } + + cx->fp = &frame; + if (hook) + hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->executeHookData); + + /* + * Use frame.rval, not result, so the last result stays rooted across any + * GC activations nested within this js_Interpret. + */ + ok = js_Interpret(cx, script->code, &frame.rval); + *result = frame.rval; + + if (hookData) { + hook = cx->runtime->executeHook; + if (hook) + hook(cx, &frame, JS_FALSE, &ok, hookData); + } + if (mark) + js_FreeRawStack(cx, mark); + cx->fp = oldfp; + + if (oldfp && oldfp != down) { + JS_ASSERT(cx->dormantFrameChain == oldfp); + cx->dormantFrameChain = oldfp->dormantNext; + oldfp->dormantNext = NULL; + } + + return ok; +} + +#if JS_HAS_EXPORT_IMPORT +/* + * If id is JSVAL_VOID, import all exported properties from obj. + */ +static JSBool +ImportProperty(JSContext *cx, JSObject *obj, jsid id) +{ + JSBool ok; + JSIdArray *ida; + JSProperty *prop; + JSObject *obj2, *target, *funobj, *closure; + JSString *str; + uintN attrs; + jsint i; + jsval value; + + if (JSVAL_IS_VOID(id)) { + ida = JS_Enumerate(cx, obj); + if (!ida) + return JS_FALSE; + ok = JS_TRUE; + if (ida->length == 0) + goto out; + } else { + ida = NULL; + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) + return JS_FALSE; + if (!prop) { + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, + ID_TO_VALUE(id), NULL); + if (str) + js_ReportIsNotDefined(cx, JS_GetStringBytes(str)); + return JS_FALSE; + } + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (!ok) + return JS_FALSE; + if (!(attrs & JSPROP_EXPORTED)) { + str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, + ID_TO_VALUE(id), NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_NOT_EXPORTED, + JS_GetStringBytes(str)); + } + return JS_FALSE; + } + } + + target = cx->fp->varobj; + i = 0; + do { + if (ida) { + id = ida->vector[i]; + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs); + if (!ok) + goto out; + if (!(attrs & JSPROP_EXPORTED)) + continue; + } + ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs); + if (!ok) + goto out; + if (JSVAL_IS_FUNCTION(cx, value)) { + funobj = JSVAL_TO_OBJECT(value); + closure = js_CloneFunctionObject(cx, funobj, obj); + if (!closure) { + ok = JS_FALSE; + goto out; + } + value = OBJECT_TO_JSVAL(closure); + } + + /* + * Handle the case of importing a property that refers to a local + * variable or formal parameter of a function activation. These + * properties are accessed by opcodes using stack slot numbers + * generated by the compiler rather than runtime name-lookup. These + * local references, therefore, bypass the normal scope chain lookup. + * So, instead of defining a new property in the activation object, + * modify the existing value in the stack slot. + */ + if (OBJ_GET_CLASS(cx, target) == &js_CallClass) { + ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop); + if (!ok) + goto out; + } else { + prop = NULL; + } + if (prop && target == obj2) { + ok = OBJ_SET_PROPERTY(cx, target, id, &value); + } else { + ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL, + attrs & ~JSPROP_EXPORTED, + NULL); + } + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (!ok) + goto out; + } while (ida && ++i < ida->length); + +out: + if (ida) + JS_DestroyIdArray(cx, ida); + return ok; +} +#endif /* JS_HAS_EXPORT_IMPORT */ + +JSBool +js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, + JSObject **objp, JSProperty **propp) +{ + JSObject *obj2; + JSProperty *prop; + uintN oldAttrs, report; + JSBool isFunction; + jsval value; + const char *type, *name; + + if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) + return JS_FALSE; + if (propp) { + *objp = obj2; + *propp = prop; + } + if (!prop) + return JS_TRUE; + + /* From here, return true, or goto bad on failure to drop prop. */ + if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) + goto bad; + + /* If either property is readonly, we have an error. */ + report = ((oldAttrs | attrs) & JSPROP_READONLY) + ? JSREPORT_ERROR + : JSREPORT_WARNING | JSREPORT_STRICT; + + if (report != JSREPORT_ERROR) { + /* + * Allow redeclaration of variables and functions, but insist that the + * new value is not a getter if the old value was, ditto for setters -- + * unless prop is impermanent (in which case anyone could delete it and + * redefine it, willy-nilly). + */ + if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) + return JS_TRUE; + if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0) + return JS_TRUE; + if (!(oldAttrs & JSPROP_PERMANENT)) + return JS_TRUE; + report = JSREPORT_ERROR; + } + + isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; + if (!isFunction) { + if (!OBJ_GET_PROPERTY(cx, obj, id, &value)) + goto bad; + isFunction = JSVAL_IS_FUNCTION(cx, value); + } + type = (oldAttrs & attrs & JSPROP_GETTER) + ? js_getter_str + : (oldAttrs & attrs & JSPROP_SETTER) + ? js_setter_str + : (oldAttrs & JSPROP_READONLY) + ? js_const_str + : isFunction + ? js_function_str + : js_var_str; + name = js_AtomToPrintableString(cx, JSID_TO_ATOM(id)); + if (!name) + goto bad; + return JS_ReportErrorFlagsAndNumber(cx, report, + js_GetErrorMessage, NULL, + JSMSG_REDECLARED_VAR, + type, name); + +bad: + if (propp) { + *objp = NULL; + *propp = NULL; + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + return JS_FALSE; +} + +JSBool +js_StrictlyEqual(jsval lval, jsval rval) +{ + jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval); + jsdouble ld, rd; + + if (ltag == rtag) { + if (ltag == JSVAL_STRING) { + JSString *lstr = JSVAL_TO_STRING(lval), + *rstr = JSVAL_TO_STRING(rval); + return js_CompareStrings(lstr, rstr) == 0; + } + if (ltag == JSVAL_DOUBLE) { + ld = *JSVAL_TO_DOUBLE(lval); + rd = *JSVAL_TO_DOUBLE(rval); + return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + } + return lval == rval; + } + if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) { + ld = *JSVAL_TO_DOUBLE(lval); + rd = JSVAL_TO_INT(rval); + return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + } + if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) { + ld = JSVAL_TO_INT(lval); + rd = *JSVAL_TO_DOUBLE(rval); + return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); + } + return lval == rval; +} + +static JSBool +InternStringElementId(JSContext *cx, jsval idval, jsid *idp) +{ + JSAtom *atom; + + atom = js_ValueToStringAtom(cx, idval); + if (!atom) + return JS_FALSE; + *idp = ATOM_TO_JSID(atom); + return JS_TRUE; +} + +static JSBool +InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp) +{ + JS_ASSERT(!JSVAL_IS_INT(idval)); + +#if JS_HAS_XML_SUPPORT + if (JSVAL_IS_OBJECT(idval)) { + *idp = OBJECT_JSVAL_TO_JSID(idval); + return JS_TRUE; + } +#endif + + return InternStringElementId(cx, idval, idp); +} + +#if JS_HAS_XML_SUPPORT +#define CHECK_ELEMENT_ID(obj, id) \ + JS_BEGIN_MACRO \ + if (JSID_IS_OBJECT(id) && !OBJECT_IS_XML(cx, obj)) { \ + SAVE_SP(fp); \ + ok = InternStringElementId(cx, OBJECT_JSID_TO_JSVAL(id), &id); \ + if (!ok) \ + goto out; \ + } \ + JS_END_MACRO + +#else +#define CHECK_ELEMENT_ID(obj, id) JS_ASSERT(!JSID_IS_OBJECT(id)) +#endif + +#ifndef MAX_INTERP_LEVEL +#if defined(XP_OS2) +#define MAX_INTERP_LEVEL 250 +#else +#define MAX_INTERP_LEVEL 1000 +#endif +#endif + +#define MAX_INLINE_CALL_COUNT 1000 + +JSBool +js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) +{ + JSRuntime *rt; + JSStackFrame *fp; + JSScript *script; + uintN inlineCallCount; + JSObject *obj, *obj2, *proto, *parent; + JSVersion currentVersion, originalVersion; + JSBranchCallback onbranch; + JSBool ok, cond; + JSTrapHandler interruptHandler; + jsint depth, len; + jsval *sp, *newsp; + void *mark; + jsbytecode *endpc, *pc2; + JSOp op, op2; +#ifdef OSSP + const JSCodeSpec *cs = NULL; +#else + const JSCodeSpec *cs; +#endif + jsatomid atomIndex; + JSAtom *atom; + uintN argc, slot, attrs; + jsval *vp, lval, rval, ltmp, rtmp; +#ifdef OSSP + jsid id = -1L; +#else + jsid id; +#endif + JSObject *withobj, *origobj, *propobj; + jsval iter_state; + JSProperty *prop; + JSScopeProperty *sprop; + JSString *str, *str2; + jsint i, j; + jsdouble d, d2; + JSClass *clasp, *funclasp; + JSFunction *fun; + JSType type; +#ifdef DEBUG +#ifdef OSSP + FILE *tracefp = NULL; +#else + FILE *tracefp; +#endif +#endif +#if JS_HAS_EXPORT_IMPORT + JSIdArray *ida; +#endif +#if JS_HAS_SWITCH_STATEMENT + jsint low, high, off, npairs; + JSBool match; +#endif +#if JS_HAS_GETTER_SETTER + JSPropertyOp getter, setter; +#endif +#if JS_HAS_XML_SUPPORT + JSBool foreach = JS_FALSE; +#endif + int stackDummy; + + *result = JSVAL_VOID; + rt = cx->runtime; + + /* Set registerized frame pointer and derived script pointer. */ + fp = cx->fp; + script = fp->script; + + /* Count of JS function calls that nest in this C js_Interpret frame. */ + inlineCallCount = 0; + + /* + * Optimized Get and SetVersion for proper script language versioning. + * + * If any native method or JSClass/JSObjectOps hook calls js_SetVersion + * and changes cx->version, the effect will "stick" and we will stop + * maintaining currentVersion. This is relied upon by testsuites, for + * the most part -- web browsers select version before compiling and not + * at run-time. + */ + currentVersion = script->version; + originalVersion = cx->version; + if (currentVersion != originalVersion) + js_SetVersion(cx, currentVersion); + + /* + * Prepare to call a user-supplied branch handler, and abort the script + * if it returns false. We reload onbranch after calling out to native + * functions (but not to getters, setters, or other native hooks). + */ +#define LOAD_BRANCH_CALLBACK(cx) (onbranch = (cx)->branchCallback) + + LOAD_BRANCH_CALLBACK(cx); + ok = JS_TRUE; +#define CHECK_BRANCH(len) \ + JS_BEGIN_MACRO \ + if (len <= 0 && onbranch) { \ + SAVE_SP(fp); \ + if (!(ok = (*onbranch)(cx, script))) \ + goto out; \ + } \ + JS_END_MACRO + + /* + * Load the debugger's interrupt hook here and after calling out to native + * functions (but not to getters, setters, or other native hooks), so we do + * not have to reload it each time through the interpreter loop -- we hope + * the compiler can keep it in a register. + * XXX if it spills, we still lose + */ +#define LOAD_INTERRUPT_HANDLER(rt) (interruptHandler = (rt)->interruptHandler) + + LOAD_INTERRUPT_HANDLER(rt); + + /* Check for too much js_Interpret nesting, or too deep a C stack. */ + if (++cx->interpLevel == MAX_INTERP_LEVEL || + !JS_CHECK_STACK_SIZE(cx, stackDummy)) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); + ok = JS_FALSE; + goto out2; + } + + /* + * Allocate operand and pc stack slots for the script's worst-case depth, + * unless we're called to interpret a part of an already active script, a + * filtering predicate expression for example. + */ + depth = (jsint) script->depth; + if (JS_LIKELY(!fp->spbase)) { + newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark); + if (!newsp) { + ok = JS_FALSE; + goto out2; + } + sp = newsp + depth; + fp->spbase = sp; + SAVE_SP(fp); + } else { + sp = fp->sp; + JS_ASSERT(JS_UPTRDIFF(sp, fp->spbase) <= depth * sizeof(jsval)); + newsp = fp->spbase - depth; + mark = NULL; + } + + endpc = script->code + script->length; + while (pc < endpc) { + fp->pc = pc; + op = (JSOp) *pc; + do_op: + cs = &js_CodeSpec[op]; + len = cs->length; + +#ifdef DEBUG + tracefp = (FILE *) cx->tracefp; + if (tracefp) { + intN nuses, n; + + fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, pc)); + js_Disassemble1(cx, script, pc, + PTRDIFF(pc, script->code, jsbytecode), JS_FALSE, + tracefp); + nuses = cs->nuses; + if (nuses) { + SAVE_SP(fp); + for (n = -nuses; n < 0; n++) { + str = js_DecompileValueGenerator(cx, n, sp[n], NULL); + if (str) { + fprintf(tracefp, "%s %s", + (n == -nuses) ? " inputs:" : ",", + JS_GetStringBytes(str)); + } + } + fprintf(tracefp, " @ %d\n", sp - fp->spbase); + } + } +#endif + + if (interruptHandler) { + SAVE_SP(fp); + switch (interruptHandler(cx, script, pc, &rval, + rt->interruptHandlerData)) { + case JSTRAP_ERROR: + ok = JS_FALSE; + goto out; + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + fp->rval = rval; + goto out; +#if JS_HAS_EXCEPTIONS + case JSTRAP_THROW: + cx->throwing = JS_TRUE; + cx->exception = rval; + ok = JS_FALSE; + goto out; +#endif /* JS_HAS_EXCEPTIONS */ + default:; + } + LOAD_INTERRUPT_HANDLER(rt); + } + + switch (op) { + case JSOP_NOP: + break; + + case JSOP_GROUP: + break; + + case JSOP_PUSH: + PUSH_OPND(JSVAL_VOID); + break; + + case JSOP_POP: + sp--; + break; + + case JSOP_POP2: + sp -= 2; + break; + + case JSOP_SWAP: + /* + * N.B. JSOP_SWAP doesn't swap the corresponding generating pcs + * for the operands it swaps. + */ + ltmp = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = ltmp; + break; + + case JSOP_POPV: + *result = POP_OPND(); + break; + + case JSOP_ENTERWITH: + FETCH_OBJECT(cx, -1, rval, obj); + SAVE_SP(fp); + withobj = js_NewObject(cx, &js_WithClass, obj, fp->scopeChain); + if (!withobj) + goto out; + rval = INT_TO_JSVAL(sp - fp->spbase); + OBJ_SET_SLOT(cx, withobj, JSSLOT_PRIVATE, rval); + fp->scopeChain = withobj; + STORE_OPND(-1, OBJECT_TO_JSVAL(withobj)); + break; + + case JSOP_LEAVEWITH: + rval = POP_OPND(); + JS_ASSERT(JSVAL_IS_OBJECT(rval)); + withobj = JSVAL_TO_OBJECT(rval); + JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass); + + rval = OBJ_GET_SLOT(cx, withobj, JSSLOT_PARENT); + JS_ASSERT(JSVAL_IS_OBJECT(rval)); + fp->scopeChain = JSVAL_TO_OBJECT(rval); + break; + + case JSOP_SETRVAL: + fp->rval = POP_OPND(); + break; + + case JSOP_RETURN: + CHECK_BRANCH(-1); + fp->rval = POP_OPND(); + /* FALL THROUGH */ + + case JSOP_RETRVAL: /* fp->rval already set */ + if (inlineCallCount) + inline_return: + { + JSInlineFrame *ifp = (JSInlineFrame *) fp; + void *hookData = ifp->hookData; + + if (hookData) { + JSInterpreterHook hook = cx->runtime->callHook; + if (hook) { + hook(cx, fp, JS_FALSE, &ok, hookData); + LOAD_INTERRUPT_HANDLER(rt); + } + } + +#if JS_HAS_CALL_OBJECT + /* + * If frame has a call object, sync values and clear the back- + * pointer. This can happen for a lightweight function if it + * calls eval unexpectedly (in a way that is hidden from the + * compiler). See bug 325540. + */ + if (fp->callobj) + ok &= js_PutCallObject(cx, fp); +#endif +#if JS_HAS_ARGS_OBJECT + if (fp->argsobj) + ok &= js_PutArgsObject(cx, fp); +#endif + + /* Restore context version only if callee hasn't set version. */ + if (cx->version == currentVersion) { + currentVersion = ifp->callerVersion; + if (currentVersion != cx->version) + js_SetVersion(cx, currentVersion); + } + + /* Store the return value in the caller's operand frame. */ + vp = fp->argv - 2; + *vp = fp->rval; + + /* Restore cx->fp and release the inline frame's space. */ + cx->fp = fp = fp->down; + JS_ARENA_RELEASE(&cx->stackPool, ifp->mark); + + /* Restore sp to point just above the return value. */ + fp->sp = vp + 1; + RESTORE_SP(fp); + + /* Restore the calling script's interpreter registers. */ + script = fp->script; + depth = (jsint) script->depth; + pc = fp->pc; + endpc = script->code + script->length; + + /* Store the generating pc for the return value. */ + vp[-depth] = (jsval)pc; + + /* Set remaining variables for 'goto advance_pc'. */ + op = (JSOp) *pc; + cs = &js_CodeSpec[op]; + len = cs->length; + + /* Resume execution in the calling frame. */ + inlineCallCount--; + if (ok) + goto advance_pc; + } + goto out; + +#if JS_HAS_SWITCH_STATEMENT + case JSOP_DEFAULT: + (void) POP(); + /* FALL THROUGH */ +#endif + case JSOP_GOTO: + len = GET_JUMP_OFFSET(pc); + CHECK_BRANCH(len); + break; + + case JSOP_IFEQ: + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_FALSE) { + len = GET_JUMP_OFFSET(pc); + CHECK_BRANCH(len); + } + break; + + case JSOP_IFNE: + POP_BOOLEAN(cx, rval, cond); + if (cond != JS_FALSE) { + len = GET_JUMP_OFFSET(pc); + CHECK_BRANCH(len); + } + break; + + case JSOP_OR: + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_TRUE) { + len = GET_JUMP_OFFSET(pc); + PUSH_OPND(rval); + } + break; + + case JSOP_AND: + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_FALSE) { + len = GET_JUMP_OFFSET(pc); + PUSH_OPND(rval); + } + break; + + +#if JS_HAS_SWITCH_STATEMENT + case JSOP_DEFAULTX: + (void) POP(); + /* FALL THROUGH */ +#endif + case JSOP_GOTOX: + len = GET_JUMPX_OFFSET(pc); + CHECK_BRANCH(len); + break; + + case JSOP_IFEQX: + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_FALSE) { + len = GET_JUMPX_OFFSET(pc); + CHECK_BRANCH(len); + } + break; + + case JSOP_IFNEX: + POP_BOOLEAN(cx, rval, cond); + if (cond != JS_FALSE) { + len = GET_JUMPX_OFFSET(pc); + CHECK_BRANCH(len); + } + break; + + case JSOP_ORX: + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_TRUE) { + len = GET_JUMPX_OFFSET(pc); + PUSH_OPND(rval); + } + break; + + case JSOP_ANDX: + POP_BOOLEAN(cx, rval, cond); + if (cond == JS_FALSE) { + len = GET_JUMPX_OFFSET(pc); + PUSH_OPND(rval); + } + break; + + case JSOP_TOOBJECT: + rval = FETCH_OPND(-1); + if (!JSVAL_IS_PRIMITIVE(rval)) { + obj = JSVAL_TO_OBJECT(rval); + } else { + SAVE_SP(fp); + ok = js_ValueToObject(cx, rval, &obj); + if (!ok) + goto out; + } + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + break; + +/* + * If the index value at sp[n] is not an int that fits in a jsval, it could + * be an object (an XML QName, AttributeName, or AnyName), but only if we are + * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a + * string atom id. + */ +#define FETCH_ELEMENT_ID(n, id) \ + JS_BEGIN_MACRO \ + jsval idval_ = FETCH_OPND(n); \ + if (JSVAL_IS_INT(idval_)) { \ + id = INT_JSVAL_TO_JSID(idval_); \ + } else { \ + SAVE_SP(fp); \ + ok = InternNonIntElementId(cx, idval_, &id); \ + if (!ok) \ + goto out; \ + } \ + JS_END_MACRO + +#if JS_HAS_IN_OPERATOR + case JSOP_IN: + SAVE_SP(fp); + rval = FETCH_OPND(-1); + if (JSVAL_IS_PRIMITIVE(rval)) { + str = js_DecompileValueGenerator(cx, -1, rval, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_IN_NOT_OBJECT, + JS_GetStringBytes(str)); + } + ok = JS_FALSE; + goto out; + } + obj = JSVAL_TO_OBJECT(rval); + FETCH_ELEMENT_ID(-2, id); + CHECK_ELEMENT_ID(obj, id); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); + if (!ok) + goto out; + sp--; + STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL)); + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + break; +#endif /* JS_HAS_IN_OPERATOR */ + + case JSOP_FORPROP: + /* + * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop + * is not paid for the more common cases. + */ + lval = FETCH_OPND(-1); + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + i = -2; + goto do_forinloop; + + case JSOP_FORNAME: + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + + /* + * ECMA 12.6.3 says to eval the LHS after looking for properties + * to enumerate, and bail without LHS eval if there are no props. + * We do Find here to share the most code at label do_forinloop. + * If looking for enumerable properties could have side effects, + * then we'd have to move this into the common code and condition + * it on op == JSOP_FORNAME. + */ + SAVE_SP(fp); + ok = js_FindProperty(cx, id, &obj, &obj2, &prop); + if (!ok) + goto out; + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + lval = OBJECT_TO_JSVAL(obj); + /* FALL THROUGH */ + + case JSOP_FORARG: + case JSOP_FORVAR: + /* + * JSOP_FORARG and JSOP_FORVAR don't require any lval computation + * here, because they address slots on the stack (in fp->args and + * fp->vars, respectively). + */ + /* FALL THROUGH */ + + case JSOP_FORELEM: + /* + * JSOP_FORELEM simply initializes or updates the iteration state + * and leaves the index expression evaluation and assignment to the + * enumerator until after the next property has been acquired, via + * a JSOP_ENUMELEM bytecode. + */ + i = -1; + + do_forinloop: + /* + * ECMA-compatible for/in evals the object just once, before loop. + * Bad old bytecodes (since removed) did it on every iteration. + */ + obj = JSVAL_TO_OBJECT(sp[i]); + + /* If the thing to the right of 'in' has no properties, break. */ + if (!obj) { + rval = JSVAL_FALSE; + goto end_forinloop; + } + + /* + * Save the thing to the right of 'in' as origobj. Later on, we + * use this variable to suppress enumeration of shadowed prototype + * properties. + */ + origobj = obj; + + /* + * Reach under the top of stack to find our property iterator, a + * JSObject that contains the iteration state. (An object is used + * rather than a native struct so that the iteration state is + * cleaned up via GC if the for-in loop terminates abruptly.) + */ + vp = &sp[i - 1]; + rval = *vp; + + /* + * Save sp in fp now, before any OBJ_* call-outs that might nest + * an interpreter or GC activation on this context. + */ + SAVE_SP(fp); + + /* Is this the first iteration ? */ + if (JSVAL_IS_VOID(rval)) { + /* + * Yes, create a new JSObject to hold the iterator state. + * Use NULL as the nominal parent in js_NewObject to ensure + * that we use the correct scope chain lookup to try to find the + * PropertyIterator constructor. + */ + propobj = js_NewObject(cx, &prop_iterator_class, NULL, NULL); + if (!propobj) { + ok = JS_FALSE; + goto out; + } + + /* + * Now that we've resolved the object, use the PARENT slot to + * store the object that we're iterating over. + */ + propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); + propobj->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; + + /* + * Root the parent slot so we can get it even in our finalizer + * (otherwise, it would live as long as we do, but it might be + * finalized first). + */ + ok = js_AddRoot(cx, &propobj->slots[JSSLOT_PARENT], + "propobj->parent"); + if (!ok) + goto out; + + /* + * Rewrite the iterator so we know to do the next case. + * Do this before calling the enumerator, which could + * displace cx->newborn and cause GC. + */ + *vp = OBJECT_TO_JSVAL(propobj); + + ok = +#if JS_HAS_XML_SUPPORT + (foreach && OBJECT_IS_XML(cx, obj)) + ? ((JSXMLObjectOps *) obj->map->ops)->enumerateValues + (cx, obj, JSENUMERATE_INIT, &iter_state, + NULL, NULL) + : +#endif + OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, + NULL); + if (!ok) + goto out; + + /* + * Stash private iteration state into property iterator object. + * NB: This code knows that the first slots are pre-allocated. + */ +#if JS_INITIAL_NSLOTS < 5 +#error JS_INITIAL_NSLOTS must be greater than or equal to 5. +#endif + propobj->slots[JSSLOT_ITER_STATE] = iter_state; + } else { + /* This is not the first iteration. Recover iterator state. */ + propobj = JSVAL_TO_OBJECT(rval); + JS_ASSERT(OBJ_GET_CLASS(cx, propobj) == &prop_iterator_class); + obj = JSVAL_TO_OBJECT(propobj->slots[JSSLOT_PARENT]); + iter_state = propobj->slots[JSSLOT_ITER_STATE]; + } + + enum_next_property: + { + jsid fid; + + /* Get the next jsid to be enumerated and store it in fid. */ + ok = +#if JS_HAS_XML_SUPPORT + (foreach && OBJECT_IS_XML(cx, obj)) + ? ((JSXMLObjectOps *) obj->map->ops)->enumerateValues + (cx, obj, JSENUMERATE_NEXT, &iter_state, + &fid, &rval) + : +#endif + OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &fid); + propobj->slots[JSSLOT_ITER_STATE] = iter_state; + + /* No more jsids to iterate in obj? */ + if (iter_state == JSVAL_NULL) { + /* Enumerate the properties on obj's prototype chain. */ + obj = OBJ_GET_PROTO(cx, obj); + if (!obj) { + /* End of property list -- terminate loop. */ + rval = JSVAL_FALSE; +#if JS_HAS_XML_SUPPORT + foreach = JS_FALSE; +#endif + goto end_forinloop; + } + + ok = +#if JS_HAS_XML_SUPPORT + (foreach && OBJECT_IS_XML(cx, obj)) + ? ((JSXMLObjectOps *) obj->map->ops)->enumerateValues + (cx, obj, JSENUMERATE_INIT, &iter_state, + NULL, NULL) + : +#endif + OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, + NULL); + + /* + * Stash private iteration state into property iterator object. + * We do this before checking 'ok' to ensure that propobj is + * in a valid state even if OBJ_ENUMERATE returned JS_FALSE. + * NB: This code knows that the first slots are pre-allocated. + */ + propobj->slots[JSSLOT_ITER_STATE] = iter_state; + if (!ok) + goto out; + + /* + * Update the iterator JSObject's parent link to refer to the + * current object. This is used in the iterator JSObject's + * finalizer. + */ + propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); + goto enum_next_property; + } + + /* Skip properties not owned by obj when looking from origobj. */ + ok = OBJ_LOOKUP_PROPERTY(cx, origobj, fid, &obj2, &prop); + if (!ok) + goto out; + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + + /* + * If the id was deleted, or found in a prototype or an unrelated + * object (specifically, not in an inner object for obj), skip it. + * This means that OBJ_LOOKUP_PROPERTY implementations must return + * an object either further on the prototype chain, or related by + * the JSExtendedClass.outerObject optional hook. + */ + if (!prop) + goto enum_next_property; + if (obj != obj2) { + cond = JS_FALSE; + clasp = OBJ_GET_CLASS(cx, obj2); + if (clasp->flags & JSCLASS_IS_EXTENDED) { + JSExtendedClass *xclasp; + + xclasp = (JSExtendedClass *) clasp; + cond = xclasp->outerObject && + xclasp->outerObject(cx, obj2) == obj; + } + if (!cond) + goto enum_next_property; + } + +#if JS_HAS_XML_SUPPORT + if (foreach) { + /* Clear the local foreach flag set by our prefix bytecode. */ + foreach = JS_FALSE; + + /* If obj is not XML, we must get rval given its fid. */ + if (!OBJECT_IS_XML(cx, obj)) { + ok = OBJ_GET_PROPERTY(cx, origobj, fid, &rval); + if (!ok) + goto out; + } + } else +#endif + { + /* Make rval a string for uniformity and compatibility. */ + if (JSID_IS_ATOM(fid)) { + rval = ATOM_KEY(JSID_TO_ATOM(fid)); + } +#if JS_HAS_XML_SUPPORT + else if (JSID_IS_OBJECT(fid)) { + str = js_ValueToString(cx, OBJECT_JSID_TO_JSVAL(fid)); + if (!str) { + ok = JS_FALSE; + goto out; + } + + rval = STRING_TO_JSVAL(str); + } +#endif + else if (!JS_VERSION_IS_1_2(cx)) { + str = js_NumberToString(cx, (jsdouble) JSID_TO_INT(fid)); + if (!str) { + ok = JS_FALSE; + goto out; + } + + rval = STRING_TO_JSVAL(str); + } else { + rval = INT_JSID_TO_JSVAL(fid); + } + } + + switch (op) { + case JSOP_FORARG: + slot = GET_ARGNO(pc); + JS_ASSERT(slot < fp->fun->nargs); + fp->argv[slot] = rval; + break; + + case JSOP_FORVAR: + slot = GET_VARNO(pc); + JS_ASSERT(slot < fp->fun->nvars); + fp->vars[slot] = rval; + break; + + case JSOP_FORELEM: + /* FORELEM is not a SET operation, it's more like BINDNAME. */ + PUSH_OPND(rval); + break; + + default: + /* Convert lval to a non-null object containing id. */ + VALUE_TO_OBJECT(cx, lval, obj); + if (i + 1 < 0) + STORE_OPND(i + 1, OBJECT_TO_JSVAL(obj)); + + /* Set the variable obj[id] to refer to rval. */ + fp->flags |= JSFRAME_ASSIGNING; + ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); + fp->flags &= ~JSFRAME_ASSIGNING; + if (!ok) + goto out; + break; + } + + /* Push true to keep looping through properties. */ + rval = JSVAL_TRUE; + + end_forinloop: + sp += i + 1; + PUSH_OPND(rval); + break; + } + + case JSOP_DUP: + JS_ASSERT(sp > fp->spbase); + rval = sp[-1]; + PUSH_OPND(rval); + break; + + case JSOP_DUP2: + JS_ASSERT(sp - 1 > fp->spbase); + lval = FETCH_OPND(-2); + rval = FETCH_OPND(-1); + PUSH_OPND(lval); + PUSH_OPND(rval); + break; + +#define PROPERTY_OP(n, call) \ + JS_BEGIN_MACRO \ + /* Fetch the left part and resolve it to a non-null object. */ \ + FETCH_OBJECT(cx, n, lval, obj); \ + \ + /* Get or set the property, set ok false if error, true if success. */\ + SAVE_SP(fp); \ + call; \ + if (!ok) \ + goto out; \ + JS_END_MACRO + +#define ELEMENT_OP(n, call) \ + JS_BEGIN_MACRO \ + /* Fetch the right part and resolve it to an internal id. */ \ + FETCH_ELEMENT_ID(n, id); \ + \ + /* Fetch the left part and resolve it to a non-null object. */ \ + FETCH_OBJECT(cx, n - 1, lval, obj); \ + \ + /* Ensure that id has a type suitable for use with obj. */ \ + CHECK_ELEMENT_ID(obj, id); \ + \ + /* Get or set the element, set ok false if error, true if success. */ \ + SAVE_SP(fp); \ + call; \ + if (!ok) \ + goto out; \ + JS_END_MACRO + +/* + * Direct callers, i.e. those who do not wrap CACHED_GET and CACHED_SET calls + * in PROPERTY_OP or ELEMENT_OP macro calls must SAVE_SP(fp); beforehand, just + * in case a getter or setter function is invoked. CACHED_GET and CACHED_SET + * use cx, obj, id, and rval from their caller's lexical environment. + */ +#define CACHED_GET(call) CACHED_GET_VP(call, &rval) + +#define CACHED_GET_VP(call,vp) \ + JS_BEGIN_MACRO \ + if (!OBJ_IS_NATIVE(obj)) { \ + ok = call; \ + } else { \ + JS_LOCK_OBJ(cx, obj); \ + PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \ + if (sprop) { \ + JSScope *scope_ = OBJ_SCOPE(obj); \ + slot = (uintN)sprop->slot; \ + *(vp) = (slot != SPROP_INVALID_SLOT) \ + ? LOCKED_OBJ_GET_SLOT(obj, slot) \ + : JSVAL_VOID; \ + JS_UNLOCK_SCOPE(cx, scope_); \ + ok = SPROP_GET(cx, sprop, obj, obj, vp); \ + JS_LOCK_SCOPE(cx, scope_); \ + if (ok && SPROP_HAS_VALID_SLOT(sprop, scope_)) \ + LOCKED_OBJ_SET_SLOT(obj, slot, *(vp)); \ + JS_UNLOCK_SCOPE(cx, scope_); \ + } else { \ + JS_UNLOCK_OBJ(cx, obj); \ + ok = call; \ + /* No fill here: js_GetProperty fills the cache. */ \ + } \ + } \ + JS_END_MACRO + +#define CACHED_SET(call) \ + JS_BEGIN_MACRO \ + if (!OBJ_IS_NATIVE(obj)) { \ + ok = call; \ + } else { \ + JSScope *scope_; \ + JS_LOCK_OBJ(cx, obj); \ + PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \ + if (sprop && \ + !(sprop->attrs & JSPROP_READONLY) && \ + (scope_ = OBJ_SCOPE(obj), !SCOPE_IS_SEALED(scope_))) { \ + JS_UNLOCK_SCOPE(cx, scope_); \ + ok = SPROP_SET(cx, sprop, obj, obj, &rval); \ + JS_LOCK_SCOPE(cx, scope_); \ + if (ok && SPROP_HAS_VALID_SLOT(sprop, scope_)) { \ + LOCKED_OBJ_SET_SLOT(obj, sprop->slot, rval); \ + GC_POKE(cx, JSVAL_NULL); /* XXX second arg ignored */ \ + } \ + JS_UNLOCK_SCOPE(cx, scope_); \ + } else { \ + JS_UNLOCK_OBJ(cx, obj); \ + ok = call; \ + /* No fill here: js_SetProperty writes through the cache. */ \ + } \ + } \ + JS_END_MACRO + +#define BEGIN_LITOPX_CASE(OP,PCOFF) \ + case OP: \ + atomIndex = GET_ATOM_INDEX(pc + PCOFF); \ + do_##OP: \ + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + +#define END_LITOPX_CASE \ + break; \ + + BEGIN_LITOPX_CASE(JSOP_SETCONST, 0) + obj = fp->varobj; + rval = FETCH_OPND(-1); + SAVE_SP(fp); + ok = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval, + NULL, NULL, + JSPROP_ENUMERATE | JSPROP_PERMANENT | + JSPROP_READONLY, + NULL); + if (!ok) + goto out; + STORE_OPND(-1, rval); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_BINDNAME, 0) + SAVE_SP(fp); + obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); + if (!obj) { + ok = JS_FALSE; + goto out; + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + END_LITOPX_CASE + + case JSOP_SETNAME: + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); + obj = JSVAL_TO_OBJECT(lval); + SAVE_SP(fp); + CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); + if (!ok) + goto out; + sp--; + STORE_OPND(-1, rval); + obj = NULL; + break; + +#define INTEGER_OP(OP, EXTRA_CODE) \ + JS_BEGIN_MACRO \ + FETCH_INT(cx, -1, j); \ + FETCH_INT(cx, -2, i); \ + if (!ok) \ + goto out; \ + EXTRA_CODE \ + d = i OP j; \ + sp--; \ + STORE_NUMBER(cx, -1, d); \ + JS_END_MACRO + +#define BITWISE_OP(OP) INTEGER_OP(OP, (void) 0;) +#define SIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;) + + case JSOP_BITOR: + BITWISE_OP(|); + break; + + case JSOP_BITXOR: + BITWISE_OP(^); + break; + + case JSOP_BITAND: + BITWISE_OP(&); + break; + +#define RELATIONAL_OP(OP) \ + JS_BEGIN_MACRO \ + rval = FETCH_OPND(-1); \ + lval = FETCH_OPND(-2); \ + /* Optimize for two int-tagged operands (typical loop control). */ \ + if ((lval & rval) & JSVAL_INT) { \ + ltmp = lval ^ JSVAL_VOID; \ + rtmp = rval ^ JSVAL_VOID; \ + if (ltmp && rtmp) { \ + cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \ + } else { \ + d = ltmp ? JSVAL_TO_INT(lval) : *rt->jsNaN; \ + d2 = rtmp ? JSVAL_TO_INT(rval) : *rt->jsNaN; \ + cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ + } \ + } else { \ + VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_NUMBER, &lval); \ + sp[-2] = lval; \ + VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_NUMBER, &rval); \ + if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \ + str = JSVAL_TO_STRING(lval); \ + str2 = JSVAL_TO_STRING(rval); \ + cond = js_CompareStrings(str, str2) OP 0; \ + } else { \ + VALUE_TO_NUMBER(cx, lval, d); \ + VALUE_TO_NUMBER(cx, rval, d2); \ + cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ + } \ + } \ + sp--; \ + STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ + JS_END_MACRO + +/* + * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies + * because they begin if/else chains, so callers must not put semicolons after + * the call expressions! + */ +#if JS_HAS_XML_SUPPORT +#define XML_EQUALITY_OP(OP) \ + if ((ltmp == JSVAL_OBJECT && \ + (obj2 = JSVAL_TO_OBJECT(lval)) && \ + OBJECT_IS_XML(cx, obj2)) || \ + (rtmp == JSVAL_OBJECT && \ + (obj2 = JSVAL_TO_OBJECT(rval)) && \ + OBJECT_IS_XML(cx, obj2))) { \ + JSXMLObjectOps *ops; \ + \ + ops = (JSXMLObjectOps *) obj2->map->ops; \ + if (obj2 == JSVAL_TO_OBJECT(rval)) \ + rval = lval; \ + SAVE_SP(fp); \ + ok = ops->equality(cx, obj2, rval, &cond); \ + if (!ok) \ + goto out; \ + cond = cond OP JS_TRUE; \ + } else + +#define XML_NAME_EQUALITY_OP(OP) \ + if (ltmp == JSVAL_OBJECT && \ + (obj2 = JSVAL_TO_OBJECT(lval)) && \ + ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \ + JSExtendedClass *xclasp; \ + \ + xclasp = (JSExtendedClass *) clasp; \ + SAVE_SP(fp); \ + ok = xclasp->equality(cx, obj2, rval, &cond); \ + if (!ok) \ + goto out; \ + cond = cond OP JS_TRUE; \ + } else +#else +#define XML_EQUALITY_OP(OP) /* nothing */ +#define XML_NAME_EQUALITY_OP(OP) /* nothing */ +#endif + +#define EQUALITY_OP(OP, IFNAN) \ + JS_BEGIN_MACRO \ + rval = FETCH_OPND(-1); \ + lval = FETCH_OPND(-2); \ + ltmp = JSVAL_TAG(lval); \ + rtmp = JSVAL_TAG(rval); \ + XML_EQUALITY_OP(OP) \ + if (ltmp == rtmp) { \ + if (ltmp == JSVAL_STRING) { \ + str = JSVAL_TO_STRING(lval); \ + str2 = JSVAL_TO_STRING(rval); \ + cond = js_CompareStrings(str, str2) OP 0; \ + } else if (ltmp == JSVAL_DOUBLE) { \ + d = *JSVAL_TO_DOUBLE(lval); \ + d2 = *JSVAL_TO_DOUBLE(rval); \ + cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ + } else { \ + XML_NAME_EQUALITY_OP(OP) \ + /* Handle all undefined (=>NaN) and int combinations. */ \ + cond = lval OP rval; \ + } \ + } else { \ + if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \ + cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \ + } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \ + cond = 1 OP 0; \ + } else { \ + if (ltmp == JSVAL_OBJECT) { \ + VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); \ + lval = sp[-2]; \ + ltmp = JSVAL_TAG(lval); \ + } else if (rtmp == JSVAL_OBJECT) { \ + VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); \ + rval = sp[-1]; \ + rtmp = JSVAL_TAG(rval); \ + } \ + if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \ + str = JSVAL_TO_STRING(lval); \ + str2 = JSVAL_TO_STRING(rval); \ + cond = js_CompareStrings(str, str2) OP 0; \ + } else { \ + VALUE_TO_NUMBER(cx, lval, d); \ + VALUE_TO_NUMBER(cx, rval, d2); \ + cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ + } \ + } \ + } \ + sp--; \ + STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ + JS_END_MACRO + + case JSOP_EQ: + EQUALITY_OP(==, JS_FALSE); + break; + + case JSOP_NE: + EQUALITY_OP(!=, JS_TRUE); + break; + +#if !JS_BUG_FALLIBLE_EQOPS +#define NEW_EQUALITY_OP(OP) \ + JS_BEGIN_MACRO \ + rval = FETCH_OPND(-1); \ + lval = FETCH_OPND(-2); \ + cond = js_StrictlyEqual(lval, rval) OP JS_TRUE; \ + sp--; \ + STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ + JS_END_MACRO + + case JSOP_NEW_EQ: + NEW_EQUALITY_OP(==); + break; + + case JSOP_NEW_NE: + NEW_EQUALITY_OP(!=); + break; + +#if JS_HAS_SWITCH_STATEMENT + case JSOP_CASE: + NEW_EQUALITY_OP(==); + (void) POP(); + if (cond) { + len = GET_JUMP_OFFSET(pc); + CHECK_BRANCH(len); + } else { + PUSH(lval); + } + break; + + case JSOP_CASEX: + NEW_EQUALITY_OP(==); + (void) POP(); + if (cond) { + len = GET_JUMPX_OFFSET(pc); + CHECK_BRANCH(len); + } else { + PUSH(lval); + } + break; +#endif + +#endif /* !JS_BUG_FALLIBLE_EQOPS */ + + case JSOP_LT: + RELATIONAL_OP(<); + break; + + case JSOP_LE: + RELATIONAL_OP(<=); + break; + + case JSOP_GT: + RELATIONAL_OP(>); + break; + + case JSOP_GE: + RELATIONAL_OP(>=); + break; + +#undef EQUALITY_OP +#undef RELATIONAL_OP + + case JSOP_LSH: + SIGNED_SHIFT_OP(<<); + break; + + case JSOP_RSH: + SIGNED_SHIFT_OP(>>); + break; + + case JSOP_URSH: + { + uint32 u; + + FETCH_INT(cx, -1, j); + FETCH_UINT(cx, -2, u); + j &= 31; + d = u >> j; + sp--; + STORE_NUMBER(cx, -1, d); + break; + } + +#undef INTEGER_OP +#undef BITWISE_OP +#undef SIGNED_SHIFT_OP + + case JSOP_ADD: + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); +#if JS_HAS_XML_SUPPORT + if (!JSVAL_IS_PRIMITIVE(lval) && + (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) && + VALUE_IS_XML(cx, rval)) { + JSXMLObjectOps *ops; + + ops = (JSXMLObjectOps *) obj2->map->ops; + SAVE_SP(fp); + ok = ops->concatenate(cx, obj2, rval, &rval); + if (!ok) + goto out; + sp--; + STORE_OPND(-1, rval); + break; + } +#endif + { + VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); + lval = sp[-2]; + VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); + rval = sp[-1]; + if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) { + SAVE_SP(fp); + if (cond) { + str = JSVAL_TO_STRING(lval); + ok = (str2 = js_ValueToString(cx, rval)) != NULL; + } else { + str2 = JSVAL_TO_STRING(rval); + ok = (str = js_ValueToString(cx, lval)) != NULL; + } + if (!ok) + goto out; + str = js_ConcatStrings(cx, str, str2); + if (!str) { + ok = JS_FALSE; + goto out; + } + sp--; + STORE_OPND(-1, STRING_TO_JSVAL(str)); + } else { + VALUE_TO_NUMBER(cx, lval, d); + VALUE_TO_NUMBER(cx, rval, d2); + d += d2; + sp--; + STORE_NUMBER(cx, -1, d); + } + } + break; + +#define BINARY_OP(OP) \ + JS_BEGIN_MACRO \ + FETCH_NUMBER(cx, -1, d2); \ + FETCH_NUMBER(cx, -2, d); \ + d = d OP d2; \ + sp--; \ + STORE_NUMBER(cx, -1, d); \ + JS_END_MACRO + + case JSOP_SUB: + BINARY_OP(-); + break; + + case JSOP_MUL: + BINARY_OP(*); + break; + + case JSOP_DIV: + FETCH_NUMBER(cx, -1, d2); + FETCH_NUMBER(cx, -2, d); + sp--; + if (d2 == 0) { +#if defined(XP_WIN) + /* XXX MSVC miscompiles such that (NaN == 0) */ + if (JSDOUBLE_IS_NaN(d2)) + rval = DOUBLE_TO_JSVAL(rt->jsNaN); + else +#endif + if (d == 0 || JSDOUBLE_IS_NaN(d)) + rval = DOUBLE_TO_JSVAL(rt->jsNaN); + else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) + rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity); + else + rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity); + STORE_OPND(-1, rval); + } else { + d /= d2; + STORE_NUMBER(cx, -1, d); + } + break; + + case JSOP_MOD: + FETCH_NUMBER(cx, -1, d2); + FETCH_NUMBER(cx, -2, d); + sp--; + if (d2 == 0) { + STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN)); + } else { +#if defined(XP_WIN) + /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ + if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) +#endif + d = fmod(d, d2); + STORE_NUMBER(cx, -1, d); + } + break; + + case JSOP_NOT: + POP_BOOLEAN(cx, rval, cond); + PUSH_OPND(BOOLEAN_TO_JSVAL(!cond)); + break; + + case JSOP_BITNOT: + FETCH_INT(cx, -1, i); + d = (jsdouble) ~i; + STORE_NUMBER(cx, -1, d); + break; + + case JSOP_NEG: + FETCH_NUMBER(cx, -1, d); +#ifdef HPUX + /* + * Negation of a zero doesn't produce a negative + * zero on HPUX. Perform the operation by bit + * twiddling. + */ + JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; +#else + d = -d; +#endif + STORE_NUMBER(cx, -1, d); + break; + + case JSOP_POS: + FETCH_NUMBER(cx, -1, d); + STORE_NUMBER(cx, -1, d); + break; + + case JSOP_NEW: + /* Get immediate argc and find the constructor function. */ + argc = GET_ARGC(pc); + +#if JS_HAS_INITIALIZERS + do_new: +#endif + SAVE_SP(fp); + vp = sp - (2 + argc); + JS_ASSERT(vp >= fp->spbase); + + fun = NULL; + obj2 = NULL; + lval = *vp; + if (!JSVAL_IS_OBJECT(lval) || + (obj2 = JSVAL_TO_OBJECT(lval)) == NULL || + /* XXX clean up to avoid special cases above ObjectOps layer */ + OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass || + !obj2->map->ops->construct) + { + fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT); + if (!fun) { + ok = JS_FALSE; + goto out; + } + } + + clasp = &js_ObjectClass; + if (!obj2) { + proto = parent = NULL; + fun = NULL; + } else { + /* + * Get the constructor prototype object for this function. + * Use the nominal |this| parameter slot, vp[1], as a local + * root to protect this prototype, in case it has no other + * strong refs. + */ + ok = OBJ_GET_PROPERTY(cx, obj2, + ATOM_TO_JSID(rt->atomState + .classPrototypeAtom), + &vp[1]); + if (!ok) + goto out; + rval = vp[1]; + proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL; + parent = OBJ_GET_PARENT(cx, obj2); + + if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) { + funclasp = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp; + if (funclasp) + clasp = funclasp; + } + } + obj = js_NewObject(cx, clasp, proto, parent); + if (!obj) { + ok = JS_FALSE; + goto out; + } + + /* Now we have an object with a constructor method; call it. */ + vp[1] = OBJECT_TO_JSVAL(obj); + ok = js_Invoke(cx, argc, JSINVOKE_CONSTRUCT); + RESTORE_SP(fp); + LOAD_BRANCH_CALLBACK(cx); + LOAD_INTERRUPT_HANDLER(rt); + if (!ok) { + cx->newborn[GCX_OBJECT] = NULL; + goto out; + } + + /* Check the return value and update obj from it. */ + rval = *vp; + if (JSVAL_IS_PRIMITIVE(rval)) { + if (fun || !JS_VERSION_IS_ECMA(cx)) { + *vp = OBJECT_TO_JSVAL(obj); + break; + } + /* native [[Construct]] returning primitive is error */ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_NEW_RESULT, + js_ValueToPrintableString(cx, rval)); + ok = JS_FALSE; + goto out; + } + obj = JSVAL_TO_OBJECT(rval); + JS_RUNTIME_METER(rt, constructs); + break; + + case JSOP_DELNAME: + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + + SAVE_SP(fp); + ok = js_FindProperty(cx, id, &obj, &obj2, &prop); + if (!ok) + goto out; + + /* ECMA says to return true if name is undefined or inherited. */ + rval = JSVAL_TRUE; + if (prop) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + } + PUSH_OPND(rval); + break; + + case JSOP_DELPROP: + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); + STORE_OPND(-1, rval); + break; + + case JSOP_DELELEM: + ELEMENT_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); + sp--; + STORE_OPND(-1, rval); + break; + + case JSOP_TYPEOF: + rval = FETCH_OPND(-1); + SAVE_SP(fp); + type = JS_TypeOfValue(cx, rval); + atom = rt->atomState.typeAtoms[type]; + STORE_OPND(-1, ATOM_KEY(atom)); + break; + + case JSOP_VOID: + (void) POP_OPND(); + PUSH_OPND(JSVAL_VOID); + break; + + case JSOP_INCNAME: + case JSOP_DECNAME: + case JSOP_NAMEINC: + case JSOP_NAMEDEC: + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + + SAVE_SP(fp); + ok = js_FindProperty(cx, id, &obj, &obj2, &prop); + if (!ok) + goto out; + if (!prop) + goto atom_not_defined; + + OBJ_DROP_PROPERTY(cx, obj2, prop); + lval = OBJECT_TO_JSVAL(obj); + i = 0; + goto do_incop; + + case JSOP_INCPROP: + case JSOP_DECPROP: + case JSOP_PROPINC: + case JSOP_PROPDEC: + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + lval = FETCH_OPND(-1); + i = -1; + goto do_incop; + + case JSOP_INCELEM: + case JSOP_DECELEM: + case JSOP_ELEMINC: + case JSOP_ELEMDEC: + FETCH_ELEMENT_ID(-1, id); + lval = FETCH_OPND(-2); + i = -2; + + do_incop: + VALUE_TO_OBJECT(cx, lval, obj); + if (i < 0) + STORE_OPND(i, OBJECT_TO_JSVAL(obj)); + CHECK_ELEMENT_ID(obj, id); + + /* The operand must contain a number. */ + SAVE_SP(fp); + CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); + if (!ok) + goto out; + + /* The expression result goes in rtmp, the updated value in rval. */ + if (JSVAL_IS_INT(rval) && + rval != INT_TO_JSVAL(JSVAL_INT_MIN) && + rval != INT_TO_JSVAL(JSVAL_INT_MAX)) { + if (cs->format & JOF_POST) { + rtmp = rval; + (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2); + } else { + (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2); + rtmp = rval; + } + } else { + +/* + * Initially, rval contains the value to increment or decrement, which is not + * yet converted. As above, the expression result goes in rtmp, the updated + * value goes in rval. Our caller must set vp to point at a GC-rooted jsval + * in which we home rtmp, to protect it from GC in case the unconverted rval + * is not a number. + */ +#define NONINT_INCREMENT_OP_MIDDLE() \ + JS_BEGIN_MACRO \ + VALUE_TO_NUMBER(cx, rval, d); \ + if (cs->format & JOF_POST) { \ + rtmp = rval; \ + if (!JSVAL_IS_NUMBER(rtmp)) { \ + ok = js_NewNumberValue(cx, d, &rtmp); \ + if (!ok) \ + goto out; \ + *vp = rtmp; \ + } \ + (cs->format & JOF_INC) ? d++ : d--; \ + ok = js_NewNumberValue(cx, d, &rval); \ + } else { \ + (cs->format & JOF_INC) ? ++d : --d; \ + ok = js_NewNumberValue(cx, d, &rval); \ + rtmp = rval; \ + } \ + if (!ok) \ + goto out; \ + JS_END_MACRO + + if (cs->format & JOF_POST) { + /* + * We must push early to protect the postfix increment + * or decrement result, if converted to a jsdouble from + * a non-number value, from GC nesting in the setter. + */ + vp = sp++; + SAVE_SP(fp); + --i; + } +#ifdef __GNUC__ + else vp = NULL; /* suppress bogus gcc warnings */ +#endif + + NONINT_INCREMENT_OP_MIDDLE(); + } + + fp->flags |= JSFRAME_ASSIGNING; + CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); + fp->flags &= ~JSFRAME_ASSIGNING; + if (!ok) + goto out; + sp += i; + PUSH_OPND(rtmp); + break; + +/* + * NB: This macro can't use JS_BEGIN_MACRO/JS_END_MACRO around its body because + * it must break from the switch case that calls it, not from the do...while(0) + * loop created by the JS_BEGIN/END_MACRO brackets. + */ +#define FAST_INCREMENT_OP(SLOT,COUNT,BASE,PRE,OP,MINMAX) \ + slot = SLOT; \ + JS_ASSERT(slot < fp->fun->COUNT); \ + vp = fp->BASE + slot; \ + rval = *vp; \ + if (JSVAL_IS_INT(rval) && \ + rval != INT_TO_JSVAL(JSVAL_INT_##MINMAX)) { \ + PRE = rval; \ + rval OP 2; \ + *vp = rval; \ + PUSH_OPND(PRE); \ + break; \ + } \ + goto do_nonint_fast_incop; + + case JSOP_INCARG: + FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, +=, MAX); + case JSOP_DECARG: + FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, -=, MIN); + case JSOP_ARGINC: + FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, +=, MAX); + case JSOP_ARGDEC: + FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, -=, MIN); + + case JSOP_INCVAR: + FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, +=, MAX); + case JSOP_DECVAR: + FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, -=, MIN); + case JSOP_VARINC: + FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, +=, MAX); + case JSOP_VARDEC: + FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, -=, MIN); + +#undef FAST_INCREMENT_OP + + do_nonint_fast_incop: + NONINT_INCREMENT_OP_MIDDLE(); + *vp = rval; + PUSH_OPND(rtmp); + break; + +#define FAST_GLOBAL_INCREMENT_OP(SLOWOP,PRE,OP,MINMAX) \ + slot = GET_VARNO(pc); \ + JS_ASSERT(slot < fp->nvars); \ + lval = fp->vars[slot]; \ + if (JSVAL_IS_NULL(lval)) { \ + op = SLOWOP; \ + goto do_op; \ + } \ + slot = JSVAL_TO_INT(lval); \ + obj = fp->varobj; \ + rval = OBJ_GET_SLOT(cx, obj, slot); \ + if (JSVAL_IS_INT(rval) && \ + rval != INT_TO_JSVAL(JSVAL_INT_##MINMAX)) { \ + PRE = rval; \ + rval OP 2; \ + OBJ_SET_SLOT(cx, obj, slot, rval); \ + PUSH_OPND(PRE); \ + break; \ + } \ + goto do_nonint_fast_global_incop; + + case JSOP_INCGVAR: + FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, rval, +=, MAX); + case JSOP_DECGVAR: + FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, rval, -=, MIN); + case JSOP_GVARINC: + FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, rtmp, +=, MAX); + case JSOP_GVARDEC: + FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, rtmp, -=, MIN); + +#undef FAST_GLOBAL_INCREMENT_OP + + do_nonint_fast_global_incop: + vp = sp++; + SAVE_SP(fp); + NONINT_INCREMENT_OP_MIDDLE(); + OBJ_SET_SLOT(cx, obj, slot, rval); + STORE_OPND(-1, rtmp); + break; + + case JSOP_GETPROP: + /* Get an immediate atom naming the property. */ + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + PROPERTY_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); + STORE_OPND(-1, rval); + break; + + case JSOP_SETPROP: + /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */ + rval = FETCH_OPND(-1); + + /* Get an immediate atom naming the property. */ + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + PROPERTY_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); + sp--; + STORE_OPND(-1, rval); + obj = NULL; + break; + + case JSOP_GETELEM: + ELEMENT_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); + sp--; + STORE_OPND(-1, rval); + break; + + case JSOP_SETELEM: + rval = FETCH_OPND(-1); + ELEMENT_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); + sp -= 2; + STORE_OPND(-1, rval); + obj = NULL; + break; + + case JSOP_ENUMELEM: + /* Funky: the value to set is under the [obj, id] pair. */ + FETCH_ELEMENT_ID(-1, id); + FETCH_OBJECT(cx, -2, lval, obj); + CHECK_ELEMENT_ID(obj, id); + rval = FETCH_OPND(-3); + SAVE_SP(fp); + ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + sp -= 3; + break; + +/* + * LAZY_ARGS_THISP allows the JSOP_ARGSUB bytecode to defer creation of the + * arguments object until it is truly needed. JSOP_ARGSUB optimizes away + * arguments objects when the only uses of the 'arguments' parameter are to + * fetch individual actual parameters. But if such a use were then invoked, + * e.g., arguments[i](), the 'this' parameter would and must bind to the + * caller's arguments object. So JSOP_ARGSUB sets obj to LAZY_ARGS_THISP. + */ +#define LAZY_ARGS_THISP ((JSObject *) 1) + + case JSOP_PUSHOBJ: + if (obj == LAZY_ARGS_THISP && !(obj = js_GetArgsObject(cx, fp))) { + ok = JS_FALSE; + goto out; + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_CALL: + case JSOP_EVAL: + argc = GET_ARGC(pc); + vp = sp - (argc + 2); + lval = *vp; + SAVE_SP(fp); + + if (JSVAL_IS_FUNCTION(cx, lval) && + (obj = JSVAL_TO_OBJECT(lval), + fun = (JSFunction *) JS_GetPrivate(cx, obj), + fun->interpreted && + !(fun->flags & (JSFUN_HEAVYWEIGHT | JSFUN_BOUND_METHOD)) && + argc >= (uintN)(fun->nargs + fun->extra))) + /* inline_call: */ + { + uintN nframeslots, nvars; + void *newmark; + JSInlineFrame *newifp; + JSInterpreterHook hook; + + /* Restrict recursion of lightweight functions. */ + if (inlineCallCount == MAX_INLINE_CALL_COUNT) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_OVER_RECURSED); + ok = JS_FALSE; + goto out; + } + +#if JS_HAS_JIT + /* ZZZbe should do this only if interpreted often enough. */ + ok = jsjit_Compile(cx, fun); + if (!ok) + goto out; +#endif + + /* Compute the number of stack slots needed for fun. */ + nframeslots = (sizeof(JSInlineFrame) + sizeof(jsval) - 1) + / sizeof(jsval); + nvars = fun->nvars; + script = fun->u.script; + depth = (jsint) script->depth; + + /* Allocate the frame and space for vars and operands. */ + newsp = js_AllocRawStack(cx, nframeslots + nvars + 2 * depth, + &newmark); + if (!newsp) { + ok = JS_FALSE; + goto bad_inline_call; + } + newifp = (JSInlineFrame *) newsp; + newsp += nframeslots; + + /* Initialize the stack frame. */ + memset(newifp, 0, sizeof(JSInlineFrame)); + newifp->frame.script = script; + newifp->frame.fun = fun; + newifp->frame.argc = argc; + newifp->frame.argv = vp + 2; + newifp->frame.rval = JSVAL_VOID; + newifp->frame.nvars = nvars; + newifp->frame.vars = newsp; + newifp->frame.down = fp; + newifp->frame.scopeChain = OBJ_GET_PARENT(cx, obj); + newifp->mark = newmark; + + /* Compute the 'this' parameter now that argv is set. */ + ok = js_ComputeThis(cx, JSVAL_TO_OBJECT(vp[1]), &newifp->frame); + if (!ok) { + js_FreeRawStack(cx, newmark); + goto bad_inline_call; + } +#ifdef DUMP_CALL_TABLE + LogCall(cx, *vp, argc, vp + 2); +#endif + + /* Push void to initialize local variables. */ + sp = newsp; + while (nvars--) + PUSH(JSVAL_VOID); + sp += depth; + newifp->frame.spbase = sp; + SAVE_SP(&newifp->frame); + + /* Call the debugger hook if present. */ + hook = cx->runtime->callHook; + if (hook) { + newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0, + cx->runtime->callHookData); + LOAD_INTERRUPT_HANDLER(rt); + } + + /* Switch to new version if currentVersion wasn't overridden. */ + newifp->callerVersion = cx->version; + if (cx->version == currentVersion) { + currentVersion = script->version; + if (currentVersion != cx->version) + js_SetVersion(cx, currentVersion); + } + + /* Push the frame and set interpreter registers. */ + cx->fp = fp = &newifp->frame; + pc = script->code; + endpc = pc + script->length; + inlineCallCount++; + JS_RUNTIME_METER(rt, inlineCalls); + continue; + + bad_inline_call: + script = fp->script; + depth = (jsint) script->depth; + goto out; + } + + ok = js_Invoke(cx, argc, 0); + RESTORE_SP(fp); + LOAD_BRANCH_CALLBACK(cx); + LOAD_INTERRUPT_HANDLER(rt); + if (!ok) + goto out; + JS_RUNTIME_METER(rt, nonInlineCalls); +#if JS_HAS_LVALUE_RETURN + if (cx->rval2set) { + /* + * Sneaky: use the stack depth we didn't claim in our budget, + * but that we know is there on account of [fun, this] already + * having been pushed, at a minimum (if no args). Those two + * slots have been popped and [rval] has been pushed, which + * leaves one more slot for rval2 before we might overflow. + * + * NB: rval2 must be the property identifier, and rval the + * object from which to get the property. The pair form an + * ECMA "reference type", which can be used on the right- or + * left-hand side of assignment ops. Only native methods can + * return reference types. See JSOP_SETCALL just below for + * the left-hand-side case. + */ + PUSH_OPND(cx->rval2); + cx->rval2set = JS_FALSE; + ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval)); + sp--; + STORE_OPND(-1, rval); + } +#endif + obj = NULL; + break; + +#if JS_HAS_LVALUE_RETURN + case JSOP_SETCALL: + argc = GET_ARGC(pc); + SAVE_SP(fp); + ok = js_Invoke(cx, argc, 0); + RESTORE_SP(fp); + LOAD_BRANCH_CALLBACK(cx); + LOAD_INTERRUPT_HANDLER(rt); + if (!ok) + goto out; + if (!cx->rval2set) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_LEFTSIDE_OF_ASS); + ok = JS_FALSE; + goto out; + } + PUSH_OPND(cx->rval2); + cx->rval2set = JS_FALSE; + obj = NULL; + break; +#endif + + case JSOP_NAME: + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + + SAVE_SP(fp); + ok = js_FindProperty(cx, id, &obj, &obj2, &prop); + if (!ok) + goto out; + if (!prop) { + /* Kludge to allow (typeof foo == "undefined") tests. */ + for (pc2 = pc + len; pc2 < endpc; pc2++) { + op2 = (JSOp)*pc2; + if (op2 == JSOP_TYPEOF) { + PUSH_OPND(JSVAL_VOID); + goto advance_pc; + } + if (op2 != JSOP_GROUP) + break; + } + goto atom_not_defined; + } + + /* Take the slow path if prop was not found in a native object. */ + if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + PUSH_OPND(rval); + break; + } + + /* Get and push the obj[id] property's value. */ + sprop = (JSScopeProperty *)prop; + slot = (uintN)sprop->slot; + rval = (slot != SPROP_INVALID_SLOT) + ? LOCKED_OBJ_GET_SLOT(obj2, slot) + : JSVAL_VOID; + JS_UNLOCK_OBJ(cx, obj2); + ok = SPROP_GET(cx, sprop, obj, obj2, &rval); + JS_LOCK_OBJ(cx, obj2); + if (!ok) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + goto out; + } + if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2))) + LOCKED_OBJ_SET_SLOT(obj2, slot, rval); + OBJ_DROP_PROPERTY(cx, obj2, prop); + PUSH_OPND(rval); + break; + + case JSOP_UINT16: + i = (jsint) GET_ATOM_INDEX(pc); + rval = INT_TO_JSVAL(i); + PUSH_OPND(rval); + obj = NULL; + break; + + case JSOP_UINT24: + i = (jsint) GET_LITERAL_INDEX(pc); + rval = INT_TO_JSVAL(i); + PUSH_OPND(rval); + break; + + case JSOP_LITERAL: + atomIndex = GET_LITERAL_INDEX(pc); + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + PUSH_OPND(ATOM_KEY(atom)); + obj = NULL; + break; + + case JSOP_FINDNAME: + atomIndex = GET_LITERAL_INDEX(pc); + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + SAVE_SP(fp); + obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); + if (!obj) { + ok = JS_FALSE; + goto out; + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + PUSH_OPND(ATOM_KEY(atom)); + break; + + case JSOP_LITOPX: + atomIndex = GET_LITERAL_INDEX(pc); + op = pc[1 + LITERAL_INDEX_LEN]; + switch (op) { + case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ; + case JSOP_BINDNAME: goto do_JSOP_BINDNAME; + case JSOP_CLOSURE: goto do_JSOP_CLOSURE; + case JSOP_DEFCONST: goto do_JSOP_DEFCONST; + case JSOP_DEFFUN: goto do_JSOP_DEFFUN; + case JSOP_DEFLOCALFUN: goto do_JSOP_DEFLOCALFUN; + case JSOP_DEFVAR: goto do_JSOP_DEFVAR; +#if JS_HAS_EXPORT_IMPORT + case JSOP_EXPORTNAME: goto do_JSOP_EXPORTNAME; +#endif +#if JS_HAS_XML_SUPPORT + case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD; + case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD; +#endif + case JSOP_INITCATCHVAR: goto do_JSOP_INITCATCHVAR; + case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ; + case JSOP_NUMBER: goto do_JSOP_NUMBER; + case JSOP_OBJECT: goto do_JSOP_OBJECT; +#if JS_HAS_XML_SUPPORT + case JSOP_QNAMECONST: goto do_JSOP_QNAMECONST; + case JSOP_QNAMEPART: goto do_JSOP_QNAMEPART; +#endif + case JSOP_REGEXP: goto do_JSOP_REGEXP; + case JSOP_SETCONST: goto do_JSOP_SETCONST; + case JSOP_STRING: goto do_JSOP_STRING; +#if JS_HAS_XML_SUPPORT + case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA; + case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT; + case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT; + case JSOP_XMLPI: goto do_JSOP_XMLPI; +#endif + default: JS_ASSERT(0); + } + /* NOTREACHED */ + break; + + case JSOP_NUMBER: + case JSOP_STRING: + case JSOP_OBJECT: + atomIndex = GET_ATOM_INDEX(pc); + + do_JSOP_NUMBER: + do_JSOP_STRING: + do_JSOP_OBJECT: + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + PUSH_OPND(ATOM_KEY(atom)); + obj = NULL; + break; + + BEGIN_LITOPX_CASE(JSOP_REGEXP, 0) + { + JSRegExp *re; + JSObject *funobj; + + /* + * Push a regexp object for the atom mapped by the bytecode at pc, + * cloning the literal's regexp object if necessary, to simulate in + * the pre-compile/execute-later case what ECMA specifies for the + * compile-and-go case: that scanning each regexp literal creates + * a single corresponding RegExp object. + * + * To support pre-compilation transparently, we must handle the + * case where a regexp object literal is used in a different global + * at execution time from the global with which it was scanned at + * compile time. We do this by re-wrapping the JSRegExp private + * data struct with a cloned object having the right prototype and + * parent, and having its own lastIndex property value storage. + * + * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone + * literal objects, we don't want to pay a script prolog execution + * price for all regexp literals in a script (many may not be used + * by a particular execution of that script, depending on control + * flow), so we initialize lazily here. + * + * XXX This code is specific to regular expression objects. If we + * need a similar op for other kinds of object literals, we should + * push cloning down under JSObjectOps and reuse code here. + */ + JS_ASSERT(ATOM_IS_OBJECT(atom)); + obj = ATOM_TO_OBJECT(atom); + JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); + + re = (JSRegExp *) JS_GetPrivate(cx, obj); + slot = re->cloneIndex; + if (fp->fun) { + /* + * We're in function code, not global or eval code (in eval + * code, JSOP_REGEXP is never emitted). The code generator + * recorded in fp->fun->nregexps the number of re->cloneIndex + * slots that it reserved in the cloned funobj. + */ + funobj = JSVAL_TO_OBJECT(fp->argv[-2]); + slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass); + if (!JS_GetReservedSlot(cx, funobj, slot, &rval)) + return JS_FALSE; + if (JSVAL_IS_VOID(rval)) + rval = JSVAL_NULL; + } else { + /* + * We're in global code. The code generator already arranged + * via script->numGlobalVars to reserve a global variable slot + * at cloneIndex. All global variable slots are initialized + * to null, not void, for faster testing in JSOP_*GVAR cases. + */ + rval = fp->vars[slot]; +#ifdef __GNUC__ + funobj = NULL; /* suppress bogus gcc warnings */ +#endif + } + + if (JSVAL_IS_NULL(rval)) { + /* Compute the current global object in obj2. */ + obj2 = fp->scopeChain; + while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL) + obj2 = parent; + + /* + * We must home sp here, because either js_CloneRegExpObject + * or JS_SetReservedSlot could nest a last-ditch GC. We home + * pc as well, in case js_CloneRegExpObject has to lookup the + * "RegExp" class in the global object, which could entail a + * JSNewResolveOp call. + */ + SAVE_SP(fp); + + /* + * If obj's parent is not obj2, we must clone obj so that it + * has the right parent, and therefore, the right prototype. + * + * Yes, this means we assume that the correct RegExp.prototype + * to which regexp instances (including literals) delegate can + * be distinguished solely by the instance's parent, which was + * set to the parent of the RegExp constructor function object + * when the instance was created. In other words, + * + * (/x/.__parent__ == RegExp.__parent__) implies + * (/x/.__proto__ == RegExp.prototype) + * + * (unless you assign a different object to RegExp.prototype + * at runtime, in which case, ECMA doesn't specify operation, + * and you get what you deserve). + * + * This same coupling between instance parent and constructor + * parent turns up everywhere (see jsobj.c's FindConstructor, + * js_ConstructObject, and js_NewObject). It's fundamental to + * the design of the language when you consider multiple global + * objects and separate compilation and execution, even though + * it is not specified fully in ECMA. + */ + if (OBJ_GET_PARENT(cx, obj) != obj2) { + obj = js_CloneRegExpObject(cx, obj, obj2); + if (!obj) { + ok = JS_FALSE; + goto out; + } + } + rval = OBJECT_TO_JSVAL(obj); + + /* Store the regexp object value in its cloneIndex slot. */ + if (fp->fun) { + if (!JS_SetReservedSlot(cx, funobj, slot, rval)) + return JS_FALSE; + } else { + fp->vars[slot] = rval; + } + } + + PUSH_OPND(rval); + obj = NULL; + } + END_LITOPX_CASE + + case JSOP_ZERO: + PUSH_OPND(JSVAL_ZERO); + obj = NULL; + break; + + case JSOP_ONE: + PUSH_OPND(JSVAL_ONE); + obj = NULL; + break; + + case JSOP_NULL: + PUSH_OPND(JSVAL_NULL); + obj = NULL; + break; + + case JSOP_THIS: + obj = fp->thisp; + clasp = OBJ_GET_CLASS(cx, obj); + if (clasp->flags & JSCLASS_IS_EXTENDED) { + JSExtendedClass *xclasp; + + xclasp = (JSExtendedClass *) clasp; + if (xclasp->outerObject) { + obj = xclasp->outerObject(cx, obj); + if (!obj) { + ok = JS_FALSE; + goto out; + } + } + } + + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + obj = NULL; + break; + + case JSOP_FALSE: + PUSH_OPND(JSVAL_FALSE); + obj = NULL; + break; + + case JSOP_TRUE: + PUSH_OPND(JSVAL_TRUE); + obj = NULL; + break; + +#if JS_HAS_SWITCH_STATEMENT + case JSOP_TABLESWITCH: + pc2 = pc; + len = GET_JUMP_OFFSET(pc2); + + /* + * ECMAv2 forbids conversion of discriminant, so we will skip to + * the default case if the discriminant isn't already an int jsval. + * (This opcode is emitted only for dense jsint-domain switches.) + */ + if ((cx->version & JSVERSION_MASK) == JSVERSION_DEFAULT || + (cx->version & JSVERSION_MASK) >= JSVERSION_1_4) { + rval = POP_OPND(); + if (!JSVAL_IS_INT(rval)) + break; + i = JSVAL_TO_INT(rval); + } else { + FETCH_INT(cx, -1, i); + sp--; + } + + pc2 += JUMP_OFFSET_LEN; + low = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + high = GET_JUMP_OFFSET(pc2); + + i -= low; + if ((jsuint)i < (jsuint)(high - low + 1)) { + pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i; + off = (jsint) GET_JUMP_OFFSET(pc2); + if (off) + len = off; + } + break; + + case JSOP_LOOKUPSWITCH: + lval = POP_OPND(); + pc2 = pc; + len = GET_JUMP_OFFSET(pc2); + + if (!JSVAL_IS_NUMBER(lval) && + !JSVAL_IS_STRING(lval) && + !JSVAL_IS_BOOLEAN(lval)) { + goto advance_pc; + } + + pc2 += JUMP_OFFSET_LEN; + npairs = (jsint) GET_ATOM_INDEX(pc2); + pc2 += ATOM_INDEX_LEN; + +#define SEARCH_PAIRS(MATCH_CODE) \ + while (npairs) { \ + atom = GET_ATOM(cx, script, pc2); \ + rval = ATOM_KEY(atom); \ + MATCH_CODE \ + if (match) { \ + pc2 += ATOM_INDEX_LEN; \ + len = GET_JUMP_OFFSET(pc2); \ + goto advance_pc; \ + } \ + pc2 += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; \ + npairs--; \ + } + if (JSVAL_IS_STRING(lval)) { + str = JSVAL_TO_STRING(lval); + SEARCH_PAIRS( + match = (JSVAL_IS_STRING(rval) && + ((str2 = JSVAL_TO_STRING(rval)) == str || + !js_CompareStrings(str2, str))); + ) + } else if (JSVAL_IS_DOUBLE(lval)) { + d = *JSVAL_TO_DOUBLE(lval); + SEARCH_PAIRS( + match = (JSVAL_IS_DOUBLE(rval) && + *JSVAL_TO_DOUBLE(rval) == d); + ) + } else { + SEARCH_PAIRS( + match = (lval == rval); + ) + } +#undef SEARCH_PAIRS + break; + + case JSOP_TABLESWITCHX: + pc2 = pc; + len = GET_JUMPX_OFFSET(pc2); + + /* + * ECMAv2 forbids conversion of discriminant, so we will skip to + * the default case if the discriminant isn't already an int jsval. + * (This opcode is emitted only for dense jsint-domain switches.) + */ + if ((cx->version & JSVERSION_MASK) == JSVERSION_DEFAULT || + (cx->version & JSVERSION_MASK) >= JSVERSION_1_4) { + rval = POP_OPND(); + if (!JSVAL_IS_INT(rval)) + break; + i = JSVAL_TO_INT(rval); + } else { + FETCH_INT(cx, -1, i); + sp--; + } + + pc2 += JUMPX_OFFSET_LEN; + low = GET_JUMP_OFFSET(pc2); + pc2 += JUMP_OFFSET_LEN; + high = GET_JUMP_OFFSET(pc2); + + i -= low; + if ((jsuint)i < (jsuint)(high - low + 1)) { + pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i; + off = (jsint) GET_JUMPX_OFFSET(pc2); + if (off) + len = off; + } + break; + + case JSOP_LOOKUPSWITCHX: + lval = POP_OPND(); + pc2 = pc; + len = GET_JUMPX_OFFSET(pc2); + + if (!JSVAL_IS_NUMBER(lval) && + !JSVAL_IS_STRING(lval) && + !JSVAL_IS_BOOLEAN(lval)) { + goto advance_pc; + } + + pc2 += JUMPX_OFFSET_LEN; + npairs = (jsint) GET_ATOM_INDEX(pc2); + pc2 += ATOM_INDEX_LEN; + +#define SEARCH_EXTENDED_PAIRS(MATCH_CODE) \ + while (npairs) { \ + atom = GET_ATOM(cx, script, pc2); \ + rval = ATOM_KEY(atom); \ + MATCH_CODE \ + if (match) { \ + pc2 += ATOM_INDEX_LEN; \ + len = GET_JUMPX_OFFSET(pc2); \ + goto advance_pc; \ + } \ + pc2 += ATOM_INDEX_LEN + JUMPX_OFFSET_LEN; \ + npairs--; \ + } + if (JSVAL_IS_STRING(lval)) { + str = JSVAL_TO_STRING(lval); + SEARCH_EXTENDED_PAIRS( + match = (JSVAL_IS_STRING(rval) && + ((str2 = JSVAL_TO_STRING(rval)) == str || + !js_CompareStrings(str2, str))); + ) + } else if (JSVAL_IS_DOUBLE(lval)) { + d = *JSVAL_TO_DOUBLE(lval); + SEARCH_EXTENDED_PAIRS( + match = (JSVAL_IS_DOUBLE(rval) && + *JSVAL_TO_DOUBLE(rval) == d); + ) + } else { + SEARCH_EXTENDED_PAIRS( + match = (lval == rval); + ) + } +#undef SEARCH_EXTENDED_PAIRS + break; + + case JSOP_CONDSWITCH: + break; + +#endif /* JS_HAS_SWITCH_STATEMENT */ + +#if JS_HAS_EXPORT_IMPORT + case JSOP_EXPORTALL: + SAVE_SP(fp); + obj = fp->varobj; + ida = JS_Enumerate(cx, obj); + if (!ida) { + ok = JS_FALSE; + } else { + for (i = 0, j = ida->length; i < j; i++) { + id = ida->vector[i]; + ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); + if (!ok) + break; + if (!prop) + continue; + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); + if (ok) { + attrs |= JSPROP_EXPORTED; + ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + if (!ok) + break; + } + JS_DestroyIdArray(cx, ida); + } + break; + + BEGIN_LITOPX_CASE(JSOP_EXPORTNAME, 0) + id = ATOM_TO_JSID(atom); + obj = fp->varobj; + SAVE_SP(fp); + ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); + if (!ok) + goto out; + if (!prop) { + ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, + JSPROP_EXPORTED, NULL); + } else { + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); + if (ok) { + attrs |= JSPROP_EXPORTED; + ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); + } + OBJ_DROP_PROPERTY(cx, obj2, prop); + } + if (!ok) + goto out; + END_LITOPX_CASE + + case JSOP_IMPORTALL: + id = (jsid) JSVAL_VOID; + PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); + sp--; + break; + + case JSOP_IMPORTPROP: + /* Get an immediate atom naming the property. */ + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); + sp--; + break; + + case JSOP_IMPORTELEM: + ELEMENT_OP(-1, ok = ImportProperty(cx, obj, id)); + sp -= 2; + break; +#endif /* JS_HAS_EXPORT_IMPORT */ + + case JSOP_TRAP: + switch (JS_HandleTrap(cx, script, pc, &rval)) { + case JSTRAP_ERROR: + ok = JS_FALSE; + goto out; + case JSTRAP_CONTINUE: + JS_ASSERT(JSVAL_IS_INT(rval)); + op = (JSOp) JSVAL_TO_INT(rval); + JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); + LOAD_INTERRUPT_HANDLER(rt); + goto do_op; + case JSTRAP_RETURN: + fp->rval = rval; + goto out; +#if JS_HAS_EXCEPTIONS + case JSTRAP_THROW: + cx->throwing = JS_TRUE; + cx->exception = rval; + ok = JS_FALSE; + goto out; +#endif /* JS_HAS_EXCEPTIONS */ + default:; + } + LOAD_INTERRUPT_HANDLER(rt); + break; + + case JSOP_ARGUMENTS: + SAVE_SP(fp); + ok = js_GetArgsValue(cx, fp, &rval); + if (!ok) + goto out; + PUSH_OPND(rval); + break; + + case JSOP_ARGSUB: + id = INT_TO_JSID(GET_ARGNO(pc)); + SAVE_SP(fp); + ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); + if (!ok) + goto out; + if (!obj) { + /* + * If arguments was not overridden by eval('arguments = ...'), + * set obj to the magic cookie respected by JSOP_PUSHOBJ, just + * in case this bytecode is part of an 'arguments[i](j, k)' or + * similar such invocation sequence, where the function that + * is invoked expects its 'this' parameter to be the caller's + * arguments object. + */ + obj = LAZY_ARGS_THISP; + } + PUSH_OPND(rval); + break; + +#undef LAZY_ARGS_THISP + + case JSOP_ARGCNT: + id = ATOM_TO_JSID(rt->atomState.lengthAtom); + SAVE_SP(fp); + ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); + if (!ok) + goto out; + PUSH_OPND(rval); + break; + + case JSOP_GETARG: + slot = GET_ARGNO(pc); + JS_ASSERT(slot < fp->fun->nargs); + PUSH_OPND(fp->argv[slot]); + obj = NULL; + break; + + case JSOP_SETARG: + slot = GET_ARGNO(pc); + JS_ASSERT(slot < fp->fun->nargs); + vp = &fp->argv[slot]; + GC_POKE(cx, *vp); + *vp = FETCH_OPND(-1); + obj = NULL; + break; + + case JSOP_GETVAR: + slot = GET_VARNO(pc); + JS_ASSERT(slot < fp->fun->nvars); + PUSH_OPND(fp->vars[slot]); + obj = NULL; + break; + + case JSOP_SETVAR: + slot = GET_VARNO(pc); + JS_ASSERT(slot < fp->fun->nvars); + vp = &fp->vars[slot]; + GC_POKE(cx, *vp); + *vp = FETCH_OPND(-1); + obj = NULL; + break; + + case JSOP_GETGVAR: + slot = GET_VARNO(pc); + JS_ASSERT(slot < fp->nvars); + lval = fp->vars[slot]; + if (JSVAL_IS_NULL(lval)) { + op = JSOP_NAME; + goto do_op; + } + slot = JSVAL_TO_INT(lval); + obj = fp->varobj; + rval = OBJ_GET_SLOT(cx, obj, slot); + PUSH_OPND(rval); + break; + + case JSOP_SETGVAR: + slot = GET_VARNO(pc); + JS_ASSERT(slot < fp->nvars); + rval = FETCH_OPND(-1); + lval = fp->vars[slot]; + obj = fp->varobj; + if (JSVAL_IS_NULL(lval)) { + /* + * Inline-clone and specialize JSOP_SETNAME code here because + * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval] + * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME. + */ + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + SAVE_SP(fp); + CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); + if (!ok) + goto out; + STORE_OPND(-1, rval); + } else { + slot = JSVAL_TO_INT(lval); + GC_POKE(cx, obj->slots[slot]); + OBJ_SET_SLOT(cx, obj, slot, rval); + } + obj = NULL; + break; + + case JSOP_DEFCONST: + case JSOP_DEFVAR: + atomIndex = GET_ATOM_INDEX(pc); + + do_JSOP_DEFCONST: + do_JSOP_DEFVAR: + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + obj = fp->varobj; + attrs = JSPROP_ENUMERATE; + if (!(fp->flags & JSFRAME_EVAL)) + attrs |= JSPROP_PERMANENT; + if (op == JSOP_DEFCONST) + attrs |= JSPROP_READONLY; + + /* Lookup id in order to check for redeclaration problems. */ + id = ATOM_TO_JSID(atom); + SAVE_SP(fp); + ok = js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop); + if (!ok) + goto out; + + /* Bind a variable only if it's not yet defined. */ + if (!prop) { + ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, + attrs, &prop); + if (!ok) + goto out; + JS_ASSERT(prop); + obj2 = obj; + } + + /* + * Try to optimize a property we either just created, or found + * directly in the global object, that is permanent, has a slot, + * and has stub getter and setter, into a "fast global" accessed + * by the JSOP_*GVAR opcodes. + */ + if (atomIndex < script->numGlobalVars && + (attrs & JSPROP_PERMANENT) && + obj2 == obj && + OBJ_IS_NATIVE(obj)) { + sprop = (JSScopeProperty *) prop; + if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) && + SPROP_HAS_STUB_GETTER(sprop) && + SPROP_HAS_STUB_SETTER(sprop)) { + /* + * Fast globals use fp->vars to map the global name's + * atomIndex to the permanent fp->varobj slot number, + * tagged as a jsval. The atomIndex for the global's + * name literal is identical to its fp->vars index. + */ + fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); + } + } + + OBJ_DROP_PROPERTY(cx, obj2, prop); + break; + + BEGIN_LITOPX_CASE(JSOP_DEFFUN, 0) + { + uintN flags; + + atomIndex = GET_ATOM_INDEX(pc); + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + obj = ATOM_TO_OBJECT(atom); + fun = (JSFunction *) JS_GetPrivate(cx, obj); + id = ATOM_TO_JSID(fun->atom); + + /* + * We must be at top-level (either outermost block that forms a + * function's body, or a global) scope, not inside an expression + * (JSOP_{ANON,NAMED}FUNOBJ) or compound statement (JSOP_CLOSURE) + * in the same compilation unit (ECMA Program). + * + * However, we could be in a Program being eval'd from inside a + * with statement, so we need to distinguish scope chain head from + * variables object. Hence the obj2 vs. parent distinction below. + * First we make sure the function object we're defining has the + * right scope chain. Then we define its name in fp->varobj. + * + * If static link is not current scope, clone fun's object to link + * to the current scope via parent. This clause exists to enable + * sharing of compiled functions among multiple equivalent scopes, + * splitting the cost of compilation evenly among the scopes and + * amortizing it over a number of executions. Examples include XUL + * scripts and event handlers shared among Mozilla chrome windows, + * and server-side JS user-defined functions shared among requests. + * + * NB: The Script object exposes compile and exec in the language, + * such that this clause introduces an incompatible change from old + * JS versions that supported Script. Such a JS version supported + * executing a script that defined and called functions scoped by + * the compile-time static link, not by the exec-time scope chain. + * + * We sacrifice compatibility, breaking such scripts, in order to + * promote compile-cost sharing and amortizing, and because Script + * is not and will not be standardized. + */ + obj2 = fp->scopeChain; + if (OBJ_GET_PARENT(cx, obj) != obj2) { + obj = js_CloneFunctionObject(cx, obj, obj2); + if (!obj) { + ok = JS_FALSE; + goto out; + } + } + + /* + * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All + * paths from here must flow through the "Restore fp->scopeChain" + * code below the OBJ_DEFINE_PROPERTY call. + */ + fp->scopeChain = obj; + rval = OBJECT_TO_JSVAL(obj); + + /* + * ECMA requires functions defined when entering Global code to be + * permanent, and functions defined when entering Eval code to be + * impermanent. + */ + attrs = JSPROP_ENUMERATE; + if (!(fp->flags & JSFRAME_EVAL)) + attrs |= JSPROP_PERMANENT; + + /* + * Load function flags that are also property attributes. Getters + * and setters do not need a slot, their value is stored elsewhere + * in the property itself, not in obj->slots. + */ + flags = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); + if (flags) { + attrs |= flags | JSPROP_SHARED; + rval = JSVAL_VOID; + } + + /* + * Check for a const property of the same name -- or any kind + * of property if executing with the strict option. We check + * here at runtime as well as at compile-time, to handle eval + * as well as multiple HTML script tags. + */ + parent = fp->varobj; + SAVE_SP(fp); + ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL); + if (ok) { + ok = OBJ_DEFINE_PROPERTY(cx, parent, id, rval, + (flags & JSFUN_GETTER) + ? (JSPropertyOp) obj + : NULL, + (flags & JSFUN_SETTER) + ? (JSPropertyOp) obj + : NULL, + attrs, + &prop); + } + + /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ + fp->scopeChain = obj2; + if (!ok) + goto out; + +#if 0 + if (attrs == (JSPROP_ENUMERATE | JSPROP_PERMANENT) && + script->numGlobalVars) { + /* + * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals + * use fp->vars to map the global function name's atomIndex to + * its permanent fp->varobj slot number, tagged as a jsval. + */ + sprop = (JSScopeProperty *) prop; + fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); + } +#endif + OBJ_DROP_PROPERTY(cx, parent, prop); + } + END_LITOPX_CASE + +#if JS_HAS_LEXICAL_CLOSURE + BEGIN_LITOPX_CASE(JSOP_DEFLOCALFUN, VARNO_LEN) + /* + * Define a local function (i.e., one nested at the top level of + * another function), parented by the current scope chain, and + * stored in a local variable slot that the compiler allocated. + * This is an optimization over JSOP_DEFFUN that avoids requiring + * a call object for the outer function's activation. + */ + slot = GET_VARNO(pc); + atom = js_GetAtom(cx, &script->atomMap, atomIndex); + obj = ATOM_TO_OBJECT(atom); + fun = (JSFunction *) JS_GetPrivate(cx, obj); + + parent = fp->scopeChain; + if (OBJ_GET_PARENT(cx, obj) != parent) { + SAVE_SP(fp); + obj = js_CloneFunctionObject(cx, obj, parent); + if (!obj) { + ok = JS_FALSE; + goto out; + } + } + fp->vars[slot] = OBJECT_TO_JSVAL(obj); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_ANONFUNOBJ, 0) + /* Push the specified function object literal. */ + obj = ATOM_TO_OBJECT(atom); + + /* If re-parenting, push a clone of the function object. */ + parent = fp->scopeChain; + if (OBJ_GET_PARENT(cx, obj) != parent) { + SAVE_SP(fp); + obj = js_CloneFunctionObject(cx, obj, parent); + if (!obj) { + ok = JS_FALSE; + goto out; + } + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + obj = NULL; + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_NAMEDFUNOBJ, 0) + /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */ + rval = ATOM_KEY(atom); + JS_ASSERT(JSVAL_IS_FUNCTION(cx, rval)); + + /* + * 1. Create a new object as if by the expression new Object(). + * 2. Add Result(1) to the front of the scope chain. + * + * Step 2 is achieved by making the new object's parent be the + * current scope chain, and then making the new object the parent + * of the Function object clone. + */ + SAVE_SP(fp); + obj2 = fp->scopeChain; + parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2); + if (!parent) { + ok = JS_FALSE; + goto out; + } + + /* + * 3. Create a new Function object as specified in section 13.2 + * with [parameters and body specified by the function expression + * that was parsed by the compiler into a Function object, and + * saved in the script's atom map]. + * + * Protect parent from GC after js_CloneFunctionObject calls into + * js_NewObject, which displaces the newborn object root in cx by + * allocating the clone, then runs a last-ditch GC while trying + * to allocate the clone's slots vector. Another, multi-threaded + * path: js_CloneFunctionObject => js_NewObject => OBJ_GET_CLASS + * which may suspend the current request in ClaimScope, with the + * newborn displaced as in the first scenario. + */ + fp->scopeChain = parent; + obj = js_CloneFunctionObject(cx, JSVAL_TO_OBJECT(rval), parent); + if (!obj) { + ok = JS_FALSE; + goto out; + } + + /* + * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All + * paths from here must flow through the "Restore fp->scopeChain" + * code below the OBJ_DEFINE_PROPERTY call. + */ + fp->scopeChain = obj; + rval = OBJECT_TO_JSVAL(obj); + + /* + * 4. Create a property in the object Result(1). The property's + * name is [fun->atom, the identifier parsed by the compiler], + * value is Result(3), and attributes are { DontDelete, ReadOnly }. + */ + fun = (JSFunction *) JS_GetPrivate(cx, obj); + attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); + if (attrs) { + attrs |= JSPROP_SHARED; + rval = JSVAL_VOID; + } + ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, + (attrs & JSFUN_GETTER) + ? (JSPropertyOp) obj + : NULL, + (attrs & JSFUN_SETTER) + ? (JSPropertyOp) obj + : NULL, + attrs | + JSPROP_ENUMERATE | JSPROP_PERMANENT | + JSPROP_READONLY, + NULL); + + /* Restore fp->scopeChain now that obj is defined in parent. */ + fp->scopeChain = obj2; + if (!ok) { + cx->newborn[GCX_OBJECT] = NULL; + goto out; + } + + /* + * 5. Remove Result(1) from the front of the scope chain [no-op]. + * 6. Return Result(3). + */ + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + obj = NULL; + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_CLOSURE, 0) + /* + * ECMA ed. 3 extension: a named function expression in a compound + * statement (not at the top statement level of global code, or at + * the top level of a function body). + * + * Get immediate operand atom, which is a function object literal. + * From it, get the function to close. + */ + JS_ASSERT(JSVAL_IS_FUNCTION(cx, ATOM_KEY(atom))); + obj = ATOM_TO_OBJECT(atom); + + /* + * Clone the function object with the current scope chain as the + * clone's parent. The original function object is the prototype + * of the clone. Do this only if re-parenting; the compiler may + * have seen the right parent already and created a sufficiently + * well-scoped function object. + */ + SAVE_SP(fp); + obj2 = fp->scopeChain; + if (OBJ_GET_PARENT(cx, obj) != obj2) { + obj = js_CloneFunctionObject(cx, obj, obj2); + if (!obj) { + ok = JS_FALSE; + goto out; + } + } + + /* + * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All + * paths from here must flow through the "Restore fp->scopeChain" + * code below the OBJ_DEFINE_PROPERTY call. + */ + fp->scopeChain = obj; + rval = OBJECT_TO_JSVAL(obj); + + /* + * Make a property in fp->varobj with id fun->atom and value obj, + * unless fun is a getter or setter (in which case, obj is cast to + * a JSPropertyOp and passed accordingly). + */ + fun = (JSFunction *) JS_GetPrivate(cx, obj); + attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); + if (attrs) { + attrs |= JSPROP_SHARED; + rval = JSVAL_VOID; + } + parent = fp->varobj; + ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, + (attrs & JSFUN_GETTER) + ? (JSPropertyOp) obj + : NULL, + (attrs & JSFUN_SETTER) + ? (JSPropertyOp) obj + : NULL, + attrs | JSPROP_ENUMERATE + | JSPROP_PERMANENT, + &prop); + + /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ + fp->scopeChain = obj2; + if (!ok) { + cx->newborn[GCX_OBJECT] = NULL; + goto out; + } + +#if 0 + if (attrs == 0 && script->numGlobalVars) { + /* + * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals + * use fp->vars to map the global function name's atomIndex to + * its permanent fp->varobj slot number, tagged as a jsval. + */ + sprop = (JSScopeProperty *) prop; + fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); + } +#endif + OBJ_DROP_PROPERTY(cx, parent, prop); + END_LITOPX_CASE +#endif /* JS_HAS_LEXICAL_CLOSURE */ + +#if JS_HAS_GETTER_SETTER + case JSOP_GETTER: + case JSOP_SETTER: + JS_ASSERT(len == 1); + op2 = (JSOp) *++pc; + cs = &js_CodeSpec[op2]; + len = cs->length; + switch (op2) { + case JSOP_SETNAME: + case JSOP_SETPROP: + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + rval = FETCH_OPND(-1); + i = -1; + goto gs_pop_lval; + + case JSOP_SETELEM: + rval = FETCH_OPND(-1); + FETCH_ELEMENT_ID(-2, id); + i = -2; + gs_pop_lval: + FETCH_OBJECT(cx, i - 1, lval, obj); + break; + +#if JS_HAS_INITIALIZERS + case JSOP_INITPROP: + JS_ASSERT(sp - fp->spbase >= 2); + rval = FETCH_OPND(-1); + i = -1; + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + goto gs_get_lval; + + case JSOP_INITELEM: + JS_ASSERT(sp - fp->spbase >= 3); + rval = FETCH_OPND(-1); + FETCH_ELEMENT_ID(-2, id); + i = -2; + gs_get_lval: + lval = FETCH_OPND(i-1); + JS_ASSERT(JSVAL_IS_OBJECT(lval)); + obj = JSVAL_TO_OBJECT(lval); + break; +#endif /* JS_HAS_INITIALIZERS */ + + default: + JS_ASSERT(0); + } + + /* Ensure that id has a type suitable for use with obj. */ + CHECK_ELEMENT_ID(obj, id); + + if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_GETTER_OR_SETTER, + (op == JSOP_GETTER) + ? js_getter_str + : js_setter_str); + ok = JS_FALSE; + goto out; + } + + /* + * Getters and setters are just like watchpoints from an access + * control point of view. + */ + SAVE_SP(fp); + ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs); + if (!ok) + goto out; + + if (op == JSOP_GETTER) { + getter = (JSPropertyOp) JSVAL_TO_OBJECT(rval); + setter = NULL; + attrs = JSPROP_GETTER; + } else { + getter = NULL; + setter = (JSPropertyOp) JSVAL_TO_OBJECT(rval); + attrs = JSPROP_SETTER; + } + attrs |= JSPROP_ENUMERATE | JSPROP_SHARED; + + /* Check for a readonly or permanent property of the same name. */ + ok = js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL); + if (!ok) + goto out; + + ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter, + attrs, NULL); + if (!ok) + goto out; + + obj = NULL; + sp += i; + if (cs->ndefs) + STORE_OPND(-1, rval); + break; +#endif /* JS_HAS_GETTER_SETTER */ + +#if JS_HAS_INITIALIZERS + case JSOP_NEWINIT: + argc = 0; + fp->sharpDepth++; + goto do_new; + + case JSOP_ENDINIT: + if (--fp->sharpDepth == 0) + fp->sharpArray = NULL; + + /* Re-set the newborn root to the top of this object tree. */ + JS_ASSERT(sp - fp->spbase >= 1); + lval = FETCH_OPND(-1); + JS_ASSERT(JSVAL_IS_OBJECT(lval)); + cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval); + break; + + case JSOP_INITPROP: + /* Pop the property's value into rval. */ + JS_ASSERT(sp - fp->spbase >= 2); + rval = FETCH_OPND(-1); + + /* Get the immediate property name into id. */ + atom = GET_ATOM(cx, script, pc); + id = ATOM_TO_JSID(atom); + i = -1; + goto do_init; + + case JSOP_INITELEM: + /* Pop the element's value into rval. */ + JS_ASSERT(sp - fp->spbase >= 3); + rval = FETCH_OPND(-1); + + /* Pop and conditionally atomize the element id. */ + FETCH_ELEMENT_ID(-2, id); + i = -2; + + do_init: + /* Find the object being initialized at top of stack. */ + lval = FETCH_OPND(i-1); + JS_ASSERT(JSVAL_IS_OBJECT(lval)); + obj = JSVAL_TO_OBJECT(lval); + + /* Ensure that id has a type suitable for use with obj. */ + CHECK_ELEMENT_ID(obj, id); + + /* Set the property named by obj[id] to rval. */ + SAVE_SP(fp); + ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + sp += i; + break; + +#if JS_HAS_SHARP_VARS + case JSOP_DEFSHARP: + SAVE_SP(fp); + obj = fp->sharpArray; + if (!obj) { + obj = js_NewArrayObject(cx, 0, NULL); + if (!obj) { + ok = JS_FALSE; + goto out; + } + fp->sharpArray = obj; + } + i = (jsint) GET_ATOM_INDEX(pc); + id = INT_TO_JSID(i); + rval = FETCH_OPND(-1); + if (JSVAL_IS_PRIMITIVE(rval)) { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_SHARP_DEF, numBuf); + ok = JS_FALSE; + goto out; + } + ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + break; + + case JSOP_USESHARP: + i = (jsint) GET_ATOM_INDEX(pc); + id = INT_TO_JSID(i); + obj = fp->sharpArray; + if (!obj) { + rval = JSVAL_VOID; + } else { + SAVE_SP(fp); + ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); + if (!ok) + goto out; + } + if (!JSVAL_IS_OBJECT(rval)) { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_SHARP_USE, numBuf); + ok = JS_FALSE; + goto out; + } + PUSH_OPND(rval); + break; +#endif /* JS_HAS_SHARP_VARS */ +#endif /* JS_HAS_INITIALIZERS */ + +#if JS_HAS_EXCEPTIONS + /* No-ops for ease of decompilation and jit'ing. */ + case JSOP_TRY: + case JSOP_FINALLY: + break; + + /* Reset the stack to the given depth. */ + case JSOP_SETSP: + i = (jsint) GET_ATOM_INDEX(pc); + JS_ASSERT(i >= 0); + sp = fp->spbase + i; + + obj = fp->scopeChain; + while (OBJ_GET_CLASS(cx, obj) == &js_WithClass && + JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE)) > i) { + obj = OBJ_GET_PARENT(cx, obj); + } + fp->scopeChain = obj; + break; + + case JSOP_GOSUB: + i = PTRDIFF(pc, script->main, jsbytecode) + len; + len = GET_JUMP_OFFSET(pc); + PUSH(INT_TO_JSVAL(i)); + break; + + case JSOP_GOSUBX: + i = PTRDIFF(pc, script->main, jsbytecode) + len; + len = GET_JUMPX_OFFSET(pc); + PUSH(INT_TO_JSVAL(i)); + break; + + case JSOP_RETSUB: + rval = POP(); + JS_ASSERT(JSVAL_IS_INT(rval)); + i = JSVAL_TO_INT(rval); + pc = script->main + i; + len = 0; + break; + + case JSOP_EXCEPTION: + PUSH(cx->exception); + cx->throwing = JS_FALSE; + break; + + case JSOP_THROW: + cx->throwing = JS_TRUE; + cx->exception = POP_OPND(); + ok = JS_FALSE; + /* let the code at out try to catch the exception. */ + goto out; + + BEGIN_LITOPX_CASE(JSOP_INITCATCHVAR, 0) + /* Load the value into rval, while keeping it live on stack. */ + JS_ASSERT(sp - fp->spbase >= 2); + rval = FETCH_OPND(-1); + + /* Get the immediate catch variable name into id. */ + id = ATOM_TO_JSID(atom); + + /* Find the object being initialized at top of stack. */ + lval = FETCH_OPND(-2); + JS_ASSERT(JSVAL_IS_OBJECT(lval)); + obj = JSVAL_TO_OBJECT(lval); + + SAVE_SP(fp); + + /* + * It's possible for an evil script to substitute a random object + * for the new object. Check to make sure that we don't override a + * readonly property with the below OBJ_DEFINE_PROPERTY. + */ + ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs); + if (!ok) + goto out; + if (!(attrs & (JSPROP_READONLY | JSPROP_PERMANENT | + JSPROP_GETTER | JSPROP_SETTER))) { + /* Define obj[id] to contain rval and to be permanent. */ + ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, + JSPROP_PERMANENT, NULL); + if (!ok) + goto out; + } + + /* Now that we're done with rval, pop it. */ + sp--; + END_LITOPX_CASE +#endif /* JS_HAS_EXCEPTIONS */ + +#if JS_HAS_INSTANCEOF + case JSOP_INSTANCEOF: + rval = FETCH_OPND(-1); + if (JSVAL_IS_PRIMITIVE(rval) || + !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) { + SAVE_SP(fp); + str = js_DecompileValueGenerator(cx, -1, rval, NULL); + if (str) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_INSTANCEOF_RHS, + JS_GetStringBytes(str)); + } + ok = JS_FALSE; + goto out; + } + lval = FETCH_OPND(-2); + cond = JS_FALSE; + SAVE_SP(fp); + ok = obj->map->ops->hasInstance(cx, obj, lval, &cond); + if (!ok) + goto out; + sp--; + STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); + break; +#endif /* JS_HAS_INSTANCEOF */ + +#if JS_HAS_DEBUGGER_KEYWORD + case JSOP_DEBUGGER: + { + JSTrapHandler handler = rt->debuggerHandler; + if (handler) { + SAVE_SP(fp); + switch (handler(cx, script, pc, &rval, + rt->debuggerHandlerData)) { + case JSTRAP_ERROR: + ok = JS_FALSE; + goto out; + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + fp->rval = rval; + goto out; +#if JS_HAS_EXCEPTIONS + case JSTRAP_THROW: + cx->throwing = JS_TRUE; + cx->exception = rval; + ok = JS_FALSE; + goto out; +#endif /* JS_HAS_EXCEPTIONS */ + default:; + } + LOAD_INTERRUPT_HANDLER(rt); + } + break; + } +#endif /* JS_HAS_DEBUGGER_KEYWORD */ + +#if JS_HAS_XML_SUPPORT + case JSOP_DEFXMLNS: + rval = POP(); + SAVE_SP(fp); + ok = js_SetDefaultXMLNamespace(cx, rval); + if (!ok) + goto out; + break; + + case JSOP_ANYNAME: + SAVE_SP(fp); + ok = js_GetAnyName(cx, &rval); + if (!ok) + goto out; + PUSH_OPND(rval); + break; + + BEGIN_LITOPX_CASE(JSOP_QNAMEPART, 0) + PUSH_OPND(ATOM_KEY(atom)); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_QNAMECONST, 0) + rval = ATOM_KEY(atom); + lval = FETCH_OPND(-1); + SAVE_SP(fp); + obj = js_ConstructXMLQNameObject(cx, lval, rval); + if (!obj) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + END_LITOPX_CASE + + case JSOP_QNAME: + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); + SAVE_SP(fp); + obj = js_ConstructXMLQNameObject(cx, lval, rval); + if (!obj) { + ok = JS_FALSE; + goto out; + } + sp--; + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_TOATTRNAME: + rval = FETCH_OPND(-1); + SAVE_SP(fp); + ok = js_ToAttributeName(cx, &rval); + if (!ok) + goto out; + STORE_OPND(-1, rval); + break; + + case JSOP_TOATTRVAL: + rval = FETCH_OPND(-1); + JS_ASSERT(JSVAL_IS_STRING(rval)); + SAVE_SP(fp); + str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval)); + if (!str) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, STRING_TO_JSVAL(str)); + break; + + case JSOP_ADDATTRNAME: + case JSOP_ADDATTRVAL: + rval = FETCH_OPND(-1); + lval = FETCH_OPND(-2); + str = JSVAL_TO_STRING(lval); + str2 = JSVAL_TO_STRING(rval); + SAVE_SP(fp); + str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2); + if (!str) { + ok = JS_FALSE; + goto out; + } + sp--; + STORE_OPND(-1, STRING_TO_JSVAL(str)); + break; + + case JSOP_BINDXMLNAME: + lval = FETCH_OPND(-1); + SAVE_SP(fp); + ok = js_FindXMLProperty(cx, lval, &obj, &rval); + if (!ok) + goto out; + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + PUSH_OPND(rval); + break; + + case JSOP_SETXMLNAME: + obj = JSVAL_TO_OBJECT(FETCH_OPND(-3)); + lval = FETCH_OPND(-2); + rval = FETCH_OPND(-1); + SAVE_SP(fp); + ok = js_SetXMLProperty(cx, obj, lval, &rval); + if (!ok) + goto out; + sp -= 2; + STORE_OPND(-1, rval); + obj = NULL; + break; + + case JSOP_XMLNAME: + lval = FETCH_OPND(-1); + SAVE_SP(fp); + ok = js_FindXMLProperty(cx, lval, &obj, &rval); + if (!ok) + goto out; + ok = js_GetXMLProperty(cx, obj, rval, &rval); + if (!ok) + goto out; + STORE_OPND(-1, rval); + break; + + case JSOP_DESCENDANTS: + case JSOP_DELDESC: + FETCH_OBJECT(cx, -2, lval, obj); + rval = FETCH_OPND(-1); + SAVE_SP(fp); + ok = js_GetXMLDescendants(cx, obj, rval, &rval); + if (!ok) + goto out; + + if (op == JSOP_DELDESC) { + sp[-1] = rval; /* set local root */ + ok = js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)); + if (!ok) + goto out; + rval = JSVAL_TRUE; /* always succeed */ + } + + sp--; + STORE_OPND(-1, rval); + break; + + case JSOP_FILTER: + FETCH_OBJECT(cx, -1, lval, obj); + len = GET_JUMP_OFFSET(pc); + SAVE_SP(fp); + ok = js_FilterXMLList(cx, obj, pc + cs->length, &rval); + if (!ok) + goto out; + JS_ASSERT(fp->sp == sp); + STORE_OPND(-1, rval); + break; + + case JSOP_ENDFILTER: + *result = POP_OPND(); + goto out; + + case JSOP_STARTXML: + case JSOP_STARTXMLEXPR: + break; + + case JSOP_TOXML: + rval = FETCH_OPND(-1); + SAVE_SP(fp); + obj = js_ValueToXMLObject(cx, rval); + if (!obj) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_TOXMLLIST: + rval = FETCH_OPND(-1); + SAVE_SP(fp); + obj = js_ValueToXMLListObject(cx, rval); + if (!obj) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + break; + + case JSOP_XMLTAGEXPR: + rval = FETCH_OPND(-1); + SAVE_SP(fp); + str = js_ValueToString(cx, rval); + if (!str) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, STRING_TO_JSVAL(str)); + break; + + case JSOP_XMLELTEXPR: + rval = FETCH_OPND(-1); + SAVE_SP(fp); + if (VALUE_IS_XML(cx, rval)) { + str = js_ValueToXMLString(cx, rval); + } else { + str = js_ValueToString(cx, rval); + if (str) + str = js_EscapeElementValue(cx, str); + } + if (!str) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, STRING_TO_JSVAL(str)); + break; + + BEGIN_LITOPX_CASE(JSOP_XMLOBJECT, 0) + SAVE_SP(fp); + obj = js_CloneXMLObject(cx, ATOM_TO_OBJECT(atom)); + if (!obj) { + ok = JS_FALSE; + goto out; + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + obj = NULL; + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_XMLCDATA, 0) + str = ATOM_TO_STRING(atom); + obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str); + if (!obj) { + ok = JS_FALSE; + goto out; + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT, 0) + str = ATOM_TO_STRING(atom); + obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str); + if (!obj) { + ok = JS_FALSE; + goto out; + } + PUSH_OPND(OBJECT_TO_JSVAL(obj)); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_XMLPI, 0) + str = ATOM_TO_STRING(atom); + rval = FETCH_OPND(-1); + str2 = JSVAL_TO_STRING(rval); + SAVE_SP(fp); + obj = js_NewXMLSpecialObject(cx, + JSXML_CLASS_PROCESSING_INSTRUCTION, + str, str2); + if (!obj) { + ok = JS_FALSE; + goto out; + } + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_GETMETHOD, 0) + /* Get an immediate atom naming the property. */ + id = ATOM_TO_JSID(atom); + FETCH_OBJECT(cx, -1, lval, obj); + SAVE_SP(fp); + + /* Special-case XML object method lookup, per ECMA-357. */ + if (OBJECT_IS_XML(cx, obj)) { + JSXMLObjectOps *ops; + + ops = (JSXMLObjectOps *) obj->map->ops; + obj = ops->getMethod(cx, obj, id, &rval); + if (!obj) + ok = JS_FALSE; + } else { + CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); + } + if (!ok) + goto out; + STORE_OPND(-1, rval); + END_LITOPX_CASE + + BEGIN_LITOPX_CASE(JSOP_SETMETHOD, 0) + /* Get an immediate atom naming the property. */ + id = ATOM_TO_JSID(atom); + rval = FETCH_OPND(-1); + FETCH_OBJECT(cx, -2, lval, obj); + SAVE_SP(fp); + + /* Special-case XML object method lookup, per ECMA-357. */ + if (OBJECT_IS_XML(cx, obj)) { + JSXMLObjectOps *ops; + + ops = (JSXMLObjectOps *) obj->map->ops; + ok = ops->setMethod(cx, obj, id, &rval); + } else { + CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); + } + if (!ok) + goto out; + --sp; + STORE_OPND(-1, rval); + obj = NULL; + END_LITOPX_CASE + + case JSOP_GETFUNNS: + ok = js_GetFunctionNamespace(cx, &rval); + if (!ok) + goto out; + PUSH_OPND(rval); + break; + + case JSOP_FOREACH: + foreach = JS_TRUE; + break; +#endif /* JS_HAS_XML_SUPPORT */ + + default: { + char numBuf[12]; + JS_snprintf(numBuf, sizeof numBuf, "%d", op); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_BYTECODE, numBuf); + ok = JS_FALSE; + goto out; + } + } + + advance_pc: + pc += len; + +#ifdef DEBUG + if (tracefp) { + intN ndefs, n; + jsval *siter; + + ndefs = cs->ndefs; + if (ndefs) { + SAVE_SP(fp); + if (op == JSOP_FORELEM && sp[-1] == JSVAL_FALSE) + --ndefs; + for (n = -ndefs; n < 0; n++) { + str = js_DecompileValueGenerator(cx, n, sp[n], NULL); + if (str) { + fprintf(tracefp, "%s %s", + (n == -ndefs) ? " output:" : ",", + JS_GetStringBytes(str)); + } + } + fprintf(tracefp, " @ %d\n", sp - fp->spbase); + } + fprintf(tracefp, " stack: "); + for (siter = fp->spbase; siter < sp; siter++) { + str = js_ValueToSource(cx, *siter); + fprintf(tracefp, "%s ", + str ? JS_GetStringBytes(str) : ""); + } + fputc('\n', tracefp); + } +#endif + } +out: + +#if JS_HAS_EXCEPTIONS + if (!ok) { + /* + * Has an exception been raised? Also insist that we are in the + * interpreter activation that pushed fp's operand stack, to avoid + * catching exceptions within XML filtering predicate expressions, + * such as the one from tests/e4x/Regress/regress-301596.js: + * + * try { + * .(@a == 1); + * throw 5; + * } catch (e) { + * } + * + * The inner interpreter activation executing the predicate bytecode + * will throw "reference to undefined XML name @a" (or 5, in older + * versions that followed the first edition of ECMA-357 and evaluated + * unbound identifiers to undefined), and the exception must not be + * caught until control unwinds to the outer interpreter activation. + * + * Otherwise, the wrong stack depth will be restored by JSOP_SETSP, + * and the catch will move into the filtering predicate expression, + * leading to double catch execution if it rethrows. + * + * XXX This assumes the null mark case implies XML filtering predicate + * expression execution! + * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=309894 + */ + if (cx->throwing && JS_LIKELY(mark != NULL)) { + /* + * Call debugger throw hook if set (XXX thread safety?). + */ + JSTrapHandler handler = rt->throwHook; + if (handler) { + SAVE_SP(fp); + switch (handler(cx, script, pc, &rval, rt->throwHookData)) { + case JSTRAP_ERROR: + cx->throwing = JS_FALSE; + goto no_catch; + case JSTRAP_RETURN: + ok = JS_TRUE; + cx->throwing = JS_FALSE; + fp->rval = rval; + goto no_catch; + case JSTRAP_THROW: + cx->exception = rval; + case JSTRAP_CONTINUE: + default:; + } + LOAD_INTERRUPT_HANDLER(rt); + } + + /* + * Look for a try block in script that can catch this exception. + */ + SCRIPT_FIND_CATCH_START(script, pc, pc); + if (pc) { + /* Don't clear cx->throwing to save cx->exception from GC. */ + len = 0; + ok = JS_TRUE; +#if JS_HAS_XML_SUPPORT + foreach = JS_FALSE; +#endif + goto advance_pc; + } + } +no_catch:; + } +#endif + + /* + * Check whether control fell off the end of a lightweight function, or an + * exception thrown under such a function was not caught by it. If so, go + * to the inline code under JSOP_RETURN. + */ + if (inlineCallCount) { +#if JS_HAS_XML_SUPPORT + foreach = JS_FALSE; +#endif + goto inline_return; + } + + /* + * Reset sp before freeing stack slots, because our caller may GC soon. + * Clear spbase to indicate that we've popped the 2 * depth operand slots. + * Restore the previous frame's execution state. + */ + if (JS_LIKELY(mark != NULL)) { + fp->sp = fp->spbase; + fp->spbase = NULL; + js_FreeRawStack(cx, mark); + } else { + SAVE_SP(fp); + } + +out2: + if (cx->version == currentVersion && currentVersion != originalVersion) + js_SetVersion(cx, originalVersion); + cx->interpLevel--; + return ok; + +atom_not_defined: + { + const char *printable = js_AtomToPrintableString(cx, atom); + if (printable) + js_ReportIsNotDefined(cx, printable); + ok = JS_FALSE; +#ifdef OSSP + sp = NULL; +#endif + goto out; + } +} Index: ossp-pkg/js/src/jspubtd.h RCS File: /v/ossp/cvs/ossp-pkg/js/src/jspubtd.h,v co -q -kk -p'1.2' '/v/ossp/cvs/ossp-pkg/js/src/jspubtd.h,v' | diff -u /dev/null - -L'ossp-pkg/js/src/jspubtd.h' 2>/dev/null --- ossp-pkg/js/src/jspubtd.h +++ - 2024-05-12 07:43:25.706278132 +0200 @@ -0,0 +1,616 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** 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 ***** */ + +#ifndef jspubtd_h___ +#define jspubtd_h___ +/* + * JS public API typedefs. + */ +#include "jstypes.h" +#include "jscompat.h" + +JS_BEGIN_EXTERN_C + +/* Scalar typedefs. */ +typedef uint16 jschar; +typedef int32 jsint; +typedef uint32 jsuint; +typedef float64 jsdouble; +typedef jsword jsval; +typedef jsword jsid; +typedef int32 jsrefcount; /* PRInt32 if JS_THREADSAFE, see jslock.h */ + +/* + * Run-time version enumeration. See jsconfig.h for compile-time counterparts + * to these values that may be selected by the JS_VERSION macro, and tested by + * #if expressions. + */ +typedef enum JSVersion { + JSVERSION_1_0 = 100, + JSVERSION_1_1 = 110, + JSVERSION_1_2 = 120, + JSVERSION_1_3 = 130, + JSVERSION_1_4 = 140, + JSVERSION_ECMA_3 = 148, + JSVERSION_1_5 = 150, + JSVERSION_1_6 = 160, + JSVERSION_DEFAULT = 0, + JSVERSION_UNKNOWN = -1 +} JSVersion; + +#define JSVERSION_IS_ECMA(version) \ + ((version) == JSVERSION_DEFAULT || (version) >= JSVERSION_1_3) + +/* Result of typeof operator enumeration. */ +typedef enum JSType { + JSTYPE_VOID, /* undefined */ + JSTYPE_OBJECT, /* object */ + JSTYPE_FUNCTION, /* function */ + JSTYPE_STRING, /* string */ + JSTYPE_NUMBER, /* number */ + JSTYPE_BOOLEAN, /* boolean */ + JSTYPE_NULL, /* null */ + JSTYPE_XML, /* xml object */ + JSTYPE_LIMIT +} JSType; + +/* JSObjectOps.checkAccess mode enumeration. */ +typedef enum JSAccessMode { + JSACC_PROTO = 0, /* XXXbe redundant w.r.t. id */ + JSACC_PARENT = 1, /* XXXbe redundant w.r.t. id */ + JSACC_IMPORT = 2, /* import foo.bar */ + JSACC_WATCH = 3, /* a watchpoint on object foo for id 'bar' */ + JSACC_READ = 4, /* a "get" of foo.bar */ + JSACC_WRITE = 8, /* a "set" of foo.bar = baz */ + JSACC_LIMIT +} JSAccessMode; + +#define JSACC_TYPEMASK (JSACC_WRITE - 1) + +/* + * This enum type is used to control the behavior of a JSObject property + * iterator function that has type JSNewEnumerate. + */ +typedef enum JSIterateOp { + JSENUMERATE_INIT, /* Create new iterator state */ + JSENUMERATE_NEXT, /* Iterate once */ + JSENUMERATE_DESTROY /* Destroy iterator state */ +} JSIterateOp; + +/* Struct typedefs. */ +typedef struct JSClass JSClass; +typedef struct JSExtendedClass JSExtendedClass; +typedef struct JSConstDoubleSpec JSConstDoubleSpec; +typedef struct JSContext JSContext; +typedef struct JSErrorReport JSErrorReport; +typedef struct JSFunction JSFunction; +typedef struct JSFunctionSpec JSFunctionSpec; +typedef struct JSIdArray JSIdArray; +typedef struct JSProperty JSProperty; +typedef struct JSPropertySpec JSPropertySpec; +typedef struct JSObject JSObject; +typedef struct JSObjectMap JSObjectMap; +typedef struct JSObjectOps JSObjectOps; +typedef struct JSXMLObjectOps JSXMLObjectOps; +typedef struct JSRuntime JSRuntime; +typedef struct JSRuntime JSTaskState; /* XXX deprecated name */ +typedef struct JSScript JSScript; +typedef struct JSString JSString; +typedef struct JSXDRState JSXDRState; +typedef struct JSExceptionState JSExceptionState; +typedef struct JSLocaleCallbacks JSLocaleCallbacks; + +/* JSClass (and JSObjectOps where appropriate) function pointer typedefs. */ + +/* + * Add, delete, get or set a property named by id in obj. Note the jsval id + * type -- id may be a string (Unicode property identifier) or an int (element + * index). The *vp out parameter, on success, is the new property value after + * an add, get, or set. After a successful delete, *vp is JSVAL_FALSE iff + * obj[id] can't be deleted (because it's permanent). + */ +typedef JSBool +(* JS_DLL_CALLBACK JSPropertyOp)(JSContext *cx, JSObject *obj, jsval id, + jsval *vp); + +/* + * This function type is used for callbacks that enumerate the properties of + * a JSObject. The behavior depends on the value of enum_op: + * + * JSENUMERATE_INIT + * A new, opaque iterator state should be allocated and stored in *statep. + * (You can use PRIVATE_TO_JSVAL() to tag the pointer to be stored). + * + * The number of properties that will be enumerated should be returned as + * an integer jsval in *idp, if idp is non-null, and provided the number of + * enumerable properties is known. If idp is non-null and the number of + * enumerable properties can't be computed in advance, *idp should be set + * to JSVAL_ZERO. + * + * JSENUMERATE_NEXT + * A previously allocated opaque iterator state is passed in via statep. + * Return the next jsid in the iteration using *idp. The opaque iterator + * state pointed at by statep is destroyed and *statep is set to JSVAL_NULL + * if there are no properties left to enumerate. + * + * JSENUMERATE_DESTROY + * Destroy the opaque iterator state previously allocated in *statep by a + * call to this function when enum_op was JSENUMERATE_INIT. + * + * The return value is used to indicate success, with a value of JS_FALSE + * indicating failure. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSNewEnumerateOp)(JSContext *cx, JSObject *obj, + JSIterateOp enum_op, + jsval *statep, jsid *idp); + +/* + * The old-style JSClass.enumerate op should define all lazy properties not + * yet reflected in obj. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSEnumerateOp)(JSContext *cx, JSObject *obj); + +/* + * Resolve a lazy property named by id in obj by defining it directly in obj. + * Lazy properties are those reflected from some peer native property space + * (e.g., the DOM attributes for a given node reflected as obj) on demand. + * + * JS looks for a property in an object, and if not found, tries to resolve + * the given id. If resolve succeeds, the engine looks again in case resolve + * defined obj[id]. If no such property exists directly in obj, the process + * is repeated with obj's prototype, etc. + * + * NB: JSNewResolveOp provides a cheaper way to resolve lazy properties. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSResolveOp)(JSContext *cx, JSObject *obj, jsval id); + +/* + * Like JSResolveOp, but flags provide contextual information as follows: + * + * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id + * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment + * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence + * JSRESOLVE_DECLARING var, const, or function prolog declaration opcode + * JSRESOLVE_CLASSNAME class name used when constructing + * + * The *objp out parameter, on success, should be null to indicate that id + * was not resolved; and non-null, referring to obj or one of its prototypes, + * if id was resolved. + * + * This hook instead of JSResolveOp is called via the JSClass.resolve member + * if JSCLASS_NEW_RESOLVE is set in JSClass.flags. + * + * Setting JSCLASS_NEW_RESOLVE and JSCLASS_NEW_RESOLVE_GETS_START further + * extends this hook by passing in the starting object on the prototype chain + * via *objp. Thus a resolve hook implementation may define the property id + * being resolved in the object in which the id was first sought, rather than + * in a prototype object whose class led to the resolve hook being called. + * + * When using JSCLASS_NEW_RESOLVE_GETS_START, the resolve hook must therefore + * null *objp to signify "not resolved". With only JSCLASS_NEW_RESOLVE and no + * JSCLASS_NEW_RESOLVE_GETS_START, the hook can assume *objp is null on entry. + * This is not good practice, but enough existing hook implementations count + * on it that we can't break compatibility by passing the starting object in + * *objp without a new JSClass flag. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSNewResolveOp)(JSContext *cx, JSObject *obj, jsval id, + uintN flags, JSObject **objp); + +/* + * Convert obj to the given type, returning true with the resulting value in + * *vp on success, and returning false on error or exception. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSConvertOp)(JSContext *cx, JSObject *obj, JSType type, + jsval *vp); + +/* + * Finalize obj, which the garbage collector has determined to be unreachable + * from other live objects or from GC roots. Obviously, finalizers must never + * store a reference to obj. + */ +typedef void +(* JS_DLL_CALLBACK JSFinalizeOp)(JSContext *cx, JSObject *obj); + +/* + * Used by JS_AddExternalStringFinalizer and JS_RemoveExternalStringFinalizer + * to extend and reduce the set of string types finalized by the GC. + */ +typedef void +(* JS_DLL_CALLBACK JSStringFinalizeOp)(JSContext *cx, JSString *str); + +/* + * The signature for JSClass.getObjectOps, used by JS_NewObject's internals + * to discover the set of high-level object operations to use for new objects + * of the given class. All native objects have a JSClass, which is stored as + * a private (int-tagged) pointer in obj->slots[JSSLOT_CLASS]. In contrast, + * all native and host objects have a JSObjectMap at obj->map, which may be + * shared among a number of objects, and which contains the JSObjectOps *ops + * pointer used to dispatch object operations from API calls. + * + * Thus JSClass (which pre-dates JSObjectOps in the API) provides a low-level + * interface to class-specific code and data, while JSObjectOps allows for a + * higher level of operation, which does not use the object's class except to + * find the class's JSObjectOps struct, by calling clasp->getObjectOps, and to + * finalize the object. + * + * If this seems backwards, that's because it is! API compatibility requires + * a JSClass *clasp parameter to JS_NewObject, etc. Most host objects do not + * need to implement the larger JSObjectOps, and can share the common JSScope + * code and data used by the native (js_ObjectOps, see jsobj.c) ops. + * + * Further extension to preserve API compatibility: if this function returns + * a pointer to JSXMLObjectOps.base, not to JSObjectOps, then the engine calls + * extended hooks needed for E4X. + */ +typedef JSObjectOps * +(* JS_DLL_CALLBACK JSGetObjectOps)(JSContext *cx, JSClass *clasp); + +/* + * JSClass.checkAccess type: check whether obj[id] may be accessed per mode, + * returning false on error/exception, true on success with obj[id]'s last-got + * value in *vp, and its attributes in *attrsp. As for JSPropertyOp above, id + * is either a string or an int jsval. + * + * See JSCheckAccessIdOp, below, for the JSObjectOps counterpart, which takes + * a jsid (a tagged int or aligned, unique identifier pointer) rather than a + * jsval. The native js_ObjectOps.checkAccess simply forwards to the object's + * clasp->checkAccess, so that both JSClass and JSObjectOps implementors may + * specialize access checks. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSCheckAccessOp)(JSContext *cx, JSObject *obj, jsval id, + JSAccessMode mode, jsval *vp); + +/* + * Encode or decode an object, given an XDR state record representing external + * data. See jsxdrapi.h. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSXDRObjectOp)(JSXDRState *xdr, JSObject **objp); + +/* + * Check whether v is an instance of obj. Return false on error or exception, + * true on success with JS_TRUE in *bp if v is an instance of obj, JS_FALSE in + * *bp otherwise. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSHasInstanceOp)(JSContext *cx, JSObject *obj, jsval v, + JSBool *bp); + +/* + * Function type for JSClass.mark and JSObjectOps.mark, called from the GC to + * scan live GC-things reachable from obj's private data structure. For each + * such thing, a mark implementation must call + * + * JS_MarkGCThing(cx, thing, name, arg); + * + * The trailing name and arg parameters are used for GC_MARK_DEBUG-mode heap + * dumping and ref-path tracing. The mark function should pass a (typically + * literal) string naming the private data member for name, and it must pass + * the opaque arg parameter through from its caller. + * + * For the JSObjectOps.mark hook, the return value is the number of slots at + * obj->slots to scan. For JSClass.mark, the return value is ignored. + * + * NB: JSMarkOp implementations cannot allocate new GC-things (JS_NewObject + * called from a mark function will fail silently, e.g.). + */ +typedef uint32 +(* JS_DLL_CALLBACK JSMarkOp)(JSContext *cx, JSObject *obj, void *arg); + +/* + * The optional JSClass.reserveSlots hook allows a class to make computed + * per-instance object slots reservations, in addition to or instead of using + * JSCLASS_HAS_RESERVED_SLOTS(n) in the JSClass.flags initializer to reserve + * a constant-per-class number of slots. Implementations of this hook should + * return the number of slots to reserve, not including any reserved by using + * JSCLASS_HAS_RESERVED_SLOTS(n) in JSClass.flags. + * + * NB: called with obj locked by the JSObjectOps-specific mutual exclusion + * mechanism appropriate for obj, so don't nest other operations that might + * also lock obj. + */ +typedef uint32 +(* JS_DLL_CALLBACK JSReserveSlotsOp)(JSContext *cx, JSObject *obj); + +/* JSObjectOps function pointer typedefs. */ + +/* + * Create a new subclass of JSObjectMap (see jsobj.h), with the nrefs and ops + * members initialized from the same-named parameters, and with the nslots and + * freeslot members initialized according to ops and clasp. Return null on + * error, non-null on success. + * + * JSObjectMaps are reference-counted by generic code in the engine. Usually, + * the nrefs parameter to JSObjectOps.newObjectMap will be 1, to count the ref + * returned to the caller on success. After a successful construction, some + * number of js_HoldObjectMap and js_DropObjectMap calls ensue. When nrefs + * reaches 0 due to a js_DropObjectMap call, JSObjectOps.destroyObjectMap will + * be called to dispose of the map. + */ +typedef JSObjectMap * +(* JS_DLL_CALLBACK JSNewObjectMapOp)(JSContext *cx, jsrefcount nrefs, + JSObjectOps *ops, JSClass *clasp, + JSObject *obj); + +/* + * Generic type for an infallible JSObjectMap operation, used currently by + * JSObjectOps.destroyObjectMap. + */ +typedef void +(* JS_DLL_CALLBACK JSObjectMapOp)(JSContext *cx, JSObjectMap *map); + +/* + * Look for id in obj and its prototype chain, returning false on error or + * exception, true on success. On success, return null in *propp if id was + * not found. If id was found, return the first object searching from obj + * along its prototype chain in which id names a direct property in *objp, and + * return a non-null, opaque property pointer in *propp. + * + * If JSLookupPropOp succeeds and returns with *propp non-null, that pointer + * may be passed as the prop parameter to a JSAttributesOp, as a short-cut + * that bypasses id re-lookup. In any case, a non-null *propp result after a + * successful lookup must be dropped via JSObjectOps.dropProperty. + * + * NB: successful return with non-null *propp means the implementation may + * have locked *objp and added a reference count associated with *propp, so + * callers should not risk deadlock by nesting or interleaving other lookups + * or any obj-bearing ops before dropping *propp. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSLookupPropOp)(JSContext *cx, JSObject *obj, jsid id, + JSObject **objp, JSProperty **propp); + +/* + * Define obj[id], a direct property of obj named id, having the given initial + * value, with the specified getter, setter, and attributes. If the propp out + * param is non-null, *propp on successful return contains an opaque property + * pointer usable as a speedup hint with JSAttributesOp. But note that propp + * may be null, indicating that the caller is not interested in recovering an + * opaque pointer to the newly-defined property. + * + * If propp is non-null and JSDefinePropOp succeeds, its caller must be sure + * to drop *propp using JSObjectOps.dropProperty in short order, just as with + * JSLookupPropOp. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSDefinePropOp)(JSContext *cx, JSObject *obj, + jsid id, jsval value, + JSPropertyOp getter, JSPropertyOp setter, + uintN attrs, JSProperty **propp); + +/* + * Get, set, or delete obj[id], returning false on error or exception, true + * on success. If getting or setting, the new value is returned in *vp on + * success. If deleting without error, *vp will be JSVAL_FALSE if obj[id] is + * permanent, and JSVAL_TRUE if id named a direct property of obj that was in + * fact deleted, or if id names no direct property of obj (id could name a + * prototype property, or no property in obj or its prototype chain). + */ +typedef JSBool +(* JS_DLL_CALLBACK JSPropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, + jsval *vp); + +/* + * Get or set attributes of the property obj[id]. Return false on error or + * exception, true with current attributes in *attrsp. If prop is non-null, + * it must come from the *propp out parameter of a prior JSDefinePropOp or + * JSLookupPropOp call. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSAttributesOp)(JSContext *cx, JSObject *obj, jsid id, + JSProperty *prop, uintN *attrsp); + +/* + * JSObjectOps.checkAccess type: check whether obj[id] may be accessed per + * mode, returning false on error/exception, true on success with obj[id]'s + * last-got value in *vp, and its attributes in *attrsp. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSCheckAccessIdOp)(JSContext *cx, JSObject *obj, jsid id, + JSAccessMode mode, jsval *vp, + uintN *attrsp); + +/* + * A generic type for functions mapping an object to another object, or null + * if an error or exception was thrown on cx. Used by JSObjectOps.thisObject + * at present. + */ +typedef JSObject * +(* JS_DLL_CALLBACK JSObjectOp)(JSContext *cx, JSObject *obj); + +/* + * A generic type for functions taking a context, object, and property, with + * no return value. Used by JSObjectOps.dropProperty currently (see above, + * JSDefinePropOp and JSLookupPropOp, for the object-locking protocol in which + * dropProperty participates). + */ +typedef void +(* JS_DLL_CALLBACK JSPropertyRefOp)(JSContext *cx, JSObject *obj, + JSProperty *prop); + +/* + * Function type for JSObjectOps.setProto and JSObjectOps.setParent. These + * hooks must check for cycles without deadlocking, and otherwise take special + * steps. See jsobj.c, js_SetProtoOrParent, for an example. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSSetObjectSlotOp)(JSContext *cx, JSObject *obj, + uint32 slot, JSObject *pobj); + +/* + * Get and set a required slot, one that should already have been allocated. + * These operations are infallible, so required slots must be pre-allocated, + * or implementations must suppress out-of-memory errors. The native ops + * (js_ObjectOps, see jsobj.c) access slots reserved by including a call to + * the JSCLASS_HAS_RESERVED_SLOTS(n) macro in the JSClass.flags initializer. + * + * NB: the slot parameter is a zero-based index into obj->slots[], unlike the + * index parameter to the JS_GetReservedSlot and JS_SetReservedSlot API entry + * points, which is a zero-based index into the JSCLASS_RESERVED_SLOTS(clasp) + * reserved slots that come after the initial well-known slots: proto, parent, + * class, and optionally, the private data slot. + */ +typedef jsval +(* JS_DLL_CALLBACK JSGetRequiredSlotOp)(JSContext *cx, JSObject *obj, + uint32 slot); + +typedef JSBool +(* JS_DLL_CALLBACK JSSetRequiredSlotOp)(JSContext *cx, JSObject *obj, + uint32 slot, jsval v); + +typedef JSObject * +(* JS_DLL_CALLBACK JSGetMethodOp)(JSContext *cx, JSObject *obj, jsid id, + jsval *vp); + +typedef JSBool +(* JS_DLL_CALLBACK JSSetMethodOp)(JSContext *cx, JSObject *obj, jsid id, + jsval *vp); + +typedef JSBool +(* JS_DLL_CALLBACK JSEnumerateValuesOp)(JSContext *cx, JSObject *obj, + JSIterateOp enum_op, + jsval *statep, jsid *idp, jsval *vp); + +typedef JSBool +(* JS_DLL_CALLBACK JSEqualityOp)(JSContext *cx, JSObject *obj, jsval v, + JSBool *bp); + +typedef JSBool +(* JS_DLL_CALLBACK JSConcatenateOp)(JSContext *cx, JSObject *obj, jsval v, + jsval *vp); + +/* Typedef for native functions called by the JS VM. */ + +typedef JSBool +(* JS_DLL_CALLBACK JSNative)(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval); + +/* Callbacks and their arguments. */ + +typedef enum JSGCStatus { + JSGC_BEGIN, + JSGC_END, + JSGC_MARK_END, + JSGC_FINALIZE_END +} JSGCStatus; + +typedef JSBool +(* JS_DLL_CALLBACK JSGCCallback)(JSContext *cx, JSGCStatus status); + +typedef JSBool +(* JS_DLL_CALLBACK JSBranchCallback)(JSContext *cx, JSScript *script); + +typedef void +(* JS_DLL_CALLBACK JSErrorReporter)(JSContext *cx, const char *message, + JSErrorReport *report); + +typedef struct JSErrorFormatString { + const char *format; + uintN argCount; +} JSErrorFormatString; + +typedef const JSErrorFormatString * +(* JS_DLL_CALLBACK JSErrorCallback)(void *userRef, const char *locale, + const uintN errorNumber); + +#ifdef va_start +#define JS_ARGUMENT_FORMATTER_DEFINED 1 + +typedef JSBool +(* JS_DLL_CALLBACK JSArgumentFormatter)(JSContext *cx, const char *format, + JSBool fromJS, jsval **vpp, +#ifdef OSSP + va_list app); +#else + va_list *app); +#endif +#endif + +typedef JSBool +(* JS_DLL_CALLBACK JSLocaleToUpperCase)(JSContext *cx, JSString *src, + jsval *rval); + +typedef JSBool +(* JS_DLL_CALLBACK JSLocaleToLowerCase)(JSContext *cx, JSString *src, + jsval *rval); + +typedef JSBool +(* JS_DLL_CALLBACK JSLocaleCompare)(JSContext *cx, + JSString *src1, JSString *src2, + jsval *rval); + +typedef JSBool +(* JS_DLL_CALLBACK JSLocaleToUnicode)(JSContext *cx, char *src, jsval *rval); + +/* + * Security protocol types. + */ +typedef struct JSPrincipals JSPrincipals; + +/* + * XDR-encode or -decode a principals instance, based on whether xdr->mode is + * JSXDR_ENCODE, in which case *principalsp should be encoded; or JSXDR_DECODE, + * in which case implementations must return a held (via JSPRINCIPALS_HOLD), + * non-null *principalsp out parameter. Return true on success, false on any + * error, which the implementation must have reported. + */ +typedef JSBool +(* JS_DLL_CALLBACK JSPrincipalsTranscoder)(JSXDRState *xdr, + JSPrincipals **principalsp); + +/* + * Return a weak reference to the principals associated with obj, possibly via + * the immutable parent chain leading from obj to a top-level container (e.g., + * a window object in the DOM level 0). If there are no principals associated + * with obj, return null. Therefore null does not mean an error was reported; + * in no event should an error be reported or an exception be thrown by this + * callback's implementation. + */ +typedef JSPrincipals * +(* JS_DLL_CALLBACK JSObjectPrincipalsFinder)(JSContext *cx, JSObject *obj); + +JS_END_EXTERN_C + +#endif /* jspubtd_h___ */