The original source code to the JavaScript Interpreter was provided by Netscape Communications Corporation

and the modifications to the source code are derived, directly or indirectly, from such code.

The changes to the original code from Netscape Communications Corporation are documented

in the accompanying Readme file.

 

--------------------------------------------------------------

**** Start of js.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS shell.

 */

#include "jsstddef.h"

#include <errno.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsarena.h" /* Added by JSIFY */

/* Removed by JSIFY: #include "prlog.h" */

#include "jsutil.h" /* Added by JSIFY */

#include "jsprf.h"

#include "jsapi.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsdbgapi.h"

#include "jsemit.h"

#include "jsfun.h"

#include "jsgc.h"

#include "jslock.h"

#include "jsobj.h"

#include "jsparse.h"

#include "jsscope.h"

#include "jsscript.h"



#ifdef PERLCONNECT

#include "perlconnect/jsperl.h"

#endif



#ifdef LIVECONNECT

#include "jsjava.h"

#endif



#ifdef JSDEBUGGER

#include "jsdebug.h"

#ifdef JSDEBUGGER_JAVA_UI

#include "jsdjava.h"

#endif /* JSDEBUGGER_JAVA_UI */

#ifdef JSDEBUGGER_C_UI

#include "jsdb.h"

#endif /* JSDEBUGGER_C_UI */

#endif /* JSDEBUGGER */



#ifdef XP_UNIX

#include <errno.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/wait.h>

#endif



#ifdef XP_PC

#include <io.h>     /* for isatty() */

#endif



#define EXITCODE_RUNTIME_ERROR 3

#define EXITCODE_FILE_NOT_FOUND 4





size_t gStackChunkSize = 8192;

int gExitCode = 0;

FILE *gErrFile = NULL;

FILE *gOutFile = NULL;



#ifdef XP_MAC

#ifdef MAC_TEST_HACK

/* this is the data file that all Print strings will be echoed into */

FILE *gTestResultFile = NULL;

#define isatty(f) 0

#else

#define isatty(f) 1

#endif



char *strdup(const char *str)

{

    char *copy = (char *) malloc(strlen(str)+1);

    if (copy)

        strcpy(copy, str);

    return copy;

}



#ifdef XP_MAC_MPW

/* Macintosh MPW replacements for the ANSI routines.  These translate LF's to CR's because

   the MPW libraries supplied by Metrowerks don't do that for some reason.  */

static void translateLFtoCR(char *str, int length)

{

    char *limit = str + length;

    while (str != limit) {

        if (*str == '\n')

            *str = '\r';

        str++;

    }

}



int fputc(int c, FILE *file)

{

    char buffer = c;

    if (buffer == '\n')

        buffer = '\r';

    return fwrite(&buffer, 1, 1, file);

}



int fputs(const char *s, FILE *file)

{

    char buffer[4096];

    int n = strlen(s);

    int extra = 0;



    while (n > sizeof buffer) {

        memcpy(buffer, s, sizeof buffer);

        translateLFtoCR(buffer, sizeof buffer);

        extra += fwrite(buffer, 1, sizeof buffer, file);

        n -= sizeof buffer;

        s += sizeof buffer;

    }

    memcpy(buffer, s, n);

    translateLFtoCR(buffer, n);

    return extra + fwrite(buffer, 1, n, file);

}



int fprintf(FILE* file, const char *format, ...)

{

    va_list args;

    char smallBuffer[4096];

    int n;

    int bufferSize = sizeof smallBuffer;

    char *buffer = smallBuffer;

    int result;



    va_start(args, format);

    n = vsnprintf(buffer, bufferSize, format, args);

    va_end(args);

    while (n < 0) {

        if (buffer != smallBuffer)

            free(buffer);

        bufferSize <<= 1;

        buffer = malloc(bufferSize);

        if (!buffer) {

            JS_ASSERT(JS_FALSE);

            return 0;

        }

        va_start(args, format);

        n = vsnprintf(buffer, bufferSize, format, args);

        va_end(args);

    }

    translateLFtoCR(buffer, n);

    result = fwrite(buffer, 1, n, file);

    if (buffer != smallBuffer)

        free(buffer);

    return result;

}





#else

#include <SIOUX.h>

#include <MacTypes.h>



static char* mac_argv[] = { "js", NULL };



static void initConsole(StringPtr consoleName, const char* startupMessage, int *argc, char** *argv)

{

	SIOUXSettings.autocloseonquit = true;

	SIOUXSettings.asktosaveonclose = false;

	/* SIOUXSettings.initializeTB = false;

	 SIOUXSettings.showstatusline = true;*/

	puts(startupMessage);

	SIOUXSetTitle(consoleName);



	/* set up a buffer for stderr (otherwise it's a pig). */

	setvbuf(stderr, (char *) malloc(BUFSIZ), _IOLBF, BUFSIZ);



	*argc = 1;

	*argv = mac_argv;

}



#ifdef LIVECONNECT

/* Little hack to provide a default CLASSPATH on the Mac. */

#define getenv(var) mac_getenv(var)

static char* mac_getenv(const char* var)

{

	if (strcmp(var, "CLASSPATH") == 0) {

		static char class_path[] = "liveconnect.jar";

		return class_path;

	}

	return NULL;

}

#endif /* LIVECONNECT */



#endif

#endif



#ifdef JSDEBUGGER

static JSDContext *_jsdc;

#ifdef JSDEBUGGER_JAVA_UI

static JSDJContext *_jsdjc;

#endif /* JSDEBUGGER_JAVA_UI */

#endif /* JSDEBUGGER */



static JSBool reportWarnings = JS_TRUE;



typedef enum JSShellErrNum {

#define MSG_DEF(name, number, count, exception, format) \

    name = number,

#include "jsshell.msg"

#undef MSG_DEF

    JSShellErr_Limit

#undef MSGDEF

} JSShellErrNum;



static const JSErrorFormatString *

my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);



#ifdef EDITLINE

extern char     *readline(const char *prompt);

extern void     add_history(char *line);

#endif



static JSBool

GetLine(JSContext *cx, char *bufp, FILE *fh, const char *prompt) {

#ifdef EDITLINE

    /*

     * Use readline only if fh is stdin, because there's no way to specify

     * another handle.  Are other filehandles interactive?

     */

    if (fh == stdin) {

        char *linep;

        if ((linep = readline(prompt)) == NULL)

            return JS_FALSE;

        if (strlen(linep) > 0)

            add_history(linep);

        strcpy(bufp, linep);

        JS_free(cx, linep);

        bufp += strlen(bufp);

        *bufp++ = '\n';

        *bufp = '\0';

    } else

#endif

    {

        char line[256];

        fprintf(gOutFile, prompt);

#ifdef XP_MAC_MPW

        /* Print a CR after the prompt because MPW grabs the entire line when entering an interactive command */

        fputc('\n', gOutFile);

#endif

        if (fgets(line, 256, fh) == NULL)

            return JS_FALSE;

        strcpy(bufp, line);

    }

    return JS_TRUE;

}



static void

Process(JSContext *cx, JSObject *obj, char *filename)

{

    JSBool ok, hitEOF;

    JSScript *script;

    jsval result;

    JSString *str;

    char buffer[4096];

    char *bufp;

    int lineno;

    int startline;

    FILE *fh;



    if (filename != NULL && strcmp(filename, "-") != 0) {

	fh = fopen(filename, "r");

	if (!fh) {

	    JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,

			    JSSMSG_CANT_OPEN,

			    filename, strerror(errno));

            gExitCode = EXITCODE_FILE_NOT_FOUND;

	    return;

	}

    } else {

        fh = stdin;

    }



    if (!isatty(fileno(fh))) {

	/*

         * It's not interactive - just execute it.

         *

         * Support the UNIX #! shell hack; gobble the first line if it starts

	 * with '#'.  TODO - this isn't quite compatible with sharp variables,

	 * as a legal js program (using sharp variables) might start with '#'.

	 * But that would require multi-character lookahead.

	 */

	int ch = fgetc(fh);

	if (ch == '#') {

	    while((ch = fgetc(fh)) != EOF) {

		if(ch == '\n' || ch == '\r')

		    break;

	    }

	}

	ungetc(ch, fh);

        script = JS_CompileFileHandle(cx, obj, filename, fh);

        if (script) {

            (void)JS_ExecuteScript(cx, obj, script, &result);

            JS_DestroyScript(cx, script);

        }

        return;

    }



    /* It's an interactive filehandle; drop into read-eval-print loop. */

    lineno = 1;

    hitEOF = JS_FALSE;

    do {

        bufp = buffer;

        *bufp = '\0';



        /*

         * Accumulate lines until we get a 'compilable unit' - one that either

         * generates an error (before running out of source) or that compiles

         * cleanly.  This should be whenever we get a complete statement that

         * coincides with the end of a line.

         */

        startline = lineno;

        do {

            if (!GetLine(cx, bufp, fh, startline == lineno ? "js> " : "")) {

                hitEOF = JS_TRUE;

                break;

            }

            bufp += strlen(bufp);

            lineno++;

        } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));



        /* Clear any pending exception from previous failed compiles.  */

        JS_ClearPendingException(cx);

        script = JS_CompileScript(cx, obj, buffer, strlen(buffer),

#ifdef JSDEBUGGER

                                  "typein",

#else

                                  NULL,

#endif

                                  startline);

        if (script) {

            JSErrorReporter older;



            ok = JS_ExecuteScript(cx, obj, script, &result);

            if (ok && result != JSVAL_VOID) {

                /* Suppress error reports from JS_ValueToString(). */

                older = JS_SetErrorReporter(cx, NULL);

                str = JS_ValueToString(cx, result);

                JS_SetErrorReporter(cx, older);



                if (str)

                    fprintf(gOutFile, "%s\n", JS_GetStringBytes(str));

                else

                    ok = JS_FALSE;

            }

#if 0

#if JS_HAS_ERROR_EXCEPTIONS

            /*

             * Require that any time we return failure, an exception has

             * been set.

             */

            JS_ASSERT(ok || JS_IsExceptionPending(cx));



            /*

             * Also that any time an exception has been set, we've

             * returned failure.

             */

            JS_ASSERT(!JS_IsExceptionPending(cx) || !ok);

#endif /* JS_HAS_ERROR_EXCEPTIONS */

#endif

            JS_DestroyScript(cx, script);

        }

    } while (!hitEOF);

    fprintf(gOutFile, "\n");

    return;

}



static int

usage(void)

{

    fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());

    fprintf(gErrFile, "usage: js [-s] [-w] [-W] [-b branchlimit] [-c stackchunksize] [-v version] [-f scriptfile] [scriptfile] [scriptarg...]\n");

    return 2;

}



static uint32 gBranchCount;

static uint32 gBranchLimit;



static JSBool

my_BranchCallback(JSContext *cx, JSScript *script)

{

    if (++gBranchCount == gBranchLimit) {

        if (script->filename)

            fprintf(gErrFile, "%s:", script->filename);

        fprintf(gErrFile, "%u: script branches too much (%u callbacks)\n",

                script->lineno, gBranchLimit);

        gBranchCount = 0;

        return JS_FALSE;

    }

    return JS_TRUE;

}



static int

ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)

{

    int i, j;

    char *filename = NULL;

    jsint length;

    jsval *vector;

    JSObject *argsObj;

    JSBool isInteractive = JS_TRUE;



    for (i=0; i < argc; i++) {

	if (argv[i][0] == '-') {

	    switch (argv[i][1]) {

	    case 'v':

		if (i+1 == argc) {

		    return usage();

		}

		JS_SetVersion(cx, (JSVersion) atoi(argv[i+1]));

		i++;

		break;



            case 'w':

                reportWarnings = JS_TRUE;

                break;



	    case 'W':

		reportWarnings = JS_FALSE;

		break;



	    case 's':

		JS_ToggleOptions(cx, JSOPTION_STRICT);

		break;



            case 'b':

                gBranchLimit = atoi(argv[++i]);

                JS_SetBranchCallback(cx, my_BranchCallback);

		break;



            case 'c':

                /* set stack chunk size */

                gStackChunkSize = atoi(argv[++i]);

                break;



	    case 'f':

		if (i+1 == argc) {

		    return usage();

		}

		filename = argv[i+1];

		/* "-f -" means read from stdin */

		if (filename[0] == '-' && filename[1] == '\0')

		    filename = NULL;

		Process(cx, obj, filename);

                filename = NULL;

                /* XXX: js -f foo.js should interpret foo.js and then

                 * drop into interactive mode, but that breaks test

                 * harness. Just execute foo.js for now.

                 */

                isInteractive = JS_FALSE;

		i++;

		break;



	    default:

		return usage();

	    }

	} else {

	    filename = argv[i++];

            isInteractive = JS_FALSE;

	    break;

	}

    }



    length = argc - i;

    if (length == 0) {

        vector = NULL;

    } else {

        vector = (jsval *) JS_malloc(cx, length * sizeof(jsval));

        if (vector == NULL)

            return 1;



        for (j = 0; j < length; j++) {

            JSString *str = JS_NewStringCopyZ(cx, argv[i++]);

            if (str == NULL)

                return 1;

            vector[j] = STRING_TO_JSVAL(str);

        }

    }



    argsObj = JS_NewArrayObject(cx, length, vector);

    if (vector)

        JS_free(cx, vector);

    if (argsObj == NULL)

	return 1;



    if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj),

                           NULL, NULL, 0)) {

	return 1;

    }



    if (filename || isInteractive)

        Process(cx, obj, filename);

    return gExitCode;

}





static JSBool

Version(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    if (argc > 0 && JSVAL_IS_INT(argv[0]))

	*rval = INT_TO_JSVAL(JS_SetVersion(cx, (JSVersion) JSVAL_TO_INT(argv[0])));

    else

	*rval = INT_TO_JSVAL(JS_GetVersion(cx));

    return JS_TRUE;

}



static struct {

    const char  *name;

    uint32      flag;

} js_options[] = {

    {"strict",          JSOPTION_STRICT},

    {"werror",          JSOPTION_WERROR},

    {0,                 0}

};



static JSBool

Options(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    uint32 optset, flag;

    uintN i, j, found;

    JSString *str;

    const char *opt;

    char *names;



    optset = 0;

    for (i = 0; i < argc; i++) {

        str = JS_ValueToString(cx, argv[i]);

        if (!str)

            return JS_FALSE;

        opt = JS_GetStringBytes(str);

        for (j = 0; js_options[j].name; j++) {

            if (strcmp(js_options[j].name, opt) == 0) {

                optset |= js_options[j].flag;

                break;

            }

        }

    }

    optset = JS_ToggleOptions(cx, optset);



    names = NULL;

    found = 0;

    while (optset != 0) {

        flag = optset;

        optset &= optset - 1;

        flag &= ~optset;

        for (j = 0; js_options[j].name; j++) {

            if (js_options[j].flag == flag) {

                names = JS_sprintf_append(names, "%s%s",

                                          names ? "," : "", js_options[j].name);

                found++;

                break;

            }

        }

    }

    if (!found)

        names = strdup("");

    if (!names) {

        JS_ReportOutOfMemory(cx);

        return JS_FALSE;

    }



    str = JS_NewString(cx, names, strlen(names));

    if (!str) {

        free(names);

        return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static void

my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);



static void

my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);



static JSBool

Load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    uintN i;

    JSString *str;

    const char *filename;

    JSScript *script;

    JSBool ok;

    jsval result;

    JSErrorReporter older;



    for (i = 0; i < argc; i++) {

	str = JS_ValueToString(cx, argv[i]);

	if (!str)

	    return JS_FALSE;

	argv[i] = STRING_TO_JSVAL(str);

	filename = JS_GetStringBytes(str);

	errno = 0;

        older = JS_SetErrorReporter(cx, my_LoadErrorReporter);

	script = JS_CompileFile(cx, obj, filename);

	if (!script)

            ok = JS_FALSE;

        else {

            ok = JS_ExecuteScript(cx, obj, script, &result);

	    JS_DestroyScript(cx, script);

        }

        JS_SetErrorReporter(cx, older);

	if (!ok)

	    return JS_FALSE;

    }



    return JS_TRUE;

}



static JSBool

Print(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    uintN i, n;

    JSString *str;



    for (i = n = 0; i < argc; i++) {

	str = JS_ValueToString(cx, argv[i]);

	if (!str)

	    return JS_FALSE;

	fprintf(gOutFile, "%s%s", i ? " " : "", JS_GetStringBytes(str));

    }

    n++;

    if (n)

        fputc('\n', gOutFile);

    return JS_TRUE;

}



static JSBool

Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);



static JSBool

Quit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    int r = 0;



#ifdef LIVECONNECT

    JSJ_SimpleShutdown();

#endif



    JS_ConvertArguments(cx, argc, argv,"/ i", &r);



    exit(r);

    return JS_FALSE;

}



#ifdef GC_MARK_DEBUG

extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap;

#endif



static JSBool

GC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSRuntime *rt;

    uint32 preBytes;



    rt = cx->runtime;

    preBytes = rt->gcBytes;

#ifdef GC_MARK_DEBUG

    if (argc && JSVAL_IS_STRING(argv[0])) {

	char *name = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));

	FILE *file = fopen(name, "w");

	if (!file) {

	    fprintf(gErrFile, "gc: can't open %s: %s\n", strerror(errno));

	    return JS_FALSE;

	}

	js_DumpGCHeap = file;

    } else {

	js_DumpGCHeap = stdout;

    }

#endif

    js_ForceGC(cx);

#ifdef GC_MARK_DEBUG

    if (js_DumpGCHeap != stdout)

	fclose(js_DumpGCHeap);

    js_DumpGCHeap = NULL;

#endif

    fprintf(gOutFile, "before %lu, after %lu, break %08lx\n",

	    (unsigned long)preBytes, (unsigned long)rt->gcBytes,

#ifdef XP_UNIX

	    (unsigned long)sbrk(0)

#else

	    0

#endif

	    );

#ifdef JS_GCMETER

    js_DumpGCStats(rt, stdout);

#endif

    return JS_TRUE;

}



static JSScript *

ValueToScript(JSContext *cx, jsval v)

{

    JSScript *script;

    JSFunction *fun;



    if (JSVAL_IS_OBJECT(v) &&

	JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) {

	script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));

    } else {

	fun = JS_ValueToFunction(cx, v);

	if (!fun)

	    return NULL;

	script = fun->script;

    }

    return script;

}



static JSBool

GetTrapArgs(JSContext *cx, uintN argc, jsval *argv, JSScript **scriptp,

	    int32 *ip)

{

    uintN intarg;

    JSScript *script;



    *scriptp = cx->fp->down->script;

    *ip = 0;

    if (argc != 0) {

	intarg = 0;

	if (JS_TypeOfValue(cx, argv[0]) == JSTYPE_FUNCTION) {

	    script = ValueToScript(cx, argv[0]);

	    if (!script)

		return JS_FALSE;

	    *scriptp = script;

	    intarg++;

	}

	if (argc > intarg) {

	    if (!JS_ValueToInt32(cx, argv[intarg], ip))

		return JS_FALSE;

	}

    }

    return JS_TRUE;

}



static JSTrapStatus

TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,

	    void *closure)

{

    JSString *str;

    JSStackFrame *caller;



    str = (JSString *) closure;

    caller = cx->fp->down;

    if (!JS_EvaluateScript(cx, caller->scopeChain,

			   JS_GetStringBytes(str), JS_GetStringLength(str),

			   caller->script->filename, caller->script->lineno,

			   rval)) {

	return JSTRAP_ERROR;

    }

    if (*rval != JSVAL_VOID)

	return JSTRAP_RETURN;

    return JSTRAP_CONTINUE;

}



static JSBool

Trap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str;

    JSScript *script;

    int32 i;



    if (argc == 0) {

	JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TRAP_USAGE);

	return JS_FALSE;

    }

    argc--;

    str = JS_ValueToString(cx, argv[argc]);

    if (!str)

	return JS_FALSE;

    argv[argc] = STRING_TO_JSVAL(str);

    if (!GetTrapArgs(cx, argc, argv, &script, &i))

	return JS_FALSE;

    return JS_SetTrap(cx, script, script->code + i, TrapHandler, str);

}



static JSBool

Untrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSScript *script;

    int32 i;



    if (!GetTrapArgs(cx, argc, argv, &script, &i))

	return JS_FALSE;

    JS_ClearTrap(cx, script, script->code + i, NULL, NULL);

    return JS_TRUE;

}



static JSBool

LineToPC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSScript *script;

    int32 i;

    uintN lineno;

    jsbytecode *pc;



    if (argc == 0) {

	JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_LINE2PC_USAGE);

	return JS_FALSE;

    }

    script = cx->fp->down->script;

    if (!GetTrapArgs(cx, argc, argv, &script, &i))

	return JS_FALSE;

    lineno = (i == 0) ? script->lineno : (uintN)i;

    pc = JS_LineNumberToPC(cx, script, lineno);

    if (!pc)

	return JS_FALSE;

    *rval = INT_TO_JSVAL(PTRDIFF(pc, script->code, jsbytecode));

    return JS_TRUE;

}



static JSBool

PCToLine(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSScript *script;

    int32 i;

    uintN lineno;



    if (!GetTrapArgs(cx, argc, argv, &script, &i))

	return JS_FALSE;

    lineno = JS_PCToLineNumber(cx, script, script->code + i);

    if (!lineno)

	return JS_FALSE;

    *rval = INT_TO_JSVAL(lineno);

    return JS_TRUE;

}



#ifdef DEBUG



static void

SrcNotes(JSContext *cx, JSScript *script)

{

    uintN offset, delta, caseOff;

    jssrcnote *notes, *sn;

    JSSrcNoteType type;

    jsatomid atomIndex;

    JSAtom *atom;



    notes = script->notes;

    if (notes) {

	fprintf(gOutFile, "\nSource notes:\n");

	offset = 0;

	for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {

	    delta = SN_DELTA(sn);

	    offset += delta;

	    fprintf(gOutFile, "%3u: %5u [%4u] %-8s",

                    PTRDIFF(sn, notes, jssrcnote), offset, delta,

                    js_SrcNoteName[SN_TYPE(sn)]);

	    type = (JSSrcNoteType) SN_TYPE(sn);

	    switch (type) {

	      case SRC_SETLINE:

		fprintf(gOutFile, " lineno %u", (uintN) js_GetSrcNoteOffset(sn, 0));

		break;

	      case SRC_FOR:

		fprintf(gOutFile, " cond %u update %u tail %u",

		       (uintN) js_GetSrcNoteOffset(sn, 0),

		       (uintN) js_GetSrcNoteOffset(sn, 1),

		       (uintN) js_GetSrcNoteOffset(sn, 2));

		break;

	      case SRC_PCBASE:

	      case SRC_PCDELTA:

		fprintf(gOutFile, " offset %u", (uintN) js_GetSrcNoteOffset(sn, 0));

		break;

	      case SRC_LABEL:

	      case SRC_LABELBRACE:

	      case SRC_BREAK2LABEL:

	      case SRC_CONT2LABEL:

	      case SRC_FUNCDEF: {

		const char *bytes;

		JSFunction *fun;

		JSString *str;



		atomIndex = (jsatomid) js_GetSrcNoteOffset(sn, 0);

		atom = js_GetAtom(cx, &script->atomMap, atomIndex);

		if (type != SRC_FUNCDEF) {

		    bytes = ATOM_BYTES(atom);

		} else {

		    fun = (JSFunction *)

                        JS_GetPrivate(cx, ATOM_TO_OBJECT(atom));

		    str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);

		    bytes = str ? JS_GetStringBytes(str) : "N/A";

		}

		fprintf(gOutFile, " atom %u (%s)", (uintN)atomIndex, bytes);

		break;

	      }

	      case SRC_SWITCH:

		fprintf(gOutFile, " length %u", (uintN) js_GetSrcNoteOffset(sn, 0));

		caseOff = (uintN) js_GetSrcNoteOffset(sn, 1);

		if (caseOff)

		    fprintf(gOutFile, " first case offset %u", caseOff);

		break;

	      case SRC_CATCH:

		delta = (uintN) js_GetSrcNoteOffset(sn, 0);

		if (delta)

		    fprintf(gOutFile, " guard size %u", delta);

		break;

	      default:;

	    }

	    fputc('\n', gOutFile);

	}

    }

}



static JSBool

Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    uintN i;

    JSScript *script;



    for (i = 0; i < argc; i++) {

	script = ValueToScript(cx, argv[i]);

	if (!script)

	    continue;



	SrcNotes(cx, script);

    }

    return JS_TRUE;

}



static JSBool

TryNotes(JSContext *cx, JSScript *script)

{

    JSTryNote *tn = script->trynotes;



    if (!tn)

	return JS_TRUE;

    fprintf(gOutFile, "\nException table:\nstart\tend\tcatch\n");

    while (tn->start && tn->catchStart) {

	fprintf(gOutFile, "  %d\t%d\t%d\n",

	       tn->start, tn->start + tn->length, tn->catchStart);

	tn++;

    }

    return JS_TRUE;

}



static JSBool

Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSBool lines;

    uintN i;

    JSScript *script;



    if (argc > 0 &&

	JSVAL_IS_STRING(argv[0]) &&

	!strcmp(JS_GetStringBytes(JSVAL_TO_STRING(argv[0])), "-l")) {

	lines = JS_TRUE;

	argv++, argc--;

    } else {

	lines = JS_FALSE;

    }

    for (i = 0; i < argc; i++) {

	script = ValueToScript(cx, argv[i]);

	if (!script)

	    continue;



        if (JSVAL_IS_FUNCTION(cx, argv[i])) {

            JSFunction *fun = JS_ValueToFunction(cx, argv[i]);

            if (fun && (fun->flags & JSFUN_FLAGS_MASK)) {

                uint8 flags = fun->flags;

                fputs("flags:", stderr);



#define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);



                SHOW_FLAG(SETTER);

                SHOW_FLAG(GETTER);

                SHOW_FLAG(BOUND_METHOD);

                SHOW_FLAG(HEAVYWEIGHT);

                

#undef SHOW_FLAG

                putchar('\n');

            }

        }



	js_Disassemble(cx, script, lines, stdout);

	SrcNotes(cx, script);

	TryNotes(cx, script);

    }

    return JS_TRUE;

}



static JSBool

DisassWithSrc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

              jsval *rval)

{

#define LINE_BUF_LEN 512

    uintN i, len, line1, line2, bupline;

    JSScript *script;

    FILE *file;

    char linebuf[LINE_BUF_LEN];

    jsbytecode *pc, *end;

    static char sep[] = ";-------------------------";



    for (i = 0; i < argc; i++) {

	script = ValueToScript(cx, argv[i]);

	if (!script)

	    continue;



	if (!script || !script->filename) {

	    JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,

					    JSSMSG_FILE_SCRIPTS_ONLY);

	    return JS_FALSE;

	}



	file = fopen(script->filename, "r");

	if (!file) {

	    JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,

			    JSSMSG_CANT_OPEN,

			    script->filename, strerror(errno));

	    return JS_FALSE;

	}



	pc = script->code;

	end = pc + script->length;



	/* burn the leading lines */

	line2 = JS_PCToLineNumber(cx, script, pc);

	for (line1 = 0; line1 < line2 - 1; line1++)

	    fgets(linebuf, LINE_BUF_LEN, file);



	bupline = 0;

	while (pc < end) {

	    line2 = JS_PCToLineNumber(cx, script, pc);



	    if (line2 < line1) {

		if (bupline != line2) {

		    bupline = line2;

		    fprintf(gOutFile, "%s %3u: BACKUP\n", sep, line2);

		}

	    } else {

		if (bupline && line1 == line2)

		    fprintf(gOutFile, "%s %3u: RESTORE\n", sep, line2);

		bupline = 0;

		while (line1 < line2) {

		    if (!fgets(linebuf, LINE_BUF_LEN, file)) {

			JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,

				       JSSMSG_UNEXPECTED_EOF,

				       script->filename);

			goto bail;

		    }

		    line1++;

		    fprintf(gOutFile, "%s %3u: %s", sep, line1, linebuf);

		}

	    }



	    len = js_Disassemble1(cx, script, pc,

                                  PTRDIFF(pc, script->code, jsbytecode),

                                  JS_TRUE, stdout);

	    if (!len)

		return JS_FALSE;

	    pc += len;

	}



      bail:

	fclose(file);

    }

    return JS_TRUE;

#undef LINE_BUF_LEN

}



static JSBool

Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSBool bval;

    JSString *str;



    if (argc == 0) {

	*rval = BOOLEAN_TO_JSVAL(cx->tracefp != 0);

	return JS_TRUE;

    }



    switch (JS_TypeOfValue(cx, argv[0])) {

      case JSTYPE_NUMBER:

	bval = JSVAL_IS_INT(argv[0])

	       ? JSVAL_TO_INT(argv[0])

	       : (jsint) *JSVAL_TO_DOUBLE(argv[0]);

	break;

      case JSTYPE_BOOLEAN:

	bval = JSVAL_TO_BOOLEAN(argv[0]);

	break;

      default:

	str = JS_ValueToString(cx, argv[0]);

	if (!str)

	    return JS_FALSE;

	fprintf(gErrFile, "tracing: illegal argument %s\n",

		JS_GetStringBytes(str));

	return JS_TRUE;

    }

    cx->tracefp = bval ? stdout : NULL;

    return JS_TRUE;

}



static int

DumpAtom(JSHashEntry *he, int i, void *arg)

{

    FILE *fp = (FILE *) arg;

    JSAtom *atom = (JSAtom *)he;



    fprintf(fp, "%3d %08x %5lu ",

	    i, (uintN)he->keyHash, (unsigned long)atom->number);

    if (ATOM_IS_STRING(atom))

	fprintf(fp, "\"%s\"\n", ATOM_BYTES(atom));

    else if (ATOM_IS_INT(atom))

	fprintf(fp, "%ld\n", (long)ATOM_TO_INT(atom));

    else

	fprintf(fp, "%.16g\n", *ATOM_TO_DOUBLE(atom));

    return HT_ENUMERATE_NEXT;

}



static int

DumpSymbol(JSHashEntry *he, int i, void *arg)

{

    FILE *fp = (FILE *) arg;

    JSSymbol *sym = (JSSymbol *)he;



    fprintf(fp, "%3d %08x", i, (uintN)he->keyHash);

    if (JSVAL_IS_INT(sym_id(sym)))

	fprintf(fp, " [%ld]\n", (long)JSVAL_TO_INT(sym_id(sym)));

    else

	fprintf(fp, " \"%s\"\n", ATOM_BYTES(sym_atom(sym)));

    return HT_ENUMERATE_NEXT;

}



extern JS_FRIEND_DATA(JSScopeOps) js_list_scope_ops;



static void

DumpScope(JSContext *cx, JSObject *obj, JSHashEnumerator dump, FILE *fp)

{

    JSScope *scope;

    JSSymbol *sym;

    int i;



    fprintf(fp, "\n%s scope contents:\n", JS_GET_CLASS(cx, obj)->name);

    scope = OBJ_SCOPE(obj);

    if (!MAP_IS_NATIVE(&scope->map))

	return;

    if (scope->ops == &js_list_scope_ops) {

	for (sym = (JSSymbol *)scope->data, i = 0; sym;

	     sym = (JSSymbol *)sym->entry.next, i++) {

	    DumpSymbol(&sym->entry, i, fp);

	}

    } else {

	JS_HashTableDump((JSHashTable *) scope->data, dump, fp);

    }

}



/* These are callable from gdb. */

static void Dsym(JSSymbol *sym) { if (sym) DumpSymbol(&sym->entry, 0, gErrFile); }

static void Datom(JSAtom *atom) { if (atom) DumpAtom(&atom->entry, 0, gErrFile); }



static JSBool

DumpStats(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    uintN i;

    JSString *str;

    const char *bytes;

    JSAtom *atom;

    JSObject *obj2;

    JSProperty *prop;

    jsval value;



    for (i = 0; i < argc; i++) {

	str = JS_ValueToString(cx, argv[i]);

	if (!str)

	    return JS_FALSE;

	bytes = JS_GetStringBytes(str);

	if (strcmp(bytes, "arena") == 0) {

#ifdef JS_ARENAMETER

	    JS_DumpArenaStats(stdout);

#endif

	} else if (strcmp(bytes, "atom") == 0) {

	    fprintf(gOutFile, "\natom table contents:\n");

	    JS_HashTableDump(cx->runtime->atomState.table, DumpAtom, stdout);

	} else if (strcmp(bytes, "global") == 0) {

	    DumpScope(cx, cx->globalObject, DumpSymbol, stdout);

	} else {

	    atom = js_Atomize(cx, bytes, JS_GetStringLength(str), 0);

	    if (!atom)

		return JS_FALSE;

	    if (!js_FindProperty(cx, (jsid)atom, &obj, &obj2, &prop))

		return JS_FALSE;

	    if (prop) {

		OBJ_DROP_PROPERTY(cx, obj2, prop);

		if (!OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &value))

		    return JS_FALSE;

	    }

	    if (!prop || !JSVAL_IS_OBJECT(value)) {

		fprintf(gErrFile, "js: invalid stats argument %s\n",

			bytes);

		continue;

	    }

	    obj = JSVAL_TO_OBJECT(value);

	    if (obj)

		DumpScope(cx, obj, DumpSymbol, stdout);

	}

    }

    return JS_TRUE;

}



#endif /* DEBUG */



#ifdef TEST_EXPORT

static JSBool

DoExport(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSAtom *atom;

    JSObject *obj2;

    JSProperty *prop;

    JSBool ok;

    uintN attrs;



    if (argc != 2) {

	JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_DOEXP_USAGE);

	return JS_FALSE;

    }

    if (!JS_ValueToObject(cx, argv[0], &obj))

	return JS_FALSE;

    argv[0] = OBJECT_TO_JSVAL(obj);

    atom = js_ValueToStringAtom(cx, argv[1]);

    if (!atom)

	return JS_FALSE;

    if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &obj2, &prop))

	return JS_FALSE;

    if (!prop) {

	ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,

				 JSPROP_EXPORTED, NULL);

    } else {

	ok = OBJ_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs);

	if (ok) {

	    attrs |= JSPROP_EXPORTED;

	    ok = OBJ_SET_ATTRIBUTES(cx, obj, (jsid)atom, prop, &attrs);

	}

	OBJ_DROP_PROPERTY(cx, obj2, prop);

    }

    return ok;

}

#endif



#ifdef TEST_CVTARGS

#include <ctype.h>



static const char *

EscapeWideString(jschar *w)

{

    static char enuf[80];

    static char hex[] = "0123456789abcdef";

    jschar u;

    unsigned char b, c;

    int i, j;



    if (!w)

	return "";

    for (i = j = 0; i < sizeof enuf - 1; i++, j++) {

	u = w[j];

	if (u == 0)

	    break;

	b = (unsigned char)(u >> 8);

	c = (unsigned char)(u);

	if (b) {

	    if (i >= sizeof enuf - 6)

		break;

	    enuf[i++] = '\\';

	    enuf[i++] = 'u';

	    enuf[i++] = hex[b >> 4];

	    enuf[i++] = hex[b & 15];

	    enuf[i++] = hex[c >> 4];

	    enuf[i] = hex[c & 15];

	} else if (!isprint(c)) {

	    if (i >= sizeof enuf - 4)

		break;

	    enuf[i++] = '\\';

	    enuf[i++] = 'x';

	    enuf[i++] = hex[c >> 4];

	    enuf[i] = hex[c & 15];

	} else {

	    enuf[i] = (char)c;

	}

    }

    enuf[i] = 0;

    return enuf;

}



#include <stdarg.h>



static JSBool

ZZ_formatter(JSContext *cx, const char *format, JSBool fromJS, jsval **vpp,

	     va_list *app)

{

    jsval *vp;

    va_list ap;

    jsdouble re, im;



    printf("entering ZZ_formatter");

    vp = *vpp;

    ap = *app;

    if (fromJS) {

	if (!JS_ValueToNumber(cx, vp[0], &re))

	    return JS_FALSE;

	if (!JS_ValueToNumber(cx, vp[1], &im))

	    return JS_FALSE;

	*va_arg(ap, jsdouble *) = re;

	*va_arg(ap, jsdouble *) = im;

    } else {

	re = va_arg(ap, jsdouble);

	im = va_arg(ap, jsdouble);

	if (!JS_NewNumberValue(cx, re, &vp[0]))

	    return JS_FALSE;

	if (!JS_NewNumberValue(cx, im, &vp[1]))

	    return JS_FALSE;

    }

    *vpp = vp + 2;

    *app = ap;

    printf("leaving ZZ_formatter");

    return JS_TRUE;

}



static JSBool

ConvertArgs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSBool b = JS_FALSE;

    jschar c = 0;

    int32 i = 0, j = 0;

    uint32 u = 0;

    jsdouble d = 0, I = 0, re = 0, im = 0;

    char *s = NULL;

    JSString *str = NULL;

    jschar *w = NULL;

    JSObject *obj2 = NULL;

    JSFunction *fun = NULL;

    jsval v = JSVAL_VOID;

    JSBool ok;



    if (!JS_AddArgumentFormatter(cx, "ZZ", ZZ_formatter))

	return JS_FALSE;;

    ok = JS_ConvertArguments(cx, argc, argv, "b/ciujdIsSWofvZZ*",

			     &b, &c, &i, &u, &j, &d, &I, &s, &str, &w, &obj2,

			     &fun, &v, &re, &im);

    JS_RemoveArgumentFormatter(cx, "ZZ");

    if (!ok)

	return JS_FALSE;

    fprintf(gOutFile,

	    "b %u, c %x (%c), i %ld, u %lu, j %ld\n",

	    b, c, (char)c, i, u, j);

    fprintf(gOutFile,

	    "d %g, I %g, s %s, S %s, W %s, obj %s, fun %s\n"

	    "v %s, re %g, im %g\n",

	    d, I, s, str ? JS_GetStringBytes(str) : "", EscapeWideString(w),

	    JS_GetStringBytes(JS_ValueToString(cx, OBJECT_TO_JSVAL(obj2))),

	    fun ? JS_GetStringBytes(JS_DecompileFunction(cx, fun, 4)) : "",

	    JS_GetStringBytes(JS_ValueToString(cx, v)), re, im);

    return JS_TRUE;

}

#endif



static JSBool

BuildDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    fprintf(gOutFile, "built on %s at %s\n", __DATE__, __TIME__);

    return JS_TRUE;

}



static JSBool

Clear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    if (argc != 0 && !JS_ValueToObject(cx, argv[0], &obj))

        return JS_FALSE;

    JS_ClearScope(cx, obj);

    return JS_TRUE;

}



static JSBool

Intern(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str;



    str = JS_ValueToString(cx, argv[0]);

    if (!str)

        return JS_FALSE;

    if (!JS_InternUCStringN(cx, JS_GetStringChars(str),

                                JS_GetStringLength(str))) {

        return JS_FALSE;

    }

    return JS_TRUE;

}



static JSFunctionSpec shell_functions[] = {

    {"version",         Version,        0},

    {"options",         Options,        0},

    {"load",            Load,           1},

    {"print",           Print,          0},

    {"help",            Help,           0},

    {"quit",            Quit,           0},

    {"gc",              GC,             0},

    {"trap",            Trap,           3},

    {"untrap",          Untrap,         2},

    {"line2pc",         LineToPC,       0},

    {"pc2line",         PCToLine,       0},

#ifdef DEBUG

    {"dis",             Disassemble,    1},

    {"dissrc",          DisassWithSrc,  1},

    {"notes",           Notes,          1},

    {"tracing",         Tracing,        0},

    {"stats",           DumpStats,      1},

#endif

#ifdef TEST_EXPORT

    {"doexp",           DoExport,       2},

#endif

#ifdef TEST_CVTARGS

    {"cvtargs",         ConvertArgs,    0, 0, 12},

#endif

    {"build",           BuildDate,      0},

    {"clear",           Clear,          0},

    {"intern",          Intern,         1},

    {0}

};



/* NOTE: These must be kept in sync with the above. */



static char *shell_help_messages[] = {

    "version([number])      Get or set JavaScript version number",

    "options([option ...])  Get or toggle JavaScript options",

    "load(['foo.js' ...])   Load files named by string arguments",

    "print([expr ...])      Evaluate and print expressions",

    "help([name ...])       Display usage and help messages",

    "quit()                 Quit the shell",

    "gc()                   Run the garbage collector",

    "trap([fun] [pc] expr)  Trap bytecode execution",

    "untrap([fun] [pc])     Remove a trap",

    "line2pc([fun] line)    Map line number to PC",

    "pc2line([fun] [pc])    Map PC to line number",

#ifdef DEBUG

    "dis([fun])             Disassemble functions into bytecodes",

    "dissrc([fun])          Disassemble functions with source lines",

    "notes([fun])           Show source notes for functions",

    "tracing([toggle])      Turn tracing on or off",

    "stats([string ...])    Dump 'arena', 'atom', 'global' stats",

#endif

#ifdef TEST_EXPORT

    "doexp(obj, id)         Export identified property from object",

#endif

#ifdef TEST_CVTARGS

    "cvtargs(b, c, ...)     Test JS_ConvertArguments",

#endif

    "build()                Show build date and time",

    "clear([obj])           Clear properties of object",

    0

};



static void

ShowHelpHeader(void)

{

    fprintf(gOutFile, "%-9s %-22s %s\n", "Command", "Usage", "Description");

    fprintf(gOutFile, "%-9s %-22s %s\n", "=======", "=====", "===========");

}



static void

ShowHelpForCommand(uintN n)

{

    fprintf(gOutFile, "%-9.9s %s\n", shell_functions[n].name, shell_help_messages[n]);

}



static JSBool

Help(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    uintN i, j;

    int did_header, did_something;

    JSType type;

    JSFunction *fun;

    JSString *str;

    const char *bytes;



    fprintf(gOutFile, "%s\n", JS_GetImplementationVersion());

    if (argc == 0) {

	ShowHelpHeader();

	for (i = 0; shell_functions[i].name; i++)

	    ShowHelpForCommand(i);

    } else {

	did_header = 0;

	for (i = 0; i < argc; i++) {

	    did_something = 0;

	    type = JS_TypeOfValue(cx, argv[i]);

	    if (type == JSTYPE_FUNCTION) {

		fun = JS_ValueToFunction(cx, argv[i]);

		str = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;

	    } else if (type == JSTYPE_STRING) {

		str = JSVAL_TO_STRING(argv[i]);

	    } else {

		str = NULL;

	    }

	    if (str) {

		bytes = JS_GetStringBytes(str);

		for (j = 0; shell_functions[j].name; j++) {

		    if (!strcmp(bytes, shell_functions[j].name)) {

			if (!did_header) {

			    did_header = 1;

			    ShowHelpHeader();

			}

			did_something = 1;

			ShowHelpForCommand(j);

			break;

		    }

		}

	    }

	    if (!did_something) {

		str = JS_ValueToString(cx, argv[i]);

		if (!str)

		    return JS_FALSE;

		fprintf(gErrFile, "Sorry, no help for %s\n",

			JS_GetStringBytes(str));

	    }

	}

    }

    return JS_TRUE;

}



/*

 * Define a JS object called "it".  Give it class operations that printf why

 * they're being called for tutorial purposes.

 */

enum its_tinyid {

    ITS_COLOR, ITS_HEIGHT, ITS_WIDTH, ITS_FUNNY, ITS_ARRAY, ITS_RDONLY

};



static JSPropertySpec its_props[] = {

    {"color",           ITS_COLOR,	JSPROP_ENUMERATE},

    {"height",          ITS_HEIGHT,	JSPROP_ENUMERATE},

    {"width",           ITS_WIDTH,	JSPROP_ENUMERATE},

    {"funny",           ITS_FUNNY,	JSPROP_ENUMERATE},

    {"array",           ITS_ARRAY,	JSPROP_ENUMERATE},

    {"rdonly",		ITS_RDONLY,	JSPROP_READONLY},

    {0}

};



static JSBool

its_item(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    *rval = OBJECT_TO_JSVAL(obj);

    JS_SetCallReturnValue2(cx, argv[0]);

    return JS_TRUE;

}



static JSFunctionSpec its_methods[] = {

    {"item",            its_item,       0},

    {0}

};



#ifdef JSD_LOWLEVEL_SOURCE

/*

 * This facilitates sending source to JSD (the debugger system) in the shell

 * where the source is loaded using the JSFILE hack in jsscan. The function

 * below is used as a callback for the jsdbgapi JS_SetSourceHandler hook.

 * A more normal embedding (e.g. mozilla) loads source itself and can send

 * source directly to JSD without using this hook scheme.

 */

static void

SendSourceToJSDebugger(const char *filename, uintN lineno,

                       jschar *str, size_t length,

                       void **listenerTSData, JSDContext* jsdc)

{

    JSDSourceText *jsdsrc = (JSDSourceText *) *listenerTSData;



    if (!jsdsrc) {

        if (!filename)

            filename = "typein";

	if (1 == lineno) {

	    jsdsrc = JSD_NewSourceText(jsdc, filename);

	} else {

	    jsdsrc = JSD_FindSourceForURL(jsdc, filename);

	    if (jsdsrc && JSD_SOURCE_PARTIAL !=

		JSD_GetSourceStatus(jsdc, jsdsrc)) {

		jsdsrc = NULL;

	    }

	}

    }

    if (jsdsrc) {

        jsdsrc = JSD_AppendUCSourceText(jsdc,jsdsrc, str, length,

                                        JSD_SOURCE_PARTIAL);

    }

    *listenerTSData = jsdsrc;

}

#endif /* JSD_LOWLEVEL_SOURCE */



static JSBool its_noisy;    /* whether to be noisy when finalizing it */



static JSBool

its_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    if (its_noisy) {

	fprintf(gOutFile, "adding its property %s,",

	       JS_GetStringBytes(JS_ValueToString(cx, id)));

	fprintf(gOutFile, " initial value %s\n",

	       JS_GetStringBytes(JS_ValueToString(cx, *vp)));

    }

    return JS_TRUE;

}



static JSBool

its_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    if (its_noisy) {

	fprintf(gOutFile, "deleting its property %s,",

	       JS_GetStringBytes(JS_ValueToString(cx, id)));

	fprintf(gOutFile, " current value %s\n",

	       JS_GetStringBytes(JS_ValueToString(cx, *vp)));

    }

    return JS_TRUE;

}



static JSBool

its_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    if (its_noisy) {

	fprintf(gOutFile, "getting its property %s,",

	       JS_GetStringBytes(JS_ValueToString(cx, id)));

	fprintf(gOutFile, " current value %s\n",

	       JS_GetStringBytes(JS_ValueToString(cx, *vp)));

    }

    return JS_TRUE;

}



static JSBool

its_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    if (its_noisy) {

	fprintf(gOutFile, "setting its property %s,",

	       JS_GetStringBytes(JS_ValueToString(cx, id)));

	fprintf(gOutFile, " new value %s\n",

	       JS_GetStringBytes(JS_ValueToString(cx, *vp)));

    }

    if (JSVAL_IS_STRING(id) &&

	!strcmp(JS_GetStringBytes(JSVAL_TO_STRING(id)), "noisy")) {

	return JS_ValueToBoolean(cx, *vp, &its_noisy);

    }

    return JS_TRUE;

}



static JSBool

its_enumerate(JSContext *cx, JSObject *obj)

{

    if (its_noisy)

	fprintf(gOutFile, "enumerate its properties\n");

    return JS_TRUE;

}



static JSBool

its_resolve(JSContext *cx, JSObject *obj, jsval id)

{

    if (its_noisy) {

	fprintf(gOutFile, "resolving its property %s\n",

	       JS_GetStringBytes(JS_ValueToString(cx, id)));

    }

    return JS_TRUE;

}



static JSBool

its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)

{

    if (its_noisy)

	fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));

    return JS_TRUE;

}



static void

its_finalize(JSContext *cx, JSObject *obj)

{

    if (its_noisy)

	fprintf(gOutFile, "finalizing it\n");

}



static JSClass its_class = {

    "It", 0,

    its_addProperty,  its_delProperty,  its_getProperty,  its_setProperty,

    its_enumerate,    its_resolve,      its_convert,      its_finalize

};



JSErrorFormatString jsShell_ErrorFormatString[JSErr_Limit] = {

#if JS_HAS_DFLT_MSG_STRINGS

#define MSG_DEF(name, number, count, exception, format) \

    { format, count } ,

#else

#define MSG_DEF(name, number, count, exception, format) \

    { NULL, count } ,

#endif

#include "jsshell.msg"

#undef MSG_DEF

};



static const JSErrorFormatString *

my_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)

{

    if ((errorNumber > 0) && (errorNumber < JSShellErr_Limit))

        return &jsShell_ErrorFormatString[errorNumber];

    return NULL;

}



static void

my_LoadErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)

{

    if (!report) {

        fprintf(gErrFile, "%s\n", message);

        return;

    }



    /* Ignore any exceptions */

    if (JSREPORT_IS_EXCEPTION(report->flags))

        return;



    /* Otherwise, fall back to the ordinary error reporter. */

    my_ErrorReporter(cx, message, report);

}



static void

my_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)

{

    int i, j, k, n;

    char *prefix, *tmp;

    const char *ctmp;



    if (!report) {

	fprintf(gErrFile, "%s\n", message);

	return;

    }



    /* Conditionally ignore reported warnings. */

    if (JSREPORT_IS_WARNING(report->flags) && !reportWarnings)

	return;



    prefix = NULL;

    if (report->filename)

	prefix = JS_smprintf("%s:", report->filename);

    if (report->lineno) {

	tmp = prefix;

	prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);

	JS_free(cx, tmp);

    }

    if (JSREPORT_IS_WARNING(report->flags)) {

	tmp = prefix;

	prefix = JS_smprintf("%s%swarning: ",

                             tmp ? tmp : "",

                             JSREPORT_IS_STRICT(report->flags) ? "strict " : "");

	JS_free(cx, tmp);

    }



    /* embedded newlines -- argh! */

    while ((ctmp = strchr(message, '\n')) != 0) {

	ctmp++;

	if (prefix)

            fputs(prefix, gErrFile);

	fwrite(message, 1, ctmp - message, gErrFile);

	message = ctmp;

    }



    /* If there were no filename or lineno, the prefix might be empty */

    if (prefix)

        fputs(prefix, gErrFile);

    fputs(message, gErrFile);



    if (!report->linebuf) {

	fputc('\n', gErrFile);

	goto out;

    }



    /* report->linebuf usually ends with a newline. */

    n = strlen(report->linebuf);

    fprintf(gErrFile, ":\n%s%s%s%s",

            prefix,

            report->linebuf,

            (n > 0 && report->linebuf[n-1] == '\n') ? "" : "\n",

            prefix);

    n = PTRDIFF(report->tokenptr, report->linebuf, char);

    for (i = j = 0; i < n; i++) {

	if (report->linebuf[i] == '\t') {

            for (k = (j + 8) & ~7; j < k; j++) {

		fputc('.', gErrFile);

            }

	    continue;

	}

	fputc('.', gErrFile);

	j++;

    }

    fputs("^\n", gErrFile);

 out:

    if (!JSREPORT_IS_WARNING(report->flags))

        gExitCode = EXITCODE_RUNTIME_ERROR;

    JS_free(cx, prefix);

}



#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)

static JSBool

Exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFunction *fun;

    const char *name, **nargv;

    uintN i, nargc;

    JSString *str;

    pid_t pid;

    int status;



    fun = JS_ValueToFunction(cx, argv[-2]);

    if (!fun)

	return JS_FALSE;

    if (!fun->atom)

	return JS_TRUE;

    name = ATOM_BYTES(fun->atom);

    nargc = 1 + argc;

    nargv = JS_malloc(cx, (nargc + 1) * sizeof(char *));

    if (!nargv)

	return JS_FALSE;

    nargv[0] = name;

    for (i = 1; i < nargc; i++) {

	str = JS_ValueToString(cx, argv[i-1]);

	if (!str) {

	    JS_free(cx, nargv);

	    return JS_FALSE;

	}

	nargv[i] = JS_GetStringBytes(str);

    }

    nargv[nargc] = 0;

    pid = fork();

    switch (pid) {

      case -1:

	perror("js");

	break;

      case 0:

	(void) execvp(name, (char **)nargv);

	perror("js");

	exit(127);

      default:

	while (waitpid(pid, &status, 0) < 0 && errno == EINTR)

	    ;

	break;

    }

    JS_free(cx, nargv);

    return JS_TRUE;

}

#endif



#define LAZY_STANDARD_CLASSES



static JSBool

global_enumerate(JSContext *cx, JSObject *obj)

{

#ifdef LAZY_STANDARD_CLASSES

    return JS_EnumerateStandardClasses(cx, obj);

#else

    return JS_TRUE;

#endif

}



static JSBool

global_resolve(JSContext *cx, JSObject *obj, jsval id)

{

#ifdef LAZY_STANDARD_CLASSES

    JSBool resolved;



    if (!JS_ResolveStandardClass(cx, obj, id, &resolved))

        return JS_FALSE;

    if (resolved)

        return JS_TRUE;

#endif



#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)

  {

    /*

     * Do this expensive hack only for unoptimized Unix builds, which are not

     * used for benchmarking.

     */

    char *path, *comp, *full;

    const char *name;

    JSBool ok, found;

    JSFunction *fun;



    if (!JSVAL_IS_STRING(id))

	return JS_TRUE;

    path = getenv("PATH");

    if (!path)

	return JS_TRUE;

    path = JS_strdup(cx, path);

    if (!path)

	return JS_FALSE;

    name = JS_GetStringBytes(JSVAL_TO_STRING(id));

    ok = JS_TRUE;

    for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {

	if (*comp != '\0') {

	    full = JS_smprintf("%s/%s", comp, name);

	    if (!full) {

		JS_ReportOutOfMemory(cx);

		ok = JS_FALSE;

		break;

	    }

	} else {

	    full = (char *)name;

	}

	found = (access(full, X_OK) == 0);

	if (*comp != '\0')

	    free(full);

	if (found) {

	    fun = JS_DefineFunction(cx, obj, name, Exec, 0, JSPROP_ENUMERATE);

	    ok = (fun != NULL);

	    break;

	}

    }

    JS_free(cx, path);

    return ok;

  }

#else

    return JS_TRUE;

#endif

}



static JSClass global_class = {

    "global", 0,

    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,

    global_enumerate, global_resolve,   JS_ConvertStub,   JS_FinalizeStub

};



int

main(int argc, char **argv)

{

    JSVersion version;

    JSRuntime *rt;

    JSContext *cx;

    JSObject *glob, *it;

    int result;

#ifdef LIVECONNECT

    JavaVM *java_vm = NULL;

#endif

#ifdef JSDEBUGGER_JAVA_UI

    JNIEnv *java_env;

#endif



#ifdef XP_OS2

   /* these streams are normally line buffered on OS/2 and need a \n, *

    * so we need to unbuffer then to get a reasonable prompt          */

    setbuf(stdout,0);

    setbuf(stderr,0);

#endif



    gErrFile = stderr;

    gOutFile = stdout;



#ifdef XP_MAC

#ifndef XP_MAC_MPW

	initConsole("\pJavaScript Shell", "Welcome to js shell.", &argc, &argv);

#endif

#endif



#ifdef MAC_TEST_HACK

/*

        Open a file "testArgs.txt" and read each line into argc/argv.

        Re-direct all output to "results.txt"

*/

        {

                char argText[256];

                FILE *f = fopen("testargs.txt", "r");

                if (f != NULL) {

                        int maxArgs = 32; /* arbitrary max !!! */

                        argc = 1;

                        argv = malloc(sizeof(char *) * maxArgs);

                        argv[0] = NULL;

                        while (fgets(argText, 255, f) != NULL) {

                                 /* argText includes '\n' */

                                argv[argc] = malloc(strlen(argText));

                                strncpy(argv[argc], argText,

                                                    strlen(argText) - 1);

                                argv[argc][strlen(argText) - 1] = '\0';

                                argc++;

                                if (argc >= maxArgs) break;

                        }

                        fclose(f);

                }

                gTestResultFile = fopen("results.txt", "w");

        }



        gErrFile = gTestResultFile;

        gOutFile = gTestResultFile;

#endif



    version = JSVERSION_DEFAULT;



    argc--;

    argv++;



    rt = JS_NewRuntime(8L * 1024L * 1024L);

    if (!rt)

	return 1;



    cx = JS_NewContext(rt, gStackChunkSize);

    if (!cx)

	return 1;

    JS_SetErrorReporter(cx, my_ErrorReporter);



    glob = JS_NewObject(cx, &global_class, NULL, NULL);

    if (!glob)

	return 1;

#ifdef LAZY_STANDARD_CLASSES

    JS_SetGlobalObject(cx, glob);

#else

    if (!JS_InitStandardClasses(cx, glob))

	return 1;

#endif

    if (!JS_DefineFunctions(cx, glob, shell_functions))

	return 1;



    /* Set version only after there is a global object. */

    if (version != JSVERSION_DEFAULT)

	JS_SetVersion(cx, version);



    it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);

    if (!it)

	return 1;

    if (!JS_DefineProperties(cx, it, its_props))

	return 1;

    if (!JS_DefineFunctions(cx, it, its_methods))

	return 1;



#ifdef PERLCONNECT

    if (!JS_InitPerlClass(cx, glob))

	return 1;

#endif



#ifdef JSDEBUGGER

    /*

    * XXX A command line option to enable debugging (or not) would be good

    */

    _jsdc = JSD_DebuggerOnForUser(rt, NULL, NULL);

    if (!_jsdc)

	return 1;

    JSD_JSContextInUse(_jsdc, cx);

#ifdef JSD_LOWLEVEL_SOURCE

    JS_SetSourceHandler(rt, SendSourceToJSDebugger, _jsdc);

#endif /* JSD_LOWLEVEL_SOURCE */

#ifdef JSDEBUGGER_JAVA_UI

    _jsdjc = JSDJ_CreateContext();

    if (! _jsdjc)

	return 1;

    JSDJ_SetJSDContext(_jsdjc, _jsdc);

    java_env = JSDJ_CreateJavaVMAndStartDebugger(_jsdjc);

#ifdef LIVECONNECT

    if (java_env)

	(*java_env)->GetJavaVM(java_env, &java_vm);

#endif

    /*

    * XXX This would be the place to wait for the debugger to start.

    * Waiting would be nice in general, but especially when a js file

    * is passed on the cmd line.

    */

#endif /* JSDEBUGGER_JAVA_UI */

#ifdef JSDEBUGGER_C_UI

    JSDB_InitDebugger(rt, _jsdc, 0);

#endif /* JSDEBUGGER_C_UI */

#endif /* JSDEBUGGER */



#ifdef LIVECONNECT

	if (!JSJ_SimpleInit(cx, glob, java_vm, getenv("CLASSPATH")))

	    return 1;

#endif



    result = ProcessArgs(cx, glob, argv, argc);



#ifdef JSDEBUGGER

    if (_jsdc)

	JSD_DebuggerOff(_jsdc);

#endif  /* JSDEBUGGER */



#ifdef MAC_TEST_HACK

    fclose(gTestResultFile);

#endif



    JS_DestroyContext(cx);

    JS_DestroyRuntime(rt);

    JS_ShutDown();

    return result;

}

 

**** End of js.c ****

 

**** Start of js.msg ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

	This is the JavaScript error message file.



	The format for each JS error message is:



MSG_DEF(<SYMBOLIC_NAME>, <ERROR_NUMBER>, <ARGUMENT_COUNT>, <EXCEPTION_NAME>,

	<FORMAT_STRING>)



	where ;

	<SYMBOLIC_NAME> is a legal C identifer that will be used in the

	JS engine source.



	<ERROR_NUMBER> is an unique integral value identifying this error.



	<ARGUMENT_COUNT> is an integer literal specifying the total number of

	replaceable arguments in the following format string.



	<EXCEPTION_NAME> is an exception index from the enum in jsexn.c;

	JSEXN_NONE for none.  The given exception index will be raised by the

	engine when the corresponding error occurs.



	<FORMAT_STRING> is a string literal, optionally containing sequences

	{X} where X  is an integer representing the argument number that will

	be replaced with a string value when the error is reported.



	e.g.



	MSG_DEF(JSMSG_NOT_A_SUBSPECIES, 73, JSEXN_NONE, 2,

		"{0} is not a member of the {1} family")



	can be used :



	JS_ReportErrorNumber(JSMSG_NOT_A_SUBSPECIES, "Rhino", "Monkey");



	to report :



	"Rhino is not a member of the Monkey family"



*/



MSG_DEF(JSMSG_NOT_AN_ERROR,             0, 0, JSEXN_NONE, "<Error #0 is reserved>")

MSG_DEF(JSMSG_NOT_DEFINED,              1, 1, JSEXN_REFERENCEERR, "{0} is not defined")

MSG_DEF(JSMSG_NO_REG_EXPS,              2, 1, JSEXN_INTERNALERR, "sorry, regular expression are not supported")

MSG_DEF(JSMSG_MORE_ARGS_NEEDED,         3, 3, JSEXN_NONE, "{0} requires more than {1} argument{2}")

MSG_DEF(JSMSG_BAD_CHAR,                 4, 1, JSEXN_NONE, "invalid format character {0}")

MSG_DEF(JSMSG_BAD_TYPE,                 5, 1, JSEXN_NONE, "unknown type {0}")

MSG_DEF(JSMSG_CANT_LOCK,                6, 0, JSEXN_NONE, "can't lock memory")

MSG_DEF(JSMSG_CANT_UNLOCK,              7, 0, JSEXN_NONE, "can't unlock memory")

MSG_DEF(JSMSG_INCOMPATIBLE_PROTO,       8, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}")

MSG_DEF(JSMSG_NO_CONSTRUCTOR,           9, 1, JSEXN_NONE, "{0} has no constructor")

MSG_DEF(JSMSG_CANT_ALIAS,              10, 3, JSEXN_NONE, "can't alias {0} to {1} in class {2}")

MSG_DEF(JSMSG_NO_PROTO,                11, 1, JSEXN_INTERNALERR, "sorry, Array.prototype.{0} is not yet implemented")

MSG_DEF(JSMSG_BAD_SORT_ARG,            12, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument")

MSG_DEF(JSMSG_BAD_ATOMIC_NUMBER,       13, 1, JSEXN_INTERNALERR, "internal error: no index for atom {0}")

MSG_DEF(JSMSG_TOO_MANY_LITERALS,       14, 0, JSEXN_INTERNALERR, "too many literals")

MSG_DEF(JSMSG_CANT_WATCH,              15, 1, JSEXN_NONE, "can't watch non-native objects of class {0}")

MSG_DEF(JSMSG_STACK_UNDERFLOW,         16, 2, JSEXN_INTERNALERR, "internal error compiling {0}: stack underflow at pc {1}")

MSG_DEF(JSMSG_NEED_DIET,               17, 1, JSEXN_INTERNALERR, "{0} too large")

MSG_DEF(JSMSG_BAD_CASE,                18, 0, JSEXN_SYNTAXERR, "invalid case expression")

MSG_DEF(JSMSG_READ_ONLY,               19, 1, JSEXN_ERR, "{0} is read-only")

MSG_DEF(JSMSG_BAD_FORMAL,              20, 0, JSEXN_SYNTAXERR, "malformed formal parameter")

MSG_DEF(JSMSG_SAME_FORMAL,             21, 1, JSEXN_NONE, "duplicate formal argument {0}")

MSG_DEF(JSMSG_NOT_FUNCTION,            22, 1, JSEXN_TYPEERR, "{0} is not a function")

MSG_DEF(JSMSG_NOT_CONSTRUCTOR,         23, 1, JSEXN_TYPEERR, "{0} is not a constructor")

MSG_DEF(JSMSG_STACK_OVERFLOW,          24, 1, JSEXN_INTERNALERR, "stack overflow in {0}")

MSG_DEF(JSMSG_NOT_EXPORTED,            25, 1, JSEXN_NONE, "{0} is not exported")

MSG_DEF(JSMSG_OVER_RECURSED,           26, 0, JSEXN_INTERNALERR, "too much recursion")

MSG_DEF(JSMSG_IN_NOT_OBJECT,           27, 1, JSEXN_TYPEERR, "invalid 'in' operand {0}")

MSG_DEF(JSMSG_BAD_NEW_RESULT,          28, 1, JSEXN_NONE, "invalid new expression result {0}")

MSG_DEF(JSMSG_BAD_SHARP_DEF,           29, 1, JSEXN_ERR, "invalid sharp variable definition #{0}=")

MSG_DEF(JSMSG_BAD_SHARP_USE,           30, 1, JSEXN_ERR, "invalid sharp variable use #{0}#")

MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS,      31, 1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}")

MSG_DEF(JSMSG_BAD_BYTECODE,            32, 1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}")

MSG_DEF(JSMSG_BAD_RADIX,               33, 1, JSEXN_ERR, "illegal radix {0}")

MSG_DEF(JSMSG_NAN,                     34, 1, JSEXN_ERR, "{0} is not a number")

MSG_DEF(JSMSG_CANT_CONVERT,            35, 1, JSEXN_NONE, "can't convert {0} to an integer")

MSG_DEF(JSMSG_CYCLIC_VALUE,            36, 1, JSEXN_ERR, "cyclic {0} value")

MSG_DEF(JSMSG_PERMANENT,               37, 1, JSEXN_ERR, "{0} is permanent")

MSG_DEF(JSMSG_CANT_CONVERT_TO,         38, 2, JSEXN_TYPEERR, "can't convert {0} to {1}")

MSG_DEF(JSMSG_NO_PROPERTIES,           39, 1, JSEXN_TYPEERR, "{0} has no properties")

MSG_DEF(JSMSG_CANT_FIND_CLASS,         40, 1, JSEXN_NONE, "can't find class id {0}")

MSG_DEF(JSMSG_CANT_XDR_CLASS,          41, 1, JSEXN_NONE, "can't XDR class {0}")

MSG_DEF(JSMSG_BYTECODE_TOO_BIG,        42, 2, JSEXN_INTERNALERR, "bytecode {0} too large (limit {1})")

MSG_DEF(JSMSG_UNKNOWN_FORMAT,          43, 1, JSEXN_INTERNALERR, "unknown bytecode format {0}")

MSG_DEF(JSMSG_TOO_MANY_CON_ARGS,       44, 0, JSEXN_SYNTAXERR, "too many constructor arguments")

MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS,       45, 0, JSEXN_SYNTAXERR, "too many function arguments")

MSG_DEF(JSMSG_BAD_QUANTIFIER,          46, 1, JSEXN_SYNTAXERR, "invalid quantifier {0}")

MSG_DEF(JSMSG_MIN_TOO_BIG,             47, 1, JSEXN_SYNTAXERR, "overlarge minimum {0}")

MSG_DEF(JSMSG_MAX_TOO_BIG,             48, 1, JSEXN_SYNTAXERR, "overlarge maximum {0}")

MSG_DEF(JSMSG_OUT_OF_ORDER,            49, 1, JSEXN_SYNTAXERR, "maximum {0} less than minimum")

MSG_DEF(JSMSG_ZERO_QUANTIFIER,         50, 1, JSEXN_SYNTAXERR, "zero quantifier {0}")

MSG_DEF(JSMSG_UNTERM_QUANTIFIER,       51, 1, JSEXN_SYNTAXERR, "unterminated quantifier {0}")

MSG_DEF(JSMSG_EMPTY_BEFORE_STAR,       52, 0, JSEXN_SYNTAXERR, "regular expression before * could be empty")

MSG_DEF(JSMSG_EMPTY_BEFORE_PLUS,       53, 0, JSEXN_SYNTAXERR, "regular expression before + could be empty")

MSG_DEF(JSMSG_MISSING_PAREN,           54, 1, JSEXN_SYNTAXERR, "unterminated parenthetical {0}")

MSG_DEF(JSMSG_UNTERM_CLASS,            55, 1, JSEXN_SYNTAXERR, "unterminated character class {0}")

MSG_DEF(JSMSG_TRAILING_SLASH,          56, 0, JSEXN_SYNTAXERR, "trailing \\ in regular expression")

MSG_DEF(JSMSG_BAD_CLASS_RANGE,         57, 0, JSEXN_SYNTAXERR, "invalid range in character class")

MSG_DEF(JSMSG_BAD_FLAG,                58, 1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}")

MSG_DEF(JSMSG_NO_INPUT,                59, 3, JSEXN_SYNTAXERR, "no input for /{0}/{1}{2}")

MSG_DEF(JSMSG_CANT_OPEN,               60, 2, JSEXN_NONE, "can't open {0}: {1}")

MSG_DEF(JSMSG_BAD_STRING_MASK,         61, 1, JSEXN_ERR, "invalid string escape mask {0}")

MSG_DEF(JSMSG_NO_STRING_PROTO,         62, 1, JSEXN_INTERNALERR, "sorry, String.prototype.{0} is not yet implemented")

MSG_DEF(JSMSG_END_OF_DATA,             63, 0, JSEXN_NONE, "unexpected end of data")

MSG_DEF(JSMSG_SEEK_BEYOND_START,       64, 0, JSEXN_NONE, "illegal seek beyond start")

MSG_DEF(JSMSG_SEEK_BEYOND_END,         65, 0, JSEXN_NONE, "illegal seek beyond end")

MSG_DEF(JSMSG_END_SEEK,                66, 0, JSEXN_NONE, "illegal end-based seek")

MSG_DEF(JSMSG_WHITHER_WHENCE,          67, 1, JSEXN_NONE, "unknown seek whence: {0}")

MSG_DEF(JSMSG_BAD_JVAL_TYPE,           68, 1, JSEXN_NONE, "unknown jsval type {0} for XDR")

MSG_DEF(JSMSG_PAREN_BEFORE_FORMAL,     69, 0, JSEXN_SYNTAXERR, "missing ( before formal parameters")

MSG_DEF(JSMSG_MISSING_FORMAL,          70, 0, JSEXN_SYNTAXERR, "missing formal parameter")

MSG_DEF(JSMSG_PAREN_AFTER_FORMAL,      71, 0, JSEXN_SYNTAXERR, "missing ) after formal parameters")

MSG_DEF(JSMSG_CURLY_BEFORE_BODY,       72, 0, JSEXN_SYNTAXERR, "missing { before function body")

MSG_DEF(JSMSG_CURLY_AFTER_BODY,        73, 0, JSEXN_SYNTAXERR, "missing } after function body")

MSG_DEF(JSMSG_PAREN_BEFORE_COND,       74, 0, JSEXN_SYNTAXERR, "missing ( before condition")

MSG_DEF(JSMSG_PAREN_AFTER_COND,        75, 0, JSEXN_SYNTAXERR, "missing ) after condition")

MSG_DEF(JSMSG_NO_IMPORT_NAME,          76, 0, JSEXN_SYNTAXERR, "missing name in import statement")

MSG_DEF(JSMSG_NAME_AFTER_DOT,          77, 0, JSEXN_SYNTAXERR, "missing name after . operator")

MSG_DEF(JSMSG_BRACKET_IN_INDEX,        78, 0, JSEXN_SYNTAXERR, "missing ] in index expression")

MSG_DEF(JSMSG_NO_EXPORT_NAME,          79, 0, JSEXN_SYNTAXERR, "missing name in export statement")

MSG_DEF(JSMSG_PAREN_BEFORE_SWITCH,     80, 0, JSEXN_SYNTAXERR, "missing ( before switch expression")

MSG_DEF(JSMSG_PAREN_AFTER_SWITCH,      81, 0, JSEXN_SYNTAXERR, "missing ) after switch expression")

MSG_DEF(JSMSG_CURLY_BEFORE_SWITCH,     82, 0, JSEXN_SYNTAXERR, "missing { before switch body")

MSG_DEF(JSMSG_COLON_AFTER_CASE,        83, 0, JSEXN_SYNTAXERR, "missing : after case label")

MSG_DEF(JSMSG_WHILE_AFTER_DO,          84, 0, JSEXN_SYNTAXERR, "missing while after do-loop body")

MSG_DEF(JSMSG_PAREN_AFTER_FOR,         85, 0, JSEXN_SYNTAXERR, "missing ( after for")

MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT,     86, 0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer")

MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND,     87, 0, JSEXN_SYNTAXERR, "missing ; after for-loop condition")

MSG_DEF(JSMSG_PAREN_AFTER_FOR_CTRL,    88, 0, JSEXN_SYNTAXERR, "missing ) after for-loop control")

MSG_DEF(JSMSG_CURLY_BEFORE_TRY,        89, 0, JSEXN_SYNTAXERR, "missing { before try block")

MSG_DEF(JSMSG_CURLY_AFTER_TRY,         90, 0, JSEXN_SYNTAXERR, "missing } after try block")

MSG_DEF(JSMSG_PAREN_BEFORE_CATCH,      91, 0, JSEXN_SYNTAXERR, "missing ( before catch")

MSG_DEF(JSMSG_CATCH_IDENTIFIER,        92, 0, JSEXN_SYNTAXERR, "missing identifier in catch")

MSG_DEF(JSMSG_PAREN_AFTER_CATCH,       93, 0, JSEXN_SYNTAXERR, "missing ) after catch")

MSG_DEF(JSMSG_CURLY_BEFORE_CATCH,      94, 0, JSEXN_SYNTAXERR, "missing { before catch block")

MSG_DEF(JSMSG_CURLY_AFTER_CATCH,       95, 0, JSEXN_SYNTAXERR, "missing } after catch block")

MSG_DEF(JSMSG_CURLY_BEFORE_FINALLY,    96, 0, JSEXN_SYNTAXERR, "missing { before finally block")

MSG_DEF(JSMSG_CURLY_AFTER_FINALLY,     97, 0, JSEXN_SYNTAXERR, "missing } after finally block")

MSG_DEF(JSMSG_CATCH_OR_FINALLY,        98, 0, JSEXN_SYNTAXERR, "missing catch or finally after try")

MSG_DEF(JSMSG_PAREN_BEFORE_WITH,       99, 0, JSEXN_SYNTAXERR, "missing ( before with-statement object")

MSG_DEF(JSMSG_PAREN_AFTER_WITH,       100, 0, JSEXN_SYNTAXERR, "missing ) after with-statement object")

MSG_DEF(JSMSG_CURLY_IN_COMPOUND,      101, 0, JSEXN_SYNTAXERR, "missing } in compound statement")

MSG_DEF(JSMSG_NO_VARIABLE_NAME,       102, 0, JSEXN_SYNTAXERR, "missing variable name")

MSG_DEF(JSMSG_COLON_IN_COND,          103, 0, JSEXN_SYNTAXERR, "missing : in conditional expression")

MSG_DEF(JSMSG_PAREN_AFTER_ARGS,       104, 0, JSEXN_SYNTAXERR, "missing ) after argument list")

MSG_DEF(JSMSG_BRACKET_AFTER_LIST,     105, 0, JSEXN_SYNTAXERR, "missing ] after element list")

MSG_DEF(JSMSG_COLON_AFTER_ID,         106, 0, JSEXN_SYNTAXERR, "missing : after property id")

MSG_DEF(JSMSG_CURLY_AFTER_LIST,       107, 0, JSEXN_SYNTAXERR, "missing } after property list")

MSG_DEF(JSMSG_PAREN_IN_PAREN,         108, 0, JSEXN_SYNTAXERR, "missing ) in parenthetical")

MSG_DEF(JSMSG_SEMI_BEFORE_STMNT,      109, 0, JSEXN_SYNTAXERR, "missing ; before statement")

MSG_DEF(JSMSG_NO_RETURN_VALUE,        110, 1, JSEXN_TYPEERR, "function {0} does not always return a value")

MSG_DEF(JSMSG_DUPLICATE_FORMAL,       111, 1, JSEXN_TYPEERR, "duplicate formal argument {0}")

MSG_DEF(JSMSG_EQUAL_AS_ASSIGN,        112, 1, JSEXN_NONE, "test for equality (==) mistyped as assignment (=)?{0}")

MSG_DEF(JSMSG_BAD_IMPORT,             113, 0, JSEXN_SYNTAXERR, "invalid import expression")

MSG_DEF(JSMSG_TOO_MANY_DEFAULTS,      114, 0, JSEXN_SYNTAXERR, "more than one switch default")

MSG_DEF(JSMSG_TOO_MANY_CASES,         115, 0, JSEXN_INTERNALERR, "too many switch cases")

MSG_DEF(JSMSG_BAD_SWITCH,             116, 0, JSEXN_SYNTAXERR, "invalid switch statement")

MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE,       117, 0, JSEXN_SYNTAXERR, "invalid for/in left-hand side")

MSG_DEF(JSMSG_CATCH_AFTER_GENERAL,    118, 0, JSEXN_SYNTAXERR, "catch after unconditional catch")

MSG_DEF(JSMSG_CATCH_WITHOUT_TRY,      119, 0, JSEXN_SYNTAXERR, "catch without try")

MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY,    120, 0, JSEXN_SYNTAXERR, "finally without try")

MSG_DEF(JSMSG_LABEL_NOT_FOUND,        121, 0, JSEXN_SYNTAXERR, "label not found")

MSG_DEF(JSMSG_TOUGH_BREAK,            122, 0, JSEXN_SYNTAXERR, "invalid break")

MSG_DEF(JSMSG_BAD_CONTINUE,           123, 0, JSEXN_SYNTAXERR, "invalid continue")

MSG_DEF(JSMSG_BAD_RETURN,             124, 0, JSEXN_SYNTAXERR, "invalid return")

MSG_DEF(JSMSG_BAD_LABEL,              125, 0, JSEXN_SYNTAXERR, "invalid label")

MSG_DEF(JSMSG_DUPLICATE_LABEL,        126, 0, JSEXN_SYNTAXERR, "duplicate label")

MSG_DEF(JSMSG_VAR_HIDES_ARG,          127, 1, JSEXN_TYPEERR, "variable {0} hides argument")

MSG_DEF(JSMSG_BAD_VAR_INIT,           128, 0, JSEXN_SYNTAXERR, "invalid variable initialization")

MSG_DEF(JSMSG_BAD_LEFTSIDE_OF_ASS,    129, 0, JSEXN_SYNTAXERR, "invalid assignment left-hand side")

MSG_DEF(JSMSG_BAD_OPERAND,            130, 1, JSEXN_SYNTAXERR, "invalid {0} operand")

MSG_DEF(JSMSG_BAD_PROP_ID,            131, 0, JSEXN_SYNTAXERR, "invalid property id")

MSG_DEF(JSMSG_RESERVED_ID,            132, 1, JSEXN_SYNTAXERR, "{0} is a reserved identifier")

MSG_DEF(JSMSG_SYNTAX_ERROR,           133, 0, JSEXN_SYNTAXERR, "syntax error")

MSG_DEF(JSMSG_BAD_SHARP_VAR_DEF,      134, 0, JSEXN_SYNTAXERR, "invalid sharp variable definition")

MSG_DEF(JSMSG_BAD_PROTOTYPE,          135, 1, JSEXN_TYPEERR,   "'prototype' property of {0} is not an object")

MSG_DEF(JSMSG_MISSING_EXPONENT,       136, 0, JSEXN_SYNTAXERR, "missing exponent")

MSG_DEF(JSMSG_OUT_OF_MEMORY,          137, 0, JSEXN_ERR, "out of memory")

MSG_DEF(JSMSG_UNTERMINATED_STRING,    138, 0, JSEXN_SYNTAXERR, "unterminated string literal")

MSG_DEF(JSMSG_NESTED_COMMENT,         139, 0, JSEXN_SYNTAXERR, "nested comment")

MSG_DEF(JSMSG_UNTERMINATED_COMMENT,   140, 0, JSEXN_SYNTAXERR, "unterminated comment")

MSG_DEF(JSMSG_UNTERMINATED_REGEXP,    141, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal")

MSG_DEF(JSMSG_BAD_REGEXP_FLAG,        142, 0, JSEXN_SYNTAXERR, "invalid flag after regular expression")

MSG_DEF(JSMSG_SHARPVAR_TOO_BIG,       143, 0, JSEXN_SYNTAXERR, "overlarge sharp variable number")

MSG_DEF(JSMSG_ILLEGAL_CHARACTER,      144, 0, JSEXN_SYNTAXERR, "illegal character")

MSG_DEF(JSMSG_BAD_OCTAL,              145, 1, JSEXN_NONE, "{0} is not a legal ECMA-262 octal constant")

MSG_DEF(JSMSG_BAD_INDIRECT_CALL,      146, 1, JSEXN_EVALERR, "function {0} must be called directly, and not by way of a function of another name.")

MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION,     147, 1, JSEXN_NONE, "uncaught exception: {0}")

MSG_DEF(JSMSG_INVALID_BACKREF,        148, 0, JSEXN_SYNTAXERR, "non-octal digit in an escape sequence that doesn't match a back-reference")

MSG_DEF(JSMSG_BAD_BACKREF,            149, 0, JSEXN_SYNTAXERR, "back-reference exceeds number of capturing parentheses")

MSG_DEF(JSMSG_PRECISION_RANGE,        150, 1, JSEXN_RANGEERR, "precision {0} out of range")

MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER,   151, 1, JSEXN_SYNTAXERR, "invalid {0} usage")

MSG_DEF(JSMSG_BAD_ARRAY_LENGTH,       152, 0, JSEXN_RANGEERR, "invalid array length")

MSG_DEF(JSMSG_CANT_DESCRIBE_PROPS,    153, 1, JSEXN_NONE, "can't describe non-native properties of class {0}")

MSG_DEF(JSMSG_BAD_APPLY_ARGS,         154, 0, JSEXN_TYPEERR, "second argument to Function.prototype.apply must be an array")

MSG_DEF(JSMSG_REDECLARED_VAR,         155, 2, JSEXN_TYPEERR, "redeclaration of {0} {1}")

MSG_DEF(JSMSG_UNDECLARED_VAR,         156, 1, JSEXN_TYPEERR, "assignment to undeclared variable {0}")

MSG_DEF(JSMSG_ANON_NO_RETURN_VALUE,   157, 0, JSEXN_TYPEERR, "anonymous function does not always return a value")

MSG_DEF(JSMSG_DEPRECATED_USAGE,       158, 1, JSEXN_REFERENCEERR, "deprecated {0} usage")

MSG_DEF(JSMSG_BAD_URI,                159, 0, JSEXN_URIERR, "malformed URI sequence")

MSG_DEF(JSMSG_GETTER_ONLY,            160, 0, JSEXN_TYPEERR, "setting a property that has only a getter")

MSG_DEF(JSMSG_TRAILING_COMMA,         161, 0, JSEXN_SYNTAXERR, "trailing comma is not legal in ECMA-262 object initializers")

MSG_DEF(JSMSG_UNDEFINED_PROP,         162, 1, JSEXN_TYPEERR, "reference to undefined property {0}")

MSG_DEF(JSMSG_USELESS_EXPR,           163, 0, JSEXN_TYPEERR, "useless expression")

MSG_DEF(JSMSG_REDECLARED_PARAM,       164, 1, JSEXN_TYPEERR, "redeclaration of formal parameter {0}")

MSG_DEF(JSMSG_NEWREGEXP_FLAGGED,      165, 0, JSEXN_TYPEERR, "can't supply flags when constructing one RegExp from another")

 

**** End of js.msg ****

 

**** Start of jsaddr.c ****

 

/* -*- Mode: C; tab-width: 8 -*-

 * Copyright  1996 Netscape Communications Corporation, All Rights Reserved.

 */



#include <stdio.h>

#include <stdlib.h>



#include "jsapi.h"

#include "jsinterp.h"



/* These functions are needed to get the addresses of certain functions

 * in the JS module. On WIN32 especially, these symbols have a different

 * address from the actual address of these functions in the JS module.

 * This is because on WIN32, import function address fixups are done only

 * at load time and function calls are made by indirection - that is by

 * using a couple extra instructions to lookup the actual function address

 * in the importing module's import address table.

 */



JS_EXPORT_API(JSPropertyOp)

js_GetArgumentAddress()

{

	return ((void *)js_GetArgument);

}



JS_EXPORT_API(JSPropertyOp)

js_SetArgumentAddress()

{

	return ((void *)js_SetArgument);

}



JS_EXPORT_API(JSPropertyOp)

js_GetLocalVariableAddress()

{

	return ((void *)js_GetLocalVariable);

}



JS_EXPORT_API(JSPropertyOp)

js_SetLocalVariableAddress()

{

	return ((void *)js_SetLocalVariable);

}

 

**** End of jsaddr.c ****

 

**** Start of jsaddr.h ****

 

/* -*- Mode: C; tab-width: 8 -*-

 * Copyright  1996 Netscape Communications Corporation, All Rights Reserved.

 */



#ifndef jsaddr_h___

#define jsaddr_h___



JS_EXTERN_API(JSPropertyOp)

js_GetArgumentAddress();



JS_EXTERN_API(JSPropertyOp)

js_SetArgumentAddress();



JS_EXTERN_API(JSPropertyOp)

js_GetLocalVariableAddress();



JS_EXTERN_API(JSPropertyOp)

js_SetLocalVariableAddress();



#endif /* jsaddr_h___ */

 

**** End of jsaddr.h ****

 

**** Start of jsapi.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JavaScript API.

 */

#include "jsstddef.h"

#include <ctype.h>

#include <stdarg.h>

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsarena.h" /* Added by JSIFY */

#include "jsutil.h" /* Added by JSIFY */

#include "jsclist.h"

#include "jsprf.h"

#include "jsapi.h"

#include "jsarray.h"

#include "jsatom.h"

#include "jsbool.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsdate.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"



#if JS_HAS_FILE_OBJECT

#include "jsfile.h"

#endif



#ifdef HAVE_VA_LIST_AS_ARRAY

#define JS_ADDRESSOF_VA_LIST(ap) (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(jsval)

JS_GetNaNValue(JSContext *cx)

{

    CHECK_REQUEST(cx);

    return DOUBLE_TO_JSVAL(cx->runtime->jsNaN);

}



JS_PUBLIC_API(jsval)

JS_GetNegativeInfinityValue(JSContext *cx)

{

    CHECK_REQUEST(cx);

    return DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity);

}



JS_PUBLIC_API(jsval)

JS_GetPositiveInfinityValue(JSContext *cx)

{

    CHECK_REQUEST(cx);

    return DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity);

}



JS_PUBLIC_API(jsval)

JS_GetEmptyStringValue(JSContext *cx)

{

    CHECK_REQUEST(cx);

    return STRING_TO_JSVAL(cx->runtime->emptyString);

}



// DREAMWEAVER added this function

JS_PUBLIC_API(JSBool)

JS_DoubleIsNaN(jsdouble n)

{

    return JSDOUBLE_IS_NaN(n);

}



static JSBool

TryArgumentFormatter(JSContext *cx, const char **formatp, JSBool fromJS,

		     jsval **vpp, va_list *app)

{

    const char *format;

    JSArgumentFormatMap *map;



    format = *formatp;

    for (map = cx->argumentFormatMap; map; map = map->next) {

	if (!strncmp(format, map->format, map->length)) {

	    *formatp = format + map->length;

	    return map->formatter(cx, format, fromJS, vpp, app);

	}

    }

    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CHAR, format);

    return JS_FALSE;

}



JS_PUBLIC_API(JSBool)

JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format,

		    ...)

{

    va_list ap;

    JSBool ok;



    va_start(ap, format);

    ok = JS_ConvertArgumentsVA(cx, argc, argv, format, ap);

    va_end(ap);

    return ok;

}



JS_PUBLIC_API(JSBool)

JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv,

		      const char *format, va_list ap)

{

    jsval *sp;

    JSBool required;

    char c;

    JSFunction *fun;

    jsdouble d;

    JSString *str;

    JSObject *obj;



    CHECK_REQUEST(cx);

    sp = argv;

    required = JS_TRUE;

    while ((c = *format++) != '\0') {

	if (isspace(c))

	    continue;

	if (c == '/') {

	    required = JS_FALSE;

	    continue;

	}

	if (sp == argv + argc) {

	    if (required) {

		fun = js_ValueToFunction(cx, &argv[-2], JS_FALSE);

		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 **) = str->chars;

	    else

		*va_arg(ap, JSString **) = str;

	    break;

	  case 'o':

	    if (!js_ValueToObject(cx, *sp, &obj))

		return JS_FALSE;

	    *sp = OBJECT_TO_JSVAL(obj);

	    *va_arg(ap, JSObject **) = obj;

	    break;

	  case 'f':

	    fun = js_ValueToFunction(cx, sp, JS_FALSE);

	    if (!fun)

		return JS_FALSE;

	    *sp = OBJECT_TO_JSVAL(fun->object);

	    *va_arg(ap, JSFunction **) = fun;

	    break;

	  case 'v':

	    *va_arg(ap, jsval *) = *sp;

	    break;

	  case '*':

	    break;

	  default:

	    format--;

	    if (!TryArgumentFormatter(cx, &format, JS_TRUE, &sp,

                                      JS_ADDRESSOF_VA_LIST(ap))) {

		return JS_FALSE;

            }

	    /* NB: the formatter already updated sp, so we continue here. */

	    continue;

	}

	sp++;

    }

    return JS_TRUE;

}



JS_PUBLIC_API(jsval *)

JS_PushArguments(JSContext *cx, void **markp, const char *format, ...)

{

    va_list ap;

    jsval *argv;



    va_start(ap, format);

    argv = JS_PushArgumentsVA(cx, markp, format, ap);

    va_end(ap);

    return argv;

}



JS_PUBLIC_API(jsval *)

JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap)

{

    uintN argc;

    jsval *argv, *sp;

    char c;

    const char *cp;

    JSString *str;

    JSFunction *fun;

    JSStackHeader *sh;



    CHECK_REQUEST(cx);

    *markp = NULL;

    argc = 0;

    for (cp = format; (c = *cp) != '\0'; cp++) {

	/*

	 * Count non-space non-star characters as individual jsval arguments.

	 * This may over-allocate stack, but we'll fix below.

	 */

	if (isspace(c) || c == '*')

	    continue;

	argc++;

    }

    sp = js_AllocStack(cx, argc, markp);

    if (!sp)

	return NULL;

    argv = sp;

    while ((c = *format++) != '\0') {

	if (isspace(c) || c == '*')

	    continue;

	switch (c) {

	  case 'b':

	    *sp = BOOLEAN_TO_JSVAL((JSBool) va_arg(ap, int));

	    break;

	  case 'c':

	    *sp = INT_TO_JSVAL((uint16) va_arg(ap, unsigned int));

	    break;

	  case 'i':

	  case 'j':

	    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,

                                      JS_ADDRESSOF_VA_LIST(ap))) {

		goto bad;

            }

	    /* NB: the formatter already updated sp, so we continue here. */

	    continue;

	}

	sp++;

    }



    /*

     * We may have overallocated stack due to a multi-character format code

     * handled by a JSArgumentFormatter.  Give back that stack space!

     */

    JS_ASSERT(sp <= argv + argc);

    if (sp < argv + argc) {

        /* Return slots not pushed to the current stack arena. */

	cx->stackPool.current->avail = (jsuword)sp;



        /* Reduce the count of slots the GC will scan in this stack segment. */

        sh = cx->stackHeaders;

        JS_ASSERT(JS_STACK_SEGMENT(sh) + sh->nslots == argv + argc);

        sh->nslots -= argc - (sp - argv);

    }

    return argv;



bad:

    js_FreeStack(cx, *markp);

    return NULL;

}



JS_PUBLIC_API(void)

JS_PopArguments(JSContext *cx, void *mark)

{

    CHECK_REQUEST(cx);

    js_FreeStack(cx, mark);

}



JS_PUBLIC_API(JSBool)

JS_AddArgumentFormatter(JSContext *cx, const char *format,

			JSArgumentFormatter formatter)

{

    size_t length;

    JSArgumentFormatMap **mpp, *map;



    length = strlen(format);

    mpp = &cx->argumentFormatMap;

    while ((map = *mpp) != NULL) {

	/* Insert before any shorter string to match before prefixes. */

	if (map->length < length)

	    break;

	if (map->length == length && !strcmp(map->format, format))

	    goto out;

	mpp = &map->next;

    }

    map = (JSArgumentFormatMap *) JS_malloc(cx, sizeof *map);

    if (!map)

	return JS_FALSE;

    map->format = format;

    map->length = length;

    map->next = *mpp;

    *mpp = map;

out:

    map->formatter = formatter;

    return JS_TRUE;

}



JS_PUBLIC_API(void)

JS_RemoveArgumentFormatter(JSContext *cx, const char *format)

{

    size_t length;

    JSArgumentFormatMap **mpp, *map;



    length = strlen(format);

    mpp = &cx->argumentFormatMap;

    while ((map = *mpp) != NULL) {

	if (map->length == length && !strcmp(map->format, format)) {

	    *mpp = map->next;

	    JS_free(cx, map);

	    return;

	}

	mpp = &map->next;

    }

}



JS_PUBLIC_API(JSBool)

JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp)

{

    JSBool ok, b;

    JSObject *obj;

    JSFunction *fun;

    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:

	fun = js_ValueToFunction(cx, &v, JS_FALSE);

	ok = (fun != NULL);

	if (ok)

	    *vp = OBJECT_TO_JSVAL(fun->object);

	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);

	    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, JS_FALSE);

}



JS_PUBLIC_API(JSFunction *)

JS_ValueToConstructor(JSContext *cx, jsval v)

{

    CHECK_REQUEST(cx);

    return js_ValueToFunction(cx, &v, JS_TRUE);

}



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)) {

        /* XXX JSVAL_IS_OBJECT(v) is true for null too! Can we change ECMA? */

	obj = JSVAL_TO_OBJECT(v);

	if (obj &&

	    (ops = obj->map->ops,

	     ops == &js_ObjectOps

	     ? (clasp = OBJ_GET_CLASS(cx, obj),

		clasp->call || clasp == &js_FunctionClass)

	     : ops->call != 0)) {

	    type = JSTYPE_FUNCTION;

	} else {

	    type = JSTYPE_OBJECT;

	}

    } 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)

{

    CHECK_REQUEST(cx);

    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;

    memset(rt, 0, sizeof(JSRuntime));

    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;

    js_SetupLocks(20, 32);      /* this is asymmetric with JS_ShutDown. */

    rt->rtLock = JS_NEW_LOCK();

    if (!rt->rtLock)

	goto bad;

    rt->stateChange = JS_NEW_CONDVAR(rt->rtLock);

    if (!rt->stateChange)

	goto bad;

    rt->setSlotLock = JS_NEW_LOCK();

    if (!rt->setSlotLock)

	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;

    JS_INIT_CLIST(&rt->contextList);

    JS_INIT_CLIST(&rt->trapList);

    JS_INIT_CLIST(&rt->watchPointList);

    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 (rt->contextList.next != &rt->contextList) {

        JSContext *cx, *iter = NULL;

        uintN cxcount = 0;

        while ((cx = js_ContextIterator(rt, &iter)) != NULL)

            cxcount++;

        fprintf(stderr,

"JS API usage error: %u contexts left in runtime upon JS_DestroyRuntime.\n",

                cxcount);

    }

#endif



    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->scopeSharingDone)

        JS_DESTROY_CONDVAR(rt->scopeSharingDone);

#endif

    free(rt);

}



JS_PUBLIC_API(void)

JS_ShutDown(void)

{

    JS_ArenaShutDown();

    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;     /* don't assume NULL puns as 0 */

                scope->ownercx = NULL;  /* NB: set last, after lock init */

                nshares++;

                JS_RUNTIME_METER(rt, sharedScopes);

            }

        }

        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, iterp);

}



JS_PUBLIC_API(JSVersion)

JS_GetVersion(JSContext *cx)

{

    return cx->version;

}



JS_PUBLIC_API(JSVersion)

JS_SetVersion(JSContext *cx, JSVersion version)

{

    JSVersion oldVersion;



    CHECK_REQUEST(cx);

    oldVersion = cx->version;

    if (version == oldVersion)

        return oldVersion;



    cx->version = version;



#if !JS_BUG_FALLIBLE_EQOPS

    if (cx->version == JSVERSION_1_2) {

	cx->jsop_eq = JSOP_NEW_EQ;

	cx->jsop_ne = JSOP_NEW_NE;

    } else {

	cx->jsop_eq = JSOP_EQ;

	cx->jsop_ne = JSOP_NE;

    }

#endif /* !JS_BUG_FALLIBLE_EQOPS */



    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_1_5,     "1.5"},

    {JSVERSION_DEFAULT, "default"},

    {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;

}



JS_PUBLIC_API(uint32)

JS_SetOptions(JSContext *cx, uint32 options)

{

    uint32 oldopts = cx->options;

    cx->options = options;

    return oldopts;

}



JS_PUBLIC_API(uint32)

JS_ToggleOptions(JSContext *cx, uint32 options)

{

    uint32 oldopts = cx->options;

    cx->options ^= options;

    return oldopts;

}



JS_PUBLIC_API(const char *)

JS_GetImplementationVersion(void)

{

    return "JavaScript-C 1.5 pre-release 3 2001-03-07";

}





JS_PUBLIC_API(JSObject *)

JS_GetGlobalObject(JSContext *cx)

{

    return cx->globalObject;

}



JS_PUBLIC_API(void)

JS_SetGlobalObject(JSContext *cx, JSObject *obj)

{

    cx->globalObject = obj;

}



static JSObject *

InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj)

{

    JSDHashTable *table;

    JSRuntime *rt;

    JSString *idstr;

    JSDHashEntryHdr *entry;

    JSObject *fun_proto, *obj_proto;



    /* If cx has no global object, use obj so prototypes can be found. */

    if (!cx->globalObject)

        cx->globalObject = obj;



    /* Record both Function and Object in cx->resolving, if we are resolving. */

    table = cx->resolving;

    if (table) {

        rt = cx->runtime;

        idstr = ATOM_TO_STRING(rt->atomState.FunctionAtom);

        entry = JS_DHashTableOperate(table, idstr, JS_DHASH_LOOKUP);

        if (JS_DHASH_ENTRY_IS_BUSY(entry))

            idstr = ATOM_TO_STRING(rt->atomState.ObjectAtom);



        entry = JS_DHashTableOperate(table, idstr, JS_DHASH_ADD);

        if (!entry) {

            JS_ReportOutOfMemory(cx);

            return NULL;

        }

        ((JSDHashEntryStub *)entry)->key = idstr;

    }



    /* Initialize the function class first so constructors can be made. */

    fun_proto = js_InitFunctionClass(cx, obj);

    if (!fun_proto)

        return NULL;



    /* Initialize the object class next so Object.prototype works. */

    obj_proto = js_InitObjectClass(cx, obj);

    if (!obj_proto)

        return NULL;



    /* 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);



    /* If resolving, remove the other entry (Object or Function) from table. */

    if (table)

        JS_DHashTableRawRemove(table, entry);

    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, (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_ARGS_OBJECT

           js_InitArgumentsClass(cx, obj) &&

#endif

#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_FILE_OBJECT

           js_InitFileClass(cx, obj, JS_TRUE) &&

#endif

           js_InitDateClass(cx, obj);

}



#define ATOM_OFFSET(name)       offsetof(JSAtomState, name##Atom)

#define OFFSET_TO_ATOM(rt,off)  (*(JSAtom **)((char*)&(rt)->atomState + (off)))

#define TAG_ATOM_OFFSET(name)   ((const char *) ATOM_OFFSET(name))

#define TAG_CHAR_STRING(name)   name

#define UNTAG_ATOM_OFFSET(ptr)  ((size_t)(ptr))

#define UNTAG_CHAR_STRING(ptr)  ptr

#define IS_ATOM_OFFSET(ptr)     ((size_t)(ptr) < sizeof(JSAtomState))



/*

 * 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_ARGS_OBJECT

    {js_InitArgumentsClass,         ATOM_OFFSET(Arguments)},

#endif

#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

    {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;

    const char  *name;          /* tagged (const char *) or atom offset */

} JSStdName;



static JSAtom *

StdNameToAtom(JSContext *cx, const char *name)

{

    if (IS_ATOM_OFFSET(name))

        return OFFSET_TO_ATOM(cx->runtime, UNTAG_ATOM_OFFSET(name));

    name = UNTAG_CHAR_STRING(name);

    return js_Atomize(cx, name, strlen(name), 0);

}



static JSStdName standard_class_names[] = {

    /* ECMA requires that eval be a direct property of the global object. */

    {js_InitObjectClass,        TAG_ATOM_OFFSET(eval)},



    /* Global properties and functions defined by the Number class. */

    {js_InitNumberClass,        TAG_CHAR_STRING(js_NaN_str)},

    {js_InitNumberClass,        TAG_CHAR_STRING(js_Infinity_str)},

    {js_InitNumberClass,        TAG_CHAR_STRING(js_isNaN_str)},

    {js_InitNumberClass,        TAG_CHAR_STRING(js_isFinite_str)},

    {js_InitNumberClass,        TAG_CHAR_STRING(js_parseFloat_str)},

    {js_InitNumberClass,        TAG_CHAR_STRING(js_parseInt_str)},



    /* String global functions. */

#ifndef MOZILLA_CLIENT

    /* These two are predefined in a backward-compatible way by the DOM. */

    {js_InitStringClass,        TAG_CHAR_STRING(js_escape_str)},

    {js_InitStringClass,        TAG_CHAR_STRING(js_unescape_str)},

#endif

    {js_InitStringClass,        TAG_CHAR_STRING(js_decodeURI_str)},

    {js_InitStringClass,        TAG_CHAR_STRING(js_encodeURI_str)},

    {js_InitStringClass,        TAG_CHAR_STRING(js_decodeURIComponent_str)},

    {js_InitStringClass,        TAG_CHAR_STRING(js_encodeURIComponent_str)},

#if JS_HAS_UNEVAL

    {js_InitStringClass,        TAG_CHAR_STRING(js_uneval_str)},

#endif



    /* Exception constructors. */

#if JS_HAS_ERROR_EXCEPTIONS

    {js_InitExceptionClasses,   TAG_ATOM_OFFSET(Error)},

    {js_InitExceptionClasses,   TAG_CHAR_STRING(js_InternalError_str)},

    {js_InitExceptionClasses,   TAG_CHAR_STRING(js_EvalError_str)},

    {js_InitExceptionClasses,   TAG_CHAR_STRING(js_RangeError_str)},

    {js_InitExceptionClasses,   TAG_CHAR_STRING(js_ReferenceError_str)},

    {js_InitExceptionClasses,   TAG_CHAR_STRING(js_SyntaxError_str)},

    {js_InitExceptionClasses,   TAG_CHAR_STRING(js_TypeError_str)},

    {js_InitExceptionClasses,   TAG_CHAR_STRING(js_URIError_str)},

#endif



    {NULL,                      NULL}

};



static JSStdName object_prototype_names[] = {

    /* Object.prototype properties (global delegates to Object.prototype). */

    {js_InitObjectClass,        TAG_ATOM_OFFSET(proto)},

    {js_InitObjectClass,        TAG_ATOM_OFFSET(parent)},

    {js_InitObjectClass,        TAG_ATOM_OFFSET(count)},

#if JS_HAS_TOSOURCE

    {js_InitObjectClass,        TAG_ATOM_OFFSET(toSource)},

#endif

    {js_InitObjectClass,        TAG_ATOM_OFFSET(toString)},

    {js_InitObjectClass,        TAG_ATOM_OFFSET(toLocaleString)},

    {js_InitObjectClass,        TAG_ATOM_OFFSET(valueOf)},

#if JS_HAS_OBJ_WATCHPOINT

    {js_InitObjectClass,        TAG_CHAR_STRING(js_watch_str)},

    {js_InitObjectClass,        TAG_CHAR_STRING(js_unwatch_str)},

#endif

#if JS_HAS_NEW_OBJ_METHODS

    {js_InitObjectClass,        TAG_CHAR_STRING(js_hasOwnProperty_str)},

    {js_InitObjectClass,        TAG_CHAR_STRING(js_isPrototypeOf_str)},

    {js_InitObjectClass,        TAG_CHAR_STRING(js_propertyIsEnumerable_str)},

#endif

#if JS_HAS_GETTER_SETTER

    {js_InitObjectClass,        TAG_CHAR_STRING(js_defineGetter_str)},

    {js_InitObjectClass,        TAG_CHAR_STRING(js_defineSetter_str)},

#endif



    {NULL,                      NULL}

};



JS_PUBLIC_API(JSBool)

JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id,

                        JSBool *resolved)

{

    JSString *idstr;

    JSDHashTable *table;

    JSDHashEntryHdr *entry;

    JSRuntime *rt;

    JSAtom *atom;

    JSObjectOp init;

    uintN i;

    JSBool ok;



    CHECK_REQUEST(cx);

    *resolved = JS_FALSE;



    if (!JSVAL_IS_STRING(id))

        return JS_TRUE;

    idstr = JSVAL_TO_STRING(id);

    table = cx->resolving;

    if (table) {

        entry = JS_DHashTableOperate(table, idstr, JS_DHASH_LOOKUP);

        if (JS_DHASH_ENTRY_IS_BUSY(entry))

            return JS_TRUE;

    }

    rt = cx->runtime;



#if JS_HAS_UNDEFINED

    /* See if 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, (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].name);

            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].name);

                if (idstr == ATOM_TO_STRING(atom)) {

                    init = standard_class_names[i].init;

                    break;

                }

            }

        }

    }



    if (!init) {

        ok = JS_TRUE;

    } else {

        if (!table) {

            table = JS_NewDHashTable(JS_DHashGetStubOps(),

                                     NULL,

                                     sizeof(JSDHashEntryStub),

                                     JS_DHASH_MIN_SIZE);

            if (!table)

                goto outofmem;

            cx->resolving = table;

        }

        entry = JS_DHashTableOperate(table, idstr, JS_DHASH_ADD);

        if (!entry)

            goto outofmem;

        ((JSDHashEntryStub *)entry)->key = idstr;



        if (init(cx, obj))

            ok = *resolved = JS_TRUE;

        else

            ok = JS_FALSE;



        JS_DHashTableRawRemove(table, entry);

        if (table->entryCount == 0) {

            JS_DHashTableDestroy(table);

            cx->resolving = NULL;

        }

    }

    return ok;



outofmem:

    JS_ReportOutOfMemory(cx);

    return JS_FALSE;

}



static JSBool

HasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom, JSBool *ownp)

{

    JSObject *pobj;

    JSProperty *prop;



    if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop))

        return JS_FALSE;

    if (prop)

        OBJ_DROP_PROPERTY(cx, pobj, prop);

    *ownp = (pobj == obj && prop);

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj)

{

    JSRuntime *rt;

    JSAtom *atom;

    JSBool found;

    uintN i;



    CHECK_REQUEST(cx);

    rt = cx->runtime;



#if JS_HAS_UNDEFINED

    /* See if we need to bind 'undefined' and define it if so. */

    atom = rt->atomState.typeAtoms[JSTYPE_VOID];

    if (!HasOwnProperty(cx, obj, atom, &found))

        return JS_FALSE;

    if (!found &&

        !OBJ_DEFINE_PROPERTY(cx, obj, (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 (!HasOwnProperty(cx, obj, atom, &found))

            return JS_FALSE;

        if (!found && !standard_class_atoms[i].init(cx, obj))

            return JS_FALSE;

    }



    return JS_TRUE;

}



#undef ATOM_OFFSET

#undef OFFSET_TO_ATOM

#undef TAG_ATOM_OFFSET

#undef TAG_CHAR_STRING

#undef UNTAG_ATOM_OFFSET

#undef UNTAG_CHAR_STRING

#undef IS_ATOM_OFFSET



JS_PUBLIC_API(JSObject *)

JS_GetScopeChain(JSContext *cx)

{

    CHECK_REQUEST(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)

{

    char *p = (char *) JS_malloc(cx, strlen(s) + 1);

    if (!p)

	return NULL;

    return strcpy(p, s);

}



JS_PUBLIC_API(jsdouble *)

JS_NewDouble(JSContext *cx, jsdouble d)

{

    CHECK_REQUEST(cx);

    return js_NewDouble(cx, d);

}



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);

}



JS_PUBLIC_API(JSBool)

JS_AddRoot(JSContext *cx, void *rp)

{

    CHECK_REQUEST(cx);

    return js_AddRoot(cx, rp, NULL);

}



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);

}



#ifdef DEBUG



#include "jshash.h" /* Added by JSIFY */



typedef struct NamedRootDumpArgs {

    void (*dump)(const char *name, void *rp, void *data);

    void *data;

} NamedRootDumpArgs;



JS_STATIC_DLL_CALLBACK(intN)

js_named_root_dumper(JSHashEntry *he, intN i, void *arg)

{

    NamedRootDumpArgs *args = (NamedRootDumpArgs *) arg;



    if (he->value)

	args->dump((char *) he->value, (void *)he->key, args->data);

    return HT_ENUMERATE_NEXT;

}



#if XP_MAC //AJFMOD

#pragma export on

#endif



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_HashTableEnumerateEntries(rt->gcRootsHash, js_named_root_dumper, &args);

}



#if XP_MAC //AJFMOD

#pragma export off

#endif



#endif /* DEBUG */



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_UnlockGCThing(JSContext *cx, void *thing)

{

    JSBool ok;



    CHECK_REQUEST(cx);

    ok = js_UnlockGCThing(cx, thing);

    if (!ok)

	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_UNLOCK);

    return ok;

}



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)

{

    if (cx->stackPool.current == &cx->stackPool.first)

	JS_FinishArenaPool(&cx->stackPool);

    JS_FinishArenaPool(&cx->codePool);

    JS_FinishArenaPool(&cx->tempPool);

    js_ForceGC(cx);

}



JS_PUBLIC_API(void)

JS_MaybeGC(JSContext *cx)

{

    JSRuntime *rt;

    uint32 bytes, lastBytes;



    rt = cx->runtime;

    bytes = rt->gcBytes;

    lastBytes = rt->gcLastBytes;

    if ((bytes > 8192 && bytes > lastBytes + lastBytes / 2) ||

        rt->gcMallocBytes > rt->gcMaxBytes) {

        /*

         * 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);

    }

}



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(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_AllocGCThing(cx, (uintN) type);

    if (!str)

        return NULL;

    str->length = length;

    str->chars = chars;

    return str;

}



/************************************************************************/



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 = (jsid)atom;

    }

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_IdToValue(JSContext *cx, jsid id, jsval *vp)

{

    CHECK_REQUEST(cx);

    *vp = js_IdToValue(id);

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    CHECK_REQUEST(cx);

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_EnumerateStub(JSContext *cx, JSObject *obj)

{

    CHECK_REQUEST(cx);

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id)

{

    CHECK_REQUEST(cx);

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp)

{

    CHECK_REQUEST(cx);

#if JS_BUG_EAGER_TOSTRING

    if (type == JSTYPE_STRING)

	return JS_TRUE;

#endif

    return js_TryValueOf(cx, obj, type, vp);

}



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;

    JSBool named;

    JSFunction *fun;

    jsval junk;



    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;



    if (!constructor) {

	/* Lacking a constructor, name the prototype (e.g., Math). */

	named = OBJ_DEFINE_PROPERTY(cx, obj, (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;



	/* Connect constructor and prototype by named properties. */

	ctor = fun->object;

	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;

    }

    return proto;



bad:

    if (named)

	(void) OBJ_DELETE_PROPERTY(cx, obj, (jsid)atom, &junk);

    cx->newborn[GCX_OBJECT] = NULL;

    return NULL;

}



#ifdef JS_THREADSAFE

JS_PUBLIC_API(JSClass *)

JS_GetClass(JSContext *cx, JSObject *obj)

{

    CHECK_REQUEST(cx);

    return OBJ_GET_CLASS(cx, obj);

}

#else

JS_PUBLIC_API(JSClass *)

JS_GetClass(JSObject *obj)

{

    CHECK_REQUEST(cx);

    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], JS_FALSE);

	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(void *)

JS_GetPrivate(JSContext *cx, JSObject *obj)

{

    jsval v;



    CHECK_REQUEST(cx);

    JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE);

    v = OBJ_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)

{

    CHECK_REQUEST(cx);

    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)

{

    CHECK_REQUEST(cx);

    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(OBJ_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;



    CHECK_REQUEST(cx);

    parent = JSVAL_TO_OBJECT(OBJ_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,

			  (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(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(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);

}



static JSBool

DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value,

	       JSPropertyOp getter, JSPropertyOp setter, uintN attrs,

	       JSProperty **propp)

{

    jsid id;

    JSAtom *atom;



    if (attrs & JSPROP_INDEX) {

	id = INT_TO_JSVAL((jsint)name);

	atom = NULL;

        attrs &= ~JSPROP_INDEX;

    } else {

	atom = js_Atomize(cx, name, strlen(name), 0);

	if (!atom)

	    return JS_FALSE;

	id = (jsid)atom;

    }

    return OBJ_DEFINE_PROPERTY(cx, obj, id, value, getter, setter, attrs,

			       propp);

}



#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,

		 JSProperty **propp)

{

    JSAtom *atom;



    atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name,namelen), 0);

    if (!atom)

	return JS_FALSE;

    return OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, value, getter, setter,

			       attrs, propp);

}



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,

			NULL)) {

	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, NULL);

	if (!ok)

	    break;

    }

    return ok;

}



JS_PUBLIC_API(JSBool)

JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps)

{

    JSBool ok;

    JSProperty *prop;

    JSScopeProperty *sprop;



    CHECK_REQUEST(cx);

    for (ok = JS_TRUE; ps->name; ps++) {

	ok = DefineProperty(cx, obj, ps->name, JSVAL_VOID,

			    ps->getter, ps->setter, ps->flags,

			    &prop);

	if (!ok)

	    break;

	if (prop) {

	    if (OBJ_IS_NATIVE(obj)) {

		sprop = (JSScopeProperty *)prop;

#ifdef JS_DOUBLE_HASHING

                sprop->attrs |= JSPROP_INDEX;

                sprop->tinyid = ps->tinyid;

#else

		sprop->id = INT_TO_JSVAL(ps->tinyid);

#endif

	    }

	    OBJ_DROP_PROPERTY(cx, obj, prop);

	}

    }

    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, NULL);

}



JS_PUBLIC_API(JSBool)

JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name,

			    int8 tinyid, jsval value,

			    JSPropertyOp getter, JSPropertyOp setter,

			    uintN attrs)

{

    JSBool ok;

    JSProperty *prop;

    JSScopeProperty *sprop;



    CHECK_REQUEST(cx);

    ok = DefineProperty(cx, obj, name, value, getter, setter, attrs, &prop);

    if (ok && prop) {

	if (OBJ_IS_NATIVE(obj)) {

	    sprop = (JSScopeProperty *)prop;

#ifdef JS_DOUBLE_HASHING

            sprop->attrs |= JSPROP_INDEX;

            sprop->tinyid = tinyid;

#else

	    sprop->id = INT_TO_JSVAL(tinyid);

#endif

	}

	OBJ_DROP_PROPERTY(cx, obj, prop);

    }

    return ok;

}



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, (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, (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;

    JSScope *scope;

    JSBool ok;



    CHECK_REQUEST(cx);

    /* XXXbe push this into jsobj.c or jsscope.c */

    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(obj2)) {

	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 {

	scope = OBJ_SCOPE(obj);

	ok = (scope->ops->add(cx, scope, (jsid)atom, (JSScopeProperty *)prop)

	      != NULL);

    }

    OBJ_DROP_PROPERTY(cx, obj, prop);

    return ok;

}



/* DREAMWEAVER jschang, Adobe - (formerly JS_AliasElement)

 * JS_AliasElementToProperty assigns the array element so that it is an

 * alias to the named property.  I changed the name so that it is more

 * descriptive and because I needed to add the function

 * JS_AliasPropertyToElement.

 * 

 * HACK snewman 3/22/01: this seems to be a function we added back in the JS

 * 1.2 days based on JS_AliasProperty -- I haven't checked with Jeff, but

 * the code looks similar to JS_AliasProperty.  It had been copied without

 * modification into the JS1.5rc2 code.  While updating to JS1.5rc3, I

 * looked to see if JS_AliasProperty had changed since JS1.2, and indeed

 * it has -- this code:

 * 

 *     JS_ReportError(cx, "can't alias %s to %s in class %s",

 *               alias, name, OBJ_GET_CLASS(cx, obj2)->name);

 * 

 * was changed to this:

 * 

 *     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS,

 *                alias, name, OBJ_GET_CLASS(cx, obj2)->name);

 * 

 * and this:

 * 

 *    scope = (JSScope *) obj->map;

 * 

 * was changed to this:

 * 

 *    scope = OBJ_SCOPE(obj);

 * 

 * I've applied the scope change here, and in JS_AliasPropertyToElement

 * (below).  Probably the ReportError change should be made as well, but

 * this would require some research, and I doubt that it's important.

 * (Famous last words...)

 */

JS_PUBLIC_API(JSBool)

JS_AliasElementToProperty(JSContext *cx, JSObject *obj, const char *name, jsint alias)

{

    JSObject *obj2;

    JSProperty *prop;

    JSScope *scope;

    JSBool ok;



    CHECK_REQUEST(cx);

    /* XXXbe push this into jsobj.c or jsscope.c */

    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(obj2)) {

	OBJ_DROP_PROPERTY(cx, obj2, prop);

	JS_ReportError(cx, "can't alias array element %ld to %s in class %s",

		       (long)alias, name, OBJ_GET_CLASS(cx, obj2)->name);

	return JS_FALSE;

    }

    scope = OBJ_SCOPE(obj);

    ok = (scope->ops->add(cx, scope, INT_TO_JSVAL(alias),

			  (JSScopeProperty *)prop)

	  != NULL);

    OBJ_DROP_PROPERTY(cx, obj, prop);

    return ok;

}



/* DREAMWEAVER

 * jschang, Adobe - (new function added 11/12/99 for OptionsArray setter function) 

 * JS_AliasPropertyToElement assigns the named property so that it is an 

 * alias to the specified array element. 

 * 

 * snewman 3/22/01: see comments for JS_AliasElementToProperty (above).

 */

JS_PUBLIC_API(JSBool)

JS_AliasPropertyToElement(JSContext *cx, JSObject *obj, jsint index, const char *alias)

{

    JSObject *obj2;

    JSProperty *prop;

    JSScope *scope;

    JSBool ok;

    JSAtom *atom;



    CHECK_REQUEST(cx);

    /* XXXbe push this into jsobj.c or jsscope.c */

    if (!OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSVAL(index), &obj2, &prop))

	return JS_FALSE;

    if (!prop) {

	JS_ReportError(cx, "array element %ld is not defined", (long)index);

	return JS_FALSE;

    }

    if (obj2 != obj || !OBJ_IS_NATIVE(obj2)) {

	OBJ_DROP_PROPERTY(cx, obj2, prop);

	JS_ReportError(cx, "can't alias %s to array element %ld in class %s",

		       alias, (long)index, OBJ_GET_CLASS(cx, obj2)->name);

	return JS_FALSE;

    }

    scope = OBJ_SCOPE(obj);

    atom = js_Atomize(cx, alias, strlen(alias), 0);

    if (!atom) {

	ok = JS_FALSE;

    } else {

	scope = OBJ_SCOPE(obj);

	ok = (scope->ops->add(cx, scope, (jsid)atom, (JSScopeProperty *)prop)

	      != 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))

               ? 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)

{

    JSObject *obj2;

    JSProperty *prop;

    JSBool ok;



    if (!atom)

	return JS_FALSE;

    if (!OBJ_LOOKUP_PROPERTY(cx, obj, (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_GET_ATTRIBUTES(cx, obj, (jsid)atom, prop, attrsp);

    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, (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, (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);

}



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_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_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, (jsid)atom, vp);

}



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, (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, (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, NULL);

}



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);

}



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)

{

    JSBool ok;

    JSProperty *prop;

    JSScopeProperty *sprop;



    CHECK_REQUEST(cx);

    ok = DefineUCProperty(cx, obj, name, namelen, value, getter, setter, attrs,

			  &prop);

    if (ok && prop) {

	if (OBJ_IS_NATIVE(obj)) {

	    sprop = (JSScopeProperty *)prop;

#ifdef JS_DOUBLE_HASHING

            sprop->attrs |= JSPROP_INDEX;

            sprop->tinyid = tinyid;

#else

	    sprop->id = INT_TO_JSVAL(tinyid);

#endif

	}

	OBJ_DROP_PROPERTY(cx, obj, 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, (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, (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, (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)

{

    CHECK_REQUEST(cx);

    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_JSVAL(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;

    JSScope *scope;

    JSBool ok;



    CHECK_REQUEST(cx);

    /* XXXbe push this into jsobj.c or jsscope.c */

    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(obj2)) {

	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;

    }

    scope = OBJ_SCOPE(obj);

    ok = (scope->ops->add(cx, scope, INT_TO_JSVAL(alias),

			  (JSScopeProperty *)prop)

	  != NULL);

    OBJ_DROP_PROPERTY(cx, obj, 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_JSVAL(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_JSVAL(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_JSVAL(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_JSVAL(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];

    while (1) {

	if (i == ida->length) {

	    /* Grow length by factor of 1.5 instead of doubling. */

	    jsint newlen = ida->length + (((jsuint)ida->length + 1) >> 1);

	    ida = js_GrowIdArray(cx, ida, newlen);

	    if (!ida)

		goto error;

	    vector = &ida->vector[0];

	}



	if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &id))

	    goto error;



	/* No more jsid's to enumerate ? */

	if (iter_state == JSVAL_NULL)

	    break;

	vector[i++] = id;

    }

    ida->length = i;

    return ida;



error:

    if (iter_state != JSVAL_NULL)

	OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);

    if (ida)

	JS_DestroyIdArray(cx, ida);

    return NULL;

}



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(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(JSBool)

JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs)

{

    JSFunction *fun;



    CHECK_REQUEST(cx);

    for (; fs->name; fs++) {

	fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs,

				fs->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);

}



static JSScript *

CompileTokenStream(JSContext *cx, JSObject *obj, JSTokenStream *ts,

		   void *tempMark, JSBool *eofp)

{

    JSBool eof;

    JSCodeGenerator cg;

    JSScript *script;



    CHECK_REQUEST(cx);

    eof = JS_FALSE;

    if (!js_InitCodeGenerator(cx, &cg, ts->filename, ts->lineno,

			      ts->principals)) {

	script = NULL;

	goto out;

    }

    if (!js_CompileTokenStream(cx, obj, ts, &cg)) {

	script = NULL;

        eof = (ts->flags & TSF_EOF) != 0;

	goto out;

    }

    script = js_NewScriptFromCG(cx, &cg, NULL);

out:

    if (eofp)

        *eofp = eof;

    if (!js_CloseTokenStream(cx, ts)) {

        if (script)

	    js_DestroyScript(cx, script);

	script = NULL;

    }

    cg.tempMark = tempMark;

    js_FinishCodeGenerator(cx, &cg);

    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);

}



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;



    CHECK_REQUEST(cx);

    mark = JS_ARENA_MARK(&cx->tempPool);

    ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals);

    if (!ts)

	return NULL;

    return CompileTokenStream(cx, obj, ts, mark, NULL);

}



extern JS_PUBLIC_API(JSBool)

JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj,

                          const char *bytes, size_t length)

{

    jschar *chars;

    JSScript *script;

    void *mark;

    JSTokenStream *ts;

    JSErrorReporter older;

    JSBool hitEOF, result;

    JSExceptionState *exnState;



    CHECK_REQUEST(cx);

    mark = JS_ARENA_MARK(&cx->tempPool);

    chars = js_InflateString(cx, bytes, length);

    if (!chars)

	return JS_TRUE;

    exnState = JS_SaveExceptionState(cx);

    ts = js_NewTokenStream(cx, chars, length, NULL, 0, NULL);

    if (!ts) {

        result = JS_TRUE;

        goto out;

    }



    older = JS_SetErrorReporter(cx, NULL);

    script = CompileTokenStream(cx, obj, ts, mark, &hitEOF);

    JS_SetErrorReporter(cx, older);



    if (script == NULL) {

        /*

         * We ran into an error, but it was because we ran out of source,

         * and not for some other reason.  For this case (and this case

         * only) we return false, so the calling function knows to try to

         * collect more source.

         */

        result = hitEOF ? JS_FALSE : JS_TRUE;

    } else {

        result = JS_TRUE;

        js_DestroyScript(cx, script);

    }



out:

    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;



    CHECK_REQUEST(cx);

    mark = JS_ARENA_MARK(&cx->tempPool);

    ts = js_NewFileTokenStream(cx, filename, stdin);

    if (!ts)

	return NULL;

    return CompileTokenStream(cx, obj, ts, mark, NULL);

}



JS_PUBLIC_API(JSScript *)

JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename,

                     FILE *fh)

{

    return JS_CompileFileHandleForPrincipals(cx, obj, filename, fh, NULL);

}



JS_PUBLIC_API(JSScript *)

JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj,

                                  const char *filename, FILE *fh,

                                  JSPrincipals *principals)

{

    void *mark;

    JSTokenStream *ts;



    CHECK_REQUEST(cx);

    mark = JS_ARENA_MARK(&cx->tempPool);

    ts = js_NewFileTokenStream(cx, NULL, fh);

    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);

    }

    return CompileTokenStream(cx, obj, ts, mark, NULL);

}



JS_PUBLIC_API(JSObject *)

JS_NewScriptObject(JSContext *cx, JSScript *script)

{

    JSObject *obj;



    CHECK_REQUEST(cx);

    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(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;

    JSScopeProperty *sprop;



    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_DefineProperty(cx, fun->object, (jsid)argAtom,

				   JSVAL_VOID, js_GetArgument, js_SetArgument,

				   JSPROP_ENUMERATE|JSPROP_PERMANENT,

				   (JSProperty **)&sprop)) {

		break;

	    }

	    JS_ASSERT(sprop);

	    sprop->id = INT_TO_JSVAL(i);

	    OBJ_DROP_PROPERTY(cx, fun->object, (JSProperty *)sprop);

	}

	if (i < nargs) {

	    fun = NULL;

	    goto out;

	}

    }

    if (!js_CompileFunctionBody(cx, ts, fun)) {

	fun = NULL;

        goto out;

    }

    if (funAtom) {

        if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)funAtom,

                                 OBJECT_TO_JSVAL(fun->object),

                                 NULL, NULL, 0, NULL)) {

            return NULL;

        }

    }

out:

    if (ts)

	js_CloseTokenStream(cx, ts);

    JS_ARENA_RELEASE(&cx->tempPool, mark);

    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)

{

    CHECK_REQUEST(cx);

    if (!js_Execute(cx, obj, script, NULL, 0, rval)) {

#if JS_HAS_EXCEPTIONS

        js_ReportUncaughtException(cx);

#endif

        return JS_FALSE;

    }

    return JS_TRUE;

}



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)

{

    JSScript *script;

    JSBool ok;



    CHECK_REQUEST(cx);

    script = JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length,

					     filename, lineno);

    if (!script)

	return JS_FALSE;

    ok = js_Execute(cx, obj, script, NULL, 0, rval);

#if JS_HAS_EXCEPTIONS

    if (!ok)

        js_ReportUncaughtException(cx);

#endif

    JS_DestroyScript(cx, script);

    return ok;

}



JS_PUBLIC_API(JSBool)

JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc,

		jsval *argv, jsval *rval)

{

    CHECK_REQUEST(cx);

    if (!js_InternalCall(cx, obj, OBJECT_TO_JSVAL(fun->object), argc, argv,

			 rval)) {

#if JS_HAS_EXCEPTIONS

        js_ReportUncaughtException(cx);

#endif

        return JS_FALSE;

    }

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc,

		    jsval *argv, jsval *rval)

{

    jsval fval;



    CHECK_REQUEST(cx);

    if (!JS_GetProperty(cx, obj, name, &fval))

	return JS_FALSE;

    if (!js_InternalCall(cx, obj, fval, argc, argv, rval)) {

#if JS_HAS_EXCEPTIONS

        js_ReportUncaughtException(cx);

#endif

        return JS_FALSE;

    }

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc,

		     jsval *argv, jsval *rval)

{

    CHECK_REQUEST(cx);

    if (!js_InternalCall(cx, obj, fval, argc, argv, rval)) {

#if JS_HAS_EXCEPTIONS

        js_ReportUncaughtException(cx);

#endif

        return JS_FALSE;

    }

    return JS_TRUE;

}



JS_PUBLIC_API(JSBranchCallback)

JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb)

{

    JSBranchCallback oldcb;



    CHECK_REQUEST(cx);

    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)

{

    CHECK_REQUEST(cx);

    return cx->fp && cx->fp->constructing;

}



JS_FRIEND_API(JSBool)

JS_IsAssigning(JSContext *cx)

{

    JSStackFrame *fp;

    jsbytecode *pc;



    for (fp = cx->fp; fp && !fp->script; fp = fp->down)

        ;

    if (!fp || !(pc = fp->pc))

	return JS_FALSE;

    return (js_CodeSpec[*pc].format & JOF_SET) != 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 = js_GetStringBytes(str);

    return bytes ? bytes : "";

}



JS_PUBLIC_API(jschar *)

JS_GetStringChars(JSString *str)

{

    return str->chars;

}



JS_PUBLIC_API(size_t)

JS_GetStringLength(JSString *str)

{

    return str->length;

}



JS_PUBLIC_API(intN)

JS_CompareStrings(JSString *str1, JSString *str2)

{

    return js_CompareStrings(str1, str2);

}



/************************************************************************/



JS_PUBLIC_API(void)

JS_ReportError(JSContext *cx, const char *format, ...)

{

    va_list ap;



    CHECK_REQUEST(cx);

    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;



    CHECK_REQUEST(cx);

    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;



    CHECK_REQUEST(cx);

    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;



    CHECK_REQUEST(cx);

    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;



    CHECK_REQUEST(cx);

    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;



    CHECK_REQUEST(cx);

    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;



    CHECK_REQUEST(cx);

    /* 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)

{

    CHECK_REQUEST(cx);

#if JS_HAS_EXCEPTIONS

    return (JSBool) cx->throwing;

#else

    return JS_FALSE;

#endif

}



JS_PUBLIC_API(JSBool)

JS_GetPendingException(JSContext *cx, jsval *vp)

{

    CHECK_REQUEST(cx);

#if JS_HAS_EXCEPTIONS

    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)

{

    CHECK_REQUEST(cx);

#if JS_HAS_EXCEPTIONS

    cx->throwing = JS_FALSE;

#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_EXCEPTIONS

    CHECK_REQUEST(cx);

    return js_ErrorFromException(cx, v);

#else

    return NULL;

#endif

}



#ifdef JS_THREADSAFE

JS_PUBLIC_API(intN)

JS_GetContextThread(JSContext *cx)

{

    return cx->thread;

}



JS_PUBLIC_API(intN)

JS_SetContextThread(JSContext *cx)

{

    intN old = cx->thread;

    cx->thread = js_CurrentThreadId();

    return old;

}



JS_PUBLIC_API(intN)

JS_ClearContextThread(JSContext *cx)

{

    intN old = cx->thread;

    cx->thread = 0;

    return old;

}

#endif



/************************************************************************/



// DREAMWEAVER: DaveG - we're building a static lib, so don't include this defns

#if 0



	#ifdef XP_PC

	#if defined(XP_OS2)

	/*DSR031297 - the OS/2 equiv is dll_InitTerm, but I don't see the need for it*/

	#else

	#include <windows.h>

	/*

	 * 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_OS2 */

	#endif /* XP_PC */



#endif 

**** End of jsapi.c ****

 

**** Start of jsapi.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsapi_h___

#define jsapi_h___

/*

 * JavaScript API.

 */

#include <stddef.h>

#include <stdio.h>

#include "jspubtd.h"



JS_BEGIN_EXTERN_C



/*

 * Type tags stored in the low bits of a jsval.

 */

#define JSVAL_OBJECT            0x0     /* untagged reference to object */

#define JSVAL_INT               0x1     /* tagged 31-bit integer value */

#define JSVAL_DOUBLE            0x2     /* tagged reference to double */

#define JSVAL_STRING            0x4     /* tagged reference to string */

#define JSVAL_BOOLEAN           0x6     /* tagged boolean value */



/* Type tag bitfield length and derived macros. */

#define JSVAL_TAGBITS           3

#define JSVAL_TAGMASK           JS_BITMASK(JSVAL_TAGBITS)

#define JSVAL_TAG(v)            ((v) & JSVAL_TAGMASK)

#define JSVAL_SETTAG(v,t)       ((v) | (t))

#define JSVAL_CLRTAG(v)         ((v) & ~(jsval)JSVAL_TAGMASK)

#define JSVAL_ALIGN             JS_BIT(JSVAL_TAGBITS)



/* Predicates for type testing. */

#define JSVAL_IS_OBJECT(v)      (JSVAL_TAG(v) == JSVAL_OBJECT)

#define JSVAL_IS_NUMBER(v)      (JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v))

#define JSVAL_IS_INT(v)         (((v) & JSVAL_INT) && (v) != JSVAL_VOID)

#define JSVAL_IS_DOUBLE(v)      (JSVAL_TAG(v) == JSVAL_DOUBLE)

#define JSVAL_IS_STRING(v)      (JSVAL_TAG(v) == JSVAL_STRING)

#define JSVAL_IS_BOOLEAN(v)     (JSVAL_TAG(v) == JSVAL_BOOLEAN)

#define JSVAL_IS_NULL(v)        ((v) == JSVAL_NULL)

#define JSVAL_IS_VOID(v)        ((v) == JSVAL_VOID)

#define JSVAL_IS_PRIMITIVE(v)   (!JSVAL_IS_OBJECT(v) || JSVAL_IS_NULL(v))



/* Objects, strings, and doubles are GC'ed. */

#define JSVAL_IS_GCTHING(v)     (!((v) & JSVAL_INT) && !JSVAL_IS_BOOLEAN(v))

#define JSVAL_TO_GCTHING(v)     ((void *)JSVAL_CLRTAG(v))

#define JSVAL_TO_OBJECT(v)      ((JSObject *)JSVAL_TO_GCTHING(v))

#define JSVAL_TO_DOUBLE(v)      ((jsdouble *)JSVAL_TO_GCTHING(v))

#define JSVAL_TO_STRING(v)      ((JSString *)JSVAL_TO_GCTHING(v))

#define OBJECT_TO_JSVAL(obj)    ((jsval)(obj))

#define DOUBLE_TO_JSVAL(dp)     JSVAL_SETTAG((jsval)(dp), JSVAL_DOUBLE)

#define STRING_TO_JSVAL(str)    JSVAL_SETTAG((jsval)(str), JSVAL_STRING)



/* Lock and unlock the GC thing held by a jsval. */

#define JSVAL_LOCK(cx,v)        (JSVAL_IS_GCTHING(v)                          \

                                 ? JS_LockGCThing(cx, JSVAL_TO_GCTHING(v))    \

                                 : JS_TRUE)

#define JSVAL_UNLOCK(cx,v)      (JSVAL_IS_GCTHING(v)                          \

                                 ? JS_UnlockGCThing(cx, JSVAL_TO_GCTHING(v))  \

                                 : JS_TRUE)



/* Domain limits for the jsval int type. */

#define JSVAL_INT_POW2(n)       ((jsval)1 << (n))

#define JSVAL_INT_MIN           ((jsval)1 - JSVAL_INT_POW2(30))

#define JSVAL_INT_MAX           (JSVAL_INT_POW2(30) - 1)

#define INT_FITS_IN_JSVAL(i)    ((jsuint)((i)+JSVAL_INT_MAX) <= 2*JSVAL_INT_MAX)

#define JSVAL_TO_INT(v)         ((jsint)(v) >> 1)

#define INT_TO_JSVAL(i)         (((jsval)(i) << 1) | JSVAL_INT)



/* Convert between boolean and jsval. */

#define JSVAL_TO_BOOLEAN(v)     ((JSBool)((v) >> JSVAL_TAGBITS))

#define BOOLEAN_TO_JSVAL(b)     JSVAL_SETTAG((jsval)(b) << JSVAL_TAGBITS,     \

                                             JSVAL_BOOLEAN)



/* A private data pointer (2-byte-aligned) can be stored as an int jsval. */

#define JSVAL_TO_PRIVATE(v)     ((void *)((v) & ~JSVAL_INT))

#define PRIVATE_TO_JSVAL(p)     ((jsval)(p) | JSVAL_INT)



/* Property attributes, set in JSPropertySpec and passed to API functions. */

#define JSPROP_ENUMERATE        0x01    /* property is visible to for/in loop */

#define JSPROP_READONLY         0x02    /* not settable: assignment is no-op */

#define JSPROP_PERMANENT        0x04    /* property cannot be deleted */

#define JSPROP_EXPORTED         0x08    /* property is exported from object */

#define JSPROP_GETTER           0x10    /* property holds getter function */

#define JSPROP_SETTER           0x20    /* property holds setter function */

#define JSPROP_SHARED           0x40    /* don't allocate a value slot for this

                                           property; don't copy the property on

                                           set of the same-named property in an

                                           object that delegates to a prototype

                                           containing this property */

#define JSPROP_INDEX            0x80    /* name is actually (jsint) index */



/* Function flags, set in JSFunctionSpec and passed to JS_NewFunction etc. */

#define JSFUN_GETTER            JSPROP_GETTER

#define JSFUN_SETTER            JSPROP_SETTER

#define JSFUN_BOUND_METHOD      0x40    /* bind this to fun->object's parent */

#define JSFUN_HEAVYWEIGHT       0x80    /* activation requires a Call object */

#define JSFUN_FLAGS_MASK        0xf0    /* overlay JSFUN_* attributes */



/*

 * Well-known JS values.  The extern'd variables are initialized when the

 * first JSContext is created by JS_NewContext (see below).

 */

#define JSVAL_VOID              INT_TO_JSVAL(0 - JSVAL_INT_POW2(30))

#define JSVAL_NULL              OBJECT_TO_JSVAL(0)

#define JSVAL_ZERO              INT_TO_JSVAL(0)

#define JSVAL_ONE               INT_TO_JSVAL(1)

#define JSVAL_FALSE             BOOLEAN_TO_JSVAL(JS_FALSE)

#define JSVAL_TRUE              BOOLEAN_TO_JSVAL(JS_TRUE)



/* Don't want to export data, so provide accessors for non-inline jsvals. */

extern JS_PUBLIC_API(jsval)

JS_GetNaNValue(JSContext *cx);



extern JS_PUBLIC_API(jsval)

JS_GetNegativeInfinityValue(JSContext *cx);



extern JS_PUBLIC_API(jsval)

JS_GetPositiveInfinityValue(JSContext *cx);



extern JS_PUBLIC_API(jsval)

JS_GetEmptyStringValue(JSContext *cx);



// DREAMWEAVER added this function

extern JS_PUBLIC_API(JSBool)

JS_DoubleIsNaN(jsdouble n);



/*

 * Format is a string of the following characters (spaces are insignificant),

 * specifying the tabulated type conversions:

 *

 *   b      JSBool          Boolean

 *   c      uint16/jschar   ECMA uint16, Unicode char

 *   i      int32           ECMA int32

 *   u      uint32          ECMA uint32

 *   j      int32           Rounded int32 (coordinate)

 *   d      jsdouble        IEEE double

 *   I      jsdouble        Integral IEEE double

 *   s      char *          C string

 *   S      JSString *      Unicode string, accessed by a JSString pointer

 *   W      jschar *        Unicode character vector, 0-terminated (W for wide)

 *   o      JSObject *      Object reference

 *   f      JSFunction *    Function private

 *   v      jsval           Argument value (no conversion)

 *   *      N/A             Skip this argument (no vararg)

 *   /      N/A             End of required arguments

 *

 * The variable argument list after format must consist of &b, &c, &s, e.g.,

 * where those variables have the types given above.  For the pointer types

 * char *, JSString *, and JSObject *, the pointed-at memory returned belongs

 * to the JS runtime, not to the calling native code.  The runtime promises

 * to keep this memory valid so long as argv refers to allocated stack space

 * (so long as the native function is active).

 *

 * Fewer arguments than format specifies may be passed only if there is a /

 * in format after the last required argument specifier and argc is at least

 * the number of required arguments.  More arguments than format specifies

 * may be passed without error; it is up to the caller to deal with trailing

 * unconverted arguments.

 */

extern JS_PUBLIC_API(JSBool)

JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format,

                    ...);



#ifdef va_start

extern JS_PUBLIC_API(JSBool)

JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv,

                      const char *format, va_list ap);

#endif



/*

 * Inverse of JS_ConvertArguments: scan format and convert trailing arguments

 * into jsvals, GC-rooted if necessary by the JS stack.  Return null on error,

 * and a pointer to the new argument vector on success.  Also return a stack

 * mark on success via *markp, in which case the caller must eventually clean

 * up by calling JS_PopArguments.

 *

 * Note that the number of actual arguments supplied is specified exclusively

 * by format, so there is no argc parameter.

 */

extern JS_PUBLIC_API(jsval *)

JS_PushArguments(JSContext *cx, void **markp, const char *format, ...);



#ifdef va_start

extern JS_PUBLIC_API(jsval *)

JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap);

#endif



extern JS_PUBLIC_API(void)

JS_PopArguments(JSContext *cx, void *mark);



#ifdef JS_ARGUMENT_FORMATTER_DEFINED



/*

 * Add and remove a format string handler for JS_{Convert,Push}Arguments{,VA}.

 * The handler function has this signature (see jspubtd.h):

 *

 *   JSBool MyArgumentFormatter(JSContext *cx, const char *format,

 *                              JSBool fromJS, jsval **vpp, va_list *app);

 *

 * It should return true on success, and return false after reporting an error

 * or detecting an already-reported error.

 *

 * For a given format string, for example "AA", the formatter is called from

 * JS_ConvertArgumentsVA like so:

 *

 *   formatter(cx, "AA...", JS_TRUE, &sp, &ap);

 *

 * sp points into the arguments array on the JS stack, while ap points into

 * the stdarg.h va_list on the C stack.  The JS_TRUE passed for fromJS tells

 * the formatter to convert zero or more jsvals at sp to zero or more C values

 * accessed via pointers-to-values at ap, updating both sp (via *vpp) and ap

 * (via *app) to point past the converted arguments and their result pointers

 * on the C stack.

 *

 * When called from JS_PushArgumentsVA, the formatter is invoked thus:

 *

 *   formatter(cx, "AA...", JS_FALSE, &sp, &ap);

 *

 * where JS_FALSE for fromJS means to wrap the C values at ap according to the

 * format specifier and store them at sp, updating ap and sp appropriately.

 *

 * The "..." after "AA" is the rest of the format string that was passed into

 * JS_{Convert,Push}Arguments{,VA}.  The actual format trailing substring used

 * in each Convert or PushArguments call is passed to the formatter, so that

 * one such function may implement several formats, in order to share code.

 *

 * Remove just forgets about any handler associated with format.  Add does not

 * copy format, it points at the string storage allocated by the caller, which

 * is typically a string constant.  If format is in dynamic storage, it is up

 * to the caller to keep the string alive until Remove is called.

 */

extern JS_PUBLIC_API(JSBool)

JS_AddArgumentFormatter(JSContext *cx, const char *format,

                        JSArgumentFormatter formatter);



extern JS_PUBLIC_API(void)

JS_RemoveArgumentFormatter(JSContext *cx, const char *format);



#endif /* JS_ARGUMENT_FORMATTER_DEFINED */



extern JS_PUBLIC_API(JSBool)

JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp);



extern JS_PUBLIC_API(JSBool)

JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp);



extern JS_PUBLIC_API(JSFunction *)

JS_ValueToFunction(JSContext *cx, jsval v);



extern JS_PUBLIC_API(JSFunction *)

JS_ValueToConstructor(JSContext *cx, jsval v);



extern JS_PUBLIC_API(JSString *)

JS_ValueToString(JSContext *cx, jsval v);



extern JS_PUBLIC_API(JSBool)

JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp);



/*

 * Convert a value to a number, then to an int32, according to the ECMA rules

 * for ToInt32.

 */

extern JS_PUBLIC_API(JSBool)

JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip);



/*

 * Convert a value to a number, then to a uint32, according to the ECMA rules

 * for ToUint32.

 */

extern JS_PUBLIC_API(JSBool)

JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip);



/*

 * Convert a value to a number, then to an int32 if it fits by rounding to

 * nearest; but failing with an error report if the double is out of range

 * or unordered.

 */

extern JS_PUBLIC_API(JSBool)

JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip);



/*

 * ECMA ToUint16, for mapping a jsval to a Unicode point.

 */

extern JS_PUBLIC_API(JSBool)

JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip);



extern JS_PUBLIC_API(JSBool)

JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp);



extern JS_PUBLIC_API(JSType)

JS_TypeOfValue(JSContext *cx, jsval v);



extern JS_PUBLIC_API(const char *)

JS_GetTypeName(JSContext *cx, JSType type);



/************************************************************************/



/*

 * Initialization, locking, contexts, and memory allocation.

 */

#define JS_NewRuntime       JS_Init

#define JS_DestroyRuntime   JS_Finish

#define JS_LockRuntime      JS_Lock

#define JS_UnlockRuntime    JS_Unlock



extern JS_PUBLIC_API(JSRuntime *)

JS_NewRuntime(uint32 maxbytes);



extern JS_PUBLIC_API(void)

JS_DestroyRuntime(JSRuntime *rt);



extern JS_PUBLIC_API(void)

JS_ShutDown(void);



JS_PUBLIC_API(void *)

JS_GetRuntimePrivate(JSRuntime *rt);



JS_PUBLIC_API(void)

JS_SetRuntimePrivate(JSRuntime *rt, void *data);



#ifdef JS_THREADSAFE



extern JS_PUBLIC_API(void)

JS_BeginRequest(JSContext *cx);



extern JS_PUBLIC_API(void)

JS_EndRequest(JSContext *cx);



/* Yield to pending GC operations, regardless of request depth */

extern JS_PUBLIC_API(void)

JS_YieldRequest(JSContext *cx);



extern JS_PUBLIC_API(jsrefcount)

JS_SuspendRequest(JSContext *cx);



extern JS_PUBLIC_API(void)

JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth);



#endif /* JS_THREADSAFE */



extern JS_PUBLIC_API(void)

JS_Lock(JSRuntime *rt);



extern JS_PUBLIC_API(void)

JS_Unlock(JSRuntime *rt);



extern JS_PUBLIC_API(JSContext *)

JS_NewContext(JSRuntime *rt, size_t stackChunkSize);



extern JS_PUBLIC_API(void)

JS_DestroyContext(JSContext *cx);



extern JS_PUBLIC_API(void)

JS_DestroyContextNoGC(JSContext *cx);



extern JS_PUBLIC_API(void)

JS_DestroyContextMaybeGC(JSContext *cx);



extern JS_PUBLIC_API(void *)

JS_GetContextPrivate(JSContext *cx);



extern JS_PUBLIC_API(void)

JS_SetContextPrivate(JSContext *cx, void *data);



extern JS_PUBLIC_API(JSRuntime *)

JS_GetRuntime(JSContext *cx);



extern JS_PUBLIC_API(JSContext *)

JS_ContextIterator(JSRuntime *rt, JSContext **iterp);



extern JS_PUBLIC_API(JSVersion)

JS_GetVersion(JSContext *cx);



extern JS_PUBLIC_API(JSVersion)

JS_SetVersion(JSContext *cx, JSVersion version);



extern JS_PUBLIC_API(const char *)

JS_VersionToString(JSVersion version);



extern JS_PUBLIC_API(JSVersion)

JS_StringToVersion(const char *string);



/*

 * JS options are orthogonal to version, and may be freely composed with one

 * another as well as with version.

 *

 * JSOPTION_VAROBJFIX is recommended -- see the comments associated with the

 * prototypes for JS_ExecuteScript, JS_EvaluateScript, etc.

 */

#define JSOPTION_STRICT         JS_BIT(0)       /* warn on dubious practice */

#define JSOPTION_WERROR         JS_BIT(1)       /* convert warning to error */

#define JSOPTION_VAROBJFIX      JS_BIT(2)       /* make JS_EvaluateScript use

                                                   the last object on its 'obj'

                                                   param's scope chain as the

                                                   ECMA 'variables object' */



extern JS_PUBLIC_API(uint32)

JS_GetOptions(JSContext *cx);



extern JS_PUBLIC_API(uint32)

JS_SetOptions(JSContext *cx, uint32 options);



extern JS_PUBLIC_API(uint32)

JS_ToggleOptions(JSContext *cx, uint32 options);



extern JS_PUBLIC_API(const char *)

JS_GetImplementationVersion(void);



extern JS_PUBLIC_API(JSObject *)

JS_GetGlobalObject(JSContext *cx);



extern JS_PUBLIC_API(void)

JS_SetGlobalObject(JSContext *cx, JSObject *obj);



/*

 * Initialize standard JS class constructors, prototypes, and any top-level

 * functions and constants associated with the standard classes (e.g. isNaN

 * for Number).

 *

 * NB: This sets cx's global object to obj if it was null.

 */

extern JS_PUBLIC_API(JSBool)

JS_InitStandardClasses(JSContext *cx, JSObject *obj);



/*

 * Resolve id, which must contain either a string or an int, to a standard

 * class name in obj if possible, defining the class's constructor and/or

 * prototype and storing true in *resolved.  If id does not name a standard

 * class or a top-level property induced by initializing a standard class,

 * store false in *resolved and just return true.  Return false on error,

 * as usual for JSBool result-typed API entry points.

 *

 * This API can be called directly from a global object class's resolve op,

 * to define standard classes lazily.  The class's enumerate op should call

 * JS_EnumerateStandardClasses(cx, obj), to define eagerly during for..in

 * loops any classes not yet resolved lazily.

 */

extern JS_PUBLIC_API(JSBool)

JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id,

                        JSBool *resolved);



extern JS_PUBLIC_API(JSBool)

JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj);



extern JS_PUBLIC_API(JSObject *)

JS_GetScopeChain(JSContext *cx);



extern JS_PUBLIC_API(void *)

JS_malloc(JSContext *cx, size_t nbytes);



extern JS_PUBLIC_API(void *)

JS_realloc(JSContext *cx, void *p, size_t nbytes);



extern JS_PUBLIC_API(void)

JS_free(JSContext *cx, void *p);



extern JS_PUBLIC_API(char *)

JS_strdup(JSContext *cx, const char *s);



extern JS_PUBLIC_API(jsdouble *)

JS_NewDouble(JSContext *cx, jsdouble d);



extern JS_PUBLIC_API(JSBool)

JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval);



extern JS_PUBLIC_API(JSBool)

JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval);



/*

 * A JS GC root is a pointer to a JSObject *, JSString *, or jsdouble * that

 * itself points into the GC heap (more recently, we support this extension:

 * a root may be a pointer to a jsval v for which JSVAL_IS_GCTHING(v) is true).

 *

 * Therefore, you never pass JSObject *obj to JS_AddRoot(cx, obj).  You always

 * call JS_AddRoot(cx, &obj), passing obj by reference.  And later, before obj

 * or the structure it is embedded within goes out of scope or is freed, you

 * must call JS_RemoveRoot(cx, &obj).

 *

 * Also, use JS_AddNamedRoot(cx, &structPtr->memberObj, "structPtr->memberObj")

 * in preference to JS_AddRoot(cx, &structPtr->memberObj), in order to identify

 * roots by their source callsites.  This way, you can find the callsite while

 * debugging if you should fail to do JS_RemoveRoot(cx, &structPtr->memberObj)

 * before freeing structPtr's memory.

 */

extern JS_PUBLIC_API(JSBool)

JS_AddRoot(JSContext *cx, void *rp);



extern JS_PUBLIC_API(JSBool)

JS_RemoveRoot(JSContext *cx, void *rp);



extern JS_PUBLIC_API(JSBool)

JS_RemoveRootRT(JSRuntime *rt, void *rp);



extern JS_PUBLIC_API(JSBool)

JS_AddNamedRoot(JSContext *cx, void *rp, const char *name);



#ifdef DEBUG

extern JS_PUBLIC_API(void)

JS_DumpNamedRoots(JSRuntime *rt,

                  void (*dump)(const char *name, void *rp, void *data),

                  void *data);

#endif



extern JS_PUBLIC_API(JSBool)

JS_LockGCThing(JSContext *cx, void *thing);



extern JS_PUBLIC_API(JSBool)

JS_UnlockGCThing(JSContext *cx, void *thing);



/*

 * For implementors of JSObjectOps.mark, to mark a GC-thing reachable via a

 * property or other strong ref identified for debugging purposes by name.

 * The name argument's storage needs to live only as long as the call to

 * this routine.

 *

 * The final arg is used by GC_MARK_DEBUG code to build a ref path through

 * the GC's live thing graph.  Implementors of JSObjectOps.mark should pass

 * its final arg through to this function when marking all GC-things that are

 * directly reachable from the object being marked.

 *

 * See the JSMarkOp typedef in jspubtd.h, and the JSObjectOps struct below.

 */

extern JS_PUBLIC_API(void)

JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg);



extern JS_PUBLIC_API(void)

JS_GC(JSContext *cx);



extern JS_PUBLIC_API(void)

JS_MaybeGC(JSContext *cx);



extern JS_PUBLIC_API(JSGCCallback)

JS_SetGCCallback(JSContext *cx, JSGCCallback cb);



extern JS_PUBLIC_API(JSGCCallback)

JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb);



extern JS_PUBLIC_API(JSBool)

JS_IsAboutToBeFinalized(JSContext *cx, void *thing);



/*

 * Add an external string finalizer, one created by JS_NewExternalString (see

 * below) using a type-code returned from this function, and that understands

 * how to free or release the memory pointed at by JS_GetStringChars(str).

 *

 * Return a nonnegative type index if there is room for finalizer in the

 * global GC finalizers table, else return -1.  If the engine is compiled

 * JS_THREADSAFE and used in a multi-threaded environment, this function must

 * be invoked on the primordial thread only, at startup -- or else the entire

 * program must single-thread itself while loading a module that calls this

 * function.

 */

extern JS_PUBLIC_API(intN)

JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer);



/*

 * Remove finalizer from the global GC finalizers table, returning its type

 * code if found, -1 if not found.

 *

 * As with JS_AddExternalStringFinalizer, there is a threading restriction

 * if you compile the engine JS_THREADSAFE: this function may be called for a

 * given finalizer pointer on only one thread; different threads may call to

 * remove distinct finalizers safely.

 *

 * You must ensure that all strings with finalizer's type have been collected

 * before calling this function.  Otherwise, string data will be leaked by the

 * GC, for want of a finalizer to call.

 */

extern JS_PUBLIC_API(intN)

JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer);



/*

 * Create a new JSString whose chars member refers to external memory, i.e.,

 * memory requiring special, type-specific finalization.  The type code must

 * be a nonnegative return value from JS_AddExternalStringFinalizer.

 */

extern JS_PUBLIC_API(JSString *)

JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type);



/************************************************************************/



/*

 * Classes, objects, and properties.

 */

struct JSClass {

    const char          *name;

    uint32              flags;



    /* Mandatory non-null function pointer members. */

    JSPropertyOp        addProperty;

    JSPropertyOp        delProperty;

    JSPropertyOp        getProperty;

    JSPropertyOp        setProperty;

    JSEnumerateOp       enumerate;

    JSResolveOp         resolve;

    JSConvertOp         convert;

    JSFinalizeOp        finalize;



    /* Optionally non-null members start here. */

    JSGetObjectOps      getObjectOps;

    JSCheckAccessOp     checkAccess;

    JSNative            call;

    JSNative            construct;

    JSXDRObjectOp       xdrObject;

    JSHasInstanceOp     hasInstance;

    JSMarkOp            mark;

    jsword              spare;

};



#define JSCLASS_HAS_PRIVATE             0x01    /* objects have private slot */

#define JSCLASS_NEW_ENUMERATE           0x02    /* has JSNewEnumerateOp hook */

#define JSCLASS_NEW_RESOLVE             0x04    /* has JSNewResolveOp hook */

#define JSCLASS_PRIVATE_IS_NSISUPPORTS  0x08    /* private is (nsISupports *) */

#define JSCLASS_SHARE_ALL_PROPERTIES    0x10    /* all properties are SHARED */

#define JSCLASS_FW_SIMPLE_GET			0x20    /* class does simple js_GetProperty */

#define JSCLASS_FW_ENUM					0x40  



/* Initializer for unused members of statically initialized JSClass structs. */

#define JSCLASS_NO_OPTIONAL_MEMBERS     0,0,0,0,0,0,0,0



struct JSObjectOps {

    /* Mandatory non-null function pointer members. */

    JSNewObjectMapOp    newObjectMap;

    JSObjectMapOp       destroyObjectMap;

    JSLookupPropOp      lookupProperty;

    JSDefinePropOp      defineProperty;

    JSPropertyIdOp      getProperty;

    JSPropertyIdOp      setProperty;

    JSAttributesOp      getAttributes;

    JSAttributesOp      setAttributes;

    JSPropertyIdOp      deleteProperty;

    JSConvertOp         defaultValue;

    JSNewEnumerateOp    enumerate;

    JSCheckAccessIdOp   checkAccess;



    /* Optionally non-null members start here. */

    JSObjectOp          thisObject;

    JSPropertyRefOp     dropProperty;

    JSNative            call;

    JSNative            construct;

    JSXDRObjectOp       xdrObject;

    JSHasInstanceOp     hasInstance;

    JSSetObjectSlotOp   setProto;

    JSSetObjectSlotOp   setParent;

    JSMarkOp            mark;

    JSFinalizeOp        clear;

    jsword              spare1;

    jsword              spare2;

};



/*

 * Classes that expose JSObjectOps via a non-null getObjectOps class hook may

 * derive a property structure from this struct, return a pointer to it from

 * lookupProperty and defineProperty, and use the pointer to avoid rehashing

 * in getAttributes and setAttributes.

 *

 * The jsid type contains either an int jsval (see JSVAL_IS_INT above), or an

 * internal pointer that is opaque to users of this API, but which users may

 * convert from and to a jsval using JS_ValueToId and JS_IdToValue.

 */

struct JSProperty {

    jsid id;

};



struct JSIdArray {

    jsint length;

    jsid  vector[1];    /* actually, length jsid words */

};



extern JS_PUBLIC_API(void)

JS_DestroyIdArray(JSContext *cx, JSIdArray *ida);



extern JS_PUBLIC_API(JSBool)

JS_ValueToId(JSContext *cx, jsval v, jsid *idp);



extern JS_PUBLIC_API(JSBool)

JS_IdToValue(JSContext *cx, jsid id, jsval *vp);



#define JSRESOLVE_QUALIFIED     0x01    /* resolve a qualified property id */

#define JSRESOLVE_ASSIGNING     0x02    /* resolve on the left of assignment */



extern JS_PUBLIC_API(JSBool)

JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp);



extern JS_PUBLIC_API(JSBool)

JS_EnumerateStub(JSContext *cx, JSObject *obj);



extern JS_PUBLIC_API(JSBool)

JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id);



extern JS_PUBLIC_API(JSBool)

JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp);



extern JS_PUBLIC_API(void)

JS_FinalizeStub(JSContext *cx, JSObject *obj);



struct JSConstDoubleSpec {

    jsdouble        dval;

    const char      *name;

    uint8           flags;

    uint8           spare[3];

};



/*

 * To define an array element rather than a named property member, cast the

 * element's index to (const char *) and initialize name with it, and set the

 * JSPROP_INDEX bit in flags.

 */

struct JSPropertySpec {

    const char      *name;

    int8            tinyid;

    uint8           flags;

    JSPropertyOp    getter;

    JSPropertyOp    setter;

};



struct JSFunctionSpec {

    const char      *name;

    JSNative        call;

    uint8           nargs;

    uint8           flags;

    uint16          extra;      /* number of arg slots for local GC roots */

};



extern 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);



#ifdef JS_THREADSAFE

extern JS_PUBLIC_API(JSClass *)

JS_GetClass(JSContext *cx, JSObject *obj);



#define JS_GET_CLASS(cx,obj) JS_GetClass(cx, obj)

#else

extern JS_PUBLIC_API(JSClass *)

JS_GetClass(JSObject *obj);



#define JS_GET_CLASS(cx,obj) JS_GetClass(obj)

#endif



extern JS_PUBLIC_API(JSBool)

JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv);



extern JS_PUBLIC_API(void *)

JS_GetPrivate(JSContext *cx, JSObject *obj);



extern JS_PUBLIC_API(JSBool)

JS_SetPrivate(JSContext *cx, JSObject *obj, void *data);



extern JS_PUBLIC_API(void *)

JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp,

                      jsval *argv);



extern JS_PUBLIC_API(JSObject *)

JS_GetPrototype(JSContext *cx, JSObject *obj);



extern JS_PUBLIC_API(JSBool)

JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto);



extern JS_PUBLIC_API(JSObject *)

JS_GetParent(JSContext *cx, JSObject *obj);



extern JS_PUBLIC_API(JSBool)

JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent);



extern JS_PUBLIC_API(JSObject *)

JS_GetConstructor(JSContext *cx, JSObject *proto);



extern JS_PUBLIC_API(JSObject *)

JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent);



extern JS_PUBLIC_API(JSObject *)

JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,

                   JSObject *parent);



extern JS_PUBLIC_API(JSObject *)

JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp,

                JSObject *proto, uintN attrs);



extern JS_PUBLIC_API(JSBool)

JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds);



extern JS_PUBLIC_API(JSBool)

JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps);



extern JS_PUBLIC_API(JSBool)

JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value,

                  JSPropertyOp getter, JSPropertyOp setter, uintN attrs);



/*

 * Determine the attributes (JSPROP_* flags) of a property on a given object.

 *

 * If the object does not have a property by that name, *foundp will be

 * JS_FALSE and the value of *attrsp is undefined.

 */

extern JS_PUBLIC_API(JSBool)

JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name,

                         uintN *attrsp, JSBool *foundp);



/*

 * Set the attributes of a property on a given object.

 *

 * If the object does not have a property by that name, *foundp will be

 * JS_FALSE and nothing will be altered.

 */

extern JS_PUBLIC_API(JSBool)

JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name,

                         uintN attrs, JSBool *foundp);



extern JS_PUBLIC_API(JSBool)

JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name,

                            int8 tinyid, jsval value,

                            JSPropertyOp getter, JSPropertyOp setter,

                            uintN attrs);



extern JS_PUBLIC_API(JSBool)

JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name,

                 const char *alias);



extern JS_PUBLIC_API(JSBool)

JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp);



extern JS_PUBLIC_API(JSBool)

JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp);



extern JS_PUBLIC_API(JSBool)

JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp);



extern JS_PUBLIC_API(JSBool)

JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name);



extern JS_PUBLIC_API(JSBool)

JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name,

                   jsval *rval);



extern JS_PUBLIC_API(JSBool)

JS_DefineUCProperty(JSContext *cx, JSObject *obj,

                    const jschar *name, size_t namelen, jsval value,

                    JSPropertyOp getter, JSPropertyOp setter,

                    uintN attrs);



/*

 * Determine the attributes (JSPROP_* flags) of a property on a given object.

 *

 * If the object does not have a property by that name, *foundp will be

 * JS_FALSE and the value of *attrsp is undefined.

 */

extern JS_PUBLIC_API(JSBool)

JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj,

                           const jschar *name, size_t namelen,

                           uintN *attrsp, JSBool *foundp);



/*

 * Set the attributes of a property on a given object.

 *

 * If the object does not have a property by that name, *foundp will be

 * JS_FALSE and nothing will be altered.

 */

extern JS_PUBLIC_API(JSBool)

JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj,

                           const jschar *name, size_t namelen,

                           uintN attrs, JSBool *foundp);





extern 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);



extern JS_PUBLIC_API(JSBool)

JS_LookupUCProperty(JSContext *cx, JSObject *obj,

                    const jschar *name, size_t namelen,

                    jsval *vp);



extern JS_PUBLIC_API(JSBool)

JS_GetUCProperty(JSContext *cx, JSObject *obj,

                 const jschar *name, size_t namelen,

                 jsval *vp);



extern JS_PUBLIC_API(JSBool)

JS_SetUCProperty(JSContext *cx, JSObject *obj,

                 const jschar *name, size_t namelen,

                 jsval *vp);



extern JS_PUBLIC_API(JSBool)

JS_DeleteUCProperty2(JSContext *cx, JSObject *obj,

                     const jschar *name, size_t namelen,

                     jsval *rval);





// DREAMWEAVER: renamed parameter "vector" to "vector_arg" to avoid 

// a build error in files that include jsapi.h and also have "using

// namespace std" (or include a header file that does this).

extern JS_PUBLIC_API(JSObject *)

JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector_arg);



extern JS_PUBLIC_API(JSBool)

JS_IsArrayObject(JSContext *cx, JSObject *obj);



extern JS_PUBLIC_API(JSBool)

JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp);



extern JS_PUBLIC_API(JSBool)

JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length);



extern JS_PUBLIC_API(JSBool)

JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp);



extern JS_PUBLIC_API(JSBool)

JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value,

                 JSPropertyOp getter, JSPropertyOp setter, uintN attrs);



extern JS_PUBLIC_API(JSBool)

JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias);



// DREAMWEAVER added this function

extern JS_PUBLIC_API(JSBool)

JS_AliasElementToProperty(JSContext *cx, JSObject *obj, const char *name, jsint alias);



// DREAMWEAVER added this function

extern JS_PUBLIC_API(JSBool)

JS_AliasPropertyToElement(JSContext *cx, JSObject *obj, jsint index, const char *alias);



extern JS_PUBLIC_API(JSBool)

JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp);



extern JS_PUBLIC_API(JSBool)

JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp);



extern JS_PUBLIC_API(JSBool)

JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp);



extern JS_PUBLIC_API(JSBool)

JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index);



extern JS_PUBLIC_API(JSBool)

JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval);



extern JS_PUBLIC_API(void)

JS_ClearScope(JSContext *cx, JSObject *obj);



extern JS_PUBLIC_API(JSIdArray *)

JS_Enumerate(JSContext *cx, JSObject *obj);



extern JS_PUBLIC_API(JSBool)

JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,

               jsval *vp, uintN *attrsp);



/************************************************************************/



/*

 * Security protocol.

 */

typedef struct JSPrincipals {

    char *codebase;

    void *(*getPrincipalArray)(JSContext *cx, struct JSPrincipals *);

    JSBool (*globalPrivilegesEnabled)(JSContext *cx, struct JSPrincipals *);



    /* Don't call "destroy"; use reference counting macros below. */

    uintN refcount;

    void (*destroy)(JSContext *cx, struct JSPrincipals *);

} JSPrincipals;



#define JSPRINCIPALS_HOLD(cx, principals)               \

    ((principals)->refcount++)

#define JSPRINCIPALS_DROP(cx, principals)               \

    ((--((principals)->refcount) == 0)                  \

        ? (*(principals)->destroy)((cx), (principals))  \

        : (void) 0)



/************************************************************************/



/*

 * Functions and scripts.

 */

extern JS_PUBLIC_API(JSFunction *)

JS_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags,

               JSObject *parent, const char *name);



extern JS_PUBLIC_API(JSObject *)

JS_GetFunctionObject(JSFunction *fun);



extern JS_PUBLIC_API(const char *)

JS_GetFunctionName(JSFunction *fun);



extern JS_PUBLIC_API(JSBool)

JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs);



extern JS_PUBLIC_API(JSFunction *)

JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call,

                  uintN nargs, uintN attrs);



extern JS_PUBLIC_API(JSObject *)

JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent);



/*

 * Given a buffer, return JS_FALSE if the buffer might become a valid

 * javascript statement with the addition of more lines.  Otherwise return

 * JS_TRUE.  The intent is to support interactive compilation - accumulate

 * lines in a buffer until JS_BufferIsCompilableUnit is true, then pass it to

 * the compiler.

 */

extern JS_PUBLIC_API(JSBool)

JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj,

                          const char *bytes, size_t length);



/*

 * The JSScript objects returned by the following functions refer to string and

 * other kinds of literals, including doubles and RegExp objects.  These

 * literals are vulnerable to garbage collection; to root script objects and

 * prevent literals from being collected, create a rootable object using

 * JS_NewScriptObject, and root the resulting object using JS_Add[Named]Root.

 */

extern JS_PUBLIC_API(JSScript *)

JS_CompileScript(JSContext *cx, JSObject *obj,

                 const char *bytes, size_t length,

                 const char *filename, uintN lineno);



extern JS_PUBLIC_API(JSScript *)

JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj,

                              JSPrincipals *principals,

                              const char *bytes, size_t length,

                              const char *filename, uintN lineno);



extern JS_PUBLIC_API(JSScript *)

JS_CompileUCScript(JSContext *cx, JSObject *obj,

                   const jschar *chars, size_t length,

                   const char *filename, uintN lineno);



extern JS_PUBLIC_API(JSScript *)

JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj,

                                JSPrincipals *principals,

                                const jschar *chars, size_t length,

                                const char *filename, uintN lineno);



extern JS_PUBLIC_API(JSScript *)

JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename);



extern JS_PUBLIC_API(JSScript *)

JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename,

                     FILE *fh);



extern JS_PUBLIC_API(JSScript *)

JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj,

                                  const char *filename, FILE *fh,

                                  JSPrincipals *principals);



/*

 * NB: you must use JS_NewScriptObject and root a pointer to its return value

 * in order to keep a JSScript and its atoms safe from garbage collection after

 * creating the script via JS_Compile* and before a JS_ExecuteScript* call.

 * E.g., and without error checks:

 *

 *    JSScript *script = JS_CompileFile(cx, global, filename);

 *    JSObject *scrobj = JS_NewScriptObject(cx, script);

 *    JS_AddNamedRoot(cx, &scrobj, "scrobj");

 *    do {

 *        jsval result;

 *        JS_ExecuteScript(cx, global, script, &result);

 *        JS_GC();

 *    } while (!JSVAL_IS_BOOLEAN(result) || JSVAL_TO_BOOLEAN(result));

 *    JS_RemoveRoot(cx, &scrobj);

 */

extern JS_PUBLIC_API(JSObject *)

JS_NewScriptObject(JSContext *cx, JSScript *script);



extern JS_PUBLIC_API(void)

JS_DestroyScript(JSContext *cx, JSScript *script);



extern 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);



extern 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);



extern 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);



extern 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);



extern JS_PUBLIC_API(JSString *)

JS_DecompileScript(JSContext *cx, JSScript *script, const char *name,

                   uintN indent);



/*

 * API extension: OR this into indent to avoid pretty-printing the decompiled

 * source resulting from JS_DecompileFunction{,Body}.

 */

#define JS_DONT_PRETTY_PRINT    ((uintN)0x8000)



extern JS_PUBLIC_API(JSString *)

JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent);



extern JS_PUBLIC_API(JSString *)

JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent);



/*

 * NB: JS_ExecuteScript, JS_ExecuteScriptPart, and the JS_Evaluate*Script*

 * quadruplets all use the obj parameter as the initial scope chain header,

 * the 'this' keyword value, and the variables object (ECMA parlance for where

 * 'var' and 'function' bind names) of the execution context for script.

 *

 * Using obj as the variables object is problematic if obj's parent (which is

 * the scope chain link; see JS_SetParent and JS_NewObject) is not null: in

 * this case, variables created by 'var x = 0', e.g., go in obj, but variables

 * created by assignment to an unbound id, 'x = 0', go in the last object on

 * the scope chain linked by parent.

 *

 * ECMA calls that last scoping object the "global object", but note that many

 * embeddings have several such objects.  ECMA requires that "global code" be

 * executed with the variables object equal to this global object.  But these

 * JS API entry points provide freedom to execute code against a "sub-global",

 * i.e., a parented or scoped object, in which case the variables object will

 * differ from the last object on the scope chain, resulting in confusing and

 * non-ECMA explicit vs. implicit variable creation.

 *

 * Caveat embedders: unless you already depend on this buggy variables object

 * binding behavior, you should call JS_SetOptions(cx, JSOPTION_VAROBJFIX) or

 * JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_VAROBJFIX) -- the latter if

 * someone may have set other options on cx already -- for each context in the

 * application, if you pass parented objects as the obj parameter, or may ever

 * pass such objects in the future.

 *

 * Why a runtime option?  The alternative is to add six or so new API entry

 * points with signatures matching the following six, and that doesn't seem

 * worth the code bloat cost.  Such new entry points would probably have less

 * obvious names, too, so would not tend to be used.  The JS_SetOption call,

 * OTOH, can be more easily hacked into existing code that does not depend on

 * the bug; such code can continue to use the familiar JS_EvaluateScript,

 * etc., entry points.

 */

extern JS_PUBLIC_API(JSBool)

JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval);



/*

 * Execute either the function-defining prolog of a script, or the script's

 * main body, but not both.

 */

typedef enum JSExecPart { JSEXEC_PROLOG, JSEXEC_MAIN } JSExecPart;



extern JS_PUBLIC_API(JSBool)

JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script,

                     JSExecPart part, jsval *rval);



extern JS_PUBLIC_API(JSBool)

JS_EvaluateScript(JSContext *cx, JSObject *obj,

                  const char *bytes, uintN length,

                  const char *filename, uintN lineno,

                  jsval *rval);



extern JS_PUBLIC_API(JSBool)

JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj,

                               JSPrincipals *principals,

                               const char *bytes, uintN length,

                               const char *filename, uintN lineno,

                               jsval *rval);



extern JS_PUBLIC_API(JSBool)

JS_EvaluateUCScript(JSContext *cx, JSObject *obj,

                    const jschar *chars, uintN length,

                    const char *filename, uintN lineno,

                    jsval *rval);



extern JS_PUBLIC_API(JSBool)

JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj,

                                 JSPrincipals *principals,

                                 const jschar *chars, uintN length,

                                 const char *filename, uintN lineno,

                                 jsval *rval);



extern JS_PUBLIC_API(JSBool)

JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc,

                jsval *argv, jsval *rval);



extern JS_PUBLIC_API(JSBool)

JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc,

                    jsval *argv, jsval *rval);



extern JS_PUBLIC_API(JSBool)

JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc,

                     jsval *argv, jsval *rval);



extern JS_PUBLIC_API(JSBranchCallback)

JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb);



extern JS_PUBLIC_API(JSBool)

JS_IsRunning(JSContext *cx);



extern JS_PUBLIC_API(JSBool)

JS_IsConstructing(JSContext *cx);



/*

 * Returns true if a script is executing and its current bytecode is a set

 * (assignment) operation, even if there are native (no script) stack frames

 * between the script and the caller to JS_IsAssigning.

 */

extern JS_FRIEND_API(JSBool)

JS_IsAssigning(JSContext *cx);



/*

 * Set the second return value, which should be a string or int jsval that

 * identifies a property in the returned object, to form an ECMA reference

 * type value (obj, id).  Only native methods can return reference types,

 * and if the returned value is used on the left-hand side of an assignment

 * op, the identified property will be set.  If the return value is in an

 * r-value, the interpreter just gets obj[id]'s value.

 */

extern JS_PUBLIC_API(void)

JS_SetCallReturnValue2(JSContext *cx, jsval v);



/************************************************************************/



/*

 * Strings.

 *

 * NB: JS_NewString takes ownership of bytes on success, avoiding a copy; but

 * on error (signified by null return), it leaves bytes owned by the caller.

 * So the caller must free bytes in the error case, if it has no use for them.

 * In contrast, all the JS_New*StringCopy* functions do not take ownership of

 * the character memory passed to them -- they copy it.

 */

extern JS_PUBLIC_API(JSString *)

JS_NewString(JSContext *cx, char *bytes, size_t length);



extern JS_PUBLIC_API(JSString *)

JS_NewStringCopyN(JSContext *cx, const char *s, size_t n);



extern JS_PUBLIC_API(JSString *)

JS_NewStringCopyZ(JSContext *cx, const char *s);



extern JS_PUBLIC_API(JSString *)

JS_InternString(JSContext *cx, const char *s);



extern JS_PUBLIC_API(JSString *)

JS_NewUCString(JSContext *cx, jschar *chars, size_t length);



extern JS_PUBLIC_API(JSString *)

JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n);



extern JS_PUBLIC_API(JSString *)

JS_NewUCStringCopyZ(JSContext *cx, const jschar *s);



extern JS_PUBLIC_API(JSString *)

JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length);



extern JS_PUBLIC_API(JSString *)

JS_InternUCString(JSContext *cx, const jschar *s);



extern JS_PUBLIC_API(char *)

JS_GetStringBytes(JSString *str);



extern JS_PUBLIC_API(jschar *)

JS_GetStringChars(JSString *str);



extern JS_PUBLIC_API(size_t)

JS_GetStringLength(JSString *str);



extern JS_PUBLIC_API(intN)

JS_CompareStrings(JSString *str1, JSString *str2);



/************************************************************************/



/*

 * Locale specific string conversion callback.

 */

struct JSLocaleCallbacks {

    JSLocaleToUpperCase     localeToUpperCase;

    JSLocaleToLowerCase     localeToLowerCase;

    JSLocaleCompare         localeCompare;

};



/*

 * Establish locale callbacks. The pointer must persist as long as the

 * JSContext.  Passing NULL restores the default behaviour.

 */

extern JS_PUBLIC_API(void)

JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks);



/*

 * Return the address of the current locale callbacks struct, which may

 * be NULL.

 */

extern JS_PUBLIC_API(JSLocaleCallbacks *)

JS_GetLocaleCallbacks(JSContext *cx);



//#if XP_MAC

/************************************************************************/



/*

 * Fireworks specific

 */

typedef JSBool (*EnumDestroyerProcPtr)(jsval iter_state);



extern JS_PUBLIC_API(void) 

js_SetFwEnumDestroyer(EnumDestroyerProcPtr destroyer);



//#endif



/************************************************************************/



/*

 * Error reporting.

 */



/*

 * Report an exception represented by the sprintf-like conversion of format

 * and its arguments.  This exception message string is passed to a pre-set

 * JSErrorReporter function (set by JS_SetErrorReporter; see jspubtd.h for

 * the JSErrorReporter typedef).

 */

extern JS_PUBLIC_API(void)

JS_ReportError(JSContext *cx, const char *format, ...);



/*

 * Use an errorNumber to retrieve the format string, args are char *

 */

extern JS_PUBLIC_API(void)

JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback,

                     void *userRef, const uintN errorNumber, ...);



/*

 * Use an errorNumber to retrieve the format string, args are jschar *

 */

extern JS_PUBLIC_API(void)

JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback,

                     void *userRef, const uintN errorNumber, ...);



/*

 * As above, but report a warning instead (JSREPORT_IS_WARNING(report.flags)).

 * Return true if there was no error trying to issue the warning, and if the

 * warning was not converted into an error due to the JSOPTION_WERROR option

 * being set, false otherwise.

 */

extern JS_PUBLIC_API(JSBool)

JS_ReportWarning(JSContext *cx, const char *format, ...);



extern JS_PUBLIC_API(JSBool)

JS_ReportErrorFlagsAndNumber(JSContext *cx, uintN flags,

                             JSErrorCallback errorCallback, void *userRef,

                             const uintN errorNumber, ...);



extern JS_PUBLIC_API(JSBool)

JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags,

                               JSErrorCallback errorCallback, void *userRef,

                               const uintN errorNumber, ...);



/*

 * Complain when out of memory.

 */

extern JS_PUBLIC_API(void)

JS_ReportOutOfMemory(JSContext *cx);



struct JSErrorReport {

    const char      *filename;      /* source file name, URL, etc., or null */

    uintN           lineno;         /* source line number */

    const char      *linebuf;       /* offending source line without final \n */

    const char      *tokenptr;      /* pointer to error token in linebuf */

    const jschar    *uclinebuf;     /* unicode (original) line buffer */

    const jschar    *uctokenptr;    /* unicode (original) token pointer */

    uintN           flags;          /* error/warning, etc. */

    uintN           errorNumber;    /* the error number, e.g. see js.msg */

    const jschar    *ucmessage;     /* the (default) error message */

    const jschar    **messageArgs;  /* arguments for the error message */

};



/*

 * JSErrorReport flag values.  These may be freely composed.

 */

#define JSREPORT_ERROR      0x0     /* pseudo-flag for default case */

#define JSREPORT_WARNING    0x1     /* reported via JS_ReportWarning */

#define JSREPORT_EXCEPTION  0x2     /* exception was thrown */

#define JSREPORT_STRICT     0x4     /* error or warning due to strict option */



/*

 * If JSREPORT_EXCEPTION is set, then a JavaScript-catchable exception

 * has been thrown for this runtime error, and the host should ignore it.

 * Exception-aware hosts should also check for JS_IsExceptionPending if

 * JS_ExecuteScript returns failure, and signal or propagate the exception, as

 * appropriate.

 */

#define JSREPORT_IS_WARNING(flags)      (((flags) & JSREPORT_WARNING) != 0)

#define JSREPORT_IS_EXCEPTION(flags)    (((flags) & JSREPORT_EXCEPTION) != 0)

#define JSREPORT_IS_STRICT(flags)       (((flags) & JSREPORT_STRICT) != 0)



extern JS_PUBLIC_API(JSErrorReporter)

JS_SetErrorReporter(JSContext *cx, JSErrorReporter er);



/************************************************************************/



/*

 * Regular Expressions.

 */

#define JSREG_FOLD      0x01    /* fold uppercase to lowercase */

#define JSREG_GLOB      0x02    /* global exec, creates array of matches */

#define JSREG_MULTILINE 0x04    /* treat ^ and $ as begin and end of line */



extern JS_PUBLIC_API(JSObject *)

JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags);



extern JS_PUBLIC_API(JSObject *)

JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags);



extern JS_PUBLIC_API(void)

JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline);



extern JS_PUBLIC_API(void)

JS_ClearRegExpStatics(JSContext *cx);



extern JS_PUBLIC_API(void)

JS_ClearRegExpRoots(JSContext *cx);



/* TODO: compile, exec, get/set other statics... */



/************************************************************************/



extern JS_PUBLIC_API(JSBool)

JS_IsExceptionPending(JSContext *cx);



extern JS_PUBLIC_API(JSBool)

JS_GetPendingException(JSContext *cx, jsval *vp);



extern JS_PUBLIC_API(void)

JS_SetPendingException(JSContext *cx, jsval v);



extern JS_PUBLIC_API(void)

JS_ClearPendingException(JSContext *cx);



/*

 * Save the current exception state. This takes a snapshot of the current

 * exception state without making any change to that state.

 *

 * The returned object MUST be later passed to either JS_RestoreExceptionState

 * (to restore that saved state) or JS_DropExceptionState (to cleanup the state

 * object in case it is not desireable to restore to that state). Both

 * JS_RestoreExceptionState and JS_DropExceptionState will destroy the

 * JSExceptionState object -- so that object can not be referenced again

 * after making either of those calls.

 */

extern JS_PUBLIC_API(JSExceptionState *)

JS_SaveExceptionState(JSContext *cx);



extern JS_PUBLIC_API(void)

JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state);



extern JS_PUBLIC_API(void)

JS_DropExceptionState(JSContext *cx, JSExceptionState *state);



/*

 * If the given jsval is an engine exception with an attached error report

 * then return a pointer to that report. Else, return NULL.

 * The lifetime of the error report that might be returned is linked to the

 * lifetime of the exception.

 */

extern JS_PUBLIC_API(JSErrorReport *)

JS_ErrorFromException(JSContext *cx, jsval v);



#ifdef JS_THREADSAFE



/*

 * Associate the current thread with the given context.  This is done

 * implicitly by JS_NewContext.

 *

 * Returns the old thread id for this context, which should be treated as

 * an opaque value.  This value is provided for comparison to 0, which

 * indicates that ClearContextThread has been called on this context

 * since the last SetContextThread, or non-0, which indicates the opposite.

 */



extern JS_PUBLIC_API(intN)

JS_GetContextThread(JSContext *cx);



extern JS_PUBLIC_API(intN)

JS_SetContextThread(JSContext *cx);



extern JS_PUBLIC_API(intN)

JS_ClearContextThread(JSContext *cx);



#endif /* JS_THREADSAFE */



/************************************************************************/



JS_END_EXTERN_C



#endif /* jsapi_h___ */

 

**** End of jsapi.h ****

 

**** Start of jsarena.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * Lifetime-based fast allocation, inspired by much prior art, including

 * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"

 * David R. Hanson, Software -- Practice and Experience, Vol. 20(1).

 */

#include "jsstddef.h"

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsbit.h"

#include "jsarena.h" /* Added by JSIFY */

#include "jsutil.h" /* Added by JSIFY */

#include "jslock.h"



static JSArena *arena_freelist;



#ifdef JS_THREADSAFE

static JSLock *arena_freelist_lock;

#endif



#ifdef JS_ARENAMETER

static JSArenaStats *arena_stats_list;



#define COUNT(pool,what)  (pool)->stats.what++

#else

#define COUNT(pool,what)  /* nothing */

#endif



#define JS_ARENA_DEFAULT_ALIGN  sizeof(double)



JS_PUBLIC_API(void)

JS_InitArenaPool(JSArenaPool *pool, const char *name, JSUint32 size, JSUint32 align)

{

#ifdef JS_THREADSAFE

    /* Must come through here once in primordial thread to init safely! */

    if (!arena_freelist_lock) {

        arena_freelist_lock = JS_NEW_LOCK();

        JS_ASSERT(arena_freelist_lock);

    }

#endif

    if (align == 0)

	align = JS_ARENA_DEFAULT_ALIGN;

    pool->mask = JS_BITMASK(JS_CeilingLog2(align));

    pool->first.next = NULL;

    pool->first.base = pool->first.avail = pool->first.limit =

	JS_ARENA_ALIGN(pool, &pool->first + 1);

    pool->current = &pool->first;

    pool->arenasize = size;

#ifdef JS_ARENAMETER

    memset(&pool->stats, 0, sizeof pool->stats);

    pool->stats.name = strdup(name);

    pool->stats.next = arena_stats_list;

    arena_stats_list = &pool->stats;

#endif

}



JS_PUBLIC_API(void *)

JS_ArenaAllocate(JSArenaPool *pool, JSUint32 nb)

{

    JSArena **ap, *a, *b;

    JSUint32 sz;

    void *p;



    JS_ASSERT((nb & pool->mask) == 0);

    for (a = pool->current; a->avail + nb > a->limit; pool->current = a) {

        if (!a->next) {

            ap = &arena_freelist;

            JS_ACQUIRE_LOCK(arena_freelist_lock);

            while ((b = *ap) != NULL) {         /* reclaim a free arena */

                /*

                 * Insist on exact arenasize match if nb is not greater than

                 * arenasize.  Otherwise take any arena big enough, but not by

                 * more than nb + arenasize.

                 */

                sz = (JSUint32)(b->limit - b->base);

                if ((nb > pool->arenasize)

                    ? sz >= nb && sz <= nb + pool->arenasize

                    : sz == pool->arenasize) {

                    *ap = b->next;

                    JS_RELEASE_LOCK(arena_freelist_lock);

                    b->next = NULL;

                    a = a->next = b;

                    COUNT(pool, nreclaims);

                    goto claim;

                }

                ap = &b->next;

            }

            JS_RELEASE_LOCK(arena_freelist_lock);

            sz = JS_MAX(pool->arenasize, nb);   /* allocate a new arena */

            sz += sizeof *a + pool->mask;       /* header and alignment slop */

            b = (JSArena *) malloc(sz);

            if (!b)

                return 0;

            a = a->next = b;

            a->next = NULL;

            a->limit = (jsuword)a + sz;

            JS_COUNT_ARENA(pool,++);

            COUNT(pool, nmallocs);

        claim:

            a->base = a->avail = JS_ARENA_ALIGN(pool, a + 1);

            continue;

        }

        a = a->next;                            /* move to next arena */

    }

    p = (void *)a->avail;

    a->avail += nb;

    return p;

}



JS_PUBLIC_API(void *)

JS_ArenaRealloc(JSArenaPool *pool, void *p, JSUint32 size, JSUint32 incr)

{

    JSArena **ap, *a;

    jsuword aoff;



    ap = &pool->first.next;

    while ((a = *ap) != pool->current)

        ap = &a->next;

    JS_ASSERT(a->base == (jsuword)p);

    size += incr;

    aoff = size;

    JS_ASSERT(size > pool->arenasize);

    size += sizeof *a + pool->mask;     /* header and alignment slop */

    a = (JSArena *) realloc(a, size);

    if (!a)

        return NULL;

    *ap = a;

    pool->current = a;

#ifdef JS_ARENAMETER

    pool->stats.nreallocs++;

#endif

    a->base = JS_ARENA_ALIGN(pool, a + 1);

    a->limit = (jsuword)a + size;

    a->avail = JS_ARENA_ALIGN(pool, a->base + aoff);

    return (void *)a->base;

}



JS_PUBLIC_API(void *)

JS_ArenaGrow(JSArenaPool *pool, void *p, JSUint32 size, JSUint32 incr)

{

    void *newp;



    JS_ARENA_ALLOCATE(newp, pool, size + incr);

    if (newp)

        memcpy(newp, p, size);

    return newp;

}



/*

 * Free tail arenas linked after head, which may not be the true list head.

 * Reset pool->current to point to head in case it pointed at a tail arena.

 */

static void

FreeArenaList(JSArenaPool *pool, JSArena *head, JSBool reallyFree)

{

    JSArena **ap, *a;



    ap = &head->next;

    a = *ap;

    if (!a)

	return;



#ifdef DEBUG

    do {

	JS_ASSERT(a->base <= a->avail && a->avail <= a->limit);

	a->avail = a->base;

	JS_CLEAR_UNUSED(a);

    } while ((a = a->next) != NULL);

    a = *ap;

#endif



    if (reallyFree) {

	do {

	    *ap = a->next;

	    JS_CLEAR_ARENA(a);

	    JS_COUNT_ARENA(pool,--);

	    free(a);

	} while ((a = *ap) != NULL);

    } else {

	/* Insert the whole arena chain at the front of the freelist. */

	do {

	    ap = &(*ap)->next;

	} while (*ap);

        JS_ACQUIRE_LOCK(arena_freelist_lock);

	*ap = arena_freelist;

	arena_freelist = a;

        JS_RELEASE_LOCK(arena_freelist_lock);

	head->next = NULL;

    }



    pool->current = head;

}



JS_PUBLIC_API(void)

JS_ArenaRelease(JSArenaPool *pool, char *mark)

{

    JSArena *a;



    for (a = &pool->first; a; a = a->next) {

	if (JS_UPTRDIFF(mark, a->base) <= JS_UPTRDIFF(a->avail, a->base)) {

	    a->avail = JS_ARENA_ALIGN(pool, mark);

	    FreeArenaList(pool, a, JS_TRUE);

	    return;

	}

    }

}



JS_PUBLIC_API(void)

JS_FreeArenaPool(JSArenaPool *pool)

{

    FreeArenaList(pool, &pool->first, JS_FALSE);

    COUNT(pool, ndeallocs);

}



JS_PUBLIC_API(void)

JS_FinishArenaPool(JSArenaPool *pool)

{

    FreeArenaList(pool, &pool->first, JS_TRUE);

#ifdef JS_ARENAMETER

    {

	JSArenaStats *stats, **statsp;



	if (pool->stats.name)

	    free(pool->stats.name);

	for (statsp = &arena_stats_list; (stats = *statsp) != 0;

	     statsp = &stats->next) {

	    if (stats == &pool->stats) {

		*statsp = stats->next;

		return;

	    }

	}

    }

#endif

}



JS_PUBLIC_API(void)

JS_CompactArenaPool(JSArenaPool *pool)

{

#if 0 /* XP_MAC */

    JSArena *a = pool->first.next;



    while (a) {

        reallocSmaller(a, a->avail - (jsuword)a);

        a->limit = a->avail;

        a = a->next;

    }

#endif

}



JS_PUBLIC_API(void)

JS_ArenaFinish()

{

    JSArena *a, *next;



    JS_ACQUIRE_LOCK(arena_freelist_lock);

    a = arena_freelist;

    arena_freelist = NULL;

    JS_RELEASE_LOCK(arena_freelist_lock);

    for (; a; a = next) {

        next = a->next;

        free(a);

    }

}



JS_PUBLIC_API(void)

JS_ArenaShutDown(void)

{

#ifdef JS_THREADSAFE

    /* Must come through here once in the process's last thread! */

    if (arena_freelist_lock) {

        JS_DESTROY_LOCK(arena_freelist_lock);

        arena_freelist_lock = NULL;

    }

#endif

}



#ifdef JS_ARENAMETER

JS_PUBLIC_API(void)

JS_ArenaCountAllocation(JSArenaPool *pool, JSUint32 nb)

{

    pool->stats.nallocs++;

    pool->stats.nbytes += nb;

    if (nb > pool->stats.maxalloc)

        pool->stats.maxalloc = nb;

    pool->stats.variance += nb * nb;

}



JS_PUBLIC_API(void)

JS_ArenaCountInplaceGrowth(JSArenaPool *pool, JSUint32 size, JSUint32 incr)

{

    pool->stats.ninplace++;

}



JS_PUBLIC_API(void)

JS_ArenaCountGrowth(JSArenaPool *pool, JSUint32 size, JSUint32 incr)

{

    pool->stats.ngrows++;

    pool->stats.nbytes += incr;

    pool->stats.variance -= size * size;

    size += incr;

    if (size > pool->stats.maxalloc)

        pool->stats.maxalloc = size;

    pool->stats.variance += size * size;

}



JS_PUBLIC_API(void)

JS_ArenaCountRelease(JSArenaPool *pool, char *mark)

{

    pool->stats.nreleases++;

}



JS_PUBLIC_API(void)

JS_ArenaCountRetract(JSArenaPool *pool, char *mark)

{

    pool->stats.nfastrels++;

}



#include <math.h>

#include <stdio.h>



JS_PUBLIC_API(void)

JS_DumpArenaStats(FILE *fp)

{

    JSArenaStats *stats;

    uint32 nallocs, nbytes;

    double mean, variance, sigma;



    for (stats = arena_stats_list; stats; stats = stats->next) {

        nallocs = stats->nallocs;

        if (nallocs != 0) {

            nbytes = stats->nbytes;

            mean = (double)nbytes / nallocs;

            variance = stats->variance * nallocs - nbytes * nbytes;

            if (variance < 0 || nallocs == 1)

                variance = 0;

            else

                variance /= nallocs * (nallocs - 1);

            sigma = sqrt(variance);

	} else {

	    mean = variance = sigma = 0;

	}



        fprintf(fp, "\n%s allocation statistics:\n", stats->name);

        fprintf(fp, "              number of arenas: %u\n", stats->narenas);

        fprintf(fp, "         number of allocations: %u\n", stats->nallocs);

        fprintf(fp, " number of free arena reclaims: %u\n", stats->nreclaims);

        fprintf(fp, "        number of malloc calls: %u\n", stats->nmallocs);

        fprintf(fp, "       number of deallocations: %u\n", stats->ndeallocs);

        fprintf(fp, "  number of allocation growths: %u\n", stats->ngrows);

        fprintf(fp, "    number of in-place growths: %u\n", stats->ninplace);

        fprintf(fp, " number of realloc'ing growths: %u\n", stats->nreallocs);

        fprintf(fp, "number of released allocations: %u\n", stats->nreleases);

        fprintf(fp, "       number of fast releases: %u\n", stats->nfastrels);

        fprintf(fp, "         total bytes allocated: %u\n", stats->nbytes);

        fprintf(fp, "          mean allocation size: %g\n", mean);

        fprintf(fp, "            standard deviation: %g\n", sigma);

        fprintf(fp, "       maximum allocation size: %u\n", stats->maxalloc);

    }

}

#endif /* JS_ARENAMETER */

 

**** End of jsarena.c ****

 

**** Start of jsarena.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsarena_h___

#define jsarena_h___

/*

 * Lifetime-based fast allocation, inspired by much prior art, including

 * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"

 * David R. Hanson, Software -- Practice and Experience, Vol. 20(1).

 *

 * Also supports LIFO allocation (JS_ARENA_MARK/JS_ARENA_RELEASE).

 */

#include <stdlib.h>

#include "jstypes.h"

#include "jscompat.h"



JS_BEGIN_EXTERN_C



typedef struct JSArena JSArena;

typedef struct JSArenaPool JSArenaPool;



struct JSArena {

    JSArena     *next;          /* next arena for this lifetime */

    jsuword     base;           /* aligned base address, follows this header */

    jsuword     limit;          /* one beyond last byte in arena */

    jsuword     avail;          /* points to next available byte */

};



#ifdef JS_ARENAMETER

typedef struct JSArenaStats JSArenaStats;



struct JSArenaStats {

    JSArenaStats *next;         /* next in arenaStats list */

    char        *name;          /* name for debugging */

    uint32      narenas;        /* number of arenas in pool */

    uint32      nallocs;        /* number of JS_ARENA_ALLOCATE() calls */

    uint32      nreclaims;      /* number of reclaims from freeArenas */

    uint32      nmallocs;       /* number of malloc() calls */

    uint32      ndeallocs;      /* number of lifetime deallocations */

    uint32      ngrows;         /* number of JS_ARENA_GROW() calls */

    uint32      ninplace;       /* number of in-place growths */

    uint32      nreallocs;      /* number of arena grow extending reallocs */

    uint32      nreleases;      /* number of JS_ARENA_RELEASE() calls */

    uint32      nfastrels;      /* number of "fast path" releases */

    size_t      nbytes;         /* total bytes allocated */

    size_t      maxalloc;       /* maximum allocation size in bytes */

    double      variance;       /* size variance accumulator */

};

#endif



struct JSArenaPool {

    JSArena     first;          /* first arena in pool list */

    JSArena     *current;       /* arena from which to allocate space */

    size_t      arenasize;      /* net exact size of a new arena */

    jsuword     mask;           /* alignment mask (power-of-2 - 1) */

#ifdef JS_ARENAMETER

    JSArenaStats stats;

#endif

};



/*

 * If the including .c file uses only one power-of-2 alignment, it may define

 * JS_ARENA_CONST_ALIGN_MASK to the alignment mask and save a few instructions

 * per ALLOCATE and GROW.

 */

#ifdef JS_ARENA_CONST_ALIGN_MASK

#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + JS_ARENA_CONST_ALIGN_MASK) \

				 & ~(jsuword)JS_ARENA_CONST_ALIGN_MASK)



#define JS_INIT_ARENA_POOL(pool, name, size) \

	JS_InitArenaPool(pool, name, size, JS_ARENA_CONST_ALIGN_MASK + 1)

#else

#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + (pool)->mask) & ~(pool)->mask)

#endif



#define JS_ARENA_ALLOCATE(p, pool, nb)                                        \

    JS_ARENA_ALLOCATE_CAST(p, void *, pool, nb)



#define JS_ARENA_ALLOCATE_TYPE(p, type, pool)                                 \

    JS_ARENA_ALLOCATE_CAST(p, type *, pool, sizeof(type))



#define JS_ARENA_ALLOCATE_CAST(p, type, pool, nb)                             \

    JS_BEGIN_MACRO                                                            \

	JSArena *_a = (pool)->current;                                        \

	size_t _nb = JS_ARENA_ALIGN(pool, nb);                                \

	jsuword _p = _a->avail;                                               \

	jsuword _q = _p + _nb;                                                \

	if (_q > _a->limit)                                                   \

	    _p = (jsuword)JS_ArenaAllocate(pool, _nb);                        \

	else                                                                  \

	    _a->avail = _q;                                                   \

	p = (type) _p;                                                        \

	JS_ArenaCountAllocation(pool, nb);                                    \

    JS_END_MACRO



#define JS_ARENA_GROW(p, pool, size, incr)                                    \

    JS_ARENA_GROW_CAST(p, void *, pool, size, incr)



#define JS_ARENA_GROW_CAST(p, type, pool, size, incr)                         \

    JS_BEGIN_MACRO                                                            \

        JSArena *_a = (pool)->current;                                        \

        if (_a->avail == (jsuword)(p) + JS_ARENA_ALIGN(pool, size)) {         \

            size_t _nb = (size) + (incr);                                     \

            jsuword _q = (jsuword)(p) + JS_ARENA_ALIGN(pool, _nb);            \

            if (_q <= _a->limit) {                                            \

                _a->avail = _q;                                               \

                JS_ArenaCountInplaceGrowth(pool, size, incr);                 \

            } else if ((jsuword)(p) == _a->base) {                            \

                p = (type) JS_ArenaRealloc(pool, p, size, incr);              \

            } else {                                                          \

                p = (type) JS_ArenaGrow(pool, p, size, incr);                 \

            }                                                                 \

        } else {                                                              \

            p = (type) JS_ArenaGrow(pool, p, size, incr);                     \

        }                                                                     \

        JS_ArenaCountGrowth(pool, size, incr);                                \

    JS_END_MACRO



#define JS_ARENA_MARK(pool)     ((void *) (pool)->current->avail)

#define JS_UPTRDIFF(p,q)        ((jsuword)(p) - (jsuword)(q))



#ifdef DEBUG

#define JS_FREE_PATTERN         0xDA

#define JS_CLEAR_UNUSED(a)      (JS_ASSERT((a)->avail <= (a)->limit),         \

                                 memset((void*)(a)->avail, JS_FREE_PATTERN,   \

                                        (a)->limit - (a)->avail))

#define JS_CLEAR_ARENA(a)       memset((void*)(a), JS_FREE_PATTERN,           \

                                       (a)->limit - (jsuword)(a))

#else

#define JS_CLEAR_UNUSED(a)      /* nothing */

#define JS_CLEAR_ARENA(a)       /* nothing */

#endif



#define JS_ARENA_RELEASE(pool, mark)                                          \

    JS_BEGIN_MACRO                                                            \

        char *_m = (char *)(mark);                                            \

        JSArena *_a = (pool)->current;                                        \

        if (_a != &(pool)->first &&                                           \

            JS_UPTRDIFF(_m, _a->base) <= JS_UPTRDIFF(_a->avail, _a->base)) {  \

            _a->avail = (jsuword)JS_ARENA_ALIGN(pool, _m);                    \

            JS_CLEAR_UNUSED(_a);                                              \

            JS_ArenaCountRetract(pool, _m);                                   \

        } else {                                                              \

            JS_ArenaRelease(pool, _m);                                        \

        }                                                                     \

        JS_ArenaCountRelease(pool, _m);                                       \

    JS_END_MACRO



#ifdef JS_ARENAMETER

#define JS_COUNT_ARENA(pool,op) ((pool)->stats.narenas op)

#else

#define JS_COUNT_ARENA(pool,op)

#endif



#define JS_ARENA_DESTROY(pool, a, pnext)                                      \

    JS_BEGIN_MACRO                                                            \

        JS_COUNT_ARENA(pool,--);                                              \

        if ((pool)->current == (a)) (pool)->current = &(pool)->first;         \

        *(pnext) = (a)->next;                                                 \

        JS_CLEAR_ARENA(a);                                                    \

        free(a);                                                              \

        (a) = NULL;                                                           \

    JS_END_MACRO



/*

 * Initialize an arena pool with the given name for debugging and metering,

 * with a minimum size per arena of size bytes.

 */

extern JS_PUBLIC_API(void)

JS_InitArenaPool(JSArenaPool *pool, const char *name, JSUint32 size,

                 JSUint32 align);



/*

 * Free the arenas in pool.  The user may continue to allocate from pool

 * after calling this function.  There is no need to call JS_InitArenaPool()

 * again unless JS_FinishArenaPool(pool) has been called.

 */

extern JS_PUBLIC_API(void)

JS_FreeArenaPool(JSArenaPool *pool);



/*

 * Free the arenas in pool and finish using it altogether.

 */

extern JS_PUBLIC_API(void)

JS_FinishArenaPool(JSArenaPool *pool);



/*

 * Compact all of the arenas in a pool so that no space is wasted.

 */

extern JS_PUBLIC_API(void)

JS_CompactArenaPool(JSArenaPool *pool);



/*

 * Finish using arenas, freeing all memory associated with them except for

 * any locks needed for thread safety.

 */

extern JS_PUBLIC_API(void)

JS_ArenaFinish(void);



/*

 * Free any locks or other memory needed for thread safety, just before

 * shutting down.  At that point, we must be called by a single thread.

 *

 * After shutting down, the next thread to call JS_InitArenaPool must not

 * race with any other thread.  Once a pool has been initialized, threads

 * may safely call jsarena.c functions on thread-local pools.  The upshot

 * is that pools are per-thread, but the underlying global freelist is

 * thread-safe, provided that both the first pool initialization and the

 * shut-down call are single-threaded.

 */

extern JS_PUBLIC_API(void)

JS_ArenaShutDown(void);



/*

 * Friend functions used by the JS_ARENA_*() macros.

 */

extern JS_PUBLIC_API(void *)

JS_ArenaAllocate(JSArenaPool *pool, JSUint32 nb);



extern JS_PUBLIC_API(void *)

JS_ArenaRealloc(JSArenaPool *pool, void *p, JSUint32 size, JSUint32 incr);



extern JS_PUBLIC_API(void *)

JS_ArenaGrow(JSArenaPool *pool, void *p, JSUint32 size, JSUint32 incr);



extern JS_PUBLIC_API(void)

JS_ArenaRelease(JSArenaPool *pool, char *mark);



#ifdef JS_ARENAMETER



#include <stdio.h>



extern JS_PUBLIC_API(void)

JS_ArenaCountAllocation(JSArenaPool *pool, JSUint32 nb);



extern JS_PUBLIC_API(void)

JS_ArenaCountInplaceGrowth(JSArenaPool *pool, JSUint32 size, JSUint32 incr);



extern JS_PUBLIC_API(void)

JS_ArenaCountGrowth(JSArenaPool *pool, JSUint32 size, JSUint32 incr);



extern JS_PUBLIC_API(void)

JS_ArenaCountRelease(JSArenaPool *pool, char *mark);



extern JS_PUBLIC_API(void)

JS_ArenaCountRetract(JSArenaPool *pool, char *mark);



extern JS_PUBLIC_API(void)

JS_DumpArenaStats(FILE *fp);



#else  /* !JS_ARENAMETER */



#define JS_ArenaCountAllocation(ap, nb)                 /* nothing */

#define JS_ArenaCountInplaceGrowth(ap, size, incr)      /* nothing */

#define JS_ArenaCountGrowth(ap, size, incr)             /* nothing */

#define JS_ArenaCountRelease(ap, mark)                  /* nothing */

#define JS_ArenaCountRetract(ap, mark)                  /* nothing */



#endif /* !JS_ARENAMETER */



JS_END_EXTERN_C



#endif /* jsarena_h___ */

 

**** End of jsarena.h ****

 

**** Start of jsarray.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS array class.

 */

#include "jsstddef.h"

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jsapi.h"

#include "jsarray.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsfun.h"

#include "jsgc.h"

#include "jsinterp.h"

#include "jslock.h"

#include "jsnum.h"

#include "jsobj.h"

#include "jsstr.h"



/* 2^32 - 1 as a number and a string */

#define MAXINDEX 4294967295u

#define MAXSTR   "4294967295"



/*

 * Determine if the id represents an array index.

 *

 * An id is an array index according to ECMA by (15.4):

 *

 * "Array objects give special treatment to a certain class of property names.

 * A property name P (in the form of a string value) is an array index if and

 * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal

 * to 2^32-1."

 *

 * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)

 * except that by using signed 32-bit integers we miss the top half of the

 * valid range. This function checks the string representation itself; note

 * that calling a standard conversion routine might allow strings such as

 * "08" or "4.0" as array indices, which they are not.

 */

static JSBool

IdIsIndex(jsid id, jsuint *indexp)

{

    JSString *str;

    jschar *cp;



    if (JSVAL_IS_INT(id)) {

	jsint i;

	i = JSVAL_TO_INT(id);

	if (i < 0)

	    return JS_FALSE;

	*indexp = (jsuint)i;

	return JS_TRUE;

    }



    /* It must be a string. */

    str = JSVAL_TO_STRING(id);

    cp = str->chars;

    if (JS7_ISDEC(*cp) && str->length < sizeof(MAXSTR)) {

	jsuint index = JS7_UNDEC(*cp++);

	jsuint oldIndex = 0;

	jsuint c = 0;

	if (index != 0) {

	    while (JS7_ISDEC(*cp)) {

		oldIndex = index;

		c = JS7_UNDEC(*cp);

		index = 10*index + c;

		cp++;

	    }

	}

	/* Make sure all characters were consumed and that it couldn't

	 * have overflowed.

	 */

	if (*cp == 0 &&

	     (oldIndex < (MAXINDEX / 10) ||

	      (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10))))

	{

	    *indexp = index;

	    return JS_TRUE;

	}

    }

    return JS_FALSE;

}



static JSBool

ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp)

{

    jsint i;



    if (JSVAL_IS_INT(v)) {

	i = JSVAL_TO_INT(v);

        if (i < 0) {

            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

				 JSMSG_BAD_ARRAY_LENGTH);

            return JS_FALSE;

        }

	if (lengthp)

	    *lengthp = (jsuint) i;

	return JS_TRUE;

    }

    if (JSVAL_IS_DOUBLE(v)) {

        jsdouble d;

	/* mccabe gets his wish */

        if (!js_ValueToNumber(cx, v, &d))

	    return JS_FALSE;

        if (!js_DoubleToECMAUint32(cx, d, (uint32 *)lengthp))

            return JS_FALSE;

        if (JSDOUBLE_IS_NaN(d) || (d != *(uint32 *)lengthp)) {

            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

				 JSMSG_BAD_ARRAY_LENGTH);

            return JS_FALSE;

        }

	return JS_TRUE;

    }

    return JS_FALSE;

}



JSBool

js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)

{

    jsid id;

    jsint i;

    jsval v;



    id = (jsid) cx->runtime->atomState.lengthAtom;

    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))

	return JS_FALSE;



    /* Short-circuit, because js_ValueToECMAUint32 fails when

     * called during init time.

     */

    if (JSVAL_IS_INT(v)) {

	i = JSVAL_TO_INT(v);

	/* jsuint cast does ToUint32. */

	*lengthp = (jsuint)i;

	return JS_TRUE;

    }

    return js_ValueToECMAUint32(cx, v, (uint32 *)lengthp);

}



static JSBool

IndexToValue(JSContext *cx, jsuint length, jsval *vp)

{

    if (length <= JSVAL_INT_MAX) {

	*vp = INT_TO_JSVAL(length);

	return JS_TRUE;

    }

    return js_NewDoubleValue(cx, (jsdouble)length, vp);

}



static JSBool

IndexToId(JSContext *cx, jsuint length, jsid *idp)

{

    JSString *str;

    JSAtom *atom;



    if (length <= JSVAL_INT_MAX) {

	*idp = (jsid) INT_TO_JSVAL(length);

    } else {

	str = js_NumberToString(cx, (jsdouble)length);

	if (!str)

	    return JS_FALSE;

	atom = js_AtomizeString(cx, str, 0);

	if (!atom)

	    return JS_FALSE;

	*idp = (jsid)atom;



    }

    return JS_TRUE;

}



JSBool

js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length)

{

    jsval v;

    jsid id;



    if (!IndexToValue(cx, length, &v))

	return JS_FALSE;

    id = (jsid) cx->runtime->atomState.lengthAtom;

    return OBJ_SET_PROPERTY(cx, obj, id, &v);

}



JSBool

js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)

{

    JSErrorReporter older;

    jsid id;

    JSBool ok;

    jsval v;



    older = JS_SetErrorReporter(cx, NULL);

    id = (jsid) cx->runtime->atomState.lengthAtom;

    ok = OBJ_GET_PROPERTY(cx, obj, id, &v);

    JS_SetErrorReporter(cx, older);

    if (!ok)

	return JS_FALSE;

    return ValueIsLength(cx, v, lengthp);

}



/*

 * This get function is specific to Array.prototype.length and other array

 * instance length properties.  It calls back through the class get function

 * in case some magic happens there (see call_getProperty in jsfun.c).

 */

static JSBool

array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    return OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, id, vp);

}



static JSBool

array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    jsuint newlen, oldlen, slot;

    jsid id2;

    jsval junk;



    if (!ValueIsLength(cx, *vp, &newlen))

	return JS_FALSE;

    if (!js_GetLengthProperty(cx, obj, &oldlen))

	return JS_FALSE;

    for (slot = newlen; slot < oldlen; slot++) {

	if (!IndexToId(cx, slot, &id2))

	    return JS_FALSE;

	if (!OBJ_DELETE_PROPERTY(cx, obj, id2, &junk))

	    return JS_FALSE;

    }

    return IndexToValue(cx, newlen, vp);

}



static JSBool

array_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    jsuint index, length;



    if (!(IdIsIndex(id, &index)))

	return JS_TRUE;

    if (!js_GetLengthProperty(cx, obj, &length))

	return JS_FALSE;

    if (index >= length) {

	length = index + 1;

	return js_SetLengthProperty(cx, obj, length);

    }

    return JS_TRUE;

}



static JSBool

array_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)

{

    jsuint length;



    if (cx->version == JSVERSION_1_2) {

	if (!js_GetLengthProperty(cx, obj, &length))

	    return JS_FALSE;

	switch (type) {

	  case JSTYPE_NUMBER:

	    return IndexToValue(cx, length, vp);

	  case JSTYPE_BOOLEAN:

	    *vp = BOOLEAN_TO_JSVAL(length > 0);

	    return JS_TRUE;

	  default:

	    return JS_TRUE;

	}

    }

    return js_TryValueOf(cx, obj, type, vp);

}



JSClass js_ArrayClass = {

    "Array",

    0,

    array_addProperty, JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,

    JS_EnumerateStub,  JS_ResolveStub,    array_convert,     JS_FinalizeStub,

    JSCLASS_NO_OPTIONAL_MEMBERS

};



static JSBool

array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize,

	       jsval *rval, JSBool localeString)

{

    JSBool ok;

    jsval v;

    jsuint length, index;

    jschar *chars, *ochars;

    size_t nchars, growth, seplen;

    const jschar *sepstr;

    JSString *str;

    JSHashEntry *he;

    JSObject *obj2;



    ok = js_GetLengthProperty(cx, obj, &length);

    if (!ok)

	return JS_FALSE;

    ok = JS_TRUE;



    if (literalize) {

	he = js_EnterSharpObject(cx, obj, NULL, &chars);

	if (!he)

	    return JS_FALSE;

	if (IS_SHARP(he)) {

#if JS_HAS_SHARP_VARS

	    nchars = js_strlen(chars);

#else

	    chars[0] = '[';

	    chars[1] = ']';

	    chars[2] = 0;

	    nchars = 2;

#endif

	    goto make_string;

	}



	/*

	 * Allocate 1 + 3 + 1 for "[", the worst-case closing ", ]", and the

	 * terminating 0.

	 */

	growth = (1 + 3 + 1) * sizeof(jschar);

	if (!chars) {

	    nchars = 0;

	    chars = (jschar *) malloc(growth);

	    if (!chars)

		goto done;

	} else {

	    MAKE_SHARP(he);

	    nchars = js_strlen(chars);

	    chars = (jschar *)

                realloc((ochars = chars), nchars * sizeof(jschar) + growth);

	    if (!chars) {

		free(ochars);

		goto done;

	    }

	}

	chars[nchars++] = '[';

    } else {

	if (length == 0) {

	    *rval = JS_GetEmptyStringValue(cx);

	    return ok;

	}

	chars = NULL;

	nchars = 0;

    }

    sepstr = NULL;

    seplen = sep->length;



    v = JSVAL_NULL;

    for (index = 0; index < length; index++) {

	ok = JS_GetElement(cx, obj, index, &v);

	if (!ok)

	    goto done;



	if (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v)) {

	    str = cx->runtime->emptyString;

        } else {

            if (localeString) {

                if (!js_ValueToObject(cx, v, &obj2))

                    goto doneBad;

                if (!js_TryMethod(cx, obj2, cx->runtime->atomState.toLocaleStringAtom, 0, NULL, &v))

                    goto doneBad;

                str = JSVAL_TO_STRING(v);

            }

            else

                str = (literalize ? js_ValueToSource : js_ValueToString)(cx, v);

	    if (!str) {

  doneBad:

		ok = JS_FALSE;

		goto done;

	    }

	}



	/* Allocate 3 + 1 at end for ", ", closing bracket, and zero. */

	growth = (nchars + (sepstr ? seplen : 0) +

		  str->length +

		  3 + 1) * sizeof(jschar);

	if (!chars) {

	    chars = (jschar *) malloc(growth);

	    if (!chars)

		goto done;

	} else {

	    chars = (jschar *) realloc((ochars = chars), growth);

	    if (!chars) {

		free(ochars);

		goto done;

	    }

	}



	if (sepstr) {

	    js_strncpy(&chars[nchars], sepstr, seplen);

	    nchars += seplen;

	}

	sepstr = sep->chars;



	js_strncpy(&chars[nchars], str->chars, str->length);

	nchars += str->length;

    }



  done:

    if (literalize) {

	if (chars) {

	    if (JSVAL_IS_VOID(v)) {

		chars[nchars++] = ',';

		chars[nchars++] = ' ';

	    }

	    chars[nchars++] = ']';

	}

	js_LeaveSharpObject(cx, NULL);

    }

    if (!ok) {

	if (chars)

	    free(chars);

	return ok;

    }



  make_string:

    if (!chars) {

	JS_ReportOutOfMemory(cx);

	return JS_FALSE;

    }

    chars[nchars] = 0;

    str = js_NewString(cx, chars, nchars, 0);

    if (!str) {

	free(chars);

	return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static jschar   comma_space_ucstr[] = {',', ' ', 0};

static jschar   comma_ucstr[]       = {',', 0};

static JSString comma_space         = {2, comma_space_ucstr};

static JSString comma               = {1, comma_ucstr};



#if JS_HAS_TOSOURCE

static JSBool

array_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	       jsval *rval)

{

    return array_join_sub(cx, obj, &comma_space, JS_TRUE, rval, JS_FALSE);

}

#endif



static JSBool

array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	       jsval *rval)

{

    JSBool literalize;



    /*

     * JS1.2 arrays convert to array literals, with a comma followed by a space

     * between each element.

     */

    literalize = (cx->version == JSVERSION_1_2);

    return array_join_sub(cx, obj, literalize ? &comma_space : &comma,

			  literalize, rval, JS_FALSE);

}



static JSBool

array_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	       jsval *rval)

{

    /*

     *  Passing comma_space here as the separator. Need a way to get a

     *  locale-specific version.

     */

    return array_join_sub(cx, obj, &comma_space, JS_FALSE, rval, JS_TRUE);

}



static JSBool

array_join(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str;



    if (argc == 0)

	return array_join_sub(cx, obj, &comma, JS_FALSE, rval, JS_FALSE);

    str = js_ValueToString(cx, argv[0]);

    if (!str)

	return JS_FALSE;

    argv[0] = STRING_TO_JSVAL(str);

    return array_join_sub(cx, obj, str, JS_FALSE, rval, JS_FALSE);

}



#if !JS_HAS_MORE_PERL_FUN

static JSBool

array_nyi(JSContext *cx, const char *what)

{

    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_PROTO, what);

    return JS_FALSE;

}

#endif



static JSBool

InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector)

{

    jsval v;

    jsid id;

    jsuint index;



    if (!IndexToValue(cx, length, &v))

	return JS_FALSE;

    id = (jsid) cx->runtime->atomState.lengthAtom;

    if (!OBJ_DEFINE_PROPERTY(cx, obj, id, v,

                             array_length_getter, array_length_setter,

                             JSPROP_PERMANENT,

                             NULL)) {

	  return JS_FALSE;

    }

    if (!vector)

	return JS_TRUE;

    for (index = 0; index < length; index++) {

	if (!IndexToId(cx, index, &id))

	    return JS_FALSE;

	

	// DREAMWEAVER CHANGE

	// original code was JS_PropertyStub, JS_PropertyStub instead of

	// NULL, NULL

	// 

	// An explanation from DaveG:

	// 

	// > Let's suppose the user's document contains a [select] tag:

	// > 

	// >   [select name="mySelectTag"]

	// >     [option]one[/option]

	// >     [option]two[/option]

	// >   [/select]

	// > 

	// > That tag can be accessed through the DOM as follows:

	// > 

	// >   theSelect = document.forms[0].mySelectTag

	// >   alert("first option = " + theSelect.options[0]);

	// >   theSelect.options[0] = "new first option";

	// > 

	// > In order to implement that functionality, I found that I needed to override

	// > the default implementation of the Array type.  I implemented the options

	// > property on the select object so that it returns an OptionsArray object

	// > instead of an Array object.  The OptionsArray type is a subclass of Array,

	// > but it has its own implementation for getting the value of options[0] (the

	// > value is sucked out of the corresponding TTAG_OPTION Run in the Run tree)

	// > and setting the value of options[0] (the value is the Run tree is updated).

	// > 

	// > I found that I needed to change the base implementation of Array, so that I

	// > could create a sub-class of it (OptionsArray).

	if (!OBJ_DEFINE_PROPERTY(cx, obj, id, vector[index],

								 NULL, NULL, // DaveG: use getter/setter defined for class

                                 JSPROP_ENUMERATE,

                                 NULL)) {

	    return JS_FALSE;

	}

    }

    return JS_TRUE;

}



static JSBool

array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	      jsval *rval)

{

    jsuint len, half, i;

    jsid id, id2;

    jsval v, v2;



    if (!js_GetLengthProperty(cx, obj, &len))

	return JS_FALSE;



    half = len / 2;

    for (i = 0; i < half; i++) {

	if (!IndexToId(cx, i, &id))

	    return JS_FALSE;

	if (!IndexToId(cx, len - i - 1, &id2))

	    return JS_FALSE;

	if (!OBJ_GET_PROPERTY(cx, obj, id, &v))

	    return JS_FALSE;

	if (!OBJ_GET_PROPERTY(cx, obj, id2, &v2))

	    return JS_FALSE;



#if JS_HAS_SPARSE_ARRAYS

        /* This part isn't done yet. */



        if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))

                    return JS_FALSE;

                if (!prop) {

                    OBJ_DELETE_PROPERTY(cx, obj, id2, &v); /* v is junk. */

                    continue;

                }

                OBJ_DROP_PROPERTY(cx, obj2, prop);

#endif



	if (!OBJ_SET_PROPERTY(cx, obj, id, &v2))

	    return JS_FALSE;

	if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))

	    return JS_FALSE;

    }



    *rval = OBJECT_TO_JSVAL(obj);

    return JS_TRUE;

}



typedef struct QSortArgs {

    void         *vec;

    size_t       elsize;

    void         *pivot;

    JSComparator cmp;

    void         *arg;

} QSortArgs;



static int

sort_compare(const void *a, const void *b, void *arg);



static void

js_qsort_r(QSortArgs *qa, int lo, int hi)

{

    void *pivot, *vec, *arg, *a, *b;

    size_t elsize;

    JSComparator cmp;

    JSBool fastmove;

    int i, j, lohi, hilo;



    pivot = qa->pivot;

    vec = qa->vec;

    elsize = qa->elsize;

    cmp = qa->cmp;

    arg = qa->arg;



    fastmove = (cmp == sort_compare);

#define MEMMOVE(p,q,n) \

    (fastmove ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memmove(p, q, n))



    while (lo < hi) {

        i = lo;

        j = hi;

        a = (char *)vec + i * elsize;

        MEMMOVE(pivot, a, elsize);

        while (i < j) {

            b = (char *)vec + j * elsize;

            if (cmp(b, pivot, arg) >= 0) {

                j--;

                continue;

            }

            MEMMOVE(a, b, elsize);

            while (cmp(a, pivot, arg) <= 0) {

                i++;

                a = (char *)vec + i * elsize;

                if (i == j)

                    goto store_pivot;

            }

            MEMMOVE(b, a, elsize);

        }

        if (i > lo) {

      store_pivot:

            MEMMOVE(a, pivot, elsize);

        }

        if (i - lo < hi - i) {

            lohi = i - 1;

            if (lo < lohi)

                js_qsort_r(qa, lo, lohi);

            lo = i + 1;

        } else {

            hilo = i + 1;

            if (hilo < hi)

                js_qsort_r(qa, hilo, hi);

            hi = i - 1;

        }

    }



#undef MEMMOVE

}



JSBool

js_qsort(void *vec, size_t nel, size_t elsize, JSComparator cmp, void *arg)

{

    void *pivot;

    QSortArgs qa;



    pivot = malloc(elsize);

    if (!pivot)

	return JS_FALSE;

    qa.vec = vec;

    qa.elsize = elsize;

    qa.pivot = pivot;

    qa.cmp = cmp;

    qa.arg = arg;

    js_qsort_r(&qa, 0, (int)(nel - 1));

    free(pivot);

    return JS_TRUE;

}



typedef struct CompareArgs {

    JSContext  *context;

    jsval      fval;

    JSBool     status;

} CompareArgs;



static int

sort_compare(const void *a, const void *b, void *arg)

{

    jsval av = *(const jsval *)a, bv = *(const jsval *)b;

    CompareArgs *ca = (CompareArgs *) arg;

    JSContext *cx = ca->context;

    jsdouble cmp = -1;

    jsval fval, argv[2], rval;

    JSBool ok;



    fval = ca->fval;

    if (fval == JSVAL_NULL) {

	JSString *astr, *bstr;



	if (av == bv) {

	    cmp = 0;

	} else if (av == JSVAL_VOID || bv == JSVAL_VOID) {

	    /* Put undefined properties at the end. */

	    cmp = (av == JSVAL_VOID) ? 1 : -1;

	} else if ((astr = js_ValueToString(cx, av)) != NULL &&

		   (bstr = js_ValueToString(cx, bv)) != NULL) {

	    cmp = js_CompareStrings(astr, bstr);

	} else {

	    ca->status = JS_FALSE;

	}

    } else {

	argv[0] = av;

	argv[1] = bv;

	ok = js_InternalCall(cx,

			     OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fval)),

			     fval, 2, argv, &rval);

	if (ok) {

	    ok = js_ValueToNumber(cx, rval, &cmp);

	    /* Clamp cmp to -1, 0, 1. */

	    if (JSDOUBLE_IS_NaN(cmp)) {

		/* XXX report some kind of error here?  ECMA talks about

		 * 'consistent compare functions' that don't return NaN, but is

		 * silent about what the result should be.  So we currently

		 * ignore it.

		 */

		cmp = 0;

	    } else if (cmp != 0) {

		cmp = cmp > 0 ? 1 : -1;

	    }

	} else {

	    ca->status = ok;

	}

    }

    return (int)cmp;

}



/* XXXmccabe do the sort helper functions need to take int?  (Or can we claim

 * that 2^32 * 32 is too large to worry about?)  Something dumps when I change

 * to unsigned int; is qsort using -1 as a fencepost?

 */

static JSBool

array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsval fval;

    CompareArgs ca;

    jsuint len, newlen, i;

    jsval *vec;

    jsid id;



    if (argc > 0) {

	if (JSVAL_IS_PRIMITIVE(argv[0])) {

	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

				 JSMSG_BAD_SORT_ARG);

	    return JS_FALSE;

	}

	fval = argv[0];

    } else {

	fval = JSVAL_NULL;

    }



    if (!js_GetLengthProperty(cx, obj, &len))

	return JS_FALSE;

    if (len == 0)

        return JS_TRUE;

    vec = (jsval *) JS_malloc(cx, (size_t) len * sizeof(jsval));

    if (!vec)

	return JS_FALSE;



#if JS_HAS_SPARSE_ARRAYS

    newlen = 0;

#else

    newlen = len;

#endif



    for (i = 0; i < len; i++) {

	ca.status = IndexToId(cx, i, &id);

	if (!ca.status)

	    goto out;

#if JS_HAS_SPARSE_ARRAYS

        {

            JSObject *obj2;

            JSProperty *prop;

            ca.status = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);

            if (!ca.status)

                goto out;

            if (!prop) {

                vec[i] = JSVAL_VOID;

                continue;

            } else {

                newlen++;

                OBJ_DROP_PROPERTY(cx, obj2, prop);

            }

        }

#endif

	ca.status = OBJ_GET_PROPERTY(cx, obj, id, &vec[i]);

	if (!ca.status)

	    goto out;

    }



    ca.context = cx;

    ca.fval = fval;

    ca.status = JS_TRUE;

    if (!js_qsort(vec, (size_t) len, sizeof(jsval), sort_compare, &ca)) {

	JS_ReportOutOfMemory(cx);

	ca.status = JS_FALSE;

    }



    if (ca.status) {

	ca.status = InitArrayObject(cx, obj, newlen, vec);

	if (ca.status)

	    *rval = OBJECT_TO_JSVAL(obj);

#if JS_HAS_SPARSE_ARRAYS

        /* set length of newly-created array object to old length. */

        if (ca.status && newlen < len) {

            ca.status = js_SetLengthProperty(cx, obj, len);



            /* Delete any leftover properties greater than newlen. */

            while (ca.status && newlen < len) {

                jsval junk;



                ca.status = !IndexToId(cx, newlen, &id) ||

                    !OBJ_DELETE_PROPERTY(cx, obj, id, &junk);

                newlen++;

            }

        }

#endif

    }



out:

    if (vec)

	JS_free(cx, vec);

    return ca.status;

}



#ifdef NOTYET

/*

 * From "Programming perl", Larry Wall and Randall L. Schwartz, Copyright XXX

 * O'Reilly & Associates, Inc., but with Java primitive type sizes for i, l,

 * and so on:

 *

 *  a   An ASCII string, will be null padded.

 *  A   An ASCII string, will be space padded.

 *  b   A bit string, low-to-high order.

 *  B   A bit string, high-to-low order.

 *  h   A hexadecimal string, low nybble first.

 *  H   A hexadecimal string, high nybble first.

 *  c   A signed char value.

 *  C   An unsigned char value.

 *  s   A signed short (16-bit) value.

 *  S   An unsigned short (16-bit) value.

 *  i   A signed integer (32-bit) value.

 *  I   An unsigned integer (32-bit) value.

 *  l   A signed long (64-bit) value.

 *  L   An unsigned long (64-bit) value.

 *  n   A short in "network" byte order.

 *  N   An integer in "network" byte order.

 *  f   A single-precision float in IEEE format.

 *  d   A double-precision float in IEEE format.

 *  p   A pointer to a string.

 *  x   A null byte.

 *  X   Back up one byte.

 *  @   Null-fill to absolute position.

 *  u   A uuencoded string.

 *

 * Each letter may be followed by a number giving the repeat count.  Together

 * the letter and repeat count make a field specifier.  Field specifiers may

 * be separated by whitespace, which will be ignored.

 */

static JSBool

array_pack(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

#if JS_HAS_MORE_PERL_FUN

#else

    return array_nyi(cx, "pack");

#endif

}

#endif /* NOTYET */



static JSBool

array_push(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

#if JS_HAS_MORE_PERL_FUN

    jsuint length;

    uintN i;

    jsid id;



    if (!js_GetLengthProperty(cx, obj, &length))

	return JS_FALSE;

    for (i = 0; i < argc; i++) {

	if (!IndexToId(cx, length + i, &id))

	    return JS_FALSE;

	if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i]))

	    return JS_FALSE;

    }



    /*

     * If JS1.2, follow Perl4 by returning the last thing pushed.  Otherwise,

     * return the new array length.

     */

    length += argc;

    if (cx->version == JSVERSION_1_2) {

	*rval = argc ? argv[argc-1] : JSVAL_VOID;

    } else {

	if (!IndexToValue(cx, length, rval))

	    return JS_FALSE;

    }

    return js_SetLengthProperty(cx, obj, length);

#else

    return array_nyi(cx, "push");

#endif

}



static JSBool

array_pop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

#if JS_HAS_MORE_PERL_FUN

    jsuint index;

    jsid id;

    jsval junk;



    if (!js_GetLengthProperty(cx, obj, &index))

	return JS_FALSE;

    if (index > 0) {

	index--;

	if (!IndexToId(cx, index, &id))

	    return JS_FALSE;



	/* Get the to-be-deleted property's value into rval. */

	if (!OBJ_GET_PROPERTY(cx, obj, id, rval))

	    return JS_FALSE;



	if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk))

	    return JS_FALSE;

    }

    return js_SetLengthProperty(cx, obj, index);

#else

    return array_nyi(cx, "pop");

#endif

}



static JSBool

array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

#if JS_HAS_MORE_PERL_FUN

    jsuint length, i;

    jsid id, id2;

    jsval v, junk;



    if (!js_GetLengthProperty(cx, obj, &length))

	return JS_FALSE;

    if (length > 0) {

	length--;

	id = JSVAL_ZERO;



	/* Get the to-be-deleted property's value into rval ASAP. */

	if (!OBJ_GET_PROPERTY(cx, obj, id, rval))

	    return JS_FALSE;



	/*

	 * Slide down the array above the first element.

	 */

	if (length > 0) {

	    for (i = 1; i <= length; i++) {

		if (!IndexToId(cx, i, &id))

		    return JS_FALSE;

		if (!IndexToId(cx, i - 1, &id2))

		    return JS_FALSE;

		if (!OBJ_GET_PROPERTY(cx, obj, id, &v))

		    return JS_FALSE;

		if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))

		    return JS_FALSE;

	    }

	}



	/* Delete the only or last element. */

	if (!OBJ_DELETE_PROPERTY(cx, obj, id, &junk))

	    return JS_FALSE;

    }

    return js_SetLengthProperty(cx, obj, length);

#else

    return array_nyi(cx, "shift");

#endif

}



static JSBool

array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	      jsval *rval)

{

#if JS_HAS_MORE_PERL_FUN

    jsuint length, last;

    uintN i;

    jsid id, id2;

    jsval v;

#if JS_HAS_SPARSE_ARRAYS

    JSObject *obj2;

    JSProperty *prop;

#endif



    if (!js_GetLengthProperty(cx, obj, &length))

	return JS_FALSE;

    if (argc > 0) {

	/* Slide up the array to make room for argc at the bottom. */

	if (length > 0) {

	    last = length;

	    while (last--) {

		if (!IndexToId(cx, last, &id))

		    return JS_FALSE;

		if (!IndexToId(cx, last + argc, &id2))

		    return JS_FALSE;

#if JS_HAS_SPARSE_ARRAYS

                if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))

                    return JS_FALSE;

                if (!prop) {

                    OBJ_DELETE_PROPERTY(cx, obj, id2, &v); /* v is junk. */

                    continue;

                }

                OBJ_DROP_PROPERTY(cx, obj2, prop);

#endif

                if (!OBJ_GET_PROPERTY(cx, obj, id, &v))

		    return JS_FALSE;

		if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))

		    return JS_FALSE;

	    }

	}



	/* Copy from argv to the bottom of the array. */

	for (i = 0; i < argc; i++) {

	    if (!IndexToId(cx, i, &id))

		return JS_FALSE;

	    if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i]))

		return JS_FALSE;

	}



	/* Follow Perl by returning the new array length. */

	length += argc;

	if (!js_SetLengthProperty(cx, obj, length))

	    return JS_FALSE;

    }

    return IndexToValue(cx, length, rval);

#else

    return array_nyi(cx, "unshift");

#endif

}



static JSBool

array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

#if JS_HAS_MORE_PERL_FUN

    jsuint length, begin, end, count, delta, last;

    uintN i;

    jsdouble d;

    jsid id, id2;

    jsval v;

    JSObject *obj2;



    /* Nothing to do if no args.  Otherwise lock and load length. */

    if (argc == 0)

	return JS_TRUE;

    if (!js_GetLengthProperty(cx, obj, &length))

	return JS_FALSE;



    /* Convert the first argument into a starting index. */

    if (!js_ValueToNumber(cx, *argv, &d))

	return JS_FALSE;

    d = js_DoubleToInteger(d);

    if (d < 0) {

	d += length;

	if (d < 0)

	    d = 0;

    } else if (d > length) {

	d = length;

    }

    begin = (jsuint)d; /* d has been clamped to uint32 */

    argc--;

    argv++;



    /* Convert the second argument from a count into a fencepost index. */

    delta = length - begin;

    if (argc == 0) {

	count = delta;

	end = length;

    } else {

	if (!js_ValueToNumber(cx, *argv, &d))

	    return JS_FALSE;

	d = js_DoubleToInteger(d);

	if (d < 0)

	    d = 0;

	else if (d > delta)

	    d = delta;

	count = (jsuint)d;

	end = begin + count;

	argc--;

	argv++;

    }



    if (count == 1 && cx->version == JSVERSION_1_2) {

	/*

	 * JS lacks "list context", whereby in Perl one turns the single

	 * scalar that's spliced out into an array just by assigning it to

	 * @single instead of $single, or by using it as Perl push's first

	 * argument, for instance.

	 *

	 * JS1.2 emulated Perl too closely and returned a non-Array for

	 * the single-splice-out case, requiring callers to test and wrap

	 * in [] if necessary.  So JS1.3, default, and other versions all

	 * return an array of length 1 for uniformity.

	 */

	if (!IndexToId(cx, begin, &id))

	    return JS_FALSE;

	if (!OBJ_GET_PROPERTY(cx, obj, id, rval))

	    return JS_FALSE;

    } else {

	if (cx->version != JSVERSION_1_2 || count > 0) {

	    /*

	     * Create a new array value to return.  Our ECMA v2 proposal specs

	     * that splice always returns an array value, even when given no

	     * arguments.  We think this is best because it eliminates the need

	     * for callers to do an extra test to handle the empty splice case.

	     */

	    obj2 = js_NewArrayObject(cx, 0, NULL);

	    if (!obj2)

		return JS_FALSE;

	    *rval = OBJECT_TO_JSVAL(obj2);



            /* If there are elements to remove, put them into the return value. */

            if (count > 0) {

                for (last = begin; last < end; last++) {

                    if (!IndexToId(cx, last, &id))

                        return JS_FALSE;

                    if (!IndexToId(cx, last - begin, &id2))

                        return JS_FALSE;

                    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))

                        return JS_FALSE;

                    if (!OBJ_SET_PROPERTY(cx, obj2, id2, &v))

                        return JS_FALSE;

                }

            }

	}

    }



    /* Find the direction (up or down) to copy and make way for argv. */

    if (argc > count) {

	delta = (jsuint)argc - count;

	last = length;

	/* (uint) end could be 0, so can't use vanilla >= test */

	while (last-- > end) {

	    if (!IndexToId(cx, last, &id))

		return JS_FALSE;

	    if (!IndexToId(cx, last + delta, &id2))

		return JS_FALSE;

	    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))

		return JS_FALSE;

	    if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))

		return JS_FALSE;

	}

	length += delta;

    } else if (argc < count) {

	delta = count - (jsuint)argc;

	for (last = end; last < length; last++) {

	    if (!IndexToId(cx, last, &id))

		return JS_FALSE;

	    if (!IndexToId(cx, last - delta, &id2))

		return JS_FALSE;

	    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))

		return JS_FALSE;

	    if (!OBJ_SET_PROPERTY(cx, obj, id2, &v))

		return JS_FALSE;

	}

	length -= delta;

    }



    /* Copy from argv into the hole to complete the splice. */

    for (i = 0; i < argc; i++) {

	if (!IndexToId(cx, begin + i, &id))

	    return JS_FALSE;

	if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i]))

	    return JS_FALSE;

    }



    /* Update length in case we deleted elements from the end. */

    return js_SetLengthProperty(cx, obj, length);

#else

    return array_nyi(cx, "splice");

#endif

}



#if JS_HAS_SEQUENCE_OPS

/*

 * Python-esque sequence operations.

 */

static JSBool

array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSObject *nobj, *aobj;

    jsuint slot, length, alength;

    jsid id, id2;

    jsval v;

    uintN i;



    nobj = js_NewArrayObject(cx, 0, NULL);

    if (!nobj)

	return JS_FALSE;



    /* Only add the first element as an array if it looks like one.  Treat the

     * target the same way as the arguments.

     */



    /* XXXmccabe Might make sense to recast all of this as a do-while. */

    if (js_HasLengthProperty(cx, obj, &length)) {

	for (slot = 0; slot < length; slot++) {

	    if (!IndexToId(cx, slot, &id))

		return JS_FALSE;

	    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))

		return JS_FALSE;

	    if (!OBJ_SET_PROPERTY(cx, nobj, id, &v))

		return JS_FALSE;

	}

    } else {

	length = 1;

	v = OBJECT_TO_JSVAL(obj);

	if (!OBJ_SET_PROPERTY(cx, nobj, JSVAL_ZERO, &v))

	    return JS_FALSE;

    }



    for (i = 0; i < argc; i++) {

	v = argv[i];

	if (JSVAL_IS_OBJECT(v)) {

	    aobj = JSVAL_TO_OBJECT(v);

	    if (aobj && js_HasLengthProperty(cx, aobj, &alength)) {

		for (slot = 0; slot < alength; slot++) {

		    if (!IndexToId(cx, slot, &id))

			return JS_FALSE;

		    if (!IndexToId(cx, length + slot, &id2))

			return JS_FALSE;

		    if (!OBJ_GET_PROPERTY(cx, aobj, id, &v))

			return JS_FALSE;

		    if (!OBJ_SET_PROPERTY(cx, nobj, id2, &v))

			return JS_FALSE;

		}

		length += alength;

		continue;

	    }

	}



	if (!IndexToId(cx, length, &id))

	    return JS_FALSE;

	if (!OBJ_SET_PROPERTY(cx, nobj, id, &v))

	    return JS_FALSE;

	length++;

    }



    *rval = OBJECT_TO_JSVAL(nobj);

    return JS_TRUE;

}



static JSBool

array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSObject *nobj;

    jsuint length, begin, end, slot;

    jsdouble d;

    jsid id, id2;

    jsval v;



    nobj = js_NewArrayObject(cx, 0, NULL);

    if (!nobj)

	return JS_FALSE;



    if (!js_GetLengthProperty(cx, obj, &length))

	return JS_FALSE;

    begin = 0;

    end = length;



    if (argc > 0) {

	if (!js_ValueToNumber(cx, argv[0], &d))

	    return JS_FALSE;

	d = js_DoubleToInteger(d);

	if (d < 0) {

	    d += length;

	    if (d < 0)

		d = 0;

	} else if (d > length) {

	    d = length;

	}

	begin = (jsuint)d;



	if (argc > 1) {

	    if (!js_ValueToNumber(cx, argv[1], &d))

		return JS_FALSE;

	    d = js_DoubleToInteger(d);

	    if (d < 0) {

		d += length;

		if (d < 0)

		    d = 0;

	    } else if (d > length) {

		d = length;

	    }

	    end = (jsuint)d;

	}

    }



    for (slot = begin; slot < end; slot++) {

	if (!IndexToId(cx, slot, &id))

	    return JS_FALSE;

	if (!IndexToId(cx, slot - begin, &id2))

	    return JS_FALSE;

	if (!OBJ_GET_PROPERTY(cx, obj, id, &v))

	    return JS_FALSE;

	if (!OBJ_SET_PROPERTY(cx, nobj, id2, &v))

	    return JS_FALSE;

    }

    *rval = OBJECT_TO_JSVAL(nobj);

    return JS_TRUE;

}

#endif /* JS_HAS_SEQUENCE_OPS */



static JSFunctionSpec array_methods[] = {

#if JS_HAS_TOSOURCE

    {js_toSource_str,       array_toSource,         0,0,0},

#endif

    {js_toString_str,       array_toString,         0,0,0},

    {js_toLocaleString_str, array_toLocaleString,   0,0,0},



    /* Perl-ish methods. */

    {"join",                array_join,             1,0,0},

    {"reverse",             array_reverse,          0,0,0},

    {"sort",                array_sort,             1,0,0},

#ifdef NOTYET

    {"pack",                array_pack,             1,0,0},

#endif

    {"push",                array_push,             1,0,0},

    {"pop",                 array_pop,              0,0,0},

    {"shift",               array_shift,            0,0,0},

    {"unshift",             array_unshift,          1,0,0},

    {"splice",              array_splice,           1,0,0},



    /* Python-esque sequence methods. */

#if JS_HAS_SEQUENCE_OPS

    {"concat",              array_concat,           0,0,0},

    {"slice",               array_slice,            0,0,0},

#endif



    {0,0,0,0,0}

};



static JSBool

Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsuint length;

    jsval *vector;



    /* If called without new, replace obj with a new Array object. */

    if (!cx->fp->constructing) {

	obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL);

	if (!obj)

	    return JS_FALSE;

	*rval = OBJECT_TO_JSVAL(obj);

    }



    if (argc == 0) {

	length = 0;

	vector = NULL;

    } else if (cx->version == JSVERSION_1_2) {

	length = (jsuint) argc;

	vector = argv;

    } else {

        if (argc > 1) {

	    length = (jsuint) argc;

	    vector = argv;

        } else if (!JSVAL_IS_NUMBER(argv[0])) {

	    length = 1;

	    vector = argv;

        } else if (ValueIsLength(cx, argv[0], &length)) {

	    vector = NULL;

	} else {

	    return JS_FALSE;

	}

    }

    return InitArrayObject(cx, obj, length, vector);

}



JSObject *

js_InitArrayClass(JSContext *cx, JSObject *obj)

{

    JSObject *proto;



    proto = JS_InitClass(cx, obj, NULL, &js_ArrayClass, Array, 1,

			 NULL, array_methods, NULL, NULL);



    /* Initialize the Array prototype object so it gets a length property. */

    if (!proto || !InitArrayObject(cx, proto, 0, NULL))

	return NULL;

    return proto;

}



JSObject *

js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector)

{

    JSObject *obj;



    obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL);

    if (!obj)

	return NULL;

    if (!InitArrayObject(cx, obj, length, vector)) {

	cx->newborn[GCX_OBJECT] = NULL;

	return NULL;

    }

    return obj;

}



// DREAMWEAVER: Added by DaveG so Dreamweaver can access this InitArrayObject

JSBool

JS_InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector)

{

    return InitArrayObject(cx, obj, length, vector);

}

 

**** End of jsarray.c ****

 

**** Start of jsarray.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsarray_h___

#define jsarray_h___

/*

 * JS Array interface.

 */

#include "jsprvtd.h"

#include "jspubtd.h"



JS_BEGIN_EXTERN_C



extern JSClass js_ArrayClass;



extern JSObject *

js_InitArrayClass(JSContext *cx, JSObject *obj);



extern JSObject *

js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector);



extern JSBool

js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);



extern JSBool

js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length);



extern JSBool

js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);



/*

 * JS-specific qsort function.

 */

typedef int (*JSComparator)(const void *a, const void *b, void *arg);



extern JSBool

js_qsort(void *vec, size_t nel, size_t elsize, JSComparator cmp, void *arg);



// DREAMWEAVER: Added this function

extern JSBool

JS_InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector);



JS_END_EXTERN_C



#endif /* jsarray_h___ */

 

**** End of jsarray.h ****

 

**** Start of jsatom.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS atom table.

 */

#include "jsstddef.h"

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jshash.h" /* Added by JSIFY */

#include "jsprf.h"

#include "jsapi.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsgc.h"

#include "jslock.h"

#include "jsnum.h"

#include "jsopcode.h"

#include "jsstr.h"



extern const char js_Error_str[];       /* trivial, from jsexn.h */



/*

 * Keep this in sync with jspubtd.h -- an assertion below will insist that

 * its length match the JSType enum's JSTYPE_LIMIT limit value.

 */

const char *js_type_str[] = {

    "undefined",

    "object",

    "function",

    "string",

    "number",

    "boolean",

};



const char *js_boolean_str[] = {

    js_false_str,

    js_true_str

};



const char js_Arguments_str[]       = "Arguments";

const char js_Array_str[]           = "Array";

const char js_Boolean_str[]         = "Boolean";

const char js_Call_str[]            = "Call";

const char js_Date_str[]            = "Date";

const char js_Function_str[]        = "Function";

const char js_Math_str[]            = "Math";

const char js_Number_str[]          = "Number";

const char js_Object_str[]          = "Object";

const char js_RegExp_str[]          = "RegExp";

const char js_Script_str[]          = "Script";

const char js_String_str[]          = "String";

const char js_anonymous_str[]       = "anonymous";

const char js_arguments_str[]       = "arguments";

const char js_arity_str[]           = "arity";

const char js_callee_str[]          = "callee";

const char js_caller_str[]          = "caller";

const char js_class_prototype_str[] = "prototype";

const char js_constructor_str[]     = "constructor";

const char js_count_str[]           = "__count__";

const char js_eval_str[]            = "eval";

const char js_getter_str[]          = "getter";

const char js_get_str[]             = "get";

const char js_index_str[]           = "index";

const char js_input_str[]           = "input";

const char js_length_str[]          = "length";

const char js_name_str[]            = "name";

const char js_parent_str[]          = "__parent__";

const char js_proto_str[]           = "__proto__";

const char js_setter_str[]          = "setter";

const char js_set_str[]             = "set";

const char js_toSource_str[]        = "toSource";

const char js_toString_str[]        = "toString";

const char js_toLocaleString_str[]  = "toLocaleString";

const char js_valueOf_str[]         = "valueOf";



#define HASH_OBJECT(o)  ((JSHashNumber)(o) >> JSVAL_TAGBITS)

#define HASH_INT(i)     ((JSHashNumber)(i))

#define HASH_DOUBLE(dp) ((JSHashNumber)(((uint32*)(dp))[0] ^ ((uint32*)(dp))[1]))

#define HASH_BOOLEAN(b) ((JSHashNumber)(b))



JS_STATIC_DLL_CALLBACK(JSHashNumber)

js_hash_atom_key(const void *key)

{

    jsval v;

    jsdouble *dp;



    /* Order JSVAL_IS_* tests by likelihood of success. */

    v = (jsval)key;

    if (JSVAL_IS_STRING(v))

	return js_HashString(JSVAL_TO_STRING(v));

    if (JSVAL_IS_INT(v))

	return HASH_INT(JSVAL_TO_INT(v));

    if (JSVAL_IS_DOUBLE(v)) {

	dp = JSVAL_TO_DOUBLE(v);

	return HASH_DOUBLE(dp);

    }

    if (JSVAL_IS_OBJECT(v))

	return HASH_OBJECT(JSVAL_TO_OBJECT(v));

    if (JSVAL_IS_BOOLEAN(v))

	return HASH_BOOLEAN(JSVAL_TO_BOOLEAN(v));

    return (JSHashNumber)v;

}



JS_STATIC_DLL_CALLBACK(intN)

js_compare_atom_keys(const void *k1, const void *k2)

{

    jsval v1, v2;



    v1 = (jsval)k1, v2 = (jsval)k2;

    if (JSVAL_IS_STRING(v1) && JSVAL_IS_STRING(v2))

	return !js_CompareStrings(JSVAL_TO_STRING(v1), JSVAL_TO_STRING(v2));

    if (JSVAL_IS_DOUBLE(v1) && JSVAL_IS_DOUBLE(v2)) {

	double d1 = *JSVAL_TO_DOUBLE(v1);

	double d2 = *JSVAL_TO_DOUBLE(v2);

	if (JSDOUBLE_IS_NaN(d1))

	    return JSDOUBLE_IS_NaN(d2);

#ifdef XP_PC

	/* XXX MSVC miscompiles such that (NaN == 0) */

	if (JSDOUBLE_IS_NaN(d2))

	    return JS_FALSE;

#endif

	return d1 == d2;

    }

    return v1 == v2;

}



JS_STATIC_DLL_CALLBACK(int)

js_compare_stub(const void *v1, const void *v2)

{

    return 1;

}



JS_STATIC_DLL_CALLBACK(void *)

js_alloc_atom_space(void *priv, size_t size)

{

    return malloc(size);

}



JS_STATIC_DLL_CALLBACK(void)

js_free_atom_space(void *priv, void *item)

{

    free(item);

}



JS_STATIC_DLL_CALLBACK(JSHashEntry *)

js_alloc_atom(void *priv, const void *key)

{

    JSAtomState *state = (JSAtomState *) priv;

    JSAtom *atom;



    atom = (JSAtom *) malloc(sizeof(JSAtom));

    if (!atom)

	return NULL;

#ifdef JS_THREADSAFE

    state->tablegen++;

#endif

    atom->entry.key = key;

    atom->entry.value = NULL;

    atom->flags = 0;

    atom->kwindex = -1;

    atom->number = state->number++;

    return &atom->entry;

}



JS_STATIC_DLL_CALLBACK(void)

js_free_atom(void *priv, JSHashEntry *he, uintN flag)

{

    if (flag != HT_FREE_ENTRY)

	return;

#ifdef JS_THREADSAFE

    ((JSAtomState *)priv)->tablegen++;

#endif

    free(he);

}



static JSHashAllocOps atom_alloc_ops = {

    js_alloc_atom_space,    js_free_atom_space,

    js_alloc_atom,          js_free_atom

};



#define JS_ATOM_HASH_SIZE   1024



JSBool

js_InitAtomState(JSContext *cx, JSAtomState *state)

{

    uintN i;



    memset(state, 0, sizeof *state);

    state->runtime = cx->runtime;

    state->table = JS_NewHashTable(JS_ATOM_HASH_SIZE, js_hash_atom_key,

				   js_compare_atom_keys, js_compare_stub,

				   &atom_alloc_ops, state);

    if (!state->table) {

	JS_ReportOutOfMemory(cx);

	return JS_FALSE;

    }

#ifdef JS_THREADSAFE

    js_InitLock(&state->lock);

    state->tablegen = 0;

#endif



#define FROB(lval,str)                                                        \

    JS_BEGIN_MACRO                                                            \

	if (!(state->lval = js_Atomize(cx, str, strlen(str), ATOM_PINNED)))   \

	    goto bad;                                                         \

    JS_END_MACRO



    JS_ASSERT(sizeof js_type_str / sizeof js_type_str[0] == JSTYPE_LIMIT);

    for (i = 0; i < JSTYPE_LIMIT; i++)

	FROB(typeAtoms[i],        js_type_str[i]);



    FROB(booleanAtoms[0],         js_false_str);

    FROB(booleanAtoms[1],         js_true_str);

    FROB(nullAtom,                js_null_str);



    FROB(ArgumentsAtom,           js_Arguments_str);

    FROB(ArrayAtom,               js_Array_str);

    FROB(BooleanAtom,             js_Boolean_str);

    FROB(CallAtom,                js_Call_str);

    FROB(DateAtom,                js_Date_str);

    FROB(ErrorAtom,               js_Error_str);

    FROB(FunctionAtom,            js_Function_str);

    FROB(MathAtom,                js_Math_str);

    FROB(NumberAtom,              js_Number_str);

    FROB(ObjectAtom,              js_Object_str);

    FROB(RegExpAtom,              js_RegExp_str);

    FROB(ScriptAtom,              js_Script_str);

    FROB(StringAtom,              js_String_str);

    FROB(anonymousAtom,           js_anonymous_str);

    FROB(argumentsAtom,           js_arguments_str);

    FROB(arityAtom,               js_arity_str);

    FROB(calleeAtom,              js_callee_str);

    FROB(callerAtom,              js_caller_str);

    FROB(classPrototypeAtom,      js_class_prototype_str);

    FROB(constructorAtom,         js_constructor_str);

    FROB(countAtom,               js_count_str);

    FROB(evalAtom,                js_eval_str);

    FROB(getAtom,                 js_get_str);

    FROB(getterAtom,              js_getter_str);

    FROB(indexAtom,               js_index_str);

    FROB(inputAtom,               js_input_str);

    FROB(lengthAtom,              js_length_str);

    FROB(nameAtom,                js_name_str);

    FROB(parentAtom,              js_parent_str);

    FROB(protoAtom,               js_proto_str);

    FROB(setAtom,                 js_set_str);

    FROB(setterAtom,              js_setter_str);

    FROB(toSourceAtom,            js_toSource_str);

    FROB(toStringAtom,            js_toString_str);

    FROB(toLocaleStringAtom,      js_toLocaleString_str);

    FROB(valueOfAtom,             js_valueOf_str);



#undef FROB



    return JS_TRUE;



bad:

    js_FreeAtomState(cx, state);

    return JS_FALSE;

}



/* NB: cx unused; js_FinishAtomState calls us with null cx. */

void

js_FreeAtomState(JSContext *cx, JSAtomState *state)

{

    if (state->interns != 0)

        return;

    state->runtime = NULL;

    JS_HashTableDestroy(state->table);

    state->table = NULL;

    state->number = 0;

#ifdef JS_THREADSAFE

    js_FinishLock(&state->lock);

#endif

}



typedef struct UninternArgs {

    JSRuntime   *rt;

    jsatomid    leaks;

} UninternArgs;



JS_STATIC_DLL_CALLBACK(intN)

js_atom_uninterner(JSHashEntry *he, intN i, void *arg)

{

    JSAtom *atom;

    UninternArgs *args;



    atom = (JSAtom *)he;

    args = (UninternArgs *)arg;

    if (!(atom->flags & ATOM_INTERNED) || !ATOM_IS_STRING(atom))

        args->leaks++;

    if (ATOM_IS_STRING(atom))

        js_FinalizeStringRT(args->rt, ATOM_TO_STRING(atom));

    return HT_ENUMERATE_NEXT;

}



void

js_FinishAtomState(JSAtomState *state)

{

    UninternArgs args;



    if (state->interns == 0)

        return;

    args.rt = state->runtime;

    args.leaks = 0;

    JS_HashTableEnumerateEntries(state->table, js_atom_uninterner, &args);

#ifdef DEBUG

    if (args.leaks != 0) {

        fprintf(stderr,

"JS engine warning: %lu atoms remain after destroying the JSRuntime.\n"

"                   These atoms may point to freed memory. Things reachable\n"

"                   through them have not been finalized.\n",

                (unsigned long) args.leaks);

    }

#endif

    state->interns = 0;

    js_FreeAtomState(NULL, state);

}



typedef struct MarkArgs {

    uintN           gcflags;

    JSGCThingMarker mark;

    void            *data;

} MarkArgs;



JS_STATIC_DLL_CALLBACK(intN)

js_atom_marker(JSHashEntry *he, intN i, void *arg)

{

    JSAtom *atom;

    MarkArgs *args;

    jsval key;



    atom = (JSAtom *)he;

    args = (MarkArgs *)arg;

    if ((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) ||

        (args->gcflags & GC_KEEP_ATOMS)) {

	atom->flags |= ATOM_MARK;

	key = ATOM_KEY(atom);

	if (JSVAL_IS_GCTHING(key)) {

	    args->mark(JSVAL_TO_GCTHING(key), args->data);

	}

    }

    return HT_ENUMERATE_NEXT;

}



void

js_MarkAtomState(JSAtomState *state, uintN gcflags, JSGCThingMarker mark,

                 void *data)

{

    MarkArgs args;



    args.gcflags = gcflags;

    args.mark = mark;

    args.data = data;

    JS_HashTableEnumerateEntries(state->table, js_atom_marker, &args);

}



JS_STATIC_DLL_CALLBACK(intN)

js_atom_sweeper(JSHashEntry *he, intN i, void *arg)

{

    JSAtom *atom;



    atom = (JSAtom *)he;

    if (atom->flags & ATOM_MARK) {

        atom->flags &= ~ATOM_MARK;

        return HT_ENUMERATE_NEXT;

    }

    JS_ASSERT((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) == 0);

    atom->entry.key = NULL;

    atom->flags = 0;

    return HT_ENUMERATE_REMOVE;

}



void

js_SweepAtomState(JSAtomState *state)

{

    JS_HashTableEnumerateEntries(state->table, js_atom_sweeper, NULL);

}



JS_STATIC_DLL_CALLBACK(intN)

js_atom_unpinner(JSHashEntry *he, intN i, void *arg)

{

    JSAtom *atom;



    atom = (JSAtom *)he;

    atom->flags &= ~ATOM_PINNED;

    return HT_ENUMERATE_NEXT;

}



void

js_UnpinPinnedAtoms(JSAtomState *state)

{

    JS_HashTableEnumerateEntries(state->table, js_atom_unpinner, NULL);

}



static JSAtom *

js_AtomizeHashedKey(JSContext *cx, jsval key, JSHashNumber keyHash, uintN flags)

{

    JSAtomState *state;

    JSHashTable *table;

    JSHashEntry *he, **hep;

    JSAtom *atom;



    state = &cx->runtime->atomState;

    JS_LOCK(&state->lock, cx);

    table = state->table;

    hep = JS_HashTableRawLookup(table, keyHash, (void *)key);

    if ((he = *hep) == NULL) {

	he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL);

	if (!he) {

	    JS_ReportOutOfMemory(cx);

	    atom = NULL;

	    goto out;

	}

    }



    atom = (JSAtom *)he;

    atom->flags |= flags;

out:

    JS_UNLOCK(&state->lock,cx);

    return atom;

}



JSAtom *

js_AtomizeObject(JSContext *cx, JSObject *obj, uintN flags)

{

    jsval key;

    JSHashNumber keyHash;



    /* XXX must be set in the following order or MSVC1.52 will crash */

    keyHash = HASH_OBJECT(obj);

    key = OBJECT_TO_JSVAL(obj);

    return js_AtomizeHashedKey(cx, key, keyHash, flags);

}



JSAtom *

js_AtomizeBoolean(JSContext *cx, JSBool b, uintN flags)

{

    jsval key;

    JSHashNumber keyHash;



    key = BOOLEAN_TO_JSVAL(b);

    keyHash = HASH_BOOLEAN(b);

    return js_AtomizeHashedKey(cx, key, keyHash, flags);

}



JSAtom *

js_AtomizeInt(JSContext *cx, jsint i, uintN flags)

{

    jsval key;

    JSHashNumber keyHash;



    key = INT_TO_JSVAL(i);

    keyHash = HASH_INT(i);

    return js_AtomizeHashedKey(cx, key, keyHash, flags);

}



/* Worst-case alignment grain and aligning macro for 2x-sized buffer. */

#define ALIGNMENT(t)    JS_MAX(JSVAL_ALIGN, sizeof(t))

#define ALIGN(b,t)      ((t*) &(b)[ALIGNMENT(t) - (jsuword)(b) % ALIGNMENT(t)])



JSAtom *

js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags)

{

    jsdouble *dp;

    JSHashNumber keyHash;

    jsval key;

    JSAtomState *state;

    JSHashTable *table;

    JSHashEntry *he, **hep;

    JSAtom *atom;

    char buf[2 * ALIGNMENT(double)];



    dp = ALIGN(buf, double);

    *dp = d;

    keyHash = HASH_DOUBLE(dp);

    key = DOUBLE_TO_JSVAL(dp);

    state = &cx->runtime->atomState;

    JS_LOCK(&state->lock, cx);

    table = state->table;

    hep = JS_HashTableRawLookup(table, keyHash, (void *)key);

    if ((he = *hep) == NULL) {

#ifdef JS_THREADSAFE

	uint32 gen = state->tablegen;

#endif

	JS_UNLOCK(&state->lock,cx);

	if (!js_NewDoubleValue(cx, d, &key))

	    return NULL;

	JS_LOCK(&state->lock, cx);

#ifdef JS_THREADSAFE

	if (state->tablegen != gen) {

	    hep = JS_HashTableRawLookup(table, keyHash, (void *)key);

	    if ((he = *hep) != NULL) {

		atom = (JSAtom *)he;

		goto out;

	    }

	}

#endif

	he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL);

	if (!he) {

	    JS_ReportOutOfMemory(cx);

	    atom = NULL;

	    goto out;

	}

    }



    atom = (JSAtom *)he;

    atom->flags |= flags;

out:

    JS_UNLOCK(&state->lock,cx);

    return atom;

}



JSAtom *

js_AtomizeString(JSContext *cx, JSString *str, uintN flags)

{

    JSHashNumber keyHash;

    jsval key;

    JSAtomState *state;

    JSHashTable *table;

    JSHashEntry *he, **hep;

    JSAtom *atom;



    keyHash = js_HashString(str);

    key = STRING_TO_JSVAL(str);

    state = &cx->runtime->atomState;

    JS_LOCK(&state->lock, cx);

    table = state->table;

    hep = JS_HashTableRawLookup(table, keyHash, (void *)key);

    if ((he = *hep) == NULL) {

	if (flags & ATOM_TMPSTR) {

#ifdef JS_THREADSAFE

	    uint32 gen = state->tablegen;

#endif

	    JS_UNLOCK(&state->lock, cx);

            str = (flags & ATOM_NOCOPY)

                  ? js_NewString(cx, str->chars, str->length, 0)

                  : js_NewStringCopyN(cx, str->chars, str->length, 0);

	    if (!str)

		return NULL;

	    key = STRING_TO_JSVAL(str);

	    JS_LOCK(&state->lock, cx);

#ifdef JS_THREADSAFE

	    if (state->tablegen != gen) {

		hep = JS_HashTableRawLookup(table, keyHash, (void *)key);

		if ((he = *hep) != NULL) {

		    atom = (JSAtom *)he;

		    if (flags & ATOM_NOCOPY)

			str->chars = NULL;

		    goto out;

		}

	    }

#endif

	}

	he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL);

	if (!he) {

	    JS_ReportOutOfMemory(cx);

	    atom = NULL;

	    goto out;

	}

    }



    atom = (JSAtom *)he;

    atom->flags |= flags & (ATOM_PINNED | ATOM_INTERNED);

    if (flags & ATOM_INTERNED)

        state->interns++;

out:

    JS_UNLOCK(&state->lock,cx);

    return atom;

}



JS_FRIEND_API(JSAtom *)

js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags)

{

    jschar *chars;

    JSString *str;

    JSAtom *atom;

    char buf[2 * ALIGNMENT(JSString)];



    /*

     * Avoiding the malloc in js_InflateString on shorter strings saves us

     * over 20,000 malloc calls on mozilla browser startup. This compares to

     * only 131 calls where the string is longer than a 31 char (net) buffer.

     * The vast majority of atomized strings are already in the hashtable. So

     * js_AtomizeString rarely has to copy the temp string we make.

     */

#define ATOMIZE_BUF_MAX 32

    jschar inflated[ATOMIZE_BUF_MAX];



    if (length < ATOMIZE_BUF_MAX) {

        js_InflateStringToBuffer(inflated, bytes, length);

        chars = inflated;

    } else {

        chars = js_InflateString(cx, bytes, length);

        if (!chars)

	    return NULL;

        flags |= ATOM_NOCOPY;

    }



    str = ALIGN(buf, JSString);



    str->chars = chars;

    str->length = length;

    atom = js_AtomizeString(cx, str, ATOM_TMPSTR | flags);

    if (chars != inflated && (!atom || ATOM_TO_STRING(atom)->chars != chars))

	JS_free(cx, chars);

    return atom;

}



JS_FRIEND_API(JSAtom *)

js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags)

{

    JSString *str;

    char buf[2 * ALIGNMENT(JSString)];



    str = ALIGN(buf, JSString);

    str->chars = (jschar *)chars;

    str->length = length;

    return js_AtomizeString(cx, str, ATOM_TMPSTR | flags);

}



JSAtom *

js_AtomizeValue(JSContext *cx, jsval value, uintN flags)

{

    if (JSVAL_IS_STRING(value))

	return js_AtomizeString(cx, JSVAL_TO_STRING(value), flags);

    if (JSVAL_IS_INT(value))

	return js_AtomizeInt(cx, JSVAL_TO_INT(value), flags);

    if (JSVAL_IS_DOUBLE(value))

	return js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(value), flags);

    if (JSVAL_IS_OBJECT(value))

	return js_AtomizeObject(cx, JSVAL_TO_OBJECT(value), flags);

    if (JSVAL_IS_BOOLEAN(value))

	return js_AtomizeBoolean(cx, JSVAL_TO_BOOLEAN(value), flags);

    return js_AtomizeHashedKey(cx, value, (JSHashNumber)value, flags);

}



JSAtom *

js_ValueToStringAtom(JSContext *cx, jsval v)

{

    JSString *str;



    str = js_ValueToString(cx, v);

    if (!str)

	return NULL;

    return js_AtomizeString(cx, str, 0);

}



JS_STATIC_DLL_CALLBACK(JSHashNumber)

js_hash_atom_ptr(const void *key)

{

    const JSAtom *atom = key;

    return atom->number;

}



JS_STATIC_DLL_CALLBACK(void *)

js_alloc_temp_space(void *priv, size_t size)

{

    JSContext *cx = priv;

    void *space;



    JS_ARENA_ALLOCATE(space, &cx->tempPool, size);

    if (!space)

        JS_ReportOutOfMemory(cx);

    return space;

}



JS_STATIC_DLL_CALLBACK(void)

js_free_temp_space(void *priv, void *item)

{

}



JS_STATIC_DLL_CALLBACK(JSHashEntry *)

js_alloc_temp_entry(void *priv, const void *key)

{

    JSContext *cx = priv;

    JSAtomListElement *ale;



    JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool);

    if (!ale) {

        JS_ReportOutOfMemory(cx);

        return NULL;

    }

    return &ale->entry;

}



JS_STATIC_DLL_CALLBACK(void)

js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag)

{

}



static JSHashAllocOps temp_alloc_ops = {

    js_alloc_temp_space,    js_free_temp_space,

    js_alloc_temp_entry,    js_free_temp_entry

};



JSAtomListElement *

js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al)

{

    JSAtomListElement *ale, *ale2, *next;

    JSHashEntry **hep;



    ATOM_LIST_LOOKUP(ale, hep, al, atom);

    if (!ale) {

        if (al->count <= 5) {

            /* Few enough for linear search, no hash table needed. */

            JS_ASSERT(!al->table);

            ale = (JSAtomListElement *)js_alloc_temp_entry(cx, atom);

            if (!ale)

                return NULL;

            ALE_SET_ATOM(ale, atom);

            ALE_SET_NEXT(ale, al->list);

            al->list = ale;

        } else {

            /* We want to hash.  Have we already made a hash table? */

            if (!al->table) {

                /* No hash table yet, so hep had better be null! */

                JS_ASSERT(!hep);

                al->table = JS_NewHashTable(8, js_hash_atom_ptr,

                                            JS_CompareValues, JS_CompareValues,

                                            &temp_alloc_ops, cx);

                if (!al->table)

                    return NULL;



                /* Insert each ale on al->list into the new hash table. */

                for (ale2 = al->list; ale2; ale2 = next) {

                    next = ALE_NEXT(ale2);

                    ale2->entry.keyHash = ALE_ATOM(ale2)->number;

                    hep = JS_HashTableRawLookup(al->table, ale2->entry.keyHash,

                                                ale2->entry.key);

                    ALE_SET_NEXT(ale2, *hep);

                    *hep = &ale2->entry;

                }

                al->list = NULL;



                /* Set hep for insertion of atom's ale, immediately below. */

                hep = JS_HashTableRawLookup(al->table, atom->number, atom);

            }



            /* Finally, add an entry for atom into the hash bucket at hep. */

            ale = (JSAtomListElement *)

                JS_HashTableRawAdd(al->table, hep, atom->number, atom, NULL);

            if (!ale)

                return NULL;

        }



	ALE_SET_INDEX(ale, al->count++);

    }

    return ale;

}



JS_FRIEND_API(JSAtom *)

js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i)

{

    JSAtom *atom;

    static JSAtom dummy;



    JS_ASSERT(map->vector && i < map->length);

    if (!map->vector || i >= map->length) {

	char numBuf[12];

	JS_snprintf(numBuf, sizeof numBuf, "%lu", (unsigned long)i);

	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

			     JSMSG_BAD_ATOMIC_NUMBER, numBuf);

	return &dummy;

    }

    atom = map->vector[i];

    JS_ASSERT(atom);

    return atom;

}



JS_STATIC_DLL_CALLBACK(intN)

js_map_atom(JSHashEntry *he, intN i, void *arg)

{

    JSAtomListElement *ale = (JSAtomListElement *)he;

    JSAtom **vector = arg;



    vector[ALE_INDEX(ale)] = ALE_ATOM(ale);

    return HT_ENUMERATE_NEXT;

}



#ifdef DEBUG

jsrefcount js_atom_map_count;

jsrefcount js_atom_map_hash_table_count;

#endif



JS_FRIEND_API(JSBool)

js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al)

{

    JSAtom **vector;

    JSAtomListElement *ale;

    uint32 count;



#ifdef DEBUG

    JS_ATOMIC_INCREMENT(&js_atom_map_count);

#endif

    ale = al->list;

    if (!ale && !al->table) {

	map->vector = NULL;

	map->length = 0;

	return JS_TRUE;

    }



    count = al->count;

    if (count >= ATOM_INDEX_LIMIT) {

	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

			     JSMSG_TOO_MANY_LITERALS);

	return JS_FALSE;

    }

    vector = (JSAtom **) JS_malloc(cx, (size_t) count * sizeof *vector);

    if (!vector)

	return JS_FALSE;



    if (al->table) {

#ifdef DEBUG

        JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count);

#endif

        JS_HashTableEnumerateEntries(al->table, js_map_atom, vector);

    } else {

        do {

            vector[ALE_INDEX(ale)] = ALE_ATOM(ale);

        } while ((ale = ALE_NEXT(ale)) != NULL);

    }

    ATOM_LIST_INIT(al);



    map->vector = vector;

    map->length = (jsatomid)count;

    return JS_TRUE;

}



JS_FRIEND_API(void)

js_FreeAtomMap(JSContext *cx, JSAtomMap *map)

{

    if (map->vector) {

	JS_free(cx, map->vector);

	map->vector = NULL;

    }

    map->length = 0;

}

 

**** End of jsatom.c ****

 

**** Start of jsatom.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsatom_h___

#define jsatom_h___

/*

 * JS atom table.

 */

#include <stddef.h>

#include "jstypes.h"

#include "jshash.h" /* Added by JSIFY */

#include "jsapi.h"

#include "jsprvtd.h"

#include "jspubtd.h"



#ifdef JS_THREADSAFE

#include "jslock.h"

#endif



JS_BEGIN_EXTERN_C



#define ATOM_PINNED     0x01            /* atom is pinned against GC */

#define ATOM_INTERNED   0x02            /* pinned variant for JS_Intern* API */

#define ATOM_MARK       0x04            /* atom is reachable via GC */

#define ATOM_NOCOPY     0x40            /* don't copy atom string bytes */

#define ATOM_TMPSTR     0x80            /* internal, to avoid extra string */



struct JSAtom {

    JSHashEntry         entry;          /* key is jsval, value keyword info */

    uint8               flags;          /* pinned, interned, and mark flags */

    int8                kwindex;        /* keyword index, -1 if not keyword */

    jsatomid            number;         /* atom serial number and hash code */

};



#define ATOM_KEY(atom)           ((jsval)(atom)->entry.key)

#define ATOM_IS_OBJECT(atom)     JSVAL_IS_OBJECT(ATOM_KEY(atom))

#define ATOM_TO_OBJECT(atom)     JSVAL_TO_OBJECT(ATOM_KEY(atom))

#define ATOM_IS_INT(atom)        JSVAL_IS_INT(ATOM_KEY(atom))

#define ATOM_TO_INT(atom)        JSVAL_TO_INT(ATOM_KEY(atom))

#define ATOM_IS_DOUBLE(atom)     JSVAL_IS_DOUBLE(ATOM_KEY(atom))

#define ATOM_TO_DOUBLE(atom)     JSVAL_TO_DOUBLE(ATOM_KEY(atom))

#define ATOM_IS_STRING(atom)     JSVAL_IS_STRING(ATOM_KEY(atom))

#define ATOM_TO_STRING(atom)     JSVAL_TO_STRING(ATOM_KEY(atom))

#define ATOM_IS_BOOLEAN(atom)    JSVAL_IS_BOOLEAN(ATOM_KEY(atom))

#define ATOM_TO_BOOLEAN(atom)    JSVAL_TO_BOOLEAN(ATOM_KEY(atom))

#define ATOM_BYTES(atom)         JS_GetStringBytes(ATOM_TO_STRING(atom))



struct JSAtomListElement {

    JSHashEntry         entry;

};



#define ALE_ATOM(ale)   ((JSAtom *) (ale)->entry.key)

#define ALE_INDEX(ale)  ((jsatomid) (ale)->entry.value)

#define ALE_JSOP(ale)   ((JSOp) (ale)->entry.value)

#define ALE_NEXT(ale)   ((JSAtomListElement *) (ale)->entry.next)



#define ALE_SET_ATOM(ale,atom)  ((ale)->entry.key = (const void *)(atom))

#define ALE_SET_INDEX(ale,index)((ale)->entry.value = (void *)(index))

#define ALE_SET_JSOP(ale,op)    ((ale)->entry.value = (void *)(op))

#define ALE_SET_NEXT(ale,link)  ((ale)->entry.next = (JSHashEntry *)(link))



struct JSAtomList {

    JSAtomListElement   *list;          /* literals indexed for mapping */

    JSHashTable         *table;         /* hash table if list gets too long */

    jsuint              count;          /* count of indexed literals */

};



#define ATOM_LIST_INIT(al)  ((al)->list = NULL, (al)->table = NULL,           \

                             (al)->count = 0)



#define ATOM_LIST_SEARCH(_ale,_al,_atom)                                      \

    JS_BEGIN_MACRO                                                            \

        JSHashEntry **_hep;                                                   \

        ATOM_LIST_LOOKUP(_ale, _hep, _al, _atom);                             \

    JS_END_MACRO



#define ATOM_LIST_LOOKUP(_ale,_hep,_al,_atom)                                 \

    JS_BEGIN_MACRO                                                            \

        if ((_al)->table) {                                                   \

            _hep = JS_HashTableRawLookup((_al)->table, _atom->number, _atom); \

            _ale = *_hep ? (JSAtomListElement *) *_hep : NULL;                \

        } else {                                                              \

            JSAtomListElement **_alep = &(_al)->list;                         \

            _hep = NULL;                                                      \

            while ((_ale = *_alep) != NULL) {                                 \

                if (ALE_ATOM(_ale) == (_atom)) {                              \

                    /* Hit, move atom's element to the front of the list. */  \

                    *_alep = ALE_NEXT(_ale);                                  \

                    ALE_SET_NEXT(_ale, (_al)->list);                          \

                    (_al)->list = _ale;                                       \

                    break;                                                    \

                }                                                             \

                _alep = (JSAtomListElement **)&_ale->entry.next;              \

            }                                                                 \

        }                                                                     \

    JS_END_MACRO



struct JSAtomMap {

    JSAtom              **vector;       /* array of ptrs to indexed atoms */

    jsatomid            length;         /* count of (to-be-)indexed atoms */

};



struct JSAtomState {

    JSRuntime           *runtime;       /* runtime that owns us */

    JSHashTable         *table;         /* hash table containing all atoms */

    jsatomid            number;         /* one beyond greatest atom number */

    jsatomid            interns;        /* number of interned strings */



    /* Type names and value literals. */

    JSAtom              *typeAtoms[JSTYPE_LIMIT];

    JSAtom              *booleanAtoms[2];

    JSAtom              *nullAtom;



    /* Various built-in or commonly-used atoms. */

    JSAtom              *ArgumentsAtom;

    JSAtom              *ArrayAtom;

    JSAtom              *BooleanAtom;

    JSAtom              *CallAtom;

    JSAtom              *DateAtom;

    JSAtom              *ErrorAtom;

    JSAtom              *FunctionAtom;

    JSAtom              *MathAtom;

    JSAtom              *NumberAtom;

    JSAtom              *ObjectAtom;

    JSAtom              *RegExpAtom;

    JSAtom              *ScriptAtom;

    JSAtom              *StringAtom;

    JSAtom              *anonymousAtom;

    JSAtom              *argumentsAtom;

    JSAtom              *arityAtom;

    JSAtom              *calleeAtom;

    JSAtom              *callerAtom;

    JSAtom              *classPrototypeAtom;

    JSAtom              *constructorAtom;

    JSAtom              *countAtom;

    JSAtom              *evalAtom;

    JSAtom              *getAtom;

    JSAtom              *getterAtom;

    JSAtom              *indexAtom;

    JSAtom              *inputAtom;

    JSAtom              *lengthAtom;

    JSAtom              *nameAtom;

    JSAtom              *parentAtom;

    JSAtom              *protoAtom;

    JSAtom              *setAtom;

    JSAtom              *setterAtom;

    JSAtom              *toLocaleStringAtom;

    JSAtom              *toSourceAtom;

    JSAtom              *toStringAtom;

    JSAtom              *valueOfAtom;



#ifdef JS_THREADSAFE

    JSThinLock          lock;

    volatile uint32     tablegen;

#endif

};



/* Well-known predefined strings and their atoms. */

extern const char   *js_type_str[];

extern const char   *js_boolean_str[];



extern const char   js_Arguments_str[];

extern const char   js_Array_str[];

extern const char   js_Boolean_str[];

extern const char   js_Call_str[];

extern const char   js_Date_str[];

extern const char   js_Function_str[];

extern const char   js_Math_str[];

extern const char   js_Number_str[];

extern const char   js_Object_str[];

extern const char   js_RegExp_str[];

extern const char   js_Script_str[];

extern const char   js_String_str[];

extern const char   js_anonymous_str[];

extern const char   js_arguments_str[];

extern const char   js_arity_str[];

extern const char   js_callee_str[];

extern const char   js_caller_str[];

extern const char   js_class_prototype_str[];

extern const char   js_constructor_str[];

extern const char   js_count_str[];

extern const char   js_eval_str[];

extern const char   js_getter_str[];

extern const char   js_get_str[];

extern const char   js_index_str[];

extern const char   js_input_str[];

extern const char   js_length_str[];

extern const char   js_name_str[];

extern const char   js_parent_str[];

extern const char   js_proto_str[];

extern const char   js_setter_str[];

extern const char   js_set_str[];

extern const char   js_toSource_str[];

extern const char   js_toString_str[];

extern const char   js_toLocaleString_str[];

extern const char   js_valueOf_str[];



/*

 * Initialize atom state.  Return true on success, false with an out of

 * memory error report on failure.

 */

extern JSBool

js_InitAtomState(JSContext *cx, JSAtomState *state);



/*

 * Free and clear atom state (except for any interned string atoms).

 */

extern void

js_FreeAtomState(JSContext *cx, JSAtomState *state);



/*

 * Interned strings are atoms that live until state's runtime is destroyed.

 * This function frees all interned string atoms, and then frees and clears

 * state's members (just as js_FreeAtomState does), unless there aren't any

 * interned strings in state -- in which case state must be "free" already.

 *

 * NB: js_FreeAtomState is called for each "last" context being destroyed in

 * a runtime, where there may yet be another context created in the runtime;

 * whereas js_FinishAtomState is called from JS_DestroyRuntime, when we know

 * that no more contexts will be created.  Thus we minimize garbage during

 * context-free episodes on a runtime, while preserving atoms created by the

 * JS_Intern*String APIs for the life of the runtime.

 */

extern void

js_FinishAtomState(JSAtomState *state);



/*

 * Atom garbage collection hooks.

 */

typedef void

(*JSGCThingMarker)(void *thing, void *data);



extern void

js_MarkAtomState(JSAtomState *state, uintN gcflags, JSGCThingMarker mark,

                 void *data);



extern void

js_SweepAtomState(JSAtomState *state);



extern void

js_UnpinPinnedAtoms(JSAtomState *state);



/*

 * Find or create the atom for an object.  If we create a new atom, give it the

 * type indicated in flags.  Return 0 on failure to allocate memory.

 */

extern JSAtom *

js_AtomizeObject(JSContext *cx, JSObject *obj, uintN flags);



/*

 * Find or create the atom for a Boolean value.  If we create a new atom, give

 * it the type indicated in flags.  Return 0 on failure to allocate memory.

 */

extern JSAtom *

js_AtomizeBoolean(JSContext *cx, JSBool b, uintN flags);



/*

 * Find or create the atom for an integer value.  If we create a new atom, give

 * it the type indicated in flags.  Return 0 on failure to allocate memory.

 */

extern JSAtom *

js_AtomizeInt(JSContext *cx, jsint i, uintN flags);



/*

 * Find or create the atom for a double value.  If we create a new atom, give

 * it the type indicated in flags.  Return 0 on failure to allocate memory.

 */

extern JSAtom *

js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags);



/*

 * Find or create the atom for a string.  If we create a new atom, give it the

 * type indicated in flags.  Return 0 on failure to allocate memory.

 */

extern JSAtom *

js_AtomizeString(JSContext *cx, JSString *str, uintN flags);



extern JS_FRIEND_API(JSAtom *)

js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags);



extern JS_FRIEND_API(JSAtom *)

js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags);



/*

 * This variant handles all value tag types.

 */

extern JSAtom *

js_AtomizeValue(JSContext *cx, jsval value, uintN flags);



/*

 * Convert v to an atomized string.

 */

extern JSAtom *

js_ValueToStringAtom(JSContext *cx, jsval v);



/*

 * Assign atom an index and insert it on al.

 */

extern JSAtomListElement *

js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al);



/*

 * Get the atom with index i from map.

 */

extern JS_FRIEND_API(JSAtom *)

js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i);



/*

 * For all unmapped atoms recorded in al, add a mapping from the atom's index

 * to its address.  The GC must not run until all indexed atoms in atomLists

 * have been mapped by scripts connected to live objects (Function and Script

 * class objects have scripts as/in their private data -- the GC knows about

 * these two classes).

 */

extern JS_FRIEND_API(JSBool)

js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al);



/*

 * Free map->vector and clear map.

 */

extern JS_FRIEND_API(void)

js_FreeAtomMap(JSContext *cx, JSAtomMap *map);



JS_END_EXTERN_C



#endif /* jsatom_h___ */

 

**** End of jsatom.h ****

 

**** Start of jsbit.h ****

 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

/*

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsbit_h___

#define jsbit_h___



#include "jstypes.h"

JS_BEGIN_EXTERN_C



/*

** A jsbitmap_t is a long integer that can be used for bitmaps

*/

typedef unsigned long jsbitmap_t;



#define JS_TEST_BIT(_map,_bit) \

    ((_map)[(_bit)>>JS_BITS_PER_LONG_LOG2] & (1L << ((_bit) & (JS_BITS_PER_LONG-1))))

#define JS_SET_BIT(_map,_bit) \

    ((_map)[(_bit)>>JS_BITS_PER_LONG_LOG2] |= (1L << ((_bit) & (JS_BITS_PER_LONG-1))))

#define JS_CLEAR_BIT(_map,_bit) \

    ((_map)[(_bit)>>JS_BITS_PER_LONG_LOG2] &= ~(1L << ((_bit) & (JS_BITS_PER_LONG-1))))



/*

** Compute the log of the least power of 2 greater than or equal to n

*/

extern JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 i);



/*

** Compute the log of the greatest power of 2 less than or equal to n

*/

extern JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 i);



/*

** Macro version of JS_CeilingLog2: Compute the log of the least power of

** 2 greater than or equal to _n. The result is returned in _log2.

*/

#define JS_CEILING_LOG2(_log2,_n)   \

  JS_BEGIN_MACRO                    \

    JSUint32 j_ = (JSUint32)(_n); 	\

    (_log2) = 0;                    \

    if ((j_) & ((j_)-1))            \

	(_log2) += 1;               \

    if ((j_) >> 16)                 \

	(_log2) += 16, (j_) >>= 16; \

    if ((j_) >> 8)                  \

	(_log2) += 8, (j_) >>= 8;   \

    if ((j_) >> 4)                  \

	(_log2) += 4, (j_) >>= 4;   \

    if ((j_) >> 2)                  \

	(_log2) += 2, (j_) >>= 2;   \

    if ((j_) >> 1)                  \

	(_log2) += 1;               \

  JS_END_MACRO



/*

** Macro version of JS_FloorLog2: Compute the log of the greatest power of

** 2 less than or equal to _n. The result is returned in _log2.

**

** This is equivalent to finding the highest set bit in the word.

*/

#define JS_FLOOR_LOG2(_log2,_n)   \

  JS_BEGIN_MACRO                    \

    JSUint32 j_ = (JSUint32)(_n); 	\

    (_log2) = 0;                    \

    if ((j_) >> 16)                 \

	(_log2) += 16, (j_) >>= 16; \

    if ((j_) >> 8)                  \

	(_log2) += 8, (j_) >>= 8;   \

    if ((j_) >> 4)                  \

	(_log2) += 4, (j_) >>= 4;   \

    if ((j_) >> 2)                  \

	(_log2) += 2, (j_) >>= 2;   \

    if ((j_) >> 1)                  \

	(_log2) += 1;               \

  JS_END_MACRO



JS_END_EXTERN_C

#endif /* jsbit_h___ */

 

**** End of jsbit.h ****

 

**** Start of jsbool.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS boolean implementation.

 */

#include "jsstddef.h"

#include "jstypes.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jsapi.h"

#include "jsatom.h"

#include "jsbool.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsinterp.h"

#include "jslock.h"

#include "jsnum.h"

#include "jsobj.h"

#include "jsstr.h"



static JSClass boolean_class = {

    "Boolean",

    JSCLASS_HAS_PRIVATE,

    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,

    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,

    JSCLASS_NO_OPTIONAL_MEMBERS

};



#if JS_HAS_TOSOURCE

#include "jsprf.h"



static JSBool

bool_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	      jsval *rval)

{

    jsval v;

    char buf[32];

    JSString *str;



    if (!JS_InstanceOf(cx, obj, &boolean_class, argv))

	return JS_FALSE;

    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);

    if (!JSVAL_IS_BOOLEAN(v))

	return js_obj_toSource(cx, obj, argc, argv, rval);

    JS_snprintf(buf, sizeof buf, "(new %s(%s))",

		boolean_class.name,

		js_boolean_str[JSVAL_TO_BOOLEAN(v) ? 1 : 0]);

    str = JS_NewStringCopyZ(cx, buf);

    if (!str)

	return JS_FALSE;

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}

#endif



static JSBool

bool_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	      jsval *rval)

{

    jsval v;

    JSAtom *atom;

    JSString *str;



    if (!JS_InstanceOf(cx, obj, &boolean_class, argv))

	return JS_FALSE;

    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);

    if (!JSVAL_IS_BOOLEAN(v))

	return js_obj_toString(cx, obj, argc, argv, rval);

    atom = cx->runtime->atomState.booleanAtoms[JSVAL_TO_BOOLEAN(v) ? 1 : 0];

    str = ATOM_TO_STRING(atom);

    if (!str)

	return JS_FALSE;

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

bool_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    if (!JS_InstanceOf(cx, obj, &boolean_class, argv))

	return JS_FALSE;

    *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);

    return JS_TRUE;

}



static JSFunctionSpec boolean_methods[] = {

#if JS_HAS_TOSOURCE

    {js_toSource_str,   bool_toSource,          0,0,0},

#endif

    {js_toString_str,	bool_toString,		0,0,0},

    {js_valueOf_str,	bool_valueOf,		0,0,0},

    {0,0,0,0,0}

};



#ifdef XP_MAC

#undef Boolean

#define Boolean js_Boolean

#endif



static JSBool

Boolean(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSBool b;

    jsval bval;



    if (argc != 0) {

	if (!js_ValueToBoolean(cx, argv[0], &b))

	    return JS_FALSE;

	bval = BOOLEAN_TO_JSVAL(b);

    } else {

	bval = JSVAL_FALSE;

    }

    if (!cx->fp->constructing) {

	*rval = bval;

	return JS_TRUE;

    }

    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, bval);

    return JS_TRUE;

}



JSObject *

js_InitBooleanClass(JSContext *cx, JSObject *obj)

{

    JSObject *proto;



    proto = JS_InitClass(cx, obj, NULL, &boolean_class, Boolean, 1,

			NULL, boolean_methods, NULL, NULL);

    if (!proto)

	return NULL;

    OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_FALSE);

    return proto;

}



JSObject *

js_BooleanToObject(JSContext *cx, JSBool b)

{

    JSObject *obj;



    obj = js_NewObject(cx, &boolean_class, NULL, NULL);

    if (!obj)

	return NULL;

    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, BOOLEAN_TO_JSVAL(b));

    return obj;

}



JSString *

js_BooleanToString(JSContext *cx, JSBool b)

{

    return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[b ? 1 : 0]);

}



JSBool

js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp)

{

    JSBool b;

    jsdouble d;



#if defined XP_PC && defined _MSC_VER && _MSC_VER <= 800

    /* MSVC1.5 coredumps */

    if (!bp)

	return JS_TRUE;

    /* This should be an if-else chain, but MSVC1.5 crashes if it is. */

#define ELSE

#else

#define ELSE else

#endif



    if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {

	/* Must return early to avoid falling thru to JSVAL_IS_OBJECT case. */

	*bp = JS_FALSE;

	return JS_TRUE;

    }

    if (JSVAL_IS_OBJECT(v)) {

	if (!JSVERSION_IS_ECMA(cx->version)) {

	    if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), JSTYPE_BOOLEAN, &v))

		return JS_FALSE;

	    if (!JSVAL_IS_BOOLEAN(v))

		v = JSVAL_TRUE;		/* non-null object is true */

	    b = JSVAL_TO_BOOLEAN(v);

	} else {

	    b = JS_TRUE;

	}

    } ELSE

    if (JSVAL_IS_STRING(v)) {

	b = JSVAL_TO_STRING(v)->length ? JS_TRUE : JS_FALSE;

    } ELSE

    if (JSVAL_IS_INT(v)) {

	b = JSVAL_TO_INT(v) ? JS_TRUE : JS_FALSE;

    } ELSE

    if (JSVAL_IS_DOUBLE(v)) {

	d = *JSVAL_TO_DOUBLE(v);

	b = (!JSDOUBLE_IS_NaN(d) && d != 0) ? JS_TRUE : JS_FALSE;

    } ELSE

#if defined XP_PC && defined _MSC_VER && _MSC_VER <= 800

    if (JSVAL_IS_BOOLEAN(v)) {

	b = JSVAL_TO_BOOLEAN(v);

    }

#else

    {

        JS_ASSERT(JSVAL_IS_BOOLEAN(v));

	b = JSVAL_TO_BOOLEAN(v);

    }

#endif



#undef ELSE

    *bp = b;

    return JS_TRUE;

}

 

**** End of jsbool.c ****

 

**** Start of jsbool.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsbool_h___

#define jsbool_h___

/*

 * JS boolean interface.

 */



JS_BEGIN_EXTERN_C



extern JSObject *

js_InitBooleanClass(JSContext *cx, JSObject *obj);



extern JSObject *

js_BooleanToObject(JSContext *cx, JSBool b);



extern JSString *

js_BooleanToString(JSContext *cx, JSBool b);



extern JSBool

js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp);



JS_END_EXTERN_C



#endif /* jsbool_h___ */

 

**** End of jsbool.h ****

 

**** Start of jsclist.h ****

 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

/*

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsclist_h___

#define jsclist_h___



#include "jstypes.h"



/*

** Circular linked list

*/

typedef struct JSCListStr {

    struct JSCListStr *next;

    struct JSCListStr *prev;

} JSCList;



/*

** Insert element "_e" into the list, before "_l".

*/

#define JS_INSERT_BEFORE(_e,_l)	 \

    JS_BEGIN_MACRO		 \

	(_e)->next = (_l);	 \

	(_e)->prev = (_l)->prev; \

	(_l)->prev->next = (_e); \

	(_l)->prev = (_e);	 \

    JS_END_MACRO



/*

** Insert element "_e" into the list, after "_l".

*/

#define JS_INSERT_AFTER(_e,_l)	 \

    JS_BEGIN_MACRO		 \

	(_e)->next = (_l)->next; \

	(_e)->prev = (_l);	 \

	(_l)->next->prev = (_e); \

	(_l)->next = (_e);	 \

    JS_END_MACRO



/*

** Return the element following element "_e"

*/

#define JS_NEXT_LINK(_e)	 \

    	((_e)->next)

/*

** Return the element preceding element "_e"

*/

#define JS_PREV_LINK(_e)	 \

    	((_e)->prev)



/*

** Append an element "_e" to the end of the list "_l"

*/

#define JS_APPEND_LINK(_e,_l) JS_INSERT_BEFORE(_e,_l)



/*

** Insert an element "_e" at the head of the list "_l"

*/

#define JS_INSERT_LINK(_e,_l) JS_INSERT_AFTER(_e,_l)



/* Return the head/tail of the list */

#define JS_LIST_HEAD(_l) (_l)->next

#define JS_LIST_TAIL(_l) (_l)->prev



/*

** Remove the element "_e" from it's circular list.

*/

#define JS_REMOVE_LINK(_e)	       \

    JS_BEGIN_MACRO		       \

	(_e)->prev->next = (_e)->next; \

	(_e)->next->prev = (_e)->prev; \

    JS_END_MACRO



/*

** Remove the element "_e" from it's circular list. Also initializes the

** linkage.

*/

#define JS_REMOVE_AND_INIT_LINK(_e)    \

    JS_BEGIN_MACRO		       \

	(_e)->prev->next = (_e)->next; \

	(_e)->next->prev = (_e)->prev; \

	(_e)->next = (_e);	       \

	(_e)->prev = (_e);	       \

    JS_END_MACRO



/*

** Return non-zero if the given circular list "_l" is empty, zero if the

** circular list is not empty

*/

#define JS_CLIST_IS_EMPTY(_l) \

    ((_l)->next == (_l))



/*

** Initialize a circular list

*/

#define JS_INIT_CLIST(_l)  \

    JS_BEGIN_MACRO	   \

	(_l)->next = (_l); \

	(_l)->prev = (_l); \

    JS_END_MACRO



#define JS_INIT_STATIC_CLIST(_l) \

    {(_l), (_l)}



#endif /* jsclist_h___ */

 

**** End of jsclist.h ****

 

**** Start of jscntxt.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS execution context.

 */

#include "jsstddef.h"

#include <stdarg.h>

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsarena.h" /* Added by JSIFY */

#include "jsutil.h" /* Added by JSIFY */

#include "jsclist.h"

#include "jsprf.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsdbgapi.h"

#include "jsexn.h"

#include "jsgc.h"

#include "jslock.h"

#include "jsnum.h"

#include "jsobj.h"

#include "jsopcode.h"

#include "jsscan.h"

#include "jsscript.h"

#include "jsstr.h"



JSContext *

js_NewContext(JSRuntime *rt, size_t stackChunkSize)

{

    JSContext *cx;

    JSBool ok, first;



    cx = (JSContext *) malloc(sizeof *cx);

    if (!cx)

	return NULL;

    memset(cx, 0, sizeof *cx);



    cx->runtime = rt;

#ifdef JS_THREADSAFE

    js_InitContextForLocking(cx);

#endif



    JS_LOCK_RUNTIME(rt);

    for (;;) {

	first = (rt->contextList.next == &rt->contextList);

	if (rt->state == JSRTS_UP) {

	    JS_ASSERT(!first);

	    break;

	}

	if (rt->state == JSRTS_DOWN) {

	    JS_ASSERT(first);

	    rt->state = JSRTS_LAUNCHING;

	    break;

	}

	JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT);

    }

    JS_APPEND_LINK(&cx->links, &rt->contextList);

    JS_UNLOCK_RUNTIME(rt);



    /*

     * First we do the infallible, every-time per-context initializations.

     * Should a later, fallible initialization (js_InitRegExpStatics, e.g.,

     * or the stuff under 'if (first)' below) fail, at least the version

     * and arena-pools will be valid and safe to use (say, from the last GC

     * done by js_DestroyContext).

     */

    cx->version = JSVERSION_DEFAULT;

    cx->jsop_eq = JSOP_EQ;

    cx->jsop_ne = JSOP_NE;

    JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval));

    JS_InitArenaPool(&cx->codePool, "code", 1024, sizeof(jsbytecode));

    JS_InitArenaPool(&cx->notePool, "note", 256, sizeof(jssrcnote));

    JS_InitArenaPool(&cx->tempPool, "temp", 1024, sizeof(jsdouble));



#if JS_HAS_REGEXPS

    if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) {

	js_DestroyContext(cx, JS_NO_GC);

	return NULL;

    }

#endif

#if JS_HAS_EXCEPTIONS

    cx->throwing = JS_FALSE;

#endif



    /*

     * If cx is the first context on this runtime, initialize well-known atoms,

     * keywords, numbers, and strings.  If one of these steps should fail, the

     * runtime will be left in a partially initialized state, with zeroes and

     * nulls stored in the default-initialized remainder of the struct.  We'll

     * clean the runtime up under js_DestroyContext, because cx will be "last"

     * as well as "first".

     */

    if (first) {

	ok = js_InitAtomState(cx, &rt->atomState);

	if (ok)

	    ok = js_InitScanner(cx);

        if (ok)

            ok = js_InitRuntimeNumberState(cx);

        if (ok)

            ok = js_InitRuntimeStringState(cx);

	if (!ok) {

            js_DestroyContext(cx, JS_NO_GC);

	    return NULL;

	}



	JS_LOCK_RUNTIME(rt);

	rt->state = JSRTS_UP;

	JS_NOTIFY_ALL_CONDVAR(rt->stateChange);

	JS_UNLOCK_RUNTIME(rt);

    }



    return cx;

}



void

js_DestroyContext(JSContext *cx, JSGCMode gcmode)

{

    JSRuntime *rt;

    JSBool last;

    JSArgumentFormatMap *map;



    rt = cx->runtime;



    /* Remove cx from context list first. */

    JS_LOCK_RUNTIME(rt);

    JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING);

    JS_REMOVE_LINK(&cx->links);

    last = (rt->contextList.next == &rt->contextList);

    if (last)

    	rt->state = JSRTS_LANDING;

    JS_UNLOCK_RUNTIME(rt);



    if (last) {

	/* Unpin all pinned atoms before final GC. */

	js_UnpinPinnedAtoms(&rt->atomState);



	/* Unlock and clear GC things held by runtime pointers. */

        js_FinishRuntimeNumberState(cx);

        js_FinishRuntimeStringState(cx);



	/* Clear debugging state to remove GC roots. */

	JS_ClearAllTraps(cx);

	JS_ClearAllWatchPoints(cx);

    }



#if JS_HAS_REGEXPS

    /*

     * Remove more GC roots in regExpStatics, then collect garbage.

     * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within

     * XXX this function call to wait for any racing GC to complete, in the

     * XXX case where JS_DestroyContext is called outside of a request on cx

     */

    js_FreeRegExpStatics(cx, &cx->regExpStatics);

#endif



#ifdef JS_THREADSAFE

    /*

     * Destroying a context implicitly calls JS_EndRequest().  Also, we must

     * end our request here in case we are "last" -- in that event, another

     * js_DestroyContext that was not last might be waiting in the GC for our

     * request to end.  We'll let it run below, just before we do the truly

     * final GC and then free atom state.

     *

     * At this point, cx must be inaccessible to other threads.  It's off the

     * rt->contextList, and it should not be reachable via any object private

     * data structure.

     */

    while (cx->requestDepth != 0)

        JS_EndRequest(cx);

#endif



    if (last) {

        /* Always force, so we wait for any racing GC to finish. */

        js_ForceGC(cx);



        /* Iterate until no finalizer removes a GC root or lock. */

        while (rt->gcPoke)

            js_GC(cx, GC_LAST_CONTEXT);



        /* Free atom state last, now that no scripts survive. */

        js_FreeAtomState(cx, &rt->atomState);



        /* Take the runtime down, now that it has no contexts or atoms. */

        JS_LOCK_RUNTIME(rt);

        rt->state = JSRTS_DOWN;

        JS_NOTIFY_ALL_CONDVAR(rt->stateChange);

        JS_UNLOCK_RUNTIME(rt);

    } else {

        if (gcmode == JS_FORCE_GC)

            js_ForceGC(cx);

        else if (gcmode == JS_MAYBE_GC)

            JS_MaybeGC(cx);

    }



    /* Free the stuff hanging off of cx. */

    JS_FinishArenaPool(&cx->stackPool);

    JS_FinishArenaPool(&cx->codePool);

    JS_FinishArenaPool(&cx->notePool);

    JS_FinishArenaPool(&cx->tempPool);

    if (cx->lastMessage)

	free(cx->lastMessage);



    /* Remove any argument formatters. */

    map = cx->argumentFormatMap;

    while (map) {

        JSArgumentFormatMap *temp = map;

        map = map->next;

        JS_free(cx, temp);

    }



    /* Finally, free cx itself. */

    free(cx);

}



JSBool

js_LiveContext(JSRuntime *rt, JSContext *cx)

{

    JSCList *cl;



    for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) {

        if (cl == &cx->links)

            return JS_TRUE;

    }

    JS_RUNTIME_METER(rt, deadContexts);

    return JS_FALSE;

}



JSContext *

js_ContextIterator(JSRuntime *rt, JSContext **iterp)

{

    JSContext *cx = *iterp;



    JS_LOCK_RUNTIME(rt);

    if (!cx)

        cx = (JSContext *)&rt->contextList;

    cx = (JSContext *)cx->links.next;

    if (&cx->links == &rt->contextList)

        cx = NULL;

    *iterp = cx;

    JS_UNLOCK_RUNTIME(rt);

    return cx;

}



static void

ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)

{

    /*

     * Check the error report, and set a JavaScript-catchable exception

     * if the error is defined to have an associated exception.  If an

     * exception is thrown, then the JSREPORT_EXCEPTION flag will be set

     * on the error report, and exception-aware hosts should ignore it.

     */

    if (reportp && reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION)

        reportp->flags |= JSREPORT_EXCEPTION;



#if JS_HAS_ERROR_EXCEPTIONS

    /*

     * Call the error reporter only if an exception wasn't raised.

     *

     * If an exception was raised, then we call the debugErrorHook

     * (if present) to give it a chance to see the error before it

     * propagates out of scope.  This is needed for compatability

     * with the old scheme.

     */

    if (!js_ErrorToException(cx, message, reportp)) {

        js_ReportErrorAgain(cx, message, reportp);

    } else if (cx->runtime->debugErrorHook && cx->errorReporter) {

        JSDebugErrorHook hook = cx->runtime->debugErrorHook;

        /* test local in case debugErrorHook changed on another thread */

        if (hook)

            hook(cx, message, reportp, cx->runtime->debugErrorHookData);

    }

#else

    js_ReportErrorAgain(cx, message, reportp);

#endif

}



/*

 * We don't post an exception in this case, since doing so runs into

 * complications of pre-allocating an exception object which required

 * running the Exception class initializer early etc.

 * Instead we just invoke the errorReporter with an "Out Of Memory"

 * type message, and then hope the process ends swiftly.

 */

void

js_ReportOutOfMemory(JSContext *cx, JSErrorCallback errorCallback)

{

    JSStackFrame *fp = cx->fp;

    JSErrorReport report;

    JSErrorReporter onError = cx->errorReporter;

    /* Get the message for this error, but we won't expand any arguments. */

    const JSErrorFormatString *fmtData = (*errorCallback)(NULL, NULL, JSMSG_OUT_OF_MEMORY);

    const char *msg = fmtData ? fmtData->format : "Out Of Memory";



    memset(&report, 0, sizeof (struct JSErrorReport));

    /* Fill out the report, but don't do anything that requires an allocation. */

    report.errorNumber = JSMSG_OUT_OF_MEMORY;

    report.flags = JSREPORT_ERROR;



    /* Walk stack until we find a frame that is associated with

       some script rather than a native frame. */

    while (fp && (!fp->script || !fp->pc))

        fp = fp->down;



    if (fp) {

        report.filename = fp->script->filename;

        report.lineno = js_PCToLineNumber(fp->script, fp->pc);

    }



    /*

     * If debugErrorHook is present then we give it a chance to veto

     * sending the error on to the regular ErrorReporter.

     */

    if (onError) {

        JSDebugErrorHook hook = cx->runtime->debugErrorHook;

        if (hook &&

            !hook(cx, msg, &report, cx->runtime->debugErrorHookData)) {

            onError = NULL;

        }

    }



    if (onError)

        (*onError)(cx, msg, &report);

}



JSBool

js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)

{

    JSStackFrame *fp;

    JSErrorReport report, *reportp;

    char *last;

    JSBool warning;



    fp = cx->fp;



    /* Walk stack until we find a frame that is associated with

       some script rather than a native frame. */

    while (fp && (!fp->script || !fp->pc))

        fp = fp->down;



    reportp = &report;

    memset(reportp, 0, sizeof (struct JSErrorReport));

    report.flags = flags;

    if (fp) {

        report.filename = fp->script->filename;

        report.lineno = js_PCToLineNumber(fp->script, fp->pc);

	/* XXX should fetch line somehow */

    }

    last = JS_vsmprintf(format, ap);

    if (!last)

	return JS_FALSE;



    ReportError(cx, last, reportp);

    free(last);



    warning = JSREPORT_IS_WARNING(reportp->flags);

    if (warning && JS_HAS_WERROR_OPTION(cx)) {

        reportp->flags &= ~JSREPORT_WARNING;

        warning = JS_FALSE;

    }

    return warning;

}



/*

 * The arguments from ap need to be packaged up into an array and stored

 * into the report struct.

 *

 * The format string addressed by the error number may contain operands

 * identified by the format {N}, where N is a decimal digit. Each of these

 * is to be replaced by the Nth argument from the va_list. The complete

 * message is placed into reportp->ucmessage converted to a JSString.

 *

 * Returns true if the expansion succeeds (can fail if out of memory).

 */

JSBool

js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,

			void *userRef, const uintN errorNumber,

			char **messagep, JSErrorReport *reportp,

                        JSBool *warningp, JSBool charArgs, va_list ap)

{

    const JSErrorFormatString *fmtData;

    int i;

    int argCount;



    *warningp = JSREPORT_IS_WARNING(reportp->flags);

    if (*warningp && JS_HAS_WERROR_OPTION(cx)) {

        reportp->flags &= ~JSREPORT_WARNING;

        *warningp = JS_FALSE;

    }



    *messagep = NULL;

    if (callback) {

	fmtData = (*callback)(userRef, "Mountain View", errorNumber);

	if (fmtData != NULL) {

            size_t totalArgsLength = 0;

            size_t argLengths[10]; /* only {0} thru {9} supported */

	    argCount = fmtData->argCount;

            JS_ASSERT(argCount <= 10);

	    if (argCount > 0) {

		/*

                 * Gather the arguments into an array, and accumulate

                 * their sizes. We allocate 1 more than necessary and

                 * null it out to act as the caboose when we free the

                 * pointers later.

		 */

                reportp->messageArgs = (const jschar **)

                    JS_malloc(cx, sizeof(jschar *) * (argCount + 1));

                if (!reportp->messageArgs)

                    return JS_FALSE;

                reportp->messageArgs[argCount] = NULL;

                for (i = 0; i < argCount; i++) {

                    if (charArgs) {

                        char *charArg = va_arg(ap, char *);

                        reportp->messageArgs[i]

                            = js_InflateString(cx, charArg, strlen(charArg));

                        if (!reportp->messageArgs[i])

                            goto error;

                    }

                    else

                        reportp->messageArgs[i] = va_arg(ap, jschar *);

                    argLengths[i] = js_strlen(reportp->messageArgs[i]);

                    totalArgsLength += argLengths[i];

                }

                /* NULL-terminate for easy copying. */

                reportp->messageArgs[i] = NULL;

            }

	    /*

	     * Parse the error format, substituting the argument X

	     * for {X} in the format.

	     */

            if (argCount > 0) {

                if (fmtData->format) {

                    const char *fmt;

                    const jschar *arg;

                    jschar *out;

                    int expandedArgs = 0;

                    size_t expandedLength

                        = strlen(fmtData->format)

                            - (3 * argCount) /* exclude the {n} */

                            + totalArgsLength;

		    /*

		     * Note - the above calculation assumes that each argument

		     * is used once and only once in the expansion !!!

		     */

                    reportp->ucmessage = out = (jschar *)

                        JS_malloc(cx, (expandedLength + 1) * sizeof(jschar));

                    if (!out)

                        goto error;

                    fmt = fmtData->format;

                    while (*fmt) {

                        if (*fmt == '{') {	/* balance} */

                            if (isdigit(fmt[1])) {

                                int d = JS7_UNDEC(fmt[1]);

                                JS_ASSERT(expandedArgs < argCount);

                                arg = reportp->messageArgs[d];

                                js_strncpy(out, arg, argLengths[d]);

                                out += argLengths[d];

                                fmt += 3;

                                expandedArgs++;

                                continue;

                            }

                        }

                        /*

                         * is this kosher?

                         */

                        *out++ = (unsigned char)(*fmt++);

                    }

                    JS_ASSERT(expandedArgs == argCount);

                    *out = 0;

                    *messagep =

                        js_DeflateString(cx, reportp->ucmessage,

		                         (size_t)(out - reportp->ucmessage));

                    if (!*messagep)

                        goto error;

		}

            } else {

                /*

                 * Zero arguments: the format string (if it exists) is the

                 * entire message.

                 */

                if (fmtData->format) {

                    *messagep = JS_strdup(cx, fmtData->format);

                    if (!*messagep)

                        goto error;

                    reportp->ucmessage

                        = js_InflateString(cx, *messagep, strlen(*messagep));

                    if (!reportp->ucmessage)

                        goto error;

                }

            }

	}

    }

    if (*messagep == NULL) {

        /* where's the right place for this ??? */

        const char *defaultErrorMessage

            = "No error message available for error number %d";

        size_t nbytes = strlen(defaultErrorMessage) + 16;

        *messagep = (char *)JS_malloc(cx, nbytes);

        if (!*messagep)

            goto error;

        JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber);

    }

    return JS_TRUE;



error:

    if (reportp->messageArgs) {

        i = 0;

        while (reportp->messageArgs[i])

            JS_free(cx, (void *)reportp->messageArgs[i++]);

        JS_free(cx, (void *)reportp->messageArgs);

        reportp->messageArgs = NULL;

    }

    if (reportp->ucmessage) {

        JS_free(cx, (void *)reportp->ucmessage);

        reportp->ucmessage = NULL;

    }

    if (*messagep) {

        JS_free(cx, (void *)*messagep);

        *messagep = NULL;

    }

    return JS_FALSE;

}



JSBool

js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,

                       void *userRef, const uintN errorNumber,

                       JSBool charArgs, va_list ap)

{

    JSStackFrame *fp;

    JSErrorReport report;

    char *message;

    JSBool warning;



    report.messageArgs = NULL;

    report.ucmessage = NULL;

    message = NULL;



    fp = cx->fp;

    if (fp && fp->script && fp->pc) {

	report.filename = fp->script->filename;

	report.lineno = js_PCToLineNumber(fp->script, fp->pc);

    } else {

        /*  We can't find out where the error was from the current

            frame so see if the next frame has a script/pc combo we

            could use */

        if (fp && fp->down && fp->down->script && fp->down->pc) {

	    report.filename = fp->down->script->filename;

	    report.lineno = js_PCToLineNumber(fp->down->script, fp->down->pc);

        }

        else {

	    report.filename = NULL;

	    report.lineno = 0;

        }

    }



    /* XXX should fetch line somehow */

    report.linebuf = NULL;

    report.tokenptr = NULL;

    report.flags = flags;

    report.errorNumber = errorNumber;



    /*

     * XXX js_ExpandErrorArguments only sometimes fills these in, so we

     * initialize them to clear garbage.

     */

    report.uclinebuf = NULL;

    report.uctokenptr = NULL;

    report.ucmessage = NULL;

    report.messageArgs = NULL;



    if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,

				 &message, &report, &warning, charArgs, ap)) {

	return JS_FALSE;

    }



    ReportError(cx, message, &report);



    if (message)

	JS_free(cx, message);

    if (report.messageArgs) {

        int i = 0;

        while (report.messageArgs[i])

            JS_free(cx, (void *)report.messageArgs[i++]);

        JS_free(cx, (void *)report.messageArgs);

    }

    if (report.ucmessage)

        JS_free(cx, (void *)report.ucmessage);



    return warning;

}



JS_FRIEND_API(void)

js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp)

{

    JSErrorReporter onError;



    if (!message)

        return;



    if (cx->lastMessage)

        free(cx->lastMessage);

    cx->lastMessage = JS_strdup(cx, message);

    if (!cx->lastMessage)

        return;

    onError = cx->errorReporter;



    /*

     * If debugErrorHook is present then we give it a chance to veto

     * sending the error on to the regular ErrorReporter.

     */

    if (onError) {

        JSDebugErrorHook hook = cx->runtime->debugErrorHook;

        if (hook &&

            !hook(cx, cx->lastMessage, reportp, cx->runtime->debugErrorHookData)) {

            onError = NULL;

        }

    }

    if (onError)

        (*onError)(cx, cx->lastMessage, reportp);

}



void

js_ReportIsNotDefined(JSContext *cx, const char *name)

{

    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name);

}



#if defined DEBUG && defined XP_UNIX

/* For gdb usage. */

void js_traceon(JSContext *cx)  { cx->tracefp = stderr; }

void js_traceoff(JSContext *cx) { cx->tracefp = NULL; }

#endif



JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = {

#if JS_HAS_DFLT_MSG_STRINGS

#define MSG_DEF(name, number, count, exception, format) \

    { format, count } ,

#else

#define MSG_DEF(name, number, count, exception, format) \

    { NULL, count } ,

#endif

#include "js.msg"

#undef MSG_DEF

};



const JSErrorFormatString *

js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber)

{

    if ((errorNumber > 0) && (errorNumber < JSErr_Limit))

        return &js_ErrorFormatString[errorNumber];

    return NULL;

}

 

**** End of jscntxt.c ****

 

**** Start of jscntxt.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jscntxt_h___

#define jscntxt_h___

/*

 * JS execution context.

 */

#include "jsarena.h" /* Added by JSIFY */

#include "jsclist.h"

#include "jslong.h"

#include "jsatom.h"

#include "jsconfig.h"

#include "jsdhash.h"

#include "jsgc.h"

#include "jsinterp.h"

#include "jsobj.h"

#include "jsprvtd.h"

#include "jspubtd.h"

#include "jsregexp.h"



JS_BEGIN_EXTERN_C



typedef enum JSGCMode { JS_NO_GC, JS_MAYBE_GC, JS_FORCE_GC } JSGCMode;



typedef enum JSRuntimeState {

    JSRTS_DOWN,

    JSRTS_LAUNCHING,

    JSRTS_UP,

    JSRTS_LANDING

} JSRuntimeState;



struct JSRuntime {

    JSRuntimeState      state;



    /* Garbage collector state, used by jsgc.c. */

    JSArenaPool         gcArenaPool;

    JSHashTable         *gcRootsHash;

    JSHashTable         *gcLocksHash;

    JSGCThing           *gcFreeList;

    jsrefcount          gcDisabled;

    uint32              gcBytes;

    uint32              gcLastBytes;

    uint32              gcMaxBytes;

    uint32              gcLevel;

    uint32              gcNumber;

    JSPackedBool        gcPoke;

    JSPackedBool        gcRunning;

    JSGCCallback        gcCallback;

    uint32              gcMallocBytes;

#ifdef JS_GCMETER

    JSGCStats           gcStats;

#endif



    /* Literal table maintained by jsatom.c functions. */

    JSAtomState         atomState;



    /* Random number generator state, used by jsmath.c. */

    JSBool              rngInitialized;

    int64               rngMultiplier;

    int64               rngAddend;

    int64               rngMask;

    int64               rngSeed;

    jsdouble            rngDscale;



    /* Well-known numbers held for use by this runtime's contexts. */

    jsdouble            *jsNaN;

    jsdouble            *jsNegativeInfinity;

    jsdouble            *jsPositiveInfinity;



    /* Empty string held for use by this runtime's contexts. */

    JSString            *emptyString;



    /* List of active contexts sharing this runtime. */

    JSCList             contextList;



    /* These are used for debugging -- see jsprvtd.h and jsdbgapi.h. */

    JSTrapHandler       interruptHandler;

    void                *interruptHandlerData;

    JSNewScriptHook     newScriptHook;

    void                *newScriptHookData;

    JSDestroyScriptHook destroyScriptHook;

    void                *destroyScriptHookData;

    JSTrapHandler       debuggerHandler;

    void                *debuggerHandlerData;

    JSSourceHandler     sourceHandler;

    void                *sourceHandlerData;

    JSInterpreterHook   executeHook;

    void                *executeHookData;

    JSInterpreterHook   callHook;

    void                *callHookData;

    JSObjectHook        objectHook;

    void                *objectHookData;

    JSTrapHandler       throwHook;

    void                *throwHookData;

    JSDebugErrorHook    debugErrorHook;

    void                *debugErrorHookData;



    /* More debugging state, see jsdbgapi.c. */

    JSCList             trapList;

    JSCList             watchPointList;



    /* Weak links to properties, indexed by quickened get/set opcodes. */

    /* XXX must come after JSCLists or MSVC alignment bug bites empty lists */

    JSPropertyCache     propertyCache;



    /* Client opaque pointer */

    void                *data;



#ifdef JS_THREADSAFE

    /* These combine to interlock the GC and new requests. */

    PRLock              *gcLock;

    PRCondVar           *gcDone;

    PRCondVar           *requestDone;

    uint32              requestCount;

    jsword              gcThread;



    /* Lock and owning thread pointer for JS_LOCK_RUNTIME. */

    PRLock              *rtLock;

#ifdef DEBUG

    jsword              rtLockOwner;

#endif



    /* Used to synchronize down/up state change; uses rtLock. */

    PRCondVar           *stateChange;



    /* Used to serialize cycle checks when setting __proto__ or __parent__. */

    PRLock              *setSlotLock;

    JSScope             *setSlotScope;  /* deadlock avoidance, see jslock.c */



    /*

     * State for sharing single-threaded scopes, once a second thread tries to

     * lock a scope.  The scopeSharingDone condvar is protected by rt->gcLock,

     * to minimize number of locks taken in JS_EndRequest.

     *

     * The scopeSharingTodo linked list is likewise "global" per runtime, not

     * one-list-per-context, to conserve space over all contexts, optimizing

     * for the likely case that scopes become shared rarely, and among a very

     * small set of threads (contexts).

     */

    PRCondVar           *scopeSharingDone;

    JSScope             *scopeSharingTodo;



/*

 * Magic terminator for the rt->scopeSharingTodo linked list, threaded through

 * scope->u.link.  This hack allows us to test whether a scope is on the list

 * by asking whether scope->u.link is non-null.  We use a large, likely bogus

 * pointer here to distinguish this value from any valid u.count (small int)

 * value.

 */

#define NO_SCOPE_SHARING_TODO   ((JSScope *) 0xfeedbeef)

#endif



#ifdef DEBUG

    /* Function invocation metering. */

    jsrefcount          inlineCalls;

    jsrefcount          nativeCalls;

    jsrefcount          nonInlineCalls;

    jsrefcount          constructs;



    /* Scope lock metering. */

    jsrefcount          claimAttempts;

    jsrefcount          claimedScopes;

    jsrefcount          deadContexts;

    jsrefcount          deadlocksAvoided;

    jsrefcount          liveScopes;

    jsrefcount          sharedScopes;

    jsrefcount          totalScopes;



    /* String instrumentation. */

    jsrefcount          liveStrings;

    jsrefcount          totalStrings;

    double              lengthSum;

    double              lengthSquaredSum;

#endif

};



#ifdef DEBUG

# define JS_RUNTIME_METER(rt, which)    JS_ATOMIC_INCREMENT(&(rt)->which)

# define JS_RUNTIME_UNMETER(rt, which)  JS_ATOMIC_DECREMENT(&(rt)->which)

#else

# define JS_RUNTIME_METER(rt, which)    /* nothing */

# define JS_RUNTIME_UNMETER(rt, which)  /* nothing */

#endif



#define JS_ENABLE_GC(rt)    JS_ATOMIC_DECREMENT(&(rt)->gcDisabled);

#define JS_DISABLE_GC(rt)   JS_ATOMIC_INCREMENT(&(rt)->gcDisabled);



#ifdef JS_ARGUMENT_FORMATTER_DEFINED

/*

 * Linked list mapping format strings for JS_{Convert,Push}Arguments{,VA} to

 * formatter functions.  Elements are sorted in non-increasing format string

 * length order.

 */

struct JSArgumentFormatMap {

    const char          *format;

    size_t              length;

    JSArgumentFormatter formatter;

    JSArgumentFormatMap *next;

};

#endif



struct JSStackHeader {

    uintN               nslots;

    JSStackHeader       *down;

};



#define JS_STACK_SEGMENT(sh)    ((jsval *)(sh) + 2)



struct JSContext {

    JSCList             links;



    /* Interpreter activation count. */

    uintN               interpLevel;



    /* Runtime version control identifier and equality operators. */

    JSVersion           version;

    jsbytecode          jsop_eq;

    jsbytecode          jsop_ne;



    /* Data shared by threads in an address space. */

    JSRuntime           *runtime;



    /* Stack arena pool and frame pointer register. */

    JSArenaPool         stackPool;

    JSStackFrame        *fp;



    /* Temporary arena pools used while compiling and decompiling. */

    JSArenaPool         codePool;

    JSArenaPool         notePool;

    JSArenaPool         tempPool;



    /* Top-level object and pointer to top stack frame's scope chain. */

    JSObject            *globalObject;



    /* Most recently created things by type, members of the GC's root set. */

    JSGCThing           *newborn[GCX_NTYPES];



    /* Regular expression class statics (XXX not shared globally). */

    JSRegExpStatics     regExpStatics;



    /* State for object and array toSource conversion. */

    JSSharpObjectMap    sharpObjectMap;



    /* Argument formatter support for JS_{Convert,Push}Arguments{,VA}. */

    JSArgumentFormatMap *argumentFormatMap;



    /* Last message string and trace file for debugging. */

    char                *lastMessage;

#ifdef DEBUG

    void                *tracefp;

#endif



    /* Per-context optional user callbacks. */

    JSBranchCallback    branchCallback;

    JSErrorReporter     errorReporter;



    /* Client opaque pointer */

    void                *data;



    /* GC and thread-safe state. */

    JSStackFrame        *dormantFrameChain; /* dormant stack frame to scan */

#ifdef JS_THREADSAFE

    jsword              thread;

    jsrefcount          requestDepth;

    JSScope             *scopeToShare;      /* weak reference, see jslock.c */

#endif



#if JS_HAS_LVALUE_RETURN

    /*

     * Secondary return value from native method called on the left-hand side

     * of an assignment operator.  The native should store the object in which

     * to set a property in *rval, and return the property's id expressed as a

     * jsval by calling JS_SetCallReturnValue2(cx, idval).

     */

    jsval               rval2;

    JSPackedBool        rval2set;

#endif



    /* Exception state (NB: throwing packs with rval2set, above). */

    JSPackedBool        throwing;           /* is there a pending exception? */

    jsval               exception;          /* most-recently-thrown exceptin */



    /* Per-context options. */

    uint32              options;            /* see jsapi.h for JSOPTION_* */



    /* Delay JS_SetVersion scanner effects until they're needed. */

    JSVersion           scannerVersion;



    /* Locale specific callbacks for string conversion. */

    JSLocaleCallbacks   *localeCallbacks;



    /* Non-null if init'ing standard classes lazily, to stop recursion. */

    JSDHashTable        *resolving;



    /* PDL of stack headers describing stack slots not rooted by argv, etc. */

    JSStackHeader       *stackHeaders;

};



/* Slightly more readable macros, also to hide bitset implementation detail. */

#define JS_HAS_STRICT_OPTION(cx)    ((cx)->options & JSOPTION_STRICT)

#define JS_HAS_WERROR_OPTION(cx)    ((cx)->options & JSOPTION_WERROR)



extern JSContext *

js_NewContext(JSRuntime *rt, size_t stackChunkSize);



extern void

js_DestroyContext(JSContext *cx, JSGCMode gcmode);



extern JSBool

js_LiveContext(JSRuntime *rt, JSContext *cx);



extern JSContext *

js_ContextIterator(JSRuntime *rt, JSContext **iterp);



/*

 * Report an exception, which is currently realized as a printf-style format

 * string and its arguments.

 */

typedef enum JSErrNum {

#define MSG_DEF(name, number, count, exception, format) \

    name = number,

#include "js.msg"

#undef MSG_DEF

    JSErr_Limit

} JSErrNum;



extern const JSErrorFormatString *

js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber);



#ifdef va_start

extern JSBool

js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap);



extern JSBool

js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback,

		       void *userRef, const uintN errorNumber,

                       JSBool charArgs, va_list ap);



extern JSBool

js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,

			void *userRef, const uintN errorNumber,

			char **message, JSErrorReport *reportp,

                        JSBool *warningp, JSBool charArgs, va_list ap);

#endif



extern void

js_ReportOutOfMemory(JSContext *cx, JSErrorCallback errorCallback);



/*

 * Report an exception using a previously composed JSErrorReport.

 * XXXbe remove from "friend" API

 */

extern JS_FRIEND_API(void)

js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *report);



extern void

js_ReportIsNotDefined(JSContext *cx, const char *name);



extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit];



JS_END_EXTERN_C



#endif /* jscntxt_h___ */

 

**** End of jscntxt.h ****

 

**** Start of jscompat.h ****

 

/*

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * 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 Netscape are

 * Copyright (C) 1998-1999 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/* -*- Mode: C; tab-width: 8 -*-

 * Copyright  1996-1999 Netscape Communications Corporation, All Rights Reserved.

 */

#ifndef jscompat_h___

#define jscompat_h___

/*

 * Compatibility glue for various NSPR versions.  We must always define int8,

 * int16, jsword, and so on to minimize differences with js/ref, no matter what

 * the NSPR typedef names may be.

 */

#include "jstypes.h"

#include "jslong.h"



typedef JSIntn intN;

typedef JSUintn uintN;

typedef JSUword jsuword;

typedef JSWord jsword;

typedef float float32;

#define allocPriv allocPool

#endif /* jscompat_h___ */

 

**** End of jscompat.h ****

 

**** Start of jsconfig.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS configuration macros.

 */

#ifndef JS_VERSION

#define JS_VERSION 150

#endif



#if JS_VERSION == 100



#define JS_BUG_AUTO_INDEX_PROPS 1       /* new object o: o.p = v sets o[0] */

#define JS_BUG_NULL_INDEX_PROPS 1       /* o[0] defaults to null, not void */

#define JS_BUG_EMPTY_INDEX_ZERO 1       /* o[""] is equivalent to o[0] */

#define JS_BUG_SHORT_CIRCUIT    1       /* 1 && 1 => true, 1 && 0 => 0 bug */

#define JS_BUG_EAGER_TOSTRING   1       /* o.toString() trumps o.valueOf() */

#define JS_BUG_VOID_TOSTRING    0       /* void 0 + 0 == "undefined0" */

#define JS_BUG_EVAL_THIS_FUN    0       /* eval('this') in function f is f */

#define JS_BUG_EVAL_THIS_SCOPE  0       /* Math.eval('sin(x)') vs. local x */

#define JS_BUG_FALLIBLE_EQOPS   1       /* fallible/intransitive equality ops */

#define JS_BUG_FALLIBLE_TONUM   1       /* fallible ValueToNumber primitive */

#define JS_BUG_WITH_CLOSURE     0       /* with(o)function f(){} sets o.f */

#define JS_BUG_SET_ENUMERATE    1       /* o.p=q flags o.p JSPROP_ENUMERATE */



#define JS_HAS_PROP_DELETE      0       /* delete o.p removes p from o */

#define JS_HAS_CALL_OBJECT      0       /* fun.caller is stack frame obj */

#define JS_HAS_LABEL_STATEMENT  0       /* has break/continue to label: */

#define JS_HAS_DO_WHILE_LOOP    0       /* has do {...} while (b) */

#define JS_HAS_SWITCH_STATEMENT 0       /* has switch (v) {case c: ...} */

#define JS_HAS_SOME_PERL_FUN    0       /* has array.join/reverse/sort */

#define JS_HAS_MORE_PERL_FUN    0       /* has array.push, str.substr, etc */

#define JS_HAS_VALUEOF_HINT     0       /* valueOf(hint) where hint is typeof */

#define JS_HAS_LEXICAL_CLOSURE  0       /* nested functions, lexically closed */

#define JS_HAS_APPLY_FUNCTION   0       /* has fun.apply(obj, argArray) */

#define JS_HAS_CALL_FUNCTION    0       /* has fun.call(obj, arg1, ... argN) */

#define JS_HAS_OBJ_PROTO_PROP   0       /* has o.__proto__ etc. */

#define JS_HAS_REGEXPS          0       /* has perl r.e.s via RegExp, /pat/ */

#define JS_HAS_SEQUENCE_OPS     0       /* has array.slice, string.concat */

#define JS_HAS_INITIALIZERS     0       /* has var o = {'foo': 42, 'bar':3} */

#define JS_HAS_OBJ_WATCHPOINT   0       /* has o.watch and o.unwatch */

#define JS_HAS_EXPORT_IMPORT    0       /* has export fun; import obj.fun */

#define JS_HAS_EVAL_THIS_SCOPE  0       /* Math.eval is same as with (Math) */

#define JS_HAS_TRIPLE_EQOPS     0       /* has === and !== identity eqops */

#define JS_HAS_SHARP_VARS       0       /* has #n=, #n# for object literals */

#define JS_HAS_REPLACE_LAMBDA   0       /* has string.replace(re, lambda) */

#define JS_HAS_SCRIPT_OBJECT    0       /* has (new Script("x++")).exec() */

#define JS_HAS_XDR              0       /* has XDR API and object methods */

#define JS_HAS_EXCEPTIONS       0       /* has exception handling */

#define JS_HAS_UNDEFINED        0       /* has global "undefined" property */

#define JS_HAS_TOSOURCE         0       /* has Object/Array toSource method */

#define JS_HAS_IN_OPERATOR      0       /* has in operator ('p' in {p:1}) */

#define JS_HAS_INSTANCEOF       0       /* has {p:1} instanceof Object */

#define JS_HAS_ARGS_OBJECT      0       /* has minimal ECMA arguments object */

#define JS_HAS_DEBUGGER_KEYWORD 0       /* has hook for debugger keyword */

#define JS_HAS_ERROR_EXCEPTIONS 0       /* has error object hierarchy */

#define JS_HAS_CATCH_GUARD      0       /* has exception handling catch guard */

#define JS_HAS_NEW_OBJ_METHODS  0       /* has Object.prototype query methods */

#define JS_HAS_SPARSE_ARRAYS    0       /* array methods preserve empty elems */

#define JS_HAS_DFLT_MSG_STRINGS 1       /* provides English error messages */

#define JS_HAS_NUMBER_FORMATS   0       /* numbers have formatting methods */

#define JS_HAS_GETTER_SETTER    0       /* has JS2 getter/setter functions */

#define JS_HAS_UNEVAL           0       /* has uneval() top-level function */

#define JS_HAS_CONST            0       /* has JS2 const as alternative var */

#define JS_HAS_FUN_EXPR_STMT    0       /* has function expression statement */

#define JS_HAS_LVALUE_RETURN    0       /* has o.item(i) = j; for native item */



#elif JS_VERSION == 110



#define JS_BUG_AUTO_INDEX_PROPS 0       /* new object o: o.p = v sets o[0] */

#define JS_BUG_NULL_INDEX_PROPS 1       /* o[0] defaults to null, not void */

#define JS_BUG_EMPTY_INDEX_ZERO 1       /* o[""] is equivalent to o[0] */

#define JS_BUG_SHORT_CIRCUIT    1       /* 1 && 1 => true, 1 && 0 => 0 bug */

#define JS_BUG_EAGER_TOSTRING   1       /* o.toString() trumps o.valueOf() */

#define JS_BUG_VOID_TOSTRING    0       /* void 0 + 0 == "undefined0" */

#define JS_BUG_EVAL_THIS_FUN    1       /* eval('this') in function f is f */

#define JS_BUG_EVAL_THIS_SCOPE  1       /* Math.eval('sin(x)') vs. local x */

#define JS_BUG_FALLIBLE_EQOPS   1       /* fallible/intransitive equality ops */

#define JS_BUG_FALLIBLE_TONUM   1       /* fallible ValueToNumber primitive */

#define JS_BUG_WITH_CLOSURE     0       /* with(o)function f(){} sets o.f */

#define JS_BUG_SET_ENUMERATE    1       /* o.p=q flags o.p JSPROP_ENUMERATE */



#define JS_HAS_PROP_DELETE      0       /* delete o.p removes p from o */

#define JS_HAS_CALL_OBJECT      0       /* fun.caller is stack frame obj */

#define JS_HAS_LABEL_STATEMENT  0       /* has break/continue to label: */

#define JS_HAS_DO_WHILE_LOOP    0       /* has do {...} while (b) */

#define JS_HAS_SWITCH_STATEMENT 0       /* has switch (v) {case c: ...} */

#define JS_HAS_SOME_PERL_FUN    1       /* has array.join/reverse/sort */

#define JS_HAS_MORE_PERL_FUN    0       /* has array.push, str.substr, etc */

#define JS_HAS_VALUEOF_HINT     0       /* valueOf(hint) where hint is typeof */

#define JS_HAS_LEXICAL_CLOSURE  0       /* nested functions, lexically closed */

#define JS_HAS_APPLY_FUNCTION   0       /* has apply(fun, arg1, ... argN) */

#define JS_HAS_CALL_FUNCTION    0       /* has fun.call(obj, arg1, ... argN) */

#define JS_HAS_OBJ_PROTO_PROP   0       /* has o.__proto__ etc. */

#define JS_HAS_REGEXPS          0       /* has perl r.e.s via RegExp, /pat/ */

#define JS_HAS_SEQUENCE_OPS     0       /* has array.slice, string.concat */

#define JS_HAS_INITIALIZERS     0       /* has var o = {'foo': 42, 'bar':3} */

#define JS_HAS_OBJ_WATCHPOINT   0       /* has o.watch and o.unwatch */

#define JS_HAS_EXPORT_IMPORT    0       /* has export fun; import obj.fun */

#define JS_HAS_EVAL_THIS_SCOPE  0       /* Math.eval is same as with (Math) */

#define JS_HAS_TRIPLE_EQOPS     0       /* has === and !== identity eqops */

#define JS_HAS_SHARP_VARS       0       /* has #n=, #n# for object literals */

#define JS_HAS_REPLACE_LAMBDA   0       /* has string.replace(re, lambda) */

#define JS_HAS_SCRIPT_OBJECT    0       /* has (new Script("x++")).exec() */

#define JS_HAS_XDR              0       /* has XDR API and object methods */

#define JS_HAS_EXCEPTIONS       0       /* has exception handling */

#define JS_HAS_UNDEFINED        0       /* has global "undefined" property */

#define JS_HAS_TOSOURCE         0       /* has Object/Array toSource method */

#define JS_HAS_IN_OPERATOR      0       /* has in operator ('p' in {p:1}) */

#define JS_HAS_INSTANCEOF       0       /* has {p:1} instanceof Object */

#define JS_HAS_ARGS_OBJECT      0       /* has minimal ECMA arguments object */

#define JS_HAS_DEBUGGER_KEYWORD 0       /* has hook for debugger keyword */

#define JS_HAS_ERROR_EXCEPTIONS 0       /* has error object hierarchy */

#define JS_HAS_CATCH_GUARD      0       /* has exception handling catch guard */

#define JS_HAS_NEW_OBJ_METHODS  0       /* has Object.prototype query methods */

#define JS_HAS_SPARSE_ARRAYS    0       /* array methods preserve empty elems */

#define JS_HAS_DFLT_MSG_STRINGS 1       /* provides English error messages */

#define JS_HAS_NUMBER_FORMATS   0       /* numbers have formatting methods */

#define JS_HAS_GETTER_SETTER    0       /* has JS2 getter/setter functions */

#define JS_HAS_UNEVAL           0       /* has uneval() top-level function */

#define JS_HAS_CONST            0       /* has JS2 const as alternative var */

#define JS_HAS_FUN_EXPR_STMT    0       /* has function expression statement */

#define JS_HAS_LVALUE_RETURN    0       /* has o.item(i) = j; for native item */



#elif JS_VERSION == 120



#define JS_BUG_AUTO_INDEX_PROPS 0       /* new object o: o.p = v sets o[0] */

#define JS_BUG_NULL_INDEX_PROPS 0       /* o[0] defaults to null, not void */

#define JS_BUG_EMPTY_INDEX_ZERO 0       /* o[""] is equivalent to o[0] */

#define JS_BUG_SHORT_CIRCUIT    0       /* 1 && 1 => true, 1 && 0 => 0 bug */

#define JS_BUG_EAGER_TOSTRING   0       /* o.toString() trumps o.valueOf() */

#define JS_BUG_VOID_TOSTRING    1       /* void 0 + 0 == "undefined0" */

#define JS_BUG_EVAL_THIS_FUN    0       /* eval('this') in function f is f */

#define JS_BUG_EVAL_THIS_SCOPE  0       /* Math.eval('sin(x)') vs. local x */

#define JS_BUG_FALLIBLE_EQOPS   0       /* fallible/intransitive equality ops */

#define JS_BUG_FALLIBLE_TONUM   0       /* fallible ValueToNumber primitive */

#define JS_BUG_WITH_CLOSURE     1       /* with(o)function f(){} sets o.f */

#define JS_BUG_SET_ENUMERATE    1       /* o.p=q flags o.p JSPROP_ENUMERATE */



#define JS_HAS_PROP_DELETE      1       /* delete o.p removes p from o */

#define JS_HAS_CALL_OBJECT      1       /* fun.caller is stack frame obj */

#define JS_HAS_LABEL_STATEMENT  1       /* has break/continue to label: */

#define JS_HAS_DO_WHILE_LOOP    1       /* has do {...} while (b) */

#define JS_HAS_SWITCH_STATEMENT 1       /* has switch (v) {case c: ...} */

#define JS_HAS_SOME_PERL_FUN    1       /* has array.join/reverse/sort */

#define JS_HAS_MORE_PERL_FUN    1       /* has array.push, str.substr, etc */

#define JS_HAS_VALUEOF_HINT     1       /* valueOf(hint) where hint is typeof */

#define JS_HAS_LEXICAL_CLOSURE  1       /* nested functions, lexically closed */

#define JS_HAS_APPLY_FUNCTION   1       /* has apply(fun, arg1, ... argN) */

#define JS_HAS_CALL_FUNCTION    0       /* has fun.call(obj, arg1, ... argN) */

#define JS_HAS_OBJ_PROTO_PROP   1       /* has o.__proto__ etc. */

#define JS_HAS_REGEXPS          1       /* has perl r.e.s via RegExp, /pat/ */

#define JS_HAS_SEQUENCE_OPS     1       /* has array.slice, string.concat */

#define JS_HAS_INITIALIZERS     1       /* has var o = {'foo': 42, 'bar':3} */

#define JS_HAS_OBJ_WATCHPOINT   1       /* has o.watch and o.unwatch */

#define JS_HAS_EXPORT_IMPORT    1       /* has export fun; import obj.fun */

#define JS_HAS_EVAL_THIS_SCOPE  1       /* Math.eval is same as with (Math) */

#define JS_HAS_TRIPLE_EQOPS     0       /* has === and !== identity eqops */

#define JS_HAS_SHARP_VARS       0       /* has #n=, #n# for object literals */

#define JS_HAS_REPLACE_LAMBDA   0       /* has string.replace(re, lambda) */

#define JS_HAS_SCRIPT_OBJECT    0       /* has (new Script("x++")).exec() */

#define JS_HAS_XDR              0       /* has XDR API and object methods */

#define JS_HAS_EXCEPTIONS       0       /* has exception handling */

#define JS_HAS_UNDEFINED        0       /* has global "undefined" property */

#define JS_HAS_TOSOURCE         0       /* has Object/Array toSource method */

#define JS_HAS_IN_OPERATOR      0       /* has in operator ('p' in {p:1}) */

#define JS_HAS_INSTANCEOF       0       /* has {p:1} instanceof Object */

#define JS_HAS_ARGS_OBJECT      0       /* has minimal ECMA arguments object */

#define JS_HAS_DEBUGGER_KEYWORD 0       /* has hook for debugger keyword */

#define JS_HAS_ERROR_EXCEPTIONS 0       /* has error object hierarchy */

#define JS_HAS_CATCH_GUARD      0       /* has exception handling catch guard */

#define JS_HAS_NEW_OBJ_METHODS  0       /* has Object.prototype query methods */

#define JS_HAS_SPARSE_ARRAYS    0       /* array methods preserve empty elems */

#define JS_HAS_DFLT_MSG_STRINGS 1       /* provides English error messages */

#define JS_HAS_NUMBER_FORMATS   0       /* numbers have formatting methods */

#define JS_HAS_GETTER_SETTER    0       /* has JS2 getter/setter functions */

#define JS_HAS_UNEVAL           0       /* has uneval() top-level function */

#define JS_HAS_CONST            0       /* has JS2 const as alternative var */

#define JS_HAS_FUN_EXPR_STMT    0       /* has function expression statement */

#define JS_HAS_LVALUE_RETURN    0       /* has o.item(i) = j; for native item */



#elif JS_VERSION == 130



#define JS_BUG_AUTO_INDEX_PROPS 0       /* new object o: o.p = v sets o[0] */

#define JS_BUG_NULL_INDEX_PROPS 0       /* o[0] defaults to null, not void */

#define JS_BUG_EMPTY_INDEX_ZERO 0       /* o[""] is equivalent to o[0] */

#define JS_BUG_SHORT_CIRCUIT    0       /* 1 && 1 => true, 1 && 0 => 0 bug */

#define JS_BUG_EAGER_TOSTRING   0       /* o.toString() trumps o.valueOf() */

#define JS_BUG_VOID_TOSTRING    0       /* void 0 + 0 == "undefined0" */

#define JS_BUG_EVAL_THIS_FUN    0       /* eval('this') in function f is f */

#define JS_BUG_EVAL_THIS_SCOPE  0       /* Math.eval('sin(x)') vs. local x */

#define JS_BUG_FALLIBLE_EQOPS   0       /* fallible/intransitive equality ops */

#define JS_BUG_FALLIBLE_TONUM   0       /* fallible ValueToNumber primitive */

#define JS_BUG_WITH_CLOSURE     1       /* with(o)function f(){} sets o.f */

#define JS_BUG_SET_ENUMERATE    0       /* o.p=q flags o.p JSPROP_ENUMERATE */



#define JS_HAS_PROP_DELETE      1       /* delete o.p removes p from o */

#define JS_HAS_CALL_OBJECT      1       /* fun.caller is stack frame obj */

#define JS_HAS_LABEL_STATEMENT  1       /* has break/continue to label: */

#define JS_HAS_DO_WHILE_LOOP    1       /* has do {...} while (b) */

#define JS_HAS_SWITCH_STATEMENT 1       /* has switch (v) {case c: ...} */

#define JS_HAS_SOME_PERL_FUN    1       /* has array.join/reverse/sort */

#define JS_HAS_MORE_PERL_FUN    1       /* has array.push, str.substr, etc */

#define JS_HAS_VALUEOF_HINT     1       /* valueOf(hint) where hint is typeof */

#define JS_HAS_LEXICAL_CLOSURE  1       /* nested functions, lexically closed */

#define JS_HAS_APPLY_FUNCTION   1       /* has apply(fun, arg1, ... argN) */

#define JS_HAS_CALL_FUNCTION    1       /* has fun.call(obj, arg1, ... argN) */

#define JS_HAS_OBJ_PROTO_PROP   1       /* has o.__proto__ etc. */

#define JS_HAS_REGEXPS          1       /* has perl r.e.s via RegExp, /pat/ */

#define JS_HAS_SEQUENCE_OPS     1       /* has array.slice, string.concat */

#define JS_HAS_INITIALIZERS     1       /* has var o = {'foo': 42, 'bar':3} */

#define JS_HAS_OBJ_WATCHPOINT   1       /* has o.watch and o.unwatch */

#define JS_HAS_EXPORT_IMPORT    1       /* has export fun; import obj.fun */

#define JS_HAS_EVAL_THIS_SCOPE  1       /* Math.eval is same as with (Math) */

#define JS_HAS_TRIPLE_EQOPS     1       /* has === and !== identity eqops */

#define JS_HAS_SHARP_VARS       1       /* has #n=, #n# for object literals */

#define JS_HAS_REPLACE_LAMBDA   1       /* has string.replace(re, lambda) */

#define JS_HAS_SCRIPT_OBJECT    1       /* has (new Script("x++")).exec() */

#define JS_HAS_XDR              1       /* has XDR API and object methods */

#define JS_HAS_EXCEPTIONS       0       /* has exception handling */

#define JS_HAS_UNDEFINED        1       /* has global "undefined" property */

#define JS_HAS_TOSOURCE         1       /* has Object/Array toSource method */

#define JS_HAS_IN_OPERATOR      0       /* has in operator ('p' in {p:1}) */

#define JS_HAS_INSTANCEOF       0       /* has {p:1} instanceof Object */

#define JS_HAS_ARGS_OBJECT      1       /* has minimal ECMA arguments object */

#define JS_HAS_DEBUGGER_KEYWORD 1       /* has hook for debugger keyword */

#define JS_HAS_ERROR_EXCEPTIONS 0       /* has error object hierarchy */

#define JS_HAS_CATCH_GUARD      0       /* has exception handling catch guard */

#define JS_HAS_NEW_OBJ_METHODS  0       /* has Object.prototype query methods */

#define JS_HAS_SPARSE_ARRAYS    0       /* array methods preserve empty elems */

#define JS_HAS_DFLT_MSG_STRINGS 1       /* provides English error messages */

#define JS_HAS_NUMBER_FORMATS   0       /* numbers have formatting methods */

#define JS_HAS_GETTER_SETTER    0       /* has JS2 getter/setter functions */

#define JS_HAS_UNEVAL           0       /* has uneval() top-level function */

#define JS_HAS_CONST            0       /* has JS2 const as alternative var */

#define JS_HAS_FUN_EXPR_STMT    0       /* has function expression statement */

#define JS_HAS_LVALUE_RETURN    0       /* has o.item(i) = j; for native item */



#elif JS_VERSION == 140



#define JS_BUG_AUTO_INDEX_PROPS 0       /* new object o: o.p = v sets o[0] */

#define JS_BUG_NULL_INDEX_PROPS 0       /* o[0] defaults to null, not void */

#define JS_BUG_EMPTY_INDEX_ZERO 0       /* o[""] is equivalent to o[0] */

#define JS_BUG_SHORT_CIRCUIT    0       /* 1 && 1 => true, 1 && 0 => 0 bug */

#define JS_BUG_EAGER_TOSTRING   0       /* o.toString() trumps o.valueOf() */

#define JS_BUG_VOID_TOSTRING    0       /* void 0 + 0 == "undefined0" */

#define JS_BUG_EVAL_THIS_FUN    0       /* eval('this') in function f is f */

#define JS_BUG_EVAL_THIS_SCOPE  0       /* Math.eval('sin(x)') vs. local x */

#define JS_BUG_FALLIBLE_EQOPS   0       /* fallible/intransitive equality ops */

#define JS_BUG_FALLIBLE_TONUM   0       /* fallible ValueToNumber primitive */

#define JS_BUG_WITH_CLOSURE     1       /* with(o)function f(){} sets o.f */

#define JS_BUG_SET_ENUMERATE    0       /* o.p=q flags o.p JSPROP_ENUMERATE */



#define JS_HAS_PROP_DELETE      1       /* delete o.p removes p from o */

#define JS_HAS_CALL_OBJECT      1       /* fun.caller is stack frame obj */

#define JS_HAS_LABEL_STATEMENT  1       /* has break/continue to label: */

#define JS_HAS_DO_WHILE_LOOP    1       /* has do {...} while (b) */

#define JS_HAS_SWITCH_STATEMENT 1       /* has switch (v) {case c: ...} */

#define JS_HAS_SOME_PERL_FUN    1       /* has array.join/reverse/sort */

#define JS_HAS_MORE_PERL_FUN    1       /* has array.push, str.substr, etc */

#define JS_HAS_VALUEOF_HINT     1       /* valueOf(hint) where hint is typeof */

#define JS_HAS_LEXICAL_CLOSURE  1       /* nested functions, lexically closed */

#define JS_HAS_APPLY_FUNCTION   1       /* has apply(fun, arg1, ... argN) */

#define JS_HAS_CALL_FUNCTION    1       /* has fun.call(obj, arg1, ... argN) */

#define JS_HAS_OBJ_PROTO_PROP   1       /* has o.__proto__ etc. */

#define JS_HAS_REGEXPS          1       /* has perl r.e.s via RegExp, /pat/ */

#define JS_HAS_SEQUENCE_OPS     1       /* has array.slice, string.concat */

#define JS_HAS_INITIALIZERS     1       /* has var o = {'foo': 42, 'bar':3} */

#define JS_HAS_OBJ_WATCHPOINT   1       /* has o.watch and o.unwatch */

#define JS_HAS_EXPORT_IMPORT    1       /* has export fun; import obj.fun */

#define JS_HAS_EVAL_THIS_SCOPE  1       /* Math.eval is same as with (Math) */

#define JS_HAS_TRIPLE_EQOPS     1       /* has === and !== identity eqops */

#define JS_HAS_SHARP_VARS       1       /* has #n=, #n# for object literals */

#define JS_HAS_REPLACE_LAMBDA   1       /* has string.replace(re, lambda) */

#define JS_HAS_SCRIPT_OBJECT    1       /* has (new Script("x++")).exec() */

#define JS_HAS_XDR		1	/* has XDR API and object methods */

#define JS_HAS_EXCEPTIONS	1	/* has exception handling */

#define JS_HAS_UNDEFINED        1       /* has global "undefined" property */

#define JS_HAS_TOSOURCE         1       /* has Object/Array toSource method */

#define JS_HAS_IN_OPERATOR      1       /* has in operator ('p' in {p:1}) */

#define JS_HAS_INSTANCEOF       1       /* has {p:1} instanceof Object */

#define JS_HAS_ARGS_OBJECT      1       /* has minimal ECMA arguments object */

#define JS_HAS_DEBUGGER_KEYWORD 1       /* has hook for debugger keyword */

#define JS_HAS_ERROR_EXCEPTIONS 0       /* rt errors reflected as exceptions */

#define JS_HAS_CATCH_GUARD      0       /* has exception handling catch guard */

#define JS_HAS_NEW_OBJ_METHODS  0       /* has Object.prototype query methods */

#define JS_HAS_SPARSE_ARRAYS    0       /* array methods preserve empty elems */

#define JS_HAS_DFLT_MSG_STRINGS 1       /* provides English error messages */

#define JS_HAS_NUMBER_FORMATS   0       /* numbers have formatting methods */

#define JS_HAS_GETTER_SETTER    0       /* has JS2 getter/setter functions */

#define JS_HAS_UNEVAL           0       /* has uneval() top-level function */

#define JS_HAS_CONST            0       /* has JS2 const as alternative var */

#define JS_HAS_FUN_EXPR_STMT    0       /* has function expression statement */

#define JS_HAS_LVALUE_RETURN    0       /* has o.item(i) = j; for native item */



#elif JS_VERSION == 150



#define JS_BUG_AUTO_INDEX_PROPS 0       /* new object o: o.p = v sets o[0] */

#define JS_BUG_NULL_INDEX_PROPS 0       /* o[0] defaults to null, not void */

#define JS_BUG_EMPTY_INDEX_ZERO 0       /* o[""] is equivalent to o[0] */

#define JS_BUG_SHORT_CIRCUIT    0       /* 1 && 1 => true, 1 && 0 => 0 bug */

#define JS_BUG_EAGER_TOSTRING   0       /* o.toString() trumps o.valueOf() */

#define JS_BUG_VOID_TOSTRING    0       /* void 0 + 0 == "undefined0" */

#define JS_BUG_EVAL_THIS_FUN    0       /* eval('this') in function f is f */

#define JS_BUG_EVAL_THIS_SCOPE  0       /* Math.eval('sin(x)') vs. local x */

#define JS_BUG_FALLIBLE_EQOPS   0       /* fallible/intransitive equality ops */

#define JS_BUG_FALLIBLE_TONUM   0       /* fallible ValueToNumber primitive */

#define JS_BUG_WITH_CLOSURE     0       /* with(o)function f(){} sets o.f */

#define JS_BUG_SET_ENUMERATE    0       /* o.p=q flags o.p JSPROP_ENUMERATE */



#define JS_HAS_PROP_DELETE      1       /* delete o.p removes p from o */

#define JS_HAS_CALL_OBJECT      1       /* fun.caller is stack frame obj */

#define JS_HAS_LABEL_STATEMENT  1       /* has break/continue to label: */

#define JS_HAS_DO_WHILE_LOOP    1       /* has do {...} while (b) */

#define JS_HAS_SWITCH_STATEMENT 1       /* has switch (v) {case c: ...} */

#define JS_HAS_SOME_PERL_FUN    1       /* has array.join/reverse/sort */

#define JS_HAS_MORE_PERL_FUN    1       /* has array.push, str.substr, etc */

#define JS_HAS_VALUEOF_HINT     1       /* valueOf(hint) where hint is typeof */

#define JS_HAS_LEXICAL_CLOSURE  1       /* nested functions, lexically closed */

#define JS_HAS_APPLY_FUNCTION   1       /* has apply(fun, arg1, ... argN) */

#define JS_HAS_CALL_FUNCTION    1       /* has fun.call(obj, arg1, ... argN) */

#define JS_HAS_OBJ_PROTO_PROP   1       /* has o.__proto__ etc. */

#define JS_HAS_REGEXPS          1       /* has perl r.e.s via RegExp, /pat/ */

#define JS_HAS_SEQUENCE_OPS     1       /* has array.slice, string.concat */

#define JS_HAS_INITIALIZERS     1       /* has var o = {'foo': 42, 'bar':3} */

#define JS_HAS_OBJ_WATCHPOINT   1       /* has o.watch and o.unwatch */

#define JS_HAS_EXPORT_IMPORT    1       /* has export fun; import obj.fun */

#define JS_HAS_EVAL_THIS_SCOPE  1       /* Math.eval is same as with (Math) */

#define JS_HAS_TRIPLE_EQOPS     1       /* has === and !== identity eqops */

#define JS_HAS_SHARP_VARS       1       /* has #n=, #n# for object literals */

#define JS_HAS_REPLACE_LAMBDA   1       /* has string.replace(re, lambda) */

#define JS_HAS_SCRIPT_OBJECT    1       /* has (new Script("x++")).exec() */

#define JS_HAS_XDR              1       /* has XDR API and object methods */

#define JS_HAS_EXCEPTIONS       1       /* has exception handling */

#define JS_HAS_UNDEFINED        1       /* has global "undefined" property */

#define JS_HAS_TOSOURCE         1       /* has Object/Array toSource method */

#define JS_HAS_IN_OPERATOR      1       /* has in operator ('p' in {p:1}) */

#define JS_HAS_INSTANCEOF       1       /* has {p:1} instanceof Object */

#define JS_HAS_ARGS_OBJECT      1       /* has minimal ECMA arguments object */

#define JS_HAS_DEBUGGER_KEYWORD 1       /* has hook for debugger keyword */

#define JS_HAS_ERROR_EXCEPTIONS 1       /* rt errors reflected as exceptions */

#define JS_HAS_CATCH_GUARD      1       /* has exception handling catch guard */

#define JS_HAS_NEW_OBJ_METHODS  1       /* has Object.prototype query methods */

#define JS_HAS_SPARSE_ARRAYS    0       /* array methods preserve empty elems */

#define JS_HAS_DFLT_MSG_STRINGS 1       /* provides English error messages */

#define JS_HAS_NUMBER_FORMATS   1       /* numbers have formatting methods */

#define JS_HAS_GETTER_SETTER    1       /* has JS2 getter/setter functions */

#define JS_HAS_UNEVAL           1       /* has uneval() top-level function */

#define JS_HAS_CONST            1       /* has JS2 const as alternative var */

#define JS_HAS_FUN_EXPR_STMT    1       /* has function expression statement */

#define JS_HAS_LVALUE_RETURN    1       /* has o.item(i) = j; for native item */



#else



#error "unknown JS_VERSION"



#endif

 

**** End of jsconfig.h ****

 

**** Start of jscpucfg.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * Generate CPU-specific bit-size and similar #defines.

 */

#include <stdio.h>



#ifdef CROSS_COMPILE

#include <prtypes.h>

#define INT64 PRInt64

#else



#ifdef __MWERKS__

#define XP_MAC 1

#endif



/************************************************************************/



/* Generate cpucfg.h */

#ifdef XP_MAC

#include <Types.h>

#define INT64	UnsignedWide

#else

#ifdef XP_PC

#ifdef WIN32

#if defined(__GNUC__)

#define INT64   long long

#else

#define INT64	_int64

#endif /* __GNUC__ */

#else

#define INT64	long

#endif

#else

#if defined(HPUX) || defined(__QNX__) || defined(_SCO_DS) || defined(UNIXWARE)

#define INT64	long

#else

#define INT64	long long

#endif

#endif

#endif



#endif /* CROSS_COMPILE */



typedef void *prword;



struct align_short {

    char c;

    short a;

};

struct align_int {

    char c;

    int a;

};

struct align_long {

    char c;

    long a;

};

struct align_int64 {

    char c;

    INT64 a;

};

struct align_fakelonglong {

    char c;

    struct {

	long hi, lo;

    } a;

};

struct align_float {

    char c;

    float a;

};

struct align_double {

    char c;

    double a;

};

struct align_pointer {

    char c;

    void *a;

};

struct align_prword {

    char c;

    prword a;

};



#define ALIGN_OF(type) \

    (((char*)&(((struct align_##type *)0)->a)) - ((char*)0))



unsigned int bpb;



static int Log2(unsigned int n)

{

    int log2 = 0;



    if (n & (n-1))

	log2++;

    if (n >> 16)

	log2 += 16, n >>= 16;

    if (n >> 8)

	log2 += 8, n >>= 8;

    if (n >> 4)

	log2 += 4, n >>= 4;

    if (n >> 2)

	log2 += 2, n >>= 2;

    if (n >> 1)

	log2++;

    return log2;

}



/* We assume that int's are 32 bits */

static void do64(void)

{

    union {

	long i;

	char c[4];

    } u;



    u.i = 0x01020304;

    if (u.c[0] == 0x01) {

	printf("#undef  IS_LITTLE_ENDIAN\n");

	printf("#define IS_BIG_ENDIAN 1\n\n");

    } else {

	printf("#define IS_LITTLE_ENDIAN 1\n");

	printf("#undef  IS_BIG_ENDIAN\n\n");

    }

}



static void do32(void)

{

    union {

	long i;

	char c[4];

    } u;



    u.i = 0x01020304;

    if (u.c[0] == 0x01) {

	printf("#undef  IS_LITTLE_ENDIAN\n");

	printf("#define IS_BIG_ENDIAN 1\n\n");

    } else {

	printf("#define IS_LITTLE_ENDIAN 1\n");

	printf("#undef  IS_BIG_ENDIAN\n\n");

    }

}



/*

 * Conceivably this could actually be used, but there is lots of code out

 * there with ands and shifts in it that assumes a byte is exactly 8 bits,

 * so forget about porting THIS code to all those non 8 bit byte machines.

 */

static void BitsPerByte(void)

{

    bpb = 8;

}



int main(int argc, char **argv)

{

    int sizeof_char, sizeof_short, sizeof_int, sizeof_int64, sizeof_long,

        sizeof_float, sizeof_double, sizeof_word, sizeof_dword;

    int bits_per_int64_log2, align_of_short, align_of_int, align_of_long,

        align_of_int64, align_of_float, align_of_double, align_of_pointer,

        align_of_word;



    BitsPerByte();



    printf("#ifndef js_cpucfg___\n");

    printf("#define js_cpucfg___\n\n");



    printf("/* AUTOMATICALLY GENERATED - DO NOT EDIT */\n\n");



#ifdef CROSS_COMPILE

#if defined(IS_LITTLE_ENDIAN)

    printf("#define IS_LITTLE_ENDIAN 1\n");

    printf("#undef  IS_BIG_ENDIAN\n\n");

#elif defined(IS_BIG_ENDIAN)

    printf("#undef  IS_LITTLE_ENDIAN\n");

    printf("#define IS_BIG_ENDIAN 1\n\n");

#else

#error "Endianess not defined."

#endif



    sizeof_char		= PR_BYTES_PER_BYTE;

    sizeof_short	= PR_BYTES_PER_SHORT;

    sizeof_int		= PR_BYTES_PER_INT;

    sizeof_int64	= PR_BYTES_PER_INT64;

    sizeof_long		= PR_BYTES_PER_LONG;

    sizeof_float	= PR_BYTES_PER_FLOAT;

    sizeof_double	= PR_BYTES_PER_DOUBLE;

    sizeof_word		= PR_BYTES_PER_WORD;

    sizeof_dword	= PR_BYTES_PER_DWORD;



    bits_per_int64_log2 = PR_BITS_PER_INT64_LOG2;



    align_of_short	= PR_ALIGN_OF_SHORT;

    align_of_int	= PR_ALIGN_OF_INT;

    align_of_long	= PR_ALIGN_OF_LONG;

    align_of_int64	= PR_ALIGN_OF_INT64;

    align_of_float	= PR_ALIGN_OF_FLOAT;

    align_of_double	= PR_ALIGN_OF_DOUBLE;

    align_of_pointer	= PR_ALIGN_OF_POINTER;

    align_of_word	= PR_ALIGN_OF_WORD;



#else /* !CROSS_COMPILE */

    if (sizeof(long) == 8) {

	do64();

    } else {

	do32();

    }



    sizeof_char		= sizeof(char);

    sizeof_short	= sizeof(short);

    sizeof_int		= sizeof(int);

    sizeof_int64	= 8;

    sizeof_long		= sizeof(long);

    sizeof_float	= sizeof(float);

    sizeof_double	= sizeof(double);

    sizeof_word		= sizeof(prword);

    sizeof_dword	= 8;



    bits_per_int64_log2 = 6;



    align_of_short	= ALIGN_OF(short);

    align_of_int	= ALIGN_OF(int);

    align_of_long	= ALIGN_OF(long);

    if (sizeof(INT64) < 8) {

	/* this machine doesn't actually support int64's */

        align_of_int64	= ALIGN_OF(fakelonglong);

    } else {

        align_of_int64	= ALIGN_OF(int64);

    }

    align_of_float	= ALIGN_OF(float);

    align_of_double	= ALIGN_OF(double);

    align_of_pointer	= ALIGN_OF(pointer);

    align_of_word	= ALIGN_OF(prword);



#endif /* CROSS_COMPILE */



    printf("#define JS_BYTES_PER_BYTE   %dL\n", sizeof_char);

    printf("#define JS_BYTES_PER_SHORT  %dL\n", sizeof_short);

    printf("#define JS_BYTES_PER_INT    %dL\n", sizeof_int);

    printf("#define JS_BYTES_PER_INT64  %dL\n", sizeof_int64);

    printf("#define JS_BYTES_PER_LONG   %dL\n", sizeof_long);

    printf("#define JS_BYTES_PER_FLOAT  %dL\n", sizeof_float);

    printf("#define JS_BYTES_PER_DOUBLE %dL\n", sizeof_double);

    printf("#define JS_BYTES_PER_WORD   %dL\n", sizeof_word);

    printf("#define JS_BYTES_PER_DWORD  %dL\n", sizeof_dword);

    printf("\n");



    printf("#define JS_BITS_PER_BYTE    %dL\n", bpb);

    printf("#define JS_BITS_PER_SHORT   %dL\n", bpb * sizeof_short);

    printf("#define JS_BITS_PER_INT     %dL\n", bpb * sizeof_int);

    printf("#define JS_BITS_PER_INT64   %dL\n", bpb * sizeof_int64);

    printf("#define JS_BITS_PER_LONG    %dL\n", bpb * sizeof_long);

    printf("#define JS_BITS_PER_FLOAT   %dL\n", bpb * sizeof_float);

    printf("#define JS_BITS_PER_DOUBLE  %dL\n", bpb * sizeof_double);

    printf("#define JS_BITS_PER_WORD    %dL\n", bpb * sizeof_word);

    printf("\n");



    printf("#define JS_BITS_PER_BYTE_LOG2   %dL\n", Log2(bpb));

    printf("#define JS_BITS_PER_SHORT_LOG2  %dL\n", Log2(bpb * sizeof_short));

    printf("#define JS_BITS_PER_INT_LOG2    %dL\n", Log2(bpb * sizeof_int));

    printf("#define JS_BITS_PER_INT64_LOG2  %dL\n", bits_per_int64_log2);

    printf("#define JS_BITS_PER_LONG_LOG2   %dL\n", Log2(bpb * sizeof_long));

    printf("#define JS_BITS_PER_FLOAT_LOG2  %dL\n", Log2(bpb * sizeof_float));

    printf("#define JS_BITS_PER_DOUBLE_LOG2 %dL\n", Log2(bpb * sizeof_double));

    printf("#define JS_BITS_PER_WORD_LOG2   %dL\n", Log2(bpb * sizeof_word));

    printf("\n");



    printf("#define JS_ALIGN_OF_SHORT   %dL\n", align_of_short);

    printf("#define JS_ALIGN_OF_INT     %dL\n", align_of_int);

    printf("#define JS_ALIGN_OF_LONG    %dL\n", align_of_long);

    printf("#define JS_ALIGN_OF_INT64   %dL\n", align_of_int64);

    printf("#define JS_ALIGN_OF_FLOAT   %dL\n", align_of_float);

    printf("#define JS_ALIGN_OF_DOUBLE  %dL\n", align_of_double);

    printf("#define JS_ALIGN_OF_POINTER %dL\n", align_of_pointer);

    printf("#define JS_ALIGN_OF_WORD    %dL\n", align_of_word);

    printf("\n");



    printf("#define JS_BYTES_PER_WORD_LOG2   %dL\n", Log2(sizeof_word));

    printf("#define JS_BYTES_PER_DWORD_LOG2  %dL\n", Log2(sizeof_dword));

    printf("#define JS_WORDS_PER_DWORD_LOG2  %dL\n", Log2(sizeof_dword/sizeof_word));

    printf("\n");



    printf("#endif /* js_cpucfg___ */\n");



    return 0;

}

 

**** End of jscpucfg.c ****

 

**** Start of jscpucfg.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef js_cpucfg___

#define js_cpucfg___



#include "jsosdep.h"



#ifdef XP_MAC

#undef  IS_LITTLE_ENDIAN

#define IS_BIG_ENDIAN 1



#define JS_BYTES_PER_BYTE   1L

#define JS_BYTES_PER_SHORT  2L

#define JS_BYTES_PER_INT    4L

#define JS_BYTES_PER_INT64  8L

#define JS_BYTES_PER_LONG   4L

#define JS_BYTES_PER_FLOAT  4L

#define JS_BYTES_PER_DOUBLE 8L

#define JS_BYTES_PER_WORD   4L

#define JS_BYTES_PER_DWORD  8L



#define JS_BITS_PER_BYTE    8L

#define JS_BITS_PER_SHORT   16L

#define JS_BITS_PER_INT     32L

#define JS_BITS_PER_INT64   64L

#define JS_BITS_PER_LONG    32L

#define JS_BITS_PER_FLOAT   32L

#define JS_BITS_PER_DOUBLE  64L

#define JS_BITS_PER_WORD    32L



#define JS_BITS_PER_BYTE_LOG2   3L

#define JS_BITS_PER_SHORT_LOG2  4L

#define JS_BITS_PER_INT_LOG2    5L

#define JS_BITS_PER_INT64_LOG2  6L

#define JS_BITS_PER_LONG_LOG2   5L

#define JS_BITS_PER_FLOAT_LOG2  5L

#define JS_BITS_PER_DOUBLE_LOG2 6L

#define JS_BITS_PER_WORD_LOG2   5L



#define JS_ALIGN_OF_SHORT   2L

#define JS_ALIGN_OF_INT     4L

#define JS_ALIGN_OF_LONG    4L

#define JS_ALIGN_OF_INT64   2L

#define JS_ALIGN_OF_FLOAT   4L

#define JS_ALIGN_OF_DOUBLE  4L

#define JS_ALIGN_OF_POINTER 4L

#define JS_ALIGN_OF_WORD    4L



#define JS_BYTES_PER_WORD_LOG2   2L

#define JS_BYTES_PER_DWORD_LOG2  3L

#define PR_WORDS_PER_DWORD_LOG2  1L



#elif defined(XP_PC)



#if defined( _WIN32) || defined(XP_OS2)

#undef IS_LITTLE_ENDIAN

#define IS_LITTLE_ENDIAN 1

#undef  IS_BIG_ENDIAN



#define JS_BYTES_PER_BYTE   1L

#define JS_BYTES_PER_SHORT  2L

#define JS_BYTES_PER_INT    4L

#define JS_BYTES_PER_INT64  8L

#define JS_BYTES_PER_LONG   4L

#define JS_BYTES_PER_FLOAT  4L

#define JS_BYTES_PER_DOUBLE 8L

#define JS_BYTES_PER_WORD   4L

#define JS_BYTES_PER_DWORD  8L



#define JS_BITS_PER_BYTE    8L

#define JS_BITS_PER_SHORT   16L

#define JS_BITS_PER_INT     32L

#define JS_BITS_PER_INT64   64L

#define JS_BITS_PER_LONG    32L

#define JS_BITS_PER_FLOAT   32L

#define JS_BITS_PER_DOUBLE  64L

#define JS_BITS_PER_WORD    32L



#define JS_BITS_PER_BYTE_LOG2   3L

#define JS_BITS_PER_SHORT_LOG2  4L

#define JS_BITS_PER_INT_LOG2    5L

#define JS_BITS_PER_INT64_LOG2  6L

#define JS_BITS_PER_LONG_LOG2   5L

#define JS_BITS_PER_FLOAT_LOG2  5L

#define JS_BITS_PER_DOUBLE_LOG2 6L

#define JS_BITS_PER_WORD_LOG2   5L



#define JS_ALIGN_OF_SHORT   2L

#define JS_ALIGN_OF_INT     4L

#define JS_ALIGN_OF_LONG    4L

#define JS_ALIGN_OF_INT64   8L

#define JS_ALIGN_OF_FLOAT   4L

#define JS_ALIGN_OF_DOUBLE  4L

#define JS_ALIGN_OF_POINTER 4L

#define JS_ALIGN_OF_WORD    4L



#define JS_BYTES_PER_WORD_LOG2   2L

#define JS_BYTES_PER_DWORD_LOG2  3L

#define PR_WORDS_PER_DWORD_LOG2  1L

#endif /* _WIN32 || XP_OS2 */



#if defined(_WINDOWS) && !defined(_WIN32) /* WIN16 */

#define IS_LITTLE_ENDIAN 1

#undef  IS_BIG_ENDIAN



#define JS_BYTES_PER_BYTE   1L

#define JS_BYTES_PER_SHORT  2L

#define JS_BYTES_PER_INT    2L

#define JS_BYTES_PER_INT64  8L

#define JS_BYTES_PER_LONG   4L

#define JS_BYTES_PER_FLOAT  4L

#define JS_BYTES_PER_DOUBLE 8L

#define JS_BYTES_PER_WORD   4L

#define JS_BYTES_PER_DWORD  8L



#define JS_BITS_PER_BYTE    8L

#define JS_BITS_PER_SHORT   16L

#define JS_BITS_PER_INT     16L

#define JS_BITS_PER_INT64   64L

#define JS_BITS_PER_LONG    32L

#define JS_BITS_PER_FLOAT   32L

#define JS_BITS_PER_DOUBLE  64L

#define JS_BITS_PER_WORD    32L



#define JS_BITS_PER_BYTE_LOG2   3L

#define JS_BITS_PER_SHORT_LOG2  4L

#define JS_BITS_PER_INT_LOG2    4L

#define JS_BITS_PER_INT64_LOG2  6L

#define JS_BITS_PER_LONG_LOG2   5L

#define JS_BITS_PER_FLOAT_LOG2  5L

#define JS_BITS_PER_DOUBLE_LOG2 6L

#define JS_BITS_PER_WORD_LOG2   5L



#define JS_ALIGN_OF_SHORT   2L

#define JS_ALIGN_OF_INT     2L

#define JS_ALIGN_OF_LONG    2L

#define JS_ALIGN_OF_INT64   2L

#define JS_ALIGN_OF_FLOAT   2L

#define JS_ALIGN_OF_DOUBLE  2L

#define JS_ALIGN_OF_POINTER 2L

#define JS_ALIGN_OF_WORD    2L



#define JS_BYTES_PER_WORD_LOG2   2L

#define JS_BYTES_PER_DWORD_LOG2  3L

#define PR_WORDS_PER_DWORD_LOG2  1L

#endif /* defined(_WINDOWS) && !defined(_WIN32) */



#elif defined(XP_UNIX) || defined(XP_BEOS)



#error "This file is supposed to be auto-generated on UNIX platforms, but the"

#error "static version for Mac and Windows platforms is being used."

#error "Something's probably wrong with paths/headers/dependencies/Makefiles."



#else



#error "Must define one of XP_MAC, XP_PC, XP_UNIX, or XP_BEOS"



#endif



#endif /* js_cpucfg___ */

 

**** End of jscpucfg.h ****

 

**** Start of jsdate.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS date methods.

 */



/*

 * "For example, OS/360 devotes 26 bytes of the permanently

 *  resident date-turnover routine to the proper handling of

 *  December 31 on leap years (when it is Day 366).  That

 *  might have been left to the operator."

 *

 * Frederick Brooks, 'The Second-System Effect'.

 */



#include "jsstddef.h"

#include <ctype.h>

#include <math.h>

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsprf.h"

#include "prmjtime.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jsapi.h"

#include "jsconfig.h"

#include "jscntxt.h"

#include "jsdate.h"

#include "jsinterp.h"

#include "jsnum.h"

#include "jsobj.h"

#include "jsstr.h"



/*

 * The JS 'Date' object is patterned after the Java 'Date' object.

 * Here is an script:

 *

 *    today = new Date();

 *

 *    print(today.toLocaleString());

 *

 *    weekDay = today.getDay();

 *

 *

 * These Java (and ECMA-262) methods are supported:

 *

 *     UTC

 *     getDate (getUTCDate)

 *     getDay (getUTCDay)

 *     getHours (getUTCHours)

 *     getMinutes (getUTCMinutes)

 *     getMonth (getUTCMonth)

 *     getSeconds (getUTCSeconds)

 *     getMilliseconds (getUTCMilliseconds)

 *     getTime

 *     getTimezoneOffset

 *     getYear

 *     getFullYear (getUTCFullYear)

 *     parse

 *     setDate (setUTCDate)

 *     setHours (setUTCHours)

 *     setMinutes (setUTCMinutes)

 *     setMonth (setUTCMonth)

 *     setSeconds (setUTCSeconds)

 *     setMilliseconds (setUTCMilliseconds)

 *     setTime

 *     setYear (setFullYear, setUTCFullYear)

 *     toGMTString (toUTCString)

 *     toLocaleString

 *     toString

 *

 *

 * These Java methods are not supported

 *

 *     setDay

 *     before

 *     after

 *     equals

 *     hashCode

 */



/*

 * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language

 * definition and reduce dependence on NSPR.  NSPR is used to get the current

 * time in milliseconds, the time zone offset, and the daylight savings time

 * offset for a given time.  NSPR is also used for Date.toLocaleString(), for

 * locale-specific formatting, and to get a string representing the timezone.

 * (Which turns out to be platform-dependent.)

 *

 * To do:

 * (I did some performance tests by timing how long it took to run what

 *  I had of the js ECMA conformance tests.)

 *

 * - look at saving results across multiple calls to supporting

 * functions; the toString functions compute some of the same values

 * multiple times.  Although - I took a quick stab at this, and I lost

 * rather than gained.  (Fractionally.)  Hard to tell what compilers/processors

 * are doing these days.

 *

 * - look at tweaking function return types to return double instead

 * of int; this seems to make things run slightly faster sometimes.

 * (though it could be architecture-dependent.)  It'd be good to see

 * how this does on win32.  (Tried it on irix.)  Types could use a

 * general going-over.

 */



/*

 * Supporting functions - ECMA 15.9.1.*

 */



#define HalfTimeDomain  8.64e15

#define HoursPerDay     24.0

#define MinutesPerDay   (HoursPerDay * MinutesPerHour)

#define MinutesPerHour  60.0

#define SecondsPerDay   (MinutesPerDay * SecondsPerMinute)

#define SecondsPerHour  (MinutesPerHour * SecondsPerMinute)

#define SecondsPerMinute 60.0



#ifdef XP_PC

/* Work around msvc double optimization bug by making these runtime values; if

 * they're available at compile time, msvc optimizes division by them by

 * computing the reciprocal and multiplying instead of dividing - this loses

 * when the reciprocal isn't representable in a double.

 */

static jsdouble msPerSecond = 1000.0;

static jsdouble msPerDay = SecondsPerDay * 1000.0;

static jsdouble msPerHour = SecondsPerHour * 1000.0;

static jsdouble msPerMinute = SecondsPerMinute * 1000.0;

#else

#define msPerDay        (SecondsPerDay * msPerSecond)

#define msPerHour       (SecondsPerHour * msPerSecond)

#define msPerMinute     (SecondsPerMinute * msPerSecond)

#define msPerSecond     1000.0

#endif



#define Day(t)          floor((t) / msPerDay)



static jsdouble

TimeWithinDay(jsdouble t)

{

    jsdouble result;

    result = fmod(t, msPerDay);

    if (result < 0)

	result += msPerDay;

    return result;

}



#define DaysInYear(y)   ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0))  \

			 ? 366 : 365)



/* math here has to be f.p, because we need

 *  floor((1968 - 1969) / 4) == -1

 */

#define DayFromYear(y)  (365 * ((y)-1970) + floor(((y)-1969)/4.0)            \

			 - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0))

#define TimeFromYear(y) (DayFromYear(y) * msPerDay)



static jsint

YearFromTime(jsdouble t)

{

    jsint lo = (jsint) floor((t / msPerDay) / 366) + 1970;

    jsint hi = (jsint) floor((t / msPerDay) / 365) + 1970;

    jsint mid;



    /* above doesn't work for negative dates... */

    if (hi < lo) {

	jsint temp = lo;

	lo = hi;

	hi = temp;

    }



    /* Use a simple binary search algorithm to find the right

       year.  This seems like brute force... but the computation

       of hi and lo years above lands within one year of the

       correct answer for years within a thousand years of

       1970; the loop below only requires six iterations

       for year 270000. */

    while (hi > lo) {

	mid = (hi + lo) / 2;

	if (TimeFromYear(mid) > t) {

	    hi = mid - 1;

	} else {

	    if (TimeFromYear(mid) <= t) {

		jsint temp = mid + 1;

		if (TimeFromYear(temp) > t) {

		    return mid;

		}

		lo = mid + 1;

	    }

	}

    }

    return lo;

}



#define InLeapYear(t)   (JSBool) (DaysInYear(YearFromTime(t)) == 366)



#define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year)))



/*

 * The following array contains the day of year for the first day of

 * each month, where index 0 is January, and day 0 is January 1.

 */

static jsdouble firstDayOfMonth[2][12] = {

    {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0},

    {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0}

};



#define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m];



static intN

MonthFromTime(jsdouble t)

{

    intN d, step;

    jsint year = YearFromTime(t);

    d = DayWithinYear(t, year);



    if (d < (step = 31))

	return 0;

    step += (InLeapYear(t) ? 29 : 28);

    if (d < step)

	return 1;

    if (d < (step += 31))

	return 2;

    if (d < (step += 30))

	return 3;

    if (d < (step += 31))

	return 4;

    if (d < (step += 30))

	return 5;

    if (d < (step += 31))

	return 6;

    if (d < (step += 31))

	return 7;

    if (d < (step += 30))

	return 8;

    if (d < (step += 31))

	return 9;

    if (d < (step += 30))

	return 10;

    return 11;

}



static intN

DateFromTime(jsdouble t)

{

    intN d, step, next;

    jsint year = YearFromTime(t);

    d = DayWithinYear(t, year);



    if (d <= (next = 30))

	return d + 1;

    step = next;

    next += (InLeapYear(t) ? 29 : 28);

    if (d <= next)

	return d - step;

    step = next;

    if (d <= (next += 31))

	return d - step;

    step = next;

    if (d <= (next += 30))

	return d - step;

    step = next;

    if (d <= (next += 31))

	return d - step;

    step = next;

    if (d <= (next += 30))

	return d - step;

    step = next;

    if (d <= (next += 31))

	return d - step;

    step = next;

    if (d <= (next += 31))

	return d - step;

    step = next;

    if (d <= (next += 30))

	return d - step;

    step = next;

    if (d <= (next += 31))

	return d - step;

    step = next;

    if (d <= (next += 30))

	return d - step;

    step = next;

    return d - step;

}



static intN

WeekDay(jsdouble t)

{

    jsint result;

    result = (jsint) Day(t) + 4;

    result = result % 7;

    if (result < 0)

	result += 7;

    return (intN) result;

}



/* LocalTZA gets set by js_InitDateClass() */

static jsdouble LocalTZA;



static jsdouble

DaylightSavingTA(jsdouble t)

{

    volatile int64 PR_t;

    int64 ms2us;

    int64 offset;

    jsdouble result;



    /* abort if NaN */

    if (JSDOUBLE_IS_NaN(t))

	return t;



    /* put our t in an LL, and map it to usec for prtime */

    JSLL_D2L(PR_t, t);

    JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC);

    JSLL_MUL(PR_t, PR_t, ms2us);



    offset = PRMJ_DSTOffset(PR_t);



    JSLL_DIV(offset, offset, ms2us);

    JSLL_L2D(result, offset);

    return result;

}



#define LocalTime(t)    ((t) + LocalTZA + DaylightSavingTA(t))



static jsdouble

UTC(jsdouble t)

{

    return t - LocalTZA - DaylightSavingTA(t - LocalTZA);

}



static intN

HourFromTime(jsdouble t)

{

    intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay);

    if (result < 0)

	result += (intN)HoursPerDay;

    return result;

}



static intN

MinFromTime(jsdouble t)

{

    intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour);

    if (result < 0)

	result += (intN)MinutesPerHour;

    return result;

}



static intN

SecFromTime(jsdouble t)

{

    intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute);

    if (result < 0)

	result += (intN)SecondsPerMinute;

    return result;

}



static intN

msFromTime(jsdouble t)

{

    intN result = (intN) fmod(t, msPerSecond);

    if (result < 0)

	result += (intN)msPerSecond;

    return result;

}



#define MakeTime(hour, min, sec, ms) \

(((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms)



static jsdouble

MakeDay(jsdouble year, jsdouble month, jsdouble date)

{

    jsdouble result;

    JSBool leap;

    jsdouble yearday;

    jsdouble monthday;



    year += floor(month / 12);



    month = fmod(month, 12.0);

    if (month < 0)

	month += 12;



    leap = (DaysInYear((jsint) year) == 366);



    yearday = floor(TimeFromYear(year) / msPerDay);

    monthday = DayFromMonth(month, leap);



    result = yearday

	     + monthday

	     + date - 1;

    return result;

}



#define MakeDate(day, time) (day * msPerDay + time)



#define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \

		      && !((d < 0 ? -d : d) > HalfTimeDomain)) \

		     ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN)



/**

 * end of ECMA 'support' functions

 */



/*

 * Other Support routines and definitions

 */



static JSClass date_class = {

    js_Date_str,

    JSCLASS_HAS_PRIVATE,

    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,

    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,

    JSCLASS_NO_OPTIONAL_MEMBERS

};



/* for use by date_parse */



static char* wtb[] = {

    "am", "pm",

    "monday", "tuesday", "wednesday", "thursday", "friday",

    "saturday", "sunday",

    "january", "february", "march", "april", "may", "june",

    "july", "august", "september", "october", "november", "december",

    "gmt", "ut", "utc",

    "est", "edt",

    "cst", "cdt",

    "mst", "mdt",

    "pst", "pdt"

    /* time zone table needs to be expanded */

};



static int ttb[] = {

    -1, -2, 0, 0, 0, 0, 0, 0, 0,       /* AM/PM */

    2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,

    10000 + 0, 10000 + 0, 10000 + 0,   /* GMT/UT/UTC */

    10000 + 5 * 60, 10000 + 4 * 60,    /* EST/EDT */

    10000 + 6 * 60, 10000 + 5 * 60,    /* CST/CDT */

    10000 + 7 * 60, 10000 + 6 * 60,    /* MST/MDT */

    10000 + 8 * 60, 10000 + 7 * 60     /* PST/PDT */

};



/* helper for date_parse */

static JSBool

date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off,

		   int count, int ignoreCase)

{

    JSBool result = JS_FALSE;

    /* return true if matches, otherwise, false */



    while (count > 0 && s1[s1off] && s2[s2off]) {

	if (ignoreCase) {

	    if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) {

		break;

	    }

	} else {

	    if ((jschar)s1[s1off] != s2[s2off]) {

		break;

	    }

	}

	s1off++;

	s2off++;

	count--;

    }



    if (count == 0) {

	result = JS_TRUE;

    }



    return result;

}



/* find UTC time from given date... no 1900 correction! */

static jsdouble

date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour,

		  jsdouble min, jsdouble sec, jsdouble msec)

{

    jsdouble day;

    jsdouble msec_time;

    jsdouble result;



    day = MakeDay(year, mon, mday);

    msec_time = MakeTime(hour, min, sec, msec);

    result = MakeDate(day, msec_time);

    return result;

}



/*

 * See ECMA 15.9.4.[3-10];

 */

/* XXX this function must be above date_parseString to avoid a

   horrid bug in the Win16 1.52 compiler */

#define MAXARGS        7

static JSBool

date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble array[MAXARGS];

    uintN loop;

    jsdouble d;



    for (loop = 0; loop < MAXARGS; loop++) {

	if (loop < argc) {

	    if (!js_ValueToNumber(cx, argv[loop], &d))

		return JS_FALSE;

	    /* return NaN if any arg is NaN */

	    if (!JSDOUBLE_IS_FINITE(d)) {

		return js_NewNumberValue(cx, d, rval);

	    }

	    array[loop] = floor(d);

	} else {

	    array[loop] = 0;

	}

    }



    /* adjust 2-digit years into the 20th century */

    if (array[0] >= 0 && array[0] <= 99)

	array[0] += 1900;



    /* if we got a 0 for 'date' (which is out of range)

     * pretend it's a 1.  (So Date.UTC(1972, 5) works) */

    if (array[2] < 1)

	array[2] = 1;



    d = date_msecFromDate(array[0], array[1], array[2],

			      array[3], array[4], array[5], array[6]);

    d = TIMECLIP(d);



    return js_NewNumberValue(cx, d, rval);

}



static JSBool

date_parseString(const jschar *s, jsdouble *result)

{

    jsdouble msec;



    int year = -1;

    int mon = -1;

    int mday = -1;

    int hour = -1;

    int min = -1;

    int sec = -1;

    int c = -1;

    int i = 0;

    int n = -1;

    jsdouble tzoffset = -1;  /* was an int, overflowed on win16!!! */

    int prevc = 0;

    int limit = 0;

    JSBool seenplusminus = JS_FALSE;



    if (s == 0)

	goto syntax;

    limit = js_strlen(s);

    while (i < limit) {

	c = s[i];

	i++;

	if (c <= ' ' || c == ',' || c == '-') {

	    if (c == '-' && '0' <= s[i] && s[i] <= '9') {

	      prevc = c;

	    }

	    continue;

	}

	if (c == '(') { /* comments) */

	    int depth = 1;

	    while (i < limit) {

		c = s[i];

		i++;

		if (c == '(') depth++;

		else if (c == ')')

		    if (--depth <= 0)

			break;

	    }

	    continue;

	}

	if ('0' <= c && c <= '9') {

	    n = c - '0';

	    while (i < limit && '0' <= (c = s[i]) && c <= '9') {

		n = n * 10 + c - '0';

		i++;

	    }



	    /* allow TZA before the year, so

	     * 'Wed Nov 05 21:49:11 GMT-0800 1997'

	     * works */



	    /* uses of seenplusminus allow : in TZA, so Java

	     * no-timezone style of GMT+4:30 works

	     */



	    if ((prevc == '+' || prevc == '-')/*  && year>=0 */) {

		/* make ':' case below change tzoffset */

		seenplusminus = JS_TRUE;



		/* offset */

		if (n < 24)

		    n = n * 60; /* EG. "GMT-3" */

		else

		    n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */

		if (prevc == '+')       /* plus means east of GMT */

		    n = -n;

		if (tzoffset != 0 && tzoffset != -1)

		    goto syntax;

		tzoffset = n;

	    } else if (n >= 70  ||

		       (prevc == '/' && mon >= 0 && mday >= 0 && year < 0)) {

		if (year >= 0)

		    goto syntax;

		else if (c <= ' ' || c == ',' || c == '/' || i >= limit)

		    year = n < 100 ? n + 1900 : n;

		else

		    goto syntax;

	    } else if (c == ':') {

		if (hour < 0)

		    hour = /*byte*/ n;

		else if (min < 0)

		    min = /*byte*/ n;

		else

		    goto syntax;

	    } else if (c == '/') {

		if (mon < 0)

		    mon = /*byte*/ n-1;

		else if (mday < 0)

		    mday = /*byte*/ n;

		else

		    goto syntax;

	    } else if (i < limit && c != ',' && c > ' ' && c != '-') {

		goto syntax;

	    } else if (seenplusminus && n < 60) {  /* handle GMT-3:30 */

		if (tzoffset < 0)

		    tzoffset -= n;

		else

		    tzoffset += n;

	    } else if (hour >= 0 && min < 0) {

		min = /*byte*/ n;

	    } else if (min >= 0 && sec < 0) {

		sec = /*byte*/ n;

	    } else if (mday < 0) {

		mday = /*byte*/ n;

	    } else {

		goto syntax;

	    }

	    prevc = 0;

	} else if (c == '/' || c == ':' || c == '+' || c == '-') {

	    prevc = c;

	} else {

	    int st = i - 1;

	    int k;

	    while (i < limit) {

		c = s[i];

		if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))

		    break;

		i++;

	    }

	    if (i <= st + 1)

		goto syntax;

	    for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;)

		if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) {

		    int action = ttb[k];

		    if (action != 0) {

                        if (action < 0) {

                            /*

                             * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as

                             * 12:30, instead of blindly adding 12 if PM.

                             */

                            JS_ASSERT(action == -1 || action == -2);

                            if (hour > 12 || hour < 0) {

                                goto syntax;

                            } else {

                                if (action == -1 && hour == 12) { /* am */

                                    hour = 0;

                                } else if (action == -2 && hour != 12) { /* pm */

                                    hour += 12;

                                }

                            }

			} else if (action <= 13) { /* month! */

			    if (mon < 0) {

				mon = /*byte*/ (action - 2);

			    } else {

				goto syntax;

			    }

			} else {

			    tzoffset = action - 10000;

			}

		    }

		    break;

		}

	    if (k < 0)

		goto syntax;

	    prevc = 0;

	}

    }

    if (year < 0 || mon < 0 || mday < 0)

	goto syntax;

    if (sec < 0)

	sec = 0;

    if (min < 0)

	min = 0;

    if (hour < 0)

	hour = 0;

    if (tzoffset == -1) { /* no time zone specified, have to use local */

	jsdouble msec_time;

	msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);



	*result = UTC(msec_time);

	return JS_TRUE;

    }



    msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0);

    msec += tzoffset * msPerMinute;

    *result = msec;

    return JS_TRUE;



syntax:

    /* syntax error */

    *result = 0;

    return JS_FALSE;

}



static JSBool

date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str;

    jsdouble result;



    str = js_ValueToString(cx, argv[0]);

    if (!str)

	return JS_FALSE;

    if (!date_parseString(str->chars, &result)) {

	*rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);

	return JS_TRUE;

    }



    result = TIMECLIP(result);

    return js_NewNumberValue(cx, result, rval);

}



/*

 * Check that obj is an object of class Date, and get the date value.

 * Return NULL on failure.

 */

static jsdouble *

date_getProlog(JSContext *cx, JSObject *obj, jsval *argv)

{

    if (!JS_InstanceOf(cx, obj, &date_class, argv))

	return NULL;

    return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE));

}



/*

 * See ECMA 15.9.5.4 thru 15.9.5.23

 */

static JSBool

date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;



    return js_NewNumberValue(cx, *date, rval);

}



static JSBool

date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = YearFromTime(LocalTime(result));



    /*

     * During the great date rewrite of 1.3, we tried to track the evolving ECMA

     * standard, which then had a definition of getYear which always subtracted

     * 1900.  Which we implemented, not realizing that it was incompatible with

     * the old behavior...  now, rather than thrash the behavior yet again,

     * we've decided to leave it with the - 1900 behavior and point people to

     * the getFullYear method.  But we try to protect existing scripts that

     * have specified a version...

     */

    if (cx->version == JSVERSION_1_0 ||

        cx->version == JSVERSION_1_1 ||

        cx->version == JSVERSION_1_2)

    {

        if (result >= 1900 && result < 2000)

            result -= 1900;

    } else {

        result -= 1900;

    }

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		 jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = YearFromTime(LocalTime(result));

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		    jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = YearFromTime(result);

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	      jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = MonthFromTime(LocalTime(result));

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		 jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = MonthFromTime(result);

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = LocalTime(result);

    result = DateFromTime(result);

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = DateFromTime(result);

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = LocalTime(result);

    result = WeekDay(result);

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	       jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = WeekDay(result);

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	      jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = HourFromTime(LocalTime(result));

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		 jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = HourFromTime(result);

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = MinFromTime(LocalTime(result));

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		   jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = MinFromTime(result);

    return js_NewNumberValue(cx, result, rval);

}



/* Date.getSeconds is mapped to getUTCSeconds */



static JSBool

date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = SecFromTime(result);

    return js_NewNumberValue(cx, result, rval);

}



/* Date.getMilliseconds is mapped to getUTCMilliseconds */



static JSBool

date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		     jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    result = msFromTime(result);

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		       jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    result = *date;



    /*

     * Return the time zone offset in minutes for the current locale

     * that is appropriate for this time. This value would be a

     * constant except for daylight savings time.

     */

    result = (result - LocalTime(result)) / msPerMinute;

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble result;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;



    if (!js_ValueToNumber(cx, argv[0], &result))

	return JS_FALSE;



    result = TIMECLIP(result);



    *date = result;

    return js_NewNumberValue(cx, result, rval);

}



static JSBool

date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	      uintN maxargs, JSBool local, jsval *rval)

{

    uintN i;

    jsdouble args[4], *argp, *stop;

    jsdouble hour, min, sec, msec;

    jsdouble lorutime; /* Local or UTC version of *date */



    jsdouble msec_time;

    jsdouble result;



    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;



    result = *date;



    /* just return NaN if the date is already NaN */

    if (!JSDOUBLE_IS_FINITE(result))

	return js_NewNumberValue(cx, result, rval);



    /* Satisfy the ECMA rule that if a function is called with

     * fewer arguments than the specified formal arguments, the

     * remaining arguments are set to undefined.  Seems like all

     * the Date.setWhatever functions in ECMA are only varargs

     * beyond the first argument; this should be set to undefined

     * if it's not given.  This means that "d = new Date();

     * d.setMilliseconds()" returns NaN.  Blech.

     */

    if (argc == 0)

	argc = 1;   /* should be safe, because length of all settors is 1 */

    else if (argc > maxargs)

	argc = maxargs;  /* clamp argc */



    for (i = 0; i < argc; i++) {

	if (!js_ValueToNumber(cx, argv[i], &args[i]))

	    return JS_FALSE;

	if (!JSDOUBLE_IS_FINITE(args[i])) {

	    *date = *cx->runtime->jsNaN;

	    return js_NewNumberValue(cx, *date, rval);

	}

	args[i] = js_DoubleToInteger(args[i]);

    }



    if (local)

	lorutime = LocalTime(result);

    else

	lorutime = result;



    argp = args;

    stop = argp + argc;

    if (maxargs >= 4 && argp < stop)

	hour = *argp++;

    else

	hour = HourFromTime(lorutime);



    if (maxargs >= 3 && argp < stop)

	min = *argp++;

    else

	min = MinFromTime(lorutime);



    if (maxargs >= 2 && argp < stop)

	sec = *argp++;

    else

	sec = SecFromTime(lorutime);



    if (maxargs >= 1 && argp < stop)

	msec = *argp;

    else

	msec = msFromTime(lorutime);



    msec_time = MakeTime(hour, min, sec, msec);

    result = MakeDate(Day(lorutime), msec_time);



/*     fprintf(stderr, "%f\n", result); */



    if (local)

	result = UTC(result);



/*     fprintf(stderr, "%f\n", result); */



    *date = TIMECLIP(result);

    return js_NewNumberValue(cx, *date, rval);

}



static JSBool

date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc,

		     jsval *argv, jsval *rval)

{

    return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval);

}



static JSBool

date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc,

			jsval *argv, jsval *rval)

{

    return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval);

}



static JSBool

date_setSeconds(JSContext *cx, JSObject *obj, uintN argc,

		jsval *argv, jsval *rval)

{

    return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval);

}



static JSBool

date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc,

		   jsval *argv, jsval *rval)

{

    return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval);

}



static JSBool

date_setMinutes(JSContext *cx, JSObject *obj, uintN argc,

		jsval *argv, jsval *rval)

{

    return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval);

}



static JSBool

date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc,

		   jsval *argv, jsval *rval)

{

    return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval);

}



static JSBool

date_setHours(JSContext *cx, JSObject *obj, uintN argc,

	      jsval *argv, jsval *rval)

{

    return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval);

}



static JSBool

date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc,

		 jsval *argv, jsval *rval)

{

    return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval);

}



static JSBool

date_makeDate(JSContext *cx, JSObject *obj, uintN argc,

	      jsval *argv, uintN maxargs, JSBool local, jsval *rval)

{

    uintN i;

    jsdouble lorutime; /* local or UTC version of *date */

    jsdouble args[3], *argp, *stop;

    jsdouble year, month, day;

    jsdouble result;



    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;



    result = *date;



    /* see complaint about ECMA in date_MakeTime */

    if (argc == 0)

	argc = 1;   /* should be safe, because length of all settors is 1 */

    else if (argc > maxargs)

	argc = maxargs;   /* clamp argc */



    for (i = 0; i < argc; i++) {

	if (!js_ValueToNumber(cx, argv[i], &args[i]))

	    return JS_FALSE;

	if (!JSDOUBLE_IS_FINITE(args[i])) {

	    *date = *cx->runtime->jsNaN;

	    return js_NewNumberValue(cx, *date, rval);

	}

	args[i] = js_DoubleToInteger(args[i]);

    }



    /* return NaN if date is NaN and we're not setting the year,

     * If we are, use 0 as the time. */

    if (!(JSDOUBLE_IS_FINITE(result))) {

	if (argc < 3)

	    return js_NewNumberValue(cx, result, rval);

	else

	    lorutime = +0.;

    } else {

	if (local)

	    lorutime = LocalTime(result);

	else

	    lorutime = result;

    }



    argp = args;

    stop = argp + argc;

    if (maxargs >= 3 && argp < stop)

	year = *argp++;

    else

	year = YearFromTime(lorutime);



    if (maxargs >= 2 && argp < stop)

	month = *argp++;

    else

	month = MonthFromTime(lorutime);



    if (maxargs >= 1 && argp < stop)

	day = *argp++;

    else

	day = DateFromTime(lorutime);



    day = MakeDay(year, month, day); /* day within year */

    result = MakeDate(day, TimeWithinDay(lorutime));



    if (local)

	result = UTC(result);



    *date = TIMECLIP(result);

    return js_NewNumberValue(cx, *date, rval);

}



static JSBool

date_setDate(JSContext *cx, JSObject *obj, uintN argc,

	     jsval *argv, jsval *rval)

{

    return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval);

}



static JSBool

date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc,

		jsval *argv, jsval *rval)

{

    return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval);

}



static JSBool

date_setMonth(JSContext *cx, JSObject *obj, uintN argc,

	      jsval *argv, jsval *rval)

{

    return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval);

}



static JSBool

date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc,

		 jsval *argv, jsval *rval)

{

    return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval);

}



static JSBool

date_setFullYear(JSContext *cx, JSObject *obj, uintN argc,

		 jsval *argv, jsval *rval)

{

    return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval);

}



static JSBool

date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc,

		    jsval *argv, jsval *rval)

{

    return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval);

}



static JSBool

date_setYear(JSContext *cx, JSObject *obj, uintN argc,

	     jsval *argv, jsval *rval)

{

    jsdouble t;

    jsdouble year;

    jsdouble day;

    jsdouble result;



    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;



    result = *date;



    if (!js_ValueToNumber(cx, argv[0], &year))

	return JS_FALSE;

    if (!JSDOUBLE_IS_FINITE(year)) {

	*date = *cx->runtime->jsNaN;

	return js_NewNumberValue(cx, *date, rval);

    }



    year = js_DoubleToInteger(year);



    if (!JSDOUBLE_IS_FINITE(result)) {

	t = +0.0;

    } else {

	t = LocalTime(result);

    }



    if (year >= 0 && year <= 99)

	year += 1900;



    day = MakeDay(year, MonthFromTime(t), DateFromTime(t));

    result = MakeDate(day, TimeWithinDay(t));

    result = UTC(result);



    *date = TIMECLIP(result);

    return js_NewNumberValue(cx, *date, rval);

}



/* constants for toString, toUTCString */

static char js_NaN_date_str[] = "Invalid Date";

static const char* days[] =

{

   "Sun","Mon","Tue","Wed","Thu","Fri","Sat"

};

static const char* months[] =

{

   "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"

};



static JSBool

date_toGMTString(JSContext *cx, JSObject *obj, uintN argc,

		 jsval *argv, jsval *rval)

{

    char buf[100];

    JSString *str;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;



    if (!JSDOUBLE_IS_FINITE(*date)) {

	JS_snprintf(buf, sizeof buf, js_NaN_date_str);

    } else {

	jsdouble temp = *date;



	/* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it

	 * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.

	 */

	JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",

		    days[WeekDay(temp)],

		    DateFromTime(temp),

		    months[MonthFromTime(temp)],

		    YearFromTime(temp),

		    HourFromTime(temp),

		    MinFromTime(temp),

		    SecFromTime(temp));

    }

    str = JS_NewStringCopyZ(cx, buf);

    if (!str)

	return JS_FALSE;

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



/* for Date.toLocaleString; interface to PRMJTime date struct.

 * If findEquivalent is true, then try to map the year to an equivalent year

 * that's in range.

 */

static void

new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent)

{

    jsint year = YearFromTime(timeval);

    int16 adjustedYear;



    /* If the year doesn't fit in a PRMJTime, find something to do about it. */

    if (year > 32767 || year < -32768) {

	if (findEquivalent) {

	    /* We're really just trying to get a timezone string; map the year

	     * to some equivalent year in the range 0 to 2800.  Borrowed from

	     * A. D. Olsen.

	     */

	    jsint cycles;

#define CYCLE_YEARS 2800L

	    cycles = (year >= 0) ? year / CYCLE_YEARS

				 : -1 - (-1 - year) / CYCLE_YEARS;

	    adjustedYear = (int16)(year - cycles * CYCLE_YEARS);

	} else {

	    /* Clamp it to the nearest representable year. */

	    adjustedYear = (int16)((year > 0) ? 32767 : - 32768);

	}

    } else {

	adjustedYear = (int16)year;

    }



    split->tm_usec = (int32) msFromTime(timeval) * 1000;

    split->tm_sec = (int8) SecFromTime(timeval);

    split->tm_min = (int8) MinFromTime(timeval);

    split->tm_hour = (int8) HourFromTime(timeval);

    split->tm_mday = (int8) DateFromTime(timeval);

    split->tm_mon = (int8) MonthFromTime(timeval);

    split->tm_wday = (int8) WeekDay(timeval);

    split->tm_year = (int16) adjustedYear;

    split->tm_yday = (int16) DayWithinYear(timeval, year);



    /* not sure how this affects things, but it doesn't seem

       to matter. */

    split->tm_isdst = (DaylightSavingTA(timeval) != 0);

}



typedef enum formatspec {

    FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME

} formatspec;



/* helper function */

static JSBool

date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval)

{

    char buf[100];

    JSString *str;

    char tzbuf[100];

    JSBool usetz;

    size_t i, tzlen;

    PRMJTime split;



    if (!JSDOUBLE_IS_FINITE(date)) {

	JS_snprintf(buf, sizeof buf, js_NaN_date_str);

    } else {

	jsdouble local = LocalTime(date);



	/* offset from GMT in minutes.  The offset includes daylight savings,

	   if it applies. */

	jsint minutes = (jsint) floor((LocalTZA + DaylightSavingTA(date))

				      / msPerMinute);



	/* map 510 minutes to 0830 hours */

	intN offset = (minutes / 60) * 100 + minutes % 60;



	/* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is

	 * printed as 'GMT-0800' rather than as 'PST' to avoid

	 * operating-system dependence on strftime (which

	 * PRMJ_FormatTimeUSEnglish calls, for %Z only.)  win32 prints

	 * PST as 'Pacific Standard Time.'  This way we always know

	 * what we're getting, and can parse it if we produce it.

	 * The OS TZA string is included as a comment.

	 */



	/* get a timezone string from the OS to include as a

	   comment. */

	new_explode(date, &split, JS_TRUE);

	PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split);



        /* Decide whether to use the resulting timezone string.

         *

         * Reject it if it contains any non-ASCII, non-alphanumeric characters.

         * It's then likely in some other character encoding, and we probably

         * won't display it correctly.

         */

        usetz = JS_TRUE;

        tzlen = strlen(tzbuf);

        if (tzlen > 100) {

            usetz = JS_FALSE;

        } else {

            for (i = 0; i < tzlen; i++) {

                jschar c = tzbuf[i];

                if (c > 127 ||

                    !(isalpha(c) || isdigit(c) ||

                      c == ' ' || c == '(' || c == ')')) {

                    usetz = JS_FALSE;

                }

            }

        }



        /* Also reject it if it's not parenthesized or if it's '()'. */

        if (tzbuf[0] != '(' || tzbuf[1] == ')')

            usetz = JS_FALSE;



        switch (format) {

          case FORMATSPEC_FULL:

            /*

             * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it

             * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.

             */

            /* Tue Oct 31 09:41:40 GMT-0800 (PST) 2000 */

            JS_snprintf(buf, sizeof buf,

                        "%s %s %.2d %.2d:%.2d:%.2d GMT%+.4d %s%s%.4d",

                        days[WeekDay(local)],

                        months[MonthFromTime(local)],

                        DateFromTime(local),

                        HourFromTime(local),

                        MinFromTime(local),

                        SecFromTime(local),

                        offset,

                        usetz ? tzbuf : "",

                        usetz ? " " : "",

                        YearFromTime(local));

            break;

          case FORMATSPEC_DATE:

            /* Tue Oct 31 2000 */

            JS_snprintf(buf, sizeof buf,

                        "%s %s %.2d %.4d",

                        days[WeekDay(local)],

                        months[MonthFromTime(local)],

                        DateFromTime(local),

                        YearFromTime(local));

            break;

          case FORMATSPEC_TIME:

            /* 09:41:40 GMT-0800 (PST) */

            JS_snprintf(buf, sizeof buf,

                        "%.2d:%.2d:%.2d GMT%+.4d%s%s",

                        HourFromTime(local),

                        MinFromTime(local),

                        SecFromTime(local),

                        offset,

                        usetz ? " " : "",

                        usetz ? tzbuf : "");

            break;

        }

    }



    str = JS_NewStringCopyZ(cx, buf);

    if (!str)

	return JS_FALSE;

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc,

		    jsval *argv, jsval *rval, char *format)

{

    char buf[100];

    JSString *str;

    PRMJTime split;

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;



    if (!JSDOUBLE_IS_FINITE(*date)) {

	JS_snprintf(buf, sizeof buf, js_NaN_date_str);

    } else {

	intN result_len;

	jsdouble local = LocalTime(*date);

	new_explode(local, &split, JS_FALSE);



	/* let PRMJTime format it.	 */

	result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);



	/* If it failed, default to toString. */

	if (result_len == 0)

	    return date_format(cx, *date, FORMATSPEC_FULL, rval);



        /* Hacked check against undesired 2-digit year 00/00/00 form. */

        if (buf[result_len - 3] == '/' &&

            isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1])) {

            JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),

                        "%d", js_DateGetYear(cx, obj));

        }



    }



    str = JS_NewStringCopyZ(cx, buf);

    if (!str)

	return JS_FALSE;

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,

		    jsval *argv, jsval *rval)

{

    /* Use '%#c' for windows, because '%c' is

     * backward-compatible and non-y2k with msvc; '%#c' requests that a

     * full year be used in the result string.

     */

    return date_toLocaleHelper(cx, obj, argc, argv, rval,

#if defined(_WIN32) && !defined(__MWERKS__)

				   "%#c"

#else

				   "%c"

#endif

				   );

}



static JSBool

date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc,

		    jsval *argv, jsval *rval)

{

    /* Use '%#x' for windows, because '%x' is

     * backward-compatible and non-y2k with msvc; '%#x' requests that a

     * full year be used in the result string.

     */

    return date_toLocaleHelper(cx, obj, argc, argv, rval,

#if defined(_WIN32) && !defined(__MWERKS__)

				   "%#x"

#else

				   "%x"

#endif

				   );

}



static JSBool

date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc,

		    jsval *argv, jsval *rval)

{

    return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X");

}



static JSBool

date_toTimeString(JSContext *cx, JSObject *obj, uintN argc,

		    jsval *argv, jsval *rval)

{

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    return date_format(cx, *date, FORMATSPEC_TIME, rval);

}



static JSBool

date_toDateString(JSContext *cx, JSObject *obj, uintN argc,

		    jsval *argv, jsval *rval)

{

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    return date_format(cx, *date, FORMATSPEC_DATE, rval);

}



#if JS_HAS_TOSOURCE

#include <string.h>

#include "jsdtoa.h"



static JSBool

date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	      jsval *rval)

{

    jsdouble *date;

    char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes;

    JSString *str;



    date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;



    numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date);

    if (!numStr) {

	JS_ReportOutOfMemory(cx);

	return JS_FALSE;

    }



    bytes = JS_smprintf("(new %s(%s))", date_class.name, numStr);

    if (!bytes) {

	JS_ReportOutOfMemory(cx);

	return JS_FALSE;

    }



    str = JS_NewString(cx, bytes, strlen(bytes));

    if (!str) {

	free(bytes);

	return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}

#endif



static JSBool

date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	      jsval *rval)

{

    jsdouble *date = date_getProlog(cx, obj, argv);

    if (!date)

	return JS_FALSE;

    return date_format(cx, *date, FORMATSPEC_FULL, rval);

}



#if JS_HAS_VALUEOF_HINT

static JSBool

date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	     jsval *rval)

{

    /* It is an error to call date_valueOf on a non-date object, but we don't

     * need to check for that explicitly here because every path calls

     * date_getProlog, which does the check.

     */



    /* If called directly with no arguments, convert to a time number. */

    if (argc == 0)

	return date_getTime(cx, obj, argc, argv, rval);



    /* Convert to number only if the hint was given, otherwise favor string. */

    if (argc == 1) {

	JSString *str, *str2;



	str = js_ValueToString(cx, argv[0]);

	if (!str)

	    return JS_FALSE;

	str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);

	if (!js_CompareStrings(str, str2))

	    return date_getTime(cx, obj, argc, argv, rval);

    }

    return date_toString(cx, obj, argc, argv, rval);

}

#else

#define date_valueOf date_getTime

#endif





/*

 * creation and destruction

 */



static JSFunctionSpec date_static_methods[] = {

    {"UTC",               date_UTC,               MAXARGS,0,0 },

    {"parse",             date_parse,             1,0,0 },

    {0,0,0,0,0}

};



static JSFunctionSpec date_methods[] = {

    {"getTime",             date_getTime,           0,0,0 },

    {"getTimezoneOffset",   date_getTimezoneOffset, 0,0,0 },

    {"getYear",             date_getYear,           0,0,0 },

    {"getFullYear",         date_getFullYear,       0,0,0 },

    {"getUTCFullYear",      date_getUTCFullYear,    0,0,0 },

    {"getMonth",            date_getMonth,          0,0,0 },

    {"getUTCMonth",         date_getUTCMonth,       0,0,0 },

    {"getDate",             date_getDate,           0,0,0 },

    {"getUTCDate",          date_getUTCDate,        0,0,0 },

    {"getDay",              date_getDay,            0,0,0 },

    {"getUTCDay",           date_getUTCDay,         0,0,0 },

    {"getHours",            date_getHours,          0,0,0 },

    {"getUTCHours",         date_getUTCHours,       0,0,0 },

    {"getMinutes",          date_getMinutes,        0,0,0 },

    {"getUTCMinutes",       date_getUTCMinutes,     0,0,0 },

    {"getSeconds",          date_getUTCSeconds,     0,0,0 },

    {"getUTCSeconds",       date_getUTCSeconds,     0,0,0 },

    {"getMilliseconds",     date_getUTCMilliseconds,0,0,0 },

    {"getUTCMilliseconds",  date_getUTCMilliseconds,0,0,0 },

    {"setTime",             date_setTime,           1,0,0 },

    {"setYear",             date_setYear,           1,0,0 },

    {"setFullYear",         date_setFullYear,       3,0,0 },

    {"setUTCFullYear",      date_setUTCFullYear,    3,0,0 },

    {"setMonth",            date_setMonth,          2,0,0 },

    {"setUTCMonth",         date_setUTCMonth,       2,0,0 },

    {"setDate",             date_setDate,           1,0,0 },

    {"setUTCDate",          date_setUTCDate,        1,0,0 },

    {"setHours",            date_setHours,          4,0,0 },

    {"setUTCHours",         date_setUTCHours,       4,0,0 },

    {"setMinutes",          date_setMinutes,        3,0,0 },

    {"setUTCMinutes",       date_setUTCMinutes,     3,0,0 },

    {"setSeconds",          date_setSeconds,        2,0,0 },

    {"setUTCSeconds",       date_setUTCSeconds,     2,0,0 },

    {"setMilliseconds",     date_setMilliseconds,   1,0,0 },

    {"setUTCMilliseconds",  date_setUTCMilliseconds,1,0,0 },

    {"toUTCString",         date_toGMTString,       0,0,0 },

    {js_toLocaleString_str, date_toLocaleString,    0,0,0 },

    {"toLocaleDateString",  date_toLocaleDateString,0,0,0 },

    {"toLocaleTimeString",  date_toLocaleTimeString,0,0,0 },

    {"toDateString",        date_toDateString,      0,0,0 },

    {"toTimeString",        date_toTimeString,      0,0,0 },

#if JS_HAS_TOSOURCE

    {js_toSource_str,       date_toSource,          0,0,0 },

#endif

    {js_toString_str,       date_toString,          0,0,0 },

    {js_valueOf_str,        date_valueOf,           0,0,0 },

    {0,0,0,0,0}

};



static jsdouble *

date_constructor(JSContext *cx, JSObject* obj)

{

    jsdouble *date;



    date = js_NewDouble(cx, 0.0);

    if (!date)

	return NULL;

    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date));

    return date;

}



static JSBool

Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble *date;

    JSString *str;

    jsdouble d;



    /* Date called as function */

    if (!cx->fp->constructing) {

	int64 us, ms, us2ms;

	jsdouble msec_time;



	/* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS',

	 * so compute ms from PRMJ_Now.

	 */

	us = PRMJ_Now();

	JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);

	JSLL_DIV(ms, us, us2ms);

	JSLL_L2D(msec_time, ms);



	return date_format(cx, msec_time, FORMATSPEC_FULL, rval);

    }



    /* Date called as constructor */

    if (argc == 0) {

	int64 us, ms, us2ms;

	jsdouble msec_time;



	date = date_constructor(cx, obj);

	if (!date)

	    return JS_FALSE;



	us = PRMJ_Now();

	JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC);

	JSLL_DIV(ms, us, us2ms);

	JSLL_L2D(msec_time, ms);



	*date = msec_time;

    } else if (argc == 1) {

	if (!JSVAL_IS_STRING(argv[0])) {

	    /* the argument is a millisecond number */

	    if (!js_ValueToNumber(cx, argv[0], &d))

		    return JS_FALSE;

	    date = date_constructor(cx, obj);

	    if (!date)

		return JS_FALSE;

	    *date = TIMECLIP(d);

	} else {

	    /* the argument is a string; parse it. */

	    date = date_constructor(cx, obj);

	    if (!date)

		return JS_FALSE;



	    str = js_ValueToString(cx, argv[0]);

	    if (!str)

		return JS_FALSE;



	    if (!date_parseString(str->chars, date))

		*date = *cx->runtime->jsNaN;

	    *date = TIMECLIP(*date);

	}

    } else {

	jsdouble array[MAXARGS];

	uintN loop;

	jsdouble double_arg;

	jsdouble day;

	jsdouble msec_time;



	for (loop = 0; loop < MAXARGS; loop++) {

	    if (loop < argc) {

		if (!js_ValueToNumber(cx, argv[loop], &double_arg))

		    return JS_FALSE;

		/* if any arg is NaN, make a NaN date object

		   and return */

		if (!JSDOUBLE_IS_FINITE(double_arg)) {

		    date = date_constructor(cx, obj);

		    if (!date)

			return JS_FALSE;

		    *date = *cx->runtime->jsNaN;

		    return JS_TRUE;

		}

		array[loop] = js_DoubleToInteger(double_arg);

	    } else {

                if (loop == 2) {

                    array[loop] = 1; /* Default the date argument to 1. */

                } else {

                    array[loop] = 0;

                }

	    }

	}



	date = date_constructor(cx, obj);

	if (!date)

	    return JS_FALSE;



	/* adjust 2-digit years into the 20th century */

	if (array[0] >= 0 && array[0] <= 99)

	    array[0] += 1900;



	day = MakeDay(array[0], array[1], array[2]);

	msec_time = MakeTime(array[3], array[4], array[5], array[6]);

	msec_time = MakeDate(day, msec_time);

	msec_time = UTC(msec_time);

	*date = TIMECLIP(msec_time);

    }

    return JS_TRUE;

}



JSObject *

js_InitDateClass(JSContext *cx, JSObject *obj)

{

    JSObject *proto;

    jsdouble *proto_date;



    /* set static LocalTZA */

    LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond);

    proto = JS_InitClass(cx, obj, NULL, &date_class, Date, MAXARGS,

			 NULL, date_methods, NULL, date_static_methods);

    if (!proto)

	return NULL;



    /* Alias toUTCString with toGMTString.  (ECMA B.2.6) */

    if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString"))

        return NULL;



    /* Set the value of the Date.prototype date to NaN */

    proto_date = date_constructor(cx, proto);

    if (!proto_date)

	return NULL;

    *proto_date = *cx->runtime->jsNaN;



    return proto;

}



JS_FRIEND_API(JSObject *)

js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)

{

    JSObject *obj;

    jsdouble *date;



    obj = js_NewObject(cx, &date_class, NULL, NULL);

    if (!obj)

	return NULL;



    JS_DefineFunctions(cx, obj, date_methods);



    date = date_constructor(cx, obj);

    if (!date)

	return NULL;



    *date = msec_time;

    return obj;

}



JS_FRIEND_API(JSObject *)

js_NewDateObject(JSContext* cx, int year, int mon, int mday,

                 int hour, int min, int sec)

{

    JSObject *obj;

    jsdouble msec_time;



    msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0);

    obj = js_NewDateObjectMsec(cx, UTC(msec_time));

    return obj;

}



JS_FRIEND_API(JSBool)

js_DateIsValid(JSContext *cx, JSObject* obj)

{

    jsdouble *date = date_getProlog(cx, obj, NULL);



    if (!date || JSDOUBLE_IS_NaN(*date))

        return JS_FALSE;

    else

        return JS_TRUE;

}



JS_FRIEND_API(int)

js_DateGetYear(JSContext *cx, JSObject* obj)

{

    jsdouble *date = date_getProlog(cx, obj, NULL);



    /* Preserve legacy API behavior of returning 0 for invalid dates. */

    if (!date || JSDOUBLE_IS_NaN(*date))

	return 0;

    return (int) YearFromTime(LocalTime(*date));

}



JS_FRIEND_API(int)

js_DateGetMonth(JSContext *cx, JSObject* obj)

{

    jsdouble *date = date_getProlog(cx, obj, NULL);



    if (!date || JSDOUBLE_IS_NaN(*date))

	return 0;

    return (int) MonthFromTime(LocalTime(*date));

}



JS_FRIEND_API(int)

js_DateGetDate(JSContext *cx, JSObject* obj)

{

    jsdouble *date = date_getProlog(cx, obj, NULL);



    if (!date || JSDOUBLE_IS_NaN(*date))

	return 0;

    return (int) DateFromTime(LocalTime(*date));

}



JS_FRIEND_API(int)

js_DateGetHours(JSContext *cx, JSObject* obj)

{

    jsdouble *date = date_getProlog(cx, obj, NULL);



    if (!date || JSDOUBLE_IS_NaN(*date))

	return 0;

    return (int) HourFromTime(LocalTime(*date));

}



JS_FRIEND_API(int)

js_DateGetMinutes(JSContext *cx, JSObject* obj)

{

    jsdouble *date = date_getProlog(cx, obj, NULL);



    if (!date || JSDOUBLE_IS_NaN(*date))

	return 0;

    return (int) MinFromTime(LocalTime(*date));

}



JS_FRIEND_API(int)

js_DateGetSeconds(JSContext *cx, JSObject* obj)

{

    jsdouble *date = date_getProlog(cx, obj, NULL);



    if (!date || JSDOUBLE_IS_NaN(*date))

	return 0;

    return (int) SecFromTime(*date);

}



extern JS_FRIEND_API(void)

js_DateSetYear(JSContext *cx, JSObject *obj, int year)

{

    jsdouble local;

    jsdouble *date = date_getProlog(cx, obj, NULL);

    if (!date)

	return;

    local = LocalTime(*date);

    /* reset date if it was NaN */

    if (JSDOUBLE_IS_NaN(local))

	local = 0;

    local = date_msecFromDate(year,

			      MonthFromTime(local),

			      DateFromTime(local),

			      HourFromTime(local),

			      MinFromTime(local),

			      SecFromTime(local),

			      msFromTime(local));

    *date = UTC(local);

}



extern JS_FRIEND_API(void)

js_DateSetMonth(JSContext *cx, JSObject *obj, int month)

{

    jsdouble local;

    jsdouble *date = date_getProlog(cx, obj, NULL);

    if (!date)

	return;

    local = LocalTime(*date);

    /* bail if date was NaN */

    if (JSDOUBLE_IS_NaN(local))

	return;

    local = date_msecFromDate(YearFromTime(local),

			      month,

			      DateFromTime(local),

			      HourFromTime(local),

			      MinFromTime(local),

			      SecFromTime(local),

			      msFromTime(local));

    *date = UTC(local);

}



extern JS_FRIEND_API(void)

js_DateSetDate(JSContext *cx, JSObject *obj, int date)

{

    jsdouble local;

    jsdouble *datep = date_getProlog(cx, obj, NULL);

    if (!datep)

	return;

    local = LocalTime(*datep);

    if (JSDOUBLE_IS_NaN(local))

	return;

    local = date_msecFromDate(YearFromTime(local),

			      MonthFromTime(local),

			      date,

			      HourFromTime(local),

			      MinFromTime(local),

			      SecFromTime(local),

			      msFromTime(local));

    *datep = UTC(local);

}



extern JS_FRIEND_API(void)

js_DateSetHours(JSContext *cx, JSObject *obj, int hours)

{

    jsdouble local;

    jsdouble *date = date_getProlog(cx, obj, NULL);

    if (!date)

	return;

    local = LocalTime(*date);

    if (JSDOUBLE_IS_NaN(local))

	return;

    local = date_msecFromDate(YearFromTime(local),

			      MonthFromTime(local),

			      DateFromTime(local),

			      hours,

			      MinFromTime(local),

			      SecFromTime(local),

			      msFromTime(local));

    *date = UTC(local);

}



extern JS_FRIEND_API(void)

js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes)

{

    jsdouble local;

    jsdouble *date = date_getProlog(cx, obj, NULL);

    if (!date)

	return;

    local = LocalTime(*date);

    if (JSDOUBLE_IS_NaN(local))

	return;

    local = date_msecFromDate(YearFromTime(local),

			      MonthFromTime(local),

			      DateFromTime(local),

			      HourFromTime(local),

			      minutes,

			      SecFromTime(local),

			      msFromTime(local));

    *date = UTC(local);

}



extern JS_FRIEND_API(void)

js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds)

{

    jsdouble local;

    jsdouble *date = date_getProlog(cx, obj, NULL);

    if (!date)

	return;

    local = LocalTime(*date);

    if (JSDOUBLE_IS_NaN(local))

	return;

    local = date_msecFromDate(YearFromTime(local),

			      MonthFromTime(local),

			      DateFromTime(local),

			      HourFromTime(local),

			      MinFromTime(local),

			      seconds,

			      msFromTime(local));

    *date = UTC(local);

}



extern JS_FRIEND_API(jsdouble)

js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj)

{

    jsdouble *date = date_getProlog(cx, obj, NULL);

    if (!date || JSDOUBLE_IS_NaN(*date))

        return 0;

    return (*date);

}

 

**** End of jsdate.c ****

 

**** Start of jsdate.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS Date class interface.

 */



#ifndef jsdate_h___

#define jsdate_h___



JS_BEGIN_EXTERN_C



extern JSObject *

js_InitDateClass(JSContext *cx, JSObject *obj);



/*

 * These functions provide a C interface to the date/time object

 */



/*

 * Construct a new Date Object from a time value given in milliseconds UTC

 * since the epoch.

 */

extern JS_FRIEND_API(JSObject*)

js_NewDateObjectMsec(JSContext* cx, jsdouble msec_time);



/*

 * Construct a new Date Object from an exploded local time value.

 */

extern JS_FRIEND_API(JSObject*)

js_NewDateObject(JSContext* cx, int year, int mon, int mday,

				int hour, int min, int sec);



/*

 * Detect whether the internal date value is NaN.  (Because failure is

 * out-of-band for js_DateGet*)

 */

extern JS_FRIEND_API(JSBool)

js_DateIsValid(JSContext *cx, JSObject* obj);



extern JS_FRIEND_API(int)

js_DateGetYear(JSContext *cx, JSObject* obj);



extern JS_FRIEND_API(int)

js_DateGetMonth(JSContext *cx, JSObject* obj);



extern JS_FRIEND_API(int)

js_DateGetDate(JSContext *cx, JSObject* obj);



extern JS_FRIEND_API(int)

js_DateGetHours(JSContext *cx, JSObject* obj);



extern JS_FRIEND_API(int)

js_DateGetMinutes(JSContext *cx, JSObject* obj);



extern JS_FRIEND_API(int)

js_DateGetSeconds(JSContext *cx, JSObject* obj);



extern JS_FRIEND_API(void)

js_DateSetYear(JSContext *cx, JSObject *obj, int year);



extern JS_FRIEND_API(void)

js_DateSetMonth(JSContext *cx, JSObject *obj, int year);



extern JS_FRIEND_API(void)

js_DateSetDate(JSContext *cx, JSObject *obj, int date);



extern JS_FRIEND_API(void)

js_DateSetHours(JSContext *cx, JSObject *obj, int hours);



extern JS_FRIEND_API(void)

js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes);



extern JS_FRIEND_API(void)

js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds);



extern JS_FRIEND_API(jsdouble)

js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj);



JS_END_EXTERN_C



#endif /* jsdate_h___ */

 

**** End of jsdate.h ****

 

**** Start of jsdbgapi.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS debugging API.

 */

#include "jsstddef.h"

#include <string.h>

#include "jstypes.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jsclist.h"

#include "jsapi.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsdbgapi.h"

#include "jsfun.h"

#include "jsgc.h"

#include "jsinterp.h"

#include "jslock.h"

#include "jsobj.h"

#include "jsopcode.h"

#include "jsscope.h"

#include "jsscript.h"

#include "jsstr.h"



typedef struct JSTrap {

    JSCList         links;

    JSScript        *script;

    jsbytecode      *pc;

    JSOp            op;

    JSTrapHandler   handler;

    void            *closure;

} JSTrap;



static JSTrap *

FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc)

{

    JSTrap *trap;



    for (trap = (JSTrap *)rt->trapList.next;

	 trap != (JSTrap *)&rt->trapList;

	 trap = (JSTrap *)trap->links.next) {

	if (trap->script == script && trap->pc == pc)

	    return trap;

    }

    return NULL;

}



void

js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op)

{

    JSTrap *trap;



    trap = FindTrap(cx->runtime, script, pc);

    if (trap)

	trap->op = op;

    else

	*pc = (jsbytecode)op;

}



JS_PUBLIC_API(JSBool)

JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,

	   JSTrapHandler handler, void *closure)

{

    JSRuntime *rt;

    JSTrap *trap;



    rt = cx->runtime;

    trap = FindTrap(rt, script, pc);

    if (trap) {

	/* Restore opcode at pc so it can be saved again. */

	*pc = (jsbytecode)trap->op;

    } else {

	trap = (JSTrap *) JS_malloc(cx, sizeof *trap);

	if (!trap || !js_AddRoot(cx, &trap->closure, "trap->closure")) {

	    if (trap)

		JS_free(cx, trap);

	    return JS_FALSE;

	}

    }

    JS_APPEND_LINK(&trap->links, &rt->trapList);

    trap->script = script;

    trap->pc = pc;

    trap->op = (JSOp)*pc;

    trap->handler = handler;

    trap->closure = closure;

    *pc = JSOP_TRAP;

    return JS_TRUE;

}



JS_PUBLIC_API(JSOp)

JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)

{

    JSTrap *trap;



    trap = FindTrap(cx->runtime, script, pc);

    if (!trap) {

	JS_ASSERT(0);	/* XXX can't happen */

	return JSOP_LIMIT;

    }

    return trap->op;

}



static void

DestroyTrap(JSContext *cx, JSTrap *trap)

{

    JS_REMOVE_LINK(&trap->links);

    *trap->pc = (jsbytecode)trap->op;

    js_RemoveRoot(cx->runtime, &trap->closure);

    JS_free(cx, trap);

}



JS_PUBLIC_API(void)

JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,

	     JSTrapHandler *handlerp, void **closurep)

{

    JSTrap *trap;



    trap = FindTrap(cx->runtime, script, pc);

    if (handlerp)

	*handlerp = trap ? trap->handler : NULL;

    if (closurep)

	*closurep = trap ? trap->closure : NULL;

    if (trap)

	DestroyTrap(cx, trap);

}



JS_PUBLIC_API(void)

JS_ClearScriptTraps(JSContext *cx, JSScript *script)

{

    JSRuntime *rt;

    JSTrap *trap, *next;



    rt = cx->runtime;

    for (trap = (JSTrap *)rt->trapList.next;

	 trap != (JSTrap *)&rt->trapList;

	 trap = next) {

	next = (JSTrap *)trap->links.next;

	if (trap->script == script)

	    DestroyTrap(cx, trap);

    }

}



JS_PUBLIC_API(void)

JS_ClearAllTraps(JSContext *cx)

{

    JSRuntime *rt;

    JSTrap *trap, *next;



    rt = cx->runtime;

    for (trap = (JSTrap *)rt->trapList.next;

	 trap != (JSTrap *)&rt->trapList;

	 trap = next) {

	next = (JSTrap *)trap->links.next;

	DestroyTrap(cx, trap);

    }

}



JS_PUBLIC_API(JSTrapStatus)

JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)

{

    JSTrap *trap;

    JSTrapStatus status;

    jsint op;



    trap = FindTrap(cx->runtime, script, pc);

    if (!trap) {

	JS_ASSERT(0);	/* XXX can't happen */

	return JSTRAP_ERROR;

    }

    /*

     * It's important that we not use 'trap->' after calling the callback --

     * the callback might remove the trap!

     */

    op = (jsint)trap->op;

    status = trap->handler(cx, script, pc, rval, trap->closure);

    if (status == JSTRAP_CONTINUE) {

	/* By convention, return the true op to the interpreter in rval. */

	*rval = INT_TO_JSVAL(op);

    }

    return status;

}



JS_PUBLIC_API(JSBool)

JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure)

{

    rt->interruptHandler = handler;

    rt->interruptHandlerData = closure;

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep)

{

    if (handlerp)

	*handlerp = (JSTrapHandler)rt->interruptHandler;

    if (closurep)

	*closurep = rt->interruptHandlerData;

    rt->interruptHandler = 0;

    rt->interruptHandlerData = 0;

    return JS_TRUE;

}





typedef struct JSWatchPoint {

    JSCList             links;

    JSObject            *object;	/* weak link, see js_FinalizeObject */

    jsval               userid;

    JSScopeProperty     *sprop;

    JSPropertyOp        setter;

    JSWatchPointHandler handler;

    void                *closure;

    jsrefcount          nrefs;

} JSWatchPoint;



#define HoldWatchPoint(wp) ((wp)->nrefs++)



static void

DropWatchPoint(JSContext *cx, JSWatchPoint *wp)

{

    if (--wp->nrefs != 0)

	return;

    SPROP_SETTER(wp->sprop, wp->object) = wp->setter;

    JS_LOCK_OBJ_VOID(cx, wp->object,

		     js_DropScopeProperty(cx, OBJ_SCOPE(wp->object),

					  wp->sprop));

    JS_REMOVE_LINK(&wp->links);

    js_RemoveRoot(cx->runtime, &wp->closure);

    JS_free(cx, wp);

}



static JSWatchPoint *

FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid)

{

    JSWatchPoint *wp;



    for (wp = (JSWatchPoint *)rt->watchPointList.next;

	 wp != (JSWatchPoint *)&rt->watchPointList;

	 wp = (JSWatchPoint *)wp->links.next) {

	if (wp->object == obj && wp->userid == userid)

	    return wp;

    }

    return NULL;

}



JSScopeProperty *

js_FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid)

{

    JSWatchPoint *wp;



    wp = FindWatchPoint(rt, obj, userid);

    if (!wp)

	return NULL;

    return wp->sprop;

}



JSBool JS_DLL_CALLBACK

js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSRuntime *rt;

    JSWatchPoint *wp;

    JSScopeProperty *sprop;

    JSSymbol *sym;

    jsval userid, value;

    jsid symid;

    JSScope *scope;

    JSAtom *atom;

    JSBool ok;



    rt = cx->runtime;

    for (wp = (JSWatchPoint *)rt->watchPointList.next;

	 wp != (JSWatchPoint *)&rt->watchPointList;

	 wp = (JSWatchPoint *)wp->links.next) {

	sprop = wp->sprop;

	if (wp->object == obj && sprop->id == id) {

	    JS_LOCK_OBJ(cx, obj);

	    sym = sprop->symbols;

	    if (!sym) {

		userid = wp->userid;

		atom = NULL;

		if (JSVAL_IS_INT(userid)) {

		    symid = (jsid)userid;

		} else {

		    atom = js_ValueToStringAtom(cx, userid);

		    if (!atom) {

			JS_UNLOCK_OBJ(cx, obj);

			return JS_FALSE;

		    }

		    symid = (jsid)atom;

		}

		scope = OBJ_SCOPE(obj);

		JS_ASSERT(scope->props);

		ok = LOCKED_OBJ_GET_CLASS(obj)->addProperty(cx, obj, sprop->id,

							    &value);

		if (!ok) {

		    JS_UNLOCK_OBJ(cx, obj);

		    return JS_FALSE;

		}

		ok = (scope->ops->add(cx, scope, symid, sprop) != NULL);

		if (!ok) {

		    JS_UNLOCK_OBJ(cx, obj);

		    return JS_FALSE;

		}

		sym = sprop->symbols;

	    }

	    JS_UNLOCK_OBJ(cx, obj);

	    HoldWatchPoint(wp);

            ok = wp->handler(cx, obj, js_IdToValue(sym_id(sym)),

                             (SPROP_HAS_VALID_SLOT(sprop))

                             ? OBJ_GET_SLOT(cx, obj, wp->sprop->slot)

                             : JSVAL_VOID,

                             vp, wp->closure);

            if (ok) {

                /*

                 * Create pseudo-frame for call to setter so that any 

                 * stackwalking security code in the setter will correctly

                 * identify the guilty party.

                 */

                JSObject *funobj = (JSObject *) wp->closure;

                JSFunction *fun = (JSFunction *) JS_GetPrivate(cx, funobj);

                JSStackFrame frame;

                memset(&frame, 0, sizeof(frame));

                frame.script = fun->script;

                frame.fun = fun;

                frame.down = cx->fp;

                cx->fp = &frame;

		ok = wp->setter(cx, obj, id, vp);

                cx->fp = frame.down;

            }

	    DropWatchPoint(cx, wp);

	    return ok;

	}

    }

    JS_ASSERT(0);	/* XXX can't happen */

    return JS_FALSE;

}



JS_PUBLIC_API(JSBool)

JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,

		 JSWatchPointHandler handler, void *closure)

{

    JSAtom *atom;

    jsid symid;

    JSObject *pobj;

    JSScopeProperty *sprop;

    JSRuntime *rt;

    JSWatchPoint *wp;



    if (!OBJ_IS_NATIVE(obj)) {

	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,

			     OBJ_GET_CLASS(cx, obj)->name);

	return JS_FALSE;

    }



    if (JSVAL_IS_INT(id)) {

	symid = (jsid)id;

	atom = NULL;

    } else {

	atom = js_ValueToStringAtom(cx, id);

	if (!atom)

	    return JS_FALSE;

	symid = (jsid)atom;

    }



    if (!js_LookupProperty(cx, obj, symid, &pobj, (JSProperty **)&sprop))

	return JS_FALSE;

    rt = cx->runtime;

    if (!sprop) {

	/* Check for a deleted symbol watchpoint, which holds its property. */

	sprop = js_FindWatchPoint(rt, obj, id);

	if (sprop) {

#ifdef JS_THREADSAFE

	    /* Emulate js_LookupProperty if thread-safe. */

	    JS_LOCK_OBJ(cx, obj);

	    sprop->nrefs++;

#endif

	} else {

	    /* Make a new property in obj so we can watch for the first set. */

	    if (!js_DefineProperty(cx, obj, symid, JSVAL_VOID, NULL, NULL, 0,

				   (JSProperty **)&sprop)) {

		sprop = NULL;

	    }

	}

    } else if (pobj != obj) {

	/* Clone the prototype property so we can watch the right object. */

	jsval value;

	JSPropertyOp getter, setter;

	uintN attrs;



	if (OBJ_IS_NATIVE(pobj)) {

            value = (SPROP_HAS_VALID_SLOT(sprop))

                    ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)

                    : JSVAL_VOID;

	} else {

	    if (!OBJ_GET_PROPERTY(cx, pobj, id, &value)) {

		OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);

		return JS_FALSE;

	    }

	}

	getter = SPROP_GETTER(sprop, pobj);

	setter = SPROP_SETTER(sprop, pobj);

	attrs = sprop->attrs;

	OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);



	if (!js_DefineProperty(cx, obj, symid, value, getter, setter, attrs,

			       (JSProperty **)&sprop)) {

	    sprop = NULL;

	}

    }

    if (!sprop)

	return JS_FALSE;



    wp = FindWatchPoint(rt, obj, id);

    if (!wp) {

	wp = (JSWatchPoint *) JS_malloc(cx, sizeof *wp);

	if (!wp)

	    return JS_FALSE;

	if (!js_AddRoot(cx, &wp->closure, "wp->closure")) {

	    JS_free(cx, wp);

	    return JS_FALSE;

	}

	JS_APPEND_LINK(&wp->links, &rt->watchPointList);

	wp->object = obj;

	wp->userid = id;

	wp->sprop = js_HoldScopeProperty(cx, OBJ_SCOPE(obj), sprop);

	wp->setter = SPROP_SETTER(sprop, obj);

	SPROP_SETTER(sprop, obj) = js_watch_set;

	wp->nrefs = 1;

    }

    wp->handler = handler;

    wp->closure = closure;

    OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop);

    return JS_TRUE;

}



JS_PUBLIC_API(void)

JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id,

		   JSWatchPointHandler *handlerp, void **closurep)

{

    JSRuntime *rt;

    JSWatchPoint *wp;



    rt = cx->runtime;

    for (wp = (JSWatchPoint *)rt->watchPointList.next;

	 wp != (JSWatchPoint *)&rt->watchPointList;

	 wp = (JSWatchPoint *)wp->links.next) {

	if (wp->object == obj && wp->userid == id) {

	    if (handlerp)

		*handlerp = wp->handler;

	    if (closurep)

		*closurep = wp->closure;

	    DropWatchPoint(cx, wp);

	    return;

	}

    }

    if (handlerp)

	*handlerp = NULL;

    if (closurep)

	*closurep = NULL;

}



JS_PUBLIC_API(void)

JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)

{

    JSRuntime *rt;

    JSWatchPoint *wp, *next;



    rt = cx->runtime;

    for (wp = (JSWatchPoint *)rt->watchPointList.next;

	 wp != (JSWatchPoint *)&rt->watchPointList;

	 wp = next) {

	next = (JSWatchPoint *)wp->links.next;

	if (wp->object == obj)

	    DropWatchPoint(cx, wp);

    }

}



JS_PUBLIC_API(void)

JS_ClearAllWatchPoints(JSContext *cx)

{

    JSRuntime *rt;

    JSWatchPoint *wp, *next;



    rt = cx->runtime;

    for (wp = (JSWatchPoint *)rt->watchPointList.next;

	 wp != (JSWatchPoint *)&rt->watchPointList;

	 wp = next) {

	next = (JSWatchPoint *)wp->links.next;

	DropWatchPoint(cx, wp);

    }

}



JS_PUBLIC_API(uintN)

JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)

{

    return js_PCToLineNumber(script, pc);

}



JS_PUBLIC_API(jsbytecode *)

JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)

{

    return js_LineNumberToPC(script, lineno);

}



JS_PUBLIC_API(JSScript *)

JS_GetFunctionScript(JSContext *cx, JSFunction *fun)

{

    return fun->script;

}



JS_PUBLIC_API(JSPrincipals *)

JS_GetScriptPrincipals(JSContext *cx, JSScript *script)

{

    return script->principals;

}





/*

 *  Stack Frame Iterator

 */

JS_PUBLIC_API(JSStackFrame *)

JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)

{

    *iteratorp = (*iteratorp == NULL) ? cx->fp : (*iteratorp)->down;

    return *iteratorp;

}



JS_PUBLIC_API(JSScript *)

JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)

{

    return fp->script;

}



JS_PUBLIC_API(jsbytecode *)

JS_GetFramePC(JSContext *cx, JSStackFrame *fp)

{

    return fp->pc;

}



JS_PUBLIC_API(void *)

JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp)

{

    if (fp->annotation) {

	JSPrincipals *principals = fp->script

	    ? fp->script->principals

	    : NULL;



	if (principals == NULL)

	    return NULL;



	if (principals->globalPrivilegesEnabled(cx, principals)) {

	    /*

	     * Only give out an annotation if privileges have not

	     * been revoked globally.

	     */

	    return fp->annotation;

	}

    }



    return NULL;

}



JS_PUBLIC_API(void)

JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation)

{

    fp->annotation = annotation;

}



JS_PUBLIC_API(void *)

JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp)

{

    JSPrincipals *principals = fp->script

	? fp->script->principals

	: NULL;



    return principals

	? principals->getPrincipalArray(cx, principals)

	: NULL;

}



JS_PUBLIC_API(JSBool)

JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp)

{

    return fp->fun && fp->fun->native;

}



/* this is deprecated, use JS_GetFrameScopeChain instead */

JS_PUBLIC_API(JSObject *)

JS_GetFrameObject(JSContext *cx, JSStackFrame *fp)

{

    return fp->scopeChain;

}



JS_PUBLIC_API(JSObject *)

JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)

{

    /* Force creation of argument and call objects if not yet created */

    (void) JS_GetFrameCallObject(cx, fp);

    return fp->scopeChain;

}



JS_PUBLIC_API(JSObject *)

JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)

{

    if (! fp->fun)

        return NULL;

#if JS_HAS_ARGS_OBJECT

    /* Force creation of argument object if not yet created */

    (void) js_GetArgsObject(cx, fp);

#endif

#if JS_HAS_CALL_OBJECT

    /*

     * XXX ill-defined: null return here means error was reported, unlike a

     *     null returned above or in the #else

     */

    return js_GetCallObject(cx, fp, NULL);

#else

    return NULL;

#endif /* JS_HAS_CALL_OBJECT */

}





JS_PUBLIC_API(JSObject *)

JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)

{

    return fp->thisp;

}



JS_PUBLIC_API(JSFunction *)

JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)

{

    return fp->fun;

}



JS_PUBLIC_API(JSObject *)

JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)

{

    return fp->argv && fp->fun ? JSVAL_TO_OBJECT(fp->argv[-2]) : NULL;

}



JS_PUBLIC_API(JSBool)

JS_IsContructorFrame(JSContext *cx, JSStackFrame *fp)

{

    return fp->constructing;

}



JS_PUBLIC_API(JSBool)

JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp)

{

    return fp->special & JSFRAME_DEBUGGER;

}



JS_PUBLIC_API(jsval)

JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp)

{

    return fp->rval;

}



JS_PUBLIC_API(void)

JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval)

{

    fp->rval = rval;

}



/************************************************************************/



JS_PUBLIC_API(const char *)

JS_GetScriptFilename(JSContext *cx, JSScript *script)

{

    return script->filename;

}



JS_PUBLIC_API(uintN)

JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)

{

    return script->lineno;

}



JS_PUBLIC_API(uintN)

JS_GetScriptLineExtent(JSContext *cx, JSScript *script)

{

    return js_GetScriptLineExtent(script);

}



/***************************************************************************/



JS_PUBLIC_API(void)

JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)

{

    rt->newScriptHook = hook;

    rt->newScriptHookData = callerdata;

}



JS_PUBLIC_API(void)

JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,

			void *callerdata)

{

    rt->destroyScriptHook = hook;

    rt->destroyScriptHookData = callerdata;

}



/***************************************************************************/



JS_PUBLIC_API(JSBool)

JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,

			const char *bytes, uintN length,

			const char *filename, uintN lineno,

			jsval *rval)

{

    JSScript *script;

    JSBool ok;



    script = JS_CompileScriptForPrincipals(cx, fp->scopeChain,

					   fp->script ? fp->script->principals

						      : NULL,

					   bytes, length, filename, lineno);

    if (!script)

	return JS_FALSE;

    ok = js_Execute(cx, fp->scopeChain, script, fp, JSFRAME_DEBUGGER, rval);

    js_DestroyScript(cx, script);

    return ok;

}



/************************************************************************/



/* XXXbe this all needs to be reworked to avoid requiring JSScope types. */



JS_PUBLIC_API(JSScopeProperty *)

JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)

{

    JSScopeProperty *sprop;

    JSScope *scope;



    sprop = *iteratorp;

    scope = OBJ_SCOPE(obj);

    sprop = (sprop == NULL) ? scope->props : sprop->next;

    *iteratorp = sprop;

    return sprop;

}



JS_PUBLIC_API(JSBool)

JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,

		   JSPropertyDesc *pd)

{

    JSSymbol *sym;

    JSPropertyOp getter;



    sym = sprop->symbols;

    pd->id = sym ? js_IdToValue(sym_id(sym)) : JSVAL_VOID;

    if (!sym || !js_GetProperty(cx, obj, sym_id(sym), &pd->value)) {

        pd->value = (SPROP_HAS_VALID_SLOT(sprop))

                    ? OBJ_GET_SLOT(cx, obj, sprop->slot)

                    : JSVAL_VOID;

    }

    getter = SPROP_GETTER(sprop, obj);

    pd->flags = ((sprop->attrs & JSPROP_ENUMERATE)      ? JSPD_ENUMERATE : 0)

	      | ((sprop->attrs & JSPROP_READONLY)       ? JSPD_READONLY  : 0)

	      | ((sprop->attrs & JSPROP_PERMANENT)      ? JSPD_PERMANENT : 0)

#if JS_HAS_CALL_OBJECT

	      | ((getter == js_GetCallVariable)  ? JSPD_VARIABLE  : 0)

#endif /* JS_HAS_CALL_OBJECT */

	      | ((getter == js_GetArgument)      ? JSPD_ARGUMENT  : 0)

	      | ((getter == js_GetLocalVariable) ? JSPD_VARIABLE  : 0);

#if JS_HAS_CALL_OBJECT

    /* for Call Object 'real' getter isn't passed in to us */

    if (OBJ_GET_CLASS(cx, obj) == &js_CallClass &&

	getter == js_CallClass.getProperty) {

	pd->flags |= JSPD_ARGUMENT;

    }

#endif /* JS_HAS_CALL_OBJECT */

    pd->spare = 0;

    pd->slot = (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE))

	       ? (unsigned int)JSVAL_TO_INT(sprop->id)

	       : 0;

    if (!sym || !sym->next || (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE))) {

	pd->alias = JSVAL_VOID;

    } else {

	pd->alias = js_IdToValue(sym_id(sym->next));

	pd->flags |= JSPD_ALIAS;

    }

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)

{

    JSClass *clasp;

    JSScope *scope;

    uint32 i, n;

    JSPropertyDesc *pd;

    JSScopeProperty *sprop;



    clasp = OBJ_GET_CLASS(cx, obj);

    if (!OBJ_IS_NATIVE(obj) || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {

	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

			     JSMSG_CANT_DESCRIBE_PROPS, clasp->name);

    	return JS_FALSE;

    }

    if (!clasp->enumerate(cx, obj))

	return JS_FALSE;



    /* have no props, or object's scope has not mutated from that of proto */

    scope = OBJ_SCOPE(obj);

    if (!scope->props ||

	(OBJ_GET_PROTO(cx,obj) && scope == OBJ_SCOPE(OBJ_GET_PROTO(cx,obj)))) {

	pda->length = 0;

	pda->array = NULL;

	return JS_TRUE;

    }



    n = scope->map.freeslot;

    pd = (JSPropertyDesc *) JS_malloc(cx, (size_t)n * sizeof(JSPropertyDesc));

    if (!pd)

	return JS_FALSE;

    i = 0;

    for (sprop = scope->props; sprop; sprop = sprop->next) {

	if (!js_AddRoot(cx, &pd[i].id, NULL))

	    goto bad;

	if (!js_AddRoot(cx, &pd[i].value, NULL))

	    goto bad;

	JS_GetPropertyDesc(cx, obj, sprop, &pd[i]);

	if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL))

	    goto bad;

	if (++i == n)

	    break;

    }

    pda->length = i;

    pda->array = pd;

    return JS_TRUE;



bad:

    pda->length = i + 1;

    pda->array = pd;

    JS_PutPropertyDescArray(cx, pda);

    return JS_FALSE;

}



JS_PUBLIC_API(void)

JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)

{

    JSPropertyDesc *pd;

    uint32 i;



    pd = pda->array;

    for (i = 0; i < pda->length; i++) {

	js_RemoveRoot(cx->runtime, &pd[i].id);

	js_RemoveRoot(cx->runtime, &pd[i].value);

	if (pd[i].flags & JSPD_ALIAS)

	    js_RemoveRoot(cx->runtime, &pd[i].alias);

    }

    JS_free(cx, pd);

}



/************************************************************************/



JS_PUBLIC_API(JSBool)

JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure)

{

    rt->debuggerHandler = handler;

    rt->debuggerHandlerData = closure;

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)

{

    rt->sourceHandler = handler;

    rt->sourceHandlerData = closure;

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)

{

    rt->executeHook = hook;

    rt->executeHookData = closure;

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)

{

    rt->callHook = hook;

    rt->callHookData = closure;

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure)

{

    rt->objectHook = hook;

    rt->objectHookData = closure;

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure)

{

    rt->throwHook = hook;

    rt->throwHookData = closure;

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)

{

    rt->debugErrorHook = hook;

    rt->debugErrorHookData = closure;

    return JS_TRUE;

}



/************************************************************************/



extern JS_FRIEND_DATA(JSScopeOps) js_list_scope_ops;



static size_t

GetSymbolTotalSize(JSContext *cx, JSSymbol *sym)

{

    JSScopeProperty *sprop;

    size_t nbytes;



    sprop = sym_property(sym);

    nbytes = sizeof *sprop;



    /* XXX don't count sprop->id, assume it's shared */



    if (sprop->attrs & JSPROP_GETTER)

        nbytes += JS_GetObjectTotalSize(cx, (JSObject *) sprop->getter);

    if (sprop->attrs & JSPROP_SETTER)

        nbytes += JS_GetObjectTotalSize(cx, (JSObject *) sprop->setter);



    /* XXX don't count sym_id, assume it's shared */

    /* XXX don't worry about aliases (extra symbols for an sprop) */

    nbytes += sizeof *sym;

    return nbytes;

}



typedef struct SymbolEnumArgs {

    JSContext *cx;

    size_t nbytes;

} SymbolEnumArgs;



static intN

SymbolEnumerator(JSHashEntry *he, intN i, void *arg)

{

    JSSymbol *sym = (JSSymbol *) he;

    SymbolEnumArgs *args = (SymbolEnumArgs *) arg;



    args->nbytes += GetSymbolTotalSize(args->cx, sym);

    return HT_ENUMERATE_NEXT;

}



JS_PUBLIC_API(size_t)

JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)

{

    size_t nbytes;

    JSScope *scope;

    JSSymbol *sym;

    JSHashTable *table;

    SymbolEnumArgs args;



    nbytes = sizeof *obj + obj->map->nslots * sizeof obj->slots[0];

    if (OBJ_IS_NATIVE(obj)) {

        scope = OBJ_SCOPE(obj);

        if (scope->object == obj) {

            nbytes += sizeof *scope;

            if (scope->ops == &js_list_scope_ops) {

                for (sym = scope->data; sym; sym = (JSSymbol *) sym->entry.next)

                    nbytes += GetSymbolTotalSize(cx, sym);

            } else {

                table = scope->data;

                nbytes += sizeof *table;

                nbytes += JS_BIT(JS_HASH_BITS - table->shift)

                          * sizeof table->buckets[0];

                args.cx = cx;

                args.nbytes = 0;

                JS_HashTableEnumerateEntries(table, SymbolEnumerator, &args);

                nbytes += args.nbytes;

            }

        }

    }

    return nbytes;

}



static size_t

GetAtomTotalSize(JSContext *cx, JSAtom *atom)

{

    size_t nbytes;



    nbytes = sizeof *atom;

    if (ATOM_IS_STRING(atom)) {

        nbytes += sizeof(JSString);

        nbytes += (ATOM_TO_STRING(atom)->length + 1) * sizeof(jschar);

    } else if (ATOM_IS_DOUBLE(atom)) {

        nbytes += sizeof(jsdouble);

        nbytes += sizeof *ATOM_TO_DOUBLE(atom);

    } else if (ATOM_IS_OBJECT(atom)) {

        nbytes += JS_GetObjectTotalSize(cx, ATOM_TO_OBJECT(atom));

    }

    return nbytes;

}



JS_PUBLIC_API(size_t)

JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun)

{

    size_t nbytes, obytes;

    JSObject *obj;

    JSAtom *atom;



    nbytes = sizeof *fun;

    JS_ASSERT(fun->nrefs);

    obj = fun->object;

    if (obj) {

        obytes = JS_GetObjectTotalSize(cx, obj);

        if (fun->nrefs > 1)

            obytes = (obytes + fun->nrefs - 1) / fun->nrefs;

        nbytes += obytes;

    }

    if (fun->script)

        nbytes += JS_GetScriptTotalSize(cx, fun->script);

    atom = fun->atom;

    if (atom)

        nbytes += GetAtomTotalSize(cx, atom);

    return nbytes;

}



#include "jsemit.h"



JS_PUBLIC_API(size_t)

JS_GetScriptTotalSize(JSContext *cx, JSScript *script)

{

    size_t nbytes, pbytes;

    JSObject *obj;

    jsatomid i;

    jssrcnote *sn, *notes;

    JSTryNote *tn, *tnotes;

    JSPrincipals *principals;



    nbytes = sizeof *script;

    obj = script->object;

    if (obj)

        nbytes += JS_GetObjectTotalSize(cx, obj);



    nbytes += script->length * sizeof script->code[0];

    nbytes += script->atomMap.length * sizeof script->atomMap.vector[0];

    for (i = 0; i < script->atomMap.length; i++)

        nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]);



    if (script->filename)

        nbytes += strlen(script->filename) + 1;



    notes = script->notes;

    if (notes) {

        for (sn = notes; !SN_IS_TERMINATOR(sn); sn += SN_LENGTH(sn))

            continue;

        nbytes += (sn - notes + 1) * sizeof *sn;

    }



    tnotes = script->trynotes;

    if (tnotes) {

        for (tn = tnotes; tn->catchStart; tn++)

            continue;

        nbytes += (tn - tnotes + 1) * sizeof *tn;

    }



    principals = script->principals;

    if (principals) {

        JS_ASSERT(principals->refcount);

        pbytes = sizeof *principals;

        if (principals->refcount > 1)

            pbytes = (pbytes + principals->refcount - 1) / principals->refcount;

        nbytes += pbytes;

    }



    return nbytes;

}

 

**** End of jsdbgapi.c ****

 

**** Start of jsdbgapi.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsdbgapi_h___

#define jsdbgapi_h___

/*

 * JS debugger API.

 */

#include "jsapi.h"

#include "jsopcode.h"

#include "jsprvtd.h"



JS_BEGIN_EXTERN_C



extern void

js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op);



extern JS_PUBLIC_API(JSBool)

JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,

	   JSTrapHandler handler, void *closure);



extern JS_PUBLIC_API(JSOp)

JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc);



extern JS_PUBLIC_API(void)

JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,

	     JSTrapHandler *handlerp, void **closurep);



extern JS_PUBLIC_API(void)

JS_ClearScriptTraps(JSContext *cx, JSScript *script);



extern JS_PUBLIC_API(void)

JS_ClearAllTraps(JSContext *cx);



extern JS_PUBLIC_API(JSTrapStatus)

JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval);



extern JS_PUBLIC_API(JSBool)

JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure);



extern JS_PUBLIC_API(JSBool)

JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep);



/************************************************************************/



extern JS_PUBLIC_API(JSBool)

JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,

		 JSWatchPointHandler handler, void *closure);



extern JS_PUBLIC_API(void)

JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id,

		   JSWatchPointHandler *handlerp, void **closurep);



extern JS_PUBLIC_API(void)

JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj);



extern JS_PUBLIC_API(void)

JS_ClearAllWatchPoints(JSContext *cx);



#ifdef JS_HAS_OBJ_WATCHPOINT

/*

 * Hide these non-API function prototypes by testing whether the internal

 * header file "jsconfig.h" has been included.

 */

extern JSScopeProperty *

js_FindWatchPoint(JSRuntime *rt, JSObject *obj, jsval userid);



extern JSBool JS_DLL_CALLBACK

js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

#endif



/************************************************************************/



extern JS_PUBLIC_API(uintN)

JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc);



extern JS_PUBLIC_API(jsbytecode *)

JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno);



extern JS_PUBLIC_API(JSScript *)

JS_GetFunctionScript(JSContext *cx, JSFunction *fun);



extern JS_PUBLIC_API(JSPrincipals *)

JS_GetScriptPrincipals(JSContext *cx, JSScript *script);



/*

 * Stack Frame Iterator

 *

 * Used to iterate through the JS stack frames to extract

 * information from the frames.

 */



extern JS_PUBLIC_API(JSStackFrame *)

JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp);



extern JS_PUBLIC_API(JSScript *)

JS_GetFrameScript(JSContext *cx, JSStackFrame *fp);



extern JS_PUBLIC_API(jsbytecode *)

JS_GetFramePC(JSContext *cx, JSStackFrame *fp);



extern JS_PUBLIC_API(JSBool)

JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp);



extern JS_PUBLIC_API(void *)

JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp);



extern JS_PUBLIC_API(void)

JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation);



extern JS_PUBLIC_API(void *)

JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp);



/* this is deprecated, use JS_GetFrameScopeChain instead */

extern JS_PUBLIC_API(JSObject *)

JS_GetFrameObject(JSContext *cx, JSStackFrame *fp);



extern JS_PUBLIC_API(JSObject *)

JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp);



extern JS_PUBLIC_API(JSObject *)

JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp);



extern JS_PUBLIC_API(JSObject *)

JS_GetFrameThis(JSContext *cx, JSStackFrame *fp);



extern JS_PUBLIC_API(JSFunction *)

JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp);



extern JS_PUBLIC_API(JSObject *)

JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp);



extern JS_PUBLIC_API(JSBool)

JS_IsContructorFrame(JSContext *cx, JSStackFrame *fp);



extern JS_PUBLIC_API(JSBool)

JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp);



extern JS_PUBLIC_API(jsval)

JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp);



extern JS_PUBLIC_API(void)

JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval);



/************************************************************************/



extern JS_PUBLIC_API(const char *)

JS_GetScriptFilename(JSContext *cx, JSScript *script);



extern JS_PUBLIC_API(uintN)

JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script);



extern JS_PUBLIC_API(uintN)

JS_GetScriptLineExtent(JSContext *cx, JSScript *script);



/************************************************************************/



/*

 * Hook setters for script creation and destruction, see jsprvtd.h for the

 * typedefs.  These macros provide binary compatibility and newer, shorter

 * synonyms.

 */

#define JS_SetNewScriptHook     JS_SetNewScriptHookProc

#define JS_SetDestroyScriptHook JS_SetDestroyScriptHookProc



extern JS_PUBLIC_API(void)

JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata);



extern JS_PUBLIC_API(void)

JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,

			void *callerdata);



/************************************************************************/



extern JS_PUBLIC_API(JSBool)

JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,

			const char *bytes, uintN length,

			const char *filename, uintN lineno,

			jsval *rval);



/************************************************************************/



typedef struct JSPropertyDesc {

    jsval           id;         /* primary id, a string or int */

    jsval           value;      /* property value */

    uint8           flags;      /* flags, see below */

    uint8           spare;      /* unused */

    uint16          slot;       /* argument/variable slot */

    jsval           alias;      /* alias id if JSPD_ALIAS flag */

} JSPropertyDesc;



#define JSPD_ENUMERATE  0x01    /* visible to for/in loop */

#define JSPD_READONLY   0x02    /* assignment is error */

#define JSPD_PERMANENT  0x04    /* property cannot be deleted */

#define JSPD_ALIAS      0x08    /* property has an alias id */

#define JSPD_ARGUMENT   0x10    /* argument to function */

#define JSPD_VARIABLE   0x20    /* local variable in function */



typedef struct JSPropertyDescArray {

    uint32          length;     /* number of elements in array */

    JSPropertyDesc  *array;     /* alloc'd by Get, freed by Put */

} JSPropertyDescArray;



extern JS_PUBLIC_API(JSScopeProperty *)

JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp);



extern JS_PUBLIC_API(JSBool)

JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,

		   JSPropertyDesc *pd);



extern JS_PUBLIC_API(JSBool)

JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda);



extern JS_PUBLIC_API(void)

JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda);



/************************************************************************/



extern JS_PUBLIC_API(JSBool)

JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure);



extern JS_PUBLIC_API(JSBool)

JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure);



extern JS_PUBLIC_API(JSBool)

JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure);



extern JS_PUBLIC_API(JSBool)

JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure);



extern JS_PUBLIC_API(JSBool)

JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure);



extern JS_PUBLIC_API(JSBool)

JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure);



extern JS_PUBLIC_API(JSBool)

JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure);



/************************************************************************/



extern JS_PUBLIC_API(size_t)

JS_GetObjectTotalSize(JSContext *cx, JSObject *obj);



extern JS_PUBLIC_API(size_t)

JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun);



extern JS_PUBLIC_API(size_t)

JS_GetScriptTotalSize(JSContext *cx, JSScript *script);



JS_END_EXTERN_C



#endif /* jsdbgapi_h___ */

 

**** End of jsdbgapi.h ****

 

**** Start of jsdhash.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * implied. See the License for the specific language governing

 * rights and limitations under the License.

 *

 * The Original Code is Mozilla JavaScript code.

 *

 * The Initial Developer of the Original Code is Netscape

 * Communications Corporation.  Portions created by Netscape are

 * Copyright (C) 1999,2000 Netscape Communications Corporation.

 * All Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * Double hashing implementation.

 */

#include <stdlib.h>

#include <string.h>

#include "jsbit.h"

#include "jsdhash.h"

#include "jsutil.h"     /* for JS_ASSERT */



#ifdef JS_DHASHMETER

# define METER(x)       x

#else

# define METER(x)       /* nothing */

#endif



JS_PUBLIC_API(void *)

JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes)

{

    return malloc(nbytes);

}



JS_PUBLIC_API(void)

JS_DHashFreeTable(JSDHashTable *table, void *ptr)

{

    free(ptr);

}



JS_PUBLIC_API(JSDHashNumber)

JS_DHashStringKey(JSDHashTable *table, const void *key)

{

    const char *s;

    size_t n, m;

    JSDHashNumber h;



    s = key;

    n = strlen(s);

    h = 0;

    if (n < 16) {

	/* Hash every char in a short string. */

	for (; n; s++, n--)

	    h = (h >> 28) ^ (h << 4) ^ *s;

    } else {

	/* Sample a la java.lang.String.hash(). */

	for (m = n / 8; n >= m; s += m, n -= m)

	    h = (h >> 28) ^ (h << 4) ^ *s;

    }

    return h;

}



JS_PUBLIC_API(const void *)

JS_DHashGetKeyStub(JSDHashTable *table, JSDHashEntryHdr *entry)

{

    JSDHashEntryStub *stub = (JSDHashEntryStub *)entry;

    

    return stub->key;

}



JS_PUBLIC_API(JSDHashNumber)

JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key)

{

    return (JSDHashNumber)key >> 2;

}



JS_PUBLIC_API(JSBool)

JS_DHashMatchEntryStub(JSDHashTable *table,

                       const JSDHashEntryHdr *entry,

                       const void *key)

{

    JSDHashEntryStub *stub = (JSDHashEntryStub *)entry;



    return stub->key == key;

}



JS_PUBLIC_API(void)

JS_DHashMoveEntryStub(JSDHashTable *table,

                      const JSDHashEntryHdr *from,

                      JSDHashEntryHdr *to)

{

    memcpy(to, from, table->entrySize);

}



JS_PUBLIC_API(void)

JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry)

{

    memset(entry, 0, table->entrySize);

}



JS_PUBLIC_API(void)

JS_DHashFinalizeStub(JSDHashTable *table)

{

}



static JSDHashTableOps stub_ops = {

    JS_DHashAllocTable,

    JS_DHashFreeTable,

    JS_DHashGetKeyStub,

    JS_DHashVoidPtrKeyStub,

    JS_DHashMatchEntryStub,

    JS_DHashMoveEntryStub,

    JS_DHashClearEntryStub,

    JS_DHashFinalizeStub

};



JS_PUBLIC_API(JSDHashTableOps *)

JS_DHashGetStubOps(void)

{

    return &stub_ops;

}



JS_PUBLIC_API(JSDHashTable *)

JS_NewDHashTable(JSDHashTableOps *ops, void *data, uint32 entrySize,

                 uint32 capacity)

{

    JSDHashTable *table;



    table = (JSDHashTable *) malloc(sizeof *table);

    if (!table)

        return NULL;

    if (!JS_DHashTableInit(table, ops, data, entrySize, capacity)) {

        free(table);

        return NULL;

    }

    return table;

}



JS_PUBLIC_API(void)

JS_DHashTableDestroy(JSDHashTable *table)

{

    JS_DHashTableFinish(table);

    free(table);

}



JS_PUBLIC_API(JSBool)

JS_DHashTableInit(JSDHashTable *table, JSDHashTableOps *ops, void *data,

                  uint32 entrySize, uint32 capacity)

{

    int log2;

    uint32 nbytes;



    table->ops = ops;

    table->data = data;

    if (capacity < JS_DHASH_MIN_SIZE)

        capacity = JS_DHASH_MIN_SIZE;

    log2 = JS_CeilingLog2(capacity);

    capacity = JS_BIT(log2);

    table->hashShift = JS_DHASH_BITS - log2;

    table->sizeLog2 = log2;

    table->sizeMask = JS_BITMASK(table->sizeLog2);

    table->entrySize = entrySize;

    table->entryCount = table->removedCount = 0;

    nbytes = capacity * entrySize;

    table->entryStore = ops->allocTable(table, nbytes);

    if (!table->entryStore)

        return JS_FALSE;

    memset(table->entryStore, 0, nbytes);

    METER(memset(&table->stats, 0, sizeof table->stats));

    return JS_TRUE;

}



JS_PUBLIC_API(void)

JS_DHashTableFinish(JSDHashTable *table)

{

    table->ops->finalize(table);

    table->ops->freeTable(table, table->entryStore);

}



/*

 * Double hashing needs the second hash code to be relatively prime to table

 * size, so we simply make hash2 odd.

 */

#define HASH1(hash0, shift)         ((hash0) >> (shift))

#define HASH2(hash0,log2,shift)     ((((hash0) << (log2)) >> (shift)) | 1)



/* Reserve keyHash 0 for free entries and 1 for removed-entry sentinels. */

#define MARK_ENTRY_FREE(entry)      ((entry)->keyHash = 0)

#define MARK_ENTRY_REMOVED(entry)   ((entry)->keyHash = 1)

#define ENTRY_IS_LIVE(entry)        ((entry)->keyHash >= 2)

#define ENSURE_LIVE_KEYHASH(hash0)  if (hash0 < 2) hash0 -= 2; else (void)0



/* Compute the address of the indexed entry in table. */

#define ADDRESS_ENTRY(table, index) \

    ((JSDHashEntryHdr *)((table)->entryStore + (index) * (table)->entrySize))



static JSDHashEntryHdr *

SearchTable(JSDHashTable *table, const void *key, JSDHashNumber keyHash)

{

    JSDHashNumber hash1, hash2;

    int hashShift;

    JSDHashEntryHdr *entry;

    JSDHashMatchEntry matchEntry;



    METER(table->stats.searches++);



    /* Compute the primary hash address. */

    hashShift = table->hashShift;

    hash1 = HASH1(keyHash, hashShift);

    entry = ADDRESS_ENTRY(table, hash1);



    /* Miss: return space for a new entry. */

    if (JS_DHASH_ENTRY_IS_FREE(entry)) {

        METER(table->stats.misses++);

        return entry;

    }



    /* Hit: return entry. */

    matchEntry = table->ops->matchEntry;

    if (entry->keyHash == keyHash && matchEntry(table, entry, key)) {

        METER(table->stats.hits++);

        return entry;

    }



    /* Collision: double hash. */

    hash2 = HASH2(keyHash, table->sizeLog2, hashShift);

    do {

        METER(table->stats.steps++);

        hash1 -= hash2;

        hash1 &= table->sizeMask;

        entry = ADDRESS_ENTRY(table, hash1);

        if (JS_DHASH_ENTRY_IS_FREE(entry)) {

            METER(table->stats.misses++);

            return entry;

        }

    } while (entry->keyHash != keyHash || !matchEntry(table, entry, key));



    METER(table->stats.hits++);

    return entry;

}



JS_PUBLIC_API(JSDHashEntryHdr *)

JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op)

{

    int change;

    JSDHashNumber keyHash;

    uint32 i, size, capacity, nbytes, entrySize;

    JSDHashEntryHdr *entry, *oldEntry, *newEntry;

    char *entryStore, *newEntryStore, *entryAddr;

    JSDHashGetKey getKey;

    JSDHashMoveEntry moveEntry;



    /* Usually we don't grow or shrink the table. */

    change = 0;



    /* Avoid 0 and 1 hash codes, they indicate free and deleted entries. */

    keyHash = table->ops->hashKey(table, key);

    ENSURE_LIVE_KEYHASH(keyHash);

    keyHash *= JS_DHASH_GOLDEN_RATIO;

    entry = SearchTable(table, key, keyHash);



    switch (op) {

      case JS_DHASH_LOOKUP:

        METER(table->stats.lookups++);

        break;



      case JS_DHASH_ADD:

        if (JS_DHASH_ENTRY_IS_FREE(entry)) {

            /* Initialize the entry, indicating that it's no longer free. */

            METER(table->stats.addMisses++);

            entry->keyHash = keyHash;

            table->entryCount++;



            /* If alpha is >= .75, set change to trigger table growth below. */

            size = JS_BIT(table->sizeLog2);

            if (table->entryCount + table->removedCount >= size - (size >> 2)) {

                METER(table->stats.grows++);

                change = 1;

                capacity = size << 1;

            }

        }

        METER(else table->stats.addHits++);

        break;



      case JS_DHASH_REMOVE:

        if (JS_DHASH_ENTRY_IS_BUSY(entry)) {

            /* Clear this entry and mark it as "removed". */

            METER(table->stats.removeHits++);

            JS_DHashTableRawRemove(table, entry);



            /* Shrink if alpha is <= .25 and table isn't too small already. */

            size = JS_BIT(table->sizeLog2);

            if (size > JS_DHASH_MIN_SIZE && table->entryCount <= size >> 2) {

                METER(table->stats.shrinks++);

                change = -1;

                capacity = size >> 1;

            }

        }

        METER(else table->stats.removeMisses++);

        entry = NULL;

        break;



      default:

        JS_ASSERT(0);

    }



    if (change) {

        entrySize = table->entrySize;

        nbytes = capacity * entrySize;

        newEntryStore = table->ops->allocTable(table, nbytes);

        if (!newEntryStore) {

            /* If we just grabbed the last free entry, undo and fail hard. */

            if (op == JS_DHASH_ADD &&

                table->entryCount + table->removedCount == size) {

                METER(table->stats.addFailures++);

                MARK_ENTRY_FREE(entry);

                table->entryCount--;

                entry = NULL;

            }

        } else {

            memset(newEntryStore, 0, nbytes);

            entryStore = table->entryStore;

            table->entryStore = newEntryStore;



            table->sizeLog2 += change;

            table->sizeMask = JS_BITMASK(table->sizeLog2);

            table->hashShift = JS_DHASH_BITS - table->sizeLog2;

            table->removedCount = 0;



            getKey = table->ops->getKey;

            moveEntry = table->ops->moveEntry;

            entryAddr = entryStore;

            for (i = 0; i < size; i++) {

                oldEntry = (JSDHashEntryHdr *)entryAddr;

                if (oldEntry != entry && ENTRY_IS_LIVE(oldEntry)) {

                    newEntry = SearchTable(table, getKey(table,oldEntry),

                                           oldEntry->keyHash);

                    JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(newEntry));

                    moveEntry(table, oldEntry, newEntry);

                    newEntry->keyHash = oldEntry->keyHash;

                }

                entryAddr += entrySize;

            }

            table->ops->freeTable(table, entryStore);



            if (op == JS_DHASH_ADD) {

                entry = SearchTable(table, key, keyHash);

                JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(entry));

                entry->keyHash = keyHash;

            }

        }

    }



    return entry;

}



JS_PUBLIC_API(void)

JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry)

{

    table->ops->clearEntry(table, entry);

    MARK_ENTRY_REMOVED(entry);

    table->removedCount++;

    table->entryCount--;

}



JS_PUBLIC_API(uint32)

JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg)

{

    char *entryAddr;

    uint32 i, j, n, entrySize;

    JSDHashEntryHdr *entry;

    JSDHashOperator op;



    entryAddr = table->entryStore;

    entrySize = table->entrySize;

    n = JS_BIT(table->sizeLog2);

    for (i = j = 0; i < n; i++) {

        entry = (JSDHashEntryHdr *)entryAddr;

        if (ENTRY_IS_LIVE(entry)) {

            op = etor(table, entry, j++, arg);

            if (op & JS_DHASH_REMOVE) {

                METER(table->stats.removeEnums++);

                JS_DHashTableRawRemove(table, entry);

            }

            if (op & JS_DHASH_STOP)

                break;

        }

        entryAddr += entrySize;

    }

    return j;

}



#ifdef JS_DHASHMETER

#include <math.h>

#include <stdio.h>



JS_PUBLIC_API(void)

JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp)

{

    char *entryAddr;

    uint32 entrySize, entryCount;

    uint32 i, tableSize, chainLen, maxChainLen, chainCount;

    JSDHashNumber hash1, hash2, saveHash1, maxChainHash1, maxChainHash2;

    double sqsum, mean, variance, sigma;

    JSDHashEntryHdr *entry, *probe;



    entryAddr = table->entryStore;

    entrySize = table->entrySize;

    tableSize = JS_BIT(table->sizeLog2);

    chainCount = maxChainLen = 0;

    hash2 = 0;

    sqsum = 0;



    for (i = 0; i < tableSize; i++) {

        entry = (JSDHashEntryHdr *)entryAddr;

        entryAddr += entrySize;

        if (!ENTRY_IS_LIVE(entry))

            continue;

        hash1 = saveHash1 = HASH1(entry->keyHash, table->hashShift);

        probe = ADDRESS_ENTRY(table, hash1);

        chainLen = 1;

        if (probe == entry) {

            /* Start of a (possibly unit-length) chain. */

            chainCount++;

        } else {

            hash2 = HASH2(entry->keyHash, table->sizeLog2, table->hashShift);

            do {

                chainLen++;

                hash1 -= hash2;

                hash1 &= table->sizeMask;

                probe = ADDRESS_ENTRY(table, hash1);

            } while (probe != entry);

        }

        sqsum += chainLen * chainLen;

        if (chainLen > maxChainLen) {

            maxChainLen = chainLen;

            maxChainHash1 = saveHash1;

            maxChainHash2 = hash2;

        }

    }



    entryCount = table->entryCount;

    mean = (double)entryCount / chainCount;

    variance = chainCount * sqsum - entryCount * entryCount;

    if (variance < 0 || chainCount == 1)

        variance = 0;

    else

        variance /= chainCount * (chainCount - 1);

    sigma = sqrt(variance);



    fprintf(fp, "Double hashing statistics:\n");

    fprintf(fp, "    table size (in entries): %u\n", tableSize);

    fprintf(fp, "          number of entries: %u\n", table->entryCount);

    fprintf(fp, "  number of removed entries: %u\n", table->removedCount);

    fprintf(fp, "         number of searches: %u\n", table->stats.searches);

    fprintf(fp, "             number of hits: %u\n", table->stats.hits);

    fprintf(fp, "           number of misses: %u\n", table->stats.misses);

    fprintf(fp, "      mean steps per search: %g\n", (double)table->stats.steps

                                                     / table->stats.searches);

    fprintf(fp, "     mean hash chain length: %g\n", mean);

    fprintf(fp, "         standard deviation: %g\n", sigma);

    fprintf(fp, "  maximum hash chain length: %u\n", maxChainLen);

    fprintf(fp, "          number of lookups: %u\n", table->stats.lookups);

    fprintf(fp, " adds that made a new entry: %u\n", table->stats.addMisses);

    fprintf(fp, "   adds that found an entry: %u\n", table->stats.addHits);

    fprintf(fp, "               add failures: %u\n", table->stats.addFailures);

    fprintf(fp, "             useful removes: %u\n", table->stats.removeHits);

    fprintf(fp, "            useless removes: %u\n", table->stats.removeMisses);

    fprintf(fp, "  removes while enumerating: %u\n", table->stats.removeEnums);

    fprintf(fp, "            number of grows: %u\n", table->stats.grows);

    fprintf(fp, "          number of shrinks: %u\n", table->stats.shrinks);



    if (maxChainLen && hash2) {

        fputs("Maximum hash chain:\n", fp);

        hash1 = maxChainHash1;

        hash2 = maxChainHash2;

        entry = ADDRESS_ENTRY(table, hash1);

        i = 0;

        do {

            if (dump(table, entry, i++, fp) != JS_DHASH_NEXT)

                break;

            hash1 -= hash2;

            hash1 &= table->sizeMask;

            entry = ADDRESS_ENTRY(table, hash1);

        } while (JS_DHASH_ENTRY_IS_BUSY(entry));

    }

}

#endif /* JS_DHASHMETER */

 

**** End of jsdhash.c ****

 

**** Start of jsdhash.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * implied. See the License for the specific language governing

 * rights and limitations under the License.

 *

 * The Original Code is Mozilla JavaScript code.

 *

 * The Initial Developer of the Original Code is Netscape

 * Communications Corporation.  Portions created by Netscape are

 * Copyright (C) 1999,2000 Netscape Communications Corporation.

 * All Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsdhash_h___

#define jsdhash_h___

/*

 * Double hashing, a la Knuth 6.

 */

#include "jstypes.h"



JS_BEGIN_EXTERN_C



/* Minimum table size, or gross entry count (net is at most .75 loaded). */

#ifndef JS_DHASH_MIN_SIZE

#define JS_DHASH_MIN_SIZE 16

#endif



/*

 * Multiplicative hash uses an unsigned 32 bit integer and the golden ratio,

 * expressed as a fixed-point 32-bit fraction.

 */

#define JS_DHASH_BITS           32

#define JS_DHASH_GOLDEN_RATIO   0x9E3779B9U



/* Primitive and forward-struct typedefs. */

typedef uint32                  JSDHashNumber;

typedef struct JSDHashEntryHdr  JSDHashEntryHdr;

typedef struct JSDHashEntryStub JSDHashEntryStub;

typedef struct JSDHashTable     JSDHashTable;

typedef struct JSDHashTableOps  JSDHashTableOps;



/*

 * Table entry header structure.

 *

 * In order to allow in-line allocation of key and value, we do not declare

 * either here.  Instead, the API uses const void *key as a formal parameter,

 * and asks each entry for its key when necessary via a getKey callback, used

 * when growing or shrinking the table.  Other callback types are defined

 * below and grouped into the JSDHashTableOps structure, for single static

 * initialization per hash table sub-type.

 *

 * Each hash table sub-type should nest the JSDHashEntryHdr structure at the

 * front of its particular entry type.  The keyHash member contains the result

 * of multiplying the hash code returned from the hashKey callback (see below)

 * by JS_DHASH_GOLDEN_RATIO.  Its value is table size invariant.  keyHash is

 * maintained automatically by JS_DHashTableOperate -- users should never set

 * it, and its only uses should be via the entry macros below.

 */

struct JSDHashEntryHdr {

    JSDHashNumber       keyHash;        /* every entry must begin like this */

};



#define JS_DHASH_ENTRY_IS_FREE(entry)   ((entry)->keyHash == 0)

#define JS_DHASH_ENTRY_IS_BUSY(entry)   (!JS_DHASH_ENTRY_IS_FREE(entry))



/*

 * A JSDHashTable is currently 8 words (without the JS_DHASHMETER overhead)

 * on most architectures, and may be allocated on the stack or within another

 * structure or class (see below for the Init and Finish functions to use).

 */

struct JSDHashTable {

    JSDHashTableOps     *ops;           /* virtual operations, see below */

    void                *data;          /* ops- and instance-specific data */

    int16               hashShift;      /* multiplicative hash shift */

    int16               sizeLog2;       /* log2(table size) */

    uint32              sizeMask;       /* JS_BITMASK(log2(table size)) */

    uint32              entrySize;      /* number of bytes in an entry */

    uint32              entryCount;     /* number of entries in table */

    uint32              removedCount;   /* removed entry sentinels in table */

    char                *entryStore;    /* entry storage */

#ifdef JS_DHASHMETER

    struct JSDHashStats {

        uint32          searches;       /* total number of table searches */

        uint32          steps;          /* hash chain links traversed */

        uint32          hits;           /* searches that found key */

        uint32          misses;         /* searches that didn't find key */

        uint32          lookups;        /* number of JS_DHASH_LOOKUPs */

        uint32          addMisses;      /* adds that miss, and do work */

        uint32          addHits;        /* adds that hit an existing entry */

        uint32          addFailures;    /* out-of-memory during add growth */

        uint32          removeHits;     /* removes that hit, and do work */

        uint32          removeMisses;   /* useless removes that miss */

        uint32          removeEnums;    /* removes done by Enumerate */

        uint32          grows;          /* table expansions */

        uint32          shrinks;        /* table contractions */

    } stats;

#endif

};



#ifndef CRT_CALL

#ifdef XP_OS2_VACPP

#define CRT_CALL _Optlink

#else

#define CRT_CALL

#endif

#endif



/*

 * Table space at entryStore is allocated and freed using these callbacks.

 * The allocator should return null on error only (not if called with nbytes

 * equal to 0; but note that jsdhash.c code will never call with 0 nbytes).

 */

typedef void *

(* CRT_CALL JSDHashAllocTable)(JSDHashTable *table, uint32 nbytes);



typedef void

(* CRT_CALL JSDHashFreeTable) (JSDHashTable *table, void *ptr);



/*

 * When a table grows or shrinks, each entry is queried for its key using this

 * callback.  NB: in that event, entry is not in table any longer; it's in the

 * old entryStore vector, which is due to be freed once all entries have been

 * moved via moveEntry callbacks.

 */

typedef const void *

(* CRT_CALL JSDHashGetKey)    (JSDHashTable *table, JSDHashEntryHdr *entry);



/*

 * Compute the hash code for a given key to be looked up, added, or removed

 * from table.  A hash code may have any JSDHashNumber value.

 */

typedef JSDHashNumber

(* CRT_CALL JSDHashHashKey)   (JSDHashTable *table, const void *key);



/*

 * Compare the key identifying entry in table with the provided key parameter.

 * Return JS_TRUE if keys match, JS_FALSE otherwise.

 */

typedef JSBool

(* CRT_CALL JSDHashMatchEntry)(JSDHashTable *table,

                               const JSDHashEntryHdr *entry,

                               const void *key);



/*

 * Copy the data starting at from to the new entry storage at to.  Do not add

 * reference counts for any strong references in the entry, however, as this

 * is a "move" operation: the old entry storage at from will be freed without

 * any reference-decrementing callback shortly.

 */

typedef void

(* CRT_CALL JSDHashMoveEntry)(JSDHashTable *table,

                              const JSDHashEntryHdr *from,

                              JSDHashEntryHdr *to);



/*

 * Clear the entry and drop any strong references it holds.  This callback is

 * invoked during a JS_DHASH_REMOVE operation (see below for operation codes),

 * but only if the given key is found in the table.

 */

typedef void

(* CRT_CALL JSDHashClearEntry)(JSDHashTable *table, JSDHashEntryHdr *entry);



/*

 * Called when a table (whether allocated dynamically by itself, or nested in

 * a larger structure, or allocated on the stack) is finished.  This callback

 * allows table->ops-specific code to finalize table->data.

 */

typedef void

(* CRT_CALL JSDHashFinalize)  (JSDHashTable *table);



/* Finally, the "vtable" structure for JSDHashTable. */

struct JSDHashTableOps {

    JSDHashAllocTable   allocTable;

    JSDHashFreeTable    freeTable;

    JSDHashGetKey       getKey;

    JSDHashHashKey      hashKey;

    JSDHashMatchEntry   matchEntry;

    JSDHashMoveEntry    moveEntry;

    JSDHashClearEntry   clearEntry;

    JSDHashFinalize     finalize;

};



/*

 * Default implementations for the above ops.

 */

extern JS_PUBLIC_API(void *)

JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes);



extern JS_PUBLIC_API(void)

JS_DHashFreeTable(JSDHashTable *table, void *ptr);



extern JS_PUBLIC_API(JSDHashNumber)

JS_DHashStringKey(JSDHashTable *table, const void *key);



/* A minimal entry contains a keyHash header and a void key pointer. */

struct JSDHashEntryStub {

    JSDHashEntryHdr hdr;

    const void      *key;

};



extern JS_PUBLIC_API(const void *)

JS_DHashGetKeyStub(JSDHashTable *table, JSDHashEntryHdr *entry);



extern JS_PUBLIC_API(JSDHashNumber)

JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key);



extern JS_PUBLIC_API(JSBool)

JS_DHashMatchEntryStub(JSDHashTable *table,

                       const JSDHashEntryHdr *entry,

                       const void *key);



extern JS_PUBLIC_API(void)

JS_DHashMoveEntryStub(JSDHashTable *table,

                      const JSDHashEntryHdr *from,

                      JSDHashEntryHdr *to);



extern JS_PUBLIC_API(void)

JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry);



extern JS_PUBLIC_API(void)

JS_DHashFinalizeStub(JSDHashTable *table);



/*

 * If you use JSDHashEntryStub or a subclass of it as your entry struct, and

 * if your entries move via memcpy and clear via memset(0), you can use these

 * stub operations.

 */

extern JS_PUBLIC_API(JSDHashTableOps *)

JS_DHashGetStubOps(void);



/*

 * Dynamically allocate a new JSDHashTable using malloc, initialize it using

 * JS_DHashTableInit, and return its address.  Return null on malloc failure.

 * Note that the entry storage at table->entryStore will be allocated using

 * the ops->allocTable callback.

 */

extern JS_PUBLIC_API(JSDHashTable *)

JS_NewDHashTable(JSDHashTableOps *ops, void *data, uint32 entrySize,

                 uint32 capacity);



/*

 * Finalize table's data, free its entry storage (via table->ops->freeTable),

 * and return the memory starting at table to the malloc heap.

 */

extern JS_PUBLIC_API(void)

JS_DHashTableDestroy(JSDHashTable *table);



/*

 * Initialize table with ops, data, entrySize, and capacity.  Capacity is a

 * guess for the smallest table size at which the table will usually be less

 * than 75% loaded (the table will grow or shrink as needed; capacity serves

 * only to avoid inevitable early growth from JS_DHASH_MIN_SIZE).

 */

extern JS_PUBLIC_API(JSBool)

JS_DHashTableInit(JSDHashTable *table, JSDHashTableOps *ops, void *data,

                  uint32 entrySize, uint32 capacity);



/*

 * Finalize table's data, free its entry storage using table->ops->freeTable,

 * and leave its members unchanged from their last live values (which leaves

 * pointers dangling).  If you want to burn cycles clearing table, it's up to

 * your code to call memset.

 */

extern JS_PUBLIC_API(void)

JS_DHashTableFinish(JSDHashTable *table);



/*

 * To consolidate keyHash computation and table grow/shrink code, we use a

 * single entry point for lookup, add, and remove operations.  The operation

 * codes are declared here, along with codes returned by JSDHashEnumerator

 * functions, which control JS_DHashTableEnumerate's behavior.

 */

typedef enum JSDHashOperator {

    JS_DHASH_LOOKUP = 0,        /* lookup entry */

    JS_DHASH_ADD = 1,           /* add entry */

    JS_DHASH_REMOVE = 2,        /* remove entry, or enumerator says remove */

    JS_DHASH_NEXT = 0,          /* enumerator says continue */

    JS_DHASH_STOP = 1           /* enumerator says stop */

} JSDHashOperator;



/*

 * To lookup a key in table, call:

 *

 *  entry = JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP);

 *

 * If JS_DHASH_ENTRY_IS_BUSY(entry) is true, key was found and it identifies

 * entry.  If JS_DHASH_ENTRY_IS_FREE(entry) is true, key was not found.

 *

 * To add an entry identified by key to table, call:

 *

 *  entry = JS_DHashTableOperate(table, key, JS_DHASH_ADD);

 *

 * If entry is null upon return, the table is severely overloaded, and new

 * memory can't be allocated for new entry storage via table->ops->allocTable.

 * Otherwise, entry->keyHash has been set so that JS_DHASH_ENTRY_IS_BUSY(entry)

 * is true, and it is up to the caller to initialize the key and value parts

 * of the entry sub-type, if they have not been set already (i.e. if entry was

 * not already in the table).

 *

 * To remove an entry identified by key from table, call:

 *

 *  (void) JS_DHashTableOperate(table, key, JS_DHASH_REMOVE);

 *

 * If key's entry is found, it is cleared (via table->ops->clearEntry) and

 * the entry is marked so that JS_DHASH_ENTRY_IS_FREE(entry).  This operation

 * returns null unconditionally; you should ignore its return value.

 */

extern JS_PUBLIC_API(JSDHashEntryHdr *)

JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op);



/*

 * Remove an entry already accessed via LOOKUP or ADD.

 *

 * NB: this is a "raw" or low-level routine, intended to be used only where

 * the inefficiency of a full JS_DHashTableOperate (which rehashes in order

 * to find the entry given its key) is not tolerable.  This function does not

 * shrink the table if it is underloaded.  It does not update stats #ifdef

 * JS_DHASHMETER, either.

 */

extern JS_PUBLIC_API(void)

JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry);



/*

 * Enumerate entries in table using etor:

 *

 *   count = JS_DHashTableEnumerate(table, etor, arg);

 *

 * JS_DHashTableEnumerate calls etor like so:

 *

 *   op = etor(table, entry, number, arg);

 *

 * where number is a zero-based ordinal assigned to live entries according to

 * their order in table->entryStore.

 *

 * The return value, op, is treated as a set of flags.  If op is JS_DHASH_NEXT,

 * then continue enumerating.  If op contains JS_DHASH_REMOVE, then clear (via

 * table->ops->clearEntry) and free entry.  Then we check whether op contains

 * JS_DHASH_STOP; if so, stop enumerating and return the number of live entries

 * that were enumerated so far.  Return the total number of live entries when

 * enumeration completes normally.

 *

 * If etor calls JS_DHashTableOperate on table, it must return JS_DHASH_STOP;

 * otherwise undefined behavior results.

 */

typedef JSDHashOperator

(* CRT_CALL JSDHashEnumerator)(JSDHashTable *table, JSDHashEntryHdr *hdr,

                               uint32 number, void *arg);



extern JS_PUBLIC_API(uint32)

JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg);



#ifdef JS_DHASHMETER

#include <stdio.h>



extern JS_PUBLIC_API(void)

JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp);

#endif



JS_END_EXTERN_C



#endif /* jsdhash_h___ */

 

**** End of jsdhash.h ****

 

**** Start of jsdtoa.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * 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 */



#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 errno to ERANGE).  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(n) and freed

 *  by FREE_DTOA_LOCK(n) for n = 0 or 1.  (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



#include "errno.h"

#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



/* Stefan Hanske <sh990154@mail.uni-greifswald.de> reports:

 *  ARM is a little endian architecture but 64 bit double words are stored

 * differently: the 32 bit words are in little endian byte order, the two words

 * are stored in big endian`s way.

 */

#if defined (IEEE_8087) && !defined(__arm) && !defined(__arm32__) && !defined(__arm26__)

#define word0(x) ((ULong *)&x)[1]

#define word1(x) ((ULong *)&x)[0]

#else

#define word0(x) ((ULong *)&x)[0]

#define word1(x) ((ULong *)&x)[1]

#endif



/* The following definition of Storeinc is appropriate for MIPS processors.

 * An alternative that might be better on some machines is

 * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff)

 */

#if defined(IEEE_8087)

#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \

((unsigned short *)a)[0] = (unsigned short)c, a++)

#else

#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \

((unsigned short *)a)[1] = (unsigned short)c, a++)

#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(n) PR_Lock(freelist_lock)

#define FREE_DTOA_LOCK(n) PR_Unlock(freelist_lock)

#else

#undef MULTIPLE_THREADS

#define ACQUIRE_DTOA_LOCK(n)    /*nothing*/

#define FREE_DTOA_LOCK(n)   /*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 */

};



typedef struct Bigint Bigint;



static Bigint *freelist[Kmax+1];



/* Allocate a Bigint with 2^k words. */

static Bigint *Balloc(int32 k)

{

    int32 x;

    Bigint *rv;

#ifndef Omit_Private_Memory

    uint32 len;

#endif



    ACQUIRE_DTOA_LOCK(0);

    if ((rv = freelist[k]) != NULL)

        freelist[k] = rv->next;

    FREE_DTOA_LOCK(0);

    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

        rv->k = k;

        rv->maxwds = x;

    }

    rv->sign = rv->wds = 0;

    return rv;

}



static void Bfree(Bigint *v)

{

    if (v) {

        ACQUIRE_DTOA_LOCK(0);

        v->next = freelist[v->k];

        freelist[v->k] = v;

        FREE_DTOA_LOCK(0);

    }

}



#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. */

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;



    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);

            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);

    b->x[0] = y9;

    b->wds = 1;



    i = 9;

    if (9 < nd0) {

        s += 9;

        do b = multadd(b, 10, *s++ - '0');

        while(++i < nd0);

        s++;

    }

    else

        s += 10;

    for(; i < nd; i++)

        b = multadd(b, 10, *s++ - '0');

    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);

    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);

    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. */

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 (!(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);

        /* 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);

        p5->next = 0;

#endif

    }

    for(;;) {

        if (k & 1) {

            b1 = mult(b, p5);

            Bfree(b);

            b = b1;

        }

        if (!(k >>= 1))

            break;

        if (!(p51 = p5->next)) {

#ifdef JS_THREADSAFE

            Bigint *wasted_effort = NULL;

            p51 = mult(p5, p5);

            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 = p5->next = mult(p5,p5);

            p51->next = 0;

#endif

        }

        p5 = p51;

    }

    return b;

}



/* Return b * 2^k.  Deallocate the old b.  k must be nonnegative. */

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);

    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;

    Bfree(b);

    return b1;

}



/* Return -1, 0, or 1 depending on whether a<b, a==b, or a>b, 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);

        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);

    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;



    L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1;

#ifndef Sudden_Underflow

    if (L > 0) {

#endif

        word0(a) = L;

        word1(a) = 0;

#ifndef Sudden_Underflow

    }

    else {

        L = -L >> Exp_shift;

        if (L < Exp_shift) {

            word0(a) = 0x80000 >> L;

            word1(a) = 0;

        }

        else {

            word0(a) = 0;

            L -= Exp_shift;

            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;

#define d0 word0(d)

#define d1 word1(d)



    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) {

        d0 = Exp_1 | y >> (Ebits - k);

        w = xa > xa0 ? *--xa : 0;

        d1 = y << (32-Ebits + k) | w >> (Ebits - k);

        goto ret_d;

    }

    z = xa > xa0 ? *--xa : 0;

    if (k -= Ebits) {

        d0 = Exp_1 | y << k | z >> (32 - k);

        y = xa > xa0 ? *--xa : 0;

        d1 = z << k | y >> (32 - k);

    }

    else {

        d0 = Exp_1 | y;

        d1 = z;

    }

  ret_d:

#undef d0

#undef 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)



    b = Balloc(1);

    x = b->x;



    z = d0 & Frac_mask;

    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





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)

        word0(da) += k*Exp_msk1;

    else {

        k = -k;

        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)

{

#ifdef JS_THREADSAFE

    if (initialized == JS_TRUE) 

    {

        PR_DestroyLock(freelist_lock);

        PR_DestroyLock(p5s_lock);

        initialized = JS_FALSE;

    }

#endif

}



/* nspr2 watcom bug ifdef omitted */



JS_FRIEND_API(double)

JS_strtod(CONST char *s00, char **se)

{

    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;



#ifdef JS_THREADSAFE

    if (!initialized) InitDtoa();

#endif



	bb = bd = bs = delta = NULL;

    sign = nz0 = nz = 0;

    rv = 0.;

    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:

                errno = 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. */

            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) */

                word0(rv) = Big0;

                word1(rv) = Big1;

                }

            else

                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) {

                    word1(rv) = 0;

                    word0(rv) &= 0xffffffff << (j-32);

                    if (!word0(rv))

                        word0(rv) = 1;

                    }

                else

                    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.;

                    errno = ERANGE;

                    if (bd0)

                        goto retfree;

                    goto ret;

                }

#ifndef Avoid_Underflow

                word0(rv) = Tiny0;

                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);



    for(;;) {

        bd = Balloc(bd0->k);

        Bcopy(bd, bd0);

        bb = d2b(rv, &bbe, &bbbits);    /* rv = bb * 2^bbe */

        bs = i2b(1);



        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);

            bb1 = mult(bs, bb);

            Bfree(bb);

            bb = bb1;

        }

        if (bb2 > 0)

            bb = lshift(bb, bb2);

        if (bd5 > 0)

            bd = pow5mult(bd, bd5);

        if (bd2 > 0)

            bd = lshift(bd, bd2);

        if (bs2 > 0)

            bs = lshift(bs, bs2);

        delta = diff(bb, bd);

        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 (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*/

                    word0(rv) = (word0(rv) & Exp_mask) + Exp_msk1;

                    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

                word0(rv) = L | Bndry_mask1;

                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;

            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;

                word0(rv) = Big0;

                word1(rv) = Big1;

                goto cont;

            }

            else

                word0(rv) += P*Exp_msk1;

        }

        else {

#ifdef Sudden_Underflow

            if ((word0(rv) & Exp_mask) <= P*Exp_msk1) {

                rv0 = 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;

                            word0(rv) = Tiny0;

                            word1(rv) = Tiny1;

                            goto cont;

                        }

                    else

                        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)

                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);

    }

#ifdef Avoid_Underflow

    if (scale) {

        word0(rv0) = Exp_1 - P*Exp_msk1;

        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. */

                word0(rv) += P*Exp_msk1;

                word0(rv0) = Exp_1 - 2*P*Exp_msk1;

#endif

                rv += ulp(rv);

                }

            else

                word1(rv) &= ~1;

        }

        rv *= rv0;

    }

#endif /* Avoid_Underflow */

retfree:

    Bfree(bb);

    Bfree(bd);

    Bfree(bs);

    Bfree(bd0);

    Bfree(delta);

ret:

    if (se)

        *se = (char *)s;

    return sign ? -rv : rv;

}





/* 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<<k) - 1;



    w = b->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;



#ifdef JS_THREADSAFE

    if (!initialized) InitDtoa();

#endif



    if (word0(d) & Sign_bit) {

        /* set sign for everything, including 0's and NaNs */

        *sign = 1;

        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;

    }

    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;

        return JS_TRUE;

    }



    b = d2b(d, &be, &bbits);

#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;

        word0(d2) &= Frac_mask1;

        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;

        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.;

        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;

    mhi = mlo = 0;

    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);

        /* (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);

                b1 = mult(mhi, b);

                Bfree(b);

                b = b1;

            }

            if ((j = b5 - m5) != 0)

                b = pow5mult(b, j);

        }

        else

            b = pow5mult(b, b5);

    }

    /* 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 (s5 > 0)

        S = pow5mult(S, s5);

    /* 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 (s2 > 0)

        S = lshift(S, s2);

    /* 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 (leftright)

                mhi = multadd(mhi, 10, 0);

            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 || (i = cmp(b,S = multadd(S,5,0))) < 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);



        /* Compute mlo -- check for special case

         * that d is a normalized power of 2.

         */



        mlo = mhi;

        if (spec_case) {

            mhi = Balloc(mhi->k);

            Bcopy(mhi, mlo);

            mhi = lshift(mhi, Log2P);

        }

        /* 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);

            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);

                    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 (mlo == mhi)

                mlo = mhi = multadd(mhi, 10, 0);

            else {

                mlo = multadd(mlo, 10, 0);

                mhi = multadd(mhi, 10, 0);

            }

        }

    }

    else

        for(i = 1;; i++) {

            *s++ = (char)(dig = quorem(b,S) + '0');

            if (i >= ilim)

                break;

            b = multadd(b, 10, 0);

        }



    /* Round off last digit */



    b = lshift(b, 1);

    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;

}





/* 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 */



    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. */



    if (!JS_dtoa(d, dtoaModes[mode], mode >= DTOSTR_FIXED, precision, &decPt, &sign, &numEnd, numBegin, bufferSize-2))

        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

#ifdef XP_PC

            && !((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;

        }



        /* 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 {

            /* XXX We really should check for null here, but none of the routines we call is out-of-memory-safe,

             * so this change would need to be made pervasively in this file. */

            int32 e;

            int32 bits;  /* Number of significant bits in di; not used. */

            Bigint *b = d2b(di, &e, &bits);

            b = lshift(b, e);

            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;

            

            *p++ = '.';

            b = d2b(df, &e, &bbits);

            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);

            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<<Log2P);

            }

            b = lshift(b, e + s2);

            s = i2b(1);

            s = lshift(s, s2);

            /* At this point we have the following:

             *   s = 2^s2;

             *   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);

                digit = quorem2(b, s2);

                if (mlo == mhi)

                    mlo = mhi = multadd(mlo, base, 0);

                else {

                    mlo = multadd(mlo, base, 0);

                    mhi = multadd(mhi, base, 0);

                }



                /* 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);

                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);

                        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';

    }

    return buffer;

}

 

**** End of jsdtoa.c ****

 

**** Start of jsdtoa.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsdtoa_h___

#define jsdtoa_h___

/*

 * Public interface to portable double-precision floating point to string

 * and back conversion package.

 */



#include "jscompat.h"



JS_BEGIN_EXTERN_C



/*

 * JS_strtod() returns as a double-precision floating-point number

 * the  value represented by the character string pointed to by

 * s00.  The string is scanned up to  the  first  unrecognized

 * character.

 * If the value of se is not (char **)NULL,  a  pointer  to

 * the  character terminating the scan is returned in the location pointed

 * to by se.  If no number can be  formed, se is set to s00r, and

 * zero is returned.

 */

JS_FRIEND_API(double)

JS_strtod(const char *s00, char **se);



/*

 * Modes for converting floating-point numbers to strings.

 *

 * Some of the modes can round-trip; this means that if the number is converted to

 * a string using one of these mode and then converted back to a number, the result

 * will be identical to the original number (except that, due to ECMA, -0 will get converted

 * to +0).  These round-trip modes return the minimum number of significand digits that

 * permit the round trip.

 *

 * Some of the modes take an integer parameter <precision>.

 */

/* NB: Keep this in sync with number_constants[]. */

typedef enum JSDToStrMode {

    DTOSTR_STANDARD,              /* Either fixed or exponential format; round-trip */

    DTOSTR_STANDARD_EXPONENTIAL,  /* Always exponential format; round-trip */

    DTOSTR_FIXED,                 /* Round to <precision> digits after the decimal point; exponential if number is large */

    DTOSTR_EXPONENTIAL,           /* Always exponential format; <precision> significant digits */

    DTOSTR_PRECISION              /* Either fixed or exponential format; <precision> significant digits */

} JSDToStrMode;





/* Maximum number of characters (including trailing null) that a DTOSTR_STANDARD or DTOSTR_STANDARD_EXPONENTIAL

 * conversion can produce.  This maximum is reached for a number like -1.2345678901234567e+123. */

#define DTOSTR_STANDARD_BUFFER_SIZE 25



/* Maximum number of characters (including trailing null) that one of the other conversions

 * can produce.  This maximum is reached for TO_FIXED, which can generate up to 21 digits before the decimal point. */

#define DTOSTR_VARIABLE_BUFFER_SIZE(precision) ((precision)+24 > DTOSTR_STANDARD_BUFFER_SIZE ? (precision)+24 : DTOSTR_STANDARD_BUFFER_SIZE)



/*

 * Convert dval according to the given mode and return a pointer to the resulting ASCII string.

 * The result is held somewhere in buffer, but not necessarily at the beginning.  The size of

 * buffer is given in bufferSize, and must be at least as large as given by the above macros.

 *

 * Return NULL if out of memory.

 */

JS_FRIEND_API(char *)

JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double dval);



/*

 * Convert d to a string in the given base.  The integral part of d will be printed exactly

 * in that base, regardless of how large it is, because there is no exponential notation for non-base-ten

 * numbers.  The fractional part will be rounded to as few digits as possible while still preserving

 * the round-trip property (analogous to that of printing decimal numbers).  In other words, if one were

 * to read the resulting string in via a hypothetical base-number-reading routine that rounds to the nearest

 * IEEE double (and to an even significand if there are two equally near doubles), then the result would

 * equal d (except for -0.0, which converts to "0", and NaN, which is not equal to itself).

 *

 * Return NULL if out of memory.  If the result is not NULL, it must be released via free().

 */

JS_FRIEND_API(char *)

JS_dtobasestr(int base, double d);



JS_END_EXTERN_C





// DREAMWEAVER snewman 3/28/01: added declaration to avoid "no prototype" warning

extern void js_FinishDtoa(void);



#endif /* jsdtoa_h___ */

 

**** End of jsdtoa.h ****

 

**** Start of jsemit.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS bytecode generation.

 */

#include "jsstddef.h"

#ifdef HAVE_MEMORY_H

#include <memory.h>

#endif

#include <string.h>

#include "jstypes.h"

#include "jsarena.h" /* Added by JSIFY */

#include "jsutil.h" /* Added by JSIFY */

#include "jsbit.h"

#include "jsprf.h"

#include "jsapi.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsemit.h"

#include "jsfun.h"

#include "jsnum.h"

#include "jsopcode.h"

#include "jsparse.h"

#include "jsscan.h"

#include "jsscope.h"

#include "jsscript.h"



/* Allocation grain counts, must be powers of two in general. */

#define BYTECODE_GRAIN  256     /* code allocation increment */

#define SRCNOTE_GRAIN   64      /* initial srcnote allocation increment */

#define TRYNOTE_GRAIN   64      /* trynote allocation increment */



/* Macros to compute byte sizes from typed element counts. */

#define BYTECODE_SIZE(n)        ((n) * sizeof(jsbytecode))

#define SRCNOTE_SIZE(n)         ((n) * sizeof(jssrcnote))

#define TRYNOTE_SIZE(n)         ((n) * sizeof(JSTryNote))



JS_FRIEND_API(JSBool)

js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg,

                     const char *filename, uintN lineno,

                     JSPrincipals *principals)

{

    memset(cg, 0, sizeof *cg);

    TREE_CONTEXT_INIT(&cg->treeContext);

    cg->treeContext.flags |= TCF_COMPILING;

    cg->codeMark = JS_ARENA_MARK(&cx->codePool);

    cg->noteMark = JS_ARENA_MARK(&cx->notePool);

    cg->tempMark = JS_ARENA_MARK(&cx->tempPool);

    cg->current = &cg->main;

    cg->filename = filename;

    cg->firstLine = cg->currentLine = lineno;

    cg->principals = principals;

    ATOM_LIST_INIT(&cg->atomList);

    cg->noteMask = SRCNOTE_GRAIN - 1;

    return JS_TRUE;

}



JS_FRIEND_API(void)

js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg)

{

    TREE_CONTEXT_FINISH(&cg->treeContext);

    JS_ARENA_RELEASE(&cx->codePool, cg->codeMark);

    JS_ARENA_RELEASE(&cx->notePool, cg->noteMark);

    JS_ARENA_RELEASE(&cx->tempPool, cg->tempMark);

}



static ptrdiff_t

EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta)

{

    jsbytecode *base, *limit, *next;

    ptrdiff_t offset, length;

    size_t incr, size;



    base = CG_BASE(cg);

    next = CG_NEXT(cg);

    limit = CG_LIMIT(cg);

    offset = PTRDIFF(next, base, jsbytecode);

    if ((jsuword)(next + delta) > (jsuword)limit) {

        length = offset + delta;

        length = (length <= BYTECODE_GRAIN)

                 ? BYTECODE_GRAIN

                 : JS_BIT(JS_CeilingLog2(length));

        incr = BYTECODE_SIZE(length);

        if (!base) {

            JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, &cx->codePool, incr);

        } else {

            size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode));

            incr -= size;

            JS_ARENA_GROW_CAST(base, jsbytecode *, &cx->codePool, size, incr);

        }

        if (!base) {

            JS_ReportOutOfMemory(cx);

            return -1;

        }

        CG_BASE(cg) = base;

        CG_LIMIT(cg) = base + length;

        CG_NEXT(cg) = base + offset;

    }

    return offset;

}



static void

UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target)

{

    jsbytecode *pc;

    JSCodeSpec *cs;

    intN nuses;



    pc = CG_CODE(cg, target);

    cs = &js_CodeSpec[pc[0]];

    nuses = cs->nuses;

    if (nuses < 0)

        nuses = 2 + GET_ARGC(pc);       /* stack: fun, this, [argc arguments] */

    cg->stackDepth -= nuses;

    if (cg->stackDepth < 0) {

        char numBuf[12];

        JS_snprintf(numBuf, sizeof numBuf, "%d", target);

        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                             JSMSG_STACK_UNDERFLOW,

                             cg->filename ? cg->filename : "stdin", numBuf);

    }

    cg->stackDepth += cs->ndefs;

    if ((uintN)cg->stackDepth > cg->maxStackDepth)

        cg->maxStackDepth = cg->stackDepth;

}



ptrdiff_t

js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op)

{

    ptrdiff_t offset = EmitCheck(cx, cg, op, 1);



    if (offset >= 0) {

        *CG_NEXT(cg)++ = (jsbytecode)op;

        UpdateDepth(cx, cg, offset);

    }

    return offset;

}



ptrdiff_t

js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1)

{

    ptrdiff_t offset = EmitCheck(cx, cg, op, 2);



    if (offset >= 0) {

        jsbytecode *next = CG_NEXT(cg);

        next[0] = (jsbytecode)op;

        next[1] = op1;

        CG_NEXT(cg) = next + 2;

        UpdateDepth(cx, cg, offset);

    }

    return offset;

}



ptrdiff_t

js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1,

         jsbytecode op2)

{

    ptrdiff_t offset = EmitCheck(cx, cg, op, 3);



    if (offset >= 0) {

        jsbytecode *next = CG_NEXT(cg);

        next[0] = (jsbytecode)op;

        next[1] = op1;

        next[2] = op2;

        CG_NEXT(cg) = next + 3;

        UpdateDepth(cx, cg, offset);

    }

    return offset;

}



ptrdiff_t

js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra)

{

    ptrdiff_t length = 1 + (ptrdiff_t)extra;

    ptrdiff_t offset = EmitCheck(cx, cg, op, length);



    if (offset >= 0) {

        jsbytecode *next = CG_NEXT(cg);

        *next = (jsbytecode)op;

        CG_NEXT(cg) = next + length;

        UpdateDepth(cx, cg, offset);

    }

    return offset;

}



/* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */

const char js_with_statement_str[] = "with statement";



static const char *statementName[] = {

    "block",                 /* BLOCK */

    "label statement",       /* LABEL */

    "if statement",          /* IF */

    "else statement",        /* ELSE */

    "switch statement",      /* SWITCH */

    js_with_statement_str,   /* WITH */

    "try statement",         /* TRY */

    "catch block",           /* CATCH */

    "finally statement",     /* FINALLY */

    "do loop",               /* DO_LOOP */

    "for loop",              /* FOR_LOOP */

    "for/in loop",           /* FOR_IN_LOOP */

    "while loop",            /* WHILE_LOOP */

};



static const char *

StatementName(JSCodeGenerator *cg)

{

    if (!cg->treeContext.topStmt)

        return "script";

    return statementName[cg->treeContext.topStmt->type];

}



static void

ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg)

{

    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET,

                         StatementName(cg));

}



JSBool

js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc,

                 ptrdiff_t off)

{

    if (off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off) {

        ReportStatementTooLarge(cx, cg);

        return JS_FALSE;

    }

    SET_JUMP_OFFSET(pc, off);

    return JS_TRUE;

}



JSBool

js_InWithStatement(JSTreeContext *tc)

{

    JSStmtInfo *stmt;



    for (stmt = tc->topStmt; stmt; stmt = stmt->down) {

        if (stmt->type == STMT_WITH)

            return JS_TRUE;

    }

    return JS_FALSE;

}



JSBool

js_InCatchBlock(JSTreeContext *tc, JSAtom *atom)

{

    JSStmtInfo *stmt;



    for (stmt = tc->topStmt; stmt; stmt = stmt->down) {

        if (stmt->type == STMT_CATCH && stmt->label == atom)

            return JS_TRUE;

    }

    return JS_FALSE;

}



void

js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,

                 ptrdiff_t top)

{

    stmt->type = type;

    SET_STATEMENT_TOP(stmt, top);

    stmt->label = NULL;

    stmt->down = tc->topStmt;

    tc->topStmt = stmt;

}



/*

 * Emit a jump op with offset pointing to the previous jump of this type,

 * so that we can walk back up the chain fixing up the final destination.

 */

#define EMIT_CHAINED_JUMP(cx, cg, last, op, jmp)                              \

    JS_BEGIN_MACRO                                                            \

        ptrdiff_t offset, delta;                                              \

        offset = CG_OFFSET(cg);                                               \

        delta = offset - (last);                                              \

        last = offset;                                                        \

        jmp = js_Emit3((cx), (cg), (op), JUMP_OFFSET_HI(delta),               \

                       JUMP_OFFSET_LO(delta));                                \

    JS_END_MACRO



/* Emit additional bytecode(s) for non-local jumps. */

static JSBool

EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,

                      JSBool preserveTop)

{

    JSStmtInfo *stmt;

    ptrdiff_t jmp;



    /*

     * If we're here as part of processing a return, emit JSOP_SWAP to preserve

     * the top element.

     */

    for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) {

        switch (stmt->type) {

          case STMT_FINALLY:

            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)

                return JS_FALSE;

            EMIT_CHAINED_JUMP(cx, cg, stmt->gosub, JSOP_GOSUB, jmp);

            if (jmp < 0)

                return JS_FALSE;

            break;

          case STMT_WITH:

          case STMT_CATCH:

            if (preserveTop) {

                if (js_Emit1(cx, cg, JSOP_SWAP) < 0)

                    return JS_FALSE;

            }

            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)

                return JS_FALSE;

            cg->stackDepth++;

            if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)

                return JS_FALSE;

            break;

          case STMT_FOR_IN_LOOP:

            cg->stackDepth += 2;

            if (preserveTop) {

                if (js_Emit1(cx, cg, JSOP_SWAP) < 0 ||

                    js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||

                    js_Emit1(cx, cg, JSOP_POP) < 0 ||

                    js_Emit1(cx, cg, JSOP_SWAP) < 0 ||

                    js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||

                    js_Emit1(cx, cg, JSOP_POP) < 0) {

                    return JS_FALSE;

                }

            } else {

                /* JSOP_POP2 isn't decompiled, and doesn't need a src note. */

                if (js_Emit1(cx, cg, JSOP_POP2) < 0)

                    return JS_FALSE;

            }

            break;

          default:;

        }

    }



    return JS_TRUE;

}



static ptrdiff_t

EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,

         ptrdiff_t *last, JSAtomListElement *label, JSSrcNoteType noteType)

{

    intN index;

    ptrdiff_t jmp;



    if (!EmitNonLocalJumpFixup(cx, cg, toStmt, JS_FALSE))

        return -1;



    if (label) {

        index = js_NewSrcNote(cx, cg, noteType);

        if (index < 0)

            return -1;

        if (!js_SetSrcNoteOffset(cx, cg, (uintN)index, 0,

                                 (ptrdiff_t) ALE_INDEX(label))) {

            return -1;

        }

    }



    EMIT_CHAINED_JUMP(cx, cg, *last, JSOP_GOTO, jmp);

    return jmp;

}



static JSBool

PatchGotos(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt,

           ptrdiff_t last, jsbytecode *target, jsbytecode op)

{

    jsbytecode *pc, *top;

    ptrdiff_t delta, jumpOffset;



    pc = CG_CODE(cg, last);

    top = CG_CODE(cg, stmt->top);

    while (pc != CG_CODE(cg, -1)) {

        JS_ASSERT(*pc == op);

        delta = GET_JUMP_OFFSET(pc);

        jumpOffset = PTRDIFF(target, pc, jsbytecode);

        CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, jumpOffset);

        pc -= delta;

    }

    return JS_TRUE;

}



ptrdiff_t

js_EmitBreak(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt,

             JSAtomListElement *label)

{

    return EmitGoto(cx, cg, stmt, &stmt->breaks, label, SRC_BREAK2LABEL);

}



ptrdiff_t

js_EmitContinue(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt,

                JSAtomListElement *label)

{

    return EmitGoto(cx, cg, stmt, &stmt->continues, label, SRC_CONT2LABEL);

}



extern void

js_PopStatement(JSTreeContext *tc)

{

    tc->topStmt = tc->topStmt->down;

}



JSBool

js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg)

{

    JSStmtInfo *stmt;



    stmt = cg->treeContext.topStmt;

    if (!PatchGotos(cx, cg, stmt, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) ||

        !PatchGotos(cx, cg, stmt, stmt->continues, CG_CODE(cg, stmt->update),

                    JSOP_GOTO)) {

        return JS_FALSE;

    }

    js_PopStatement(&cg->treeContext);

    return JS_TRUE;

}



/*

 * Emit a bytecode and its 2-byte constant (atom) index immediate operand.

 * NB: We use cx and cg from our caller's lexical environment, and return

 * false on error.

 */

#define EMIT_ATOM_INDEX_OP(op, atomIndex)                                     \

    JS_BEGIN_MACRO                                                            \

        if (js_Emit3(cx, cg, op, ATOM_INDEX_HI(atomIndex),                    \

                                 ATOM_INDEX_LO(atomIndex)) < 0) {             \

            return JS_FALSE;                                                  \

        }                                                                     \

    JS_END_MACRO



static JSBool

EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)

{

    JSAtomListElement *ale;



    ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);

    if (!ale)

        return JS_FALSE;

    EMIT_ATOM_INDEX_OP(op, ALE_INDEX(ale));

    return JS_TRUE;

}



/*

 * This routine tries to optimize name gets and sets to stack slot loads and

 * stores, given the variables object and scope chain in cx's top frame, the

 * compile-time context in tc, and a TOK_NAME node pn.  It returns false on

 * error, true on success.

 *

 * The caller can inspect pn->pn_slot for a non-negative slot number to tell

 * whether optimization occurred, in which case LookupArgOrVar also updated

 * pn->pn_op.  If pn->pn_slot is still -1 on return, pn->pn_op nevertheless

 * may have been optimized, e.g., from JSOP_NAME to JSOP_ARGUMENTS.  Whether

 * or not pn->pn_op was modified, if this function finds an argument or local

 * variable name, pn->pn_attrs will contain the property's attributes after a

 * successful return.

 */

static JSBool

LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)

{

    JSObject *obj, *pobj;

    JSClass *clasp;

    JSAtom *atom;

    JSScopeProperty *sprop;

    JSOp op;



    JS_ASSERT(pn->pn_type == TOK_NAME);

    if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS)

        return JS_TRUE;



    /*

     * We can't optimize if var and closure (a local function not in a larger

     * expression and not at top-level within another's body) collide.

     * XXX suboptimal: keep track of colliding names and deoptimize only those

     */

    if (tc->flags & TCF_FUN_CLOSURE_VS_VAR)

        return JS_TRUE;



    /*

     * We can't optimize if we're not compiling a function body, whether via

     * eval, or directly when compiling a function statement or expression.

     */

    obj = cx->fp->varobj;

    clasp = OBJ_GET_CLASS(cx, obj);

    if (clasp != &js_FunctionClass && clasp != &js_CallClass)

        return JS_TRUE;



    /*

     * We can't optimize if we're in an eval called inside a with statement,

     * or we're compiling a with statement and its body, or we're in a catch

     * block whose exception variable has the same name as pn.

     */

    atom = pn->pn_atom;

    if (cx->fp->scopeChain != obj ||

        js_InWithStatement(tc) ||

        js_InCatchBlock(tc, atom)) {

        return JS_TRUE;

    }



    /*

     * Ok, we may be able to optimize name to stack slot.  We must check for

     * the predefined arguments variable.  It may be overridden by assignment,

     * in which case the function is heavyweight and the interpreter will look

     * up 'arguments' in the function's call object.

     */

    if (pn->pn_op == JSOP_NAME &&

        atom == cx->runtime->atomState.argumentsAtom) {

        pn->pn_op = JSOP_ARGUMENTS;

        return JS_TRUE;

    }



    /*

     * Look for an argument or variable property in the function, or its call

     * object, not found in any prototype object.  Rewrite pn_op and update pn

     * accordingly.  NB: We know that JSOP_DELNAME on an argument or variable

     * must evaluate to false, due to JSPROP_PERMANENT.

     */

    if (!js_LookupProperty(cx, obj, (jsid)atom, &pobj, (JSProperty **)&sprop))

        return JS_FALSE;

    op = pn->pn_op;

    if (sprop) {

        if (pobj == obj) {

            JSPropertyOp getter = SPROP_GETTER(sprop, pobj);



            if (getter == js_GetArgument) {

                switch (op) {

                  case JSOP_NAME:     op = JSOP_GETARG; break;

                  case JSOP_SETNAME:  op = JSOP_SETARG; break;

                  case JSOP_INCNAME:  op = JSOP_INCARG; break;

                  case JSOP_NAMEINC:  op = JSOP_ARGINC; break;

                  case JSOP_DECNAME:  op = JSOP_DECARG; break;

                  case JSOP_NAMEDEC:  op = JSOP_ARGDEC; break;

                  case JSOP_FORNAME:  op = JSOP_FORARG; break;

                  case JSOP_DELNAME:  op = JSOP_FALSE; break;

                  default: JS_ASSERT(0);

                }

            } else if (getter == js_GetLocalVariable ||

                       getter == js_GetCallVariable)

            {

                switch (op) {

                  case JSOP_NAME:     op = JSOP_GETVAR; break;

                  case JSOP_SETNAME:  op = JSOP_SETVAR; break;

                  case JSOP_SETCONST: op = JSOP_SETVAR; break;

                  case JSOP_INCNAME:  op = JSOP_INCVAR; break;

                  case JSOP_NAMEINC:  op = JSOP_VARINC; break;

                  case JSOP_DECNAME:  op = JSOP_DECVAR; break;

                  case JSOP_NAMEDEC:  op = JSOP_VARDEC; break;

                  case JSOP_FORNAME:  op = JSOP_FORVAR; break;

                  case JSOP_DELNAME:  op = JSOP_FALSE; break;

                  default: JS_ASSERT(0);

                }

            }

            if (op != pn->pn_op) {

                pn->pn_op = op;

                pn->pn_slot = JSVAL_TO_INT(sprop->id);

            }

            pn->pn_attrs = sprop->attrs;

        }

        OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);

    }



    if (pn->pn_slot < 0) {

        /* We couldn't optimize it, so it's not an arg or local var name. */

        tc->flags |= TCF_FUN_USES_NONLOCALS;

    }

    return JS_TRUE;

}



/*

 * If pn contains a useful expression, return true with *answer set to true.

 * If pn contains a useless expression, return true with *answer set to false.

 * Return false on error.

 *

 * The caller should initialize *answer to false and invoke this function on

 * an expression statement or similar subtree to decide whether the tree could

 * produce code that has any side effects.  For an expression statement, we

 * define useless code as code with no side effects, because the main effect,

 * the value left on the stack after the code executes, will be discarded by a

 * pop bytecode.

 */

static JSBool

CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,

                 JSBool *answer)

{

    JSBool ok;

    JSParseNode *pn2;



    ok = JS_TRUE;

    if (!pn || *answer)

        return ok;



    switch (pn->pn_arity) {

      case PN_FUNC:

        /*

         * A named function is presumed useful: we can't yet know that it is

         * not called.  The side effects are the creation of a scope object

         * to parent this function object, and the binding of the function's

         * name in that scope object.  See comments at case JSOP_NAMEDFUNOBJ:

         * in jsinterp.c.

         */

        if (pn->pn_fun->atom)

            *answer = JS_TRUE;

        break;



      case PN_LIST:

        if (pn->pn_type == TOK_NEW || pn->pn_type == TOK_LP) {

            /*

             * All invocation operations (construct, call) are presumed to be

             * useful, because they may have side effects even if their main

             * effect (their return value) is discarded.

             */

            *answer = JS_TRUE;

        } else {

            for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)

                ok &= CheckSideEffects(cx, tc, pn2, answer);

        }

        break;



      case PN_TERNARY:

        ok = CheckSideEffects(cx, tc, pn->pn_kid1, answer) &&

             CheckSideEffects(cx, tc, pn->pn_kid2, answer) &&

             CheckSideEffects(cx, tc, pn->pn_kid3, answer);

        break;



      case PN_BINARY:

        if (pn->pn_type == TOK_ASSIGN) {

            /*

             * Assignment is presumed to be useful, even if the next operation

             * is another assignment overwriting this one's ostensible effect,

             * because the left operand may be a property with a setter that

             * has side effects.

             */

            *answer = JS_TRUE;

        } else {

            if (pn->pn_type == TOK_LB) {

                pn2 = pn->pn_left;

                if (pn2->pn_type == TOK_NAME && !LookupArgOrVar(cx, tc, pn2))

                    return JS_FALSE;

                if (pn2->pn_op != JSOP_ARGUMENTS) {

                    /*

                     * Any indexed property reference could call a getter with

                     * side effects, except for arguments[i] where arguments is

                     * unambiguous.

                     */

                    *answer = JS_TRUE;

                }

            }

            ok = CheckSideEffects(cx, tc, pn->pn_left, answer) &&

                 CheckSideEffects(cx, tc, pn->pn_right, answer);

        }

        break;



      case PN_UNARY:

        if (pn->pn_type == TOK_INC || pn->pn_type == TOK_DEC ||

            pn->pn_type == TOK_DELETE ||

            pn->pn_type == TOK_THROW ||

            pn->pn_type == TOK_DEFSHARP) {

            /* All these operations have effects that we must commit. */

            *answer = JS_TRUE;

        } else {

            ok = CheckSideEffects(cx, tc, pn->pn_kid, answer);

        }

        break;



      case PN_NAME:

        if (pn->pn_type == TOK_NAME) {

            if (!LookupArgOrVar(cx, tc, pn))

                return JS_FALSE;

            if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) {

                /*

                 * Not an argument or local variable use, so this expression

                 * could invoke a getter that has side effects.

                 */

                *answer = JS_TRUE;

            }

        }

        pn2 = pn->pn_expr;

        if (pn->pn_type == TOK_DOT && pn2->pn_type == TOK_NAME) {

            if (!LookupArgOrVar(cx, tc, pn2))

                return JS_FALSE;

            if (!(pn2->pn_op == JSOP_ARGUMENTS &&

                  pn->pn_atom == cx->runtime->atomState.lengthAtom)) {

                /*

                 * Any dotted property reference could call a getter, except

                 * for arguments.length where arguments is unambiguous.

                 */

                *answer = JS_TRUE;

            }

        }

        ok = CheckSideEffects(cx, tc, pn2, answer);

        break;



      case PN_NULLARY:

        if (pn->pn_type == TOK_DEBUGGER)

            *answer = JS_TRUE;

        break;

    }

    return ok;

}



static JSBool

EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)

{

    JSParseNode *pn2;

    JSAtomListElement *ale;



    pn2 = pn->pn_expr;

    if (pn->pn_type == TOK_DOT && pn2->pn_type == TOK_NAME) {

        if (!LookupArgOrVar(cx, &cg->treeContext, pn2))

            return JS_FALSE;

        if (pn2->pn_op == JSOP_ARGUMENTS &&

            pn->pn_atom == cx->runtime->atomState.lengthAtom) {

            return js_Emit1(cx, cg, JSOP_ARGCNT) >= 0;

        }

    }

    if (!js_EmitTree(cx, cg, pn2))

        return JS_FALSE;

    if (js_NewSrcNote2(cx, cg, SRC_PCBASE,

                       (ptrdiff_t)(CG_OFFSET(cg) - pn2->pn_offset)) < 0) {

        return JS_FALSE;

    }

    if (!pn->pn_atom) {

        JS_ASSERT(op == JSOP_IMPORTALL);

        if (js_Emit1(cx, cg, op) < 0)

            return JS_FALSE;

    } else {

        ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);

        if (!ale)

            return JS_FALSE;

        EMIT_ATOM_INDEX_OP(op, ALE_INDEX(ale));

    }

    return JS_TRUE;

}



static JSBool

EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)

{

    JSParseNode *left, *right;

    jsint slot;



    left = pn->pn_left;

    right = pn->pn_right;

    if (left->pn_type == TOK_NAME && right->pn_type == TOK_NUMBER) {

        if (!LookupArgOrVar(cx, &cg->treeContext, left))

            return JS_FALSE;

        if (left->pn_op == JSOP_ARGUMENTS &&

            JSDOUBLE_IS_INT(right->pn_dval, slot) &&

            slot >= 0) {

            EMIT_ATOM_INDEX_OP(JSOP_ARGSUB, (jsatomid)slot);

            return JS_TRUE;

        }

    }

    if (!js_EmitTree(cx, cg, left) || !js_EmitTree(cx, cg, right))

        return JS_FALSE;

    if (js_NewSrcNote2(cx, cg, SRC_PCBASE,

                       (ptrdiff_t)(CG_OFFSET(cg) - left->pn_offset)) < 0) {

        return JS_FALSE;

    }

    return js_Emit1(cx, cg, op) >= 0;

}



static JSBool

EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg)

{

    jsint ival;

    jsatomid atomIndex;

    JSAtom *atom;

    JSAtomListElement *ale;



    if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) {

        if (ival == 0)

            return js_Emit1(cx, cg, JSOP_ZERO) >= 0;

        if (ival == 1)

            return js_Emit1(cx, cg, JSOP_ONE) >= 0;

        if ((jsuint)ival < (jsuint)ATOM_INDEX_LIMIT) {

            atomIndex = (jsatomid)ival;

            EMIT_ATOM_INDEX_OP(JSOP_UINT16, atomIndex);

            return JS_TRUE;

        }

        atom = js_AtomizeInt(cx, ival, 0);

    } else {

        atom = js_AtomizeDouble(cx, dval, 0);

    }

    if (!atom)

        return JS_FALSE;

    ale = js_IndexAtom(cx, atom, &cg->atomList);

    if (!ale)

        return JS_FALSE;

    EMIT_ATOM_INDEX_OP(JSOP_NUMBER, ALE_INDEX(ale));

    return JS_TRUE;

}



JSBool

js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body,

                    JSFunction *fun)

{

    JSStackFrame *fp, frame;

    JSObject *funobj;

    JSBool ok;



    if (!js_AllocTryNotes(cx, cg))

        return JS_FALSE;



    fp = cx->fp;

    funobj = fun->object;

    if (!fp || fp->fun != fun || fp->varobj != funobj ||

        fp->scopeChain != funobj) {

        memset(&frame, 0, sizeof frame);

        frame.fun = fun;

        frame.varobj = frame.scopeChain = funobj;

        frame.down = fp;

        cx->fp = &frame;

    }

    ok = js_EmitTree(cx, cg, body);

    cx->fp = fp;

    if (!ok)

        return JS_FALSE;



    fun->script = js_NewScriptFromCG(cx, cg, fun);

    if (!fun->script)

        return JS_FALSE;

    if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)

        fun->flags |= JSFUN_HEAVYWEIGHT;

    return JS_TRUE;

}



/* A macro for inlining at the top of js_EmitTree (whence it came). */

#define UPDATE_LINENO_NOTES(cx, cg, pn)                                       \

    JS_BEGIN_MACRO                                                            \

        uintN _line = (pn)->pn_pos.begin.lineno;                              \

        uintN _delta = _line - (cg)->currentLine;                             \

        (cg)->currentLine = _line;                                            \

        if (_delta) {                                                         \

            /*                                                                \

             * Encode any change in the current source line number by using   \

             * either several SRC_NEWLINE notes or one SRC_SETLINE note,      \

             * whichever consumes less space.                                 \

             */                                                               \

            if (_delta >= (uintN)(2 + ((_line > SN_3BYTE_OFFSET_MASK)<<1))) { \

                if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)_line) < 0)\

                    return JS_FALSE;                                          \

            } else {                                                          \

                do {                                                          \

                    if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0)               \

                        return JS_FALSE;                                      \

                } while (--_delta != 0);                                      \

            }                                                                 \

        }                                                                     \

    JS_END_MACRO



/* A function, so that we avoid macro-bloating all the other callsites. */

static JSBool

UpdateLinenoNotes(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)

{

    UPDATE_LINENO_NOTES(cx, cg, pn);

    return JS_TRUE;

}



JSBool

js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)

{

    JSBool ok, useful;

    JSCodeGenerator cg2;

    JSStmtInfo *stmt, stmtInfo;

    ptrdiff_t top, off, tmp, beq, jmp;

    JSParseNode *pn2, *pn3, *pn4;

    JSAtom *atom;

    JSAtomListElement *ale;

    jsatomid atomIndex;

    intN noteIndex;

    JSOp op;

    uint32 argc;



    pn->pn_offset = top = CG_OFFSET(cg);



    /* Emit notes to tell the current bytecode's source line number. */

    UPDATE_LINENO_NOTES(cx, cg, pn);



    switch (pn->pn_type) {

      case TOK_FUNCTION:

      {

        JSFunction *fun;



        /* Generate code for the function's body. */

        pn2 = pn->pn_body;

        if (!js_InitCodeGenerator(cx, &cg2, cg->filename,

                                  pn->pn_pos.begin.lineno,

                                  cg->principals)) {

            return JS_FALSE;

        }

        cg2.treeContext.flags = pn->pn_flags | TCF_IN_FUNCTION;

        cg2.treeContext.tryCount = pn->pn_tryCount;

        fun = pn->pn_fun;

        if (!js_EmitFunctionBody(cx, &cg2, pn2, fun))

            return JS_FALSE;



        /*

         * We need an activation object if an inner peeks out, or if such

         * inner-peeking caused one of our inners to become heavyweight.

         */

        if (cg2.treeContext.flags & 

            (TCF_FUN_USES_NONLOCALS | TCF_FUN_HEAVYWEIGHT)) {

            cg->treeContext.flags |= TCF_FUN_HEAVYWEIGHT;

        }

        js_FinishCodeGenerator(cx, &cg2);



        /* Make the function object a literal in the outer script's pool. */

        atom = js_AtomizeObject(cx, fun->object, 0);

        if (!atom)

            return JS_FALSE;

        ale = js_IndexAtom(cx, atom, &cg->atomList);

        if (!ale)

            return JS_FALSE;

        atomIndex = ALE_INDEX(ale);



#if JS_HAS_LEXICAL_CLOSURE

        /* Emit a bytecode pointing to the closure object in its immediate. */

        if (pn->pn_op != JSOP_NOP) {

            EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);

            break;

        }

#endif



        /* Top-level named functions need a nop for decompilation. */

        noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)atomIndex);

        if (noteIndex < 0 ||

            js_Emit1(cx, cg, JSOP_NOP) < 0) {

            return JS_FALSE;

        }



        /*

         * Top-levels also need a prolog op to predefine their names in the

         * variable object, or if local, to fill their stack slots.

         */

        CG_SWITCH_TO_PROLOG(cg);

#if JS_HAS_LEXICAL_CLOSURE

        if (cg->treeContext.flags & TCF_IN_FUNCTION) {

            JSObject *obj, *pobj;

            JSScopeProperty *sprop;

            uintN slot;

            jsbytecode *pc;



            obj = OBJ_GET_PARENT(cx, pn->pn_fun->object);

            if (!js_LookupProperty(cx, obj, (jsid)fun->atom, &pobj,

                                   (JSProperty **)&sprop)) {

                return JS_FALSE;

            }

            JS_ASSERT(sprop && pobj == obj);

            slot = (uintN) JSVAL_TO_INT(sprop->id);

            OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);



            /* Emit [JSOP_DEFLOCALFUN, local variable slot, atomIndex]. */

            off = js_EmitN(cx, cg, JSOP_DEFLOCALFUN, VARNO_LEN+ATOM_INDEX_LEN);

            if (off < 0)

                return JS_FALSE;

            pc = CG_CODE(cg, off);

            SET_VARNO(pc, slot);

            pc += VARNO_LEN;

            SET_ATOM_INDEX(pc, atomIndex);

        } else

#endif

            EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex);

        CG_SWITCH_TO_MAIN(cg);



        break;

      }



#if JS_HAS_EXPORT_IMPORT

      case TOK_EXPORT:

        pn2 = pn->pn_head;

        if (pn2->pn_type == TOK_STAR) {

            /*

             * 'export *' must have no other elements in the list (what would

             * be the point?).

             */

            if (js_Emit1(cx, cg, JSOP_EXPORTALL) < 0)

                return JS_FALSE;

        } else {

            /*

             * If not 'export *', the list consists of NAME nodes identifying

             * properties of the variables object to flag as exported.

             */

            do {

                ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);

                if (!ale)

                    return JS_FALSE;

                EMIT_ATOM_INDEX_OP(JSOP_EXPORTNAME, ALE_INDEX(ale));

            } while ((pn2 = pn2->pn_next) != NULL);

        }

        break;



      case TOK_IMPORT:

        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {

            /*

             * Each subtree on an import list is rooted by a DOT or LB node.

             * A DOT may have a null pn_atom member, in which case pn_op must

             * be JSOP_IMPORTALL -- see EmitPropOp above.

             */

            if (!js_EmitTree(cx, cg, pn2))

                return JS_FALSE;

        }

        break;

#endif /* JS_HAS_EXPORT_IMPORT */



      case TOK_IF:

        /* Emit code for the condition before pushing stmtInfo. */

        if (!js_EmitTree(cx, cg, pn->pn_kid1))

            return JS_FALSE;

        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF, CG_OFFSET(cg));



        /* Emit an annotated branch-if-false around the then part. */

        noteIndex = js_NewSrcNote(cx, cg, SRC_IF);

        if (noteIndex < 0)

            return JS_FALSE;

        beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);

        if (beq < 0)

            return JS_FALSE;



        /* Emit code for the then and optional else parts. */

        if (!js_EmitTree(cx, cg, pn->pn_kid2))

            return JS_FALSE;

        pn3 = pn->pn_kid3;

        if (pn3) {

            /* Modify stmtInfo and the branch-if-false source note. */

            stmtInfo.type = STMT_ELSE;

            SN_SET_TYPE(&cg->notes[noteIndex], SRC_IF_ELSE);



            /* Jump at end of then part around the else part. */

            jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);

            if (jmp < 0)

                return JS_FALSE;



            /* Ensure the branch-if-false comes here, then emit the else. */

            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);

            if (!js_EmitTree(cx, cg, pn3))

                return JS_FALSE;



            /* Fixup the jump around the else part. */

            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);

        } else {

            /* No else part, fixup the branch-if-false to come here. */

            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);

        }

        return js_PopStatementCG(cx, cg);



#if JS_HAS_SWITCH_STATEMENT

      case TOK_SWITCH:

      {

        JSOp switchop;

        uint32 ncases, tablen = 0;

        JSScript *script;

        jsint i, low, high;

        jsdouble d;

        size_t switchsize, tablesize;

        void *mark;

        JSParseNode **table;

        jsbytecode *pc;

        JSBool hasDefault = JS_FALSE;

        JSBool isEcmaSwitch = cx->version == JSVERSION_DEFAULT ||

                              cx->version >= JSVERSION_1_4;

        ptrdiff_t defaultOffset = -1;



        /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */

        switchop = JSOP_TABLESWITCH;



        /* Emit code for the discriminant first. */

        if (!js_EmitTree(cx, cg, pn->pn_kid1))

            return JS_FALSE;



        /* Switch bytecodes run from here till end of final case. */

        top = CG_OFFSET(cg);

        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_SWITCH, top);



        pn2 = pn->pn_kid2;

        ncases = pn2->pn_count;



        if (ncases == 0 ||

            (ncases == 1 &&

             (hasDefault = (pn2->pn_head->pn_type == TOK_DEFAULT)))) {

            ncases = 0;

            low = 0;

            high = -1;

            ok = JS_TRUE;

        } else {

            low  = JSVAL_INT_MAX;

            high = JSVAL_INT_MIN;

            cg2.current = NULL;

            for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {

                if (pn3->pn_type == TOK_DEFAULT) {

                    hasDefault = JS_TRUE;

                    ncases--;   /* one of the "cases" was the default */

                    continue;

                }

                JS_ASSERT(pn3->pn_type == TOK_CASE);

                pn4 = pn3->pn_left;

                if (isEcmaSwitch) {

                    if (switchop == JSOP_CONDSWITCH)

                        continue;

                    switch (pn4->pn_type) {

                      case TOK_NUMBER:

                        d = pn4->pn_dval;

                        if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) {

                            pn3->pn_val = INT_TO_JSVAL(i);

                        } else {

                            atom = js_AtomizeDouble(cx, d, 0);

                            if (!atom)

                                return JS_FALSE;

                            pn3->pn_val = ATOM_KEY(atom);

                        }

                        break;

                      case TOK_STRING:

                        pn3->pn_val = ATOM_KEY(pn4->pn_atom);

                        break;

                      case TOK_PRIMARY:

                        if (pn4->pn_op == JSOP_TRUE) {

                            pn3->pn_val = JSVAL_TRUE;

                            break;

                        }

                        if (pn4->pn_op == JSOP_FALSE) {

                            pn3->pn_val = JSVAL_FALSE;

                            break;

                        }

                        /* FALL THROUGH */

                      default:

                        switchop = JSOP_CONDSWITCH;

                        continue;

                    }

                } else {

                    /* Pre-ECMAv2 switch evals case exprs at compile time. */

                    if (!js_InitCodeGenerator(cx, &cg2, cg->filename,

                                              pn3->pn_pos.begin.lineno,

                                              cg->principals)) {

                        return JS_FALSE;

                    }

                    cg2.currentLine = pn4->pn_pos.begin.lineno;

                    if (!js_EmitTree(cx, &cg2, pn4))

                        return JS_FALSE;

                    if (js_Emit1(cx, &cg2, JSOP_POPV) < 0)

                        return JS_FALSE;

                    script = js_NewScriptFromCG(cx, &cg2, NULL);

                    if (!script)

                        return JS_FALSE;

                    ok = js_Execute(cx, cx->fp->scopeChain, script, cx->fp, 0,

                                    &pn3->pn_val);

                    js_DestroyScript(cx, script);

                    if (!ok)

                        return JS_FALSE;

                }



                if (!JSVAL_IS_NUMBER(pn3->pn_val) &&

                    !JSVAL_IS_STRING(pn3->pn_val) &&

                    !JSVAL_IS_BOOLEAN(pn3->pn_val)) {

                    cg->currentLine = pn3->pn_pos.begin.lineno;

                    js_ReportCompileErrorNumber(cx, NULL, cg, JSREPORT_ERROR,

                                                JSMSG_BAD_CASE);

                    return JS_FALSE;

                }



                if (switchop != JSOP_TABLESWITCH)

                    continue;

                if (!JSVAL_IS_INT(pn3->pn_val)) {

                    switchop = JSOP_LOOKUPSWITCH;

                    continue;

                }

                i = JSVAL_TO_INT(pn3->pn_val);

                if ((jsuint)(i + (jsint)JS_BIT(15)) >= (jsuint)JS_BIT(16)) {

                    switchop = JSOP_LOOKUPSWITCH;

                    continue;

                }

                if (i < low)

                    low = i;

                if (high < i)

                    high = i;

            }

            if (switchop == JSOP_CONDSWITCH) {

                JS_ASSERT(!cg2.current);

            } else {

                if (cg2.current)

                    js_FinishCodeGenerator(cx, &cg2);

                if (switchop == JSOP_TABLESWITCH) {

                    tablen = (uint32)(high - low + 1);

                    if (tablen >= JS_BIT(16) || tablen > 2 * ncases)

                        switchop = JSOP_LOOKUPSWITCH;

                }

            }

        }



        /*

         * Emit a note with two offsets: first tells total switch code length,

         * second tells offset to first JSOP_CASE if condswitch.

         */

        noteIndex = js_NewSrcNote3(cx, cg, SRC_SWITCH, 0, 0);

        if (noteIndex < 0)

            return JS_FALSE;



        if (switchop == JSOP_CONDSWITCH) {

            /*

             * 0 bytes of immediate for unoptimized ECMAv2 switch.

             */

            switchsize = 0;

        } else if (switchop == JSOP_TABLESWITCH) {

            /*

             * 3 offsets (len, low, high) before the table, 1 per entry.

             */

            switchsize = (size_t)(JUMP_OFFSET_LEN * (3 + tablen));

        } else {

            /*

             * JSOP_LOOKUPSWITCH:

             * 1 offset (len) and 1 atom index (npairs) before the table,

             * 1 atom index and 1 jump offset per entry.

             */

            switchsize = (size_t)(JUMP_OFFSET_LEN + ATOM_INDEX_LEN +

                                  (ATOM_INDEX_LEN + JUMP_OFFSET_LEN) * ncases);

        }



        /* Emit switchop and switchsize bytes of jump or lookup table. */

        if (js_EmitN(cx, cg, switchop, switchsize) < 0)

            return JS_FALSE;



        off = -1;

        if (switchop == JSOP_CONDSWITCH) {

            intN caseNoteIndex = -1;



            /* Emit code for evaluating cases and jumping to case statements. */

            for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {

                pn4 = pn3->pn_left;

                if (pn4 && !js_EmitTree(cx, cg, pn4))

                    return JS_FALSE;

                if (caseNoteIndex >= 0) {

                    /* off is the previous JSOP_CASE's bytecode offset. */

                    if (!js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0,

                                             CG_OFFSET(cg) - off)) {

                        return JS_FALSE;

                    }

                }

                if (pn3->pn_type == TOK_DEFAULT)

                    continue;

                caseNoteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);

                if (caseNoteIndex < 0)

                    return JS_FALSE;

                off = js_Emit3(cx, cg, JSOP_CASE, 0, 0);

                if (off < 0)

                    return JS_FALSE;

                pn3->pn_offset = off;

                if (pn3 == pn2->pn_head) {

                    /* Switch note's second offset is to first JSOP_CASE. */

                    if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1,

                                             off - top)) {

                        return JS_FALSE;

                    }

                }

            }



            /* Emit default even if no explicit default statement. */

            defaultOffset = js_Emit3(cx, cg, JSOP_DEFAULT, 0, 0);

            if (defaultOffset < 0)

                return JS_FALSE;

        }



        /* Emit code for each case's statements, copying pn_offset up to pn3. */

        for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {

            if (switchop == JSOP_CONDSWITCH && pn3->pn_type != TOK_DEFAULT) {

                pn3->pn_val = INT_TO_JSVAL(pn3->pn_offset - top);

                CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, pn3->pn_offset);

            }

            pn4 = pn3->pn_right;

            if (!js_EmitTree(cx, cg, pn4))

                return JS_FALSE;

            pn3->pn_offset = pn4->pn_offset;

            if (pn3->pn_type == TOK_DEFAULT)

                off = pn3->pn_offset - top;

        }



        if (!hasDefault) {

            /* If no default case, offset for default is to end of switch. */

            off = CG_OFFSET(cg) - top;

        }



        /* We better have set "off" by now. */

        JS_ASSERT(off != -1);



        /* Set the default offset (to end of switch if no default). */

        pc = NULL;

        if (switchop == JSOP_CONDSWITCH) {

            JS_ASSERT(defaultOffset != -1);

            if (!js_SetJumpOffset(cx, cg, CG_CODE(cg, defaultOffset),

                                  off - (defaultOffset - top))) {

                return JS_FALSE;

            }

        } else {

            pc = CG_CODE(cg, top);

            if (!js_SetJumpOffset(cx, cg, pc, off))

                return JS_FALSE;

            pc += JUMP_OFFSET_LEN;

        }



        /* Set the SRC_SWITCH note's offset operand to tell end of switch. */

        off = CG_OFFSET(cg) - top;

        if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, off))

            return JS_FALSE;



        if (switchop == JSOP_TABLESWITCH) {

            /* Fill in jump table. */

            if (!js_SetJumpOffset(cx, cg, pc, low))

                return JS_FALSE;

            pc += JUMP_OFFSET_LEN;

            if (!js_SetJumpOffset(cx, cg, pc, high))

                return JS_FALSE;

            pc += JUMP_OFFSET_LEN;

            if (tablen) {

                /* Avoid bloat for a compilation unit with many switches. */

                mark = JS_ARENA_MARK(&cx->tempPool);

                tablesize = (size_t)tablen * sizeof *table;

                JS_ARENA_ALLOCATE_CAST(table, JSParseNode **, &cx->tempPool,

                                       tablesize);

                if (!table) {

                    JS_ReportOutOfMemory(cx);

                    return JS_FALSE;

                }

                memset(table, 0, tablesize);

                for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {

                    if (pn3->pn_type == TOK_DEFAULT)

                        continue;

                    i = JSVAL_TO_INT(pn3->pn_val);

                    i -= low;

                    JS_ASSERT((uint32)i < tablen);

                    table[i] = pn3;

                }

                for (i = 0; i < (jsint)tablen; i++) {

                    pn3 = table[i];

                    off = pn3 ? pn3->pn_offset - top : 0;

                    if (!js_SetJumpOffset(cx, cg, pc, off))

                        return JS_FALSE;

                    pc += JUMP_OFFSET_LEN;

                }

                JS_ARENA_RELEASE(&cx->tempPool, mark);

            }

        } else if (switchop == JSOP_LOOKUPSWITCH) {

            /* Fill in lookup table. */

            SET_ATOM_INDEX(pc, ncases);

            pc += ATOM_INDEX_LEN;



            for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {

                if (pn3->pn_type == TOK_DEFAULT)

                    continue;

                atom = js_AtomizeValue(cx, pn3->pn_val, 0);

                if (!atom)

                    return JS_FALSE;

                ale = js_IndexAtom(cx, atom, &cg->atomList);

                if (!ale)

                    return JS_FALSE;

                SET_ATOM_INDEX(pc, ALE_INDEX(ale));

                pc += ATOM_INDEX_LEN;



                off = pn3->pn_offset - top;

                if (!js_SetJumpOffset(cx, cg, pc, off))

                    return JS_FALSE;

                pc += JUMP_OFFSET_LEN;

            }

        }



        return js_PopStatementCG(cx, cg);

      }

#endif /* JS_HAS_SWITCH_STATEMENT */



      case TOK_WHILE:

        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top);

        if (!js_EmitTree(cx, cg, pn->pn_left))

            return JS_FALSE;

        noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);

        if (noteIndex < 0)

            return JS_FALSE;

        beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);

        if (beq < 0)

            return JS_FALSE;

        if (!js_EmitTree(cx, cg, pn->pn_right))

            return JS_FALSE;

        jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);

        if (jmp < 0)

            return JS_FALSE;

        CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,jmp), top - jmp);

        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);

        return js_PopStatementCG(cx, cg);



#if JS_HAS_DO_WHILE_LOOP

      case TOK_DO:

        /* Emit an annotated nop so we know to decompile a 'do' keyword. */

        if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 ||

            js_Emit1(cx, cg, JSOP_NOP) < 0) {

            return JS_FALSE;

        }



        /* Compile the loop body. */

        top = CG_OFFSET(cg);

        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_DO_LOOP, top);

        if (!js_EmitTree(cx, cg, pn->pn_left))

            return JS_FALSE;



        /* Set loop and enclosing label update offsets, for continue. */

        stmt = &stmtInfo;

        do {

            stmt->update = CG_OFFSET(cg);

        } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);



        /* Compile the loop condition, now that continues know where to go. */

        if (!js_EmitTree(cx, cg, pn->pn_right))

            return JS_FALSE;



        /* Re-use the SRC_WHILE note, this time for the JSOP_IFNE opcode. */

        if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0)

            return JS_FALSE;

        jmp = top - CG_OFFSET(cg);

        if (js_Emit3(cx, cg, JSOP_IFNE,

                     JUMP_OFFSET_HI(jmp), JUMP_OFFSET_LO(jmp)) < 0) {

            return JS_FALSE;

        }

        return js_PopStatementCG(cx, cg);

#endif /* JS_HAS_DO_WHILE_LOOP */



      case TOK_FOR:

        pn2 = pn->pn_left;

        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top);



        if (pn2->pn_type == TOK_IN) {

            /* If the left part is var x = i, bind x, evaluate i, and pop. */

            pn3 = pn2->pn_left;

            if (pn3->pn_type == TOK_VAR && pn3->pn_head->pn_expr) {

                if (!js_EmitTree(cx, cg, pn3))

                    return JS_FALSE;

                /* Set pn3 to the variable name, to avoid another var note. */

                pn3 = pn3->pn_head;

                JS_ASSERT(pn3->pn_type == TOK_NAME);

            }



            /* Fix stmtInfo and emit a push to allocate the iterator. */

            stmtInfo.type = STMT_FOR_IN_LOOP;

            noteIndex = -1;

            if (js_Emit1(cx, cg, JSOP_PUSH) < 0)

                return JS_FALSE;



            /* Compile the object expression to the right of 'in'. */

            if (!js_EmitTree(cx, cg, pn2->pn_right))

                return JS_FALSE;

            if (js_Emit1(cx, cg, JSOP_TOOBJECT) < 0)

                return JS_FALSE;



            top = CG_OFFSET(cg);

            SET_STATEMENT_TOP(&stmtInfo, top);



            /* Compile a JSOP_FOR* bytecode based on the left hand side. */

            switch (pn3->pn_type) {

              case TOK_VAR:

                pn3 = pn3->pn_head;

                if (js_NewSrcNote(cx, cg, SRC_VAR) < 0)

                    return JS_FALSE;

                /* FALL THROUGH */

              case TOK_NAME:

                pn3->pn_op = JSOP_FORNAME;

                if (!LookupArgOrVar(cx, &cg->treeContext, pn3))

                    return JS_FALSE;

                op = pn3->pn_op;

                if (pn3->pn_slot >= 0) {

                    if (pn3->pn_attrs & JSPROP_READONLY)

                        op = JSOP_GETVAR;

                    atomIndex = (jsatomid) pn3->pn_slot;

                    EMIT_ATOM_INDEX_OP(op, atomIndex);

                } else {

                    if (!EmitAtomOp(cx, pn3, op, cg))

                        return JS_FALSE;

                }

                break;

              case TOK_DOT:

                if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg))

                    return JS_FALSE;

                break;

              case TOK_LB:

                /*

                 * We separate the first/next bytecode from the enumerator

                 * variable binding to avoid any side-effects in the index

                 * expression (e.g., for (x[i++] in {}) should not bind x[i]

                 * or increment i at all).

                 */

                if (!js_Emit1(cx, cg, JSOP_FORELEM))

                    return JS_FALSE;

                beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);

                if (beq < 0)

                    return JS_FALSE;

                if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg))

                    return JS_FALSE;

                break;

              default:

                JS_ASSERT(0);

            }

            if (pn3->pn_type != TOK_LB) {

                /* Pop and test the loop condition generated by JSOP_FOR*. */

                beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);

                if (beq < 0)

                    return JS_FALSE;

            }

        } else {

            if (!pn2->pn_kid1) {

                /* No initializer: emit an annotated nop for the decompiler. */

                noteIndex = js_NewSrcNote(cx, cg, SRC_FOR);

                if (noteIndex < 0 ||

                    js_Emit1(cx, cg, JSOP_NOP) < 0) {

                    return JS_FALSE;

                }

            } else {

                if (!js_EmitTree(cx, cg, pn2->pn_kid1))

                    return JS_FALSE;

                noteIndex = js_NewSrcNote(cx, cg, SRC_FOR);

                if (noteIndex < 0 ||

                    js_Emit1(cx, cg, JSOP_POP) < 0) {

                    return JS_FALSE;

                }

            }



            top = CG_OFFSET(cg);

            SET_STATEMENT_TOP(&stmtInfo, top);

            if (!pn2->pn_kid2) {

                /* No loop condition: flag this fact in the source notes. */

                if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, 0))

                    return JS_FALSE;

                beq = 0;

            } else {

                if (!js_EmitTree(cx, cg, pn2->pn_kid2))

                    return JS_FALSE;

                if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,

                                         (ptrdiff_t)(CG_OFFSET(cg) - top))) {

                    return JS_FALSE;

                }

                beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);

                if (beq < 0)

                    return JS_FALSE;

            }

        }



        /* Emit code for the loop body. */

        if (!js_EmitTree(cx, cg, pn->pn_right))

            return JS_FALSE;



        if (pn2->pn_type != TOK_IN) {

            /* Set the second note offset so we can find the update part. */

            JS_ASSERT(noteIndex != -1);

            if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1,

                                     (ptrdiff_t)(CG_OFFSET(cg) - top))) {

                return JS_FALSE;

            }



            pn3 = pn2->pn_kid3;

            if (pn3) {

                /* Set loop and enclosing "update" offsets, for continue. */

                stmt = &stmtInfo;

                do {

                    stmt->update = CG_OFFSET(cg);

                } while ((stmt = stmt->down) != NULL &&

                         stmt->type == STMT_LABEL);



                if (!js_EmitTree(cx, cg, pn3))

                    return JS_FALSE;

                if (js_Emit1(cx, cg, JSOP_POP) < 0)

                    return JS_FALSE;



                /* Restore the absolute line number for source note readers. */

                off = (ptrdiff_t) pn->pn_pos.end.lineno;

                if (cg->currentLine != (uintN) off) {

                    if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0)

                        return JS_FALSE;

                    cg->currentLine = (uintN) off;

                }

            }



            /* The third note offset helps us find the loop-closing jump. */

            if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2,

                                     (ptrdiff_t)(CG_OFFSET(cg) - top))) {

                return JS_FALSE;

            }

        }



        /* Emit the loop-closing jump and fixup all jump offsets. */

        jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);

        if (jmp < 0)

            return JS_FALSE;

        CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,jmp), top - jmp);

        if (beq > 0)

            CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);



        /* Now fixup all breaks and continues (before for/in's final POP2). */

        if (!js_PopStatementCG(cx, cg))

            return JS_FALSE;



        if (pn2->pn_type == TOK_IN) {

            /*

             * Generate the object and iterator pop opcodes after popping the

             * stmtInfo stack, so breaks will go to this pop bytecode.

             */

            if (pn3->pn_type != TOK_LB) {

                if (js_Emit1(cx, cg, JSOP_POP2) < 0)

                    return JS_FALSE;

            } else {

                /*

                 * With 'for(x[i]...)', there's only the object on the stack,

                 * so we need to hide the pop.

                 */

                if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)

                    return JS_FALSE;

                if (js_Emit1(cx, cg, JSOP_POP) < 0)

                    return JS_FALSE;

            }

        }

        break;



      case TOK_BREAK:

        stmt = cg->treeContext.topStmt;

        atom = pn->pn_atom;

        if (atom) {

            ale = js_IndexAtom(cx, atom, &cg->atomList);

            if (!ale)

                return JS_FALSE;

            while (stmt->type != STMT_LABEL || stmt->label != atom)

                stmt = stmt->down;

        } else {

            ale = NULL;

            while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH)

                stmt = stmt->down;

        }

        if (js_EmitBreak(cx, cg, stmt, ale) < 0)

            return JS_FALSE;

        break;



      case TOK_CONTINUE:

        stmt = cg->treeContext.topStmt;

        atom = pn->pn_atom;

        if (atom) {

            /* Find the loop statement enclosed by the matching label. */

            JSStmtInfo *loop = NULL;

            ale = js_IndexAtom(cx, atom, &cg->atomList);

            if (!ale)

                return JS_FALSE;

            while (stmt->type != STMT_LABEL || stmt->label != atom) {

                if (STMT_IS_LOOP(stmt))

                    loop = stmt;

                stmt = stmt->down;

            }

            stmt = loop;

        } else {

            ale = NULL;

            while (!STMT_IS_LOOP(stmt))

                stmt = stmt->down;

            if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0)

                return JS_FALSE;

        }

        if (js_EmitContinue(cx, cg, stmt, ale) < 0)

            return JS_FALSE;

        break;



      case TOK_WITH:

        if (!js_EmitTree(cx, cg, pn->pn_left))

            return JS_FALSE;

        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WITH, CG_OFFSET(cg));

        if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0)

            return JS_FALSE;

        if (!js_EmitTree(cx, cg, pn->pn_right))

            return JS_FALSE;

        if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)

            return JS_FALSE;

        return js_PopStatementCG(cx, cg);



#if JS_HAS_EXCEPTIONS



      case TOK_TRY: {

        ptrdiff_t start, end;

        ptrdiff_t catchStart = -1, finallyCatch = -1, catchjmp = -1;

        JSParseNode *iter;

        uint16 depth;



/* Emit JSOP_GOTO that points to the first op after the catch/finally blocks */

#define EMIT_CATCH_GOTO(cx, cg, jmp)                                          \

    EMIT_CHAINED_JUMP(cx, cg, stmtInfo.catchJump, JSOP_GOTO, jmp);



/* Emit JSOP_GOSUB that points to the finally block. */

#define EMIT_FINALLY_GOSUB(cx, cg, jmp)                                       \

    EMIT_CHAINED_JUMP(cx, cg, stmtInfo.gosub, JSOP_GOSUB, jmp);



        /*

         * Push stmtInfo to track jumps-over-catches and gosubs-to-finally

         * for later fixup.

         * 

         * When a finally block is `active' (STMT_FINALLY on the treeContext),

         * non-local jumps (including jumps-over-catches) result in a GOSUB

         * being written into the bytecode stream and fixed-up later (c.f.

         * EMIT_CHAINED_JUMP and PatchGotos).

         */

        js_PushStatement(&cg->treeContext, &stmtInfo, 

                         pn->pn_kid3 ? STMT_FINALLY : STMT_BLOCK,

                         CG_OFFSET(cg));



        /*

         * About JSOP_SETSP:

         * An exception can be thrown while the stack is in an unbalanced

         * state, and this causes problems with things like function invocation

         * later on.

         *

         * To fix this, we compute the `balanced' stack depth upon try entry,

         * and then restore the stack to this depth when we hit the first catch

         * or finally block.  We can't just zero the stack, because things like

         * for/in and with that are active upon entry to the block keep state

         * variables on the stack.

         */

        depth = cg->stackDepth;



        /* Mark try location for decompilation, then emit try block. */

        if (js_Emit1(cx, cg, JSOP_TRY) < 0)

            return JS_FALSE;

        start = CG_OFFSET(cg);

        if (!js_EmitTree(cx, cg, pn->pn_kid1))

            return JS_FALSE;



        /* Emit (hidden) jump over catch and/or finally. */

        if (pn->pn_kid3) {

            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)

                return JS_FALSE;

            EMIT_FINALLY_GOSUB(cx, cg, jmp);

            if (jmp < 0)

                return JS_FALSE;

        }



        if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)

            return JS_FALSE;

        EMIT_CATCH_GOTO(cx, cg, jmp);

        if (jmp < 0)

            return JS_FALSE;



        end = CG_OFFSET(cg);



        /* If this try has a catch block, emit it. */

        iter = pn->pn_kid2;

        if (iter) {

            catchStart = end;



            /*

             * The emitted code for a catch block looks like:

             *

             * [ popscope ]                        only if 2nd+ catch block

             * name Object

             * pushobj

             * newinit

             * exception

             * initcatchvar <atom>

             * enterwith

             * [< catchguard code >]               if there's a catchguard

             * [ifeq <offset to next catch block>]         " "

             * < catch block contents >

             * leavewith

             * goto <end of catch blocks>          non-local; finally applies

             *

             * If there's no catch block without a catchguard, the last

             * <offset to next catch block> points to rethrow code.  This

             * code will GOSUB to the finally code if appropriate, and is

             * also used for the catch-all trynote for capturing exceptions

             * thrown from catch{} blocks.

             */

            for (;;) {

                JSStmtInfo stmtInfo2;

                JSParseNode *disc;

                ptrdiff_t guardnote;



                if (!UpdateLinenoNotes(cx, cg, iter))

                    return JS_FALSE;



                if (catchjmp != -1) {

                    /* Fix up and clean up previous catch block. */

                    CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchjmp);

                    if ((uintN)++cg->stackDepth > cg->maxStackDepth)

                        cg->maxStackDepth = cg->stackDepth;

                    if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||

                        js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) {

                        return JS_FALSE;

                    }

                } else {

                    /* Set stack to original depth (see SETSP comment above). */

                    EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth);

                }



                /* Non-zero guardnote is length of catchguard. */

                guardnote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0);

                if (guardnote < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0)

                    return JS_FALSE;



                /* Construct the scope holder and push it on. */

                ale = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom,

                                   &cg->atomList);

                if (!ale)

                    return JS_FALSE;

                EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));



                if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0 ||

                    js_Emit1(cx, cg, JSOP_NEWINIT) < 0 ||

                    js_Emit1(cx, cg, JSOP_EXCEPTION) < 0) {

                    return JS_FALSE;

                }



                /* initcatchvar <atomIndex> */

                disc = iter->pn_kid1;

                ale = js_IndexAtom(cx, disc->pn_atom, &cg->atomList);

                if (!ale)

                    return JS_FALSE;



                EMIT_ATOM_INDEX_OP(JSOP_INITCATCHVAR, ALE_INDEX(ale));

                if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||

                    js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) {

                    return JS_FALSE;

                }



                /* boolean_expr */

                if (disc->pn_expr) {

                    ptrdiff_t guardstart = CG_OFFSET(cg);

                    if (!js_EmitTree(cx, cg, disc->pn_expr))

                        return JS_FALSE;

                    /* ifeq <next block> */

                    catchjmp = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);

                    if (catchjmp < 0)

                        return JS_FALSE;

                    if (!js_SetSrcNoteOffset(cx, cg, guardnote, 0,

                                             (ptrdiff_t)CG_OFFSET(cg) -

                                             guardstart)) {

                        return JS_FALSE;

                    }

                }



                /* Emit catch block. */

                js_PushStatement(&cg->treeContext, &stmtInfo2, STMT_CATCH,

                                 CG_OFFSET(cg));

                stmtInfo2.label = disc->pn_atom;

                if (!js_EmitTree(cx, cg, iter->pn_kid3))

                    return JS_FALSE;

                js_PopStatementCG(cx, cg);



                /*

                 * Jump over the remaining catch blocks.

                 * This counts as a non-local jump, so do the finally thing.

                 */



                /* popscope */

                if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||

                    js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) {

                    return JS_FALSE;

                }



                /* gosub <finally>, if required */

                if (pn->pn_kid3) {

                    EMIT_FINALLY_GOSUB(cx, cg, jmp);

                    if (jmp < 0)

                        return JS_FALSE;

                }



                /* This will get fixed up to jump to after catch/finally. */

                if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)

                    return JS_FALSE;

                EMIT_CATCH_GOTO(cx, cg, jmp);

                if (jmp < 0)

                    return JS_FALSE;

                if (!iter->pn_kid2)     /* leave iter at last catch */

                    break;

                iter = iter->pn_kid2;

            }

        }



        /*

         * We use a [leavewith],[gosub],rethrow block for rethrowing

         * when there's no unguarded catch, and also for running finally

         * code while letting an uncaught exception pass through.

         */

        if (pn->pn_kid3 ||

            (catchjmp != -1 && iter->pn_kid1->pn_expr)) {

            /*

             * Emit another stack fix, because the catch could itself

             * throw an exception in an unbalanced state, and the finally

             * may need to call functions etc.

             */

            EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth);



            if (catchjmp != -1 && iter->pn_kid1->pn_expr)

                CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchjmp);



            /* Last discriminant jumps to rethrow if none match. */

            if ((uintN)++cg->stackDepth > cg->maxStackDepth)

                cg->maxStackDepth = cg->stackDepth;

            if (pn->pn_kid2 &&

                (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||

                 js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)) {

                return JS_FALSE;

            }



            if (pn->pn_kid3) {

                finallyCatch = CG_OFFSET(cg);

                EMIT_FINALLY_GOSUB(cx, cg, jmp);

                if (jmp < 0)

                    return JS_FALSE;

            }

            if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||

                js_Emit1(cx, cg, JSOP_EXCEPTION) < 0 ||

                js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||

                js_Emit1(cx, cg, JSOP_THROW) < 0) {

                return JS_FALSE;

            }

        }



        /*

         * If we have a finally, it belongs here, and we have to fix up the

         * gosubs that might have been emitted before non-local jumps.

         */

        if (pn->pn_kid3) {

            if (!PatchGotos(cx, cg, &stmtInfo, stmtInfo.gosub, CG_NEXT(cg),

                            JSOP_GOSUB)) {

                return JS_FALSE;

            }

            js_PopStatementCG(cx, cg);

            if (!UpdateLinenoNotes(cx, cg, pn->pn_kid3))

                return JS_FALSE;

            if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 ||

                !js_EmitTree(cx, cg, pn->pn_kid3) ||

                js_Emit1(cx, cg, JSOP_RETSUB) < 0) {

                return JS_FALSE;

            }

        } else {

            js_PopStatementCG(cx, cg);

        }



        if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||

            js_Emit1(cx, cg, JSOP_NOP) < 0) {

            return JS_FALSE;

        }



        /* Fix up the end-of-try/catch jumps to come here. */

        if (!PatchGotos(cx, cg, &stmtInfo, stmtInfo.catchJump, CG_NEXT(cg),

                        JSOP_GOTO)) {

            return JS_FALSE;

        }



        /*

         * Add the try note last, to let post-order give us the right ordering

         * (first to last, inner to outer).

         */

        if (pn->pn_kid2) {

            JS_ASSERT(catchStart != -1);

            if (!js_NewTryNote(cx, cg, start, end, catchStart))

                return JS_FALSE;

        }



        /*

         * If we've got a finally, mark try+catch region with additional

         * trynote to catch exceptions (re)thrown from a catch block or

         * for the try{}finally{} case.

         */

        if (pn->pn_kid3) {

            JS_ASSERT(finallyCatch != -1);

            if (!js_NewTryNote(cx, cg, start, finallyCatch, finallyCatch))

                return JS_FALSE;

        }

        break;

      }



#endif /* JS_HAS_EXCEPTIONS */



      case TOK_VAR:

        off = noteIndex = -1;

        for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {

            JS_ASSERT(pn2->pn_type == TOK_NAME);

            if (!LookupArgOrVar(cx, &cg->treeContext, pn2))

                return JS_FALSE;

            op = pn2->pn_op;

            if (pn2->pn_slot >= 0) {

                atomIndex = (jsatomid) pn2->pn_slot;

            } else {

                ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);

                if (!ale)

                    return JS_FALSE;

                atomIndex = ALE_INDEX(ale);



                /* Emit a prolog bytecode to predefine the var w/ void value. */

                CG_SWITCH_TO_PROLOG(cg);

                EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);

                CG_SWITCH_TO_MAIN(cg);

            }

            if (pn2->pn_expr) {

                if (op == JSOP_SETNAME)

                    EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);

                if (!js_EmitTree(cx, cg, pn2->pn_expr))

                    return JS_FALSE;

            }

            if (pn2 == pn->pn_head &&

                js_NewSrcNote(cx, cg,

                              (pn->pn_op == JSOP_DEFCONST)

                              ? SRC_CONST

                              : SRC_VAR) < 0) {

                return JS_FALSE;

            }

            EMIT_ATOM_INDEX_OP(op, atomIndex);

            tmp = CG_OFFSET(cg);

            if (noteIndex >= 0) {

                if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))

                    return JS_FALSE;

            }

            if (!pn2->pn_next)

                break;

            off = tmp;

            noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);

            if (noteIndex < 0 ||

                js_Emit1(cx, cg, JSOP_POP) < 0) {

                return JS_FALSE;

            }

        }

        if (pn->pn_extra) {

            if (js_Emit1(cx, cg, JSOP_POP) < 0)

                return JS_FALSE;

        }

        break;



      case TOK_RETURN:

        /* Push a return value */

        pn2 = pn->pn_kid;

        if (pn2) {

            if (!js_EmitTree(cx, cg, pn2))

                return JS_FALSE;

        } else {

            if (js_Emit1(cx, cg, JSOP_PUSH) < 0)

                return JS_FALSE;

        }



        /* 

         * EmitNonLocalJumpFixup emits JSOP_SWAPs to maintain the return value

         * at the top of the stack, so the return still executes OK. 

         */

        if (!EmitNonLocalJumpFixup(cx, cg, NULL, JS_TRUE))

            return JS_FALSE;

        if (js_Emit1(cx, cg, JSOP_RETURN) < 0)

            return JS_FALSE;

        break;



      case TOK_LC:

        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top);

        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {

            if (!js_EmitTree(cx, cg, pn2))

                return JS_FALSE;

        }

        return js_PopStatementCG(cx, cg);



      case TOK_SEMI:

        pn2 = pn->pn_kid;

        if (pn2) {

            /*

             * Top-level JS_Execute/EvaluateScript, debugger, and eval frames

             * may need the last expression statement's value as the script's

             * result, even though it appears useless otherwise.

             */

            useful = !cx->fp->fun || cx->fp->special;

            if (!useful) {

                if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful))

                    return JS_FALSE;

            }

            if (!useful) {

                cg->currentLine = pn2->pn_pos.begin.lineno;

                if (!js_ReportCompileErrorNumber(cx, NULL, cg,

                                                 JSREPORT_WARNING |

                                                 JSREPORT_STRICT,

                                                 JSMSG_USELESS_EXPR)) {

                    return JS_FALSE;

                }

            } else {

                if (!js_EmitTree(cx, cg, pn2))

                    return JS_FALSE;

                if (js_Emit1(cx, cg, JSOP_POPV) < 0)

                    return JS_FALSE;

            }

        }

        break;



      case TOK_COLON:

        /* Emit an annotated nop so we know to decompile a label. */

        atom = pn->pn_atom;

        ale = js_IndexAtom(cx, atom, &cg->atomList);

        if (!ale)

            return JS_FALSE;

        pn2 = pn->pn_expr;

        noteIndex = js_NewSrcNote2(cx, cg,

                                   (pn2->pn_type == TOK_LC)

                                   ? SRC_LABELBRACE

                                   : SRC_LABEL,

                                   (ptrdiff_t) ALE_INDEX(ale));

        if (noteIndex < 0 ||

            js_Emit1(cx, cg, JSOP_NOP) < 0) {

            return JS_FALSE;

        }



        /* Emit code for the labeled statement. */

        js_PushStatement(&cg->treeContext, &stmtInfo, STMT_LABEL, CG_OFFSET(cg));

        stmtInfo.label = atom;

        if (!js_EmitTree(cx, cg, pn2))

            return JS_FALSE;

        if (!js_PopStatementCG(cx, cg))

            return JS_FALSE;



        /* If the statement was compound, emit a note for the end brace. */

        if (pn2->pn_type == TOK_LC) {

            if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||

                js_Emit1(cx, cg, JSOP_NOP) < 0) {

                return JS_FALSE;

            }

        }

        break;



      case TOK_COMMA:

        /*

         * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands.

         * These notes help the decompiler bracket the bytecodes generated

         * from each sub-expression that follows a comma.

         */

        off = noteIndex = -1;

        for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {

            if (!js_EmitTree(cx, cg, pn2))

                return JS_FALSE;

            tmp = CG_OFFSET(cg);

            if (noteIndex >= 0) {

                if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))

                    return JS_FALSE;

            }

            if (!pn2->pn_next)

                break;

            off = tmp;

            noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);

            if (noteIndex < 0 ||

                js_Emit1(cx, cg, JSOP_POP) < 0) {

                return JS_FALSE;

            }

        }

        break;



      case TOK_ASSIGN:

        /*

         * Check left operand type and generate specialized code for it.

         * Specialize to avoid ECMA "reference type" values on the operand

         * stack, which impose pervasive runtime "GetValue" costs.

         */

        pn2 = pn->pn_left;

        JS_ASSERT(pn2->pn_type != TOK_RP);

        atomIndex = (jsatomid) -1; /* Suppress warning. */

        switch (pn2->pn_type) {

          case TOK_NAME:

            if (!LookupArgOrVar(cx, &cg->treeContext, pn2))

                return JS_FALSE;

            if (pn2->pn_slot >= 0) {

                atomIndex = (jsatomid) pn2->pn_slot;

            } else {

                ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);

                if (!ale)

                    return JS_FALSE;

                atomIndex = ALE_INDEX(ale);

                EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);

            }

            break;

          case TOK_DOT:

            if (!js_EmitTree(cx, cg, pn2->pn_expr))

                return JS_FALSE;

            ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);

            if (!ale)

                return JS_FALSE;

            atomIndex = ALE_INDEX(ale);

            break;

          case TOK_LB:

            if (!js_EmitTree(cx, cg, pn2->pn_left))

                return JS_FALSE;

            if (!js_EmitTree(cx, cg, pn2->pn_right))

                return JS_FALSE;

            break;

#if JS_HAS_LVALUE_RETURN

          case TOK_LP:

            if (!js_EmitTree(cx, cg, pn2))

                return JS_FALSE;

            break;

#endif

          default:

            JS_ASSERT(0);

        }



        op = pn->pn_op;

#if JS_HAS_GETTER_SETTER

        if (op == JSOP_GETTER || op == JSOP_SETTER) {

            /* We'll emit these prefix bytecodes after emitting the r.h.s. */

        } else

#endif

        /* If += or similar, dup the left operand and get its value. */

        if (op != JSOP_NOP) {

            switch (pn2->pn_type) {

              case TOK_NAME:

                if (pn2->pn_op != JSOP_SETNAME) {

                    EMIT_ATOM_INDEX_OP((pn2->pn_op == JSOP_SETARG)

                                       ? JSOP_GETARG

                                       : JSOP_GETVAR,

                                       atomIndex);

                    break;

                }

                /* FALL THROUGH */

              case TOK_DOT:

                if (js_Emit1(cx, cg, JSOP_DUP) < 0)

                    return JS_FALSE;

                EMIT_ATOM_INDEX_OP(JSOP_GETPROP, atomIndex);

                break;

              case TOK_LB:

#if JS_HAS_LVALUE_RETURN

              case TOK_LP:

#endif

                if (js_Emit1(cx, cg, JSOP_DUP2) < 0)

                    return JS_FALSE;

                if (js_Emit1(cx, cg, JSOP_GETELEM) < 0)

                    return JS_FALSE;

                break;

              default:;

            }

        }



        /* Now emit the right operand (it may affect the namespace). */

        if (!js_EmitTree(cx, cg, pn->pn_right))

            return JS_FALSE;



        /* If += etc., emit the binary operator with a decompiler note. */

        if (op != JSOP_NOP) {

            if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0 ||

                js_Emit1(cx, cg, op) < 0) {

                return JS_FALSE;

            }

        }



        /* Left parts such as a.b.c and a[b].c need a decompiler note. */

        if (pn2->pn_type != TOK_NAME) {

            if (js_NewSrcNote2(cx, cg, SRC_PCBASE,

                               (ptrdiff_t)(CG_OFFSET(cg) - top)) < 0) {

                return JS_FALSE;

            }

        }



        /* Finally, emit the specialized assignment bytecode. */

        switch (pn2->pn_type) {

          case TOK_NAME:

            if (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY)) {

          case TOK_DOT:

                EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex);

            }

            break;

          case TOK_LB:

#if JS_HAS_LVALUE_RETURN

          case TOK_LP:

#endif

            if (js_Emit1(cx, cg, JSOP_SETELEM) < 0)

                return JS_FALSE;

            break;

          default:;

        }

        break;



      case TOK_HOOK:

        /* Emit the condition, then branch if false to the else part. */

        if (!js_EmitTree(cx, cg, pn->pn_kid1))

            return JS_FALSE;

        if (js_NewSrcNote(cx, cg, SRC_COND) < 0)

            return JS_FALSE;

        beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);

        if (beq < 0 || !js_EmitTree(cx, cg, pn->pn_kid2))

            return JS_FALSE;



        /* Jump around else, fixup the branch, emit else, fixup jump. */

        jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);

        if (jmp < 0)

            return JS_FALSE;

        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);

        if (!js_EmitTree(cx, cg, pn->pn_kid3))

            return JS_FALSE;

        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);

        break;



      case TOK_OR:

        /* Emit left operand, emit pop-if-converts-to-false-else-jump. */

        if (!js_EmitTree(cx, cg, pn->pn_left))

            return JS_FALSE;

#if JS_BUG_SHORT_CIRCUIT

        beq = js_Emit3(cx, cg, JSOP_IFEQ, 0, 0);

        tmp = js_Emit1(cx, cg, JSOP_TRUE);

        jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);

        if (beq < 0 || tmp < 0 || jmp < 0)

            return JS_FALSE;

        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);

#else

        /*

         * JSOP_OR converts the operand on the stack to boolean, and if true,

         * leaves the original operand value on the stack and jumps; otherwise

         * it pops and falls into the next bytecode.

         */

        jmp = js_Emit3(cx, cg, JSOP_OR, 0, 0);

        if (jmp < 0)

            return JS_FALSE;

#endif

        if (!js_EmitTree(cx, cg, pn->pn_right))

            return JS_FALSE;

        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);

        break;



      case TOK_AND:

        /* && is like || except it uses a pop-if-converts-to-true-else-jump. */

        if (!js_EmitTree(cx, cg, pn->pn_left))

            return JS_FALSE;

#if JS_BUG_SHORT_CIRCUIT

        beq = js_Emit3(cx, cg, JSOP_IFNE, 0, 0);

        tmp = js_Emit1(cx, cg, JSOP_FALSE);

        jmp = js_Emit3(cx, cg, JSOP_GOTO, 0, 0);

        if (beq < 0 || tmp < 0 || jmp < 0)

            return JS_FALSE;

        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);

#else

        jmp = js_Emit3(cx, cg, JSOP_AND, 0, 0);

        if (jmp < 0)

            return JS_FALSE;

#endif

        if (!js_EmitTree(cx, cg, pn->pn_right))

            return JS_FALSE;

        CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);

        break;



      case TOK_BITOR:

      case TOK_BITXOR:

      case TOK_BITAND:

      case TOK_EQOP:

      case TOK_RELOP:

#if JS_HAS_IN_OPERATOR

      case TOK_IN:

#endif

#if JS_HAS_INSTANCEOF

      case TOK_INSTANCEOF:

#endif

      case TOK_SHOP:

      case TOK_PLUS:

      case TOK_MINUS:

      case TOK_STAR:

      case TOK_DIVOP:

        /* Binary operators that evaluate both operands unconditionally. */

        if (!js_EmitTree(cx, cg, pn->pn_left))

            return JS_FALSE;

        if (!js_EmitTree(cx, cg, pn->pn_right))

            return JS_FALSE;

        if (js_Emit1(cx, cg, pn->pn_op) < 0)

            return JS_FALSE;

        break;



#if JS_HAS_EXCEPTIONS

      case TOK_THROW:

#endif

      case TOK_UNARYOP:

        /* Unary op, including unary +/-. */

        if (!js_EmitTree(cx, cg, pn->pn_kid))

            return JS_FALSE;

        if (js_Emit1(cx, cg, pn->pn_op) < 0)

            return JS_FALSE;

        break;



      case TOK_INC:

      case TOK_DEC:

        /* Emit lvalue-specialized code for ++/-- operators. */

        pn2 = pn->pn_kid;

        JS_ASSERT(pn2->pn_type != TOK_RP);

        op = pn->pn_op;

        switch (pn2->pn_type) {

          case TOK_NAME:

            pn2->pn_op = op;

            if (!LookupArgOrVar(cx, &cg->treeContext, pn2))

                return JS_FALSE;

            op = pn2->pn_op;

            if (pn2->pn_slot >= 0) {

                if (pn2->pn_attrs & JSPROP_READONLY)

                    op = JSOP_GETVAR;

                atomIndex = (jsatomid) pn2->pn_slot;

                EMIT_ATOM_INDEX_OP(op, atomIndex);

            } else {

                if (!EmitAtomOp(cx, pn2, op, cg))

                    return JS_FALSE;

            }

            break;

          case TOK_DOT:

            if (!EmitPropOp(cx, pn2, op, cg))

                return JS_FALSE;

            break;

          case TOK_LB:

            if (!EmitElemOp(cx, pn2, op, cg))

                return JS_FALSE;

            break;

          default:

            JS_ASSERT(0);

        }

        break;



      case TOK_DELETE:

        /* Under ECMA 3, deleting a non-reference returns true. */

        pn2 = pn->pn_kid;

        switch (pn2->pn_type) {

          case TOK_NAME:

            pn2->pn_op = JSOP_DELNAME;

            if (!LookupArgOrVar(cx, &cg->treeContext, pn2))

                return JS_FALSE;

            op = pn2->pn_op;

            if (op == JSOP_FALSE) {

                if (js_Emit1(cx, cg, op) < 0)

                    return JS_FALSE;

            } else {

                if (!EmitAtomOp(cx, pn2, op, cg))

                    return JS_FALSE;

            }

            break;

          case TOK_DOT:

            if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg))

                return JS_FALSE;

            break;

          case TOK_LB:

            if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg))

                return JS_FALSE;

            break;

          default:

            if (js_Emit1(cx, cg, JSOP_TRUE) < 0)

                return JS_FALSE;

        }

        break;



      case TOK_DOT:

        /*

         * Pop a stack operand, convert it to object, get a property named by

         * this bytecode's immediate-indexed atom operand, and push its value

         * (not a reference to it).  This bytecode sets the virtual machine's

         * "obj" register to the left operand's ToObject conversion result,

         * for use by JSOP_PUSHOBJ.

         */

        return EmitPropOp(cx, pn, pn->pn_op, cg);



      case TOK_LB:

        /*

         * Pop two operands, convert the left one to object and the right one

         * to property name (atom or tagged int), get the named property, and

         * push its value.  Set the "obj" register to the result of ToObject

         * on the left operand.

         */

        return EmitElemOp(cx, pn, pn->pn_op, cg);



      case TOK_NEW:

      case TOK_LP:

        /*

         * Emit function call or operator new (constructor call) code.

         * First, emit code for the left operand to evaluate the callable or

         * constructable object expression.

         */

        pn2 = pn->pn_head;

        if (!js_EmitTree(cx, cg, pn2))

            return JS_FALSE;



        /* Remember start of callable-object bytecode for decompilation hint. */

        off = pn2->pn_offset;



        /*

         * Push the virtual machine's "obj" register, which was set by a name,

         * property, or element get (or set) bytecode.

         */

        if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)

            return JS_FALSE;



        /*

         * Emit code for each argument in order, then emit the JSOP_*CALL or

         * JSOP_NEW bytecode with a two-byte immediate telling how many args

         * were pushed on the operand stack.

         */

        for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) {

            if (!js_EmitTree(cx, cg, pn2))

                return JS_FALSE;

        }

        if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0)

            return JS_FALSE;

        argc = pn->pn_count - 1;

        if (js_Emit3(cx, cg, pn->pn_op, ARGC_HI(argc), ARGC_LO(argc)) < 0)

            return JS_FALSE;

        break;



#if JS_HAS_INITIALIZERS

      case TOK_RB:

        /*

         * Emit code for [a, b, c] of the form:

         *   t = new Array; t[0] = a; t[1] = b; t[2] = c; t;

         * but use a stack slot for t and avoid dup'ing and popping it via

         * the JSOP_NEWINIT and JSOP_INITELEM bytecodes.

         */

        ale = js_IndexAtom(cx, cx->runtime->atomState.ArrayAtom,

                           &cg->atomList);

        if (!ale)

            return JS_FALSE;

        EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));

        if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)

            return JS_FALSE;

        if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)

            return JS_FALSE;



        pn2 = pn->pn_head;

#if JS_HAS_SHARP_VARS

        if (pn2 && pn2->pn_type == TOK_DEFSHARP) {

            EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);

            pn2 = pn2->pn_next;

        }

#endif



        for (atomIndex = 0; pn2; pn2 = pn2->pn_next) {

            /* PrimaryExpr enforced ATOM_INDEX_LIMIT, so in-line optimize. */

            JS_ASSERT(atomIndex < ATOM_INDEX_LIMIT);

            if (atomIndex == 0) {

                if (js_Emit1(cx, cg, JSOP_ZERO) < 0)

                    return JS_FALSE;

            } else if (atomIndex == 1) {

                if (js_Emit1(cx, cg, JSOP_ONE) < 0)

                    return JS_FALSE;

            } else {

                EMIT_ATOM_INDEX_OP(JSOP_UINT16, (jsatomid)atomIndex);

            }



            /* Sub-optimal: holes in a sparse initializer are void-filled. */

            if (pn2->pn_type == TOK_COMMA) {

                if (js_Emit1(cx, cg, JSOP_PUSH) < 0)

                    return JS_FALSE;

            } else {

                if (!js_EmitTree(cx, cg, pn2))

                    return JS_FALSE;

            }

            if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)

                return JS_FALSE;



            atomIndex++;

        }



        if (pn->pn_extra) {

            /* Emit a source note so we know to decompile an extra comma. */

            if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0)

                return JS_FALSE;

        }



        /* Emit an op for sharp array cleanup and decompilation. */

        if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)

            return JS_FALSE;

        break;



      case TOK_RC:

        /*

         * Emit code for {p:a, '%q':b, 2:c} of the form:

         *   t = new Object; t.p = a; t['%q'] = b; t[2] = c; t;

         * but use a stack slot for t and avoid dup'ing and popping it via

         * the JSOP_NEWINIT and JSOP_INITELEM bytecodes.

         */

        ale = js_IndexAtom(cx, cx->runtime->atomState.ObjectAtom,

                           &cg->atomList);

        if (!ale)

            return JS_FALSE;

        EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));



        if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)

            return JS_FALSE;

        if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)

            return JS_FALSE;



        pn2 = pn->pn_head;

#if JS_HAS_SHARP_VARS

        if (pn2 && pn2->pn_type == TOK_DEFSHARP) {

            EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);

            pn2 = pn2->pn_next;

        }

#endif



        for (; pn2; pn2 = pn2->pn_next) {

            /* Emit an index for t[2], else map an atom for t.p or t['%q']. */

            pn3 = pn2->pn_left;

            switch (pn3->pn_type) {

              case TOK_NUMBER:

                if (!EmitNumberOp(cx, pn3->pn_dval, cg))

                    return JS_FALSE;

                break;

              case TOK_NAME:

              case TOK_STRING:

                ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList);

                if (!ale)

                    return JS_FALSE;

                break;

              default:

                JS_ASSERT(0);

            }



            /* Emit code for the property initializer. */

            if (!js_EmitTree(cx, cg, pn2->pn_right))

                return JS_FALSE;



#if JS_HAS_GETTER_SETTER

            op = pn2->pn_op;

            if (op == JSOP_GETTER || op == JSOP_SETTER) {

                if (js_Emit1(cx, cg, op) < 0)

                    return JS_FALSE;

            }

#endif

            /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */

            if (pn3->pn_type == TOK_NUMBER) {

                if (js_NewSrcNote(cx, cg, SRC_LABEL) < 0)

                    return JS_FALSE;

                if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)

                    return JS_FALSE;

            } else {

                EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale));

            }

        }



        /* Emit an op for sharpArray cleanup and decompilation. */

        if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)

            return JS_FALSE;

        break;



#if JS_HAS_SHARP_VARS

      case TOK_DEFSHARP:

        if (!js_EmitTree(cx, cg, pn->pn_kid))

            return JS_FALSE;

        EMIT_ATOM_INDEX_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num);

        break;



      case TOK_USESHARP:

        EMIT_ATOM_INDEX_OP(JSOP_USESHARP, (jsatomid) pn->pn_num);

        break;

#endif /* JS_HAS_SHARP_VARS */

#endif /* JS_HAS_INITIALIZERS */



      case TOK_RP:

        /*

         * The node for (e) has e as its kid, enabling users who want to nest

         * assignment expressions in conditions to avoid the error correction

         * done by Condition (from x = y to x == y) by double-parenthesizing.

         */

        if (!js_EmitTree(cx, cg, pn->pn_kid))

            return JS_FALSE;

        if (js_Emit1(cx, cg, JSOP_GROUP) < 0)

            return JS_FALSE;

        break;



      case TOK_NAME:

        if (!LookupArgOrVar(cx, &cg->treeContext, pn))

            return JS_FALSE;

        op = pn->pn_op;

        if (op == JSOP_ARGUMENTS) {

            if (js_Emit1(cx, cg, op) < 0)

                return JS_FALSE;

            break;

        }

        if (pn->pn_slot >= 0) {

            atomIndex = (jsatomid) pn->pn_slot;

            EMIT_ATOM_INDEX_OP(op, atomIndex);

            break;

        }

        /* FALL THROUGH */

      case TOK_STRING:

      case TOK_OBJECT:

        /*

         * The scanner and parser associate JSOP_NAME with TOK_NAME, although

         * other bytecodes may result instead (JSOP_BINDNAME/JSOP_SETNAME,

         * JSOP_FORNAME, etc.).  Among JSOP_*NAME* variants, only JSOP_NAME

         * may generate the first operand of a call or new expression, so only

         * it sets the "obj" virtual machine register to the object along the

         * scope chain in which the name was found.

         *

         * Token types for STRING and OBJECT have corresponding bytecode ops

         * in pn_op and emit the same format as NAME, so they share this code.

         */

        return EmitAtomOp(cx, pn, pn->pn_op, cg);



      case TOK_NUMBER:

        return EmitNumberOp(cx, pn->pn_dval, cg);



      case TOK_PRIMARY:

        return js_Emit1(cx, cg, pn->pn_op) >= 0;



#if JS_HAS_DEBUGGER_KEYWORD

      case TOK_DEBUGGER:

        return js_Emit1(cx, cg, JSOP_DEBUGGER) >= 0;

#endif /* JS_HAS_DEBUGGER_KEYWORD */



      default:

        JS_ASSERT(0);

    }



    return JS_TRUE;

}



JS_FRIEND_DATA(const char *) js_SrcNoteName[] = {

    "null",

    "if",

    "if-else",

    "while",

    "for",

    "continue",

    "var",

    "pcdelta",

    "assignop",

    "cond",

    "reserved0",

    "hidden",

    "pcbase",

    "label",

    "labelbrace",

    "endbrace",

    "break2label",

    "cont2label",

    "switch",

    "funcdef",

    "catch",

    "const",

    "newline",

    "setline",

    "xdelta"

};



uint8 js_SrcNoteArity[] = {

    0,  /* SRC_NULL */

    0,  /* SRC_IF */

    0,  /* SRC_IF_ELSE */

    0,  /* SRC_WHILE */

    3,  /* SRC_FOR */

    0,  /* SRC_CONTINUE */

    0,  /* SRC_VAR */

    1,  /* SRC_PCDELTA */

    0,  /* SRC_ASSIGNOP */

    0,  /* SRC_COND */

    0,  /* SRC_RESERVED0 */

    0,  /* SRC_HIDDEN */

    1,  /* SRC_PCBASE */

    1,  /* SRC_LABEL */

    1,  /* SRC_LABELBRACE */

    0,  /* SRC_ENDBRACE */

    1,  /* SRC_BREAK2LABEL */

    1,  /* SRC_CONT2LABEL */

    2,  /* SRC_SWITCH */

    1,  /* SRC_FUNCDEF */

    1,  /* SRC_CATCH */

    0,  /* SRC_CONST */

    0,  /* SRC_NEWLINE */

    1,  /* SRC_SETLINE */

    0   /* SRC_XDELTA */

};



static intN

AllocSrcNote(JSContext *cx, JSCodeGenerator *cg)

{

    intN index;

    JSArenaPool *pool;

    size_t size;



    index = cg->noteCount;

    if (((uintN)index & cg->noteMask) == 0) {

        pool = &cx->notePool;

        size = SRCNOTE_SIZE(cg->noteMask + 1);

        if (!cg->notes) {

            /* Allocate the first note array lazily; leave noteMask alone. */

            JS_ARENA_ALLOCATE_CAST(cg->notes, jssrcnote *, pool, size);

        } else {

            /* Grow by doubling note array size; update noteMask on success. */

            JS_ARENA_GROW_CAST(cg->notes, jssrcnote *, pool, size, size);

            if (cg->notes)

                cg->noteMask = (cg->noteMask << 1) | 1;

        }

        if (!cg->notes) {

            JS_ReportOutOfMemory(cx);

            return -1;

        }

    }



    cg->noteCount = index + 1;

    return index;

}



intN

js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type)

{

    intN index, n;

    jssrcnote *sn;

    ptrdiff_t offset, delta, xdelta;



    /*

     * Claim a note slot in cg->notes by growing it if necessary and then

     * incrementing cg->noteCount.

     */

    index = AllocSrcNote(cx, cg);

    sn = &cg->notes[index];



    /*

     * Compute delta from the last annotated bytecode's offset.  If it's too

     * big to fit in sn, allocate one or more xdelta notes and reset sn.

     */

    offset = CG_OFFSET(cg);

    delta = offset - cg->lastNoteOffset;

    cg->lastNoteOffset = offset;

    if (delta >= SN_DELTA_LIMIT) {

        do {

            xdelta = JS_MIN(delta, SN_XDELTA_MASK);

            SN_MAKE_XDELTA(sn, xdelta);

            delta -= xdelta;

            index = AllocSrcNote(cx, cg);

            if (index < 0)

                return -1;

            sn = &cg->notes[index];

        } while (delta >= SN_DELTA_LIMIT);

    }



    /*

     * Initialize type and delta, then allocate the minimum number of notes

     * needed for type's arity.  Usually, we won't need more, but if an offset

     * does take two bytes, js_SetSrcNoteOffset will grow cg->notes.

     */

    SN_MAKE_NOTE(sn, type, delta);

    for (n = (intN)js_SrcNoteArity[type]; n > 0; n--) {

        if (js_NewSrcNote(cx, cg, SRC_NULL) < 0)

            return -1;

    }

    return index;

}



intN

js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,

               ptrdiff_t offset)

{

    intN index;



    index = js_NewSrcNote(cx, cg, type);

    if (index >= 0) {

        if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset))

            return -1;

    }

    return index;

}



intN

js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,

               ptrdiff_t offset1, ptrdiff_t offset2)

{

    intN index;



    index = js_NewSrcNote(cx, cg, type);

    if (index >= 0) {

        if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset1))

            return -1;

        if (!js_SetSrcNoteOffset(cx, cg, index, 1, offset2))

            return -1;

    }

    return index;

}



static JSBool

GrowSrcNotes(JSContext *cx, JSCodeGenerator *cg)

{

    JSArenaPool *pool;

    size_t size;



    /* Grow by doubling note array size; update noteMask on success. */

    pool = &cx->notePool;

    size = SRCNOTE_SIZE(cg->noteMask + 1);

    JS_ARENA_GROW_CAST(cg->notes, jssrcnote *, pool, size, size);

    if (!cg->notes) {

        JS_ReportOutOfMemory(cx);

        return JS_FALSE;

    }

    cg->noteMask = (cg->noteMask << 1) | 1;

    return JS_TRUE;

}



uintN

js_SrcNoteLength(jssrcnote *sn)

{

    uintN arity;

    jssrcnote *base;



    arity = (intN)js_SrcNoteArity[SN_TYPE(sn)];

    for (base = sn++; arity--; sn++) {

        if (*sn & SN_3BYTE_OFFSET_FLAG)

            sn += 2;

    }

    return sn - base;

}



JS_FRIEND_API(ptrdiff_t)

js_GetSrcNoteOffset(jssrcnote *sn, uintN which)

{

    /* Find the offset numbered which (i.e., skip exactly which offsets). */

    JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);

    JS_ASSERT(which < js_SrcNoteArity[SN_TYPE(sn)]);

    for (sn++; which; sn++, which--) {

        if (*sn & SN_3BYTE_OFFSET_FLAG)

            sn += 2;

    }

    if (*sn & SN_3BYTE_OFFSET_FLAG) {

        return (ptrdiff_t)((((uint32)(sn[0] & SN_3BYTE_OFFSET_MASK)) << 16)

                           | (sn[1] << 8) | sn[2]);

    }

    return (ptrdiff_t)*sn;

}



JSBool

js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index,

                    uintN which, ptrdiff_t offset)

{

    jssrcnote *sn;

    ptrdiff_t diff;



    if (offset >= (((ptrdiff_t)SN_3BYTE_OFFSET_FLAG) << 16)) {

        ReportStatementTooLarge(cx, cg);

        return JS_FALSE;

    }



    /* Find the offset numbered which (i.e., skip exactly which offsets). */

    sn = &cg->notes[index];

    JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);

    JS_ASSERT(which < js_SrcNoteArity[SN_TYPE(sn)]);

    for (sn++; which; sn++, which--) {

        if (*sn & SN_3BYTE_OFFSET_FLAG)

            sn += 2;

    }



    /* See if the new offset requires three bytes. */

    if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) {

        /* Maybe this offset was already set to a three-byte value. */

        if (!(*sn & SN_3BYTE_OFFSET_FLAG)) {

            /* Losing, need to insert another two bytes for this offset. */

            index = PTRDIFF(sn, cg->notes, jssrcnote);



            /*

             * Simultaneously test to see if the source note array must grow to

             * accomodate either the first or second byte of additional storage

             * required by this 3-byte offset.

             */

            if (((cg->noteCount + 1) & cg->noteMask) <= 1) {

                if (!GrowSrcNotes(cx, cg))

                    return JS_FALSE;

                sn = cg->notes + index;

            }

            cg->noteCount += 2;



            diff = cg->noteCount - (index + 3);

            JS_ASSERT(diff >= 0);

            if (diff > 0)

                memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff));

        }

        *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16));

        *sn++ = (jssrcnote)(offset >> 8);

    }

    *sn = (jssrcnote)offset;

    return JS_TRUE;

}



jssrcnote *

js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg)

{

    uintN count;

    jssrcnote *tmp, *final;



    count = cg->noteCount;

    tmp   = cg->notes;

    final = (jssrcnote *) JS_malloc(cx, SRCNOTE_SIZE(count + 1));

    if (!final)

        return NULL;

    memcpy(final, tmp, SRCNOTE_SIZE(count));

    SN_MAKE_TERMINATOR(&final[count]);

    return final;

}



JSBool

js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg)

{

    size_t size, incr;

    ptrdiff_t delta;



    size = TRYNOTE_SIZE(cg->treeContext.tryCount);

    if (size <= cg->tryNoteSpace)

        return JS_TRUE;



    /*

     * Allocate trynotes from cx->tempPool.

     * XXX too much growing and we bloat, as other tempPool allocators block

     * in-place growth, and we never recycle old free space in an arena.

     */

    if (!cg->tryBase) {

        size = JS_ROUNDUP(size, TRYNOTE_SIZE(TRYNOTE_GRAIN));

        JS_ARENA_ALLOCATE_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size);

        if (!cg->tryBase)

            return JS_FALSE;

        cg->tryNoteSpace = size;

        cg->tryNext = cg->tryBase;

    } else {

        delta = PTRDIFF((char *)cg->tryNext, (char *)cg->tryBase, char);

        incr = size - cg->tryNoteSpace;

        incr = JS_ROUNDUP(incr, TRYNOTE_SIZE(TRYNOTE_GRAIN));

        size = cg->tryNoteSpace;

        JS_ARENA_GROW_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size, incr);

        if (!cg->tryBase)

            return JS_FALSE;

        cg->tryNoteSpace = size + incr;

        cg->tryNext = (JSTryNote *)((char *)cg->tryBase + delta);

    }

    return JS_TRUE;

}



JSTryNote *

js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start,

              ptrdiff_t end, ptrdiff_t catchStart)

{

    JSTryNote *tn;



    JS_ASSERT(cg->tryBase <= cg->tryNext);

    JS_ASSERT(catchStart >= 0);

    tn = cg->tryNext++;

    tn->start = start;

    tn->length = end - start;

    tn->catchStart = catchStart;

    return tn;

}



JSBool

js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote **tryp)

{

    uintN count;

    JSTryNote *tmp, *final;



    count = PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote);

    if (!count) {

        *tryp = NULL;

        return JS_TRUE;

    }



    tmp = cg->tryBase;

    final = (JSTryNote *) JS_malloc(cx, TRYNOTE_SIZE(count + 1));

    if (!final) {

        *tryp = NULL;

        return JS_FALSE;

    }

    memcpy(final, tmp, TRYNOTE_SIZE(count));

    final[count].start = 0;

    final[count].length = CG_OFFSET(cg);

    final[count].catchStart = 0;

    *tryp = final;

    return JS_TRUE;

}

 

**** End of jsemit.c ****

 

**** Start of jsemit.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsemit_h___

#define jsemit_h___

/*

 * JS bytecode generation.

 */



#include "jsstddef.h"

#include "jstypes.h"

#include "jsatom.h"

#include "jsopcode.h"

#include "jsprvtd.h"

#include "jspubtd.h"



JS_BEGIN_EXTERN_C



typedef enum JSStmtType {

    STMT_BLOCK        = 0,      /* compound statement: { s1[;... sN] } */

    STMT_LABEL        = 1,      /* labeled statement:  L: s */

    STMT_IF           = 2,      /* if (then) statement */

    STMT_ELSE         = 3,      /* else clause of if statement */

    STMT_SWITCH       = 4,      /* switch statement */

    STMT_WITH         = 5,      /* with statement */

    STMT_TRY          = 6,      /* try statement */

    STMT_CATCH        = 7,      /* catch block */

    STMT_FINALLY      = 8,      /* finally statement */

    STMT_DO_LOOP      = 9,      /* do/while loop statement */

    STMT_FOR_LOOP     = 10,     /* for loop statement */

    STMT_FOR_IN_LOOP  = 11,     /* for/in loop statement */

    STMT_WHILE_LOOP   = 12      /* while loop statement */

} JSStmtType;



#define STMT_IS_LOOP(stmt)      ((stmt)->type >= STMT_DO_LOOP)



typedef struct JSStmtInfo JSStmtInfo;



struct JSStmtInfo {

    JSStmtType      type;           /* statement type */

    ptrdiff_t       top;            /* offset of loop top from cg base */

    ptrdiff_t       update;         /* loop update offset (top if none) */

    ptrdiff_t       breaks;         /* offset of last break in loop */

    ptrdiff_t       continues;      /* offset of last continue in loop */

    ptrdiff_t       gosub;          /* offset of last GOSUB for this finally */

    ptrdiff_t       catchJump;      /* offset of last end-of-catch jump */

    JSAtom          *label;         /* name of LABEL or CATCH var */

    JSStmtInfo      *down;          /* info for enclosing statement */

};



#define SET_STATEMENT_TOP(stmt, top)                                         \

    ((stmt)->top = (stmt)->update = (top), (stmt)->breaks =                  \

     (stmt)->continues = (stmt)->catchJump = (stmt)->gosub = (-1))



struct JSTreeContext {              /* tree context for semantic checks */

    uint32          flags;          /* statement state flags, see below */

    uint32          tryCount;       /* total count of try statements parsed */

    JSStmtInfo      *topStmt;       /* top of statement info stack */

    JSAtomList      decls;          /* function, const, and var declarations */

    JSParseNode     *nodeList;      /* list of recyclable parse-node structs */

};



#define TCF_COMPILING          0x01 /* generating bytecode; this tc is a cg */

#define TCF_IN_FUNCTION        0x02 /* parsing inside function body */

#define TCF_RETURN_EXPR        0x04 /* function has 'return expr;' */

#define TCF_RETURN_VOID        0x08 /* function has 'return;' */

#define TCF_IN_FOR_INIT        0x10 /* parsing init expr of for; exclude 'in' */

#define TCF_FUN_CLOSURE_VS_VAR 0x20 /* function and var with same name */

#define TCF_FUN_USES_NONLOCALS 0x40 /* function refers to non-local names */

#define TCF_FUN_HEAVYWEIGHT    0x80 /* function needs Call object per call */

#define TCF_FUN_FLAGS          0xE0 /* flags to propagate from FunctionBody */



#define TREE_CONTEXT_INIT(tc)                                                 \

    ((tc)->flags = 0, (tc)->tryCount = 0, (tc)->topStmt = NULL,               \

     ATOM_LIST_INIT(&(tc)->decls), (tc)->nodeList = NULL)



#define TREE_CONTEXT_FINISH(tc)                                               \

    ((void)0)



struct JSCodeGenerator {

    JSTreeContext   treeContext;    /* base state: statement info stack, etc. */

    void            *codeMark;      /* low watermark in cx->codePool */

    void            *noteMark;      /* low watermark in cx->notePool */

    void            *tempMark;      /* low watermark in cx->tempPool */

    struct {

        jsbytecode  *base;          /* base of JS bytecode vector */

        jsbytecode  *limit;         /* one byte beyond end of bytecode */

        jsbytecode  *next;          /* pointer to next free bytecode */

    } prolog, main, *current;

    const char      *filename;      /* null or weak link to source filename */

    uintN           firstLine;      /* first line, for js_NewScriptFromCG */

    uintN           currentLine;    /* line number for tree-based srcnote gen */

    JSPrincipals    *principals;    /* principals for constant folding eval */

    JSAtomList      atomList;       /* literals indexed for mapping */

    intN            stackDepth;     /* current stack depth in script frame */

    uintN           maxStackDepth;  /* maximum stack depth so far */

    jssrcnote       *notes;         /* source notes, see below */

    uintN           noteCount;      /* number of source notes so far */

    uintN           noteMask;       /* growth increment for notes */

    ptrdiff_t       lastNoteOffset; /* code offset for last source note */

    JSTryNote       *tryBase;       /* first exception handling note */

    JSTryNote       *tryNext;       /* next available note */

    size_t          tryNoteSpace;   /* # of bytes allocated at tryBase */

};



#define CG_BASE(cg)             ((cg)->current->base)

#define CG_LIMIT(cg)            ((cg)->current->limit)

#define CG_NEXT(cg)             ((cg)->current->next)

#define CG_CODE(cg,offset)      (CG_BASE(cg) + (offset))

#define CG_OFFSET(cg)           PTRDIFF(CG_NEXT(cg), CG_BASE(cg), jsbytecode)



#define CG_PROLOG_BASE(cg)      ((cg)->prolog.base)

#define CG_PROLOG_LIMIT(cg)     ((cg)->prolog.limit)

#define CG_PROLOG_NEXT(cg)      ((cg)->prolog.next)

#define CG_PROLOG_CODE(cg,poff) (CG_PROLOG_BASE(cg) + (poff))

#define CG_PROLOG_OFFSET(cg)    PTRDIFF(CG_PROLOG_NEXT(cg), CG_PROLOG_BASE(cg),\

                                        jsbytecode)



#define CG_SWITCH_TO_MAIN(cg)   ((cg)->current = &(cg)->main)

#define CG_SWITCH_TO_PROLOG(cg) ((cg)->current = &(cg)->prolog)



/*

 * Initialize cg to allocate bytecode space from cx->codePool, source note

 * space from cx->notePool, and all other arena-allocated temporaries from

 * cx->tempPool.  Return true on success.  Report an error and return false

 * if the initial code segment can't be allocated.

 */

extern JS_FRIEND_API(JSBool)

js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg,

		     const char *filename, uintN lineno,

		     JSPrincipals *principals);



/*

 * Release cx->codePool, cx->notePool, and cx->tempPool to marks set by

 * js_InitCodeGenerator.  Note that cgs are magic: they own the arena pool

 * "tops-of-stack" space above their codeMark, noteMark, and tempMark points.

 * This means you cannot alloc from tempPool and save the pointer beyond the

 * next JS_FinishCodeGenerator.

 */

extern JS_FRIEND_API(void)

js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg);



/*

 * Emit one bytecode.

 */

extern ptrdiff_t

js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op);



/*

 * Emit two bytecodes, an opcode (op) with a byte of immediate operand (op1).

 */

extern ptrdiff_t

js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1);



/*

 * Emit three bytecodes, an opcode with two bytes of immediate operands.

 */

extern ptrdiff_t

js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1,

	 jsbytecode op2);



/*

 * Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand.

 */

extern ptrdiff_t

js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra);



/*

 * Unsafe macro to call js_SetJumpOffset and return false if it does.

 */

#define CHECK_AND_SET_JUMP_OFFSET(cx,cg,pc,off)                               \

    JS_BEGIN_MACRO                                                            \

	if (!js_SetJumpOffset(cx, cg, pc, off))                               \

	    return JS_FALSE;                                                  \

    JS_END_MACRO



#define CHECK_AND_SET_JUMP_OFFSET_AT(cx,cg,off)                               \

    CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,off), CG_OFFSET(cg) - (off))



extern JSBool

js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc,

		 ptrdiff_t off);



/* Test whether we're in a with statement. */

extern JSBool

js_InWithStatement(JSTreeContext *tc);



/* Test whether we're in a catch block with exception named by atom. */

extern JSBool

js_InCatchBlock(JSTreeContext *tc, JSAtom *atom);



/*

 * Push the C-stack-allocated struct at stmt onto the stmtInfo stack.

 */

extern void

js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,

		 ptrdiff_t top);



/*

 * Emit a break instruction, recording it for backpatching.

 */

extern ptrdiff_t

js_EmitBreak(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt,

	     JSAtomListElement *label);



/*

 * Emit a continue instruction, recording it for backpatching.

 */

extern ptrdiff_t

js_EmitContinue(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt,

		JSAtomListElement *label);



/*

 * Pop tc->topStmt.  If the top JSStmtInfo struct is not stack-allocated, it

 * is up to the caller to free it.

 */

extern void

js_PopStatement(JSTreeContext *tc);



/*

 * Like js_PopStatement(&cg->treeContext), also patch breaks and continues.

 * May fail if a jump offset overflows.

 */

extern JSBool

js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg);



/*

 * Emit code into cg for the tree rooted at pn.

 */

extern JSBool

js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn);



/*

 * Emit code into cg for the tree rooted at body, then create a persistent

 * script for fun from cg.

 */

extern JSBool

js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body,

		    JSFunction *fun);



/*

 * Source notes generated along with bytecode for decompiling and debugging.

 * A source note is a uint8 with 5 bits of type and 3 of offset from the pc of

 * the previous note.  If 3 bits of offset aren't enough, extended delta notes

 * (SRC_XDELTA) consisting of 2 set high order bits followed by 6 offset bits

 * are emitted before the next note.  Some notes have operand offsets encoded

 * immediately after them, in note bytes or byte-triples.

 *

 *                 Source Note               Extended Delta

 *              +7-6-5-4-3+2-1-0+           +7-6-5+4-3-2-1-0+

 *              |note-type|delta|           |1 1| ext-delta |

 *              +---------+-----+           +---+-----------+

 *

 * At most one "gettable" note (i.e., a note of type other than SRC_NEWLINE,

 * SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode.

 *

 * NB: the js_SrcNoteName and js_SrcNoteArity arrays in jsemit.c are indexed

 * by this enum, so their initializers need to match the order here.

 */

typedef enum JSSrcNoteType {

    SRC_NULL        = 0,        /* terminates a note vector */

    SRC_IF          = 1,        /* JSOP_IFEQ bytecode is from an if-then */

    SRC_IF_ELSE     = 2,        /* JSOP_IFEQ bytecode is from an if-then-else */

    SRC_WHILE       = 3,        /* JSOP_IFEQ is from a while loop */

    SRC_FOR         = 4,        /* JSOP_NOP or JSOP_POP in for loop head */

    SRC_CONTINUE    = 5,        /* JSOP_GOTO is a continue, not a break;

                                   also used on JSOP_ENDINIT if extra comma

                                   at end of array literal: [1,2,,] */

    SRC_VAR         = 6,        /* JSOP_NAME/SETNAME/FORNAME in a var decl */

    SRC_PCDELTA     = 7,        /* offset from comma-operator to next POP,

				   or from CONDSWITCH to first CASE opcode */

    SRC_ASSIGNOP    = 8,        /* += or another assign-op follows */

    SRC_COND        = 9,        /* JSOP_IFEQ is from conditional ?: operator */

    SRC_RESERVED0   = 10,       /* reserved for future use */

    SRC_HIDDEN      = 11,       /* opcode shouldn't be decompiled */

    SRC_PCBASE      = 12,       /* offset of first obj.prop.subprop bytecode */

    SRC_LABEL       = 13,       /* JSOP_NOP for label: with atomid immediate */

    SRC_LABELBRACE  = 14,       /* JSOP_NOP for label: {...} begin brace */

    SRC_ENDBRACE    = 15,       /* JSOP_NOP for label: {...} end brace */

    SRC_BREAK2LABEL = 16,       /* JSOP_GOTO for 'break label' with atomid */

    SRC_CONT2LABEL  = 17,       /* JSOP_GOTO for 'continue label' with atomid */

    SRC_SWITCH      = 18,       /* JSOP_*SWITCH with offset to end of switch,

				   2nd off to first JSOP_CASE if condswitch */

    SRC_FUNCDEF     = 19,       /* JSOP_NOP for function f() with atomid */

    SRC_CATCH       = 20,       /* catch block has guard */

    SRC_CONST       = 21,       /* JSOP_SETCONST in a const decl */

    SRC_NEWLINE     = 22,       /* bytecode follows a source newline */

    SRC_SETLINE     = 23,       /* a file-absolute source line number note */

    SRC_XDELTA      = 24        /* 24-31 are for extended delta notes */

} JSSrcNoteType;



#define SN_TYPE_BITS            5

#define SN_DELTA_BITS           3

#define SN_XDELTA_BITS          6

#define SN_TYPE_MASK            (JS_BITMASK(SN_TYPE_BITS) << SN_DELTA_BITS)

#define SN_DELTA_MASK           ((ptrdiff_t)JS_BITMASK(SN_DELTA_BITS))

#define SN_XDELTA_MASK          ((ptrdiff_t)JS_BITMASK(SN_XDELTA_BITS))



#define SN_MAKE_NOTE(sn,t,d)    (*(sn) = (jssrcnote)                          \

					  (((t) << SN_DELTA_BITS)             \

					   | ((d) & SN_DELTA_MASK)))

#define SN_MAKE_XDELTA(sn,d)    (*(sn) = (jssrcnote)                          \

					  ((SRC_XDELTA << SN_DELTA_BITS)      \

					   | ((d) & SN_XDELTA_MASK)))



#define SN_IS_XDELTA(sn)        ((*(sn) >> SN_DELTA_BITS) >= SRC_XDELTA)

#define SN_TYPE(sn)             (SN_IS_XDELTA(sn) ? SRC_XDELTA                \

						  : *(sn) >> SN_DELTA_BITS)

#define SN_SET_TYPE(sn,type)    SN_MAKE_NOTE(sn, type, SN_DELTA(sn))

#define SN_IS_GETTABLE(sn)      (SN_TYPE(sn) < SRC_NEWLINE)



#define SN_DELTA(sn)            ((ptrdiff_t)(SN_IS_XDELTA(sn)                 \

					     ? *(sn) & SN_XDELTA_MASK         \

					     : *(sn) & SN_DELTA_MASK))

#define SN_SET_DELTA(sn,delta)  (SN_IS_XDELTA(sn)                             \

				 ? SN_MAKE_XDELTA(sn, delta)                  \

				 : SN_MAKE_NOTE(sn, SN_TYPE(sn), delta))



#define SN_DELTA_LIMIT          ((ptrdiff_t)JS_BIT(SN_DELTA_BITS))

#define SN_XDELTA_LIMIT         ((ptrdiff_t)JS_BIT(SN_XDELTA_BITS))



/*

 * Offset fields follow certain notes and are frequency-encoded: an offset in

 * [0,0x7f] consumes one byte, an offset in [0x80,0x7fffff] takes three, and

 * the high bit of the first byte is set.

 */

#define SN_3BYTE_OFFSET_FLAG    0x80

#define SN_3BYTE_OFFSET_MASK    0x7f



extern JS_FRIEND_DATA(const char *) js_SrcNoteName[];

extern JS_FRIEND_DATA(uint8)        js_SrcNoteArity[];

extern JS_FRIEND_API(uintN)         js_SrcNoteLength(jssrcnote *sn);



#define SN_LENGTH(sn)           ((js_SrcNoteArity[SN_TYPE(sn)] == 0) ? 1      \

				 : js_SrcNoteLength(sn))

#define SN_NEXT(sn)             ((sn) + SN_LENGTH(sn))



/* A source note array is terminated by an all-zero element. */

#define SN_MAKE_TERMINATOR(sn)  (*(sn) = SRC_NULL)

#define SN_IS_TERMINATOR(sn)    (*(sn) == SRC_NULL)



/*

 * Append a new source note of the given type (and therefore size) to cg's

 * notes dynamic array, updating cg->noteCount.  Return the new note's index

 * within the array pointed at by cg->notes.  Return -1 if out of memory.

 */

extern intN

js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type);



extern intN

js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,

	       ptrdiff_t offset);



extern intN

js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,

	       ptrdiff_t offset1, ptrdiff_t offset2);



/*

 * Get and set the offset operand identified by which (0 for the first, etc.).

 */

extern JS_FRIEND_API(ptrdiff_t)

js_GetSrcNoteOffset(jssrcnote *sn, uintN which);



extern JSBool

js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index,

		    uintN which, ptrdiff_t offset);



/*

 * Finish taking source notes in cx's notePool by copying them to new

 * stable store allocated via JS_malloc.  Return null on malloc failure,

 * which means this function reported an error.

 */

extern jssrcnote *

js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg);



/*

 * Allocate cg->treeContext.tryCount notes (plus one for the end sentinel)

 * from cx->tempPool and set up cg->tryBase/tryNext for exactly tryCount

 * js_NewTryNote calls.  The storage is freed by js_FinishCodeGenerator.

 */

extern JSBool

js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg);



/*

 * Grab the next trynote slot in cg, filling it in appropriately.

 */

extern JSTryNote *

js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start,

	      ptrdiff_t end, ptrdiff_t catchStart);



/*

 * Finish generating exception information, and copy it to JS_malloc

 * storage.

 */

extern JSBool

js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote **tryp);



JS_END_EXTERN_C



#endif /* jsemit_h___ */

 

**** End of jsemit.h ****

 

**** Start of jsexn.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS standard exception implementation.

 */



#include <string.h>

#include "jsstddef.h"

#include "jstypes.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jsprf.h"

#include "jsapi.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsexn.h"

#include "jsfun.h"

#include "jsnum.h"



#if JS_HAS_ERROR_EXCEPTIONS

#if !JS_HAS_EXCEPTIONS

# error "JS_HAS_EXCEPTIONS must be defined to use JS_HAS_ERROR_EXCEPTIONS"

#endif



/* XXX consider adding rt->atomState.messageAtom */

static char js_message_str[]  = "message";

static char js_filename_str[] = "fileName";

static char js_lineno_str[]   = "lineNumber";



/* Forward declarations for ExceptionClass's initializer. */

static JSBool

Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);



static void

exn_finalize(JSContext *cx, JSObject *obj);



static JSClass ExceptionClass = {

    "Exception",

    JSCLASS_HAS_PRIVATE,

    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,

    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   exn_finalize,

    NULL,             NULL,             NULL,             Exception,

    NULL,             NULL,             NULL,             0

};



/*

 * A copy of the JSErrorReport originally generated.

 */

typedef struct JSExnPrivate {

    JSErrorReport *errorReport;

} JSExnPrivate;



/*

 * Undo all the damage done by exn_newPrivate.

 */

static void

exn_destroyPrivate(JSContext *cx, JSExnPrivate *privateData)

{

    JSErrorReport *report;

    const jschar **args;



    if (!privateData) 

        return;

    report = privateData->errorReport;

    if (report) {

        if (report->uclinebuf)

	    JS_free(cx, (void *)report->uclinebuf);

        if (report->filename)

	    JS_free(cx, (void *)report->filename);

        if (report->ucmessage)

	    JS_free(cx, (void *)report->ucmessage);

        if (report->messageArgs) {

            args = report->messageArgs;

            while (*args != NULL)

                JS_free(cx, (void *)*args++);

            JS_free(cx, (void *)report->messageArgs);

        }

        JS_free(cx, report);

    }

    JS_free(cx, privateData);

}



/*

 * Copy everything interesting about an error into allocated memory.

 */

static JSExnPrivate *

exn_newPrivate(JSContext *cx, JSErrorReport *report)

{

    intN i;

    JSExnPrivate *newPrivate;

    JSErrorReport *newReport;

    size_t capacity;



    newPrivate = (JSExnPrivate *)JS_malloc(cx, sizeof (JSExnPrivate));

    if (!newPrivate)

        return NULL;

    memset(newPrivate, 0, sizeof (JSExnPrivate));



    /* Copy the error report */

    newReport = (JSErrorReport *)JS_malloc(cx, sizeof (JSErrorReport));

    if (!newReport)

        goto error;

    memset(newReport, 0, sizeof (JSErrorReport));

    newPrivate->errorReport = newReport;

    

    if (report->filename != NULL) {

        newReport->filename = JS_strdup(cx, report->filename);

        if (!newReport->filename)

            goto error;

    } else {

        newReport->filename = NULL;

    }



    newReport->lineno = report->lineno;



    /*

     * We don't need to copy linebuf and tokenptr, because they

     * point into the deflated string cache.  (currently?)

     */

    newReport->linebuf = report->linebuf;

    newReport->tokenptr = report->tokenptr;



    /*

     * But we do need to copy uclinebuf, uctokenptr, because they're

     * pointers into internal tokenstream structs, and may go away.

     *

     * NOTE nothing uses this and I'm not really maintaining it until

     * I know it's the desired API.

     */

    if (report->uclinebuf != NULL) {

	capacity = js_strlen(report->uclinebuf) + 1;

        newReport->uclinebuf =

            (const jschar *)JS_malloc(cx, capacity * sizeof(jschar));

        if (!newReport->uclinebuf)

            goto error;

	js_strncpy((jschar *)newReport->uclinebuf, report->uclinebuf, capacity);

	newReport->uctokenptr = newReport->uclinebuf + (report->uctokenptr -

							report->uclinebuf);

    } else {

	newReport->uclinebuf = newReport->uctokenptr = NULL;

    }



    if (report->ucmessage != NULL) {

        capacity = js_strlen(report->ucmessage) + 1;

        newReport->ucmessage = (const jschar *)JS_malloc(cx, capacity * sizeof(jschar));

        if (!newReport->ucmessage)

            goto error;

        js_strncpy((jschar *)newReport->ucmessage, report->ucmessage, capacity);



        if (report->messageArgs) {

            for (i = 0; report->messageArgs[i] != NULL; i++)

                ;

            JS_ASSERT(i);

            newReport->messageArgs =

                (const jschar **)JS_malloc(cx, (i + 1) * sizeof(jschar *));

            if (!newReport->messageArgs)

                goto error;

            for (i = 0; report->messageArgs[i] != NULL; i++) {

                capacity = js_strlen(report->messageArgs[i]) + 1;

                newReport->messageArgs[i] =

                    (const jschar *)JS_malloc(cx, capacity * sizeof(jschar));

                if (!newReport->messageArgs[i])

                    goto error;

                js_strncpy((jschar *)(newReport->messageArgs[i]),

                           report->messageArgs[i], capacity);

            }

            newReport->messageArgs[i] = NULL;

        } else {

            newReport->messageArgs = NULL;

        }

    } else {

        newReport->ucmessage = NULL;

        newReport->messageArgs = NULL;

    }

    newReport->errorNumber = report->errorNumber;



    /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */

    newReport->flags = report->flags;



    return newPrivate;

error:

    exn_destroyPrivate(cx, newPrivate);

    return NULL;

}



static void

exn_finalize(JSContext *cx, JSObject *obj)

{

    JSExnPrivate *privateData;

    jsval privateValue;



    privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);



    if (privateValue != JSVAL_NULL) {

        privateData = (JSExnPrivate*) JSVAL_TO_PRIVATE(privateValue);

        if (privateData)

            exn_destroyPrivate(cx, privateData);

    }

}



JSErrorReport *

js_ErrorFromException(JSContext *cx, jsval exn)

{

    JSObject *obj;

    JSExnPrivate *privateData;

    jsval privateValue;



    if (JSVAL_IS_PRIMITIVE(exn))

        return NULL;

    obj = JSVAL_TO_OBJECT(exn);

    if (OBJ_GET_CLASS(cx, obj) != &ExceptionClass)

        return NULL;

    privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);

    if (privateValue == JSVAL_NULL)

        return NULL;

    privateData = (JSExnPrivate*) JSVAL_TO_PRIVATE(privateValue);

    if (!privateData)

        return NULL;



    JS_ASSERT(privateData->errorReport);

    return privateData->errorReport;

}



/*

 * This must be kept in synch with the exceptions array below.

 * XXX use a jsexn.tbl file a la jsopcode.tbl

 */

typedef enum JSExnType {

    JSEXN_NONE = -1,

      JSEXN_ERR,

	JSEXN_INTERNALERR,

	JSEXN_EVALERR,

	JSEXN_RANGEERR,

	JSEXN_REFERENCEERR,

	JSEXN_SYNTAXERR,

	JSEXN_TYPEERR,

	JSEXN_URIERR,

	JSEXN_LIMIT

} JSExnType;



struct JSExnSpec {

    int protoIndex;

    const char *name;

    JSNative native;

};



/*

 * All *Error constructors share the same JSClass, ExceptionClass.  But each

 * constructor function for an *Error class must have a distinct native 'call'

 * function pointer, in order for instanceof to work properly across multiple

 * standard class sets.  See jsfun.c:fun_hasInstance.

 */

#define MAKE_EXCEPTION_CTOR(name)                                             \

const char js_##name##_str[] = #name;                                         \

static JSBool                                                                 \

name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)      \

{                                                                             \

    return Exception(cx, obj, argc, argv, rval);                              \

}



MAKE_EXCEPTION_CTOR(Error)

MAKE_EXCEPTION_CTOR(InternalError)

MAKE_EXCEPTION_CTOR(EvalError)

MAKE_EXCEPTION_CTOR(RangeError)

MAKE_EXCEPTION_CTOR(ReferenceError)

MAKE_EXCEPTION_CTOR(SyntaxError)

MAKE_EXCEPTION_CTOR(TypeError)

MAKE_EXCEPTION_CTOR(URIError)



#undef MAKE_EXCEPTION_CTOR



static struct JSExnSpec exceptions[] = {

    { JSEXN_NONE,       js_Error_str,           Error },

    { JSEXN_ERR,        js_InternalError_str,   InternalError },

    { JSEXN_ERR,        js_EvalError_str,       EvalError },

    { JSEXN_ERR,        js_RangeError_str,      RangeError },

    { JSEXN_ERR,        js_ReferenceError_str,  ReferenceError },

    { JSEXN_ERR,        js_SyntaxError_str,     SyntaxError },

    { JSEXN_ERR,        js_TypeError_str,       TypeError },

    { JSEXN_ERR,        js_URIError_str,        URIError },

    {0,0,NULL}

};



static JSBool

Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSBool ok;

    jsval pval;

    int32 lineno;

    JSString *message, *filename;

    

    if (!cx->fp->constructing) {

        /*

         * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when

         * called as functions, without operator new.  But as we do not give

         * each constructor a distinct JSClass, whose .name member is used by

         * js_NewObject to find the class prototype, we must get the class

         * prototype ourselves.

         */

        if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]),

                              (jsid)cx->runtime->atomState.classPrototypeAtom,

                              &pval)) {

            return JS_FALSE;

        }

        obj = js_NewObject(cx, &ExceptionClass, JSVAL_TO_OBJECT(pval), NULL);

        if (!obj)

            return JS_FALSE;

        *rval = OBJECT_TO_JSVAL(obj);

    }



    /*

     * If it's a new object of class Exception, then null out the private

     * data so that the finalizer doesn't attempt to free it.

     */

    if (OBJ_GET_CLASS(cx, obj) == &ExceptionClass)

        OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, JSVAL_NULL);



    /* Set the 'message' property. */

    if (argc > 0) {

        message = js_ValueToString(cx, argv[0]);

        if (!message)

            return JS_FALSE;

    } else {

        message = cx->runtime->emptyString;

    }

    ok = JS_DefineProperty(cx, obj, js_message_str, STRING_TO_JSVAL(message),

                           NULL, NULL, JSPROP_ENUMERATE);

    if (!ok)

        return JS_FALSE;



    /* Set the 'fileName' property. */

    if (argc > 1) {

        filename = js_ValueToString(cx, argv[1]);

        if (!filename)

            return JS_FALSE;

    } else {

        filename = cx->runtime->emptyString;

    }

    ok = JS_DefineProperty(cx, obj, js_filename_str,

                           STRING_TO_JSVAL(filename),

                           NULL, NULL, JSPROP_ENUMERATE);

    if (!ok)

        return JS_FALSE;



    /* Set the 'lineNumber' property. */

    if (argc > 2) {

        ok = js_ValueToInt32(cx, argv[2], &lineno);

        if (!ok)

            return JS_FALSE;

    } else {

        lineno = 0;

    }

    return JS_DefineProperty(cx, obj, js_lineno_str,

                             INT_TO_JSVAL(lineno),

                             NULL, NULL, JSPROP_ENUMERATE);

    

}



/*

 * Convert to string.

 *

 * This method only uses JavaScript-modifiable properties name, message.  It

 * is left to the host to check for private data and report filename and line

 * number information along with this message.

 */

static JSBool

exn_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsval v;

    JSString *name, *message, *result;

    jschar *chars, *cp;

    size_t length;



    if (!OBJ_GET_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.nameAtom, &v))

        return JS_FALSE;

    name = js_ValueToString(cx, v);

    if (!name)

        return JS_FALSE;



    if (!JS_GetProperty(cx, obj, js_message_str, &v) ||

        !(message = js_ValueToString(cx, v))) {

        return JS_FALSE;

    }



    if (message->length > 0) {

        length = name->length + message->length + 2;

        cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar));

        if (!chars)

            return JS_FALSE;



        js_strncpy(cp, name->chars, name->length);

        cp += name->length;

        *cp++ = ':'; *cp++ = ' ';

        js_strncpy(cp, message->chars, message->length);

        cp += message->length;

        *cp = 0;



        result = js_NewString(cx, chars, length, 0);

        if (!result) {

            JS_free(cx, chars);

            return JS_FALSE;

        }

    } else {

        result = name;

    }



    *rval = STRING_TO_JSVAL(result);

    return JS_TRUE;

}



#if JS_HAS_TOSOURCE

/*

 * Return a string that may eval to something similar to the original object.

 */

static JSBool

exn_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsval v;

    int32 lineno;

    JSString *name, *message, *filename, *lineno_as_str, *result;

    jschar *chars, *cp;

    size_t length;



    if (!OBJ_GET_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.nameAtom, &v))

        return JS_FALSE;

    name = js_ValueToString(cx, v);

    if (!name)

        return JS_FALSE;



    if (!JS_GetProperty(cx, obj, js_message_str, &v) ||

        !(message = js_ValueToString(cx, v))) {

        return JS_FALSE;

    }



    if (!JS_GetProperty(cx, obj, js_filename_str, &v) ||

        !(filename = js_ValueToString(cx, v))) {

        return JS_FALSE;

    }



    if (!JS_GetProperty(cx, obj, js_lineno_str, &v) ||

        !js_ValueToInt32 (cx, v, &lineno)) {

        return JS_FALSE;

    }



    if (lineno != 0) {

        if (!(lineno_as_str = js_ValueToString(cx, v))) {

            return JS_FALSE;

        }

    } else {

        lineno_as_str = NULL;

    }    

    

    length = (message->length > 0) ? name->length + message->length + 10

        : name->length + 8;



    if (filename->length > 0) {

        /* append filename as ``, "{filename}"'' */

        length += 4 + filename->length;

        if (lineno_as_str) {

            /* append lineno as ``, {lineno_as_str}'' */

            length += 2 + lineno_as_str->length;

        }

    } else {

        if (lineno_as_str) {

            /*

             * no filename, but have line number,

             * need to append ``, "", {lineno_as_str}''

             */

            length += 6 + lineno_as_str->length;

        }

    }



    cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar));

    if (!chars)

        return JS_FALSE;



    *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' ';

    js_strncpy(cp, name->chars, name->length);

    cp += name->length;

    *cp++ = '(';

    if (message->length > 0) {

        *cp++ = '"';

        js_strncpy(cp, message->chars, message->length);

        cp += message->length;

        *cp++ = '"';

    }



    if (filename->length > 0) {

        /* append filename as ``, "{filename}"'' */

        *cp++ = ','; *cp++ = ' '; *cp++ = '"';

        js_strncpy(cp, filename->chars, filename->length);

        cp += filename->length;

        *cp++ = '"';

        if (lineno_as_str) {

            /* append lineno as ``, {lineno_as_str}'' */

            *cp++ = ','; *cp++ = ' ';

            js_strncpy(cp, lineno_as_str->chars, lineno_as_str->length);

            cp += lineno_as_str->length;

        }

    } else {

        if (lineno_as_str) {

            /*

             * no filename, but have line number,

             * need to append ``, "", {lineno_as_str}''

             */

            *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"';

            *cp++ = ','; *cp++ = ' ';

            js_strncpy(cp, lineno_as_str->chars, lineno_as_str->length);

            cp += lineno_as_str->length;

        }

    }



    *cp++ = ')'; *cp++ = ')'; *cp = 0;



    result = js_NewString(cx, chars, length, 0);

    if (!result) {

        JS_free(cx, chars);

        return JS_FALSE;

    }



    *rval = STRING_TO_JSVAL(result);

    return JS_TRUE;

}

#endif



static JSFunctionSpec exception_methods[] = {

#if JS_HAS_TOSOURCE

    {js_toSource_str,   exn_toSource,           0,0,0},

#endif

    {js_toString_str,   exn_toString,           0,0,0},

    {0,0,0,0,0}

};



JSObject *

js_InitExceptionClasses(JSContext *cx, JSObject *obj)

{

    int i;

    JSObject *protos[JSEXN_LIMIT];



    /* Initialize the prototypes first. */

    for (i = 0; exceptions[i].name != 0; i++) {

        JSAtom *atom;

        JSFunction *fun;

        JSString *nameString;

        int protoIndex = exceptions[i].protoIndex;



        /* Make the prototype for the current constructor name. */

        protos[i] = js_NewObject(cx, &ExceptionClass,

                                 (protoIndex != JSEXN_NONE)

                                 ? protos[protoIndex]

                                 : NULL,

                                 obj);

        if (!protos[i])

            return NULL;



        /* So exn_finalize knows whether to destroy private data. */

        OBJ_SET_SLOT(cx, protos[i], JSSLOT_PRIVATE, JSVAL_NULL);



        atom = js_Atomize(cx, exceptions[i].name, strlen(exceptions[i].name), 0);

        if (!atom)

            return NULL;



        /* Make a constructor function for the current name. */

        fun = js_DefineFunction(cx, obj, atom, exceptions[i].native, 1, 0);

        if (!fun)

            return NULL;



        /* Make this constructor make objects of class Exception. */

        fun->clasp = &ExceptionClass;



        /* Make the prototype and constructor links. */

        if (!js_SetClassPrototype(cx, fun->object, protos[i],

                                  JSPROP_READONLY | JSPROP_PERMANENT)) {

            return NULL;

        }



        /* proto bootstrap bit from JS_InitClass omitted. */

        nameString = JS_NewStringCopyZ(cx, exceptions[i].name);

        if (!nameString)

            return NULL;



        /* Add the name property to the prototype. */

        if (!JS_DefineProperty(cx, protos[i], js_name_str,

                               STRING_TO_JSVAL(nameString),

                               NULL, NULL,

                               JSPROP_ENUMERATE)) {

            return NULL;

        }

    }



    /*

     * Add an empty message property.  (To Exception.prototype only,

     * because this property will be the same for all the exception

     * protos.)

     */

    if (!JS_DefineProperty(cx, protos[0], js_message_str,

                           STRING_TO_JSVAL(cx->runtime->emptyString),

                           NULL, NULL, JSPROP_ENUMERATE)) {

        return NULL;

    }

    if (!JS_DefineProperty(cx, protos[0], js_filename_str,

                           STRING_TO_JSVAL(cx->runtime->emptyString),

                           NULL, NULL, JSPROP_ENUMERATE)) {

        return NULL;

    }

    if (!JS_DefineProperty(cx, protos[0], js_lineno_str,

                           INT_TO_JSVAL(0),

                           NULL, NULL, JSPROP_ENUMERATE)) {

        return NULL;

    }



    /*

     * Add methods only to Exception.prototype, because ostensibly all

     * exception types delegate to that.

     */

    if (!JS_DefineFunctions(cx, protos[0], exception_methods))

        return NULL;



    return protos[0];

}



static JSExnType errorToExceptionNum[] = {

#define MSG_DEF(name, number, count, exception, format) \

    exception,

#include "js.msg"

#undef MSG_DEF

};



#if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES )

/* For use below... get character strings for error name and exception name */

static struct exnname { char *name; char *exception; } errortoexnname[] = {

#define MSG_DEF(name, number, count, exception, format) \

    {#name, #exception},

#include "js.msg"

#undef MSG_DEF

};

#endif /* DEBUG */



JSBool

js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp)

{

    JSErrNum errorNumber;

    JSObject *errObject, *errProto;

    JSExnType exn;

    JSExnPrivate *privateData;

    JSString *msgstr, *fnamestr;



    /* Find the exception index associated with this error. */

    JS_ASSERT(reportp);

    if (JSREPORT_IS_WARNING(reportp->flags))

        return JS_FALSE;

    errorNumber = (JSErrNum) reportp->errorNumber;

    exn = errorToExceptionNum[errorNumber];

    JS_ASSERT(exn < JSEXN_LIMIT);



#if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )

    /* Print the error name and the associated exception name to stderr */

    fprintf(stderr, "%s\t%s\n",

	    errortoexnname[errorNumber].name,

	    errortoexnname[errorNumber].exception);

#endif



    /*

     * Return false (no exception raised) if no exception is associated

     * with the given error number.

     */

    if (exn == JSEXN_NONE)

	return JS_FALSE;



    /*

     * Try to get an appropriate prototype by looking up the corresponding

     * exception constructor name in the current context.  If the constructor

     * has been deleted or overwritten, this may fail or return NULL, and

     * js_NewObject will fall back to using Object.prototype.

     */

    if (!js_GetClassPrototype(cx, exceptions[exn].name, &errProto))

        errProto = NULL;



    /*

     * Use js_NewObject instead of js_ConstructObject, because

     * js_ConstructObject seems to require a frame.

     */

    errObject = js_NewObject(cx, &ExceptionClass, errProto, NULL);

    if (!errObject)

        return JS_FALSE;



    /* Store 'message' as a javascript-visible value. */

    msgstr = JS_NewStringCopyZ(cx, message);

    if (!msgstr)

        return JS_FALSE;

    if (!JS_DefineProperty(cx, errObject, js_message_str,

                           STRING_TO_JSVAL(msgstr), NULL, NULL,

                           JSPROP_ENUMERATE)) {

        return JS_FALSE;

    }



    if (reportp) {

        if (reportp->filename) {

            fnamestr = JS_NewStringCopyZ(cx, reportp->filename);

            if (!fnamestr)

                return JS_FALSE;

            /* Store 'filename' as a javascript-visible value. */

            if (!JS_DefineProperty(cx, errObject, js_filename_str,

                                   STRING_TO_JSVAL(fnamestr), NULL, NULL,

                                   JSPROP_ENUMERATE)) {

                return JS_FALSE;

            }

            /* Store 'lineno' as a javascript-visible value. */

            if (!JS_DefineProperty(cx, errObject, js_lineno_str,

                                   INT_TO_JSVAL((int)reportp->lineno), NULL,

                                   NULL, JSPROP_ENUMERATE)) {

                return JS_FALSE;

            }

        }

        

    }

    

    /*

     * Construct a new copy of the error report, and store it in the

     * exception objects' private data.  We can't use the error report

     * handed in, because it's stack-allocated, and may point to transient

     * data in the JSTokenStream.

     */

    privateData = exn_newPrivate(cx, reportp);

    OBJ_SET_SLOT(cx, errObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(privateData));



    /* Set the generated Exception object as the current exception. */

    JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));



    /* Flag the error report passed in to indicate an exception was raised. */

    reportp->flags |= JSREPORT_EXCEPTION;



    return JS_TRUE;

}

#endif /* JS_HAS_ERROR_EXCEPTIONS */



#if JS_HAS_EXCEPTIONS



extern JSBool

js_ReportUncaughtException(JSContext *cx)

{

    JSObject *exnObject;

    JSString *str;

    jsval exn;

    JSErrorReport *reportp;

    const char *bytes;



    if (!JS_IsExceptionPending(cx))

        return JS_FALSE;



    if (!JS_GetPendingException(cx, &exn))

        return JS_FALSE;



    /*

     * Because js_ValueToString below could error and an exception object

     * could become unrooted, we root it here.

     */

    if (JSVAL_IS_OBJECT(exn) && exn != JSVAL_NULL) {

        exnObject = JSVAL_TO_OBJECT(exn);

        if (!js_AddRoot(cx, &exnObject, "exn.report.root"))

            return JS_FALSE;

    } else {

        exnObject = NULL;

    }

    str = js_ValueToString(cx, exn);



#if JS_HAS_ERROR_EXCEPTIONS

    reportp = js_ErrorFromException(cx, exn);

#else

    reportp = NULL;

#endif



    if (str != NULL) {

	bytes = js_GetStringBytes(str);

    }

    else {

	bytes = "null";

    }



    if (reportp == NULL) {

        /*

         * XXXmccabe todo: Instead of doing this, synthesize an error report

         * struct that includes the filename, lineno where the exception was

         * originally thrown.

         */

        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                             JSMSG_UNCAUGHT_EXCEPTION, bytes);

    } else {

        /* Flag the error as an exception. */

        reportp->flags |= JSREPORT_EXCEPTION;

        js_ReportErrorAgain(cx, bytes, reportp);

    }



    if (exnObject != NULL)

        js_RemoveRoot(cx->runtime, &exnObject);

    return JS_TRUE;

}



#endif	    /* JS_HAS_EXCEPTIONS */

 

**** End of jsexn.c ****

 

**** Start of jsexn.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS runtime exception classes.

 */



#ifndef jsexn_h___

#define jsexn_h___



JS_BEGIN_EXTERN_C



/*

 * Initialize the exception constructor/prototype hierarchy.

 */

extern JSObject *

js_InitExceptionClasses(JSContext *cx, JSObject *obj);



/*

 * String constants naming the exception classes.

 */

extern const char js_Error_str[];

extern const char js_InternalError_str[];

extern const char js_EvalError_str[];

extern const char js_RangeError_str[];

extern const char js_ReferenceError_str[];

extern const char js_SyntaxError_str[];

extern const char js_TypeError_str[];

extern const char js_URIError_str[];



/*

 * Given a JSErrorReport, check to see if there is an exception associated with

 * the error number.  If there is, then create an appropriate exception object,

 * set it as the pending exception, and set the JSREPORT_EXCEPTION flag on the

 * error report.  Exception-aware host error reporters should probably ignore

 * error reports so flagged.  Returns JS_TRUE if an associated exception is

 * found and set, JS_FALSE otherwise..

 */

extern JSBool

js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp);



/*

 * Called if a JS API call to js_Execute or js_InternalCall fails; calls the

 * error reporter with the error report associated with any uncaught exception

 * that has been raised.  Returns true if there was an exception pending, and

 * the error reporter was actually called.

 *

 * The JSErrorReport * that the error reporter is called with is currently

 * associated with a JavaScript object, and is not guaranteed to persist after

 * the object is collected.  Any persistent uses of the JSErrorReport contents

 * should make their own copy.

 *

 * The flags field of the JSErrorReport will have the JSREPORT_EXCEPTION flag

 * set; embeddings that want to silently propagate JavaScript exceptions to

 * other contexts may want to use an error reporter that ignores errors with

 * this flag.

 */

extern JSBool

js_ReportUncaughtException(JSContext *cx);



extern JSErrorReport *

js_ErrorFromException(JSContext *cx, jsval exn);



JS_END_EXTERN_C



#endif /* jsexn_h___ */

 

**** End of jsexn.h ****

 

**** Start of jsfile.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS File object

 */

#if JS_HAS_FILE_OBJECT



#include "jsstddef.h"



/* ----------------- Platform-specific includes and defines ----------------- */

#ifdef XP_MAC

#   define FILESEPARATOR         ':'

#   define FILESEPARATOR2        '\0'

#   define CURRENT_DIR          "HARD DISK:Desktop Folder"

/*  TODO: #include <???> */

#elif defined(XP_PC) || defined(XP_OS2)

#   include <direct.h>

#   include <io.h>

#   include <sys/types.h>

#   include <sys/stat.h>

#   define FILESEPARATOR        '\\'

#   define FILESEPARATOR2       '/'

#   define CURRENT_DIR          "c:\\"

#   define POPEN                _popen

#   define PCLOSE               _pclose

#elif defined(XP_UNIX) || defined(XP_BEOS)

#   include <strings.h>

#   include <stdio.h>

#   include <stdlib.h>

#   include <unistd.h>

#   define FILESEPARATOR        '/'

#   define FILESEPARATOR2       '\0'

#   define CURRENT_DIR          "/"

#   define POPEN                popen

#   define PCLOSE               pclose

#endif



/* --------------- Platform-independent includes and defines ---------------- */

#include "jsapi.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsdate.h"

#include "jsdbgapi.h"

#include "jsemit.h"

#include "jsfun.h"

#include "jslock.h"

#include "jsobj.h"

#include "jsparse.h"

#include "jsscan.h"

#include "jsscope.h"

#include "jsscript.h"

#include "jsstr.h"

#include "jsutil.h" /* Added by JSIFY */

#include <string.h>



/* NSPR dependencies */

#include "prio.h"

#include "prerror.h"



#define SPECIAL_FILE_STRING     "Special File"

#define CURRENTDIR_PROPERTY     "currentDir"

#define SEPARATOR_PROPERTY      "separator"

#define FILE_CONSTRUCTOR        "File"

#define PIPE_SYMBOL             '|'



#define ASCII                   0

#define UTF8                    1

#define UCS2                    2



#define asciistring             "text"

#define utfstring               "binary"

#define unicodestring           "unicode"



#define MAX_PATH_LENGTH         1024

#define MODE_SIZE               256

#define NUMBER_SIZE             32

#define MAX_LINE_LENGTH         256

#define URL_PREFIX              "file://"



#define STDINPUT_NAME           "Standard input stream"

#define STDOUTPUT_NAME          "Standard output stream"

#define STDERROR_NAME           "Standard error stream"



#define RESOLVE_PATH            js_canonicalPath	/* js_absolutePath */



/* Error handling */

typedef enum JSFileErrNum {

#define MSG_DEF(name, number, count, exception, format) \

    name = number,

#include "jsfile.msg"

#undef MSG_DEF

    JSFileErr_Limit

#undef MSGDEF

} JSFileErrNum;



#define JSFILE_HAS_DFLT_MSG_STRINGS 1



JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = {

#if JSFILE_HAS_DFLT_MSG_STRINGS

#define MSG_DEF(name, number, count, exception, format) \

    { format, count } ,

#else

#define MSG_DEF(name, number, count, exception, format) \

    { NULL, count } ,

#endif

#include "jsfile.msg"

#undef MSG_DEF

};



const JSErrorFormatString *

JSFile_GetErrorMessage(void *userRef, const char *locale,

                                                        const uintN errorNumber)

{

    if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit))

        return &JSFile_ErrorFormatString[errorNumber];

	else

	    return NULL;

}



#define JSFILE_CHECK_NATIVE(op)     \

    if(file->isNative){         \

        JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s", \

						op, file->path);   \

        goto out;   \

    }



#define JSFILE_CHECK_WRITE      \

    if (!file->isOpen){     \

        JS_ReportWarning(cx,    \

                "File %s is closed, will open it for writing, proceeding",  \

                file->path);    \

        js_FileOpen(cx, obj, file, "write,append,create");     \

    }else   \

    if(!js_canWrite(cx, file)){     \

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \

            JSFILEMSG_CANNOT_WRITE, file->path);    \

        goto out;   \

    }



#define JSFILE_CHECK_READ      \

    if (!file->isOpen){     \

        JS_ReportWarning(cx,    \

                "File %s is closed, will open it for reading, proceeding",  \

                file->path); \

        js_FileOpen(cx, obj, file, "read");     \

    }else   \

    if(!js_canRead(cx, file)){     \

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \

            JSFILEMSG_CANNOT_READ, file->path);    \

        goto out;   \

    }



#define JSFILE_CHECK_OPEN(op)      \

    if(!file->isOpen){     \

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \

            JSFILEMSG_FILE_MUST_BE_CLOSED, op);    \

        goto out;   \

    }



#define JSFILE_CHECK_CLOSED(op)      \

    if(file->isOpen){     \

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \

            JSFILEMSG_FILE_MUST_BE_OPEN, op);    \

        goto out;   \

    }



#define JSFILE_CHECK_ONE_ARG(op)      \

    if (argc!=1){   \

        char str[NUMBER_SIZE];  \

                                \

        sprintf(str, "%d", argc);   \

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \

            JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str);  \

        goto out;   \

    }





/*

    Security mechanism, should define a callback for this.

    The parameters are as follows:

    SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file)

*/

#define SECURITY_CHECK(cx, ps, op, file)    \

        /* Define a callback here... */





/* Structure representing the file internally */

typedef struct JSFile {

    char        *path;          /* the path to the file. */

    JSBool      isOpen;

    JSString    *linebuffer;    /* temp buffer used by readln. */

    int32       mode;           /* mode used to open the file: read, write, append, create, etc.. */

    int32       type;           /* Asciiz, utf, unicode */

    char        byteBuffer[3];  /* bytes read in advance by js_FileRead ( UTF8 encoding ) */

    jsint       nbBytesInBuf;   /* number of bytes stored in the buffer above */

    jschar      charBuffer;     /* character read in advance by readln ( mac files only ) */

    JSBool      charBufferUsed; /* flag indicating if the buffer above is being used */

    JSBool      hasRandomAccess;   /* can the file be randomly accessed? false for stdin, and

                                 UTF-encoded files. */

    JSBool      hasAutoflush;   /* should we force a flush for each line break? */

    JSBool      isNative;       /* if the file is using OS-specific file FILE type */

    /* We can actually put the following two in a union since they should never be used at the same time */

    PRFileDesc  *handle;        /* the handle for the file, if open.  */

    FILE        *nativehandle;  /* native handle, for stuff NSPR doesn't do. */

    JSBool      isPipe;         /* if the file is really an OS pipe */

} JSFile;



/* a few forward declarations... */

static JSClass file_class;

JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename);

static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);

static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);



/* --------------------------- New filename manipulation procesures -------------------------- */

/* assumes we don't have leading/trailing spaces */

static JSBool

js_filenameHasAPipe(const char *filename)

{

#ifdef XP_MAC

    /* pipes are not supported on the MAC */

    return JS_FALSE;

#else

    if(!filename) return JS_FALSE;

    return  filename[0]==PIPE_SYMBOL ||

            filename[strlen(filename)-1]==PIPE_SYMBOL;

#endif

}



static JSBool

js_isAbsolute(const char *name)

{

#ifdef XP_PC

    return (strlen(name)>1)?((name[1]==':')?JS_TRUE:JS_FALSE):JS_FALSE;

#else

    return (name[0]

#   if defined(XP_UNIX) || defined(XP_BEOS)

            ==

#   else

            !=

#   endif

            FILESEPARATOR)?JS_TRUE:JS_FALSE;

#endif

}



/*

    Concatinates base and name to produce a valid filename.

    Returned string must be freed.

*/

static char*

js_combinePath(JSContext *cx, const char *base, const char *name)

{

    int len = strlen(base)-1;

    char* result = (char*)JS_malloc(cx, strlen(base)+strlen(name)+2);



    if (!result)  return NULL;



    strcpy(result, base);



    if (base[len]!=FILESEPARATOR

#ifdef XP_PC

            && base[len]!=FILESEPARATOR2

#endif

            ) {

      result[len+1] = FILESEPARATOR;

      result[len+2] = '\0';

    }

    strcat(result, name);

    return result;

}



/* Extract the last component from a path name. Returned string must be freed */

static char *

js_fileBaseName(JSContext *cx, const char *pathname)

{

    jsint index, aux;

    char *result;



#ifdef XP_PC

    /* First, get rid of the drive selector */

    if ((strlen(pathname)>=2)&&(pathname[1]==':')) {

        pathname = &pathname[2];

    }

#endif

    index = strlen(pathname)-1;

    /*

        remove trailing separators -- don't necessarily need to check for

        FILESEPARATOR2, but that's fine

    */

    while ((index>0)&&((pathname[index]==FILESEPARATOR)||

                       (pathname[index]==FILESEPARATOR2))) index--;

    aux = index;

    /* now find the next separator */

    while ((index>=0)&&(pathname[index]!=FILESEPARATOR)&&

                      (pathname[index]!=FILESEPARATOR2)) index--;

    /* allocate and copy */

    result = (char*)JS_malloc(cx, aux-index+1);

    if (!result)  return NULL;

    strncpy(result, &pathname[index+1], aux-index);

    result[aux-index] = '\0';

    return result;

}



/*

    Returns everytynig but the last component from a path name.

    Returned string must be freed. Returned string must be freed.

*/

static char *

js_fileDirectoryName(JSContext *cx, const char *pathname)

{

    jsint index;

    char  *result;



#ifdef XP_PC

    char  drive = '\0';

    const char *oldpathname = pathname;



    /* First, get rid of the drive selector */

    if ((strlen(pathname)>=2)&&(pathname[1]==':')) {

        drive = pathname[0];

        pathname = &pathname[2];

    }

#endif

    index = strlen(pathname)-1;

    while ((index>0)&&((pathname[index]==FILESEPARATOR)||

                       (pathname[index]==FILESEPARATOR2))) index--;

    while ((index>0)&&(pathname[index]!=FILESEPARATOR)&&

                      (pathname[index]!=FILESEPARATOR2)) index--;



    if (index>=0){

        result = (char*)JS_malloc(cx, index+4);

        if (!result)  return NULL;

#ifdef XP_PC

        if (drive!='\0') {

            result[0] = toupper(drive);

            result[1] = ':';

            strncpy(&result[2], pathname, index);

			result[index+3] = '\0';

        }else

#endif

        {

            strncpy(result, pathname, index);

			result[index] = '\0';

        }



        /* add terminating separator */

        index = strlen(result)-1;

        result[index] = FILESEPARATOR;

        result[index+1] = '\0';

    } else{

#ifdef XP_PC

        result = JS_strdup(cx, oldpathname); /* may include drive selector */

#else

        result = JS_strdup(cx, pathname);

#endif

    }



    return result;

}



static char *

js_absolutePath(JSContext *cx, const char * path)

{

    JSObject *obj;

    JSString *str;

    jsval prop;



    if (js_isAbsolute(path)){

        return JS_strdup(cx, path);

    }else{

        obj = JS_GetGlobalObject(cx);

        if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) {

            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                 JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR);

            return JS_strdup(cx, path);

        }

        obj = JSVAL_TO_OBJECT(prop);

        if (!JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, &prop)) {

            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                 JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR);

            return JS_strdup(cx, path);

        }

        str = JS_ValueToString(cx, prop);

        if (!str ) {

            return JS_strdup(cx, path);

        }

        /* should we have an array of curr dirs indexed by drive for windows? */

        return js_combinePath(cx, JS_GetStringBytes(str), path);

    }

}



/* Side effect: will remove spaces in the beginning/end of the filename */

static char *

js_canonicalPath(JSContext *cx, char *oldpath)

{

    char *tmp;

    char *path = oldpath;

    char *base, *dir, *current, *result;

    jsint c;

    jsint back = 0;

    unsigned int i = 0, j = strlen(path)-1;



    /* This is probably optional */

	/* Remove possible spaces in the beginning and end */

    while(i<strlen(path)-1 && path[i]==' ') i++;

	while(j>=0 && path[j]==' ') j--;



	tmp = JS_malloc(cx, j-i+2);

	strncpy(tmp, &path[i], j-i+1);

    tmp[j-i+1] = '\0';



    path = tmp;



    /* pipe support */

    if(js_filenameHasAPipe(path))

        return JS_strdup(cx, path);

    /* file:// support */

    if(!strncmp(path, URL_PREFIX, strlen(URL_PREFIX)))

        return js_canonicalPath(cx, &path[strlen(URL_PREFIX)-1]);



    if (!js_isAbsolute(path))

        path = js_absolutePath(cx, path);

    else

        path = JS_strdup(cx, path);



    result = JS_strdup(cx, "");



    current = path;



    base = js_fileBaseName(cx, current);

    dir = js_fileDirectoryName(cx, current);



    /* TODO: MAC -- not going to work??? */

    while (strcmp(dir, current)) {

        if (!strcmp(base, "..")) {

            back++;

        } else

        if(!strcmp(base, ".")){

            /* ??? */

        } else {

            if (back>0)

                back--;

            else {

                tmp = result;

                result = JS_malloc(cx, strlen(base)+1+strlen(tmp)+1);

                if (!result) {

                    JS_free(cx, dir);

                    JS_free(cx, base);

                    JS_free(cx, current);

                    return NULL;

                }

                strcpy(result, base);

                c = strlen(result);

                if (strlen(tmp)>0) {

                    result[c] = FILESEPARATOR;

                    result[c+1] = '\0';

                    strcat(result, tmp);

                }

                JS_free(cx, tmp);

            }

        }

        JS_free(cx, current);

        JS_free(cx, base);

        current = dir;

        base =  js_fileBaseName(cx, current);

        dir = js_fileDirectoryName(cx, current);

    }



    tmp = result;

    result = JS_malloc(cx, strlen(dir)+1+strlen(tmp)+1);

    if (!result) {

        JS_free(cx, dir);

        JS_free(cx, base);

        JS_free(cx, current);

        return NULL;

    }

    strcpy(result, dir);

    c = strlen(result);

    if (strlen(tmp)>0) {

        if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) {

            result[c] = FILESEPARATOR;

            result[c+1] = '\0';

        }

        strcat(result, tmp);

    }

    JS_free(cx, tmp);

    JS_free(cx, dir);

    JS_free(cx, base);

    JS_free(cx, current);



    return result;

}



/* -------------------------- Text conversion ------------------------------- */

/* The following is ripped from libi18n/unicvt.c and include files.. */



/*

 * UTF8 defines and macros

 */

#define ONE_OCTET_BASE          0x00    /* 0xxxxxxx */

#define ONE_OCTET_MASK          0x7F    /* x1111111 */

#define CONTINUING_OCTET_BASE   0x80    /* 10xxxxxx */

#define CONTINUING_OCTET_MASK   0x3F    /* 00111111 */

#define TWO_OCTET_BASE          0xC0    /* 110xxxxx */

#define TWO_OCTET_MASK          0x1F    /* 00011111 */

#define THREE_OCTET_BASE        0xE0    /* 1110xxxx */

#define THREE_OCTET_MASK        0x0F    /* 00001111 */

#define FOUR_OCTET_BASE         0xF0    /* 11110xxx */

#define FOUR_OCTET_MASK         0x07    /* 00000111 */

#define FIVE_OCTET_BASE         0xF8    /* 111110xx */

#define FIVE_OCTET_MASK         0x03    /* 00000011 */

#define SIX_OCTET_BASE          0xFC    /* 1111110x */

#define SIX_OCTET_MASK          0x01    /* 00000001 */



#define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK  ) == ONE_OCTET_BASE)

#define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK  ) == TWO_OCTET_BASE)

#define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE)

#define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE)

#define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE)

#define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK  ) == SIX_OCTET_BASE)

#define IS_UTF8_2ND_THRU_6TH(x) \

                    (( (x)&~CONTINUING_OCTET_MASK  ) == CONTINUING_OCTET_BASE)

#define IS_UTF8_1ST_OF_UCS2(x) \

            IS_UTF8_1ST_OF_1(x) \

            || IS_UTF8_1ST_OF_2(x) \

            || IS_UTF8_1ST_OF_3(x)





#define MAX_UCS2            0xFFFF

#define DEFAULT_CHAR        0x003F  /* Default char is "?" */

#define BYTE_MASK           0xBF

#define BYTE_MARK           0x80





/* Function: one_ucs2_to_utf8_char

 *

 * Function takes one UCS-2 char and writes it to a UTF-8 buffer.

 * We need a UTF-8 buffer because we don't know before this

 * function how many bytes of utf-8 data will be written. It also

 * takes a pointer to the end of the UTF-8 buffer so that we don't

 * overwrite data. This function returns the number of UTF-8 bytes

 * of data written, or -1 if the buffer would have been overrun.

 */



#define LINE_SEPARATOR      0x2028

#define PARAGRAPH_SEPARATOR 0x2029

static int16 one_ucs2_to_utf8_char(unsigned char *tobufp,

        unsigned char *tobufendp, uint16 onechar)

{



     int16 numUTF8bytes = 0;



    if((onechar == LINE_SEPARATOR)||(onechar == PARAGRAPH_SEPARATOR))

    {

        strcpy((char*)tobufp, "\n");

        return strlen((char*)tobufp);;

    }



        if (onechar < 0x80) {               numUTF8bytes = 1;

        } else if (onechar < 0x800) {       numUTF8bytes = 2;

        } else if (onechar <= MAX_UCS2) {   numUTF8bytes = 3;

        } else { numUTF8bytes = 2;

                 onechar = DEFAULT_CHAR;

        }



        tobufp += numUTF8bytes;



        /* return error if we don't have space for the whole character */

        if (tobufp > tobufendp) {

            return(-1);

        }





        switch(numUTF8bytes) {



            case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;

                    *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;

                    *--tobufp = onechar |  THREE_OCTET_BASE;

                    break;



            case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;

                    *--tobufp = onechar | TWO_OCTET_BASE;

                    break;

            case 1: *--tobufp = (unsigned char)onechar;  break;

        }



        return(numUTF8bytes);

}



/*

 * utf8_to_ucs2_char

 *

 * Convert a utf8 multibyte character to ucs2

 *

 * inputs: pointer to utf8 character(s)

 *         length of utf8 buffer ("read" length limit)

 *         pointer to return ucs2 character

 *

 * outputs: number of bytes in the utf8 character

 *          -1 if not a valid utf8 character sequence

 *          -2 if the buffer is too short

 */

static int16

utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p)

{

    uint16 lead, cont1, cont2;



    /*

     * Check for minimum buffer length

     */

    if ((buflen < 1) || (utf8p == NULL)) {

        return -2;

    }

    lead = (uint16) (*utf8p);



    /*

     * Check for a one octet sequence

     */

    if (IS_UTF8_1ST_OF_1(lead)) {

        *ucs2p = lead & ONE_OCTET_MASK;

        return 1;

    }



    /*

     * Check for a two octet sequence

     */

    if (IS_UTF8_1ST_OF_2(*utf8p)) {

        if (buflen < 2)

            return -2;

        cont1 = (uint16) *(utf8p+1);

        if (!IS_UTF8_2ND_THRU_6TH(cont1))

            return -1;

        *ucs2p =  (lead & TWO_OCTET_MASK) << 6;

        *ucs2p |= cont1 & CONTINUING_OCTET_MASK;

        return 2;

    }



    /*

     * Check for a three octet sequence

     */

    else if (IS_UTF8_1ST_OF_3(lead)) {

        if (buflen < 3)

            return -2;

        cont1 = (uint16) *(utf8p+1);

        cont2 = (uint16) *(utf8p+2);

        if (   (!IS_UTF8_2ND_THRU_6TH(cont1))

            || (!IS_UTF8_2ND_THRU_6TH(cont2)))

            return -1;

        *ucs2p =  (lead & THREE_OCTET_MASK) << 12;

        *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6;

        *ucs2p |= cont2 & CONTINUING_OCTET_MASK;

        return 3;

    }

    else { /* not a valid utf8/ucs2 character */

        return -1;

    }

}



/* ----------------------------- Helper functions --------------------------- */

/* Ripped off from lm_win.c .. */

/* where is strcasecmp?.. for now, it's case sensitive..

 *

 * strcasecmp is in strings.h, but on windows it's called _stricmp...

 * will need to #ifdef this

*/



static int32

js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name)

{

    char *comma, *equal, *current;

    char *options = JS_strdup(cx, oldoptions);

    int32 found = 0;



	current = options;

    for (;;) {

        comma = strchr(current, ',');

        if (comma) *comma = '\0';

        equal = strchr(current, '=');

        if (equal) *equal = '\0';

        if (strcmp(current, name) == 0) {

            if (!equal || strcmp(equal + 1, "yes") == 0)

                found = 1;

            else

                found = atoi(equal + 1);

        }

        if (equal) *equal = '=';

        if (comma) *comma = ',';

        if (found || !comma)

            break;

        current = comma + 1;

    }

    JS_free(cx, options);

    return found;

}



/* empty the buffer */

static void

js_ResetBuffers(JSFile * file)

{

    file->charBufferUsed = JS_FALSE;

    file->nbBytesInBuf = 0;

    file->linebuffer = NULL;    /* TODO: check for mem. leak? */

}



/* Reset file attributes */

static void

js_ResetAttributes(JSFile * file){

	file->mode = file->type = 0;

    file->isOpen = JS_FALSE;

    file->handle = NULL;

    file->nativehandle = NULL;

    file->hasRandomAccess = JS_TRUE; /* innocent until proven guilty */

	file->hasAutoflush = JS_FALSE;

    file->isNative = JS_FALSE;

    file->isPipe = JS_FALSE;



	js_ResetBuffers(file);

}



static JSBool

js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){

    JSString *type, *mask;

    jsval v[2];

    jsval rval;



    type =  JS_InternString(cx, asciistring);

    mask =  JS_NewStringCopyZ(cx, mode);

    v[0] = STRING_TO_JSVAL(mask);

    v[1] = STRING_TO_JSVAL(type);



    if (!file_open(cx, obj, 2, v, &rval)) {

        return JS_FALSE;

    }

    return JS_TRUE;

}



/* Buffered version of PR_Read. Used by js_FileRead */

static int32

js_BufferedRead(JSFile * f, char *buf, int32 len)

{

    int32 count = 0;



    while (f->nbBytesInBuf>0&&len>0) {

        buf[0] = f->byteBuffer[0];

        f->byteBuffer[0] = f->byteBuffer[1];

        f->byteBuffer[1] = f->byteBuffer[2];

        f->nbBytesInBuf--;

        len--;

        buf+=1;

        count++;

    }



    if (len>0) {

        count+= (!f->isNative)?

                    PR_Read(f->handle, buf, len):

                    fread(buf, 1, len, f->nativehandle);

    }

    return count;

}



static int32

js_FileRead(JSContext *cx, JSFile * file, jschar*buf, int32 len, int32 mode)

{

    unsigned char*aux;

    int32 count, i;

    jsint remainder;

    unsigned char utfbuf[3];



    if (file->charBufferUsed) {

        buf[0] = file->charBuffer;

        buf++;

        len--;

        file->charBufferUsed = JS_FALSE;

    }



    switch (mode) {

    case ASCII:

        aux = (unsigned char*)JS_malloc(cx, len);

        if (!aux) {

        return 0;

        }

        count = js_BufferedRead(file, aux, len);

        if (count==-1) {

        JS_free(cx, aux);

        return 0;

        }

        for (i = 0;i<len;i++) {

        buf[i] = (jschar)aux[i];

        }

        JS_free(cx, aux);

        break;

    case UTF8:

        remainder = 0;

        for (count = 0;count<len;count++) {

            i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);

            if (i<=0) {

                return count;

            }

            i = utf8_to_ucs2_char(utfbuf, (int16)i, &buf[count] );

            if (i<0) {

                return count;

            } else {

                if (i==1) {

                    utfbuf[0] = utfbuf[1];

                    utfbuf[1] = utfbuf[2];

                    remainder = 2;

                } else

                if (i==2) {

                    utfbuf[0] = utfbuf[2];

                    remainder = 1;

                } else

                if (i==3)

                    remainder = 0;

            }

        }

        while (remainder>0) {

            file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];

            file->nbBytesInBuf++;

            utfbuf[0] = utfbuf[1];

            utfbuf[1] = utfbuf[2];

            remainder--;

        }

        break;

    case UCS2:

        count = js_BufferedRead(file, (char*)buf, len*2)>>1;

        if (count==-1) {

            return 0;

        }

        break;

    }



    if(count==-1){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_OP_FAILED, "read", file->path);

    }



    return count;

}



static int32

js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode)

{

    int32 count, i;

    jsint remainder;

    unsigned char utfbuf[3];

    jschar tmp;



    switch (mode) {

    case ASCII:

        count = PR_Seek(file->handle, len, PR_SEEK_CUR);

        break;

    case UTF8:

        remainder = 0;

        for (count = 0;count<len;count++) {

            i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);

            if (i<=0) {

                return 0;

            }

            i = utf8_to_ucs2_char(utfbuf, (int16)i, &tmp );

            if (i<0) {

                return 0;

            } else {



                if (i==1) {

                    utfbuf[0] = utfbuf[1];

                    utfbuf[1] = utfbuf[2];

                    remainder = 2;

                } else

                if (i==2) {

                    utfbuf[0] = utfbuf[2];

                    remainder = 1;

                } else

                if (i==3)

                    remainder = 0;

            }

        }

        while (remainder>0) {

            file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];

            file->nbBytesInBuf++;

            utfbuf[0] = utfbuf[1];

            utfbuf[1] = utfbuf[2];

            remainder--;

        }

      break;

    case UCS2:

        count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2;

        break;

    }



    if(count==-1){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_OP_FAILED, "seek", file->path);

    }



    return count;

}



static int32

js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)

{

    unsigned char   *aux;

    int32           count, i, j;

    unsigned char   *utfbuf;



    switch (mode) {

    case ASCII:

        aux = (unsigned char*)JS_malloc(cx, len);

        if (!aux)  return 0;



        for (i = 0; i<len; i++) {

            aux[i]=buf[i]%256;

        }



        count = (!file->isNative)?

                    PR_Write(file->handle, aux, len):

                    fwrite(aux, 1, len, file->nativehandle);



        if (count==-1) {

            JS_free(cx, aux);

            return 0;

        }

        JS_free(cx, aux);

        break;

    case UTF8:

        utfbuf = (unsigned char*)JS_malloc(cx, len*3);

        if (!utfbuf)  return 0;

        i = 0;

        for (count = 0;count<len;count++) {

            j = one_ucs2_to_utf8_char(utfbuf+i, utfbuf+len*3, buf[count]);

            if (j==-1) {

                JS_free(cx, utfbuf);

                return 0;

            }

            i+=j;

        }

        j = (!file->isNative)?

                PR_Write(file->handle, utfbuf, i):

                fwrite(utfbuf, 1, i, file->nativehandle);



        if (j<i) {

            JS_free(cx, utfbuf);

            return 0;

        }

        JS_free(cx, utfbuf);

      break;

    case UCS2:

        count = (!file->isNative)?

                PR_Write(file->handle, buf, len*2)>>1:

                fwrite(buf, 1, len*2, file->nativehandle)>>1;



        if (count==-1) {

            return 0;

        }

        break;

    }

    if(count==-1){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_OP_FAILED, "write", file->path);

    }

    return count;

}



/* ----------------------------- Property checkers -------------------------- */

static JSBool

js_exists(JSContext *cx, JSFile *file)

{

    if(!file->isNative){

        return (PR_Access(file->path, PR_ACCESS_EXISTS)==PR_SUCCESS);

    }else{

        /* doesn't make sense for a pipe of stdstream */

        return JS_FALSE;

    }

}



static JSBool

js_canRead(JSContext *cx, JSFile *file)

{

    if(!file->isNative){

        if(file->isOpen&&!(file->mode&PR_RDONLY)) return JS_FALSE;

        return (PR_Access(file->path, PR_ACCESS_READ_OK)==PR_SUCCESS);

    }else{

        if(file->isPipe){

            /* pipe open for reading */

            return file->path[0]==PIPE_SYMBOL;

        }else{

            return !strcmp(file->path, STDINPUT_NAME);

        }

    }

}



static JSBool

js_canWrite(JSContext *cx, JSFile *file)

{

    if(!file->isNative){

        if(file->isOpen&&!(file->mode&PR_WRONLY)) return JS_FALSE;

        return (PR_Access(file->path, PR_ACCESS_WRITE_OK)==PR_SUCCESS);

    }else{

        if(file->isPipe){

            /* pipe open for writing */

            return file->path[strlen(file->path)-1]==PIPE_SYMBOL;

        }else{

            return  !strcmp(file->path, STDOUTPUT_NAME) ||

                    !strcmp(file->path, STDERROR_NAME);

        }

    }

}



static JSBool

js_isFile(JSContext *cx, JSFile *file)

{

    if(!file->isNative){

        PRFileInfo info;



        if ((file->isOpen)?

                        PR_GetOpenFileInfo(file->handle, &info):

                        PR_GetFileInfo(file->path, &info)!=PR_SUCCESS){

            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);

            return JS_FALSE;

        }else

            return (info.type==PR_FILE_FILE);

    }else{

        /* doesn't make sense for a pipe of stdstream */

        return JS_FALSE;

    }

}



static JSBool

js_isDirectory(JSContext *cx, JSFile *file)

{

    if(!file->isNative){

        PRFileInfo info;



		/* hack needed to get get_property to work */

		if(!js_exists(cx, file)) return JS_FALSE;



        if ((file->isOpen)?

                        PR_GetOpenFileInfo(file->handle, &info):

                        PR_GetFileInfo(file->path, &info)!=PR_SUCCESS){

            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);

            return JS_FALSE;

        }else

            return (info.type==PR_FILE_DIRECTORY);

    }else{

        /* doesn't make sense for a pipe of stdstream */

        return JS_FALSE;

    }

}



static jsval

js_size(JSContext *cx, JSFile *file)

{

    PRFileInfo info;



    JSFILE_CHECK_NATIVE("size");



    if ((file->isOpen)?

                    PR_GetOpenFileInfo(file->handle, &info):

                    PR_GetFileInfo(file->path, &info)!=PR_SUCCESS){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);

        goto out;

    }else

        return INT_TO_JSVAL(info.size);

out:

    return JSVAL_VOID;

}



/* Return the parent object */

static jsval

js_parent(JSContext *cx, JSFile *file)

{

    char *str;



    /* since we only care about pipes and native files, return NULL */

    if(file->isNative)  return JSVAL_VOID;



    str = js_fileDirectoryName(cx, file->path);

    /* root.parent = null ??? */

    if(!strcmp(file->path, str) ||

            (!strncmp(str, file->path, strlen(str)-1)&&

            file->path[strlen(file->path)]-1)==FILESEPARATOR){

        return JSVAL_NULL;

    }else{

        return OBJECT_TO_JSVAL(js_NewFileObject(cx, str));

        JS_free(cx, str);

    }

}



static jsval

js_name(JSContext *cx, JSFile *file){

    return  file->isPipe?

                JSVAL_VOID:

                STRING_TO_JSVAL(JS_NewStringCopyZ(cx, js_fileBaseName(cx, file->path)));

}



/* ------------------------------ File object methods ---------------------------- */

static JSBool

file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    JSString	*strmode, *strtype;

    char        *ctype, *mode;

    int32       mask, type;

    int         len;



    SECURITY_CHECK(cx, NULL, "open", file);



    /* A native file that is already open */

    if(file->isOpen && file->isNative){

        JS_ReportWarning(cx, "Native file %s is already open, proceeding",

            file->path);

        goto good;

    }



    /* Close before proceeding */

    if (file->isOpen) {

        JS_ReportWarning(cx,

            "File %s is already open, we will close it and reopen, proceeding",

            file->path);

        if(!file_close(cx, obj, 0, NULL, rval)) goto out;

    }



    if(js_isDirectory(cx, file)){

        JS_ReportWarning(cx, "%s seems to be a directory, there is no point in "

                "trying to open it, proceeding", file->path);

        goto good;

    }



    /* Path must be defined at this point */

    len = strlen(file->path);



    /* Mode */

    if (argc>=1){

        strmode = JS_ValueToString(cx, argv[0]);

        if (!strmode){

            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, argv[0]);

            goto out;

        }

        mode = JS_strdup(cx, JS_GetStringBytes(strmode));

    }else{

        if(file->path[0]==PIPE_SYMBOL){

            /* pipe default mode */

            mode = JS_strdup(cx, "read");

        }else

        if(file->path[len-1]==PIPE_SYMBOL){

            /* pipe default mode */

            mode = JS_strdup(cx, "write");

        }else{

            /* non-destructive, permissive defaults. */

            mode = JS_strdup(cx, "readWrite,append,create");

        }

    }



    /* Process the mode */

    mask = 0;

    /* TODO: this is pretty ugly, BTW, we walk thru the string too many times */

    mask|=(js_FileHasOption(cx, mode, "read"))?      PR_RDONLY       :   0;

    mask|=(js_FileHasOption(cx, mode, "write"))?     PR_WRONLY       :   0;

    mask|=(js_FileHasOption(cx, mode, "readWrite"))? PR_RDWR         :   0;

    mask|=(js_FileHasOption(cx, mode, "append"))?    PR_APPEND       :   0;

    mask|=(js_FileHasOption(cx, mode, "create"))?    PR_CREATE_FILE  :   0;

    mask|=(js_FileHasOption(cx, mode, "replace"))?   PR_TRUNCATE     :   0;



    if((mask&PR_RDWR)) mask|=(PR_RDONLY|PR_WRONLY);

    if((mask&PR_RDONLY)&&(mask&PR_WRONLY)) mask|=PR_RDWR;



    file->hasAutoflush|=(js_FileHasOption(cx, mode, "autoflush"));



    /* Type */

    if (argc>1) {

        strtype = JS_ValueToString(cx, argv[1]);

        if (!strtype) {

            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, argv[1]);

            goto out;

        }

        ctype = JS_GetStringBytes(strtype);



        if(!strcmp(ctype, utfstring))

            type = UTF8;

        else

        if (!strcmp(ctype, unicodestring))

            type = UCS2;

        else{

            if(strcmp(ctype, asciistring)){

                JS_ReportWarning(cx, "File type %s is not supported, using "

                        "'text' instead, proceeding", ctype);

            }

            type = ASCII;

        }

    }else{

        type = ASCII;

    }



    /* Save the relevant fields */

    file->type = type;

    file->mode = mask;

    file->nativehandle = NULL;

    file->hasRandomAccess = (type!=UTF8);



    /*

		Deal with pipes here. We can't use NSPR for pipes,

        so we have to use POPEN.

	*/

    if(file->path[0]==PIPE_SYMBOL || file->path[len-1]==PIPE_SYMBOL){

        if(file->path[0]==PIPE_SYMBOL && file->path[len-1]==PIPE_SYMBOL){

            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

				JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED);

                goto out;

        }else{

            char pipemode[3];

            SECURITY_CHECK(cx, NULL, "pipe_open", file);



            if(file->path[0] == PIPE_SYMBOL){

                if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){

                    JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

						JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES,

						mode, file->path);

                    goto out;

                }

                /* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for writing */

                pipemode[0] = 'r';

                pipemode[1] = file->type==UTF8?'b':'t';

                pipemode[2] = '\0';

                file->nativehandle = POPEN(&file->path[1], pipemode);

            }else

            if(file->path[len-1] == PIPE_SYMBOL){

                char *command = JS_malloc(cx, len);



                strncpy(command, file->path, len-1);

                command[len-1] = '\0';

                /* open(STATUS, "netstat -an 2>&1 |") */

                pipemode[0] = 'w';

                pipemode[1] = file->type==UTF8?'b':'t';

                pipemode[2] = '\0';

                file->nativehandle = POPEN(command, pipemode);

				JS_free(cx, command);

            }

            /* set the flags */

            file->isNative = JS_TRUE;

            file->isPipe  = JS_TRUE;

            file->hasRandomAccess = JS_FALSE;

        }

    }else{

        /* TODO: what about the permissions?? Java ignores the problem... */

        file->handle = PR_Open(file->path, mask, 0644);

    }



    js_ResetBuffers(file);

    JS_free(cx, mode);

    mode = NULL;



    /* Set the open flag and return result */

    if (file->handle==NULL && file->nativehandle==NULL){

		file->isOpen = JS_FALSE;



        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_OP_FAILED, "open", file->path);

        goto out;

    }else

        goto good;

good:

    file->isOpen = JS_TRUE;

    *rval = JSVAL_TRUE;

    return JS_TRUE;

out:

    if(mode) JS_free(cx, mode);

    *rval = JSVAL_VOID;

    return JS_FALSE;

}



static JSBool

file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile  *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);



    SECURITY_CHECK(cx, NULL, "close", file);



    if(!file->isOpen){

        JS_ReportWarning(cx, "File %s is not open, can't close it, proceeding",

            file->path);

        goto out;

    }



    if(!file->isPipe){

        if(file->isNative){

            JS_ReportWarning(cx, "Unable to close a native file, proceeding", file->path);

            goto out;

        }else{

            if(file->handle && PR_Close(file->handle)){

                JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                    JSFILEMSG_OP_FAILED, "close", file->path);



                goto out;

            }

        }

    }else{

        if(PCLOSE(file->nativehandle)==-1){

            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                JSFILEMSG_OP_FAILED, "pclose", file->path);

            goto out;

        }

    }



    js_ResetAttributes(file);

    *rval = JSVAL_TRUE;

    return JS_TRUE;

out:

    *rval = JSVAL_FALSE;

    return JS_FALSE;

}





static JSBool

file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

	JSFile  *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);



    SECURITY_CHECK(cx, NULL, "remove", file);

    JSFILE_CHECK_NATIVE("remove");

    JSFILE_CHECK_CLOSED("remove");



    if ((js_isDirectory(cx, file) ?

            PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) {

        js_ResetAttributes(file);

        *rval = JSVAL_TRUE;

        return JS_TRUE;

    } else {

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_OP_FAILED, "remove", file->path);

        goto out;

    }

out:

    *rval = JSVAL_FALSE;

    return JS_FALSE;

}



/* Raw PR-based function. No text processing. Just raw data copying. */

static JSBool

file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    char        *dest = NULL;

    PRFileDesc  *handle = NULL;

    char        *buffer;

    jsval		count, size;

    JSBool      fileInitiallyOpen=JS_FALSE;



    SECURITY_CHECK(cx, NULL, "copyTo", file);   /* may need a second argument!*/

    JSFILE_CHECK_ONE_ARG("copyTo");

    JSFILE_CHECK_NATIVE("copyTo");

    /* remeber the state */

    fileInitiallyOpen = file->isOpen;

    JSFILE_CHECK_READ;



    dest = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));



    /* make sure we are not reading a file open for writing */

    if (file->isOpen && !js_canRead(cx, file)) {

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, file->path);

        goto out;

    }



    if (file->handle==NULL){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_OP_FAILED, "open", file->path);

        goto out;

    }



    handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644);



    if(!handle){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_OP_FAILED, "open", dest);

        goto out;

    }



    if ((size=js_size(cx, file))==JSVAL_VOID) {

        goto out;

    }



    buffer = JS_malloc(cx, size);



    count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size));



    /* reading panic */

    if (count!=size) {

        JS_free(cx, buffer);

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

              JSFILEMSG_COPY_READ_ERROR, file->path);

        goto out;

    }



    count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size)));



    /* writing panic */

    if (count!=size) {

        JS_free(cx, buffer);

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

              JSFILEMSG_COPY_WRITE_ERROR, file->path);

        goto out;

    }



    JS_free(cx, buffer);



	if(!fileInitiallyOpen){

		if(!file_close(cx, obj, 0, NULL, rval)) goto out;

	}



    if(PR_Close(handle)!=PR_SUCCESS){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

              JSFILEMSG_OP_FAILED, "close", dest);

        goto out;

    }



    *rval = JSVAL_TRUE;

    return JS_TRUE;

out:

    if(file->isOpen && !fileInitiallyOpen){

        if(PR_Close(file->handle)!=PR_SUCCESS){

            JS_ReportWarning(cx, "Can't close %s, proceeding", file->path);

        }

    }



    if(handle && PR_Close(handle)!=PR_SUCCESS){

        JS_ReportWarning(cx, "Can't close %s, proceeding", dest);

    }



    *rval = JSVAL_FALSE;

    return JS_FALSE;

}



static JSBool

file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile  *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    char    *dest;



    SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/

    JSFILE_CHECK_ONE_ARG("renameTo");

    JSFILE_CHECK_NATIVE("renameTo");

    JSFILE_CHECK_CLOSED("renameTo");



    dest = RESOLVE_PATH(cx, JS_GetStringBytes(JS_ValueToString(cx, argv[0])));



    if (PR_Rename(file->path, dest)==PR_SUCCESS){

        /* copy the new filename */

        JS_free(cx, file->path);

        file->path = dest;

        *rval = JSVAL_TRUE;

        return JS_TRUE;

    }else{

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_RENAME_FAILED, file->path, dest);

        goto out;

    }

out:

    *rval = JSVAL_FALSE;

    return JS_FALSE;

}



static JSBool

file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);



    SECURITY_CHECK(cx, NULL, "flush", file);

    JSFILE_CHECK_NATIVE("flush");

    JSFILE_CHECK_OPEN("flush");



    if (PR_Sync(file->handle)==PR_SUCCESS){

      *rval = JSVAL_TRUE;

      return JS_TRUE;

    }else{

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

           JSFILEMSG_OP_FAILED, "flush", file->path);

       goto out;

    }

out:

    *rval = JSVAL_FALSE;

    return JS_FALSE;

}



static JSBool

file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    JSString    *str;

    int32       count;

    uintN       i;



    SECURITY_CHECK(cx, NULL, "write", file);

    JSFILE_CHECK_WRITE;



    for (i = 0; i<argc; i++) {

        str = JS_ValueToString(cx, argv[i]);

        count = js_FileWrite(cx, file, JS_GetStringChars(str),

            JS_GetStringLength(str), file->type);

        if (count==-1){

          *rval = JSVAL_FALSE;

          return JS_FALSE;

        }

    }



    *rval = JSVAL_TRUE;

    return JS_TRUE;

out:

    *rval = JSVAL_FALSE;

    return JS_FALSE;

}



static JSBool

file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    JSString    *str;



    SECURITY_CHECK(cx, NULL, "writeln", file);

    JSFILE_CHECK_WRITE;



    /* don't report an error here */

    if(!file_write(cx, obj, argc, argv, rval))  return JS_FALSE;

    /* don't do security here -- we passed the check in file_write */

    str = JS_NewStringCopyZ(cx, "\n");



    if (js_FileWrite(cx, file, JS_GetStringChars(str), JS_GetStringLength(str),

            file->type)==-1){

        *rval = JSVAL_FALSE;

        return JS_FALSE;

    }



    /* eol causes flush if hasAutoflush is turned on */

    if (file->hasAutoflush)

        file_flush(cx, obj, 0, NULL, rval);



    *rval =  JSVAL_TRUE;

    return JS_TRUE;

out:

    *rval = JSVAL_FALSE;

    return JS_FALSE;

}



static JSBool

file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    jsuint      i;

    jsuint      limit;

    JSObject    *array;

    JSObject    *elem;

    jsval       elemval;



    SECURITY_CHECK(cx, NULL, "writeAll", file);

    JSFILE_CHECK_ONE_ARG("writeAll");

    JSFILE_CHECK_WRITE;



    if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) {

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR);

        goto out;

    }



    array = JSVAL_TO_OBJECT(argv[0]);



    JS_GetArrayLength(cx, array, &limit);



    for (i = 0; i<limit; i++) {

        if (!JS_GetElement(cx, array, i, &elemval))  return JS_FALSE;

        elem = JSVAL_TO_OBJECT(elemval);

        file_writeln(cx, obj, 1, &elemval, rval);

    }



    *rval = JSVAL_TRUE;

    return JS_TRUE;

out:

    *rval = JSVAL_FALSE;

    return JS_FALSE;

}



static JSBool

file_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    JSString    *str;

    int32       want, count;

    jschar      *buf;



    SECURITY_CHECK(cx, NULL, "read", file);

    JSFILE_CHECK_ONE_ARG("read");

    JSFILE_CHECK_READ;



    if (!JS_ValueToInt32(cx, argv[0], &want)){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "read", argv[0]);

        goto out;

    }



    /* want = (want>262144)?262144:want; * arbitrary size limitation */



    buf = JS_malloc(cx, want*sizeof buf[0]);

    if (!buf)  goto out;



    count =  js_FileRead(cx, file, buf, want, file->type);

    if (count>0) {

        str = JS_NewUCStringCopyN(cx, buf, count);

        *rval = STRING_TO_JSVAL(str);

        JS_free(cx, buf);

        return JS_TRUE;

    } else {

        JS_free(cx, buf);

        goto out;

    }

out:

    *rval = JSVAL_FALSE;

    return JS_FALSE;

}



static JSBool

file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    JSString    *str;

    jschar      *buf;

    int32       offset;

    intN        room;

    jschar      data, data2;

    JSBool      endofline;



    SECURITY_CHECK(cx, NULL, "readln", file);

    JSFILE_CHECK_READ;



    if (!file->linebuffer) {

        buf = JS_malloc(cx, MAX_LINE_LENGTH*(sizeof data));

        if (!buf) goto out;

        file->linebuffer = JS_NewUCString(cx, buf, MAX_LINE_LENGTH);

    }

    room = JS_GetStringLength(file->linebuffer);

    offset = 0;



    /* XXX TEST ME!! TODO: yes, please do */

    for(;;) {

        if (!js_FileRead(cx, file, &data, 1, file->type)) {

            endofline = JS_FALSE;

            goto loop;

        }

        switch (data) {

        case '\n' :

            endofline = JS_TRUE;

            goto loop;

        case '\r' :

            if (!js_FileRead(cx, file, &data2, 1, file->type)) {

                endofline = JS_TRUE;

                goto loop;

            }

            if (data2!='\n') { /* We read one char too far.  Buffer it. */

                file->charBuffer = data2;

                file->charBufferUsed = JS_TRUE;

            }

            endofline = JS_TRUE;

            goto loop;

        default:

            if (--room < 0) {

                buf = JS_malloc(cx, (offset+MAX_LINE_LENGTH)*sizeof data);

                if (!buf) return JS_FALSE;

                room = MAX_LINE_LENGTH-1;

                memcpy(buf, JS_GetStringChars(file->linebuffer),

                    JS_GetStringLength(file->linebuffer));

                /* what follows may not be the cleanest way. */

                file->linebuffer->chars = buf;

                file->linebuffer->length =  offset+MAX_LINE_LENGTH;

            }

            file->linebuffer->chars[offset++] = data;

            break;

        }

    }

loop:

    file->linebuffer->chars[offset] = 0;

    if ((endofline==JS_TRUE)) {

        str = JS_NewUCStringCopyN(cx, JS_GetStringChars(file->linebuffer),

                                    offset);

        *rval = STRING_TO_JSVAL(str);

        return JS_TRUE;

    }else{

        goto out;

    }

out:

    *rval = JSVAL_NULL;

    return JS_FALSE;

}



static JSBool

file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    JSObject    *array;

    jsint       len;

    jsval       line;



    SECURITY_CHECK(cx, NULL, "readAll", file);

    JSFILE_CHECK_READ;



    array = JS_NewArrayObject(cx, 0, NULL);

    len = 0;



    while(file_readln(cx, obj, 0, NULL, &line)){

        JS_SetElement(cx, array, len, &line);

        len++;

    }



    *rval = OBJECT_TO_JSVAL(array);

    return JS_TRUE;

out:

    *rval = JSVAL_FALSE;

    return JS_FALSE;

}



static JSBool

file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    int32       toskip;

    int32       pos;



    SECURITY_CHECK(cx, NULL, "seek", file);

    JSFILE_CHECK_ONE_ARG("seek");

    JSFILE_CHECK_NATIVE("seek");

    JSFILE_CHECK_READ;



    if (!JS_ValueToInt32(cx, argv[0], &toskip)){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "seek", argv[0]);

        goto out;

    }



    if(!file->hasRandomAccess){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_NO_RANDOM_ACCESS, file->path);

       goto out;

    }



    if(js_isDirectory(cx, file)){

        JS_ReportWarning(cx,"Seek on directories is not supported, proceeding");

        goto out;

    }



    pos = js_FileSeek(cx, file, toskip, file->type);



    if (pos!=-1) {

        *rval = INT_TO_JSVAL(pos);

        return JS_TRUE;

    }

out:

    *rval = JSVAL_VOID;

    return JS_FALSE;

}



static JSBool

file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    PRDir       *dir;

    PRDirEntry  *entry;

    JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    JSObject    *array;

    JSObject    *eachFile;

    jsint       len;

    jsval       v;

    JSRegExp    *re = NULL;

    JSFunction  *func = NULL;

    JSString    *str;

    jsval       args[1];

    char        *filePath;



    SECURITY_CHECK(cx, NULL, "list", file);

    JSFILE_CHECK_NATIVE("list");



    if (argc==1) {

        if (JSVAL_IS_REGEXP(cx, argv[0])) {

            re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));

        }else

        if (JSVAL_IS_FUNCTION(cx, argv[0])) {

            func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));

        }else{

            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, argv[0]);

            goto out;

        }

    }



    if (!js_isDirectory(cx, file)) {

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, file->path);

        goto out;

    }



    dir = PR_OpenDir(file->path);

    if(!dir){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_OP_FAILED, "open", file->path);

        goto out;

    }



    /* create JSArray here... */

    array = JS_NewArrayObject(cx, 0, NULL);

    len = 0;



    while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))!=NULL) {

        /* first, check if we have a regexp */

        if (re!=NULL) {

            size_t index = 0;



            str = JS_NewStringCopyZ(cx, entry->name);

            if(!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &v)){

                /* don't report anything here */

                goto out;

            }

            /* not matched! */

            if (JSVAL_IS_NULL(v)) {

                continue;

            }

        }else

        if (func!=NULL) {

            str = JS_NewStringCopyZ(cx, entry->name);

            args[0] = STRING_TO_JSVAL(str);

            if(!JS_CallFunction(cx, obj, func, 1, args, &v)){

                goto out;

            }



            if (v==JSVAL_FALSE) {

                continue;

            }

        }



        filePath = js_combinePath(cx, file->path, (char*)entry->name);



        eachFile = js_NewFileObject(cx, filePath);

        JS_free(cx, filePath);

        if (!eachFile){

            JS_ReportWarning(cx, "File %s cannot be retrieved", filePath);

            continue;

        }

        v = OBJECT_TO_JSVAL(eachFile);

        JS_SetElement(cx, array, len, &v);

        JS_SetProperty(cx, array, entry->name, &v);

        len++;

    }



    if(PR_CloseDir(dir)!=PR_SUCCESS){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_OP_FAILED, "close", file->path);

        goto out;

    }

    *rval = OBJECT_TO_JSVAL(array);

    return JS_TRUE;

out:

    *rval = JSVAL_NULL;

    return JS_FALSE;

}



static JSBool

file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);



    SECURITY_CHECK(cx, NULL, "mkdir", file);

    JSFILE_CHECK_ONE_ARG("mkdir");

    JSFILE_CHECK_NATIVE("mkdir");



    /* if the current file is not a directory, find out the directory name */

    if (!js_isDirectory(cx, file)) {

        char        *dir = js_fileDirectoryName(cx, file->path);

        JSObject    *dirObj = js_NewFileObject(cx, dir);



        JS_free(cx, dir);



        /* call file_mkdir with the right set of parameters if needed */

        if (file_mkdir(cx, dirObj, argc, argv, rval))

			return JS_TRUE;

		else

            goto out;

    }else{

        char *dirName = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));

        char *fullName;



        fullName = js_combinePath(cx, file->path, dirName);

        if (PR_MkDir(fullName, 0755)==PR_SUCCESS){

            *rval = JSVAL_TRUE;

            JS_free(cx, fullName);

            return JS_TRUE;

        }else{

            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                JSFILEMSG_OP_FAILED, "mkdir", fullName);

            JS_free(cx, fullName);

            goto out;

        }

    }

out:

    *rval = JSVAL_FALSE;

    return JS_FALSE;

}



static JSBool

file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval)

{

    JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);



    *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, file->path));

    return JS_TRUE;

}



static JSBool

file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    char url[MAX_PATH_LENGTH];

    jschar *urlChars;



	JSFILE_CHECK_NATIVE("toURL");



    sprintf(url, "file://%s", file->path);

    /* TODO: js_escape in jsstr.h may go away at some point */



    urlChars = js_InflateString(cx, url, strlen(url));

    if (urlChars == NULL) return JS_FALSE;

    *rval = STRING_TO_JSVAL(js_NewString(cx, urlChars, strlen(url), 0));

    if (!str_escape(cx, obj, 0, rval, rval)) return JS_FALSE;



    return JS_TRUE;

out:

    *rval = JSVAL_VOID;

    return JS_FALSE;

}





static void

file_finalize(JSContext *cx, JSObject *obj)

{

    JSFile *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);



	if(file){

        /* close the file before exiting */

        if(file->isOpen && !file->isNative){

            jsval vp;

            file_close(cx, obj, 0, NULL, &vp);

        }



		if (file->path)

			JS_free(cx, file->path);



		JS_free(cx, file);

	}

}



/*

    Allocates memory for the file object, sets fields to defaults.

*/

static JSFile*

file_init(JSContext *cx, JSObject *obj, char *bytes)

{

    JSFile      *file;



    file = JS_malloc(cx, sizeof *file);

    if (!file) return NULL;

    memset(file, 0 , sizeof *file);



    js_ResetAttributes(file);



    file->path = RESOLVE_PATH(cx, bytes);



    if (!JS_SetPrivate(cx, obj, file)) {

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path);

        JS_free(cx, file);

        return NULL;

    }else

        return file;

}



/* Returns a JSObject. This function is globally visible */

JS_PUBLIC_API(JSObject*)

js_NewFileObject(JSContext *cx, char *filename)

{

    JSObject    *obj;

    JSFile      *file;



    obj = JS_NewObject(cx, &file_class, NULL, NULL);

    if (!obj){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObject");

        return NULL;

    }

    file = file_init(cx, obj, filename);

    if(!file) return NULL;

    return obj;

}



/* Internal function, used for cases which NSPR file support doesn't cover */

JSObject*

js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename,

    int32 mode, JSBool open, JSBool randomAccess)

{

    JSObject *obj;

    JSFile   *file;

#ifdef XP_MAC

    JS_ReportWarning(cx, "Native files are not fully supported on the MAC");

#endif



    obj = JS_NewObject(cx, &file_class, NULL, NULL);

    if (!obj){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObjectFromFILE");

        return NULL;

    }

    file = file_init(cx, obj, filename);

    if(!file) return NULL;



    file->nativehandle = nativehandle;



    /* free result of RESOLVE_PATH from file_init. */

    JS_ASSERT(file->path != NULL);

    JS_free(cx, file->path);



    file->path = strdup(filename);

    file->isOpen = open;

    file->mode = mode;

    file->hasRandomAccess = randomAccess;

    file->isNative = JS_TRUE;

    return obj;

}



/*

    Real file constructor that is called from JavaScript.

    Basically, does error processing and calls file_init.

*/

static JSBool

file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

    jsval *rval)

{

    JSString *str;

    JSFile   *file;



    str = (argc==0)?JS_InternString(cx, ""):JS_ValueToString(cx, argv[0]);



    if (!str){

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, argv[0]);

        goto out;

    }



    file = file_init(cx, obj, JS_GetStringBytes(str));

    if (!file)  goto out;



    SECURITY_CHECK(cx, NULL, "constructor", file);



    return JS_TRUE;

out:

    *rval = JSVAL_VOID;

    return JS_FALSE;

}



/* -------------------- File methods and properties ------------------------- */

static JSFunctionSpec file_functions[] = {

    { "open",           file_open, 0},

    { "close",          file_close, 0},

    { "remove",         file_remove, 0},

    { "copyTo",         file_copyTo, 0},

    { "renameTo",       file_renameTo, 0},

    { "flush",          file_flush, 0},

    { "seek",           file_seek, 0},

    { "read",           file_read, 0},

    { "readln",         file_readln, 0},

    { "readAll",        file_readAll, 0},

    { "write",          file_write, 0},

    { "writeln",        file_writeln, 0},

    { "writeAll",       file_writeAll, 0},

    { "list",           file_list, 0},

    { "mkdir",          file_mkdir, 0},

	{ "toString",       file_toString, 0},

    { "toURL",			file_toURL, 0},

    {0}

};



enum file_tinyid {

    FILE_LENGTH             = -2,

    FILE_PARENT             = -3,

    FILE_PATH               = -4,

    FILE_NAME               = -5,

    FILE_ISDIR              = -6,

    FILE_ISFILE             = -7,

    FILE_EXISTS             = -8,

    FILE_CANREAD            = -9,

    FILE_CANWRITE           = -10,

    FILE_OPEN               = -11,

    FILE_TYPE               = -12,

    FILE_MODE               = -13,

    FILE_CREATED            = -14,

    FILE_MODIFIED           = -15,

    FILE_SIZE               = -16,

    FILE_RANDOMACCESS       = -17,

    FILE_POSITION           = -18,

    FILE_APPEND             = -19,

    FILE_REPLACE            = -20,

    FILE_AUTOFLUSH          = -21,

    FILE_ISNATIVE           = -22,

};



static JSPropertySpec file_props[] = {

   {"length",          FILE_LENGTH,        JSPROP_ENUMERATE | JSPROP_READONLY },

   {"parent",          FILE_PARENT,        JSPROP_ENUMERATE | JSPROP_READONLY },

   {"path",            FILE_PATH,          JSPROP_ENUMERATE | JSPROP_READONLY },

   {"name",            FILE_NAME,          JSPROP_ENUMERATE | JSPROP_READONLY },

   {"isDirectory",     FILE_ISDIR,         JSPROP_ENUMERATE | JSPROP_READONLY },

   {"isFile",          FILE_ISFILE,        JSPROP_ENUMERATE | JSPROP_READONLY },

   {"exists",          FILE_EXISTS,        JSPROP_ENUMERATE | JSPROP_READONLY },

   {"canRead",         FILE_CANREAD,       JSPROP_ENUMERATE | JSPROP_READONLY },

   {"canWrite",        FILE_CANWRITE,      JSPROP_ENUMERATE | JSPROP_READONLY },

   {"canAppend",       FILE_APPEND,        JSPROP_ENUMERATE | JSPROP_READONLY },

   {"canReplace",      FILE_REPLACE,       JSPROP_ENUMERATE | JSPROP_READONLY },

   {"isOpen",          FILE_OPEN,          JSPROP_ENUMERATE | JSPROP_READONLY },

   {"type",            FILE_TYPE,          JSPROP_ENUMERATE | JSPROP_READONLY },

   {"mode",            FILE_MODE,          JSPROP_ENUMERATE | JSPROP_READONLY },

   {"creationTime",    FILE_CREATED,       JSPROP_ENUMERATE | JSPROP_READONLY },

   {"lastModified",    FILE_MODIFIED,      JSPROP_ENUMERATE | JSPROP_READONLY },

   {"size",            FILE_SIZE,          JSPROP_ENUMERATE | JSPROP_READONLY },

   {"hasRandomAccess", FILE_RANDOMACCESS,  JSPROP_ENUMERATE | JSPROP_READONLY },

   {"hasAutoFlush",    FILE_AUTOFLUSH,     JSPROP_ENUMERATE | JSPROP_READONLY },

   {"position",        FILE_POSITION,      JSPROP_ENUMERATE },

   {"isNative",        FILE_ISNATIVE,      JSPROP_ENUMERATE | JSPROP_READONLY },

   {0}

};



/* ------------------------- Property getter/setter ------------------------- */

static JSBool

file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSFile      *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    char        *str;

    jsint       tiny;

    PRFileInfo  info;

	JSBool		flag;

    PRExplodedTime

                expandedTime;



    tiny = JSVAL_TO_INT(id);

    if(!file) return JS_TRUE;



    switch (tiny) {

    case FILE_PARENT:

        SECURITY_CHECK(cx, NULL, "parent", file);

        *vp = js_parent(cx, file);

        break;

    case FILE_PATH:

        *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, file->path));

        break;

    case FILE_NAME:

        *vp = js_name(cx, file);

        break;

    case FILE_ISDIR:

        SECURITY_CHECK(cx, NULL, "isDirectory", file);

        *vp = BOOLEAN_TO_JSVAL(js_isDirectory(cx, file));

        break;

    case FILE_ISFILE:

        SECURITY_CHECK(cx, NULL, "isFile", file);

        *vp = BOOLEAN_TO_JSVAL(js_isFile(cx, file));

        break;

    case FILE_EXISTS:

        SECURITY_CHECK(cx, NULL, "exists", file);

        *vp = BOOLEAN_TO_JSVAL(js_exists(cx, file));

        break;

    case FILE_ISNATIVE:

        SECURITY_CHECK(cx, NULL, "isNative", file);

        *vp = BOOLEAN_TO_JSVAL(file->isNative);

        break;

    case FILE_CANREAD:

        SECURITY_CHECK(cx, NULL, "canRead", file);

        *vp = BOOLEAN_TO_JSVAL(js_canRead(cx, file));

        break;

    case FILE_CANWRITE:

        SECURITY_CHECK(cx, NULL, "canWrite", file);

        *vp = BOOLEAN_TO_JSVAL(js_canWrite(cx, file));

        break;

    case FILE_OPEN:

        SECURITY_CHECK(cx, NULL, "isOpen", file);

        *vp = BOOLEAN_TO_JSVAL(file->isOpen);

        break;

    case FILE_APPEND :

        SECURITY_CHECK(cx, NULL, "canAppend", file);

        JSFILE_CHECK_OPEN("canAppend");

        *vp = BOOLEAN_TO_JSVAL(!file->isNative &&

                (file->mode&PR_APPEND)==PR_APPEND);

        break;

    case FILE_REPLACE :

        SECURITY_CHECK(cx, NULL, "canReplace", file);

        JSFILE_CHECK_OPEN("canReplace");

        *vp = BOOLEAN_TO_JSVAL(!file->isNative &&

                (file->mode&PR_TRUNCATE)==PR_TRUNCATE);

        break;

    case FILE_AUTOFLUSH :

        SECURITY_CHECK(cx, NULL, "hasAutoFlush", file);

        JSFILE_CHECK_OPEN("hasAutoFlush");

        *vp = BOOLEAN_TO_JSVAL(!file->isNative && file->hasAutoflush);

        break;

    case FILE_TYPE:

        SECURITY_CHECK(cx, NULL, "type", file);

        JSFILE_CHECK_OPEN("type");

        if(js_isDirectory(cx, file)){

            *vp = JSVAL_VOID;

            break;

        }



        switch (file->type) {

        case ASCII:

            *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, asciistring));

            break;

        case UTF8:

            *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, utfstring));

            break;

        case UCS2:

            *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, unicodestring));

            break;

        default:

            JS_ReportWarning(cx, "Unsupported file type %d, proceeding",

                file->type);

        }

        break;

    case FILE_MODE:

        SECURITY_CHECK(cx, NULL, "mode", file);

        JSFILE_CHECK_OPEN("mode");

        str = (char*)JS_malloc(cx, MODE_SIZE);

        str[0] = '\0';

        flag = JS_FALSE;



        if ((file->mode&PR_RDONLY)==PR_RDONLY) {

            if (flag) strcat(str, ",");

            strcat(str, "read");

            flag = JS_TRUE;

        }

        if ((file->mode&PR_WRONLY)==PR_WRONLY) {

            if (flag) strcat(str, ",");

            strcat(str, "write");

            flag = JS_TRUE;

        }

        if ((file->mode&PR_RDWR)==PR_RDWR) {

            if (flag) strcat(str, ",");

            strcat(str, "readWrite");

            flag = JS_TRUE;

        }

        if ((file->mode&PR_APPEND)==PR_APPEND) {

            if (flag) strcat(str, ",");

            strcat(str, "append");

            flag = JS_TRUE;

        }

        if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) {

            if (flag) strcat(str, ",");

            strcat(str, "create");

            flag = JS_TRUE;

        }

        if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) {

            if (flag) strcat(str, ",");

            strcat(str, "replace");

            flag = JS_TRUE;

        }

        if (file->hasAutoflush) {

            if (flag) strcat(str, ",");

            strcat(str, "hasAutoFlush");

            flag = JS_TRUE;

        }

        *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));

        JS_free(cx, str);

        break;

    case FILE_CREATED:

        SECURITY_CHECK(cx, NULL, "creationTime", file);

        JSFILE_CHECK_NATIVE("creationTime");

        if(((file->isOpen)?

                        PR_GetOpenFileInfo(file->handle, &info):

                        PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){

            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);

            goto out;

        }



        PR_ExplodeTime(info.creationTime, PR_LocalTimeParameters,&expandedTime);

        *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx,  expandedTime.tm_year,

                                    expandedTime.tm_month,

                                    expandedTime.tm_mday,

                                    expandedTime.tm_hour,

                                    expandedTime.tm_min,

                                    expandedTime.tm_sec));

        break;

    case FILE_MODIFIED:

        SECURITY_CHECK(cx, NULL, "lastModified", file);

        JSFILE_CHECK_NATIVE("lastModified");

        if(((file->isOpen)?

                        PR_GetOpenFileInfo(file->handle, &info):

                        PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){

            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);

            goto out;

        }



        PR_ExplodeTime(info.modifyTime, PR_LocalTimeParameters, &expandedTime);

        *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year,

                                    expandedTime.tm_month,

                                    expandedTime.tm_mday,

                                    expandedTime.tm_hour,

                                    expandedTime.tm_min,

                                    expandedTime.tm_sec));

        break;

    case FILE_SIZE:

        SECURITY_CHECK(cx, NULL, "size", file);

        *vp = js_size(cx, file);

        break;

    case FILE_LENGTH:

        SECURITY_CHECK(cx, NULL, "length", file);

        JSFILE_CHECK_NATIVE("length");



        if (js_isDirectory(cx, file)) { /* XXX debug me */

            PRDir       *dir;

            PRDirEntry  *entry;

            jsint       count = 0;



            if(!(dir = PR_OpenDir(file->path))){

                JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                    JSFILEMSG_CANNOT_OPEN_DIR, file->path);

                goto out;

            }



            while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))) {

                count++;

            }



            if(!PR_CloseDir(dir)){

                JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                    JSFILEMSG_OP_FAILED, "close", file->path);



                goto out;

            }



            *vp = INT_TO_JSVAL(count);

            break;

        }else{

            /* return file size */

            *vp = js_size(cx, file);

        }

        break;

    case FILE_RANDOMACCESS:

            SECURITY_CHECK(cx, NULL, "hasRandomAccess", file);

            JSFILE_CHECK_OPEN("hasRandomAccess");

            *vp = BOOLEAN_TO_JSVAL(file->hasRandomAccess);

        break;

    case FILE_POSITION:

        SECURITY_CHECK(cx, NULL, "position", file);

        JSFILE_CHECK_NATIVE("position");

        JSFILE_CHECK_OPEN("position");



        if(!file->hasRandomAccess){

            JS_ReportWarning(cx, "File %s doesn't support random access, can't report the position, proceeding");

            *vp = JSVAL_VOID;

            break;

        }



        if (file->isOpen && js_isFile(cx, file)) {

            int pos = PR_Seek(file->handle, 0, PR_SEEK_CUR);

            if(pos!=-1){

                *vp = INT_TO_JSVAL(pos);

            }else{

                JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                    JSFILEMSG_CANNOT_REPORT_POSITION, file->path);

                goto out;

            }

        }else {

            JS_ReportWarning(cx, "File %s is closed or not a plain file,"

                " can't report position, proceeding");

            goto out;

        }

        break;

    default:

        SECURITY_CHECK(cx, NULL, "file_access", file);

		/* this is some other property -- try to use the dir["file"] syntax */

        if(js_isDirectory(cx, file)){

			PRDir *dir = NULL;

			PRDirEntry *entry = NULL;

            char *prop_name = JS_GetStringBytes(JS_ValueToString(cx, id));



            /* no native files past this point */

            dir = PR_OpenDir(file->path);

            if(!dir) {

                /* This is probably not a directory */

				JS_ReportWarning(cx, "Can't open directory %s", file->path);

                return JS_FALSE;

            }



            while((entry = PR_ReadDir(dir, PR_SKIP_NONE))!=NULL){

				if(!strcmp(entry->name, prop_name)){

                    str = js_combinePath(cx, file->path, prop_name);

                    *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, str));

					JS_free(cx, str);

                    return JS_TRUE;

				}

			}

		}

    }

    return JS_TRUE;

out:

	*vp = JSVAL_VOID;

    return JS_FALSE;

}



static JSBool

file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSFile  *file = JS_GetInstancePrivate(cx, obj, &file_class, NULL);

    jsint   slot;



    if (JSVAL_IS_STRING(id)){

        return JS_TRUE;

    }



    slot = JSVAL_TO_INT(id);



    switch (slot) {

    /* File.position  = 10 */

    case FILE_POSITION:

        SECURITY_CHECK(cx, NULL, "set_position", file);

        JSFILE_CHECK_NATIVE("set_position");



        if(!file->hasRandomAccess){

            JS_ReportWarning(cx, "File %s doesn't support random access, can't "

                "report the position, proceeding");

            goto out;

        }



        if (file->isOpen && js_isFile(cx, file)) {

            int32 pos;

		    int32 offset;



			if (!JS_ValueToInt32(cx, *vp, &offset)){

				JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

					JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "position", *vp);

				goto out;

			}



			pos = PR_Seek(file->handle, offset, PR_SEEK_SET);



            if(pos!=-1){

                *vp = INT_TO_JSVAL(pos);

            }else{

                JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

                    JSFILEMSG_CANNOT_SET_POSITION, file->path);

                goto out;

            }

        } else {

            JS_ReportWarning(cx, "File %s is closed or not a file, can't set "

                "position, proceeding", file->path);

            goto out;

        }

    }



    return JS_TRUE;

out:

	*vp = JSVAL_VOID;

	return JS_FALSE;

}



/*

    File.currentDir = new File("D:\") or File.currentDir = "D:\"

*/

static JSBool

file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSObject *rhsObject;

    char     *path;

    JSFile   *file = JS_GetInstancePrivate(cx, rhsObject, &file_class, NULL);



    /* Look at the rhs and extract a file object from it */

    if (JSVAL_IS_OBJECT(*vp)){

        if (JS_InstanceOf(cx, rhsObject, &file_class, NULL)){

            /* Braindamaged rhs -- just return the old value */

            if (file && (!js_exists(cx, file) || !js_isDirectory(cx, file))){

                JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);

                goto out;

            }else{

                rhsObject = JSVAL_TO_OBJECT(*vp);

                chdir(file->path);

                return JS_TRUE;

            }

        }else

            goto out;

    }else{

        path      = JS_GetStringBytes(JS_ValueToString(cx, *vp));

        rhsObject = js_NewFileObject(cx, path);

        if (!rhsObject)  goto out;



        if (!file || !js_exists(cx, file) || !js_isDirectory(cx, file)){

            JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp);

        }else{

            *vp = OBJECT_TO_JSVAL(rhsObject);

            chdir(path);

        }

    }

    return JS_TRUE;

out:

	*vp = JSVAL_VOID;

	return JS_FALSE;

}



/* Declare class */

static JSClass file_class = {

    FILE_CONSTRUCTOR, JSCLASS_HAS_PRIVATE,

    JS_PropertyStub,  JS_PropertyStub,  file_getProperty,  file_setProperty,

    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   file_finalize

};



/* -------------------- Functions exposed to the outside -------------------- */

JS_PUBLIC_API(JSObject*)

js_InitFileClass(JSContext *cx, JSObject* obj, JSBool initStandardStreams)

{

    JSObject *file, *ctor, *afile;

    jsval    vp;

    char     *currentdir;

    char     separator[2];



    file = JS_InitClass(cx, obj, NULL, &file_class, file_constructor, 1,

        file_props, file_functions, NULL, NULL);

    if (!file) {

        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,

            JSFILEMSG_INIT_FAILED);

        return NULL;

    }



    ctor = JS_GetConstructor(cx, file);

    if (!ctor)  return NULL;



	/* Define CURRENTDIR property. We are doing this to get a

	slash at the end of the current dir */

    afile = js_NewFileObject(cx, CURRENT_DIR);

    currentdir =  JS_malloc(cx, MAX_PATH_LENGTH);

    currentdir =  getcwd(currentdir, MAX_PATH_LENGTH);

    afile = js_NewFileObject(cx, currentdir);

    JS_free(cx, currentdir);

    vp = OBJECT_TO_JSVAL(afile);

    JS_DefinePropertyWithTinyId(cx, ctor, CURRENTDIR_PROPERTY, 0, vp,

                JS_PropertyStub, file_currentDirSetter,

                JSPROP_ENUMERATE | JSPROP_READONLY );



    if(initStandardStreams){

        /* Code to create stdin, stdout, and stderr. Insert in the appropriate place. */

        /* Define input */

        vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdin,

                STDINPUT_NAME, PR_RDONLY, JS_TRUE, JS_FALSE));

        JS_SetProperty(cx, ctor, "input", &vp);



        /* Define output */

        vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdout,

                STDOUTPUT_NAME, PR_WRONLY, JS_TRUE, JS_FALSE));

        JS_SetProperty(cx, ctor, "output", &vp);



        /* Define error */

        vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stderr,

                STDERROR_NAME, PR_WRONLY, JS_TRUE, JS_FALSE));

        JS_SetProperty(cx, ctor, "error", &vp);

    }

    separator[0] = FILESEPARATOR;

    separator[1] = '\0';

    vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, separator));

    JS_DefinePropertyWithTinyId(cx, ctor, SEPARATOR_PROPERTY, 0, vp,

                JS_PropertyStub, JS_PropertyStub,

                JSPROP_ENUMERATE | JSPROP_READONLY );

    return file;

}

#endif /* JS_HAS_FILE_OBJECT */

 

**** End of jsfile.c ****

 

**** Start of jsfile.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef _jsfile_h__

#define _jsfile_h__



#if JS_HAS_FILE_OBJECT

extern JS_PUBLIC_API(JSObject*)

js_InitFileClass(JSContext *cx, JSObject* obj, JSBool initStandardStreams);



extern JS_PUBLIC_API(JSObject*)

js_NewFileObject(JSContext *cx, char *bytes);

#endif /* JS_HAS_FILE_OBJECT */

#endif /* _jsfile_h__ */

 

**** End of jsfile.h ****

 

**** Start of jsfun.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS function support.

 */

#include "jsstddef.h"

#include <string.h>

#include "jstypes.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jsapi.h"

#include "jsarray.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsfun.h"

#include "jsgc.h"

#include "jsinterp.h"

#include "jslock.h"

#include "jsnum.h"

#include "jsobj.h"

#include "jsopcode.h"

#include "jsparse.h"

#include "jsscan.h"

#include "jsscope.h"

#include "jsscript.h"

#include "jsstr.h"

#include "jsexn.h"



enum {

    CALL_ARGUMENTS  = -1,       /* predefined arguments local variable */

    CALL_CALLEE     = -2,       /* reference to active function's object */

    ARGS_LENGTH     = -3,       /* number of actual args, arity if inactive */

    ARGS_CALLEE     = -4,       /* reference from arguments to active funobj */

    FUN_ARITY       = -5,       /* number of formal parameters; desired argc */

    FUN_NAME        = -6,       /* function name, "" if anonymous */

    FUN_CALL        = -7,       /* function's top Call object in this context */

    FUN_CALLER      = -8        /* Function.prototype.caller, backward compat */

};



#undef TEST_BIT

#undef SET_BIT

#define TEST_BIT(tinyid, bitset)    ((bitset) & JS_BIT(-(tinyid) - 1))

#define SET_BIT(tinyid, bitset)     ((bitset) |= JS_BIT(-(tinyid) - 1))



#if JS_HAS_ARGS_OBJECT



JSBool

js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp)

{

    JSObject *argsobj;



    if (TEST_BIT(CALL_ARGUMENTS, fp->overrides)) {

        JS_ASSERT(fp->callobj);

        return OBJ_GET_PROPERTY(cx, fp->callobj,

                                (jsid) cx->runtime->atomState.argumentsAtom,

                                vp);

    }

    argsobj = js_GetArgsObject(cx, fp);

    if (!argsobj)

        return JS_FALSE;

    *vp = OBJECT_TO_JSVAL(argsobj);

    return JS_TRUE;

}



JSBool

js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id,

                   JSObject **objp, jsval *vp)

{

    jsval val;

    JSObject *obj;

    uintN slot;



    if (TEST_BIT(CALL_ARGUMENTS, fp->overrides)) {

        JS_ASSERT(fp->callobj);

        if (!OBJ_GET_PROPERTY(cx, fp->callobj,

                              (jsid) cx->runtime->atomState.argumentsAtom,

                              &val)) {

            return JS_FALSE;

        }

        if (JSVAL_IS_PRIMITIVE(val)) {

            obj = js_ValueToNonNullObject(cx, val);

            if (!obj)

                return JS_FALSE;

        } else {

            obj = JSVAL_TO_OBJECT(val);

        }

        *objp = obj;

        return OBJ_GET_PROPERTY(cx, obj, id, vp);

    }



    *objp = NULL;

    *vp = JSVAL_VOID;

    if (JSVAL_IS_INT(id)) {

        slot = (uintN) JSVAL_TO_INT(id);

        if (slot < fp->argc)

            *vp = fp->argv[slot];

    } else {

        if (id == (jsid) cx->runtime->atomState.lengthAtom)

            *vp = INT_TO_JSVAL((jsint) fp->argc);

    }

    return JS_TRUE;

}



JSObject *

js_GetArgsObject(JSContext *cx, JSStackFrame *fp)

{

    JSObject *argsobj;



    /* Create an arguments object for fp only if it lacks one. */

    JS_ASSERT(fp->fun);

    argsobj = fp->argsobj;

    if (argsobj)

        return argsobj;



    /* Link the new object to fp so it can get actual argument values. */

    argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL);

    if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) {

        cx->newborn[GCX_OBJECT] = NULL;

        return NULL;

    }

    fp->argsobj = argsobj;

    return argsobj;

}



static JSBool

args_enumerate(JSContext *cx, JSObject *obj);



JSBool

js_PutArgsObject(JSContext *cx, JSStackFrame *fp)

{

    JSObject *argsobj;

    JSBool ok;

    JSRuntime *rt;

    jsval rval;



    /*

     * Reuse args_enumerate here to reflect fp's actual arguments as indexed

     * elements of argsobj.

     */

    argsobj = fp->argsobj;

    ok = args_enumerate(cx, argsobj);



    /*

     * Now get the prototype properties so we snapshot fp->fun and fp->argc

     * before fp goes away.

     */

    rt = cx->runtime;

    ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.calleeAtom, &rval);

    ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.calleeAtom, &rval);

    ok &= js_GetProperty(cx, argsobj, (jsid)rt->atomState.lengthAtom, &rval);

    ok &= js_SetProperty(cx, argsobj, (jsid)rt->atomState.lengthAtom, &rval);



    /*

     * Clear the private pointer to fp, which is about to go away (js_Invoke).

     * Do this last because the args_enumerate and js_GetProperty calls above

     * need to follow the private slot to find fp.

     */

    ok &= JS_SetPrivate(cx, argsobj, NULL);

    fp->argsobj = NULL;

    return ok;

}



static JSBool

Arguments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    if (JS_HAS_STRICT_OPTION(cx) &&

        !JS_ReportErrorFlagsAndNumber(cx,

                                      JSREPORT_WARNING | JSREPORT_STRICT,

                                      js_GetErrorMessage, NULL,

                                      JSMSG_DEPRECATED_USAGE,

                                      js_ArgumentsClass.name)) {

        return JS_FALSE;

    }



    if (!cx->fp->constructing) {

        obj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL);

        if (!obj)

            return JS_FALSE;

        *rval = OBJECT_TO_JSVAL(obj);

    }

    return JS_TRUE;

}



static JSPropertySpec args_props[] = {

    {js_length_str,     ARGS_LENGTH,    0,0,0},

    {js_callee_str,     ARGS_CALLEE,    0,0,0},

    {0,0,0,0,0}

};



static JSBool

args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    jsint slot;

    JSStackFrame *fp;



    if (!JSVAL_IS_INT(id))

        return JS_TRUE;

    slot = JSVAL_TO_INT(id);

    fp = (JSStackFrame *) JS_GetPrivate(cx, obj);



    switch (slot) {

      case ARGS_CALLEE:

        if (fp && !TEST_BIT(slot, fp->overrides))

            *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object);

        break;



      case ARGS_LENGTH:

        if (fp && !TEST_BIT(slot, fp->overrides))

            *vp = INT_TO_JSVAL((jsint)fp->argc);

        break;



      default:

        if (fp && (uintN)slot < fp->argc)

            *vp = fp->argv[slot];

        break;

    }

    return JS_TRUE;

}



static JSBool

args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    jsint slot;

    JSStackFrame *fp;

    jsdouble argc;



    if (!JSVAL_IS_INT(id))

        return JS_TRUE;

    slot = JSVAL_TO_INT(id);

    fp = (JSStackFrame *) JS_GetPrivate(cx, obj);



    switch (slot) {

      case ARGS_CALLEE:

        if (fp)

            SET_BIT(slot, fp->overrides);

        break;



      case ARGS_LENGTH:

        if (fp) {

            if (!js_ValueToNumber(cx, *vp, &argc))

                return JS_FALSE;

            argc = js_DoubleToInteger(argc);

            if (0 <= argc && argc < fp->argc)

                fp->argc = (uintN)argc;

            SET_BIT(slot, fp->overrides);

        }

        break;



      default:

        if (fp && (uintN)slot < fp->argc)

            fp->argv[slot] = *vp;

        break;

    }

    return JS_TRUE;

}



static JSBool

args_enumerate(JSContext *cx, JSObject *obj)

{

    JSStackFrame *fp;

    uintN attrs, slot;



    fp = (JSStackFrame *) JS_GetPrivate(cx, obj);

    if (!fp)

        return JS_TRUE;



    /* XXX ECMA specs DontEnum, contrary to all other array-like objects */

    attrs = JSVERSION_IS_ECMA(cx->version) ? 0 : JSPROP_ENUMERATE;



    for (slot = 0; slot < fp->argc; slot++) {

        if (!js_DefineProperty(cx, obj, (jsid) INT_TO_JSVAL((jsint)slot),

                               fp->argv[slot], NULL, NULL, attrs,

                               NULL)) {

            return JS_FALSE;

        }

    }

    return JS_TRUE;

}



JSClass js_ArgumentsClass = {

    js_Arguments_str,

    JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,

    JS_PropertyStub,  JS_PropertyStub,

    args_getProperty, args_setProperty,

    args_enumerate,   JS_ResolveStub,

    JS_ConvertStub,   JS_FinalizeStub,

    JSCLASS_NO_OPTIONAL_MEMBERS

};



#endif /* JS_HAS_ARGS_OBJECT */



#if JS_HAS_CALL_OBJECT



JSObject *

js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent)

{

    JSObject *callobj, *funobj;



    /* Create a call object for fp only if it lacks one. */

    JS_ASSERT(fp->fun);

    callobj = fp->callobj;

    if (callobj)

        return callobj;



    /* The default call parent is its function's parent (static link). */

    if (!parent) {

        funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object;

        if (funobj)

            parent = OBJ_GET_PARENT(cx, funobj);

    }



    /* Create the call object and link it to its stack frame. */

    callobj = js_NewObject(cx, &js_CallClass, NULL, parent);

    if (!callobj || !JS_SetPrivate(cx, callobj, fp)) {

        cx->newborn[GCX_OBJECT] = NULL;

        return NULL;

    }

    fp->callobj = callobj;



    /* Make callobj be the scope chain and the variables object. */

    fp->scopeChain = callobj;

    fp->varobj = callobj;

    return callobj;

}



static JSBool

call_enumerate(JSContext *cx, JSObject *obj);



JSBool

js_PutCallObject(JSContext *cx, JSStackFrame *fp)

{

    JSObject *callobj;

    JSBool ok;

    jsid argsid;

    jsval aval;



    /*

     * Reuse call_enumerate here to reflect all actual args and vars into the

     * call object from fp.

     */

    callobj = fp->callobj;

    if (!callobj)

        return JS_TRUE;

    ok = call_enumerate(cx, callobj);



    /*

     * Get the arguments object to snapshot fp's actual argument values.

     */

    if (fp->argsobj) {

        argsid = (jsid) cx->runtime->atomState.argumentsAtom;

        ok &= js_GetProperty(cx, callobj, argsid, &aval);

        ok &= js_SetProperty(cx, callobj, argsid, &aval);

        ok &= js_PutArgsObject(cx, fp);

    }



    /*

     * Clear the private pointer to fp, which is about to go away (js_Invoke).

     * Do this last because the call_enumerate and js_GetProperty calls above

     * need to follow the private slot to find fp.

     */

    ok &= JS_SetPrivate(cx, callobj, NULL);

    fp->callobj = NULL;

    return ok;

}



static JSBool

Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    if (JS_HAS_STRICT_OPTION(cx) &&

        !JS_ReportErrorFlagsAndNumber(cx,

                                      JSREPORT_WARNING | JSREPORT_STRICT,

                                      js_GetErrorMessage, NULL,

                                      JSMSG_DEPRECATED_USAGE,

                                      js_CallClass.name)) {

        return JS_FALSE;

    }



    if (!cx->fp->constructing) {

        obj = js_NewObject(cx, &js_CallClass, NULL, NULL);

        if (!obj)

            return JS_FALSE;

        *rval = OBJECT_TO_JSVAL(obj);

    }

    return JS_TRUE;

}



static JSPropertySpec call_props[] = {

    {js_arguments_str,  CALL_ARGUMENTS, JSPROP_PERMANENT,0,0},

    {"__callee__",      CALL_CALLEE,    0,0,0},

    {"__call__",        FUN_CALL,       0,0,0},

    {0,0,0,0,0}

};



static JSBool

call_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSStackFrame *fp;

    jsint slot;



    fp = (JSStackFrame *) JS_GetPrivate(cx, obj);

    if (!JSVAL_IS_INT(id))

        return JS_TRUE;

    slot = JSVAL_TO_INT(id);



    switch (slot) {

      case CALL_ARGUMENTS:

        if (fp && !TEST_BIT(slot, fp->overrides)) {

            JSObject *argsobj = js_GetArgsObject(cx, fp);

            if (!argsobj)

                return JS_FALSE;

            *vp = OBJECT_TO_JSVAL(argsobj);

        }

        break;



      case CALL_CALLEE:

        if (fp && !TEST_BIT(slot, fp->overrides))

            *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object);

        break;



      case FUN_CALL:

        if (fp && !TEST_BIT(slot, fp->overrides))

            *vp = OBJECT_TO_JSVAL(obj);

        break;



      default:

        if (fp && (uintN)slot < fp->argc)

            *vp = fp->argv[slot];

        break;

    }

    return JS_TRUE;

}



static JSBool

call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSStackFrame *fp;

    jsint slot;



    fp = (JSStackFrame *) JS_GetPrivate(cx, obj);

    if (!JSVAL_IS_INT(id))

        return JS_TRUE;

    slot = JSVAL_TO_INT(id);



    switch (slot) {

      case CALL_ARGUMENTS:

      case CALL_CALLEE:

      case FUN_CALL:

        if (fp)

            SET_BIT(slot, fp->overrides);

        break;



      default:

        if (fp && (uintN)slot < fp->argc)

            fp->argv[slot] = *vp;

        break;

    }

    return JS_TRUE;

}



JSBool

js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSStackFrame *fp;



    JS_ASSERT(JSVAL_IS_INT(id));

    fp = (JSStackFrame *) JS_GetPrivate(cx, obj);

    if (fp) {

        /* XXX no jsint slot commoning here to avoid MSVC1.52 crashes */

        if ((uintN)JSVAL_TO_INT(id) < fp->nvars)

            *vp = fp->vars[JSVAL_TO_INT(id)];

    }

    return JS_TRUE;

}



JSBool

js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSStackFrame *fp;



    JS_ASSERT(JSVAL_IS_INT(id));

    fp = (JSStackFrame *) JS_GetPrivate(cx, obj);

    if (fp) {

        /* XXX jsint slot is block-local here to avoid MSVC1.52 crashes */

        jsint slot = JSVAL_TO_INT(id);

        if ((uintN)slot < fp->nvars)

            fp->vars[slot] = *vp;

    }

    return JS_TRUE;

}



static JSBool

call_enumerate(JSContext *cx, JSObject *obj)

{

    JSStackFrame *fp;

    JSFunction *fun;

    JSScope *scope;

    JSScopeProperty *sprop;

    JSPropertyOp getter;

    JSProperty *prop;



    fp = (JSStackFrame *) JS_GetPrivate(cx, obj);

    if (!fp)

        return JS_TRUE;

    fun = fp->fun;

    if (!fun->script || !fun->object)

        return JS_TRUE;



    /* Reflect actual args for formal parameters, and all local variables. */

    scope = OBJ_SCOPE(fun->object);

    for (sprop = scope->props; sprop; sprop = sprop->next) {

        getter = SPROP_GETTER_SCOPE(sprop, scope);

        if (getter != js_GetArgument && getter != js_GetLocalVariable)

            continue;



        /* Trigger reflection in call_resolve by doing a lookup. */

        if (!js_LookupProperty(cx, obj, sym_id(sprop->symbols), &obj, &prop))

            return JS_FALSE;

        OBJ_DROP_PROPERTY(cx, obj, prop);

    }



    return JS_TRUE;

}



static JSBool

call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,

             JSObject **objp)

{

    JSStackFrame *fp;

    JSObject *funobj;

    JSString *str;

    JSAtom *atom;

    JSObject *obj2;

    JSScopeProperty *sprop;

    JSPropertyOp getter, setter;

    jsval propid, *vp;

    jsid symid;

    uintN attrs, slot, nslots;



    fp = (JSStackFrame *) JS_GetPrivate(cx, obj);

    if (!fp)

        return JS_TRUE;



    if (!JSVAL_IS_STRING(id))

        return JS_TRUE;



    funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object;

    if (!funobj)

        return JS_TRUE;



    str = JSVAL_TO_STRING(id);

    atom = js_AtomizeString(cx, str, 0);

    if (!atom)

        return JS_FALSE;

    if (!OBJ_LOOKUP_PROPERTY(cx, funobj, (jsid)atom, &obj2,

                             (JSProperty **)&sprop)) {

        return JS_FALSE;

    }



    if (sprop) {

        getter = SPROP_GETTER(sprop, obj2);

        propid = sprop->id;

        symid = (jsid) sym_atom(sprop->symbols);

        attrs = sprop->attrs;

        OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);

        if (getter == js_GetArgument || getter == js_GetLocalVariable) {

            if (getter == js_GetArgument) {

                vp = fp->argv;

                nslots = fp->fun->nargs;

                getter = setter = NULL;

            } else {

                vp = fp->vars;

                nslots = fp->nvars;

                getter = js_GetCallVariable;

                setter = js_SetCallVariable;

            }

            slot = (uintN)JSVAL_TO_INT(propid);

            if (!js_DefineProperty(cx, obj, symid,

                                   (slot < nslots) ? vp[slot] : JSVAL_VOID,

                                   getter, setter, attrs,

                                   (JSProperty **)&sprop)) {

                return JS_FALSE;

            }

            JS_ASSERT(sprop);

            if (slot < nslots)

                sprop->id = INT_TO_JSVAL(slot);

            OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop);

            *objp = obj;

        }

    }

    return JS_TRUE;

}



static JSBool

call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)

{

    JSStackFrame *fp;



    if (type == JSTYPE_FUNCTION) {

        fp = (JSStackFrame *) JS_GetPrivate(cx, obj);

        if (fp)

            *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object);

    }

    return JS_TRUE;

}



JSClass js_CallClass = {

    js_Call_str,

    JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,

    JS_PropertyStub,  JS_PropertyStub,

    call_getProperty, call_setProperty,

    call_enumerate,   (JSResolveOp)call_resolve,

    call_convert,     JS_FinalizeStub,

    JSCLASS_NO_OPTIONAL_MEMBERS

};



#endif /* JS_HAS_CALL_OBJECT */



/* SHARED because fun_getProperty always computes a new value. */

#define FUNCTION_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED)



static JSPropertySpec function_props[] = {

    {js_arguments_str, CALL_ARGUMENTS, FUNCTION_PROP_ATTRS,0,0},

    {js_arity_str,     FUN_ARITY,      FUNCTION_PROP_ATTRS,0,0},

    {js_length_str,    ARGS_LENGTH,    FUNCTION_PROP_ATTRS,0,0},

    {js_name_str,      FUN_NAME,       FUNCTION_PROP_ATTRS,0,0},

    {"__call__",       FUN_CALL,       FUNCTION_PROP_ATTRS,0,0},

    {js_caller_str,    FUN_CALLER,     FUNCTION_PROP_ATTRS,0,0},

    {0,0,0,0,0}

};



static JSBool

fun_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    /* Make delete f.length fail even though length is in f.__proto__. */

    if (!JSVAL_IS_INT(id)) {

        if (id == ATOM_KEY(cx->runtime->atomState.arityAtom) ||

            id == ATOM_KEY(cx->runtime->atomState.callerAtom) ||

            id == ATOM_KEY(cx->runtime->atomState.lengthAtom) ||

            id == ATOM_KEY(cx->runtime->atomState.nameAtom)) {

            *vp = JSVAL_FALSE;

        }

    }

    return JS_TRUE;

}



static JSBool

fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    jsint slot;

    JSFunction *fun;

    JSStackFrame *fp;

#if defined XP_PC && defined _MSC_VER &&_MSC_VER <= 800

    /* MSVC1.5 coredumps */

    jsval bogus = *vp;

#endif



    if (!JSVAL_IS_INT(id))

        return JS_TRUE;

    slot = JSVAL_TO_INT(id);



    /* No valid function object should lack private data, but check anyway. */

    fun = (JSFunction *) JS_GetPrivate(cx, obj);

    if (!fun)

        return JS_TRUE;



    /* Find fun's top-most activation record. */

    for (fp = cx->fp; fp && (fp->fun != fun || fp->special); fp = fp->down)

        continue;



    switch (slot) {

      case CALL_ARGUMENTS:

#if JS_HAS_ARGS_OBJECT

        /* Warn if strict about f.arguments or equivalent unqualified uses. */

        if (JS_HAS_STRICT_OPTION(cx) &&

            !JS_ReportErrorFlagsAndNumber(cx,

                                          JSREPORT_WARNING | JSREPORT_STRICT,

                                          js_GetErrorMessage, NULL,

                                          JSMSG_DEPRECATED_USAGE,

                                          js_arguments_str)) {

            return JS_FALSE;

        }

        if (fp) {

            if (!js_GetArgsValue(cx, fp, vp))

                return JS_FALSE;

        } else {

            *vp = JSVAL_NULL;

        }

        break;

#else  /* !JS_HAS_ARGS_OBJECT */

        *vp = OBJECT_TO_JSVAL(fp ? obj : NULL);

        break;

#endif /* !JS_HAS_ARGS_OBJECT */



      case ARGS_LENGTH:

        if (!JSVERSION_IS_ECMA(cx->version))

            *vp = INT_TO_JSVAL((jsint)(fp && fp->fun ? fp->argc : fun->nargs));

        else

      case FUN_ARITY:

            *vp = INT_TO_JSVAL((jsint)fun->nargs);

        break;



      case FUN_NAME:

        *vp = fun->atom

              ? ATOM_KEY(fun->atom)

              : STRING_TO_JSVAL(cx->runtime->emptyString);

        break;



      case FUN_CALL:

        if (fp && fp->fun) {

            JSObject *callobj = js_GetCallObject(cx, fp, NULL);

            if (!callobj)

                return JS_FALSE;

            *vp = OBJECT_TO_JSVAL(callobj);

        } else {

            *vp = JSVAL_NULL;

        }

        break;



      case FUN_CALLER:

        if (fp && fp->down && fp->down->fun)

            *vp = fp->down->argv[-2];

        else

            *vp = JSVAL_NULL;

        break;



      default:

        /* XXX fun[0] and fun.arguments[0] are equivalent. */

        if (fp && fp->fun && (uintN)slot < fp->argc)

#if defined XP_PC && defined _MSC_VER &&_MSC_VER <= 800

          /* MSVC1.5 coredumps */

          if (bogus == *vp)

#endif

            *vp = fp->argv[slot];

        break;

    }



    return JS_TRUE;

}



static JSBool

fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,

            JSObject **objp)

{

    JSFunction *fun;

    JSString *str;

    JSAtom *prototypeAtom;



    if (!JSVAL_IS_STRING(id))

        return JS_TRUE;



    /* No valid function object should lack private data, but check anyway. */

    fun = (JSFunction *) JS_GetPrivate(cx, obj);

    if (!fun || !fun->object)

        return JS_TRUE;



    /* No need to reflect fun.prototype in 'fun.prototype = ...'. */

    if (flags & JSRESOLVE_ASSIGNING)

        return JS_TRUE;



    str = JSVAL_TO_STRING(id);

    prototypeAtom = cx->runtime->atomState.classPrototypeAtom;

    if (str == ATOM_TO_STRING(prototypeAtom)) {

        JSObject *parentProto, *proto;

        jsval pval;



        parentProto = NULL;

        if (fun->object != obj && fun->object) {

            /*

             * Clone of a function: make its prototype property value have the

             * same class as the clone-parent's prototype.

             */

            if (!OBJ_GET_PROPERTY(cx, fun->object, (jsid)prototypeAtom, &pval))

                return JS_FALSE;

            if (JSVAL_IS_OBJECT(pval))

                parentProto = JSVAL_TO_OBJECT(pval);

        }



        /*

         * If resolving "prototype" in a clone, clone the parent's prototype.

         * Beware of the wacky case of a user function Object() -- trying to

         * build a prototype for that will recur back here ad perniciem.

         */

        if (fun->atom != cx->runtime->atomState.ObjectAtom) {

            /*

             * Pass the constructor's (obj's) parent as the prototype parent,

             * to avoid defaulting to parentProto.constructor.__parent__.

             */

            proto = js_NewObject(cx, &js_ObjectClass, parentProto,

                                 OBJ_GET_PARENT(cx, obj));

            if (!proto)

                return JS_FALSE;

        }



        /*

         * ECMA says that constructor.prototype is DontEnum | DontDelete for

         * user-defined functions, but DontEnum | ReadOnly | DontDelete for

         * native "system" constructors such as Object or Function.  So lazily

         * set the former here in fun_resolve, but eagerly define the latter

         * in JS_InitClass, with the right attributes.

         */

        if (!js_SetClassPrototype(cx, obj, proto, JSPROP_PERMANENT)) {

            cx->newborn[GCX_OBJECT] = NULL;

            return JS_FALSE;

        }

        *objp = obj;

    }



    return JS_TRUE;

}



static JSBool

fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)

{

    switch (type) {

      case JSTYPE_FUNCTION:

        *vp = OBJECT_TO_JSVAL(obj);

        return JS_TRUE;

      default:

        return js_TryValueOf(cx, obj, type, vp);

    }

}



static void

fun_finalize(JSContext *cx, JSObject *obj)

{

    JSFunction *fun;



    /* No valid function object should lack private data, but check anyway. */

    fun = (JSFunction *) JS_GetPrivate(cx, obj);

    if (!fun)

        return;

    if (fun->object == obj)

        fun->object = NULL;

    JS_ATOMIC_DECREMENT(&fun->nrefs);

    if (fun->nrefs)

        return;

    if (fun->script)

        js_DestroyScript(cx, fun->script);

    JS_free(cx, fun);

}



#if JS_HAS_XDR



#include "jsxdrapi.h"



enum {

    JSXDR_FUNARG = 1,

    JSXDR_FUNVAR = 2,

    JSXDR_FUNCONST = 3

};



/* XXX store parent and proto, if defined */

static JSBool

fun_xdrObject(JSXDRState *xdr, JSObject **objp)

{

    JSContext *cx;

    JSFunction *fun;

    JSString *atomstr;

    char *propname;

    JSScopeProperty *sprop;

    JSBool magic;

    jsid propid;

    JSAtom *atom;

    uintN i;

    uint32 type;

#ifdef DEBUG

    uintN nvars = 0, nargs = 0;

#endif



    cx = xdr->cx;

    if (xdr->mode == JSXDR_ENCODE) {

        /*

         * No valid function object should lack private data, but fail soft

         * (return true, no error report) in case one does due to API pilot

         * or internal error.

         */

        fun = (JSFunction *) JS_GetPrivate(cx, *objp);

        if (!fun)

            return JS_TRUE;

        atomstr = fun->atom ? ATOM_TO_STRING(fun->atom) : NULL;

    } else {

        fun = js_NewFunction(cx, NULL, NULL, 0, 0, NULL, NULL);

        if (!fun)

            return JS_FALSE;

    }



    if (!JS_XDRStringOrNull(xdr, &atomstr) ||

        !JS_XDRUint16(xdr, &fun->nargs) ||

        !JS_XDRUint16(xdr, &fun->extra) ||

        !JS_XDRUint16(xdr, &fun->nvars) ||

        !JS_XDRUint8(xdr, &fun->flags)) {

        return JS_FALSE;

    }



    /* do arguments and local vars */

    if (fun->object) {

        if (xdr->mode == JSXDR_ENCODE) {

            JSScope *scope = OBJ_SCOPE(fun->object);



            for (sprop = scope->props; sprop; sprop = sprop->next) {

                JSPropertyOp getter = SPROP_GETTER_SCOPE(sprop, scope);



                if (getter == js_GetArgument) {

                    type = JSXDR_FUNARG;

                    JS_ASSERT(nargs++ <= fun->nargs);

                } else if (getter == js_GetLocalVariable) {

                    type = (sprop->attrs & JSPROP_READONLY)

                           ? JSXDR_FUNCONST

                           : JSXDR_FUNVAR;

                    JS_ASSERT(nvars++ <= fun->nvars);

                } else {

                    continue;

                }

                propname = ATOM_BYTES(sym_atom(sprop->symbols));

                propid = sprop->id;

                if (!JS_XDRUint32(xdr, &type) ||

                    !JS_XDRUint32(xdr, (uint32 *)&propid) ||

                    !JS_XDRCString(xdr, &propname)) {

                    return JS_FALSE;

                }

            }

        } else {

            JSPropertyOp getter, setter;



            i = fun->nvars + fun->nargs;

            while (i--) {

                uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;



                if (!JS_XDRUint32(xdr, &type) ||

                    !JS_XDRUint32(xdr, (uint32 *)&propid) ||

                    !JS_XDRCString(xdr, &propname)) {

                    return JS_FALSE;

                }

                JS_ASSERT(type == JSXDR_FUNARG || type == JSXDR_FUNVAR ||

                          type == JSXDR_FUNCONST);

                if (type == JSXDR_FUNARG) {

                    getter = js_GetArgument;

                    setter = js_SetArgument;

                    JS_ASSERT(nargs++ <= fun->nargs);

                } else if (type == JSXDR_FUNVAR || type == JSXDR_FUNCONST) {

                    getter = js_GetLocalVariable;

                    setter = js_SetLocalVariable;

                    if (type == JSXDR_FUNCONST)

                        attrs |= JSPROP_READONLY;

                    JS_ASSERT(nvars++ <= fun->nvars);

                } else {

                    getter = NULL;

                    setter = NULL;

                }

                atom = js_Atomize(cx, propname, strlen(propname), 0);

                JS_free(cx, propname);

                if (!atom ||

                    !OBJ_DEFINE_PROPERTY(cx, fun->object, (jsid)atom,

                                         JSVAL_VOID, getter, setter, attrs,

                                         (JSProperty **)&sprop)) {

                    return JS_FALSE;

                }

                sprop->id = propid;

                OBJ_DROP_PROPERTY(cx, fun->object, (JSProperty *)sprop);

            }

        }

    }

    if (!js_XDRScript(xdr, &fun->script, &magic) ||

        !magic) {

        return JS_FALSE;

    }



    if (xdr->mode == JSXDR_DECODE) {

        *objp = fun->object;

        if (atomstr) {

            fun->atom = js_AtomizeString(cx, atomstr, 0);

            if (!fun->atom)

                return JS_FALSE;



            if (!OBJ_DEFINE_PROPERTY(cx, cx->globalObject,

                                     (jsid)fun->atom, OBJECT_TO_JSVAL(*objp),

                                     NULL, NULL, JSPROP_ENUMERATE,

                                     NULL)) {

                return JS_FALSE;

            }

        }

    }



    return JS_TRUE;

}



#else  /* !JS_HAS_XDR */



#define fun_xdrObject NULL



#endif /* !JS_HAS_XDR */



#if JS_HAS_INSTANCEOF



/*

 * [[HasInstance]] internal method for Function objects: fetch the .prototype

 * property of its 'this' parameter, and walks the prototype chain of v (only

 * if v is an object) returning true if .prototype is found.

 */

static JSBool

fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)

{

    jsval pval, cval;

    JSString *str;

    JSObject *proto, *obj2;

    JSFunction *cfun, *ofun;



    if (!OBJ_GET_PROPERTY(cx, obj,

                          (jsid)cx->runtime->atomState.classPrototypeAtom,

                          &pval)) {

        return JS_FALSE;

    }



    if (JSVAL_IS_PRIMITIVE(pval)) {

        /*

         * Throw a runtime error if instanceof is called on a function that

         * has a non-object as its .prototype value.

         */

        str = js_DecompileValueGenerator(cx, -1, OBJECT_TO_JSVAL(obj), NULL);

        if (str) {

            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                 JSMSG_BAD_PROTOTYPE, JS_GetStringBytes(str));

        }

        return JS_FALSE;

    }



    proto = JSVAL_TO_OBJECT(pval);

    if (!js_IsDelegate(cx, proto, v, bp))

        return JS_FALSE;



    if (!*bp && !JSVAL_IS_PRIMITIVE(v)) {

        /*

         * Extension for "brutal sharing" of standard class constructors: if

         * a script is compiled using a single, shared set of constructors, in

         * particular Function and RegExp, but executed many times using other

         * sets of standard constructors, then (/re/ instanceof RegExp), e.g.,

         * will be false.

         *

         * We extend instanceof in this case to look for a matching native or

         * script underlying the function object found in the 'constructor'

         * property of the object in question (which is JSVAL_TO_OBJECT(v)),

         * or found in the 'constructor' property of one of its prototypes.

         *

         * See also jsexn.c, where the *Error constructors are defined, each

         * with its own native function, to satisfy (e instanceof Error) even

         * when exceptions cross standard-class sharing boundaries.  Note that

         * Error.prototype may not lie on e's __proto__ chain in that case.

         */

        obj2 = JSVAL_TO_OBJECT(v);

        do {

            if (!OBJ_GET_PROPERTY(cx, obj2,

                                  (jsid)cx->runtime->atomState.constructorAtom,

                                  &cval)) {

                return JS_FALSE;

            }



            if (JSVAL_IS_FUNCTION(cx, cval)) {

                cfun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(cval));

                ofun = (JSFunction *) JS_GetPrivate(cx, obj);

                if (cfun->native == ofun->native &&

                    cfun->script == ofun->script) {

                    *bp = JS_TRUE;

                    break;

                }

            }

        } while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL);

    }



    return JS_TRUE;

}



#else  /* !JS_HAS_INSTANCEOF */



#define fun_hasInstance NULL



#endif /* !JS_HAS_INSTANCEOF */



static uint32

fun_mark(JSContext *cx, JSObject *obj, void *arg)

{

    JSFunction *fun;



    fun = (JSFunction *) JS_GetPrivate(cx, obj);

    if (fun) {

        if (fun->atom)

            GC_MARK_ATOM(cx, fun->atom, arg);

        if (fun->script)

            js_MarkScript(cx, fun->script, arg);

    }

    return 0;

}



JSClass js_FunctionClass = {

    js_Function_str,

    JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,

    JS_PropertyStub,  fun_delProperty,

    fun_getProperty,  JS_PropertyStub,

    JS_EnumerateStub, (JSResolveOp)fun_resolve,

    fun_convert,      fun_finalize,

    NULL,             NULL,

    NULL,             NULL,

    fun_xdrObject,    fun_hasInstance,

    fun_mark,         0

};



static JSBool

fun_toString_sub(JSContext *cx, JSObject *obj, uint32 indent,

                 uintN argc, jsval *argv, jsval *rval)

{

    jsval fval;

    JSFunction *fun;

    JSString *str;



    fval = argv[-1];

    if (!JSVAL_IS_FUNCTION(cx, fval)) {

        /*

         * If we don't have a function to start off with, try converting the

         * object to a function.  If that doesn't work, complain.

         */

        if (JSVAL_IS_OBJECT(fval)) {

            obj = JSVAL_TO_OBJECT(fval);

            if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION,

                                                 &fval)) {

                return JS_FALSE;

            }

        }

        if (!JSVAL_IS_FUNCTION(cx, fval)) {

            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                 JSMSG_INCOMPATIBLE_PROTO,

                                 js_Function_str, js_toString_str,

                                 JS_GetTypeName(cx, JS_TypeOfValue(cx, fval)));

            return JS_FALSE;

        }

    }



    obj = JSVAL_TO_OBJECT(fval);

    fun = (JSFunction *) JS_GetPrivate(cx, obj);

    if (!fun)

        return JS_TRUE;

    if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))

        return JS_FALSE;

    str = JS_DecompileFunction(cx, fun, (uintN)indent);

    if (!str)

        return JS_FALSE;

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return fun_toString_sub(cx, obj, 0, argc, argv, rval);

}



#if JS_HAS_TOSOURCE

static JSBool

fun_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return fun_toString_sub(cx, obj, JS_DONT_PRETTY_PRINT, argc, argv, rval);

}

#endif



static const char js_call_str[] = "call";



#if JS_HAS_CALL_FUNCTION

static JSBool

fun_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsval fval, *sp, *oldsp;

    void *mark;

    uintN i;

    JSStackFrame *fp;

    JSBool ok;



    if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1]))

        return JS_FALSE;

    fval = argv[-1];



    if (!JSVAL_IS_FUNCTION(cx, fval)) {

        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                             JSMSG_INCOMPATIBLE_PROTO,

                             js_Function_str, js_call_str,

                             JS_GetStringBytes(JS_ValueToString(cx, fval)));

        return JS_FALSE;

    }



    if (argc == 0) {

        /* Call fun with its parent as the 'this' parameter if no args. */

        obj = OBJ_GET_PARENT(cx, obj);

    } else {

        /* Otherwise convert the first arg to 'this' and skip over it. */

        if (!js_ValueToObject(cx, argv[0], &obj))

            return JS_FALSE;

        argc--;

        argv++;

    }



    /* Allocate stack space for fval, obj, and the args. */

    sp = js_AllocStack(cx, 2 + argc, &mark);

    if (!sp)

        return JS_FALSE;



    /* Push fval, obj, and the args. */

    *sp++ = fval;

    *sp++ = OBJECT_TO_JSVAL(obj);

    for (i = 0; i < argc; i++)

        *sp++ = argv[i];



    /* Lift current frame to include the args and do the call. */

    fp = cx->fp;

    oldsp = fp->sp;

    fp->sp = sp;

    ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL);



    /* Store rval and pop stack back to our frame's sp. */

    *rval = fp->sp[-1];

    fp->sp = oldsp;

    js_FreeStack(cx, mark);

    return ok;

}

#endif /* JS_HAS_CALL_FUNCTION */



#if JS_HAS_APPLY_FUNCTION

static JSBool

fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsval fval, *sp, *oldsp;

    JSObject *aobj;

    jsuint length = 0;

    void *mark;

    uintN i;

    JSBool ok;

    JSStackFrame *fp;



    if (argc == 0) {

        /* Will get globalObject as 'this' and no other agurments. */

        return fun_call(cx, obj, argc, argv, rval);

    }



    if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1]))

        return JS_FALSE;

    fval = argv[-1];



    if (!JSVAL_IS_FUNCTION(cx, fval)) {

        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                             JSMSG_INCOMPATIBLE_PROTO,

                             js_Function_str, "apply",

                             JS_GetStringBytes(JS_ValueToString(cx, fval)));

        return JS_FALSE;

    }



    if (argc >= 2) {

        /* If the 2nd arg is null or void, call the function with 0 args. */

        if (JSVAL_IS_NULL(argv[1]) || JSVAL_IS_VOID(argv[1])) {

            argc = 0;

        } else {

            /* The second arg must be an array (or arguments object). */

            if (JSVAL_IS_PRIMITIVE(argv[1]) ||

                (aobj = JSVAL_TO_OBJECT(argv[1]),

                OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass &&

                OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass))

            {

                JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                     JSMSG_BAD_APPLY_ARGS);

                return JS_FALSE;

            }

            if (!js_GetLengthProperty(cx, aobj, &length))

                return JS_FALSE;

        }

    }



    /* Convert the first arg to 'this' and skip over it. */

    if (!js_ValueToObject(cx, argv[0], &obj))

        return JS_FALSE;



    /* Allocate stack space for fval, obj, and the args. */

    argc = (uintN)length;

    sp = js_AllocStack(cx, 2 + argc, &mark);

    if (!sp)

        return JS_FALSE;



    /* Push fval, obj, and aobj's elements as args. */

    *sp++ = fval;

    *sp++ = OBJECT_TO_JSVAL(obj);

    for (i = 0; i < argc; i++) {

        ok = JS_GetElement(cx, aobj, (jsint)i, sp);

        if (!ok)

            goto out;

        sp++;

    }



    /* Lift current frame to include the args and do the call. */

    fp = cx->fp;

    oldsp = fp->sp;

    fp->sp = sp;

    ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL);



    /* Store rval and pop stack back to our frame's sp. */

    *rval = fp->sp[-1];

    fp->sp = oldsp;

out:

    js_FreeStack(cx, mark);

    return ok;

}

#endif /* JS_HAS_APPLY_FUNCTION */



static JSFunctionSpec function_methods[] = {

#if JS_HAS_TOSOURCE

    {js_toSource_str,   fun_toSource,   0,0,0},

#endif

    {js_toString_str,   fun_toString,   1,0,0},

#if JS_HAS_APPLY_FUNCTION

    {"apply",           fun_apply,      1,0,0},

#endif

#if JS_HAS_CALL_FUNCTION

    {js_call_str,       fun_call,       1,0,0},

#endif

    {0,0,0,0,0}

};



JSBool

js_IsIdentifier(JSString *str)

{

    size_t n;

    jschar *s, c;



    n = str->length;

    s = str->chars;

    c = *s;

    /*

     * We don't handle unicode escape sequences here

     * because they won't be in the input string.

     * (Right?)

     */

    if (n == 0 || !JS_ISIDENT_START(c))

        return JS_FALSE;

    for (n--; n != 0; n--) {

        c = *++s;

        if (!JS_ISIDENT(c))

            return JS_FALSE;

    }

    return JS_TRUE;

}



static JSBool

Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFunction *fun;

    JSObject *parent;

    uintN i, n, lineno;

    JSAtom *atom;

    const char *filename;

    JSObject *obj2;

    JSScopeProperty *sprop;

    JSString *str, *arg;

    JSStackFrame *fp;

    void *mark;

    JSTokenStream *ts;

    JSPrincipals *principals;

    jschar *collected_args, *cp;

    size_t args_length;

    JSTokenType tt;

    jsid oldArgId;

    JSBool ok;



    if (cx->fp && !cx->fp->constructing) {

        obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL);

        if (!obj)

            return JS_FALSE;

        *rval = OBJECT_TO_JSVAL(obj);

    }

    fun = (JSFunction *) JS_GetPrivate(cx, obj);

    if (fun)

        return JS_TRUE;



#if JS_HAS_CALL_OBJECT

    /*

     * NB: (new Function) is not lexically closed by its caller, it's just an

     * anonymous function in the top-level scope that its constructor inhabits.

     * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,

     * and so would a call to f from another top-level's script or function.

     *

     * In older versions, before call objects, a new Function was adopted by

     * its running context's globalObject, which might be different from the

     * top-level reachable from scopeChain (in HTML frames, e.g.).

     */

    parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]));

#else

    /* Set up for dynamic parenting (see js_Invoke in jsinterp.c). */

    parent = NULL;

#endif



    fun = js_NewFunction(cx, obj, NULL, 0, 0, parent,

                         (JSVERSION_IS_ECMA(cx->version))

                         ? cx->runtime->atomState.anonymousAtom

                         : NULL);



    if (!fun)

        return JS_FALSE;



    if ((fp = cx->fp) != NULL && (fp = fp->down) != NULL && fp->script) {

        filename = fp->script->filename;

        lineno = js_PCToLineNumber(fp->script, fp->pc);

        principals = fp->script->principals;

    } else {

        filename = NULL;

        lineno = 0;

        principals = NULL;

    }



    n = argc ? argc - 1 : 0;

    if (n > 0) {

        /*

         * Collect the function-argument arguments into one string, separated

         * by commas, then make a tokenstream from that string, and scan it to

         * get the arguments.  We need to throw the full scanner at the

         * problem, because the argument string can legitimately contain

         * comments and linefeeds.  XXX It might be better to concatenate

         * everything up into a function definition and pass it to the

         * compiler, but doing it this way is less of a delta from the old

         * code.  See ECMA 15.3.2.1.

         */

        args_length = 0;

        for (i = 0; i < n; i++) {

            /* Collect the lengths for all the function-argument arguments. */

            arg = JSVAL_TO_STRING(argv[i]);

            args_length += arg->length;

        }

        /* Add 1 for each joining comma. */

        args_length += n - 1;



        /*

         * Allocate a string to hold the concatenated arguments, including room

         * for a terminating 0.  Mark cx->tempPool for later release, to free

         * collected_args and its tokenstream in one swoop.

         */

        mark = JS_ARENA_MARK(&cx->tempPool);

        JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool,

                               (args_length+1) * sizeof(jschar));

        if (!cp)

            return JS_FALSE;

        collected_args = cp;



        /*

         * Concatenate the arguments into the new string, separated by commas.

         */

        for (i = 0; i < n; i++) {

            arg = JSVAL_TO_STRING(argv[i]);

            (void) js_strncpy(cp, arg->chars, arg->length);

            cp += arg->length;



            /* Add separating comma or terminating 0. */

            *cp++ = (i + 1 < n) ? ',' : 0;

        }



        /*

         * Make a tokenstream (allocated from cx->tempPool) that reads from

         * the given string.

         */

        ts = js_NewTokenStream(cx, collected_args, args_length, filename,

                               lineno, principals);

        if (!ts) {

            JS_ARENA_RELEASE(&cx->tempPool, mark);

            return JS_FALSE;

        }



        /* The argument string may be empty or contain no tokens. */

        tt = js_GetToken(cx, ts);

        if (tt != TOK_EOF) {

            while (1) {

                /*

                 * Check that it's a name.  This also implicitly guards against

                 * TOK_ERROR, which was already reported.

                 */

                if (tt != TOK_NAME)

                    goto bad_formal;



                /*

                 * Get the atom corresponding to the name from the tokenstream;

                 * we're assured at this point that it's a valid identifier.

                 */

                atom = CURRENT_TOKEN(ts).t_atom;

                if (!js_LookupProperty(cx, obj, (jsid)atom, &obj2,

                                       (JSProperty **)&sprop)) {

                    goto bad_formal;

                }

                if (sprop && obj2 == obj) {

                    if (JS_HAS_STRICT_OPTION(cx)) {

                        JS_ASSERT(SPROP_GETTER(sprop, obj) == js_GetArgument);

                        OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);

                        if (!js_ReportCompileErrorNumber(cx, ts, NULL,

                                                         JSREPORT_WARNING |

                                                         JSREPORT_STRICT,

                                                         JSMSG_DUPLICATE_FORMAL,

                                                         ATOM_BYTES(atom))) {

                            goto bad_formal;

                        }

                    }



                    /*

                     * A duplicate parameter name. We create a dummy symbol

                     * entry with property id of the parameter number and set

                     * the id to the name of the parameter.  See jsopcode.c:

                     * the decompiler knows to treat this case specially.

                     */

                    oldArgId = (jsid) sprop->id;

                    OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);

                    sprop = NULL;

                    if (!js_DefineProperty(cx, obj, oldArgId, JSVAL_VOID,

                                           js_GetArgument, js_SetArgument,

                                           JSPROP_ENUMERATE | JSPROP_PERMANENT,

                                           (JSProperty **)&sprop)) {

                        goto bad_formal;

                    }

                    sprop->id = (jsid) atom;

                }

                if (sprop)

                    OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);

                if (!js_DefineProperty(cx, obj, (jsid)atom, JSVAL_VOID,

                                       js_GetArgument, js_SetArgument,

                                       JSPROP_ENUMERATE | JSPROP_PERMANENT,

                                       (JSProperty **)&sprop)) {

                    goto bad_formal;

                }

                JS_ASSERT(sprop);

                sprop->id = INT_TO_JSVAL(fun->nargs++);

                OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop);



                /*

                 * Get the next token.  Stop on end of stream.  Otherwise

                 * insist on a comma, get another name, and iterate.

                 */

                tt = js_GetToken(cx, ts);

                if (tt == TOK_EOF)

                    break;

                if (tt != TOK_COMMA)

                    goto bad_formal;

                tt = js_GetToken(cx, ts);

            }

        }



        /* Clean up. */

        ok = js_CloseTokenStream(cx, ts);

        JS_ARENA_RELEASE(&cx->tempPool, mark);

        if (!ok)

            return JS_FALSE;

    }



    if (argc) {

        str = js_ValueToString(cx, argv[argc-1]);

    } else {

        /* Can't use cx->runtime->emptyString because we're called too early. */

        str = js_NewStringCopyZ(cx, js_empty_ucstr, 0);

    }

    if (!str)

        return JS_FALSE;

    if (argv) {

        /* Use the last arg (or this if argc == 0) as a local GC root. */

        argv[(intn)(argc-1)] = STRING_TO_JSVAL(str);

    }



    if ((fp = cx->fp) != NULL && (fp = fp->down) != NULL && fp->script) {

        filename = fp->script->filename;

        lineno = js_PCToLineNumber(fp->script, fp->pc);

        principals = fp->script->principals;

    } else {

        filename = NULL;

        lineno = 0;

        principals = NULL;

    }



    mark = JS_ARENA_MARK(&cx->tempPool);

    ts = js_NewTokenStream(cx, str->chars, str->length, filename, lineno,

                           principals);

    if (!ts) {

        ok = JS_FALSE;

    } else {

        ok = js_CompileFunctionBody(cx, ts, fun) &&

             js_CloseTokenStream(cx, ts);

    }

    JS_ARENA_RELEASE(&cx->tempPool, mark);

    return ok;



bad_formal:

    /*

     * Report "malformed formal parameter" iff no illegal char or similar

     * scanner error was already reported.

     */

    if (!(ts->flags & TSF_ERROR))

        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_FORMAL);



    /*

     * Clean up the arguments string and tokenstream if we failed to parse

     * the arguments.

     */

    (void)js_CloseTokenStream(cx, ts);

    JS_ARENA_RELEASE(&cx->tempPool, mark);

    return JS_FALSE;

}



JSObject *

js_InitFunctionClass(JSContext *cx, JSObject *obj)

{

    JSObject *proto;

    JSAtom *atom;

    JSFunction *fun;



    proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,

                         function_props, function_methods, NULL, NULL);

    if (!proto)

        return NULL;

    atom = js_Atomize(cx, js_FunctionClass.name, strlen(js_FunctionClass.name),

                      0);

    if (!atom)

        goto bad;

    fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, atom);

    if (!fun)

        goto bad;

    fun->script = js_NewScript(cx, 0);

    if (!fun->script)

        goto bad;

    return proto;



bad:

    cx->newborn[GCX_OBJECT] = NULL;

    return NULL;

}



#if JS_HAS_ARGS_OBJECT

JSObject *

js_InitArgumentsClass(JSContext *cx, JSObject *obj)

{

    return JS_InitClass(cx, obj, NULL, &js_ArgumentsClass, Arguments, 0,

                        args_props, NULL, NULL, NULL);

}

#endif



#if JS_HAS_CALL_OBJECT

JSObject *

js_InitCallClass(JSContext *cx, JSObject *obj)

{

    return JS_InitClass(cx, obj, NULL, &js_CallClass, Call, 0,

                        call_props, NULL, NULL, NULL);

}

#endif



JSFunction *

js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,

               uintN flags, JSObject *parent, JSAtom *atom)

{

    JSFunction *fun;



    /* Allocate a function struct. */

    fun = (JSFunction *) JS_malloc(cx, sizeof *fun);

    if (!fun)

        return NULL;



    /* If funobj is null, allocate an object for it. */

    if (funobj) {

        OBJ_SET_PARENT(cx, funobj, parent);

    } else {

        funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent);

        if (!funobj) {

            JS_free(cx, fun);

            return NULL;

        }

    }



    /* Initialize all function members. */

    fun->nrefs = 0;

    fun->object = NULL;

    fun->native = native;

    fun->script = NULL;

    fun->nargs = nargs;

    fun->extra = 0;

    fun->nvars = 0;

    fun->flags = flags & JSFUN_FLAGS_MASK;

    fun->spare = 0;

    fun->atom = atom;

    fun->clasp = NULL;



	// DREAMWEAVER: see jsprofiler.h

#ifdef DREAMWEAVER_JAVASCRIPT_PROFILING

	fun->dummyFunction = -1;

#endif





    /* Link fun to funobj and vice versa. */

    if (!js_LinkFunctionObject(cx, fun, funobj)) {

        cx->newborn[GCX_OBJECT] = NULL;

        JS_free(cx, fun);

        return NULL;

    }

    return fun;

}



JSObject *

js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)

{

    JSObject *newfunobj;

    JSFunction *fun;



    JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass);

    newfunobj = js_NewObject(cx, &js_FunctionClass, funobj, parent);

    if (!newfunobj)

        return NULL;

    fun = (JSFunction *) JS_GetPrivate(cx, funobj);

    if (!js_LinkFunctionObject(cx, fun, newfunobj)) {

        cx->newborn[GCX_OBJECT] = NULL;

        return NULL;

    }

    return newfunobj;

}



JSBool

js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *funobj)

{

    if (!fun->object)

        fun->object = funobj;

    if (!JS_SetPrivate(cx, funobj, fun))

        return JS_FALSE;

    JS_ATOMIC_INCREMENT(&fun->nrefs);

    return JS_TRUE;

}



JSFunction *

js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,

                  uintN nargs, uintN attrs)

{

    JSFunction *fun;



    fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom);

    if (!fun)

        return NULL;

    if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, OBJECT_TO_JSVAL(fun->object),

                             NULL, NULL, attrs, NULL)) {

        return NULL;

    }

    return fun;

}



JSFunction *

js_ValueToFunction(JSContext *cx, jsval *vp, JSBool constructing)

{

    jsval v;

    JSObject *obj;



    v = *vp;

    obj = NULL;

    if (JSVAL_IS_OBJECT(v)) {

        obj = JSVAL_TO_OBJECT(v);

        if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) {

            if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v))

                return NULL;

            obj = JSVAL_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL;

        }

    }

    if (!obj) {

        js_ReportIsNotFunction(cx, vp, constructing);

        return NULL;

    }

    return (JSFunction *) JS_GetPrivate(cx, obj);

}



void

js_ReportIsNotFunction(JSContext *cx, jsval *vp, JSBool constructing)

{

    JSType type;

    JSString *fallback;

    JSStackFrame *fp;

    JSString *str;



    /*

     * We provide the typename as the fallback to handle the case when

     * valueOf is not a function, which prevents ValueToString from being

     * called as the default case inside js_DecompileValueGenerator (and

     * so recursing back to here).

     */

    type = JS_TypeOfValue(cx, *vp);

    fallback = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]);

    fp = cx->fp;

    str = js_DecompileValueGenerator(cx, fp ? vp - fp->sp : JSDVG_IGNORE_STACK,

                                     *vp, fallback);

    if (str) {

        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                             (uintN)(constructing ? JSMSG_NOT_CONSTRUCTOR

                                                  : JSMSG_NOT_FUNCTION),

                             JS_GetStringBytes(str));

    }

}

 

**** End of jsfun.c ****

 

**** Start of jsfun.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsfun_h___

#define jsfun_h___

/*

 * JS function definitions.

 */

#include "jsprvtd.h"

#include "jspubtd.h"



JS_BEGIN_EXTERN_C



struct JSFunction {

    jsrefcount	 nrefs;		/* number of referencing objects */

    JSObject     *object;       /* back-pointer to GC'ed object header */

    JSNative     native;        /* native method pointer or null */

    JSScript     *script;       /* interpreted bytecode descriptor or null */

    uint16       nargs;         /* minimum number of actual arguments */

    uint16       extra;         /* number of arg slots for local GC roots */

    uint16       nvars;         /* number of local variables */

    uint8        flags;         /* bound method and other flags, see jsapi.h */

    uint8        spare;         /* reserved for future use */

    JSAtom       *atom;         /* name for diagnostics and decompiling */

    JSClass      *clasp;        /* if non-null, constructor for this class */

// DREAMWEAVER: see jsprofiler.h

#ifdef DREAMWEAVER_JAVASCRIPT_PROFILING

	int			 dummyFunction;	/* see jsprofiler.h */

#endif

};



extern JSClass js_ArgumentsClass;

extern JSClass js_CallClass;



/* JS_FRIEND_DATA so that JSVAL_IS_FUNCTION is callable from outside */

extern JS_FRIEND_DATA(JSClass) js_FunctionClass;



/*

 * NB: jsapi.h and jsobj.h must be included before any call to this macro.

 */

#define JSVAL_IS_FUNCTION(cx, v)                                              \

    (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) &&                              \

     OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass)



extern JSBool

js_IsIdentifier(JSString *str);



extern JSObject *

js_InitFunctionClass(JSContext *cx, JSObject *obj);



extern JSObject *

js_InitArgumentsClass(JSContext *cx, JSObject *obj);



extern JSObject *

js_InitCallClass(JSContext *cx, JSObject *obj);



extern JSFunction *

js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,

	       uintN flags, JSObject *parent, JSAtom *atom);



extern JSObject *

js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent);



extern JSBool

js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object);



extern JSFunction *

js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,

		  uintN nargs, uintN flags);



extern JSFunction *

js_ValueToFunction(JSContext *cx, jsval *vp, JSBool constructing);



extern void

js_ReportIsNotFunction(JSContext *cx, jsval *vp, JSBool constructing);



extern JSObject *

js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent);



extern JSBool

js_PutCallObject(JSContext *cx, JSStackFrame *fp);



extern JSBool

js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp);



extern JSBool

js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp);



extern JSBool

js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp);



extern JSBool

js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id,

                   JSObject **objp, jsval *vp);



extern JSObject *

js_GetArgsObject(JSContext *cx, JSStackFrame *fp);



extern JSBool

js_PutArgsObject(JSContext *cx, JSStackFrame *fp);



extern JSBool

js_XDRFunction(JSXDRState *xdr, JSObject **objp);



JS_END_EXTERN_C



#endif /* jsfun_h___ */

 

**** End of jsfun.h ****

 

**** Start of jsgc.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS Mark-and-Sweep Garbage Collector.

 *

 * This GC allocates only fixed-sized things big enough to contain two words

 * (pointers) on any host architecture.  It allocates from an arena pool (see

 * jsarena.h).  It uses an ideally parallel array of flag bytes to hold the

 * mark bit, finalizer type index, etc.

 *

 * XXX swizzle page to freelist for better locality of reference

 */

#include "jsstddef.h"

#include <stdlib.h>     /* for free, called by JS_ARENA_DESTROY */

#include <string.h>	/* for memset, called by jsarena.h macros if DEBUG */

#include "jstypes.h"

#include "jsarena.h" /* Added by JSIFY */

#include "jsutil.h" /* Added by JSIFY */

#include "jshash.h" /* Added by JSIFY */

#include "jsapi.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsgc.h"

#include "jsinterp.h"

#include "jslock.h"

#include "jsnum.h"

#include "jsobj.h"

#include "jsscope.h"

#include "jsscript.h"

#include "jsstr.h"



/*

 * GC arena sizing depends on amortizing arena overhead using a large number

 * of things per arena, and on the thing/flags ratio of 8:1 on most platforms.

 *

 * On 64-bit platforms, we would have half as many things per arena because

 * pointers are twice as big, so we double the bytes for things per arena.

 * This preserves the 1024 byte flags sub-arena size, which relates to the

 * GC_PAGE_SIZE (see below for why).

 */

#if JS_BYTES_PER_WORD == 8

# define GC_THINGS_SHIFT 14     /* 16KB for things on Alpha, etc. */

#else

# define GC_THINGS_SHIFT 13     /* 8KB for things on most platforms */

#endif

#define GC_THINGS_SIZE  JS_BIT(GC_THINGS_SHIFT)

#define GC_FLAGS_SIZE   (GC_THINGS_SIZE / sizeof(JSGCThing))

#define GC_ARENA_SIZE   (GC_THINGS_SIZE + GC_FLAGS_SIZE)



/*

 * The private JSGCThing struct, which describes a gcFreelist element.

 */

struct JSGCThing {

    JSGCThing   *next;

    uint8       *flagp;

};



/*

 * A GC arena contains one flag byte for every thing in its heap, and supports

 * O(1) lookup of a flag given its thing's address.

 *

 * To implement this, we take advantage of the thing/flags numerology: given

 * the 8K bytes worth of GC-things, there are 1K flag bytes.  We mask a thing's

 * address with ~1023 to find a JSGCPageInfo record at the front of a mythical

 * "GC page" within the larger 8K thing arena.  That JSGCPageInfo contains a

 * pointer to the 128 flag bytes corresponding to the things in the page, so we

 * index into this flags array using the thing's index within its page.

 *

 * To align thing pages on 1024-byte boundaries, we must allocate the 9KB of

 * flags+things arena payload, then find the first 0 mod 1024 boundary after

 * the first payload address.  That's where things start, with a JSGCPageInfo

 * taking up the first thing-slot, as usual for 0 mod 1024 byte boundaries.

 * The effect of this alignment trick is to split the flags into at most 2

 * discontiguous spans, one before the things and one after (if we're really

 * lucky, and the arena payload starts on a 0 mod 1024 byte boundary, no need

 * to split).

 *

 * The overhead of this scheme for most platforms is (16+8*(8+1))/(16+9K) or

 * .95% (assuming 16 byte JSArena header size, and 8 byte JSGCThing size).

 *

 * Here's some ASCII art showing an arena:

 *

 *   split

 *     |

 *     V

 *  +--+-------+-------+-------+-------+-------+-------+-------+-------+-----+

 *  |fB|  tp0  |  tp1  |  tp2  |  tp3  |  tp4  |  tp5  |  tp6  |  tp7  | fA  |

 *  +--+-------+-------+-------+-------+-------+-------+-------+-------+-----+

 *              ^                                 ^

 *  tI ---------+                                 |

 *  tJ -------------------------------------------+

 *

 *  - fB are the "before split" flags, fA are the "after split" flags

 *  - tp0-tp7 are the 8 thing pages

 *  - thing tI points into tp1, whose flags are below the split, in fB

 *  - thing tJ points into tp5, clearly above the split

 *

 * In general, one of the thing pages will have some of its things' flags on

 * the low side of the split, and the rest of its things' flags on the high

 * side.  All the other pages have flags only below or only above.  Therefore

 * we'll have to test something to decide whether the split divides flags in

 * a given thing's page.  So we store the split pointer (the pointer to tp0)

 * in each JSGCPageInfo, along with the flags pointer for the 128 flag bytes

 * ideally starting, for tp0 things, at the beginning of the arena's payload

 * (at the start of fB).

 *

 * That is, each JSGCPageInfo's flags pointer is 128 bytes from the previous,

 * or at the start of the arena if there is no previous page in this arena.

 * Thus these ideal 128-byte flag pages run contiguously from the start of the

 * arena (right over the split!), and the JSGCPageInfo flags pointers contain

 * no discontinuities over the split created by the thing pages.  So if, for a

 * given JSGCPageInfo *pi, we find that

 *

 *  pi->flags + ((jsuword)thing % 1023) / sizeof(JSGCThing) >= pi->split

 *

 * then we must add GC_THINGS_SIZE to the nominal flags pointer to jump over

 * all the thing pages that split the flags into two discontiguous spans.

 *

 * (If we need to implement card-marking for an incremental GC write barrier,

 * we can use the low byte of the pi->split pointer as the card-mark, for an

 * extremely efficient write barrier: when mutating an object obj, just store

 * a 1 byte at (uint8 *) ((jsuword)obj & ~1023) for little-endian platforms.

 * When finding flags, we'll of course have to mask split with ~255, but it is

 * guaranteed to be 1024-byte aligned, so no information is lost by overlaying

 * the card-mark byte on split's low byte.)

 */

#define GC_PAGE_SHIFT   10

#define GC_PAGE_MASK    ((jsuword) JS_BITMASK(GC_PAGE_SHIFT))

#define GC_PAGE_SIZE    JS_BIT(GC_PAGE_SHIFT)



typedef struct JSGCPageInfo {

    uint8       *split;

    uint8       *flags;

} JSGCPageInfo;



#define FIRST_THING_PAGE(a)     (((a)->base + GC_FLAGS_SIZE) & ~GC_PAGE_MASK)



static JSGCThing *

gc_new_arena(JSArenaPool *pool)

{

    uint8 *flagp, *split, *pagep, *limit;

    JSArena *a;

    JSGCThing *thing;

    JSGCPageInfo *pi;



    /* Use JS_ArenaAllocate to grab another 9K-net-size hunk of space. */

    flagp = (uint8 *) JS_ArenaAllocate(pool, GC_ARENA_SIZE);

    if (!flagp)

        return NULL;

    a = pool->current;



    /* Reset a->avail to start at the flags split, aka the first thing page. */

    a->avail = FIRST_THING_PAGE(a);

    split = pagep = (uint8 *) a->avail;

    a->avail += sizeof(JSGCPageInfo);

    thing = (JSGCThing *) a->avail;

    a->avail += sizeof(JSGCThing);



    /* Initialize the JSGCPageInfo records at the start of every thing page. */

    limit = pagep + GC_THINGS_SIZE;

    do {

        pi = (JSGCPageInfo *) pagep;

        pi->split = split;

        pi->flags = flagp;

        flagp += GC_PAGE_SIZE >> (GC_THINGS_SHIFT -  GC_PAGE_SHIFT);

        pagep += GC_PAGE_SIZE;

    } while (pagep < limit);

    return thing;

}



static uint8 *

gc_find_flags(void *thing)

{

    JSGCPageInfo *pi;

    uint8 *flagp;



    pi = (JSGCPageInfo *) ((jsuword)thing & ~GC_PAGE_MASK);

    flagp = pi->flags + ((jsuword)thing & GC_PAGE_MASK) / sizeof(JSGCThing);

    if (flagp >= pi->split)

        flagp += GC_THINGS_SIZE;

    return flagp;

}



JSBool js_IsAboutToBeFinalized(JSContext *cx, void *thing)

{

    uint8 flags = *gc_find_flags(thing);

    return !(flags & (GCF_MARK | GCF_LOCKMASK | GCF_FINAL));

}



typedef void (*GCFinalizeOp)(JSContext *cx, JSGCThing *thing);



static GCFinalizeOp gc_finalizers[GCX_NTYPES];



intN

js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop,

                                 JSStringFinalizeOp newop)

{

    uintN i;



    for (i = GCX_EXTERNAL_STRING; i < GCX_NTYPES; i++) {

        if (gc_finalizers[i] == (GCFinalizeOp) oldop) {

            gc_finalizers[i] = (GCFinalizeOp) newop;

            return (intN) i;

        }

    }

    return -1;

}



#ifdef JS_GCMETER

#define METER(x) x

#else

#define METER(x) /* nothing */

#endif



/* Initial size of the gcRootsHash table (SWAG, small enough to amortize). */

#define GC_ROOTS_SIZE   256

#define GC_FINALIZE_LEN 1024



static JSHashNumber   gc_hash_root(const void *key);



JSBool

js_InitGC(JSRuntime *rt, uint32 maxbytes)

{

    JS_ASSERT(sizeof(JSGCThing) == sizeof(JSGCPageInfo));

    JS_ASSERT(sizeof(JSGCThing) >= sizeof(JSObject));

    JS_ASSERT(sizeof(JSGCThing) >= sizeof(JSString));

    JS_ASSERT(sizeof(JSGCThing) >= sizeof(jsdouble));

    JS_ASSERT(GC_FLAGS_SIZE >= GC_PAGE_SIZE);

    JS_ASSERT(sizeof(JSStackHeader) >= 2 * sizeof(jsval));



    if (!gc_finalizers[GCX_OBJECT]) {

	gc_finalizers[GCX_OBJECT] = (GCFinalizeOp)js_FinalizeObject;

	gc_finalizers[GCX_STRING] = (GCFinalizeOp)js_FinalizeString;

#ifdef DEBUG

	gc_finalizers[GCX_DOUBLE] = (GCFinalizeOp)js_FinalizeDouble;

#endif

    }



    JS_InitArenaPool(&rt->gcArenaPool, "gc-arena", GC_ARENA_SIZE,

		     sizeof(JSGCThing));

    rt->gcRootsHash = JS_NewHashTable(GC_ROOTS_SIZE, gc_hash_root,

				      JS_CompareValues, JS_CompareValues,

				      NULL, NULL);

    if (!rt->gcRootsHash)

	return JS_FALSE;

    rt->gcLocksHash = NULL;     /* create lazily */

    rt->gcMaxBytes = maxbytes;

    return JS_TRUE;

}



#ifdef JS_GCMETER

void

js_DumpGCStats(JSRuntime *rt, FILE *fp)

{

    fprintf(fp, "\nGC allocation statistics:\n");

    fprintf(fp, "     bytes currently allocated: %lu\n", rt->gcBytes);

    fprintf(fp, "                alloc attempts: %lu\n", rt->gcStats.alloc);

    fprintf(fp, "            GC freelist length: %lu\n", rt->gcStats.freelen);

    fprintf(fp, "  recycles through GC freelist: %lu\n", rt->gcStats.recycle);

    fprintf(fp, "alloc retries after running GC: %lu\n", rt->gcStats.retry);

    fprintf(fp, "           allocation failures: %lu\n", rt->gcStats.fail);

    fprintf(fp, "              valid lock calls: %lu\n", rt->gcStats.lock);

    fprintf(fp, "            valid unlock calls: %lu\n", rt->gcStats.unlock);

    fprintf(fp, "   locks that hit stuck counts: %lu\n", rt->gcStats.stuck);

    fprintf(fp, " unlocks that saw stuck counts: %lu\n", rt->gcStats.unstuck);

    fprintf(fp, "          mark recursion depth: %lu\n", rt->gcStats.depth);

    fprintf(fp, "  maximum mark recursion depth: %lu\n", rt->gcStats.maxdepth);

    fprintf(fp, "      maximum GC nesting level: %lu\n", rt->gcStats.maxlevel);

    fprintf(fp, "   potentially useful GC calls: %lu\n", rt->gcStats.poke);

    fprintf(fp, "              useless GC calls: %lu\n", rt->gcStats.nopoke);

    fprintf(fp, "     thing arenas freed so far: %lu\n", rt->gcStats.afree);

    fprintf(fp, "  extra stack segments scanned: %lu\n", rt->gcStats.stackseg);

    fprintf(fp, "   stack segment slots scanned: %lu\n", rt->gcStats.segslots);

#ifdef JS_ARENAMETER

    JS_DumpArenaStats(fp);

#endif

}

#endif



#if DEBUG

JS_STATIC_DLL_CALLBACK(intN)

js_root_printer(JSHashEntry *he, intN i, void *arg)

{

    uint32 *leakedroots = (uint32 *)arg;



    *leakedroots += 1;

    fprintf(stderr,

            "JS engine warning: leaking GC root \'%s\' at %p\n",

            he->value ? (char *)he->value : "", he->key);



    return HT_ENUMERATE_NEXT;

}

#endif



void

js_FinishGC(JSRuntime *rt)

{

#ifdef JS_ARENAMETER

    JS_DumpArenaStats(stdout);

#endif

#ifdef JS_GCMETER

    js_DumpGCStats(rt, stdout);

#endif

    JS_FinishArenaPool(&rt->gcArenaPool);

    JS_ArenaFinish();



#ifdef DEBUG

    {

        uint32 leakedroots = 0;



        /* Warn (but don't assert) debug builds of any remaining roots. */

        JS_HashTableEnumerateEntries(rt->gcRootsHash, js_root_printer,

                                     &leakedroots);

        if (leakedroots > 0) {

            if (leakedroots == 1) {

                fprintf(stderr,

"JS engine warning: 1 GC root remains after destroying the JSRuntime.\n"

"                   This root may point to freed memory. Objects reachable\n"

"                   through it have not been finalized.\n");

            } else {

                fprintf(stderr,

"JS engine warning: %lu GC roots remain after destroying the JSRuntime.\n"

"                   These roots may point to freed memory. Objects reachable\n"

"                   through them have not been finalized.\n",

                        (unsigned long) leakedroots);

            }

        }

    }

#endif



    JS_HashTableDestroy(rt->gcRootsHash);

    rt->gcRootsHash = NULL;

    if (rt->gcLocksHash) {

        JS_HashTableDestroy(rt->gcLocksHash);

        rt->gcLocksHash = NULL;

    }

    rt->gcFreeList = NULL;

}



JSBool

js_AddRoot(JSContext *cx, void *rp, const char *name)

{

    JSRuntime *rt;

    JSBool ok;



    /*

     * Due to the long-standing, but now removed, use of rt->gcLock across the

     * bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking

     * properly with a racing GC, without calling JS_AddRoot from a request.

     * We have to preserve API compatibility here, now that we avoid holding

     * rt->gcLock across the mark phase (including the root hashtable mark).

     *

     * If the GC is running and we're called on another thread, wait for this

     * GC activation to finish.  We can safely wait here (in the case where we

     * are called within a request on another thread's context) without fear

     * of deadlock because the GC doesn't set rt->gcRunning until after it has

     * waited for all active requests to end.

     */

    rt = cx->runtime;

    JS_LOCK_GC(rt);

#ifdef JS_THREADSAFE

    JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0);

    if (rt->gcRunning && rt->gcThread != js_CurrentThreadId()) {

        do {

            JS_AWAIT_GC_DONE(rt);

        } while (rt->gcLevel > 0);

    }

#endif

    ok = (JS_HashTableAdd(rt->gcRootsHash, rp, (void *)name) != NULL);

    JS_UNLOCK_GC(rt);

    if (!ok)

	JS_ReportOutOfMemory(cx);

    return ok;

}



JSBool

js_RemoveRoot(JSRuntime *rt, void *rp)

{

    /*

     * Due to the JS_RemoveRootRT API, we may be called outside of a request.

     * Same synchronization drill as above in js_AddRoot.

     */

    JS_LOCK_GC(rt);

#ifdef JS_THREADSAFE

    JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0);

    if (rt->gcRunning && rt->gcThread != js_CurrentThreadId()) {

        do {

            JS_AWAIT_GC_DONE(rt);

        } while (rt->gcLevel > 0);

    }

#endif

    JS_HashTableRemove(rt->gcRootsHash, rp);

    rt->gcPoke = JS_TRUE;

    JS_UNLOCK_GC(rt);

    return JS_TRUE;

}



void *

js_AllocGCThing(JSContext *cx, uintN flags)

{

    JSBool tried_gc;

    JSRuntime *rt;

    JSGCThing *thing;

    uint8 *flagp;



#ifdef TOO_MUCH_GC

    js_GC(cx, GC_KEEP_ATOMS);

    tried_gc = JS_TRUE;

#else

    tried_gc = JS_FALSE;

#endif



    rt = cx->runtime;

    JS_LOCK_GC(rt);

    JS_ASSERT(!rt->gcRunning);

    if (rt->gcRunning) {

        METER(rt->gcStats.finalfail++);

        JS_UNLOCK_GC(rt);

        return NULL;

    }

    METER(rt->gcStats.alloc++);

retry:

    thing = rt->gcFreeList;

    if (thing) {

	rt->gcFreeList = thing->next;

	flagp = thing->flagp;

	METER(rt->gcStats.freelen--);

	METER(rt->gcStats.recycle++);

    } else {

        if (rt->gcBytes < rt->gcMaxBytes &&

            (tried_gc || rt->gcMallocBytes < rt->gcMaxBytes))

        {

            /*

             * Inline form of JS_ARENA_ALLOCATE adapted to truncate the current

             * arena's limit to a GC_PAGE_SIZE boundary, and to skip over every

             * GC_PAGE_SIZE-byte-aligned thing (which is actually not a thing,

             * it's a JSGCPageInfo record).

             */

            JSArenaPool *pool = &rt->gcArenaPool;

            JSArena *a = pool->current;

            size_t nb = sizeof(JSGCThing);

            jsuword p = a->avail;

            jsuword q = p + nb;



            if (q > (a->limit & ~GC_PAGE_MASK)) {

                thing = gc_new_arena(pool);

            } else {

                if ((p & GC_PAGE_MASK) == 0) {

                    /* Beware, p points to a JSGCPageInfo record! */

                    p = q;

                    q += nb;

                    JS_ArenaCountAllocation(pool, nb);

                }

                a->avail = q;

                thing = (JSGCThing *)p;

            }

            JS_ArenaCountAllocation(pool, nb);

        }



        /* Consider doing a "last ditch" GC if thing couldn't be allocated. */

        if (!thing) {

            if (!tried_gc) {

                JS_UNLOCK_GC(rt);

                js_GC(cx, GC_KEEP_ATOMS);

                tried_gc = JS_TRUE;

                JS_LOCK_GC(rt);

                METER(rt->gcStats.retry++);

                goto retry;

            }

            METER(rt->gcStats.fail++);

            JS_UNLOCK_GC(rt);

            JS_ReportOutOfMemory(cx);

            return NULL;

        }



        /* Find the flags pointer given thing's address. */

        flagp = gc_find_flags(thing);

    }

    *flagp = (uint8)flags;

    rt->gcBytes += sizeof(JSGCThing) + sizeof(uint8);

    cx->newborn[flags & GCF_TYPEMASK] = thing;



    /*

     * Clear thing before unlocking in case a GC run is about to scan it,

     * finding it via cx->newborn[].

     */

    thing->next = NULL;

    thing->flagp = NULL;

    JS_UNLOCK_GC(rt);

    return thing;

}



static JSHashNumber

gc_hash_thing(const void *key)

{

    JSHashNumber num = (JSHashNumber) key;	/* help lame MSVC1.5 on Win16 */



    return num >> JSVAL_TAGBITS;

}



#define gc_lock_get_count(he)   ((jsrefcount)(he)->value)

#define gc_lock_set_count(he,n) ((jsrefcount)((he)->value = (void *)(n)))

#define gc_lock_increment(he)   gc_lock_set_count(he, gc_lock_get_count(he)+1)

#define gc_lock_decrement(he)   gc_lock_set_count(he, gc_lock_get_count(he)-1)



JSBool

js_LockGCThing(JSContext *cx, void *thing)

{

    JSRuntime *rt;

    uint8 *flagp, flags, lockbits;

    JSBool ok;

    JSHashEntry **hep, *he;



    if (!thing)

	return JS_TRUE;

    flagp = gc_find_flags(thing);

    flags = *flagp;



    ok = JS_TRUE;

    rt = cx->runtime;

    JS_LOCK_GC(rt);

    lockbits = (flags & GCF_LOCKMASK);



    if (lockbits != GCF_LOCKMASK) {

        if ((flags & GCF_TYPEMASK) == GCX_OBJECT) {

            /* Objects may require "deep locking", i.e., rooting by value. */

            if (lockbits == 0) {

                if (!rt->gcLocksHash) {

                    rt->gcLocksHash = JS_NewHashTable(GC_ROOTS_SIZE,

                                                      gc_hash_thing,

                                                      JS_CompareValues,

                                                      JS_CompareValues,

                                                      NULL, NULL);

                    if (!rt->gcLocksHash)

                        goto outofmem;

                } else {

                    JS_ASSERT(!JS_HashTableLookup(rt->gcLocksHash, thing));

                }

                he = JS_HashTableAdd(rt->gcLocksHash, thing, NULL);

                if (!he)

                    goto outofmem;

                gc_lock_set_count(he, 1);

                *flagp = (uint8)(flags + GCF_LOCK);

            } else {

                JS_ASSERT(lockbits == GCF_LOCK);

                hep = JS_HashTableRawLookup(rt->gcLocksHash,

                                            gc_hash_thing(thing),

                                            thing);

                he = *hep;

                JS_ASSERT(he);

                if (he) {

                    JS_ASSERT(gc_lock_get_count(he) >= 1);

                    gc_lock_increment(he);

                }

            }

        } else {

            *flagp = (uint8)(flags + GCF_LOCK);

        }

    } else {

	METER(rt->gcStats.stuck++);

    }



    METER(rt->gcStats.lock++);

out:

    JS_UNLOCK_GC(rt);

    return ok;



outofmem:

    JS_ReportOutOfMemory(cx);

    ok = JS_FALSE;

    goto out;

}



JSBool

js_UnlockGCThing(JSContext *cx, void *thing)

{

    JSRuntime *rt;

    uint8 *flagp, flags, lockbits;

    JSHashEntry **hep, *he;



    if (!thing)

	return JS_TRUE;

    flagp = gc_find_flags(thing);

    flags = *flagp;



    rt = cx->runtime;

    JS_LOCK_GC(rt);

    lockbits = (flags & GCF_LOCKMASK);



    if (lockbits != GCF_LOCKMASK) {

        if ((flags & GCF_TYPEMASK) == GCX_OBJECT) {

            /* Defend against a call on an unlocked object. */

            if (lockbits != 0) {

                JS_ASSERT(lockbits == GCF_LOCK);

                hep = JS_HashTableRawLookup(rt->gcLocksHash,

                                            gc_hash_thing(thing),

                                            thing);

                he = *hep;

                JS_ASSERT(he);

                if (he && gc_lock_decrement(he) == 0) {

                    JS_HashTableRawRemove(rt->gcLocksHash, hep, he);

                    *flagp = (uint8)(flags & ~GCF_LOCKMASK);

                }

            }

        } else {

            *flagp = (uint8)(flags - GCF_LOCK);

        }

    } else {

	METER(rt->gcStats.unstuck++);

    }



    rt->gcPoke = JS_TRUE;

    METER(rt->gcStats.unlock++);

    JS_UNLOCK_GC(rt);

    return JS_TRUE;

}



#ifdef GC_MARK_DEBUG



#include <stdio.h>

#include <stdlib.h>

#include "jsprf.h"



JS_FRIEND_DATA(FILE *) js_DumpGCHeap;

JS_EXPORT_DATA(void *) js_LiveThingToFind;



#ifdef HAVE_XPCONNECT

#include "dump_xpc.h"

#endif



static const char *

gc_object_class_name(void* thing)

{

    uint8 *flagp = gc_find_flags(thing);

    const char *className = "";



    if (flagp && ((*flagp & GCF_TYPEMASK) == GCX_OBJECT)) {

        JSObject  *obj = (JSObject *)thing;

        JSClass   *clasp = JSVAL_TO_PRIVATE(obj->slots[JSSLOT_CLASS]);

        className = clasp->name;

#ifdef HAVE_XPCONNECT

        if ((clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) &&

            (clasp->flags & JSCLASS_HAS_PRIVATE)) {

            jsval privateValue = obj->slots[JSSLOT_PRIVATE];

            void  *privateThing = JSVAL_IS_VOID(privateValue)

                                  ? NULL

                                  : JSVAL_TO_PRIVATE(privateValue);



            const char *xpcClassName = GetXPCObjectClassName(privateThing);

            if (xpcClassName)

                className = xpcClassName;

        }

#endif

    }



    return className;

}



static void

gc_dump_thing(JSGCThing *thing, uint8 flags, GCMarkNode *prev, FILE *fp)

{

    GCMarkNode *next = NULL;

    char *path = NULL;



    while (prev) {

        next = prev;

        prev = prev->prev;

    }

    while (next) {

        path = JS_sprintf_append(path, "%s(%s).",

                                 next->name,

                                 gc_object_class_name(next->thing));

        next = next->next;

    }

    if (!path)

        return;



    fprintf(fp, "%08lx ", (long)thing);

    switch (flags & GCF_TYPEMASK) {

      case GCX_OBJECT:

      {

        JSObject  *obj = (JSObject *)thing;

        jsval     privateValue = obj->slots[JSSLOT_PRIVATE];

        void      *privateThing = JSVAL_IS_VOID(privateValue)

                                  ? NULL

                                  : JSVAL_TO_PRIVATE(privateValue);

        const char *className = gc_object_class_name(thing);

        fprintf(fp, "object %08p %s", privateThing, className);

        break;

      }

      case GCX_STRING:

      case GCX_EXTERNAL_STRING:

      default:

        fprintf(fp, "string %s", JS_GetStringBytes((JSString *)thing));

        break;

      case GCX_DOUBLE:

        fprintf(fp, "double %g", *(jsdouble *)thing);

        break;

    }

    fprintf(fp, " via %s\n", path);

    free(path);

}



#endif /* !GC_MARK_DEBUG */



static void

gc_mark_atom_key_thing(void *thing, void *arg)

{

    JSContext *cx = (JSContext *) arg;



    GC_MARK(cx, thing, "atom", NULL);

}



void

js_MarkAtom(JSContext *cx, JSAtom *atom, void *arg)

{

    jsval key;



    if (atom->flags & ATOM_MARK)

	return;

    atom->flags |= ATOM_MARK;

    key = ATOM_KEY(atom);

    if (JSVAL_IS_GCTHING(key)) {

#ifdef GC_MARK_DEBUG

	char name[32];



	if (JSVAL_IS_STRING(key)) {

	    JS_snprintf(name, sizeof name, "'%s'",

			JS_GetStringBytes(JSVAL_TO_STRING(key)));

	} else {

	    JS_snprintf(name, sizeof name, "<%x>", key);

	}

#endif

	GC_MARK(cx, JSVAL_TO_GCTHING(key), name, arg);

    }

}



void

js_MarkGCThing(JSContext *cx, void *thing, void *arg)

{

    uint8 flags, *flagp;

    JSRuntime *rt;

    JSObject *obj;

    uint32 nslots;

    jsval v, *vp, *end;

#ifdef GC_MARK_DEBUG

    JSScope *scope;

    JSScopeProperty *sprop;

#endif



    if (!thing)

	return;



    flagp = gc_find_flags(thing);

    flags = *flagp;

    JS_ASSERT(flags != GCF_FINAL);

#ifdef GC_MARK_DEBUG

    if (js_LiveThingToFind == thing)

        gc_dump_thing(thing, flags, arg, stderr);

#endif



    if (flags & GCF_MARK)

	return;



    *flagp |= GCF_MARK;

    rt = cx->runtime;

    METER(if (++rt->gcStats.depth > rt->gcStats.maxdepth)

	      rt->gcStats.maxdepth = rt->gcStats.depth);



#ifdef GC_MARK_DEBUG

    if (js_DumpGCHeap)

        gc_dump_thing(thing, flags, arg, js_DumpGCHeap);

#endif



    if ((flags & GCF_TYPEMASK) == GCX_OBJECT) {

	obj = (JSObject *) thing;

	vp = obj->slots;

        if (!vp) {

            /* If obj->slots is null, obj must be a newborn. */

            JS_ASSERT(!obj->map);

            goto out;

        }

        nslots = (obj->map->ops->mark)

                 ? obj->map->ops->mark(cx, obj, arg)

                 : obj->map->freeslot;

#ifdef GC_MARK_DEBUG

        scope = OBJ_IS_NATIVE(obj) ? OBJ_SCOPE(obj) : NULL;

#endif

        for (end = vp + nslots; vp < end; vp++) {

            v = *vp;

            if (JSVAL_IS_GCTHING(v)) {

#ifdef GC_MARK_DEBUG

                char name[32];



                if (scope) {

                    uint32 slot;

                    jsval nval;



                    slot = vp - obj->slots;

                    for (sprop = scope->props; ; sprop = sprop->next) {

                        if (!sprop) {

                            switch (slot) {

                              case JSSLOT_PROTO:

                                strcpy(name, "__proto__");

                                break;

                              case JSSLOT_PARENT:

                                strcpy(name, "__parent__");

                                break;

                              case JSSLOT_PRIVATE:

                                strcpy(name, "__private__");

                                break;

                              default:

                                JS_snprintf(name, sizeof name,

                                            "**UNKNOWN SLOT %ld**",

                                            (long)slot);

                                break;

                            }

                            break;

                        }

                        if (sprop->slot == slot) {

                            nval = sprop->symbols

                                   ? js_IdToValue(sym_id(sprop->symbols))

                                   : sprop->id;

                            if (JSVAL_IS_INT(nval)) {

                                JS_snprintf(name, sizeof name, "%ld",

                                            (long)JSVAL_TO_INT(nval));

                            } else if (JSVAL_IS_STRING(nval)) {

                                JS_snprintf(name, sizeof name, "%s",

                                  JS_GetStringBytes(JSVAL_TO_STRING(nval)));

                            } else {

                                strcpy(name, "**FINALIZED ATOM KEY**");

                            }

                            break;

                        }

                    }

                } else {

                    strcpy(name, "**UNKNOWN OBJECT MAP ENTRY**");

                }

#endif

                GC_MARK(cx, JSVAL_TO_GCTHING(v), name, arg);

            }

        }

    }



out:

    METER(rt->gcStats.depth--);

}



static JSHashNumber

gc_hash_root(const void *key)

{

    JSHashNumber num = (JSHashNumber) key;	/* help lame MSVC1.5 on Win16 */



    return num >> 2;

}



JS_STATIC_DLL_CALLBACK(intN)

gc_root_marker(JSHashEntry *he, intN i, void *arg)

{

    jsval *rp = (jsval *)he->key;

    jsval v = *rp;



    /* Ignore null object and scalar values. */

    if (!JSVAL_IS_NULL(v) && JSVAL_IS_GCTHING(v)) {

        JSContext *cx = (JSContext *)arg;

#ifdef DEBUG

        JSArena *a;

        jsuword firstpage;

        JSBool root_points_to_gcArenaPool = JS_FALSE;

	void *thing = JSVAL_TO_GCTHING(v);



        for (a = cx->runtime->gcArenaPool.first.next; a; a = a->next) {

            firstpage = FIRST_THING_PAGE(a);

	    if (JS_UPTRDIFF(thing, firstpage) < a->avail - firstpage) {

                root_points_to_gcArenaPool = JS_TRUE;

                break;

            }

        }

        if (!root_points_to_gcArenaPool && he->value) {

            fprintf(stderr,

"JS API usage error: the address passed to JS_AddNamedRoot currently holds an\n"

"invalid jsval.  This is usually caused by a missing call to JS_RemoveRoot.\n"

"The root's name is \"%s\".\n",

                    (const char *) he->value);

        }

        JS_ASSERT(root_points_to_gcArenaPool);

#endif



        GC_MARK(cx, JSVAL_TO_GCTHING(v), he->value ? he->value : "root", NULL);

    }

    return HT_ENUMERATE_NEXT;

}



JS_STATIC_DLL_CALLBACK(intN)

gc_lock_marker(JSHashEntry *he, intN i, void *arg)

{

    void *thing = (void *)he->key;

    JSContext *cx = (JSContext *)arg;



    GC_MARK(cx, thing, "locked object", NULL);

    return HT_ENUMERATE_NEXT;

}



JS_FRIEND_API(void)

js_ForceGC(JSContext *cx)

{

    uintN i;



    for (i = 0; i < GCX_NTYPES; i++)

        cx->newborn[i] = NULL;

    cx->runtime->gcPoke = JS_TRUE;

    js_GC(cx, 0);

    JS_ArenaFinish();

}



#define GC_MARK_JSVALS(cx, len, vec, name)                                    \

    JS_BEGIN_MACRO                                                            \

        jsval _v, *_vp, *_end;                                                \

                                                                              \

        for (_vp = vec, _end = _vp + len; _vp < _end; _vp++) {                \

            _v = *_vp;                                                        \

            if (JSVAL_IS_GCTHING(_v))                                         \

                GC_MARK(cx, JSVAL_TO_GCTHING(_v), name, NULL);                \

        }                                                                     \

    JS_END_MACRO



void

js_GC(JSContext *cx, uintN gcflags)

{

    JSRuntime *rt;

    JSContext *iter, *acx;

    JSStackFrame *fp, *chain;

    uintN i, depth, nslots;

    JSStackHeader *sh;

    JSArena *a, **ap;

    uint8 flags, *flagp, *split;

    JSGCThing *thing, *limit, **flp, **oflp;

    GCFinalizeOp finalizer;

    JSBool all_clear;

#ifdef JS_THREADSAFE

    jsword currentThread;

    uint32 requestDebit;

#endif



    rt = cx->runtime;

#ifdef JS_THREADSAFE

    /* Avoid deadlock. */

    JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt));

#endif



    /* Don't run gc if it is disabled (unless this is the last context). */

    if (rt->gcDisabled && !(gcflags & GC_LAST_CONTEXT))

        return;



    /*

     * Let the API user decide to defer a GC if it wants to (unless this

     * is the last context). Call the callback regardless.

     */

    if (rt->gcCallback) {

        if (!rt->gcCallback(cx, JSGC_BEGIN) && !(gcflags & GC_LAST_CONTEXT))

            return;

    }



    /* Lock out other GC allocator and collector invocations. */

    JS_LOCK_GC(rt);



    /* Do nothing if no assignment has executed since the last GC. */

    if (!rt->gcPoke) {

	METER(rt->gcStats.nopoke++);

	JS_UNLOCK_GC(rt);

	return;

    }

    rt->gcPoke = JS_FALSE;

    METER(rt->gcStats.poke++);



#ifdef JS_THREADSAFE

    /* Bump gcLevel and return rather than nest on this thread. */

    currentThread = js_CurrentThreadId();

    if (rt->gcThread == currentThread) {

	JS_ASSERT(rt->gcLevel > 0);

	rt->gcLevel++;

	METER(if (rt->gcLevel > rt->gcStats.maxlevel)

		  rt->gcStats.maxlevel = rt->gcLevel);

        JS_UNLOCK_GC(rt);

        return;

    }



    /*

     * If we're in one or more requests (possibly on more than one context)

     * running on the current thread, indicate, temporarily, that all these

     * requests are inactive.  NB: if cx->thread is 0, then cx is not using

     * the request model, and does not contribute to rt->requestCount.

     */

    requestDebit = 0;

    if (cx->thread) {

        /*

         * Check all contexts for any with the same thread-id.  XXX should we

         * keep a sub-list of contexts having the same id?

         */

        iter = NULL;

        while ((acx = js_ContextIterator(rt, &iter)) != NULL) {

            if (acx->thread == cx->thread && acx->requestDepth)

                requestDebit++;

        }

    } else {

        /*

         * We assert, but check anyway, in case someone is misusing the API.

         * Avoiding the loop over all of rt's contexts is a win in the event

         * that the GC runs only on request-less contexts with 0 thread-ids,

         * in a special thread such as might be used by the UI/DOM/Layout

         * "mozilla" or "main" thread in Mozilla-the-browser.

         */

        JS_ASSERT(cx->requestDepth == 0);

        if (cx->requestDepth)

            requestDebit = 1;

    }

    if (requestDebit) {

        JS_ASSERT(requestDebit <= rt->requestCount);

        rt->requestCount -= requestDebit;

        if (rt->requestCount == 0)

            JS_NOTIFY_REQUEST_DONE(rt);

    }



    /* If another thread is already in GC, don't attempt GC; wait instead. */

    if (rt->gcLevel > 0) {

        /* Bump gcLevel to restart the current GC, so it finds new garbage. */

	rt->gcLevel++;

	METER(if (rt->gcLevel > rt->gcStats.maxlevel)

		  rt->gcStats.maxlevel = rt->gcLevel);



        /* Wait for the other thread to finish, then resume our request. */

        while (rt->gcLevel > 0)

            JS_AWAIT_GC_DONE(rt);

        if (requestDebit)

            rt->requestCount += requestDebit;

	JS_UNLOCK_GC(rt);

	return;

    }



    /* No other thread is in GC, so indicate that we're now in GC. */

    rt->gcLevel = 1;

    rt->gcThread = currentThread;



    /* Wait for all other requests to finish. */

    while (rt->requestCount > 0)

	JS_AWAIT_REQUEST_DONE(rt);



#else  /* !JS_THREADSAFE */



    /* Bump gcLevel and return rather than nest; the outer gc will restart. */

    rt->gcLevel++;

    METER(if (rt->gcLevel > rt->gcStats.maxlevel)

	      rt->gcStats.maxlevel = rt->gcLevel);

    if (rt->gcLevel > 1)

	return;



#endif /* !JS_THREADSAFE */



    /*

     * Set rt->gcRunning here within the GC lock, and after waiting for any

     * active requests to end, so that new requests that try to JS_AddRoot,

     * JS_RemoveRoot, or JS_RemoveRootRT block in JS_BeginRequest waiting for

     * rt->gcLevel to drop to zero, while request-less calls to the *Root*

     * APIs block in js_AddRoot or js_RemoveRoot (see above in this file),

     * waiting for GC to finish.

     */

    rt->gcRunning = JS_TRUE;

    JS_UNLOCK_GC(rt);



    /* Reset malloc counter */

    rt->gcMallocBytes = 0;



    /* Drop atoms held by the property cache, and clear property weak links. */

    js_FlushPropertyCache(cx);

restart:

    rt->gcNumber++;



    /*

     * Mark phase.

     */

    JS_HashTableEnumerateEntries(rt->gcRootsHash, gc_root_marker, cx);

    if (rt->gcLocksHash)

        JS_HashTableEnumerateEntries(rt->gcLocksHash, gc_lock_marker, cx);

    js_MarkAtomState(&rt->atomState, gcflags, gc_mark_atom_key_thing, cx);

    iter = NULL;

    while ((acx = js_ContextIterator(rt, &iter)) != NULL) {

	/*

	 * Iterate frame chain and dormant chains. Temporarily tack current

	 * frame onto the head of the dormant list to ease iteration.

	 *

	 * (NB: see comment on this whole "dormant" thing in js_Execute.)

	 */

	chain = acx->fp;

	if (chain) {

	    JS_ASSERT(!chain->dormantNext);

	    chain->dormantNext = acx->dormantFrameChain;

	} else {

	    chain = acx->dormantFrameChain;

	}



	for (fp = chain; fp; fp = chain = chain->dormantNext) {

            do {

                if (fp->callobj)

                    GC_MARK(cx, fp->callobj, "call object", NULL);

                if (fp->argsobj)

                    GC_MARK(cx, fp->argsobj, "arguments object", NULL);

                if (fp->varobj)

                    GC_MARK(cx, fp->varobj, "variables object", NULL);

                if (fp->script) {

                    js_MarkScript(cx, fp->script, NULL);

                    depth = fp->script->depth;

                    if (JS_UPTRDIFF(fp->sp, fp->spbase) < depth * sizeof(jsval))

                        nslots = fp->sp - fp->spbase;

                    else

                        nslots = depth;

                    GC_MARK_JSVALS(cx, nslots, fp->spbase, "operand");

                }

                GC_MARK(cx, fp->thisp, "this", NULL);

                if (fp->argv)

                    GC_MARK_JSVALS(cx, fp->argc, fp->argv, "arg");

                if (JSVAL_IS_GCTHING(fp->rval))

                    GC_MARK(cx, JSVAL_TO_GCTHING(fp->rval), "rval", NULL);

                if (fp->vars)

                    GC_MARK_JSVALS(cx, fp->nvars, fp->vars, "var");

                GC_MARK(cx, fp->scopeChain, "scope chain", NULL);

                if (fp->sharpArray)

                    GC_MARK(cx, fp->sharpArray, "sharp array", NULL);

            } while ((fp = fp->down) != NULL);

	}



	/* Cleanup temporary "dormant" linkage. */

	if (acx->fp)

	    acx->fp->dormantNext = NULL;



        /* Mark other roots-by-definition in acx. */

	GC_MARK(cx, acx->globalObject, "global object", NULL);

	GC_MARK(cx, acx->newborn[GCX_OBJECT], "newborn object", NULL);

	GC_MARK(cx, acx->newborn[GCX_STRING], "newborn string", NULL);

	GC_MARK(cx, acx->newborn[GCX_DOUBLE], "newborn double", NULL);

	for (i = GCX_EXTERNAL_STRING; i < GCX_NTYPES; i++)

            GC_MARK(cx, acx->newborn[i], "newborn external string", NULL);

#if JS_HAS_EXCEPTIONS

	if (acx->throwing && JSVAL_IS_GCTHING(acx->exception))

	    GC_MARK(cx, JSVAL_TO_GCTHING(acx->exception), "exception", NULL);

#endif

#if JS_HAS_LVALUE_RETURN

        if (acx->rval2set && JSVAL_IS_GCTHING(acx->rval2))

            GC_MARK(cx, JSVAL_TO_GCTHING(acx->rval2), "rval2", NULL);

#endif



        for (sh = acx->stackHeaders; sh; sh = sh->down) {

            METER(rt->gcStats.stackseg++);

            METER(rt->gcStats.segslots += sh->nslots);

            GC_MARK_JSVALS(cx, sh->nslots, JS_STACK_SEGMENT(sh), "stack");

        }

    }



    if (rt->gcCallback)

        (void) rt->gcCallback(cx, JSGC_MARK_END);



    /*

     * Sweep phase.

     * Finalize as we sweep, outside of rt->gcLock, but with rt->gcRunning set

     * so that any attempt to allocate a GC-thing from a finalizer will fail,

     * rather than nest badly and leave the unmarked newborn to be swept.

     */

    js_SweepAtomState(&rt->atomState);

    for (a = rt->gcArenaPool.first.next; a; a = a->next) {

        flagp = (uint8 *) a->base;

        split = (uint8 *) FIRST_THING_PAGE(a);

        limit = (JSGCThing *) a->avail;

        for (thing = (JSGCThing *) split; thing < limit; thing++) {

            if (((jsuword)thing & GC_PAGE_MASK) == 0) {

                flagp++;

                thing++;

            }

            flags = *flagp;

            if (flags & GCF_MARK) {

                *flagp &= ~GCF_MARK;

            } else if (!(flags & (GCF_LOCKMASK | GCF_FINAL))) {

                /* Call the finalizer with GCF_FINAL ORed into flags. */

                finalizer = gc_finalizers[flags & GCF_TYPEMASK];

                if (finalizer) {

                    *flagp = (uint8)(flags | GCF_FINAL);

                    finalizer(cx, thing);

                }



                /* Set flags to GCF_FINAL, signifying that thing is free. */

                *flagp = GCF_FINAL;



                JS_ASSERT(rt->gcBytes >= sizeof(JSGCThing) + sizeof(uint8));

                rt->gcBytes -= sizeof(JSGCThing) + sizeof(uint8);

            }

            if (++flagp == split)

                flagp += GC_THINGS_SIZE;

        }

    }



    /*

     * Free phase.

     * Free any unused arenas and rebuild the JSGCThing freelist.

     */

    ap = &rt->gcArenaPool.first.next;

    a = *ap;

    if (!a)

	goto out;

    all_clear = JS_TRUE;

    flp = oflp = &rt->gcFreeList;

    *flp = NULL;

    METER(rt->gcStats.freelen = 0);



    do {

        flagp = (uint8 *) a->base;

        split = (uint8 *) FIRST_THING_PAGE(a);

        limit = (JSGCThing *) a->avail;

        for (thing = (JSGCThing *) split; thing < limit; thing++) {

            if (((jsuword)thing & GC_PAGE_MASK) == 0) {

                flagp++;

                thing++;

            }

            if (*flagp != GCF_FINAL) {

                all_clear = JS_FALSE;

            } else {

                thing->flagp = flagp;

                *flp = thing;

                flp = &thing->next;

                METER(rt->gcStats.freelen++);

            }

            if (++flagp == split)

                flagp += GC_THINGS_SIZE;

        }



        if (all_clear) {

            JS_ARENA_DESTROY(&rt->gcArenaPool, a, ap);

            flp = oflp;

            METER(rt->gcStats.afree++);

        } else {

            ap = &a->next;

            all_clear = JS_TRUE;

            oflp = flp;

        }

    } while ((a = *ap) != NULL);



    /* Terminate the new freelist. */

    *flp = NULL;



out:

    JS_LOCK_GC(rt);

    if (rt->gcLevel > 1) {

        rt->gcLevel = 1;

        JS_UNLOCK_GC(rt);

        goto restart;

    }

    rt->gcLevel = 0;

    rt->gcLastBytes = rt->gcBytes;

    rt->gcRunning = JS_FALSE;



#ifdef JS_THREADSAFE

    /* If we were invoked during a request, pay back the temporary debit. */

    if (requestDebit)

	rt->requestCount += requestDebit;

    rt->gcThread = 0;

    JS_NOTIFY_GC_DONE(rt);

    JS_UNLOCK_GC(rt);

#endif



    if (rt->gcCallback)

        (void) rt->gcCallback(cx, JSGC_END);

}

 

**** End of jsgc.c ****

 

**** Start of jsgc.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsgc_h___

#define jsgc_h___

/*

 * JS Garbage Collector.

 */

#include "jsprvtd.h"

#include "jspubtd.h"



JS_BEGIN_EXTERN_C



/* GC thing type indexes. */

#define GCX_OBJECT              0               /* JSObject */

#define GCX_STRING              1               /* JSString */

#define GCX_DOUBLE              2               /* jsdouble */

#define GCX_EXTERNAL_STRING     3               /* JSString w/ external chars */

#define GCX_NTYPES_LOG2         3               /* type index bits */

#define GCX_NTYPES              JS_BIT(GCX_NTYPES_LOG2)



/* GC flag definitions, must fit in 8 bits (type index goes in the low bits). */

#define GCF_TYPEMASK    JS_BITMASK(GCX_NTYPES_LOG2)

#define GCF_MARK        JS_BIT(GCX_NTYPES_LOG2)

#define GCF_FINAL       JS_BIT(GCX_NTYPES_LOG2 + 1)

#define GCF_LOCKSHIFT   (GCX_NTYPES_LOG2 + 2)   /* lock bit shift and mask */

#define GCF_LOCKMASK    (JS_BITMASK(8 - GCF_LOCKSHIFT) << GCF_LOCKSHIFT)

#define GCF_LOCK        JS_BIT(GCF_LOCKSHIFT)   /* lock request bit in API */



#if 1

/*

 * Since we're forcing a GC from JS_GC anyway, don't bother wasting cycles

 * loading oldval.  XXX remove implied force, fix jsinterp.c's "second arg

 * ignored", etc.

 */

#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JS_TRUE)

#else

#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JSVAL_IS_GCTHING(oldval))

#endif



extern intN

js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop,

                                 JSStringFinalizeOp newop);



extern JSBool

js_InitGC(JSRuntime *rt, uint32 maxbytes);



extern void

js_FinishGC(JSRuntime *rt);



extern JSBool

js_AddRoot(JSContext *cx, void *rp, const char *name);



extern JSBool

js_RemoveRoot(JSRuntime *rt, void *rp);



extern void *

js_AllocGCThing(JSContext *cx, uintN flags);



extern JSBool

js_LockGCThing(JSContext *cx, void *thing);



extern JSBool

js_UnlockGCThing(JSContext *cx, void *thing);



extern JSBool 

js_IsAboutToBeFinalized(JSContext *cx, void *thing);



extern void

js_MarkAtom(JSContext *cx, JSAtom *atom, void *arg);



/* We avoid a large number of unnecessary calls by doing the flag check first */

#define GC_MARK_ATOM(cx, atom, arg)                                           \

    JS_BEGIN_MACRO                                                            \

        if (!((atom)->flags & ATOM_MARK))                                     \

            js_MarkAtom(cx, atom, arg);                                       \

    JS_END_MACRO



extern void

js_MarkGCThing(JSContext *cx, void *thing, void *arg);



#ifdef GC_MARK_DEBUG



typedef struct GCMarkNode GCMarkNode;



struct GCMarkNode {

    void        *thing;

    const char  *name;

    GCMarkNode  *next;

    GCMarkNode  *prev;

};



#define GC_MARK(_cx, _thing, _name, _prev)                                    \

    JS_BEGIN_MACRO                                                            \

        GCMarkNode _node;                                                     \

        _node.thing = _thing;                                                 \

        _node.name  = _name;                                                  \

        _node.next  = NULL;                                                   \

        _node.prev  = _prev;                                                  \

        if (_prev) ((GCMarkNode *)(_prev))->next = &_node;                    \

        js_MarkGCThing(_cx, _thing, &_node);                                  \

    JS_END_MACRO



#else  /* !GC_MARK_DEBUG */



#define GC_MARK(cx, thing, name, prev)   js_MarkGCThing(cx, thing, NULL)



#endif /* !GC_MARK_DEBUG */



extern JS_FRIEND_API(void)

js_ForceGC(JSContext *cx);



/*

 * Flags to modify how a GC marks and sweeps:

 *   GC_KEEP_ATOMS      Don't sweep unmarked atoms, they may be in use by the

 *                      compiler, or by an API function that calls js_Atomize,

 *                      when the GC is called from js_AllocGCThing, due to a

 *                      malloc failure or runtime GC-thing limit.

 *   GC_LAST_CONTEXT    Called from js_DestroyContext for last JSContext in a

 *                      JSRuntime, when it is imperative that rt->gcPoke gets

 *                      cleared early in js_GC, if it is set.

 */

#define GC_KEEP_ATOMS       0x1

#define GC_LAST_CONTEXT     0x2



extern void

js_GC(JSContext *cx, uintN gcflags);



#ifdef JS_GCMETER



typedef struct JSGCStats {

    uint32  alloc;      /* number of allocation attempts */

    uint32  freelen;    /* gcFreeList length */

    uint32  recycle;    /* number of things recycled through gcFreeList */

    uint32  retry;      /* allocation attempt retries after running the GC */

    uint32  fail;       /* allocation failures */

    uint32  finalfail;  /* finalizer calls allocator failures */

    uint32  lock;       /* valid lock calls */

    uint32  unlock;     /* valid unlock calls */

    uint32  stuck;      /* stuck reference counts seen by lock calls */

    uint32  unstuck;    /* unlock calls that saw a stuck lock count */

    uint32  depth;      /* mark recursion depth */

    uint32  maxdepth;   /* maximum mark recursion depth */

    uint32  maxlevel;   /* maximum GC nesting (indirect recursion) level */

    uint32  poke;       /* number of potentially useful GC calls */

    uint32  nopoke;     /* useless GC calls where js_PokeGC was not set */

    uint32  afree;      /* thing arenas freed so far */

    uint32  stackseg;   /* total extraordinary stack segments scanned */

    uint32  segslots;   /* total stack segment jsval slots scanned */

} JSGCStats;



extern void

js_DumpGCStats(JSRuntime *rt, FILE *fp);



#endif /* JS_GCMETER */



JS_END_EXTERN_C



#endif /* jsgc_h___ */

 

**** End of jsgc.h ****

 

**** Start of jshash.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * PR hash table package.

 */

#include "jsstddef.h"

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsbit.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jshash.h" /* Added by JSIFY */



/* Compute the number of buckets in ht */

#define NBUCKETS(ht)    JS_BIT(JS_HASH_BITS - (ht)->shift)



/* The smallest table has 16 buckets */

#define MINBUCKETSLOG2  4

#define MINBUCKETS      JS_BIT(MINBUCKETSLOG2)



/* Compute the maximum entries given n buckets that we will tolerate, ~90% */

#define OVERLOADED(n)   ((n) - ((n) >> 3))



/* Compute the number of entries below which we shrink the table by half */

#define UNDERLOADED(n)  (((n) > MINBUCKETS) ? ((n) >> 2) : 0)



/*

** Stubs for default hash allocator ops.

*/

static void *

DefaultAllocTable(void *pool, size_t size)

{

    return malloc(size);

}



static void

DefaultFreeTable(void *pool, void *item)

{

    free(item);

}



static JSHashEntry *

DefaultAllocEntry(void *pool, const void *key)

{

    return (JSHashEntry*) malloc(sizeof(JSHashEntry));

}



static void

DefaultFreeEntry(void *pool, JSHashEntry *he, uintN flag)

{

    if (flag == HT_FREE_ENTRY)

        free(he);

}



static JSHashAllocOps defaultHashAllocOps = {

    DefaultAllocTable, DefaultFreeTable,

    DefaultAllocEntry, DefaultFreeEntry

};



JS_PUBLIC_API(JSHashTable *)

JS_NewHashTable(uint32 n, JSHashFunction keyHash,

                JSHashComparator keyCompare, JSHashComparator valueCompare,

                JSHashAllocOps *allocOps, void *allocPriv)

{

    JSHashTable *ht;

    size_t nb;



    if (n <= MINBUCKETS) {

        n = MINBUCKETSLOG2;

    } else {

        n = JS_CeilingLog2(n);

        if ((int32)n < 0)

            return NULL;

    }



    if (!allocOps) allocOps = &defaultHashAllocOps;



    ht = (JSHashTable*) (*allocOps->allocTable)(allocPriv, sizeof *ht);

    if (!ht)

	return NULL;

    memset(ht, 0, sizeof *ht);

    ht->shift = JS_HASH_BITS - n;

    n = JS_BIT(n);

#if defined(XP_PC) && defined _MSC_VER && _MSC_VER <= 800

    if (n > 16000) {

        (*allocOps->freeTable)(allocPriv, ht);

        return NULL;

    }

#endif  /* WIN16 */

    nb = n * sizeof(JSHashEntry *);

    ht->buckets = (JSHashEntry**) (*allocOps->allocTable)(allocPriv, nb);

    if (!ht->buckets) {

        (*allocOps->freeTable)(allocPriv, ht);

        return NULL;

    }

    memset(ht->buckets, 0, nb);



    ht->keyHash = keyHash;

    ht->keyCompare = keyCompare;

    ht->valueCompare = valueCompare;

    ht->allocOps = allocOps;

    ht->allocPriv = allocPriv;

    return ht;

}



JS_PUBLIC_API(void)

JS_HashTableDestroy(JSHashTable *ht)

{

    uint32 i, n;

    JSHashEntry *he, *next;

    JSHashAllocOps *allocOps = ht->allocOps;

    void *allocPriv = ht->allocPriv;



    n = NBUCKETS(ht);

    for (i = 0; i < n; i++) {

        for (he = ht->buckets[i]; he; he = next) {

            next = he->next;

            (*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);

        }

    }

#ifdef DEBUG

    memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);

#endif

    (*allocOps->freeTable)(allocPriv, ht->buckets);

#ifdef DEBUG

    memset(ht, 0xDB, sizeof *ht);

#endif

    (*allocOps->freeTable)(allocPriv, ht);

}



/*

** Multiplicative hash, from Knuth 6.4.

*/

JS_PUBLIC_API(JSHashEntry **)

JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key)

{

    JSHashEntry *he, **hep, **hep0;

    JSHashNumber h;



#ifdef HASHMETER

    ht->nlookups++;

#endif

    h = keyHash * JS_GOLDEN_RATIO;

    h >>= ht->shift;

    hep = hep0 = &ht->buckets[h];

    while ((he = *hep) != NULL) {

        if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {

            /* Move to front of chain if not already there */

            if (hep != hep0) {

                *hep = he->next;

                he->next = *hep0;

                *hep0 = he;

            }

            return hep0;

        }

        hep = &he->next;

#ifdef HASHMETER

        ht->nsteps++;

#endif

    }

    return hep;

}



JS_PUBLIC_API(JSHashEntry *)

JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep,

                   JSHashNumber keyHash, const void *key, void *value)

{

    uint32 i, n;

    JSHashEntry *he, *next, **oldbuckets;

    size_t nb;



    /* Grow the table if it is overloaded */

    n = NBUCKETS(ht);

    if (ht->nentries >= OVERLOADED(n)) {

#ifdef HASHMETER

        ht->ngrows++;

#endif

        ht->shift--;

        oldbuckets = ht->buckets;

#if defined(XP_PC) && defined _MSC_VER && _MSC_VER <= 800

        if (2 * n > 16000)

            return NULL;

#endif  /* WIN16 */

        nb = 2 * n * sizeof(JSHashEntry *);

        ht->buckets = (JSHashEntry**) (*ht->allocOps->allocTable)(ht->allocPriv, nb);

        if (!ht->buckets) {

            ht->buckets = oldbuckets;

            return NULL;

	}

        memset(ht->buckets, 0, nb);



        for (i = 0; i < n; i++) {

            for (he = oldbuckets[i]; he; he = next) {

                next = he->next;

                hep = JS_HashTableRawLookup(ht, he->keyHash, he->key);

                JS_ASSERT(*hep == NULL);

                he->next = NULL;

                *hep = he;

            }

        }

#ifdef DEBUG

        memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);

#endif

        (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);

        hep = JS_HashTableRawLookup(ht, keyHash, key);

    }



    /* Make a new key value entry */

    he = (*ht->allocOps->allocEntry)(ht->allocPriv, key);

    if (!he)

	return NULL;

    he->keyHash = keyHash;

    he->key = key;

    he->value = value;

    he->next = *hep;

    *hep = he;

    ht->nentries++;

    return he;

}



JS_PUBLIC_API(JSHashEntry *)

JS_HashTableAdd(JSHashTable *ht, const void *key, void *value)

{

    JSHashNumber keyHash;

    JSHashEntry *he, **hep;



    keyHash = (*ht->keyHash)(key);

    hep = JS_HashTableRawLookup(ht, keyHash, key);

    if ((he = *hep) != NULL) {

        /* Hit; see if values match */

        if ((*ht->valueCompare)(he->value, value)) {

            /* key,value pair is already present in table */

            return he;

        }

        if (he->value)

            (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE);

        he->value = value;

        return he;

    }

    return JS_HashTableRawAdd(ht, hep, keyHash, key, value);

}



JS_PUBLIC_API(void)

JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he)

{

    uint32 i, n;

    JSHashEntry *next, **oldbuckets;

    size_t nb;



    *hep = he->next;

    (*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY);



    /* Shrink table if it's underloaded */

    n = NBUCKETS(ht);

    if (--ht->nentries < UNDERLOADED(n)) {

#ifdef HASHMETER

        ht->nshrinks++;

#endif

        ht->shift++;

        oldbuckets = ht->buckets;

        nb = n * sizeof(JSHashEntry*) / 2;

        ht->buckets = (JSHashEntry**) (*ht->allocOps->allocTable)(ht->allocPriv, nb);

        if (!ht->buckets) {

            ht->buckets = oldbuckets;

            return;

        }

        memset(ht->buckets, 0, nb);



        for (i = 0; i < n; i++) {

            for (he = oldbuckets[i]; he; he = next) {

                next = he->next;

                hep = JS_HashTableRawLookup(ht, he->keyHash, he->key);

                JS_ASSERT(*hep == NULL);

                he->next = NULL;

                *hep = he;

            }

        }

#ifdef DEBUG

        memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);

#endif

        (*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);

    }

}



JS_PUBLIC_API(JSBool)

JS_HashTableRemove(JSHashTable *ht, const void *key)

{

    JSHashNumber keyHash;

    JSHashEntry *he, **hep;



    keyHash = (*ht->keyHash)(key);

    hep = JS_HashTableRawLookup(ht, keyHash, key);

    if ((he = *hep) == NULL)

        return JS_FALSE;



    /* Hit; remove element */

    JS_HashTableRawRemove(ht, hep, he);

    return JS_TRUE;

}



JS_PUBLIC_API(void *)

JS_HashTableLookup(JSHashTable *ht, const void *key)

{

    JSHashNumber keyHash;

    JSHashEntry *he, **hep;



    keyHash = (*ht->keyHash)(key);

    hep = JS_HashTableRawLookup(ht, keyHash, key);

    if ((he = *hep) != NULL) {

        return he->value;

    }

    return NULL;

}



/*

** Iterate over the entries in the hash table calling func for each

** entry found. Stop if "f" says to (return value & JS_ENUMERATE_STOP).

** Return a count of the number of elements scanned.

*/

JS_PUBLIC_API(int)

JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg)

{

    JSHashEntry *he, **hep;

    uint32 i, nbuckets;

    int rv, n = 0;

    JSHashEntry *todo = NULL;



    nbuckets = NBUCKETS(ht);

    for (i = 0; i < nbuckets; i++) {

        hep = &ht->buckets[i];

        while ((he = *hep) != NULL) {

            rv = (*f)(he, n, arg);

            n++;

            if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) {

                *hep = he->next;

                if (rv & HT_ENUMERATE_REMOVE) {

                    he->next = todo;

                    todo = he;

                }

            } else {

                hep = &he->next;

            }

            if (rv & HT_ENUMERATE_STOP) {

                goto out;

            }

        }

    }



out:

    hep = &todo;

    while ((he = *hep) != NULL) {

        JS_HashTableRawRemove(ht, hep, he);

    }

    return n;

}



#ifdef HASHMETER

#include <math.h>

#include <stdio.h>



JS_PUBLIC_API(void)

JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)

{

    double sqsum, mean, variance, sigma;

    uint32 nchains, nbuckets, nentries;

    uint32 i, n, maxChain, maxChainLen;

    JSHashEntry *he;



    sqsum = 0;

    nchains = 0;

    maxChainLen = 0;

    nbuckets = NBUCKETS(ht);

    for (i = 0; i < nbuckets; i++) {

        he = ht->buckets[i];

        if (!he)

            continue;

        nchains++;

        for (n = 0; he; he = he->next)

            n++;

        sqsum += n * n;

        if (n > maxChainLen) {

            maxChainLen = n;

            maxChain = i;

        }

    }

    nentries = ht->nentries;

    mean = (double)nentries / nchains;

    variance = nchains * sqsum - nentries * nentries;

    if (variance < 0 || nchains == 1)

        variance = 0;

    else

        variance /= nchains * (nchains - 1);

    sigma = sqrt(variance);



    fprintf(fp, "\nHash table statistics:\n");

    fprintf(fp, "     number of lookups: %u\n", ht->nlookups);

    fprintf(fp, "     number of entries: %u\n", ht->nentries);

    fprintf(fp, "       number of grows: %u\n", ht->ngrows);

    fprintf(fp, "     number of shrinks: %u\n", ht->nshrinks);

    fprintf(fp, "   mean steps per hash: %g\n", (double)ht->nsteps

                                                / ht->nlookups);

    fprintf(fp, "mean hash chain length: %g\n", mean);

    fprintf(fp, "    standard deviation: %g\n", sigma);

    fprintf(fp, " max hash chain length: %u\n", maxChainLen);

    fprintf(fp, "        max hash chain: [%u]\n", maxChain);



    for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++)

        if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT)

            break;

}

#endif /* HASHMETER */



JS_PUBLIC_API(int)

JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp)

{

    int count;



    count = JS_HashTableEnumerateEntries(ht, dump, fp);

#ifdef HASHMETER

    JS_HashTableDumpMeter(ht, dump, fp);

#endif

    return count;

}



JS_PUBLIC_API(JSHashNumber)

JS_HashString(const void *key)

{

    JSHashNumber h;

    const unsigned char *s;



    h = 0;

    for (s = (const unsigned char *)key; *s; s++)

        h = (h >> 28) ^ (h << 4) ^ *s;

    return h;

}



JS_PUBLIC_API(int)

JS_CompareValues(const void *v1, const void *v2)

{

    return v1 == v2;

}

 

**** End of jshash.c ****

 

**** Start of jshash.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jshash_h___

#define jshash_h___

/*

 * API to portable hash table code.

 */

#include <stddef.h>

#include <stdio.h>

#include "jstypes.h"

#include "jscompat.h"



JS_BEGIN_EXTERN_C



typedef uint32 JSHashNumber;

typedef struct JSHashEntry JSHashEntry;

typedef struct JSHashTable JSHashTable;



#define JS_HASH_BITS 32

#define JS_GOLDEN_RATIO 0x9E3779B9U



typedef JSHashNumber (* JS_DLL_CALLBACK JSHashFunction)(const void *key);

typedef intN (* JS_DLL_CALLBACK JSHashComparator)(const void *v1, const void *v2);

typedef intN (* JS_DLL_CALLBACK JSHashEnumerator)(JSHashEntry *he, intN i, void *arg);



/* Flag bits in JSHashEnumerator's return value */

#define HT_ENUMERATE_NEXT       0       /* continue enumerating entries */

#define HT_ENUMERATE_STOP       1       /* stop enumerating entries */

#define HT_ENUMERATE_REMOVE     2       /* remove and free the current entry */

#define HT_ENUMERATE_UNHASH     4       /* just unhash the current entry */



typedef struct JSHashAllocOps {

    void *              (*allocTable)(void *pool, size_t size);

    void                (*freeTable)(void *pool, void *item);

    JSHashEntry *       (*allocEntry)(void *pool, const void *key);

    void                (*freeEntry)(void *pool, JSHashEntry *he, uintN flag);

} JSHashAllocOps;



#define HT_FREE_VALUE   0               /* just free the entry's value */

#define HT_FREE_ENTRY   1               /* free value and entire entry */



struct JSHashEntry {

    JSHashEntry         *next;          /* hash chain linkage */

    JSHashNumber        keyHash;        /* key hash function result */

    const void          *key;           /* ptr to opaque key */

    void                *value;         /* ptr to opaque value */

};



struct JSHashTable {

    JSHashEntry         **buckets;      /* vector of hash buckets */

    uint32              nentries;       /* number of entries in table */

    uint32              shift;          /* multiplicative hash shift */

    JSHashFunction      keyHash;        /* key hash function */

    JSHashComparator    keyCompare;     /* key comparison function */

    JSHashComparator    valueCompare;   /* value comparison function */

    JSHashAllocOps      *allocOps;      /* allocation operations */

    void                *allocPriv;     /* allocation private data */

#ifdef HASHMETER

    uint32              nlookups;       /* total number of lookups */

    uint32              nsteps;         /* number of hash chains traversed */

    uint32              ngrows;         /* number of table expansions */

    uint32              nshrinks;       /* number of table contractions */

#endif

};



/*

 * Create a new hash table.

 * If allocOps is null, use default allocator ops built on top of malloc().

 */

extern JS_PUBLIC_API(JSHashTable *)

JS_NewHashTable(uint32 n, JSHashFunction keyHash,

                JSHashComparator keyCompare, JSHashComparator valueCompare,

                JSHashAllocOps *allocOps, void *allocPriv);



extern JS_PUBLIC_API(void)

JS_HashTableDestroy(JSHashTable *ht);



/* Low level access methods */

extern JS_PUBLIC_API(JSHashEntry **)

JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key);



extern JS_PUBLIC_API(JSHashEntry *)

JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, JSHashNumber keyHash,

                   const void *key, void *value);



extern JS_PUBLIC_API(void)

JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he);



/* Higher level access methods */

extern JS_PUBLIC_API(JSHashEntry *)

JS_HashTableAdd(JSHashTable *ht, const void *key, void *value);



extern JS_PUBLIC_API(JSBool)

JS_HashTableRemove(JSHashTable *ht, const void *key);



extern JS_PUBLIC_API(intN)

JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg);



extern JS_PUBLIC_API(void *)

JS_HashTableLookup(JSHashTable *ht, const void *key);



extern JS_PUBLIC_API(intN)

JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp);



/* General-purpose C string hash function. */

extern JS_PUBLIC_API(JSHashNumber)

JS_HashString(const void *key);



/* Stub function just returns v1 == v2 */

extern JS_PUBLIC_API(intN)

JS_CompareValues(const void *v1, const void *v2);



JS_END_EXTERN_C



#endif /* jshash_h___ */

 

**** End of jshash.h ****

 

**** Start of jsify.pl ****

 

#!/usr/local/bin/perl



# This script modifies C code to use the hijacked NSPR routines that are

# now baked into the JavaScript engine rather than using the NSPR

# routines that they were based on, i.e. types like PRArenaPool are changed

# to JSArenaPool.

#

# This script was used in 9/98 to facilitate the incorporation of some NSPR

# code into the JS engine so as to minimize dependency on NSPR.

#



# Command-line: jsify.pl [options] [filename]*

#

# Options:

#  -r         Reverse direction of transformation, i.e. JS ==> NSPR2

#  -outdir    Directory in which to place output files





# NSPR2 symbols that will be modified to JS symbols, e.g.

# PRArena <==> JSArena



@NSPR_symbols = (

"PRArena",

"PRArenaPool",

"PRArenaStats",

"PR_ARENAMETER",

"PR_ARENA_",

"PR_ARENA_ALIGN",

"PR_ARENA_ALLOCATE",

"PR_ARENA_CONST_ALIGN_MASK",

"PR_ARENA_DEFAULT_ALIGN",

"PR_ARENA_DESTROY",

"PR_ARENA_GROW",

"PR_ARENA_MARK",

"PR_ARENA_RELEASE",



"PR_smprintf",

"PR_smprintf_free",

"PR_snprintf",

"PR_sprintf_append",

"PR_sscanf",

"PR_sxprintf",

"PR_vsmprintf",

"PR_vsnprintf",

"PR_vsprintf_append",

"PR_vsxprintf",



"PRCList",

"PRCListStr",

"PRCLists",



"PRDestroyEventProc",

"PREvent",

"PREventFunProc",

"PREventQueue",

"PRHandleEventProc",

"PR_PostEvent",

"PR_PostSynchronousEvent",

"PR_ProcessPendingEvents",

"PR_CreateEventQueue",

"PR_DequeueEvent",

"PR_DestroyEvent",

"PR_DestroyEventQueue",

"PR_EventAvailable",

"PR_EventLoop",

"PR_GetEvent",

"PR_GetEventOwner",

"PR_GetEventQueueMonitor",

"PR_GetEventQueueSelectFD",

"PR_GetMainEventQueue",

"PR_HandleEvent",

"PR_InitEvent",

"PR_ENTER_EVENT_QUEUE_MONITOR",

"PR_EXIT_EVENT_QUEUE_MONITOR",

"PR_MapEvents",

"PR_RevokeEvents",



"PR_cnvtf",

"PR_dtoa",

"PR_strtod",



"PRFileDesc",



"PR_HASH_BITS",

"PR_GOLDEN_RATIO",

"PRHashAllocOps",

"PRHashComparator",

"PRHashEntry",

"PRHashEnumerator",

"PRHashFunction",

"PRHashNumber",

"PRHashTable",

"PR_HashString",

"PR_HashTableAdd",

"PR_HashTableDestroy",

"PR_HashTableDump",

"PR_HashTableEnumerateEntries",

"PR_HashTableLookup",

"PR_HashTableRawAdd",

"PR_HashTableRawLookup",

"PR_HashTableRawRemove",

"PR_HashTableRemove",



"PRBool",

"PRFloat64",

"PRInt16",

"PRInt32",

"PRInt64",

"PRInt8",

"PRIntn",

"PRUint16",

"PRUint32",

"PRUint64",

"PRUint8",

"PRUintn",

"PRPtrDiff",

"PRPtrdiff",

"PRUptrdiff",

"PRUword",

"PRWord",

"PRPackedBool",

"PRSize",

"PRStatus",

"pruword",

"prword",

"prword_t",



"PR_ALIGN_OF_DOUBLE",

"PR_ALIGN_OF_FLOAT",

"PR_ALIGN_OF_INT",

"PR_ALIGN_OF_INT64",

"PR_ALIGN_OF_LONG",

"PR_ALIGN_OF_POINTER",

"PR_ALIGN_OF_SHORT",

"PR_ALIGN_OF_WORD",

"PR_BITS_PER_BYTE",

"PR_BITS_PER_BYTE_LOG2",

"PR_BITS_PER_DOUBLE",

"PR_BITS_PER_DOUBLE_LOG2",

"PR_BITS_PER_FLOAT",

"PR_BITS_PER_FLOAT_LOG2",

"PR_BITS_PER_INT",

"PR_BITS_PER_INT64",

"PR_BITS_PER_INT64_LOG2",

"PR_BITS_PER_INT_LOG2",

"PR_BITS_PER_LONG",

"PR_BITS_PER_LONG_LOG2",

"PR_BITS_PER_SHORT",

"PR_BITS_PER_SHORT_LOG2",

"PR_BITS_PER_WORD",

"PR_BITS_PER_WORD_LOG2",

"PR_BYTES_PER_BYTE",

"PR_BYTES_PER_DOUBLE",

"PR_BYTES_PER_DWORD",

"PR_BYTES_PER_DWORD_LOG2",

"PR_BYTES_PER_FLOAT",

"PR_BYTES_PER_INT",

"PR_BYTES_PER_INT64",

"PR_BYTES_PER_LONG",

"PR_BYTES_PER_SHORT",

"PR_BYTES_PER_WORD",

"PR_BYTES_PER_WORD_LOG2",



"PRSegment",

"PRSegmentAccess",

"PRStuffFunc",

"PRThread",



"PR_APPEND_LINK",



"PR_ASSERT",



"PR_ATOMIC_DWORD_LOAD",

"PR_ATOMIC_DWORD_STORE",



"PR_Abort",



"PR_ArenaAllocate",

"PR_ArenaCountAllocation",

"PR_ArenaCountGrowth",

"PR_ArenaCountInplaceGrowth",

"PR_ArenaCountRelease",

"PR_ArenaCountRetract",

"PR_ArenaFinish",

"PR_ArenaGrow",

"PR_ArenaRelease",

"PR_CompactArenaPool",

"PR_DumpArenaStats",

"PR_FinishArenaPool",

"PR_FreeArenaPool",

"PR_InitArenaPool",



"PR_Assert",



"PR_AttachThread",



"PR_BEGIN_EXTERN_C",

"PR_BEGIN_MACRO",



"PR_BIT",

"PR_BITMASK",



"PR_BUFFER_OVERFLOW_ERROR",



"PR_CALLBACK",

"PR_CALLBACK_DECL",

"PR_CALLOC",

"PR_CEILING_LOG2",

"PR_CLEAR_ARENA",

"PR_CLEAR_BIT",

"PR_CLEAR_UNUSED",

"PR_CLIST_IS_EMPTY",

"PR_COUNT_ARENA",

"PR_CURRENT_THREAD",



"PR_GetSegmentAccess",

"PR_GetSegmentSize",

"PR_GetSegmentVaddr",

"PR_GrowSegment",

"PR_DestroySegment",

"PR_MapSegment",

"PR_NewSegment",

"PR_Segment",

"PR_Seg",

"PR_SEGMENT_NONE",

"PR_SEGMENT_RDONLY",

"PR_SEGMENT_RDWR",



"PR_Calloc",

"PR_CeilingLog2",

"PR_CompareStrings",

"PR_CompareValues",

"PR_DELETE",

"PR_END_EXTERN_C",

"PR_END_MACRO",

"PR_ENUMERATE_STOP",

"PR_FAILURE",

"PR_FALSE",

"PR_FLOOR_LOG2",

"PR_FREEIF",

"PR_FREE_PATTERN",

"PR_FloorLog2",

"PR_FormatTime",

"PR_Free",



"PR_GetEnv",

"PR_GetError",

"PR_INIT_ARENA_POOL",

"PR_INIT_CLIST",

"PR_INIT_STATIC_CLIST",

"PR_INLINE",

"PR_INSERT_AFTER",

"PR_INSERT_BEFORE",

"PR_INSERT_LINK",

"PR_INT32",

"PR_INTERVAL_NO_TIMEOUT",

"PR_INTERVAL_NO_WAIT",

"PR_Init",

"PR_LIST_HEAD",

"PR_LIST_TAIL",

"PR_LOG",

"PR_LOGGING",

"PR_LOG_ALWAYS",

"PR_LOG_BEGIN",

"PR_LOG_DEBUG",

"PR_LOG_DEFINE",

"PR_LOG_END",

"PR_LOG_ERROR",

"PR_LOG_MAX",

"PR_LOG_MIN",

"PR_LOG_NONE",

"PR_LOG_NOTICE",

"PR_LOG_TEST",

"PR_LOG_WARN",

"PR_LOG_WARNING",

"PR_LogFlush",

"PR_LogPrint",

"PR_MALLOC",

"PR_MAX",

"PR_MD_calloc",

"PR_MD_free",

"PR_MD_malloc",

"PR_MD_realloc",

"PR_MIN",

"PR_Malloc",

"PR_NEW",

"PR_NEWZAP",

"PR_NEXT_LINK",

"PR_NOT_REACHED",

"PR_NewCondVar",

"PR_NewHashTable",

"PR_NewLogModule",

"PR_PREV_LINK",

"PR_PUBLIC_API",

"PR_PUBLIC_DATA",

"PR_RANGE_ERROR",

"PR_REALLOC",

"PR_REMOVE_AND_INIT_LINK",

"PR_REMOVE_LINK",

"PR_ROUNDUP",

"PR_Realloc",



"PR_SET_BIT",

"PR_STATIC_CALLBACK",

"PR_SUCCESS",

"PR_SetError",

"PR_SetLogBuffering",

"PR_SetLogFile",



"PR_TEST_BIT",

"PR_TRUE",

"PR_UINT32",

"PR_UPTRDIFF",



"prarena_h___",

"prbit_h___",

"prclist_h___",

"prdtoa_h___",

"prlog_h___",

"prlong_h___",

"prmacos_h___",

"prmem_h___",

"prprf_h___",

"prtypes_h___",



"prarena",

"prbit",

"prbitmap_t",

"prclist",

"prcpucfg",

"prdtoa",

"prhash",

"plhash",

"prlong",

"prmacos",

"prmem",

"prosdep",

"protypes",

"prprf",

"prtypes"

);



while ($ARGV[0] =~ /^-/) {

    if ($ARGV[0] eq "-r") {

	shift;

	$reverse_conversion = 1;

    } elsif ($ARGV[0] eq "-outdir") {

	shift;

	$outdir = shift;

    }

}



# Given an NSPR symbol compute the JS equivalent or

# vice-versa

sub subst {

    local ($replacement);

    local ($sym) = @_;

    

    $replacement = substr($sym,0,2) eq "pr" ? "js" : "JS";

    $replacement .= substr($sym, 2);

    return $replacement;

}



# Build the regular expression that will convert between the NSPR

# types and the JS types

if ($reverse_conversion) {

    die "Not implemented yet";

} else {

    foreach $sym (@NSPR_symbols) {

	$regexp .= $sym . "|"

    }

    # Get rid of the last "!"

    chop $regexp;



    # Replace PR* with JS* and replace pr* with js*

    $regexp = 's/(^|\\W)(' . $regexp . ')/$1 . &subst($2)/eg';

#    print $regexp;

}



# Pre-compile a little subroutine to perform the regexp substitution

# between NSPR types and JS types

eval('sub convert_from_NSPR {($line) = @_; $line =~ ' . $regexp . ';}');



sub convert_mallocs {

    ($line) = @_;

    $line =~ s/PR_MALLOC/malloc/g;

    $line =~ s/PR_REALLOC/realloc/g;

    $line =~ s/PR_FREE/free/g;

    return $line;

}



sub convert_includes {

    ($line) = @_;

    if ($line !~ /include/) {

	return $line;

    }



    if ($line =~ /prlog\.h/) {

	$line = '#include "jsutil.h"'. " /* Added by JSIFY */\n";

    } elsif ($line =~ /plhash\.h/) {

	$line = '#include "jshash.h"'. " /* Added by JSIFY */\n";

    } elsif ($line =~ /plarena\.h/) {

	$line = '#include "jsarena.h"'. " /* Added by JSIFY */\n";

    } elsif ($line =~ /prmem\.h/) {

	$line  = "";

    } elsif ($line =~ /jsmsg\.def/) {

	$line  = '#include "js.msg"' . "\n";

    } elsif ($line =~ /shellmsg\.def/) {

	$line  = '#include "jsshell.msg"' . "\n";

    } elsif ($line =~ /jsopcode\.def/) {

	$line  = '#include "jsopcode.tbl"' . "\n";

    }

    return $line;

}



sub convert_declarations {

    ($line) = @_;

    $line =~ s/PR_EXTERN/JS_EXTERN_API/g;

    $line =~ s/PR_IMPLEMENT_DATA/JS_EXPORT_DATA/g;

    $line =~ s/PR_IMPLEMENT/JS_EXPORT_API/g;

    $line =~ s/PR_CALLBACK/JS_DLL_CALLBACK/g;

    $line =~ s/PR_STATIC_CALLBACK/JS_STATIC_DLL_CALLBACK/g;

    $line =~ s/PR_IMPORT/JS_IMPORT/g;

    $line =~ s/PR_PUBLIC_API/JS_EXPORT_API/g;

    $line =~ s/PR_PUBLIC_DATA/JS_EXPORT_DATA/g;

    return $line;

}    



sub convert_long_long_macros {

    ($line) = @_;

    $line =~ s/\b(LL_)/JSLL_/g;

    return $line;

}    



sub convert_asserts {

    ($line) = @_;

    $line =~ s/\bPR_ASSERT/JS_ASSERT/g;

    return $line;

}



while ($#ARGV >= 0) {

    $infile = shift;



    # Change filename, e.g. prtime.h to jsprtime.h, except for legacy

    # files that start with 'prmj', like prmjtime.h.

    $outfile = $infile;

    if ($infile !~ /^prmj/) {

	$outfile =~ s/^pr/js/;

	$outfile =~ s/^pl/js/;

    }

    

    if ($outdir) {

	$outfile = $outdir . '/' . $outfile;

    }	



    if ($infile eq $outfile) {

	die "Error: refuse to overwrite $outfile, use -outdir option."

    }

    die "Can't open $infile" if !open(INFILE, "<$infile");

    die "Can't open $outfile for writing" if !open(OUTFILE, ">$outfile");



    while (<INFILE>) {

	$line = $_;

	

	#Get rid of #include "prlog.h"

	&convert_includes($line);



	# Rename PR_EXTERN, PR_IMPORT, etc.

	&convert_declarations($line);



	# Convert from PR_MALLOC to malloc, etc.

	&convert_mallocs($line);

	

	# Convert from PR_ASSERT to JS_ASSERT

#	&convert_asserts($line);



	# Convert from, e.g. PRArena to JSPRArena

	&convert_from_NSPR($line);



	# Change LL_* macros to JSLL_*

	&convert_long_long_macros($line);

	

	print OUTFILE $line;

    }

}

 

**** End of jsify.pl ****

 

**** Start of jsinterp.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JavaScript bytecode interpreter.

 */

#include "jsstddef.h"

#include <stdio.h>

#include <string.h>

#include <math.h>

#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



// DREAMWEAVER: inserted this declaration (see usage below).

//extern JSBool JSCallNativeSafe (JSNative native, JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

//		      jsval *rval);





void

js_FlushPropertyCache(JSContext *cx)

{

    JSPropertyCache *cache;



    cache = &cx->runtime->propertyCache;

    if (cache->empty)

        return;

    memset(cache->table, 0, sizeof cache->table);

    cache->empty = JS_TRUE;

    cache->flushes++;

}



void

js_FlushPropertyCacheByProp(JSContext *cx, JSProperty *prop)

{

    JSPropertyCache *cache;

    JSBool empty;

    JSPropertyCacheEntry *end, *pce, entry;

    JSProperty *pce_prop;



    cache = &cx->runtime->propertyCache;

    if (cache->empty)

        return;



    empty = JS_TRUE;

    end = &cache->table[PROPERTY_CACHE_SIZE];

    for (pce = &cache->table[0]; pce < end; pce++) {

        PCE_LOAD(cache, pce, entry);

        pce_prop = PCE_PROPERTY(entry);

        if (pce_prop) {

            if (pce_prop == prop) {

                PCE_OBJECT(entry) = NULL;

                PCE_PROPERTY(entry) = NULL;

                PCE_STORE(cache, pce, entry);

            } else {

                empty = JS_FALSE;

            }

        }

    }

    cache->empty = empty;

}



/*

 * Class for for/in loop property iterator objects.

 */

static void prop_iterator_finalize(JSContext *cx, JSObject *obj); // Forward declare



static JSClass prop_iterator_class = {

    "PropertyIterator",

    JSCLASS_HAS_PRIVATE,

    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,

    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   prop_iterator_finalize,

    JSCLASS_NO_OPTIONAL_MEMBERS

};



#if 1 //SRJMOD

#define JSSLOT_ITER_STATE    (JSSLOT_FREE(&prop_iterator_class))

#define JSSLOT_ITR_STATE    (JSSLOT_FREE(&prop_iterator_class))

#else

#define JSSLOT_ITER_STATE   (JSSLOT_START)

#define JSSLOT_ITR_STATE    (JSSLOT_START)

#endif



//#if XP_MAC //SRJMOD

static EnumDestroyerProcPtr FwEnumDestroyer;



void js_SetFwEnumDestroyer(EnumDestroyerProcPtr destroyer)

{

	FwEnumDestroyer = destroyer;

}

//#else

//extern JSBool FwEnumDestroyer(jsval iter_state);

//#endif



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 (iter_state != JSVAL_NULL && !JSVAL_IS_PRIMITIVE(iteratee)) {

		jsval private = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);

		if (private != JSVAL_NULL) {

			long classflags = (long)JSVAL_TO_PRIVATE(private);

			if (classflags & JSCLASS_FW_ENUM) {

				// destroy it a special way

				//#if XP_MAC

					if((*FwEnumDestroyer)(iter_state))

				//#else

				//	if (FwEnumDestroyer(iter_state))

				//#endif

					{

						// This is VERY IMPORTANT!  otherwise we'll crash! (ajf 12/4/2002)

    					js_RemoveRoot(cx->runtime, &obj->slots[JSSLOT_PARENT]);

						return;

					}

			}

		}

        OBJ_ENUMERATE(cx, JSVAL_TO_OBJECT(iteratee), JSENUMERATE_DESTROY,

                      &iter_state, NULL);

    }

    js_RemoveRoot(cx->runtime, &obj->slots[JSSLOT_PARENT]);

}



/*

 * 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

 * 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 PUSH_NUMBER(cx, d)                                                    \

    JS_BEGIN_MACRO                                                            \

        sp++;                                                                 \

        STORE_NUMBER(cx, -1, d);                                              \

    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_OBJECT(v) && v != JSVAL_NULL) {                          \

            obj = JSVAL_TO_OBJECT(v);                                         \

        } else {                                                              \

            SAVE_SP(fp);                                                      \

            obj = js_ValueToNonNullObject(cx, v);                             \

            if (!obj) {                                                       \

                ok = JS_FALSE;                                                \

                goto out;                                                     \

            }                                                                 \

        }                                                                     \

    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



// DREAMWEAVER snewman 3/28/01: added prototype to eliminate compiler

// warning.

JS_FRIEND_API(jsval *)

js_AllocRawStack(JSContext *cx, uintN nslots, void **markp);



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;

}



// DREAMWEAVER snewman 3/28/01: added prototype to eliminate compiler

// warning.

JS_FRIEND_API(void)

js_FreeRawStack(JSContext *cx, void *mark);



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->spbase && fp->script) {

#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;

    }



    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)

{

    JSFunction *fun;

    JSStackFrame *fp;



    JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass);

    fun = (JSFunction *) JS_GetPrivate(cx, obj);

    for (fp = cx->fp; fp; fp = fp->down) {

        /* Find most recent non-native function frame. */

        if (fp->fun && !fp->fun->native) {

            if (fp->fun == fun) {

                JS_ASSERT((uintN)JSVAL_TO_INT(id) < fp->fun->nargs);

                *vp = fp->argv[JSVAL_TO_INT(id)];

            }

            return JS_TRUE;

        }

    }

    return JS_TRUE;

}



JSBool

js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSFunction *fun;

    JSStackFrame *fp;



    JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass);

    fun = (JSFunction *) JS_GetPrivate(cx, obj);

    for (fp = cx->fp; fp; fp = fp->down) {

        /* Find most recent non-native function frame. */

        if (fp->fun && !fp->fun->native) {

            if (fp->fun == fun) {

                JS_ASSERT((uintN)JSVAL_TO_INT(id) < fp->fun->nargs);

                fp->argv[JSVAL_TO_INT(id)] = *vp;

            }

            return JS_TRUE;

        }

    }

    return JS_TRUE;

}



JSBool

js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSFunction *fun;

    JSStackFrame *fp;

    jsint slot;



    JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass);

    fun = (JSFunction *) JS_GetPrivate(cx, obj);

    for (fp = cx->fp; fp; fp = fp->down) {

        /* Find most recent non-native function frame. */

        if (fp->fun && !fp->fun->native) {

            if (fp->fun == fun) {

                slot = JSVAL_TO_INT(id);

                JS_ASSERT((uintN)slot < fp->fun->nvars);

                if ((uintN)slot < fp->nvars)

                    *vp = fp->vars[slot];

            }

            return JS_TRUE;

        }

    }

    return JS_TRUE;

}



JSBool

js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSFunction *fun;

    JSStackFrame *fp;

    jsint slot;



    JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass);

    fun = (JSFunction *) JS_GetPrivate(cx, obj);

    for (fp = cx->fp; fp; fp = fp->down) {

        /* Find most recent non-native function frame. */

        if (fp->fun && !fp->fun->native) {

            if (fp->fun == fun) {

                slot = JSVAL_TO_INT(id);

                JS_ASSERT((uintN)slot < fp->fun->nvars);

                if ((uintN)slot < fp->nvars)

                    fp->vars[slot] = *vp;

            }

            return JS_TRUE;

        }

    }

    return JS_TRUE;

}



/*

 * Compute the 'this' parameter and store it in frame as frame.thisp.

 * Activation objects ("Call" objects not created with "new Call()", i.e.,

 * "Call" objects that have private data) may not be referred to by 'this',

 * as dictated by ECMA.

 *

 * N.B.: fp->argv must be set, fp->argv[-1] the nominal 'this' paramter as

 * a jsval, and fp->argv[-2] must be the callee object reference, usually a

 * function object.  Also, fp->constructing must be set if we are preparing

 * for a constructor call.

 */

static JSBool

ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp)

{

    JSObject *parent;



    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->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->constructing);

        parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fp->argv[-2]));

        if (!parent) {

            thisp = cx->globalObject;

        } else {

            /* walk up to find the top-level object */

            thisp = parent;

            while ((parent = OBJ_GET_PARENT(cx, thisp)) != NULL)

                thisp = parent;

        }

    }

    fp->thisp = thisp;

    fp->argv[-1] = OBJECT_TO_JSVAL(thisp);

    return JS_TRUE;

}



/*

 * 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)

{

    JSStackFrame *fp, frame;

    jsval *sp, *newsp, *limit;

    jsval *vp, v;

    JSObject *funobj, *parent, *thisp;

    JSClass *clasp;

    JSObjectOps *ops;

    JSBool ok;

    JSNative native;

    JSFunction *fun;

    JSScript *script;

    uintN minargs, nvars;

    void *mark;

    intN nslots, nalloc, surplus;

    JSInterpreterHook hook;

    void *hookData;



    /* Reach under args and this to find the callee on the stack. */

    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 must 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.constructing so we may test it anywhere below.

     */

    vp = sp - (2 + argc);

    v = *vp;

    frame.rval = JSVAL_VOID;

    frame.constructing = (JSPackedBool)(flags & JSINVOKE_CONSTRUCT);



    /* A callee must be an object reference. */

    if (JSVAL_IS_PRIMITIVE(v))

        goto bad;

    funobj = JSVAL_TO_OBJECT(v);



    /* Load callee parent and this parameter for later. */

    parent = OBJ_GET_PARENT(cx, funobj);

    thisp = JSVAL_TO_OBJECT(vp[1]);



    clasp = OBJ_GET_CLASS(cx, funobj);

    if (clasp != &js_FunctionClass) {

        /* Function is inlined, all other classes use object ops. */

        ops = funobj->map->ops;



        /*

         * XXX

         * 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 (cx->version == JSVERSION_1_2 ||

            ((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)) {

                funobj = JSVAL_TO_OBJECT(v);

                parent = OBJ_GET_PARENT(cx, funobj);

                fun = (JSFunction *) JS_GetPrivate(cx, funobj);



                /* Make vp refer to funobj to keep it available as argv[-2]. */

                *vp = v;

                goto have_fun;

            }

        }

        fun = NULL;

        script = NULL;

        minargs = nvars = 0;



        /* Try a call or construct native object op, using fun as fallback. */

        native = frame.constructing ? ops->construct : ops->call;

        if (!native)

            goto bad;

    } else {

        /* Get private data and set derived locals from it. */

        fun = (JSFunction *) JS_GetPrivate(cx, funobj);

have_fun:

        native = fun->native;

        script = fun->script;

        minargs = fun->nargs + fun->extra;

        nvars = fun->nvars;



        /* Handle bound method special case. */

        if (fun->flags & JSFUN_BOUND_METHOD)

            thisp = parent;

    }



    /* Initialize frame except for varobj, thisp, sp, spbase, and scopeChain. */

    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.sharpDepth = 0;

    frame.sharpArray = NULL;

    frame.overrides = 0;

    frame.special = 0;

    frame.dormantNext = NULL;



    /* Compute the 'this' parameter and store it in frame as frame.thisp. */

    ok = ComputeThis(cx, thisp, &frame);

    if (!ok)

        goto out2;



    /* From here on, control must flow through label out: to return. */

    cx->fp = &frame;

    mark = JS_ARENA_MARK(&cx->stackPool);



    /* Init these now in case we goto out before first hook call. */

    hook = cx->runtime->callHook;

    hookData = NULL;



    /* Check for missing arguments expected by the function. */

    nslots = (intN)((argc < minargs) ? minargs - argc : 0);

    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. */

            surplus = (jsval *)mark - sp;

            JS_ASSERT(surplus >= 0);

            nalloc -= surplus;

        }



        /* Check whether we have enough space in the caller's frame. */

        if (nalloc > 0) {

            /* Need space for actuals plus missing formals minus surplus. */

            newsp = js_AllocRawStack(cx, (uintN)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 == (uintN)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. */

        while (--nslots >= 0)

            PUSH(JSVAL_VOID);

    }



    /* Now allocate stack space for local variables. */

    nslots = (intN)frame.nvars;

    if (nslots) {

        surplus = (intN)((jsval *)cx->stackPool.current->avail - frame.vars);

        if (surplus < nslots) {

            newsp = js_AllocRawStack(cx, (uintN)nslots, 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. */

        while (--nslots >= 0)

            PUSH(JSVAL_VOID);

    }



    /* Store the current sp in frame before calling fun. */

    frame.spbase = sp;

    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) {

        /* 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, &v);

    } else {

        /* fun might be onerror trying to report a syntax error in itself. */

        frame.scopeChain = NULL;

        ok = JS_TRUE;

    }



out:

    if (hook && hookData)

        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



    /* Pop everything off the stack and restore cx->fp. */

    JS_ARENA_RELEASE(&cx->stackPool, mark);

    cx->fp = fp;



out2:

    /* 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, frame.constructing);

    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]);

    fp->sp = sp;

    ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL);

    if (ok) {

        RESTORE_SP(fp);

        *rval = POP_OPND();

    }



    js_FreeStack(cx, mark);

out:

    fp->sp = oldsp;

    if (oldfp != fp)

        cx->fp = oldfp;



    return ok;

}



JSBool

js_Execute(JSContext *cx, JSObject *chain, JSScript *script,

           JSStackFrame *down, uintN special, jsval *result)

{

    JSStackFrame *oldfp, frame;

    JSObject *obj, *tmp;

    JSBool ok;

    JSInterpreterHook hook;

    void *hookData;



    hook = cx->runtime->executeHook;

    hookData = 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 = frame.nvars = 0;

        frame.argv = 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 = frame.sp;

    frame.sharpDepth = 0;

    frame.constructing = JS_FALSE;

    frame.overrides = 0;

    frame.special = (uint8) special;

    frame.dormantNext = 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);



    ok = js_Interpret(cx, result);



    if (hook && hookData)

        hook(cx, &frame, JS_FALSE, &ok, hookData);

    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,

                                             js_IdToValue(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,

                                             js_IdToValue(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.  Those

         * 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,

                      JSBool *foundp)

{

    JSObject *obj2;

    JSProperty *prop;

    JSBool ok;

    uintN oldAttrs, report;

    JSBool isFunction;

    jsval value;



    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))

        return JS_FALSE;

    *foundp = (prop != NULL);

    if (!prop)

        return JS_TRUE;

    ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs);

    OBJ_DROP_PROPERTY(cx, obj2, prop);

    if (!ok)

        return JS_FALSE;



    /* 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 or setter -- or if it is, insist that the

         * property being replaced is not permanent.

         */

        if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))

            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))

            return JS_FALSE;

        isFunction = JSVAL_IS_FUNCTION(cx, value);

    }

    return JS_ReportErrorFlagsAndNumber(cx, report,

                                        js_GetErrorMessage, NULL,

                                        JSMSG_REDECLARED_VAR,

                                        isFunction

                                        ? js_function_str

                                        : (oldAttrs & JSPROP_READONLY)

                                        ? js_const_str

                                        : js_var_str,

                                        ATOM_BYTES((JSAtom *)id));

}



#if !defined XP_PC || !defined _MSC_VER || _MSC_VER > 800

#define MAX_INTERP_LEVEL 1000

#else

#define MAX_INTERP_LEVEL 30

#endif



#define MAX_INLINE_CALL_COUNT 1000



// DREAMWEAVER: added JavaScript profiling - see jsprofiler.h

#ifdef DREAMWEAVER_JAVASCRIPT_PROFILING

JSBool

js_Interpret(JSContext *cx, jsval *result)

{

	return jsprofiler_Interpret(cx, result);

}



JSBool

js_InterpretInternal(JSContext *cx, jsval *result)

#else

JSBool

js_Interpret(JSContext *cx, jsval *result)

#endif

{

    JSRuntime *rt;

    JSStackFrame *fp;

    JSScript *script;

    uintN inlineCallCount;

    JSObject *obj, *obj2, *proto, *parent;

    JSVersion currentVersion, originalVersion;

    JSBranchCallback onbranch;

    JSBool ok, cond;

    jsint depth, len;

    jsval *sp, *newsp;

    void *mark;

    jsbytecode *pc, *pc2, *endpc;

    JSOp op, op2;

    JSCodeSpec *cs;

    JSAtom *atom;

    uintN argc, slot, attrs;

    jsval *vp, lval, rval, ltmp, rtmp;

    jsid id;

    JSObject *withobj, *origobj, *propobj;

    JSIdArray *ida;

    jsval iter_state;

    JSProperty *prop;

    JSScopeProperty *sprop;

    JSString *str, *str2, *str3;

    size_t length, length2, length3;

    jschar *chars;

    jsint i, j;

    jsdouble d, d2;

    JSClass *clasp, *funclasp;

    JSFunction *fun;

    JSType type;

#ifdef DEBUG

    FILE *tracefp;

#endif

#if JS_HAS_SWITCH_STATEMENT

    jsint low, high, off, npairs;

    JSBool match;

#endif

#if JS_HAS_GETTER_SETTER

    JSPropertyOp getter, setter;

#endif



    if (cx->interpLevel == MAX_INTERP_LEVEL) {

        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);

        return JS_FALSE;

    }

    cx->interpLevel++;

    *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. */

    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.

     */

    onbranch = cx->branchCallback;

    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



    pc = script->code;

    endpc = pc + script->length;

    len = -1;



    /*

     * Allocate operand and pc stack slots for the script's worst-case depth.

     */

    depth = (jsint) script->depth;

    newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark);

    if (!newsp) {

        ok = JS_FALSE;

        goto out;

    }

    sp = newsp + depth;

    fp->spbase = sp;

    SAVE_SP(fp);



    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(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 != NULL) {

                        fprintf(tracefp, "%s %s",

                                (n == 0) ? "  inputs:" : ",",

                                JS_GetStringBytes(str));

                    }

                }

                putc('\n', tracefp);

            }

        }

#endif



        {

            JSTrapHandler handler = rt->interruptHandler;

            if (handler) {

                switch (handler(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:;

                }

            }

        }



        switch (op) {

          case JSOP_NOP:

            break;



          case JSOP_GROUP:

            obj = NULL;

            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 pc stack

             * generating pcs, as they're not needed for the current use of

             * preserving the top-of-stack return value when popping scopes

             * while returning from catch blocks.

             */

            ltmp = sp[-1];

            sp[-1] = sp[-2];

            sp[-2] = ltmp;

            break;



          case JSOP_POPV:

            *result = POP_OPND();

            break;



          case JSOP_ENTERWITH:

            rval = FETCH_OPND(-1);

            VALUE_TO_OBJECT(cx, rval, obj);

            withobj = js_NewObject(cx, &js_WithClass, obj, fp->scopeChain);

            if (!withobj)

                goto out;

            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_RETURN:

            CHECK_BRANCH(-1);

            fp->rval = POP_OPND();

            if (inlineCallCount)

          inline_return:

            {

                JSInlineFrame *ifp = (JSInlineFrame *) fp;

                void *hookData = ifp->hookData;



                if (hookData)

                    cx->runtime->callHook(cx, fp, JS_FALSE, &ok, hookData);

#if JS_HAS_ARGS_OBJECT

                if (fp->argsobj)

                    ok &= js_PutArgsObject(cx, fp);

#endif



                /* 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;



                /* Restore version and version control variable. */

                if (script->version != currentVersion) {

                    currentVersion = script->version;

                    JS_SetVersion(cx, currentVersion);

                }



                /* 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;



#if !JS_BUG_SHORT_CIRCUIT

          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;

#endif



          case JSOP_TOOBJECT:

            SAVE_SP(fp);

            ok = js_ValueToObject(cx, FETCH_OPND(-1), &obj);

            if (!ok)

                goto out;

            STORE_OPND(-1, OBJECT_TO_JSVAL(obj));

            break;



#define FETCH_ELEMENT_ID(n, id)                                               \

    JS_BEGIN_MACRO                                                            \

        /* If the index is not a jsint, atomize it. */                        \

        id = (jsid) FETCH_OPND(n);                                            \

        if (JSVAL_IS_INT(id)) {                                               \

            atom = NULL;                                                      \

        } else {                                                              \

            SAVE_SP(fp);                                                      \

            atom = js_ValueToStringAtom(cx, (jsval)id);                       \

            if (!atom) {                                                      \

                ok = JS_FALSE;                                                \

                goto out;                                                     \

            }                                                                 \

            id = (jsid)atom;                                                  \

        }                                                                     \

    JS_END_MACRO



#define POP_ELEMENT_ID(id)                                                    \

    JS_BEGIN_MACRO                                                            \

        FETCH_ELEMENT_ID(-1, id);                                             \

        sp--;                                                                 \

    JS_END_MACRO



#if JS_HAS_IN_OPERATOR

          case JSOP_IN:

            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;

            }

            sp--;

            obj = JSVAL_TO_OBJECT(rval);

            FETCH_ELEMENT_ID(-1, id);

            ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);

            if (!ok)

                goto out;

            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   = (jsid)atom;

            i = -2;

            goto do_forinloop;



          case JSOP_FORNAME:

            atom = GET_ATOM(cx, script, pc);

            id   = (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;



            /* Is this the first iteration ? */

            if (JSVAL_IS_VOID(rval)) {

                /* Yes, create a new JSObject to hold the iterator state */

                propobj = js_NewObject(cx, &prop_iterator_class, NULL, obj);

                if (!propobj) {

                    ok = JS_FALSE;

                    goto out;

                }



                /*

                 * 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], NULL);

                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 = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, 0);

                if (!ok)

                    goto out;



				// SRJMOD from old JS

				propobj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(OBJ_GET_CLASS(cx, obj)->flags);



                /*

                 * 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);

                obj = JSVAL_TO_OBJECT(propobj->slots[JSSLOT_PARENT]);

                iter_state = propobj->slots[JSSLOT_ITER_STATE];

            }



          enum_next_property:

            /* Get the next jsid to be enumerated and store it in rval. */

            OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &rval);

            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;

                    goto end_forinloop;

                }



                ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, 0);

                propobj->slots[JSSLOT_ITER_STATE] = iter_state;

                if (!ok)

                    goto out;



                /* Stash private iteration state into iterator JSObject. */

                propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj);

                goto enum_next_property;

            }



            /* Skip properties not owned by obj, and leave next id in rval. */

            ok = OBJ_LOOKUP_PROPERTY(cx, origobj, rval, &obj2, &prop);

            if (!ok)

                goto out;

            if (prop) {

                OBJ_DROP_PROPERTY(cx, obj2, prop);



                /* Yes, don't enumerate again.  Go to the next property. */

                if (obj2 != obj)

                    goto enum_next_property;

            }



            /* Make sure rval is a string for uniformity and compatibility. */

            if (!JSVAL_IS_INT(rval)) {

                rval = ATOM_KEY((JSAtom *)rval);

            } else if (cx->version != JSVERSION_1_2) {

                str = js_NumberToString(cx, (jsdouble) JSVAL_TO_INT(rval));

                if (!str) {

                    ok = JS_FALSE;

                    goto out;

                }



                rval = STRING_TO_JSVAL(str);

            }



            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);



                /* Set the variable obj[id] to refer to rval. */

                ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);

                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                                                            \

        /* Pop the left part and resolve it to a non-null object. */          \

        lval = FETCH_OPND(n);                                                 \

        VALUE_TO_OBJECT(cx, 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_ELEMENT_ID(n, id);                                              \

        PROPERTY_OP(n-1, call);                                               \

    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.

 */

#define CACHED_GET(call)                                                      \

    JS_BEGIN_MACRO                                                            \

        if (!OBJ_IS_NATIVE(obj)) {                                            \

            ok = call;                                                        \

        } else {                                                              \

            JS_LOCK_OBJ(cx, obj);                                             \

            PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, prop);           \

            if (prop) {                                                       \

                JSScope *scope_ = OBJ_SCOPE(obj);                             \

                sprop = (JSScopeProperty *)prop;                              \

                sprop->nrefs++;                                               \

                slot = (uintN)sprop->slot;                                    \

                rval = (slot != SPROP_INVALID_SLOT)                           \

                       ? LOCKED_OBJ_GET_SLOT(obj, slot)                       \

                       : JSVAL_VOID;                                          \

                JS_UNLOCK_SCOPE(cx, scope_);                                  \

                ok = SPROP_GET(cx, sprop, obj, obj, &rval);                   \

                if (ok) {                                                     \

                    JS_LOCK_SCOPE(cx, scope_);                                \

                    sprop = js_DropScopeProperty(cx, scope_, sprop);          \

                    if (sprop && SPROP_HAS_VALID_SLOT(sprop))                 \

                        LOCKED_OBJ_SET_SLOT(obj, slot, rval);                 \

                    JS_UNLOCK_SCOPE(cx, scope_);                              \

                }                                                             \

            } else {                                                          \

                JS_UNLOCK_OBJ(cx, obj);                                       \

                ok = call;                                                    \

                /* No fill here: js_GetProperty fills the cache. */           \

            }                                                                 \

        }                                                                     \

    JS_END_MACRO



#if JS_BUG_SET_ENUMERATE

#define SET_ENUMERATE_ATTR(sprop) ((sprop)->attrs |= JSPROP_ENUMERATE)

#else

#define SET_ENUMERATE_ATTR(sprop) ((void)0)

#endif



#define CACHED_SET(call)                                                      \

    JS_BEGIN_MACRO                                                            \

        if (!OBJ_IS_NATIVE(obj)) {                                            \

            ok = call;                                                        \

        } else {                                                              \

            JS_LOCK_OBJ(cx, obj);                                             \

            PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, prop);           \

            if ((sprop = (JSScopeProperty *)prop) &&                          \

                !(sprop->attrs & JSPROP_READONLY)) {                          \

                JSScope *scope_ = OBJ_SCOPE(obj);                             \

                sprop->nrefs++;                                               \

                JS_UNLOCK_SCOPE(cx, scope_);                                  \

                ok = SPROP_SET(cx, sprop, obj, obj, &rval);                   \

                if (ok) {                                                     \

                    JS_LOCK_SCOPE(cx, scope_);                                \

                    sprop = js_DropScopeProperty(cx, scope_, sprop);          \

                    if (sprop && SPROP_HAS_VALID_SLOT(sprop)) {               \

                        LOCKED_OBJ_SET_SLOT(obj, sprop->slot, rval);          \

                        SET_ENUMERATE_ATTR(sprop);                            \

                        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



          case JSOP_SETCONST:

            obj = fp->varobj;

            atom = GET_ATOM(cx, script, pc);

            rval = FETCH_OPND(-1);

            ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, rval, NULL, NULL,

                                     JSPROP_ENUMERATE | JSPROP_PERMANENT |

                                     JSPROP_READONLY,

                                     NULL);

            if (!ok)

                goto out;

            STORE_OPND(-1, rval);

            break;



          case JSOP_BINDNAME:

            atom = GET_ATOM(cx, script, pc);

            SAVE_SP(fp);

            ok = js_FindVariable(cx, (jsid)atom, &obj, &obj2, &prop);

            if (!ok)

                goto out;

            OBJ_DROP_PROPERTY(cx, obj2, prop);

            PUSH_OPND(OBJECT_TO_JSVAL(obj));

            break;



          case JSOP_SETNAME:

            atom = GET_ATOM(cx, script, pc);

            id   = (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);

            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                                                            \

        i = i OP j;                                                           \

        sp--;                                                                 \

        STORE_NUMBER(cx, -1, i);                                              \

    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;



#ifdef XP_PC

#define COMPARE_DOUBLES(LVAL, OP, RVAL, IFNAN)                                \

    ((JSDOUBLE_IS_NaN(LVAL) || JSDOUBLE_IS_NaN(RVAL))                         \

     ? (IFNAN)                                                                \

     : (LVAL) OP (RVAL))

#else

#define COMPARE_DOUBLES(LVAL, OP, RVAL, IFNAN) ((LVAL) OP (RVAL))

#endif



#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 = COMPARE_DOUBLES(d, OP, d2, JS_FALSE);                  \

            }                                                                 \

        } else {                                                              \

            VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_NUMBER, &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 = COMPARE_DOUBLES(d, OP, d2, JS_FALSE);                  \

            }                                                                 \

        }                                                                     \

        sp--;                                                                 \

        STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));                               \

    JS_END_MACRO



#define EQUALITY_OP(OP, IFNAN)                                                \

    JS_BEGIN_MACRO                                                            \

        rval = FETCH_OPND(-1);                                                \

        lval = FETCH_OPND(-2);                                                \

        ltmp = JSVAL_TAG(lval);                                               \

        rtmp = JSVAL_TAG(rval);                                               \

        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 = COMPARE_DOUBLES(d, OP, d2, IFNAN);                     \

            } else {                                                          \

                /* 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, &lval);         \

                    ltmp = JSVAL_TAG(lval);                                   \

                } else if (rtmp == JSVAL_OBJECT) {                            \

                    VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &rval);         \

                    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 = COMPARE_DOUBLES(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, IFNAN)                                            \

    JS_BEGIN_MACRO                                                            \

        rval = FETCH_OPND(-1);                                                \

        lval = FETCH_OPND(-2);                                                \

        ltmp = JSVAL_TAG(lval);                                               \

        rtmp = JSVAL_TAG(rval);                                               \

        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 = COMPARE_DOUBLES(d, OP, d2, IFNAN);                     \

            } else {                                                          \

                cond = lval OP rval;                                          \

            }                                                                 \

        } else {                                                              \

            if (ltmp == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) {                 \

                d  = *JSVAL_TO_DOUBLE(lval);                                  \

                d2 = JSVAL_TO_INT(rval);                                      \

                cond = COMPARE_DOUBLES(d, OP, d2, IFNAN);                     \

            } else if (JSVAL_IS_INT(lval) && rtmp == JSVAL_DOUBLE) {          \

                d  = JSVAL_TO_INT(lval);                                      \

                d2 = *JSVAL_TO_DOUBLE(rval);                                  \

                cond = COMPARE_DOUBLES(d, OP, d2, IFNAN);                     \

            } else {                                                          \

                cond = lval OP rval;                                          \

            }                                                                 \

        }                                                                     \

        sp--;                                                                 \

        STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));                               \

    JS_END_MACRO



          case JSOP_NEW_EQ:

            NEW_EQUALITY_OP(==, JS_FALSE);

            break;



          case JSOP_NEW_NE:

            NEW_EQUALITY_OP(!=, JS_TRUE);

            break;



#if JS_HAS_SWITCH_STATEMENT

          case JSOP_CASE:

            NEW_EQUALITY_OP(==, JS_FALSE);

            (void) POP();

            if (cond) {

                len = GET_JUMP_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);

            VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &ltmp);

            VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &rtmp);

            if ((cond = JSVAL_IS_STRING(ltmp)) || JSVAL_IS_STRING(rtmp)) {

                if (cond) {

                    str = JSVAL_TO_STRING(ltmp);

                    SAVE_SP(fp);

                    ok = (str2 = js_ValueToString(cx, rtmp)) != NULL;

                } else {

                    str2 = JSVAL_TO_STRING(rtmp);

                    SAVE_SP(fp);

                    ok = (str = js_ValueToString(cx, ltmp)) != NULL;

                }

                if (!ok)

                    goto out;

                if ((length = str->length) == 0) {

                    str3 = str2;

                } else if ((length2 = str2->length) == 0) {

                    str3 = str;

                } else {

                    length3 = length + length2;

                    chars = JS_malloc(cx, (length3 + 1) * sizeof(jschar));

                    if (!chars) {

                        ok = JS_FALSE;

                        goto out;

                    }

                    js_strncpy(chars, str->chars, length);

                    js_strncpy(chars + length, str2->chars, length2);

                    chars[length3] = 0;

                    str3 = js_NewString(cx, chars, length3, 0);

                    if (!str3) {

                        JS_free(cx, chars);

                        ok = JS_FALSE;

                        goto out;

                    }

                }

                sp--;

                STORE_OPND(-1, STRING_TO_JSVAL(str3));

            } 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) {

#ifdef XP_PC

                /* 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 {

#ifdef XP_PC

              /* 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

            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)

            {

                SAVE_SP(fp);

                fun = js_ValueToFunction(cx, vp, JS_TRUE);

                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. */

                ok = OBJ_GET_PROPERTY(cx, obj2,

                                      (jsid)rt->atomState.classPrototypeAtom,

                                      &rval);

                if (!ok)

                    goto out;

                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);

            SAVE_SP(fp);

            ok = js_Invoke(cx, argc, JSINVOKE_CONSTRUCT);

            RESTORE_SP(fp);

            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 || !JSVERSION_IS_ECMA(cx->version)) {

                    *vp = OBJECT_TO_JSVAL(obj);

                    break;

                }

                /* native [[Construct]] returning primitive is error */

                str = js_ValueToString(cx, rval);

                if (str) {

                    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                         JSMSG_BAD_NEW_RESULT,

                                         JS_GetStringBytes(str));

                }

                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   = (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   = (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 = POP_OPND();

            type = JS_TypeOfValue(cx, rval);

            atom = rt->atomState.typeAtoms[type];

            str  = ATOM_TO_STRING(atom);

            PUSH_OPND(STRING_TO_JSVAL(str));

            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   = (jsid)atom;



            SAVE_SP(fp);

            ok = js_FindProperty(cx, id, &obj, &obj2, &prop);

            if (!ok)

                goto out;

            if (!prop) {

                js_ReportIsNotDefined(cx, ATOM_BYTES(atom));

                ok = JS_FALSE;

                goto out;

            }



            OBJ_DROP_PROPERTY(cx, obj2, prop);

            lval = OBJECT_TO_JSVAL(obj);

            goto do_incop;



          case JSOP_INCPROP:

          case JSOP_DECPROP:

          case JSOP_PROPINC:

          case JSOP_PROPDEC:

            atom = GET_ATOM(cx, script, pc);

            id   = (jsid)atom;

            lval = POP_OPND();

            goto do_incop;



          case JSOP_INCELEM:

          case JSOP_DECELEM:

          case JSOP_ELEMINC:

          case JSOP_ELEMDEC:

            POP_ELEMENT_ID(id);

            lval = POP_OPND();



          do_incop:

            VALUE_TO_OBJECT(cx, lval, obj);



            /* 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.

 */

#define NONINT_INCREMENT_OP()                                                 \

    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;                                                 \

            }                                                                 \

            (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



                NONINT_INCREMENT_OP();

            }



            CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));

            if (!ok)

                goto out;

            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 = (uintN)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();

            *vp = rval;

            PUSH_OPND(rtmp);

            break;



          case JSOP_GETPROP:

            /* Get an immediate atom naming the property. */

            atom = GET_ATOM(cx, script, pc);

            id   = (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   = (jsid)atom;

            PROPERTY_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)));

            sp--;

            STORE_OPND(-1, rval);

            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);

            break;



          case JSOP_ENUMELEM:

            /* Funky: the value to set is under the [obj, id] pair. */

            FETCH_ELEMENT_ID(-1, id);

            lval = FETCH_OPND(-2);

            VALUE_TO_OBJECT(cx, lval, obj);

            rval = FETCH_OPND(-3);

            SAVE_SP(fp);

            CACHED_SET(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);



// Dreamweaver: if we're profiling, inline functions are invisible

// to the profiler, so disable them.

#ifndef DREAMWEAVER_JAVASCRIPT_PROFILING



            if (JSVAL_IS_FUNCTION(cx, lval) &&

                (obj = JSVAL_TO_OBJECT(lval),

                 fun = (JSFunction *) JS_GetPrivate(cx, obj),

                 !fun->native &&

                 fun->flags == 0 &&

                 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);

                    return JS_FALSE;

                }



#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->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 = ComputeThis(cx, JSVAL_TO_OBJECT(vp[1]), &newifp->frame);

                if (!ok) {

                    js_FreeRawStack(cx, newmark);

                    goto bad_inline_call;

                }



                /* 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);

                }



                /* Switch to new version if necessary. */

                if (script->version != currentVersion) {

                    currentVersion = script->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;

            }



#endif /* DREAMWEAVER_JAVASCRIPT_PROFILING */



            ok = js_Invoke(cx, argc, 0);

            RESTORE_SP(fp);

            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 op.  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,

                           CACHED_GET(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);

            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   = (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;

                }

                js_ReportIsNotDefined(cx, ATOM_BYTES(atom));

                ok = JS_FALSE;

                goto out;

            }



            /* 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))

                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);

            break;



          case JSOP_NUMBER:

          case JSOP_STRING:

            atom = GET_ATOM(cx, script, pc);

            PUSH_OPND(ATOM_KEY(atom));

            break;



          case JSOP_OBJECT:

            atom = GET_ATOM(cx, script, pc);

            rval = ATOM_KEY(atom);

            JS_ASSERT(JSVAL_IS_OBJECT(rval));

            PUSH_OPND(rval);

            obj = NULL;

            break;



          case JSOP_ZERO:

            PUSH_OPND(JSVAL_ZERO);

            break;



          case JSOP_ONE:

            PUSH_OPND(JSVAL_ONE);

            break;



          case JSOP_NULL:

            PUSH_OPND(JSVAL_NULL);

            break;



          case JSOP_THIS:

            PUSH_OPND(OBJECT_TO_JSVAL(fp->thisp));

            break;



          case JSOP_FALSE:

            PUSH_OPND(JSVAL_FALSE);

            break;



          case JSOP_TRUE:

            PUSH_OPND(JSVAL_TRUE);

            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_DEFAULT ||

                cx->version >= 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_CONDSWITCH:

            break;



#endif /* JS_HAS_SWITCH_STATEMENT */



#if JS_HAS_EXPORT_IMPORT

          case JSOP_EXPORTALL:

            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;



          case JSOP_EXPORTNAME:

            atom = GET_ATOM(cx, script, pc);

            id   = (jsid)atom;

            obj  = fp->varobj;

            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;

            break;



          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   = (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);

                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:;

            }

            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 = (jsid) INT_TO_JSVAL(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 ComputeThis, 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 = (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:

            obj = NULL;

            slot = GET_ARGNO(pc);

            JS_ASSERT(slot < fp->fun->nargs);

            PUSH_OPND(fp->argv[slot]);

            break;



          case JSOP_SETARG:

            obj = NULL;

            slot = GET_ARGNO(pc);

            JS_ASSERT(slot < fp->fun->nargs);

            vp = &fp->argv[slot];

            GC_POKE(cx, *vp);

            *vp = FETCH_OPND(-1);

            break;



          case JSOP_GETVAR:

            obj = NULL;

            slot = GET_VARNO(pc);

            JS_ASSERT(slot < fp->fun->nvars);

            PUSH_OPND(fp->vars[slot]);

            break;



          case JSOP_SETVAR:

            obj = NULL;

            slot = GET_VARNO(pc);

            JS_ASSERT(slot < fp->fun->nvars);

            vp = &fp->vars[slot];

            GC_POKE(cx, *vp);

            *vp = FETCH_OPND(-1);

            break;



          case JSOP_DEFCONST:

          case JSOP_DEFVAR:

          {

            JSBool defined;



            atom = GET_ATOM(cx, script, pc);

            obj = fp->varobj;

            attrs = JSPROP_ENUMERATE;

            if (!(fp->special & JSFRAME_EVAL))

                attrs |= JSPROP_PERMANENT;

            if (op == JSOP_DEFCONST)

                attrs |= JSPROP_READONLY;



            /* Lookup id in order to check for redeclaration problems. */

            id = (jsid)atom;

            ok = js_CheckRedeclaration(cx, obj, id, attrs, &defined);

            if (!ok)

                goto out;



            /* Bind a variable only if it's not yet defined. */

            if (!defined) {

                ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,

                                         attrs, NULL);

                if (!ok)

                    goto out;

            }

            break;

          }



          case JSOP_DEFFUN:

          {

            uintN flags;



            atom = GET_ATOM(cx, script, pc);

            obj = ATOM_TO_OBJECT(atom);

            fun = (JSFunction *) JS_GetPrivate(cx, obj);

            id = (jsid) fun->atom;



            /*

             * We must be at top-level (default "box", either function body or

             * global) scope, not inside a with or other compound statement 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 variables object from

             * scope chain head.  Hence the two assignments to parent 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.

             */

            parent = fp->scopeChain;

            if (OBJ_GET_PARENT(cx, obj) != parent) {

                obj = js_CloneFunctionObject(cx, obj, parent);

                if (!obj) {

                    ok = JS_FALSE;

                    goto out;

                }

            }



            /* Load function flags that are also property attributes. */

            flags = fun->flags & (JSFUN_GETTER | JSFUN_SETTER);

            attrs = flags | JSPROP_ENUMERATE;



            /*

             * 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;

            ok = js_CheckRedeclaration(cx, parent, id, attrs, &cond);

            if (!ok)

                goto out;



            ok = OBJ_DEFINE_PROPERTY(cx, parent, id,

                                     flags ? JSVAL_VOID : OBJECT_TO_JSVAL(obj),

                                     (flags & JSFUN_GETTER)

                                     ? (JSPropertyOp) obj

                                     : NULL,

                                     (flags & JSFUN_SETTER)

                                     ? (JSPropertyOp) obj

                                     : NULL,

                                     attrs,

                                     NULL);

            if (!ok)

                goto out;

            break;

          }



#if JS_HAS_LEXICAL_CLOSURE

          case JSOP_DEFLOCALFUN:

            /*

             * 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.

             */

            pc2 = pc;

            slot = GET_VARNO(pc2);

            pc2 += VARNO_LEN;

            atom = GET_ATOM(cx, script, pc2);

            obj = ATOM_TO_OBJECT(atom);

            fun = (JSFunction *) JS_GetPrivate(cx, obj);



            parent = fp->scopeChain;

            if (OBJ_GET_PARENT(cx, obj) != parent) {

                obj = js_CloneFunctionObject(cx, obj, parent);

                if (!obj) {

                    ok = JS_FALSE;

                    goto out;

                }

            }

            fp->vars[slot] = OBJECT_TO_JSVAL(obj);

            break;



          case JSOP_ANONFUNOBJ:

            /* Push the specified function object literal. */

            atom = GET_ATOM(cx, script, pc);

            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) {

                obj = js_CloneFunctionObject(cx, obj, parent);

                if (!obj) {

                    ok = JS_FALSE;

                    goto out;

                }

            }

            PUSH_OPND(OBJECT_TO_JSVAL(obj));

            break;



          case JSOP_NAMEDFUNOBJ:

            /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */

            atom = GET_ATOM(cx, script, pc);

            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);

            parent = js_ConstructObject(cx, &js_ObjectClass, NULL,

                                        fp->scopeChain);

            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].

             */

            obj = js_CloneFunctionObject(cx, JSVAL_TO_OBJECT(rval), parent);

            if (!obj) {

                ok = JS_FALSE;

                goto out;

            }



            /*

             * 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);

            ok = OBJ_DEFINE_PROPERTY(cx, parent, (jsid)fun->atom,

                                     attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj),

                                     (attrs & JSFUN_GETTER)

                                     ? (JSPropertyOp) obj

                                     : NULL,

                                     (attrs & JSFUN_SETTER)

                                     ? (JSPropertyOp) obj

                                     : NULL,

                                     attrs |

                                     JSPROP_ENUMERATE | JSPROP_PERMANENT |

                                     JSPROP_READONLY,

                                     NULL);

            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));

            break;



          case JSOP_CLOSURE:

            /*

             * 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.

             */

            atom = GET_ATOM(cx, script, pc);

            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.

             */

            parent = fp->scopeChain;

            if (OBJ_GET_PARENT(cx, obj) != parent) {

                obj = js_CloneFunctionObject(cx, obj, parent);

                if (!obj) {

                    ok = JS_FALSE;

                    goto out;

                }

            }



            /*

             * 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);

            ok = OBJ_DEFINE_PROPERTY(cx, fp->varobj, (jsid)fun->atom,

                                     attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj),

                                     (attrs & JSFUN_GETTER)

                                     ? (JSPropertyOp) obj

                                     : NULL,

                                     (attrs & JSFUN_SETTER)

                                     ? (JSPropertyOp) obj

                                     : NULL,

                                     attrs | JSPROP_ENUMERATE,

                                     NULL);

            if (!ok) {

                cx->newborn[GCX_OBJECT] = NULL;

                goto out;

            }

            break;

#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   = (jsid)atom;

                i = -1;

                rval = FETCH_OPND(i);

                goto gs_pop_lval;



              case JSOP_SETELEM:

                rval = FETCH_OPND(-1);

                i = -2;

                FETCH_ELEMENT_ID(i, id);

              gs_pop_lval:

                lval = FETCH_OPND(i-1);

                VALUE_TO_OBJECT(cx, lval, obj);

                break;



#if JS_HAS_INITIALIZERS

              case JSOP_INITPROP:

                JS_ASSERT(sp - fp->spbase >= 2);

                i = -1;

                rval = FETCH_OPND(i);

                atom = GET_ATOM(cx, script, pc);

                id   = (jsid)atom;

                goto gs_get_lval;



              case JSOP_INITELEM:

                JS_ASSERT(sp - fp->spbase >= 3);

                rval = FETCH_OPND(-1);

                i = -2;

                FETCH_ELEMENT_ID(i, id);

              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);

            }



            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.

             */

            if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs))

                return JS_FALSE;



            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;



            /* Check for a readonly or permanent property of the same name. */

            ok = js_CheckRedeclaration(cx, obj, id, attrs, &cond);

            if (!ok)

                goto out;



            ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter,

                                     attrs, NULL);

            if (!ok)

                goto out;



            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   = (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);



            /* Set the property named by obj[id] to rval. */

            ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);

            if (!ok)

                goto out;

            sp += i;

            break;



#if JS_HAS_SHARP_VARS

          case JSOP_DEFSHARP:

            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 = (jsid) INT_TO_JSVAL(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 = (jsid) INT_TO_JSVAL(i);

            obj = fp->sharpArray;

            if (!obj) {

                rval = JSVAL_VOID;

            } else {

                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;

            break;



          case JSOP_GOSUB:

            i = PTRDIFF(pc, script->main, jsbytecode) + len;

            len = GET_JUMP_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);

            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;



          case JSOP_INITCATCHVAR:

            /* Pop the property's value into rval. */

            JS_ASSERT(sp - fp->spbase >= 2);

            rval = POP_OPND();



            /* Get the immediate catch variable name into id. */

            atom = GET_ATOM(cx, script, pc);

            id   = (jsid)atom;



            /* Find the object being initialized at top of stack. */

            lval = FETCH_OPND(-1);

            JS_ASSERT(JSVAL_IS_OBJECT(lval));

            obj = JSVAL_TO_OBJECT(lval);



            /* 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;

            break;

#endif /* JS_HAS_EXCEPTIONS */



#if JS_HAS_INSTANCEOF

          case JSOP_INSTANCEOF:

            rval = FETCH_OPND(-1);

            if (JSVAL_IS_PRIMITIVE(rval)) {

                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;

            }

            obj = JSVAL_TO_OBJECT(rval);

            lval = FETCH_OPND(-2);

            cond = JS_FALSE;

            if (obj->map->ops->hasInstance) {

                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) {

                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:;

                }

            }

            break;

          }

#endif /* JS_HAS_DEBUGGER_KEYWORD */



          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;



            ndefs = cs->ndefs;

            if (ndefs) {

                SAVE_SP(fp);

                for (n = -ndefs; n < 0; n++) {

                    str = js_DecompileValueGenerator(cx, n, sp[n], NULL);

                    if (str) {

                        fprintf(tracefp, "%s %s",

                                (n == 0) ? "  output:" : ",",

                                JS_GetStringBytes(str));

                    }

                }

                putc('\n', tracefp);

            }

        }

#endif

    }

out:



#if JS_HAS_EXCEPTIONS

    /*

     * Has an exception been raised?

     */

    if (!ok && cx->throwing) {

        /*

         * Call debugger throw hook if set (XXX thread safety?).

         */

        JSTrapHandler handler = rt->throwHook;

        if (handler) {

            switch (handler(cx, script, pc, &rval, rt->throwHookData)) {

              case JSTRAP_ERROR:

                cx->throwing = JS_FALSE;

                goto no_catch;

              case JSTRAP_CONTINUE:

                cx->throwing = JS_FALSE;

                ok = JS_TRUE;

                goto advance_pc;

              case JSTRAP_RETURN:

                ok = JS_TRUE;

                cx->throwing = JS_FALSE;

                fp->rval = rval;

                goto no_catch;

              case JSTRAP_THROW:

                cx->exception = rval;

              default:;

            }

        }



        /*

         * Look for a try block within this frame that can catch the exception.

         */

        JSSCRIPT_FIND_CATCH_START(script, pc, pc);

        if (pc) {

            len = 0;

            cx->throwing = JS_FALSE;    /* caught */

            ok = JS_TRUE;

            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)

        goto inline_return;



    /*

     * Reset sp before freeing stack slots, because our caller may GC soon.

     * Restore the previous frame's execution state.

     */

    fp->sp = fp->spbase;

    js_FreeRawStack(cx, mark);

    if (currentVersion != originalVersion)

        JS_SetVersion(cx, originalVersion);

    cx->interpLevel--;

    return ok;

}

 

**** End of jsinterp.c ****

 

**** Start of jsinterp.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsinterp_h___

#define jsinterp_h___

/*

 * JS interpreter interface.

 */

#include "jsprvtd.h"

#include "jspubtd.h"



JS_BEGIN_EXTERN_C



/*

 * JS stack frame, allocated on the C stack.

 */

struct JSStackFrame {

    JSObject        *callobj;       /* lazily created Call object */

    JSObject        *argsobj;       /* lazily created arguments object */

    JSObject        *varobj;        /* variables object, where vars go */

    JSScript        *script;        /* script being interpreted */

    JSFunction      *fun;           /* function being called or null */

    JSObject        *thisp;         /* "this" pointer if in method */

    uintN           argc;           /* actual argument count */

    jsval           *argv;          /* base of argument stack slots */

    jsval           rval;           /* function return value */

    uintN           nvars;          /* local variable count */

    jsval           *vars;          /* base of variable stack slots */

    JSStackFrame    *down;          /* previous frame */

    void            *annotation;    /* used by Java security */

    JSObject        *scopeChain;    /* scope chain */

    jsbytecode      *pc;            /* program counter */

    jsval           *sp;            /* stack pointer */

    jsval           *spbase;        /* operand stack base */

    uintN           sharpDepth;     /* array/object initializer depth */

    JSObject        *sharpArray;    /* scope for #n= initializer vars */

    JSPackedBool    constructing;   /* true if called via new operator */

    uint8           overrides;      /* bit-set of overridden Call properties */

    uint8           special;        /* special frame type flags, see below */

    JSPackedBool    reserved;       /* reserved for future use */

    JSStackFrame    *dormantNext;   /* next dormant frame chain */

};



typedef struct JSInlineFrame {

    JSStackFrame    frame;          /* base struct */

    void            *mark;          /* mark before inline frame */

    void            *hookData;      /* debugger call hook data */

} JSInlineFrame;



/* JS special stack frame flags. */

#define JSFRAME_DEBUGGER    0x1     /* frame for JS_EvaluateInStackFrame */

#define JSFRAME_EVAL        0x2     /* frame for obj_eval */



/*

 * Property cache for quickened get/set property opcodes.

 */

#define PROPERTY_CACHE_LOG2     10

#define PROPERTY_CACHE_SIZE     JS_BIT(PROPERTY_CACHE_LOG2)

#define PROPERTY_CACHE_MASK     JS_BITMASK(PROPERTY_CACHE_LOG2)



#define PROPERTY_CACHE_HASH(obj, id) \

    ((((jsuword)(obj) >> JSVAL_TAGBITS) ^ (jsuword)(id)) & PROPERTY_CACHE_MASK)



#ifdef JS_THREADSAFE



#if HAVE_ATOMIC_DWORD_ACCESS



#define PCE_LOAD(cache, pce, entry)     JS_ATOMIC_DWORD_LOAD(pce, entry)

#define PCE_STORE(cache, pce, entry)    JS_ATOMIC_DWORD_STORE(pce, entry)



#else  /* !HAVE_ATOMIC_DWORD_ACCESS */



#define PCE_LOAD(cache, pce, entry)                                           \

    JS_BEGIN_MACRO                                                            \

	uint32 _prefills;                                                     \

	uint32 _fills = (cache)->fills;                                       \

	do {                                                                  \

	    /* Load until cache->fills is stable (see FILL macro below). */   \

	    _prefills = _fills;                                               \

	    (entry) = *(pce);                                                 \

	} while ((_fills = (cache)->fills) != _prefills);                     \

    JS_END_MACRO



#define PCE_STORE(cache, pce, entry)                                          \

    JS_BEGIN_MACRO                                                            \

	do {                                                                  \

	    /* Store until no racing collider stores half or all of pce. */   \

	    *(pce) = (entry);                                                 \

	} while (PCE_OBJECT(*pce) != PCE_OBJECT(entry) ||                     \

		 PCE_PROPERTY(*pce) != PCE_PROPERTY(entry));                  \

    JS_END_MACRO



#endif /* !HAVE_ATOMIC_DWORD_ACCESS */



#else  /* !JS_THREADSAFE */



#define PCE_LOAD(cache, pce, entry)     ((entry) = *(pce))

#define PCE_STORE(cache, pce, entry)    (*(pce) = (entry))



#endif /* !JS_THREADSAFE */



typedef union JSPropertyCacheEntry {

    struct {

	JSObject    *object;    /* weak link to object */

	JSProperty  *property;  /* weak link to property, or not-found id */

    } s;

#ifdef HAVE_ATOMIC_DWORD_ACCESS

    prdword align;

#endif

} JSPropertyCacheEntry;



/* These may be called in lvalue or rvalue position. */

#define PCE_OBJECT(entry)       ((entry).s.object)

#define PCE_PROPERTY(entry)     ((entry).s.property)



typedef struct JSPropertyCache {

    JSPropertyCacheEntry table[PROPERTY_CACHE_SIZE];

    JSBool               empty;

    uint32               fills;

    uint32               recycles;

    uint32               tests;

    uint32               misses;

    uint32               flushes;

} JSPropertyCache;



#define PROPERTY_CACHE_FILL(cx, cache, obj, id, prop)                         \

    JS_BEGIN_MACRO                                                            \

	uintN _hashIndex = (uintN)PROPERTY_CACHE_HASH(obj, id);               \

	JSPropertyCache *_cache = (cache);                                    \

	JSPropertyCacheEntry *_pce = &_cache->table[_hashIndex];              \

	JSPropertyCacheEntry _entry;                                          \

	JSProperty *_pce_prop;                                                \

	PCE_LOAD(_cache, _pce, _entry);                                       \

	_pce_prop = PCE_PROPERTY(_entry);                                     \

	if (_pce_prop && _pce_prop != prop)                                   \

	    _cache->recycles++;                                               \

	PCE_OBJECT(_entry) = obj;                                             \

	PCE_PROPERTY(_entry) = prop;                                          \

	_cache->empty = JS_FALSE;                                             \

	_cache->fills++;                                                      \

	PCE_STORE(_cache, _pce, _entry);                                      \

    JS_END_MACRO



#define PROPERTY_CACHE_TEST(cache, obj, id, prop)                             \

    JS_BEGIN_MACRO                                                            \

	uintN _hashIndex = (uintN)PROPERTY_CACHE_HASH(obj, id);               \

	JSPropertyCache *_cache = (cache);                                    \

	JSPropertyCacheEntry *_pce = &_cache->table[_hashIndex];              \

	JSPropertyCacheEntry _entry;                                          \

	JSProperty *_pce_prop;                                                \

	PCE_LOAD(_cache, _pce, _entry);                                       \

	_pce_prop = PCE_PROPERTY(_entry);                                     \

	_cache->tests++;                                                      \

	if (_pce_prop &&                                                      \

	    sym_id(((JSScopeProperty *)_pce_prop)->symbols) == id &&          \

	    PCE_OBJECT(_entry) == obj) {                                      \

	    prop = _pce_prop;                                                 \

	} else {                                                              \

	    _cache->misses++;                                                 \

	    prop = NULL;                                                      \

	}                                                                     \

    JS_END_MACRO



extern JS_PUBLIC_API(void)

js_FlushPropertyCache(JSContext *cx);



extern void

js_FlushPropertyCacheByProp(JSContext *cx, JSProperty *prop);



extern JS_FRIEND_API(jsval *)

js_AllocStack(JSContext *cx, uintN nslots, void **markp);



extern JS_FRIEND_API(void)

js_FreeStack(JSContext *cx, void *mark);



extern JSBool

js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp);



extern JSBool

js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp);



extern JSBool

js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp);



extern JSBool

js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp);



/*

 * NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp

 * is non-null).

 */

extern JS_FRIEND_API(JSBool)

js_Invoke(JSContext *cx, uintN argc, uintN flags);



/*

 * Consolidated js_Invoke flags.  NB: JSINVOKE_CONSTRUCT must be 1 or JS_TRUE

 * (see js_Invoke's initialization of frame.constructing).

 */

#define JSINVOKE_CONSTRUCT      0x1     /* construct object rather than call */

#define JSINVOKE_INTERNAL       0x2     /* internal call, not from a script */



/*

 * "Internal" calls may come from C or C++ code using a JSContext on which no

 * JS is running (!cx->fp), so they may need to push a dummy JSStackFrame.

 */

#define js_InternalCall(cx,obj,fval,argc,argv,rval)                           \

    js_InternalInvoke(cx, obj, fval, 0, argc, argv, rval)



#define js_InternalConstruct(cx,obj,fval,argc,argv,rval)                      \

    js_InternalInvoke(cx, obj, fval, JSINVOKE_CONSTRUCT, argc, argv, rval)



extern JSBool

js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,

                  uintN argc, jsval *argv, jsval *rval);



extern JSBool

js_Execute(JSContext *cx, JSObject *chain, JSScript *script,

           JSStackFrame *down, uintN flags, jsval *result);



extern JSBool

js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,

                      JSBool *foundp);



extern JSBool

js_Interpret(JSContext *cx, jsval *result);



//#if XP_MAC //AJFMOD

typedef JSBool (*EnumDestroyerProcPtr)(jsval iter_state);



extern void js_SetFwEnumDestroyer(EnumDestroyerProcPtr destroyer);



//#endif



JS_END_EXTERN_C



#endif /* jsinterp_h___ */

 

**** End of jsinterp.h ****

 

**** Start of jslibmath.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 *

 * This Original Code has been modified by IBM Corporation.

 * Modifications made by IBM described herein are

 * Copyright (c) International Business Machines

 * Corporation, 2000

 *

 * Modifications to Mozilla code or documentation

 * identified per MPL Section 3.3

 *

 * Date             Modified by     Description of modification

 * 04/20/2000       IBM Corp.      OS/2 VisualAge build.

 */



/*

 * By default all math calls go to fdlibm.  The defines for each platform

 * remap the math calls to native routines.

 */



#ifndef _LIBMATH_H

#define _LIBMATH_H



#include <math.h>





// DREAMWEAVER: inserted these three lines

#ifdef _WIN32

#include <float.h>

#endif





#include "jsconfig.h"

#ifdef MOZILLA_CLIENT

#include "platform.h"

#endif



/*

 * Define which platforms on which to use fdlibm.  Not used

 * by default since there can be problems with endian-ness and such.

 */



#if defined(_WIN32) && !defined(__MWERKS__)



// DREAMWEAVER: changed this from 1 to 0

#define JS_USE_FDLIBM_MATH 0



#elif defined(SUNOS4)

#define JS_USE_FDLIBM_MATH 1



#elif defined(IRIX)

#define JS_USE_FDLIBM_MATH 1



#elif defined(SOLARIS)

#define JS_USE_FDLIBM_MATH 1



#elif defined(HPUX)

#define JS_USE_FDLIBM_MATH 1



#elif defined(linux)

#define JS_USE_FDLIBM_MATH 1



#elif defined(OSF1)

/* Want to use some fdlibm functions but fdlibm broken on OSF1/alpha. */

#define JS_USE_FDLIBM_MATH 0



#elif defined(AIX)

#define JS_USE_FDLIBM_MATH 1



#elif defined(XP_OS2_VACPP)

#define JS_USE_FDLIBM_MATH 1



#else

#define JS_USE_FDLIBM_MATH 0

#endif



#if !JS_USE_FDLIBM_MATH



/*

 * Use system provided math routines.

 */



#define fd_acos acos

#define fd_asin asin

#define fd_atan atan

#define fd_atan2 atan2

#define fd_ceil ceil



// DREAMWEAVER: tweaked definition of fd_copysign

#ifdef XP_OS2_VACPP    /* OS2TODO */

#define fd_copysign 

#elif defined (_WIN32)

#define fd_copysign _copysign

#else

#define fd_copysign copysign

#endif



#define fd_cos cos

#define fd_exp exp

#define fd_fabs fabs

#define fd_floor floor

#define fd_fmod fmod

#define fd_log log

#define fd_pow pow

#define fd_sin sin

#define fd_sqrt sqrt

#define fd_tan tan



#else



/*

 * Use math routines in fdlibm.

 */



#undef __P

#ifdef __STDC__

#define __P(p)  p

#else

#define __P(p)  ()

#endif



#if defined _WIN32 || defined SUNOS4 



#define fd_acos acos

#define fd_asin asin

#define fd_atan atan

#define fd_cos cos

#define fd_sin sin

#define fd_tan tan

#define fd_exp exp

#define fd_log log

#define fd_sqrt sqrt

#define fd_ceil ceil

#define fd_fabs fabs

#define fd_floor floor

#define fd_fmod fmod



extern double fd_atan2 __P((double, double));

extern double fd_copysign __P((double, double));

extern double fd_pow __P((double, double));



#elif defined IRIX



#define fd_acos acos

#define fd_asin asin

#define fd_atan atan

#define fd_exp exp

#define fd_log log

#define fd_log10 log10

#define fd_sqrt sqrt

#define fd_fabs fabs

#define fd_floor floor

#define fd_fmod fmod



extern double fd_cos __P((double));

extern double fd_sin __P((double));

extern double fd_tan __P((double));

extern double fd_atan2 __P((double, double));

extern double fd_pow __P((double, double));

extern double fd_ceil __P((double));

extern double fd_copysign __P((double, double));



#elif defined SOLARIS



#define fd_atan atan

#define fd_cos cos

#define fd_sin sin

#define fd_tan tan

#define fd_exp exp

#define fd_sqrt sqrt

#define fd_ceil ceil

#define fd_fabs fabs

#define fd_floor floor

#define fd_fmod fmod



extern double fd_acos __P((double));

extern double fd_asin __P((double));

extern double fd_log __P((double));

extern double fd_atan2 __P((double, double));

extern double fd_pow __P((double, double));

extern double fd_copysign __P((double, double));



#elif defined HPUX



#define fd_cos cos

#define fd_sin sin

#define fd_exp exp

#define fd_sqrt sqrt

#define fd_fabs fabs

#define fd_floor floor

#define fd_fmod fmod



extern double fd_ceil __P((double));

extern double fd_acos __P((double));

extern double fd_log __P((double));

extern double fd_atan2 __P((double, double));

extern double fd_tan __P((double));

extern double fd_pow __P((double, double));

extern double fd_asin __P((double));

extern double fd_atan __P((double));

extern double fd_copysign __P((double, double));



#elif defined(linux)



#define fd_atan atan

#define fd_atan2 atan2

#define fd_ceil ceil

#define fd_cos cos

#define fd_fabs fabs

#define fd_floor floor

#define fd_fmod fmod

#define fd_sin sin

#define fd_sqrt sqrt

#define fd_tan tan

#define fd_copysign copysign



extern double fd_asin __P((double));

extern double fd_acos __P((double));

extern double fd_exp __P((double));

extern double fd_log __P((double));

extern double fd_pow __P((double, double));



#elif defined(OSF1)



#define fd_acos acos

#define fd_asin asin

#define fd_atan atan

#define fd_copysign copysign

#define fd_cos cos

#define fd_exp exp

#define fd_fabs fabs

#define fd_fmod fmod

#define fd_sin sin

#define fd_sqrt sqrt

#define fd_tan tan



extern double fd_atan2 __P((double, double));

extern double fd_ceil __P((double));

extern double fd_floor __P((double));

extern double fd_log __P((double));

extern double fd_pow __P((double, double));



#elif defined(AIX)



#define fd_acos acos

#define fd_asin asin

#define fd_atan2 atan2

#define fd_copysign copysign

#define fd_cos cos

#define fd_exp exp

#define fd_fabs fabs

#define fd_floor floor

#define fd_fmod fmod

#define fd_log log

#define fd_sin sin

#define fd_sqrt sqrt



extern double fd_atan __P((double));

extern double fd_ceil __P((double));

extern double fd_pow __P((double,double));

extern double fd_tan __P((double));



#elif defined(XP_OS2_VACPP)



#define fd_acos acos

#define fd_asin asin

#define fd_atan2 atan2

#define fd_cos cos

#define fd_exp exp

#define fd_fabs fabs

#define fd_floor floor

#define fd_fmod fmod

#define fd_log log

#define fd_sin sin

#define fd_sqrt sqrt

#define fd_atan atan

#define fd_ceil ceil

#define fd_pow pow

#define fd_tan tan



/* OS2 lacks copysign */

extern double fd_copysign __P((double, double));



#else /* other platform.. generic paranoid slow fdlibm */



extern double fd_acos __P((double));

extern double fd_asin __P((double));

extern double fd_atan __P((double));

extern double fd_cos __P((double));

extern double fd_sin __P((double));

extern double fd_tan __P((double));

 

extern double fd_exp __P((double));

extern double fd_log __P((double));

extern double fd_sqrt __P((double));



extern double fd_ceil __P((double));

extern double fd_fabs __P((double));

extern double fd_floor __P((double));

extern double fd_fmod __P((double, double));



extern double fd_atan2 __P((double, double));

extern double fd_pow __P((double, double));

extern double fd_copysign __P((double, double));



#endif



#endif /* JS_USE_FDLIBM_MATH */



#endif /* _LIBMATH_H */



 

**** End of jslibmath.h ****

 

**** Start of jslock.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifdef JS_THREADSAFE



/*

 * JS locking stubs.

 */

#include "jsstddef.h"

#include <stdlib.h>

#include "jspubtd.h"

#include "prthread.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jstypes.h"

#include "jsbit.h"

#include "jscntxt.h"

#include "jsscope.h"

#include "jspubtd.h"

#include "jslock.h"



#define ReadWord(W) (W)



#ifndef NSPR_LOCK



#include <memory.h>



static PRLock **global_locks;

static uint32 global_lock_count = 1;

static uint32 global_locks_log2 = 0;

static uint32 global_locks_mask = 0;



#define GLOBAL_LOCK_INDEX(id)   (((uint32)(id) >> 2) & global_locks_mask)



static void

js_LockGlobal(void *id)

{

    uint32 i = GLOBAL_LOCK_INDEX(id);

    PR_Lock(global_locks[i]);

}



static void

js_UnlockGlobal(void *id)

{

    uint32 i = GLOBAL_LOCK_INDEX(id);

    PR_Unlock(global_locks[i]);

}



/* Exclude Alpha NT. */

#if defined(_WIN32) && defined(_M_IX86)

#pragma warning( disable : 4035 )



static JS_INLINE int

js_CompareAndSwap(jsword *w, jsword ov, jsword nv)

{

    __asm {

        mov eax, ov

        mov ecx, nv

        mov ebx, w

        lock cmpxchg [ebx], ecx

        sete al

        and eax, 1h

    }

}



#elif defined(__GNUC__) && defined(__i386__)



/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */

static JS_INLINE int

js_CompareAndSwap(jsword *w, jsword ov, jsword nv)

{

    unsigned int res;



    __asm__ __volatile__ (

                          "lock\n"

                          "cmpxchgl %2, (%1)\n"

                          "sete %%al\n"

                          "andl $1, %%eax\n"

                          : "=a" (res)

                          : "r" (w), "r" (nv), "a" (ov)

                          : "cc", "memory");

    return (int)res;

}



#elif defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC)



static JS_INLINE int

js_CompareAndSwap(jsword *w, jsword ov, jsword nv)

{

#if defined(__GNUC__)

    unsigned int res;

    JS_ASSERT(ov != nv);

    asm volatile ("\

stbar\n\

cas [%1],%2,%3\n\

cmp %2,%3\n\

be,a 1f\n\

mov 1,%0\n\

mov 0,%0\n\

1:"

                  : "=r" (res)

                  : "r" (w), "r" (ov), "r" (nv));

    return (int)res;

#else /* !__GNUC__ */

    extern int compare_and_swap(jsword*, jsword, jsword);

    JS_ASSERT(ov != nv);

    return compare_and_swap(w, ov, nv);

#endif

}



#elif defined(AIX)



#include <sys/atomic_op.h>



static JS_INLINE int

js_CompareAndSwap(jsword *w, jsword ov, jsword nv)

{

    return !_check_lock((atomic_p)w, ov, nv);

}



#else



#error "Define NSPR_LOCK if your platform lacks a compare-and-swap instruction."



#endif /* arch-tests */



#endif /* !NSPR_LOCK */



jsword

js_CurrentThreadId()

{

    return CurrentThreadId();

}



void

js_InitLock(JSThinLock *tl)

{

#ifdef NSPR_LOCK

    tl->owner = 0;

    tl->fat = (JSFatLock*)JS_NEW_LOCK();

#else

    memset(tl, 0, sizeof(JSThinLock));

#endif

}



void

js_FinishLock(JSThinLock *tl)

{

#ifdef NSPR_LOCK

    tl->owner = 0xdeadbeef;

    if (tl->fat)

        JS_DESTROY_LOCK(((JSLock*)tl->fat));

#else

    JS_ASSERT(tl->owner == 0);

    JS_ASSERT(tl->fat == NULL);

#endif

}



static void js_Dequeue(JSThinLock *);



#ifdef DEBUG_SCOPE_COUNT



#include <stdio.h>

#include "jsdhash.h"



static FILE *logfp;

static JSDHashTable logtbl;



typedef struct logentry {

    JSDHashEntryStub stub;

    char             op;

    const char       *file;

    int              line;

} logentry;



static void

logit(JSScope *scope, char op, const char *file, int line)

{

    logentry *entry;



    if (!logfp) {

        logfp = fopen("/tmp/scope.log", "w");

        if (!logfp)

            return;

        setvbuf(logfp, NULL, _IONBF, 0);

    }

    fprintf(logfp, "%p %c %s %d\n", scope, op, file, line);



    if (!logtbl.entryStore &&

        !JS_DHashTableInit(&logtbl, JS_DHashGetStubOps(), NULL,

                           sizeof(logentry), 100)) {

        return;

    }

    entry = (logentry *) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_ADD);

    if (!entry)

        return;

    entry->stub.key = scope;

    entry->op = op;

    entry->file = file;

    entry->line = line;

}



void

js_unlog_scope(JSScope *scope)

{

    if (!logtbl.entryStore)

        return;

    (void) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_REMOVE);

}



# define LOGIT(scope,op) logit(scope, op, __FILE__, __LINE__)



#else



# define LOGIT(scope,op) /* nothing */



#endif /* DEBUG_SCOPE_COUNT */



/*

 * Return true if scope's ownercx, or the ownercx of a single-threaded scope

 * for which ownercx is waiting to become multi-threaded and shared, is cx.

 * That condition implies deadlock in ClaimScope if cx's thread were to wait

 * to share scope.

 *

 * (i) rt->gcLock held

 */

static JSBool

WillDeadlock(JSScope *scope, JSContext *cx)

{

    JSContext *ownercx;



    do {

        ownercx = scope->ownercx;

        if (ownercx == cx) {

            JS_RUNTIME_METER(cx->runtime, deadlocksAvoided);

            return JS_TRUE;

        }

    } while (ownercx && (scope = ownercx->scopeToShare) != NULL);

    return JS_FALSE;

}



/*

 * Make scope multi-threaded, i.e. share its ownership among contexts in rt

 * using a "thin" or (if necessary due to contention) "fat" lock.  Called only

 * from ClaimScope, immediately below, when we detect deadlock were we to wait

 * for scope's lock, because its ownercx is waiting on a scope owned by the

 * calling cx.

 *

 * (i) rt->gcLock held

 */

static void

ShareScope(JSRuntime *rt, JSScope *scope)

{

    JSScope **todop;



    if (scope->u.link) {

        for (todop = &rt->scopeSharingTodo; *todop != scope;

             todop = &(*todop)->u.link) {

            JS_ASSERT(*todop != NO_SCOPE_SHARING_TODO);

        }

        *todop = scope->u.link;

        JS_NOTIFY_ALL_CONDVAR(rt->scopeSharingDone);

    }

    js_InitLock(&scope->lock);

    if (scope == rt->setSlotScope) {

        /*

         * Nesting locks on another thread that's using scope->ownercx: give

         * the held lock a reentrancy count of 1 and set its lock.owner field

         * directly (no compare-and-swap needed while scope->ownercx is still

         * non-null).  See below in ClaimScope, before the ShareScope call,

         * for more on why this is necessary.

         *

         * If NSPR_LOCK is defined, we cannot deadlock holding rt->gcLock and

         * acquiring scope->lock.fat here, against another thread holding that

         * fat lock and trying to grab rt->gcLock.  This is because no other

         * thread can attempt to acquire scope->lock.fat until scope->ownercx

         * is null *and* our thread has released rt->gcLock, which interlocks

         * scope->ownercx's transition to null against tests of that member

         * in ClaimScope.

         */

        scope->lock.owner = scope->ownercx->thread;

#ifdef NSPR_LOCK

        JS_ACQUIRE_LOCK((JSLock*)scope->lock.fat);

#endif

        scope->u.count = 1;

    } else {

        scope->u.count = 0;

    }

    scope->ownercx = NULL;  /* NB: set last, after lock init */

    JS_RUNTIME_METER(rt, sharedScopes);

}



/*

 * Given a scope with apparently non-null ownercx different from cx, try to

 * set ownercx to cx, claiming exclusive (single-threaded) ownership of scope.

 * If we claim ownership, return true.  Otherwise, we wait for ownercx to be

 * set to null (indicating that scope is multi-threaded); or if waiting would

 * deadlock, we set ownercx to null ourselves via ShareScope.  In any case,

 * once ownercx is null we return false.

 */

static JSBool

ClaimScope(JSScope *scope, JSContext *cx)

{

    JSRuntime *rt;

    JSContext *ownercx;

    jsrefcount saveDepth;

    PRStatus stat;



    rt = cx->runtime;

    JS_RUNTIME_METER(rt, claimAttempts);

    JS_LOCK_GC(rt);



    /* Reload in case ownercx went away while we blocked on the lock. */

    while ((ownercx = scope->ownercx) != NULL) {

        /*

         * Avoid selflock if ownercx is dead, or is not running a request, or

         * has the same thread as cx.  Set scope->ownercx to cx so that the

         * matching JS_UNLOCK_SCOPE or JS_UNLOCK_OBJ macro call will take the

         * fast path around the corresponding js_UnlockScope or js_UnlockObj

         * function call.

         *

         * If scope->u.link is non-null, scope has already been inserted on

         * the rt->scopeSharingTodo list, because another thread's context

         * already wanted to lock scope while ownercx was running a request.

         * We can't claim any scope whose u.link is non-null at this point,

         * even if ownercx->requestDepth is 0 (see below where we suspend our

         * request before waiting on rt->scopeSharingDone).

         */

        if (!scope->u.link &&

            (!js_LiveContext(rt, ownercx) ||

             !ownercx->requestDepth ||

             ownercx->thread == cx->thread)) {

            JS_ASSERT(scope->u.count == 0);

            scope->ownercx = cx;

            JS_UNLOCK_GC(rt);

            JS_RUNTIME_METER(rt, claimedScopes);

            return JS_TRUE;

        }



        /*

         * Avoid deadlock if scope's owner context is waiting on a scope that

         * we own, by revoking scope's ownership.  This approach to deadlock

         * avoidance works because the engine never nests scope locks, except

         * for the notable case of js_SetProtoOrParent (see jsobj.c).

         *

         * If cx could hold locks on ownercx->scopeToShare, or if ownercx

         * could hold locks on scope, we would need to keep reentrancy counts

         * for all such "flyweight" (ownercx != NULL) locks, so that control

         * would unwind properly once these locks became "thin" or "fat".

         * Apart from the js_SetProtoOrParent exception, the engine promotes

         * a scope from exclusive to shared access only when locking, never

         * when holding or unlocking.

         *

         * If ownercx's thread is calling js_SetProtoOrParent, trying to lock

         * the inner scope (the scope of the object being set as the prototype

         * of the outer object), ShareScope will find the outer object's scope

         * at rt->setSlotScope.  If it's the same as scope, we give it a lock

         * held by ownercx's thread with reentrancy count of 1, then we return

         * here and break.  After that we unwind to js_[GS]etSlotThreadSafe or

         * js_LockScope (our caller), where we wait on the newly-fattened lock

         * until ownercx's thread unwinds from js_SetProtoOrParent.

         */

        if (ownercx->scopeToShare &&

            WillDeadlock(ownercx->scopeToShare, cx)) {

            ShareScope(rt, scope);

            break;

        }



        /*

         * Thanks to the non-zero NO_SCOPE_SHARING_TODO link terminator, we

         * can decide whether scope is on rt->scopeSharingTodo with a single

         * non-null test, and avoid double-insertion bugs.

         */

        if (!scope->u.link) {

            scope->u.link = rt->scopeSharingTodo;

            rt->scopeSharingTodo = scope;

            js_HoldObjectMap(cx, &scope->map);

        }



        /*

         * Inline JS_SuspendRequest before we wait on rt->scopeSharingDone,

         * saving and clearing cx->requestDepth so we don't deadlock if the

         * GC needs to run on ownercx.

         */

        saveDepth = cx->requestDepth;

        if (saveDepth) {

            cx->requestDepth = 0;

            JS_ASSERT(rt->requestCount > 0);

            rt->requestCount--;

            if (rt->requestCount == 0)

                JS_NOTIFY_REQUEST_DONE(rt);

        }



        /*

         * We know that some other thread's context owns scope, which is now

         * linked onto rt->scopeSharingTodo, awaiting the end of that other

         * thread's request.  So it is safe to wait on rt->scopeSharingDone.

         */

        cx->scopeToShare = scope;

        stat = PR_WaitCondVar(rt->scopeSharingDone, PR_INTERVAL_NO_TIMEOUT);

        JS_ASSERT(stat != PR_FAILURE);

        cx->scopeToShare = NULL;



        /*

         * Inline JS_ResumeRequest after waiting on rt->scopeSharingDone,

         * restoring cx->requestDepth.

         */

        if (saveDepth) {

            if (rt->gcThread != cx->thread) {

                while (rt->gcLevel > 0)

                    JS_AWAIT_GC_DONE(rt);

            }

            rt->requestCount++;

            cx->requestDepth = saveDepth;

        }

    }



    JS_UNLOCK_GC(rt);

    return JS_FALSE;

}



jsval

js_GetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot)

{

    jsval v;

    JSScope *scope;

#ifndef NSPR_LOCK

    JSThinLock *tl;

    jsword me;

#endif



    JS_ASSERT(OBJ_IS_NATIVE(obj));

    scope = OBJ_SCOPE(obj);

    JS_ASSERT(scope->ownercx != cx);

    JS_ASSERT(obj->slots && slot < obj->map->freeslot);

    if (scope->ownercx && ClaimScope(scope, cx))

        return obj->slots[slot];



#ifndef NSPR_LOCK

    tl = &scope->lock;

    me = cx->thread;

    JS_ASSERT(me == CurrentThreadId());

    if (js_CompareAndSwap(&tl->owner, 0, me)) {

        /*

         * Got the lock with one compare-and-swap.  Even so, someone else may

         * have mutated obj so it now has its own scope and lock, which would

         * require either a restart from the top of this routine, or a thin

         * lock release followed by fat lock acquisition.

         */

        if (scope == OBJ_SCOPE(obj)) {

            v = obj->slots[slot];

            if (!js_CompareAndSwap(&tl->owner, me, 0)) {

                /* Assert that scope locks never revert to flyweight. */

                JS_ASSERT(scope->ownercx != cx);

                LOGIT(scope, '1');

                scope->u.count = 1;

                js_UnlockObj(cx, obj);

            }

            return v;

        }

        if (!js_CompareAndSwap(&tl->owner, me, 0))

            js_Dequeue(tl);

    }

    else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) {

        return obj->slots[slot];

    }

#endif



    js_LockObj(cx, obj);

    v = obj->slots[slot];



    /*

     * Test whether cx took ownership of obj's scope during js_LockObj.

     *

     * This does not mean that a given scope reverted to flyweight from "thin"

     * or "fat" -- it does mean that obj's map pointer changed due to another

     * thread setting a property, requiring obj to cease sharing a prototype

     * object's scope (whose lock was not flyweight, else we wouldn't be here

     * in the first place!).

     */

    scope = OBJ_SCOPE(obj);

    if (scope->ownercx != cx)

        js_UnlockScope(cx, scope);

    return v;

}



void

js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v)

{

    JSScope *scope;

#ifndef NSPR_LOCK

    JSThinLock *tl;

    jsword me;

#endif



    JS_ASSERT(OBJ_IS_NATIVE(obj));

    scope = OBJ_SCOPE(obj);

    JS_ASSERT(scope->ownercx != cx);

    JS_ASSERT(obj->slots && slot < obj->map->freeslot);

    if (scope->ownercx && ClaimScope(scope, cx)) {

        obj->slots[slot] = v;

        return;

    }



#ifndef NSPR_LOCK

    tl = &scope->lock;

    me = cx->thread;

    JS_ASSERT(me == CurrentThreadId());

    if (js_CompareAndSwap(&tl->owner, 0, me)) {

        if (scope == OBJ_SCOPE(obj)) {

            obj->slots[slot] = v;

            if (!js_CompareAndSwap(&tl->owner, me, 0)) {

                /* Assert that scope locks never revert to flyweight. */

                JS_ASSERT(scope->ownercx != cx);

                LOGIT(scope, '1');

                scope->u.count = 1;

                js_UnlockObj(cx, obj);

            }

            return;

        }

        if (!js_CompareAndSwap(&tl->owner, me, 0))

            js_Dequeue(tl);

    }

    else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) {

        obj->slots[slot] = v;

        return;

    }

#endif



    js_LockObj(cx, obj);

    obj->slots[slot] = v;



    /*

     * Same drill as above, in js_GetSlotThreadSafe.  Note that we cannot

     * assume obj has its own mutable scope (where scope->object == obj) yet,

     * because OBJ_SET_SLOT is called for the "universal", common slots such

     * as JSSLOT_PROTO and JSSLOT_PARENT, without a prior js_GetMutableScope.

     * See also the JSPROP_SHARED attribute and its usage.

     */

    scope = OBJ_SCOPE(obj);

    if (scope->ownercx != cx)

        js_UnlockScope(cx, scope);

}



#ifndef NSPR_LOCK



static JSFatLock *

NewFatlock()

{

    JSFatLock *fl = (JSFatLock *)malloc(sizeof(JSFatLock)); /* for now */

    if (!fl) return NULL;

    fl->susp = 0;

    fl->next = NULL;

    fl->prevp = NULL;

    fl->slock = PR_NewLock();

    fl->svar = PR_NewCondVar(fl->slock);

    return fl;

}



static void

DestroyFatlock(JSFatLock *fl)

{

    PR_DestroyLock(fl->slock);

    PR_DestroyCondVar(fl->svar);

    free(fl);

}



static JSFatLock *

ListOfFatlocks(int l)

{

    JSFatLock *m;

    JSFatLock *m0;

    int i;



    JS_ASSERT(l>0);

    m0 = m = NewFatlock();

    for (i=1; i<l; i++) {

        m->next = NewFatlock();

        m = m->next;

    }

    return m0;

}



static void

DeleteListOfFatlocks(JSFatLock *m)

{

    JSFatLock *m0;

    for (; m; m=m0) {

        m0 = m->next;

        DestroyFatlock(m);

    }

}



static JSFatLockTable *fl_list_table = NULL;

static uint32          fl_list_table_len = 0;



static JSFatLock *

GetFatlock(void *id)

{

    JSFatLock *m;



    uint32 i = GLOBAL_LOCK_INDEX(id);

    if (fl_list_table[i].free == NULL) {

#ifdef DEBUG

        printf("Ran out of fat locks!\n");

#endif

        fl_list_table[i].free = ListOfFatlocks(10);

    }

    m = fl_list_table[i].free;

    fl_list_table[i].free = m->next;

    m->susp = 0;

    m->next = fl_list_table[i].taken;

    m->prevp = &fl_list_table[i].taken;

    if (fl_list_table[i].taken)

        fl_list_table[i].taken->prevp = &m->next;

    fl_list_table[i].taken = m;

    return m;

}



static void

PutFatlock(JSFatLock *m, void *id)

{

    uint32 i;

    if (m == NULL)

        return;



    /* Unlink m from fl_list_table[i].taken. */

    *m->prevp = m->next;

    if (m->next)

        m->next->prevp = m->prevp;



    /* Insert m in fl_list_table[i].free. */

    i = GLOBAL_LOCK_INDEX(id);

    m->next = fl_list_table[i].free;

    fl_list_table[i].free = m;

}



#endif /* !NSPR_LOCK */



JSBool

js_SetupLocks(int l, int g)

{

#ifndef NSPR_LOCK

    uint32 i;



    if (global_locks)

        return JS_TRUE;

#ifdef DEBUG

    if (l > 10000 || l < 0) /* l == number of initially allocated fat locks */

        printf("Bad number %d in js_SetupLocks()!\n", l);

    if (g > 100 || g < 0)   /* g equals number of global locks */

        printf("Bad number %d in js_SetupLocks()!\n", l);

#endif

    global_locks_log2 = JS_CeilingLog2(g);

    global_locks_mask = JS_BITMASK(global_locks_log2);

    global_lock_count = JS_BIT(global_locks_log2);

    global_locks = (PRLock **) malloc(global_lock_count * sizeof(PRLock*));

    if (!global_locks)

        return JS_FALSE;

    for (i = 0; i < global_lock_count; i++) {

        global_locks[i] = PR_NewLock();

        if (!global_locks[i]) {

            global_lock_count = i;

            js_CleanupLocks();

            return JS_FALSE;

        }

    }

    fl_list_table = (JSFatLockTable *) malloc(i * sizeof(JSFatLockTable));

    if (!fl_list_table) {

        js_CleanupLocks();

        return JS_FALSE;

    }

    fl_list_table_len = global_lock_count;

    for (i = 0; i < global_lock_count; i++) {

        fl_list_table[i].free = ListOfFatlocks(l);

        if (!fl_list_table[i].free) {

            fl_list_table_len = i;

            js_CleanupLocks();

            return JS_FALSE;

        }

        fl_list_table[i].taken = NULL;

    }

#endif /* !NSPR_LOCK */

    return JS_TRUE;

}



/* pull in the cleanup function from jsdtoa.c */

extern void js_FinishDtoa(void);



void

js_CleanupLocks()

{

#ifndef NSPR_LOCK

    uint32 i;



    if (global_locks) {

        for (i = 0; i < global_lock_count; i++)

            PR_DestroyLock(global_locks[i]);

        free(global_locks);

        global_locks = NULL;

        global_lock_count = 1;

        global_locks_log2 = 0;

        global_locks_mask = 0;

    }

    if (fl_list_table) {

        for (i = 0; i < fl_list_table_len; i++) {

            DeleteListOfFatlocks(fl_list_table[i].free);

            fl_list_table[i].free = NULL;

            DeleteListOfFatlocks(fl_list_table[i].taken);

            fl_list_table[i].taken = NULL;

        }

        free(fl_list_table);

        fl_list_table = NULL;

        fl_list_table_len = 0;

    }

#endif /* !NSPR_LOCK */

    js_FinishDtoa();

}



void

js_InitContextForLocking(JSContext *cx)

{

    cx->thread = CurrentThreadId();

    JS_ASSERT(Thin_GetWait(cx->thread) == 0);

}



#ifndef NSPR_LOCK



/*

 * Fast locking and unlocking is implemented by delaying the allocation of a

 * system lock (fat lock) until contention.  As long as a locking thread A

 * runs uncontended, the lock is represented solely by storing A's identity in

 * the object being locked.

 *

 * If another thread B tries to lock the object currently locked by A, B is

 * enqueued into a fat lock structure (which might have to be allocated and

 * pointed to by the object), and suspended using NSPR conditional variables

 * (wait).  A wait bit (Bacon bit) is set in the lock word of the object,

 * signalling to A that when releasing the lock, B must be dequeued and

 * notified.

 *

 * The basic operation of the locking primitives (js_Lock, js_Unlock,

 * js_Enqueue, and js_Dequeue) is compare-and-swap.  Hence, when locking into

 * the word pointed at by p, compare-and-swap(p, 0, A) success implies that p

 * is unlocked.  Similarly, when unlocking p, if compare-and-swap(p, A, 0)

 * succeeds this implies that p is uncontended (no one is waiting because the

 * wait bit is not set).

 *

 * When dequeueing, the lock is released, and one of the threads suspended on

 * the lock is notified.  If other threads still are waiting, the wait bit is

 * kept (in js_Enqueue), and if not, the fat lock is deallocated.

 *

 * The functions js_Enqueue, js_Dequeue, js_SuspendThread, and js_ResumeThread

 * are serialized using a global lock.  For scalability, a hashtable of global

 * locks is used, which is indexed modulo the thin lock pointer.

 */



/*

 * Invariants:

 * (i)  global lock is held

 * (ii) fl->susp >= 0

 */

static int

js_SuspendThread(JSThinLock *tl)

{

    JSFatLock *fl;

    PRStatus stat;



    if (tl->fat == NULL)

        fl = tl->fat = GetFatlock(tl);

    else

        fl = tl->fat;

    JS_ASSERT(fl->susp >= 0);

    fl->susp++;

    PR_Lock(fl->slock);

    js_UnlockGlobal(tl);

    stat = PR_WaitCondVar(fl->svar, PR_INTERVAL_NO_TIMEOUT);

    JS_ASSERT(stat != PR_FAILURE);

    PR_Unlock(fl->slock);

    js_LockGlobal(tl);

    fl->susp--;

    if (fl->susp == 0) {

        PutFatlock(fl, tl);

        tl->fat = NULL;

    }

    return tl->fat == NULL;

}



/*

 * (i)  global lock is held

 * (ii) fl->susp > 0

 */

static void

js_ResumeThread(JSThinLock *tl)

{

    JSFatLock *fl = tl->fat;

    PRStatus stat;



    JS_ASSERT(fl != NULL);

    JS_ASSERT(fl->susp > 0);

    PR_Lock(fl->slock);

    js_UnlockGlobal(tl);

    stat = PR_NotifyCondVar(fl->svar);

    JS_ASSERT(stat != PR_FAILURE);

    PR_Unlock(fl->slock);

}



static void

js_Enqueue(JSThinLock *tl, jsword me)

{

    jsword o, n;



    js_LockGlobal(tl);

    for (;;) {

        o = ReadWord(tl->owner);

        n = Thin_SetWait(o);

        if (o != 0 && js_CompareAndSwap(&tl->owner, o, n)) {

            if (js_SuspendThread(tl))

                me = Thin_RemoveWait(me);

            else

                me = Thin_SetWait(me);

        }

        else if (js_CompareAndSwap(&tl->owner, 0, me)) {

            js_UnlockGlobal(tl);

            return;

        }

    }

}



static void

js_Dequeue(JSThinLock *tl)

{

    jsword o;



    js_LockGlobal(tl);

    o = ReadWord(tl->owner);

    JS_ASSERT(Thin_GetWait(o) != 0);

    JS_ASSERT(tl->fat != NULL);

    if (!js_CompareAndSwap(&tl->owner, o, 0)) /* release it */

        JS_ASSERT(0);

    js_ResumeThread(tl);

}



JS_INLINE void

js_Lock(JSThinLock *tl, jsword me)

{

    JS_ASSERT(me == CurrentThreadId());

    if (js_CompareAndSwap(&tl->owner, 0, me))

        return;

    if (Thin_RemoveWait(ReadWord(tl->owner)) != me)

        js_Enqueue(tl, me);

#ifdef DEBUG

    else

        JS_ASSERT(0);

#endif

}



JS_INLINE void

js_Unlock(JSThinLock *tl, jsword me)

{

    JS_ASSERT(me == CurrentThreadId());

    if (js_CompareAndSwap(&tl->owner, me, 0))

        return;

    if (Thin_RemoveWait(ReadWord(tl->owner)) == me)

        js_Dequeue(tl);

#ifdef DEBUG

    else

        JS_ASSERT(0);

#endif

}



#endif /* !NSPR_LOCK */



void

js_LockRuntime(JSRuntime *rt)

{

    PR_Lock(rt->rtLock);

#ifdef DEBUG

    rt->rtLockOwner = CurrentThreadId();

#endif

}



void

js_UnlockRuntime(JSRuntime *rt)

{

#ifdef DEBUG

    rt->rtLockOwner = 0;

#endif

    PR_Unlock(rt->rtLock);

}



void

js_LockScope(JSContext *cx, JSScope *scope)

{

    jsword me = cx->thread;



    JS_ASSERT(me == CurrentThreadId());

    JS_ASSERT(scope->ownercx != cx);

    if (scope->ownercx && ClaimScope(scope, cx))

        return;



    if (Thin_RemoveWait(ReadWord(scope->lock.owner)) == me) {

        JS_ASSERT(scope->u.count > 0);

        LOGIT(scope, '+');

        scope->u.count++;

    } else {

        JSThinLock *tl = &scope->lock;

        JS_LOCK0(tl, me);

        JS_ASSERT(scope->u.count == 0);

        LOGIT(scope, '1');

        scope->u.count = 1;

    }

}



void

js_UnlockScope(JSContext *cx, JSScope *scope)

{

    jsword me = cx->thread;



    JS_ASSERT(scope->ownercx == NULL);

    JS_ASSERT(scope->u.count > 0);

    if (Thin_RemoveWait(ReadWord(scope->lock.owner)) != me) {

        JS_ASSERT(0);   /* unbalanced unlock */

        return;

    }

    LOGIT(scope, '-');

    if (--scope->u.count == 0) {

        JSThinLock *tl = &scope->lock;

        JS_UNLOCK0(tl, me);

    }

}



/*

 * NB: oldscope may be null if our caller is js_GetMutableScope and it just

 * dropped the last reference to oldscope.

 */

void

js_TransferScopeLock(JSContext *cx, JSScope *oldscope, JSScope *newscope)

{

    jsword me;

    JSThinLock *tl;



    JS_ASSERT(JS_IS_SCOPE_LOCKED(newscope));



    /*

     * If the last reference to oldscope went away, newscope needs no lock

     * state update.

     */

    if (!oldscope)

	return;

    JS_ASSERT(JS_IS_SCOPE_LOCKED(oldscope));



    /*

     * If oldscope is single-threaded, there's nothing to do.

     * XXX if (!newscope->ownercx), assume newscope->u.count is properly set

     */

    if (oldscope->ownercx) {

        JS_ASSERT(oldscope->ownercx == cx);

        JS_ASSERT(newscope->ownercx == cx || !newscope->ownercx);

        return;

    }



    /*

     * We transfer oldscope->u.count only if newscope is not single-threaded.

     * Flow unwinds from here through some number of JS_UNLOCK_SCOPE and/or

     * JS_UNLOCK_OBJ macro calls, which will decrement newscope->u.count only

     * if they find newscope->ownercx != cx.

     */

    if (newscope->ownercx != cx) {

        JS_ASSERT(!newscope->ownercx);

        newscope->u.count = oldscope->u.count;

    }



    /*

     * Reset oldscope's lock state so that it is completely unlocked.

     */

    LOGIT(oldscope, '0');

    oldscope->u.count = 0;

    tl = &oldscope->lock;

    me = cx->thread;

    JS_UNLOCK0(tl, me);

}



void

js_LockObj(JSContext *cx, JSObject *obj)

{

    JSScope *scope;



    JS_ASSERT(OBJ_IS_NATIVE(obj));

    for (;;) {

        scope = OBJ_SCOPE(obj);

        js_LockScope(cx, scope);



        /* If obj still has this scope, we're done. */

        if (scope == OBJ_SCOPE(obj))

            return;



        /* Lost a race with a mutator; retry with obj's new scope. */

        js_UnlockScope(cx, scope);

    }

}



void

js_UnlockObj(JSContext *cx, JSObject *obj)

{

    JS_ASSERT(OBJ_IS_NATIVE(obj));

    js_UnlockScope(cx, OBJ_SCOPE(obj));

}



#ifdef DEBUG

JSBool

js_IsRuntimeLocked(JSRuntime *rt)

{

    return CurrentThreadId() == rt->rtLockOwner;

}



JSBool

js_IsObjLocked(JSObject *obj)

{

    JSScope *scope = OBJ_SCOPE(obj);



    return MAP_IS_NATIVE(&scope->map) &&

           (scope->ownercx ||

            CurrentThreadId() == Thin_RemoveWait(ReadWord(scope->lock.owner)));

}



JSBool

js_IsScopeLocked(JSScope *scope)

{

    return scope->ownercx ||

           CurrentThreadId() == Thin_RemoveWait(ReadWord(scope->lock.owner));

}

#endif



#endif /* JS_THREADSAFE */

 

**** End of jslock.c ****

 

**** Start of jslock.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */

#ifndef jslock_h__

#define jslock_h__



#ifdef JS_THREADSAFE



#include "jstypes.h"

#include "pratom.h"

#include "prlock.h"

#include "prcvar.h"

#include "jshash.h" /* Added by JSIFY */



#include "jsprvtd.h"    /* for JSScope, etc. */

#include "jspubtd.h"    /* for JSRuntime, etc. */



#define Thin_GetWait(W) ((jsword)(W) & 0x1)

#define Thin_SetWait(W) ((jsword)(W) | 0x1)

#define Thin_RemoveWait(W) ((jsword)(W) & ~0x1)



typedef struct JSFatLock JSFatLock;



struct JSFatLock {

    int         susp;

    PRLock      *slock;

    PRCondVar   *svar;

    JSFatLock   *next;

    JSFatLock   **prevp;

};



typedef struct JSThinLock {

    jsword      owner;

    JSFatLock   *fat;

} JSThinLock;



typedef PRLock JSLock;



typedef struct JSFatLockTable {

    JSFatLock   *free;

    JSFatLock   *taken;

} JSFatLockTable;



/*

 * Atomic increment and decrement for a reference counter, given jsrefcount *p.

 * NB: jsrefcount is int32, aka PRInt32, so that pratom.h functions work.

 */

#define JS_ATOMIC_INCREMENT(p)      PR_AtomicIncrement((PRInt32 *)(p))

#define JS_ATOMIC_DECREMENT(p)      PR_AtomicDecrement((PRInt32 *)(p))



#define CurrentThreadId()           (jsword)PR_GetCurrentThread()

#define JS_CurrentThreadId()        js_CurrentThreadId()

#define JS_NEW_LOCK()               PR_NewLock()

#define JS_DESTROY_LOCK(l)          PR_DestroyLock(l)

#define JS_ACQUIRE_LOCK(l)          PR_Lock(l)

#define JS_RELEASE_LOCK(l)          PR_Unlock(l)

#define JS_LOCK0(P,M)               js_Lock(P,M)

#define JS_UNLOCK0(P,M)             js_Unlock(P,M)



#define JS_NEW_CONDVAR(l)           PR_NewCondVar(l)

#define JS_DESTROY_CONDVAR(cv)      PR_DestroyCondVar(cv)

#define JS_WAIT_CONDVAR(cv,to)      PR_WaitCondVar(cv,to)

#define JS_NO_TIMEOUT               PR_INTERVAL_NO_TIMEOUT

#define JS_NOTIFY_CONDVAR(cv)       PR_NotifyCondVar(cv)

#define JS_NOTIFY_ALL_CONDVAR(cv)   PR_NotifyAllCondVar(cv)



/*

 * Include jsscope.h so JS_LOCK_OBJ macro callers don't have to include it.

 * Since there is a JSThinLock member in JSScope, we can't nest this include

 * much earlier (see JSThinLock's typedef, above).  Yes, that means there is

 * an #include cycle between jslock.h and jsscope.h: moderate-sized XXX here,

 * to be fixed by moving JS_LOCK_SCOPE to jsscope.h, JS_LOCK_OBJ to jsobj.h,

 * and so on.

 *

 * We also need jsscope.h #ifdef DEBUG for SET_OBJ_INFO and SET_SCOPE_INFO,

 * but we do not want any nested includes that depend on DEBUG.  Those lead

 * to build bustage when someone makes a change that depends in a subtle way

 * on jsscope.h being included directly or indirectly, but does not test by

 * building optimized as well as DEBUG.

 */

#include "jsscope.h"



#ifdef DEBUG



#define SET_OBJ_INFO(obj_,file_,line_)                                        \

    SET_SCOPE_INFO(OBJ_SCOPE(obj_),file_,line_)



#define SET_SCOPE_INFO(scope_,file_,line_)                                    \

    ((scope_)->ownercx ? (void)0 :                                            \

     (JS_ASSERT((scope_)->u.count > 0 && (scope_)->u.count <= 4),             \

      (void)((scope_)->file[(scope_)->u.count-1] = (file_),                   \

             (scope_)->line[(scope_)->u.count-1] = (line_))))

#endif /* DEBUG */



#define JS_LOCK_RUNTIME(rt)         js_LockRuntime(rt)

#define JS_UNLOCK_RUNTIME(rt)       js_UnlockRuntime(rt)



/*

 * NB: The JS_LOCK_OBJ and JS_UNLOCK_OBJ macros work *only* on native objects

 * (objects for which OBJ_IS_NATIVE returns true).  All uses of these macros in

 * the engine are predicated on OBJ_IS_NATIVE or equivalent checks.  These uses

 * are for optimizations above the JSObjectOps layer, under which object locks

 * normally hide.

 */

#define JS_LOCK_OBJ(cx,obj)         ((OBJ_SCOPE(obj)->ownercx == (cx))        \

                                     ? (void)0                                \

                                     : (js_LockObj(cx, obj),                  \

                                        SET_OBJ_INFO(obj,__FILE__,__LINE__)))

#define JS_UNLOCK_OBJ(cx,obj)       ((OBJ_SCOPE(obj)->ownercx == (cx))        \

                                     ? (void)0 : js_UnlockObj(cx, obj))



#define JS_LOCK_SCOPE(cx,scope)     ((scope)->ownercx == (cx) ? (void)0 :     \

                                     (js_LockScope(cx, scope),                \

                                      SET_SCOPE_INFO(scope,__FILE__,__LINE__)))

#define JS_UNLOCK_SCOPE(cx,scope)   ((scope)->ownercx == (cx) ? (void)0 :     \

                                     js_UnlockScope(cx, scope))

#define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope)                           \

                                    js_TransferScopeLock(cx, scope, newscope)



extern jsword js_CurrentThreadId();

extern void js_LockRuntime(JSRuntime *rt);

extern void js_UnlockRuntime(JSRuntime *rt);

extern void js_LockObj(JSContext *cx, JSObject *obj);

extern void js_UnlockObj(JSContext *cx, JSObject *obj);

extern void js_LockScope(JSContext *cx, JSScope *scope);

extern void js_UnlockScope(JSContext *cx, JSScope *scope);

extern int js_SetupLocks(int,int);

extern void js_CleanupLocks();

extern void js_InitContextForLocking(JSContext *);

extern void js_TransferScopeLock(JSContext *, JSScope *, JSScope *);

extern jsval js_GetSlotThreadSafe(JSContext *, JSObject *, uint32);

extern void js_SetSlotThreadSafe(JSContext *, JSObject *, uint32, jsval);

extern void js_InitLock(JSThinLock *);

extern void js_FinishLock(JSThinLock *);



#ifdef DEBUG



#define JS_IS_RUNTIME_LOCKED(rt)    js_IsRuntimeLocked(rt)

#define JS_IS_OBJ_LOCKED(obj)       js_IsObjLocked(obj)

#define JS_IS_SCOPE_LOCKED(scope)   js_IsScopeLocked(scope)



extern JSBool js_IsRuntimeLocked(JSRuntime *rt);

extern JSBool js_IsObjLocked(JSObject *obj);

extern JSBool js_IsScopeLocked(JSScope *scope);



#else



#define JS_IS_RUNTIME_LOCKED(rt)    0

#define JS_IS_OBJ_LOCKED(obj)       1

#define JS_IS_SCOPE_LOCKED(scope)   1



#endif /* DEBUG */



#define JS_LOCK_OBJ_VOID(cx, obj, e)                                          \

    JS_BEGIN_MACRO                                                            \

        JS_LOCK_OBJ(cx, obj);                                                 \

        e;                                                                    \

        JS_UNLOCK_OBJ(cx, obj);                                               \

    JS_END_MACRO



#define JS_LOCK_VOID(cx, e)                                                   \

    JS_BEGIN_MACRO                                                            \

        JSRuntime *_rt = (cx)->runtime;                                       \

        JS_LOCK_RUNTIME_VOID(_rt, e);                                         \

    JS_END_MACRO



#if defined(JS_USE_ONLY_NSPR_LOCKS) ||                                        \

    !( (defined(_WIN32) && defined(_M_IX86)) ||                               \

       (defined(__GNUC__) && defined(__i386__)) ||                            \

       (defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC)) ||        \

       defined(AIX) )



#define NSPR_LOCK 1



#undef JS_LOCK0

#undef JS_UNLOCK0

#define JS_LOCK0(P,M)   (JS_ACQUIRE_LOCK(((JSLock*)(P)->fat)), (P)->owner = (M))

#define JS_UNLOCK0(P,M) ((P)->owner = 0, JS_RELEASE_LOCK(((JSLock*)(P)->fat)))



#else  /* arch-tests */



#undef NSPR_LOCK



extern JS_INLINE void js_Lock(JSThinLock *tl, jsword me);

extern JS_INLINE void js_Unlock(JSThinLock *tl, jsword me);



#endif /* arch-tests */



#else  /* !JS_THREADSAFE */



#define JS_ATOMIC_INCREMENT(p)      (++*(p))

#define JS_ATOMIC_DECREMENT(p)      (--*(p))



#define JS_CurrentThreadId() 0

#define JS_NEW_LOCK()               NULL

#define JS_DESTROY_LOCK(l)          ((void)0)

#define JS_ACQUIRE_LOCK(l)          ((void)0)

#define JS_RELEASE_LOCK(l)          ((void)0)

#define JS_LOCK0(P,M)               ((void)0)

#define JS_UNLOCK0(P,M)             ((void)0)



#define JS_NEW_CONDVAR(l)           NULL

#define JS_DESTROY_CONDVAR(cv)      ((void)0)

#define JS_WAIT_CONDVAR(cv,to)      ((void)0)

#define JS_NOTIFY_CONDVAR(cv)       ((void)0)

#define JS_NOTIFY_ALL_CONDVAR(cv)   ((void)0)



#define JS_LOCK_RUNTIME(rt)         ((void)0)

#define JS_UNLOCK_RUNTIME(rt)       ((void)0)

#define JS_LOCK_OBJ(cx,obj)         ((void)0)

#define JS_UNLOCK_OBJ(cx,obj)       ((void)0)

#define JS_LOCK_OBJ_VOID(cx,obj,e)  (e)

#define JS_LOCK_SCOPE(cx,scope)     ((void)0)

#define JS_UNLOCK_SCOPE(cx,scope)   ((void)0)

#define JS_TRANSFER_SCOPE_LOCK(c,o,n) ((void)0)



#define JS_IS_RUNTIME_LOCKED(rt)    1

#define JS_IS_OBJ_LOCKED(obj)       1

#define JS_IS_SCOPE_LOCKED(scope)   1

#define JS_LOCK_VOID(cx, e)         JS_LOCK_RUNTIME_VOID((cx)->runtime, e)



#endif /* !JS_THREADSAFE */



#define JS_LOCK_RUNTIME_VOID(rt,e)                                            \

    JS_BEGIN_MACRO                                                            \

        JS_LOCK_RUNTIME(rt);                                                  \

        e;                                                                    \

        JS_UNLOCK_RUNTIME(rt);                                                \

    JS_END_MACRO



#define JS_LOCK_GC(rt)              JS_ACQUIRE_LOCK((rt)->gcLock)

#define JS_UNLOCK_GC(rt)            JS_RELEASE_LOCK((rt)->gcLock)

#define JS_LOCK_GC_VOID(rt,e)       (JS_LOCK_GC(rt), (e), JS_UNLOCK_GC(rt))

#define JS_AWAIT_GC_DONE(rt)        JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT)

#define JS_NOTIFY_GC_DONE(rt)       JS_NOTIFY_ALL_CONDVAR((rt)->gcDone)

#define JS_AWAIT_REQUEST_DONE(rt)   JS_WAIT_CONDVAR((rt)->requestDone,        \

                                                    JS_NO_TIMEOUT)

#define JS_NOTIFY_REQUEST_DONE(rt)  JS_NOTIFY_CONDVAR((rt)->requestDone)



#define JS_LOCK(P,CX)               JS_LOCK0(P,(CX)->thread)

#define JS_UNLOCK(P,CX)             JS_UNLOCK0(P,(CX)->thread)



#ifndef SET_OBJ_INFO

#define SET_OBJ_INFO(obj,f,l)       ((void)0)

#endif

#ifndef SET_SCOPE_INFO

#define SET_SCOPE_INFO(scope,f,l)   ((void)0)

#endif



#endif /* jslock_h___ */

 

**** End of jslock.h ****

 

**** Start of jslog2.c ****

 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

/*

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#include "jsstddef.h"

#include "jsbit.h"



/*

** Compute the log of the least power of 2 greater than or equal to n

*/

JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 n)

{

    JSIntn log2 = 0;



    if (n & (n-1))

	log2++;

    if (n >> 16)

	log2 += 16, n >>= 16;

    if (n >> 8)

	log2 += 8, n >>= 8;

    if (n >> 4)

	log2 += 4, n >>= 4;

    if (n >> 2)

	log2 += 2, n >>= 2;

    if (n >> 1)

	log2++;

    return log2;

}



/*

** Compute the log of the greatest power of 2 less than or equal to n.

** This really just finds the highest set bit in the word.

*/

JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 n)

{

    JSIntn log2 = 0;



    if (n >> 16)

	log2 += 16, n >>= 16;

    if (n >> 8)

	log2 += 8, n >>= 8;

    if (n >> 4)

	log2 += 4, n >>= 4;

    if (n >> 2)

	log2 += 2, n >>= 2;

    if (n >> 1)

	log2++;

    return log2;

}

 

**** End of jslog2.c ****

 

**** Start of jslong.c ****

 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

/*

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#include "jsstddef.h"

#include "jstypes.h"

#include "jslong.h"



static JSInt64 ll_zero = JSLL_INIT( 0x00000000,0x00000000 );

static JSInt64 ll_maxint = JSLL_INIT( 0x7fffffff, 0xffffffff );

static JSInt64 ll_minint = JSLL_INIT( 0x80000000, 0x00000000 );



#ifdef HAVE_WATCOM_BUG_2

JSInt64 __pascal __loadds __export

    JSLL_Zero(void) { return ll_zero; }

JSInt64 __pascal __loadds __export

    JSLL_MaxInt(void) { return ll_maxint; }

JSInt64 __pascal __loadds __export

    JSLL_MinInt(void) { return ll_minint; }

#else

JS_PUBLIC_API(JSInt64) JSLL_Zero(void) { return ll_zero; }

JS_PUBLIC_API(JSInt64) JSLL_MaxInt(void) { return ll_maxint; }

JS_PUBLIC_API(JSInt64) JSLL_MinInt(void) { return ll_minint; }

#endif



#ifndef JS_HAVE_LONG_LONG

/*

** Divide 64-bit a by 32-bit b, which must be normalized so its high bit is 1.

*/

static void norm_udivmod32(JSUint32 *qp, JSUint32 *rp, JSUint64 a, JSUint32 b)

{

    JSUint32 d1, d0, q1, q0;

    JSUint32 r1, r0, m;



    d1 = jshi16(b);

    d0 = jslo16(b);

    r1 = a.hi % d1;

    q1 = a.hi / d1;

    m = q1 * d0;

    r1 = (r1 << 16) | jshi16(a.lo);

    if (r1 < m) {

        q1--, r1 += b;

        if (r1 >= b	/* i.e., we didn't get a carry when adding to r1 */

	    && r1 < m) {

	    q1--, r1 += b;

	}

    }

    r1 -= m;

    r0 = r1 % d1;

    q0 = r1 / d1;

    m = q0 * d0;

    r0 = (r0 << 16) | jslo16(a.lo);

    if (r0 < m) {

        q0--, r0 += b;

        if (r0 >= b

	    && r0 < m) {

	    q0--, r0 += b;

	}

    }

    *qp = (q1 << 16) | q0;

    *rp = r0 - m;

}



static JSUint32 CountLeadingZeros(JSUint32 a)

{

    JSUint32 t;

    JSUint32 r = 32;



    if ((t = a >> 16) != 0)

	r -= 16, a = t;

    if ((t = a >> 8) != 0)

	r -= 8, a = t;

    if ((t = a >> 4) != 0)

	r -= 4, a = t;

    if ((t = a >> 2) != 0)

	r -= 2, a = t;

    if ((t = a >> 1) != 0)

	r -= 1, a = t;

    if (a & 1)

	r--;

    return r;

}



JS_PUBLIC_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint64 b)

{

    JSUint32 n0, n1, n2;

    JSUint32 q0, q1;

    JSUint32 rsh, lsh;



    n0 = a.lo;

    n1 = a.hi;



    if (b.hi == 0) {

	if (b.lo > n1) {

	    /* (0 q0) = (n1 n0) / (0 D0) */



	    lsh = CountLeadingZeros(b.lo);



	    if (lsh) {

		/*

		 * Normalize, i.e. make the most significant bit of the

		 * denominator be set.

		 */

		b.lo = b.lo << lsh;

		n1 = (n1 << lsh) | (n0 >> (32 - lsh));

		n0 = n0 << lsh;

	    }



	    a.lo = n0, a.hi = n1;

	    norm_udivmod32(&q0, &n0, a, b.lo);

	    q1 = 0;



	    /* remainder is in n0 >> lsh */

	} else {

	    /* (q1 q0) = (n1 n0) / (0 d0) */



	    if (b.lo == 0)		/* user wants to divide by zero! */

		b.lo = 1 / b.lo;	/* so go ahead and crash */



	    lsh = CountLeadingZeros(b.lo);



	    if (lsh == 0) {

		/*

		 * From (n1 >= b.lo)

		 *   && (the most significant bit of b.lo is set),

		 * conclude that

		 *	(the most significant bit of n1 is set)

		 *   && (the leading quotient digit q1 = 1).

		 *

		 * This special case is necessary, not an optimization

		 * (Shifts counts of 32 are undefined).

		 */

		n1 -= b.lo;

		q1 = 1;

	    } else {

		/*

		 * Normalize.

		 */

		rsh = 32 - lsh;



		b.lo = b.lo << lsh;

		n2 = n1 >> rsh;

		n1 = (n1 << lsh) | (n0 >> rsh);

		n0 = n0 << lsh;



		a.lo = n1, a.hi = n2;

		norm_udivmod32(&q1, &n1, a, b.lo);

	    }



	    /* n1 != b.lo... */



	    a.lo = n0, a.hi = n1;

	    norm_udivmod32(&q0, &n0, a, b.lo);



	    /* remainder in n0 >> lsh */

	}



	if (rp) {

	    rp->lo = n0 >> lsh;

	    rp->hi = 0;

	}

    } else {

	if (b.hi > n1) {

	    /* (0 0) = (n1 n0) / (D1 d0) */



	    q0 = 0;

	    q1 = 0;



	    /* remainder in (n1 n0) */

	    if (rp) {

		rp->lo = n0;

		rp->hi = n1;

	    }

	} else {

	    /* (0 q0) = (n1 n0) / (d1 d0) */



	    lsh = CountLeadingZeros(b.hi);

	    if (lsh == 0) {

		/*

		 * From (n1 >= b.hi)

		 *   && (the most significant bit of b.hi is set),

		 * conclude that

		 *      (the most significant bit of n1 is set)

		 *   && (the quotient digit q0 = 0 or 1).

		 *

		 * This special case is necessary, not an optimization.

		 */



		/*

		 * The condition on the next line takes advantage of that

		 * n1 >= b.hi (true due to control flow).

		 */

		if (n1 > b.hi || n0 >= b.lo) {

		    q0 = 1;

		    a.lo = n0, a.hi = n1;

		    JSLL_SUB(a, a, b);

		} else {

		    q0 = 0;

		}

		q1 = 0;



		if (rp) {

		    rp->lo = n0;

		    rp->hi = n1;

		}

	    } else {

		JSInt64 m;



		/*

		 * Normalize.

		 */

		rsh = 32 - lsh;



		b.hi = (b.hi << lsh) | (b.lo >> rsh);

		b.lo = b.lo << lsh;

		n2 = n1 >> rsh;

		n1 = (n1 << lsh) | (n0 >> rsh);

		n0 = n0 << lsh;



		a.lo = n1, a.hi = n2;

		norm_udivmod32(&q0, &n1, a, b.hi);

		JSLL_MUL32(m, q0, b.lo);



		if ((m.hi > n1) || ((m.hi == n1) && (m.lo > n0))) {

		    q0--;

		    JSLL_SUB(m, m, b);

		}



		q1 = 0;



		/* Remainder is ((n1 n0) - (m1 m0)) >> lsh */

		if (rp) {

		    a.lo = n0, a.hi = n1;

		    JSLL_SUB(a, a, m);

		    rp->lo = (a.hi << rsh) | (a.lo >> lsh);

		    rp->hi = a.hi >> lsh;

		}

	    }

	}

    }



    if (qp) {

	qp->lo = q0;

	qp->hi = q1;

    }

}

#endif /* !JS_HAVE_LONG_LONG */

 

**** End of jslong.c ****

 

**** Start of jslong.h ****

 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

/*

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

** File:                jslong.h

** Description: Portable access to 64 bit numerics

**

** Long-long (64-bit signed integer type) support. Some C compilers

** don't support 64 bit integers yet, so we use these macros to

** support both machines that do and don't.

**/

#ifndef jslong_h___

#define jslong_h___



#include "jstypes.h"



JS_BEGIN_EXTERN_C



/***********************************************************************

** DEFINES:     JSLL_MaxInt

**              JSLL_MinInt

**              JSLL_Zero

** DESCRIPTION:

**      Various interesting constants and static variable

**      initializer

***********************************************************************/

#ifdef HAVE_WATCOM_BUG_2

JSInt64 __pascal __loadds __export

    JSLL_MaxInt(void);

JSInt64 __pascal __loadds __export

    JSLL_MinInt(void);

JSInt64 __pascal __loadds __export

    JSLL_Zero(void);

#else

extern JS_PUBLIC_API(JSInt64) JSLL_MaxInt(void);

extern JS_PUBLIC_API(JSInt64) JSLL_MinInt(void);

extern JS_PUBLIC_API(JSInt64) JSLL_Zero(void);

#endif



#define JSLL_MAXINT   JSLL_MaxInt()

#define JSLL_MININT   JSLL_MinInt()

#define JSLL_ZERO     JSLL_Zero()



#ifdef JS_HAVE_LONG_LONG



#if JS_BYTES_PER_LONG == 8

#define JSLL_INIT(hi, lo)  ((hi ## L << 32) + lo ## L)

#elif defined(WIN32) || defined(WIN16)

#define JSLL_INIT(hi, lo)  ((hi ## i64 << 32) + lo ## i64)

#else

#define JSLL_INIT(hi, lo)  ((hi ## LL << 32) + lo ## LL)

#endif



/***********************************************************************

** MACROS:      JSLL_*

** DESCRIPTION:

**      The following macros define portable access to the 64 bit

**      math facilities.

**

***********************************************************************/



/***********************************************************************

** MACROS:      JSLL_<relational operators>

**

**  JSLL_IS_ZERO        Test for zero

**  JSLL_EQ             Test for equality

**  JSLL_NE             Test for inequality

**  JSLL_GE_ZERO        Test for zero or positive

**  JSLL_CMP            Compare two values

***********************************************************************/

#define JSLL_IS_ZERO(a)       ((a) == 0)

#define JSLL_EQ(a, b)         ((a) == (b))

#define JSLL_NE(a, b)         ((a) != (b))

#define JSLL_GE_ZERO(a)       ((a) >= 0)

#define JSLL_CMP(a, op, b)    ((JSInt64)(a) op (JSInt64)(b))

#define JSLL_UCMP(a, op, b)   ((JSUint64)(a) op (JSUint64)(b))



/***********************************************************************

** MACROS:      JSLL_<logical operators>

**

**  JSLL_AND            Logical and

**  JSLL_OR             Logical or

**  JSLL_XOR            Logical exclusion

**  JSLL_OR2            A disgusting deviation

**  JSLL_NOT            Negation (one's compliment)

***********************************************************************/

#define JSLL_AND(r, a, b)        ((r) = (a) & (b))

#define JSLL_OR(r, a, b)        ((r) = (a) | (b))

#define JSLL_XOR(r, a, b)        ((r) = (a) ^ (b))

#define JSLL_OR2(r, a)        ((r) = (r) | (a))

#define JSLL_NOT(r, a)        ((r) = ~(a))



/***********************************************************************

** MACROS:      JSLL_<mathematical operators>

**

**  JSLL_NEG            Negation (two's compliment)

**  JSLL_ADD            Summation (two's compliment)

**  JSLL_SUB            Difference (two's compliment)

***********************************************************************/

#define JSLL_NEG(r, a)        ((r) = -(a))

#define JSLL_ADD(r, a, b)     ((r) = (a) + (b))

#define JSLL_SUB(r, a, b)     ((r) = (a) - (b))



/***********************************************************************

** MACROS:      JSLL_<mathematical operators>

**

**  JSLL_MUL            Product (two's compliment)

**  JSLL_DIV            Quotient (two's compliment)

**  JSLL_MOD            Modulus (two's compliment)

***********************************************************************/

#define JSLL_MUL(r, a, b)        ((r) = (a) * (b))

#define JSLL_DIV(r, a, b)        ((r) = (a) / (b))

#define JSLL_MOD(r, a, b)        ((r) = (a) % (b))



/***********************************************************************

** MACROS:      JSLL_<shifting operators>

**

**  JSLL_SHL            Shift left [0..64] bits

**  JSLL_SHR            Shift right [0..64] bits with sign extension

**  JSLL_USHR           Unsigned shift right [0..64] bits

**  JSLL_ISHL           Signed shift left [0..64] bits

***********************************************************************/

#define JSLL_SHL(r, a, b)     ((r) = (JSInt64)(a) << (b))

#define JSLL_SHR(r, a, b)     ((r) = (JSInt64)(a) >> (b))

#define JSLL_USHR(r, a, b)    ((r) = (JSUint64)(a) >> (b))

#define JSLL_ISHL(r, a, b)    ((r) = (JSInt64)(a) << (b))



/***********************************************************************

** MACROS:      JSLL_<conversion operators>

**

**  JSLL_L2I            Convert to signed 32 bit

**  JSLL_L2UI           Convert to unsigned 32 bit

**  JSLL_L2F            Convert to floating point

**  JSLL_L2D            Convert to floating point

**  JSLL_I2L            Convert signed to 64 bit

**  JSLL_UI2L           Convert unsigned to 64 bit

**  JSLL_F2L            Convert float to 64 bit

**  JSLL_D2L            Convert float to 64 bit

***********************************************************************/

#define JSLL_L2I(i, l)        ((i) = (JSInt32)(l))

#define JSLL_L2UI(ui, l)        ((ui) = (JSUint32)(l))

#define JSLL_L2F(f, l)        ((f) = (JSFloat64)(l))

#define JSLL_L2D(d, l)        ((d) = (JSFloat64)(l))



#define JSLL_I2L(l, i)        ((l) = (JSInt64)(i))

#define JSLL_UI2L(l, ui)        ((l) = (JSInt64)(ui))

#define JSLL_F2L(l, f)        ((l) = (JSInt64)(f))

#define JSLL_D2L(l, d)        ((l) = (JSInt64)(d))



/***********************************************************************

** MACROS:      JSLL_UDIVMOD

** DESCRIPTION:

**  Produce both a quotient and a remainder given an unsigned 

** INPUTS:      JSUint64 a: The dividend of the operation

**              JSUint64 b: The quotient of the operation

** OUTPUTS:     JSUint64 *qp: pointer to quotient

**              JSUint64 *rp: pointer to remainder

***********************************************************************/

#define JSLL_UDIVMOD(qp, rp, a, b) \

    (*(qp) = ((JSUint64)(a) / (b)), \

     *(rp) = ((JSUint64)(a) % (b)))



#else  /* !JS_HAVE_LONG_LONG */



#ifdef IS_LITTLE_ENDIAN

#define JSLL_INIT(hi, lo) {JS_INT32(lo), JS_INT32(hi)}

#else

#define JSLL_INIT(hi, lo) {JS_INT32(hi), JS_INT32(lo)}

#endif



#define JSLL_IS_ZERO(a)         (((a).hi == 0) && ((a).lo == 0))

#define JSLL_EQ(a, b)           (((a).hi == (b).hi) && ((a).lo == (b).lo))

#define JSLL_NE(a, b)           (((a).hi != (b).hi) || ((a).lo != (b).lo))

#define JSLL_GE_ZERO(a)         (((a).hi >> 31) == 0)



#ifdef DEBUG

#define JSLL_CMP(a, op, b)      (JS_ASSERT((#op)[1] != '='), JSLL_REAL_CMP(a, op, b))

#define JSLL_CMP(a, op, b)      (JS_ASSERT((#op)[1] != '='), JSLL_REAL_CMP(a, op, b))

#else

#define JSLL_CMP(a, op, b)      JSLL_REAL_CMP(a, op, b)

#define JSLL_CMP(a, op, b)      JSLL_REAL_CMP(a, op, b)

#endif



#define JSLL_REAL_CMP(a,op,b)   (((JSInt32)(a).hi op (JSInt32)(b).hi) || \

                                 (((a).hi == (b).hi) && ((a).lo op (b).lo)))

#define JSLL_REAL_UCMP(a,op,b)  (((a).hi op (b).hi) || \

                                 (((a).hi == (b).hi) && ((a).lo op (b).lo)))



#define JSLL_AND(r, a, b)       ((r).lo = (a).lo & (b).lo, \

                                 (r).hi = (a).hi & (b).hi)

#define JSLL_OR(r, a, b)        ((r).lo = (a).lo | (b).lo, \

                                 (r).hi = (a).hi | (b).hi)

#define JSLL_XOR(r, a, b)       ((r).lo = (a).lo ^ (b).lo, \

                                 (r).hi = (a).hi ^ (b).hi)

#define JSLL_OR2(r, a)          ((r).lo = (r).lo | (a).lo, \

                                 (r).hi = (r).hi | (a).hi)

#define JSLL_NOT(r, a)          ((r).lo = ~(a).lo, \

                                 (r).hi = ~(a).hi)



#define JSLL_NEG(r, a)          ((r).lo = -(JSInt32)(a).lo, \

                                 (r).hi = -(JSInt32)(a).hi - ((r).lo != 0))

#define JSLL_ADD(r, a, b) { \

    JSInt64 _a, _b; \

    _a = a; _b = b; \

    (r).lo = _a.lo + _b.lo; \

    (r).hi = _a.hi + _b.hi + ((r).lo < _b.lo); \

}



#define JSLL_SUB(r, a, b) { \

    JSInt64 _a, _b; \

    _a = a; _b = b; \

    (r).lo = _a.lo - _b.lo; \

    (r).hi = _a.hi - _b.hi - (_a.lo < _b.lo); \

}



#define JSLL_MUL(r, a, b) { \

    JSInt64 _a, _b; \

    _a = a; _b = b; \

    JSLL_MUL32(r, _a.lo, _b.lo); \

    (r).hi += _a.hi * _b.lo + _a.lo * _b.hi; \

}



#define jslo16(a)        ((a) & JS_BITMASK(16))

#define jshi16(a)        ((a) >> 16)



#define JSLL_MUL32(r, a, b) { \

     JSUint32 _a1, _a0, _b1, _b0, _y0, _y1, _y2, _y3; \

     _a1 = jshi16(a), _a0 = jslo16(a); \

     _b1 = jshi16(b), _b0 = jslo16(b); \

     _y0 = _a0 * _b0; \

     _y1 = _a0 * _b1; \

     _y2 = _a1 * _b0; \

     _y3 = _a1 * _b1; \

     _y1 += jshi16(_y0);                         /* can't carry */ \

     _y1 += _y2;                                /* might carry */ \

     if (_y1 < _y2)    \

        _y3 += (JSUint32)(JS_BIT(16));  /* propagate */ \

     (r).lo = (jslo16(_y1) << 16) + jslo16(_y0); \

     (r).hi = _y3 + jshi16(_y1); \

}



#define JSLL_UDIVMOD(qp, rp, a, b)    jsll_udivmod(qp, rp, a, b)



extern JS_PUBLIC_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint64 b);



#define JSLL_DIV(r, a, b) { \

    JSInt64 _a, _b; \

    JSUint32 _negative = (JSInt32)(a).hi < 0; \

    if (_negative) { \

    JSLL_NEG(_a, a); \

    } else { \

    _a = a; \

    } \

    if ((JSInt32)(b).hi < 0) { \

    _negative ^= 1; \

    JSLL_NEG(_b, b); \

    } else { \

    _b = b; \

    } \

    JSLL_UDIVMOD(&(r), 0, _a, _b); \

    if (_negative) \

    JSLL_NEG(r, r); \

}



#define JSLL_MOD(r, a, b) { \

    JSInt64 _a, _b; \

    JSUint32 _negative = (JSInt32)(a).hi < 0; \

    if (_negative) { \

    JSLL_NEG(_a, a); \

    } else { \

    _a = a; \

    } \

    if ((JSInt32)(b).hi < 0) { \

    JSLL_NEG(_b, b); \

    } else { \

    _b = b; \

    } \

    JSLL_UDIVMOD(0, &(r), _a, _b); \

    if (_negative) \

    JSLL_NEG(r, r); \

}



#define JSLL_SHL(r, a, b) { \

    if (b) { \

    JSInt64 _a; \

        _a = a; \

        if ((b) < 32) { \

        (r).lo = _a.lo << ((b) & 31); \

        (r).hi = (_a.hi << ((b) & 31)) | (_a.lo >> (32 - (b))); \

    } else { \

        (r).lo = 0; \

        (r).hi = _a.lo << ((b) & 31); \

    } \

    } else { \

    (r) = (a); \

    } \

}



/* a is an JSInt32, b is JSInt32, r is JSInt64 */

#define JSLL_ISHL(r, a, b) { \

    if (b) { \

    JSInt64 _a; \

    _a.lo = (a); \

    _a.hi = 0; \

        if ((b) < 32) { \

        (r).lo = (a) << ((b) & 31); \

        (r).hi = ((a) >> (32 - (b))); \

    } else { \

        (r).lo = 0; \

        (r).hi = (a) << ((b) & 31); \

    } \

    } else { \

    (r).lo = (a); \

    (r).hi = 0; \

    } \

}



#define JSLL_SHR(r, a, b) { \

    if (b) { \

    JSInt64 _a; \

        _a = a; \

    if ((b) < 32) { \

        (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \

        (r).hi = (JSInt32)_a.hi >> ((b) & 31); \

    } else { \

        (r).lo = (JSInt32)_a.hi >> ((b) & 31); \

        (r).hi = (JSInt32)_a.hi >> 31; \

    } \

    } else { \

    (r) = (a); \

    } \

}



#define JSLL_USHR(r, a, b) { \

    if (b) { \

    JSInt64 _a; \

        _a = a; \

    if ((b) < 32) { \

        (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \

        (r).hi = _a.hi >> ((b) & 31); \

    } else { \

        (r).lo = _a.hi >> ((b) & 31); \

        (r).hi = 0; \

    } \

    } else { \

    (r) = (a); \

    } \

}



#define JSLL_L2I(i, l)        ((i) = (l).lo)

#define JSLL_L2UI(ui, l)        ((ui) = (l).lo)

#define JSLL_L2F(f, l)        { double _d; JSLL_L2D(_d, l); (f) = (JSFloat64)_d; }



#define JSLL_L2D(d, l) { \

    int _negative; \

    JSInt64 _absval; \

 \

    _negative = (l).hi >> 31; \

    if (_negative) { \

    JSLL_NEG(_absval, l); \

    } else { \

    _absval = l; \

    } \

    (d) = (double)_absval.hi * 4.294967296e9 + _absval.lo; \

    if (_negative) \

    (d) = -(d); \

}



#define JSLL_I2L(l, i)        { JSInt32 _i = (i) >> 31; (l).lo = (i); (l).hi = _i; }

#define JSLL_UI2L(l, ui)      ((l).lo = (ui), (l).hi = 0)

#define JSLL_F2L(l, f)        { double _d = (double)f; JSLL_D2L(l, _d); }



#define JSLL_D2L(l, d) { \

    int _negative; \

    double _absval, _d_hi; \

    JSInt64 _lo_d; \

 \

    _negative = ((d) < 0); \

    _absval = _negative ? -(d) : (d); \

 \

    (l).hi = _absval / 4.294967296e9; \

    (l).lo = 0; \

    JSLL_L2D(_d_hi, l); \

    _absval -= _d_hi; \

    _lo_d.hi = 0; \

    if (_absval < 0) { \

    _lo_d.lo = -_absval; \

    JSLL_SUB(l, l, _lo_d); \

    } else { \

    _lo_d.lo = _absval; \

    JSLL_ADD(l, l, _lo_d); \

    } \

 \

    if (_negative) \

    JSLL_NEG(l, l); \

}



#endif /* !JS_HAVE_LONG_LONG */



JS_END_EXTERN_C



#endif /* jslong_h___ */

 

**** End of jslong.h ****

 

**** Start of jsmath.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS math package.

 */

#include "jsstddef.h"

#include "jslibmath.h"

#include <stdlib.h>

#include "jstypes.h"

#include "jslong.h"

#include "prmjtime.h"

#include "jsapi.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jslock.h"

#include "jsmath.h"

#include "jsnum.h"

#include "jsobj.h"



#ifndef M_E

#define M_E		2.7182818284590452354

#endif

#ifndef M_LOG2E

#define M_LOG2E		1.4426950408889634074

#endif

#ifndef M_LOG10E

#define M_LOG10E	0.43429448190325182765

#endif

#ifndef M_LN2

#define M_LN2		0.69314718055994530942

#endif

#ifndef M_LN10

#define M_LN10		2.30258509299404568402

#endif

#ifndef M_PI

#define M_PI		3.14159265358979323846

#endif

#ifndef M_SQRT2

#define M_SQRT2		1.41421356237309504880

#endif

#ifndef M_SQRT1_2

#define M_SQRT1_2	0.70710678118654752440

#endif



static JSConstDoubleSpec math_constants[] = {

    {M_E,       "E",            0, {0,0,0}},

    {M_LOG2E,   "LOG2E",        0, {0,0,0}},

    {M_LOG10E,  "LOG10E",       0, {0,0,0}},

    {M_LN2,     "LN2",          0, {0,0,0}},

    {M_LN10,    "LN10",         0, {0,0,0}},

    {M_PI,      "PI",           0, {0,0,0}},

    {M_SQRT2,   "SQRT2",        0, {0,0,0}},

    {M_SQRT1_2, "SQRT1_2",      0, {0,0,0}},

    {0,0,0,{0,0,0}}

};



static JSClass math_class = {

    "Math",

    0,

    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,

    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,

    JSCLASS_NO_OPTIONAL_MEMBERS

};



static JSBool

math_abs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

        return JS_FALSE;

    z = fd_fabs(x);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_acos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

        return JS_FALSE;

    z = fd_acos(x);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_asin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

        return JS_FALSE;

#ifdef XP_MAC

    if (x == 0)

        return js_NewNumberValue(cx, x, rval);

#endif    

    z = fd_asin(x);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_atan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

        return JS_FALSE;

#ifdef XP_MAC

    if (x == 0)

        return js_NewNumberValue(cx, x, rval);

#endif    

    z = fd_atan(x);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_atan2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, y, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

	return JS_FALSE;

    if (!js_ValueToNumber(cx, argv[1], &y))

	return JS_FALSE;

    z = fd_atan2(x, y);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_ceil(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

        return JS_FALSE;

    z = fd_ceil(x);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_cos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

        return JS_FALSE;

    z = fd_cos(x);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_exp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

        return JS_FALSE;

#ifdef _WIN32

    if (!JSDOUBLE_IS_NaN(x)) {

        if (x == *cx->runtime->jsPositiveInfinity) {

            *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity);

            return JS_TRUE;

        }

        if (x == *cx->runtime->jsNegativeInfinity) {

            *rval = JSVAL_ZERO;

            return JS_TRUE;

        }

    }

#endif

    z = fd_exp(x);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_floor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

        return JS_FALSE;

    z = fd_floor(x);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

        return JS_FALSE;

    z = fd_log(x);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_max(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z = *cx->runtime->jsNegativeInfinity;

    uintN i;



    if (argc == 0) {

        *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity);

        return JS_TRUE;

    }

    for (i = 0; i < argc; i++) {

        if (!js_ValueToNumber(cx, argv[i], &x))

            return JS_FALSE;

        if (JSDOUBLE_IS_NaN(x)) {

            *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);

            return JS_TRUE;

        }

        if ((x==0)&&(x==z)&&(fd_copysign(1.0,z)==-1))

            z = x;

        else

            z = (x > z) ? x : z;

    }

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_min(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z = *cx->runtime->jsPositiveInfinity;

    uintN i;



    if (argc == 0) {

        *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity);

        return JS_TRUE;

    }

    for (i = 0; i < argc; i++) {

        if (!js_ValueToNumber(cx, argv[i], &x))

            return JS_FALSE;

        if (JSDOUBLE_IS_NaN(x)) {

            *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);

            return JS_TRUE;

        }

        if ((x==0)&&(x==z)&&(fd_copysign(1.0,x)==-1))

            z = x;

        else

            z = (x < z) ? x : z;

    }

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, y, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

	return JS_FALSE;

    if (!js_ValueToNumber(cx, argv[1], &y))

        return JS_FALSE;

    z = fd_pow(x, y);

    return js_NewNumberValue(cx, z, rval);

}



/*

 * Math.random() support, lifted from java.util.Random.java.

 */

static void

random_setSeed(JSRuntime *rt, int64 seed)

{

    int64 tmp;



    JSLL_I2L(tmp, 1000);

    JSLL_DIV(seed, seed, tmp);

    JSLL_XOR(tmp, seed, rt->rngMultiplier);

    JSLL_AND(rt->rngSeed, tmp, rt->rngMask);

}



static void

random_init(JSRuntime *rt)

{

    int64 tmp, tmp2;



    /* Do at most once. */

    if (rt->rngInitialized)

	return;

    rt->rngInitialized = JS_TRUE;



    /* rt->rngMultiplier = 0x5DEECE66DL */

    JSLL_ISHL(tmp, 0x5D, 32);

    JSLL_UI2L(tmp2, 0xEECE66DL);

    JSLL_OR(rt->rngMultiplier, tmp, tmp2);



    /* rt->rngAddend = 0xBL */

    JSLL_I2L(rt->rngAddend, 0xBL);



    /* rt->rngMask = (1L << 48) - 1 */

    JSLL_I2L(tmp, 1);

    JSLL_SHL(tmp2, tmp, 48);

    JSLL_SUB(rt->rngMask, tmp2, tmp);



    /* rt->rngDscale = (jsdouble)(1L << 54) */

    JSLL_SHL(tmp2, tmp, 54);

    JSLL_L2D(rt->rngDscale, tmp2);



    /* Finally, set the seed from current time. */

    random_setSeed(rt, PRMJ_Now());

}



static uint32

random_next(JSRuntime *rt, int bits)

{

    int64 nextseed, tmp;

    uint32 retval;



    JSLL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier);

    JSLL_ADD(nextseed, nextseed, rt->rngAddend);

    JSLL_AND(nextseed, nextseed, rt->rngMask);

    rt->rngSeed = nextseed;

    JSLL_USHR(tmp, nextseed, 48 - bits);

    JSLL_L2I(retval, tmp);

    return retval;

}



static jsdouble

random_nextDouble(JSRuntime *rt)

{

    int64 tmp, tmp2;

    jsdouble d;



    JSLL_ISHL(tmp, random_next(rt, 27), 27);

    JSLL_UI2L(tmp2, random_next(rt, 27));

    JSLL_ADD(tmp, tmp, tmp2);

    JSLL_L2D(d, tmp);

    return d / rt->rngDscale;

}



static JSBool

math_random(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSRuntime *rt;

    jsdouble z;



    rt = cx->runtime;

    JS_LOCK_RUNTIME(rt);

    random_init(rt);

    z = random_nextDouble(rt);

    JS_UNLOCK_RUNTIME(rt);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_round(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

        return JS_FALSE;

    z = fd_copysign(fd_floor(x + 0.5), x);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_sin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

        return JS_FALSE;

    z = fd_sin(x);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_sqrt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

        return JS_FALSE;

    z = fd_sqrt(x);

    return js_NewNumberValue(cx, z, rval);

}



static JSBool

math_tan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x, z;



    if (!js_ValueToNumber(cx, argv[0], &x))

        return JS_FALSE;

    z = fd_tan(x);

    return js_NewNumberValue(cx, z, rval);

}



#if JS_HAS_TOSOURCE

static JSBool

math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	      jsval *rval)

{

    *rval = ATOM_KEY(cx->runtime->atomState.MathAtom);

    return JS_TRUE;

}

#endif



static JSFunctionSpec math_static_methods[] = {

#if JS_HAS_TOSOURCE

    {js_toSource_str,   math_toSource,		0, 0, 0},

#endif

    {"abs",		math_abs,		1, 0, 0},

    {"acos",		math_acos,		1, 0, 0},

    {"asin",		math_asin,		1, 0, 0},

    {"atan",		math_atan,		1, 0, 0},

    {"atan2",		math_atan2,		2, 0, 0},

    {"ceil",		math_ceil,		1, 0, 0},

    {"cos",		math_cos,		1, 0, 0},

    {"exp",		math_exp,		1, 0, 0},

    {"floor",		math_floor,		1, 0, 0},

    {"log",		math_log,		1, 0, 0},

    {"max",		math_max,		2, 0, 0},

    {"min",		math_min,		2, 0, 0},

    {"pow",		math_pow,		2, 0, 0},

    {"random",		math_random,		0, 0, 0},

    {"round",		math_round,		1, 0, 0},

    {"sin",		math_sin,		1, 0, 0},

    {"sqrt",		math_sqrt,		1, 0, 0},

    {"tan",		math_tan,		1, 0, 0},

    {0,0,0,0,0}

};



JSObject *

js_InitMathClass(JSContext *cx, JSObject *obj)

{

    JSObject *Math;

    

    Math = JS_DefineObject(cx, obj, "Math", &math_class, NULL, 0);

    if (!Math)

        return NULL;

    if (!JS_DefineFunctions(cx, Math, math_static_methods))

        return NULL;

    if (!JS_DefineConstDoubles(cx, Math, math_constants))

        return NULL;

    return Math;

}

 

**** End of jsmath.c ****

 

**** Start of jsmath.h ****

 

/* 

 * The contents of this file are subject to the Netscape 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/NPL/

 * 

 * 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 Netscape are

 * Copyright (C) 1998-1999 Netscape Communications Corporation. All

 * Rights Reserved.

 * 

 * Contributor(s):

 * 

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/* -*- Mode: C; tab-width: 8 -*-

 * Copyright (C) 1998-1999 Netscape Communications Corporation, All Rights Reserved.

 */



#ifndef jsmath_h___

#define jsmath_h___

/*

 * JS math functions.

 */



JS_BEGIN_EXTERN_C



extern JSObject *

js_InitMathClass(JSContext *cx, JSObject *obj);



JS_END_EXTERN_C



#endif /* jsmath_h___ */

 

**** End of jsmath.h ****

 

**** Start of jsnum.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 *

 * This Original Code has been modified by IBM Corporation.

 * Modifications made by IBM described herein are

 * Copyright (c) International Business Machines

 * Corporation, 2000

 *

 * Modifications to Mozilla code or documentation

 * identified per MPL Section 3.3

 *

 * Date         Modified by     Description of modification

 * 05/15/2000  IBM Corp.       Modified OS/2 floating point init.

 */



/*

 * JS number type and wrapper class.

 */

#include "jsstddef.h"

#include <errno.h>

#ifdef XP_PC

#include <float.h>

#endif

#include <math.h>

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jsapi.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsdtoa.h"

#include "jsgc.h"

#include "jsinterp.h"

#include "jsnum.h"

#include "jsobj.h"

#include "jsopcode.h"

#include "jsprf.h"

#include "jsstr.h"



union dpun {

    struct {

#ifdef IS_LITTLE_ENDIAN

	uint32 lo, hi;

#else

	uint32 hi, lo;

#endif

    } s;

    jsdouble d;

};



static JSBool

num_isNaN(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x;



    if (!js_ValueToNumber(cx, argv[0], &x))

	return JS_FALSE;

    *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_NaN(x));

    return JS_TRUE;

}



static JSBool

num_isFinite(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble x;



    if (!js_ValueToNumber(cx, argv[0], &x))

	return JS_FALSE;

    *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_FINITE(x));

    return JS_TRUE;

}



static JSBool

num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str;

    jsdouble d;

    const jschar *ep;



    str = js_ValueToString(cx, argv[0]);

    if (!str)

	return JS_FALSE;

    if (!js_strtod(cx, str->chars, &ep, &d))

	return JS_FALSE;

    if (ep == str->chars) {

	*rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);

	return JS_TRUE;

    }

    return js_NewNumberValue(cx, d, rval);

}



/* See ECMA 15.1.2.2. */

static JSBool

num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str;

    jsint radix;

    jsdouble d;

    const jschar *ep;



    str = js_ValueToString(cx, argv[0]);

    if (!str)

	return JS_FALSE;



    if (argc > 1) {

	if (!js_ValueToECMAInt32(cx, argv[1], &radix))

	    return JS_FALSE;

    } else

	radix = 0;



    if (radix != 0 && (radix < 2 || radix > 36)) {

	*rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);

	return JS_TRUE;

    }

    if (!js_strtointeger(cx, str->chars, &ep, radix, &d))

	return JS_FALSE;

    if (ep == str->chars) {

	*rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN);

	return JS_TRUE;

    }

    return js_NewNumberValue(cx, d, rval);

}



const char js_Infinity_str[]   = "Infinity";

const char js_NaN_str[]        = "NaN";

const char js_isNaN_str[]      = "isNaN";

const char js_isFinite_str[]   = "isFinite";

const char js_parseFloat_str[] = "parseFloat";

const char js_parseInt_str[]   = "parseInt";



static JSFunctionSpec number_functions[] = {

    {"isNaN",           num_isNaN,              1,0,0},

    {"isFinite",        num_isFinite,           1,0,0},

    {"parseFloat",      num_parseFloat,         1,0,0},

    {"parseInt",        num_parseInt,           2,0,0},

    {0,0,0,0,0}

};



static JSClass number_class = {

    "Number",

    JSCLASS_HAS_PRIVATE,

    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,

    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,

    JSCLASS_NO_OPTIONAL_MEMBERS

};



static JSBool

Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsdouble d;

    jsval v;



    if (argc != 0) {

	if (!js_ValueToNumber(cx, argv[0], &d))

	    return JS_FALSE;

    } else {

	d = 0.0;

    }

    if (!js_NewNumberValue(cx, d, &v))

	return JS_FALSE;

    if (!cx->fp->constructing) {

	*rval = v;

	return JS_TRUE;

    }

    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v);

    return JS_TRUE;

}



#if JS_HAS_TOSOURCE

static JSBool

num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsval v;

    jsdouble d;

    char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr;

    char buf[64];

    JSString *str;



    if (!JS_InstanceOf(cx, obj, &number_class, argv))

	return JS_FALSE;

    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);

    JS_ASSERT(JSVAL_IS_NUMBER(v));

    d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);

    numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d);

    if (!numStr) {

	JS_ReportOutOfMemory(cx);

	return JS_FALSE;

    }

    JS_snprintf(buf, sizeof buf, "(new %s(%s))", number_class.name, numStr);

    str = JS_NewStringCopyZ(cx, buf);

    if (!str)

	return JS_FALSE;

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}

#endif



static JSBool

num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsval v;

    jsdouble d;

    jsint base;

    JSString *str;



    if (!JS_InstanceOf(cx, obj, &number_class, argv))

	return JS_FALSE;

    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);

    JS_ASSERT(JSVAL_IS_NUMBER(v));

    d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);

    base = 10;

    if (argc != 0) {

	if (!js_ValueToECMAInt32(cx, argv[0], &base))

	    return JS_FALSE;

	if (base < 2 || base > 36) {

	    char numBuf[12];

	    JS_snprintf(numBuf, sizeof numBuf, "%ld", (long) base);

	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX,

	    			 numBuf);

	    return JS_FALSE;

	}

    }

    if (base == 10)

	str = js_NumberToString(cx, d);

    else {

	char *dStr = JS_dtobasestr(base, d);

	if (!dStr) {

	    JS_ReportOutOfMemory(cx);

	    return JS_FALSE;

	}

	str = JS_NewStringCopyZ(cx, dStr);

	free(dStr);

    }

    if (!str)

	return JS_FALSE;

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc,

                   jsval *argv, jsval *rval)

{

/*

 *  For now, forcibly ignore the first (or any) argument and return toString().

 *  ECMA allows this, although it doesn't 'encourage it'.

 *  [The first argument is being reserved by ECMA and we don't want it confused

 *  with a radix]

 */

    return num_toString(cx, obj, 0, argv, rval);

}



static JSBool

num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    if (!JS_InstanceOf(cx, obj, &number_class, argv))

	return JS_FALSE;

    *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);

    return JS_TRUE;

}





#if JS_HAS_NUMBER_FORMATS

#define MAX_PRECISION 100



static JSBool

num_to(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, JSDToStrMode zeroArgMode,

       JSDToStrMode oneArgMode, jsint precisionMin, jsint precisionMax, jsint precisionOffset)

{

    jsval v;

    jsdouble d, precision;

    JSString *str;

    char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)], *numStr; /* Use MAX_PRECISION+1 because precisionOffset can be 1 */



    if (!JS_InstanceOf(cx, obj, &number_class, argv))

	return JS_FALSE;

    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);

    JS_ASSERT(JSVAL_IS_NUMBER(v));

    d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v);



    if (JSVAL_IS_VOID(argv[0])) {

	precision = 0.0;

	oneArgMode = zeroArgMode;

    } else {

	if (!js_ValueToNumber(cx, argv[0], &precision))

	    return JS_FALSE;

	precision = js_DoubleToInteger(precision);

        if (precision < precisionMin || precision > precisionMax) {

            numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, precision);

            if (!numStr)

                JS_ReportOutOfMemory(cx);

            else

                JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr);

            return JS_FALSE;

        }

    }



    numStr = JS_dtostr(buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d);

    if (!numStr) {

	JS_ReportOutOfMemory(cx);

	return JS_FALSE;

    }

    str = JS_NewStringCopyZ(cx, numStr);

    if (!str)

	return JS_FALSE;

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

num_toFixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */

    return num_to(cx, obj, argc, argv, rval, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0);

}



static JSBool

num_toExponential(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */

    return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, MAX_PRECISION, 1);

}



static JSBool

num_toPrecision(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */

    return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0);

}

#endif /* JS_HAS_NUMBER_FORMATS */





static JSFunctionSpec number_methods[] = {

#if JS_HAS_TOSOURCE

    {js_toSource_str,       num_toSource,       0,0,0},

#endif

    {js_toString_str,	    num_toString,       0,0,0},

    {js_toLocaleString_str, num_toLocaleString, 0,0,0},

    {js_valueOf_str,	    num_valueOf,        0,0,0},

#if JS_HAS_NUMBER_FORMATS

    {"toFixed",             num_toFixed,        1,0,0},

    {"toExponential",       num_toExponential,  1,0,0},

    {"toPrecision",         num_toPrecision,    1,0,0},

#endif

    {0,0,0,0,0}

};



/* NB: Keep this in synch with number_constants[]. */

enum nc_slot {

    NC_NaN,

    NC_POSITIVE_INFINITY,

    NC_NEGATIVE_INFINITY,

    NC_MAX_VALUE,

    NC_MIN_VALUE,

    NC_LIMIT

};



/*

 * Some to most C compilers forbid spelling these at compile time, or barf

 * if you try, so all but MAX_VALUE are set up by js_InitRuntimeNumberState

 * using union dpun.

 */

static JSConstDoubleSpec number_constants[] = {

    {0,                         js_NaN_str,          0,{0,0,0}},

    {0,                         "POSITIVE_INFINITY", 0,{0,0,0}},

    {0,                         "NEGATIVE_INFINITY", 0,{0,0,0}},

    {1.7976931348623157E+308,   "MAX_VALUE",         0,{0,0,0}},

    {0,                         "MIN_VALUE",         0,{0,0,0}},

    {0,0,0,{0,0,0}}

};



static jsdouble NaN;



#if !defined __MWERKS__ && defined XP_PC && (defined _M_IX86 || defined __GNUC__)



/*

 * On Alpha platform this is handled via Compiler option.

 */

#define FIX_FPU() _control87(MCW_EM, MCW_EM)



#else



#define FIX_FPU() ((void)0)



#endif



JSBool

js_InitRuntimeNumberState(JSContext *cx)

{

    JSRuntime *rt;

    union dpun u;



    rt = cx->runtime;

    JS_ASSERT(!rt->jsNaN);



    FIX_FPU();



    u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK;

    u.s.lo = 0xffffffff;

    number_constants[NC_NaN].dval = NaN = u.d;

    rt->jsNaN = js_NewDouble(cx, NaN);

    if (!rt->jsNaN || !js_LockGCThing(cx, rt->jsNaN))

        return JS_FALSE;



    u.s.hi = JSDOUBLE_HI32_EXPMASK;

    u.s.lo = 0x00000000;

    number_constants[NC_POSITIVE_INFINITY].dval = u.d;

    rt->jsPositiveInfinity = js_NewDouble(cx, u.d);

    if (!rt->jsPositiveInfinity ||

        !js_LockGCThing(cx, rt->jsPositiveInfinity)) {

        return JS_FALSE;

    }



    u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK;

    u.s.lo = 0x00000000;

    number_constants[NC_NEGATIVE_INFINITY].dval = u.d;

    rt->jsNegativeInfinity = js_NewDouble(cx, u.d);

    if (!rt->jsNegativeInfinity ||

        !js_LockGCThing(cx, rt->jsNegativeInfinity)) {

        return JS_FALSE;

    }



    u.s.hi = 0;

    u.s.lo = 1;

    number_constants[NC_MIN_VALUE].dval = u.d;



    return JS_TRUE;

}



void

js_FinishRuntimeNumberState(JSContext *cx)

{

    JSRuntime *rt = cx->runtime;



    js_UnlockGCThing(cx, rt->jsNaN);

    js_UnlockGCThing(cx, rt->jsNegativeInfinity);

    js_UnlockGCThing(cx, rt->jsPositiveInfinity);



    rt->jsNaN = NULL;

    rt->jsNegativeInfinity = NULL;

    rt->jsPositiveInfinity = NULL;

}



JSObject *

js_InitNumberClass(JSContext *cx, JSObject *obj)

{

    JSObject *proto, *ctor;

    JSRuntime *rt;



    /* XXX must do at least once per new thread, so do it per JSContext... */

    FIX_FPU();



    if (!JS_DefineFunctions(cx, obj, number_functions))

	return NULL;



    proto = JS_InitClass(cx, obj, NULL, &number_class, Number, 1,

			 NULL, number_methods, NULL, NULL);

    if (!proto || !(ctor = JS_GetConstructor(cx, proto)))

	return NULL;

    OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_ZERO);

    if (!JS_DefineConstDoubles(cx, ctor, number_constants))

	return NULL;



    /* ECMA 15.1.1.1 */

    rt = cx->runtime;

    if (!JS_DefineProperty(cx, obj, js_NaN_str, DOUBLE_TO_JSVAL(rt->jsNaN),

			   NULL, NULL, JSPROP_PERMANENT)) {

	return NULL;

    }



    /* ECMA 15.1.1.2 */

    if (!JS_DefineProperty(cx, obj, js_Infinity_str,

			   DOUBLE_TO_JSVAL(rt->jsPositiveInfinity),

			   NULL, NULL, JSPROP_PERMANENT)) {

	return NULL;

    }

    return proto;

}



jsdouble *

js_NewDouble(JSContext *cx, jsdouble d)

{

    jsdouble *dp;



    dp = (jsdouble *) js_AllocGCThing(cx, GCX_DOUBLE);

    if (!dp)

	return NULL;

    *dp = d;

    return dp;

}



void

js_FinalizeDouble(JSContext *cx, jsdouble *dp)

{

    *dp = NaN;

}



JSBool

js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval)

{

    jsdouble *dp;



    dp = js_NewDouble(cx, d);

    if (!dp)

	return JS_FALSE;

    *rval = DOUBLE_TO_JSVAL(dp);

    return JS_TRUE;

}



JSBool

js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)

{

    jsint i;



    if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) {

	*rval = INT_TO_JSVAL(i);

    } else {

	if (!js_NewDoubleValue(cx, d, rval))

	    return JS_FALSE;

    }

    return JS_TRUE;

}



JSObject *

js_NumberToObject(JSContext *cx, jsdouble d)

{

    JSObject *obj;

    jsval v;



    obj = js_NewObject(cx, &number_class, NULL, NULL);

    if (!obj)

	return NULL;

    if (!js_NewNumberValue(cx, d, &v)) {

	cx->newborn[GCX_OBJECT] = NULL;

	return NULL;

    }

    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v);

    return obj;

}



JSString *

js_NumberToString(JSContext *cx, jsdouble d)

{

    jsint i;

    char buf[DTOSTR_STANDARD_BUFFER_SIZE];

    char *numStr = buf;



    if (JSDOUBLE_IS_INT(d, i))

	JS_snprintf(buf, sizeof buf, "%ld", (long)i);

    else {

	numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d);

	if (!numStr) {

	    JS_ReportOutOfMemory(cx);

	    return NULL;

	}

    }

    return JS_NewStringCopyZ(cx, numStr);

}



JSBool

js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp)

{

    JSObject *obj;

    JSString *str;

    const jschar *ep;



    if (JSVAL_IS_OBJECT(v)) {

	obj = JSVAL_TO_OBJECT(v);

	if (!obj) {

	    *dp = 0;

	    return JS_TRUE;

	}

	if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_NUMBER, &v))

	    return JS_FALSE;

    }

    if (JSVAL_IS_INT(v)) {

	*dp = (jsdouble)JSVAL_TO_INT(v);

    } else if (JSVAL_IS_DOUBLE(v)) {

	*dp = *JSVAL_TO_DOUBLE(v);

    } else if (JSVAL_IS_STRING(v)) {

	str = JSVAL_TO_STRING(v);

	errno = 0;

	/*

         * Note that ECMA doesn't treat a string beginning with a '0' as an

         * octal number here.  This works because all such numbers will be

         * interpreted as decimal by js_strtod and will never get passed to

         * js_strtointeger (which would interpret them as octal).

         */

         if ((!js_strtod(cx, str->chars, &ep, dp) ||

              js_SkipWhiteSpace(ep) != str->chars + str->length) &&

	     (!js_strtointeger(cx, str->chars, &ep, 0, dp) ||

              js_SkipWhiteSpace(ep) != str->chars + str->length)) {

	    goto badstr;

	}

    } else if (JSVAL_IS_BOOLEAN(v)) {

	*dp = JSVAL_TO_BOOLEAN(v) ? 1 : 0;

    } else {

#if JS_BUG_FALLIBLE_TONUM

	str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);

badstr:

	if (str) {

	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NAN,

				 JS_GetStringBytes(str));



	}

	return JS_FALSE;

#else

badstr:

	*dp = *cx->runtime->jsNaN;

#endif

    }

    return JS_TRUE;

}



JSBool

js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip)

{

    jsdouble d;



    if (!js_ValueToNumber(cx, v, &d))

	return JS_FALSE;

    return js_DoubleToECMAInt32(cx, d, ip);

}



JSBool

js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip)

{

    jsdouble two32 = 4294967296.0;

    jsdouble two31 = 2147483648.0;



    if (!JSDOUBLE_IS_FINITE(d) || d == 0) {

	*ip = 0;

	return JS_TRUE;

    }

    d = fmod(d, two32);

    d = d >= 0 ? d : d + two32;

    if (d >= two31)

	*ip = (int32)(d - two32);

    else

	*ip = (int32)d;

    return JS_TRUE;

}



JSBool

js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip)

{

    jsdouble d;



    if (!js_ValueToNumber(cx, v, &d))

	return JS_FALSE;

    return js_DoubleToECMAUint32(cx, d, ip);

}



JSBool

js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip)

{

    JSBool neg;

    jsdouble two32 = 4294967296.0;



    if (!JSDOUBLE_IS_FINITE(d) || d == 0) {

	*ip = 0;

	return JS_TRUE;

    }



    neg = (d < 0);

    d = floor(neg ? -d : d);

    d = neg ? -d : d;



    d = fmod(d, two32);



    d = d >= 0 ? d : d + two32;

    *ip = (uint32)d;

    return JS_TRUE;

}



JSBool

js_ValueToInt32(JSContext *cx, jsval v, int32 *ip)

{

    jsdouble d;

    JSString *str;



    if (!js_ValueToNumber(cx, v, &d))

	return JS_FALSE;

    if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) {

	str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);

	if (str) {

	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

				 JSMSG_CANT_CONVERT, JS_GetStringBytes(str));



	}

	return JS_FALSE;

    }

    *ip = (int32)floor(d + 0.5);     /* Round to nearest */

    return JS_TRUE;

}



JSBool

js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip)

{

    jsdouble d;

    jsuint i, m;

    JSBool neg;



    if (!js_ValueToNumber(cx, v, &d))

	return JS_FALSE;

    if (d == 0 || !JSDOUBLE_IS_FINITE(d)) {

	*ip = 0;

	return JS_TRUE;

    }

    i = (jsuint)d;

    if ((jsdouble)i == d) {

	*ip = (uint16)i;

	return JS_TRUE;

    }

    neg = (d < 0);

    d = floor(neg ? -d : d);

    d = neg ? -d : d;

    m = JS_BIT(16);

    d = fmod(d, (double)m);

    if (d < 0)

	d += m;

    *ip = (uint16) d;

    return JS_TRUE;

}



jsdouble

js_DoubleToInteger(jsdouble d)

{

    JSBool neg;



    if (d == 0)

	return d;

    if (!JSDOUBLE_IS_FINITE(d)) {

	if (JSDOUBLE_IS_NaN(d))

	    return 0;

	return d;

    }

    neg = (d < 0);

    d = floor(neg ? -d : d);

    return neg ? -d : d;

}





JSBool

js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp)

{

    size_t i;

    char *cstr, *istr, *estr;

    JSBool negative;

    jsdouble d;

    const jschar *s1 = js_SkipWhiteSpace(s);

    size_t length = js_strlen(s1);



    cstr = (char *) malloc(length + 1);

    if (!cstr)

	return JS_FALSE;

    for (i = 0; i <= length; i++) {

	if (s1[i] >> 8) {

	    cstr[i] = 0;

	    break;

	}

	cstr[i] = (char)s1[i];

    }



    istr = cstr;

    if ((negative = (*istr == '-')) != 0 || *istr == '+')

	istr++;

    if (!strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) {

	d = *(negative ? cx->runtime->jsNegativeInfinity : cx->runtime->jsPositiveInfinity);

	estr = istr + 8;

    } else {

	errno = 0;

	d = JS_strtod(cstr, &estr);

	if (errno == ERANGE) {

	    if (d == HUGE_VAL)

		d = *cx->runtime->jsPositiveInfinity;

	    else if (d == -HUGE_VAL)

		d = *cx->runtime->jsNegativeInfinity;

        }

#ifdef HPUX

        if (d == 0.0 && negative) {

            /*

             * "-0", "-1e-2000" come out as positive zero

    		 * here on HPUX. Force a negative zero instead.

             */

            JSDOUBLE_HI32(d) = JSDOUBLE_HI32_SIGNBIT;

            JSDOUBLE_LO32(d) = 0;

        }

#endif

    }



    free(cstr);

    i = estr - cstr;

    *ep = i ? s1 + i : s;

    *dp = d;

    return JS_TRUE;

}



struct BinaryDigitReader

{

    uintN base;			/* Base of number; must be a power of 2 */

    uintN digit;		/* Current digit value in radix given by base */

    uintN digitMask;		/* Mask to extract the next bit from digit */

    const jschar *digits;	/* Pointer to the remaining digits */

    const jschar *end;		/* Pointer to first non-digit */

};



/* Return the next binary digit from the number or -1 if done */

static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr)

{

    intN bit;



    if (bdr->digitMask == 0) {

	uintN c;



	if (bdr->digits == bdr->end)

	    return -1;



	c = *bdr->digits++;

	if ('0' <= c && c <= '9')

	    bdr->digit = c - '0';

	else if ('a' <= c && c <= 'z')

	    bdr->digit = c - 'a' + 10;

	else bdr->digit = c - 'A' + 10;

	bdr->digitMask = bdr->base >> 1;

    }

    bit = (bdr->digit & bdr->digitMask) != 0;

    bdr->digitMask >>= 1;

    return bit;

}



JSBool

js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, jsdouble *dp)

{

    JSBool negative;

    jsdouble value;

    const jschar *start;

    const jschar *s1 = js_SkipWhiteSpace(s);



    if ((negative = (*s1 == '-')) != 0 || *s1 == '+')

	s1++;



    if (base == 0) {

	/* No base supplied, or some base that evaluated to 0. */

	if (*s1 == '0') {

	    /* It's either hex or octal; only increment char if str isn't '0' */

	    if (s1[1] == 'X' || s1[1] == 'x') { /* Hex */

		s1 += 2;

		base = 16;

	    } else {    /* Octal */

		base = 8;

            }

	} else {

	    base = 10; /* Default to decimal. */

        }

    } else if (base == 16 && *s1 == '0' && (s1[1] == 'X' || s1[1] == 'x')) {

	/* If base is 16, ignore hex prefix. */

	s1 += 2;

    }



    /*

     * Done with the preliminaries; find some prefix of the string that's

     * a number in the given base.

     */

    start = s1; /* Mark - if string is empty, we return NaN. */

    value = 0.0;

    while (1) {

	uintN digit;

	jschar c = *s1;

	if ('0' <= c && c <= '9')

	    digit = c - '0';

	else if ('a' <= c && c <= 'z')

	    digit = c - 'a' + 10;

	else if ('A' <= c && c <= 'Z')

	    digit = c - 'A' + 10;

	else

	    break;

	if (digit >= (uintN)base)

	    break;

	value = value * base + digit;

	s1++;

    }



    if (value >= 9007199254740992.0) {

	if (base == 10) {

            /*

             * If we're accumulating a decimal number and the number is >=

             * 2^53, then the result from the repeated multiply-add above may

             * be inaccurate.  Call JS_strtod to get the correct answer.

             */

	    size_t i;

	    size_t length = s1 - start;

	    char *cstr = (char *) malloc(length + 1);

	    char *estr;



	    if (!cstr)

		return JS_FALSE;

	    for (i = 0; i != length; i++)

		cstr[i] = (char)start[i];

	    cstr[length] = 0;



	    errno = 0;

	    value = JS_strtod(cstr, &estr);

	    if (errno == ERANGE && value == HUGE_VAL)

		value = *cx->runtime->jsPositiveInfinity;

	    free(cstr);

	} else if ((base & (base - 1)) == 0) {

            /*

             * The number may also be inaccurate for power-of-two bases.  This

             * happens if the addition in value * base + digit causes a round-

             * down to an even least significant mantissa bit when the first

             * dropped bit is a one.  If any of the following digits in the

             * number (which haven't been added in yet) are nonzero, then the

             * correct action would have been to round up instead of down.  An

             * example occurs when reading the number 0x1000000000000081, which

             * rounds to 0x1000000000000000 instead of 0x1000000000000100.

             */

	    struct BinaryDigitReader bdr;

	    intN bit, bit2;

	    intN j;



	    bdr.base = base;

	    bdr.digitMask = 0;

	    bdr.digits = start;

	    bdr.end = s1;

	    value = 0.0;



	    /* Skip leading zeros. */

	    do {

		bit = GetNextBinaryDigit(&bdr);

	    } while (bit == 0);



	    if (bit == 1) {

		/* Gather the 53 significant bits (including the leading 1) */

		value = 1.0;

		for (j = 52; j; j--) {

		    bit = GetNextBinaryDigit(&bdr);

		    if (bit < 0)

			goto done;

		    value = value*2 + bit;

		}

		/* bit2 is the 54th bit (the first dropped from the mantissa) */

		bit2 = GetNextBinaryDigit(&bdr);

		if (bit2 >= 0) {

		    jsdouble factor = 2.0;

		    intN sticky = 0;  /* sticky is 1 if any bit beyond the 54th is 1 */

		    intN bit3;



		    while ((bit3 = GetNextBinaryDigit(&bdr)) >= 0) {

			sticky |= bit3;

			factor *= 2;

		    }

		    value += bit2 & (bit | sticky);

		    value *= factor;

		}

	      done:;

	    }

	}

    }

    /* We don't worry about inaccurate numbers for any other base. */



    if (s1 == start) {

	*dp = 0.0;

	*ep = s;

    } else {

	*dp = negative ? -value : value;

	*ep = s1;

    }

    return JS_TRUE;

}

 

**** End of jsnum.c ****

 

**** Start of jsnum.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsnum_h___

#define jsnum_h___

/*

 * JS number (IEEE double) interface.

 *

 * JS numbers are optimistically stored in the top 31 bits of 32-bit integers,

 * but floating point literals, results that overflow 31 bits, and division and

 * modulus operands and results require a 64-bit IEEE double.  These are GC'ed

 * and pointed to by 32-bit jsvals on the stack and in object properties.

 *

 * When a JS number is treated as an object (followed by . or []), the runtime

 * wraps it with a JSObject whose valueOf method returns the unwrapped number.

 */



JS_BEGIN_EXTERN_C



#ifdef IS_LITTLE_ENDIAN

#define JSDOUBLE_HI32(x)        (((uint32 *)&(x))[1])

#define JSDOUBLE_LO32(x)        (((uint32 *)&(x))[0])

#else

#define JSDOUBLE_HI32(x)        (((uint32 *)&(x))[0])

#define JSDOUBLE_LO32(x)        (((uint32 *)&(x))[1])

#endif

#define JSDOUBLE_HI32_SIGNBIT   0x80000000

#define JSDOUBLE_HI32_EXPMASK   0x7ff00000

#define JSDOUBLE_HI32_MANTMASK  0x000fffff



#define JSDOUBLE_IS_NaN(x)                                                    \

    ((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) == JSDOUBLE_HI32_EXPMASK &&   \

     (JSDOUBLE_LO32(x) || (JSDOUBLE_HI32(x) & JSDOUBLE_HI32_MANTMASK)))



#define JSDOUBLE_IS_INFINITE(x)                                               \

    ((JSDOUBLE_HI32(x) & ~JSDOUBLE_HI32_SIGNBIT) == JSDOUBLE_HI32_EXPMASK &&   \

     !JSDOUBLE_LO32(x))



#define JSDOUBLE_IS_FINITE(x)                                                 \

    ((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) != JSDOUBLE_HI32_EXPMASK)



#define JSDOUBLE_IS_NEGZERO(d)  (JSDOUBLE_HI32(d) == JSDOUBLE_HI32_SIGNBIT && \

				 JSDOUBLE_LO32(d) == 0)



/*

 * JSDOUBLE_IS_INT first checks that d is neither NaN nor infinite, to avoid

 * raising SIGFPE on platforms such as Alpha Linux, then (only if the cast is

 * safe) leaves i as (jsint)d.  This also avoid anomalous NaN floating point

 * comparisons under MSVC.

 */

#define JSDOUBLE_IS_INT(d, i) (JSDOUBLE_IS_FINITE(d)                          \

                               && !JSDOUBLE_IS_NEGZERO(d)                     \

			       && ((d) == (i = (jsint)(d))))



/* Initialize number constants and runtime state for the first context. */

extern JSBool

js_InitRuntimeNumberState(JSContext *cx);



extern void

js_FinishRuntimeNumberState(JSContext *cx);



/* Initialize the Number class, returning its prototype object. */

extern JSObject *

js_InitNumberClass(JSContext *cx, JSObject *obj);



/*

 * String constants for global function names, used in jsapi.c and jsnum.c.

 */

extern const char js_Infinity_str[];

extern const char js_NaN_str[];

extern const char js_isNaN_str[];

extern const char js_isFinite_str[];

extern const char js_parseFloat_str[];

extern const char js_parseInt_str[];



/* GC-allocate a new JS number. */

extern jsdouble *

js_NewDouble(JSContext *cx, jsdouble d);



extern void

js_FinalizeDouble(JSContext *cx, jsdouble *dp);



extern JSBool

js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval);



extern JSBool

js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval);



/* Construct a Number instance that wraps around d. */

extern JSObject *

js_NumberToObject(JSContext *cx, jsdouble d);



/* Convert a number to a GC'ed string. */

extern JSString *

js_NumberToString(JSContext *cx, jsdouble d);



/*

 * Convert a value to a number, returning false after reporting any error,

 * otherwise returning true with *dp set.

 */

extern JSBool

js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp);



/*

 * Convert a value or a double to an int32, according to the ECMA rules

 * for ToInt32.

 */

extern JSBool

js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip);



extern JSBool

js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip);



/*

 * Convert a value or a double to a uint32, according to the ECMA rules

 * for ToUint32.

 */

extern JSBool

js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip);



extern JSBool

js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip);



/*

 * Convert a value to a number, then to an int32 if it fits by rounding to

 * nearest; but failing with an error report if the double is out of range

 * or unordered.

 */

extern JSBool

js_ValueToInt32(JSContext *cx, jsval v, int32 *ip);



/*

 * Convert a value to a number, then to a uint16 according to the ECMA rules

 * for ToUint16.

 */

extern JSBool

js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip);



/*

 * Convert a jsdouble to an integral number, stored in a jsdouble.

 * If d is NaN, return 0.  If d is an infinity, return it without conversion.

 */

extern jsdouble

js_DoubleToInteger(jsdouble d);



/*

 * Similar to strtod except that replaces overflows with infinities of the correct

 * sign and underflows with zeros of the correct sign.  Guaranteed to return the

 * closest double number to the given input in dp.

 * Also allows inputs of the form [+|-]Infinity, which produce an infinity of the

 * appropriate sign.  The case of the "Infinity" string must match.

 * If the string does not have a number in it, set *ep to s and return 0.0 in dp.

 * Return false if out of memory.

 */

extern JSBool

js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp);



/*

 * Similar to strtol except that handles integers of arbitrary size.  Guaranteed to

 * return the closest double number to the given input when radix is 10 or a power of 2.

 * May experience roundoff errors for very large numbers of a different radix.

 * If the string does not have a number in it, set *ep to s and return 0.0 in dp.

 * Return false if out of memory.

 */

extern JSBool

js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint radix, jsdouble *dp);



JS_END_EXTERN_C



#endif /* jsnum_h___ */

 

**** End of jsnum.h ****

 

**** Start of jsobj.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS object implementation.

 */

#include "jsstddef.h"

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsarena.h" /* Added by JSIFY */

#include "jsutil.h" /* Added by JSIFY */

#include "jshash.h" /* Added by JSIFY */

#include "jsprf.h"

#include "jsapi.h"

#include "jsatom.h"

#include "jsbool.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsfun.h"

#include "jsgc.h"

#include "jsinterp.h"

#include "jslock.h"

#include "jsnum.h"

#include "jsobj.h"

#include "jsscope.h"

#include "jsscript.h"

#include "jsstr.h"

#include "jsopcode.h"



#if JS_HAS_OBJ_WATCHPOINT

#include "jsdbgapi.h"

#endif



#ifdef JS_THREADSAFE

#define NATIVE_DROP_PROPERTY js_DropProperty



extern void

js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);

#else

#define NATIVE_DROP_PROPERTY NULL

#endif



#ifdef XP_MAC

#pragma export on

#endif



JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {

    js_NewObjectMap,        js_DestroyObjectMap,

#if defined JS_THREADSAFE && defined DEBUG

    _js_LookupProperty,     js_DefineProperty,

#else

    js_LookupProperty,      js_DefineProperty,

#endif

    js_GetProperty,         js_SetProperty,

    js_GetAttributes,       js_SetAttributes,

    js_DeleteProperty,      js_DefaultValue,

    js_Enumerate,           js_CheckAccess,

    NULL,                   NATIVE_DROP_PROPERTY,

    js_Call,                js_Construct,

    NULL,                   js_HasInstance,

    js_SetProtoOrParent,    js_SetProtoOrParent,

    js_Mark,                js_Clear,

    0,                      0

};



#ifdef XP_MAC

#pragma export off

#endif



JSClass js_ObjectClass = {

    js_Object_str,

    0,

    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,

    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,

    JSCLASS_NO_OPTIONAL_MEMBERS

};



#if JS_HAS_OBJ_PROTO_PROP



static JSBool

obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);



static JSBool

obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);



static JSBool

obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);



static JSPropertySpec object_props[] = {

    /* These two must come first; see object_props[slot].name usage below. */

    {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,

                                                  obj_getSlot,  obj_setSlot},

    {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,

                                                  obj_getSlot,  obj_setSlot},

    {js_count_str, 0,            JSPROP_PERMANENT,obj_getCount, obj_getCount},

    {0,0,0,0,0}

};



/* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */

#define JSSLOT_COUNT 2



static JSBool

ReportStrictSlot(JSContext *cx, uint32 slot)

{

    if (slot == JSSLOT_PROTO)

        return JS_TRUE;

    return JS_ReportErrorFlagsAndNumber(cx,

                                        JSREPORT_WARNING | JSREPORT_STRICT,

                                        js_GetErrorMessage, NULL,

                                        JSMSG_DEPRECATED_USAGE,

                                        object_props[slot].name);

}



static JSBool

obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    uint32 slot;

    JSAccessMode mode;

    uintN attrs;



    slot = (uint32) JSVAL_TO_INT(id);

    if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))

        return JS_FALSE;

    if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {

        id = (jsid)cx->runtime->atomState.protoAtom;

        mode = JSACC_PROTO;

    } else {

        id = (jsid)cx->runtime->atomState.parentAtom;

        mode = JSACC_PARENT;

    }

    if (!OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, &attrs))

        return JS_FALSE;

    *vp = OBJ_GET_SLOT(cx, obj, slot);

    return JS_TRUE;

}



static JSBool

obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSObject *pobj;

    uint32 slot;



    if (!JSVAL_IS_OBJECT(*vp))

        return JS_TRUE;

    pobj = JSVAL_TO_OBJECT(*vp);

    slot = (uint32) JSVAL_TO_INT(id);

    if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))

        return JS_FALSE;

    return js_SetProtoOrParent(cx, obj, slot, pobj);

}



static JSBool

obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    jsval iter_state;

    jsid num_properties;

    JSBool ok;



    if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))

        return JS_FALSE;



    /* Get the number of properties to enumerate. */

    iter_state = JSVAL_NULL;

    ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);

    if (!ok)

        goto out;



    if (!JSVAL_IS_INT(num_properties)) {

        JS_ASSERT(0);

        *vp = JSVAL_ZERO;

        goto out;

    }

    *vp = num_properties;



out:

    if (iter_state != JSVAL_NULL)

        ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);

    return ok;

}



#else  /* !JS_HAS_OBJ_PROTO_PROP */



#define object_props NULL



#endif /* !JS_HAS_OBJ_PROTO_PROP */



JSBool

js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)

{

    JSRuntime *rt;

    JSObject *obj2, *oldproto;

    JSScope *scope, *newscope;



    /*

     * Serialize all proto and parent setting in order to detect cycles.

     * We nest locks in this function, and only here, in the following orders:

     *

     * (1)  rt->setSlotLock < pobj's scope lock;

     *      rt->setSlotLock < pobj's proto-or-parent's scope lock;

     *      rt->setSlotLock < pobj's grand-proto-or-parent's scope lock;

     *      etc...

     * (2)  rt->setSlotLock < obj's scope lock < pobj's scope lock.

     *

     * We avoid AB-BA deadlock by restricting obj from being on pobj's parent

     * or proto chain (pobj may already be on obj's parent or proto chain; it

     * could be moving up or down).  We finally order obj with respect to pobj

     * at the bottom of this routine (just before releasing rt->setSlotLock),

     * by making pobj be obj's prototype or parent.

     *

     * After we have set the slot and released rt->setSlotLock, another call

     * to js_SetProtoOrParent could nest locks according to the first order

     * list above, but it cannot deadlock with any other thread.  For there

     * to be a deadlock, other parts of the engine would have to nest scope

     * locks in the opposite order.  XXXbe ensure they don't!

     */

    rt = cx->runtime;

    JS_ACQUIRE_LOCK(rt->setSlotLock);

    obj2 = pobj;

    while (obj2) {

        if (obj2 == obj) {

            JS_RELEASE_LOCK(rt->setSlotLock);

            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                 JSMSG_CYCLIC_VALUE, object_props[slot].name);

            return JS_FALSE;

        }

        obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot));

    }



    if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {

        /* Check to see whether obj shares its prototype's scope. */

        JS_LOCK_OBJ(cx, obj);

        scope = OBJ_SCOPE(obj);

        oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO));

        if (oldproto && OBJ_SCOPE(oldproto) == scope) {

            /* Either obj needs a new empty scope, or it should share pobj's. */

            if (!pobj) {

                /* With no proto and no scope of its own, obj is truly empty. */

                scope = js_GetMutableScope(cx, obj);

                if (!scope) {

                    JS_UNLOCK_OBJ(cx, obj);

                    JS_RELEASE_LOCK(rt->setSlotLock);

                    return JS_FALSE;

                }

            } else if (OBJ_IS_NATIVE(pobj) && OBJ_SCOPE(pobj) != scope) {

#ifdef JS_THREADSAFE

                /*

                 * We are about to nest scope locks.  Help jslock.c:ShareScope

                 * keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while

                 * avoiding deadlock, by recording scope in rt->setSlotScope.

                 */

                if (scope->ownercx) {

                    JS_ASSERT(scope->ownercx == cx);

                    rt->setSlotScope = scope;

                }

#endif



                /* We can't deadlock because we checked for cycles above (2). */

                JS_LOCK_OBJ(cx, pobj);

                newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map);

                obj->map = &newscope->map;

                js_DropObjectMap(cx, &scope->map, obj);

                JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);

                scope = newscope;

#ifdef JS_THREADSAFE

                rt->setSlotScope = NULL;

#endif

            }

        }

        LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj));

        JS_UNLOCK_SCOPE(cx, scope);

    } else {

        OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj));

    }



    JS_RELEASE_LOCK(rt->setSlotLock);

    return JS_TRUE;

}



JS_STATIC_DLL_CALLBACK(JSHashNumber)

js_hash_object(const void *key)

{

    return (JSHashNumber)key >> JSVAL_TAGBITS;

}



static JSHashEntry *

MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)

{

    JSSharpObjectMap *map;

    JSHashTable *table;

    JSHashNumber hash;

    JSHashEntry **hep, *he;

    jsatomid sharpid;

    JSIdArray *ida;

    JSBool ok;

    jsint i, length;

    jsid id;

#if JS_HAS_GETTER_SETTER

    JSObject *obj2;

    JSProperty *prop;

    uintN attrs;

#endif

    jsval val;



    map = &cx->sharpObjectMap;

    table = map->table;

    hash = js_hash_object(obj);

    hep = JS_HashTableRawLookup(table, hash, obj);

    he = *hep;

    if (!he) {

        sharpid = 0;

        he = JS_HashTableRawAdd(table, hep, hash, obj, (void *)sharpid);

        if (!he) {

            JS_ReportOutOfMemory(cx);

            return NULL;

        }

        ida = JS_Enumerate(cx, obj);

        if (!ida)

            return NULL;

        ok = JS_TRUE;

        for (i = 0, length = ida->length; i < length; i++) {

            id = ida->vector[i];

#if JS_HAS_GETTER_SETTER

            ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);

            if (!ok)

                break;

            if (prop) {

                ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);

                if (ok) {

                    if (OBJ_IS_NATIVE(obj2) &&

                        (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {

                        val = JSVAL_NULL;

                        if (attrs & JSPROP_GETTER) {

                            val = (jsval)

                                SPROP_GETTER((JSScopeProperty*)prop, obj2);

                        }

                        if (attrs & JSPROP_SETTER) {

                            if (val != JSVAL_NULL) {

                                /* Mark the getter, then set val to setter. */

                                ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),

                                                       NULL)

                                      != NULL);

                            }

                            val = (jsval)

                                SPROP_SETTER((JSScopeProperty*)prop, obj2);

                        }

                    } else {

                        ok = OBJ_GET_PROPERTY(cx, obj, id, &val);

                    }

                }

                OBJ_DROP_PROPERTY(cx, obj2, prop);

            }

#else

            ok = OBJ_GET_PROPERTY(cx, obj, id, &val);

#endif

            if (!ok)

                break;

            if (!JSVAL_IS_PRIMITIVE(val) &&

                !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {

                ok = JS_FALSE;

                break;

            }

        }

        if (!ok || !idap)

            JS_DestroyIdArray(cx, ida);

        if (!ok)

            return NULL;

    } else {

        sharpid = (jsatomid) he->value;

        if (sharpid == 0) {

            sharpid = ++map->sharpgen << 1;

            he->value = (void *) sharpid;

        }

        ida = NULL;

    }

    if (idap)

        *idap = ida;

    return he;

}



JSHashEntry *

js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,

                    jschar **sp)

{

    JSSharpObjectMap *map;

    JSHashTable *table;

    JSIdArray *ida;

    JSHashNumber hash;

    JSHashEntry *he, **hep;

    jsatomid sharpid;

    char buf[20];

    size_t len;



    *sp = NULL; /* Set to null in case we return an early error. */

    map = &cx->sharpObjectMap;

    table = map->table;

    if (!table) {

        table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,

                                JS_CompareValues, NULL, NULL);

        if (!table) {

            JS_ReportOutOfMemory(cx);

            return NULL;

        }

        map->table = table;

    }



    ida = NULL;

    if (map->depth == 0) {

        he = MarkSharpObjects(cx, obj, &ida);

        if (!he)

            goto bad;

        JS_ASSERT((((jsatomid) he->value) & SHARP_BIT) == 0);

        if (!idap) {

            JS_DestroyIdArray(cx, ida);

            ida = NULL;

        }

    } else {

        hash = js_hash_object(obj);

        hep = JS_HashTableRawLookup(table, hash, obj);

        he = *hep;



        /*

         * It's possible that the value of a property has changed from the

         * first time the object's properties are traversed (when the property

         * ids are entered into the hash table) to the second (when they are

         * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not

         * idempotent.

         */

        if (!he) {

            he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);

            if (!he) {

                JS_ReportOutOfMemory(cx);

                goto bad;

            }

            *sp = NULL;

            sharpid = 0;

            goto out;

        }

    }



    sharpid = (jsatomid) he->value;

    if (sharpid == 0) {

        *sp = NULL;

    } else {

        len = JS_snprintf(buf, sizeof buf, "#%u%c",

                          sharpid >> 1, (sharpid & SHARP_BIT) ? '#' : '=');

        *sp = js_InflateString(cx, buf, len);

        if (!*sp) {

            if (ida)

                JS_DestroyIdArray(cx, ida);

            goto bad;

        }

    }



out:

    JS_ASSERT(he);

    if ((sharpid & SHARP_BIT) == 0) {

        if (idap && !ida) {

            ida = JS_Enumerate(cx, obj);

            if (!ida) {

                if (*sp) {

                    JS_free(cx, *sp);

                    *sp = NULL;

                }

                goto bad;

            }

        }

        map->depth++;

    }



    if (idap)

        *idap = ida;

    return he;



bad:

    /* Clean up the sharpObjectMap table on outermost error. */

    if (map->depth == 0) {

        map->sharpgen = 0;

        JS_HashTableDestroy(map->table);

        map->table = NULL;

    }

    return NULL;

}



void

js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)

{

    JSSharpObjectMap *map;

    JSIdArray *ida;



    map = &cx->sharpObjectMap;

    JS_ASSERT(map->depth > 0);

    if (--map->depth == 0) {

        map->sharpgen = 0;

        JS_HashTableDestroy(map->table);

        map->table = NULL;

    }

    if (idap) {

        ida = *idap;

        if (ida) {

            JS_DestroyIdArray(cx, ida);

            *idap = NULL;

        }

    }

}



#define OBJ_TOSTRING_EXTRA	3	/* for 3 local GC roots */



#if JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE

JSBool

js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                jsval *rval)

{

    JSBool ok, outermost;

    JSHashEntry *he;

    JSIdArray *ida;

    jschar *chars, *ochars, *vsharp;

    const jschar *vchars;

    size_t nchars, vlength, vsharplength;

    char *comma;

    jsint i, j, length, valcnt;

    jsid id;

#if JS_HAS_GETTER_SETTER

    JSObject *obj2;

    JSProperty *prop;

    uintN attrs;

#endif

    jsval val[2];

    JSString *gsop[2];

    JSString *idstr, *valstr, *str;



    /*

     * obj_toString for 1.2 calls toSource, and doesn't want the extra parens

     * on the outside.

     */

    outermost = (cx->version != JSVERSION_1_2 && cx->sharpObjectMap.depth == 0);

    he = js_EnterSharpObject(cx, obj, &ida, &chars);

    if (!he)

        return JS_FALSE;

    if (IS_SHARP(he)) {

        /*

         * We didn't enter -- obj is already "sharp", meaning we've visited it

         * already in our depth first search, and therefore chars contains a

         * string of the form "#n#".

         */

        JS_ASSERT(!ida);

#if JS_HAS_SHARP_VARS

        nchars = js_strlen(chars);

#else

        chars[0] = '{';

        chars[1] = '}';

        chars[2] = 0;

        nchars = 2;

#endif

        goto make_string;

    }

    JS_ASSERT(ida);

    ok = JS_TRUE;



    if (!chars) {

        /* If outermost, allocate 4 + 1 for "({})" and the terminator. */

        chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));

        nchars = 0;

        if (!chars)

            goto error;

        if (outermost)

            chars[nchars++] = '(';

    } else {

        /* js_EnterSharpObject returned a string of the form "#n=" in chars. */

        MAKE_SHARP(he);

        nchars = js_strlen(chars);

        chars = (jschar *)

            realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));

        if (!chars) {

            free(ochars);

            goto error;

        }

        if (outermost) {

            /*

             * No need for parentheses around the whole shebang, because #n=

             * unambiguously begins an object initializer, and never a block

             * statement.

             */

            outermost = JS_FALSE;

        }

    }



    chars[nchars++] = '{';



    comma = NULL;



    for (i = 0, length = ida->length; i < length; i++) {

        /* Get strings for id and value and GC-root them via argv. */

        id = ida->vector[i];



#if JS_HAS_GETTER_SETTER



        ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);

        if (!ok)

            goto error;

        if (prop) {

            ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);

            if (!ok)

                goto error;

            if (OBJ_IS_NATIVE(obj2) &&

                (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {

                valcnt = 0;

                if (attrs & JSPROP_GETTER) {

                    val[valcnt] = (jsval)

                        SPROP_GETTER((JSScopeProperty *)prop, obj2);

#ifdef OLD_GETTER_SETTER

                    gsop[valcnt] =

                        ATOM_TO_STRING(cx->runtime->atomState.getterAtom);

#else

                    gsop[valcnt] =

                        ATOM_TO_STRING(cx->runtime->atomState.getAtom);

#endif

                    valcnt++;

                }

                if (attrs & JSPROP_SETTER) {

                    val[valcnt] = (jsval)

                        SPROP_SETTER((JSScopeProperty *)prop, obj2);

#ifdef OLD_GETTER_SETTER

                    gsop[valcnt] =

                        ATOM_TO_STRING(cx->runtime->atomState.setterAtom);

#else

                    gsop[valcnt] =

                        ATOM_TO_STRING(cx->runtime->atomState.setAtom);

#endif

                    valcnt++;

                }

            } else {

                valcnt = 1;

                gsop[0] = NULL;

                ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);

            }

            OBJ_DROP_PROPERTY(cx, obj2, prop);

        }



#else  /* !JS_HAS_GETTER_SETTER */



        valcnt = 1;

        gsop[0] = NULL;

        ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);



#endif /* !JS_HAS_GETTER_SETTER */



        if (!ok)

            goto error;



        /* Convert id to a jsval and then to a string. */

        id = js_IdToValue(id);

        idstr = js_ValueToString(cx, id);

        if (!idstr) {

            ok = JS_FALSE;

            goto error;

        }

        argv[0] = STRING_TO_JSVAL(idstr);



        /* If id is a non-identifier string, it needs to be quoted. */

        if (JSVAL_IS_STRING(id) && !js_IsIdentifier(idstr)) {

            idstr = js_QuoteString(cx, idstr, (jschar)'\'');

            if (!idstr) {

                ok = JS_FALSE;

                goto error;

            }

            argv[0] = STRING_TO_JSVAL(idstr);

        }



        for (j = 0; j < valcnt; j++) {

            /* Convert val[j] to its canonical source form. */

            valstr = js_ValueToSource(cx, val[j]);

            if (!valstr) {

                ok = JS_FALSE;

                goto error;

            }

            argv[1+j] = STRING_TO_JSVAL(valstr);

            vchars = valstr->chars;

            vlength = valstr->length;



#ifndef OLD_GETTER_SETTER

            /* Remove 'function ' from beginning of valstr. */

            if (gsop[j]) {

                int n = strlen(js_function_str) + 1;

                vchars += n;

                vlength -= n;

            }

#endif



            /* If val[j] is a non-sharp object, consider sharpening it. */

            vsharp = NULL;

            vsharplength = 0;

#if JS_HAS_SHARP_VARS

            if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {

                he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,

                                         &vsharp);

                if (!he) {

                    ok = JS_FALSE;

                    goto error;

                }

                if (IS_SHARP(he)) {

                    vchars = vsharp;

                    vlength = js_strlen(vchars);

                } else {

                    if (vsharp) {

                        vsharplength = js_strlen(vsharp);

                        MAKE_SHARP(he);

                    }

                    js_LeaveSharpObject(cx, NULL);

                }

            }

#endif



            /* Allocate 1 + 1 at end for closing brace and terminating 0. */

            chars = (jschar *)

                realloc((ochars = chars),

                        (nchars + (comma ? 2 : 0) +

                         idstr->length + 1 +

                         (gsop[j] ? 1 + gsop[j]->length : 0) +

                         vsharplength + vlength +

                         (outermost ? 2 : 1) + 1) * sizeof(jschar));

            if (!chars) {

                /* Save code space on error: let JS_free ignore null vsharp. */

                JS_free(cx, vsharp);

                free(ochars);

                goto error;

            }



            if (comma) {

                chars[nchars++] = comma[0];

                chars[nchars++] = comma[1];

            }

            comma = ", ";



#ifdef OLD_GETTER_SETTER

            js_strncpy(&chars[nchars], idstr->chars, idstr->length);

            nchars += idstr->length;

            if (gsop[j]) {

                chars[nchars++] = ' ';

                js_strncpy(&chars[nchars], gsop[j]->chars, gsop[j]->length);

                nchars += gsop[j]->length;

            }

            chars[nchars++] = ':';

#else

            if (gsop[j]) {

                js_strncpy(&chars[nchars], gsop[j]->chars, gsop[j]->length);

                nchars += gsop[j]->length;

                chars[nchars++] = ' ';

            }

            js_strncpy(&chars[nchars], idstr->chars, idstr->length);

            nchars += idstr->length;

            if (!gsop[j])

                chars[nchars++] = ':';

#endif

            if (vsharplength) {

                js_strncpy(&chars[nchars], vsharp, vsharplength);

                nchars += vsharplength;

            }

            js_strncpy(&chars[nchars], vchars, vlength);

            nchars += vlength;



            if (vsharp)

                JS_free(cx, vsharp);

        }

    }



    chars[nchars++] = '}';

    if (outermost)

        chars[nchars++] = ')';

    chars[nchars] = 0;



  error:

    js_LeaveSharpObject(cx, &ida);



    if (!ok) {

        if (chars)

            free(chars);

        return ok;

    }



    if (!chars) {

        JS_ReportOutOfMemory(cx);

        return JS_FALSE;

    }

  make_string:

    str = js_NewString(cx, chars, nchars, 0);

    if (!str) {

        free(chars);

        return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}

#endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */



JSBool

js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                jsval *rval)

{

    jschar *chars;

    size_t nchars;

    const char *clazz, *prefix;

    JSString *str;



#if JS_HAS_INITIALIZERS

    if (cx->version == JSVERSION_1_2)

        return js_obj_toSource(cx, obj, argc, argv, rval);

#endif



    clazz = OBJ_GET_CLASS(cx, obj)->name;

    nchars = 9 + strlen(clazz);		/* 9 for "[object ]" */

    chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));

    if (!chars)

        return JS_FALSE;



    prefix = "[object ";

    nchars = 0;

    while ((chars[nchars] = (jschar)*prefix) != 0)

        nchars++, prefix++;

    while ((chars[nchars] = (jschar)*clazz) != 0)

        nchars++, clazz++;

    chars[nchars++] = ']';

    chars[nchars] = 0;



    str = js_NewString(cx, chars, nchars, 0);

    if (!str) {

        JS_free(cx, chars);

        return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    *rval = OBJECT_TO_JSVAL(obj);

    return JS_TRUE;

}



static JSBool

obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSStackFrame *fp, *caller;

    JSBool indirectCall;

    JSObject *scopeobj;

    JSString *str;

    const char *file;

    uintN line;

    JSPrincipals *principals;

    JSScript *script;

    JSBool ok;

#if JS_HAS_EVAL_THIS_SCOPE

    JSObject *callerScopeChain = NULL, *callerVarObj = NULL;

    JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE;

#endif



    fp = cx->fp;

    caller = fp->down;

    indirectCall = (!caller->pc || *caller->pc != JSOP_EVAL);



    if (JSVERSION_IS_ECMA(cx->version) &&

        indirectCall &&

        !JS_ReportErrorFlagsAndNumber(cx,

                                      JSREPORT_WARNING | JSREPORT_STRICT,

                                      js_GetErrorMessage, NULL,

                                      JSMSG_BAD_INDIRECT_CALL,

                                      js_eval_str)) {

        return JS_FALSE;

    }



    if (!JSVAL_IS_STRING(argv[0])) {

        *rval = argv[0];

        return JS_TRUE;

    }



#if JS_HAS_SCRIPT_OBJECT

    /*

     * Script.prototype.compile/exec and Object.prototype.eval all take an

     * optional trailing argument that overrides the scope object.

     */

    scopeobj = NULL;

    if (argc >= 2) {

        if (!js_ValueToObject(cx, argv[1], &scopeobj))

            return JS_FALSE;

        argv[1] = OBJECT_TO_JSVAL(scopeobj);

    }

    if (!scopeobj)

#endif

    {

#if JS_HAS_EVAL_THIS_SCOPE

        /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */

        if (indirectCall) {

            callerScopeChain = caller->scopeChain;

            if (obj != callerScopeChain) {

                scopeobj = js_NewObject(cx, &js_WithClass, obj,

                                        callerScopeChain);

                if (!scopeobj)

                    return JS_FALSE;



                /* Set fp->scopeChain too, for the compiler. */

                caller->scopeChain = fp->scopeChain = scopeobj;

                setCallerScopeChain = JS_TRUE;

            }



            callerVarObj = caller->varobj;

            if (obj != callerVarObj) {

                /* Set fp->varobj too, for the compiler. */

                caller->varobj = fp->varobj = obj;

                setCallerVarObj = JS_TRUE;

            }

        }

        /* From here on, control must exit through label out with ok set. */

#endif



#if JS_BUG_EVAL_THIS_SCOPE

        /* An old version used the object in which eval was found for scope. */

        scopeobj = obj;

#else

        /* Compile using caller's current scope object. */

        scopeobj = caller->scopeChain;

#endif

    }



    str = JSVAL_TO_STRING(argv[0]);

    if (caller->script) {

        file = caller->script->filename;

        line = js_PCToLineNumber(caller->script, caller->pc);

        principals = caller->script->principals;

    } else {

        file = NULL;

        line = 0;

        principals = NULL;

    }



    fp->special |= JSFRAME_EVAL;

    script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,

                                             str->chars, str->length,

                                             file, line);

    if (!script) {

        ok = JS_FALSE;

        goto out;

    }



#if !JS_BUG_EVAL_THIS_SCOPE

#if JS_HAS_SCRIPT_OBJECT

    if (argc < 2)

#endif

    {

        /* Execute using caller's new scope object (might be a Call object). */

        scopeobj = caller->scopeChain;

    }

#endif

    ok = js_Execute(cx, scopeobj, script, caller, fp->special & JSFRAME_EVAL,

                    rval);

    JS_DestroyScript(cx, script);



out:

#if JS_HAS_EVAL_THIS_SCOPE

    /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */

    if (setCallerScopeChain)

        caller->scopeChain = callerScopeChain;

    if (setCallerVarObj)

        caller->varobj = callerVarObj;

#endif

    return ok;

}



#if JS_HAS_OBJ_WATCHPOINT



static JSBool

obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,

                  void *closure)

{

    JSObject *funobj;

    jsval argv[3];



    funobj = (JSObject *) closure;

    argv[0] = id;

    argv[1] = old;

    argv[2] = *nvp;

    return js_InternalCall(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp);

}



static JSBool

obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSFunction *fun;

    jsval userid, value;

    jsid symid;

    uintN attrs;



    fun = js_ValueToFunction(cx, &argv[1], JS_FALSE);

    if (!fun)

        return JS_FALSE;

    argv[1] = OBJECT_TO_JSVAL(fun->object);



    /* Compute the unique int/atom symbol id needed by js_LookupProperty. */

    userid = argv[0];

    if (!JS_ValueToId(cx, userid, &symid))

        return JS_FALSE;



    if (!OBJ_CHECK_ACCESS(cx, obj, symid, JSACC_WATCH, &value, &attrs))

        return JS_FALSE;

    if (attrs & JSPROP_READONLY)

        return JS_TRUE;

    return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, fun->object);

}



static JSBool

obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);

    return JS_TRUE;

}



#endif /* JS_HAS_OBJ_WATCHPOINT */



#if JS_HAS_NEW_OBJ_METHODS

/*

 * Prototype and property query methods, to complement the 'in' and

 * 'instanceof' operators.

 */



/* Proposed ECMA 15.2.4.5. */

static JSBool

obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                   jsval *rval)

{

    jsid id;

    JSObject *obj2;

    JSProperty *prop;



    if (!JS_ValueToId(cx, argv[0], &id))

        return JS_FALSE;

    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))

        return JS_FALSE;

    *rval = BOOLEAN_TO_JSVAL(prop && obj2 == obj);

    if (prop)

        OBJ_DROP_PROPERTY(cx, obj2, prop);

    return JS_TRUE;

}



/* Proposed ECMA 15.2.4.6. */

static JSBool

obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                  jsval *rval)

{

    JSBool b;



    if (!js_IsDelegate(cx, obj, *argv, &b))

        return JS_FALSE;

    *rval = BOOLEAN_TO_JSVAL(b);

    return JS_TRUE;

}



/* Proposed ECMA 15.2.4.7. */

static JSBool

obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                         jsval *rval)

{

    jsid id;

    uintN attrs;

    JSObject *obj2;

    JSProperty *prop;



    if (!JS_ValueToId(cx, argv[0], &id))

        return JS_FALSE;

    /* Be compatible with an error in the ECMA spec; return false unless hasOwnProperty. */

    if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))

        return JS_FALSE;

    if (prop && obj2 != obj) {

        *rval = JSVAL_FALSE;

        return JS_TRUE;

    }

    if (!OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs))

        return JS_FALSE;

    *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);

    return JS_TRUE;

}

#endif /* JS_HAS_NEW_OBJ_METHODS */



#if JS_HAS_GETTER_SETTER

static JSBool

obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                 jsval *rval)

{

    jsval fval, junk;

    jsid id;

    JSBool found;

    uintN attrs;



    fval = argv[1];

    if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {

        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                             JSMSG_BAD_GETTER_OR_SETTER,

                             js_getter_str);

        return JS_FALSE;

    }



    if (!JS_ValueToId(cx, argv[0], &id))

        return JS_FALSE;

    if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, &found))

        return JS_FALSE;

    /*

     * Getters and setters are just like watchpoints from an access

     * control point of view.

     */

    if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))

        return JS_FALSE;

    return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,

                               (JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL,

                               JSPROP_GETTER, NULL);

}



static JSBool

obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                 jsval *rval)

{

    jsval fval, junk;

    jsid id;

    JSBool found;

    uintN attrs;



    fval = argv[1];

    if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {

        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                             JSMSG_BAD_GETTER_OR_SETTER,

                             js_setter_str);

        return JS_FALSE;

    }



    if (!JS_ValueToId(cx, argv[0], &id))

        return JS_FALSE;

    if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, &found))

        return JS_FALSE;

    /*

     * Getters and setters are just like watchpoints from an access

     * control point of view.

     */

    if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))

        return JS_FALSE;

    return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,

                               NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval),

                               JSPROP_SETTER, NULL);

}

#endif /* JS_HAS_GETTER_SETTER */



#if JS_HAS_OBJ_WATCHPOINT

const char js_watch_str[] = "watch";

const char js_unwatch_str[] = "unwatch";

#endif

#if JS_HAS_NEW_OBJ_METHODS

const char js_hasOwnProperty_str[] = "hasOwnProperty";

const char js_isPrototypeOf_str[] = "isPrototypeOf";

const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";

#endif

#if JS_HAS_GETTER_SETTER

const char js_defineGetter_str[] = "__defineGetter__";

const char js_defineSetter_str[] = "__defineSetter__";

#endif



static JSFunctionSpec object_methods[] = {

#if JS_HAS_TOSOURCE

    {js_toSource_str,             js_obj_toSource,    0, 0, OBJ_TOSTRING_EXTRA},

#endif

    {js_toString_str,             js_obj_toString,    0, 0, OBJ_TOSTRING_EXTRA},

    {js_toLocaleString_str,       js_obj_toString,    0, 0, OBJ_TOSTRING_EXTRA},

    {js_valueOf_str,              obj_valueOf,        0,0,0},

    {js_eval_str,                 obj_eval,           1,0,0},

#if JS_HAS_OBJ_WATCHPOINT

    {js_watch_str,                obj_watch,          2,0,0},

    {js_unwatch_str,              obj_unwatch,        1,0,0},

#endif

#if JS_HAS_NEW_OBJ_METHODS

    {js_hasOwnProperty_str,       obj_hasOwnProperty, 1,0,0},

    {js_isPrototypeOf_str,        obj_isPrototypeOf,  1,0,0},

    {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0},

#endif

#if JS_HAS_GETTER_SETTER

    {js_defineGetter_str,         obj_defineGetter,   2,0,0},

    {js_defineSetter_str,         obj_defineSetter,   2,0,0},

#endif

    {0,0,0,0,0}

};



static JSBool

Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    if (argc == 0) {

        /* Trigger logic below to construct a blank object. */

        obj = NULL;

    } else {

        /* If argv[0] is null or undefined, obj comes back null. */

        if (!js_ValueToObject(cx, argv[0], &obj))

            return JS_FALSE;

    }

    if (!obj) {

        JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));

        if (cx->fp->constructing)

            return JS_TRUE;

        obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);

        if (!obj)

            return JS_FALSE;

    }

    *rval = OBJECT_TO_JSVAL(obj);

    return JS_TRUE;

}



/*

 * ObjectOps and Class for with-statement stack objects.

 */

static JSBool

with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,

                    JSProperty **propp

#if defined JS_THREADSAFE && defined DEBUG

                    , const char *file, uintN line

#endif

                    )

{

	JSBool ret;

	jsval val;

	

    JSObject *proto = OBJ_GET_PROTO(cx, obj);

    if (!proto)

        return js_LookupProperty(cx, obj, id, objp, propp);

	

    /* DREAMWEAVER

	 * DaveG - if the lookup didn't find the property, do a get_property to

     * see if the prop exists.  If it does, define the property now.

	 * 

	 * snewman 2/22/01: this is necessary because Dreamweaver defines

	 * object classes with additional properties, but doesn't quite

	 * follow the rules when doing so.  See dw_js_notes.txt for details.

	 * 

	 * The full change we've made to this routine is as follows:

	 * 

	 * 1. Insert the declarations of ret and val, above.

	 * 2. Convert the next line to assign the result of OBJ_LOOKUP_PROPERTY

	 *    into ret, instead of returning it directly.

	 * 3. Insert the following seven lines (the "if (ret && *propp == NULL)"

	 *    and the code inside it).

	 * 4. Add "return ret".

	 */

    ret = OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);

	

    if (ret && *propp == NULL) {

	val = JSVAL_VOID;

	OBJ_GET_PROPERTY(cx, proto, id, &val);

	if (val != JSVAL_VOID) {

	    OBJ_DEFINE_PROPERTY(cx, proto, id, val, NULL, NULL, 0, NULL);

	    OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);

	}

    }

	

	return ret;

}



static JSBool

with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)

{

    JSObject *proto = OBJ_GET_PROTO(cx, obj);

    if (!proto)

        return js_GetProperty(cx, obj, id, vp);

    return OBJ_GET_PROPERTY(cx, proto, id, vp);

}



static JSBool

with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)

{

    JSObject *proto = OBJ_GET_PROTO(cx, obj);

    if (!proto)

        return js_SetProperty(cx, obj, id, vp);

    return OBJ_SET_PROPERTY(cx, proto, id, vp);

}



static JSBool

with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,

                   uintN *attrsp)

{

    JSObject *proto = OBJ_GET_PROTO(cx, obj);

    if (!proto)

        return js_GetAttributes(cx, obj, id, prop, attrsp);

    return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);

}



static JSBool

with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,

                   uintN *attrsp)

{

    JSObject *proto = OBJ_GET_PROTO(cx, obj);

    if (!proto)

        return js_SetAttributes(cx, obj, id, prop, attrsp);

    return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);

}



static JSBool

with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)

{

    JSObject *proto = OBJ_GET_PROTO(cx, obj);

    if (!proto)

        return js_DeleteProperty(cx, obj, id, rval);

    return OBJ_DELETE_PROPERTY(cx, proto, id, rval);

}



static JSBool

with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)

{

    JSObject *proto = OBJ_GET_PROTO(cx, obj);

    if (!proto)

        return js_DefaultValue(cx, obj, hint, vp);

    return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);

}



static JSBool

with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,

               jsval *statep, jsid *idp)

{

    JSObject *proto = OBJ_GET_PROTO(cx, obj);

    if (!proto)

        return js_Enumerate(cx, obj, enum_op, statep, idp);

    return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);

}



static JSBool

with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,

                 jsval *vp, uintN *attrsp)

{

    JSObject *proto = OBJ_GET_PROTO(cx, obj);

    if (!proto)

        return js_CheckAccess(cx, obj, id, mode, vp, attrsp);

    return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);

}



static JSObject *

with_ThisObject(JSContext *cx, JSObject *obj)

{

    JSObject *proto = OBJ_GET_PROTO(cx, obj);

    if (!proto)

        return obj;

    return OBJ_THIS_OBJECT(cx, proto);

}



JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {

    js_NewObjectMap,        js_DestroyObjectMap,

    with_LookupProperty,    js_DefineProperty,

    with_GetProperty,       with_SetProperty,

    with_GetAttributes,     with_SetAttributes,

    with_DeleteProperty,    with_DefaultValue,

    with_Enumerate,         with_CheckAccess,

    with_ThisObject,        NATIVE_DROP_PROPERTY,

    NULL,                   NULL,

    NULL,                   NULL,

    js_SetProtoOrParent,    js_SetProtoOrParent,

    js_Mark,                js_Clear,

    0,                      0

};



static JSObjectOps *

with_getObjectOps(JSContext *cx, JSClass *clasp)

{

    return &js_WithObjectOps;

}



JSClass js_WithClass = {

    "With",

    0,

    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,

    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,

    with_getObjectOps,

    0,0,0,0,0,0,0

};



#if JS_HAS_OBJ_PROTO_PROP

static JSBool

With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSObject *parent, *proto;

    jsval v;



    if (JS_HAS_STRICT_OPTION(cx)) {

        if (!JS_ReportErrorFlagsAndNumber(cx,

                                          JSREPORT_WARNING | JSREPORT_STRICT,

                                          js_GetErrorMessage, NULL,

                                          JSMSG_DEPRECATED_USAGE,

                                          js_WithClass.name)) {

            return JS_FALSE;

        }

    }



    if (!cx->fp->constructing) {

        obj = js_NewObject(cx, &js_WithClass, NULL, NULL);

        if (!obj)

            return JS_FALSE;

        *rval = OBJECT_TO_JSVAL(obj);

    }



    parent = cx->fp->scopeChain;

    if (argc > 0) {

        if (!js_ValueToObject(cx, argv[0], &proto))

            return JS_FALSE;

        v = OBJECT_TO_JSVAL(proto);

        if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v))

            return JS_FALSE;

        if (argc > 1) {

            if (!js_ValueToObject(cx, argv[1], &parent))

                return JS_FALSE;

        }

    }

    v = OBJECT_TO_JSVAL(parent);

    return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v);

}

#endif



JSObject *

js_InitObjectClass(JSContext *cx, JSObject *obj)

{

    JSObject *proto;

    jsval eval;



#if JS_HAS_SHARP_VARS

    JS_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1);

#endif



    proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1,

                         object_props, object_methods, NULL, NULL);

#if JS_HAS_OBJ_PROTO_PROP

    if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0,

                      NULL, NULL, NULL, NULL)) {

        return NULL;

    }

#endif



    /* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */

    if (!OBJ_GET_PROPERTY(cx, proto, (jsid)cx->runtime->atomState.evalAtom,

                          &eval)) {

        return NULL;

    }

    if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.evalAtom,

                             eval, NULL, NULL, 0, NULL)) {

        return NULL;

    }



    return proto;

}



void

js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,

                 JSClass *clasp)

{

    map->nrefs = nrefs;

    map->ops = ops;

    map->nslots = 0;

    map->freeslot = JSSLOT_FREE(clasp);

}



JSObjectMap *

js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,

                JSClass *clasp, JSObject *obj)

{

    return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);

}



void

js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)

{

    js_DestroyScope(cx, (JSScope *)map);

}



JSObjectMap *

js_HoldObjectMap(JSContext *cx, JSObjectMap *map)

{

    JS_ASSERT(map->nrefs >= 0);

    JS_ATOMIC_INCREMENT(&map->nrefs);

    return map;

}



JSObjectMap *

js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)

{

    JS_ASSERT(map->nrefs > 0);

    JS_ATOMIC_DECREMENT(&map->nrefs);

    if (map->nrefs == 0) {

        map->ops->destroyObjectMap(cx, map);

        return NULL;

    }

    if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)

        ((JSScope *)map)->object = NULL;

    return map;

}



JSObject *

js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)

{

    JSObject *obj, *ctor;

    JSObjectOps *ops;

    JSObjectMap *map;

    jsval cval;

    uint32 i;



    /* Allocate an object from the GC heap and zero it. */

    obj = (JSObject *) js_AllocGCThing(cx, GCX_OBJECT);

    if (!obj)

        return NULL;



    /* Bootstrap the ur-object, and make it the default prototype object. */

    if (!proto) {

        if (!js_GetClassPrototype(cx, clasp->name, &proto))

            goto bad;

        if (!proto && !js_GetClassPrototype(cx, js_ObjectClass.name, &proto))

            goto bad;

    }



    /* Always call the class's getObjectOps hook if it has one. */

    ops = clasp->getObjectOps

          ? clasp->getObjectOps(cx, clasp)

          : &js_ObjectOps;



    if (proto && (map = proto->map)->ops == ops) {

        /* Default parent to the parent of the prototype's constructor. */

        if (!parent) {

            if (!OBJ_GET_PROPERTY(cx, proto,

                                  (jsid)cx->runtime->atomState.constructorAtom,

                                  &cval)) {

                goto bad;

            }

            if (JSVAL_IS_OBJECT(cval) && (ctor = JSVAL_TO_OBJECT(cval)) != NULL)

                parent = OBJ_GET_PARENT(cx, ctor);

        }



        /* Share the given prototype's map. */

        obj->map = js_HoldObjectMap(cx, map);

    } else {

        /* Leave parent alone.  Allocate a new map for obj. */

        map = ops->newObjectMap(cx, 1, ops, clasp, obj);

        if (!map)

            goto bad;

        if (map->nslots == 0)

            map->nslots = JS_INITIAL_NSLOTS;

        obj->map = map;

    }



    /* Set the proto, parent, and class properties. */

    obj->slots = (jsval *) JS_malloc(cx, JS_INITIAL_NSLOTS * sizeof(jsval));

    if (!obj->slots)

        goto bad;

    obj->slots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);

    obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);

    obj->slots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);

    for (i = JSSLOT_CLASS+1; i < JS_INITIAL_NSLOTS; i++)

        obj->slots[i] = JSVAL_VOID;



    if (cx->runtime->objectHook) {

        cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);

    }



    return obj;



bad:

    cx->newborn[GCX_OBJECT] = NULL;

    return NULL;

}



static JSBool

FindConstructor(JSContext *cx, const char *name, jsval *vp)

{

    JSAtom *atom;

    JSObject *obj, *tmp;

    JSObject *pobj;

    JSScopeProperty *sprop;



    atom = js_Atomize(cx, name, strlen(name), 0);

    if (!atom)

        return JS_FALSE;



    if (cx->fp && (tmp = cx->fp->scopeChain) != NULL) {

        /* Find the topmost object in the scope chain. */

        do {

            obj = tmp;

            tmp = OBJ_GET_PARENT(cx, obj);

        } while (tmp);

    } else {

        obj = cx->globalObject;

        if (!obj) {

            *vp = JSVAL_VOID;

            return JS_TRUE;

        }

    }



    if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, (JSProperty**)&sprop))

        return JS_FALSE;

    if (!sprop)  {

        *vp = JSVAL_VOID;

        return JS_TRUE;

    }



    JS_ASSERT(OBJ_IS_NATIVE(pobj));

    JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop));

    *vp = OBJ_GET_SLOT(cx, pobj, sprop->slot);

    OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);

    return JS_TRUE;

}



JSObject *

js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,

                   JSObject *parent)

{

    jsval cval, rval;

    JSObject *obj, *ctor;



    if (!FindConstructor(cx, clasp->name, &cval))

        return NULL;



    /*

     * If proto or parent are NULL, set them to Constructor.prototype and/or

     * Constructor.__parent__, just like JSOP_NEW does.

     */

    ctor = JSVAL_TO_OBJECT(cval);

    if (!parent)

        parent = OBJ_GET_PARENT(cx, ctor);

    if (!proto) {

        if (!OBJ_GET_PROPERTY(cx, ctor,

                              (jsid)cx->runtime->atomState.classPrototypeAtom,

                              &rval)) {

            return NULL;

        }

        if (JSVAL_IS_OBJECT(rval))

            proto = JSVAL_TO_OBJECT(rval);

    }



    obj = js_NewObject(cx, clasp, proto, parent);

    if (!obj)

        return NULL;



    if (!js_InternalConstruct(cx, obj, cval, 0, NULL, &rval))

        goto bad;

    return JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : obj;

bad:

    cx->newborn[GCX_OBJECT] = NULL;

    return NULL;

}



void

js_FinalizeObject(JSContext *cx, JSObject *obj)

{

    JSObjectMap *map;



    /* Cope with stillborn objects that have no map. */

    map = obj->map;

    if (!map)

        return;



    if (cx->runtime->objectHook) {

        cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);

    }



#if JS_HAS_OBJ_WATCHPOINT

    /* Remove all watchpoints with weak links to obj. */

    JS_ClearWatchPointsForObject(cx, obj);

#endif



    /* Finalize obj first, in case it needs map and slots. */

    OBJ_GET_CLASS(cx, obj)->finalize(cx, obj);



    /* Drop map and free slots. */

    js_DropObjectMap(cx, map, obj);

    obj->map = NULL;

    JS_free(cx, obj->slots);

    obj->slots = NULL;

}



JSBool

js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)

{

    JSObjectMap *map;

    uint32 nslots;

    size_t nbytes;

    jsval *newslots;



    map = obj->map;

    nslots = map->nslots;

    if (map->freeslot >= nslots) {

        nslots = JS_MAX(map->freeslot, nslots);

        if (nslots < JS_INITIAL_NSLOTS)

            nslots = JS_INITIAL_NSLOTS;

        else

            nslots += (nslots + 1) / 2;



        nbytes = (size_t)nslots * sizeof(jsval);

#if defined(XP_PC) && defined _MSC_VER && _MSC_VER <= 800

        if (nbytes > 60000U) {

            JS_ReportOutOfMemory(cx);

            return JS_FALSE;

        }

#endif



        if (obj->slots) {

            newslots = (jsval *) JS_realloc(cx, obj->slots, nbytes);

        } else {

            /* obj must be newborn and unshared at this point. */

            newslots = (jsval *) JS_malloc(cx, nbytes);

        }

        if (!newslots)

            return JS_FALSE;

        obj->slots = newslots;

        map->nslots = nslots;

    }



#ifdef TOO_MUCH_GC

    obj->slots[map->freeslot] = JSVAL_VOID;

#endif

    *slotp = map->freeslot++;

    return JS_TRUE;

}



void

js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)

{

    JSObjectMap *map;

    uint32 nslots;

    size_t nbytes;

    jsval *newslots;



    OBJ_CHECK_SLOT(obj, slot);

    obj->slots[slot] = JSVAL_VOID;

    map = obj->map;

    if (map->freeslot == slot + 1)

        map->freeslot = slot;

    nslots = map->nslots;

    if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {

        nslots = map->freeslot;

        nslots += nslots / 2;

        nbytes = (size_t)nslots * sizeof(jsval);

        newslots = (jsval *) JS_realloc(cx, obj->slots, nbytes);

        if (!newslots)

            return;

        obj->slots = newslots;

        map->nslots = nslots;

    }

}



#if JS_BUG_EMPTY_INDEX_ZERO

#define CHECK_FOR_EMPTY_INDEX(id)                                             \

    JS_BEGIN_MACRO                                                            \

        if (_str->length == 0)                                                \

            id = JSVAL_ZERO;                                                  \

    JS_END_MACRO

#else

#define CHECK_FOR_EMPTY_INDEX(id) /* nothing */

#endif



/* JSVAL_INT_MAX as a string */

#define JSVAL_INT_MAX_STRING "1073741823"



#define CHECK_FOR_FUNNY_INDEX(id)                                             \

    JS_BEGIN_MACRO                                                            \

        if (!JSVAL_IS_INT(id)) {                                              \

            JSAtom *_atom = (JSAtom *)id;                                     \

            JSString *_str = ATOM_TO_STRING(_atom);                           \

            const jschar *_cp = _str->chars;                                  \

            JSBool _negative = (*_cp == '-');                                 \

            if (_negative) _cp++;                                             \

            if (JS7_ISDEC(*_cp) &&                                            \

                _str->length - _negative <= sizeof(JSVAL_INT_MAX_STRING) - 1) \

            {                                                                 \

                jsuint _index = JS7_UNDEC(*_cp++);                            \

                jsuint _oldIndex = 0;                                         \

                jsuint _c = 0;                                                \

                if (_index != 0) {                                            \

                    while (JS7_ISDEC(*_cp)) {                                 \

                        _oldIndex = _index;                                   \

                        _c = JS7_UNDEC(*_cp);                                 \

                        _index = 10 * _index + _c;                            \

                        _cp++;                                                \

                    }                                                         \

                }                                                             \

                if (*_cp == 0 &&                                              \

                    (_oldIndex < (JSVAL_INT_MAX / 10) ||                      \

                     (_oldIndex == (JSVAL_INT_MAX / 10) &&                    \

                      _c <= (JSVAL_INT_MAX % 10)))) {                         \

                    if (_negative) _index = 0 - _index;                       \

                    id = INT_TO_JSVAL((jsint)_index);                         \

                }                                                             \

            } else {                                                          \

                CHECK_FOR_EMPTY_INDEX(id);                                    \

            }                                                                 \

        }                                                                     \

    JS_END_MACRO





JSBool

js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,

                  JSPropertyOp getter, JSPropertyOp setter, uintN attrs,

                  JSProperty **propp)

{

    JSClass *clasp;

    JSScope *scope;

    JSScopeProperty *sprop;



    /*

     * Handle old bug that took empty string as zero index.  Also convert

     * string indices to integers if appropriate.

     */

    CHECK_FOR_FUNNY_INDEX(id);



    /* Lock if object locking is required by this implementation. */

    JS_LOCK_OBJ(cx, obj);



#if JS_HAS_GETTER_SETTER

    /*

     * If defining a getter or setter, we must check for its counterpart and

     * update the attributes and property ops.  A getter or setter is really

     * only half of a property.

     */

    if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {

        JSObject *pobj;



        if (!js_LookupProperty(cx, obj, id, &pobj, (JSProperty **)&sprop))

            goto bad;

        if (sprop &&

            pobj == obj &&

            (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {

            sprop->attrs |= attrs;

            if (attrs & JSPROP_GETTER)

                SPROP_GETTER(sprop, pobj) = getter;

            else

                SPROP_SETTER(sprop, pobj) = setter;

            if (propp)

                *propp = (JSProperty *) sprop;

#ifdef JS_THREADSAFE

            else {

                /* Release sprop and the lock acquired by js_LookupProperty. */

                js_DropProperty(cx, obj, (JSProperty *)sprop);

            }

#endif

            /* Release our lock on obj, in which js_LookupProperty's nested. */

            JS_UNLOCK_OBJ(cx, obj);

            return JS_TRUE;

        }



        if (sprop) {

            /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */

            OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);

        }

    }

#endif /* JS_HAS_GETTER_SETTER */



    /* Use the object's class getter and setter by default. */

    clasp = LOCKED_OBJ_GET_CLASS(obj);

    if (!getter)

        getter = clasp->getProperty;

    if (!setter)

        setter = clasp->setProperty;



    /* Find a sharable scope, or get a new one for obj. */

    scope = js_MutateScope(cx, obj, id, getter, setter, attrs, &sprop);

    if (!scope)

        goto bad;



    /* Add the property only if MutateScope didn't find a shared scope. */

    if (!sprop) {

        if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)

            attrs |= JSPROP_SHARED;

        sprop = js_NewScopeProperty(cx, scope, id, getter, setter, attrs);

        if (!sprop)

            goto bad;

        /* XXXbe called with lock held */

        if (!clasp->addProperty(cx, obj, sprop->id, &value) ||

            !scope->ops->add(cx, scope, id, sprop)) {

            js_DestroyScopeProperty(cx, scope, sprop);

            goto bad;

        }

        PROPERTY_CACHE_FILL(cx, &cx->runtime->propertyCache, obj, id,

                            (JSProperty *)sprop);

    }



    if (SPROP_HAS_VALID_SLOT(sprop))

        LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);

    if (propp) {

#ifdef JS_THREADSAFE

        js_HoldScopeProperty(cx, scope, sprop);

#endif

        *propp = (JSProperty *) sprop;

    } else {

        JS_UNLOCK_OBJ(cx, obj);

    }

    return JS_TRUE;



bad:

    JS_UNLOCK_OBJ(cx, obj);

    return JS_FALSE;

}



#if defined JS_THREADSAFE && defined DEBUG

JS_FRIEND_API(JSBool)

_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,

                   JSProperty **propp, const char *file, uintN line)

#else

JS_FRIEND_API(JSBool)

js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,

                  JSProperty **propp)

#endif

{

    JSHashNumber hash;

    JSScope *scope;

    JSSymbol *sym;

    JSClass *clasp;

    JSResolveOp resolve;

    JSNewResolveOp newresolve;

    uintN flags;

    uint32 format;

    JSObject *obj2, *proto;

    JSScopeProperty *sprop;



    /*

     * Handle old bug that took empty string as zero index.  Also convert

     * string indices to integers if appropriate.

     */

    CHECK_FOR_FUNNY_INDEX(id);



    /* Search scopes starting with obj and following the prototype link. */

    hash = js_HashValue(id);

    for (;;) {

        JS_LOCK_OBJ(cx, obj);

        SET_OBJ_INFO(obj, file, line);

        scope = OBJ_SCOPE(obj);

        if (scope->object == obj) {

            sym = scope->ops->lookup(cx, scope, id, hash);

        } else {

            /* Shared prototype scope: try resolve before lookup. */

            sym = NULL;

        }

        if (!sym) {

            clasp = LOCKED_OBJ_GET_CLASS(obj);

            resolve = clasp->resolve;

            if (resolve != JS_ResolveStub) {

                if (clasp->flags & JSCLASS_NEW_RESOLVE) {

                    newresolve = (JSNewResolveOp)resolve;

                    flags = 0;

                    if (cx->fp && cx->fp->pc) {

                        format = js_CodeSpec[*cx->fp->pc].format;

                        if ((format & JOF_MODEMASK) != JOF_NAME)

                            flags |= JSRESOLVE_QUALIFIED;

                        if (format & JOF_SET)

                            flags |= JSRESOLVE_ASSIGNING;

                    }

                    obj2 = NULL;

                    JS_UNLOCK_OBJ(cx, obj);

                    if (!newresolve(cx, obj, js_IdToValue(id), flags, &obj2))

                        return JS_FALSE;

                    JS_LOCK_OBJ(cx, obj);

                    SET_OBJ_INFO(obj, file, line);

                    if (obj2) {

                        scope = OBJ_SCOPE(obj2);

                        if (MAP_IS_NATIVE(&scope->map))

                            sym = scope->ops->lookup(cx, scope, id, hash);

                    }

                } else {

                    JS_UNLOCK_OBJ(cx, obj);

                    if (!resolve(cx, obj, js_IdToValue(id)))

                        return JS_FALSE;

                    JS_LOCK_OBJ(cx, obj);

                    SET_OBJ_INFO(obj, file, line);

                    scope = OBJ_SCOPE(obj);

                    if (MAP_IS_NATIVE(&scope->map))

                        sym = scope->ops->lookup(cx, scope, id, hash);

                }

            }

        }

        if (sym && (sprop = sym_property(sym)) != NULL) {

            JS_ASSERT(OBJ_SCOPE(obj) == scope);

            *objp = scope->object;	/* XXXbe hide in jsscope.[ch] */

#ifdef JS_THREADSAFE

            js_HoldScopeProperty(cx, scope, sprop);

#endif

            *propp = (JSProperty *) sprop;

            return JS_TRUE;

        }

        proto = LOCKED_OBJ_GET_PROTO(obj);

        JS_UNLOCK_OBJ(cx, obj);

        if (!proto)

            break;

        if (!OBJ_IS_NATIVE(proto))

            return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);

        obj = proto;

    }

    *objp = NULL;

    *propp = NULL;

    return JS_TRUE;

}



JS_FRIEND_API(JSBool)

js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,

                JSProperty **propp)

{

    JSRuntime *rt;

    JSObject *obj, *pobj, *lastobj;

    JSProperty *prop;



    rt = cx->runtime;

    obj = cx->fp->scopeChain;

    do {

        /* Try the property cache and return immediately on cache hit. */

        JS_LOCK_OBJ(cx, obj);

        PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, prop);

        if (prop) {

#ifdef JS_THREADSAFE

            JS_ASSERT(OBJ_IS_NATIVE(obj));

            ((JSScopeProperty *)prop)->nrefs++;

#endif

            *objp = obj;

            *pobjp = obj;

            *propp = prop;

            return JS_TRUE;

        }

        JS_UNLOCK_OBJ(cx, obj);



        /* If cache miss, take the slow path. */

        if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))

            return JS_FALSE;

        if (prop) {

            PROPERTY_CACHE_FILL(cx, &rt->propertyCache, pobj, id, prop);

            *objp = obj;

            *pobjp = pobj;

            *propp = prop;

            return JS_TRUE;

        }

        lastobj = obj;

    } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);



    *objp = lastobj;

    *pobjp = NULL;

    *propp = NULL;

    return JS_TRUE;

}



JSBool

js_FindVariable(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,

                JSProperty **propp)

{

    JSObject *obj;

    JSProperty *prop;



    /*

     * First look for id's property along the "with" statement and the

     * statically-linked scope chains.

     */

    if (!js_FindProperty(cx, id, objp, pobjp, propp))

        return JS_FALSE;

    if (*propp)

        return JS_TRUE;



    /*

     * Use the top-level scope from the scope chain, which won't end in the

     * same scope as cx->globalObject for cross-context function calls.

     */

    obj = *objp;

    JS_ASSERT(obj);



    /*

     * Make a top-level variable.

     */

    if (JS_HAS_STRICT_OPTION(cx)) {

        JSString *str = JSVAL_TO_STRING(js_IdToValue(id));

        if (!JS_ReportErrorFlagsAndNumber(cx,

                                          JSREPORT_WARNING | JSREPORT_STRICT,

                                          js_GetErrorMessage, NULL,

                                          JSMSG_UNDECLARED_VAR,

                                          JS_GetStringBytes(str))) {

            return JS_FALSE;

        }

    }

    if (!OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,

                             JSPROP_ENUMERATE, &prop)) {

        return JS_FALSE;

    }

    *pobjp = obj;

    *propp = prop;

    return JS_TRUE;

}



JSBool

js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)

{

    JSObject *obj2;

    JSScopeProperty *sprop;

    JSScope *scope;

    uint32 slot;



    if (!js_LookupProperty(cx, obj, id, &obj2, (JSProperty **)&sprop))

        return JS_FALSE;

    if (!sprop) {

        jsval default_val;



        /*

         * Handle old bug that took empty string as zero index.  Also convert

         * string indices to integers if appropriate.

         */

        CHECK_FOR_FUNNY_INDEX(id);



#if JS_BUG_NULL_INDEX_PROPS

        /* Indexed properties defaulted to null in old versions. */

        default_val = (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0)

                      ? JSVAL_NULL

                      : JSVAL_VOID;

#else

        default_val = JSVAL_VOID;

#endif

        *vp = default_val;



        if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, js_IdToValue(id), vp))

            return JS_FALSE;



        /*

         * Give a strict warning if foo.bar is evaluated by a script for an

         * object foo with no property named 'bar'.

         */

        if (JS_HAS_STRICT_OPTION(cx) &&

            *vp == default_val &&

            cx->fp && cx->fp->pc &&

            (*cx->fp->pc == JSOP_GETPROP || *cx->fp->pc == JSOP_GETELEM))

        {

            jsbytecode *pc, *endpc;

            JSString *str;



            /* Kludge to allow (typeof foo == "undefined") tests. */

            JS_ASSERT(cx->fp->script);

            pc = cx->fp->pc;

            pc += js_CodeSpec[*pc].length;

            endpc = cx->fp->script->code + cx->fp->script->length;

            while (pc < endpc) {

                if (*pc == JSOP_TYPEOF)

                    return JS_TRUE;

                if (*pc != JSOP_GROUP)

                    break;

                pc++;

            }



            /* Ok, bad undefined property reference: whine about it. */

            str = js_DecompileValueGenerator(cx, JS_FALSE, js_IdToValue(id),

                                             NULL);

            if (!str ||

                !JS_ReportErrorFlagsAndNumber(cx,

                                              JSREPORT_WARNING|JSREPORT_STRICT,

                                              js_GetErrorMessage, NULL,

                                              JSMSG_UNDEFINED_PROP,

                                              JS_GetStringBytes(str))) {

                return JS_FALSE;

            }

        }

        return JS_TRUE;

    }



    if (!OBJ_IS_NATIVE(obj2)) {

        OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);

        return OBJ_GET_PROPERTY(cx, obj2, id, vp);

    }



    /* Unlock obj2 before calling getter, relock after to avoid deadlock. */

    scope = OBJ_SCOPE(obj2);

    slot = sprop->slot;

    *vp = (slot != SPROP_INVALID_SLOT)

          ? LOCKED_OBJ_GET_SLOT(obj2, slot)

          : JSVAL_VOID;

#ifndef JS_THREADSAFE

    sprop->nrefs++;

#endif

    JS_UNLOCK_SCOPE(cx, scope);

    if (!SPROP_GET(cx, sprop, obj, obj2, vp)) {

        JS_LOCK_OBJ_VOID(cx, obj2, js_DropScopeProperty(cx, scope, sprop));

        return JS_FALSE;

    }



	if (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_FW_SIMPLE_GET) {

	/* Don't set the property *vp in the prototype (obj2). */

	OBJ_DROP_PROPERTY(cx, obj2, (JSProperty *)sprop);

	return JS_TRUE;

    }



    JS_LOCK_SCOPE(cx, scope);

    sprop = js_DropScopeProperty(cx, scope, sprop);

    if (sprop && SPROP_HAS_VALID_SLOT(sprop)) {

        LOCKED_OBJ_SET_SLOT(obj2, slot, *vp);

        PROPERTY_CACHE_FILL(cx, &cx->runtime->propertyCache, obj2, id,

                            (JSProperty *)sprop);

    }

    JS_UNLOCK_SCOPE(cx, scope);

    return JS_TRUE;

}



JSBool

js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)

{

    JSRuntime *rt;

    JSClass *clasp;

    JSScope *scope;

    JSHashNumber hash;

    JSSymbol *sym, *protosym;

    JSScopeProperty *sprop;

    jsval userid;

    JSObject *proto, *tmp;

    JSPropertyOp getter, setter;

    uintN attrs;

    JSBool ok;

    jsval pval;

    uint32 slot;

    JSString *str;



    /*

     * Handle old bug that took empty string as zero index.  Also convert

     * string indices to integers if appropriate.

     */

    CHECK_FOR_FUNNY_INDEX(id);



    rt = cx->runtime;

    JS_LOCK_OBJ(cx, obj);

    clasp = LOCKED_OBJ_GET_CLASS(obj);

    scope = OBJ_SCOPE(obj);

    hash = js_HashValue(id);



    sym = scope->ops->lookup(cx, scope, id, hash);

    if (sym) {

        sprop = sym_property(sym);

#if JS_HAS_OBJ_WATCHPOINT

        if (!sprop && scope->object == obj) {

            uint32 nslots;

            jsval *slots;



            /*

             * Deleted property place-holder, could have a watchpoint that

             * holds the deleted-but-watched property.  If so, slots may have

             * shrunk, or at least freeslot may have shrunk due to the delete

             * operation destroying the property.

             */

            sprop = js_FindWatchPoint(rt, obj, js_IdToValue(id));

            if (sprop &&

                (slot = sprop->slot) != SPROP_INVALID_SLOT &&

                slot >= scope->map.freeslot) {

                if (slot >= scope->map.nslots) {

                    nslots = slot + slot / 2;

                    slots = (jsval *)

                        JS_realloc(cx, obj->slots, nslots * sizeof(jsval));

                    if (!slots) {

                        JS_UNLOCK_OBJ(cx, obj);

                        return JS_FALSE;

                    }

                    scope->map.nslots = nslots;

                    obj->slots = slots;

                }

                scope->map.freeslot = slot + 1;

            }

        }

#endif

    } else {

        sprop = NULL;

    }



    if (!sprop || (proto = scope->object) != obj) {

        /* Find a prototype property with the same id. */

        if (sprop) {

            /* Already found, check for a readonly prototype property. */

            attrs = sprop->attrs;

            if (attrs & JSPROP_READONLY)

                goto read_only;



            /* Don't clone a setter or shared prototype property. */

            if (attrs & (JSPROP_SETTER | JSPROP_SHARED)) {

                sprop->nrefs++;

                JS_UNLOCK_SCOPE(cx, scope);



                ok = SPROP_SET(cx, sprop, obj, obj, vp);

                JS_LOCK_OBJ_VOID(cx, proto,

                                 js_DropScopeProperty(cx, scope, sprop));

                return ok;

            }



            /* XXXbe ECMA violation: inherit attrs, etc. */

            userid = sprop->id;

            getter = SPROP_GETTER_SCOPE(sprop, scope);

            setter = SPROP_SETTER_SCOPE(sprop, scope);

            sym = NULL;

        } else {

            /* Not found via a shared scope: we must follow the proto chain. */

            proto = LOCKED_OBJ_GET_PROTO(obj);

            sprop = NULL;

            attrs = JSPROP_ENUMERATE;

            userid = JSVAL_NULL;

            getter = clasp->getProperty;

            setter = clasp->setProperty;



            JS_UNLOCK_OBJ(cx, obj);

            while (proto) {

                JS_LOCK_OBJ(cx, proto);

                if (OBJ_IS_NATIVE(proto)) {

                    scope = OBJ_SCOPE(proto);

                    protosym = scope->ops->lookup(cx, scope, id, hash);

                    if (protosym) {

                        sprop = sym_property(protosym);

                        if (sprop) {

                            /*

                             * Repeat the readonly and setter/shared code here.

                             * It's tricky to fuse with the code above because

                             * we must hold proto's scope-lock while loading

                             * from sprop, and finally release that lock and

                             * reacquire obj's scope-lock in this case (where

                             * obj and proto are not sharing a scope).

                             */

                            attrs = sprop->attrs;

                            if (attrs & JSPROP_READONLY) {

                                JS_UNLOCK_OBJ(cx, proto);

                                goto unlocked_read_only;

                            }

                            if (attrs & (JSPROP_SETTER | JSPROP_SHARED)) {

                                sprop->nrefs++;

                                JS_UNLOCK_SCOPE(cx, scope);



                                ok = SPROP_SET(cx, sprop, obj, obj, vp);

                                JS_LOCK_OBJ_VOID(cx, proto,

                                    js_DropScopeProperty(cx, scope, sprop));

                                return ok;

                            }



                            /* XXXbe ECMA violation: inherit attrs, etc. */

                            userid = sprop->id;

                            getter = SPROP_GETTER_SCOPE(sprop, scope);

                            setter = SPROP_SETTER_SCOPE(sprop, scope);

                            JS_UNLOCK_OBJ(cx, proto);

                            break;

                        }

                    }

                }

                tmp = LOCKED_OBJ_GET_PROTO(proto);

                JS_UNLOCK_OBJ(cx, proto);

                proto = tmp;

            }

            JS_LOCK_OBJ(cx, obj);

        }



        /* Find or make a property descriptor with the right heritage. */

        scope = js_MutateScope(cx, obj, id, getter, setter, attrs, &sprop);

        if (!scope) {

            JS_UNLOCK_OBJ(cx, obj);

            return JS_FALSE;

        }

        if (!sprop) {

            if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)

                attrs |= JSPROP_SHARED;

            sprop = js_NewScopeProperty(cx, scope, id, getter, setter, attrs);

            if (!sprop) {

                JS_UNLOCK_OBJ(cx, obj);

                return JS_FALSE;

            }

            if (!JSVAL_IS_NULL(userid))

                sprop->id = userid;

        }



        /* XXXbe called with obj locked */

        if (!clasp->addProperty(cx, obj, sprop->id, vp)) {

            js_DestroyScopeProperty(cx, scope, sprop);

            JS_UNLOCK_OBJ(cx, obj);

            return JS_FALSE;

        }



        /* Initialize new properties to undefined. */

        if (SPROP_HAS_VALID_SLOT(sprop))

            LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);



        if (sym) {

            /* Null-valued symbol left behind from a delete operation. */

            sym->entry.value = js_HoldScopeProperty(cx, scope, sprop);

        }

    }



    if (!sym) {

        /* Need a new symbol as well as a new property. */

        sym = scope->ops->add(cx, scope, id, sprop);

        if (!sym) {

            js_DestroyScopeProperty(cx, scope, sprop);

            JS_UNLOCK_OBJ(cx, obj);

            return JS_FALSE;

        }

#if JS_BUG_AUTO_INDEX_PROPS

        if (SPROP_HAS_VALID_SLOT(sprop)) {

            jsid id2 = (jsid) INT_TO_JSVAL(sprop->slot - JSSLOT_START);

            if (!scope->ops->add(cx, scope, id2, sprop)) {

                scope->ops->remove(cx, scope, id);

                JS_UNLOCK_OBJ(cx, obj);

                return JS_FALSE;

            }

            PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id2,

                                (JSProperty *)sprop);

        }

#endif

        PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id,

                            (JSProperty *)sprop);

    }



    /* Check for readonly now that we have sprop. */

    if (sprop->attrs & JSPROP_READONLY) {

read_only:

        JS_UNLOCK_OBJ(cx, obj);



unlocked_read_only:

        if (JSVERSION_IS_ECMA(cx->version))

            return JS_TRUE;

        str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,

                                         js_IdToValue(id), NULL);

        if (str) {

            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                 JSMSG_READ_ONLY, JS_GetStringBytes(str));

        }

        return JS_FALSE;

    }



    /* Get the current property value from its slot. */

    slot = sprop->slot;

    if (slot != SPROP_INVALID_SLOT) {

        JS_ASSERT(slot < obj->map->freeslot);

        pval = LOCKED_OBJ_GET_SLOT(obj, slot);

    }



    /* Hold sprop across setter callout, and drop after, in case of delete. */

    sprop->nrefs++;



    /* Avoid deadlock by unlocking obj while calling sprop's setter. */

    JS_UNLOCK_OBJ(cx, obj);



    /* Let the setter modify vp before copying from it to obj->slots[slot]. */

    if (!SPROP_SET(cx, sprop, obj, obj, vp)) {

        JS_LOCK_OBJ_VOID(cx, obj, js_DropScopeProperty(cx, scope, sprop));

        return JS_FALSE;

    }



    /* Relock obj until we are done with sprop. */

    JS_LOCK_OBJ(cx, obj);



    sprop = js_DropScopeProperty(cx, scope, sprop);



    /*

     * Check whether sprop is still around (was not deleted), and whether it

     * has a slot (it may never have had one, or we may have lost a race with

     * someone who cleared scope).

     */

    if (sprop && SPROP_HAS_VALID_SLOT(sprop)) {

        GC_POKE(cx, pval);

        LOCKED_OBJ_SET_SLOT(obj, slot, *vp);



#if JS_BUG_SET_ENUMERATE

        /* Setting a property makes it enumerable. */

        sprop->attrs |= JSPROP_ENUMERATE;

#endif

    }

    JS_UNLOCK_OBJ(cx, obj);

    return JS_TRUE;

}



JSBool

js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,

                 uintN *attrsp)

{

    JSBool noprop, ok;

    JSScopeProperty *sprop;



    noprop = !prop;

    if (noprop) {

        if (!js_LookupProperty(cx, obj, id, &obj, &prop))

            return JS_FALSE;

        if (!prop) {

            *attrsp = 0;

            return JS_TRUE;

        }

        if (!OBJ_IS_NATIVE(obj)) {

            ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp);

            OBJ_DROP_PROPERTY(cx, obj, prop);

            return ok;

        }

    }

    sprop = (JSScopeProperty *)prop;

    *attrsp = sprop->attrs;

    if (noprop)

        OBJ_DROP_PROPERTY(cx, obj, prop);

    return JS_TRUE;

}



JSBool

js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,

                 uintN *attrsp)

{

    JSBool noprop, ok;

    JSScopeProperty *sprop;



    noprop = !prop;

    if (noprop) {

        if (!js_LookupProperty(cx, obj, id, &obj, &prop))

            return JS_FALSE;

        if (!prop)

            return JS_TRUE;

        if (!OBJ_IS_NATIVE(obj)) {

            ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp);

            OBJ_DROP_PROPERTY(cx, obj, prop);

            return ok;

        }

    }

    sprop = (JSScopeProperty *)prop;

    sprop->attrs = *attrsp;

    if (noprop)

        OBJ_DROP_PROPERTY(cx, obj, prop);

    return JS_TRUE;

}



JSBool

js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)

{

#if JS_HAS_PROP_DELETE

    JSRuntime *rt;

    JSObject *proto;

    JSProperty *prop;

    JSScopeProperty *sprop;

    JSString *str;

    JSScope *scope;

    JSSymbol *sym;



    rt = cx->runtime;



    *rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID;



    /*

     * Handle old bug that took empty string as zero index.  Also convert

     * string indices to integers if appropriate.

     */

    CHECK_FOR_FUNNY_INDEX(id);



    if (!js_LookupProperty(cx, obj, id, &proto, &prop))

        return JS_FALSE;

    if (!prop || proto != obj) {

        if (prop)

            OBJ_DROP_PROPERTY(cx, proto, prop);

        /*

         * If no property, or the property comes from a prototype, call the

         * class's delProperty hook with rval as the result parameter.

         */

        return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, js_IdToValue(id),

                                                   rval);

    }



    sprop = (JSScopeProperty *)prop;

    if (sprop->attrs & JSPROP_PERMANENT) {

        OBJ_DROP_PROPERTY(cx, obj, prop);

        if (JSVERSION_IS_ECMA(cx->version)) {

            *rval = JSVAL_FALSE;

            return JS_TRUE;

        }

        str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,

                                         js_IdToValue(id), NULL);

        if (str) {

            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                 JSMSG_PERMANENT, JS_GetStringBytes(str));

        }

        return JS_FALSE;

    }



    /* XXXbe called with obj locked */

    if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, sprop->id, rval)) {

        OBJ_DROP_PROPERTY(cx, obj, prop);

        return JS_FALSE;

    }



    if (SPROP_HAS_VALID_SLOT(sprop))

        GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));

    scope = OBJ_SCOPE(obj);



    /*

     * Purge cache only if prop is not about to be destroyed (since

     * js_DestroyScopeProperty purges for us).

     */

    if (sprop->nrefs != 1) {

        PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, NULL);

    }



#if JS_HAS_OBJ_WATCHPOINT

    if (SPROP_SETTER_SCOPE(sprop, scope) == js_watch_set) {

        /*

         * Keep the symbol around with null value in case of re-set.

         * The watchpoint will hold the "deleted" property until it

         * is removed by obj_unwatch or a native JS_ClearWatchPoint.

         * See js_SetProperty for the re-set logic.

         */

        for (sym = sprop->symbols; sym; sym = sym->next) {

            if (sym_id(sym) == id) {

                sym->entry.value = NULL;

                sprop = js_DropScopeProperty(cx, scope, sprop);

                JS_ASSERT(sprop);

                goto out;

            }

        }

    }

#endif /* JS_HAS_OBJ_WATCHPOINT */



    scope->ops->remove(cx, scope, id);

out:

    OBJ_DROP_PROPERTY(cx, obj, prop);

    return JS_TRUE;

#else  /* !JS_HAS_PROP_DELETE */

    jsval null = JSVAL_NULL;



    *rval = JSVAL_VOID;

    return js_SetProperty(cx, obj, id, &null);

#endif /* !JS_HAS_PROP_DELETE */

}



JSBool

js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)

{

    jsval v;

    JSString *str;



    v = OBJECT_TO_JSVAL(obj);

    switch (hint) {

      case JSTYPE_STRING:

        /*

         * Propagate the exception if js_TryMethod finds an appropriate

         * method, and calling that method returned failure.

         */

        if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL,

                          &v))

            return JS_FALSE;



        if (!JSVAL_IS_PRIMITIVE(v)) {

            if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))

                return JS_FALSE;



            /*

             * JS1.2 never failed (except for malloc failure) to convert an

             * object to a string.  ECMA requires an error if both toString

             * and valueOf fail to produce a primitive value.

             */

            if (!JSVAL_IS_PRIMITIVE(v) && cx->version == JSVERSION_1_2) {

                char *bytes = JS_smprintf("[object %s]",

                                          OBJ_GET_CLASS(cx, obj)->name);

                if (!bytes)

                    return JS_FALSE;

                str = JS_NewString(cx, bytes, strlen(bytes));

                if (!str) {

                    free(bytes);

                    return JS_FALSE;

                }

                v = STRING_TO_JSVAL(str);

                goto out;

            }

        }

        break;



      default:

        if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))

            return JS_FALSE;

        if (!JSVAL_IS_PRIMITIVE(v)) {

            JSType type = JS_TypeOfValue(cx, v);

            if (type == hint ||

                (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) {

                goto out;

            }

            /* Don't convert to string (source object literal) for JS1.2. */

            if (cx->version == JSVERSION_1_2 && hint == JSTYPE_BOOLEAN)

                goto out;

            if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,

                              NULL, &v))

                return JS_FALSE;

        }

        break;

    }

    if (!JSVAL_IS_PRIMITIVE(v)) {

        /* Avoid recursive death through js_DecompileValueGenerator. */

        if (hint == JSTYPE_STRING) {

            str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name);

            if (!str)

                return JS_FALSE;

        } else {

            str = NULL;

        }

        *vp = OBJECT_TO_JSVAL(obj);

        str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, str);

        if (str) {

            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                 JSMSG_CANT_CONVERT_TO,

                                 JS_GetStringBytes(str),

                                 (hint == JSTYPE_VOID)

                                 ? "primitive type"

                                 : js_type_str[hint]);

        }

        return JS_FALSE;

    }

out:

    *vp = v;

    return JS_TRUE;

}



extern JSIdArray *

js_NewIdArray(JSContext *cx, jsint length)

{

    JSIdArray *ida;



    ida = (JSIdArray *)

        JS_malloc(cx, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));

    if (ida)

        ida->length = length;

    return ida;

}



extern JSIdArray *

js_GrowIdArray(JSContext *cx, JSIdArray *ida, jsint length)

{

    ida = (JSIdArray *)

        JS_realloc(cx, ida, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));

    if (ida)

        ida->length = length;

    return ida;

}



/* Private type used to iterate over all properties of a native JS object */

typedef struct JSNativeIteratorState {

    jsint next_index;   /* index into jsid array */

    JSIdArray *ida;     /* All property ids in enumeration */

} JSNativeIteratorState;



/*

 * This function is used to enumerate the properties of native JSObjects

 * and those host objects that do not define a JSNewEnumerateOp-style iterator

 * function.

 */

JSBool

js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,

             jsval *statep, jsid *idp)

{

    JSObject *proto_obj;

    JSClass *clasp;

    JSEnumerateOp enumerate;

    JSScopeProperty *sprop;

    jsint i, length;

    JSScope *scope;

    JSIdArray *ida;

    JSNativeIteratorState *state;



    clasp = OBJ_GET_CLASS(cx, obj);

    enumerate = clasp->enumerate;

    if (clasp->flags & JSCLASS_NEW_ENUMERATE)

        return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);



    switch (enum_op) {



    case JSENUMERATE_INIT:

        if (!enumerate(cx, obj))

            goto init_error;

        length = 0;



        /*

         * The set of all property ids is pre-computed when the iterator

         * is initialized so as to avoid problems with properties being

         * deleted during the iteration.

         */

        JS_LOCK_OBJ(cx, obj);

        scope = OBJ_SCOPE(obj);



        /*

         * If this object shares a scope with its prototype, don't enumerate

         * its properties.  Otherwise they will be enumerated a second time

         * when the prototype object is enumerated.

         */

        proto_obj = OBJ_GET_PROTO(cx, obj);

        if (proto_obj && scope == OBJ_SCOPE(proto_obj)) {

            ida = js_NewIdArray(cx, 0);

            if (!ida) {

                JS_UNLOCK_OBJ(cx, obj);

                goto init_error;

            }

        } else {

            /* Object has a private scope; Enumerate all props in scope. */

            for (sprop = scope->props; sprop; sprop = sprop->next) {

                if ((sprop->attrs & JSPROP_ENUMERATE) && sprop->symbols)

                    length++;

            }

            ida = js_NewIdArray(cx, length);

            if (!ida) {

                JS_UNLOCK_OBJ(cx, obj);

                goto init_error;

            }

            i = 0;

            for (sprop = scope->props; sprop; sprop = sprop->next) {

                if ((sprop->attrs & JSPROP_ENUMERATE) && sprop->symbols) {

                    JS_ASSERT(i < length);

                    ida->vector[i++] = sym_id(sprop->symbols);

                }

            }

        }

        JS_UNLOCK_OBJ(cx, obj);



        state = (JSNativeIteratorState *)

            JS_malloc(cx, sizeof(JSNativeIteratorState));

        if (!state) {

            JS_DestroyIdArray(cx, ida);

            goto init_error;

        }

        state->ida = ida;

        state->next_index = 0;

        *statep = PRIVATE_TO_JSVAL(state);

        if (idp)

            *idp = INT_TO_JSVAL(length);

        return JS_TRUE;



    case JSENUMERATE_NEXT:

        state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);

        ida = state->ida;

        length = ida->length;

        if (state->next_index != length) {

            *idp = ida->vector[state->next_index++];

            return JS_TRUE;

        }



        /* Fall through ... */



    case JSENUMERATE_DESTROY:

        state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);

        JS_DestroyIdArray(cx, state->ida);

        JS_free(cx, state);

        *statep = JSVAL_NULL;

        return JS_TRUE;



    default:

        JS_ASSERT(0);

        return JS_FALSE;

    }



init_error:

    *statep = JSVAL_NULL;

    return JS_FALSE;

}



JSBool

js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,

               jsval *vp, uintN *attrsp)

{

    JSObject *pobj;

    JSProperty *prop;

    JSScopeProperty *sprop;

    JSClass *clasp;

    JSBool ok;



    if (!js_LookupProperty(cx, obj, id, &pobj, &prop))

        return JS_FALSE;

    if (!prop) {

        *vp = JSVAL_VOID;

        *attrsp = 0;

        clasp = OBJ_GET_CLASS(cx, obj);

        return !clasp->checkAccess ||

               clasp->checkAccess(cx, obj, id, mode, vp);

    }

    if (!OBJ_IS_NATIVE(pobj)) {

        OBJ_DROP_PROPERTY(cx, pobj, prop);

        return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp);

    }

    sprop = (JSScopeProperty *)prop;

    *vp = (SPROP_HAS_VALID_SLOT(sprop))

          ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)

          : JSVAL_VOID;

    *attrsp = sprop->attrs;

    clasp = LOCKED_OBJ_GET_CLASS(obj);

    if (clasp->checkAccess) {

        JS_UNLOCK_OBJ(cx, pobj);

        ok = clasp->checkAccess(cx, obj, id, mode, vp);

        JS_LOCK_OBJ(cx, pobj);

    } else {

        ok = JS_TRUE;

    }

    OBJ_DROP_PROPERTY(cx, pobj, prop);

    return ok;

}



JSBool

js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSClass *clasp;



    clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));

    if (!clasp->call) {

        /*

         * The decompiler may need to access the args of the function in

         * progress, so we switch the function pointer in the frame to the

         * function below us, rather than the one we had hoped to call.

         * XXXbe doesn't this case arise for js_Construct too?

         */

        JSStackFrame *fp = cx->fp;

        JSFunction *fun = fp->fun;

        if (fp->down)   /* guaranteed ? */

            fp->fun = fp->down->fun;

        js_ReportIsNotFunction(cx, &argv[-2], JS_FALSE);

        fp->fun = fun;

        return JS_FALSE;

    }

    return clasp->call(cx, obj, argc, argv, rval);

}



JSBool

js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

             jsval *rval)

{

    JSClass *clasp;



    clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));

    if (!clasp->construct) {

        js_ReportIsNotFunction(cx, &argv[-2], JS_TRUE);

        return JS_FALSE;

    }

    return clasp->construct(cx, obj, argc, argv, rval);

}



JSBool

js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)

{

    JSClass *clasp;



    clasp = OBJ_GET_CLASS(cx, obj);

    if (clasp->hasInstance)

        return clasp->hasInstance(cx, obj, v, bp);

    *bp = JS_FALSE;

    return JS_TRUE;

}



JSBool

js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)

{

    JSObject *obj2;



    *bp = JS_FALSE;

    if (JSVAL_IS_PRIMITIVE(v))

        return JS_TRUE;

    obj2 = JSVAL_TO_OBJECT(v);

    while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) {

        if (obj2 == obj) {

            *bp = JS_TRUE;

            break;

        }

    }

    return JS_TRUE;

}



#ifdef JS_THREADSAFE

void

js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)

{

    js_DropScopeProperty(cx, OBJ_SCOPE(obj), (JSScopeProperty *)prop);

    JS_UNLOCK_OBJ(cx, obj);

}

#endif



JSBool

js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop)

{

    jsval v;

    JSObject *ctor;



    if (!FindConstructor(cx, name, &v))

        return JS_FALSE;

    if (JSVAL_IS_FUNCTION(cx, v)) {

        ctor = JSVAL_TO_OBJECT(v);

        if (!OBJ_GET_PROPERTY(cx, ctor,

                              (jsid)cx->runtime->atomState.classPrototypeAtom,

                              &v)) {

            return JS_FALSE;

        }

    }

    *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;

    return JS_TRUE;

}



JSBool

js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,

                     uintN attrs)

{

    /*

     * Use the given attributes for the prototype property of the constructor,

     * as user-defined constructors have a DontEnum | DontDelete prototype (it

     * may be reset), while native or "system" constructors require DontEnum |

     * ReadOnly | DontDelete.

     */

    if (!OBJ_DEFINE_PROPERTY(cx, ctor,

                             (jsid)cx->runtime->atomState.classPrototypeAtom,

                             OBJECT_TO_JSVAL(proto), NULL, NULL,

                             attrs, NULL)) {

        return JS_FALSE;

    }



    /*

     * ECMA says that Object.prototype.constructor, or f.prototype.constructor

     * for a user-defined function f, is DontEnum.

     */

    return OBJ_DEFINE_PROPERTY(cx, proto,

                               (jsid)cx->runtime->atomState.constructorAtom,

                               OBJECT_TO_JSVAL(ctor), NULL, NULL,

                               0, NULL);

}



JSBool

js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)

{

    JSObject *obj;



    if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {

        obj = NULL;

    } else if (JSVAL_IS_OBJECT(v)) {

        obj = JSVAL_TO_OBJECT(v);

        if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v))

            return JS_FALSE;

        if (JSVAL_IS_OBJECT(v))

            obj = JSVAL_TO_OBJECT(v);

    } else {

        if (JSVAL_IS_STRING(v)) {

            obj = js_StringToObject(cx, JSVAL_TO_STRING(v));

        } else if (JSVAL_IS_INT(v)) {

            obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));

        } else if (JSVAL_IS_DOUBLE(v)) {

            obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));

        } else {

            JS_ASSERT(JSVAL_IS_BOOLEAN(v));

            obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));

        }

        if (!obj)

            return JS_FALSE;

    }

    *objp = obj;

    return JS_TRUE;

}



JSObject *

js_ValueToNonNullObject(JSContext *cx, jsval v)

{

    JSObject *obj;

    JSString *str;



    if (!js_ValueToObject(cx, v, &obj))

        return NULL;

    if (!obj) {

        str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);

        if (str) {

            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                 JSMSG_NO_PROPERTIES, JS_GetStringBytes(str));

        }

    }

    return obj;

}



JSBool

js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)

{

#if JS_HAS_VALUEOF_HINT

    jsval argv[1];



    argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);

    return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv,

                        rval);

#else

    return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 0, NULL,

                        rval);

#endif

}



JSBool

js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,

             uintN argc, jsval *argv, jsval *rval)

{

    JSErrorReporter older;

    jsval fval;

    JSBool ok;



    /*

     * Report failure only if an appropriate method was found, and calling it

     * returned failure.  We propagate failure in this case to make exceptions

     * behave properly.

     */

    older = JS_SetErrorReporter(cx, NULL);

    if (OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &fval) &&

        !JSVAL_IS_PRIMITIVE(fval)) {

        ok = js_InternalCall(cx, obj, fval, argc, argv, rval);

    } else {

        ok = JS_TRUE;

    }

    JS_SetErrorReporter(cx, older);

    return ok;

}



#if JS_HAS_XDR



#include "jsxdrapi.h"



JSBool

js_XDRObject(JSXDRState *xdr, JSObject **objp)

{

    JSContext *cx;

    JSClass *clasp;

    const char *className;

    uint32 classId, classDef;

    JSBool ok;

    JSObject *proto;



    cx = xdr->cx;

    if (xdr->mode == JSXDR_ENCODE) {

        clasp = OBJ_GET_CLASS(cx, *objp);

        className = clasp->name;

        classId = JS_FindClassIdByName(xdr, className);

        classDef = !classId;

        if (classDef && !JS_RegisterClass(xdr, clasp, &classId))

            return JS_FALSE;

    } else {

        classDef = 0;

        className = NULL;

        clasp = NULL;           /* quell GCC overwarning */

    }



    /* XDR a flag word followed (if true) by the class name. */

    if (!JS_XDRUint32(xdr, &classDef))

        return JS_FALSE;

    if (classDef && !JS_XDRCString(xdr, (char **) &className))

        return JS_FALSE;



    /* From here on, return through out: to free className if it was set. */

    ok = JS_XDRUint32(xdr, &classId);

    if (!ok)

        goto out;



    if (xdr->mode != JSXDR_ENCODE) {

        if (classDef) {

            ok = js_GetClassPrototype(cx, className, &proto);

            if (!ok)

                goto out;

            clasp = OBJ_GET_CLASS(cx, proto);

            ok = JS_RegisterClass(xdr, clasp, &classId);

            if (!ok)

                goto out;

        } else {

            clasp = JS_FindClassById(xdr, classId);

            if (!clasp) {

                char numBuf[12];

                JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);

                JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                     JSMSG_CANT_FIND_CLASS, numBuf);

                ok = JS_FALSE;

                goto out;

            }

        }

    }



    if (!clasp->xdrObject) {

        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                             JSMSG_CANT_XDR_CLASS, clasp->name);

        ok = JS_FALSE;

    } else {

        ok = clasp->xdrObject(xdr, objp);

    }

out:

    if (xdr->mode != JSXDR_ENCODE && className)

        JS_free(cx, (void *)className);

    return ok;

}



#endif /* JS_HAS_XDR */



uint32

js_Mark(JSContext *cx, JSObject *obj, void *arg)

{

    JSScope *scope;

    JSScopeProperty *sprop;

    JSSymbol *sym;

    JSClass *clasp;



    JS_ASSERT(OBJ_IS_NATIVE(obj));

    scope = OBJ_SCOPE(obj);



    for (sprop = scope->props; sprop; sprop = sprop->next) {

        for (sym = sprop->symbols; sym; sym = sym->next) {

            if (JSVAL_IS_INT(sym_id(sym)))

                continue;

            GC_MARK_ATOM(cx, sym_atom(sym), arg);

        }

#if JS_HAS_GETTER_SETTER

        if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {

#ifdef GC_MARK_DEBUG

            char buf[64];

            JSAtom *atom = sym_atom(sprop->symbols);

            const char *id = (atom && ATOM_IS_STRING(atom))

                             ? JS_GetStringBytes(ATOM_TO_STRING(atom))

                             : "unknown";

#endif



            if (sprop->attrs & JSPROP_GETTER) {

#ifdef GC_MARK_DEBUG

                JS_snprintf(buf, sizeof buf, "%s %s",

                            id, js_getter_str);

#endif

                GC_MARK(cx,

                        JSVAL_TO_GCTHING((jsval)

                            SPROP_GETTER_SCOPE(sprop, scope)),

                        buf,

                        arg);

            }

            if (sprop->attrs & JSPROP_SETTER) {

#ifdef GC_MARK_DEBUG

                JS_snprintf(buf, sizeof buf, "%s %s",

                            id, js_setter_str);

#endif

                GC_MARK(cx,

                        JSVAL_TO_GCTHING((jsval)

                            SPROP_SETTER_SCOPE(sprop, scope)),

                        buf,

                        arg);

            }

        }

#endif /* JS_HAS_GETTER_SETTER */

    }



    /* No one runs while the GC is running, so we can use LOCKED_... here. */

    clasp = LOCKED_OBJ_GET_CLASS(obj);

    if (clasp->mark)

        (void) clasp->mark(cx, obj, arg);

    return (scope->object == obj) ? obj->map->freeslot : JS_INITIAL_NSLOTS;

}



void

js_Clear(JSContext *cx, JSObject *obj)

{

    JSScope *scope;

    uint32 i, n;



    /*

     * Clear our scope of all symbols and properties, only if we own the scope

     * (i.e., not if obj is unmutated and sharing its prototype's scope).

     */

    JS_LOCK_OBJ(cx, obj);

    scope = OBJ_SCOPE(obj);

    if (scope->object == obj) {

        scope->ops->clear(cx, scope);



        /* Clear slot values and reset freeslot so we're consistent. */

        i = scope->map.nslots;

        n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj));

        while (--i >= n)

            obj->slots[i] = JSVAL_VOID;

        scope->map.freeslot = n;

    }

    JS_UNLOCK_OBJ(cx, obj);

}



#ifdef DEBUG



/* Routines to print out values during debugging. */



void printChar(jschar *cp) {

    fprintf(stderr, "jschar* (0x%p) \"", cp);

    while (*cp)

        fputc(*cp++, stderr);

    fputc('"', stderr);

    fputc('\n', stderr);

}



void printString(JSString *str) {

    jsuint i;

    fprintf(stderr, "string (0x%p) \"", str);

    for (i=0; i < str->length; i++)

        fputc(str->chars[i], stderr);

    fputc('"', stderr);

    fputc('\n', stderr);

}



void printVal(JSContext *cx, jsval val);



void printObj(JSContext *cx, JSObject *jsobj) {

    jsuint i;

    jsval val;

    JSClass *clasp;



    fprintf(stderr, "object 0x%p\n", jsobj);

    clasp = OBJ_GET_CLASS(cx, jsobj);

    fprintf(stderr, "class 0x%p %s\n", clasp, clasp->name);

    for (i=0; i < jsobj->map->nslots; i++) {

        fprintf(stderr, "slot %3d ", i);

        val = jsobj->slots[i];

        if (JSVAL_IS_OBJECT(val))

            fprintf(stderr, "object 0x%p\n", JSVAL_TO_OBJECT(val));

        else

            printVal(cx, val);

    }

}



void printVal(JSContext *cx, jsval val) {

    fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val);

    if (JSVAL_IS_NULL(val)) {

        fprintf(stderr, "null\n");

    } else if (JSVAL_IS_VOID(val)) {

        fprintf(stderr, "undefined\n");

    } else if (JSVAL_IS_OBJECT(val)) {

        printObj(cx, JSVAL_TO_OBJECT(val));

    } else if (JSVAL_IS_INT(val)) {

        fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val));

    } else if (JSVAL_IS_STRING(val)) {

        printString(JSVAL_TO_STRING(val));

    } else if (JSVAL_IS_DOUBLE(val)) {

        fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val));

    } else {

        JS_ASSERT(JSVAL_IS_BOOLEAN(val));

        fprintf(stderr, "(boolean) %s\n",

                JSVAL_TO_BOOLEAN(val) ? "true" : "false");

    }

    fflush(stderr);

}



void printId(JSContext *cx, jsid id) {

    fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id);

    printVal(cx, js_IdToValue(id));

}



void printAtom(JSAtom *atom) {

    printString(ATOM_TO_STRING(atom));

}



#endif



 

**** End of jsobj.c ****

 

**** Start of jsobj.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsobj_h___

#define jsobj_h___

/*

 * JS object definitions.

 *

 * A JS object consists of a possibly-shared object descriptor containing

 * ordered property names, called the map; and a dense vector of property

 * values, called slots.  The map/slot pointer pair is GC'ed, while the map

 * is reference counted and the slot vector is malloc'ed.

 */

#include "jshash.h" /* Added by JSIFY */

#include "jsprvtd.h"

#include "jspubtd.h"



JS_BEGIN_EXTERN_C



struct JSObjectMap {

    jsrefcount  nrefs;          /* count of all referencing objects */

    JSObjectOps *ops;           /* high level object operation vtable */

    uint32      nslots;         /* length of obj->slots vector */

    uint32      freeslot;       /* index of next free obj->slots element */

};



/* Shorthand macros for frequently-made calls. */

#if defined JS_THREADSAFE && defined DEBUG

#define OBJ_LOOKUP_PROPERTY(cx,obj,id,objp,propp)                             \

    (obj)->map->ops->lookupProperty(cx,obj,id,objp,propp,__FILE__,__LINE__)

#else

#define OBJ_LOOKUP_PROPERTY(cx,obj,id,objp,propp)                             \

    (obj)->map->ops->lookupProperty(cx,obj,id,objp,propp)

#endif

#define OBJ_DEFINE_PROPERTY(cx,obj,id,value,getter,setter,attrs,propp)        \

    (obj)->map->ops->defineProperty(cx,obj,id,value,getter,setter,attrs,propp)

#define OBJ_GET_PROPERTY(cx,obj,id,vp)                                        \

    (obj)->map->ops->getProperty(cx,obj,id,vp)

#define OBJ_SET_PROPERTY(cx,obj,id,vp)                                        \

    (obj)->map->ops->setProperty(cx,obj,id,vp)

#define OBJ_GET_ATTRIBUTES(cx,obj,id,prop,attrsp)                             \

    (obj)->map->ops->getAttributes(cx,obj,id,prop,attrsp)

#define OBJ_SET_ATTRIBUTES(cx,obj,id,prop,attrsp)                             \

    (obj)->map->ops->setAttributes(cx,obj,id,prop,attrsp)

#define OBJ_DELETE_PROPERTY(cx,obj,id,rval)                                   \

    (obj)->map->ops->deleteProperty(cx,obj,id,rval)

#define OBJ_DEFAULT_VALUE(cx,obj,hint,vp)                                     \

    (obj)->map->ops->defaultValue(cx,obj,hint,vp)

#define OBJ_ENUMERATE(cx,obj,enum_op,statep,idp)                              \

    (obj)->map->ops->enumerate(cx,obj,enum_op,statep,idp)

#define OBJ_CHECK_ACCESS(cx,obj,id,mode,vp,attrsp)                            \

    (obj)->map->ops->checkAccess(cx,obj,id,mode,vp,attrsp)



/* These two are time-optimized to avoid stub calls. */

#define OBJ_THIS_OBJECT(cx,obj)                                               \

    ((obj)->map->ops->thisObject                                              \

     ? (obj)->map->ops->thisObject(cx,obj)                                    \

     : (obj))

#define OBJ_DROP_PROPERTY(cx,obj,prop)                                        \

    ((obj)->map->ops->dropProperty                                            \

     ? (obj)->map->ops->dropProperty(cx,obj,prop)                             \

     : (void)0)



struct JSObject {

    JSObjectMap *map;

    jsval       *slots;

};



#define JSSLOT_PROTO        0

#define JSSLOT_PARENT       1

#define JSSLOT_CLASS        2

#define JSSLOT_PRIVATE      3

#define JSSLOT_START        3



#define JSSLOT_FREE(clasp)  (((clasp)->flags & JSCLASS_HAS_PRIVATE)           \

			     ? JSSLOT_PRIVATE + 1                             \

			     : JSSLOT_START)



#define JS_INITIAL_NSLOTS   5



#ifdef DEBUG

#define MAP_CHECK_SLOT(map,slot) \

    JS_ASSERT((uint32)slot < JS_MAX((map)->nslots, (map)->freeslot))

#define OBJ_CHECK_SLOT(obj,slot) \

    MAP_CHECK_SLOT((obj)->map, slot)

#else

#define OBJ_CHECK_SLOT(obj,slot) ((void)0)

#endif



/* Fast macros for accessing obj->slots while obj is locked (if thread-safe). */

#define LOCKED_OBJ_GET_SLOT(obj,slot) \

    (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot])

#define LOCKED_OBJ_SET_SLOT(obj,slot,value) \

    (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot] = (value))

#define LOCKED_OBJ_GET_PROTO(obj) \

    JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO))

#define LOCKED_OBJ_GET_CLASS(obj) \

    ((JSClass *)JSVAL_TO_PRIVATE(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_CLASS)))



#ifdef JS_THREADSAFE



/* Thread-safe functions and wrapper macros for accessing obj->slots. */

#define OBJ_GET_SLOT(cx,obj,slot)                                             \

    (OBJ_CHECK_SLOT(obj, slot),                                               \

     (!OBJ_IS_NATIVE(obj) || OBJ_SCOPE(obj)->ownercx == cx)                   \

     ? LOCKED_OBJ_GET_SLOT(obj, slot)                                         \

     : js_GetSlotThreadSafe(cx, obj, slot))



#define OBJ_SET_SLOT(cx,obj,slot,value)                                       \

    (OBJ_CHECK_SLOT(obj, slot),                                               \

     (!OBJ_IS_NATIVE(obj) || OBJ_SCOPE(obj)->ownercx == cx)                   \

     ? (void) LOCKED_OBJ_SET_SLOT(obj, slot, value)                           \

     : js_SetSlotThreadSafe(cx, obj, slot, value))



#else   /* !JS_THREADSAFE */



#define OBJ_GET_SLOT(cx,obj,slot)       LOCKED_OBJ_GET_SLOT(obj,slot)

#define OBJ_SET_SLOT(cx,obj,slot,value) LOCKED_OBJ_SET_SLOT(obj,slot,value)



#endif /* !JS_THREADSAFE */



/* Thread-safe proto, parent, and class access macros. */

#define OBJ_GET_PROTO(cx,obj) \

    JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO))

#define OBJ_SET_PROTO(cx,obj,proto) \

    OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto))



#define OBJ_GET_PARENT(cx,obj) \

    JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT))

#define OBJ_SET_PARENT(cx,obj,parent) \

    OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent))



#define OBJ_GET_CLASS(cx,obj) \

    ((JSClass *)JSVAL_TO_PRIVATE(OBJ_GET_SLOT(cx, obj, JSSLOT_CLASS)))



/* Test whether a map or object is native. */

#define MAP_IS_NATIVE(map)                                                    \

    ((map)->ops == &js_ObjectOps ||                                           \

     ((map)->ops && (map)->ops->newObjectMap == js_ObjectOps.newObjectMap))



#define OBJ_IS_NATIVE(obj)  MAP_IS_NATIVE((obj)->map)



extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps;

extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps;

extern JSClass  js_ObjectClass;

extern JSClass  js_WithClass;



struct JSSharpObjectMap {

    jsrefcount  depth;

    jsatomid    sharpgen;

    JSHashTable *table;

};



#define SHARP_BIT       1

#define IS_SHARP(he)	((jsatomid)(he)->value & SHARP_BIT)

#define MAKE_SHARP(he)  ((he)->value = (void*)((jsatomid)(he)->value|SHARP_BIT))



extern JSHashEntry *

js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,

		    jschar **sp);



extern void

js_LeaveSharpObject(JSContext *cx, JSIdArray **idap);



extern JSBool

js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		jsval *rval);



extern JSBool

js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		jsval *rval);



extern JSObject *

js_InitObjectClass(JSContext *cx, JSObject *obj);



/* Select Object.prototype method names shared between jsapi.c and jsobj.c. */

extern const char js_watch_str[];

extern const char js_unwatch_str[];

extern const char js_hasOwnProperty_str[];

extern const char js_isPrototypeOf_str[];

extern const char js_propertyIsEnumerable_str[];

extern const char js_defineGetter_str[];

extern const char js_defineSetter_str[];



extern void

js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,

		 JSClass *clasp);



extern JSObjectMap *

js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,

		JSClass *clasp, JSObject *obj);



extern void

js_DestroyObjectMap(JSContext *cx, JSObjectMap *map);



extern JSObjectMap *

js_HoldObjectMap(JSContext *cx, JSObjectMap *map);



extern JSObjectMap *

js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj);



extern JSObject *

js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent);



extern JSObject *

js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,

		   JSObject *parent);



extern void

js_FinalizeObject(JSContext *cx, JSObject *obj);



extern JSBool

js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp);



extern void

js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot);



/*

 * On error, return false.  On success, if propp is non-null, return true with

 * obj locked and with a held property in *propp; if propp is null, return true

 * but release obj's lock first.  Therefore all callers who pass non-null propp

 * result parameters must later call OBJ_DROP_PROPERTY(cx, obj, *propp) both to

 * drop the held property, and to release the lock on obj.

 */

extern JSBool

js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,

		  JSPropertyOp getter, JSPropertyOp setter, uintN attrs,

		  JSProperty **propp);



/*

 * Unlike js_DefineProperty, propp must be non-null.  On success, and if id was

 * found, return true with *objp non-null and locked, and with a held property

 * stored in *propp.  If successful but id was not found, return true with both

 * *objp and *propp null.  Therefore all callers who receive a non-null *propp

 * must later call OBJ_DROP_PROPERTY(cx, *objp, *propp).

 */

#if defined JS_THREADSAFE && defined DEBUG

extern JS_FRIEND_API(JSBool)

_js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,

		   JSProperty **propp, const char *file, uintN line);



#define js_LookupProperty(cx,obj,id,objp,propp) \

    _js_LookupProperty(cx,obj,id,objp,propp,__FILE__,__LINE__)

#else

extern JS_FRIEND_API(JSBool)

js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,

		  JSProperty **propp);

#endif



extern JS_FRIEND_API(JSBool)

js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,

		JSProperty **propp);



extern JSBool

js_FindVariable(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,

		JSProperty **propp);



extern JSObject *

js_FindVariableScope(JSContext *cx, JSFunction **funp);



extern JSBool

js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp);



extern JSBool

js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp);



extern JSBool

js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,

		 uintN *attrsp);



extern JSBool

js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,

		 uintN *attrsp);



extern JSBool

js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval);



extern JSBool

js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp);



extern JSIdArray *

js_NewIdArray(JSContext *cx, jsint length);



extern JSIdArray *

js_GrowIdArray(JSContext *cx, JSIdArray *ida, jsint length);



extern JSBool

js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,

	     jsval *statep, jsid *idp);



extern JSBool

js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,

	       jsval *vp, uintN *attrsp);



extern JSBool

js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);



extern JSBool

js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	     jsval *rval);



extern JSBool

js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);



extern JSBool

js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj);



extern JSBool

js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp);



extern JSBool

js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop);



extern JSBool

js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,

		     uintN attrs);



extern JSBool

js_ValueToObject(JSContext *cx, jsval v, JSObject **objp);



extern JSObject *

js_ValueToNonNullObject(JSContext *cx, jsval v);



extern JSBool

js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval);



extern JSBool

js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,

	     uintN argc, jsval *argv, jsval *rval);



extern JSBool

js_XDRObject(JSXDRState *xdr, JSObject **objp);



extern uint32

js_Mark(JSContext *cx, JSObject *obj, void *arg);



extern void

js_Clear(JSContext *cx, JSObject *obj);



JS_END_EXTERN_C



#endif /* jsobj_h___ */

 

**** End of jsobj.h ****

 

**** Start of jsopcode.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS bytecode descriptors, disassemblers, and decompilers.

 */

#include "jsstddef.h"

#ifdef HAVE_MEMORY_H

#include <memory.h>

#endif

#include <stdarg.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsarena.h" /* Added by JSIFY */

#include "jsutil.h" /* Added by JSIFY */

#include "jsdtoa.h"

#include "jsprf.h"

#include "jsapi.h"

#include "jsarray.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsdbgapi.h"

#include "jsemit.h"

#include "jsfun.h"

#include "jslock.h"

#include "jsobj.h"

#include "jsopcode.h"

#include "jsscope.h"

#include "jsscript.h"

#include "jsstr.h"



char js_const_str[]         = "const";

char js_var_str[]           = "var";

char js_function_str[]      = "function";

char js_in_str[]            = "in";

char js_instanceof_str[]    = "instanceof";

char js_new_str[]           = "new";

char js_delete_str[]        = "delete";

char js_typeof_str[]        = "typeof";

char js_void_str[]          = "void";

char js_null_str[]          = "null";

char js_this_str[]          = "this";

char js_false_str[]         = "false";

char js_true_str[]          = "true";



char *js_incop_str[]        = {"++", "--"};



/* Pollute the namespace locally for MSVC Win16, but not for WatCom.  */

#ifdef __WINDOWS_386__

    #ifdef FAR

	#undef FAR

    #endif

#else  /* !__WINDOWS_386__ */

#ifndef FAR

#define FAR

#endif

#endif /* !__WINDOWS_386__ */



JSCodeSpec FAR js_CodeSpec[] = {

#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \

    {name,token,length,nuses,ndefs,prec,format},

#include "jsopcode.tbl"

#undef OPDEF

};



uintN js_NumCodeSpecs = sizeof (js_CodeSpec) / sizeof js_CodeSpec[0];



/************************************************************************/



#ifdef DEBUG



JS_FRIEND_API(void)

js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)

{

    jsbytecode *pc, *end;

    uintN len;



    pc = script->code;

    end = pc + script->length;

    while (pc < end) {

	if (pc == script->main)

	    fputs("main:\n", fp);

	len = js_Disassemble1(cx, script, pc,

                              PTRDIFF(pc, script->code, jsbytecode),

                              lines, fp);

	if (!len)

	    return;

	pc += len;

    }

}



JS_FRIEND_API(uintN)

js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,

		JSBool lines, FILE *fp)

{

    JSOp op;

    JSCodeSpec *cs;

    intN len, off;

    JSAtom *atom;

    JSString *str;

    char *cstr;



    op = (JSOp)*pc;

    if (op >= JSOP_LIMIT) {

	char numBuf1[12], numBuf2[12];

	JS_snprintf(numBuf1, sizeof numBuf1, "%d", op);

	JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT);

	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

			     JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);

	return 0;

    }

    cs = &js_CodeSpec[op];

    len = (intN)cs->length;

    fprintf(fp, "%05u:", loc);

    if (lines)

	fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc));

    fprintf(fp, "  %s", cs->name);

    switch (cs->format & JOF_TYPEMASK) {

      case JOF_BYTE:

	if (op == JSOP_TRAP) {

	    op = JS_GetTrapOpcode(cx, script, pc);

	    if (op == JSOP_LIMIT)

		return 0;

	    len = (intN)js_CodeSpec[op].length;

	}

	break;



      case JOF_JUMP:

	off = GET_JUMP_OFFSET(pc);

	fprintf(fp, " %u (%d)", loc + off, off);

	break;



      case JOF_CONST:

	atom = GET_ATOM(cx, script, pc);

	str = js_ValueToSource(cx, ATOM_KEY(atom));

	if (!str)

	    return 0;

	cstr = js_DeflateString(cx, str->chars, str->length);

	if (!cstr)

	    return 0;

	fprintf(fp, " %s", cstr);

	JS_free(cx, cstr);

	break;



      case JOF_UINT16:

	fprintf(fp, " %u", GET_ARGC(pc));

	break;



#if JS_HAS_SWITCH_STATEMENT

      case JOF_TABLESWITCH:

      {

	jsbytecode *pc2, *end;

	jsint i, low, high;



	pc2 = pc;

	off = GET_JUMP_OFFSET(pc2);

	end = pc + off;

	pc2 += JUMP_OFFSET_LEN;

	low = GET_JUMP_OFFSET(pc2);

	pc2 += JUMP_OFFSET_LEN;

	high = GET_JUMP_OFFSET(pc2);

	pc2 += JUMP_OFFSET_LEN;

	fprintf(fp, " defaultOffset %d low %d high %d", off, low, high);

        for (i = low; i <= high; i++) {

            off = GET_JUMP_OFFSET(pc2);

            fprintf(fp, "\n\t%d: %d", i, off);

            pc2 += JUMP_OFFSET_LEN;

        }

	len = 1 + pc2 - pc;

	break;

      }



      case JOF_LOOKUPSWITCH:

      {

	jsbytecode *pc2 = pc;

	jsint npairs;



	off = GET_JUMP_OFFSET(pc2);

	pc2 += JUMP_OFFSET_LEN;

	npairs = (jsint) GET_ATOM_INDEX(pc2);

	pc2 += ATOM_INDEX_LEN;

	fprintf(fp, " offset %d npairs %u", off, (uintN) npairs);

	while (npairs) {

	    atom = GET_ATOM(cx, script, pc2);

	    pc2 += ATOM_INDEX_LEN;

	    off = GET_JUMP_OFFSET(pc2);

	    pc2 += JUMP_OFFSET_LEN;



	    str = js_ValueToSource(cx, ATOM_KEY(atom));

	    if (!str)

		return 0;

	    cstr = js_DeflateString(cx, str->chars, str->length);

	    if (!cstr)

		return 0;

	    fprintf(fp, "\n\t%s: %d", cstr, off);

	    JS_free(cx, cstr);

	    npairs--;

	}

	len = 1 + pc2 - pc;

	break;

      }

#endif /* JS_HAS_SWITCH_STATEMENT */



      case JOF_QARG:

	fprintf(fp, " %u", GET_ARGNO(pc));

	break;



      case JOF_QVAR:

	fprintf(fp, " %u", GET_VARNO(pc));

	break;



#if JS_HAS_LEXICAL_CLOSURE

      case JOF_DEFLOCALVAR:

        fprintf(fp, " %u", GET_VARNO(pc));

        pc += VARNO_LEN;

        atom = GET_ATOM(cx, script, pc);

        str = js_ValueToSource(cx, ATOM_KEY(atom));

        if (!str)

            return 0;

        cstr = js_DeflateString(cx, str->chars, str->length);

        if (!cstr)

            return 0;

        fprintf(fp, " %s", cstr);

        JS_free(cx, cstr);

        break;

#endif



      default: {

	char numBuf[12];

	JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);

	JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

			     JSMSG_UNKNOWN_FORMAT, numBuf);

	return 0;

      }

    }

    fputs("\n", fp);

    return len;

}



#endif /* DEBUG */



/************************************************************************/



/*

 * Sprintf, but with unlimited and automatically allocated buffering.

*/

typedef struct Sprinter {

    JSContext       *context;       /* context executing the decompiler */

    JSArenaPool     *pool;          /* string allocation pool */

    char            *base;          /* base address of buffer in pool */

    size_t          size;           /* size of buffer allocated at base */

    ptrdiff_t       offset;         /* offset of next free char in buffer */

} Sprinter;



#define INIT_SPRINTER(cx, sp, ap, off) \

    ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0,  \

     (sp)->offset = off)



#define OFF2STR(sp,off) ((sp)->base + (off))

#define STR2OFF(sp,str) ((str) - (sp)->base)



static JSBool

SprintAlloc(Sprinter *sp, size_t nb)

{

    if (!sp->base) {

	JS_ARENA_ALLOCATE_CAST(sp->base, char *, sp->pool, nb);

    } else {

	JS_ARENA_GROW_CAST(sp->base, char *, sp->pool, sp->size, nb);

    }

    if (!sp->base) {

	JS_ReportOutOfMemory(sp->context);

	return JS_FALSE;

    }

    sp->size += nb;

    return JS_TRUE;

}



static ptrdiff_t

SprintPut(Sprinter *sp, const char *s, size_t len)

{

    ptrdiff_t nb, offset;

    char *bp;



    /* Allocate space for s, including the '\0' at the end. */

    nb = (sp->offset + len + 1) - sp->size;

    if (nb > 0 && !SprintAlloc(sp, nb))

	return -1;



    /* Advance offset and copy s into sp's buffer. */

    offset = sp->offset;

    sp->offset += len;

    bp = sp->base + offset;

    memcpy(bp, s, len);

    bp[len] = 0;

    return offset;

}



static ptrdiff_t

Sprint(Sprinter *sp, const char *format, ...)

{

    va_list ap;

    char *bp;

    ptrdiff_t offset;



    va_start(ap, format);

    bp = JS_vsmprintf(format, ap);	/* XXX vsaprintf */

    va_end(ap);

    if (!bp) {

	JS_ReportOutOfMemory(sp->context);

	return -1;

    }

    offset = SprintPut(sp, bp, strlen(bp));

    free(bp);

    return offset;

}



jschar js_EscapeMap[] = {

    '\b', 'b',

    '\f', 'f',

    '\n', 'n',

    '\r', 'r',

    '\t', 't',

    '\v', 'v',

    '"',  '"',

    '\'', '\'',

    '\\', '\\',

    0

};



static char *

QuoteString(Sprinter *sp, JSString *str, jschar quote)

{

    ptrdiff_t off, len, nb;

    const jschar *s, *t, *u, *z;

    char *bp;

    jschar c;

    JSBool ok;



    /* Sample off first for later return value pointer computation. */

    off = sp->offset;

    if (Sprint(sp, "%c", (char)quote) < 0)

	return NULL;



    /* Loop control variables: z points at end of string sentinel. */

    s = str->chars;

    z = s + str->length;

    for (t = s; t < z; s = ++t) {

	/* Move t forward from s past un-quote-worthy characters. */

	c = *t;

	while (JS_ISPRINT(c) && c != quote && c != '\\' && !(c >> 8)) {

	    c = *++t;

	    if (t == z)

		break;

	}

	len = PTRDIFF(t, s, jschar);



	/* Allocate space for s, including the '\0' at the end. */

	nb = (sp->offset + len + 1) - sp->size;

	if (nb > 0 && !SprintAlloc(sp, nb))

	    return NULL;



	/* Advance sp->offset and copy s into sp's buffer. */

	bp = sp->base + sp->offset;

	sp->offset += len;

	while (--len >= 0)

	    *bp++ = (char) *s++;

	*bp = '\0';



	if (t == z)

	    break;



	/* Use js_EscapeMap, \u, or \x only if necessary. */

	if ((u = js_strchr(js_EscapeMap, c)) != NULL)

	    ok = Sprint(sp, "\\%c", (char)u[1]) >= 0;

	else

	    ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0;

	if (!ok)

	    return NULL;

    }



    /* Sprint the closing quote and return the quoted string. */

    if (Sprint(sp, "%c", (char)quote) < 0)

	return NULL;

    return OFF2STR(sp, off);

}



JSString *

js_QuoteString(JSContext *cx, JSString *str, jschar quote)

{

    void *mark;

    Sprinter sprinter;

    char *bytes;

    JSString *escstr;



    mark = JS_ARENA_MARK(&cx->tempPool);

    INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);

    bytes = QuoteString(&sprinter, str, quote);

    escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;

    JS_ARENA_RELEASE(&cx->tempPool, mark);

    return escstr;

}



/************************************************************************/



struct JSPrinter {

    Sprinter        sprinter;       /* base class state */

    JSArenaPool     pool;           /* string allocation pool */

    uintN           indent;         /* indentation in spaces */

    JSBool          pretty;         /* pretty-print: indent, use newlines */

    JSScript        *script;        /* script being printed */

    JSScope         *scope;         /* script function scope */

};



JSPrinter *

js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty)

{

    JSPrinter *jp;

    JSStackFrame *fp;

    JSObjectMap *map;



    jp = (JSPrinter *) JS_malloc(cx, sizeof(JSPrinter));

    if (!jp)

	return NULL;

    INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);

    JS_InitArenaPool(&jp->pool, name, 256, 1);

    jp->indent = indent;

    jp->pretty = pretty;

    jp->script = NULL;

    jp->scope = NULL;

    fp = cx->fp;

    if (fp && fp->fun && fp->fun->object) {

	map = fp->fun->object->map;

	if (MAP_IS_NATIVE(map))

	    jp->scope = (JSScope *)map;

    }

    return jp;

}



void

js_DestroyPrinter(JSPrinter *jp)

{

    JS_FinishArenaPool(&jp->pool);

    JS_free(jp->sprinter.context, jp);

}



JSString *

js_GetPrinterOutput(JSPrinter *jp)

{

    JSContext *cx;

    JSString *str;



    cx = jp->sprinter.context;

    if (!jp->sprinter.base)

	return cx->runtime->emptyString;

    str = JS_NewStringCopyZ(cx, jp->sprinter.base);

    if (!str)

	return NULL;

    JS_FreeArenaPool(&jp->pool);

    INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);

    return str;

}



int

js_printf(JSPrinter *jp, const char *format, ...)

{

    va_list ap;

    char *bp, *fp;

    int cc;



    if (*format == '\0')

        return 0;



    va_start(ap, format);



    /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */

    if (*format == '\t') {

        if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0)

	    return -1;

	format++;

    }



    /* Suppress newlines (must be once per format, at the end) if not pretty. */

    fp = NULL;

    if (!jp->pretty && format[cc = strlen(format)-1] == '\n') {

        fp = JS_strdup(jp->sprinter.context, format);

        if (!fp)

            return -1;

        fp[cc] = '\0';

        format = fp;

    }



    /* Allocate temp space, convert format, and put. */

    bp = JS_vsmprintf(format, ap);	/* XXX vsaprintf */

    if (fp) {

        JS_free(jp->sprinter.context, fp);

        format = NULL;

    }

    if (!bp) {

	JS_ReportOutOfMemory(jp->sprinter.context);

	return -1;

    }



    cc = strlen(bp);

    if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)

	cc = -1;

    free(bp);



    va_end(ap);

    return cc;

}



JSBool

js_puts(JSPrinter *jp, const char *s)

{

    return SprintPut(&jp->sprinter, s, strlen(s)) >= 0;

}



/************************************************************************/



typedef struct SprintStack {

    Sprinter    sprinter;       /* sprinter for postfix to infix buffering */

    ptrdiff_t   *offsets;       /* stack of postfix string offsets */

    jsbytecode  *opcodes;       /* parallel stack of JS opcodes */

    uintN       top;            /* top of stack index */

    JSPrinter   *printer;       /* permanent output goes here */

} SprintStack;



/* Gap between stacked strings to allow for insertion of parens and commas. */

#define PAREN_SLOP	(2 + 1)



/*

 * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,

 * JSOP_SETPROP, and JSOP_SETELEM, respectively.  See the first assertion in

 * PushOff.

 */

#define JSOP_GETPROP2   254

#define JSOP_GETELEM2   255



static JSBool

PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)

{

    uintN top;



#if JSOP_LIMIT > JSOP_GETPROP2

#error JSOP_LIMIT must be <= JSOP_GETPROP2

#endif

    if (!SprintAlloc(&ss->sprinter, PAREN_SLOP))

	return JS_FALSE;



    /* ss->top points to the next free slot; be paranoid about overflow. */

    top = ss->top;

    JS_ASSERT(top < ss->printer->script->depth);

    if (top >= ss->printer->script->depth) {

	JS_ReportOutOfMemory(ss->sprinter.context);

	return JS_FALSE;

    }



    /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */

    ss->offsets[top] = off;

    ss->opcodes[top] = (op == JSOP_GETPROP2) ? JSOP_GETPROP

		     : (op == JSOP_GETELEM2) ? JSOP_GETELEM

		     : (jsbytecode) op;

    ss->top = ++top;

    ss->sprinter.offset += PAREN_SLOP;

    return JS_TRUE;

}



static ptrdiff_t

PopOff(SprintStack *ss, JSOp op)

{

    uintN top;

    JSCodeSpec *cs, *topcs;

    ptrdiff_t off;



    /* ss->top points to the next free slot; be paranoid about underflow. */

    top = ss->top;

    JS_ASSERT(top != 0);

    if (top == 0)

	return 0;



    ss->top = --top;

    topcs = &js_CodeSpec[ss->opcodes[top]];

    cs = &js_CodeSpec[op];

    if (topcs->prec != 0 && topcs->prec < cs->prec) {

	ss->offsets[top] -= 2;

	ss->sprinter.offset = ss->offsets[top];

	off = Sprint(&ss->sprinter, "(%s)",

		     OFF2STR(&ss->sprinter, ss->sprinter.offset + 2));

    } else {

	off = ss->sprinter.offset = ss->offsets[top];

    }

    return off;

}



#if JS_HAS_SWITCH_STATEMENT

typedef struct TableEntry {

    jsval       key;

    ptrdiff_t   offset;

} TableEntry;



static int

CompareOffsets(const void *v1, const void *v2, void *arg)

{

    const TableEntry *te1 = (const TableEntry *) v1,

                     *te2 = (const TableEntry *) v2;



    return te1->offset - te2->offset;

}



static JSBool

Decompile(SprintStack *ss, jsbytecode *pc, intN nb);



static JSBool

DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,

		jsbytecode *pc, ptrdiff_t switchLength,

		ptrdiff_t defaultOffset, JSBool isCondSwitch)

{

    JSContext *cx;

    JSPrinter *jp;

    char *lval, *rval;

    uintN i;

    ptrdiff_t diff, off, off2, caseExprOff;

    jsval key;

    JSString *str;



    cx = ss->sprinter.context;

    jp = ss->printer;



    lval = OFF2STR(&ss->sprinter, PopOff(ss, JSOP_NOP));

    js_printf(jp, "\tswitch (%s) {\n", lval);



    if (tableLength) {

	diff = table[0].offset - defaultOffset;

	if (diff > 0) {

	    jp->indent += 2;

	    js_printf(jp, "\tdefault:\n");

	    jp->indent += 2;

	    if (!Decompile(ss, pc + defaultOffset, diff))

		return JS_FALSE;

	    jp->indent -= 4;

	}



	caseExprOff = isCondSwitch

                      ? (ptrdiff_t) js_CodeSpec[JSOP_CONDSWITCH].length

                      : 0;



	for (i = 0; i < tableLength; i++) {

	    off = table[i].offset;

	    if (i + 1 < tableLength)

		off2 = table[i + 1].offset;

	    else

		off2 = switchLength;



	    key = table[i].key;

	    if (isCondSwitch) {

		ptrdiff_t nextCaseExprOff;



		/*

		 * key encodes the JSOP_CASE bytecode's offset from switchtop.

		 * The next case expression follows immediately, unless we are

		 * at the last case.

		 */

		nextCaseExprOff = (ptrdiff_t)

		    (JSVAL_TO_INT(key) + js_CodeSpec[JSOP_CASE].length);

		jp->indent += 2;

		if (!Decompile(ss, pc + caseExprOff,

			       nextCaseExprOff - caseExprOff)) {

		    return JS_FALSE;

		}

		caseExprOff = nextCaseExprOff;

	   } else {

		/*

		 * key comes from an atom, not the decompiler, so we need to

		 * quote it if it's a string literal.

		 */

		str = js_ValueToString(cx, key);

		if (!str)

		    return JS_FALSE;

		jp->indent += 2;

		if (JSVAL_IS_STRING(key)) {

		    rval = QuoteString(&ss->sprinter, str, (jschar)'"');

		    if (!rval)

			return JS_FALSE;

		} else {

		    rval = JS_GetStringBytes(str);

		}

		js_printf(jp, "\tcase %s:\n", rval);

	    }



	    jp->indent += 2;

	    if (off <= defaultOffset && defaultOffset < off2) {

		diff = defaultOffset - off;

		if (diff != 0) {

		    if (!Decompile(ss, pc + off, diff))

			return JS_FALSE;

		    off = defaultOffset;

		}

		jp->indent -= 2;

		js_printf(jp, "\tdefault:\n");

		jp->indent += 2;

	    }

	    if (!Decompile(ss, pc + off, off2 - off))

		return JS_FALSE;

	    jp->indent -= 4;

	}

    }



    if (defaultOffset == switchLength) {

	jp->indent += 2;

	js_printf(jp, "\tdefault:\n");

	jp->indent -= 2;

    }

    js_printf(jp, "\t}\n");

    return JS_TRUE;

}

#endif



static JSAtom *

GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot)

{

    JSScope *scope;

    JSScopeProperty *sprop;

    JSObject *obj, *proto;



    scope = jp->scope;

    while (scope) {

        for (sprop = scope->props; sprop; sprop = sprop->next) {

            if (SPROP_GETTER_SCOPE(sprop, scope) != getter)

                continue;

            if ((uintN)JSVAL_TO_INT(sprop->id) == slot)

                return sym_atom(sprop->symbols);

        }

        obj = scope->object;

        if (!obj)

            break;

        proto = OBJ_GET_PROTO(jp->sprinter.context, obj);

        if (!proto)

            break;

        scope = OBJ_SCOPE(proto);

    }

    return NULL;

}



static const char *

VarPrefix(jssrcnote *sn)

{

    char *kw;

    static char buf[8];



    kw = NULL;

    if (sn) {

        if (SN_TYPE(sn) == SRC_VAR)

            kw = js_var_str;

        else if (SN_TYPE(sn) == SRC_CONST)

            kw = js_const_str;

    }

    if (!kw)

        return "";

    JS_snprintf(buf, sizeof buf, "%s ", kw);

    return buf;

}



static JSBool

IsASCIIIdentifier(JSString *str)

{

    size_t n;

    jschar *s, c;



    n = str->length;

    s = str->chars;

    c = *s;

    if (n == 0 || !JS_ISIDENT_START(c) || !JS_ISPRINT(c))

        return JS_FALSE;

    for (n--; n != 0; n--) {

        c = *++s;

        if (!JS_ISIDENT(c) || !JS_ISPRINT(c))

            return JS_FALSE;

    }

    return JS_TRUE;

}



static JSBool

Decompile(SprintStack *ss, jsbytecode *pc, intN nb)

{

    JSContext *cx;

    JSPrinter *jp, *jp2;

    jsbytecode *endpc, *done, *forelem_done;

    ptrdiff_t len, todo, oplen, cond, next, tail;

    JSOp op, lastop, saveop;

    JSCodeSpec *cs, *topcs;

    jssrcnote *sn;

    const char *lval, *rval = NULL, *xval;

    jsint i, argc;

    char **argv;

    JSAtom *atom;

    JSObject *obj;

    JSFunction *fun;

    JSString *str;

    JSBool ok;

    jsval key;



/*

 * Local macros

*/

#define DECOMPILE_CODE(pc,nb)	if (!Decompile(ss, pc, nb)) return JS_FALSE

#define POP_STR()		OFF2STR(&ss->sprinter, PopOff(ss, op))

#define LOCAL_ASSERT(expr)	JS_ASSERT(expr); if (!(expr)) return JS_FALSE



    cx = ss->sprinter.context;

    jp = ss->printer;

    endpc = pc + nb;

    todo = -2;			/* NB: different from Sprint() error return. */

    op = JSOP_NOP;

    while (pc < endpc) {

	lastop = op;

	op = saveop = (JSOp) *pc;

	if (op >= JSOP_LIMIT) {

	    switch (op) {

	      case JSOP_GETPROP2:

		saveop = JSOP_GETPROP;

		break;

	      case JSOP_GETELEM2:

		saveop = JSOP_GETELEM;

		break;

	      default:;

	    }

	}

	cs = &js_CodeSpec[saveop];

	len = oplen = cs->length;



	if (cs->token) {

	    switch (cs->nuses) {

	      case 2:

		rval = POP_STR();

		lval = POP_STR();

		if ((sn = js_GetSrcNote(jp->script, pc)) != NULL &&

		    SN_TYPE(sn) == SRC_ASSIGNOP) {

		    /* Print only the right operand of the assignment-op. */

		    todo = SprintPut(&ss->sprinter, rval, strlen(rval));

		} else {

		    todo = Sprint(&ss->sprinter, "%s %s %s",

				  lval, cs->token, rval);

		}

		break;



	      case 1:

		rval = POP_STR();

		todo = Sprint(&ss->sprinter, "%s%s", cs->token, rval);

		break;



	      case 0:

#if JS_HAS_GETTER_SETTER

                if (op == JSOP_GETTER || op == JSOP_SETTER) {

                    todo = -2;

                    break;

                }

#endif

		todo = SprintPut(&ss->sprinter, cs->token, strlen(cs->token));

		break;



	      default:

		todo = -2;

		break;

	    }

	} else {

	    switch (op) {

	      case JSOP_NOP:

		/*

                 * Check for a do-while loop, a for-loop with an empty

                 * initializer part, a labeled statement, a function

                 * definition, or try/finally.

		 */

		sn = js_GetSrcNote(jp->script, pc);

		todo = -2;

		switch (sn ? SN_TYPE(sn) : SRC_NULL) {

#if JS_HAS_DO_WHILE_LOOP

		  case SRC_WHILE:

		    js_printf(jp, "\tdo {\n");	/* balance} */

		    jp->indent += 4;

		    break;

#endif /* JS_HAS_DO_WHILE_LOOP */



		  case SRC_FOR:

		    rval = "";



		  do_forloop:

		    /* Skip the JSOP_NOP or JSOP_POP bytecode. */

		    pc++;



		    /* Get the cond, next, and loop-closing tail offsets. */

		    cond = js_GetSrcNoteOffset(sn, 0);

		    next = js_GetSrcNoteOffset(sn, 1);

		    tail = js_GetSrcNoteOffset(sn, 2);

		    LOCAL_ASSERT(tail + GET_JUMP_OFFSET(pc + tail) == 0);



		    /* Print the keyword and the possibly empty init-part. */

		    js_printf(jp, "\tfor (%s;", rval);



		    if (pc[cond] == JSOP_IFEQ) {

			/* Decompile the loop condition. */

			DECOMPILE_CODE(pc, cond);

			js_printf(jp, " %s", POP_STR());

		    }



		    /* Need a semicolon whether or not there was a cond. */

		    js_puts(jp, ";");



		    if (pc[next] != JSOP_GOTO) {

			/* Decompile the loop updater. */

			DECOMPILE_CODE(pc + next, tail - next - 1);

			js_printf(jp, " %s", POP_STR());

		    }



		    /* Do the loop body. */

		    js_puts(jp, ") {\n");

		    jp->indent += 4;

		    oplen = (cond) ? js_CodeSpec[pc[cond]].length : 0;

		    DECOMPILE_CODE(pc + cond + oplen, next - cond - oplen);

		    jp->indent -= 4;

		    js_printf(jp, "\t}\n");



		    /* Set len so pc skips over the entire loop. */

		    len = tail + js_CodeSpec[pc[tail]].length;

		    break;



		  case SRC_LABEL:

		    atom = js_GetAtom(cx, &jp->script->atomMap,

				      (jsatomid) js_GetSrcNoteOffset(sn, 0));

		    jp->indent -= 4;

		    js_printf(jp, "\t%s:\n", ATOM_BYTES(atom));

		    jp->indent += 4;

		    break;



		  case SRC_LABELBRACE:

		    atom = js_GetAtom(cx, &jp->script->atomMap,

				      (jsatomid) js_GetSrcNoteOffset(sn, 0));

		    js_printf(jp, "\t%s: {\n", ATOM_BYTES(atom));

		    jp->indent += 4;

		    break;



		  case SRC_ENDBRACE:

		    jp->indent -= 4;

		    js_printf(jp, "\t}\n");

		    break;



		  case SRC_CATCH:

		    jp->indent -= 4;

		    sn = js_GetSrcNote(jp->script, pc);

		    pc += oplen;

		    js_printf(jp, "\t} catch ("); /* balance) */

		    pc += 6;	/* name Object, pushobj, exception */

		    js_printf(jp, "%s",

			      ATOM_BYTES(GET_ATOM(cx, jp->script, pc)));

		    len = js_GetSrcNoteOffset(sn, 0);

		    pc += 4;	/* initcatchvar, enterwith */

		    if (len) {

			js_printf(jp, " if ");

			DECOMPILE_CODE(pc, len - 3); /* don't decompile ifeq */

			js_printf(jp, "%s", POP_STR());

			pc += len;

		    }

		    js_printf(jp, ") {\n"); /* balance} */

		    jp->indent += 4;

		    len = 0;

		    break;



		  case SRC_FUNCDEF:

		    atom = js_GetAtom(cx, &jp->script->atomMap,

				      (jsatomid) js_GetSrcNoteOffset(sn, 0));

		    JS_ASSERT(ATOM_IS_OBJECT(atom));

                  do_function:

		    obj = ATOM_TO_OBJECT(atom);

		    fun = (JSFunction *) JS_GetPrivate(cx, obj);

		    jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun),

					jp->indent, jp->pretty);

		    if (!jp2)

			return JS_FALSE;

		    jp2->scope = jp->scope;

		    if (js_DecompileFunction(jp2, fun)) {

			str = js_GetPrinterOutput(jp2);

			if (str)

			    js_printf(jp, "%s\n", JS_GetStringBytes(str));

		    }

		    js_DestroyPrinter(jp2);

		    break;



		  default:;

		}

		break;



              case JSOP_GROUP:

                /* Use last real op so PopOff adds parens if needed. */

                todo = PopOff(ss, lastop);



                /* Now add user-supplied parens only if PopOff did not. */

                cs    = &js_CodeSpec[lastop];

                topcs = &js_CodeSpec[ss->opcodes[ss->top]];

                if (topcs->prec >= cs->prec) {

                    todo = Sprint(&ss->sprinter, "(%s)",

                                  OFF2STR(&ss->sprinter, todo));

                }

                break;



	      case JSOP_PUSH:

	      case JSOP_PUSHOBJ:

	      case JSOP_BINDNAME:

		todo = Sprint(&ss->sprinter, "");

		break;



#if JS_HAS_EXCEPTIONS

              case JSOP_TRY:

                js_printf(jp, "\ttry {\n");

                jp->indent += 4;

                break;



              case JSOP_FINALLY:

                jp->indent -= 4;

                js_printf(jp, "\t} finally {\n");

                jp->indent += 4;

                break;



	      case JSOP_GOSUB:

	      case JSOP_RETSUB:

	      case JSOP_SETSP:

		todo = -2;

		break;



	      case JSOP_EXCEPTION:

		sn = js_GetSrcNote(jp->script, pc);

		if (sn && SN_TYPE(sn) == SRC_HIDDEN)

		    todo = -2;

		break;

#endif /* JS_HAS_EXCEPTIONS */



	      case JSOP_POP:

	      case JSOP_POPV:

		sn = js_GetSrcNote(jp->script, pc);

		switch (sn ? SN_TYPE(sn) : SRC_NULL) {

		  case SRC_FOR:

		    rval = POP_STR();

		    todo = -2;

		    goto do_forloop;



		  case SRC_PCDELTA:

		    /* Pop and save to avoid blowing stack depth budget. */

		    lval = JS_strdup(cx, POP_STR());

		    if (!lval)

			return JS_FALSE;



		    /*

                     * The offset tells distance to the end of the right-hand

                     * operand of the comma operator.

                     */

		    done = pc + len;

		    pc += js_GetSrcNoteOffset(sn, 0);

		    len = 0;



		    if (!Decompile(ss, done, pc - done)) {

			JS_free(cx, (char *)lval);

			return JS_FALSE;

		    }



		    /* Pop Decompile result and print comma expression. */

		    rval = POP_STR();

		    todo = Sprint(&ss->sprinter, "%s, %s", lval, rval);

		    JS_free(cx, (char *)lval);

		    break;



		  case SRC_HIDDEN:

		    /* Hide this pop, it's from a goto in a with or for/in. */

		    todo = -2;

		    break;



		  default:

		    rval = POP_STR();

		    if (*rval != '\0')

			js_printf(jp, "\t%s;\n", rval);

		    todo = -2;

		    break;

		}

		break;



	      case JSOP_POP2:

		(void) PopOff(ss, op);

		(void) PopOff(ss, op);

		todo = -2;

		break;



	      case JSOP_ENTERWITH:

		sn = js_GetSrcNote(jp->script, pc);

		todo = -2;

		if (sn && SN_TYPE(sn) == SRC_HIDDEN)

		    break;

		rval = POP_STR();

		js_printf(jp, "\twith (%s) {\n", rval);

		jp->indent += 4;

		break;



	      case JSOP_LEAVEWITH:

		sn = js_GetSrcNote(jp->script, pc);

		todo = -2;

		if (sn && SN_TYPE(sn) == SRC_HIDDEN)

		    break;

		jp->indent -= 4;

		js_printf(jp, "\t}\n");

		break;



	      case JSOP_RETURN:

		rval = POP_STR();

		if (*rval != '\0')

		    js_printf(jp, "\t%s %s;\n", cs->name, rval);

		else

		    js_printf(jp, "\t%s;\n", cs->name);

		todo = -2;

		break;



#if JS_HAS_EXCEPTIONS

	      case JSOP_THROW:

		sn = js_GetSrcNote(jp->script, pc);

		todo = -2;

		if (sn && SN_TYPE(sn) == SRC_HIDDEN)

		    break;

		rval = POP_STR();

		js_printf(jp, "\t%s %s;\n", cs->name, rval);

		break;

#endif /* JS_HAS_EXCEPTIONS */



	      case JSOP_GOTO:

		sn = js_GetSrcNote(jp->script, pc);

		switch (sn ? SN_TYPE(sn) : SRC_NULL) {

		  case SRC_CONT2LABEL:

		    atom = js_GetAtom(cx, &jp->script->atomMap,

				      (jsatomid) js_GetSrcNoteOffset(sn, 0));

		    js_printf(jp, "\tcontinue %s;\n", ATOM_BYTES(atom));

		    break;

		  case SRC_CONTINUE:

		    js_printf(jp, "\tcontinue;\n");

		    break;

		  case SRC_BREAK2LABEL:

		    atom = js_GetAtom(cx, &jp->script->atomMap,

				      (jsatomid) js_GetSrcNoteOffset(sn, 0));

		    js_printf(jp, "\tbreak %s;\n", ATOM_BYTES(atom));

		    break;

		  case SRC_HIDDEN:

		    break;

		  default:

		    js_printf(jp, "\tbreak;\n");

		    break;

		}

		todo = -2;

		break;



	      case JSOP_IFEQ:

		len = GET_JUMP_OFFSET(pc);

		sn = js_GetSrcNote(jp->script, pc);



		switch (sn ? SN_TYPE(sn) : SRC_NULL) {

		  case SRC_IF:

		  case SRC_IF_ELSE:

		    rval = POP_STR();

		    js_printf(jp, "\tif (%s) {\n", rval);

		    jp->indent += 4;

		    if (SN_TYPE(sn) == SRC_IF) {

			DECOMPILE_CODE(pc + oplen, len - oplen);

		    } else {

			DECOMPILE_CODE(pc + oplen,

			    len - (oplen + js_CodeSpec[JSOP_GOTO].length));

			jp->indent -= 4;

			pc += len - oplen;

			LOCAL_ASSERT(*pc == JSOP_GOTO);

			oplen = js_CodeSpec[JSOP_GOTO].length;

			len = GET_JUMP_OFFSET(pc);

			js_printf(jp, "\t} else {\n");

			jp->indent += 4;

			DECOMPILE_CODE(pc + oplen, len - oplen);

		    }

		    jp->indent -= 4;

		    js_printf(jp, "\t}\n");

		    todo = -2;

		    break;



		  case SRC_WHILE:

		    rval = POP_STR();

		    js_printf(jp, "\twhile (%s) {\n", rval);

		    jp->indent += 4;

		    DECOMPILE_CODE(pc + oplen,

			len - (oplen + js_CodeSpec[JSOP_GOTO].length));

		    jp->indent -= 4;

		    js_printf(jp, "\t}\n");

		    todo = -2;

		    break;



		  case SRC_COND:

		    xval = JS_strdup(cx, POP_STR());

		    if (!xval)

			return JS_FALSE;

		    DECOMPILE_CODE(pc + oplen,

			len - (oplen + js_CodeSpec[JSOP_GOTO].length));

		    lval = JS_strdup(cx, POP_STR());

		    if (!lval) {

			JS_free(cx, (void *)xval);

			return JS_FALSE;

		    }

		    pc += len - oplen;

		    LOCAL_ASSERT(*pc == JSOP_GOTO);

		    oplen = js_CodeSpec[JSOP_GOTO].length;

		    len = GET_JUMP_OFFSET(pc);

		    DECOMPILE_CODE(pc + oplen, len - oplen);

		    rval = POP_STR();

		    todo = Sprint(&ss->sprinter, "%s ? %s : %s",

				  xval, lval, rval);

		    JS_free(cx, (void *)xval);

		    JS_free(cx, (void *)lval);

		    break;



		  default:

#if JS_BUG_SHORT_CIRCUIT

		    {

			/* top is the first clause in a disjunction (||). */

			jsbytecode *ifeq;



			lval = JS_strdup(cx, POP_STR());

			if (!lval)

			    return JS_FALSE;

			ifeq = pc + len;

			LOCAL_ASSERT(pc[oplen] == JSOP_TRUE);

			pc += oplen + js_CodeSpec[JSOP_TRUE].length;

			LOCAL_ASSERT(*pc == JSOP_GOTO);

			oplen = js_CodeSpec[JSOP_GOTO].length;

			done = pc + GET_JUMP_OFFSET(pc);

			pc += oplen;

			DECOMPILE_CODE(pc, done - ifeq);

			rval = POP_STR();

			todo = Sprint(&ss->sprinter, "%s || %s", lval, rval);

			JS_free(cx, (void *)lval);

			len = PTRDIFF(done, pc, jsbytecode);

		    }

#endif /* JS_BUG_SHORT_CIRCUIT */

		    break;

		}

		break;



	      case JSOP_IFNE:

#if JS_HAS_DO_WHILE_LOOP

		/* Check for a do-while loop's upward branch. */

		sn = js_GetSrcNote(jp->script, pc);

		if (sn && SN_TYPE(sn) == SRC_WHILE) {

		    jp->indent -= 4;

		    /* {balance: */

		    js_printf(jp, "\t} while (%s);\n", POP_STR());

		    todo = -2;

		    break;

		}

#endif /* JS_HAS_DO_WHILE_LOOP */



#if JS_BUG_SHORT_CIRCUIT

		{

		    jsbytecode *ifne;



		    /* This bytecode is used only for conjunction (&&). */

		    lval = JS_strdup(cx, POP_STR());

		    if (!lval)

			return JS_FALSE;

		    ifne = pc + GET_JUMP_OFFSET(pc);

		    LOCAL_ASSERT(pc[oplen] == JSOP_FALSE);

		    pc += len + js_CodeSpec[JSOP_FALSE].length;

		    LOCAL_ASSERT(*pc == JSOP_GOTO);

		    oplen = js_CodeSpec[JSOP_GOTO].length;

		    done = pc + GET_JUMP_OFFSET(pc);

		    pc += oplen;

		    DECOMPILE_CODE(pc, done - ifne);

		    rval = POP_STR();

		    todo = Sprint(&ss->sprinter, "%s && %s", lval, rval);

		    JS_free(cx, (void *)lval);

		    len = PTRDIFF(done, pc, jsbytecode);

		}

#endif /* JS_BUG_SHORT_CIRCUIT */

		break;



#if !JS_BUG_SHORT_CIRCUIT

	      case JSOP_OR:

		/* Top of stack is the first clause in a disjunction (||). */

		lval = JS_strdup(cx, POP_STR());

		if (!lval)

		    return JS_FALSE;

		done = pc + GET_JUMP_OFFSET(pc);

		pc += len;

		len = PTRDIFF(done, pc, jsbytecode);

		DECOMPILE_CODE(pc, len);

		rval = POP_STR();

		todo = Sprint(&ss->sprinter, "%s || %s", lval, rval);

		JS_free(cx, (char *)lval);

		break;



	      case JSOP_AND:

		/* Top of stack is the first clause in a conjunction (&&). */

		lval = JS_strdup(cx, POP_STR());

		if (!lval)

		    return JS_FALSE;

		done = pc + GET_JUMP_OFFSET(pc);

		pc += len;

		len = PTRDIFF(done, pc, jsbytecode);

		DECOMPILE_CODE(pc, len);

		rval = POP_STR();

		todo = Sprint(&ss->sprinter, "%s && %s", lval, rval);

		JS_free(cx, (char *)lval);

		break;

#endif



	      case JSOP_FORARG:

		atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));

		LOCAL_ASSERT(atom);

		lval = ATOM_BYTES(atom);

		goto do_fornameinloop;



	      case JSOP_FORVAR:

		atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));

		LOCAL_ASSERT(atom);

		lval = ATOM_BYTES(atom);

		goto do_fornameinloop;



	      case JSOP_FORNAME:

                atom = GET_ATOM(cx, jp->script, pc);

		lval = ATOM_BYTES(atom);



              do_fornameinloop:

		sn = js_GetSrcNote(jp->script, pc);

		xval = NULL;

		atom = NULL;

		goto do_forinloop;



	      case JSOP_FORPROP:

		xval = NULL;

		atom = GET_ATOM(cx, jp->script, pc);

		lval = POP_STR();

		sn = NULL;



	      do_forinloop:

		pc += oplen;

		LOCAL_ASSERT(*pc == JSOP_IFEQ);

		oplen = js_CodeSpec[JSOP_IFEQ].length;

		len = GET_JUMP_OFFSET(pc);



              do_forinbody:

		js_printf(jp, "\tfor (%s%s", VarPrefix(sn), lval);

		if (atom)

		    js_printf(jp, ".%s", ATOM_BYTES(atom));

		else if (xval)

		    js_printf(jp, "[%s]", xval);

                rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);

		js_printf(jp, " in %s) {\n", rval);

		jp->indent += 4;

		DECOMPILE_CODE(pc + oplen,

			       len - (oplen + js_CodeSpec[JSOP_GOTO].length));

		jp->indent -= 4;

		js_printf(jp, "\t}\n");

		todo = -2;

		break;



              case JSOP_FORELEM:

		pc++;

		LOCAL_ASSERT(*pc == JSOP_IFEQ);

		len = js_CodeSpec[JSOP_IFEQ].length;



                /*

                 * This gets a little wacky.  Only the length of the for loop

                 * body PLUS the element-indexing expression is known here, so

                 * we pass the after-loop pc to the JSOP_ENUMELEM case, which

                 * is immediately below, to decompile that helper bytecode via

                 * the 'forelem_done' local.

                 */

		forelem_done = pc + GET_JUMP_OFFSET(pc);

                break;



	      case JSOP_ENUMELEM:

		/*

		 * The stack has the object under the (top) index expression.

		 * The "rval" property id is underneath those two on the stack.

		 * The for loop body length can now be adjusted to account for

		 * the length of the indexing expression.

		 */

		atom = NULL;

		xval = POP_STR();

		lval = POP_STR();

		rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);

		len = forelem_done - pc;

		goto do_forinbody;



	      case JSOP_DUP2:

		rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-2]);

		todo = SprintPut(&ss->sprinter, rval, strlen(rval));

		if (todo < 0 || !PushOff(ss, todo, op))

		    return JS_FALSE;

		/* FALL THROUGH */



	      case JSOP_DUP:

		rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);

		todo = SprintPut(&ss->sprinter, rval, strlen(rval));

		break;



	      case JSOP_SETARG:

		atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));

		LOCAL_ASSERT(atom);

		goto do_setname;



	      case JSOP_SETVAR:

		atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));

		LOCAL_ASSERT(atom);

		goto do_setname;



	      case JSOP_SETCONST:

	      case JSOP_SETNAME:

		atom = GET_ATOM(cx, jp->script, pc);

	      do_setname:

		lval = ATOM_BYTES(atom);

		rval = POP_STR();

		if (op == JSOP_SETNAME)

		    (void) PopOff(ss, op);

              do_setlval:

		if ((sn = js_GetSrcNote(jp->script, pc - 1)) != NULL &&

		    SN_TYPE(sn) == SRC_ASSIGNOP) {

		    todo = Sprint(&ss->sprinter, "%s %s= %s",

				  lval, js_CodeSpec[lastop].token, rval);

		} else {

		    sn = js_GetSrcNote(jp->script, pc);

		    todo = Sprint(&ss->sprinter, "%s%s = %s",

				  VarPrefix(sn), lval, rval);

		}

		break;



	      case JSOP_NEW:

	      case JSOP_CALL:

	      case JSOP_EVAL:

#if JS_HAS_LVALUE_RETURN

	      case JSOP_SETCALL:

#endif

		saveop = op;

		op = JSOP_NOP;           /* turn off parens */

		argc = GET_ARGC(pc);

		argv = (char **)

                    JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv);

		if (!argv)

		    return JS_FALSE;



		ok = JS_TRUE;

		for (i = argc; i > 0; i--) {

		    argv[i] = JS_strdup(cx, POP_STR());

		    if (!argv[i]) {

			ok = JS_FALSE;

			break;

		    }

		}



		/* Skip the JSOP_PUSHOBJ-created empty string. */

		LOCAL_ASSERT(ss->top >= 2);

		(void) PopOff(ss, op);



		/* Get the callee's decompiled image in argv[0]. */

		argv[0] = JS_strdup(cx, POP_STR());

		if (!argv[i])

		    ok = JS_FALSE;



		lval = "(", rval = ")";

		if (saveop == JSOP_NEW) {

		    todo = Sprint(&ss->sprinter, "%s %s%s",

				  js_new_str, argv[0], lval);

		} else {

		    todo = Sprint(&ss->sprinter, "%s%s",

				  argv[0], lval);

		}

		if (todo < 0)

		    ok = JS_FALSE;



		for (i = 1; i <= argc; i++) {

		    if (!argv[i] ||

			Sprint(&ss->sprinter, "%s%s",

			       argv[i], (i < argc) ? ", " : "") < 0) {

			ok = JS_FALSE;

			break;

		    }

		}

		if (Sprint(&ss->sprinter, rval) < 0)

		    ok = JS_FALSE;



		for (i = 0; i <= argc; i++) {

		    if (argv[i])

			JS_free(cx, argv[i]);

		}

		JS_free(cx, argv);

		if (!ok)

		    return JS_FALSE;

		op = saveop;

#if JS_HAS_LVALUE_RETURN

                if (op == JSOP_SETCALL) {

                    if (!PushOff(ss, todo, op))

                        return JS_FALSE;

                    todo = Sprint(&ss->sprinter, "");

                }

#endif

		break;



	      case JSOP_DELNAME:

		atom = GET_ATOM(cx, jp->script, pc);

		lval = ATOM_BYTES(atom);

		todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval);

		break;



	      case JSOP_DELPROP:

		atom = GET_ATOM(cx, jp->script, pc);

		lval = POP_STR();

		todo = Sprint(&ss->sprinter, "%s %s.%s",

			      js_delete_str, lval, ATOM_BYTES(atom));

		break;



	      case JSOP_DELELEM:

		xval = POP_STR();

		lval = POP_STR();

		todo = Sprint(&ss->sprinter, "%s %s[%s]",

			      js_delete_str, lval, xval);

		break;



	      case JSOP_TYPEOF:

	      case JSOP_VOID:

		rval = POP_STR();

		todo = Sprint(&ss->sprinter, "%s %s", cs->name, rval);

		break;



	      case JSOP_INCARG:

	      case JSOP_DECARG:

		atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));

		LOCAL_ASSERT(atom);

		goto do_incatom;



	      case JSOP_INCVAR:

	      case JSOP_DECVAR:

		atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));

		LOCAL_ASSERT(atom);

		goto do_incatom;



	      case JSOP_INCNAME:

	      case JSOP_DECNAME:

		atom = GET_ATOM(cx, jp->script, pc);

	      do_incatom:

		lval = ATOM_BYTES(atom);

		todo = Sprint(&ss->sprinter, "%s%s",

			      js_incop_str[!(cs->format & JOF_INC)], lval);

		break;



	      case JSOP_INCPROP:

	      case JSOP_DECPROP:

		atom = GET_ATOM(cx, jp->script, pc);

		lval = POP_STR();

		todo = Sprint(&ss->sprinter, "%s%s.%s",

			      js_incop_str[!(cs->format & JOF_INC)],

			      lval, ATOM_BYTES(atom));

		break;



	      case JSOP_INCELEM:

	      case JSOP_DECELEM:

		xval = POP_STR();

		lval = POP_STR();

		todo = Sprint(&ss->sprinter, "%s%s[%s]",

			      js_incop_str[!(cs->format & JOF_INC)],

			      lval, xval);

		break;



	      case JSOP_ARGINC:

	      case JSOP_ARGDEC:

		atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));

		LOCAL_ASSERT(atom);

		goto do_atominc;



	      case JSOP_VARINC:

	      case JSOP_VARDEC:

		atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));

		LOCAL_ASSERT(atom);

		goto do_atominc;



	      case JSOP_NAMEINC:

	      case JSOP_NAMEDEC:

		atom = GET_ATOM(cx, jp->script, pc);

	      do_atominc:

		lval = ATOM_BYTES(atom);

		todo = Sprint(&ss->sprinter, "%s%s",

			      lval, js_incop_str[!(cs->format & JOF_INC)]);

		break;



	      case JSOP_PROPINC:

	      case JSOP_PROPDEC:

		atom = GET_ATOM(cx, jp->script, pc);

		lval = POP_STR();

		todo = Sprint(&ss->sprinter, "%s.%s%s",

			      lval, ATOM_BYTES(atom),

			      js_incop_str[!(cs->format & JOF_INC)]);

		break;



	      case JSOP_ELEMINC:

	      case JSOP_ELEMDEC:

		xval = POP_STR();

		lval = POP_STR();

		todo = Sprint(&ss->sprinter, "%s[%s]%s",

			      lval, xval,

			      js_incop_str[!(cs->format & JOF_INC)]);

		break;



	      case JSOP_GETPROP2:

		op = JSOP_GETPROP;

		(void) PopOff(ss, lastop);

		/* FALL THROUGH */



	      case JSOP_GETPROP:

		atom = GET_ATOM(cx, jp->script, pc);

		lval = POP_STR();

                str = ATOM_TO_STRING(atom);

                if (IsASCIIIdentifier(str)) {

                    todo = Sprint(&ss->sprinter, "%s.%s",

                                  lval, JS_GetStringBytes(str));

                } else {

                    lval = JS_strdup(cx, lval);

                    if (!lval)

                        return JS_FALSE;

                    xval = QuoteString(&ss->sprinter, str, (jschar)'\'');

                    todo = Sprint(&ss->sprinter, "%s[%s]", lval, xval);

                    JS_free(cx, (char *)lval);

                }

		break;



	      case JSOP_SETPROP:

		rval = POP_STR();

		atom = GET_ATOM(cx, jp->script, pc);

		lval = POP_STR();

                sn = js_GetSrcNote(jp->script, pc - 1);

                str = ATOM_TO_STRING(atom);

                if (IsASCIIIdentifier(str)) {

                    xval = JS_GetStringBytes(str);

                    if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {

                        todo = Sprint(&ss->sprinter, "%s.%s %s= %s",

                                      lval, xval,

                                      js_CodeSpec[lastop].token, rval);

                    } else {

                        todo = Sprint(&ss->sprinter, "%s.%s = %s",

                                      lval, xval, rval);

                    }

                } else {

                    rval = JS_strdup(cx, rval);

                    lval = JS_strdup(cx, lval);

                    if (!rval || !lval) {

                        JS_free(cx, (char *)rval);

                        return JS_FALSE;

                    }

                    xval = QuoteString(&ss->sprinter, str, (jschar)'\'');

                    if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {

                        todo = Sprint(&ss->sprinter, "%s[%s] %s= %s",

                                      lval, xval,

                                      js_CodeSpec[lastop].token, rval);

                    } else {

                        todo = Sprint(&ss->sprinter, "%s[%s] = %s",

                                      lval, xval, rval);

                    }

                    JS_free(cx, (char *)rval);

                    JS_free(cx, (char *)lval);

                }

		break;



	      case JSOP_GETELEM2:

		op = JSOP_GETELEM;

		(void) PopOff(ss, lastop);

		/* FALL THROUGH */



	      case JSOP_GETELEM:

		op = JSOP_NOP;           /* turn off parens */

		xval = POP_STR();

		op = JSOP_GETELEM;

		lval = POP_STR();

                if (*xval == '\0')

                    todo = Sprint(&ss->sprinter, "%s", lval);

                else

                    todo = Sprint(&ss->sprinter, "%s[%s]", lval, xval);

		break;



	      case JSOP_SETELEM:

		op = JSOP_NOP;           /* turn off parens */

		rval = POP_STR();

		xval = POP_STR();

		op = JSOP_SETELEM;

		lval = POP_STR();

                if (*xval == '\0')

                    goto do_setlval;

                if ((sn = js_GetSrcNote(jp->script, pc - 1)) != NULL &&

                    SN_TYPE(sn) == SRC_ASSIGNOP) {

                    todo = Sprint(&ss->sprinter, "%s[%s] %s= %s",

                                  lval, xval,

                                  js_CodeSpec[lastop].token, rval);

                } else {

                    todo = Sprint(&ss->sprinter, "%s[%s] = %s",

                                  lval, xval, rval);

                }

		break;



              case JSOP_ARGSUB:

                i = (jsint) GET_ATOM_INDEX(pc);

                todo = Sprint(&ss->sprinter, "%s[%d]",

                              js_arguments_str, (int) i);

                break;



              case JSOP_ARGCNT:

                todo = Sprint(&ss->sprinter, "%s.%s",

                              js_arguments_str, js_length_str);

                break;



	      case JSOP_GETARG:

		atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));

		LOCAL_ASSERT(atom);

		goto do_name;



	      case JSOP_GETVAR:

		atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));

		LOCAL_ASSERT(atom);

		goto do_name;



	      case JSOP_NAME:

		atom = GET_ATOM(cx, jp->script, pc);

	      do_name:

		sn = js_GetSrcNote(jp->script, pc);

		todo = Sprint(&ss->sprinter, "%s%s",

			      VarPrefix(sn), ATOM_BYTES(atom));

		break;



	      case JSOP_UINT16:

		i = (jsint) GET_ATOM_INDEX(pc);

		todo = Sprint(&ss->sprinter, "%u", (unsigned) i);

		break;



              case JSOP_NUMBER:

                atom = GET_ATOM(cx, jp->script, pc);

                key = ATOM_KEY(atom);

                if (JSVAL_IS_INT(key)) {

                    long ival = (long)JSVAL_TO_INT(key);

                    todo = Sprint(&ss->sprinter, "%ld", ival);

                } else {

                    char buf[DTOSTR_STANDARD_BUFFER_SIZE];

                    char *numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD,

                                             0, *JSVAL_TO_DOUBLE(key));

                    if (!numStr) {

                        JS_ReportOutOfMemory(cx);

                        return JS_FALSE;

                    }

                    todo = Sprint(&ss->sprinter, numStr);

                }

                break;



              case JSOP_STRING:

                atom = GET_ATOM(cx, jp->script, pc);

                rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),

                                   (jschar)'"');

                if (!rval)

                    return JS_FALSE;

                todo = Sprint(&ss->sprinter, "%s", rval);

                break;



              case JSOP_OBJECT:

              case JSOP_ANONFUNOBJ:

              case JSOP_NAMEDFUNOBJ:

                atom = GET_ATOM(cx, jp->script, pc);

                str = js_ValueToSource(cx, ATOM_KEY(atom));

                if (!str)

                    return JS_FALSE;

                todo = SprintPut(&ss->sprinter, JS_GetStringBytes(str),

                                 str->length);

                break;



#if JS_HAS_SWITCH_STATEMENT

	      case JSOP_TABLESWITCH:

	      {

		jsbytecode *pc2, *end;

		ptrdiff_t off, off2;

		jsint j, n, low, high;

		TableEntry *table;



		sn = js_GetSrcNote(jp->script, pc);

		JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);

		len = js_GetSrcNoteOffset(sn, 0);

		pc2 = pc;

		off = GET_JUMP_OFFSET(pc2);

		end = pc + off;

		pc2 += JUMP_OFFSET_LEN;

		low = GET_JUMP_OFFSET(pc2);

		pc2 += JUMP_OFFSET_LEN;

		high = GET_JUMP_OFFSET(pc2);



		n = high - low + 1;

                if (n == 0) {

                    table = NULL;

		    j = 0;

                } else {

                    table = (TableEntry *)

                            JS_malloc(cx, (size_t)n * sizeof *table);

                    if (!table)

                        return JS_FALSE;

		    for (i = j = 0; i < n; i++) {

			pc2 += JUMP_OFFSET_LEN;

			off2 = GET_JUMP_OFFSET(pc2);

			if (off2) {

			    table[j].key = INT_TO_JSVAL(low + i);

			    table[j++].offset = off2;

			}

		    }

                    js_qsort(table, (size_t)j, sizeof *table, CompareOffsets,

                             NULL);

		}



		ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off,

				     JS_FALSE);

		JS_free(cx, table);

		if (!ok)

		    return ok;

		todo = -2;

		break;

	      }



	      case JSOP_LOOKUPSWITCH:

	      {

		jsbytecode *pc2;

		ptrdiff_t off, off2;

		jsint npairs;

		TableEntry *table;



		sn = js_GetSrcNote(jp->script, pc);

		JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);

		len = js_GetSrcNoteOffset(sn, 0);

		pc2 = pc;

		off = GET_JUMP_OFFSET(pc2);

		pc2 += JUMP_OFFSET_LEN;

		npairs = (jsint) GET_ATOM_INDEX(pc2);

		pc2 += ATOM_INDEX_LEN;



		table = (TableEntry *)

                    JS_malloc(cx, (size_t)npairs * sizeof *table);

		if (!table)

		    return JS_FALSE;

		for (i = 0; i < npairs; i++) {

		    atom = GET_ATOM(cx, jp->script, pc2);

		    pc2 += ATOM_INDEX_LEN;

		    off2 = GET_JUMP_OFFSET(pc2);

		    pc2 += JUMP_OFFSET_LEN;

		    table[i].key = ATOM_KEY(atom);

		    table[i].offset = off2;

		}



		ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off,

				     JS_FALSE);

		JS_free(cx, table);

		if (!ok)

		    return ok;

		todo = -2;

		break;

	      }



	      case JSOP_CONDSWITCH:

	      {

		jsbytecode *pc2;

		ptrdiff_t off, off2, caseOff;

		jsint ncases;

		TableEntry *table;



		sn = js_GetSrcNote(jp->script, pc);

		JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);

		len = js_GetSrcNoteOffset(sn, 0);

		off = js_GetSrcNoteOffset(sn, 1);



		/*

		 * Count the cases using offsets from switch to first case,

		 * and case to case, stored in srcnote immediates.

		 */

		pc2 = pc;

		off2 = off;

		for (ncases = 0; off2 != 0; ncases++) {

		    pc2 += off2;

		    JS_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT);

		    if (*pc2 == JSOP_DEFAULT) {

			/* End of cases, but count default as a case. */

			off2 = 0;

		    } else {

			sn = js_GetSrcNote(jp->script, pc2);

			JS_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);

			off2 = js_GetSrcNoteOffset(sn, 0);

		    }

		}



		/*

		 * Allocate table and rescan the cases using their srcnotes,

		 * stashing each case's delta from switch top in table[i].key,

		 * and the distance to its statements in table[i].offset.

		 */

		table = (TableEntry *)

                    JS_malloc(cx, (size_t)ncases * sizeof *table);

		if (!table)

		    return JS_FALSE;

		pc2 = pc;

		off2 = off;

		for (i = 0; i < ncases; i++) {

		    pc2 += off2;

		    JS_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT);

		    caseOff = pc2 - pc;

		    table[i].key = INT_TO_JSVAL((jsint) caseOff);

		    table[i].offset = caseOff + GET_JUMP_OFFSET(pc2);

		    if (*pc2 == JSOP_CASE) {

			sn = js_GetSrcNote(jp->script, pc2);

			JS_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);

			off2 = js_GetSrcNoteOffset(sn, 0);

		    }

		}



		/*

		 * Find offset of default code by fetching the default offset

		 * from the end of table.  JSOP_CONDSWITCH always has a default

		 * case at the end.

		 */

		off = JSVAL_TO_INT(table[ncases-1].key);

		pc2 = pc + off;

		off += GET_JUMP_OFFSET(pc2);



		ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off,

				     JS_TRUE);

		JS_free(cx, table);

		if (!ok)

		    return ok;

		todo = -2;

		break;

	      }



	      case JSOP_CASE:

	      {

		lval = POP_STR();

		if (!lval)

		    return JS_FALSE;

		js_printf(jp, "\tcase %s:\n", lval);

		todo = -2;

		break;

	      }



#endif /* JS_HAS_SWITCH_STATEMENT */



#if !JS_BUG_FALLIBLE_EQOPS

	      case JSOP_NEW_EQ:

	      case JSOP_NEW_NE:

		rval = POP_STR();

		lval = POP_STR();

		todo = Sprint(&ss->sprinter, "%s %c%s %s",

			      lval,

			      (op == JSOP_NEW_EQ) ? '=' : '!',

#if JS_HAS_TRIPLE_EQOPS

			      JSVERSION_IS_ECMA(cx->version) ? "==" :

#endif

			      "=",

			      rval);

		break;

#endif /* !JS_BUG_FALLIBLE_EQOPS */



#if JS_HAS_LEXICAL_CLOSURE

	      case JSOP_CLOSURE:

		atom = GET_ATOM(cx, jp->script, pc);

		JS_ASSERT(ATOM_IS_OBJECT(atom));

                goto do_function;

#endif /* JS_HAS_LEXICAL_CLOSURE */



#if JS_HAS_EXPORT_IMPORT

	      case JSOP_EXPORTALL:

		js_printf(jp, "\texport *\n");

		todo = -2;

		break;



	      case JSOP_EXPORTNAME:

		atom = GET_ATOM(cx, jp->script, pc);

		js_printf(jp, "\texport %s\n", ATOM_BYTES(atom));

		todo = -2;

		break;



	      case JSOP_IMPORTALL:

		lval = POP_STR();

		js_printf(jp, "\timport %s.*\n", lval);

		todo = -2;

		break;



	      case JSOP_IMPORTPROP:

		atom = GET_ATOM(cx, jp->script, pc);

		lval = POP_STR();

		js_printf(jp, "\timport %s.%s\n", lval, ATOM_BYTES(atom));

		todo = -2;

		break;



	      case JSOP_IMPORTELEM:

		xval = POP_STR();

		op = JSOP_GETELEM;

		lval = POP_STR();

		js_printf(jp, "\timport %s[%s]\n", lval, xval);

		todo = -2;

		break;

#endif /* JS_HAS_EXPORT_IMPORT */



	      case JSOP_TRAP:

		op = JS_GetTrapOpcode(cx, jp->script, pc);

		if (op == JSOP_LIMIT)

		    return JS_FALSE;

		*pc = op;

		cs = &js_CodeSpec[op];

		len = cs->length;

		DECOMPILE_CODE(pc, len);

		*pc = JSOP_TRAP;

		todo = -2;

		break;



#if JS_HAS_INITIALIZERS

	      case JSOP_NEWINIT:

		LOCAL_ASSERT(ss->top >= 2);

		(void) PopOff(ss, op);

		lval = POP_STR();

#if JS_HAS_SHARP_VARS

		op = (JSOp)pc[len];

		if (op == JSOP_DEFSHARP) {

		    pc += len;

		    cs = &js_CodeSpec[op];

		    len = cs->length;

		    i = (jsint) GET_ATOM_INDEX(pc);

		    todo = Sprint(&ss->sprinter, "#%u=%c",

				  (unsigned) i,

				  (*lval == 'O') ? '{' : '[');

		    /* balance}] */

		} else

#endif /* JS_HAS_SHARP_VARS */

		{

		    todo = Sprint(&ss->sprinter, (*lval == 'O') ? "{" : "[");

		    /* balance}] */

		}

		break;



	      case JSOP_ENDINIT:

		rval = POP_STR();

		sn = js_GetSrcNote(jp->script, pc);

		todo = Sprint(&ss->sprinter, "%s%s%c",

			      rval,

			      (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",

			      /* [balance */

			      (*rval == '{') ? '}' : ']');

		break;



	      case JSOP_INITPROP:

              case JSOP_INITCATCHVAR:

		rval = POP_STR();

		atom = GET_ATOM(cx, jp->script, pc);

		xval = ATOM_BYTES(atom);

		lval = POP_STR();

	      do_initprop:

#ifdef OLD_GETTER_SETTER

                todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s",

                              lval,

                              (lval[1] != '\0') ? ", " : "",

                              xval,

                              (lastop == JSOP_GETTER || lastop == JSOP_SETTER)

                              ? " " : "",

                              (lastop == JSOP_GETTER) ? js_getter_str :

                              (lastop == JSOP_SETTER) ? js_setter_str :

                              "",

                              rval);

#else

                if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) {

                    todo = Sprint(&ss->sprinter, "%s%s%s %s%s",

                              lval,

                              (lval[1] != '\0') ? ", " : "",

                              (lastop == JSOP_GETTER)

                              ? js_get_str : js_set_str,

                              xval,

                              rval + strlen(js_function_str) + 1);

                } else {

                    todo = Sprint(&ss->sprinter, "%s%s%s:%s",

                              lval,

                              (lval[1] != '\0') ? ", " : "",

                              xval,

                              rval);

                }

#endif

		break;



	      case JSOP_INITELEM:

		rval = POP_STR();

		xval = POP_STR();

		lval = POP_STR();

		sn = js_GetSrcNote(jp->script, pc);

		if (sn && SN_TYPE(sn) == SRC_LABEL)

		    goto do_initprop;

		todo = Sprint(&ss->sprinter, "%s%s%s",

			      lval,

			      (lval[1] != '\0' || *xval != '0') ? ", " : "",

			      rval);

		break;



#if JS_HAS_SHARP_VARS

	      case JSOP_DEFSHARP:

		i = (jsint) GET_ATOM_INDEX(pc);

		rval = POP_STR();

		todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval);

		break;



	      case JSOP_USESHARP:

		i = (jsint) GET_ATOM_INDEX(pc);

		todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i);

		break;

#endif /* JS_HAS_SHARP_VARS */

#endif /* JS_HAS_INITIALIZERS */



#if JS_HAS_DEBUGGER_KEYWORD

	      case JSOP_DEBUGGER:

		js_printf(jp, "\tdebugger;\n");

		todo = -2;

		break;

#endif /* JS_HAS_DEBUGGER_KEYWORD */



	      default:

		todo = -2;

		break;

	    }

	}



	if (todo < 0) {

	    /* -2 means "don't push", -1 means reported error. */

	    if (todo == -1)

		return JS_FALSE;

	} else {

	    if (!PushOff(ss, todo, op))

		return JS_FALSE;

	}

	pc += len;

    }



/*

 * Undefine local macros.

 */

#undef DECOMPILE_CODE

#undef POP_STR

#undef LOCAL_ASSERT



    return JS_TRUE;

}





JSBool

js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len)

{

    SprintStack ss;

    JSContext *cx;

    void *mark, *space;

    size_t offsetsz, opcodesz;

    JSBool ok;

    JSScript *oldscript;

    char *last;



    /* Initialize a sprinter for use with the offset stack. */

    ss.printer = jp;

    cx = jp->sprinter.context;

    mark = JS_ARENA_MARK(&cx->tempPool);

    INIT_SPRINTER(cx, &ss.sprinter, &cx->tempPool, PAREN_SLOP);



    /* Allocate the parallel (to avoid padding) offset and opcode stacks. */

    offsetsz = script->depth * sizeof(ptrdiff_t);

    opcodesz = script->depth * sizeof(jsbytecode);

    JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz);

    if (!space) {

	ok = JS_FALSE;

        goto out;

    }

    ss.offsets = (ptrdiff_t *) space;

    ss.opcodes = (jsbytecode *) ((char *)space + offsetsz);

    ss.top = 0;



    /* Call recursive subroutine to do the hard work. */

    oldscript = jp->script;

    jp->script = script;

    ok = Decompile(&ss, pc, len);

    jp->script = oldscript;



    /* If the given code didn't empty the stack, do it now. */

    if (ss.top) {

	do {

	    last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_NOP));

	} while (ss.top);

	js_printf(jp, "%s", last);

    }



out:

    /* Free all temporary stuff allocated under this call. */

    JS_ARENA_RELEASE(&cx->tempPool, mark);

    return ok;

}



JSBool

js_DecompileScript(JSPrinter *jp, JSScript *script)

{

    return js_DecompileCode(jp, script, script->code, (uintN)script->length);

}



static const char native_code_str[] = "\t[native code]\n";



JSBool

js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun)

{

    JSScript *script;

    JSScope *scope, *save;

    JSBool ok;



    script = fun->script;

    if (!script) {

	js_printf(jp, native_code_str);

        return JS_TRUE;

    }

    scope = fun->object ? OBJ_SCOPE(fun->object) : NULL;

    save = jp->scope;

    jp->scope = scope;

    ok = js_DecompileCode(jp, script, script->code, (uintN)script->length);

    jp->scope = save;

    return ok;

}



JSBool

js_DecompileFunction(JSPrinter *jp, JSFunction *fun)

{

    JSScope *scope, *oldscope;

    JSScopeProperty *sprop, *snext;

    JSBool ok;

    JSAtom *atom;

    uintN indent;

    intN i;



    if (jp->pretty) {

        js_puts(jp, "\n");

	js_printf(jp, "\t");

    }

    if (fun->flags & JSFUN_GETTER)

        js_printf(jp, "%s ", js_getter_str);

    else if (fun->flags & JSFUN_SETTER)

        js_printf(jp, "%s ", js_setter_str);

    js_printf(jp, "%s %s(",

              js_function_str, fun->atom ? ATOM_BYTES(fun->atom) : "");



    scope = NULL;

    if (fun->script && fun->object) {

	/*

	 * Print the parameters.

	 *

	 * This code is complicated by the need to handle duplicate parameter

	 * names.  A duplicate parameter is stored as a property with id equal

	 * to the parameter number, but will not be in order in the linked list

	 * of symbols. So for each parameter we search the list of symbols for

	 * the appropriately numbered parameter, which we can then print.

	 */

	for (i = 0; ; i++) {

	    jsid id;

	    atom = NULL;

	    scope = OBJ_SCOPE(fun->object);

	    for (sprop = scope->props; sprop; sprop = snext) {

		snext = sprop->next;

		if (SPROP_GETTER_SCOPE(sprop, scope) != js_GetArgument)

		    continue;

		if (JSVAL_IS_INT(sprop->id) && JSVAL_TO_INT(sprop->id) == i) {

		    atom = sym_atom(sprop->symbols);

		    break;

		}

		id = (jsid) sym_atom(sprop->symbols);

		if (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) == i) {

		    atom = (JSAtom *) sprop->id;

		    break;

		}

	    }

	    if (atom == NULL)

		break;

	    js_printf(jp, (i > 0 ? ", %s" : "%s"), ATOM_BYTES(atom));

	}

    }

    js_printf(jp, ") {\n");

    indent = jp->indent;

    jp->indent += 4;

    if (fun->script && fun->object) {

	oldscope = jp->scope;

	jp->scope = scope;

	ok = js_DecompileScript(jp, fun->script);

	jp->scope = oldscope;

	if (!ok) {

	    jp->indent = indent;

	    return JS_FALSE;

	}

    } else {

	js_printf(jp, native_code_str);

    }

    jp->indent -= 4;

    js_printf(jp, "\t}");

    if (jp->pretty)

	js_puts(jp, "\n");

    return JS_TRUE;

}



JSString *

js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,

			   JSString *fallback)

{

    JSStackFrame *fp, *down;

    jsbytecode *pc, *begin, *end, *tmp;

    jsval *sp, *base, *limit;

    JSScript *script;

    JSOp op;

    JSCodeSpec *cs;

    uint32 format, mode;

    intN depth;

    jssrcnote *sn;

    uintN len, off;

    JSPrinter *jp;

    JSString *name;



    fp = cx->fp;

    if (!fp)

        goto do_fallback;



    /* Try to find sp's generating pc depth slots under it on the stack. */

    pc = fp->pc;

    if (spindex == JSDVG_SEARCH_STACK) {

        if (!pc) {

            /*

             * Current frame is native: look under it for a scripted call

             * in which a decompilable bytecode string that generated the

             * value as an actual argument might exist.

             */

            JS_ASSERT(!fp->script && fp->fun && fp->fun->native);

            down = fp->down;

            if (!down)

                goto do_fallback;

            script = down->script;

            base = fp->argv;

            limit = base + fp->argc;

        } else {

            /*

             * This should be a script activation, either a top-level

             * script or a scripted function.  But be paranoid about calls

             * to js_DecompileValueGenerator from code that hasn't fully

             * initialized a (default-all-zeroes) frame.

             */

            script = fp->script;

            base = fp->spbase;

            limit = fp->sp;

        }



        /*

         * Pure paranoia about default-zeroed frames being active while

         * js_DecompileValueGenerator is called.  It can't hurt much now;

         * error reporting performance is not an issue.

         */

        if (!script || !base || !limit)

            goto do_fallback;



        /*

         * Try to find operand-generating pc depth slots below sp.

         *

         * In the native case, we know the arguments have generating pc's

         * under them, on account of fp->down->script being non-null: all

         * compiled scripts get depth slots for generating pc's allocated

         * upon activation, at the top of js_Interpret.

         *

         * In the script or scripted function case, the same reasoning

         * applies to fp rather than to fp->down.

         */

        for (sp = base; sp < limit; sp++) {

            if (*sp == v) {

                depth = (intN)script->depth;

                pc = (jsbytecode *) sp[-depth];

                break;

            }

        }

    } else {

        /*

         * At this point, pc may or may not be null, i.e., we could be in

         * a script activation, or we could be in a native frame that was

         * called by another native function.  Check pc and script.

         */

        if (!pc)

            goto do_fallback;

        script = fp->script;

        if (!script)

            goto do_fallback;



        if (spindex != JSDVG_IGNORE_STACK) {

            depth = (intN)script->depth;

            JS_ASSERT(-depth <= spindex && spindex < 0);

            spindex -= depth;



            base = (jsval *) cx->stackPool.current->base;

            limit = (jsval *) cx->stackPool.current->avail;

            sp = fp->sp + spindex;

            if (JS_UPTRDIFF(sp, base) < JS_UPTRDIFF(limit, base))

                pc = (jsbytecode *) *sp;

        }

    }



    /*

     * Again, be paranoid, this time about possibly loading an invalid pc

     * from sp[-(1+depth)].

     */

    if (JS_UPTRDIFF(pc, script->code) >= (jsuword)script->length) {

        pc = fp->pc;

        if (!pc)

            goto do_fallback;

    }

    op = (JSOp) *pc;

    if (op == JSOP_TRAP)

        op = JS_GetTrapOpcode(cx, script, pc);

    cs = &js_CodeSpec[op];

    format = cs->format;

    mode = (format & JOF_MODEMASK);



    /* NAME ops are self-contained, but others require left context. */

    if (mode == JOF_NAME) {

        begin = pc;

    } else {

        sn = js_GetSrcNote(script, pc);

        if (!sn || SN_TYPE(sn) != SRC_PCBASE)

            goto do_fallback;

        begin = pc - js_GetSrcNoteOffset(sn, 0);

    }

    end = pc + cs->length;

    len = PTRDIFF(end, begin, jsbytecode);



    if (format & (JOF_SET | JOF_DEL | JOF_INCDEC | JOF_IMPORT)) {

        tmp = (jsbytecode *) JS_malloc(cx, len * sizeof(jsbytecode));

        if (!tmp)

            return NULL;

        memcpy(tmp, begin, len * sizeof(jsbytecode));

        if (mode == JOF_NAME) {

            tmp[0] = JSOP_NAME;

        } else {

            /*

             * We must replace the faulting pc's bytecode with a corresponding

             * JSOP_GET* code.  For JSOP_SET{PROP,ELEM}, we must use the "2nd"

             * form of JSOP_GET{PROP,ELEM}, to throw away the assignment op's

             * right-hand operand and decompile it as if it were a GET of its

             * left-hand operand.

             */

            off = len - cs->length;

            JS_ASSERT(off == (uintN) PTRDIFF(pc, begin, jsbytecode));

            if (mode == JOF_PROP) {

                tmp[off] = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP;

            } else if (mode == JOF_ELEM) {

                tmp[off] = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM;

            } else {

#if JS_HAS_LVALUE_RETURN

                JS_ASSERT(op == JSOP_SETCALL);

                tmp[off] = JSOP_CALL;

#else

                JS_ASSERT(0);

#endif

            }

        }

        begin = tmp;

    } else {

        /* No need to revise script bytecode. */

        tmp = NULL;

    }



    jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE);

    if (jp && js_DecompileCode(jp, script, begin, len))

        name = js_GetPrinterOutput(jp);

    else

        name = NULL;

    js_DestroyPrinter(jp);

    if (tmp)

        JS_free(cx, tmp);

    return name;



  do_fallback:

    return fallback ? fallback : js_ValueToString(cx, v);

}

 

**** End of jsopcode.c ****

 

**** Start of jsopcode.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsopcode_h___

#define jsopcode_h___

/*

 * JS bytecode definitions.

 */

#include <stddef.h>

#include "jsprvtd.h"

#include "jspubtd.h"



JS_BEGIN_EXTERN_C



/*

 * JS operation bytecodes.

 */

typedef enum JSOp {

#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \

    op = val,

#include "jsopcode.tbl"

#undef OPDEF

    JSOP_LIMIT

} JSOp;



/*

 * JS bytecode formats.

 */

#define JOF_BYTE          0       /* single bytecode, no immediates */

#define JOF_JUMP          1       /* signed 16-bit jump offset immediate */

#define JOF_CONST         2       /* unsigned 16-bit constant pool index */

#define JOF_UINT16        3       /* unsigned 16-bit immediate operand */

#define JOF_TABLESWITCH   4       /* table switch */

#define JOF_LOOKUPSWITCH  5       /* lookup switch */

#define JOF_QARG          6       /* quickened get/set function argument ops */

#define JOF_QVAR          7       /* quickened get/set local variable ops */

#define JOF_DEFLOCALVAR   8       /* define local var with initial value */

#define JOF_TYPEMASK      0x000f  /* mask for above immediate types */

#define JOF_NAME          0x0010  /* name operation */

#define JOF_PROP          0x0020  /* obj.prop operation */

#define JOF_ELEM          0x0030  /* obj[index] operation */

#define JOF_MODEMASK      0x0030  /* mask for above addressing modes */

#define JOF_SET           0x0040  /* set (i.e., assignment) operation */

#define JOF_DEL           0x0080  /* delete operation */

#define JOF_DEC           0x0100  /* decrement (--, not ++) opcode */

#define JOF_INC           0x0200  /* increment (++, not --) opcode */

#define JOF_INCDEC        0x0300  /* increment or decrement opcode */

#define JOF_POST          0x0400  /* postorder increment or decrement */

#define JOF_IMPORT        0x0800  /* import property op */



/*

 * Immediate operand getters, setters, and bounds.

 */

#define JUMP_OFFSET_LEN         2

#define JUMP_OFFSET_HI(off)     ((jsbytecode)((off) >> 8))

#define JUMP_OFFSET_LO(off)     ((jsbytecode)(off))

#define GET_JUMP_OFFSET(pc)     ((int16)(((pc)[1] << 8) | (pc)[2]))

#define SET_JUMP_OFFSET(pc,off) ((pc)[1] = JUMP_OFFSET_HI(off),               \

				 (pc)[2] = JUMP_OFFSET_LO(off))

#define JUMP_OFFSET_MIN         ((int16)0x8000)

#define JUMP_OFFSET_MAX         ((int16)0x7fff)



#define ATOM_INDEX_LEN          2

#define ATOM_INDEX_HI(index)    ((jsbytecode)((index) >> 8))

#define ATOM_INDEX_LO(index)    ((jsbytecode)(index))

#define GET_ATOM_INDEX(pc)      ((jsatomid)(((pc)[1] << 8) | (pc)[2]))

#define SET_ATOM_INDEX(pc,index)((pc)[1] = ATOM_INDEX_HI(index),              \

				 (pc)[2] = ATOM_INDEX_LO(index))

#define GET_ATOM(cx,script,pc)  js_GetAtom((cx), &(script)->atomMap,          \

					   GET_ATOM_INDEX(pc))

#define ATOM_INDEX_LIMIT_LOG2   16

#define ATOM_INDEX_LIMIT        ((uint32)1 << ATOM_INDEX_LIMIT_LOG2)



#define ARGC_HI(argc)           ((jsbytecode)((argc) >> 8))

#define ARGC_LO(argc)           ((jsbytecode)(argc))

#define GET_ARGC(pc)            ((uintN)(((pc)[1] << 8) | (pc)[2]))

#define ARGC_LIMIT              ((uint32)1 << 16)



/* Synonyms for quick JOF_QARG and JOF_QVAR bytecodes. */

#define GET_ARGNO(pc)           GET_ARGC(pc)

#define SET_ARGNO(pc,argno)     SET_JUMP_OFFSET(pc,argno)

#define ARGNO_LEN               JUMP_OFFSET_LEN

#define GET_VARNO(pc)           GET_ARGC(pc)

#define SET_VARNO(pc,varno)     SET_JUMP_OFFSET(pc,varno)

#define VARNO_LEN               JUMP_OFFSET_LEN



struct JSCodeSpec {

    const char          *name;          /* JS bytecode name */

    const char          *token;         /* JS source literal or null */

    int8                length;         /* length including opcode byte */

    int8                nuses;          /* arity, -1 if variadic */

    int8                ndefs;          /* number of stack results */

    uint8               prec;           /* operator precedence */

    uint32              format;         /* immediate operand format */

};



extern char             js_const_str[];

extern char             js_var_str[];

extern char             js_function_str[];

extern char             js_in_str[];

extern char             js_instanceof_str[];

extern char             js_new_str[];

extern char             js_delete_str[];

extern char             js_typeof_str[];

extern char             js_void_str[];

extern char             js_null_str[];

extern char             js_this_str[];

extern char             js_false_str[];

extern char             js_true_str[];

extern JSCodeSpec       js_CodeSpec[];

extern uintN            js_NumCodeSpecs;

extern jschar           js_EscapeMap[];



/*

 * Return a GC'ed string containing the chars in str, with any non-printing

 * chars or quotes (' or " as specified by the quote argument) escaped, and

 * with the quote character at the beginning and end of the result string.

 */

extern JSString *

js_QuoteString(JSContext *cx, JSString *str, jschar quote);



/*

 * JSPrinter operations, for printf style message formatting.  The return

 * value from js_GetPrinterOutput() is the printer's cumulative output, in

 * a GC'ed string.

 */

extern JSPrinter *

js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty);



extern void

js_DestroyPrinter(JSPrinter *jp);



extern JSString *

js_GetPrinterOutput(JSPrinter *jp);



extern int

js_printf(JSPrinter *jp, const char *format, ...);



extern JSBool

js_puts(JSPrinter *jp, const char *s);



#ifdef DEBUG

/*

 * Disassemblers, for debugging only.

 */

#include <stdio.h>



extern JS_FRIEND_API(void)

js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp);



extern JS_FRIEND_API(uintN)

js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,

		JSBool lines, FILE *fp);

#endif /* DEBUG */



/*

 * Decompilers, for script, function, and expression pretty-printing.

 */

extern JSBool

js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len);



extern JSBool

js_DecompileScript(JSPrinter *jp, JSScript *script);



extern JSBool

js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun);



extern JSBool

js_DecompileFunction(JSPrinter *jp, JSFunction *fun);



/*

 * Find the source expression that resulted in v, and return a new string

 * containing it.  Fall back on v's string conversion (fallback) if we can't

 * find the bytecode that generated and pushed v on the operand stack.

 *

 * Search the current stack frame if spindex is JSDVG_SEARCH_STACK.  Don't

 * look for v on the stack if spindex is JSDVG_IGNORE_STACK.  Otherwise,

 * spindex is the negative index of v, measured from cx->fp->sp, or from a

 * lower frame's sp if cx->fp is native.

 */

extern JSString *

js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,

			   JSString *fallback);



#define JSDVG_IGNORE_STACK      0

#define JSDVG_SEARCH_STACK      1



JS_END_EXTERN_C



#endif /* jsopcode_h___ */

 

**** End of jsopcode.h ****

 

**** Start of jsopcode.tbl ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */

/*

 * JavaScript operation bytecodes.  If you need to allocate a bytecode, look

 * for a name of the form JSOP_UNUSED* and claim it.  Otherwise, always add at

 * the end of the table.

 *

 * Includers must define an OPDEF macro of the following form:

 *

 * #define OPDEF(op,val,name,image,length,nuses,ndefs,prec,format) ...

 *

 * Selected arguments can be expanded in initializers.  The op argument is

 * expanded followed by comma in the JSOp enum (jsopcode.h), e.g.  The value

 * field must be dense for now, because jsopcode.c uses an OPDEF() expansion

 * inside the js_CodeSpec[] initializer.

 *

 * Field        Description

 * op           Bytecode name, which is the JSOp enumerator name

 * value        Bytecode value, which is the JSOp enumerator value

 * name         C string containing name for disassembler

 * image        C string containing "image" for pretty-printer, null if ugly

 * length       Number of bytes including any immediate operands

 * nuses        Number of stack slots consumed by bytecode, -1 if variadic

 * ndefs        Number of stack slots produced by bytecode

 * prec         Operator precedence, zero if not an operator

 * format       Bytecode plus immediate operand encoding format

 *

 * This file is best viewed with 116 columns:

01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345

 */



/* legend: op         val name          image       len use def prec  format */



/* Longstanding JavaScript bytecodes. */

OPDEF(JSOP_NOP,       0,  "nop",        NULL,         1,  0,  0,  0,  JOF_BYTE)

OPDEF(JSOP_PUSH,      1,  "push",       NULL,         1,  0,  1,  0,  JOF_BYTE)

OPDEF(JSOP_POPV,      2,  "popv",       NULL,         1,  1,  0,  0,  JOF_BYTE)

OPDEF(JSOP_ENTERWITH, 3,  "enterwith",  NULL,         1,  1,  1,  0,  JOF_BYTE)

OPDEF(JSOP_LEAVEWITH, 4,  "leavewith",  NULL,         1,  1,  0,  0,  JOF_BYTE)

OPDEF(JSOP_RETURN,    5,  "return",     NULL,         1,  1,  0,  0,  JOF_BYTE)

OPDEF(JSOP_GOTO,      6,  "goto",       NULL,         3,  0,  0,  0,  JOF_JUMP)

OPDEF(JSOP_IFEQ,      7,  "ifeq",       NULL,         3,  1,  0,  0,  JOF_JUMP)

OPDEF(JSOP_IFNE,      8,  "ifne",       NULL,         3,  1,  0,  0,  JOF_JUMP)



/* Get the arguments object for the current, lightweight function activation. */

OPDEF(JSOP_ARGUMENTS, 9, js_arguments_str, js_arguments_str, 1, 0, 1, 12, JOF_BYTE)



/* ECMA-compliant for-in loop with argument or local variable loop control. */

OPDEF(JSOP_FORARG,    10, "forarg",     NULL,         3,  0,  1,  0,  JOF_QARG|JOF_NAME|JOF_SET)

OPDEF(JSOP_FORVAR,    11, "forvar",     NULL,         3,  0,  1,  0,  JOF_QVAR|JOF_NAME|JOF_SET)



/* More longstanding bytecodes. */

OPDEF(JSOP_DUP,       12, "dup",        NULL,         1,  1,  2,  0,  JOF_BYTE)

OPDEF(JSOP_DUP2,      13, "dup2",       NULL,         1,  2,  4,  0,  JOF_BYTE)

OPDEF(JSOP_SETCONST,  14, "setconst",   NULL,         3,  1,  1,  1,  JOF_CONST|JOF_NAME|JOF_SET)

OPDEF(JSOP_BITOR,     15, "bitor",      "|",          1,  2,  1,  2,  JOF_BYTE)

OPDEF(JSOP_BITXOR,    16, "bitxor",     "^",          1,  2,  1,  3,  JOF_BYTE)

OPDEF(JSOP_BITAND,    17, "bitand",     "&",          1,  2,  1,  4,  JOF_BYTE)

OPDEF(JSOP_EQ,        18, "eq",         "==",         1,  2,  1,  5,  JOF_BYTE)

OPDEF(JSOP_NE,        19, "ne",         "!=",         1,  2,  1,  5,  JOF_BYTE)

OPDEF(JSOP_LT,        20, "lt",         "<",          1,  2,  1,  6,  JOF_BYTE)

OPDEF(JSOP_LE,        21, "le",         "<=",         1,  2,  1,  6,  JOF_BYTE)

OPDEF(JSOP_GT,        22, "gt",         ">",          1,  2,  1,  6,  JOF_BYTE)

OPDEF(JSOP_GE,        23, "ge",         ">=",         1,  2,  1,  6,  JOF_BYTE)

OPDEF(JSOP_LSH,       24, "lsh",        "<<",         1,  2,  1,  7,  JOF_BYTE)

OPDEF(JSOP_RSH,       25, "rsh",        ">>",         1,  2,  1,  7,  JOF_BYTE)

OPDEF(JSOP_URSH,      26, "ursh",       ">>>",        1,  2,  1,  7,  JOF_BYTE)

OPDEF(JSOP_ADD,       27, "add",        "+",          1,  2,  1,  8,  JOF_BYTE)

OPDEF(JSOP_SUB,       28, "sub",        "-",          1,  2,  1,  8,  JOF_BYTE)

OPDEF(JSOP_MUL,       29, "mul",        "*",          1,  2,  1,  9,  JOF_BYTE)

OPDEF(JSOP_DIV,       30, "div",        "/",          1,  2,  1,  9,  JOF_BYTE)

OPDEF(JSOP_MOD,       31, "mod",        "%",          1,  2,  1,  9,  JOF_BYTE)

OPDEF(JSOP_NOT,       32, "not",        "!",          1,  1,  1, 10,  JOF_BYTE)

OPDEF(JSOP_BITNOT,    33, "bitnot",     "~",          1,  1,  1, 10,  JOF_BYTE)

OPDEF(JSOP_NEG,       34, "neg",        "-",          1,  1,  1, 10,  JOF_BYTE)

OPDEF(JSOP_NEW,       35, js_new_str,   NULL,         3, -1,  1, 10,  JOF_UINT16)

OPDEF(JSOP_DELNAME,   36, "delname",    NULL,         3,  0,  1, 10,  JOF_CONST|JOF_NAME|JOF_DEL)

OPDEF(JSOP_DELPROP,   37, "delprop",    NULL,         3,  1,  1, 10,  JOF_CONST|JOF_PROP|JOF_DEL)

OPDEF(JSOP_DELELEM,   38, "delelem",    NULL,         1,  2,  1, 10,  JOF_BYTE |JOF_ELEM|JOF_DEL)

OPDEF(JSOP_TYPEOF,    39, js_typeof_str,NULL,         1,  1,  1, 10,  JOF_BYTE)

OPDEF(JSOP_VOID,      40, js_void_str,  NULL,         1,  1,  1, 10,  JOF_BYTE)

OPDEF(JSOP_INCNAME,   41, "incname",    NULL,         3,  0,  1, 10,  JOF_CONST|JOF_NAME|JOF_INC)

OPDEF(JSOP_INCPROP,   42, "incprop",    NULL,         3,  1,  1, 10,  JOF_CONST|JOF_PROP|JOF_INC)

OPDEF(JSOP_INCELEM,   43, "incelem",    NULL,         1,  2,  1, 10,  JOF_BYTE |JOF_ELEM|JOF_INC)

OPDEF(JSOP_DECNAME,   44, "decname",    NULL,         3,  0,  1, 10,  JOF_CONST|JOF_NAME|JOF_DEC)

OPDEF(JSOP_DECPROP,   45, "decprop",    NULL,         3,  1,  1, 10,  JOF_CONST|JOF_PROP|JOF_DEC)

OPDEF(JSOP_DECELEM,   46, "decelem",    NULL,         1,  2,  1, 10,  JOF_BYTE |JOF_ELEM|JOF_DEC)

OPDEF(JSOP_NAMEINC,   47, "nameinc",    NULL,         3,  0,  1, 10,  JOF_CONST|JOF_NAME|JOF_INC|JOF_POST)

OPDEF(JSOP_PROPINC,   48, "propinc",    NULL,         3,  1,  1, 10,  JOF_CONST|JOF_PROP|JOF_INC|JOF_POST)

OPDEF(JSOP_ELEMINC,   49, "eleminc",    NULL,         1,  2,  1, 10,  JOF_BYTE |JOF_ELEM|JOF_INC|JOF_POST)

OPDEF(JSOP_NAMEDEC,   50, "namedec",    NULL,         3,  0,  1, 10,  JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST)

OPDEF(JSOP_PROPDEC,   51, "propdec",    NULL,         3,  1,  1, 10,  JOF_CONST|JOF_PROP|JOF_DEC|JOF_POST)

OPDEF(JSOP_ELEMDEC,   52, "elemdec",    NULL,         1,  2,  1, 10,  JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_POST)

OPDEF(JSOP_GETPROP,   53, "getprop",    NULL,         3,  1,  1, 11,  JOF_CONST|JOF_PROP)

OPDEF(JSOP_SETPROP,   54, "setprop",    NULL,         3,  2,  1,  1,  JOF_CONST|JOF_PROP|JOF_SET)

OPDEF(JSOP_GETELEM,   55, "getelem",    NULL,         1,  2,  1, 11,  JOF_BYTE |JOF_ELEM)

OPDEF(JSOP_SETELEM,   56, "setelem",    NULL,         1,  3,  1,  1,  JOF_BYTE |JOF_ELEM|JOF_SET)

OPDEF(JSOP_PUSHOBJ,   57, "pushobj",    NULL,         1,  0,  1,  0,  JOF_BYTE)

OPDEF(JSOP_CALL,      58, "call",       NULL,         3, -1,  1, 11,  JOF_UINT16)

OPDEF(JSOP_NAME,      59, "name",       NULL,         3,  0,  1, 12,  JOF_CONST|JOF_NAME)

OPDEF(JSOP_NUMBER,    60, "number",     NULL,         3,  0,  1, 12,  JOF_CONST)

OPDEF(JSOP_STRING,    61, "string",     NULL,         3,  0,  1, 12,  JOF_CONST)

OPDEF(JSOP_ZERO,      62, "zero",       "0",          1,  0,  1, 12,  JOF_BYTE)

OPDEF(JSOP_ONE,       63, "one",        "1",          1,  0,  1, 12,  JOF_BYTE)

OPDEF(JSOP_NULL,      64, js_null_str,  js_null_str,  1,  0,  1, 12,  JOF_BYTE)

OPDEF(JSOP_THIS,      65, js_this_str,  js_this_str,  1,  0,  1, 12,  JOF_BYTE)

OPDEF(JSOP_FALSE,     66, js_false_str, js_false_str, 1,  0,  1, 12,  JOF_BYTE)

OPDEF(JSOP_TRUE,      67, js_true_str,  js_true_str,  1,  0,  1, 12,  JOF_BYTE)

OPDEF(JSOP_OR,        68, "or",         NULL,         3,  1,  0,  0,  JOF_JUMP)

OPDEF(JSOP_AND,       69, "and",        NULL,         3,  1,  0,  0,  JOF_JUMP)



/* The switch bytecodes have variable length. */

OPDEF(JSOP_TABLESWITCH,  70, "tableswitch",  NULL,   -1,  1,  0,  0,  JOF_TABLESWITCH)

OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL,   -1,  1,  0,  0,  JOF_LOOKUPSWITCH)



/* New, infallible/transitive identity ops. */

OPDEF(JSOP_NEW_EQ,    72, "eq",         NULL,         1,  2,  1,  5,  JOF_BYTE)

OPDEF(JSOP_NEW_NE,    73, "ne",         NULL,         1,  2,  1,  5,  JOF_BYTE)



/* Lexical closure constructor. */

OPDEF(JSOP_CLOSURE,   74, "closure",    NULL,         3,  0,  0,  0,  JOF_CONST)



/* Export and import ops. */

OPDEF(JSOP_EXPORTALL, 75, "exportall",  NULL,         1,  0,  0,  0,  JOF_BYTE)

OPDEF(JSOP_EXPORTNAME,76, "exportname", NULL,         3,  0,  0,  0,  JOF_CONST|JOF_NAME)

OPDEF(JSOP_IMPORTALL, 77, "importall",  NULL,         1,  1,  0,  0,  JOF_BYTE)

OPDEF(JSOP_IMPORTPROP,78, "importprop", NULL,         3,  1,  0,  0,  JOF_CONST|JOF_PROP|JOF_IMPORT)

OPDEF(JSOP_IMPORTELEM,79, "importelem", NULL,         1,  2,  0,  0,  JOF_BYTE |JOF_ELEM|JOF_IMPORT)



/* Push object literal. */

OPDEF(JSOP_OBJECT,    80, "object",     NULL,         3,  0,  1, 12,  JOF_CONST)



/* Pop value and discard it. */

OPDEF(JSOP_POP,       81, "pop",        NULL,         1,  1,  0,  0,  JOF_BYTE)



/* Convert value to number, for unary +. */

OPDEF(JSOP_POS,       82, "pos",        "+",          1,  1,  1, 10,  JOF_BYTE)



/* Trap into debugger for breakpoint, etc. */

OPDEF(JSOP_TRAP,      83, "trap",       NULL,         1,  0,  0,  0,  JOF_BYTE)



/* Fast get/set ops for function arguments and local variables. */

OPDEF(JSOP_GETARG,    84, "getarg",     NULL,         3,  0,  1, 12,  JOF_QARG |JOF_NAME)

OPDEF(JSOP_SETARG,    85, "setarg",     NULL,         3,  1,  1,  1,  JOF_QARG |JOF_NAME|JOF_SET)

OPDEF(JSOP_GETVAR,    86, "getvar",     NULL,         3,  0,  1, 12,  JOF_QVAR |JOF_NAME)

OPDEF(JSOP_SETVAR,    87, "setvar",     NULL,         3,  1,  1,  1,  JOF_QVAR |JOF_NAME|JOF_SET)



/* Push unsigned 16-bit int constant. */

OPDEF(JSOP_UINT16,    88, "uint16",     NULL,         3,  0,  1, 12,  JOF_UINT16)



/* Object and array literal support. */

OPDEF(JSOP_NEWINIT,   89, "newinit",    NULL,         1,  2,  1, 10,  JOF_BYTE)

OPDEF(JSOP_ENDINIT,   90, "endinit",    NULL,         1,  0,  0,  0,  JOF_BYTE)

OPDEF(JSOP_INITPROP,  91, "initprop",   NULL,         3,  1,  0,  0,  JOF_CONST|JOF_PROP)

OPDEF(JSOP_INITELEM,  92, "initelem",   NULL,         1,  2,  0,  0,  JOF_BYTE |JOF_ELEM)

OPDEF(JSOP_DEFSHARP,  93, "defsharp",   NULL,         3,  0,  0,  0,  JOF_UINT16)

OPDEF(JSOP_USESHARP,  94, "usesharp",   NULL,         3,  0,  1,  0,  JOF_UINT16)



/* Fast inc/dec ops for args and local vars. */

OPDEF(JSOP_INCARG,    95, "incarg",     NULL,         3,  0,  1, 10,  JOF_QARG |JOF_NAME|JOF_INC)

OPDEF(JSOP_INCVAR,    96, "incvar",     NULL,         3,  0,  1, 10,  JOF_QVAR |JOF_NAME|JOF_INC)

OPDEF(JSOP_DECARG,    97, "decarg",     NULL,         3,  0,  1, 10,  JOF_QARG |JOF_NAME|JOF_DEC)

OPDEF(JSOP_DECVAR,    98, "decvar",     NULL,         3,  0,  1, 10,  JOF_QVAR |JOF_NAME|JOF_DEC)

OPDEF(JSOP_ARGINC,    99, "arginc",     NULL,         3,  0,  1, 10,  JOF_QARG |JOF_NAME|JOF_INC|JOF_POST)

OPDEF(JSOP_VARINC,    100,"varinc",     NULL,         3,  0,  1, 10,  JOF_QVAR |JOF_NAME|JOF_INC|JOF_POST)

OPDEF(JSOP_ARGDEC,    101,"argdec",     NULL,         3,  0,  1, 10,  JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST)

OPDEF(JSOP_VARDEC,    102,"vardec",     NULL,         3,  0,  1, 10,  JOF_QVAR |JOF_NAME|JOF_DEC|JOF_POST)



/* ECMA-compliant for/in ops. */

OPDEF(JSOP_TOOBJECT,  103,"toobject",   NULL,         1,  1,  1,  0,  JOF_BYTE)

OPDEF(JSOP_FORNAME,   104,"forname",    NULL,         3,  0,  1,  0,  JOF_CONST|JOF_NAME|JOF_SET)

OPDEF(JSOP_FORPROP,   105,"forprop",    NULL,         3,  1,  1,  0,  JOF_CONST|JOF_PROP|JOF_SET)

OPDEF(JSOP_FORELEM,   106,"forelem",    NULL,         1,  2,  4,  0,  JOF_BYTE |JOF_ELEM)

OPDEF(JSOP_POP2,      107,"pop2",       NULL,         1,  2,  0,  0,  JOF_BYTE)



/* ECMA-compliant assignment ops. */

OPDEF(JSOP_BINDNAME,  108,"bindname",   NULL,         3,  0,  1,  0,  JOF_CONST|JOF_NAME)

OPDEF(JSOP_SETNAME,   109,"setname",    NULL,         3,  2,  1,  1,  JOF_CONST|JOF_NAME|JOF_SET)



/* Exception handling ops. */

OPDEF(JSOP_THROW,     110,"throw",      NULL,         1,  1,  0,  0,  JOF_BYTE)



/* 'in' and 'instanceof' ops. */

OPDEF(JSOP_IN,        111,js_in_str,    js_in_str,    1,  2,  1,  6,  JOF_BYTE)

OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,6,JOF_BYTE)



/* debugger op */

OPDEF(JSOP_DEBUGGER,  113,"debugger",   NULL,         1,  0,  0,  0,  JOF_BYTE)



/* gosub/retsub for finally handling */

OPDEF(JSOP_GOSUB,     114,"gosub",      NULL,         3,  0,  1,  0,  JOF_JUMP)

OPDEF(JSOP_RETSUB,    115,"retsub",     NULL,         1,  1,  0,  0,  JOF_BYTE)



/* More exception handling ops. */

OPDEF(JSOP_EXCEPTION, 116,"exception",  NULL,         1,  0,  1,  0,  JOF_BYTE)

OPDEF(JSOP_SETSP,     117,"setsp",      NULL,         3,  0,  0,  0,  JOF_UINT16)



/*

 * ECMA-compliant switch statement ops.

 * CONDSWITCH is a decompilable NOP; CASE is ===, POP, jump if true, re-push

 * lval if false; and DEFAULT is POP lval and GOTO.

 */

OPDEF(JSOP_CONDSWITCH,118,"condswitch", NULL,         1,  0,  0,  0,  JOF_BYTE)

OPDEF(JSOP_CASE,      119,"case",       NULL,         3,  1,  0,  0,  JOF_JUMP)

OPDEF(JSOP_DEFAULT,   120,"default",    NULL,         3,  1,  0,  0,  JOF_JUMP)



/*

 * ECMA-compliant call to eval op

 */

OPDEF(JSOP_EVAL,      121,"eval",       NULL,         3, -1,  1, 11,  JOF_UINT16)



/*

 * ECMA-compliant helper for 'for (x[i] in o)' loops.

 */

OPDEF(JSOP_ENUMELEM,  122,"enumelem",   NULL,         1,  3,  0,  1,  JOF_BYTE |JOF_ELEM|JOF_SET)



/*

 * Getter and setter prefix bytecodes.  These modify the next bytecode, either

 * an assignment or a property initializer code, which then defines a property

 * getter or setter.

 */

OPDEF(JSOP_GETTER,    123,js_getter_str,js_getter_str,1,  0,  0,  0,  JOF_BYTE)

OPDEF(JSOP_SETTER,    124,js_setter_str,js_setter_str,1,  0,  0,  0,  JOF_BYTE)



/*

 * Prolog bytecodes for defining function, var, and const names.

 */

OPDEF(JSOP_DEFFUN,    125,"deffun",     NULL,         3,  0,  0,  0,  JOF_CONST|JOF_SET)

OPDEF(JSOP_DEFCONST,  126,"defconst",   NULL,         3,  0,  0,  0,  JOF_CONST|JOF_NAME|JOF_SET)

OPDEF(JSOP_DEFVAR,    127,"defvar",     NULL,         3,  0,  0,  0,  JOF_CONST|JOF_NAME)



/* Auto-clone (if needed due to re-parenting) and push an anonymous function. */

OPDEF(JSOP_ANONFUNOBJ,  128, "anonfunobj",  NULL,     3,  0,  1, 12,  JOF_CONST)



/* ECMA ed. 3 named function expression. */

OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL,     3,  0,  1, 12,  JOF_CONST)



/* Like JSOP_INITPROP, but specialized to make a DontDelete property for ECMA ed. 3 catch variables. */

OPDEF(JSOP_INITCATCHVAR,130, "initcatchvar",NULL,     3,  1,  0,  0,  JOF_CONST|JOF_PROP)



/* ECMA-mandated parenthesization opcode, which nulls the reference base register, obj; see jsinterp.c. */

OPDEF(JSOP_GROUP,       131, "group",       NULL,     1,  0,  0,  0,  JOF_BYTE)



/* Host object extension: given 'o.item(i) = j', the left-hand side compiles JSOP_SETCALL, rather than JSOP_CALL. */

OPDEF(JSOP_SETCALL,     132, "setcall",     NULL,     3, -1,  2, 11,  JOF_UINT16|JOF_SET)



/*

 * Exception handling no-ops, for more economical byte-coding than SRC_TRYFIN

 * srcnote-annotated JSOP_NOPs.

 */

OPDEF(JSOP_TRY,         133,"try",        NULL,       1,  0,  0,  0,  JOF_BYTE)

OPDEF(JSOP_FINALLY,     134,"finally",    NULL,       1,  0,  0,  0,  JOF_BYTE)



/*

 * Swap the top two stack elements.

 * N.B. JSOP_SWAP doesn't swap the corresponding pc stack generating pcs, as

 * they're not needed for the current use of preserving the top-of-stack return

 * value when popping scopes while returning from catch blocks.

 */

OPDEF(JSOP_SWAP,        135,"swap",       NULL,       1,  2,  2,  0,  JOF_BYTE)



/*

 * Bytecodes that avoid making an arguments object in most cases:

 * JSOP_ARGSUB gets arguments[i] from fp->argv, iff i is in [0, fp->argc-1].

 * JSOP_ARGCNT returns fp->argc.

 */

OPDEF(JSOP_ARGSUB,      136,"argsub",     NULL,       3,  0,  1, 12,  JOF_QARG |JOF_NAME)

OPDEF(JSOP_ARGCNT,      137,"argcnt",     NULL,       1,  0,  1, 12,  JOF_BYTE)



/*

 * Define a local function object as a local variable.

 * The local variable's slot number is the first immediate two-byte operand.

 * The function object's atom index is the second immediate operand.

 */

OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL,       5,  0,  0,  0,  JOF_DEFLOCALVAR)

 

**** End of jsopcode.tbl ****

 

**** Start of jsosdep.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsosdep_h___

#define jsosdep_h___

/*

 * OS (and machine, and compiler XXX) dependent information.

 */



#ifdef MOZILLA_CLIENT

#include "platform.h"

#endif



#ifdef XP_PC



#if defined(_WIN32) || defined (XP_OS2)

#define JS_HAVE_LONG_LONG

#else

#undef JS_HAVE_LONG_LONG

#endif

#endif /* XP_PC */



#ifdef XP_MAC

#define JS_HAVE_LONG_LONG



JS_BEGIN_EXTERN_C



#include <stddef.h>



extern void* reallocSmaller(void* block, size_t newSize);



extern char* strdup(const char* str);



JS_END_EXTERN_C



#endif /* XP_MAC */



#ifdef XP_BEOS

#define JS_HAVE_LONG_LONG

#endif





#ifdef XP_UNIX



/*

 * Get OS specific header information.

 */

#if defined(AIXV3) || defined(AIX)

#define JS_HAVE_LONG_LONG



#elif defined(BSDI)

#define JS_HAVE_LONG_LONG



#elif defined(HPUX)

#define JS_HAVE_LONG_LONG



#elif defined(IRIX)

#define JS_HAVE_LONG_LONG



#elif defined(linux)

#define JS_HAVE_LONG_LONG



#elif defined(OSF1)

#define JS_HAVE_LONG_LONG



#elif defined(_SCO_DS)

#undef JS_HAVE_LONG_LONG



#elif defined(SOLARIS)

#define JS_HAVE_LONG_LONG



#elif defined(SUNOS4)

#undef JS_HAVE_LONG_LONG



/*

** Missing function prototypes

*/



extern void *sbrk(int);



#elif defined(UNIXWARE)

#undef JS_HAVE_LONG_LONG



#elif defined(VMS) && defined(__ALPHA)

#define JS_HAVE_LONG_LONG



#endif



#endif /* XP_UNIX */



#endif /* jsosdep_h___ */

 

**** End of jsosdep.h ****

 

**** Start of jsotypes.h ****

 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

/*

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * This section typedefs the old 'native' types to the new PR<type>s.

 * These definitions are scheduled to be eliminated at the earliest

 * possible time. The NSPR API is implemented and documented using

 * the new definitions.

 */



/*

 * Note that we test for PROTYPES_H, not JSOTYPES_H.  This is to avoid

 * double-definitions of scalar types such as uint32, if NSPR's

 * protypes.h is also included.

 */

#ifndef PROTYPES_H

#define PROTYPES_H



#ifdef XP_BEOS

/* BeOS defines most int types in SupportDefs.h (int8, uint8, int16,

 * uint16, int32, uint32, int64, uint64), so in the interest of

 * not conflicting with other definitions elsewhere we have to skip the

 * #ifdef jungle below, duplicate some definitions, and do our stuff.

 */

#include <SupportDefs.h>



typedef JSUintn uintn;

#ifndef _XP_Core_

typedef JSIntn intn;

#endif



#else



/* SVR4 typedef of uint is commonly found on UNIX machines. */

#ifdef XP_UNIX

#include <sys/types.h>

#else

typedef JSUintn uint;

#endif



typedef JSUintn uintn;

typedef JSUint64 uint64;

#if !defined(XP_MAC) && !defined(_WIN32) && !defined(XP_OS2)

typedef JSUint32 uint32;

#else

typedef unsigned long uint32;

#endif

typedef JSUint16 uint16;

typedef JSUint8 uint8;



#ifndef _XP_Core_

typedef JSIntn intn;

#endif



/*

 * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h, a very

 * common header file) defines the types int8, int16, int32, and int64.

 * So we don't define these four types here to avoid conflicts in case

 * the code also includes sys/types.h.

 */

#ifdef AIX4_3

#include <sys/inttypes.h>

#else

typedef JSInt64 int64;



/* /usr/include/model.h on HP-UX defines int8, int16, and int32 */

#ifdef HPUX

#include <model.h>

#else

#if !defined(XP_MAC) && !defined(_WIN32) && !defined(XP_OS2)

typedef JSInt32 int32;

#else

typedef long int32;

#endif

typedef JSInt16 int16;

typedef JSInt8 int8;

#endif /* HPUX */

#endif /* AIX4_3 */



#endif	/* XP_BEOS */



typedef JSFloat64 float64;



/* Re: jsbit.h */

#define TEST_BIT	JS_TEST_BIT

#define SET_BIT		JS_SET_BIT

#define CLEAR_BIT	JS_CLEAR_BIT



/* Re: prarena.h->plarena.h */

#define PRArena PLArena

#define PRArenaPool PLArenaPool

#define PRArenaStats PLArenaStats

#define PR_ARENA_ALIGN PL_ARENA_ALIGN

#define PR_INIT_ARENA_POOL PL_INIT_ARENA_POOL

#define PR_ARENA_ALLOCATE PL_ARENA_ALLOCATE

#define PR_ARENA_GROW PL_ARENA_GROW

#define PR_ARENA_MARK PL_ARENA_MARK

#define PR_CLEAR_UNUSED PL_CLEAR_UNUSED

#define PR_CLEAR_ARENA PL_CLEAR_ARENA

#define PR_ARENA_RELEASE PL_ARENA_RELEASE

#define PR_COUNT_ARENA PL_COUNT_ARENA

#define PR_ARENA_DESTROY PL_ARENA_DESTROY

#define PR_InitArenaPool PL_InitArenaPool

#define PR_FreeArenaPool PL_FreeArenaPool

#define PR_FinishArenaPool PL_FinishArenaPool

#define PR_CompactArenaPool PL_CompactArenaPool

#define PR_ArenaFinish PL_ArenaFinish

#define PR_ArenaAllocate PL_ArenaAllocate

#define PR_ArenaGrow PL_ArenaGrow

#define PR_ArenaRelease PL_ArenaRelease

#define PR_ArenaCountAllocation PL_ArenaCountAllocation

#define PR_ArenaCountInplaceGrowth PL_ArenaCountInplaceGrowth

#define PR_ArenaCountGrowth PL_ArenaCountGrowth

#define PR_ArenaCountRelease PL_ArenaCountRelease

#define PR_ArenaCountRetract PL_ArenaCountRetract



/* Re: prevent.h->plevent.h */

#define PREvent PLEvent

#define PREventQueue PLEventQueue

#define PR_CreateEventQueue PL_CreateEventQueue

#define PR_DestroyEventQueue PL_DestroyEventQueue

#define PR_GetEventQueueMonitor PL_GetEventQueueMonitor

#define PR_ENTER_EVENT_QUEUE_MONITOR PL_ENTER_EVENT_QUEUE_MONITOR

#define PR_EXIT_EVENT_QUEUE_MONITOR PL_EXIT_EVENT_QUEUE_MONITOR

#define PR_PostEvent PL_PostEvent

#define PR_PostSynchronousEvent PL_PostSynchronousEvent

#define PR_GetEvent PL_GetEvent

#define PR_EventAvailable PL_EventAvailable

#define PREventFunProc PLEventFunProc

#define PR_MapEvents PL_MapEvents

#define PR_RevokeEvents PL_RevokeEvents

#define PR_ProcessPendingEvents PL_ProcessPendingEvents

#define PR_WaitForEvent PL_WaitForEvent

#define PR_EventLoop PL_EventLoop

#define PR_GetEventQueueSelectFD PL_GetEventQueueSelectFD

#define PRHandleEventProc PLHandleEventProc

#define PRDestroyEventProc PLDestroyEventProc

#define PR_InitEvent PL_InitEvent

#define PR_GetEventOwner PL_GetEventOwner

#define PR_HandleEvent PL_HandleEvent

#define PR_DestroyEvent PL_DestroyEvent

#define PR_DequeueEvent PL_DequeueEvent

#define PR_GetMainEventQueue PL_GetMainEventQueue



/* Re: prhash.h->plhash.h */

#define PRHashEntry PLHashEntry

#define PRHashTable PLHashTable

#define PRHashNumber PLHashNumber

#define PRHashFunction PLHashFunction

#define PRHashComparator PLHashComparator

#define PRHashEnumerator PLHashEnumerator

#define PRHashAllocOps PLHashAllocOps

#define PR_NewHashTable PL_NewHashTable

#define PR_HashTableDestroy PL_HashTableDestroy

#define PR_HashTableRawLookup PL_HashTableRawLookup

#define PR_HashTableRawAdd PL_HashTableRawAdd

#define PR_HashTableRawRemove PL_HashTableRawRemove

#define PR_HashTableAdd PL_HashTableAdd

#define PR_HashTableRemove PL_HashTableRemove

#define PR_HashTableEnumerateEntries PL_HashTableEnumerateEntries

#define PR_HashTableLookup PL_HashTableLookup

#define PR_HashTableDump PL_HashTableDump

#define PR_HashString PL_HashString

#define PR_CompareStrings PL_CompareStrings

#define PR_CompareValues PL_CompareValues



#ifdef XP_MAC

#ifndef TRUE				/* Mac standard is lower case true */

	#define TRUE 1

#endif

#ifndef FALSE				/* Mac standard is lower case false */

	#define FALSE 0

#endif

#endif



#endif /* !defined(PROTYPES_H) */

 

**** End of jsotypes.h ****

 

**** Start of jsparse.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS parser.

 *

 * This is a recursive-descent parser for the JavaScript language specified by

 * "The JavaScript 1.5 Language Specification".  It uses lexical and semantic

 * feedback to disambiguate non-LL(1) structures.  It generates trees of nodes

 * induced by the recursive parsing (not precise syntax trees, see jsparse.h).

 * After tree construction, it rewrites trees to fold constants and evaluate

 * compile-time expressions.  Finally, it calls js_EmitTree (see jsemit.h) to

 * generate bytecode.

 *

 * This parser attempts no error recovery.  The dense JSTokenType enumeration

 * was designed with error recovery built on 64-bit first and follow bitsets

 * in mind, however.

 */

#include "jsstddef.h"

#include <stdlib.h>

#include <string.h>

#include <math.h>

#include "jstypes.h"

#include "jsarena.h" /* Added by JSIFY */

#include "jsutil.h" /* Added by JSIFY */

#include "jsapi.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsemit.h"

#include "jsfun.h"

#include "jsinterp.h"

#include "jslock.h"

#include "jsnum.h"

#include "jsobj.h"

#include "jsopcode.h"

#include "jsparse.h"

#include "jsscan.h"

#include "jsscope.h"

#include "jsscript.h"

#include "jsstr.h"



/*

 * JS parsers, from lowest to highest precedence.

 *

 * Each parser takes a context and a token stream, and emits bytecode using

 * a code generator.

 */



typedef JSParseNode *

JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);



typedef JSParseNode *

JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,

               JSBool allowCallSyntax);



static JSParser FunctionStmt;

#if JS_HAS_LEXICAL_CLOSURE

static JSParser FunctionExpr;

#endif

static JSParser Statements;

static JSParser Statement;

static JSParser Variables;

static JSParser Expr;

static JSParser AssignExpr;

static JSParser CondExpr;

static JSParser OrExpr;

static JSParser AndExpr;

static JSParser BitOrExpr;

static JSParser BitXorExpr;

static JSParser BitAndExpr;

static JSParser EqExpr;

static JSParser RelExpr;

static JSParser ShiftExpr;

static JSParser AddExpr;

static JSParser MulExpr;

static JSParser UnaryExpr;

static JSMemberParser MemberExpr;

static JSParser PrimaryExpr;



/*

 * Insist that the next token be of type tt, or report errno and return null.

 * NB: this macro uses cx and ts from its lexical environment.

 */



#define MUST_MATCH_TOKEN(tt, errno)                                           \

    JS_BEGIN_MACRO                                                            \

        if (js_GetToken(cx, ts) != tt) {                                      \

            js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \

            return NULL;                                                      \

        }                                                                     \

    JS_END_MACRO



#ifdef METER_PARSENODES

static uint32 parsenodes = 0;

static uint32 maxparsenodes = 0;

static uint32 recyclednodes = 0;

#endif



/*

 * Allocate a JSParseNode from cx's temporary arena.

 */

static JSParseNode *

NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity,

             JSTreeContext *tc)

{

    JSParseNode *pn;



    pn = tc->nodeList;

    if (pn) {

        tc->nodeList = pn->pn_next;

    } else {

        JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);

        if (!pn)

            return NULL;

    }

    pn->pn_type = tok->type;

    pn->pn_pos = tok->pos;

    pn->pn_op = JSOP_NOP;

    pn->pn_arity = arity;

    pn->pn_next = NULL;

#ifdef METER_PARSENODES

    parsenodes++;

    if (parsenodes - recyclednodes > maxparsenodes)

        maxparsenodes = parsenodes - recyclednodes;

#endif

    return pn;

}



static JSParseNode *

NewBinary(JSContext *cx, JSTokenType tt,

          JSOp op, JSParseNode *left, JSParseNode *right,

          JSTreeContext *tc)

{

    JSParseNode *pn;



    if (!left || !right)

        return NULL;

    pn = tc->nodeList;

    if (pn) {

        tc->nodeList = pn->pn_next;

    } else {

        JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);

        if (!pn)

            return NULL;

    }

    pn->pn_type = tt;

    pn->pn_pos.begin = left->pn_pos.begin;

    pn->pn_pos.end = right->pn_pos.end;

    pn->pn_op = op;

    pn->pn_arity = PN_BINARY;

    pn->pn_left = left;

    pn->pn_right = right;

    pn->pn_next = NULL;

#ifdef METER_PARSENODES

    parsenodes++;

    if (parsenodes - recyclednodes > maxparsenodes)

        maxparsenodes = parsenodes - recyclednodes;

#endif

    return pn;

}



static void

RecycleTree(JSParseNode *pn, JSTreeContext *tc)

{

    JSParseNode *pn2;



    if (!pn)

        return;

    JS_ASSERT(pn != tc->nodeList);      /* catch back-to-back dup recycles */

    switch (pn->pn_arity) {

      case PN_FUNC:

        RecycleTree(pn->pn_body, tc);

        break;

      case PN_LIST:

        while ((pn2 = pn->pn_head) != NULL) {

            pn->pn_head = pn2->pn_next;

            RecycleTree(pn2, tc);

        }

        break;

      case PN_TERNARY:

        RecycleTree(pn->pn_kid1, tc);

        RecycleTree(pn->pn_kid2, tc);

        RecycleTree(pn->pn_kid3, tc);

        break;

      case PN_BINARY:

        RecycleTree(pn->pn_left, tc);

        RecycleTree(pn->pn_right, tc);

        break;

      case PN_UNARY:

        RecycleTree(pn->pn_kid, tc);

        break;

      case PN_NAME:

        RecycleTree(pn->pn_expr, tc);

        break;

      case PN_NULLARY:

        break;

    }

    pn->pn_next = tc->nodeList;

    tc->nodeList = pn;

#ifdef METER_PARSENODES

    recyclednodes++;

#endif

}



static JSBool

WellTerminated(JSContext *cx, JSTokenStream *ts, JSTokenType lastExprType)

{

    JSTokenType tt;



    tt = js_PeekTokenSameLine(cx, ts);

    if (tt == TOK_ERROR)

        return JS_FALSE;

    if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {

#if JS_HAS_LEXICAL_CLOSURE

        if ((tt == TOK_FUNCTION || lastExprType == TOK_FUNCTION) &&

            cx->version < JSVERSION_1_2) {

            /*

             * Checking against version < 1.2 and version >= 1.0

             * in the above line breaks old javascript, so we keep it

             * this way for now... XXX warning needed?

             */

            return JS_TRUE;

        }

#endif

        js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                    JSMSG_SEMI_BEFORE_STMNT);

        return JS_FALSE;

    }

    return JS_TRUE;

}



#if JS_HAS_GETTER_SETTER

static JSTokenType

CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)

{

    JSAtom *atom;

    JSRuntime *rt;

    JSOp op;



    JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);

    atom = CURRENT_TOKEN(ts).t_atom;

    rt = cx->runtime;

    if (atom == rt->atomState.getterAtom)

        op = JSOP_GETTER;

    else if (atom == rt->atomState.setterAtom)

        op = JSOP_SETTER;

    else

        return TOK_NAME;

    if (js_PeekTokenSameLine(cx, ts) != tt)

        return TOK_NAME;

    (void) js_GetToken(cx, ts);

    if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {

        js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                    JSMSG_BAD_GETTER_OR_SETTER,

                                    (op == JSOP_GETTER)

                                    ? js_getter_str

                                    : js_setter_str);

        return TOK_ERROR;

    }

    CURRENT_TOKEN(ts).t_op = op;

    if (!js_ReportCompileErrorNumber(cx, ts, NULL,

                                     JSREPORT_WARNING |

                                     JSREPORT_STRICT,

                                     JSMSG_DEPRECATED_USAGE,

                                     ATOM_BYTES(atom))) {

        return TOK_ERROR;

    }

    return tt;

}

#endif



/*

 * Parse a top-level JS script.

 */

JS_FRIEND_API(JSParseNode *)

js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)

{

    JSStackFrame *fp, frame;

    JSTreeContext tc;

    JSParseNode *pn;



    JS_ASSERT(cx->runtime->gcDisabled);



    /*

     * Push a compiler frame if we have no frames, or if the top frame is a

     * lightweight function activation, or if its scope chain doesn't match

     * the one passed to us.

     */

    fp = cx->fp;

    if (!fp || !fp->varobj || fp->scopeChain != chain) {

        memset(&frame, 0, sizeof frame);

        frame.varobj = frame.scopeChain = chain;

        if (cx->options & JSOPTION_VAROBJFIX) {

            while ((chain = JS_GetParent(cx, chain)) != NULL)

                frame.varobj = chain;

        }

        frame.down = fp;

        cx->fp = &frame;

    }



    TREE_CONTEXT_INIT(&tc);

    pn = Statements(cx, ts, &tc);

    if (pn) {

        if (!js_MatchToken(cx, ts, TOK_EOF)) {

            js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                        JSMSG_SYNTAX_ERROR);

            pn = NULL;

        } else {

            pn->pn_type = TOK_LC;

            if (!js_FoldConstants(cx, pn, &tc))

                pn = NULL;

        }

    }



    TREE_CONTEXT_FINISH(&tc);

    cx->fp = fp;

    return pn;

}





// DREAMWEAVER snewman 3/16/01: added this flag to detect re-entrant calls

// to the JS compiler.  I'm getting a wierd illegal-access error when the

// internal JavaScript debugger contains a compile-time error, and I think

// it's because this causes us to invoke the compiler reentrantly and it

// gets mixed up about tempPool.

JSBool gInCompileTokenStream = JS_FALSE;





/*

 * Compile a top-level script.

 */

JS_FRIEND_API(JSBool)

js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,

                      JSCodeGenerator *cg)

{

    JSStackFrame *fp, frame;

    JSParseNode *pn;

    JSBool ok;

	

    // DREAMWEAVER snewman 3/16/01: see declaration of gInCompileTokenStream.

    JS_ASSERT(!gInCompileTokenStream);

    gInCompileTokenStream = JS_TRUE;

	

#ifdef METER_PARSENODES

    void *sbrk(ptrdiff_t), *before = sbrk(0);

#endif



    /*

     * Push a compiler frame if we have no frames, or if the top frame is a

     * lightweight function activation, or if its scope chain doesn't match

     * the one passed to us.

     */

    fp = cx->fp;

    if (!fp || !fp->varobj || fp->scopeChain != chain) {

        memset(&frame, 0, sizeof frame);

        frame.varobj = frame.scopeChain = chain;

        if (cx->options & JSOPTION_VAROBJFIX) {

            while ((chain = JS_GetParent(cx, chain)) != NULL)

                frame.varobj = chain;

        }

        frame.down = fp;

        cx->fp = &frame;

    }



    /*

     * Prevent GC activation (possible if out of memory when atomizing,

     * or from pre-ECMAv2 switch case expr eval in the unlikely case of a

     * branch-callback -- unlikely because it means the switch case must

     * have called a function).

     */

    JS_DISABLE_GC(cx->runtime);



    pn = Statements(cx, ts, &cg->treeContext);

    if (!pn) {

        ok = JS_FALSE;

    } else if (!js_MatchToken(cx, ts, TOK_EOF)) {

        js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                    JSMSG_SYNTAX_ERROR);

        ok = JS_FALSE;

    } else {

#ifdef METER_PARSENODES

        printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n",

               (char *)sbrk(0) - (char *)before,

               parsenodes,

               maxparsenodes,

               parsenodes - recyclednodes);

        before = sbrk(0);

#endif

        pn->pn_type = TOK_LC;

        ok = js_FoldConstants(cx, pn, &cg->treeContext) &&

             js_AllocTryNotes(cx, cg) &&

             js_EmitTree(cx, cg, pn);

    }



#ifdef METER_PARSENODES

    printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",

           (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);

#endif

#ifdef JS_ARENAMETER

    JS_DumpArenaStats(stdout);

#endif

    JS_ENABLE_GC(cx->runtime);

    cx->fp = fp;

	

    // DREAMWEAVER snewman 3/16/01: see declaration of gInCompileTokenStream.

    gInCompileTokenStream = JS_FALSE;

	

    return ok;

}



/*

 * Insist on a final return before control flows out of pn, but don't be too

 * smart about loops (do {...; return e2;} while(0) at the end of a function

 * that contains an early return e1 will get a strict-option-only warning).

 */

static JSBool

HasFinalReturn(JSParseNode *pn)

{

    JSBool ok, hasDefault;

    JSParseNode *pn2, *pn3;



    switch (pn->pn_type) {

      case TOK_LC:

        if (!pn->pn_head)

            return JS_FALSE;

        return HasFinalReturn(PN_LAST(pn));



      case TOK_IF:

        ok = HasFinalReturn(pn->pn_kid2);

        ok &= pn->pn_kid3 && HasFinalReturn(pn->pn_kid3);

        return ok;



#if JS_HAS_SWITCH_STATEMENT

      case TOK_SWITCH:

        ok = JS_TRUE;

        hasDefault = JS_FALSE;

        for (pn2 = pn->pn_kid2->pn_head; ok && pn2; pn2 = pn2->pn_next) {

            if (pn2->pn_type == TOK_DEFAULT)

                hasDefault = JS_TRUE;

            pn3 = pn2->pn_right;

            JS_ASSERT(pn3->pn_type == TOK_LC);

            if (pn3->pn_head)

                ok &= HasFinalReturn(PN_LAST(pn3));

        }

        /* If a final switch has no default case, we judge it harshly. */

        ok &= hasDefault;

        return ok;

#endif /* JS_HAS_SWITCH_STATEMENT */



      case TOK_WITH:

        return HasFinalReturn(pn->pn_right);



      case TOK_RETURN:

        return JS_TRUE;



#if JS_HAS_EXCEPTIONS

      case TOK_THROW:

        return JS_TRUE;



      case TOK_TRY:

        /* If we have a finally block that returns, we are done. */

        if (pn->pn_kid3 && HasFinalReturn(pn->pn_kid3))

            return JS_TRUE;



        /* Else check the try block and any and all catch statements. */

        ok = HasFinalReturn(pn->pn_kid1);

        if (pn->pn_kid2)

            ok &= HasFinalReturn(pn->pn_kid2);

        return ok;



      case TOK_CATCH:

        /* Check this block's code and iterate over further catch blocks. */

        ok = HasFinalReturn(pn->pn_kid3);

        for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2)

            ok &= HasFinalReturn(pn2->pn_kid3);

        return ok;

#endif



      default:

        return JS_FALSE;

    }

}



static JSBool

ReportNoReturnValue(JSContext *cx, JSTokenStream *ts)

{

    JSFunction *fun;

    JSBool ok;



    fun = cx->fp->fun;

    if (fun->atom) {

        char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom));

        ok = js_ReportCompileErrorNumber(cx, ts, NULL,

                                         JSREPORT_WARNING |

                                         JSREPORT_STRICT,

                                         JSMSG_NO_RETURN_VALUE, name);

    } else {

        ok = js_ReportCompileErrorNumber(cx, ts, NULL,

                                         JSREPORT_WARNING |

                                         JSREPORT_STRICT,

                                         JSMSG_ANON_NO_RETURN_VALUE);

    }

    return ok;

}



static JSBool

CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)

{

    return HasFinalReturn(pn) || ReportNoReturnValue(cx, ts);

}



static JSParseNode *

FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun,

             JSTreeContext *tc)

{

    JSStackFrame *fp, frame;

    JSObject *funobj;

    uintN oldflags;

    JSParseNode *pn;



    fp = cx->fp;

    funobj = fun->object;

    if (!fp || fp->fun != fun || fp->varobj != funobj ||

        fp->scopeChain != funobj) {

        memset(&frame, 0, sizeof frame);

        frame.fun = fun;

        frame.varobj = frame.scopeChain = funobj;

        frame.down = fp;

        cx->fp = &frame;

    }



    oldflags = tc->flags;

    tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);

    tc->flags |= TCF_IN_FUNCTION;

    pn = Statements(cx, ts, tc);



    /* Check for falling off the end of a function that returns a value. */

    if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) {

        if (!CheckFinalReturn(cx, ts, pn))

            pn = NULL;

    }



    cx->fp = fp;

    tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);

    return pn;

}



/*

 * Compile a JS function body, which might appear as the value of an event

 * handler attribute in an HTML <INPUT> tag.

 */

JSBool

js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)

{

    JSCodeGenerator funcg;

    JSStackFrame *fp, frame;

    JSObject *funobj;

    JSParseNode *pn;

    JSBool ok;



    if (!js_InitCodeGenerator(cx, &funcg, ts->filename, ts->lineno,

                              ts->principals)) {

        return JS_FALSE;

    }



    /* Prevent GC activation while compiling. */

    JS_DISABLE_GC(cx->runtime);



    /* Push a JSStackFrame for use by FunctionBody and js_EmitFunctionBody. */

    fp = cx->fp;

    funobj = fun->object;

    JS_ASSERT(!fp || fp->fun != fun || fp->varobj != funobj ||

              fp->scopeChain != funobj);

    memset(&frame, 0, sizeof frame);

    frame.fun = fun;

    frame.varobj = frame.scopeChain = funobj;

    frame.down = fp;

    cx->fp = &frame;



    /* Ensure that the body looks like a block statement to js_EmitTree. */

    CURRENT_TOKEN(ts).type = TOK_LC;

    pn = FunctionBody(cx, ts, fun, &funcg.treeContext);

    if (!pn) {

        ok = JS_FALSE;

    } else {

        ok = js_FoldConstants(cx, pn, &funcg.treeContext) &&

             js_EmitFunctionBody(cx, &funcg, pn, fun);

    }



    /* Restore saved state and release code generation arenas. */

    cx->fp = fp;

    JS_ENABLE_GC(cx->runtime);

    js_FinishCodeGenerator(cx, &funcg);

    return ok;

}



static JSParseNode *

FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,

            JSBool lambda)

{

    JSParseNode *pn, *body;

    JSOp op, prevop;

    JSAtom *funAtom, *argAtom;

    JSFunction *fun;

    JSObject *parent;

    JSObject *pobj;

    JSScopeProperty *sprop;

    JSTreeContext funtc;

    jsid oldArgId;

    JSAtomListElement *ale;



    /* Make a TOK_FUNCTION node. */

    pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_FUNC, tc);

    if (!pn)

        return NULL;

    op = CURRENT_TOKEN(ts).t_op;



    /* Scan the optional function name into funAtom. */

    if (js_MatchToken(cx, ts, TOK_NAME))

        funAtom = CURRENT_TOKEN(ts).t_atom;

    else

        funAtom = NULL;



    /* Find the nearest variable-declaring scope and use it as our parent. */

    parent = cx->fp->varobj;

    fun = js_NewFunction(cx, NULL, NULL, 0, 0, parent, funAtom);

    if (!fun)

        return NULL;



#if JS_HAS_GETTER_SETTER

    if (op != JSOP_NOP)

        fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;

#endif



    /* Now parse formal argument list and compute fun->nargs. */

    MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);

    if (!js_MatchToken(cx, ts, TOK_RP)) {

        do {

            MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL);

            argAtom = CURRENT_TOKEN(ts).t_atom;

            pobj = NULL;

            if (!js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj,

                                   (JSProperty **)&sprop)) {

                return NULL;

            }

            if (sprop && pobj == fun->object) {

                if (SPROP_GETTER(sprop, pobj) == js_GetArgument) {

                    if (JS_HAS_STRICT_OPTION(cx)) {

                        OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);

                        if (!js_ReportCompileErrorNumber(cx, ts, NULL,

                                                         JSREPORT_WARNING |

                                                         JSREPORT_STRICT,

                                                         JSMSG_DUPLICATE_FORMAL,

                                                         ATOM_BYTES(argAtom))) {

                            return NULL;

                        }

                    }



                    /*

                     * A duplicate parameter name. We create a dummy symbol

                     * entry with property id of the parameter number and set

                     * the id to the name of the parameter.

                     * The decompiler will know to treat this case specially.

                     */

                    oldArgId = (jsid) sprop->id;

                    OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);

                    sprop = NULL;

                    if (!js_DefineProperty(cx, fun->object,

                                           oldArgId, JSVAL_VOID,

                                           js_GetArgument, js_SetArgument,

                                           JSPROP_ENUMERATE | JSPROP_PERMANENT,

                                           (JSProperty **)&sprop)) {

                        return NULL;

                    }

                    sprop->id = (jsid) argAtom;

                }

            }

            if (sprop) {

                OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);

                sprop = NULL;

            }

            if (!js_DefineProperty(cx, fun->object,

                                   (jsid)argAtom, JSVAL_VOID,

                                   js_GetArgument, js_SetArgument,

                                   JSPROP_ENUMERATE | JSPROP_PERMANENT,

                                   (JSProperty **)&sprop)) {

                return NULL;

            }

            JS_ASSERT(sprop);

            sprop->id = INT_TO_JSVAL(fun->nargs++);

            OBJ_DROP_PROPERTY(cx, fun->object, (JSProperty *)sprop);

        } while (js_MatchToken(cx, ts, TOK_COMMA));



        MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);

    }



    MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);

    pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;



    TREE_CONTEXT_INIT(&funtc);

    body = FunctionBody(cx, ts, fun, &funtc);

    if (!body)

        return NULL;



    MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);

    pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;



#if JS_HAS_LEXICAL_CLOSURE

    /*

     * If we collected flags that indicate nested heavyweight functions, or

     * this function contains heavyweight-making statements (references to

     * __parent__ or __proto__; use of with, eval, import, or export; and

     * assignment to arguments), flag the function as heavyweight (requiring

     * a call object per invocation).

     */

    if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {

        fun->flags |= JSFUN_HEAVYWEIGHT;

        tc->flags |= TCF_FUN_HEAVYWEIGHT;

    } else {

        /*

         * If this function is a named statement function not at top-level

         * (i.e. a JSOP_CLOSURE), or if it refers to unqualified names that

         * are not local args or vars (TCF_FUN_USES_NONLOCALS), then our

         * enclosing function, if any, must be heavyweight.

         */

        if ((!lambda && funAtom && tc->topStmt) ||

            (funtc.flags & TCF_FUN_USES_NONLOCALS)) {

            tc->flags |= TCF_FUN_HEAVYWEIGHT;

        }

    }

#endif



    /*

     * Record names for function statements in tc->decls so we know when to

     * avoid optimizing variable references that might name a function.

     */

    if (!lambda && funAtom) {

        ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);

        if (ale) {

            prevop = ALE_JSOP(ale);

            if ((JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) &&

                !js_ReportCompileErrorNumber(cx, ts, NULL,

                                             (prevop != JSOP_DEFCONST)

                                             ? JSREPORT_WARNING|JSREPORT_STRICT

                                             : JSREPORT_ERROR,

                                             JSMSG_REDECLARED_VAR,

                                             (prevop == JSOP_DEFFUN ||

                                              prevop == JSOP_CLOSURE)

                                             ? js_function_str

                                             : (prevop == JSOP_DEFCONST)

                                             ? js_const_str

                                             : js_var_str,

                                             ATOM_BYTES(funAtom))) {

                return NULL;

            }

            if (tc->topStmt && prevop == JSOP_DEFVAR)

                tc->flags |= TCF_FUN_CLOSURE_VS_VAR;

        } else {

            ale = js_IndexAtom(cx, funAtom, &tc->decls);

            if (!ale)

                return NULL;

        }

        ALE_SET_JSOP(ale, tc->topStmt ? JSOP_CLOSURE : JSOP_DEFFUN);



#if JS_HAS_LEXICAL_CLOSURE

        /*

         * A function nested at top level inside another's body needs only a

         * local variable to bind its name to its value, and not an activation

         * object property (it might also need the activation property, if the

         * outer function contains with statements, e.g., but the stack slot

         * wins when jsemit.c's LookupArgOrVar can optimize a JSOP_NAME into a

         * JSOP_GETVAR bytecode).

         */

        if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) {

            JSStackFrame *fp;

            JSObject *varobj;



            /*

             * Define a property on the outer function so that LookupArgOrVar

             * can properly optimize accesses.

             *

             * XXX Here and in Variables, we use the function object's scope,

             * XXX arguably polluting it, when we could use a compiler-private

             * XXX scope structure.  Tradition!

             */

            fp = cx->fp;

            varobj = fp->varobj;

            JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass);

            JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj));

            if (!js_DefineProperty(cx, varobj, (jsid)funAtom,

                                   OBJECT_TO_JSVAL(fun->object),

                                   js_GetLocalVariable, js_SetLocalVariable,

                                   JSPROP_ENUMERATE,

                                   (JSProperty **)&sprop)) {

                return NULL;

            }



            /* Allocate a slot for this property. */

            sprop->id = INT_TO_JSVAL(fp->fun->nvars++);

            OBJ_DROP_PROPERTY(cx, varobj, (JSProperty *)sprop);

        }





		// DREAMWEAVER Adobe change - jschang 6/1/01

		// if the containing object has the special property - MM_defineFunctionsOnCompile 

		// then define the property now - this is because we need to compile scripts 

		// in the document, so we can execute some of the functions selectively later on

		// - JavaScript 1.2 did this autmoatically, but the JavaScript 1.5 only defines the 

		// property when you execute the script that contains the function 

		// definition, but we can't just execute any old script, we only execute 

		// specific behavior functions that we define and have control over.

		//

		// The following code that defines the property is copied and modified from 

		// jsinterp.c, js_Interpret, case JSOP_DEFFUN:

		else {

            JSStackFrame *fp;

            JSObject *varobj;

			JSBool ok, cond;

			jsval specialPropVal;

            uintN flags, attrs;



            fp = cx->fp;

            varobj = fp->varobj;

			

			// check the special property

			if ( JS_GetProperty(cx, varobj, "MM_defineFunctionsOnCompile", &specialPropVal) && 

					JS_TRUE == JSVAL_TO_BOOLEAN(specialPropVal) )

			{

				/*

				 * We must be at top-level (default "box", either function body or

				 * global) scope, not inside a with or other compound statement 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 variables object from

				 * scope chain head.  Hence the two assignments to parent 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.

				 */

				/***** jschang - this is in js_Execute: I don't think we need it here

				parent = fp->scopeChain;

				if (OBJ_GET_PARENT(cx, obj) != parent) {

					obj = js_CloneFunctionObject(cx, obj, parent);

					if (!obj) {

						ok = JS_FALSE;

						goto out;

					}

				}

				*****/



				/* Load function flags that are also property attributes. */

				flags = fun->flags & (JSFUN_GETTER | JSFUN_SETTER);

				attrs = flags | JSPROP_ENUMERATE;



				/*

				 * 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;

				if (js_CheckRedeclaration(cx, parent, (jsid)funAtom, attrs, &cond))

				{

					ok = OBJ_DEFINE_PROPERTY(cx, parent, (jsid)funAtom,

											 OBJECT_TO_JSVAL(fun->object),

											 (flags & JSFUN_GETTER)

											 ? (JSPropertyOp) fun->object

											 : NULL,

											 (flags & JSFUN_SETTER)

											 ? (JSPropertyOp) fun->object

											 : NULL,

											 attrs,

											 NULL);

				}

			}

		}

#endif

    }



#if JS_HAS_LEXICAL_CLOSURE

    if (lambda || !funAtom) {

        /*

         * ECMA ed. 3 standard: function expression, possibly anonymous (even

         * if at top-level, an unnamed function is an expression statement, not

         * a function declaration).

         */

        op = fun->atom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;

    } else if (tc->topStmt) {

        /*

         * ECMA ed. 3 extension: a function expression statement not at the

         * top level, e.g., in a compound statement such as the "then" part

         * of an "if" statement, binds a closure only if control reaches that

         * sub-statement.

         */

        op = JSOP_CLOSURE;

    } else

#endif

        op = JSOP_NOP;



    pn->pn_op = op;

    pn->pn_fun = fun;

    pn->pn_body = body;

    pn->pn_flags = funtc.flags & TCF_FUN_FLAGS;

    pn->pn_tryCount = funtc.tryCount;

    TREE_CONTEXT_FINISH(&funtc);

    return pn;

}



static JSParseNode *

FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    return FunctionDef(cx, ts, tc, JS_FALSE);

}



#if JS_HAS_LEXICAL_CLOSURE

static JSParseNode *

FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    return FunctionDef(cx, ts, tc, JS_TRUE);

}

#endif



/*

 * Parse the statements in a block, creating a TOK_LC node that lists the

 * statements' trees.  If called from block-parsing code, the caller must

 * match { before and } after.

 */

static JSParseNode *

Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn, *pn2;

    JSTokenType tt;



    pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);

    if (!pn)

        return NULL;

    PN_INIT_LIST(pn);



    ts->flags |= TSF_REGEXP;

    while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {

        ts->flags &= ~TSF_REGEXP;

        pn2 = Statement(cx, ts, tc);

        if (!pn2)

            return NULL;

        ts->flags |= TSF_REGEXP;



        /* If compiling top-level statements, emit as we go to save space. */

        if (!tc->topStmt && (tc->flags & TCF_COMPILING)) {

            if (cx->fp->fun &&

                JS_HAS_STRICT_OPTION(cx) &&

                (tc->flags & TCF_RETURN_EXPR)) {

                /*

                 * Check pn2 for lack of a final return statement if it is the

                 * last statement in the block.

                 */

                tt = js_PeekToken(cx, ts);

                if ((tt == TOK_EOF || tt == TOK_RC) &&

                    !CheckFinalReturn(cx, ts, pn2)) {

                    tt = TOK_ERROR;

                    break;

                }



                /*

                 * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to

                 * CheckFinalReturn again.

                 */

                tc->flags &= ~TCF_RETURN_EXPR;

            }

            if (!js_FoldConstants(cx, pn2, tc) ||

                !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||

                !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {

                tt = TOK_ERROR;

                break;

            }

            RecycleTree(pn2, tc);

        } else {

            PN_APPEND(pn, pn2);

        }

    }

    ts->flags &= ~TSF_REGEXP;

    if (tt == TOK_ERROR)

        return NULL;



    pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;

    return pn;

}



static JSParseNode *

Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn, *pn2;



    MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);

    pn = Expr(cx, ts, tc);

    if (!pn)

        return NULL;

    MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);



    /*

     * Check for (a = b) and "correct" it to (a == b).

     * XXX not ECMA, but documented in several books -- need a compile option

     */

    if (pn->pn_type == TOK_ASSIGN && pn->pn_op == JSOP_NOP) {

        JSBool rewrite = JS_HAS_STRICT_OPTION(cx) ||

                         !JSVERSION_IS_ECMA(cx->version);



        // DREAMWEAVER dgeorge 4/23/02: there are lots of Dreamweaver

        // extensions, such as the "Javascript Integration Kit for

        // Flash 5", which will break if we stop treating

        // a single equals sign as equality.  Therefore, I'm forcing

        // rewrite to be true.  Also see the code in CompileOrExecScript

        // (in JSInterp.cpp) to handle this case.

        rewrite = JS_TRUE;



        if (!js_ReportCompileErrorNumber(cx, ts, NULL,

                                         JSREPORT_WARNING | JSREPORT_STRICT,

                                         JSMSG_EQUAL_AS_ASSIGN,

                                         rewrite

                                         ? "\nAssuming equality test"

                                         : "")) {

            return NULL;

        }

        if (rewrite) {

            pn->pn_type = TOK_EQOP;

            pn->pn_op = (JSOp)cx->jsop_eq;

            pn2 = pn->pn_left;

            switch (pn2->pn_op) {

              case JSOP_SETNAME:

                pn2->pn_op = JSOP_NAME;

                break;

              case JSOP_SETPROP:

                pn2->pn_op = JSOP_GETPROP;

                break;

              case JSOP_SETELEM:

                pn2->pn_op = JSOP_GETELEM;

                break;

              default:

                JS_ASSERT(0);

            }

        }

    }

    return pn;

}



static JSBool

MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)

{

    JSAtom *label;

#if JS_HAS_LABEL_STATEMENT

    JSTokenType tt;



    tt = js_PeekTokenSameLine(cx, ts);

    if (tt == TOK_ERROR)

        return JS_FALSE;

    if (tt == TOK_NAME) {

        (void) js_GetToken(cx, ts);

        label = CURRENT_TOKEN(ts).t_atom;

    } else {

        label = NULL;

    }

#else

    label = NULL;

#endif

    pn->pn_atom = label;

    if (pn->pn_pos.end.lineno == ts->lineno)

        return WellTerminated(cx, ts, TOK_ERROR);

    return JS_TRUE;

}



#if JS_HAS_EXPORT_IMPORT

static JSParseNode *

ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn, *pn2, *pn3;

    JSTokenType tt;



    MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);

    pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);

    if (!pn)

        return NULL;

    pn->pn_op = JSOP_NAME;

    pn->pn_atom = CURRENT_TOKEN(ts).t_atom;

    pn->pn_expr = NULL;

    pn->pn_slot = -1;

    pn->pn_attrs = 0;



    ts->flags |= TSF_REGEXP;

    while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {

        ts->flags &= ~TSF_REGEXP;

        if (pn->pn_op == JSOP_IMPORTALL)

            goto bad_import;



        if (tt == TOK_DOT) {

            pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);

            if (!pn2)

                return NULL;

            if (js_MatchToken(cx, ts, TOK_STAR)) {

                pn2->pn_op = JSOP_IMPORTALL;

                pn2->pn_atom = NULL;

            } else {

                MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);

                pn2->pn_op = JSOP_GETPROP;

                pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;

                pn2->pn_slot = -1;

                pn2->pn_attrs = 0;

            }

            pn2->pn_expr = pn;

            pn2->pn_pos.begin = pn->pn_pos.begin;

            pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;

        } else {

            /* Make a TOK_LB node. */

            pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);

            if (!pn2)

                return NULL;

            pn3 = Expr(cx, ts, tc);

            if (!pn3)

                return NULL;



            MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);

            pn2->pn_pos.begin = pn->pn_pos.begin;

            pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;



            pn2->pn_op = JSOP_GETELEM;

            pn2->pn_left = pn;

            pn2->pn_right = pn3;

        }



        pn = pn2;

        ts->flags |= TSF_REGEXP;

    }

    ts->flags &= ~TSF_REGEXP;

    if (tt == TOK_ERROR)

        return NULL;

    js_UngetToken(ts);



    switch (pn->pn_op) {

      case JSOP_GETPROP:

        pn->pn_op = JSOP_IMPORTPROP;

        break;

      case JSOP_GETELEM:

        pn->pn_op = JSOP_IMPORTELEM;

        break;

      case JSOP_IMPORTALL:

        break;

      default:

        goto bad_import;

    }

    return pn;



  bad_import:

    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_BAD_IMPORT);

    return NULL;

}

#endif /* JS_HAS_EXPORT_IMPORT */



extern const char js_with_statement_str[];



static JSParseNode *

Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSTokenType tt, lastExprType;

    JSParseNode *pn, *pn1, *pn2, *pn3, *pn4;

    JSStmtInfo stmtInfo, *stmt, *stmt2;

    JSAtom *label;



    ts->flags |= TSF_REGEXP;

    tt = js_GetToken(cx, ts);

    ts->flags &= ~TSF_REGEXP;



#if JS_HAS_GETTER_SETTER

    if (tt == TOK_NAME) {

        tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);

        if (tt == TOK_ERROR)

            return NULL;

    }

#endif



    switch (tt) {

#if JS_HAS_EXPORT_IMPORT

      case TOK_EXPORT:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);

        if (!pn)

            return NULL;

        PN_INIT_LIST(pn);

        if (js_MatchToken(cx, ts, TOK_STAR)) {

            pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);

            if (!pn2)

                return NULL;

            PN_APPEND(pn, pn2);

        } else {

            do {

                MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME);

                pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);

                if (!pn2)

                    return NULL;

                pn2->pn_op = JSOP_NAME;

                pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;

                pn2->pn_expr = NULL;

                pn2->pn_slot = -1;

                pn2->pn_attrs = 0;

                PN_APPEND(pn, pn2);

            } while (js_MatchToken(cx, ts, TOK_COMMA));

        }

        pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;

        if (pn->pn_pos.end.lineno == ts->lineno &&

            !WellTerminated(cx, ts, TOK_ERROR)) {

            return NULL;

        }

        tc->flags |= TCF_FUN_HEAVYWEIGHT;

        break;



      case TOK_IMPORT:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);

        if (!pn)

            return NULL;

        PN_INIT_LIST(pn);

        do {

            pn2 = ImportExpr(cx, ts, tc);

            if (!pn2)

                return NULL;

            PN_APPEND(pn, pn2);

        } while (js_MatchToken(cx, ts, TOK_COMMA));

        pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;

        if (pn->pn_pos.end.lineno == ts->lineno &&

            !WellTerminated(cx, ts, TOK_ERROR)) {

            return NULL;

        }

        tc->flags |= TCF_FUN_HEAVYWEIGHT;

        break;

#endif /* JS_HAS_EXPORT_IMPORT */



      case TOK_FUNCTION:

        pn = FunctionStmt(cx, ts, tc);

        if (!pn)

            return NULL;

        if (pn->pn_pos.end.lineno == ts->lineno &&

            !WellTerminated(cx, ts, TOK_FUNCTION)) {

            return NULL;

        }

        break;



      case TOK_IF:

        /* An IF node has three kids: condition, then, and optional else. */

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);

        if (!pn)

            return NULL;

        pn1 = Condition(cx, ts, tc);

        if (!pn1)

            return NULL;

        js_PushStatement(tc, &stmtInfo, STMT_IF, -1);

        pn2 = Statement(cx, ts, tc);

        if (!pn2)

            return NULL;

        if (js_MatchToken(cx, ts, TOK_ELSE)) {

            stmtInfo.type = STMT_ELSE;

            pn3 = Statement(cx, ts, tc);

            if (!pn3)

                return NULL;

            pn->pn_pos.end = pn3->pn_pos.end;

        } else {

            pn3 = NULL;

            pn->pn_pos.end = pn2->pn_pos.end;

        }

        js_PopStatement(tc);

        pn->pn_kid1 = pn1;

        pn->pn_kid2 = pn2;

        pn->pn_kid3 = pn3;

        return pn;



#if JS_HAS_SWITCH_STATEMENT

      case TOK_SWITCH:

      {

        JSParseNode *pn5;

        JSBool seenDefault = JS_FALSE;



        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);

        if (!pn)

            return NULL;

        MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH);



        /* pn1 points to the switch's discriminant. */

        pn1 = Expr(cx, ts, tc);

        if (!pn1)

            return NULL;



        MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH);

        MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH);



        /* pn2 is a list of case nodes. The default case has pn_left == NULL */

        pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);

        if (!pn2)

            return NULL;

        PN_INIT_LIST(pn2);



        js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1);



        while ((tt = js_GetToken(cx, ts)) != TOK_RC) {

            switch (tt) {

              case TOK_DEFAULT:

                if (seenDefault) {

                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                                JSMSG_TOO_MANY_DEFAULTS);

                    return NULL;

                }

                seenDefault = JS_TRUE;

                /* fall through */



              case TOK_CASE:

                pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);

                if (!pn3)

                    return NULL;

                if (tt == TOK_DEFAULT) {

                    pn3->pn_left = NULL;

                } else {

                    pn3->pn_left = Expr(cx, ts, tc);

                    if (!pn3->pn_left)

                        return NULL;

                }

                PN_APPEND(pn2, pn3);

                if (pn2->pn_count == JS_BIT(16)) {

                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                                JSMSG_TOO_MANY_CASES);

                    return NULL;

                }

                break;



              case TOK_ERROR:

                return NULL;



              default:

                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                            JSMSG_BAD_SWITCH);

                return NULL;

            }

            MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);



            pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);

            if (!pn4)

                return NULL;

            pn4->pn_type = TOK_LC;

            PN_INIT_LIST(pn4);

            while ((tt = js_PeekToken(cx, ts)) != TOK_RC &&

                    tt != TOK_CASE && tt != TOK_DEFAULT) {

                if (tt == TOK_ERROR)

                    return NULL;

                pn5 = Statement(cx, ts, tc);

                if (!pn5)

                    return NULL;

                pn4->pn_pos.end = pn5->pn_pos.end;

                PN_APPEND(pn4, pn5);

            }

            pn3->pn_pos.end = pn4->pn_pos.end;

            pn3->pn_right = pn4;

        }



        js_PopStatement(tc);



        pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;

        pn->pn_kid1 = pn1;

        pn->pn_kid2 = pn2;

        return pn;

      }

#endif /* JS_HAS_SWITCH_STATEMENT */



      case TOK_WHILE:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);

        if (!pn)

            return NULL;

        js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1);

        pn2 = Condition(cx, ts, tc);

        if (!pn2)

            return NULL;

        pn->pn_left = pn2;

        pn2 = Statement(cx, ts, tc);

        if (!pn2)

            return NULL;

        js_PopStatement(tc);

        pn->pn_pos.end = pn2->pn_pos.end;

        pn->pn_right = pn2;

        return pn;



#if JS_HAS_DO_WHILE_LOOP

      case TOK_DO:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);

        if (!pn)

            return NULL;

        js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1);

        pn2 = Statement(cx, ts, tc);

        if (!pn2)

            return NULL;

        pn->pn_left = pn2;

        MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO);

        pn2 = Condition(cx, ts, tc);

        if (!pn2)

            return NULL;

        js_PopStatement(tc);

        pn->pn_pos.end = pn2->pn_pos.end;

        pn->pn_right = pn2;

        break;

#endif /* JS_HAS_DO_WHILE_LOOP */



      case TOK_FOR:

        /* A FOR node is binary, left is loop control and right is the body. */

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);

        if (!pn)

            return NULL;

        js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);



        MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);

        tt = js_PeekToken(cx, ts);

        if (tt == TOK_SEMI) {

            /* No initializer -- set first kid of left sub-node to null. */

            pn1 = NULL;

        } else {

            /* Set pn1 to a var list or an initializing expression. */

#if JS_HAS_IN_OPERATOR

            /*

             * Set the TCF_IN_FOR_INIT flag during parsing of the first clause

             * of the for statement.  This flag will be used by the RelExpr

             * production; if it is set, then the 'in' keyword will not be

             * recognized as an operator, leaving it available to be parsed as

             * part of a for/in loop.  A side effect of this restriction is

             * that (unparenthesized) expressions involving an 'in' operator

             * are illegal in the init clause of an ordinary for loop.

             */

            tc->flags |= TCF_IN_FOR_INIT;

#endif /* JS_HAS_IN_OPERATOR */

            if (tt == TOK_VAR) {

                (void) js_GetToken(cx, ts);

                pn1 = Variables(cx, ts, tc);

            } else {

                pn1 = Expr(cx, ts, tc);

            }

#if JS_HAS_IN_OPERATOR

            tc->flags &= ~TCF_IN_FOR_INIT;

#endif /* JS_HAS_IN_OPERATOR */

            if (!pn1)

                return NULL;

        }



        /*

         * We can be sure that it's a for/in loop if there's still an 'in'

         * keyword here, even if JavaScript recognizes 'in' as an operator,

         * as we've excluded 'in' from being parsed in RelExpr by setting

         * the TCF_IN_FOR_INIT flag in our JSTreeContext.

         */

        if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {

            stmtInfo.type = STMT_FOR_IN_LOOP;



            /* Check that the left side of the 'in' is valid. */

            if ((pn1->pn_type == TOK_VAR)

                ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST)

                : (pn1->pn_type != TOK_NAME &&

                   pn1->pn_type != TOK_DOT &&

                   pn1->pn_type != TOK_LB)) {

                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                            JSMSG_BAD_FOR_LEFTSIDE);

                return NULL;

            }



            /* Beware 'for (arguments in ...)' with or without a 'var'. */

            pn2 = (pn1->pn_type == TOK_VAR) ? pn1->pn_head : pn1;

            if (pn2->pn_type == TOK_NAME &&

                pn2->pn_atom == cx->runtime->atomState.argumentsAtom) {

                tc->flags |= TCF_FUN_HEAVYWEIGHT;

            }



            /* Parse the object expression as the right operand of 'in'. */

            pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);

            if (!pn2)

                return NULL;

            pn->pn_left = pn2;

        } else {

            /* Parse the loop condition or null into pn2. */

            MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);

            if (js_PeekToken(cx, ts) == TOK_SEMI) {

                pn2 = NULL;

            } else {

                pn2 = Expr(cx, ts, tc);

                if (!pn2)

                    return NULL;

            }



            /* Parse the update expression or null into pn3. */

            MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);

            if (js_PeekToken(cx, ts) == TOK_RP) {

                pn3 = NULL;

            } else {

                pn3 = Expr(cx, ts, tc);

                if (!pn3)

                    return NULL;

            }



            /* Build the RESERVED node to use as the left kid of pn. */

            pn4 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);

            if (!pn4)

                return NULL;

            pn4->pn_type = TOK_RESERVED;

            pn4->pn_kid1 = pn1;

            pn4->pn_kid2 = pn2;

            pn4->pn_kid3 = pn3;

            pn->pn_left = pn4;

        }



        MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);



        /* Parse the loop body into pn->pn_right. */

        pn2 = Statement(cx, ts, tc);

        if (!pn2)

            return NULL;

        pn->pn_right = pn2;

        js_PopStatement(tc);



        /* Record the absolute line number for source note emission. */

        pn->pn_pos.end = pn2->pn_pos.end;

        return pn;



#if JS_HAS_EXCEPTIONS

      case TOK_TRY: {

        JSParseNode *catchtail = NULL;

        /*

         * try nodes are ternary.

         * kid1 is the try Statement

         * kid2 is the catch node

         * kid3 is the finally Statement

         *

         * catch nodes are ternary.

         * kid1 is the discriminant

         * kid2 is the next catch node, or NULL

         * kid3 is the catch block (on kid3 so that we can always append a

         *                          new catch pn on catchtail->kid2)

         *

         * catch discriminant nodes are binary

         * atom is the receptacle

         * expr is the discriminant code

         *

         * finally nodes are unary (just the finally expression)

         */

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);

        pn->pn_op = JSOP_NOP;



        MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);

        js_PushStatement(tc, &stmtInfo, STMT_TRY, -1);

        pn->pn_kid1 = Statements(cx, ts, tc);

        if (!pn->pn_kid1)

            return NULL;

        MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);

        js_PopStatement(tc);



        catchtail = pn;

        while (js_PeekToken(cx, ts) == TOK_CATCH) {

            /* check for another catch after unconditional catch */

            if (catchtail != pn && !catchtail->pn_kid1->pn_expr) {

                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                            JSMSG_CATCH_AFTER_GENERAL);

                return NULL;

            }



            /*

             * legal catch forms are:

             * catch (v)

             * catch (v if <boolean_expression>)

             * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD)

             */

            (void) js_GetToken(cx, ts); /* eat `catch' */

            pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);

            if (!pn2)

                return NULL;



            /*

             * We use a PN_NAME for the discriminant (catchguard) node

             * with the actual discriminant code in the initializer spot

             */

            MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);

            MUST_MATCH_TOKEN(TOK_NAME, JSMSG_CATCH_IDENTIFIER);

            pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);

            if (!pn3)

                return NULL;



            pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;

            pn3->pn_expr = NULL;

#if JS_HAS_CATCH_GUARD

            /*

             * We use `catch (x if x === 5)' (not `catch (x : x === 5)') to

             * avoid conflicting with the JS2/ECMA2 proposed catchguard syntax.

             */

            if (js_PeekToken(cx, ts) == TOK_IF) {

                (void)js_GetToken(cx, ts); /* eat `if' */

                pn3->pn_expr = Expr(cx, ts, tc);

                if (!pn3->pn_expr)

                    return NULL;

            }

#endif

            pn2->pn_kid1 = pn3;



            MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);



            MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);

            js_PushStatement(tc, &stmtInfo, STMT_CATCH, -1);

            stmtInfo.label = pn3->pn_atom;

            pn2->pn_kid3 = Statements(cx, ts, tc);

            if (!pn2->pn_kid3)

                return NULL;

            MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);

            js_PopStatement(tc);



            catchtail = catchtail->pn_kid2 = pn2;

        }

        catchtail->pn_kid2 = NULL;



        if (js_MatchToken(cx, ts, TOK_FINALLY)) {

            tc->tryCount++;

            MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);

            js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);

            pn->pn_kid3 = Statements(cx, ts, tc);

            if (!pn->pn_kid3)

                return NULL;

            MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY);

            js_PopStatement(tc);

        } else {

            pn->pn_kid3 = NULL;

        }

        if (!pn->pn_kid2 && !pn->pn_kid3) {

            js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                        JSMSG_CATCH_OR_FINALLY);

            return NULL;

        }

        tc->tryCount++;

        return pn;

      }



      case TOK_THROW:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);

        if (!pn)

            return NULL;

        pn2 = Expr(cx, ts, tc);

        if (!pn2)

            return NULL;

        pn->pn_pos.end = pn2->pn_pos.end;

        if (pn->pn_pos.end.lineno == ts->lineno &&

            !WellTerminated(cx, ts, TOK_ERROR)) {

            return NULL;

        }

        pn->pn_op = JSOP_THROW;

        pn->pn_kid = pn2;

        break;



      /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */

      case TOK_CATCH:

        js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                    JSMSG_CATCH_WITHOUT_TRY);

        return NULL;



      case TOK_FINALLY:

        js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                    JSMSG_FINALLY_WITHOUT_TRY);

        return NULL;



#endif /* JS_HAS_EXCEPTIONS */



      case TOK_BREAK:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);

        if (!pn)

            return NULL;

        if (!MatchLabel(cx, ts, pn))

            return NULL;

        stmt = tc->topStmt;

        label = pn->pn_atom;

        if (label) {

            for (; ; stmt = stmt->down) {

                if (!stmt) {

                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                                JSMSG_LABEL_NOT_FOUND);

                    return NULL;

                }

                if (stmt->type == STMT_LABEL && stmt->label == label)

                    break;

            }

        } else {

            for (; ; stmt = stmt->down) {

                if (!stmt) {

                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                                JSMSG_TOUGH_BREAK);

                    return NULL;

                }

                if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH)

                    break;

            }

        }

        if (label)

            pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;

        break;



      case TOK_CONTINUE:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);

        if (!pn)

            return NULL;

        if (!MatchLabel(cx, ts, pn))

            return NULL;

        stmt = tc->topStmt;

        label = pn->pn_atom;

        if (label) {

            for (stmt2 = NULL; ; stmt = stmt->down) {

                if (!stmt) {

                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                                JSMSG_LABEL_NOT_FOUND);

                    return NULL;

                }

                if (stmt->type == STMT_LABEL) {

                    if (stmt->label == label) {

                        if (!stmt2 || !STMT_IS_LOOP(stmt2)) {

                            js_ReportCompileErrorNumber(cx, ts, NULL,

                                                        JSREPORT_ERROR,

                                                        JSMSG_BAD_CONTINUE);

                            return NULL;

                        }

                        break;

                    }

                } else {

                    stmt2 = stmt;

                }

            }

        } else {

            for (; ; stmt = stmt->down) {

                if (!stmt) {

                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                                JSMSG_BAD_CONTINUE);

                    return NULL;

                }

                if (STMT_IS_LOOP(stmt))

                    break;

            }

        }

        if (label)

            pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;

        break;



      case TOK_WITH:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);

        if (!pn)

            return NULL;

        MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);

        pn2 = Expr(cx, ts, tc);

        if (!pn2)

            return NULL;

        MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH);

        pn->pn_left = pn2;



        js_PushStatement(tc, &stmtInfo, STMT_WITH, -1);

        pn2 = Statement(cx, ts, tc);

        if (!pn2)

            return NULL;

        js_PopStatement(tc);



        /* Deprecate after parsing, in case of WERROR option. */

        if (JS_HAS_STRICT_OPTION(cx)) {

            if (!js_ReportCompileErrorNumber(cx, ts, NULL,

                                             JSREPORT_WARNING | JSREPORT_STRICT,

                                             JSMSG_DEPRECATED_USAGE,

                                             js_with_statement_str)) {

                return NULL;

            }

        }



        pn->pn_pos.end = pn2->pn_pos.end;

        pn->pn_right = pn2;

        tc->flags |= TCF_FUN_HEAVYWEIGHT;

        return pn;



      case TOK_VAR:

        pn = Variables(cx, ts, tc);

        if (!pn)

            return NULL;

        if (pn->pn_pos.end.lineno == ts->lineno &&

            !WellTerminated(cx, ts, TOK_ERROR)) {

            return NULL;

        }

        /* Tell js_EmitTree to generate a final POP. */

        pn->pn_extra = JS_TRUE;

        break;



      case TOK_RETURN:

        if (!(tc->flags & TCF_IN_FUNCTION)) {

            js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                        JSMSG_BAD_RETURN);

            return NULL;

        }

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);

        if (!pn)

            return NULL;



        /* This is ugly, but we don't want to require a semicolon. */

        ts->flags |= TSF_REGEXP;

        tt = js_PeekTokenSameLine(cx, ts);

        ts->flags &= ~TSF_REGEXP;

        if (tt == TOK_ERROR)

            return NULL;



        if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {

            pn2 = Expr(cx, ts, tc);

            if (!pn2)

                return NULL;

            if (pn2->pn_pos.end.lineno == ts->lineno &&

                !WellTerminated(cx, ts, TOK_ERROR)) {

                return NULL;

            }

            tc->flags |= TCF_RETURN_EXPR;

            pn->pn_pos.end = pn2->pn_pos.end;

            pn->pn_kid = pn2;

        } else {

            tc->flags |= TCF_RETURN_VOID;

            pn->pn_kid = NULL;

        }



        if (JS_HAS_STRICT_OPTION(cx) &&

            (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0) {

            /*

             * We must be in a frame with a non-native function, because

             * we're compiling one.

             */

            if (!ReportNoReturnValue(cx, ts))

                return NULL;

        }

        break;



      case TOK_LC:

        js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);

        pn = Statements(cx, ts, tc);

        if (!pn)

            return NULL;



        MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND);

        js_PopStatement(tc);

        return pn;



      case TOK_EOL:

      case TOK_SEMI:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);

        if (!pn)

            return NULL;

        pn->pn_type = TOK_SEMI;

        pn->pn_kid = NULL;

        return pn;



#if JS_HAS_DEBUGGER_KEYWORD

      case TOK_DEBUGGER:

        if (!WellTerminated(cx, ts, TOK_ERROR))

            return NULL;

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);

        if (!pn)

            return NULL;

        pn->pn_type = TOK_DEBUGGER;

        tc->flags |= TCF_FUN_HEAVYWEIGHT;

        return pn;

#endif /* JS_HAS_DEBUGGER_KEYWORD */



      case TOK_ERROR:

        return NULL;



      default:

        lastExprType = CURRENT_TOKEN(ts).type;

        js_UngetToken(ts);

        pn2 = Expr(cx, ts, tc);

        if (!pn2)

            return NULL;



        if (js_PeekToken(cx, ts) == TOK_COLON) {

            if (pn2->pn_type != TOK_NAME) {

                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                            JSMSG_BAD_LABEL);

                return NULL;

            }

            label = pn2->pn_atom;

            for (stmt = tc->topStmt; stmt; stmt = stmt->down) {

                if (stmt->type == STMT_LABEL && stmt->label == label) {

                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                                JSMSG_DUPLICATE_LABEL);

                    return NULL;

                }

            }

            (void) js_GetToken(cx, ts);



            /* Push a label struct and parse the statement. */

            js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1);

            stmtInfo.label = label;

            pn = Statement(cx, ts, tc);

            if (!pn)

                return NULL;



            /* Pop the label, set pn_expr, and return early. */

            js_PopStatement(tc);

            pn2->pn_type = TOK_COLON;

            pn2->pn_pos.end = pn->pn_pos.end;

            pn2->pn_expr = pn;

            return pn2;

        }



        /* Check termination of (possibly multi-line) function expression. */

        if (pn2->pn_pos.end.lineno == ts->lineno &&

            !WellTerminated(cx, ts, lastExprType)) {

            return NULL;

        }



        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);

        if (!pn)

            return NULL;

        pn->pn_type = TOK_SEMI;

        pn->pn_pos = pn2->pn_pos;

        pn->pn_kid = pn2;

        break;

    }



    (void) js_MatchToken(cx, ts, TOK_SEMI);

    return pn;

}



static JSParseNode *

Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn, *pn2;

    JSObject *obj, *pobj;

    JSStackFrame *fp;

    JSFunction *fun;

    JSClass *clasp;

    JSPropertyOp getter, setter, currentGetter, currentSetter;

    JSAtom *atom;

    JSAtomListElement *ale;

    JSOp prevop;

    JSProperty *prop;

    JSScopeProperty *sprop;

    JSBool ok;



    /*

     * The tricky part of this code is to create special parsenode opcodes for

     * getting and setting variables (which will be stored as special slots in

     * the frame).  The complex special case is an eval() inside a function.

     * If the evaluated string references variables in the enclosing function,

     * then we need to generate the special variable opcodes.  We determine

     * this by looking up the variable id in the current variable scope.

     */

    JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_VAR);

    pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);

    if (!pn)

        return NULL;

    pn->pn_op = CURRENT_TOKEN(ts).t_op;

    pn->pn_extra = JS_FALSE;            /* assume no JSOP_POP needed */

    PN_INIT_LIST(pn);



    /*

     * Skip eval and debugger frames when looking for the function whose code

     * is being compiled.  If we are called from FunctionBody, TCF_IN_FUNCTION

     * will be set in tc->flags, and we can be sure fp->fun is the function to

     * use.  But if a function calls eval, the string argument is treated as a

     * Program (per ECMA), so TCF_IN_FUNCTION won't be set.

     *

     * What's more, when the following code is reached from eval, cx->fp->fun

     * is eval's JSFunction (a native function), so we need to skip its frame.

     * We should find the scripted caller's function frame just below it, but

     * we code a loop out of paranoia.

     */

    for (fp = cx->fp; fp->special && fp->down; fp = fp->down)

        continue;

    obj = fp->varobj;

    fun = fp->fun;

    clasp = OBJ_GET_CLASS(cx, obj);

    if (fun && clasp == &js_FunctionClass) {

        /* We are compiling code inside a function */

        getter = js_GetLocalVariable;

        setter = js_SetLocalVariable;

    } else if (fun && clasp == &js_CallClass) {

        /* We are compiling code from an eval inside a function */

        getter = js_GetCallVariable;

        setter = js_SetCallVariable;

    } else {

        getter = clasp->getProperty;

        setter = clasp->setProperty;

    }



    ok = JS_TRUE;

    do {

        currentGetter = getter;

        currentSetter = setter;

        MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);

        atom = CURRENT_TOKEN(ts).t_atom;



        ATOM_LIST_SEARCH(ale, &tc->decls, atom);

        if (ale) {

            prevop = ALE_JSOP(ale);

            if ((JS_HAS_STRICT_OPTION(cx) ||

                 pn->pn_op == JSOP_DEFCONST ||

                 prevop == JSOP_DEFCONST) &&

                !js_ReportCompileErrorNumber(cx, ts, NULL,

                                             (pn->pn_op != JSOP_DEFCONST &&

                                              prevop != JSOP_DEFCONST)

                                             ? JSREPORT_WARNING|JSREPORT_STRICT

                                             : JSREPORT_ERROR,

                                             JSMSG_REDECLARED_VAR,

                                             (prevop == JSOP_DEFFUN ||

                                              prevop == JSOP_CLOSURE)

                                             ? js_function_str

                                             : (prevop == JSOP_DEFCONST)

                                             ? js_const_str

                                             : js_var_str,

                                             ATOM_BYTES(atom))) {

                return NULL;

            }

            if (pn->pn_op == JSOP_DEFVAR && prevop == JSOP_CLOSURE)

                tc->flags |= TCF_FUN_CLOSURE_VS_VAR;

        } else {

            ale = js_IndexAtom(cx, atom, &tc->decls);

            if (!ale)

                return NULL;

        }

        ALE_SET_JSOP(ale, pn->pn_op);



        pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);

        if (!pn2)

            return NULL;

        pn2->pn_op = JSOP_NAME;

        pn2->pn_atom = atom;

        pn2->pn_expr = NULL;

        pn2->pn_slot = -1;

        pn2->pn_attrs = (pn->pn_op == JSOP_DEFCONST)

                        ? JSPROP_ENUMERATE | JSPROP_PERMANENT |

                          JSPROP_READONLY

                        : JSPROP_ENUMERATE | JSPROP_PERMANENT;

        PN_APPEND(pn, pn2);



        if (!OBJ_LOOKUP_PROPERTY(cx, obj, (jsid)atom, &pobj, &prop))

            return NULL;

        if (pobj == obj &&

            OBJ_IS_NATIVE(pobj) &&

            (sprop = (JSScopeProperty *)prop) != NULL) {

            if (SPROP_GETTER(sprop, pobj) == js_GetArgument) {

                if (pn->pn_op == JSOP_DEFCONST) {

                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                                JSMSG_REDECLARED_PARAM,

                                                ATOM_BYTES(atom));

                    return NULL;

                }

                currentGetter = js_GetArgument;

                currentSetter = js_SetArgument;

                if (JS_HAS_STRICT_OPTION(cx)) {

                    ok = js_ReportCompileErrorNumber(cx, ts, NULL,

                                                     JSREPORT_WARNING |

                                                     JSREPORT_STRICT,

                                                     JSMSG_VAR_HIDES_ARG,

                                                     ATOM_BYTES(atom));

                }

            } else {

                ok = JS_TRUE;

                if (fun) {

                    /* Not an argument, must be a redeclared local var. */

                    if (clasp == &js_FunctionClass) {

                        JS_ASSERT(SPROP_GETTER(sprop,pobj) == js_GetLocalVariable);

                        JS_ASSERT(JSVAL_IS_INT(sprop->id) &&

                                  JSVAL_TO_INT(sprop->id) < fun->nvars);

                    } else if (clasp == &js_CallClass) {

                        if (SPROP_GETTER(sprop, pobj) == js_GetCallVariable) {

                            /*

                             * Referencing a variable introduced by a var

                             * statement in the enclosing function. Check

                             * that the slot number we have is in range.

                             */

                            JS_ASSERT(JSVAL_IS_INT(sprop->id) &&

                                      JSVAL_TO_INT(sprop->id) < fun->nvars);

                        } else {

                            /*

                             * A variable introduced through another eval:

                             * don't use the special getters and setters

                             * since we can't allocate a slot in the frame.

                             */

                            currentGetter = SPROP_GETTER(sprop, pobj);

                            currentSetter = SPROP_SETTER(sprop, pobj);

                        }

                    }



                    /* Override the old getter and setter, to handle eval. */

                    SPROP_GETTER(sprop, pobj) = currentGetter;

                    SPROP_SETTER(sprop, pobj) = currentSetter;

                } else {

                    /* Global var: (re-)set id a la js_DefineProperty. */

                    sprop->id = ATOM_KEY(atom);

                }

            }

        } else {

            /*

             * Property not found in current variable scope: we have not seen

             * this variable before.  Define a new local variable by adding a

             * property to the function's scope, allocating one slot in the

             * function's frame.  Global variables and any locals declared in

             * with statement bodies are handled at runtime, by script prolog

             * JSOP_DEFVAR bytecodes generated for slot-less vars.

             */

            sprop = NULL;

            if (prop) {

                OBJ_DROP_PROPERTY(cx, pobj, prop);

                prop = NULL;

            }

            if (currentGetter == js_GetCallVariable) {

                /* Can't increase fun->nvars in an active frame! */

                currentGetter = clasp->getProperty;

                currentSetter = clasp->setProperty;

            }

            if (currentGetter == js_GetLocalVariable &&

                atom != cx->runtime->atomState.argumentsAtom &&

                fp->scopeChain == obj &&

                !js_InWithStatement(tc)) {

                ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, JSVAL_VOID,

                                         currentGetter, currentSetter,

                                         pn2->pn_attrs, &prop);

                if (ok) {

                    pobj = obj;



                    /*

                     * Allocate more room for variables in the function's

                     * frame.  We can do this only before the function is

                     * first called.

                     */

                    sprop = (JSScopeProperty *)prop;

                    sprop->id = INT_TO_JSVAL(fun->nvars++);

                }

            }

        }



        if (js_MatchToken(cx, ts, TOK_ASSIGN)) {

            if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {

                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                            JSMSG_BAD_VAR_INIT);

                ok = JS_FALSE;

            } else {

                pn2->pn_expr = AssignExpr(cx, ts, tc);

                if (!pn2->pn_expr) {

                    ok = JS_FALSE;

                } else {

                    pn2->pn_op = (pn->pn_op == JSOP_DEFCONST)

                                 ? JSOP_SETCONST

                                 : JSOP_SETNAME;

                    if (atom == cx->runtime->atomState.argumentsAtom)

                        tc->flags |= TCF_FUN_HEAVYWEIGHT;

                }

            }

        }



        if (prop)

            OBJ_DROP_PROPERTY(cx, pobj, prop);

        if (!ok)

            return NULL;

    } while (js_MatchToken(cx, ts, TOK_COMMA));



    pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;

    return pn;

}



static JSParseNode *

Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn, *pn2;



    pn = AssignExpr(cx, ts, tc);

    if (pn && js_MatchToken(cx, ts, TOK_COMMA)) {

        pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);

        if (!pn2)

            return NULL;

        pn2->pn_pos.begin = pn->pn_pos.begin;

        PN_INIT_LIST_1(pn2, pn);

        pn = pn2;

        do {

            pn2 = AssignExpr(cx, ts, tc);

            if (!pn2)

                return NULL;

            PN_APPEND(pn, pn2);

        } while (js_MatchToken(cx, ts, TOK_COMMA));

        pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;

    }

    return pn;

}



static JSParseNode *

AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn, *pn2;

    JSTokenType tt;

    JSOp op;



    pn = CondExpr(cx, ts, tc);

    if (!pn)

        return NULL;



    tt = js_GetToken(cx, ts);

#if JS_HAS_GETTER_SETTER

    if (tt == TOK_NAME) {

        tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN);

        if (tt == TOK_ERROR)

            return NULL;

    }

#endif

    if (tt != TOK_ASSIGN) {

        js_UngetToken(ts);

        return pn;

    }



    op = CURRENT_TOKEN(ts).t_op;

    for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid)

        ;

    switch (pn2->pn_type) {

      case TOK_NAME:

        pn2->pn_op = JSOP_SETNAME;

        if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom)

            tc->flags |= TCF_FUN_HEAVYWEIGHT;

        break;

      case TOK_DOT:

        pn2->pn_op = JSOP_SETPROP;

        break;

      case TOK_LB:

        pn2->pn_op = JSOP_SETELEM;

        break;

#if JS_HAS_LVALUE_RETURN

      case TOK_LP:

        pn2->pn_op = JSOP_SETCALL;

        break;

#endif

      default:

        js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                    JSMSG_BAD_LEFTSIDE_OF_ASS);

        return NULL;

    }

    pn = NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc);

    return pn;

}



static JSParseNode *

CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn, *pn1, *pn2, *pn3;

#if JS_HAS_IN_OPERATOR

    uintN oldflags;

#endif /* JS_HAS_IN_OPERATOR */



    pn = OrExpr(cx, ts, tc);

    if (pn && js_MatchToken(cx, ts, TOK_HOOK)) {

        pn1 = pn;

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_TERNARY, tc);

        if (!pn)

            return NULL;

#if JS_HAS_IN_OPERATOR

        /*

         * Always accept the 'in' operator in the middle clause of a ternary,

         * where it's unambiguous, even if we might be parsing the init of a

         * for statement.

         */

        oldflags = tc->flags;

        tc->flags &= ~TCF_IN_FOR_INIT;

#endif /* JS_HAS_IN_OPERATOR */

        pn2 = AssignExpr(cx, ts, tc);

#if JS_HAS_IN_OPERATOR

        tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);

#endif /* JS_HAS_IN_OPERATOR */



        if (!pn2)

            return NULL;

        MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);

        pn3 = AssignExpr(cx, ts, tc);

        if (!pn3)

            return NULL;

        pn->pn_pos.begin = pn1->pn_pos.begin;

        pn->pn_pos.end = pn3->pn_pos.end;

        pn->pn_kid1 = pn1;

        pn->pn_kid2 = pn2;

        pn->pn_kid3 = pn3;

    }

    return pn;

}



static JSParseNode *

OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn;



    pn = AndExpr(cx, ts, tc);

    if (pn && js_MatchToken(cx, ts, TOK_OR))

        pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc);

    return pn;

}



static JSParseNode *

AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn;



    pn = BitOrExpr(cx, ts, tc);

    if (pn && js_MatchToken(cx, ts, TOK_AND))

        pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc);

    return pn;

}



static JSParseNode *

BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn;



    pn = BitXorExpr(cx, ts, tc);

    while (pn && js_MatchToken(cx, ts, TOK_BITOR)) {

        pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc),

                       tc);

    }

    return pn;

}



static JSParseNode *

BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn;



    pn = BitAndExpr(cx, ts, tc);

    while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) {

        pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc),

                       tc);

    }

    return pn;

}



static JSParseNode *

BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn;



    pn = EqExpr(cx, ts, tc);

    while (pn && js_MatchToken(cx, ts, TOK_BITAND))

        pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc);

    return pn;

}



static JSParseNode *

EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn;

    JSOp op;



    pn = RelExpr(cx, ts, tc);

    while (pn && js_MatchToken(cx, ts, TOK_EQOP)) {

        op = CURRENT_TOKEN(ts).t_op;

        pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc);

    }

    return pn;

}



static JSParseNode *

RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn;

    JSTokenType tt;

    JSOp op;

#if JS_HAS_IN_OPERATOR

    uintN inForInitFlag;



    inForInitFlag = tc->flags & TCF_IN_FOR_INIT;

    /*

     * Uses of the in operator in ShiftExprs are always unambiguous,

     * so unset the flag that prohibits recognizing it.

     */

    tc->flags &= ~TCF_IN_FOR_INIT;

#endif /* JS_HAS_IN_OPERATOR */



    pn = ShiftExpr(cx, ts, tc);

    while (pn &&

           (js_MatchToken(cx, ts, TOK_RELOP)

#if JS_HAS_IN_OPERATOR

            /*

             * Recognize the 'in' token as an operator only if we're not

             * currently in the init expr of a for loop.

             */

            || (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN))

#endif /* JS_HAS_IN_OPERATOR */

#if JS_HAS_INSTANCEOF

            || js_MatchToken(cx, ts, TOK_INSTANCEOF)

#endif /* JS_HAS_INSTANCEOF */

            )) {

        tt = CURRENT_TOKEN(ts).type;

        op = CURRENT_TOKEN(ts).t_op;

        pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc);

    }

#if JS_HAS_IN_OPERATOR

    /* Restore previous state of inForInit flag. */

    tc->flags |= inForInitFlag;

#endif /* JS_HAS_IN_OPERATOR */



    return pn;

}



static JSParseNode *

ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn;

    JSOp op;



    pn = AddExpr(cx, ts, tc);

    while (pn && js_MatchToken(cx, ts, TOK_SHOP)) {

        op = CURRENT_TOKEN(ts).t_op;

        pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc);

    }

    return pn;

}



static JSParseNode *

AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn;

    JSTokenType tt;

    JSOp op;



    pn = MulExpr(cx, ts, tc);

    while (pn &&

           (js_MatchToken(cx, ts, TOK_PLUS) ||

            js_MatchToken(cx, ts, TOK_MINUS))) {

        tt = CURRENT_TOKEN(ts).type;

        op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB;

        pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc);

    }

    return pn;

}



static JSParseNode *

MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSParseNode *pn;

    JSTokenType tt;

    JSOp op;



    pn = UnaryExpr(cx, ts, tc);

    while (pn &&

           (js_MatchToken(cx, ts, TOK_STAR) ||

            js_MatchToken(cx, ts, TOK_DIVOP))) {

        tt = CURRENT_TOKEN(ts).type;

        op = CURRENT_TOKEN(ts).t_op;

        pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc);

    }

    return pn;

}



static JSParseNode *

SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,

           const char *name)

{

    while (kid->pn_type == TOK_RP)

        kid = kid->pn_kid;

    if (kid->pn_type != TOK_NAME &&

        kid->pn_type != TOK_DOT &&

        kid->pn_type != TOK_LB) {

        js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                    JSMSG_BAD_OPERAND, name);

        return NULL;

    }

    pn->pn_kid = kid;

    return kid;

}



static const char *incop_name_str[] = {"increment", "decrement"};



static JSBool

SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,

            JSParseNode *pn, JSParseNode *kid,

            JSTokenType tt, JSBool preorder)

{

    JSOp op;



    kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]);

    if (!kid)

        return JS_FALSE;

    switch (kid->pn_type) {

      case TOK_NAME:

        op = (tt == TOK_INC)

             ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC)

             : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC);

        if (kid->pn_atom == cx->runtime->atomState.argumentsAtom)

            tc->flags |= TCF_FUN_HEAVYWEIGHT;

        break;



      case TOK_DOT:

        op = (tt == TOK_INC)

             ? (preorder ? JSOP_INCPROP : JSOP_PROPINC)

             : (preorder ? JSOP_DECPROP : JSOP_PROPDEC);

        break;



      case TOK_LB:

        op = (tt == TOK_INC)

             ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC)

             : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC);

        break;



      default:

        JS_ASSERT(0);

        op = JSOP_NOP;

    }

    pn->pn_op = op;

    return JS_TRUE;

}



static JSParseNode *

UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSTokenType tt;

    JSParseNode *pn, *pn2;



    ts->flags |= TSF_REGEXP;

    tt = js_GetToken(cx, ts);

    ts->flags &= ~TSF_REGEXP;



    switch (tt) {

      case TOK_UNARYOP:

      case TOK_PLUS:

      case TOK_MINUS:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);

        if (!pn)

            return NULL;

        pn->pn_type = TOK_UNARYOP;      /* PLUS and MINUS are binary */

        pn->pn_op = CURRENT_TOKEN(ts).t_op;

        pn2 = UnaryExpr(cx, ts, tc);

        if (!pn2)

            return NULL;

        pn->pn_pos.end = pn2->pn_pos.end;

        pn->pn_kid = pn2;

        break;



      case TOK_INC:

      case TOK_DEC:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);

        if (!pn)

            return NULL;

        pn2 = MemberExpr(cx, ts, tc, JS_TRUE);

        if (!pn2)

            return NULL;

        if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE))

            return NULL;

        pn->pn_pos.end = pn2->pn_pos.end;

        break;



      case TOK_DELETE:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);

        if (!pn)

            return NULL;

        pn2 = UnaryExpr(cx, ts, tc);

        if (!pn2)

            return NULL;

        pn->pn_pos.end = pn2->pn_pos.end;



        /*

         * Under ECMA3, deleting any unary expression is valid -- it simply

         * returns true. Here we strip off any parentheses.

         */

        while (pn2->pn_type == TOK_RP)

            pn2 = pn2->pn_kid;

        pn->pn_kid = pn2;

        break;



      case TOK_ERROR:

        return NULL;



      default:

        js_UngetToken(ts);

        pn = MemberExpr(cx, ts, tc, JS_TRUE);

        if (!pn)

            return NULL;



        /* Don't look across a newline boundary for a postfix incop. */

        if (pn->pn_pos.end.lineno == ts->lineno) {

            tt = js_PeekTokenSameLine(cx, ts);

            if (tt == TOK_INC || tt == TOK_DEC) {

                (void) js_GetToken(cx, ts);

                pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);

                if (!pn2)

                    return NULL;

                if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE))

                    return NULL;

                pn2->pn_pos.begin = pn->pn_pos.begin;

                pn = pn2;

            }

        }

        break;

    }

    return pn;

}



static JSParseNode *

ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,

             JSParseNode *listNode)

{

    JSBool matched;



    ts->flags |= TSF_REGEXP;

    matched = js_MatchToken(cx, ts, TOK_RP);

    ts->flags &= ~TSF_REGEXP;

    if (!matched) {

        do {

            JSParseNode *argNode = AssignExpr(cx, ts, tc);

            if (!argNode)

                return NULL;

            PN_APPEND(listNode, argNode);

        } while (js_MatchToken(cx, ts, TOK_COMMA));



        MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_ARGS);

    }

    return listNode;

}



static JSParseNode *

MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,

           JSBool allowCallSyntax)

{

    JSParseNode *pn, *pn2, *pn3;

    JSTokenType tt;



    /* Check for new expression first. */

    ts->flags |= TSF_REGEXP;

    tt = js_PeekToken(cx, ts);

    ts->flags &= ~TSF_REGEXP;

    if (tt == TOK_NEW) {

        (void) js_GetToken(cx, ts);



        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);

        if (!pn)

            return NULL;

        pn2 = MemberExpr(cx, ts, tc, JS_FALSE);

        if (!pn2)

            return NULL;

        pn->pn_op = JSOP_NEW;

        PN_INIT_LIST_1(pn, pn2);



        if (js_MatchToken(cx, ts, TOK_LP)) {

            pn = ArgumentList(cx, ts, tc, pn);

            if (!pn)

                return NULL;

        }

        if (pn->pn_count - 1 >= ARGC_LIMIT) {

            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                 JSMSG_TOO_MANY_CON_ARGS);

            return NULL;

        }

        pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;

    } else {

        pn = PrimaryExpr(cx, ts, tc);

        if (!pn)

            return NULL;

    }



    while ((tt = js_GetToken(cx, ts)) > TOK_EOF) {

        if (tt == TOK_DOT) {

            pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);

            if (!pn2)

                return NULL;

            MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);

            pn2->pn_pos.begin = pn->pn_pos.begin;

            pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;

            pn2->pn_op = JSOP_GETPROP;

            pn2->pn_expr = pn;

            pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;

        } else if (tt == TOK_LB) {

            pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);

            if (!pn2)

                return NULL;

            pn3 = Expr(cx, ts, tc);

            if (!pn3)

                return NULL;



            MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);

            pn2->pn_pos.begin = pn->pn_pos.begin;

            pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;



            /* Optimize o['p'] to o.p by rewriting pn2. */

            if (pn3->pn_type == TOK_STRING) {

                pn2->pn_type = TOK_DOT;

                pn2->pn_op = JSOP_GETPROP;

                pn2->pn_arity = PN_NAME;

                pn2->pn_expr = pn;

                pn2->pn_atom = pn3->pn_atom;

            } else {

                pn2->pn_op = JSOP_GETELEM;

                pn2->pn_left = pn;

                pn2->pn_right = pn3;

            }

        } else if (allowCallSyntax && tt == TOK_LP) {

            pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);

            if (!pn2)

                return NULL;



            /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */

            pn2->pn_op = JSOP_CALL;

            if (pn->pn_op == JSOP_NAME &&

                pn->pn_atom == cx->runtime->atomState.evalAtom) {

                if (JSVERSION_IS_ECMA(cx->version))

                    pn2->pn_op = JSOP_EVAL;

                tc->flags |= TCF_FUN_HEAVYWEIGHT;

            }



            PN_INIT_LIST_1(pn2, pn);



            pn2 = ArgumentList(cx, ts, tc, pn2);

            if (!pn2)

                return NULL;

            if (pn2->pn_count - 1 >= ARGC_LIMIT) {

                JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                     JSMSG_TOO_MANY_FUN_ARGS);

                return NULL;

            }

            pn2->pn_pos.end = PN_LAST(pn2)->pn_pos.end;

        } else {

            js_UngetToken(ts);

            return pn;

        }



        pn = pn2;

    }

    if (tt == TOK_ERROR)

        return NULL;

    return pn;

}



static JSParseNode *

PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)

{

    JSTokenType tt;

    JSParseNode *pn, *pn2, *pn3;

    char *badWord;

#if JS_HAS_GETTER_SETTER

    JSAtom *atom;

    JSRuntime *rt;

#endif



#if JS_HAS_SHARP_VARS

    JSParseNode *defsharp;

    JSBool notsharp;



    defsharp = NULL;

    notsharp = JS_FALSE;

  again:

    /*

     * Control flows here after #n= is scanned.  If the following primary is

     * not valid after such a "sharp variable" definition, the token type case

     * should set notsharp.

     */

#endif



    ts->flags |= TSF_REGEXP;

    tt = js_GetToken(cx, ts);

    ts->flags &= ~TSF_REGEXP;



#if JS_HAS_GETTER_SETTER

    if (tt == TOK_NAME) {

        tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);

        if (tt == TOK_ERROR)

            return NULL;

    }

#endif



    switch (tt) {

#if JS_HAS_LEXICAL_CLOSURE

      case TOK_FUNCTION:

        pn = FunctionExpr(cx, ts, tc);

        if (!pn)

            return NULL;

        break;

#endif



#if JS_HAS_INITIALIZERS

      case TOK_LB:

      {

        JSBool matched;

        jsuint atomIndex;



        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);

        if (!pn)

            return NULL;

        pn->pn_type = TOK_RB;

        pn->pn_extra = JS_FALSE;



#if JS_HAS_SHARP_VARS

        if (defsharp) {

            PN_INIT_LIST_1(pn, defsharp);

            defsharp = NULL;

        } else

#endif

            PN_INIT_LIST(pn);



        ts->flags |= TSF_REGEXP;

        matched = js_MatchToken(cx, ts, TOK_RB);

        ts->flags &= ~TSF_REGEXP;

        if (!matched) {

            for (atomIndex = 0; atomIndex < ATOM_INDEX_LIMIT; atomIndex++) {

                ts->flags |= TSF_REGEXP;

                tt = js_PeekToken(cx, ts);

                ts->flags &= ~TSF_REGEXP;

                if (tt == TOK_RB) {

                    pn->pn_extra = JS_TRUE;

                    break;

                }



                if (tt == TOK_COMMA) {

                    /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */

                    js_MatchToken(cx, ts, TOK_COMMA);

                    pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);

                } else

                    pn2 = AssignExpr(cx, ts, tc);

                if (!pn2)

                    return NULL;

                PN_APPEND(pn, pn2);



                if (tt != TOK_COMMA) {

                    /* If we didn't already match TOK_COMMA in above case. */

                    if (!js_MatchToken(cx, ts, TOK_COMMA))

                        break;

                }

            }



            MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);

        }

        pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;

        return pn;

      }



      case TOK_LC:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);

        if (!pn)

            return NULL;

        pn->pn_type = TOK_RC;



#if JS_HAS_SHARP_VARS

        if (defsharp) {

            PN_INIT_LIST_1(pn, defsharp);

            defsharp = NULL;

        } else

#endif

            PN_INIT_LIST(pn);



        if (!js_MatchToken(cx, ts, TOK_RC)) {

            do {

                JSOp op;



                tt = js_GetToken(cx, ts);

                switch (tt) {

                  case TOK_NUMBER:

                    pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);

                    if (pn3)

                        pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;

                    break;

                  case TOK_NAME:

#if JS_HAS_GETTER_SETTER

                    atom = CURRENT_TOKEN(ts).t_atom;

                    rt = cx->runtime;

                    if (atom == rt->atomState.getAtom ||

                        atom == rt->atomState.setAtom) {

                        op = (atom == rt->atomState.getAtom)

                             ? JSOP_GETTER

                             : JSOP_SETTER;

                        if (js_MatchToken(cx, ts, TOK_NAME)) {

                            pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME,

                                               tc);

                            if (!pn3)

                                return NULL;

                            pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;

                            pn3->pn_expr = NULL;



                            /* We have to fake a 'function' token here. */

                            CURRENT_TOKEN(ts).t_op = JSOP_NOP;

                            CURRENT_TOKEN(ts).type = TOK_FUNCTION;

                            pn2 = FunctionDef(cx, ts, tc, JS_TRUE);

                            pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc);

                            goto skip;

                        }

                    }

                    /* else fall thru ... */

#endif

                  case TOK_STRING:

                    pn3 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);

                    if (pn3)

                        pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;

                    break;

                  case TOK_RC:

                    if (JS_HAS_STRICT_OPTION(cx) &&

                        !js_ReportCompileErrorNumber(cx, ts, NULL,

                                                     JSREPORT_WARNING |

                                                     JSREPORT_STRICT,

                                                     JSMSG_TRAILING_COMMA)) {

                        return NULL;

                    }

                    goto end_obj_init;

                  default:

                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                                JSMSG_BAD_PROP_ID);

                    return NULL;

                }



                tt = js_GetToken(cx, ts);

#if JS_HAS_GETTER_SETTER

                if (tt == TOK_NAME) {

                    tt = CheckGetterOrSetter(cx, ts, TOK_COLON);

                    if (tt == TOK_ERROR)

                        return NULL;

                }

#endif

                if (tt != TOK_COLON) {

                    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                                JSMSG_COLON_AFTER_ID);

                    return NULL;

                }

                op = CURRENT_TOKEN(ts).t_op;

                pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc),

                                tc);

#if JS_HAS_GETTER_SETTER

              skip:

#endif

                if (!pn2)

                    return NULL;

                PN_APPEND(pn, pn2);

            } while (js_MatchToken(cx, ts, TOK_COMMA));



            MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST);

        }

      end_obj_init:

        pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;

        return pn;



#if JS_HAS_SHARP_VARS

      case TOK_DEFSHARP:

        if (defsharp)

            goto badsharp;

        defsharp = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);

        if (!defsharp)

            return NULL;

        defsharp->pn_kid = NULL;

        defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;

        goto again;



      case TOK_USESHARP:

        /* Check for forward/dangling references at runtime, to allow eval. */

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);

        if (!pn)

            return NULL;

        pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;

        notsharp = JS_TRUE;

        break;

#endif /* JS_HAS_SHARP_VARS */

#endif /* JS_HAS_INITIALIZERS */



      case TOK_LP:

      {

#if JS_HAS_IN_OPERATOR

        uintN oldflags;

#endif

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY, tc);

        if (!pn)

            return NULL;

#if JS_HAS_IN_OPERATOR

        /*

         * Always accept the 'in' operator in a parenthesized expression,

         * where it's unambiguous, even if we might be parsing the init of a

         * for statement.

         */

        oldflags = tc->flags;

        tc->flags &= ~TCF_IN_FOR_INIT;

#endif

        pn2 = Expr(cx, ts, tc);

#if JS_HAS_IN_OPERATOR

        tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);

#endif

        if (!pn2)

            return NULL;



        MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);

        pn->pn_type = TOK_RP;

        pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;

        pn->pn_kid = pn2;

        break;

      }



      case TOK_STRING:

#if JS_HAS_SHARP_VARS

        notsharp = JS_TRUE;

#endif

        /* FALL THROUGH */

      case TOK_NAME:

      case TOK_OBJECT:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);

        if (!pn)

            return NULL;

        pn->pn_op = CURRENT_TOKEN(ts).t_op;

        pn->pn_atom = CURRENT_TOKEN(ts).t_atom;

        if (tt == TOK_NAME) {

            pn->pn_arity = PN_NAME;

            pn->pn_expr = NULL;

            pn->pn_slot = -1;

            pn->pn_attrs = 0;



            /* Unqualified __parent__ and __proto__ uses require activations. */

            if (pn->pn_atom == cx->runtime->atomState.parentAtom ||

                pn->pn_atom == cx->runtime->atomState.protoAtom) {

                tc->flags |= TCF_FUN_HEAVYWEIGHT;

            }

        }

        break;



      case TOK_NUMBER:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);

        if (!pn)

            return NULL;

        pn->pn_dval = CURRENT_TOKEN(ts).t_dval;

#if JS_HAS_SHARP_VARS

        notsharp = JS_TRUE;

#endif

        break;



      case TOK_PRIMARY:

        pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NULLARY, tc);

        if (!pn)

            return NULL;

        pn->pn_op = CURRENT_TOKEN(ts).t_op;

#if JS_HAS_SHARP_VARS

        notsharp = JS_TRUE;

#endif

        break;



#if !JS_HAS_EXPORT_IMPORT

      case TOK_EXPORT:

      case TOK_IMPORT:

#endif

      case TOK_RESERVED:

        badWord = js_DeflateString(cx, CURRENT_TOKEN(ts).ptr,

                                   (size_t) CURRENT_TOKEN(ts).pos.end.index

                                          - CURRENT_TOKEN(ts).pos.begin.index);

        js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                    JSMSG_RESERVED_ID, badWord);

        JS_free(cx, badWord);

        return NULL;



      case TOK_ERROR:

        /* The scanner or one of its subroutines reported the error. */

        return NULL;



      default:

        js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                    JSMSG_SYNTAX_ERROR);

        return NULL;

    }



#if JS_HAS_SHARP_VARS

    if (defsharp) {

        if (notsharp) {

  badsharp:

            js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                        JSMSG_BAD_SHARP_VAR_DEF);

            return NULL;

        }

        defsharp->pn_kid = pn;

        return defsharp;

    }

#endif

    return pn;

}



JSBool

js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)

{

    JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;



    switch (pn->pn_arity) {

      case PN_FUNC:

        if (!js_FoldConstants(cx, pn->pn_body, tc))

            return JS_FALSE;

        break;



      case PN_LIST:

        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {

            if (!js_FoldConstants(cx, pn2, tc))

                return JS_FALSE;

        }

        break;



      case PN_TERNARY:

        /* Any kid may be null (e.g. for (;;)). */

        pn1 = pn->pn_kid1;

        pn2 = pn->pn_kid2;

        pn3 = pn->pn_kid3;

        if (pn1 && !js_FoldConstants(cx, pn1, tc))

            return JS_FALSE;

        if (pn2 && !js_FoldConstants(cx, pn2, tc))

            return JS_FALSE;

        if (pn3 && !js_FoldConstants(cx, pn3, tc))

            return JS_FALSE;

        break;



      case PN_BINARY:

        /* First kid may be null (for default case in switch). */

        pn1 = pn->pn_left;

        pn2 = pn->pn_right;

        if (pn1 && !js_FoldConstants(cx, pn1, tc))

            return JS_FALSE;

        if (!js_FoldConstants(cx, pn2, tc))

            return JS_FALSE;

        break;



      case PN_UNARY:

        /* Our kid may be null (e.g. return; vs. return e;). */

        pn1 = pn->pn_kid;

        if (pn1 && !js_FoldConstants(cx, pn1, tc))

            return JS_FALSE;

        break;



      case PN_NAME:

        pn1 = pn->pn_expr;

        if (pn1 && !js_FoldConstants(cx, pn1, tc))

            return JS_FALSE;

        break;



      case PN_NULLARY:

        break;

    }



    switch (pn->pn_type) {

      case TOK_IF:

      case TOK_HOOK:

        /* Reduce 'if (C) T; else E' into T for true C, E for false. */

        switch (pn1->pn_type) {

          case TOK_NUMBER:

            if (pn1->pn_dval == 0)

                pn2 = pn3;

            break;

          case TOK_STRING:

            if (ATOM_TO_STRING(pn1->pn_atom)->length == 0)

                pn2 = pn3;

            break;

          case TOK_PRIMARY:

            if (pn1->pn_op == JSOP_TRUE)

                break;

            if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {

                pn2 = pn3;

                break;

            }

            /* FALL THROUGH */

          default:

            /* Early return to dodge common code that copies pn2 to pn. */

            return JS_TRUE;

        }



        if (pn2) {

            /* pn2 is the then- or else-statement subtree to compile. */

            PN_MOVE_NODE(pn, pn2);

        } else {

            /* False condition and no else: make pn an empty statement. */

            pn->pn_type = TOK_SEMI;

            pn->pn_arity = PN_UNARY;

            pn->pn_kid = NULL;

        }

        RecycleTree(pn2, tc);

        if (pn3 && pn3 != pn2)

            RecycleTree(pn3, tc);

        break;



      case TOK_PLUS:

        if (pn1->pn_type == TOK_STRING && pn2->pn_type == TOK_STRING) {

            JSString *str1, *str2;

            size_t length, length1, length2, nbytes;

            void *mark;

            jschar *chars;



            /* Concatenate string constants. */

            str1 = ATOM_TO_STRING(pn1->pn_atom);

            str2 = ATOM_TO_STRING(pn2->pn_atom);

            length1 = str1->length;

            length2 = str2->length;

            length = length1 + length2;

            nbytes = (length + 1) * sizeof(jschar);

            mark = JS_ARENA_MARK(&cx->tempPool);

            JS_ARENA_ALLOCATE_CAST(chars, jschar *, &cx->tempPool, nbytes);

            if (!chars) {

                JS_ReportOutOfMemory(cx);

                return JS_FALSE;

            }

            js_strncpy(chars, str1->chars, length1);

            js_strncpy(chars + length1, str2->chars, length2);

            chars[length] = 0;

            pn->pn_atom = js_AtomizeChars(cx, chars, length, 0);

            JS_ARENA_RELEASE(&cx->tempPool, mark);

            if (!pn->pn_atom)

                return JS_FALSE;

            pn->pn_type = TOK_STRING;

            pn->pn_op = JSOP_STRING;

            pn->pn_arity = PN_NULLARY;

            RecycleTree(pn1, tc);

            RecycleTree(pn2, tc);

            break;

        }

        /* FALL THROUGH */



      case TOK_STAR:

        /* The * in 'import *;' parses as a nullary star node. */

        if (pn->pn_arity == PN_NULLARY)

            break;

        /* FALL THROUGH */



      case TOK_SHOP:

      case TOK_MINUS:

      case TOK_DIVOP:

        if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) {

            jsdouble d, d2;

            int32 i, j;

            uint32 u;



            /* Fold two numeric constants. */

            d = pn1->pn_dval;

            d2 = pn2->pn_dval;

            switch (pn->pn_op) {

              case JSOP_LSH:

              case JSOP_RSH:

                if (!js_DoubleToECMAInt32(cx, d, &i))

                    return JS_FALSE;

                if (!js_DoubleToECMAInt32(cx, d2, &j))

                    return JS_FALSE;

                j &= 31;

                d = (pn->pn_op == JSOP_LSH) ? i << j : i >> j;

                break;



              case JSOP_URSH:

                if (!js_DoubleToECMAUint32(cx, d, &u))

                    return JS_FALSE;

                if (!js_DoubleToECMAInt32(cx, d2, &j))

                    return JS_FALSE;

                j &= 31;

                d = u >> j;

                break;



              case JSOP_ADD:

                d += d2;

                break;



              case JSOP_SUB:

                d -= d2;

                break;



              case JSOP_MUL:

                d *= d2;

                break;



              case JSOP_DIV:

                if (d2 == 0) {

#ifdef XP_PC

                    /* XXX MSVC miscompiles such that (NaN == 0) */

                    if (JSDOUBLE_IS_NaN(d2))

                        d = *cx->runtime->jsNaN;

                    else

#endif

                    if (d == 0 || JSDOUBLE_IS_NaN(d))

                        d = *cx->runtime->jsNaN;

                    else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)

                        d = *cx->runtime->jsNegativeInfinity;

                    else

                        d = *cx->runtime->jsPositiveInfinity;

                } else {

                    d /= d2;

                }

                break;



              case JSOP_MOD:

                if (d2 == 0) {

                    d = *cx->runtime->jsNaN;

                } else {

#ifdef XP_PC

                  /* 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);

                }

                break;



              default:;

            }

            pn->pn_type = TOK_NUMBER;

            pn->pn_op = JSOP_NUMBER;

            pn->pn_arity = PN_NULLARY;

            pn->pn_dval = d;

            RecycleTree(pn1, tc);

            RecycleTree(pn2, tc);

        }

        break;



      case TOK_UNARYOP:

        if (pn1->pn_type == TOK_NUMBER) {

            jsdouble d;

            int32 i;



            /* Operate on one numeric constants. */

            d = pn1->pn_dval;

            switch (pn->pn_op) {

              case JSOP_BITNOT:

                if (!js_DoubleToECMAInt32(cx, d, &i))

                    return JS_FALSE;

                d = ~i;

                break;



              case JSOP_NEG:

#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

                break;



              case JSOP_POS:

                break;



              case JSOP_NOT:

                pn->pn_type = TOK_PRIMARY;

                pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE;

                pn->pn_arity = PN_NULLARY;

                /* FALL THROUGH */



              default:

                /* Return early to dodge the common TOK_NUMBER code. */

                return JS_TRUE;

            }

            pn->pn_type = TOK_NUMBER;

            pn->pn_op = JSOP_NUMBER;

            pn->pn_arity = PN_NULLARY;

            pn->pn_dval = d;

            RecycleTree(pn1, tc);

        }

        break;



      default:;

    }



    return JS_TRUE;

}

 

**** End of jsparse.c ****

 

**** Start of jsparse.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsparse_h___

#define jsparse_h___

/*

 * JS parser definitions.

 */

#include "jsprvtd.h"

#include "jspubtd.h"

#include "jsscan.h"



JS_BEGIN_EXTERN_C



/*

 * Parsing builds a tree of nodes that directs code generation.  This tree is

 * not a concrete syntax tree in all respects (for example, || and && are left

 * associative, but (A && B && C) translates into the right-associated tree

 * <A && <B && C>> so that code generation can emit a left-associative branch

 * around <B && C> when A is false).  Nodes are labeled by token type, with a

 * JSOp secondary label when needed:

 *

 * Label        Variant     Members

 * -----        -------     -------

 * <Definitions>

 * TOK_FUNCTION func        pn_fun: function, contains arg and var properties

 *                            NB: We create the function object at parse (not

 *                            emit) time, in order to specialize arg and var

 *                            bytecodes early.

 *                          pn_body: TOK_LC node for function body statements

 *                          pn_flags: TCF_FUN_* flags (see jsemit.h) collected

 *                            while parsing the function's body

 *                          pn_tryCount: of try statements in function

 *

 * <Statements>

 * TOK_LC       list        pn_head: list of pn_count statements

 * TOK_EXPORT   list        pn_head: list of pn_count TOK_NAMEs or one TOK_STAR

 *                            (which is not a multiply node)

 * TOK_IMPORT   list        pn_head: list of pn_count sub-trees of the form

 *                            a.b.*, a[b].*, a.*, a.b, or a[b] -- but never a.

 *                            Each member is expressed with TOK_DOT or TOK_LB.

 *                            Each sub-tree's root node has a pn_op in the set

 *                            JSOP_IMPORT{ALL,PROP,ELEM}

 * TOK_IF       ternary     pn_kid1: cond, pn_kid2: then, pn_kid3: else or null

 * TOK_SWITCH   binary      pn_left: discriminant

 *                          pn_right: list of TOK_CASE nodes, with at most one

 *                            TOK_DEFAULT node

 * TOK_CASE,    binary      pn_left: case expr or null if TOK_DEFAULT

 * TOK_DEFAULT              pn_right: TOK_LC node for this case's statements

 * TOK_WHILE    binary      pn_left: cond, pn_right: body

 * TOK_DO       binary      pn_left: body, pn_right: cond

 * TOK_FOR      binary      pn_left: either

 *                            for/in loop: a binary TOK_IN node with

 *                              pn_left:  TOK_VAR or TOK_NAME to left of 'in'

 *                              pn_right: object expr to right of 'in'

 *                            for(;;) loop: a ternary TOK_RESERVED node with

 *                              pn_kid1:  init expr before first ';'

 *                              pn_kid2:  cond expr before second ';'

 *                              pn_kid3:  update expr after second ';'

 *                              any kid may be null

 *                          pn_right: body

 * TOK_THROW    unary       pn_op: JSOP_THROW, pn_kid: exception

 * TOK_TRY      ternary     pn_kid1: try block

 *                          pn_kid2: catch blocks or null

 *                          pn_kid3: finally block or null

 * TOK_CATCH    ternary     pn_kid1: PN_NAME node for catch var (with pn_expr

 *                                   null or the catch guard expression)

 *                          pn_kid2: more catch blocks or null

 *                          pn_kid3: catch block statements

 * TOK_BREAK    name        pn_atom: label or null

 * TOK_CONTINUE name        pn_atom: label or null

 * TOK_WITH     binary      pn_left: head expr, pn_right: body

 * TOK_VAR      list        pn_head: list of pn_count TOK_NAME nodes

 *                          each name node has pn_atom: variable name and

 *                          pn_expr: initializer or null

 * TOK_RETURN   unary       pn_kid: return expr or null

 * TOK_SEMI     unary       pn_kid: expr or null statement

 * TOK_COLON    name        pn_atom: label, pn_expr: labeled statement

 *

 * <Expressions>

 * TOK_COMMA    list        pn_head: list of pn_count comma-separated exprs

 * TOK_ASSIGN   binary      pn_left: lvalue, pn_right: rvalue

 *                          pn_op: JSOP_ADD for +=, etc.

 * TOK_HOOK     ternary     pn_kid1: cond, pn_kid2: then, pn_kid3: else

 * TOK_OR       binary      pn_left: first in || chain, pn_right: rest of chain

 * TOK_AND      binary      pn_left: first in && chain, pn_right: rest of chain

 * TOK_BITOR    binary      pn_left: left-assoc | expr, pn_right: ^ expr

 * TOK_BITXOR   binary      pn_left: left-assoc ^ expr, pn_right: & expr

 * TOK_BITAND   binary      pn_left: left-assoc & expr, pn_right: EQ expr

 * TOK_EQOP     binary      pn_left: left-assoc EQ expr, pn_right: REL expr

 *                          pn_op: JSOP_EQ, JSOP_NE, JSOP_NEW_EQ, JSOP_NEW_NE

 * TOK_RELOP    binary      pn_left: left-assoc REL expr, pn_right: SH expr

 *                          pn_op: JSOP_LT, JSOP_LE, JSOP_GT, JSOP_GE

 * TOK_SHOP     binary      pn_left: left-assoc SH expr, pn_right: ADD expr

 *                          pn_op: JSOP_LSH, JSOP_RSH, JSOP_URSH

 * TOK_PLUS,    binary      pn_left: left-assoc ADD expr, pn_right: MUL expr

 * TOK_MINUS                pn_op: JSOP_ADD, JSOP_SUB

 * TOK_STAR,    binary      pn_left: left-assoc MUL expr, pn_right: UNARY expr

 * TOK_DIVOP                pn_op: JSOP_MUL, JSOP_DIV, JSOP_MOD

 * TOK_UNARYOP  unary       pn_kid: UNARY expr, pn_op: JSOP_NEG, JSOP_POS,

 *                          JSOP_NOT, JSOP_BITNOT, JSOP_TYPEOF, JSOP_VOID

 * TOK_INC,     unary       pn_kid: MEMBER expr

 * TOK_DEC

 * TOK_NEW      list        pn_head: list of ctor, arg1, arg2, ... argN

 *                          pn_count: 1 + N (where N is number of args)

 *                          ctor is a MEMBER expr

 * TOK_DELETE   unary       pn_kid: MEMBER expr

 * TOK_DOT      name        pn_expr: MEMBER expr to left of .

 *                          pn_atom: name to right of .

 * TOK_LB       binary      pn_left: MEMBER expr to left of [

 *                          pn_right: expr between [ and ]

 * TOK_LP       list        pn_head: list of call, arg1, arg2, ... argN

 *                          pn_count: 1 + N (where N is number of args)

 *                          call is a MEMBER expr naming a callable object

 * TOK_RB       list        pn_head: list of pn_count array element exprs

 *                          [,,] holes are represented by TOK_COMMA nodes

 *                          #n=[...] produces TOK_DEFSHARP at head of list

 *                          pn_extra: true if extra comma at end

 * TOK_RC       list        pn_head: list of pn_count TOK_COLON nodes where

 *                          each has pn_left: property id, pn_right: value

 *                          #n={...} produces TOK_DEFSHARP at head of list

 * TOK_DEFSHARP unary       pn_num: jsint value of n in #n=

 *                          pn_kid: null for #n=[...] and #n={...}, primary

 *                          if #n=primary for function, paren, name, object

 *                          literal expressions

 * TOK_USESHARP nullary     pn_num: jsint value of n in #n#

 * TOK_RP       unary       pn_kid: parenthesized expression

 * TOK_NAME,    name        pn_atom: name, string, or object atom

 * TOK_STRING,              pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT

 * TOK_OBJECT               If JSOP_NAME, pn_op may be JSOP_*ARG or JSOP_*VAR

 *                          with pn_slot >= 0 and pn_attrs telling const-ness

 * TOK_NUMBER   dval        pn_dval: double value of numeric literal

 * TOK_PRIMARY  nullary     pn_op: JSOp bytecode

 */

typedef enum JSParseNodeArity {

    PN_FUNC     = -3,

    PN_LIST     = -2,

    PN_TERNARY  =  3,

    PN_BINARY   =  2,

    PN_UNARY    =  1,

    PN_NAME     = -1,

    PN_NULLARY  =  0

} JSParseNodeArity;



struct JSParseNode {

    JSTokenType         pn_type;

    JSTokenPos          pn_pos;

    JSOp                pn_op;

    ptrdiff_t           pn_offset;      /* first generated bytecode offset */

    JSParseNodeArity    pn_arity;

    union {

	struct {                        /* TOK_FUNCTION node */

	    JSFunction  *fun;           /* function object private data */

	    JSParseNode *body;          /* TOK_LC list of statements */

            uint32      flags;          /* accumulated tree context flags */

	    uint32      tryCount;       /* count of try statements in body */

	} func;

	struct {                        /* list of next-linked nodes */

	    JSParseNode *head;          /* first node in list */

	    JSParseNode **tail;         /* ptr to ptr to last node in list */

	    uint32      count;          /* number of nodes in list */

	    JSBool      extra;          /* extra comma flag for [1,2,,] */

	} list;

	struct {                        /* ternary: if, for(;;), ?: */

	    JSParseNode *kid1;          /* condition, discriminant, etc. */

	    JSParseNode *kid2;          /* then-part, case list, etc. */

	    JSParseNode *kid3;          /* else-part, default case, etc. */

	} ternary;

	struct {                        /* two kids if binary */

	    JSParseNode *left;

	    JSParseNode *right;

	    jsval       val;            /* switch case value */

	} binary;

	struct {                        /* one kid if unary */

	    JSParseNode *kid;

	    jsint       num;            /* -1 or sharp variable number */

	} unary;

	struct {                        /* name, labeled statement, etc. */

	    JSAtom      *atom;          /* name or label atom, null if slot */

	    JSParseNode *expr;          /* object or initializer */

	    jsint       slot;           /* -1 or arg or local var slot */

            uintN       attrs;          /* attributes if local var or const */

	} name;

	jsdouble        dval;           /* aligned numeric literal value */

    } pn_u;

    JSParseNode         *pn_next;       /* to align dval and pn_u on RISCs */

};



#define pn_fun          pn_u.func.fun

#define pn_body         pn_u.func.body

#define pn_flags        pn_u.func.flags

#define pn_tryCount     pn_u.func.tryCount

#define pn_head         pn_u.list.head

#define pn_tail         pn_u.list.tail

#define pn_count        pn_u.list.count

#define pn_extra        pn_u.list.extra

#define pn_kid1         pn_u.ternary.kid1

#define pn_kid2         pn_u.ternary.kid2

#define pn_kid3         pn_u.ternary.kid3

#define pn_left         pn_u.binary.left

#define pn_right        pn_u.binary.right

#define pn_val          pn_u.binary.val

#define pn_kid          pn_u.unary.kid

#define pn_num          pn_u.unary.num

#define pn_atom         pn_u.name.atom

#define pn_expr         pn_u.name.expr

#define pn_slot         pn_u.name.slot

#define pn_attrs        pn_u.name.attrs

#define pn_dval         pn_u.dval



/*

 * Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off

 * any kids in pn2->pn_u, by clearing pn2.

 */

#define PN_MOVE_NODE(pn, pn2)                                                 \

    JS_BEGIN_MACRO                                                            \

        (pn)->pn_type = (pn2)->pn_type;                                       \

        (pn)->pn_op = (pn2)->pn_op;                                           \

        (pn)->pn_arity = (pn2)->pn_arity;                                     \

        (pn)->pn_u = (pn2)->pn_u;                                             \

        (pn2)->pn_type = TOK_EOF;                                             \

        (pn2)->pn_op = JSOP_NOP;                                              \

        (pn2)->pn_arity = PN_NULLARY;                                         \

    JS_END_MACRO



/* True if pn is a parsenode representing a literal constant. */

#define PN_IS_CONSTANT(pn)                                                    \

    ((pn)->pn_type == TOK_NUMBER ||                                           \

     (pn)->pn_type == TOK_STRING ||                                           \

     ((pn)->pn_type == TOK_PRIMARY && (pn)->pn_op != JSOP_THIS))



/*

 * Compute a pointer to the last JSParseNode element in a singly-linked list.

 * NB: list must be non-empty for correct PN_LAST usage!

 */

#define PN_LAST(list) \

    ((JSParseNode *)((char *)(list)->pn_tail - offsetof(JSParseNode, pn_next)))



#define PN_INIT_LIST(list)                                                    \

    JS_BEGIN_MACRO                                                            \

	(list)->pn_head = NULL;                                               \

	(list)->pn_tail = &(list)->pn_head;                                   \

	(list)->pn_count = 0;                                                 \

    JS_END_MACRO



#define PN_INIT_LIST_1(list, pn)                                              \

    JS_BEGIN_MACRO                                                            \

	(list)->pn_head = (pn);                                               \

	(list)->pn_tail = &(pn)->pn_next;                                     \

	(list)->pn_count = 1;                                                 \

    JS_END_MACRO



#define PN_APPEND(list, pn)                                                   \

    JS_BEGIN_MACRO                                                            \

	*(list)->pn_tail = (pn);                                              \

	(list)->pn_tail = &(pn)->pn_next;                                     \

	(list)->pn_count++;                                                   \

    JS_END_MACRO



/*

 * Parse a top-level JS script.

 *

 * The caller must prevent the GC from running while this function is active,

 * because atoms and function newborns are not rooted yet.

 */

extern JS_FRIEND_API(JSParseNode *)

js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts);



extern JS_FRIEND_API(JSBool)

js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,

		      JSCodeGenerator *cg);



extern JSBool

js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun);



extern JSBool

js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc);



JS_END_EXTERN_C



#endif /* jsparse_h___ */

 

**** End of jsparse.h ****

 

**** Start of jsprf.c ****

 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

/*

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

** Portable safe sprintf code.

**

** Author: Kipp E.B. Hickman

*/

#include "jsstddef.h"

#include <stdarg.h>

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include "jsprf.h"

#include "jslong.h"

#include "jsutil.h" /* Added by JSIFY */



/*

** Note: on some platforms va_list is defined as an array,

** and requires array notation.

*/

#ifdef HAVE_VA_LIST_AS_ARRAY

#define VARARGS_ASSIGN(foo, bar)	foo[0] = bar[0]

#else

#define VARARGS_ASSIGN(foo, bar)	(foo) = (bar)

#endif



/*

** WARNING: This code may *NOT* call JS_LOG (because JS_LOG calls it)

*/



/*

** XXX This needs to be internationalized!

*/



typedef struct SprintfStateStr SprintfState;



struct SprintfStateStr {

    int (*stuff)(SprintfState *ss, const char *sp, JSUint32 len);



    char *base;

    char *cur;

    JSUint32 maxlen;



    int (*func)(void *arg, const char *sp, JSUint32 len);

    void *arg;

};



/*

** Numbered Arguement State

*/

struct NumArgState{

    int	    type;		/* type of the current ap                    */

    va_list ap;			/* point to the corresponding position on ap */

};



static JSBool  l10n_debug_init = JS_FALSE;

static JSBool  l10n_debug = JS_FALSE;



#define NAS_DEFAULT_NUM 20  /* default number of NumberedArgumentState array */





#define TYPE_INT16	0

#define TYPE_UINT16	1

#define TYPE_INTN	2

#define TYPE_UINTN	3

#define TYPE_INT32	4

#define TYPE_UINT32	5

#define TYPE_INT64	6

#define TYPE_UINT64	7

#define TYPE_STRING	8

#define TYPE_DOUBLE	9

#define TYPE_INTSTR	10

#define TYPE_UNKNOWN	20



#define _LEFT		0x1

#define _SIGNED		0x2

#define _SPACED		0x4

#define _ZEROS		0x8

#define _NEG		0x10



/*

** Fill into the buffer using the data in src

*/

static int fill2(SprintfState *ss, const char *src, int srclen, int width,

		int flags)

{

    char space = ' ';

    int rv;



    width -= srclen;

    if ((width > 0) && ((flags & _LEFT) == 0)) {	/* Right adjusting */

	if (flags & _ZEROS) {

	    space = '0';

	}

	while (--width >= 0) {

	    rv = (*ss->stuff)(ss, &space, 1);

	    if (rv < 0) {

		return rv;

	    }

	}

    }



    /* Copy out the source data */

    rv = (*ss->stuff)(ss, src, (JSUint32)srclen);

    if (rv < 0) {

	return rv;

    }



    if ((width > 0) && ((flags & _LEFT) != 0)) {	/* Left adjusting */

	while (--width >= 0) {

	    rv = (*ss->stuff)(ss, &space, 1);

	    if (rv < 0) {

		return rv;

	    }

	}

    }

    return 0;

}



/*

** Fill a number. The order is: optional-sign zero-filling conversion-digits

*/

static int fill_n(SprintfState *ss, const char *src, int srclen, int width,

		  int prec, int type, int flags)

{

    int zerowidth = 0;

    int precwidth = 0;

    int signwidth = 0;

    int leftspaces = 0;

    int rightspaces = 0;

    int cvtwidth;

    int rv;

    char sign;



    if ((type & 1) == 0) {

	if (flags & _NEG) {

	    sign = '-';

	    signwidth = 1;

	} else if (flags & _SIGNED) {

	    sign = '+';

	    signwidth = 1;

	} else if (flags & _SPACED) {

	    sign = ' ';

	    signwidth = 1;

	}

    }

    cvtwidth = signwidth + srclen;



    if (prec > 0) {

	if (prec > srclen) {

	    precwidth = prec - srclen;		/* Need zero filling */

	    cvtwidth += precwidth;

	}

    }



    if ((flags & _ZEROS) && (prec < 0)) {

	if (width > cvtwidth) {

	    zerowidth = width - cvtwidth;	/* Zero filling */

	    cvtwidth += zerowidth;

	}

    }



    if (flags & _LEFT) {

	if (width > cvtwidth) {

	    /* Space filling on the right (i.e. left adjusting) */

	    rightspaces = width - cvtwidth;

	}

    } else {

	if (width > cvtwidth) {

	    /* Space filling on the left (i.e. right adjusting) */

	    leftspaces = width - cvtwidth;

	}

    }

    while (--leftspaces >= 0) {

	rv = (*ss->stuff)(ss, " ", 1);

	if (rv < 0) {

	    return rv;

	}

    }

    if (signwidth) {

	rv = (*ss->stuff)(ss, &sign, 1);

	if (rv < 0) {

	    return rv;

	}

    }

    while (--precwidth >= 0) {

	rv = (*ss->stuff)(ss, "0", 1);

	if (rv < 0) {

	    return rv;

	}

    }

    while (--zerowidth >= 0) {

	rv = (*ss->stuff)(ss, "0", 1);

	if (rv < 0) {

	    return rv;

	}

    }

    rv = (*ss->stuff)(ss, src, (JSUint32)srclen);

    if (rv < 0) {

	return rv;

    }

    while (--rightspaces >= 0) {

	rv = (*ss->stuff)(ss, " ", 1);

	if (rv < 0) {

	    return rv;

	}

    }

    return 0;

}



/*

** Convert a long into its printable form

*/

static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,

		 int type, int flags, const char *hexp)

{

    char cvtbuf[100];

    char *cvt;

    int digits;



    /* according to the man page this needs to happen */

    if ((prec == 0) && (num == 0)) {

	return 0;

    }



    /*

    ** Converting decimal is a little tricky. In the unsigned case we

    ** need to stop when we hit 10 digits. In the signed case, we can

    ** stop when the number is zero.

    */

    cvt = cvtbuf + sizeof(cvtbuf);

    digits = 0;

    while (num) {

	int digit = (((unsigned long)num) % radix) & 0xF;

	*--cvt = hexp[digit];

	digits++;

	num = (long)(((unsigned long)num) / radix);

    }

    if (digits == 0) {

	*--cvt = '0';

	digits++;

    }



    /*

    ** Now that we have the number converted without its sign, deal with

    ** the sign and zero padding.

    */

    return fill_n(ss, cvt, digits, width, prec, type, flags);

}



/*

** Convert a 64-bit integer into its printable form

*/

static int cvt_ll(SprintfState *ss, JSInt64 num, int width, int prec, int radix,

		  int type, int flags, const char *hexp)

{

    char cvtbuf[100];

    char *cvt;

    int digits;

    JSInt64 rad;



    /* according to the man page this needs to happen */

    if ((prec == 0) && (JSLL_IS_ZERO(num))) {

	return 0;

    }



    /*

    ** Converting decimal is a little tricky. In the unsigned case we

    ** need to stop when we hit 10 digits. In the signed case, we can

    ** stop when the number is zero.

    */

    JSLL_I2L(rad, radix);

    cvt = cvtbuf + sizeof(cvtbuf);

    digits = 0;

    while (!JSLL_IS_ZERO(num)) {

	JSInt32 digit;

	JSInt64 quot, rem;

	JSLL_UDIVMOD(&quot, &rem, num, rad);

	JSLL_L2I(digit, rem);

	*--cvt = hexp[digit & 0xf];

	digits++;

	num = quot;

    }

    if (digits == 0) {

	*--cvt = '0';

	digits++;

    }



    /*

    ** Now that we have the number converted without its sign, deal with

    ** the sign and zero padding.

    */

    return fill_n(ss, cvt, digits, width, prec, type, flags);

}



/*

** Convert a double precision floating point number into its printable

** form.

**

** XXX stop using sprintf to convert floating point

*/

static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1)

{

    char fin[20];

    char fout[300];

    int amount = fmt1 - fmt0;



    JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));

    if (amount >= (int)sizeof(fin)) {

	/* Totally bogus % command to sprintf. Just ignore it */

	return 0;

    }

    memcpy(fin, fmt0, (size_t)amount);

    fin[amount] = 0;



    /* Convert floating point using the native sprintf code */

#ifdef DEBUG

    {

        const char *p = fin;

        while (*p) {

            JS_ASSERT(*p != 'L');

            p++;

        }

    }

#endif

    sprintf(fout, fin, d);



    /*

    ** This assert will catch overflow's of fout, when building with

    ** debugging on. At least this way we can track down the evil piece

    ** of calling code and fix it!

    */

    JS_ASSERT(strlen(fout) < sizeof(fout));



    return (*ss->stuff)(ss, fout, strlen(fout));

}



/*

** Convert a string into its printable form.  "width" is the output

** width. "prec" is the maximum number of characters of "s" to output,

** where -1 means until NUL.

*/

static int cvt_s(SprintfState *ss, const char *s, int width, int prec,

		 int flags)

{

    int slen;



    if (prec == 0)

	return 0;



    /* Limit string length by precision value */

    slen = s ? strlen(s) : 6;

    if (prec > 0) {

	if (prec < slen) {

	    slen = prec;

	}

    }



    /* and away we go */

    return fill2(ss, s ? s : "(null)", slen, width, flags);

}



/*

** BiuldArgArray stands for Numbered Argument list Sprintf

** for example,  

**	fmp = "%4$i, %2$d, %3s, %1d";

** the number must start from 1, and no gap among them

*/



static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArgState* nasArray )

{

    int number = 0, cn = 0, i;

    const char* p;

    char  c;

    struct NumArgState* nas;

    



    /*

    ** set the l10n_debug flag

    ** this routine should be executed only once

    ** 'cause getenv does take time

    */

    if( !l10n_debug_init ){

	l10n_debug_init = JS_TRUE;

	p = getenv( "NETSCAPE_LOCALIZATION_DEBUG" );

	if( ( p != NULL ) && ( *p == '1' ) ){

	    l10n_debug = JS_TRUE;

	}

    }





    /*

    **	first pass:

    **	detemine how many legal % I have got, then allocate space

    */



    p = fmt;

    *rv = 0;

    i = 0;

    while( ( c = *p++ ) != 0 ){

	if( c != '%' )

	    continue;

	if( ( c = *p++ ) == '%' )	/* skip %% case */

	    continue;



	while( c != 0 ){

	    if( c > '9' || c < '0' ){

		if( c == '$' ){		/* numbered argument csae */

		    if( i > 0 ){

			*rv = -1;

			if( l10n_debug )

			    printf( "either no *OR* all arguments are numbered \"%s\"\n", fmt );

			return NULL;

		    }

		    number++;

		    break;



		} else{			/* non-numbered argument case */

		    if( number > 0 ){

			if( l10n_debug )

			    printf( "either no *OR* all arguments are numbered \"%s\"\n", fmt );

			*rv = -1;

			return NULL;

		    }

		    i = 1;

		    break;

		}

	    }



	    c = *p++;

	}

    }



    if( number == 0 ){

	return NULL;

    }



    

    if( number > NAS_DEFAULT_NUM ){

	nas = (struct NumArgState*)malloc( number * sizeof( struct NumArgState ) );

	if( !nas ){

	    *rv = -1;

	    if( l10n_debug )

		printf( "malloc() error for \"%s\"\n", fmt );

	    return NULL;

	}

    } else {

	nas = nasArray;

    }



    for( i = 0; i < number; i++ ){

	nas[i].type = TYPE_UNKNOWN;

    }





    /*

    ** second pass:

    ** set nas[].type

    */



    p = fmt;

    while( ( c = *p++ ) != 0 ){

    	if( c != '%' )	continue;

	    c = *p++;

	if( c == '%' )	continue;



	cn = 0;

	while( c && c != '$' ){	    /* should imporve error check later */

	    cn = cn*10 + c - '0';

	    c = *p++;

	}



	if( !c || cn < 1 || cn > number ){

	    *rv = -1;

	    if( l10n_debug )

		printf( "invalid argument number (valid range [1, %d]), \"%s\"\n", number, fmt );

	    break;

        }



	/* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */

        cn--;

	if( nas[cn].type != TYPE_UNKNOWN )

	    continue;



        c = *p++;



        /* width */

        if (c == '*') {

	    /* not supported feature, for the argument is not numbered */

	    *rv = -1;

	    if( l10n_debug )

		printf( "* width specifier not support for numbered arguments \"%s\"\n", fmt );

	    break;

	} else {

	    while ((c >= '0') && (c <= '9')) {

	        c = *p++;

	    }

	}



	/* precision */

	if (c == '.') {

	    c = *p++;

	    if (c == '*') {

	        /* not supported feature, for the argument is not numbered */

		if( l10n_debug )

		    printf( "* precision specifier not support for numbered arguments \"%s\"\n", fmt );

	        *rv = -1;

	        break;

	    } else {

	        while ((c >= '0') && (c <= '9')) {

		    c = *p++;

		}

	    }

	}



	/* size */

	nas[cn].type = TYPE_INTN;

	if (c == 'h') {

	    nas[cn].type = TYPE_INT16;

	    c = *p++;

	} else if (c == 'L') {

	    /* XXX not quite sure here */

	    nas[cn].type = TYPE_INT64;

	    c = *p++;

	} else if (c == 'l') {

	    nas[cn].type = TYPE_INT32;

	    c = *p++;

	    if (c == 'l') {

	        nas[cn].type = TYPE_INT64;

	        c = *p++;

	    }

	}



	/* format */

	switch (c) {

	case 'd':

	case 'c':

	case 'i':

	case 'o':

	case 'u':

	case 'x':

	case 'X':

	    break;



	case 'e':

	case 'f':

	case 'g':

	    nas[ cn ].type = TYPE_DOUBLE;

	    break;



	case 'p':

	    /* XXX should use cpp */

	    if (sizeof(void *) == sizeof(JSInt32)) {

		nas[ cn ].type = TYPE_UINT32;

	    } else if (sizeof(void *) == sizeof(JSInt64)) {

	        nas[ cn ].type = TYPE_UINT64;

	    } else if (sizeof(void *) == sizeof(JSIntn)) {

	        nas[ cn ].type = TYPE_UINTN;

	    } else {

	        nas[ cn ].type = TYPE_UNKNOWN;

	    }

	    break;



	case 'C':

	case 'S':

	case 'E':

	case 'G':

	    /* XXX not supported I suppose */

	    JS_ASSERT(0);

	    nas[ cn ].type = TYPE_UNKNOWN;

	    break;



	case 's':

	    nas[ cn ].type = TYPE_STRING;

	    break;



	case 'n':

	    nas[ cn ].type = TYPE_INTSTR;

	    break;



	default:

	    JS_ASSERT(0);

	    nas[ cn ].type = TYPE_UNKNOWN;

	    break;

	}



	/* get a legal para. */

	if( nas[ cn ].type == TYPE_UNKNOWN ){

	    if( l10n_debug )

		printf( "unknown type \"%s\"\n", fmt );

	    *rv = -1;

	    break;

	}

    }





    /*

    ** third pass

    ** fill the nas[cn].ap

    */



    if( *rv < 0 ){

	if( nas != nasArray )

	    JS_DELETE( nas );

	return NULL;

    }



    cn = 0;

    while( cn < number ){

	if( nas[cn].type == TYPE_UNKNOWN ){

	    cn++;

	    continue;

	}



	VARARGS_ASSIGN(nas[cn].ap, ap);



	switch( nas[cn].type ){

	case TYPE_INT16:

	case TYPE_UINT16:

	case TYPE_INTN:

	case TYPE_UINTN:		(void)va_arg( ap, JSIntn );		break;



	case TYPE_INT32:		(void)va_arg( ap, JSInt32 );		break;



	case TYPE_UINT32:	(void)va_arg( ap, JSUint32 );	break;



	case TYPE_INT64:	(void)va_arg( ap, JSInt64 );		break;



	case TYPE_UINT64:	(void)va_arg( ap, JSUint64 );		break;



	case TYPE_STRING:	(void)va_arg( ap, char* );		break;



	case TYPE_INTSTR:	(void)va_arg( ap, JSIntn* );		break;



	case TYPE_DOUBLE:	(void)va_arg( ap, double );		break;



	default:

	    if( nas != nasArray )

		JS_DELETE( nas );

	    *rv = -1;

	    return NULL;

	}



	cn++;

    }





    return nas;

}



/*

** The workhorse sprintf code.

*/

static int dosprintf(SprintfState *ss, const char *fmt, va_list ap)

{

    char c;

    int flags, width, prec, radix, type;

    union {

	char ch;

	int i;

	long l;

	JSInt64 ll;

	double d;

	const char *s;

	int *ip;

    } u;

    const char *fmt0;

    static char *hex = "0123456789abcdef";

    static char *HEX = "0123456789ABCDEF";

    char *hexp;

    int rv, i;

    struct NumArgState* nas = NULL;

    struct NumArgState  nasArray[ NAS_DEFAULT_NUM ];

    char  pattern[20];

    const char* dolPt = NULL;  /* in "%4$.2f", dolPt will poiont to . */





    /*

    ** build an argument array, IF the fmt is numbered argument

    ** list style, to contain the Numbered Argument list pointers

    */



    nas = BuildArgArray( fmt, ap, &rv, nasArray );

    if( rv < 0 ){

	/* the fmt contains error Numbered Argument format, jliu@netscape.com */

	JS_ASSERT(0);

	return rv;

    }



    while ((c = *fmt++) != 0) {

	if (c != '%') {

	    rv = (*ss->stuff)(ss, fmt - 1, 1);

	    if (rv < 0) {

		return rv;

	    }

	    continue;

	}

	fmt0 = fmt - 1;



	/*

	** Gobble up the % format string. Hopefully we have handled all

	** of the strange cases!

	*/

	flags = 0;

	c = *fmt++;

	if (c == '%') {

	    /* quoting a % with %% */

	    rv = (*ss->stuff)(ss, fmt - 1, 1);

	    if (rv < 0) {

		return rv;

	    }

	    continue;

	}



	if( nas != NULL ){

	    /* the fmt contains the Numbered Arguments feature */

	    i = 0;

	    while( c && c != '$' ){	    /* should imporve error check later */

		i = ( i * 10 ) + ( c - '0' );

		c = *fmt++;

	    }



	    if( nas[i-1].type == TYPE_UNKNOWN ){

		if( l10n_debug )

		    printf( "numbered argument type unknown\n" );

		if( nas && ( nas != nasArray ) )

		    JS_DELETE( nas );

		return -1;

	    }



	    ap = nas[i-1].ap;

	    dolPt = fmt;

	    c = *fmt++;

	}



	/*

	 * Examine optional flags.  Note that we do not implement the

	 * '#' flag of sprintf().  The ANSI C spec. of the '#' flag is

	 * somewhat ambiguous and not ideal, which is perhaps why

	 * the various sprintf() implementations are inconsistent

	 * on this feature.

	 */

	while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {

	    if (c == '-') flags |= _LEFT;

	    if (c == '+') flags |= _SIGNED;

	    if (c == ' ') flags |= _SPACED;

	    if (c == '0') flags |= _ZEROS;

	    c = *fmt++;

	}

	if (flags & _SIGNED) flags &= ~_SPACED;

	if (flags & _LEFT) flags &= ~_ZEROS;



	/* width */

	if (c == '*') {

	    c = *fmt++;

	    width = va_arg(ap, int);

	} else {

	    width = 0;

	    while ((c >= '0') && (c <= '9')) {

		width = (width * 10) + (c - '0');

		c = *fmt++;

	    }

	}



	/* precision */

	prec = -1;

	if (c == '.') {

	    c = *fmt++;

	    if (c == '*') {

		c = *fmt++;

		prec = va_arg(ap, int);

	    } else {

		prec = 0;

		while ((c >= '0') && (c <= '9')) {

		    prec = (prec * 10) + (c - '0');

		    c = *fmt++;

		}

	    }

	}



	/* size */

	type = TYPE_INTN;

	if (c == 'h') {

	    type = TYPE_INT16;

	    c = *fmt++;

	} else if (c == 'L') {

	    /* XXX not quite sure here */

	    type = TYPE_INT64;

	    c = *fmt++;

	} else if (c == 'l') {

	    type = TYPE_INT32;

	    c = *fmt++;

	    if (c == 'l') {

		type = TYPE_INT64;

		c = *fmt++;

	    }

	}



	/* format */

	hexp = hex;

	switch (c) {

	  case 'd': case 'i':			/* decimal/integer */

	    radix = 10;

	    goto fetch_and_convert;



	  case 'o':				/* octal */

	    radix = 8;

	    type |= 1;

	    goto fetch_and_convert;



	  case 'u':				/* unsigned decimal */

	    radix = 10;

	    type |= 1;

	    goto fetch_and_convert;



	  case 'x':				/* unsigned hex */

	    radix = 16;

	    type |= 1;

	    goto fetch_and_convert;



	  case 'X':				/* unsigned HEX */

	    radix = 16;

	    hexp = HEX;

	    type |= 1;

	    goto fetch_and_convert;



	  fetch_and_convert:

	    switch (type) {

	      case TYPE_INT16:

		u.l = va_arg(ap, int);

		if (u.l < 0) {

		    u.l = -u.l;

		    flags |= _NEG;

		}

		goto do_long;

	      case TYPE_UINT16:

		u.l = va_arg(ap, int) & 0xffff;

		goto do_long;

	      case TYPE_INTN:

		u.l = va_arg(ap, int);

		if (u.l < 0) {

		    u.l = -u.l;

		    flags |= _NEG;

		}

		goto do_long;

	      case TYPE_UINTN:

		u.l = (long)va_arg(ap, unsigned int);

		goto do_long;



	      case TYPE_INT32:

		u.l = va_arg(ap, JSInt32);

		if (u.l < 0) {

		    u.l = -u.l;

		    flags |= _NEG;

		}

		goto do_long;

	      case TYPE_UINT32:

		u.l = (long)va_arg(ap, JSUint32);

	      do_long:

		rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);

		if (rv < 0) {

		    return rv;

		}

		break;



	      case TYPE_INT64:

		u.ll = va_arg(ap, JSInt64);

		if (!JSLL_GE_ZERO(u.ll)) {

		    JSLL_NEG(u.ll, u.ll);

		    flags |= _NEG;

		}

		goto do_longlong;

	      case TYPE_UINT64:

		u.ll = va_arg(ap, JSUint64);

	      do_longlong:

		rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);

		if (rv < 0) {

		    return rv;

		}

		break;

	    }

	    break;



	  case 'e':

	  case 'E':

	  case 'f':

	  case 'g':

	    u.d = va_arg(ap, double);

	    if( nas != NULL ){

		i = fmt - dolPt;

		if( i < (int)sizeof( pattern ) ){

		    pattern[0] = '%';

		    memcpy( &pattern[1], dolPt, (size_t)i );

		    rv = cvt_f(ss, u.d, pattern, &pattern[i+1] );

		}

	    } else

		rv = cvt_f(ss, u.d, fmt0, fmt);



	    if (rv < 0) {

		return rv;

	    }

	    break;



	  case 'c':

	    u.ch = va_arg(ap, int);

            if ((flags & _LEFT) == 0) {

                while (width-- > 1) {

                    rv = (*ss->stuff)(ss, " ", 1);

                    if (rv < 0) {

                        return rv;

                    }

                }

            }

	    rv = (*ss->stuff)(ss, &u.ch, 1);

	    if (rv < 0) {

		return rv;

	    }

            if (flags & _LEFT) {

                while (width-- > 1) {

                    rv = (*ss->stuff)(ss, " ", 1);

                    if (rv < 0) {

                        return rv;

                    }

                }

            }

	    break;



	  case 'p':

	    if (sizeof(void *) == sizeof(JSInt32)) {

	    	type = TYPE_UINT32;

	    } else if (sizeof(void *) == sizeof(JSInt64)) {

	    	type = TYPE_UINT64;

	    } else if (sizeof(void *) == sizeof(int)) {

		type = TYPE_UINTN;

	    } else {

		JS_ASSERT(0);

		break;

	    }

	    radix = 16;

	    goto fetch_and_convert;



#if 0

	  case 'C':

	  case 'S':

	  case 'E':

	  case 'G':

	    /* XXX not supported I suppose */

	    JS_ASSERT(0);

	    break;

#endif



	  case 's':

	    u.s = va_arg(ap, const char*);

	    rv = cvt_s(ss, u.s, width, prec, flags);

	    if (rv < 0) {

		return rv;

	    }

	    break;



	  case 'n':

	    u.ip = va_arg(ap, int*);

	    if (u.ip) {

		*u.ip = ss->cur - ss->base;

	    }

	    break;



	  default:

	    /* Not a % token after all... skip it */

#if 0

	    JS_ASSERT(0);

#endif

	    rv = (*ss->stuff)(ss, "%", 1);

	    if (rv < 0) {

		return rv;

	    }

	    rv = (*ss->stuff)(ss, fmt - 1, 1);

	    if (rv < 0) {

		return rv;

	    }

	}

    }



    /* Stuff trailing NUL */

    rv = (*ss->stuff)(ss, "\0", 1);



    if( nas && ( nas != nasArray ) ){

	JS_DELETE( nas );

    }



    return rv;

}



/************************************************************************/



static int FuncStuff(SprintfState *ss, const char *sp, JSUint32 len)

{

    int rv;



    rv = (*ss->func)(ss->arg, sp, len);

    if (rv < 0) {

	return rv;

    }

    ss->maxlen += len;

    return 0;

}



JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc func, void *arg, 

                                 const char *fmt, ...)

{

    va_list ap;

    int rv;



    va_start(ap, fmt);

    rv = JS_vsxprintf(func, arg, fmt, ap);

    va_end(ap);

    return rv;

}



JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc func, void *arg, 

                                  const char *fmt, va_list ap)

{

    SprintfState ss;

    int rv;



    ss.stuff = FuncStuff;

    ss.func = func;

    ss.arg = arg;

    ss.maxlen = 0;

    rv = dosprintf(&ss, fmt, ap);

    return (rv < 0) ? (JSUint32)-1 : ss.maxlen;

}



/*

** Stuff routine that automatically grows the malloc'd output buffer

** before it overflows.

*/

static int GrowStuff(SprintfState *ss, const char *sp, JSUint32 len)

{

    ptrdiff_t off;

    char *newbase;

    JSUint32 newlen;



    off = ss->cur - ss->base;

    if (off + len >= ss->maxlen) {

	/* Grow the buffer */

	newlen = ss->maxlen + ((len > 32) ? len : 32);

	if (ss->base) {

	    newbase = (char*) realloc(ss->base, newlen);

	} else {

	    newbase = (char*) malloc(newlen);

	}

	if (!newbase) {

	    /* Ran out of memory */

	    return -1;

	}

	ss->base = newbase;

	ss->maxlen = newlen;

	ss->cur = ss->base + off;

    }



    /* Copy data */

    while (len) {

	--len;

	*ss->cur++ = *sp++;

    }

    JS_ASSERT((JSUint32)(ss->cur - ss->base) <= ss->maxlen);

    return 0;

}



/*

** sprintf into a malloc'd buffer

*/

JS_PUBLIC_API(char *) JS_smprintf(const char *fmt, ...)

{

    va_list ap;

    char *rv;



    va_start(ap, fmt);

    rv = JS_vsmprintf(fmt, ap);

    va_end(ap);

    return rv;

}



/*

** Free memory allocated, for the caller, by JS_smprintf

*/

JS_PUBLIC_API(void) JS_smprintf_free(char *mem)

{

	JS_DELETE(mem);

}



JS_PUBLIC_API(char *) JS_vsmprintf(const char *fmt, va_list ap)

{

    SprintfState ss;

    int rv;



    ss.stuff = GrowStuff;

    ss.base = 0;

    ss.cur = 0;

    ss.maxlen = 0;

    rv = dosprintf(&ss, fmt, ap);

    if (rv < 0) {

	if (ss.base) {

	    JS_DELETE(ss.base);

	}

	return 0;

    }

    return ss.base;

}



/*

** Stuff routine that discards overflow data

*/

static int LimitStuff(SprintfState *ss, const char *sp, JSUint32 len)

{

    JSUint32 limit = ss->maxlen - (ss->cur - ss->base);



    if (len > limit) {

	len = limit;

    }

    while (len) {

	--len;

	*ss->cur++ = *sp++;

    }

    return 0;

}



/*

** sprintf into a fixed size buffer. Make sure there is a NUL at the end

** when finished.

*/

JS_PUBLIC_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...)

{

    va_list ap;

    int rv;



    JS_ASSERT((JSInt32)outlen > 0);

    if ((JSInt32)outlen <= 0) {

	return 0;

    }



    va_start(ap, fmt);

    rv = JS_vsnprintf(out, outlen, fmt, ap);

    va_end(ap);

    return rv;

}



JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen,const char *fmt,

                                  va_list ap)

{

    SprintfState ss;

    JSUint32 n;



    JS_ASSERT((JSInt32)outlen > 0);

    if ((JSInt32)outlen <= 0) {

	return 0;

    }



    ss.stuff = LimitStuff;

    ss.base = out;

    ss.cur = out;

    ss.maxlen = outlen;

    (void) dosprintf(&ss, fmt, ap);



    /* If we added chars, and we didn't append a null, do it now. */

    if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') )

        *(--ss.cur) = '\0';



    n = ss.cur - ss.base;

    return n ? n - 1 : n;

}



JS_PUBLIC_API(char *) JS_sprintf_append(char *last, const char *fmt, ...)

{

    va_list ap;

    char *rv;



    va_start(ap, fmt);

    rv = JS_vsprintf_append(last, fmt, ap);

    va_end(ap);

    return rv;

}



JS_PUBLIC_API(char *) JS_vsprintf_append(char *last, const char *fmt, va_list ap)

{

    SprintfState ss;

    int rv;



    ss.stuff = GrowStuff;

    if (last) {

	int lastlen = strlen(last);

	ss.base = last;

	ss.cur = last + lastlen;

	ss.maxlen = lastlen;

    } else {

	ss.base = 0;

	ss.cur = 0;

	ss.maxlen = 0;

    }

    rv = dosprintf(&ss, fmt, ap);

    if (rv < 0) {

	if (ss.base) {

	    JS_DELETE(ss.base);

	}

	return 0;

    }

    return ss.base;

}



 

**** End of jsprf.c ****

 

**** Start of jsprf.h ****

 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

/*

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsprf_h___

#define jsprf_h___



/*

** API for PR printf like routines. Supports the following formats

**	%d - decimal

**	%u - unsigned decimal

**	%x - unsigned hex

**	%X - unsigned uppercase hex

**	%o - unsigned octal

**	%hd, %hu, %hx, %hX, %ho - 16-bit versions of above

**	%ld, %lu, %lx, %lX, %lo - 32-bit versions of above

**	%lld, %llu, %llx, %llX, %llo - 64 bit versions of above

**	%s - string

**	%c - character

**	%p - pointer (deals with machine dependent pointer size)

**	%f - float

**	%g - float

*/

#include "jstypes.h"

#include <stdio.h>

#include <stdarg.h>



JS_BEGIN_EXTERN_C



/*

** sprintf into a fixed size buffer. Guarantees that a NUL is at the end

** of the buffer. Returns the length of the written output, NOT including

** the NUL, or (JSUint32)-1 if an error occurs.

*/

extern JS_PUBLIC_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...);



/*

** sprintf into a malloc'd buffer. Return a pointer to the malloc'd

** buffer on success, NULL on failure. Call "JS_smprintf_free" to release

** the memory returned.

*/

extern JS_PUBLIC_API(char*) JS_smprintf(const char *fmt, ...);



/*

** Free the memory allocated, for the caller, by JS_smprintf

*/

extern JS_PUBLIC_API(void) JS_smprintf_free(char *mem);



/*

** "append" sprintf into a malloc'd buffer. "last" is the last value of

** the malloc'd buffer. sprintf will append data to the end of last,

** growing it as necessary using realloc. If last is NULL, JS_sprintf_append

** will allocate the initial string. The return value is the new value of

** last for subsequent calls, or NULL if there is a malloc failure.

*/

extern JS_PUBLIC_API(char*) JS_sprintf_append(char *last, const char *fmt, ...);



/*

** sprintf into a function. The function "f" is called with a string to

** place into the output. "arg" is an opaque pointer used by the stuff

** function to hold any state needed to do the storage of the output

** data. The return value is a count of the number of characters fed to

** the stuff function, or (JSUint32)-1 if an error occurs.

*/

typedef JSIntn (*JSStuffFunc)(void *arg, const char *s, JSUint32 slen);



extern JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc f, void *arg, const char *fmt, ...);



/*

** va_list forms of the above.

*/

extern JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen, const char *fmt, va_list ap);

extern JS_PUBLIC_API(char*) JS_vsmprintf(const char *fmt, va_list ap);

extern JS_PUBLIC_API(char*) JS_vsprintf_append(char *last, const char *fmt, va_list ap);

extern JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc f, void *arg, const char *fmt, va_list ap);



/*

***************************************************************************

** FUNCTION: JS_sscanf

** DESCRIPTION:

**     JS_sscanf() scans the input character string, performs data

**     conversions, and stores the converted values in the data objects

**     pointed to by its arguments according to the format control

**     string.

**

**     JS_sscanf() behaves the same way as the sscanf() function in the

**     Standard C Library (stdio.h), with the following exceptions:

**     - JS_sscanf() handles the NSPR integer and floating point types,

**       such as JSInt16, JSInt32, JSInt64, and JSFloat64, whereas

**       sscanf() handles the standard C types like short, int, long,

**       and double.

**     - JS_sscanf() has no multibyte character support, while sscanf()

**       does.

** INPUTS:

**     const char *buf

**         a character string holding the input to scan

**     const char *fmt

**         the format control string for the conversions

**     ...

**         variable number of arguments, each of them is a pointer to

**         a data object in which the converted value will be stored

** OUTPUTS: none

** RETURNS: JSInt32

**     The number of values converted and stored.

** RESTRICTIONS:

**    Multibyte characters in 'buf' or 'fmt' are not allowed.

***************************************************************************

*/



extern JS_PUBLIC_API(JSInt32) JS_sscanf(const char *buf, const char *fmt, ...);



JS_END_EXTERN_C



#endif /* jsprf_h___ */

 

**** End of jsprf.h ****

 

**** Start of jsprhash.h ****

 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

/*

 * The contents of this file are subject to the Netscape Public License

 * Version 1.0 (the "NPL"); you may not use this file except in

 * compliance with the NPL.  You may obtain a copy of the NPL at

 * http://www.mozilla.org/NPL/

 * 

 * Software distributed under the NPL is distributed on an "AS IS" basis,

 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL

 * for the specific language governing rights and limitations under the

 * NPL.

 * 

 * The Initial Developer of this code under the NPL is Netscape

 * Communications Corporation.  Portions created by Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights

 * Reserved.

 */



#ifndef JSplhash_h___

#define JSplhash_h___

/*

 * API to portable hash table code.

 */

#include <stddef.h>

#include <stdio.h>

#include "jsprtypes.h"



JSPR_BEGIN_EXTERN_C



typedef struct JSPRHashEntry  JSPRHashEntry;

typedef struct JSPRHashTable  JSPRHashTable;

typedef JSPRUint32 JSPRHashNumber;

#define JSPR_HASH_BITS 32

typedef JSPRHashNumber (JSJS_DLL_CALLBACK *JSPRHashFunction)(const void *key);

typedef JSPRIntn (JSJS_DLL_CALLBACK *JSPRHashComparator)(const void *v1, const void *v2);

typedef JSPRIntn (JSJS_DLL_CALLBACK *JSPRHashEnumerator)(JSPRHashEntry *he, JSPRIntn i, void *arg);



/* Flag bits in JSPRHashEnumerator's return value */

#define HT_ENUMERATE_NEXT       0       /* continue enumerating entries */

#define HT_ENUMERATE_STOP       1       /* stop enumerating entries */

#define HT_ENUMERATE_REMOVE     2       /* remove and free the current entry */

#define HT_ENUMERATE_UNHASH     4       /* just unhash the current entry */



typedef struct JSPRHashAllocOps {

    void *              (JSJS_DLL_CALLBACK *allocTable)(void *pool, JSPRSize size);

    void                (JSJS_DLL_CALLBACK *freeTable)(void *pool, void *item);

    JSPRHashEntry *       (JSJS_DLL_CALLBACK *allocEntry)(void *pool, const void *key);

    void                (JSJS_DLL_CALLBACK *freeEntry)(void *pool, JSPRHashEntry *he, JSPRUintn flag);

} JSPRHashAllocOps;



#define HT_FREE_VALUE   0               /* just free the entry's value */

#define HT_FREE_ENTRY   1               /* free value and entire entry */



struct JSPRHashEntry {

    JSPRHashEntry         *next;          /* hash chain linkage */

    JSPRHashNumber        keyHash;        /* key hash function result */

    const void          *key;           /* ptr to opaque key */

    void                *value;         /* ptr to opaque value */

};



struct JSPRHashTable {

    JSPRHashEntry         **buckets;      /* vector of hash buckets */

    JSPRUint32              nentries;       /* number of entries in table */

    JSPRUint32              shift;          /* multiplicative hash shift */

    JSPRHashFunction      keyHash;        /* key hash function */

    JSPRHashComparator    keyCompare;     /* key comparison function */

    JSPRHashComparator    valueCompare;   /* value comparison function */

    JSPRHashAllocOps      *allocOps;      /* allocation operations */

    void                *allocPriv;     /* allocation private data */

#ifdef HASHMETER

    JSPRUint32              nlookups;       /* total number of lookups */

    JSPRUint32              nsteps;         /* number of hash chains traversed */

    JSPRUint32              ngrows;         /* number of table expansions */

    JSPRUint32              nshrinks;       /* number of table contractions */

#endif

};



/*

 * Create a new hash table.

 * If allocOps is null, use default allocator ops built on top of malloc().

 */

JSJS_EXTERN_API(JSPRHashTable *)

JSPR_NewHashTable(JSPRUint32 n, JSPRHashFunction keyHash,

                JSPRHashComparator keyCompare, JSPRHashComparator valueCompare,

                JSPRHashAllocOps *allocOps, void *allocPriv);



JSJS_EXTERN_API(void)

JSPR_HashTableDestroy(JSPRHashTable *ht);



/* Low level access methods */

JSJS_EXTERN_API(JSPRHashEntry **)

JSPR_HashTableRawLookup(JSPRHashTable *ht, JSPRHashNumber keyHash, const void *key);



JSJS_EXTERN_API(JSPRHashEntry *)

JSPR_HashTableRawAdd(JSPRHashTable *ht, JSPRHashEntry **hep, JSPRHashNumber keyHash,

                   const void *key, void *value);



JSJS_EXTERN_API(void)

JSPR_HashTableRawRemove(JSPRHashTable *ht, JSPRHashEntry **hep, JSPRHashEntry *he);



/* Higher level access methods */

JSJS_EXTERN_API(JSPRHashEntry *)

JSPR_HashTableAdd(JSPRHashTable *ht, const void *key, void *value);



JSJS_EXTERN_API(JSPRBool)

JSPR_HashTableRemove(JSPRHashTable *ht, const void *key);



JSJS_EXTERN_API(JSPRIntn)

JSPR_HashTableEnumerateEntries(JSPRHashTable *ht, JSPRHashEnumerator f, void *arg);



JSJS_EXTERN_API(void *)

JSPR_HashTableLookup(JSPRHashTable *ht, const void *key);



JSJS_EXTERN_API(JSPRIntn)

JSPR_HashTableDump(JSPRHashTable *ht, JSPRHashEnumerator dump, FILE *fp);



/* General-purpose C string hash function. */

JSJS_EXTERN_API(JSPRHashNumber)

JSPR_HashString(const void *key);



/* Compare strings using strcmp(), return true if equal. */

JSJS_EXTERN_API(int)

JSPR_CompareStrings(const void *v1, const void *v2);



/* Stub function just returns v1 == v2 */

JSJS_EXTERN_API(JSPRIntn)

JSPR_CompareValues(const void *v1, const void *v2);



JSPR_END_EXTERN_C



#endif /* JSplhash_h___ */

 

**** End of jsprhash.h ****

 

**** Start of jsprvtd.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsprvtd_h___

#define jsprvtd_h___

/*

 * JS private typename definitions.

 *

 * This header is included only in other .h files, for convenience and for

 * simplicity of type naming.  The alternative for structures is to use tags,

 * which are named the same as their typedef names (legal in C/C++, and less

 * noisy than suffixing the typedef name with "Struct" or "Str").  Instead,

 * all .h files that include this file may use the same typedef name, whether

 * declaring a pointer to struct type, or defining a member of struct type.

 *

 * A few fundamental scalar types are defined here too.  Neither the scalar

 * nor the struct typedefs should change much, therefore the nearly-global

 * make dependency induced by this file should not prove painful.

 */



#include "jspubtd.h"



/* Scalar typedefs. */

typedef uint8  jsbytecode;

typedef uint8  jssrcnote;

typedef uint32 jsatomid;



/* Struct typedefs. */

typedef struct JSArgumentFormatMap  JSArgumentFormatMap;

typedef struct JSCodeGenerator      JSCodeGenerator;

typedef struct JSGCThing            JSGCThing;

typedef struct JSParseNode          JSParseNode;

typedef struct JSSharpObjectMap     JSSharpObjectMap;

typedef struct JSToken              JSToken;

typedef struct JSTokenPos           JSTokenPos;

typedef struct JSTokenPtr           JSTokenPtr;

typedef struct JSTokenStream        JSTokenStream;

typedef struct JSTreeContext        JSTreeContext;

typedef struct JSTryNote            JSTryNote;



/* Friend "Advanced API" typedefs. */

typedef struct JSAtom               JSAtom;

typedef struct JSAtomList           JSAtomList;

typedef struct JSAtomListElement    JSAtomListElement;

typedef struct JSAtomMap            JSAtomMap;

typedef struct JSAtomState          JSAtomState;

typedef struct JSCodeSpec           JSCodeSpec;

typedef struct JSPrinter            JSPrinter;

typedef struct JSRegExp             JSRegExp;

typedef struct JSRegExpStatics      JSRegExpStatics;

typedef struct JSScope              JSScope;

typedef struct JSScopeOps           JSScopeOps;

typedef struct JSScopeProperty      JSScopeProperty;

typedef struct JSStackFrame         JSStackFrame;

typedef struct JSStackHeader        JSStackHeader;

typedef struct JSSubString          JSSubString;

typedef struct JSSymbol             JSSymbol;



/* "Friend" types used by jscntxt.h and jsdbgapi.h. */

typedef enum JSTrapStatus {

    JSTRAP_ERROR,

    JSTRAP_CONTINUE,

    JSTRAP_RETURN,

    JSTRAP_THROW,

    JSTRAP_LIMIT

} JSTrapStatus;



#ifndef CRT_CALL

#ifdef XP_OS2

#define CRT_CALL _Optlink

#else

#define CRT_CALL

#endif

#endif



typedef JSTrapStatus

(* CRT_CALL JSTrapHandler)(JSContext *cx, JSScript *script, jsbytecode *pc, 

                           jsval *rval, void *closure);



typedef JSBool

(* CRT_CALL JSWatchPointHandler)(JSContext *cx, JSObject *obj, jsval id,

                                 jsval old, jsval *newp, void *closure);



/* called just after script creation */

typedef void

(* CRT_CALL JSNewScriptHook)(JSContext  *cx,

                             const char *filename,  /* URL of script */

                             uintN      lineno,     /* line script starts */

                             JSScript   *script,

                             JSFunction *fun,

                             void       *callerdata);



/* called just before script destruction */

typedef void

(* CRT_CALL JSDestroyScriptHook)(JSContext *cx, 

                                 JSScript  *script, 

                                 void      *callerdata);



typedef void

(* CRT_CALL JSSourceHandler)(const char *filename, uintN lineno, 

                             jschar *str, size_t length, 

                             void **listenerTSData, void *closure);



/*

* This hook captures high level script execution and function calls

* (JS or native). 

* It is used by JS_SetExecuteHook to hook top level scripts and by

* JS_SetCallHook to hook function calls.

* It will get called twice per script or function call:

* just before execution begins and just after it finishes. In both cases

* the 'current' frame is that of the executing code.

*

* The 'before' param is JS_TRUE for the hook invocation before the execution

* and JS_FALSE for the invocation after the code has run.

*

* The 'ok' param is significant only on the post execution invocation to

* signify whether or not the code completed 'normally'.

*

* The 'closure' param is as passed to JS_SetExecuteHook or JS_SetCallHook 

* for the 'before'invocation, but is whatever value is returned from that 

* invocation for the 'after' invocation. Thus, the hook implementor *could*

* allocate a stucture in the 'before' invocation and return a pointer

* to that structure. The pointer would then be handed to the hook for

* the 'after' invocation. Alternately, the 'before' could just return the

* same value as in 'closure' to cause the 'after' invocation to be called 

* with the same 'closure' value as the 'before'.

*

* Returning NULL in the 'before' hook will cause the 'after' hook to

* NOT be called.

*/



typedef void *

(* CRT_CALL JSInterpreterHook)(JSContext *cx, JSStackFrame *fp, JSBool before,

                               JSBool *ok, void *closure);



typedef void

(* CRT_CALL JSObjectHook)(JSContext *cx, JSObject *obj, JSBool isNew, 

                          void *closure);



typedef JSBool

(* CRT_CALL JSDebugErrorHook)(JSContext *cx, const char *message,

                              JSErrorReport *report, void *closure);



#endif /* jsprvtd_h___ */

 

**** End of jsprvtd.h ****

 

**** Start of jspubtd.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#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 */



typedef enum JSVersion {

    JSVERSION_1_0     = 100,

    JSVERSION_1_1     = 110,

    JSVERSION_1_2     = 120,

    JSVERSION_1_3     = 130,

    JSVERSION_1_4     = 140,

    JSVERSION_1_5     = 150,

    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_LIMIT

} JSType;



/* JSObjectOps.checkAccess mode enumeration. */

typedef enum JSAccessMode {

    JSACC_PROTO,

    JSACC_PARENT,

    JSACC_IMPORT,

    JSACC_WATCH,

    JSACC_LIMIT

} JSAccessMode;



/*

 * 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 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 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;



#ifndef CRT_CALL

#ifdef XP_OS2_VACPP

#define CRT_CALL _Optlink

#else

#define CRT_CALL

#endif

#endif



/* JSClass (and JSObjectOps where appropriate) function pointer typedefs. */



typedef JSBool

(* CRT_CALL 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

(* CRT_CALL JSNewEnumerateOp)(JSContext *cx, JSObject *obj,

			      JSIterateOp enum_op,

			      jsval *statep, jsid *idp);



typedef JSBool

(* CRT_CALL JSEnumerateOp)(JSContext *cx, JSObject *obj);



typedef JSBool

(* CRT_CALL JSResolveOp)(JSContext *cx, JSObject *obj, jsval id);



typedef JSBool

(* CRT_CALL JSNewResolveOp)(JSContext *cx, JSObject *obj, jsval id, uintN flags,

			    JSObject **objp);



typedef JSBool

(* CRT_CALL JSConvertOp)(JSContext *cx, JSObject *obj, JSType type, jsval *vp);



typedef void

(* CRT_CALL JSFinalizeOp)(JSContext *cx, JSObject *obj);



typedef void

(* CRT_CALL JSStringFinalizeOp)(JSContext *cx, JSString *str);



typedef JSObjectOps *

(* CRT_CALL JSGetObjectOps)(JSContext *cx, JSClass *clasp);



typedef JSBool

(* CRT_CALL JSCheckAccessOp)(JSContext *cx, JSObject *obj, jsval id,

			     JSAccessMode mode, jsval *vp);



typedef JSBool

(* CRT_CALL JSXDRObjectOp)(JSXDRState *xdr, JSObject **objp);



typedef JSBool

(* CRT_CALL JSHasInstanceOp)(JSContext *cx, JSObject *obj, jsval v,

			     JSBool *bp);



typedef JSBool

(* CRT_CALL JSSetObjectSlotOp)(JSContext *cx, JSObject *obj, uint32 slot,

                               JSObject *pobj);



typedef uint32

(* CRT_CALL JSMarkOp)(JSContext *cx, JSObject *obj, void *arg);



/* JSObjectOps function pointer typedefs. */



typedef JSObjectMap *

(* CRT_CALL JSNewObjectMapOp)(JSContext *cx, jsrefcount nrefs,

			      JSObjectOps *ops, JSClass *clasp,

			      JSObject *obj);



typedef void

(* CRT_CALL JSObjectMapOp)(JSContext *cx, JSObjectMap *map);



typedef JSBool

(* CRT_CALL JSLookupPropOp)(JSContext *cx, JSObject *obj, jsid id,

			    JSObject **objp, JSProperty **propp

#if defined JS_THREADSAFE && defined DEBUG

			    , const char *file, uintN line

#endif

			    );



typedef JSBool

(* CRT_CALL JSDefinePropOp)(JSContext *cx, JSObject *obj, jsid id, jsval value,

			    JSPropertyOp getter, JSPropertyOp setter,

			    uintN attrs, JSProperty **propp);



typedef JSBool

(* CRT_CALL JSPropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, jsval *vp);



typedef JSBool

(* CRT_CALL JSAttributesOp)(JSContext *cx, JSObject *obj, jsid id,

			    JSProperty *prop, uintN *attrsp);



typedef JSBool

(* CRT_CALL JSCheckAccessIdOp)(JSContext *cx, JSObject *obj, jsid id,

			       JSAccessMode mode, jsval *vp, uintN *attrsp);



typedef JSObject *

(* CRT_CALL JSObjectOp)(JSContext *cx, JSObject *obj);



typedef void

(* CRT_CALL JSPropertyRefOp)(JSContext *cx, JSObject *obj, JSProperty *prop);



/* Typedef for native functions called by the JS VM. */



typedef JSBool

(* CRT_CALL 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

} JSGCStatus;



typedef JSBool

(* CRT_CALL JSGCCallback)(JSContext *cx, JSGCStatus status);



typedef JSBool

(* CRT_CALL JSBranchCallback)(JSContext *cx, JSScript *script);



typedef void

(* CRT_CALL JSErrorReporter)(JSContext *cx, const char *message,

			     JSErrorReport *report);



typedef struct JSErrorFormatString {

    const char *format;

    uintN argCount;

} JSErrorFormatString;



typedef const JSErrorFormatString *

(* CRT_CALL JSErrorCallback)(void *userRef, const char *locale,

			     const uintN errorNumber);



#ifdef va_start

#define JS_ARGUMENT_FORMATTER_DEFINED 1



typedef JSBool

(* CRT_CALL JSArgumentFormatter)(JSContext *cx, const char *format,

				 JSBool fromJS, jsval **vpp, va_list *app);

#endif



typedef JSBool 

(* CRT_CALL JSLocaleToUpperCase)(JSContext *cx, JSString *src, jsval *rval);



typedef JSBool

(* CRT_CALL JSLocaleToLowerCase)(JSContext *cx, JSString *src, jsval *rval);



typedef JSBool

(* CRT_CALL JSLocaleCompare)(JSContext *cx, JSString *src1, JSString *src2, jsval *rval);







JS_END_EXTERN_C



#endif /* jspubtd_h___ */

 

**** End of jspubtd.h ****

 

**** Start of jsregexp.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS regular expressions, after Perl.

 */

#include "jsstddef.h"

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsarena.h" /* Added by JSIFY */

#include "jsutil.h" /* Added by JSIFY */

#include "jsapi.h"

#include "jsarray.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsfun.h"

#include "jsgc.h"

#include "jsinterp.h"

#include "jslock.h"

#include "jsnum.h"

#include "jsobj.h"

#include "jsopcode.h"

#include "jsregexp.h"

#include "jsscan.h"

#include "jsstr.h"



#ifdef XP_MAC

#include <Carbon/Carbon.h>

#endif



#if JS_HAS_REGEXPS



/* Dreamweaver DREAMWEAVER dw Ultradev ULTRADEV ud VELCRO

 * 

 * This is a dreamweaver specific addition to the mozilla 

 * release of JS 1.5. Defining DREAMWEAVER_INCLUDE_EXTENDED_CHARS_IN_CLASSES

 * will include include Unicode characters above 255 in the \w \W \s and \S

 * classes. All changes are wrapped in this define.

 */

#define DREAMWEAVER_INCLUDE_EXTENDED_CHARS_IN_CLASSES



typedef struct RENode RENode;



typedef enum REOp {

    REOP_EMPTY      = 0,  /* match rest of input against rest of r.e. */

    REOP_ALT        = 1,  /* alternative subexpressions in kid and next */

    REOP_BOL        = 2,  /* beginning of input (or line if multiline) */

    REOP_EOL        = 3,  /* end of input (or line if multiline) */

    REOP_WBDRY      = 4,  /* match "" at word boundary */

    REOP_WNONBDRY   = 5,  /* match "" at word non-boundary */

    REOP_QUANT      = 6,  /* quantified atom: atom{1,2} */

    REOP_STAR       = 7,  /* zero or more occurrences of kid */

    REOP_PLUS       = 8,  /* one or more occurrences of kid */

    REOP_OPT        = 9,  /* optional subexpression in kid */

    REOP_LPAREN     = 10, /* left paren bytecode: kid is u.num'th sub-regexp */

    REOP_RPAREN     = 11, /* right paren bytecode */

    REOP_DOT        = 12, /* stands for any character */

    REOP_CCLASS     = 13, /* character class: [a-f] */

    REOP_DIGIT      = 14, /* match a digit char: [0-9] */

    REOP_NONDIGIT   = 15, /* match a non-digit char: [^0-9] */

    REOP_ALNUM      = 16, /* match an alphanumeric char: [0-9a-z_A-Z] */

    REOP_NONALNUM   = 17, /* match a non-alphanumeric char: [^0-9a-z_A-Z] */

    REOP_SPACE      = 18, /* match a whitespace char */

    REOP_NONSPACE   = 19, /* match a non-whitespace char */

    REOP_BACKREF    = 20, /* back-reference (e.g., \1) to a parenthetical */

    REOP_FLAT       = 21, /* match a flat string */

    REOP_FLAT1      = 22, /* match a single char */

    REOP_JUMP       = 23, /* for deoptimized closure loops */

    REOP_DOTSTAR    = 24, /* optimize .* to use a single opcode */

    REOP_ANCHOR     = 25, /* like .* but skips left context to unanchored r.e. */

    REOP_EOLONLY    = 26, /* $ not preceded by any pattern */

    REOP_UCFLAT     = 27, /* flat Unicode string; len immediate counts chars */

    REOP_UCFLAT1    = 28, /* single Unicode char */

    REOP_UCCLASS    = 29, /* Unicode character class, vector of chars to match */

    REOP_NUCCLASS   = 30, /* negated Unicode character class */

    REOP_BACKREFi   = 31, /* case-independent REOP_BACKREF */

    REOP_FLATi      = 32, /* case-independent REOP_FLAT */

    REOP_FLAT1i     = 33, /* case-independent REOP_FLAT1 */

    REOP_UCFLATi    = 34, /* case-independent REOP_UCFLAT */

    REOP_UCFLAT1i   = 35, /* case-independent REOP_UCFLAT1 */

    REOP_ANCHOR1    = 36, /* first-char discriminating REOP_ANCHOR */

    REOP_NCCLASS    = 37, /* negated 8-bit character class */

    REOP_DOTSTARMIN = 38, /* ungreedy version of REOP_DOTSTAR */

    REOP_LPARENNON  = 39, /* non-capturing version of REOP_LPAREN */

    REOP_RPARENNON  = 40, /* non-capturing version of REOP_RPAREN */

    REOP_ASSERT     = 41, /* zero width positive lookahead assertion */

    REOP_ASSERT_NOT = 42, /* zero width negative lookahead assertion */

    REOP_END

} REOp;



#define REOP_FLATLEN_MAX 255    /* maximum length of FLAT string */



struct RENode {

    uint8           op;         /* packed r.e. op bytecode */

    uint8           flags;      /* flags, see below */

#ifdef DEBUG

    uint16          offset;     /* bytecode offset */

#endif

    RENode          *next;      /* next in concatenation order */

    void            *kid;       /* first operand */

    union {

	void        *kid2;      /* second operand */

	jsint       num;        /* could be a number */

	jschar      chr;        /* or a character */

	struct {                /* or a quantifier range */

	    uint16  min;

	    uint16  max;

	} range;

	struct {                /* or a Unicode character class */

	    uint16  kidlen;     /* length of string at kid, in jschars */

	    uint16  bmsize;     /* bitmap size, based on max char code */

#ifdef	DREAMWEAVER_INCLUDE_EXTENDED_CHARS_IN_CLASSES

		JSBool	wordclass;

		JSBool	spaceclass;

#endif

            uint8   *bitmap;

	} ucclass;

    } u;

};



#define REOP(ren)   ((REOp)(ren)->op)



#define RENODE_ANCHORED 0x01    /* anchored at the front */

#define RENODE_SINGLE   0x02    /* matches a single char */

#define RENODE_NONEMPTY 0x04    /* does not match empty string */

#define RENODE_ISNEXT   0x08    /* ren is next after at least one node */

#define RENODE_GOODNEXT 0x10    /* ren->next is a tree-like edge in the graph */

#define RENODE_ISJOIN   0x20    /* ren is a join point in the graph */

#define RENODE_REALLOK  0x40    /* REOP_FLAT owns tempPool space to realloc */

#define RENODE_MINIMAL  0x80    /* un-greedy matching for ? * + {} */



typedef struct CompilerState {

    JSContext       *context;

    JSTokenStream   *tokenStream; /* For reporting errors */

    const jschar    *cpbegin;

    const jschar    *cpend;

    const jschar    *cp;

    uintN           flags;

    uintN           parenCount;

    size_t          progLength;

} CompilerState;



#ifdef DEBUG



#include <stdio.h>



static char *reopname[] = {

    "empty",

    "alt",

    "bol",

    "eol",

    "wbdry",

    "wnonbdry",

    "quant",

    "star",

    "plus",

    "opt",

    "lparen",

    "rparen",

    "dot",

    "cclass",

    "digit",

    "nondigit",

    "alnum",

    "nonalnum",

    "space",

    "nonspace",

    "backref",

    "flat",

    "flat1",

    "jump",

    "dotstar",

    "anchor",

    "eolonly",

    "ucflat",

    "ucflat1",

    "ucclass",

    "nucclass",

    "backrefi",

    "flati",

    "flat1i",

    "ucflati",

    "ucflat1i",

    "anchor1",

    "ncclass",

    "dotstar_min",

    "lparen_non",

    "rparen_non",

    "assert",

    "assert_not",

    "end"

};



static void

PrintChar(jschar c)

{

    if (c >> 8)

	printf("\\u%04X", c);

    else

#if !defined XP_PC || !defined _MSC_VER || _MSC_VER > 800

	putchar((char)c);

#else

	/* XXX is there a better way with MSVC1.52? */

	printf("%c", c);

#endif

}



static int gOffset = 0;



static JSBool

DumpRegExp(JSContext *cx, RENode *ren)

{

    static int level;

    JSBool ok;

    int i, len;

    jschar *cp;

    char *cstr;



    if (level == 0)

	printf("level offset  flags  description\n");

    level++;

    ok = JS_TRUE;

    do {

	printf("%5d %6d %6d %c%c%c%c%c%c%c  %s",

	       level,

               (int)ren->offset, (ren->next) ? (int)ren->next->offset : -1,

	       (ren->flags & RENODE_ANCHORED) ? 'A' : '-',

	       (ren->flags & RENODE_SINGLE)   ? 'S' : '-',

	       (ren->flags & RENODE_NONEMPTY) ? 'F' : '-',	/* F for full */

	       (ren->flags & RENODE_ISNEXT)   ? 'N' : '-',	/* N for next */

	       (ren->flags & RENODE_GOODNEXT) ? 'G' : '-',

	       (ren->flags & RENODE_ISJOIN)   ? 'J' : '-',

	       (ren->flags & RENODE_MINIMAL)  ? 'M' : '-',

	       reopname[ren->op]);



	switch (REOP(ren)) {

	  case REOP_ALT:

	    printf(" %d\n", ren->next->offset);

	    ok = DumpRegExp(cx, (RENode*) ren->kid);

	    if (!ok)

		goto out;

	    break;



	  case REOP_STAR:

	  case REOP_PLUS:

	  case REOP_OPT:

	  case REOP_ANCHOR1:

	  case REOP_ASSERT:

	  case REOP_ASSERT_NOT:

	    printf("\n");

	    ok = DumpRegExp(cx, (RENode*) ren->kid);

	    if (!ok)

		goto out;

	    break;



	  case REOP_QUANT:

	    printf(" next %d min %d max %d\n",

		   ren->next->offset, ren->u.range.min, ren->u.range.max);

	    ok = DumpRegExp(cx, (RENode*) ren->kid);

	    if (!ok)

		goto out;

	    break;



	  case REOP_LPAREN:

	    printf(" num %d\n", (int)ren->u.num);

	    ok = DumpRegExp(cx, (RENode*) ren->kid);

	    if (!ok)

		goto out;

	    break;



	  case REOP_LPARENNON:

            printf("\n");

	    ok = DumpRegExp(cx, (RENode*) ren->kid);

	    if (!ok)

		goto out;

	    break;



	  case REOP_RPAREN:

	    printf(" num %d\n", (int)ren->u.num);

	    break;



	  case REOP_CCLASS:

	    len = (jschar *)ren->u.kid2 - (jschar *)ren->kid;

	    cstr = js_DeflateString(cx, (jschar *)ren->kid, len);

	    if (!cstr) {

		ok = JS_FALSE;

		goto out;

	    }

	    printf(" [%s]\n", cstr);

	    JS_free(cx, cstr);

	    break;



	  case REOP_BACKREF:

	    printf(" num %d\n", (int)ren->u.num);

	    break;



	  case REOP_FLAT:

	    len = (jschar *)ren->u.kid2 - (jschar *)ren->kid;

	    cstr = js_DeflateString(cx, (jschar *)ren->kid, len);

	    if (!cstr) {

		ok = JS_FALSE;

		goto out;

	    }

	    printf(" %s (%d)\n", cstr, len);

	    JS_free(cx, cstr);

	    break;



	  case REOP_FLAT1:

	    printf(" %c ('\\%o')\n", (char)ren->u.chr, ren->u.chr);

	    break;



	  case REOP_JUMP:

	    printf(" %d\n", ren->next->offset);

	    break;



	  case REOP_UCFLAT:

	    cp = (jschar*) ren->kid;

	    len = (jschar *)ren->u.kid2 - cp;

	    for (i = 0; i < len; i++)

		PrintChar(cp[i]);

            printf("\n");

	    break;



	  case REOP_UCFLAT1:

	    PrintChar(ren->u.chr);

	    printf("\n");

	    break;



	  case REOP_UCCLASS:

	    cp = (jschar*) ren->kid;

	    len = ren->u.ucclass.kidlen;

	    printf(" [");

	    for (i = 0; i < len; i++)

		PrintChar(cp[i]);

	    printf("]\n");

	    break;



	  default:

	    printf("\n");

	    break;

	}



	if (!(ren->flags & RENODE_GOODNEXT))

	    break;

    } while ((ren = ren->next) != NULL);

out:

    level--;

    return ok;

}



#endif /* DEBUG */



static RENode *

NewRENode(CompilerState *state, REOp op, void *kid)

{

    JSContext *cx;

    RENode *ren;



    cx = state->context;

    ren = (RENode*) JS_malloc(cx, sizeof *ren);

    if (!ren) {

	JS_ReportOutOfMemory(cx);

	return NULL;

    }

    ren->op = (uint8)op;

    ren->flags = 0;

#ifdef DEBUG

    ren->offset = gOffset++;

#endif

    ren->next = NULL;

    ren->kid = kid;

    return ren;

}



static JSBool

FixNext(CompilerState *state, RENode *ren1, RENode *ren2, RENode *oldnext)

{

    JSBool goodnext;

    RENode *next, *kid, *ren;



    goodnext = ren2 && !(ren2->flags & RENODE_ISNEXT);



    /*

     * Find the final node in a list of alternatives, or concatenations, or

     * even a concatenation of alternatives followed by non-alternatives (e.g.

     * ((x|y)z)w where ((x|y)z) is ren1 and w is ren2).

     */

    for (; (next = ren1->next) != NULL && next != oldnext; ren1 = next) {

	if (REOP(ren1) == REOP_ALT) {

	    /* Find the end of this alternative's operand list. */

	    kid = (RENode*) ren1->kid;

	    if (REOP(kid) == REOP_JUMP)

		continue;

	    for (ren = kid; ren->next; ren = ren->next)

		JS_ASSERT(REOP(ren) != REOP_ALT);



	    /* Append a jump node to all but the last alternative. */

	    ren->next = NewRENode(state, REOP_JUMP, NULL);

	    if (!ren->next)

		return JS_FALSE;

	    ren->next->flags |= RENODE_ISNEXT;

	    ren->flags |= RENODE_GOODNEXT;



	    /* Recur to fix all descendent nested alternatives. */

	    if (!FixNext(state, kid, ren2, oldnext))

		return JS_FALSE;

	}

    }



    /*

     * Now ren1 points to the last alternative, or to the final node on a

     * concatenation list.  Set its next link to ren2, flagging a join point

     * if appropriate.

     */

    if (ren2) {

	if (!(ren2->flags & RENODE_ISNEXT))

	    ren2->flags |= RENODE_ISNEXT;

	else

	    ren2->flags |= RENODE_ISJOIN;

    }

    ren1->next = ren2;

    if (goodnext)

	ren1->flags |= RENODE_GOODNEXT;



    /*

     * The following ops have a kid subtree through which to recur.  Here is

     * where we fix the next links under the final ALT node's kid.

     */

    switch (REOP(ren1)) {

      case REOP_ALT:

      case REOP_QUANT:

      case REOP_STAR:

      case REOP_PLUS:

      case REOP_OPT:

      case REOP_LPAREN:

      case REOP_LPARENNON:

      case REOP_ASSERT:

      case REOP_ASSERT_NOT:

	if (!FixNext(state, (RENode*) ren1->kid, ren2, oldnext))

	    return JS_FALSE;

	break;

      default:;

    }

    return JS_TRUE;

}



static JSBool

SetNext(CompilerState *state, RENode *ren1, RENode *ren2)

{

    return FixNext(state, ren1, ren2, NULL);

}



/*

 * Parser forward declarations.

 */

typedef RENode *REParser(CompilerState *state);



static REParser ParseRegExp;

static REParser ParseAltern;

static REParser ParseItem;

static REParser ParseQuantAtom;

static REParser ParseAtom;



/*

 * Top-down regular expression grammar, based closely on Perl4.

 *

 *  regexp:     altern                  A regular expression is one or more

 *              altern '|' regexp       alternatives separated by vertical bar.

 */

static RENode *

ParseRegExp(CompilerState *state)

{

    RENode *ren, *kid, *ren1, *ren2;

    const jschar *cp;



    ren = ParseAltern(state);

    if (!ren)

	return NULL;

    cp = state->cp;

    if ((cp < state->cpend) && (*cp == '|')) {

	kid = ren;

	ren = NewRENode(state, REOP_ALT, kid);

	if (!ren)

	    return NULL;

	ren->flags = kid->flags & (RENODE_ANCHORED | RENODE_NONEMPTY);

	ren1 = ren;

	do {

	    /* (balance: */

	    state->cp = ++cp;

	    if (*cp == '|' || *cp == ')') {

		kid = NewRENode(state, REOP_EMPTY, NULL);

	    } else {

		kid = ParseAltern(state);

		cp = state->cp;

	    }

	    if (!kid)

		return NULL;

	    ren2 = NewRENode(state, REOP_ALT, kid);

	    if (!ren2)

		return NULL;

	    ren1->next = ren2;

	    ren1->flags |= RENODE_GOODNEXT;

	    ren2->flags = (kid->flags & (RENODE_ANCHORED | RENODE_NONEMPTY))

			  | RENODE_ISNEXT;

	    ren1 = ren2;

	} while ((cp < state->cpend) && (*cp == '|'));

    }

    return ren;

}



/*

 *  altern:     item                    An alternative is one or more items,

 *              item altern             concatenated together.

 */

static RENode *

ParseAltern(CompilerState *state)

{

    RENode *ren, *ren1, *ren2;

    uintN flags;

    const jschar *cp;

    jschar c;



    ren = ren1 = ParseItem(state);

    if (!ren)

	return NULL;

    flags = 0;

    cp = state->cp;

    /* (balance: */

    while ((cp < state->cpend) && (c = *cp) != '|' && c != ')') {

	ren2 = ParseItem(state);

	if (!ren2)

	    return NULL;

	if (!SetNext(state, ren1, ren2))

	    return NULL;

	flags |= ren2->flags;

	ren1 = ren2;

	cp = state->cp;

    }



    /*

     * Propagate NONEMPTY to the front of a concatenation list, so that the

     * first alternative in (^a|b) is considered non-empty.  The first node

     * in a list may match the empty string (as ^ does), but if the list is

     * non-empty, then the first node's NONEMPTY flag must be set.

     */

    ren->flags |= flags & RENODE_NONEMPTY;

    return ren;

}



/*

 *  item:       assertion               An item is either an assertion or

 *              quantatom               a quantified atom.

 *

 *  assertion:  '^'                     Assertions match beginning of string

 *                                      (or line if the class static property

 *                                      RegExp.multiline is true).

 *              '$'                     End of string (or line if the class

 *                                      static property RegExp.multiline is

 *                                      true).

 *              '\b'                    Word boundary (between \w and \W).

 *              '\B'                    Word non-boundary.

 */

static RENode *

ParseItem(CompilerState *state)

{

    const jschar *cp;

    RENode *ren;

    REOp op;



    cp = state->cp;

    if (cp < state->cpend)

        switch (*cp) {

          case '^':

	    state->cp = cp + 1;

	    ren = NewRENode(state, REOP_BOL, NULL);

	    if (ren)

	        ren->flags |= RENODE_ANCHORED;

	    return ren;



          case '$':

	    state->cp = cp + 1;

	    return NewRENode(state,

			     (cp == state->cpbegin ||

			      ((cp[-1] == '(' || cp[-1] == '|') && /*balance)*/

			       (cp - 1 == state->cpbegin || cp[-2] != '\\')))

			     ? REOP_EOLONLY

			     : REOP_EOL,

			     NULL);



          case '\\':

	    switch (*++cp) {

	      case 'b':

	        op = REOP_WBDRY;

	        break;

	      case 'B':

	        op = REOP_WNONBDRY;

	        break;

	      default:

	        return ParseQuantAtom(state);

	    }



	    /*

	     * Word boundaries and non-boundaries are flagged as non-empty so they

	     * will be prefixed by an anchoring node.

	     */

	    state->cp = cp + 1;

	    ren = NewRENode(state, op, NULL);

	    if (ren)

	        ren->flags |= RENODE_NONEMPTY;

	    return ren;



          default:;

        }

    return ParseQuantAtom(state);

}



/*

 *  quantatom:  atom                    An unquantified atom.

 *              quantatom '{' n ',' m '}'

 *                                      Atom must occur between n and m times.

 *              quantatom '{' n ',' '}' Atom must occur at least n times.

 *              quantatom '{' n '}'     Atom must occur exactly n times.

 *              quantatom '*'           Zero or more times (same as {0,}).

 *              quantatom '+'           One or more times (same as {1,}).

 *              quantatom '?'           Zero or one time (same as {0,1}).

  *

 *              any of which can be optionally followed by '?' for ungreedy

*/

static RENode *

ParseQuantAtom(CompilerState *state)

{

    RENode *ren, *ren2;

    const jschar *cp, *up;

    jschar c;

    uint32 min, max;



    ren = ParseAtom(state);

    if (!ren)

	return NULL;



    cp = state->cp;

loop:

    if (cp < state->cpend)

        switch (*cp) {

          case '{':

	    c = *++cp;

	    if (!JS7_ISDEC(c)) {

                js_ReportCompileErrorNumber(state->context, state->tokenStream,

                                            NULL, JSREPORT_ERROR,

                                            JSMSG_BAD_QUANTIFIER, state->cp);

	        return NULL;

	    }

	    min = (uint32)JS7_UNDEC(c);

	    for (c = *++cp; JS7_ISDEC(c); c = *++cp) {

	        min = 10 * min + (uint32)JS7_UNDEC(c);

	        if (min >> 16) {

                    js_ReportCompileErrorNumber(state->context,

                                                state->tokenStream, NULL,

                                                JSREPORT_ERROR,

                                                JSMSG_MIN_TOO_BIG, state->cp);

		    return NULL;

	        }

	    }

	    if (*cp == ',') {

	        up = ++cp;

	        if (JS7_ISDEC(*cp)) {

		    max = (uint32)JS7_UNDEC(*cp);

		    for (c = *++cp; JS7_ISDEC(c); c = *++cp) {

		        max = 10 * max + (uint32)JS7_UNDEC(c);

		        if (max >> 16) {

                            js_ReportCompileErrorNumber(state->context,

                                                        state->tokenStream,

                                                        NULL,

                                                        JSREPORT_ERROR,

                                                        JSMSG_MAX_TOO_BIG, up);

			    return NULL;

		        }

		    }

		    if (max == 0)

		        goto zero_quant;

		    if (min > max) {

                        js_ReportCompileErrorNumber(state->context,

                                                    state->tokenStream,

                                                    NULL,

                                                    JSREPORT_ERROR,

                                                    JSMSG_OUT_OF_ORDER, up);

		        return NULL;

		    }

	        } else {

		    /* 0 means no upper bound. */

		    max = 0;

	        }

	    } else {

	        /* Exactly n times. */

	        if (min == 0) {

          zero_quant:

                    js_ReportCompileErrorNumber(state->context,

                                                state->tokenStream,

                                                NULL,

                                                JSREPORT_ERROR,

                                                JSMSG_ZERO_QUANTIFIER,

                                                state->cp);

		    return NULL;

	        }

	        max = min;

	    }

	    if (*cp != '}') {

                js_ReportCompileErrorNumber(state->context,

                                            state->tokenStream,

                                            NULL,

                                            JSREPORT_ERROR,

                                            JSMSG_UNTERM_QUANTIFIER, state->cp);

	        return NULL;

	    }

	    cp++;



	    ren2 = NewRENode(state, REOP_QUANT, ren);

	    if (!ren2)

	        return NULL;

	    if (min > 0 && (ren->flags & RENODE_NONEMPTY))

	        ren2->flags |= RENODE_NONEMPTY;

	    ren2->u.range.min = (uint16)min;

	    ren2->u.range.max = (uint16)max;

	    ren = ren2;

            goto parseMinimalFlag;



          case '*':

	    cp++; 

	    ren = NewRENode(state, REOP_STAR, ren);

    parseMinimalFlag :

            if (*cp == '?') {

                ren->flags |= RENODE_MINIMAL;

                cp++;

            }

	    goto loop;



          case '+':

	    cp++;

	    ren2 = NewRENode(state, REOP_PLUS, ren);

	    if (!ren2)

	        return NULL;

	    if (ren->flags & RENODE_NONEMPTY)

	        ren2->flags |= RENODE_NONEMPTY;

	    ren = ren2;

	    goto parseMinimalFlag;



          case '?':

	    cp++;

	    ren = NewRENode(state, REOP_OPT, ren);

	    goto parseMinimalFlag;

        }



    state->cp = cp;

    return ren;

}



/*

 *  atom:       '(' regexp ')'          A parenthesized regexp (what matched

 *                                      can be addressed using a backreference,

 *                                      see '\' n below).

 *              '.'                     Matches any char except '\n'.

 *              '[' classlist ']'       A character class.

 *              '[' '^' classlist ']'   A negated character class.

 *              '\f'                    Form Feed.

 *              '\n'                    Newline (Line Feed).

 *              '\r'                    Carriage Return.

 *              '\t'                    Horizontal Tab.

 *              '\v'                    Vertical Tab.

 *              '\d'                    A digit (same as [0-9]).

 *              '\D'                    A non-digit.

 *              '\w'                    A word character, [0-9a-z_A-Z].

 *              '\W'                    A non-word character.

 *              '\s'                    A whitespace character, [ \b\f\n\r\t\v].

 *              '\S'                    A non-whitespace character.

 *              '\' n                   A backreference to the nth (n decimal

 *                                      and positive) parenthesized expression.

 *              '\' octal               An octal escape sequence (octal must be

 *                                      two or three digits long, unless it is

 *                                      0 for the null character).

 *              '\x' hex                A hex escape (hex must be two digits).

 *              '\c' ctrl               A control character, ctrl is a letter.

 *              '\' literalatomchar     Any character except one of the above

 *                                      that follow '\' in an atom.

 *              otheratomchar           Any character not first among the other

 *                                      atom right-hand sides.

 */

static jschar metachars[] = {

    '|', '^', '$', '{', '*', '+', '?', '(', ')', '.', '[', '\\', '}', 0

};



static jschar closurechars[] = {

    '{', '*', '+', '?', 0	/* balance} */

};



static RENode *

ParseAtom(CompilerState *state)

{

    const jschar *cp, *ocp;

    uintN tmp, num, len;

    RENode *ren, *ren2;

    jschar c;

    REOp op;



    cp = ocp = state->cp;



        /* handle /|a/ by returning an empty node for the leftside */

    if ((cp == state->cpend) || (*cp == '|'))

        return NewRENode(state, REOP_EMPTY, NULL);



    ren = NULL; /* suppress warning */

    switch (*cp) {

      case '(':

        num = -1;   /* suppress warning */

        op = REOP_END;

        if (cp[1] == '?') {

            switch (cp[2]) {

                case ':' :

                    op = REOP_LPARENNON;

                    break;

                case '=' :

                    op = REOP_ASSERT;

                    break;

                case '!' :

                    op = REOP_ASSERT_NOT;

                    break;

            }

        }

        if (op == REOP_END) {

            num = state->parenCount++;	/* \1 is numbered 0, etc. */

            op = REOP_LPAREN;

            cp++;

        }

        else

            cp += 3;

        state->cp = cp;

        /* Handle empty paren */

        if (*cp == ')') {

            ren2 = NewRENode(state, REOP_EMPTY, NULL);

        }

        else {

            ren2 = ParseRegExp(state);

            if (!ren2)

                return NULL;

            cp = state->cp;

            if (*cp != ')') {

                js_ReportCompileErrorNumber(state->context, state->tokenStream,

                                            NULL,

                                            JSREPORT_ERROR,

                                            JSMSG_MISSING_PAREN, ocp);

                return NULL;

            }

        }

        cp++;

        ren = NewRENode(state, op, ren2);

        if (!ren)

	    return NULL;

        ren->flags = ren2->flags & (RENODE_ANCHORED | RENODE_NONEMPTY);

        ren->u.num = num;

        if ((op == REOP_LPAREN) || (op == REOP_LPARENNON)) {

                /* Assume RPAREN ops immediately succeed LPAREN ops */

            ren2 = NewRENode(state, (REOp)(op + 1), NULL);

            if (!ren2 || !SetNext(state, ren, ren2))

                return NULL;

            ren2->u.num = num;

        }

        break;



      case '.':

	cp++;

        op = REOP_DOT;

        if (*cp == '*') {

	    cp++;

            op = REOP_DOTSTAR;

            if (*cp == '?') {

                cp++;

                op = REOP_DOTSTARMIN;

            }

        }

	ren = NewRENode(state, op, NULL);

	if (ren && op == REOP_DOT)

	    ren->flags = RENODE_SINGLE | RENODE_NONEMPTY;

	break;



      case '[':

        ++cp;

        ren = NewRENode(state, REOP_CCLASS, (void *)cp);

	if (!ren)

	    return NULL;



        while ((c = *++cp) != ']') {

	    if (cp == state->cpend) {

                js_ReportCompileErrorNumber(state->context, state->tokenStream,

                                            NULL,

                                            JSREPORT_ERROR,

                                            JSMSG_UNTERM_CLASS, ocp);

		return NULL;

	    }

	    if (c == '\\' && (cp+1 != state->cpend))

		cp++;

	}

	ren->u.kid2 = (void *)cp++;



        ren->u.ucclass.bitmap = NULL;



	/* Since we rule out [] and [^], we can set the non-empty flag. */

	ren->flags = RENODE_SINGLE | RENODE_NONEMPTY;

	break;



      case '\\':

	c = *++cp;

        if (cp == state->cpend) {

            js_ReportCompileErrorNumber(state->context, state->tokenStream,

                                        NULL,

                                        JSREPORT_ERROR,

                                        JSMSG_TRAILING_SLASH);

	    return NULL;

        }

	switch (c) {

	  case 'f':

	  case 'n':

	  case 'r':

	  case 't':

	  case 'v':

	    ren = NewRENode(state, REOP_FLAT1, NULL);

	    c = js_strchr(js_EscapeMap, c)[-1];

	    break;



	  case 'd':

	    ren = NewRENode(state, REOP_DIGIT, NULL);

	    break;

	  case 'D':

	    ren = NewRENode(state, REOP_NONDIGIT, NULL);

	    break;

	  case 'w':

	    ren = NewRENode(state, REOP_ALNUM, NULL);

	    break;

	  case 'W':

	    ren = NewRENode(state, REOP_NONALNUM, NULL);

	    break;

	  case 's':

	    ren = NewRENode(state, REOP_SPACE, NULL);

	    break;

	  case 'S':

	    ren = NewRENode(state, REOP_NONSPACE, NULL);

	    break;

	  case '0':

	  case '1':

	  case '2':

	  case '3':

	  case '4':

	  case '5':

	  case '6':

	  case '7':

	  case '8':

	  case '9':

            /*

                Yuk. Keeping the old style \n interpretation for 1.2

                compatibility.

            */

            if (state->context->version != JSVERSION_DEFAULT &&

			      state->context->version <= JSVERSION_1_4) {

                switch (c) {

	          case '0':

do_octal:

	            num = 0;

	            while ('0' <= (c = *++cp) && c <= '7') {

		        tmp = 8 * num + (uintN)JS7_UNDEC(c);

		        if (tmp > 0377)

		            break;

		        num = tmp;

	            }

	            cp--;

	            ren = NewRENode(state, REOP_FLAT1, NULL);

	            c = (jschar)num;

	            break;



	          case '1':

	          case '2':

	          case '3':

	          case '4':

	          case '5':

	          case '6':

	          case '7':

	          case '8':

	          case '9':

	            num = (uintN)JS7_UNDEC(c);

	            tmp = 1;

                    for (c = *++cp; JS7_ISDEC(c); c = *++cp, tmp++)

		        num = 10 * num + (uintN)JS7_UNDEC(c);

                    /* n in [8-9] and > count of parenetheses, then revert to

                    '8' or '9', ignoring the '\' */

                    if (((num == 8) || (num == 9)) && (num > state->parenCount)) {

	                ocp = --cp; /* skip beyond the '\' */

                        goto do_flat;

                    }

                    /* more than 1 digit, or a number greater than

                        the count of parentheses => it's an octal */

                    if ((tmp > 1) || (num > state->parenCount)) {

		        cp = ocp;

		        goto do_octal;

	            }

	            cp--;

	            ren = NewRENode(state, REOP_BACKREF, NULL);

	            if (!ren)

		        return NULL;

	            ren->u.num = num - 1;	/* \1 is numbered 0, etc. */



	            /* Avoid common chr- and flags-setting code after switch. */

	            ren->flags = RENODE_NONEMPTY;

	            goto bump_cp;

                }

            }

            else {

                if (c == '0') {

            	    ren = NewRENode(state, REOP_FLAT1, NULL);

                    c = 0;

                }

                else {

                    num = (uintN)JS7_UNDEC(c);

                    for (c = *++cp; JS7_ISDEC(c); c = *++cp)

		        num = 10 * num + (uintN)JS7_UNDEC(c);

                    cp--;

	            ren = NewRENode(state, REOP_BACKREF, NULL);

	            if (!ren)

		        return NULL;

	            ren->u.num = num - 1;	/* \1 is numbered 0, etc. */

	            /* Avoid common chr- and flags-setting code after switch. */

	            ren->flags = RENODE_NONEMPTY;

	            goto bump_cp;

                }

            }

            break;



	  case 'x':

	    ocp = cp;

	    c = *++cp;

	    if (JS7_ISHEX(c)) {

		num = JS7_UNHEX(c);

		c = *++cp;

		if (JS7_ISHEX(c)) {

		    num <<= 4;

		    num += JS7_UNHEX(c);

		} else {

                    if (state->context->version != JSVERSION_DEFAULT &&

			      state->context->version <= JSVERSION_1_4)

		        cp--;	/* back up so cp points to last hex char */

                    else

                        goto nothex; /* ECMA 2 insists on pairs */

		}

	    } else {

nothex:

		cp = ocp;	/* \xZZ is xZZ (Perl does \0ZZ!) */

		num = 'x';

	    }

	    ren = NewRENode(state, REOP_FLAT1, NULL);

	    c = (jschar)num;

	    break;



	  case 'c':

	    c = *++cp;

	    if (!JS7_ISLET(c)) {

		cp -= 2;

		ocp = cp;

		goto do_flat;

	    }

	    c = (jschar)JS_TOUPPER(c);

	    c = JS_TOCTRL(c);

	    ren = NewRENode(state, REOP_FLAT1, NULL);

	    break;



	  case 'u':

	    if (JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) &&

		JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4])) {

		c = (((((JS7_UNHEX(cp[1]) << 4) + JS7_UNHEX(cp[2])) << 4)

		      + JS7_UNHEX(cp[3])) << 4) + JS7_UNHEX(cp[4]);

		cp += 4;

		ren = NewRENode(state, REOP_FLAT1, NULL);

		break;

	    }



	    /* Unlike Perl \xZZ, we take \uZZZ to be literal-u then ZZZ. */

	    ocp = cp;

	    goto do_flat;



	  default:

	    ocp = cp;

	    goto do_flat;

	}



	/* Common chr- and flags-setting code for escape opcodes. */

	if (ren) {

	    ren->u.chr = c;

	    ren->flags = RENODE_SINGLE | RENODE_NONEMPTY;

	}



      bump_cp:

	/* Skip to next unparsed char. */

	cp++;

	break;



      default:



      do_flat:

	while ((c = *++cp) && (cp != state->cpend) && !js_strchr(metachars, c))

	    ;

	len = (uintN)(cp - ocp);

	if ((cp != state->cpend) && len > 1 && js_strchr(closurechars, c)) {

	    cp--;

	    len--;

	}

	if (len > REOP_FLATLEN_MAX) {

	    len = REOP_FLATLEN_MAX;

	    cp = ocp + len;

	}

	ren = NewRENode(state, (len == 1) ? REOP_FLAT1 : REOP_FLAT,

			(void *)ocp);

	if (!ren)

	    return NULL;

	ren->flags = RENODE_NONEMPTY;

	if (len > 1) {

	    ren->u.kid2 = (void *)cp;

	} else {

	    ren->flags |= RENODE_SINGLE;

	    ren->u.chr = *ocp;

	}

	break;

    }



    state->cp = cp;

    return ren;

}



JSRegExp *

js_NewRegExp(JSContext *cx, JSTokenStream *ts,

             JSString *str, uintN flags, JSBool flat)

{

    JSRegExp *re;

    void *mark;

    CompilerState state;

    RENode *ren, *ren2, *end;

    size_t resize;



    const jschar *cp;

    uintN len;



    re = NULL;

    mark = JS_ARENA_MARK(&cx->tempPool);



    state.context = cx;

    state.tokenStream = ts;

    state.cpbegin = state.cp = str->chars;

    state.cpend = state.cp + str->length;

    state.flags = flags;

    state.parenCount = 0;

    state.progLength = 0;



    if (flat) {

        len = str->length;

        cp = str->chars;

        ren = NULL;

        while (len > 0) {

	    ren2 = NewRENode(&state, 

                        (len == 1) ? REOP_FLAT1 : REOP_FLAT, (void *)cp);

	    if (!ren2)

	        goto out;

	    ren2->flags = RENODE_NONEMPTY;

	    if (len > 1) {

                if (len > REOP_FLATLEN_MAX) {

                    cp += REOP_FLATLEN_MAX;

                    len -= REOP_FLATLEN_MAX;

                }

                else {

                    cp += len;

                    len = 0;

                }

	        ren2->u.kid2 = (void *)cp;                

	    } else {

	        ren2->flags |= RENODE_SINGLE;

	        ren2->u.chr = *cp;

                len = 0;

            }

            if (ren) {

                if (!SetNext(&state, ren, ren2))

                    goto out;

            }

            else

                ren = ren2;

        }

    }

    else

        ren = ParseRegExp(&state);

    if (!ren)

	goto out;



    end = NewRENode(&state, REOP_END, NULL);

    if (!end || !SetNext(&state, ren, end))

	goto out;



#ifdef DEBUG_notme

    if (!DumpRegExp(cx, ren))

	goto out;

#endif



    resize = sizeof *re + state.progLength - 1;

    re = (JSRegExp*) JS_malloc(cx, JS_ROUNDUP(resize, sizeof(jsword)));

    if (!re)

	goto out;

    re->source = str;

    re->lastIndex = 0;

    re->parenCount = state.parenCount;

    re->flags = flags;



    re->ren = ren;



    /* Success: lock re->source string. */

    (void) js_LockGCThing(cx, str);

out:

    JS_ARENA_RELEASE(&cx->tempPool, mark);

    return re;

}



JSRegExp *

js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts, 

                JSString *str, JSString *opt, JSBool flat)

{

    uintN flags;

    jschar *cp;



    flags = 0;

    if (opt) {

	for (cp = opt->chars; *cp; cp++) {

	    switch (*cp) {

	      case 'g':

		flags |= JSREG_GLOB;

		break;

	      case 'i':

		flags |= JSREG_FOLD;

		break;

	      case 'm':

		flags |= JSREG_MULTILINE;

		break;

	      default: {

		char charBuf[2] = " ";

		charBuf[0] = (char)*cp;

                js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                            JSMSG_BAD_FLAG, charBuf);

		return NULL;

	      }

	    }

	}

    }

    return js_NewRegExp(cx, ts, str, flags, flat);

}



static void freeRENtree(JSContext *cx, RENode *ren,  RENode *stop)

{

    while (ren && (ren != stop)) {

        RENode *n;

        switch (ren->op) {

            case REOP_ALT: {

                        RENode *altStop = (RENode *)(ren->next);

                        while (altStop && (altStop->op == REOP_ALT))

                            altStop = altStop->next;

                        freeRENtree(cx, (RENode *)(ren->kid), altStop);

                    }

                    break;

            case REOP_QUANT:

            case REOP_PLUS:

            case REOP_STAR:

            case REOP_OPT:

            case REOP_LPAREN:

            case REOP_LPARENNON:

            case REOP_ASSERT:

            case REOP_ASSERT_NOT:

                freeRENtree(cx, (RENode *)(ren->kid), (RENode *)(ren->next));

                break;

            case REOP_CCLASS:

                if (ren->u.ucclass.bitmap)

                    JS_free(cx, ren->u.ucclass.bitmap);

                break;

        }

        n = ren->next;

        JS_free(cx, ren);

        ren = n;

    }

}



void

js_DestroyRegExp(JSContext *cx, JSRegExp *re)

{

    js_UnlockGCThing(cx, re->source);

    freeRENtree(cx, re->ren, NULL);

    JS_free(cx, re);

}



typedef struct MatchState {

    JSContext       *context;           /* for access to regExpStatics */

    JSBool          anchoring;          /* true if multiline anchoring ^/$ */

    jsbytecode      *pcend;             /* pc limit (fencepost) */

    const jschar    *cpbegin, *cpend;   /* cp base address and limit */

    size_t          start;              /* offset from cpbegin to start at */

    ptrdiff_t       skipped;            /* chars skipped anchoring this r.e. */

    uint8           flags;              /* flags */

    uintN           parenCount;         /* number of paren substring matches */

    JSSubString     *maybeParens;       /* possible paren substring pointers */

    JSSubString     *parens;            /* certain paren substring matches */

    JSBool          ok;                 /* indicates runtime error during matching */

    const jschar    *complete;

} MatchState;



/*

 * Returns updated cp on match, null on mismatch.

 */



static JSBool matchChar(int flags, jschar c, jschar c2)

{

    if (c == c2)

        return JS_TRUE;

    else

        if ((flags & JSREG_FOLD) != 0) {

            return ((c = (jschar)JS_TOUPPER(c)) == (c2 = (jschar)JS_TOUPPER(c2)))

                    || (JS_TOLOWER(c) == JS_TOLOWER(c2));

        }

        else

            return JS_FALSE;

}



static const jschar *matchRENodes(MatchState *state, RENode *ren, RENode *stop,

                                                const jschar *cp);



typedef struct {

    MatchState *state;

    RENode *kid;

    RENode *next;

    RENode *stop;

    int kidCount;

    int maxKid;

} GreedyState;





static const jschar *greedyRecurse(GreedyState *grState, const jschar *cp, const jschar *previousKid)

{

    const jschar *kidMatch;

    const jschar *match;

    int num = grState->state->parenCount;



#ifdef XP_MAC

    if (StackSpace() < 16384) {

        JS_ReportOutOfMemory (grState->state->context);

        grState->state->ok = JS_FALSE;

        return NULL;

    }

#endif



/*

*    When the kid match fails, we reset the parencount and run any 

*    previously succesful kid in order to restablish it's paren

*    contents.

*/

    kidMatch = matchRENodes(grState->state, grState->kid, grState->next, cp);

    grState->state->complete = NULL;



    if (kidMatch == NULL) {        

            grState->state->parenCount = num;

            if (previousKid != NULL)

                matchRENodes(grState->state, grState->kid, grState->next, previousKid);

        match = matchRENodes(grState->state, grState->next, grState->stop, cp);

        if (match) {

            if (grState->stop == NULL) {

                grState->state->complete = match;

                return cp;

            }

            return cp;

        }

            return NULL;

    }

    else {

        if (kidMatch == cp) {

            if (previousKid != NULL)

                matchRENodes(grState->state, grState->kid, grState->next, previousKid);

            return kidMatch;    /* no point pursuing an empty match forever */

        }

        if (!grState->maxKid || (++grState->kidCount < grState->maxKid)) {

            match = greedyRecurse(grState, kidMatch, cp);

            if (match)

                return match;

            if (grState->maxKid)

            --grState->kidCount;

        }

        grState->state->parenCount = num;

            matchRENodes(grState->state, grState->kid, grState->next, cp);



        if ((match = matchRENodes(grState->state, grState->next, grState->stop, kidMatch))) {

            if (grState->stop == NULL) {

                grState->state->complete = match;

            return kidMatch;

        }

            matchRENodes(grState->state, grState->kid, grState->next, cp);

            return kidMatch;

        }

            return NULL;

    }

}



static const jschar *matchGreedyKid(MatchState *state, RENode *ren, RENode *stop,

                        int kidCount, const jschar *cp, const jschar *previousKid)

{

    GreedyState grState;

    const jschar *match;

    grState.state = state;

    grState.kid = (RENode *)ren->kid;

    grState.next = ren->next;

    grState.kidCount = kidCount;

    grState.maxKid = (ren->op == REOP_QUANT) ? ren->u.range.max : 0;

    /*

        We try to match the sub-tree to completion first, and if that

        doesn't work, match only up to the given end of the sub-tree.

    */

    grState.stop = NULL;

    match = greedyRecurse(&grState, cp, previousKid);

    if (match || !stop) {

        return match;

    }

    grState.kidCount = kidCount;

    grState.stop = stop;

    return greedyRecurse(&grState, cp, previousKid);

}





static const jschar *matchNonGreedyKid(MatchState *state, RENode *ren,

                        int kidCount, int maxKid,

                        const jschar *cp)

{

    const jschar *kidMatch;

    const jschar *match;



    match = matchRENodes(state, ren->next, NULL, cp);

    if (state->complete)

        return state->complete;

    if (match) {

        state->complete = match;

        return cp;

    }

    if (match != NULL) 

        return cp;



    kidMatch = matchRENodes(state, (RENode *)ren->kid, ren->next, cp);

    if (kidMatch == NULL)

        return NULL;

    if (state->complete)

        return state->complete;

    if (kidMatch == cp)

        return kidMatch;    /* no point pursuing an empty match forever */

        return matchNonGreedyKid(state, ren, kidCount, maxKid, kidMatch);

    }



static void calcBMSize(MatchState *state, RENode *ren)

{

    const jschar *cp  = (const jschar *) ren->kid;

    const jschar *cp2 = (const jschar *) ren->u.kid2;

    uintN maxc = 0;

    jschar c, c2;

    while (cp < cp2) {

	c = *cp++;

	if (c == '\\') {

	    if (cp + 5 <= cp2 && *cp == 'u' &&

		JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) &&

		JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4])) {

		c = (((((JS7_UNHEX(cp[1]) << 4)

			+ JS7_UNHEX(cp[2])) << 4)

		      + JS7_UNHEX(cp[3])) << 4)

		    + JS7_UNHEX(cp[4]);

		cp += 5;

	    } else {

                if (maxc < 255) maxc = 255;

        	/*

		 * Octal and hex escapes can't be > 255.  Skip this

		 * backslash and let the loop pass over the remaining

		 * escape sequence as if it were text to match.

		 */

		continue;

	    }

	}

	if (state->flags & JSREG_FOLD) {

	    /*

	     * Don't assume that lowercase are above uppercase, or

	     * that c is either even when c has upper and lowercase

	     * versions.

	     */

	    if ((c2 = (jschar)JS_TOUPPER(c)) > maxc)

		maxc = c2;

	    if ((c2 = (jschar)JS_TOLOWER(c2)) > maxc)

		maxc = c2;

	}

	if (c > maxc)

	    maxc = c;

    }

    ren->u.ucclass.bmsize = (uint16)((size_t)(maxc + JS_BITS_PER_BYTE) 

                                                    / JS_BITS_PER_BYTE);

}



static JSBool buildBitmap(MatchState *state, RENode *ren)

{

    uintN   i, n, b, c, lastc, foldc, nchars;

    uint8   *bitmap;

    uint8   fill;

    JSBool  inrange;

    const jschar *cp = (const jschar *) ren->kid;

    const jschar *end = (const jschar *) ren->u.kid2;

    const jschar *ocp;



    calcBMSize(state, ren);

    ren->u.ucclass.bitmap = bitmap = (uint8*) JS_malloc(state->context, 

                                            ren->u.ucclass.bmsize);

    if (!bitmap) {

	JS_ReportOutOfMemory(state->context);

	return JS_FALSE;

    }



    if (*cp == '^') {

	fill = 0xff;

	cp++;

    } else {

	fill = 0;

    }



#ifdef	DREAMWEAVER_INCLUDE_EXTENDED_CHARS_IN_CLASSES

	ren->u.ucclass.wordclass = fill ? JS_TRUE : JS_FALSE;

	ren->u.ucclass.spaceclass = fill ? JS_TRUE : JS_FALSE;

#endif



    n = (uintN)ren->u.ucclass.bmsize;

    for (i = 0; i < n; i++)

	bitmap[i] = fill;

    nchars = n * JS_BITS_PER_BYTE;



/* Split ops up into statements to keep MSVC1.52 from crashing. */

#define MATCH_BIT(c)    { i = (c) >> 3; b = (c) & 7; b = 1 << b;              \

		  if (fill) bitmap[i] &= ~b; else bitmap[i] |= b; }



    lastc = nchars;

    inrange = JS_FALSE;



    while (cp < end) {

	c = (uintN) *cp++;

	if (c == '\\') {

	    c = *cp++;

	    switch (c) {

	      case 'b':

	      case 'f':

	      case 'n':

	      case 'r':

	      case 't':

	      case 'v':

		c = js_strchr(js_EscapeMap, (jschar)c)[-1];

		break;



#define CHECK_RANGE() if (inrange) { MATCH_BIT(lastc); MATCH_BIT('-');        \

			     inrange = JS_FALSE; }



	      case 'd':

		CHECK_RANGE();

		for (c = '0'; c <= '9'; c++)

		    MATCH_BIT(c);

		continue;



	      case 'D':

		CHECK_RANGE();

		for (c = 0; c < '0'; c++)

		    MATCH_BIT(c);

		for (c = '9' + 1; c < nchars; c++)

		    MATCH_BIT(c);

		continue;



	      case 'w':

		CHECK_RANGE();

		for (c = 0; c < nchars; c++)

		    if (JS_ISWORD(c))

			MATCH_BIT(c);

#ifdef	DREAMWEAVER_INCLUDE_EXTENDED_CHARS_IN_CLASSES

			ren->u.ucclass.wordclass = fill ? JS_FALSE : JS_TRUE ;

#endif

		continue;



	      case 'W':

		CHECK_RANGE();

		for (c = 0; c < nchars; c++)

		    if (!JS_ISWORD(c))

			MATCH_BIT(c);

#ifdef	DREAMWEAVER_INCLUDE_EXTENDED_CHARS_IN_CLASSES

			ren->u.ucclass.spaceclass = fill ? JS_FALSE : JS_TRUE ;

#endif

		continue;



	      case 's':

		CHECK_RANGE();

		for (c = 0; c < nchars; c++)

		    if (JS_ISSPACE(c))

			MATCH_BIT(c);

#ifdef	DREAMWEAVER_INCLUDE_EXTENDED_CHARS_IN_CLASSES

			ren->u.ucclass.spaceclass = fill ? JS_FALSE : JS_TRUE;

#endif

		continue;



	      case 'S':

		CHECK_RANGE();

		for (c = 0; c < nchars; c++)

		    if (!JS_ISSPACE(c))

			MATCH_BIT(c);

#ifdef	DREAMWEAVER_INCLUDE_EXTENDED_CHARS_IN_CLASSES

			ren->u.ucclass.wordclass = fill ? JS_FALSE : JS_TRUE;

#endif

		continue;



#undef CHECK_RANGE



	      case '0':

	      case '1':

	      case '2':

	      case '3':

	      case '4':

	      case '5':

	      case '6':

	      case '7':

		n = JS7_UNDEC(c);

		ocp = cp - 2;

		c = *cp;

		if ('0' <= c && c <= '7') {

		    cp++;

		    n = 8 * n + JS7_UNDEC(c);



		    c = *cp;

		    if ('0' <= c && c <= '7') {

			cp++;

			i = 8 * n + JS7_UNDEC(c);

			if (i <= 0377)

			    n = i;

			else

			    cp--;

		    }

		}

		c = n;

		break;



	      case 'x':

		ocp = cp;

		c = *cp++;

		if (JS7_ISHEX(c)) {

		    n = JS7_UNHEX(c);

		    c = *cp++;

		    if (JS7_ISHEX(c)) {

			n <<= 4;

			n += JS7_UNHEX(c);

		    }

		} else {

		    cp = ocp;	/* \xZZ is xZZ (Perl does \0ZZ!) */

		    n = 'x';

		}

		c = n;

		break;



	      case 'u':

		if (JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) &&

		    JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) {

		    n = (((((JS7_UNHEX(cp[0]) << 4)

			    + JS7_UNHEX(cp[1])) << 4)

			  + JS7_UNHEX(cp[2])) << 4)

			+ JS7_UNHEX(cp[3]);

		    c = n;

		    cp += 4;

		}

		break;



	      case 'c':

		c = *cp++;

		c = JS_TOUPPER(c);

		c = JS_TOCTRL(c);

		break;

	    }

	}



	if (inrange) {

	    if (lastc > c) {

		JS_ReportErrorNumber(state->context,

				     js_GetErrorMessage, NULL,

				     JSMSG_BAD_CLASS_RANGE);

		return JS_FALSE;

	    }

	    inrange = JS_FALSE;

	} else {

	    /* Set lastc so we match just c's bit in the for loop. */

	    lastc = c;



	    /* [balance: */

	    if (*cp == '-' && cp + 1 < end && cp[1] != ']') {

		cp++;

		inrange = JS_TRUE;

		continue;

	    }

	}



	/* Match characters in the range [lastc, c]. */

	for (; lastc <= c; lastc++) {

	    MATCH_BIT(lastc);

	    if (state->flags & JSREG_FOLD) {

		/*

		 * Must do both upper and lower for Turkish dotless i,

		 * Georgian, etc.

		 */

		foldc = JS_TOUPPER(lastc);

		MATCH_BIT(foldc);

		foldc = JS_TOLOWER(foldc);

		MATCH_BIT(foldc);

	    }

	}

	lastc = c;

    }

    return JS_TRUE;

}



static const jschar *matchRENodes(MatchState *state, RENode *ren, RENode *stop,

                                                const jschar *cp)

{

    const jschar *cp2, *kidMatch, *lastKid, *source, *cpend = state->cpend;

    jschar c;

    JSSubString *parsub;

    uintN i, b, bit, num, length;



    while ((ren != stop) && (ren != NULL))

    {

      switch (ren->op) {

        case REOP_EMPTY:

          break;

        case REOP_ALT:

          if (ren->next->op != REOP_ALT) {

              ren = (RENode *)ren->kid;

              continue;

          }

          num = state->parenCount;

          kidMatch = matchRENodes(state, (RENode *)ren->kid, stop, cp);

          if (state->complete)

              return state->complete;

          if (kidMatch != NULL)

              return kidMatch;

          for (i = num; i < state->parenCount; i++)

              state->parens[i].length = 0;

          state->parenCount = num;

          break;

        case REOP_QUANT:

          lastKid = NULL;

          for (num = 0; num < ren->u.range.min; num++) {

              kidMatch = matchRENodes(state, (RENode *)ren->kid,

                                          ren->next, cp);

              if (kidMatch == NULL)

                  return NULL;

              if (state->complete)

                  return state->complete;

              lastKid = cp;

              cp = kidMatch;

          }

          if (num == ren->u.range.max) break;

          if ((ren->flags & RENODE_MINIMAL) == 0) {

              cp2 = matchGreedyKid(state, ren, stop, num, cp, lastKid);

              if (cp2 == NULL) {

                  if (lastKid) {

                      cp = matchRENodes(state, (RENode *)ren->kid, ren->next,

                                        lastKid);

                      if (state->complete)

                          return state->complete;

              }

              }

              else {

                  if (state->complete)

                      return state->complete;

                  cp = cp2;

          }

          }

          else {

              cp = matchNonGreedyKid(state, ren, num,

                              ren->u.range.max, cp);

              if (cp == NULL)

                  return NULL;

              if (state->complete)

                  return state->complete;

          }

          break;

        case REOP_PLUS:

          kidMatch = matchRENodes(state, (RENode *)ren->kid,

                                          ren->next, cp);

          state->complete = NULL;

          if (kidMatch == NULL)

              return NULL;



          if ((ren->flags & RENODE_MINIMAL) == 0) {

              cp2 = matchGreedyKid(state, ren, stop, 1, kidMatch, cp);

              if (cp2 == NULL) {

                  cp = matchRENodes(state, (RENode *)ren->kid, ren->next, cp);

                  if (state->complete)

                      return state->complete;

              }

              else {

                  if (state->complete)

                      return state->complete;

                  cp = cp2;

          }

          }

          else {

              cp = matchNonGreedyKid(state, ren, 1, 0, kidMatch);

              if (state->complete)

                  return state->complete;

          }

          if (cp == NULL)

              return NULL;

          break;

        case REOP_STAR:

          if ((ren->flags & RENODE_MINIMAL) == 0) {

              cp2 = matchGreedyKid(state, ren, stop, 0, cp, NULL);

              if (cp2) {

                  if (state->complete)

                      return state->complete;

                  cp = cp2;

          }

          }

          else {

              cp = matchNonGreedyKid(state, ren, 0, 0, cp);

              if (cp == NULL)

                  return NULL;

              if (state->complete)

                  return state->complete;

          }

          break;

        case REOP_OPT:

          num = state->parenCount;

          if (ren->flags & RENODE_MINIMAL) {

              cp2 = matchRENodes(state, ren->next,

                                            stop, cp);

              if (state->complete)

                  return state->complete;

              if (cp2 != NULL)

                  return cp2;

          }

          kidMatch = matchRENodes(state, (RENode *)ren->kid,

                                        ren->next, cp);

          if (state->complete)

              return state->complete;



          if (kidMatch == NULL) {

              state->parenCount = num;

              break;

          }

          else {

              cp2 = matchRENodes(state, ren->next,

                                            stop, kidMatch);

              if (state->complete)

                  return state->complete;

              if (cp2 == NULL) {

                    /* need to undo the result of running the kid */

                  state->parenCount = num;

                  break;

              }

              else

                  return cp2;

          }

        case REOP_LPARENNON:

          ren = (RENode *)ren->kid;

          continue;

        case REOP_RPARENNON:

          break;

        case REOP_LPAREN:

          num = ren->u.num;

          ren = (RENode *)ren->kid;

	  parsub = &state->parens[num];

	  parsub->chars = cp;

          parsub->length = 0;

          if (num >= state->parenCount)

              state->parenCount = num + 1;

          continue;

        case REOP_RPAREN:

          num = ren->u.num;

          parsub = &state->parens[num];

          parsub->length = cp - parsub->chars;

          break;

        case REOP_ASSERT:

          kidMatch = matchRENodes(state, (RENode *)ren->kid,

                                            ren->next, cp);

          if (state->complete)

              return state->complete;

          if (kidMatch == NULL)

              return NULL;

          break;

        case REOP_ASSERT_NOT:

          kidMatch = matchRENodes(state, (RENode *)ren->kid,

                                            ren->next, cp);

          if (state->complete)

              return state->complete;

          if (kidMatch != NULL)

              return NULL;

          break;

        case REOP_BACKREF:

          num = ren->u.num;

          if (num >= state->parenCount) {

	    JS_ReportErrorNumber(state->context, js_GetErrorMessage, NULL,

				 JSMSG_BAD_BACKREF);

            state->ok = JS_FALSE;

            return NULL;

          }

          parsub = &state->parens[num];

          if ((cp + parsub->length) > cpend)

              return NULL;

          else {

              cp2 = parsub->chars;

              for (i = 0; i < parsub->length; i++) {

                  if (!matchChar(state->flags, *cp++, *cp2++))

                      return NULL;

              }

          }

          break;

        case REOP_CCLASS:

          if (cp != cpend) {

              if (ren->u.ucclass.bitmap == NULL) {

                  if (!buildBitmap(state, ren)) {

                      state->ok = JS_FALSE;

                      return NULL;

                  }

              }

              c = *cp;

              b = (c >> 3);

              if (b >= ren->u.ucclass.bmsize) {

                  cp2 = (jschar*) ren->kid;

#ifdef	DREAMWEAVER_INCLUDE_EXTENDED_CHARS_IN_CLASSES

				// for now, we'll define words in the extended range as anything that's not a space

				// this also implies any extended range unicode characters are either words or spaces,

				// which is not always true, but better than always excluding them

				if( (*cp2 >= 0x2000 && *cp2 <= 0x200A ) ||

						 *cp2 == 0x200B || //zero width space

						 *cp2 == 0x3000 || //ideographic space

						 *cp2 == 0xFEFF || //zero width no-break space

						 *cp2 == 0x0020 || //regular space (just in case)

						 *cp2 == 0x00A0  ) //no break space

				{

					if( ren->u.ucclass.spaceclass == JS_TRUE ) 

						cp++;

					else

						return NULL;

				}

				else if( ren->u.ucclass.wordclass == JS_TRUE )

					cp++;

				else

					return NULL;

#else //DREAMWEAVER_INCLUDE_EXTENDED_CHARS_IN_CLASSES

				if (*cp2 == '^')

                      cp++;

				else

					return NULL;

#endif //DREAMWEAVER_INCLUDE_EXTENDED_CHARS_IN_CLASSES

              }

              else {

                  bit = c & 7;

                  bit = 1 << bit;

                  if ((ren->u.ucclass.bitmap[b] & bit) != 0)

                      cp++;

                  else

                      return NULL;

              }

          }

          else

              return NULL;

          break;

        case REOP_DOT:

          if ((cp != cpend) && (*cp != '\n'))

              cp++;

          else

              return NULL;

          break;

        case REOP_DOTSTARMIN:

          for (cp2 = cp; cp2 < cpend; cp2++) {

              const jschar *cp3 = matchRENodes(state, ren->next, stop, cp2);

              if (cp3 != NULL)

                  return cp3;

              if (*cp2 == '\n')

                  return NULL;

          }

          return NULL;

        case REOP_DOTSTAR:

          for (cp2 = cp; cp2 < cpend; cp2++)

              if (*cp2 == '\n')

                  break;

          while (cp2 >= cp) {

              const jschar *cp3 = matchRENodes(state, ren->next, NULL, cp2);

              if (cp3 != NULL) {

                  cp = cp2;

                  break;

              }

              cp2--;

          }

          break;

       case REOP_WBDRY:

          if (((cp == state->cpbegin) || !JS_ISWORD(cp[-1]))

                  ^ ((cp >= cpend) || !JS_ISWORD(*cp)))

                ; /* leave cp */

          else

              return NULL;

          break;

       case REOP_WNONBDRY:

          if (((cp == state->cpbegin) || !JS_ISWORD(cp[-1]))

                  ^ ((cp < cpend) && JS_ISWORD(*cp)))

                ; /* leave cp */

          else

              return NULL;

          break;

        case REOP_EOLONLY:

        case REOP_EOL:

          if (cp == cpend)

              ; /* leave cp */

          else {

              if (state->context->regExpStatics.multiline

                    || ((state->flags & JSREG_MULTILINE) != 0))

                  if (*cp == '\n')

                      ;/* leave cp */

                  else

                      return NULL;

              else

                  return NULL;

          }

          break;

        case REOP_BOL:

          if (cp != state->cpbegin) {

              if ((cp < cpend)

                   && (state->context->regExpStatics.multiline

                      || ((state->flags & JSREG_MULTILINE) != 0))) {

                  if (cp[-1] == '\n') {

                      break;

                  }

              }

              return NULL;

          }

          /* leave cp */

          break;

        case REOP_DIGIT:

          if ((cp != cpend) && JS_ISDIGIT(*cp))

              cp++;

          else

              return NULL;

          break;

        case REOP_NONDIGIT:

          if ((cp != cpend) && !JS_ISDIGIT(*cp))

              cp++;

          else

              return NULL;

          break;

        case REOP_ALNUM:

          if ((cp != cpend) && JS_ISWORD(*cp))

              cp++;

          else

              return NULL;

          break;

        case REOP_NONALNUM:

          if ((cp != cpend) && !JS_ISWORD(*cp))

              cp++;

          else

              return NULL;

          break;

        case REOP_SPACE:

          if ((cp != cpend) && JS_ISSPACE(*cp))

              cp++;

          else

              return NULL;

          break;

        case REOP_NONSPACE:

          if ((cp != cpend) && !JS_ISSPACE(*cp))

              cp++;

          else

              return NULL;

          break;

        case REOP_FLAT1:

          if ((cp != cpend)

                    && matchChar(state->flags, ren->u.chr, *cp))

              cp++;

          else

              return NULL;

          break;

        case REOP_FLAT:

          source = (jschar*) ren->kid;

          length = (const jschar *)ren->u.kid2 - source;

          if ((cp + length) > cpend)

              return NULL;

          else {

              for (i = 0; i < length; i++) {

                  if (!matchChar(state->flags, *cp++, *source++))

                      return NULL;

              }

          }

          break;

        case REOP_JUMP:

          break;

        case REOP_END:

          break;

        default :

          JS_ASSERT(JS_FALSE);

          break;

        }

        ren = ren->next;

    }

    return cp;

}



static const jschar *

MatchRegExp(MatchState *state, RENode *ren, const jschar *cp)

{

    const jschar *cp2, *cp3;

    /* have to include the position beyond the last character

     in order to detect end-of-input/line condition */

    for (cp2 = cp; cp2 <= state->cpend; cp2++) {

        state->skipped = cp2 - cp;

        state->parenCount = 0;

        cp3 = matchRENodes(state, ren, NULL, cp2);

        if (!state->ok) return NULL;

        if (cp3 != NULL)

            return cp3;

    }

    return NULL;

}





JSBool

js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,

		 JSBool test, jsval *rval)

{

    MatchState state;

    const jschar *cp, *ep;

    size_t i, length, start;

    void *mark;

    JSSubString *parsub, *morepar;

    JSBool ok;

    JSRegExpStatics *res;

    ptrdiff_t matchlen;

    uintN num, morenum;

    JSString *parstr, *matchstr;

    JSObject *obj;



    /*

     * Initialize a state struct to minimize recursive argument traffic.

     */

    state.context = cx;

    state.anchoring = JS_FALSE;

    state.flags = re->flags;

    state.complete = NULL;



    /*

     * It's safe to load from cp because JSStrings have a zero at the end,

     * and we never let cp get beyond cpend.

     */

    start = *indexp;

    if (start > str->length)

	start = str->length;

    cp = str->chars + start;

    state.cpbegin = str->chars;

    state.cpend = str->chars + str->length;

    state.start = start;

    state.skipped = 0;



    /*

     * Use the temporary arena pool to grab space for parenthetical matches.

     * After the JS_ARENA_ALLOCATE early return on error, goto out to be sure

     * to free this memory.

     */

    length = 2 * sizeof(JSSubString) * re->parenCount;

    mark = JS_ARENA_MARK(&cx->tempPool);

    JS_ARENA_ALLOCATE_CAST(parsub, JSSubString *, &cx->tempPool, length);

    if (!parsub) {

	JS_ReportOutOfMemory(cx);

	return JS_FALSE;

    }

    memset(parsub, 0, length);

    state.parenCount = 0;

    state.maybeParens = parsub;

    state.parens = parsub + re->parenCount;

    state.ok = JS_TRUE;



    /*

     * Call the recursive matcher to do the real work.  Return null on mismatch

     * whether testing or not.  On match, return an extended Array object.

     */

    cp = MatchRegExp(&state, re->ren, cp);

    if (!(ok = state.ok)) goto out;

    if (!cp) {

	*rval = JSVAL_NULL;

	goto out;

    }

    i = PTRDIFF(cp, state.cpbegin, jschar);

    *indexp = i;

    matchlen = i - (start + state.skipped);

    ep = cp;

    cp -= matchlen;



    if (test) {

	/*

	 * Testing for a match and updating cx->regExpStatics: don't allocate

	 * an array object, do return true.

	 */

	*rval = JSVAL_TRUE;



        /* Avoid warning.  (gcc doesn't detect that obj is needed iff !test); */

        obj = NULL;

    } else {

	/*

	 * The array returned on match has element 0 bound to the matched

	 * string, elements 1 through state.parenCount bound to the paren

	 * matches, an index property telling the length of the left context,

	 * and an input property referring to the input string.

	 */

	obj = js_NewArrayObject(cx, 0, NULL);

	if (!obj) {

	    ok = JS_FALSE;

	    goto out;

	}

	*rval = OBJECT_TO_JSVAL(obj);



#define DEFVAL(val, id) {                                                     \

    ok = js_DefineProperty(cx, obj, id, val,                                  \

			   JS_PropertyStub, JS_PropertyStub,                  \

			   JSPROP_ENUMERATE, NULL);                           \

    if (!ok) {                                                                \

	cx->newborn[GCX_OBJECT] = NULL;                                       \

	cx->newborn[GCX_STRING] = NULL;                                       \

	goto out;                                                             \

    }                                                                         \

}



	matchstr = js_NewStringCopyN(cx, cp, matchlen, 0);

	if (!matchstr) {

	    cx->newborn[GCX_OBJECT] = NULL;

	    ok = JS_FALSE;

	    goto out;

	}

	DEFVAL(STRING_TO_JSVAL(matchstr), INT_TO_JSVAL(0));

    }



    res = &cx->regExpStatics;

    JS_ASSERT(state.parenCount <= re->parenCount);

    if (state.parenCount == 0) {

	res->parenCount = 0;

	res->lastParen = js_EmptySubString;

    } else {

	for (num = 0; num < state.parenCount; num++) {

	    parsub = &state.parens[num];

	    if (num < 9) {

		res->parens[num] = *parsub;

	    } else {

		morenum = num - 9;

		morepar = res->moreParens;

		if (!morepar) {

		    res->moreLength = 10;

		    morepar = (JSSubString*) JS_malloc(cx,

                                                       10 * sizeof(JSSubString));

		} else if (morenum > res->moreLength) {

		    res->moreLength += 10;

		    morepar = (JSSubString*) JS_realloc(cx, morepar,

					 res->moreLength * sizeof(JSSubString));

		}

		if (!morepar) {

		    cx->newborn[GCX_OBJECT] = NULL;

		    cx->newborn[GCX_STRING] = NULL;

		    ok = JS_FALSE;

		    goto out;

		}

		res->moreParens = morepar;

		morepar[morenum] = *parsub;

	    }

	    if (test)

		continue;

	    parstr = js_NewStringCopyN(cx, parsub->chars, parsub->length, 0);

	    if (!parstr) {

		cx->newborn[GCX_OBJECT] = NULL;

		cx->newborn[GCX_STRING] = NULL;

		ok = JS_FALSE;

		goto out;

	    }

	    ok = js_DefineProperty(cx, obj, INT_TO_JSVAL(num + 1),

				   STRING_TO_JSVAL(parstr), NULL, NULL,

				   JSPROP_ENUMERATE, NULL);

	    if (!ok) {

		cx->newborn[GCX_OBJECT] = NULL;

		cx->newborn[GCX_STRING] = NULL;

		goto out;

	    }

	}

	res->parenCount = num;

	res->lastParen = *parsub;

    }



    if (!test) {

	/*

	 * Define the index and input properties last for better for/in loop

	 * order (so they come after the elements).

	 */

	DEFVAL(INT_TO_JSVAL(start + state.skipped),

	       (jsid)cx->runtime->atomState.indexAtom);

	DEFVAL(STRING_TO_JSVAL(str),

	       (jsid)cx->runtime->atomState.inputAtom);

    }



#undef DEFVAL



    res->lastMatch.chars = cp;

    res->lastMatch.length = matchlen;

    if (cx->version == JSVERSION_1_2) {

	/*

	 * JS1.2 emulated Perl4.0.1.8 (patch level 36) for global regexps used

	 * in scalar contexts, and unintentionally for the string.match "list"

	 * psuedo-context.  On "hi there bye", the following would result:

	 *

	 * Language     while(/ /g){print("$`");}   s/ /$`/g

	 * perl4.036    "hi", "there"               "hihitherehi therebye"

	 * perl5        "hi", "hi there"            "hihitherehi therebye"

	 * js1.2        "hi", "there"               "hihitheretherebye"

	 */

	res->leftContext.chars = str->chars + start;

	res->leftContext.length = state.skipped;

    } else {

	/*

	 * For JS1.3 and ECMAv2, emulate Perl5 exactly:

	 *

	 * js1.3        "hi", "hi there"            "hihitherehi therebye"

	 */

	res->leftContext.chars = str->chars;

	res->leftContext.length = start + state.skipped;

    }

    res->rightContext.chars = ep;

    res->rightContext.length = state.cpend - ep;



out:

    JS_ARENA_RELEASE(&cx->tempPool, mark);

    return ok;

}



/************************************************************************/



enum regexp_tinyid {

    REGEXP_SOURCE       = -1,

    REGEXP_GLOBAL       = -2,

    REGEXP_IGNORE_CASE  = -3,

    REGEXP_LAST_INDEX   = -4,

    REGEXP_MULTILINE    = -5

};



#define REGEXP_PROP_ATTRS (JSPROP_ENUMERATE|JSPROP_PERMANENT|JSPROP_SHARED)



static JSPropertySpec regexp_props[] = {

    {"source",     REGEXP_SOURCE,      REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0},

    {"global",     REGEXP_GLOBAL,      REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0},

    {"ignoreCase", REGEXP_IGNORE_CASE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0},

    {"lastIndex",  REGEXP_LAST_INDEX,  REGEXP_PROP_ATTRS,0,0},

    {"multiline",  REGEXP_MULTILINE,   REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0},

    {0,0,0,0,0}

};



static JSBool

regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    jsint slot;

    JSRegExp *re;



    if (!JSVAL_IS_INT(id))

	return JS_TRUE;

    slot = JSVAL_TO_INT(id);

    JS_LOCK_OBJ(cx, obj);

    re = (JSRegExp*) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL);

    if (re) {

	switch (slot) {

	  case REGEXP_SOURCE:

	    *vp = STRING_TO_JSVAL(re->source);

	    break;

	  case REGEXP_GLOBAL:

	    *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_GLOB) != 0);

	    break;

	  case REGEXP_IGNORE_CASE:

	    *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_FOLD) != 0);

	    break;

	  case REGEXP_LAST_INDEX:

            if (!js_NewNumberValue(cx, (jsdouble)re->lastIndex, vp))

	        return JS_FALSE;

	    break;

	  case REGEXP_MULTILINE:

	    *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_MULTILINE) != 0);

	    break;

	}

    }

    JS_UNLOCK_OBJ(cx, obj);

    return JS_TRUE;

}



static JSBool

regexp_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    jsint slot;

    JSRegExp *re;

    jsdouble d;



    if (!JSVAL_IS_INT(id))

	return JS_TRUE;

    slot = JSVAL_TO_INT(id);

    JS_LOCK_OBJ(cx, obj);

    re = (JSRegExp*) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL);

    if (re && slot == REGEXP_LAST_INDEX) {

	if (!js_ValueToNumber(cx, *vp, &d))

	    return JS_FALSE;

	re->lastIndex = (uintN)d;

    }

    JS_UNLOCK_OBJ(cx, obj);

    return JS_TRUE;

}



/*

 * RegExp class static properties and their Perl counterparts:

 *

 *  RegExp.input                $_

 *  RegExp.multiline            $*

 *  RegExp.lastMatch            $&

 *  RegExp.lastParen            $+

 *  RegExp.leftContext          $`

 *  RegExp.rightContext         $'

 */

enum regexp_static_tinyid {

    REGEXP_STATIC_INPUT         = -1,

    REGEXP_STATIC_MULTILINE     = -2,

    REGEXP_STATIC_LAST_MATCH    = -3,

    REGEXP_STATIC_LAST_PAREN    = -4,

    REGEXP_STATIC_LEFT_CONTEXT  = -5,

    REGEXP_STATIC_RIGHT_CONTEXT = -6

};



JSBool

js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res)

{

    JS_ClearRegExpStatics(cx);

    return js_AddRoot(cx, &res->input, "res->input");

}



void

js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res)

{

    if (res->moreParens) {

	JS_free(cx, res->moreParens);

	res->moreParens = NULL;

    }

    js_RemoveRoot(cx->runtime, &res->input);

}



static JSBool

regexp_static_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    jsint slot;

    JSRegExpStatics *res;

    JSString *str;

    JSSubString *sub;



    res = &cx->regExpStatics;

    if (!JSVAL_IS_INT(id))

	return JS_TRUE;

    slot = JSVAL_TO_INT(id);

    switch (slot) {

      case REGEXP_STATIC_INPUT:

	*vp = res->input ? STRING_TO_JSVAL(res->input)

			 : JS_GetEmptyStringValue(cx);

	return JS_TRUE;

      case REGEXP_STATIC_MULTILINE:

	*vp = BOOLEAN_TO_JSVAL(res->multiline);

	return JS_TRUE;

      case REGEXP_STATIC_LAST_MATCH:

	sub = &res->lastMatch;

	break;

      case REGEXP_STATIC_LAST_PAREN:

	sub = &res->lastParen;

	break;

      case REGEXP_STATIC_LEFT_CONTEXT:

	sub = &res->leftContext;

	break;

      case REGEXP_STATIC_RIGHT_CONTEXT:

	sub = &res->rightContext;

	break;

      default:

	sub = js_RegExpParenSubString(res, slot);

	break;

    }

    str = js_NewStringCopyN(cx, sub->chars, sub->length, 0);

    if (!str)

	return JS_FALSE;

    *vp = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

regexp_static_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSRegExpStatics *res;



    if (!JSVAL_IS_INT(id))

	return JS_TRUE;

    res = &cx->regExpStatics;

    /* XXX use if-else rather than switch to keep MSVC1.52 from crashing */

    if (JSVAL_TO_INT(id) == REGEXP_STATIC_INPUT) {

	if (!JSVAL_IS_STRING(*vp) &&

	    !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) {

	    return JS_FALSE;

	}

	res->input = JSVAL_TO_STRING(*vp);

    } else if (JSVAL_TO_INT(id) == REGEXP_STATIC_MULTILINE) {

	if (!JSVAL_IS_BOOLEAN(*vp) &&

	    !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp)) {

	    return JS_FALSE;

	}

	res->multiline = JSVAL_TO_BOOLEAN(*vp);

    }

    return JS_TRUE;

}



static JSPropertySpec regexp_static_props[] = {

    {"input",

     REGEXP_STATIC_INPUT,

     JSPROP_ENUMERATE|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_setProperty},

    {"multiline",

     REGEXP_STATIC_MULTILINE,

     JSPROP_ENUMERATE|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_setProperty},

    {"lastMatch",

     REGEXP_STATIC_LAST_MATCH,

     JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_getProperty},

    {"lastParen",

     REGEXP_STATIC_LAST_PAREN,

     JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_getProperty},

    {"leftContext",

     REGEXP_STATIC_LEFT_CONTEXT,

     JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_getProperty},

    {"rightContext",

     REGEXP_STATIC_RIGHT_CONTEXT,

     JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_getProperty},



    /* XXX should have block scope and local $1, etc. */

    {"$1", 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_getProperty},

    {"$2", 1, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_getProperty},

    {"$3", 2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_getProperty},

    {"$4", 3, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_getProperty},

    {"$5", 4, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_getProperty},

    {"$6", 5, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_getProperty},

    {"$7", 6, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_getProperty},

    {"$8", 7, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_getProperty},

    {"$9", 8, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED,

     regexp_static_getProperty,    regexp_static_getProperty},



    {0,0,0,0,0}

};



static void

regexp_finalize(JSContext *cx, JSObject *obj)

{

    JSRegExp *re;



    re = (JSRegExp*) JS_GetPrivate(cx, obj);

    if (!re)

	return;

    js_DestroyRegExp(cx, re);

}



/* Forward static prototype. */

static JSBool

regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	    jsval *rval);



static JSBool

regexp_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return regexp_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval);

}



#if JS_HAS_XDR



#include "jsxdrapi.h"



static JSBool

regexp_xdrObject(JSXDRState *xdr, JSObject **objp)

{

    JSRegExp *re;

    JSString *source;

    uint8 flags;



    if (xdr->mode == JSXDR_ENCODE) {

	re = (JSRegExp*) JS_GetPrivate(xdr->cx, *objp);

	if (!re)

	    return JS_FALSE;

	source = re->source;

	flags = re->flags;

    }

    if (!JS_XDRString(xdr, &source) ||

	!JS_XDRUint8(xdr, &flags)) {

	return JS_FALSE;

    }

    if (xdr->mode == JSXDR_DECODE) {

	*objp = js_NewObject(xdr->cx, &js_RegExpClass, NULL, NULL);

	if (!*objp)

	    return JS_FALSE;

	re = js_NewRegExp(xdr->cx, NULL, source, flags, JS_FALSE);

	if (!re)

	    return JS_FALSE;

	if (!JS_SetPrivate(xdr->cx, *objp, re)) {

	    js_DestroyRegExp(xdr->cx, re);

	    return JS_FALSE;

	}

    }

    return JS_TRUE;

}



#else  /* !JS_HAS_XDR */



#define regexp_xdrObject NULL



#endif /* !JS_HAS_XDR */



JSClass js_RegExpClass = {

    js_RegExp_str,

    JSCLASS_HAS_PRIVATE,

    JS_PropertyStub,  JS_PropertyStub,  regexp_getProperty, regexp_setProperty,

    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,     regexp_finalize,

    NULL,             NULL,             regexp_call,        NULL,

    regexp_xdrObject, NULL,             NULL,               0

};



static JSBool

regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		jsval *rval)

{

    JSBool ok;

    JSRegExp *re;

    jschar *chars;

    size_t length, nflags;

    uintN flags;

    JSString *str;



    if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))

	return JS_FALSE;

    ok = JS_TRUE;

    JS_LOCK_OBJ(cx, obj);

    re = (JSRegExp*) JS_GetPrivate(cx, obj);

    if (!re) {

	*rval = STRING_TO_JSVAL(cx->runtime->emptyString);

	goto out;

    }



    length = re->source->length + 2;

    nflags = 0;

    for (flags = re->flags; flags != 0; flags &= flags - 1)

	nflags++;

    chars = (jschar*) JS_malloc(cx, (length + nflags + 1) * sizeof(jschar));

    if (!chars) {

	ok = JS_FALSE;

	goto out;

    }



    chars[0] = '/';

    js_strncpy(&chars[1], re->source->chars, length - 2);

    chars[length-1] = '/';

    if (nflags) {

	if (re->flags & JSREG_GLOB)

	    chars[length++] = 'g';

	if (re->flags & JSREG_FOLD)

	    chars[length++] = 'i';

	if (re->flags & JSREG_MULTILINE)

	    chars[length++] = 'm';

    }

    chars[length] = 0;



    str = js_NewString(cx, chars, length, 0);

    if (!str) {

	JS_free(cx, chars);

	ok = JS_FALSE;

	goto out;

    }

    *rval = STRING_TO_JSVAL(str);

out:

    JS_UNLOCK_OBJ(cx, obj);

    return ok;

}



static JSBool

regexp_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	       jsval *rval)

{

    JSString *opt, *str;

    JSRegExp *oldre, *re;

    JSBool ok;

    JSObject *obj2;



    if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))

	return JS_FALSE;

    opt = NULL;

    JS_LOCK_OBJ(cx, obj);

    if (argc == 0) {

	str = js_NewStringCopyN(cx, cx->runtime->emptyString->chars, 

                                    cx->runtime->emptyString->length, 0);

        if (!str) {

            ok = JS_FALSE;

            goto out;

        }

    } else {

        if (JSVAL_IS_OBJECT(argv[0])) {

            obj2 = JSVAL_TO_OBJECT(argv[0]);

            /*

             * If we get passed in a RegExp object we construct a new

             * RegExp that is a duplicate of it by re-compiling the

             * original source code. ECMA requires that it be an error

             * here if the flags are specified. (We must use the flags

             * from the original RegExp also).

             */

            if (obj2 && OBJ_GET_CLASS(cx, obj2) == &js_RegExpClass) {

                if (argc >= 2 && !JSVAL_IS_VOID(argv[1])) { /* 'flags' defined */

                    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, 

                                         JSMSG_NEWREGEXP_FLAGGED);

                    ok = JS_FALSE;

                    goto out;

                }

                re = (JSRegExp*) JS_GetPrivate(cx, obj2);

                if (!re) {

                    ok = JS_FALSE;

                    goto out;

                }

                re = js_NewRegExp(cx, NULL, re->source, re->flags, JS_FALSE);

                goto madeit;

            }

        }

        str = js_ValueToString(cx, argv[0]);

	if (!str) {

	    ok = JS_FALSE;

	    goto out;

	}

	argv[0] = STRING_TO_JSVAL(str);

        if (argc > 1) {

            if (JSVAL_IS_VOID(argv[1]))

                opt = NULL;

            else {

                opt = js_ValueToString(cx, argv[1]);

                if (!opt) {

                    ok = JS_FALSE;

                    goto out;

                }

                argv[1] = STRING_TO_JSVAL(opt);

            }

        }

    }

    re = js_NewRegExpOpt(cx, NULL, str, opt, JS_FALSE);

madeit:

    if (!re) {

	ok = JS_FALSE;

	goto out;

    }

    oldre = (JSRegExp*) JS_GetPrivate(cx, obj);

    ok = JS_SetPrivate(cx, obj, re);

    if (!ok) {

	js_DestroyRegExp(cx, re);

	goto out;

    }

    if (oldre)

	js_DestroyRegExp(cx, oldre);

    *rval = OBJECT_TO_JSVAL(obj);

out:

    JS_UNLOCK_OBJ(cx, obj);

    return ok;

}



static JSBool

regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		JSBool test, jsval *rval)

{

    JSBool ok, locked;

    JSRegExp *re;

    JSString *str;

    size_t i;



    if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv))

	return JS_FALSE;

    re = (JSRegExp*) JS_GetPrivate(cx, obj);

    if (!re)

	return JS_TRUE;

    ok = locked = JS_FALSE;

    if (argc == 0) {

	str = cx->regExpStatics.input;

	if (!str) {

	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

				 JSMSG_NO_INPUT,

				 JS_GetStringBytes(re->source),

				 (re->flags & JSREG_GLOB) ? "g" : "",

				 (re->flags & JSREG_FOLD) ? "i" : "",

				 (re->flags & JSREG_MULTILINE) ? "m" : "");

	    goto out;

	}

    } else {

	str = js_ValueToString(cx, argv[0]);

	if (!str)

	    goto out;

	argv[0] = STRING_TO_JSVAL(str);

    }

    if (re->flags & JSREG_GLOB) {

        JS_LOCK_OBJ(cx, obj);

        locked = JS_TRUE;

        i = re->lastIndex;

    } else {

        i = 0;

    }

    ok = js_ExecuteRegExp(cx, re, str, &i, test, rval);

    if (re->flags & JSREG_GLOB)

	re->lastIndex = (*rval == JSVAL_NULL) ? 0 : i;

out:

    if (locked)

	JS_UNLOCK_OBJ(cx, obj);

    return ok;

}



static JSBool

regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return regexp_exec_sub(cx, obj, argc, argv, JS_FALSE, rval);

}



static JSBool

regexp_test(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    if (!regexp_exec_sub(cx, obj, argc, argv, JS_TRUE, rval))

	return JS_FALSE;

    if (*rval != JSVAL_TRUE)

	*rval = JSVAL_FALSE;

    return JS_TRUE;

}



static JSFunctionSpec regexp_methods[] = {

#if JS_HAS_TOSOURCE

    {js_toSource_str,   regexp_toString,        0,0,0},

#endif

    {js_toString_str,   regexp_toString,        0,0,0},

    {"compile",         regexp_compile,         1,0,0},

    {"exec",            regexp_exec,            0,0,0},

    {"test",            regexp_test,            0,0,0},

    {0,0,0,0,0}

};



static JSBool

RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    if (!cx->fp->constructing) {

        /*

         * If first arg is regexp and no flags are given, just return the arg.

         * (regexp_compile detects the regexp + flags case and throws a

         * TypeError.)  See 10.15.3.1.

         */

        if ((argc < 2 || JSVAL_IS_VOID(argv[1])) && JSVAL_IS_OBJECT(argv[0]) &&

            OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[0])) == &js_RegExpClass) {

            *rval = argv[0];

            return JS_TRUE;

        }



        /* Otherwise, replace obj with a new RegExp object. */

	obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL);

	if (!obj)

	    return JS_FALSE;

    }

    return regexp_compile(cx, obj, argc, argv, rval);

}



JSObject *

js_InitRegExpClass(JSContext *cx, JSObject *obj)

{

    JSObject *proto, *ctor;



    proto = JS_InitClass(cx, obj, NULL, &js_RegExpClass, RegExp, 1,

			 regexp_props, regexp_methods,

			 regexp_static_props, NULL);



    if (!proto || !(ctor = JS_GetConstructor(cx, proto)))

	return NULL;

    if (!JS_AliasProperty(cx, ctor, "input",        "$_") ||

	!JS_AliasProperty(cx, ctor, "multiline",    "$*") ||

	!JS_AliasProperty(cx, ctor, "lastMatch",    "$&") ||

	!JS_AliasProperty(cx, ctor, "lastParen",    "$+") ||

	!JS_AliasProperty(cx, ctor, "leftContext",  "$`") ||

	!JS_AliasProperty(cx, ctor, "rightContext", "$'")) {

	goto bad;

    }

    return proto;



bad:

    JS_DeleteProperty(cx, obj, js_RegExpClass.name);

    return NULL;

}



JSObject *

js_NewRegExpObject(JSContext *cx, JSTokenStream *ts,

                   jschar *chars, size_t length, uintN flags)

{

    JSString *str;

    JSObject *obj;

    JSRegExp *re;



    str = js_NewStringCopyN(cx, chars, length, 0);

    if (!str)

	return NULL;

    re = js_NewRegExp(cx, ts,  str, flags, JS_FALSE);

    if (!re)

	return NULL;

    obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL);

    if (!obj || !JS_SetPrivate(cx, obj, re)) {

	js_DestroyRegExp(cx, re);

	return NULL;

    }

    return obj;

}



// DREAMWEAVER: See comment in the header file.

JSRegExpStatics *

js_GetRegExpStatics(JSContext *cx)

{

    return &(cx->regExpStatics);

}



// DREAMWEAVER: See [dgeorge 23-feb-02] comment in header file

JSSubString *

js_RegExpParenSubString(JSRegExpStatics *res, jsuint num)

{

	JSSubString *ret = NULL;



	if (num < (jsuint)(res->parenCount))

	{

		if (num < 9)

		{

			ret = &(res->parens[num]);

		}

		else

		{

			ret = &(res->moreParens[num - 9]);

		}

	}

	else

	{

		ret = &js_EmptySubString;

	}



	return ret;

}



#endif /* JS_HAS_REGEXPS */

 

**** End of jsregexp.c ****

 

**** Start of jsregexp.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsregexp_h___

#define jsregexp_h___

/*

 * JS regular expression interface.

 */

#include <stddef.h>

#include "jspubtd.h"

#include "jsstr.h"



struct JSRegExpStatics {

    JSString    *input;         /* input string to match (perl $_, GC root) */

    JSBool      multiline;      /* whether input contains newlines (perl $*) */

    uintN       parenCount;     /* number of valid elements in parens[] */

    uintN       moreLength;     /* number of allocated elements in moreParens */

    JSSubString parens[9];      /* last set of parens matched (perl $1, $2) */

    JSSubString *moreParens;    /* null or realloc'd vector for $10, etc. */

    JSSubString lastMatch;      /* last string matched (perl $&) */

    JSSubString lastParen;      /* last paren matched (perl $+) */

    JSSubString leftContext;    /* input to left of last match (perl $`) */

    JSSubString rightContext;   /* input to right of last match (perl $') */

};



/*

 * This macro is safe because moreParens is guaranteed to be allocated and big

 * enough to hold parenCount, or else be null when parenCount is 0.

 *

 * DREAMWEAVER [dgeorge 23-feb-02] I'm replacing the macro with a function because

 * it looks like the macro is being compiled incorrectly in the WinRelease

 * build

 */

/*

#define REGEXP_PAREN_SUBSTRING(res, num)                                      \

    (((jsuint)(num) < (jsuint)(res)->parenCount)                              \

     ? ((jsuint)(num) < 9)                                                    \

       ? &(res)->parens[num]                                                  \

       : &(res)->moreParens[(num) - 9]                                        \

     : &js_EmptySubString)

*/

extern JSSubString *

js_RegExpParenSubString(JSRegExpStatics *res, jsuint num);



struct JSRegExp {

    JSString    *source;        /* locked source string, sans // */

    uintN       lastIndex;      /* index after last match, for //g iterator */

    uintN       parenCount;     /* number of parenthesized submatches */

    uint8       flags;          /* flags, see jsapi.h */

    struct RENode *ren;         /* regular expression tree root */

};



extern JSRegExp *

js_NewRegExp(JSContext *cx, JSTokenStream *ts,

             JSString *str, uintN flags, JSBool flat);



extern JSRegExp *

js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts,

                JSString *str, JSString *opt, JSBool flat);



extern void

js_DestroyRegExp(JSContext *cx, JSRegExp *re);



/*

 * Execute re on input str at *indexp, returning null in *rval on mismatch.

 * On match, return true if test is true, otherwise return an array object.

 * Update *indexp and cx->regExpStatics always on match.

 */

extern JSBool

js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,

		 JSBool test, jsval *rval);



/*

 * These two add and remove GC roots, respectively, so their calls must be

 * well-ordered.

 */

extern JSBool

js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res);



extern void

js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res);



#define JSVAL_IS_REGEXP(cx, v)                                                \

    (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) &&                              \

     OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_RegExpClass)



extern JSClass js_RegExpClass;



extern JSObject *

js_InitRegExpClass(JSContext *cx, JSObject *obj);



/*

 * Create a new RegExp object.

 */

extern JSObject *

js_NewRegExpObject(JSContext *cx, JSTokenStream *ts,

                   jschar *chars, size_t length, uintN flags);



extern JSBool

js_XDRRegExp(JSXDRState *xdr, JSObject **objp);





/* DREAMWEAVER:

 * 

 * Added a function to access the statics data structure.

 *

 * We should be able to access the structure by traversing the js_context

 * data structure.  However, differences between the compiler flags for

 * the NscpJavaScript library and the Dreamweaver executable cause problems

 * on the Mac (I think the cause is that Enums are ints in Dreamweaver but

 * not in NscpJavaScript).  Instead of switching the compiler flag and

 * introducing a lot of risk, I'm adding this function to access the

 * piece of js_context that interests me.  [dgeorge 8/00]

 * 

 * [snewman 2/14/00: copied this patch from NscpJavaScript to JavaScript_1_5]

 */

extern JSRegExpStatics *

js_GetRegExpStatics(JSContext *cx);





#endif /* jsregexp_h___ */

 

**** End of jsregexp.h ****

 

**** Start of jsscan.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS lexical scanner.

 */

#include "jsstddef.h"

#include <stdio.h>	/* first to avoid trouble on some systems */

#include <errno.h>

#include <limits.h>

#include <math.h>

#ifdef HAVE_MEMORY_H

#include <memory.h>

#endif

#include <stdarg.h>

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsarena.h" /* Added by JSIFY */

#include "jsutil.h" /* Added by JSIFY */

#include "jsdtoa.h"

#include "jsprf.h"

#include "jsapi.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsemit.h"

#include "jsexn.h"

#include "jsnum.h"

#include "jsopcode.h"

#include "jsregexp.h"

#include "jsscan.h"



/* Unicode separators that are treated as line terminators, in addition to \n, \r */

#define LINE_SEPARATOR  (0x2028)

#define PARA_SEPARATOR  (0x2029)



#define RESERVE_JAVA_KEYWORDS

#define RESERVE_ECMA_KEYWORDS



static struct keyword {

    char        *name;

    JSTokenType tokentype;      /* JSTokenType */

    JSOp        op;             /* JSOp */

    JSVersion   version;        /* JSVersion */

} keywords[] = {

    {"break",           TOK_BREAK,              JSOP_NOP,   JSVERSION_DEFAULT},

    {"case",            TOK_CASE,               JSOP_NOP,   JSVERSION_DEFAULT},

    {"continue",        TOK_CONTINUE,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"default",         TOK_DEFAULT,            JSOP_NOP,   JSVERSION_DEFAULT},

    {js_delete_str,     TOK_DELETE,             JSOP_NOP,   JSVERSION_DEFAULT},

    {"do",              TOK_DO,                 JSOP_NOP,   JSVERSION_DEFAULT},

    {"else",            TOK_ELSE,               JSOP_NOP,   JSVERSION_DEFAULT},

    {"export",          TOK_EXPORT,             JSOP_NOP,   JSVERSION_1_2},

    {js_false_str,      TOK_PRIMARY,            JSOP_FALSE, JSVERSION_DEFAULT},

    {"for",             TOK_FOR,                JSOP_NOP,   JSVERSION_DEFAULT},

    {js_function_str,   TOK_FUNCTION,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"if",              TOK_IF,                 JSOP_NOP,   JSVERSION_DEFAULT},

    {js_in_str,         TOK_IN,                 JSOP_IN,    JSVERSION_DEFAULT},

    {js_new_str,        TOK_NEW,                JSOP_NEW,   JSVERSION_DEFAULT},

    {js_null_str,       TOK_PRIMARY,            JSOP_NULL,  JSVERSION_DEFAULT},

    {"return",          TOK_RETURN,             JSOP_NOP,   JSVERSION_DEFAULT},

    {"switch",          TOK_SWITCH,             JSOP_NOP,   JSVERSION_DEFAULT},

    {js_this_str,       TOK_PRIMARY,            JSOP_THIS,  JSVERSION_DEFAULT},

    {js_true_str,       TOK_PRIMARY,            JSOP_TRUE,  JSVERSION_DEFAULT},

    {js_typeof_str,     TOK_UNARYOP,            JSOP_TYPEOF,JSVERSION_DEFAULT},

    {"var",             TOK_VAR,                JSOP_DEFVAR,JSVERSION_DEFAULT},

    {js_void_str,       TOK_UNARYOP,            JSOP_VOID,  JSVERSION_DEFAULT},

    {"while",           TOK_WHILE,              JSOP_NOP,   JSVERSION_DEFAULT},

    {"with",            TOK_WITH,               JSOP_NOP,   JSVERSION_DEFAULT},



#if JS_HAS_CONST

    {js_const_str,      TOK_VAR,                JSOP_DEFCONST,JSVERSION_DEFAULT},

#else

    {js_const_str,      TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

#endif



#if JS_HAS_EXCEPTIONS

    {"try",             TOK_TRY,                JSOP_NOP,   JSVERSION_DEFAULT},

    {"catch",           TOK_CATCH,              JSOP_NOP,   JSVERSION_DEFAULT},

    {"finally",         TOK_FINALLY,            JSOP_NOP,   JSVERSION_DEFAULT},

    {"throw",           TOK_THROW,              JSOP_NOP,   JSVERSION_DEFAULT},

#else

    {"try",             TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"catch",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"finally",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"throw",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

#endif



#if JS_HAS_INSTANCEOF

    {js_instanceof_str, TOK_INSTANCEOF,         JSOP_INSTANCEOF,JSVERSION_1_4},

#else

    {js_instanceof_str, TOK_RESERVED,           JSOP_NOP,   JSVERSION_1_4},

#endif



#ifdef RESERVE_JAVA_KEYWORDS

    {"abstract",        TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"boolean",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"byte",            TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"char",            TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"class",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"double",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"extends",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"final",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"float",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"goto",            TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"implements",      TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"import",          TOK_IMPORT,             JSOP_NOP,   JSVERSION_DEFAULT},

    {"int",             TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"interface",       TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"long",            TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"native",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"package",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"private",         TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"protected",       TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"public",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"short",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"static",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"super",           TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"synchronized",    TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"throws",          TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"transient",       TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

    {"volatile",        TOK_RESERVED,           JSOP_NOP,   JSVERSION_DEFAULT},

#endif



#ifdef RESERVE_ECMA_KEYWORDS

    {"enum",           TOK_RESERVED,            JSOP_NOP,   JSVERSION_1_3},

#endif



#if JS_HAS_DEBUGGER_KEYWORD

    {"debugger",       TOK_DEBUGGER,            JSOP_NOP,   JSVERSION_1_3},

#elif defined(RESERVE_ECMA_KEYWORDS)

    {"debugger",       TOK_RESERVED,            JSOP_NOP,   JSVERSION_1_3},

#endif

    {0,                TOK_EOF,                 JSOP_NOP,   JSVERSION_DEFAULT}

};



JSBool

js_InitScanner(JSContext *cx)

{

    struct keyword *kw;

    JSAtom *atom;



    for (kw = keywords; kw->name; kw++) {

	atom = js_Atomize(cx, kw->name, strlen(kw->name), ATOM_PINNED);

	if (!atom)

	    return JS_FALSE;

	atom->kwindex = (JSVERSION_IS_ECMA(cx->version) ||

                         kw->version <= cx->version)

                        ? kw - keywords

                        : -1;

    }

    return JS_TRUE;

}



JS_FRIEND_API(void)

js_MapKeywords(void (*mapfun)(const char *))

{

    struct keyword *kw;



    for (kw = keywords; kw->name; kw++)

	mapfun(kw->name);

}



JSTokenStream *

js_NewTokenStream(JSContext *cx, const jschar *base, size_t length,

		  const char *filename, uintN lineno,

		  JSPrincipals *principals)

{

    JSTokenStream *ts;



    ts = js_NewBufferTokenStream(cx, base, length);

    if (!ts)

	return NULL;

    ts->filename = filename;

    ts->lineno = lineno;

    if (principals)

	JSPRINCIPALS_HOLD(cx, principals);

    ts->principals = principals;

    return ts;

}



JS_FRIEND_API(JSTokenStream *)

js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length)

{

    size_t nb;

    JSTokenStream *ts;



    if (cx->scannerVersion != cx->version) {

        if (!js_InitScanner(cx))

            return NULL;

        cx->scannerVersion = cx->version;

    }



    nb = sizeof(JSTokenStream) + JS_LINE_LIMIT * sizeof(jschar);

    JS_ARENA_ALLOCATE_CAST(ts, JSTokenStream *, &cx->tempPool, nb);

    if (!ts) {

	JS_ReportOutOfMemory(cx);

	return NULL;

    }

    memset(ts, 0, nb);

    ts->lineno = 1;

    ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = (jschar *)(ts + 1);

    ts->userbuf.base = (jschar *)base;

    ts->userbuf.limit = (jschar *)base + length;

    ts->userbuf.ptr = (jschar *)base;

    ts->listener = cx->runtime->sourceHandler;

    ts->listenerData = cx->runtime->sourceHandlerData;

    return ts;

}



JS_FRIEND_API(JSTokenStream *)

js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp)

{

    jschar *base;

    JSTokenStream *ts;

    FILE *file;



    JS_ARENA_ALLOCATE_CAST(base, jschar *, &cx->tempPool,

                           JS_LINE_LIMIT * sizeof(jschar));

    if (!base)

	return NULL;

    ts = js_NewBufferTokenStream(cx, base, JS_LINE_LIMIT);

    if (!ts)

	return NULL;

    if (!filename || strcmp(filename, "-") == 0) {

	file = defaultfp;

    } else {

	file = fopen(filename, "r");

	if (!file) {

	    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN,

				 filename, strerror(errno));

	    return NULL;

	}

    }

    ts->userbuf.ptr = ts->userbuf.limit;

    ts->file = file;

    ts->filename = filename;

    return ts;

}



JS_FRIEND_API(JSBool)

js_CloseTokenStream(JSContext *cx, JSTokenStream *ts)

{

    if (ts->principals)

	JSPRINCIPALS_DROP(cx, ts->principals);

    return !ts->file || fclose(ts->file) == 0;

}



static int32

GetChar(JSTokenStream *ts)

{

    int32 c;

    ptrdiff_t len, olen;

    jschar *nl;



    if (ts->ungetpos != 0) {

	c = ts->ungetbuf[--ts->ungetpos];

    } else {

        do {

	    if (ts->linebuf.ptr == ts->linebuf.limit) {

	        len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar);

	        if (len <= 0) {

		    /* Fill ts->userbuf so that \r and \r\n convert to \n. */

		    if (ts->file) {

		        JSBool crflag;

		        char cbuf[JS_LINE_LIMIT];

		        jschar *ubuf;

		        ptrdiff_t i, j;



		        crflag = (ts->flags & TSF_CRFLAG) != 0;

		        if (!fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file)) {

			    ts->flags |= TSF_EOF;

			    return EOF;

		        }

		        len = olen = strlen(cbuf);

		        JS_ASSERT(len > 0);

		        ubuf = ts->userbuf.base;

		        i = 0;

		        if (crflag) {

			    ts->flags &= ~TSF_CRFLAG;

			    if (cbuf[0] != '\n') {

			        ubuf[i++] = '\n';

			        len++;

			        ts->linepos--;

			    }

		        }

		        for (j = 0; i < len; i++, j++)

			    ubuf[i] = (jschar) (unsigned char) cbuf[j];

		        ts->userbuf.limit = ubuf + len;

		        ts->userbuf.ptr = ubuf;

		    } else {

		        ts->flags |= TSF_EOF;

		        return EOF;

		    }

	        }

                if (ts->listener) {

                    ts->listener(ts->filename, ts->lineno, ts->userbuf.ptr, len,

                                 &ts->listenerTSData, ts->listenerData);

                }



	        /*

	         * Any one of \n, \r, or \r\n ends a line (longest match wins).

                 * Also allow the Unicode line and paragraph separators.

	         */

	        for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) {

                    /*

                    * Try to prevent value-testing on most characters by

                    * filtering out characters that aren't 000x or 202x.

                    */

                    if ((*nl & 0xDFD0) == 0) {

		        if (*nl == '\n')

		            break;

		        if (*nl == '\r') {

		            if (nl + 1 < ts->userbuf.limit && nl[1] == '\n')

			        nl++;

		            break;

		        }

                        if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR)

		            break;

                    }

                }



	        /*

	         * If there was a line terminator, copy thru it into linebuf.

	         * Else copy JS_LINE_LIMIT-1 bytes into linebuf.

	         */

	        if (nl < ts->userbuf.limit)

		    len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1;

	        if (len >= JS_LINE_LIMIT)

		    len = JS_LINE_LIMIT - 1;

	        js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len);

	        ts->userbuf.ptr += len;

	        olen = len;



	        /*

	         * Make sure linebuf contains \n for EOL (don't do this in

	         * userbuf because the user's string might be readonly).

	         */

	        if (nl < ts->userbuf.limit) {

		    if (*nl == '\r') {

		        if (ts->linebuf.base[len-1] == '\r') {

                            /*

                             * Does the line segment end in \r?  We must check

                             * for a \n at the front of the next segment before

                             * storing a \n into linebuf.  This case matters

                             * only when we're reading from a file.

                             */

			    if (nl + 1 == ts->userbuf.limit && ts->file) {

			        len--;

			        ts->flags |= TSF_CRFLAG; /* clear NLFLAG? */

                                if (len == 0) {

                                    /*

                                     * This can happen when a segment ends in

                                     * \r\r.  Start over.  ptr == limit in this

                                     * case, so we'll fall into buffer-filling

                                     * code.

                                     */

                                    return GetChar(ts);

                                }

			    } else

                                ts->linebuf.base[len-1] = '\n';

		        }

		    } else if (*nl == '\n') {

		        if (nl > ts->userbuf.base &&

			    nl[-1] == '\r' &&

			    ts->linebuf.base[len-2] == '\r') {

			    len--;

			    JS_ASSERT(ts->linebuf.base[len] == '\n');

			    ts->linebuf.base[len-1] = '\n';

		        }

		    } else if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) {

                        ts->linebuf.base[len-1] = '\n';

		    }

	        }



	        /* Reset linebuf based on adjusted segment length. */

	        ts->linebuf.limit = ts->linebuf.base + len;

	        ts->linebuf.ptr = ts->linebuf.base;



	        /* Update position of linebuf within physical userbuf line. */

	        if (!(ts->flags & TSF_NLFLAG))

		    ts->linepos += ts->linelen;

	        else

		    ts->linepos = 0;

	        if (ts->linebuf.limit[-1] == '\n')

		    ts->flags |= TSF_NLFLAG;

	        else

		    ts->flags &= ~TSF_NLFLAG;



	        /* Update linelen from original segment length. */

	        ts->linelen = olen;

	    }

	    c = *ts->linebuf.ptr++;

        } while (JS_ISFORMAT(c));

    }

    if (c == '\n')

	ts->lineno++;

    return c;

}



static void

UngetChar(JSTokenStream *ts, int32 c)

{

    if (c == EOF)

	return;

    JS_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]);

    if (c == '\n')

	ts->lineno--;

    ts->ungetbuf[ts->ungetpos++] = (jschar)c;

}



static int32

PeekChar(JSTokenStream *ts)

{

    int32 c;



    c = GetChar(ts);

    UngetChar(ts, c);

    return c;

}



static JSBool

PeekChars(JSTokenStream *ts, intN n, jschar *cp)

{

    intN i, j;

    int32 c;



    for (i = 0; i < n; i++) {

	c = GetChar(ts);

	if (c == EOF)

	    break;

	cp[i] = (jschar)c;

    }

    for (j = i - 1; j >= 0; j--)

	UngetChar(ts, cp[j]);

    return i == n;

}



static void

SkipChars(JSTokenStream *ts, intN n)

{

    while (--n >= 0)

	GetChar(ts);

}



static JSBool

MatchChar(JSTokenStream *ts, int32 expect)

{

    int32 c;



    c = GetChar(ts);

    if (c == expect)

	return JS_TRUE;

    UngetChar(ts, c);

    return JS_FALSE;

}



JSBool

js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts,

                            JSCodeGenerator *cg, uintN flags,

                            const uintN errorNumber, ...)

{

    va_list ap;

    JSErrorReporter onError;

    JSErrorReport report;

    jschar *tokenptr;

    JSString *linestr = NULL;

    char *message;

    JSBool warning;



    memset(&report, 0, sizeof (struct JSErrorReport));

    report.flags = flags;

    report.errorNumber = errorNumber;

    message = NULL;



    va_start(ap, errorNumber);

    if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL,

				 errorNumber, &message, &report, &warning,

                                 JS_TRUE, ap)) {

	return JS_FALSE;

    }

    va_end(ap);



    js_AddRoot(cx, &linestr, "error line buffer");



    JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT);

    onError = cx->errorReporter;

    if (onError) {

        /* 

         * We are typically called with non-null ts and null cg from jsparse.c.

         * We can be called with null ts from the regexp compilation functions.

         * The code generator (jsemit.c) may pass null ts and non-null cg.

         */

        if (ts) {

            report.filename = ts->filename;

            report.lineno = ts->lineno;

            linestr = js_NewStringCopyN(cx, ts->linebuf.base,

                                        ts->linebuf.limit - ts->linebuf.base,

                                        0);

            report.linebuf = linestr

                ? JS_GetStringBytes(linestr)

                : NULL;

            tokenptr =

                ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].ptr;

            report.tokenptr = linestr

                ? report.linebuf + (tokenptr - ts->linebuf.base)

                : NULL;

            report.uclinebuf = linestr

                ? JS_GetStringChars(linestr)

                : NULL;

            report.uctokenptr = linestr

                ? report.uclinebuf + (tokenptr - ts->linebuf.base)

                : NULL;

        } else if (cg) {

            report.filename = cg->filename;

            report.lineno = cg->currentLine;

        }



#if JS_HAS_ERROR_EXCEPTIONS

	/*

	 * If there's a runtime exception type associated with this error

	 * number, set that as the pending exception.  For errors occuring at

	 * compile time, this is very likely to be a JSEXN_SYNTAXERR.  If an

	 * exception is thrown, then the JSREPORT_EXCEPTION flag will be set in

	 * report.flags.  Proper behavior for error reporters is probably to

	 * ignore this for all but toplevel compilation errors.

         *

	 * XXX it'd probably be best if there was only one call to this

	 * function, but there seem to be two error reporter call points.

	 */



        /*

         * Only try to raise an exception if there isn't one already set -

         * otherwise the exception will describe only the last compile error,

         * which is likely spurious.

         */

        if (!(ts && (ts->flags & TSF_ERROR)))

            (void) js_ErrorToException(cx, message, &report);



        /*

         * Suppress any compiletime errors that don't occur at the top level.

         * This may still fail, as interplevel may be zero in contexts where we

         * don't really want to call the error reporter, as when js is called

         * by other code which could catch the error.

         */

        if (cx->interpLevel != 0)

            onError = NULL;

#endif

        if (cx->runtime->debugErrorHook && onError) {

            JSDebugErrorHook hook = cx->runtime->debugErrorHook;

            /* test local in case debugErrorHook changed on another thread */

            if (hook && !hook(cx, message, &report,

                              cx->runtime->debugErrorHookData)) {

                onError = NULL;

            }

        }

        if (onError)

            (*onError)(cx, message, &report);

    }

    if (message)

        JS_free(cx, message);

    if (report.messageArgs) {

        int i = 0;

        while (report.messageArgs[i])

            JS_free(cx, (void *)report.messageArgs[i++]);

        JS_free(cx, (void *)report.messageArgs);

    }

    if (report.ucmessage)

        JS_free(cx, (void *)report.ucmessage);



    js_RemoveRoot(cx->runtime, &linestr);



    if (ts && !JSREPORT_IS_WARNING(flags)) {

        /* Set the error flag to suppress spurious reports. */

        ts->flags |= TSF_ERROR;

    }

    return warning;

}



JSTokenType

js_PeekToken(JSContext *cx, JSTokenStream *ts)

{

    JSTokenType tt;



    if (ts->lookahead != 0) {

        tt = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].type;

    } else {

	tt = js_GetToken(cx, ts);

	js_UngetToken(ts);

    }

    return tt;

}



JSTokenType

js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts)

{

    JSTokenType tt;



    JS_ASSERT(ts->lookahead == 0 ||

              CURRENT_TOKEN(ts).pos.end.lineno == ts->lineno);

    ts->flags |= TSF_NEWLINES;

    tt = js_PeekToken(cx, ts);

    ts->flags &= ~TSF_NEWLINES;

    return tt;

}



#define TBINCR         64



static JSBool

GrowTokenBuf(JSContext *cx, JSTokenBuf *tb)

{

    jschar *base;

    ptrdiff_t offset, length;

    size_t tbincr, tbsize;

    JSArenaPool *pool;



    base = tb->base;

    offset = PTRDIFF(tb->ptr, base, jschar);

    length = PTRDIFF(tb->limit, base, jschar);

    tbincr = (length + TBINCR) * sizeof(jschar);

    pool = &cx->tempPool;

    if (!base) {

	JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbincr);

    } else {

	tbsize = (size_t)(length * sizeof(jschar));

	JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbincr);

    }

    if (!base) {

	JS_ReportOutOfMemory(cx);

	return JS_FALSE;

    }

    tb->base = base;

    tb->limit = base + length + length + TBINCR;

    tb->ptr = base + offset;

    return JS_TRUE;

}



static JSBool

AddToTokenBuf(JSContext *cx, JSTokenBuf *tb, jschar c)

{

    if (tb->ptr == tb->limit && !GrowTokenBuf(cx, tb))

	return JS_FALSE;

    *tb->ptr++ = c;

    return JS_TRUE;

}



/*

* We encountered a '\', check for a following unicode

* escape sequence - returning it's value if so.

* Otherwise, non-destructively return the original '\'.

*/

static int32

GetUnicodeEscape(JSTokenStream *ts)

{

    jschar cp[5];

    int32 c;

    if (PeekChars(ts, 5, cp) && (cp[0] == 'u') &&

	    JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) &&

	    JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4])) {

	c = (((((JS7_UNHEX(cp[1]) << 4)

		+ JS7_UNHEX(cp[2])) << 4)

	      + JS7_UNHEX(cp[3])) << 4)

	    + JS7_UNHEX(cp[4]);

	SkipChars(ts, 5);

    }

    else

        c = '\\';

    return c;

}



JSTokenType

js_GetToken(JSContext *cx, JSTokenStream *ts)

{

    JSTokenType tt;

    JSToken *tp;

    int32 c;

    JSAtom *atom;

    JSBool hadUnicodeEscape;



#define INIT_TOKENBUF(tb)   ((tb)->ptr = (tb)->base)

#define FINISH_TOKENBUF(tb) if (!AddToTokenBuf(cx, tb, 0)) RETURN(TOK_ERROR)

#define TOKEN_LENGTH(tb)    ((tb)->ptr - (tb)->base - 1)

#define RETURN(tt)          { if (tt == TOK_ERROR) ts->flags |= TSF_ERROR;    \

			      tp->pos.end.index = ts->linepos +               \

				  (ts->linebuf.ptr - ts->linebuf.base) -      \

				  ts->ungetpos;                               \

			      return (tp->type = tt); }



    /* If there was a fatal error, keep returning TOK_ERROR. */

    if (ts->flags & TSF_ERROR)

	return TOK_ERROR;



    /* Check for a pushed-back token resulting from mismatching lookahead. */

    while (ts->lookahead != 0) {

        ts->lookahead--;

        ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;

        tt = CURRENT_TOKEN(ts).type;

        if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES))

            return tt;

    }



retry:

    do {

	c = GetChar(ts);

        if (c == '\n') {

            ts->flags &= ~TSF_DIRTYLINE;

            if (ts->flags & TSF_NEWLINES)

	        break;

        }

    } while (JS_ISSPACE(c));



    ts->cursor = (ts->cursor + 1) & NTOKENS_MASK;

    tp = &CURRENT_TOKEN(ts);

    tp->ptr = ts->linebuf.ptr - 1;

    tp->pos.begin.index = ts->linepos + (tp->ptr - ts->linebuf.base);

    tp->pos.begin.lineno = tp->pos.end.lineno = ts->lineno;



    if (c == EOF)

	RETURN(TOK_EOF);

    if (c != '-' && c != '\n')

        ts->flags |= TSF_DIRTYLINE;



    hadUnicodeEscape = JS_FALSE;

    if (JS_ISIDENT_START(c) ||

        (c == '\\' &&

         (c = GetUnicodeEscape(ts),

          hadUnicodeEscape = JS_ISIDENT_START(c)))) {

	INIT_TOKENBUF(&ts->tokenbuf);

        for (;;) {

	    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))

		RETURN(TOK_ERROR);

	    c = GetChar(ts);

            if (c == '\\') {

                c = GetUnicodeEscape(ts);

                if (!JS_ISIDENT(c))

                    break;

                hadUnicodeEscape = JS_TRUE;

            } else {

                if (!JS_ISIDENT(c))

                    break;

            }

        }

	UngetChar(ts, c);

	FINISH_TOKENBUF(&ts->tokenbuf);



	atom = js_AtomizeChars(cx,

			       ts->tokenbuf.base,

			       TOKEN_LENGTH(&ts->tokenbuf),

			       0);

	if (!atom)

	    RETURN(TOK_ERROR);

        if (!hadUnicodeEscape && atom->kwindex >= 0) {

            struct keyword *kw;



            kw = &keywords[atom->kwindex];

            tp->t_op = (JSOp) kw->op;

            RETURN(kw->tokentype);

        }

	tp->t_op = JSOP_NAME;

	tp->t_atom = atom;

	RETURN(TOK_NAME);

    }



    if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) {

	jsint radix;

	const jschar *endptr;

	jsdouble dval;



	radix = 10;

	INIT_TOKENBUF(&ts->tokenbuf);



	if (c == '0') {

	    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))

		RETURN(TOK_ERROR);

	    c = GetChar(ts);

	    if (JS_TOLOWER(c) == 'x') {

		if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))

		    RETURN(TOK_ERROR);

		c = GetChar(ts);

		radix = 16;

	    } else if (JS7_ISDEC(c)) {

                radix = 8;

	    }

	}



	while (JS7_ISHEX(c)) {

	    if (radix < 16) {

                if (JS7_ISLET(c))

                    break;



		/*

		 * We permit 08 and 09 as decimal numbers, which makes our

		 * behaviour a superset of the ECMA numeric grammar.  We might

		 * not always be so permissive, so we warn about it.

		 */

		if (radix == 8 && c >= '8') {

		    if (!js_ReportCompileErrorNumber(cx, ts, NULL,

                                                     JSREPORT_WARNING,

                                                     JSMSG_BAD_OCTAL,

                                                     c == '8' ? "08" : "09")) {

                        RETURN(TOK_ERROR);

                    }

		    radix = 10;

		}

            }

	    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))

		RETURN(TOK_ERROR);

	    c = GetChar(ts);

	}



	if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) {

	    if (c == '.') {

		do {

		    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))

			RETURN(TOK_ERROR);

		    c = GetChar(ts);

		} while (JS7_ISDEC(c));

	    }

	    if (JS_TOLOWER(c) == 'e') {

		if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))

		    RETURN(TOK_ERROR);

		c = GetChar(ts);

		if (c == '+' || c == '-') {

		    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))

			RETURN(TOK_ERROR);

		    c = GetChar(ts);

		}

		if (!JS7_ISDEC(c)) {

		    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                                JSMSG_MISSING_EXPONENT);

		    RETURN(TOK_ERROR);

		}

		do {

		    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))

			RETURN(TOK_ERROR);

		    c = GetChar(ts);

		} while (JS7_ISDEC(c));

	    }

	}



	UngetChar(ts, c);

	FINISH_TOKENBUF(&ts->tokenbuf);



	if (radix == 10) {

	    if (!js_strtod(cx, ts->tokenbuf.base, &endptr, &dval)) {

		js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                            JSMSG_OUT_OF_MEMORY);

		RETURN(TOK_ERROR);

	    }

	} else {

	    if (!js_strtointeger(cx, ts->tokenbuf.base, &endptr, radix, &dval)) {

		js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                            JSMSG_OUT_OF_MEMORY);

		RETURN(TOK_ERROR);

	    }

	}

	tp->t_dval = dval;

	RETURN(TOK_NUMBER);

    }



    if (c == '"' || c == '\'') {

	int32 val, qc = c;



	INIT_TOKENBUF(&ts->tokenbuf);

	while ((c = GetChar(ts)) != qc) {

	    if (c == '\n' || c == EOF) {

		UngetChar(ts, c);

		js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                            JSMSG_UNTERMINATED_STRING);

		RETURN(TOK_ERROR);

	    }

	    if (c == '\\') {

		switch (c = GetChar(ts)) {

		  case 'b': c = '\b'; break;

		  case 'f': c = '\f'; break;

		  case 'n': c = '\n'; break;

		  case 'r': c = '\r'; break;

		  case 't': c = '\t'; break;

		  case 'v': c = '\v'; break;



		  default:

		    if ('0' <= c && c < '8') {

			val = JS7_UNDEC(c);

			c = PeekChar(ts);

			if ('0' <= c && c < '8') {

			    val = 8 * val + JS7_UNDEC(c);

			    GetChar(ts);

			    c = PeekChar(ts);

			    if ('0' <= c && c < '8') {

				int32 save = val;

				val = 8 * val + JS7_UNDEC(c);

				if (val <= 0377)

				    GetChar(ts);

				else

				    val = save;

			    }

			}

			c = (jschar)val;

		    } else if (c == 'u') {

			jschar cp[4];

			if (PeekChars(ts, 4, cp) &&

			    JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) &&

			    JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) {

			    c = (((((JS7_UNHEX(cp[0]) << 4)

				    + JS7_UNHEX(cp[1])) << 4)

				  + JS7_UNHEX(cp[2])) << 4)

				+ JS7_UNHEX(cp[3]);

			    SkipChars(ts, 4);

			}

		    } else if (c == 'x') {

			jschar cp[2];

			if (PeekChars(ts, 2, cp) &&

			    JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) {

			    c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]);

			    SkipChars(ts, 2);

			}

		    } else if (c == '\n' && JSVERSION_IS_ECMA(cx->version)) {

                        /* ECMA follows C by removing escaped newlines. */

                        continue;

                    }

		    break;

		}

	    }

	    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))

		RETURN(TOK_ERROR);

	}

	FINISH_TOKENBUF(&ts->tokenbuf);

	atom = js_AtomizeChars(cx,

			       ts->tokenbuf.base,

			       TOKEN_LENGTH(&ts->tokenbuf),

			       0);

	if (!atom)

	    RETURN(TOK_ERROR);

	tp->pos.end.lineno = ts->lineno;

	tp->t_op = JSOP_STRING;

	tp->t_atom = atom;

	RETURN(TOK_STRING);

    }



    switch (c) {

      case '\n': 

        c = TOK_EOL; 

        break;



      case ';': c = TOK_SEMI; break;

      case '.': c = TOK_DOT; break;

      case '[': c = TOK_LB; break;

      case ']': c = TOK_RB; break;

      case '{': c = TOK_LC; break;

      case '}': c = TOK_RC; break;

      case '(': c = TOK_LP; break;

      case ')': c = TOK_RP; break;

      case ',': c = TOK_COMMA; break;

      case '?': c = TOK_HOOK; break;



      case ':':

        /*

         * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an

         * object initializer, likewise for setter.

         */

        tp->t_op = JSOP_NOP;

        c = TOK_COLON;

        break;



      case '|':

	if (MatchChar(ts, c)) {

	    c = TOK_OR;

	} else if (MatchChar(ts, '=')) {

	    tp->t_op = JSOP_BITOR;

	    c = TOK_ASSIGN;

	} else {

	    c = TOK_BITOR;

	}

	break;



      case '^':

	if (MatchChar(ts, '=')) {

	    tp->t_op = JSOP_BITXOR;

	    c = TOK_ASSIGN;

	} else {

	    c = TOK_BITXOR;

	}

	break;



      case '&':

	if (MatchChar(ts, c)) {

	    c = TOK_AND;

	} else if (MatchChar(ts, '=')) {

	    tp->t_op = JSOP_BITAND;

	    c = TOK_ASSIGN;

	} else {

	    c = TOK_BITAND;

	}

	break;



      case '=':

	if (MatchChar(ts, c)) {

#if JS_HAS_TRIPLE_EQOPS

	    tp->t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : (JSOp)cx->jsop_eq;

#else

	    tp->t_op = cx->jsop_eq;

#endif

	    c = TOK_EQOP;

	} else {

	    tp->t_op = JSOP_NOP;

	    c = TOK_ASSIGN;

	}

	break;



      case '!':

	if (MatchChar(ts, '=')) {

#if JS_HAS_TRIPLE_EQOPS

	    tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne;

#else

	    tp->t_op = cx->jsop_ne;

#endif

	    c = TOK_EQOP;

	} else {

	    tp->t_op = JSOP_NOT;

	    c = TOK_UNARYOP;

	}

	break;



      case '<':

	/* NB: treat HTML begin-comment as comment-till-end-of-line */

	if (MatchChar(ts, '!')) {

	    if (MatchChar(ts, '-')) {

		if (MatchChar(ts, '-'))

		    goto skipline;

		UngetChar(ts, '-');

	    }

	    UngetChar(ts, '!');

	}

	if (MatchChar(ts, c)) {

	    tp->t_op = JSOP_LSH;

	    c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;

	} else {

	    tp->t_op = MatchChar(ts, '=') ? JSOP_LE : JSOP_LT;

	    c = TOK_RELOP;

	}

	break;



      case '>':

	if (MatchChar(ts, c)) {

	    tp->t_op = MatchChar(ts, c) ? JSOP_URSH : JSOP_RSH;

	    c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_SHOP;

	} else {

	    tp->t_op = MatchChar(ts, '=') ? JSOP_GE : JSOP_GT;

	    c = TOK_RELOP;

	}

	break;



      case '*':

	tp->t_op = JSOP_MUL;

	c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_STAR;

	break;



      case '/':

	if (MatchChar(ts, '/')) {

skipline:

	    while ((c = GetChar(ts)) != EOF && c != '\n')

		/* skip to end of line */;

	    UngetChar(ts, c);

	    goto retry;

	}

	if (MatchChar(ts, '*')) {

	    while ((c = GetChar(ts)) != EOF &&

		   !(c == '*' && MatchChar(ts, '/'))) {

		if (c == '/' && MatchChar(ts, '*')) {

		    if (MatchChar(ts, '/'))

			goto retry;

		    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                                JSMSG_NESTED_COMMENT);

		}

	    }

	    if (c == EOF) {

		js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                            JSMSG_UNTERMINATED_COMMENT);

		RETURN(TOK_ERROR);

	    }

	    goto retry;

	}



#if JS_HAS_REGEXPS

	if (ts->flags & TSF_REGEXP) {

	    JSObject *obj;

	    uintN flags;



	    INIT_TOKENBUF(&ts->tokenbuf);

	    while ((c = GetChar(ts)) != '/') {

		if (c == '\n' || c == EOF) {

		    UngetChar(ts, c);

		    js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                                JSMSG_UNTERMINATED_REGEXP);

		    RETURN(TOK_ERROR);

		}

		if (c == '\\') {

		    if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))

			RETURN(TOK_ERROR);

		    c = GetChar(ts);

		}

		if (!AddToTokenBuf(cx, &ts->tokenbuf, (jschar)c))

		    RETURN(TOK_ERROR);

	    }

	    FINISH_TOKENBUF(&ts->tokenbuf);

	    for (flags = 0; ; ) {

		if (MatchChar(ts, 'g'))

		    flags |= JSREG_GLOB;

		else if (MatchChar(ts, 'i'))

		    flags |= JSREG_FOLD;

		else if (MatchChar(ts, 'm'))

		    flags |= JSREG_MULTILINE;

		else

		    break;

	    }

	    c = PeekChar(ts);

	    if (JS7_ISLET(c)) {

		tp->ptr = ts->linebuf.ptr - 1;

		js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                            JSMSG_BAD_REGEXP_FLAG);

		(void) GetChar(ts);

		RETURN(TOK_ERROR);

	    }

	    obj = js_NewRegExpObject(cx, ts,

				     ts->tokenbuf.base,

				     TOKEN_LENGTH(&ts->tokenbuf),

				     flags);

            if (!obj)

                RETURN(TOK_ERROR);

	    atom = js_AtomizeObject(cx, obj, 0);

	    if (!atom)

		RETURN(TOK_ERROR);

	    tp->t_op = JSOP_OBJECT;

	    tp->t_atom = atom;

	    RETURN(TOK_OBJECT);

	}

#endif /* JS_HAS_REGEXPS */



	tp->t_op = JSOP_DIV;

	c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;

	break;



      case '%':

	tp->t_op = JSOP_MOD;

	c = MatchChar(ts, '=') ? TOK_ASSIGN : TOK_DIVOP;

	break;



      case '~':

	tp->t_op = JSOP_BITNOT;

	c = TOK_UNARYOP;

	break;



      case '+':

	if (MatchChar(ts, '=')) {

	    tp->t_op = JSOP_ADD;

	    c = TOK_ASSIGN;

	} else if (MatchChar(ts, c)) {

	    c = TOK_INC;

	} else {

	    tp->t_op = JSOP_POS;

	    c = TOK_PLUS;

	}

	break;



      case '-':

        if (MatchChar(ts, '=')) {

            tp->t_op = JSOP_SUB;

            c = TOK_ASSIGN;

        } else if (MatchChar(ts, c)) {

            if ((PeekChar(ts) == '>') && !(ts->flags & TSF_DIRTYLINE))

                goto skipline;

            c = TOK_DEC;

        } else {

            tp->t_op = JSOP_NEG;

            c = TOK_MINUS;

        }

        ts->flags |= TSF_DIRTYLINE;

        break;



#if JS_HAS_SHARP_VARS

      case '#':

      {

	uint32 n;



	c = GetChar(ts);

	if (!JS7_ISDEC(c)) {

	    UngetChar(ts, c);

	    goto badchar;

	}

	n = (uint32)JS7_UNDEC(c);

	for (;;) {

	    c = GetChar(ts);

	    if (!JS7_ISDEC(c))

		break;

	    n = 10 * n + JS7_UNDEC(c);

	    if (n >= ATOM_INDEX_LIMIT) {

		js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                            JSMSG_SHARPVAR_TOO_BIG);

		RETURN(TOK_ERROR);

	    }

	}

	tp->t_dval = (jsdouble) n;

        if (JS_HAS_STRICT_OPTION(cx) &&

            (c == '=' || c == '#')) {

            char buf[20];

            JS_snprintf(buf, sizeof buf, "#%u%c", n, c);

            if (!JS_ReportErrorFlagsAndNumber(cx,

                                              JSREPORT_WARNING |

                                              JSREPORT_STRICT,

                                              js_GetErrorMessage, NULL,

                                              JSMSG_DEPRECATED_USAGE,

                                              buf)) {

                RETURN(TOK_ERROR);

            }

        }

	if (c == '=')

	    RETURN(TOK_DEFSHARP);

	if (c == '#')

	    RETURN(TOK_USESHARP);

	goto badchar;

      }



      badchar:

#endif /* JS_HAS_SHARP_VARS */



      default:

	js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,

                                    JSMSG_ILLEGAL_CHARACTER);

	RETURN(TOK_ERROR);

    }



    JS_ASSERT(c < TOK_LIMIT);

    RETURN((JSTokenType)c);



#undef INIT_TOKENBUF

#undef FINISH_TOKENBUF

#undef TOKEN_LENGTH

#undef RETURN

}



void

js_UngetToken(JSTokenStream *ts)

{

    JS_ASSERT(ts->lookahead < NTOKENS_MASK);

    if (ts->flags & TSF_ERROR)

	return;

    ts->lookahead++;

    ts->cursor = (ts->cursor - 1) & NTOKENS_MASK;

}



JSBool

js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt)

{

    if (js_GetToken(cx, ts) == tt)

	return JS_TRUE;

    js_UngetToken(ts);

    return JS_FALSE;

}

 

**** End of jsscan.c ****

 

**** Start of jsscan.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsscan_h___

#define jsscan_h___

/*

 * JS lexical scanner interface.

 */

#include <stddef.h>

#include <stdio.h>

#include "jsopcode.h"

#include "jsprvtd.h"

#include "jspubtd.h"



JS_BEGIN_EXTERN_C



typedef enum JSTokenType {

    TOK_ERROR = -1,                     /* well-known as the only code < EOF */

    TOK_EOF = 0,                        /* end of file */

    TOK_EOL = 1,                        /* end of line */

    TOK_SEMI = 2,                       /* semicolon */

    TOK_LB = 3, TOK_RB = 4,             /* left and right brackets */

    TOK_LC = 5, TOK_RC = 6,             /* left and right curlies (braces) */

    TOK_LP = 7, TOK_RP = 8,             /* left and right parentheses */

    TOK_COMMA = 9,                      /* comma operator */

    TOK_ASSIGN = 10,                    /* assignment ops (= += -= etc.) */

    TOK_HOOK = 11, TOK_COLON = 12,      /* conditional (?:) */

    TOK_OR = 13,                        /* logical or (||) */

    TOK_AND = 14,                       /* logical and (&&) */

    TOK_BITOR = 15,                     /* bitwise-or (|) */

    TOK_BITXOR = 16,                    /* bitwise-xor (^) */

    TOK_BITAND = 17,                    /* bitwise-and (&) */

    TOK_EQOP = 18,                      /* equality ops (== !=) */

    TOK_RELOP = 19,                     /* relational ops (< <= > >=) */

    TOK_SHOP = 20,                      /* shift ops (<< >> >>>) */

    TOK_PLUS = 21,                      /* plus */

    TOK_MINUS = 22,                     /* minus */

    TOK_STAR = 23, TOK_DIVOP = 24,      /* multiply/divide ops (* / %) */

    TOK_UNARYOP = 25,                   /* unary prefix operator */

    TOK_INC = 26, TOK_DEC = 27,         /* increment/decrement (++ --) */

    TOK_DOT = 28,                       /* member operator (.) */

    TOK_NAME = 29,                      /* identifier */

    TOK_NUMBER = 30,                    /* numeric constant */

    TOK_STRING = 31,                    /* string constant */

    TOK_OBJECT = 32,                    /* RegExp or other object constant */

    TOK_PRIMARY = 33,                   /* true, false, null, this, super */

    TOK_FUNCTION = 34,                  /* function keyword */

    TOK_EXPORT = 35,                    /* export keyword */

    TOK_IMPORT = 36,                    /* import keyword */

    TOK_IF = 37,                        /* if keyword */

    TOK_ELSE = 38,                      /* else keyword */

    TOK_SWITCH = 39,                    /* switch keyword */

    TOK_CASE = 40,                      /* case keyword */

    TOK_DEFAULT = 41,                   /* default keyword */

    TOK_WHILE = 42,                     /* while keyword */

    TOK_DO = 43,                        /* do keyword */

    TOK_FOR = 44,                       /* for keyword */

    TOK_BREAK = 45,                     /* break keyword */

    TOK_CONTINUE = 46,                  /* continue keyword */

    TOK_IN = 47,                        /* in keyword */

    TOK_VAR = 48,                       /* var keyword */

    TOK_WITH = 49,                      /* with keyword */

    TOK_RETURN = 50,                    /* return keyword */

    TOK_NEW = 51,                       /* new keyword */

    TOK_DELETE = 52,                    /* delete keyword */

    TOK_DEFSHARP = 53,                  /* #n= for object/array initializers */

    TOK_USESHARP = 54,                  /* #n# for object/array initializers */

    TOK_TRY = 55,                       /* try keyword */

    TOK_CATCH = 56,                     /* catch keyword */

    TOK_FINALLY = 57,                   /* finally keyword */

    TOK_THROW = 58,                     /* throw keyword */

    TOK_INSTANCEOF = 59,                /* instanceof keyword */

    TOK_DEBUGGER = 60,                  /* debugger keyword */

    TOK_RESERVED,                       /* reserved keywords */

    TOK_LIMIT                           /* domain size */

} JSTokenType;



#define IS_PRIMARY_TOKEN(tt) \

    ((uintN)((tt) - TOK_NAME) <= (uintN)(TOK_PRIMARY - TOK_NAME))



struct JSTokenPtr {

    uint16              index;          /* index of char in physical line */

    uint16              lineno;         /* physical line number */

};



struct JSTokenPos {

    JSTokenPtr          begin;          /* first character and line of token */

    JSTokenPtr          end;            /* index 1 past last char, last line */

};



struct JSToken {

    JSTokenType         type;           /* char value or above enumerator */

    JSTokenPos          pos;            /* token position in file */

    jschar              *ptr;           /* beginning of token in line buffer */

    union {

	struct {

	    JSOp        op;             /* operator, for minimal parser */

	    JSAtom      *atom;          /* atom table entry */

	} s;

	jsdouble        dval;           /* floating point number */

    } u;

};



#define t_op            u.s.op

#define t_atom          u.s.atom

#define t_dval          u.dval



typedef struct JSTokenBuf {

    jschar              *base;          /* base of line or stream buffer */

    jschar              *limit;         /* limit for quick bounds check */

    jschar              *ptr;           /* next char to get, or slot to use */

} JSTokenBuf;



#define JS_LINE_LIMIT   256             /* logical line buffer size limit --

					   physical line length is unlimited */

#define NTOKENS         4               /* 1 current + 2 lookahead, rounded */

#define NTOKENS_MASK    (NTOKENS-1)     /* to power of 2 to avoid divmod by 3 */



struct JSTokenStream {

    JSToken             tokens[NTOKENS];/* circular token buffer */

    uintN               cursor;         /* index of last parsed token */

    uintN               lookahead;      /* count of lookahead tokens */

    uintN               lineno;         /* current line number */

    uintN               ungetpos;       /* next free char slot in ungetbuf */

    jschar              ungetbuf[6];    /* at most 6, for \uXXXX lookahead */

    uintN               flags;          /* flags -- see below */

    ptrdiff_t           linelen;        /* physical linebuf segment length */

    ptrdiff_t           linepos;        /* linebuf offset in physical line */

    JSTokenBuf          linebuf;        /* line buffer for diagnostics */

    JSTokenBuf          userbuf;        /* user input buffer if !file */

    JSTokenBuf          tokenbuf;       /* current token string buffer */

    const char          *filename;      /* input filename or null */

    FILE                *file;          /* stdio stream if reading from file */

    JSPrincipals        *principals;    /* principals associated with source */

    JSSourceHandler     listener;       /* callback for source; eg debugger */

    void                *listenerData;  /* listener 'this' data */

    void                *listenerTSData;/* listener data for this TokenStream */

};



#define CURRENT_TOKEN(ts) ((ts)->tokens[(ts)->cursor])



/* JSTokenStream flags */

#define TSF_ERROR       0x01            /* fatal error while compiling */

#define TSF_EOF         0x02            /* hit end of file */

#define TSF_NEWLINES    0x04            /* tokenize newlines */

#define TSF_REGEXP      0x08            /* looking for a regular expression */

#define TSF_NLFLAG      0x20            /* last linebuf ended with \n */

#define TSF_CRFLAG      0x40            /* linebuf would have ended with \r */

#define TSF_DIRTYLINE   0x80            /* stuff other than whitespace since start of line */



/*

 * Create a new token stream, either from an input buffer or from a file.

 * Return null on file-open or memory-allocation failure.

 *

 * NB: All of js_New{,Buffer,File}TokenStream() return a pointer to transient

 * memory in the current context's temp pool.  This memory is deallocated via

 * JS_ARENA_RELEASE() after parsing is finished.

 */

extern JSTokenStream *

js_NewTokenStream(JSContext *cx, const jschar *base, size_t length,

		  const char *filename, uintN lineno, JSPrincipals *principals);



extern JS_FRIEND_API(JSTokenStream *)

js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length);



extern JS_FRIEND_API(JSTokenStream *)

js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp);



extern JS_FRIEND_API(JSBool)

js_CloseTokenStream(JSContext *cx, JSTokenStream *ts);



/*

 * Initialize the scanner, installing JS keywords into cx's global scope.

 */

extern JSBool

js_InitScanner(JSContext *cx);



/*

 * Friend-exported API entry point to call a mapping function on each reserved

 * identifier in the scanner's keyword table.

 */

extern JS_FRIEND_API(void)

js_MapKeywords(void (*mapfun)(const char *));



extern JSBool

js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts,

                            JSCodeGenerator *cg, uintN flags,

                            const uintN errorNumber, ...);



/*

 * Look ahead one token and return its type.

 */

extern JSTokenType

js_PeekToken(JSContext *cx, JSTokenStream *ts);



extern JSTokenType

js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts);



/*

 * Get the next token from ts.

 */

extern JSTokenType

js_GetToken(JSContext *cx, JSTokenStream *ts);



/*

 * Push back the last scanned token onto ts.

 */

extern void

js_UngetToken(JSTokenStream *ts);



/*

 * Get the next token from ts if its type is tt.

 */

extern JSBool

js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt);



JS_END_EXTERN_C



#endif /* jsscan_h___ */

 

**** End of jsscan.h ****

 

**** Start of jsscope.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS symbol tables.

 */

#include "jsstddef.h"

#include <string.h>

#include "jstypes.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jsapi.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsinterp.h"

#include "jslock.h"

#include "jsnum.h"

#include "jsscope.h"

#include "jsstr.h"



JS_STATIC_DLL_CALLBACK(JSHashNumber)

js_hash_id(const void *key)

{

    jsval v;

    const JSAtom *atom;



    v = (jsval)key;

    if (JSVAL_IS_INT(v))

	return JSVAL_TO_INT(v);

    atom = (JSAtom *) key;

    return atom->number;

}



typedef struct JSScopePrivate {

    JSContext *context;

    JSScope *scope;

} JSScopePrivate;



JS_STATIC_DLL_CALLBACK(void *)

js_alloc_scope_space(void *priv, size_t size)

{

    return JS_malloc(((JSScopePrivate *)priv)->context, size);

}



JS_STATIC_DLL_CALLBACK(void)

js_free_scope_space(void *priv, void *item)

{

    JS_free(((JSScopePrivate *)priv)->context, item);

}



JS_STATIC_DLL_CALLBACK(JSHashEntry *)

js_alloc_symbol(void *priv, const void *key)

{

    JSScopePrivate *spriv;

    JSContext *cx;

    JSSymbol *sym;



    spriv = (JSScopePrivate *) priv;

    JS_ASSERT(JS_IS_SCOPE_LOCKED(spriv->scope));

    cx = spriv->context;

    sym = (JSSymbol *) JS_malloc(cx, sizeof(JSSymbol));

    if (!sym)

	return NULL;

    sym->entry.key = key;

    return &sym->entry;

}



JS_STATIC_DLL_CALLBACK(void)

js_free_symbol(void *priv, JSHashEntry *he, uintN flag)

{

    JSScopePrivate *spriv;

    JSContext *cx;

    JSSymbol *sym, **sp;

    JSScopeProperty *sprop;



    spriv = (JSScopePrivate *) priv;

    JS_ASSERT(JS_IS_SCOPE_LOCKED(spriv->scope));

    cx = spriv->context;

    sym = (JSSymbol *)he;

    sprop = (JSScopeProperty *) sym->entry.value;

    if (sprop) {

	sym->entry.value = NULL;

	sprop = js_DropScopeProperty(cx, spriv->scope, sprop);

	if (sprop) {

	    for (sp = &sprop->symbols; *sp; sp = &(*sp)->next) {

		if (*sp == sym) {

		    *sp = sym->next;

		    if (!*sp)

			break;

		}

	    }

	    sym->next = NULL;

	}

    }



    if (flag == HT_FREE_ENTRY)

	JS_free(cx, he);

}



static JSHashAllocOps hash_scope_alloc_ops = {

    js_alloc_scope_space, js_free_scope_space,

    js_alloc_symbol, js_free_symbol

};



/************************************************************************/



JS_STATIC_DLL_CALLBACK(JSSymbol *)

js_hash_scope_lookup(JSContext *cx, JSScope *scope, jsid id, JSHashNumber hash)

{

    JSHashTable *table = (JSHashTable *) scope->data;

    JSHashEntry **hep;

    JSSymbol *sym;



    hep = JS_HashTableRawLookup(table, hash, (const void *)id);

    sym = (JSSymbol *) *hep;

    return sym;

}



#define SCOPE_ADD(PRIV, CLASS_SPECIFIC_CODE)                                  \

    JS_BEGIN_MACRO                                                            \

	if (sym) {                                                            \

	    if (sym->entry.value == sprop)                                    \

		return sym;                                                   \

	    if (sym->entry.value)                                             \

		js_free_symbol(PRIV, &sym->entry, HT_FREE_VALUE);             \

	} else {                                                              \

	    CLASS_SPECIFIC_CODE                                               \

	    sym->scope = scope;                                               \

	    sym->next = NULL;                                                 \

	}                                                                     \

	if (sprop) {                                                          \

	    sym->entry.value = js_HoldScopeProperty(cx, scope, sprop);        \

	    for (sp = &sprop->symbols; *sp; sp = &(*sp)->next)                \

		;                                                             \

	    *sp = sym;                                                        \

	} else {                                                              \

	    sym->entry.value = NULL;                                          \

	}                                                                     \

    JS_END_MACRO



JS_STATIC_DLL_CALLBACK(JSSymbol *)

js_hash_scope_add(JSContext *cx, JSScope *scope, jsid id, JSScopeProperty *sprop)

{

    JSHashTable *table = (JSHashTable *) scope->data;

    const void *key;

    JSHashNumber keyHash;

    JSHashEntry **hep;

    JSSymbol *sym, **sp;

    JSScopePrivate *priv;



    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));

    priv = (JSScopePrivate *) table->allocPriv;

    priv->context = cx;

    key = (const void *)id;

    keyHash = js_hash_id(key);

    hep = JS_HashTableRawLookup(table, keyHash, key);

    sym = (JSSymbol *) *hep;

    SCOPE_ADD(priv,

	sym = (JSSymbol *) JS_HashTableRawAdd(table, hep, keyHash, key, NULL);

	if (!sym)

	    return NULL;

    );

    return sym;

}



JS_STATIC_DLL_CALLBACK(JSBool)

js_hash_scope_remove(JSContext *cx, JSScope *scope, jsid id)

{

    JSHashTable *table = (JSHashTable *) scope->data;

    JSScopePrivate *priv;



    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));

    priv = (JSScopePrivate *) table->allocPriv;

    priv->context = cx;

    return JS_HashTableRemove(table, (const void *)id);

}



JS_STATIC_DLL_CALLBACK(intN)

js_hash_scope_slot_invalidator(JSHashEntry *he, intN i, void *arg)

{

    JSSymbol *sym = (JSSymbol *) he;

    JSScopeProperty *sprop;



    if (sym) {

        sprop = (JSScopeProperty *) sym->entry.value;

        if (sprop)

            sprop->slot = SPROP_INVALID_SLOT;

    }

    return HT_ENUMERATE_NEXT;

}



/* Forward declaration for use by js_hash_scope_clear(). */

extern JS_FRIEND_DATA(JSScopeOps) js_list_scope_ops;



JS_STATIC_DLL_CALLBACK(void)

js_hash_scope_clear(JSContext *cx, JSScope *scope)

{

    JSHashTable *table = (JSHashTable *) scope->data;

    JSScopePrivate *priv;



    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));

    priv = (JSScopePrivate *) table->allocPriv;

    priv->context = cx;

    JS_HashTableEnumerateEntries(table, js_hash_scope_slot_invalidator, NULL);

    JS_HashTableDestroy(table);

    JS_free(cx, priv);

    scope->ops = &js_list_scope_ops;

    scope->data = NULL;

}



JSScopeOps js_hash_scope_ops = {

    js_hash_scope_lookup,

    js_hash_scope_add,

    js_hash_scope_remove,

    js_hash_scope_clear

};



/************************************************************************/



JS_STATIC_DLL_CALLBACK(JSSymbol *)

js_list_scope_lookup(JSContext *cx, JSScope *scope, jsid id, JSHashNumber hash)

{

    JSSymbol *sym, **sp;

    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));



    for (sp = (JSSymbol **)&scope->data; (sym = *sp) != 0;

	 sp = (JSSymbol **)&sym->entry.next) {

	if (sym_id(sym) == id) {

	    /* Move sym to the front for shorter searches. */

            *sp = (JSSymbol *) sym->entry.next;

            sp = (JSSymbol **) &scope->data;    /* avoid (void *) aliases */

            sym->entry.next = &(*sp)->entry;    /* NB: *sp could be null! */

            *sp = sym;

	    return sym;

	}

    }

    return NULL;

}



#define HASH_THRESHOLD	5



JS_STATIC_DLL_CALLBACK(JSSymbol *)

js_list_scope_add(JSContext *cx, JSScope *scope, jsid id, JSScopeProperty *sprop)

{

    JSSymbol *list = (JSSymbol *) scope->data;

    uint32 nsyms;

    JSSymbol *sym, *next, **sp;

    JSHashTable *table;

    JSHashEntry **hep;

    JSScopePrivate priv;



    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));

    nsyms = 0;

    for (sym = list; sym; sym = (JSSymbol *)sym->entry.next) {

	if (sym_id(sym) == id)

	    break;

	nsyms++;

    }



    if (nsyms >= HASH_THRESHOLD) {

	JSScopePrivate *new_priv = (JSScopePrivate *)

            JS_malloc(cx, sizeof(JSScopePrivate));

	if (!new_priv)

            return NULL;

	new_priv->context = cx;

	new_priv->scope = scope;

	table = JS_NewHashTable(nsyms, js_hash_id,

				JS_CompareValues, JS_CompareValues,

				&hash_scope_alloc_ops, new_priv);

	if (table) {

	    for (sym = list; sym; sym = next) {

		/* Save next for loop update, before it changes in lookup. */

		next = (JSSymbol *)sym->entry.next;



		/* Now compute missing keyHash fields. */

		sym->entry.keyHash = js_hash_id(sym->entry.key);

		sym->entry.next = NULL;

		hep = JS_HashTableRawLookup(table,

					    sym->entry.keyHash,

					    sym->entry.key);

		*hep = &sym->entry;

	    }

	    table->nentries = nsyms;

	    scope->ops = &js_hash_scope_ops;

	    scope->data = table;

	    return scope->ops->add(cx, scope, id, sprop);

	}

    }



    priv.context = cx;

    priv.scope = scope;

    SCOPE_ADD(&priv,

	sym = (JSSymbol *)js_alloc_symbol(&priv, (const void *)id);

	if (!sym)

	    return NULL;

	/* Don't set keyHash until we know we need it, above. */

	sym->entry.next = (JSHashEntry *) scope->data;

	scope->data = sym;

    );

    return sym;

}



JS_STATIC_DLL_CALLBACK(JSBool)

js_list_scope_remove(JSContext *cx, JSScope *scope, jsid id)

{

    JSSymbol *sym, **sp;

    JSScopePrivate priv;



    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));

    for (sp = (JSSymbol **)&scope->data; (sym = *sp) != 0;

	 sp = (JSSymbol **)&sym->entry.next) {

	if (sym_id(sym) == id) {

	    *sp = (JSSymbol *)sym->entry.next;

	    priv.context = cx;

	    priv.scope = scope;

	    js_free_symbol(&priv, &sym->entry, HT_FREE_ENTRY);

	    return JS_TRUE;

	}

    }

    return JS_FALSE;

}



JS_STATIC_DLL_CALLBACK(void)

js_list_scope_clear(JSContext *cx, JSScope *scope)

{

    JSSymbol *sym;

    JSScopePrivate priv;

    JSScopeProperty *sprop;



    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));

    while ((sym = (JSSymbol *) scope->data) != NULL) {

        scope->data = sym->entry.next;

        priv.context = cx;

        priv.scope = scope;

        sprop = (JSScopeProperty *) sym->entry.value;

        if (sprop)

            sprop->slot = SPROP_INVALID_SLOT;

        js_free_symbol(&priv, &sym->entry, HT_FREE_ENTRY);

    }

}



JSScopeOps JS_FRIEND_DATA(js_list_scope_ops) = {

    js_list_scope_lookup,

    js_list_scope_add,

    js_list_scope_remove,

    js_list_scope_clear

};



/************************************************************************/



JSScope *

js_GetMutableScope(JSContext *cx, JSObject *obj)

{

    JSScope *scope, *newscope;



    scope = OBJ_SCOPE(obj);

    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));

    if (scope->object == obj)

	return scope;

    newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj),

			   obj);

    if (!newscope)

	return NULL;

    JS_LOCK_SCOPE(cx, newscope);

    obj->map = js_HoldObjectMap(cx, &newscope->map);

    scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj);

    JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);

    return newscope;

}



JSScope *

js_MutateScope(JSContext *cx, JSObject *obj, jsid id,

	       JSPropertyOp getter, JSPropertyOp setter, uintN attrs,

	       JSScopeProperty **propp)

{

    /* XXX pessimal */

    *propp = NULL;

    return js_GetMutableScope(cx, obj);

}



JSScope *

js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp,

	    JSObject *obj)

{

    JSScope *scope;



    scope = (JSScope *) JS_malloc(cx, sizeof(JSScope));

    if (!scope)

	return NULL;

    js_InitObjectMap(&scope->map, nrefs, ops, clasp);

    scope->object = obj;

    scope->props = NULL;

    scope->proptail = &scope->props;

    scope->ops = &js_list_scope_ops;

    scope->data = NULL;



#ifdef JS_THREADSAFE

    scope->ownercx = cx;

    memset(&scope->lock, 0, sizeof scope->lock);



    /*

     * Set u.link = NULL, not u.count = 0, in case the target architecture's

     * null pointer has a non-zero integer representation.

     */

    scope->u.link = NULL;



#ifdef DEBUG

    scope->file[0] = scope->file[1] = scope->file[2] = scope->file[3] = NULL;

    scope->line[0] = scope->line[1] = scope->line[2] = scope->line[3] = 0;

    JS_ATOMIC_INCREMENT(&cx->runtime->liveScopes);

    JS_ATOMIC_INCREMENT(&cx->runtime->totalScopes);

#endif

#endif



    return scope;

}



#ifdef DEBUG_SCOPE_COUNT

extern void

js_unlog_scope(JSScope *scope);

#endif



void

js_DestroyScope(JSContext *cx, JSScope *scope)

{

#ifdef DEBUG_SCOPE_COUNT

    js_unlog_scope(scope);

#endif

#ifdef JS_THREADSAFE

    /*

     * Scope must be single-threaded at this point, so set scope->ownercx.

     * This also satisfies the JS_IS_SCOPE_LOCKED assertions in the _clear

     * implementations.

     */

    JS_ASSERT(scope->u.count == 0);

    scope->ownercx = cx;

#endif

    scope->ops->clear(cx, scope);

#ifdef JS_THREADSAFE

    js_FinishLock(&scope->lock);

#endif

#ifdef DEBUG

    JS_ATOMIC_DECREMENT(&cx->runtime->liveScopes);

#endif

    JS_free(cx, scope);

}



JSHashNumber

js_HashValue(jsval v)

{

    return js_hash_id((const void *)v);

}



jsval

js_IdToValue(jsid id)

{

    JSAtom *atom;



    if (JSVAL_IS_INT(id))

	return id;

    atom = (JSAtom *)id;

    return ATOM_KEY(atom);

}



JSScopeProperty *

js_NewScopeProperty(JSContext *cx, JSScope *scope, jsid id,

		    JSPropertyOp getter, JSPropertyOp setter, uintN attrs)

{

    uint32 slot;

    JSScopeProperty *sprop;



    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));

    if (attrs & JSPROP_SHARED) {

        slot = SPROP_INVALID_SLOT;

    } else {

        if (!js_AllocSlot(cx, scope->object, &slot))

            return NULL;

    }

    sprop = (JSScopeProperty *) JS_malloc(cx, sizeof(JSScopeProperty));

    if (!sprop) {

        if (slot != SPROP_INVALID_SLOT)

            js_FreeSlot(cx, scope->object, slot);

	return NULL;

    }

    sprop->nrefs = 0;

    sprop->id = js_IdToValue(id);

    sprop->getter = getter;

    sprop->setter = setter;

    sprop->slot = slot;

    sprop->attrs = (uint8) attrs;

    sprop->spare = 0;

    sprop->symbols = NULL;

    sprop->next = NULL;

    sprop->prevp = scope->proptail;

    *scope->proptail = sprop;

    scope->proptail = &sprop->next;

    return sprop;

}



void

js_DestroyScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop)

{

    /*

     * Test whether obj was finalized before prop's last dereference.

     *

     * This can happen when a deleted property is held by a property iterator

     * object (which points to obj, keeping obj alive so long as the property

     * iterator is reachable).  As soon as the GC finds the iterator to be

     * unreachable, it will finalize the iterator, and may also finalize obj,

     * in an unpredictable order.  If obj is finalized first, its map will be

     * null below, and we need only free prop.

     *

     * In the more typical case of a property whose last reference (from a

     * symbol in obj's scope) goes away before obj is finalized, we must be

     * sure to free prop's slot and unlink it from obj's property list.

     */

    if (scope) {

	JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));

	if (scope->object) {

            JS_ASSERT(sprop);

            if (SPROP_HAS_VALID_SLOT(sprop))

                js_FreeSlot(cx, scope->object, sprop->slot);

	    *sprop->prevp = sprop->next;

	    if (sprop->next)

		sprop->next->prevp = sprop->prevp;

	    else

		scope->proptail = sprop->prevp;

	}

    }



    /*

     * Purge any cached weak links to prop (unless we're running the gc, which

     * flushed the whole cache), then free prop.

     */

    if (!cx->runtime->gcRunning)

        js_FlushPropertyCacheByProp(cx, (JSProperty *)sprop);

    JS_free(cx, sprop);

}



JSScopeProperty *

js_HoldScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop)

{

    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));

    if (sprop) {

	JS_ASSERT(sprop->nrefs >= 0);

	sprop->nrefs++;

    }

    return sprop;

}



JSScopeProperty *

js_DropScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop)

{

    JS_ASSERT(JS_IS_SCOPE_LOCKED(scope));

    if (sprop) {

	JS_ASSERT(sprop->nrefs > 0);

	if (--sprop->nrefs == 0) {

	    js_DestroyScopeProperty(cx, scope, sprop);

	    sprop = NULL;

	}

    }

    return sprop;

}

 

**** End of jsscope.c ****

 

**** Start of jsscope.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsscope_h___

#define jsscope_h___

/*

 * JS symbol tables.

 */

#include "jstypes.h"

#include "jshash.h" /* Added by JSIFY */

#include "jsobj.h"

#include "jsprvtd.h"

#include "jspubtd.h"



#ifdef JS_THREADSAFE

# include "jslock.h"

#endif



#ifndef JS_DOUBLE_HASHING

struct JSScopeOps {

    JSSymbol *      (*lookup)(JSContext *cx, JSScope *scope, jsid id,

			      JSHashNumber hash);

    JSSymbol *      (*add)(JSContext *cx, JSScope *scope, jsid id,

			   JSScopeProperty *sprop);

    JSBool          (*remove)(JSContext *cx, JSScope *scope, jsid id);

    void            (*clear)(JSContext *cx, JSScope *scope);

};

#endif



struct JSScope {

    JSObjectMap     map;                /* base class state */

    JSObject        *object;            /* object that owns this scope */

    JSScopeProperty *props;             /* property list in definition order */

    JSScopeProperty **proptail;         /* pointer to pointer to last prop */

#ifdef JS_DOUBLE_HASHING

    uint32          tableLength;

    JSScopeProperty *table;

    uint32          gsTableLength;      /* number of entries in gsTable */

    JSPropertyOp    gsTable[1];         /* actually, gsTableLength ops */

#else

    JSScopeOps      *ops;               /* virtual operations */

    void            *data;              /* private data specific to ops */

#endif

#ifdef JS_THREADSAFE

    JSContext       *ownercx;           /* creating context, NULL if shared */

    JSThinLock      lock;               /* binary semaphore protecting scope */

    union {                             /* union lockful and lock-free state: */

        jsrefcount  count;              /* lock entry count for reentrancy */

        JSScope     *link;              /* next link in rt->scopeSharingTodo */

    } u;

#ifdef DEBUG

    const char      *file[4];           /* file where lock was (re-)taken */

    unsigned int    line[4];            /* line where lock was (re-)taken */

#endif

#endif

};



#define OBJ_SCOPE(obj)              ((JSScope *)(obj)->map)

#define SPROP_GETTER(sprop,obj)     SPROP_GETTER_SCOPE(sprop, OBJ_SCOPE(obj))

#define SPROP_SETTER(sprop,obj)     SPROP_SETTER_SCOPE(sprop, OBJ_SCOPE(obj))



#define SPROP_INVALID_SLOT          0xffffffff

#define SPROP_HAS_VALID_SLOT(sprop) ((sprop)->slot != SPROP_INVALID_SLOT)



#ifdef JS_DOUBLE_HASHING



struct JSScopeProperty {

    jsid            id;

    uint32          slot;               /* index in obj->slots vector */

    uint8           attrs;              /* attributes, see jsapi.h JSPROP_ */

    uint8           getterIndex;        /* getter and setter method indexes */

    uint8           setterIndex;        /* in JSScope.gsTable[] */

    uint8           reserved;

    JSScopeProperty *next;              /* singly-linked list linkage */

};



#define SPROP_GETTER_SCOPE(sprop,scope) ((scope)->gsTable[(sprop)->getterIndex])

#define SPROP_SETTER_SCOPE(sprop,scope) ((scope)->gsTable[(sprop)->setterIndex])



#else  /* !JS_DOUBLE_HASHING */



struct JSSymbol {

    JSHashEntry     entry;              /* base class state */

    JSScope         *scope;             /* pointer to owning scope */

    JSSymbol        *next;              /* next in type-specific list */

};



#define sym_id(sym)             ((jsid)(sym)->entry.key)

#define sym_atom(sym)           ((JSAtom *)(sym)->entry.key)

#define sym_property(sym)       ((JSScopeProperty *)(sym)->entry.value)



struct JSScopeProperty {

    jsrefcount      nrefs;              /* number of referencing symbols */

    jsval           id;                 /* id passed to getter and setter */

    JSPropertyOp    getter;             /* getter and setter methods */

    JSPropertyOp    setter;

    uint32          slot;               /* index in obj->slots vector */

    uint8           attrs;              /* attributes, see jsapi.h JSPROP_ */

    uint8           spare;              /* reserved for future use */

    JSSymbol        *symbols;           /* list of aliasing symbols */

    JSScopeProperty *next;              /* doubly-linked list linkage */

    JSScopeProperty **prevp;

};



#define SPROP_GETTER_SCOPE(sprop,scope) ((sprop)->getter)

#define SPROP_SETTER_SCOPE(sprop,scope) ((sprop)->setter)



#endif /* !JS_DOUBLE_HASHING */



/*

 * These macros are designed to decouple getter and setter from sprop, by

 * passing obj2 (in whose scope sprop lives, and in whose scope getter and

 * setter might be stored apart from sprop -- say in scope->gsTable[i] for

 * a compressed getter or setter index i that is stored in sprop).

 */

#define SPROP_GET(cx,sprop,obj,obj2,vp)                                       \

    (((sprop)->attrs & JSPROP_GETTER)                                         \

     ? js_InternalCall(cx, obj, OBJECT_TO_JSVAL(SPROP_GETTER(sprop,obj2)),    \

                       0, 0, vp)                                              \

     : (SPROP_GETTER(sprop,obj2) != JS_PropertyStub)                          \

     ? SPROP_GETTER(sprop,obj2)(cx, OBJ_THIS_OBJECT(cx,obj), sprop->id, vp)   \

     : JS_TRUE)



#define SPROP_SET(cx,sprop,obj,obj2,vp)                                       \

    (((sprop)->attrs & JSPROP_SETTER)                                         \

     ? js_InternalCall(cx, obj, OBJECT_TO_JSVAL(SPROP_SETTER(sprop,obj2)),    \

                       1, vp, vp)                                             \

     : ((sprop)->attrs & JSPROP_GETTER)                                       \

     ? (JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,                    \

                             JSMSG_GETTER_ONLY, NULL), JS_FALSE)              \

     : (SPROP_SETTER(sprop,obj2) != JS_PropertyStub)                          \

     ? SPROP_SETTER(sprop,obj2)(cx, OBJ_THIS_OBJECT(cx,obj), sprop->id, vp)   \

     : JS_TRUE)



extern JSScope *

js_GetMutableScope(JSContext *cx, JSObject *obj);



extern JSScope *

js_MutateScope(JSContext *cx, JSObject *obj, jsid id,

	       JSPropertyOp getter, JSPropertyOp setter, uintN attrs,

	       JSScopeProperty **propp);



extern JSScope *

js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp,

	    JSObject *obj);



extern void

js_DestroyScope(JSContext *cx, JSScope *scope);



extern JSHashNumber

js_HashValue(jsval v);



extern jsval

js_IdToValue(jsid id);



extern JSScopeProperty *

js_NewScopeProperty(JSContext *cx, JSScope *scope, jsid id,

		    JSPropertyOp getter, JSPropertyOp setter, uintN attrs);



extern void

js_DestroyScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop);



extern JSScopeProperty *

js_HoldScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop);



extern JSScopeProperty *

js_DropScopeProperty(JSContext *cx, JSScope *scope, JSScopeProperty *sprop);



#endif /* jsscope_h___ */

 

**** End of jsscope.h ****

 

**** Start of jsscript.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS script operations.

 */

#include "jsstddef.h"

#include <string.h>

#include "jstypes.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jsprf.h"

#include "jsapi.h"

#include "jsatom.h"

#include "jscntxt.h"

#include "jsconfig.h"

#include "jsdbgapi.h"

#include "jsemit.h"

#include "jsfun.h"

#include "jsinterp.h"

#include "jsnum.h"

#include "jsopcode.h"

#include "jsscript.h"

#if JS_HAS_XDR

#include "jsxdrapi.h"

#endif



#if JS_HAS_SCRIPT_OBJECT



#if JS_HAS_TOSOURCE

static JSBool

script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		jsval *rval)

{

    JSScript *script;

    size_t i, j, k, n;

    char buf[16];

    jschar *s, *t;

    uint32 indent;

    JSString *str;



    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))

	return JS_FALSE;

    script = (JSScript *) JS_GetPrivate(cx, obj);



    /* Let n count the source string length, j the "front porch" length. */

    j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name);

    n = j + 2;

    if (!script) {

	/* Let k count the constructor argument string length. */

	k = 0;

        s = NULL;               /* quell GCC overwarning */

    } else {

	indent = 0;

	if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))

	    return JS_FALSE;

	str = JS_DecompileScript(cx, script, "Script.prototype.toSource",

				 (uintN)indent);

	if (!str)

	    return JS_FALSE;

	str = js_QuoteString(cx, str, '\'');

	if (!str)

	    return JS_FALSE;

	s = str->chars;

	k = str->length;

	n += k;

    }



    /* Allocate the source string and copy into it. */

    t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));

    if (!t)

	return JS_FALSE;

    for (i = 0; i < j; i++)

	t[i] = buf[i];

    for (j = 0; j < k; i++, j++)

	t[i] = s[j];

    t[i++] = ')';

    t[i++] = ')';

    t[i] = 0;



    /* Create and return a JS string for t. */

    str = JS_NewUCString(cx, t, n);

    if (!str) {

	JS_free(cx, t);

	return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}

#endif /* JS_HAS_TOSOURCE */



static JSBool

script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		jsval *rval)

{

    JSScript *script;

    uint32 indent;

    JSString *str;



    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))

	return JS_FALSE;

    script = (JSScript *) JS_GetPrivate(cx, obj);

    if (!script) {

	*rval = STRING_TO_JSVAL(cx->runtime->emptyString);

	return JS_TRUE;

    }



    indent = 0;

    if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))

	return JS_FALSE;

    str = JS_DecompileScript(cx, script, "Script.prototype.toString",

			     (uintN)indent);

    if (!str)

	return JS_FALSE;

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	       jsval *rval)

{

    JSScript *oldscript, *script;

    JSString *str;

    JSStackFrame *fp, *caller;

    JSObject *scopeobj;

    const char *file;

    uintN line;

    JSPrincipals *principals;



    /* If no args, leave private undefined and return early. */

    if (argc == 0)

	goto out;



    /* Otherwise, the first arg is the script source to compile. */

    str = js_ValueToString(cx, argv[0]);

    if (!str)

	return JS_FALSE;



    /* Compile using the caller's scope chain, which js_Invoke passes to fp. */

    fp = cx->fp;

    caller = fp->down;

    JS_ASSERT(fp->scopeChain == caller->scopeChain);



    scopeobj = NULL;

    if (argc >= 2) {

	if (!js_ValueToObject(cx, argv[1], &scopeobj))

	    return JS_FALSE;

	argv[1] = OBJECT_TO_JSVAL(scopeobj);

    }

    if (!scopeobj)

	scopeobj = caller->scopeChain;



    if (caller->script) {

	file = caller->script->filename;

	line = js_PCToLineNumber(caller->script, caller->pc);

	principals = caller->script->principals;

    } else {

	file = NULL;

	line = 0;

	principals = NULL;

    }



    /* Compile the new script using the caller's scope chain, a la eval(). */

    script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,

					     str->chars, str->length,

					     file, line);

    if (!script)

	return JS_FALSE;



    /* Swap script for obj's old script, if any. */

    oldscript = (JSScript *) JS_GetPrivate(cx, obj);

    if (!JS_SetPrivate(cx, obj, script)) {

	js_DestroyScript(cx, script);

	return JS_FALSE;

    }

    if (oldscript)

	js_DestroyScript(cx, oldscript);



    script->object = obj;

out:

    /* Return the object. */

    *rval = OBJECT_TO_JSVAL(obj);

    return JS_TRUE;

}



static JSBool

script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSScript *script;

    JSStackFrame *fp, *caller;

    JSObject *scopeobj;



    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))

	return JS_FALSE;

    script = (JSScript *) JS_GetPrivate(cx, obj);

    if (!script)

	return JS_TRUE;



    scopeobj = NULL;

    if (argc) {

	if (!js_ValueToObject(cx, argv[0], &scopeobj))

	    return JS_FALSE;

	argv[0] = OBJECT_TO_JSVAL(scopeobj);

    }



    /* Emulate eval() by using caller's this, scope chain, and sharp array. */

    fp = cx->fp;

    caller = fp->down;

    if (!scopeobj)

      scopeobj = caller->scopeChain;

    fp->thisp = caller->thisp;

    JS_ASSERT(fp->scopeChain == caller->scopeChain);

    fp->sharpArray = caller->sharpArray;

    return js_Execute(cx, scopeobj, script, fp, 0, rval);

}



#if JS_HAS_XDR

static JSBool

XDRAtom1(JSXDRState *xdr, JSAtomListElement *ale)

{

    jsval value;

    jsatomid index;



    if (xdr->mode == JSXDR_ENCODE)

	value = ATOM_KEY(ALE_ATOM(ale));



    index = ALE_INDEX(ale);

    if (!JS_XDRUint32(xdr, &index))

	return JS_FALSE;

    ALE_SET_INDEX(ale, index);



    if (!JS_XDRValue(xdr, &value))

	return JS_FALSE;



    if (xdr->mode == JSXDR_DECODE) {

	if (!ALE_SET_ATOM(ale, js_AtomizeValue(xdr->cx, value, 0)))

	    return JS_FALSE;

    }

    return JS_TRUE;

}



static JSBool

XDRAtomMap(JSXDRState *xdr, JSAtomMap *map)

{

    uint32 length;

    uintN i;

    JSBool ok;



    if (xdr->mode == JSXDR_ENCODE)

	length = map->length;



    if (!JS_XDRUint32(xdr, &length))

	return JS_FALSE;



    if (xdr->mode == JSXDR_DECODE) {

	JSContext *cx;

	void *mark;

	JSAtomList al;

	JSAtomListElement *ale;



	cx = xdr->cx;

	mark = JS_ARENA_MARK(&cx->tempPool);

	ATOM_LIST_INIT(&al);

	for (i = 0; i < length; i++) {

	    JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool);

	    if (!ale ||

		!XDRAtom1(xdr, ale)) {

		if (!ale)

		    JS_ReportOutOfMemory(cx);

		JS_ARENA_RELEASE(&cx->tempPool, mark);

		return JS_FALSE;

	    }

	    ALE_SET_NEXT(ale, al.list);

	    al.count++;

	    al.list = ale;

	}

        ok = js_InitAtomMap(cx, map, &al);

        JS_ARENA_RELEASE(&cx->tempPool, mark);

        return ok;

    }



    if (xdr->mode == JSXDR_ENCODE) {

	JSAtomListElement ale;



	for (i = 0; i < map->length; i++) {

	    ALE_SET_ATOM(&ale, map->vector[i]);

	    ALE_SET_INDEX(&ale, i);

	    if (!XDRAtom1(xdr, &ale))

		return JS_FALSE;

	}

    }

    return JS_TRUE;

}



JSBool

js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)

{

    JSScript *script;

    uint32 length, lineno, depth, magic, notelen, numtrys;

    uint32 prologLength, version;



    script = *scriptp;

    numtrys = 0;



    /*

     * Encode prologLength and version after script->length (_2), but decode

     * both new (_2) and old, prolog&version-free (_1) scripts.

     */

    if (xdr->mode == JSXDR_ENCODE)

        magic = JSXDR_MAGIC_SCRIPT_CURRENT;

    if (!JS_XDRUint32(xdr, &magic))

        return JS_FALSE;

    if (magic != JSXDR_MAGIC_SCRIPT_2 && magic != JSXDR_MAGIC_SCRIPT_1) {

        *hasMagic = JS_FALSE;

        return JS_TRUE;

    }

    *hasMagic = JS_TRUE;



    if (xdr->mode == JSXDR_ENCODE) {

        jssrcnote *end = script->notes;

        length = script->length;

        prologLength = script->main - script->code;

        version = (int32) script->version;

        lineno = (uint32)script->lineno;

        depth = (uint32)script->depth;



        /* Count the trynotes. */

        if (script->trynotes) {

            for (; script->trynotes[numtrys].catchStart; numtrys++)

                continue;

            numtrys++;          /* room for the end marker */

        }



        /* Count the src notes. */

        while (!SN_IS_TERMINATOR(end))

            end = SN_NEXT(end);

        notelen = end - script->notes;

    }



    if (!JS_XDRUint32(xdr, &length))

        return JS_FALSE;

    if (magic == JSXDR_MAGIC_SCRIPT_2) {

        if (!JS_XDRUint32(xdr, &prologLength))

            return JS_FALSE;

        if (!JS_XDRUint32(xdr, &version))

            return JS_FALSE;

    }



    if (xdr->mode == JSXDR_DECODE) {

        script = js_NewScript(xdr->cx, length);

        if (!script)

            return JS_FALSE;

        if (magic == JSXDR_MAGIC_SCRIPT_2) {

            script->main += prologLength;

            script->version = (JSVersion) version;

        }

        *scriptp = script;

    }



    if (!JS_XDRBytes(xdr, (char **)&script->code, length) ||

        !XDRAtomMap(xdr, &script->atomMap) ||

        !JS_XDRUint32(xdr, &notelen) ||

        /* malloc on decode only */

        (!(xdr->mode == JSXDR_ENCODE ||

           (script->notes = (jssrcnote *)

            JS_malloc(xdr->cx, notelen)) != NULL)) ||

        !JS_XDRBytes(xdr, (char **)&script->notes, notelen) ||

        !JS_XDRCStringOrNull(xdr, (char **)&script->filename) ||

        !JS_XDRUint32(xdr, &lineno) ||

        !JS_XDRUint32(xdr, &depth) ||

        !JS_XDRUint32(xdr, &numtrys)) {

        goto error;

    }



    if (xdr->mode == JSXDR_DECODE) {

        script->lineno = (uintN)lineno;

        script->depth = (uintN)depth;

        if (numtrys) {

            script->trynotes = (JSTryNote *)

                JS_malloc(xdr->cx, sizeof(JSTryNote) * (numtrys + 1));

            if (!script->trynotes)

                goto error;

        } else {

            script->trynotes = NULL;

        }

    }

    for (; numtrys; numtrys--) {

        JSTryNote *tn = &script->trynotes[numtrys - 1];

        uint32 start = (ptrdiff_t) tn->start,

               catchLength = (ptrdiff_t) tn->length,

               catchStart = (ptrdiff_t) tn->catchStart;

        if (!JS_XDRUint32(xdr, &start) ||

            !JS_XDRUint32(xdr, &catchLength) ||

            !JS_XDRUint32(xdr, &catchStart)) {

            goto error;

        }

        tn->start = (ptrdiff_t) start;

        tn->length = (ptrdiff_t) catchLength;

        tn->catchStart = (ptrdiff_t) catchStart;

    }

    return JS_TRUE;



  error:

    if (xdr->mode == JSXDR_DECODE) {

        js_DestroyScript(xdr->cx, script);

        *scriptp = NULL;

    }

    return JS_FALSE;

}



static JSBool

script_freeze(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	      jsval *rval)

{

    JSXDRState *xdr;

    JSScript *script;

    JSBool ok, hasMagic;

    uint32 len;

    void *buf;

    JSString *str;



    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))

	return JS_FALSE;

    script = (JSScript *) JS_GetPrivate(cx, obj);

    if (!script)

	return JS_TRUE;



    /* create new XDR */

    xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);

    if (!xdr)

	return JS_FALSE;



    /* write  */

    ok = js_XDRScript(xdr, &script, &hasMagic);

    if (!ok)

	goto out;

    if (!hasMagic) {

	*rval = JSVAL_VOID;

	goto out;

    }



    buf = JS_XDRMemGetData(xdr, &len);

    if (!buf) {

	ok = JS_FALSE;

	goto out;

    }



    JS_ASSERT((jsword)buf % sizeof(jschar) == 0);

    len /= sizeof(jschar);

    str = JS_NewUCStringCopyN(cx, (jschar *)buf, len);

    if (!str) {

	ok = JS_FALSE;

	goto out;

    }



#if IS_BIG_ENDIAN

  {

    jschar *chars;

    uint32 i;



    /* Swap bytes in Unichars to keep frozen strings machine-independent. */

    chars = JS_GetStringChars(str);

    for (i = 0; i < len; i++)

	chars[i] = JSXDR_SWAB16(chars[i]);

  }

#endif

    *rval = STRING_TO_JSVAL(str);



out:

    JS_XDRDestroy(xdr);

    return ok;

}



static JSBool

script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

	    jsval *rval)

{

    JSXDRState *xdr;

    JSString *str;

    void *buf;

    uint32 len;

    JSScript *script, *oldscript;

    JSBool ok, hasMagic;



    if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))

	return JS_FALSE;



    if (argc == 0)

	return JS_TRUE;

    str = js_ValueToString(cx, argv[0]);

    if (!str)

	return JS_FALSE;



    /* create new XDR */

    xdr = JS_XDRNewMem(cx, JSXDR_DECODE);

    if (!xdr)

	return JS_FALSE;



    buf = JS_GetStringChars(str);

    len = JS_GetStringLength(str);

#if IS_BIG_ENDIAN

  {

    jschar *from, *to;

    uint32 i;



    /* Swap bytes in Unichars to keep frozen strings machine-independent. */

    from = (jschar *)buf;

    to = (jschar *) JS_malloc(cx, len * sizeof(jschar));

    if (!to) {

        JS_XDRDestroy(xdr);

        return JS_FALSE;

    }

    for (i = 0; i < len; i++)

	to[i] = JSXDR_SWAB16(from[i]);

    buf = (char *)to;

  }

#endif

    len *= sizeof(jschar);

    JS_XDRMemSetData(xdr, buf, len);



    /* XXXbe should magic mismatch be error, or false return value? */

    ok = js_XDRScript(xdr, &script, &hasMagic);

    if (!ok)

	goto out;

    if (!hasMagic) {

	*rval = JSVAL_FALSE;

	goto out;

    }



    /* Swap script for obj's old script, if any. */

    oldscript = (JSScript *) JS_GetPrivate(cx, obj);

    ok = JS_SetPrivate(cx, obj, script);

    if (!ok) {

	JS_free(cx, script);

	goto out;

    }

    if (oldscript)

	js_DestroyScript(cx, oldscript);



    script->object = obj;



out:

    /*

     * We reset the buffer to be NULL so that it doesn't free the chars

     * memory owned by str (argv[0]).

     */

    JS_XDRMemSetData(xdr, NULL, 0);

    JS_XDRDestroy(xdr);

#if IS_BIG_ENDIAN

    JS_free(cx, buf);

#endif

    *rval = JSVAL_TRUE;

    return ok;

}



#endif /* JS_HAS_XDR */



static char js_thaw_str[] = "thaw";



static JSFunctionSpec script_methods[] = {

#if JS_HAS_TOSOURCE

    {js_toSource_str,   script_toSource,        0,0,0},

#endif

    {js_toString_str,   script_toString,        0,0,0},

    {"compile",         script_compile,         2,0,0},

    {"exec",            script_exec,            1,0,0},

#if JS_HAS_XDR

    {"freeze",		script_freeze,		0,0,0},

    {js_thaw_str,	script_thaw,		1,0,0},

#endif /* JS_HAS_XDR */

    {0,0,0,0,0}

};



#endif /* JS_HAS_SCRIPT_OBJECT */



static void

script_finalize(JSContext *cx, JSObject *obj)

{

    JSScript *script;



    script = (JSScript *) JS_GetPrivate(cx, obj);

    if (script)

	js_DestroyScript(cx, script);

}



static JSBool

script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

#if JS_HAS_SCRIPT_OBJECT

    return script_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval);

#else

    return JS_FALSE;

#endif

}



static uint32

script_mark(JSContext *cx, JSObject *obj, void *arg)

{

    JSScript *script;



    script = (JSScript *) JS_GetPrivate(cx, obj);

    if (script)

        js_MarkScript(cx, script, arg);

    return 0;

}



JS_FRIEND_DATA(JSClass) js_ScriptClass = {

    js_Script_str,

    JSCLASS_HAS_PRIVATE,

    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,

    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   script_finalize,

    NULL,             NULL,             script_call,      NULL,/*XXXbe xdr*/

    NULL,             NULL,             script_mark,      0

};



#if JS_HAS_SCRIPT_OBJECT



static JSBool

Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    /* If not constructing, replace obj with a new Script object. */

    if (!cx->fp->constructing) {

	obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);

	if (!obj)

	    return JS_FALSE;

    }

    return script_compile(cx, obj, argc, argv, rval);

}



#if JS_HAS_XDR



static JSBool

script_static_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

		   jsval *rval)

{

    obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);

    if (!obj)

	return JS_FALSE;

    if (!script_thaw(cx, obj, argc, argv, rval))

	return JS_FALSE;

    *rval = OBJECT_TO_JSVAL(obj);

    return JS_TRUE;

}



static JSFunctionSpec script_static_methods[] = {

    {js_thaw_str,       script_static_thaw,     1,0,0},

    {0,0,0,0,0}

};



#else  /* !JS_HAS_XDR */



#define script_static_methods   NULL



#endif /* !JS_HAS_XDR */



JSObject *

js_InitScriptClass(JSContext *cx, JSObject *obj)

{

    return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1,

			NULL, script_methods, NULL, script_static_methods);

}



#endif /* JS_HAS_SCRIPT_OBJECT */



JSScript *

js_NewScript(JSContext *cx, uint32 length)

{

    JSScript *script;



    script = (JSScript *)

        JS_malloc(cx, sizeof(JSScript) + length * sizeof(jsbytecode));

    if (!script)

	return NULL;

    memset(script, 0, sizeof(JSScript));

    script->code = script->main = (jsbytecode *)(script + 1);

    script->length = length;

    script->version = cx->version;

    return script;

}



JSScript *

js_NewScriptFromParams(JSContext *cx, jsbytecode *code, uint32 length,

		       jsbytecode *prolog, uint32 prologLength,

		       const char *filename, uintN lineno, uintN depth,

		       jssrcnote *notes, JSTryNote *trynotes,

		       JSPrincipals *principals)

{

    JSScript *script;



    script = js_NewScript(cx, prologLength + length);

    if (!script)

	return NULL;

    script->main += prologLength;

    memcpy(script->code, prolog, prologLength * sizeof(jsbytecode));

    memcpy(script->main, code, length * sizeof(jsbytecode));

    if (filename) {

	script->filename = JS_strdup(cx, filename);

	if (!script->filename) {

	    js_DestroyScript(cx, script);

	    return NULL;

	}

    }

    script->lineno = lineno;

    script->depth = depth;

    script->notes = notes;

    script->trynotes = trynotes;

    if (principals)

	JSPRINCIPALS_HOLD(cx, principals);

    script->principals = principals;

    return script;

}



JS_FRIEND_API(JSScript *)

js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun)

{

    JSTryNote *trynotes;

    jssrcnote *notes;

    JSScript *script;

    JSRuntime *rt;

    JSNewScriptHook hook;



    if (!js_FinishTakingTryNotes(cx, cg, &trynotes))

	return NULL;

    notes = js_FinishTakingSrcNotes(cx, cg);

    script = js_NewScriptFromParams(cx, CG_BASE(cg), CG_OFFSET(cg),

                                    CG_PROLOG_BASE(cg), CG_PROLOG_OFFSET(cg),

				    cg->filename, cg->firstLine,

				    cg->maxStackDepth, notes, trynotes,

				    cg->principals);

    if (!script)

	return NULL;

    if (!notes || !js_InitAtomMap(cx, &script->atomMap, &cg->atomList)) {

	js_DestroyScript(cx, script);

	return NULL;

    }



    /* Tell the debugger about this compiled script. */

    rt = cx->runtime;

    hook = rt->newScriptHook;

    if (hook) {

	(*hook)(cx, cg->filename, cg->firstLine, script, fun,

		rt->newScriptHookData);

    }

    return script;

}



void

js_DestroyScript(JSContext *cx, JSScript *script)

{

    JSRuntime *rt;

    JSDestroyScriptHook hook;



    rt = cx->runtime;

    hook = rt->destroyScriptHook;

    if (hook)

	(*hook)(cx, script, rt->destroyScriptHookData);



    JS_ClearScriptTraps(cx, script);

    js_FreeAtomMap(cx, &script->atomMap);

    JS_free(cx, (void *)script->filename);

    JS_free(cx, script->notes);

    JS_free(cx, script->trynotes);

    if (script->principals)

	JSPRINCIPALS_DROP(cx, script->principals);

    JS_free(cx, script);

}



void

js_MarkScript(JSContext *cx, JSScript *script, void *arg)

{

    JSAtomMap *map;

    uintN i, length;

    JSAtom **vector;



    map = &script->atomMap;

    length = map->length;

    vector = map->vector;

    for (i = 0; i < length; i++)

        GC_MARK_ATOM(cx, vector[i], arg);

}



jssrcnote *

js_GetSrcNote(JSScript *script, jsbytecode *pc)

{

    jssrcnote *sn;

    ptrdiff_t offset, target;



    sn = script->notes;

    if (!sn)

	return NULL;

    target = PTRDIFF(pc, script->main, jsbytecode);

    if ((uintN)target >= script->length)

	return NULL;

    for (offset = 0; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {

	offset += SN_DELTA(sn);

	if (offset == target && SN_IS_GETTABLE(sn))

	    return sn;

    }

    return NULL;

}



uintN

js_PCToLineNumber(JSScript *script, jsbytecode *pc)

{

    jssrcnote *sn;

    ptrdiff_t offset, target;

    uintN lineno;

    JSSrcNoteType type;



    sn = script->notes;

    if (!sn)

	return 0;

    target = PTRDIFF(pc, script->main, jsbytecode);

    lineno = script->lineno;

    for (offset = 0; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {

	offset += SN_DELTA(sn);

	type = (JSSrcNoteType) SN_TYPE(sn);

	if (type == SRC_SETLINE) {

	    if (offset <= target)

		lineno = (uintN) js_GetSrcNoteOffset(sn, 0);

	} else if (type == SRC_NEWLINE) {

	    if (offset <= target)

		lineno++;

	}

	if (offset > target)

	    break;

    }

    return lineno;

}



jsbytecode *

js_LineNumberToPC(JSScript *script, uintN target)

{

    jssrcnote *sn;

    uintN lineno;

    ptrdiff_t offset;

    JSSrcNoteType type;



    sn = script->notes;

    if (!sn)

	return NULL;

    lineno = script->lineno;

    for (offset = 0; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {

	if (lineno >= target)

	    break;

	offset += SN_DELTA(sn);

	type = (JSSrcNoteType) SN_TYPE(sn);

	if (type == SRC_SETLINE) {

	    lineno = (uintN) js_GetSrcNoteOffset(sn, 0);

	} else if (type == SRC_NEWLINE) {

	    lineno++;

	}

    }

    return script->main + offset;

}



uintN

js_GetScriptLineExtent(JSScript *script)

{

    jssrcnote *sn;

    uintN lineno;

    JSSrcNoteType type;



    sn = script->notes;

    if (!sn)

	return 0;

    lineno = script->lineno;

    for (; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {

	type = (JSSrcNoteType) SN_TYPE(sn);

	if (type == SRC_SETLINE) {

	    // DREAMWEAVER snewman 6/15/01: changed this to not update lineno

	    // if js_GetSrcNoteOffset returns a smaller value -- otherwise we

	    // were having problems debugging the JavaScript code that the

	    // template engine generates.  For some reason, the last srcnote

	    // in that function had a line number of zero.

	    uintN temp = (uintN) js_GetSrcNoteOffset(sn, 0);

	    if (lineno < temp)

	       lineno = temp;

	} else if (type == SRC_NEWLINE) {

	    lineno++;

	}

    }

    return 1 + lineno - script->lineno;

}

 

**** End of jsscript.c ****

 

**** Start of jsscript.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsscript_h___

#define jsscript_h___

/*

 * JS script descriptor.

 */

#include "jsatom.h"

#include "jsprvtd.h"



JS_BEGIN_EXTERN_C



/*

 * Exception handling runtime information.

 *

 * All fields except length are code offsets, relative to the beginning of

 * the script.  If script->trynotes is not null, it points to a vector of

 * these structs terminated by one with catchStart == 0.

 */

struct JSTryNote {

    ptrdiff_t    start;         /* start of try statement */

    ptrdiff_t    length;        /* count of try statement bytecodes */

    ptrdiff_t    catchStart;    /* start of catch block (0 if end) */

};



struct JSScript {

    jsbytecode   *code;         /* bytecodes and their immediate operands */

    uint32       length;        /* length of code vector */

    jsbytecode   *main;         /* main entry point, after predef'ing prolog */

    JSVersion    version;       /* JS version under which script was compiled */

    JSAtomMap    atomMap;       /* maps immediate index to literal struct */

    const char   *filename;     /* source filename or null */

    uintN        lineno;        /* base line number of script */

    uintN        depth;         /* maximum stack depth in slots */

    jssrcnote    *notes;        /* line number and other decompiling data */

    JSTryNote    *trynotes;     /* exception table for this script */

    JSPrincipals *principals;   /* principals for this script */

    JSObject     *object;       /* optional Script-class object wrapper */

};



#define JSSCRIPT_FIND_CATCH_START(script, pc, catchpc)                        \

    JS_BEGIN_MACRO                                                            \

        JSTryNote *_tn = (script)->trynotes;                                  \

        jsbytecode *_catchpc = NULL;                                          \

        if (_tn) {                                                            \

            ptrdiff_t _offset = PTRDIFF(pc, (script)->main, jsbytecode);      \

            while (JS_UPTRDIFF(_offset, _tn->start) >= (jsuword)_tn->length)  \

                _tn++;                                                        \

            if (_tn->catchStart)                                              \

                _catchpc = (script)->main + _tn->catchStart;                  \

        }                                                                     \

        catchpc = _catchpc;                                                   \

    JS_END_MACRO



extern JS_FRIEND_DATA(JSClass) js_ScriptClass;



extern JSObject *

js_InitScriptClass(JSContext *cx, JSObject *obj);



extern JSScript *

js_NewScript(JSContext *cx, uint32 length);



extern JSScript *

js_NewScriptFromParams(JSContext *cx, jsbytecode *code, uint32 length,

		       jsbytecode *prolog, uint32 prologLength,

		       const char *filename, uintN lineno, uintN depth,

		       jssrcnote *notes, JSTryNote *trynotes,

		       JSPrincipals *principals);



extern JS_FRIEND_API(JSScript *)

js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun);



extern void

js_DestroyScript(JSContext *cx, JSScript *script);



extern void

js_MarkScript(JSContext *cx, JSScript *script, void *arg);



extern jssrcnote *

js_GetSrcNote(JSScript *script, jsbytecode *pc);



extern uintN

js_PCToLineNumber(JSScript *script, jsbytecode *pc);



extern jsbytecode *

js_LineNumberToPC(JSScript *script, uintN lineno);



extern uintN

js_GetScriptLineExtent(JSScript *script);



extern JSBool

js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic);



JS_END_EXTERN_C



#endif /* jsscript_h___ */

 

**** End of jsscript.h ****

 

**** Start of jsshell.msg ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

	Error messages for JSShell. See js.msg for format.

*/



MSG_DEF(JSSMSG_NOT_AN_ERROR,             0, 0, JSEXN_NONE, "<Error #0 is reserved>")

MSG_DEF(JSSMSG_CANT_OPEN,                1, 2, JSEXN_NONE, "can't open {0}: {1}") 

MSG_DEF(JSSMSG_TRAP_USAGE,               2, 0, JSEXN_NONE, "usage: trap [fun] [pc] expr") 

MSG_DEF(JSSMSG_LINE2PC_USAGE,            3, 0, JSEXN_NONE, "usage: line2pc [fun] line") 

MSG_DEF(JSSMSG_FILE_SCRIPTS_ONLY,        4, 0, JSEXN_NONE, "only works on JS scripts read from files") 

MSG_DEF(JSSMSG_UNEXPECTED_EOF,           5, 1, JSEXN_NONE, "unexpected EOF in {0}") 

MSG_DEF(JSSMSG_DOEXP_USAGE,              6, 0, JSEXN_NONE, "usage: doexp obj id") 

 

**** End of jsshell.msg ****

 

**** Start of jsstddef.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * stddef inclusion here to first declare ptrdif as a signed long instead of a

 * signed int.

 */



#ifdef _WINDOWS

# ifndef XP_WIN

# define XP_WIN

# endif

#if defined(_WIN32) || defined(WIN32)

# ifndef XP_WIN32

# define XP_WIN32

# endif

#else

# ifndef XP_WIN16

# define XP_WIN16

# endif

#endif

#endif



#ifdef XP_WIN16

#ifndef _PTRDIFF_T_DEFINED

typedef long ptrdiff_t;



/*

 * The Win16 compiler treats pointer differences as 16-bit signed values.

 * This macro allows us to treat them as 17-bit signed values, stored in

 * a 32-bit type.

 */

#define PTRDIFF(p1, p2, type)                                 \

    ((((unsigned long)(p1)) - ((unsigned long)(p2))) / sizeof(type))



#define _PTRDIFF_T_DEFINED

#endif /*_PTRDIFF_T_DEFINED*/

#else /*WIN16*/



#define PTRDIFF(p1, p2, type)                                 \

	((p1) - (p2))



#endif



#include <stddef.h>





 

**** End of jsstddef.h ****

 

**** Start of jsstr.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s):

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * JS string type implementation.

 *

 * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these

 * native methods store strings (possibly newborn) converted from their 'this'

 * parameter and arguments on the stack: 'this' conversions at argv[-1], arg

 * conversions at their index (argv[0], argv[1]).  This is a legitimate method

 * of rooting things that might lose their newborn root due to subsequent GC

 * allocations in the same native method.

 */

#include "jsstddef.h"

#include <stdlib.h>

#include <string.h>

#include "jstypes.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jshash.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 "jsgc.h"

#include "jslock.h"

#include "jsnum.h"

#include "jsobj.h"

#include "jsopcode.h"

#include "jsregexp.h"

#include "jsstr.h"



#if JS_HAS_REPLACE_LAMBDA

#include "jsinterp.h"

#endif



/*

* Forward declarations for URI encode/decode and helper routines

*/

static JSBool

str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

              jsval *rval);



static JSBool

str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                        jsval *rval);



static JSBool

str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

              jsval *rval);



static JSBool

str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                        jsval *rval);



static int

OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char);



static uint32

Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length);



/*

 * Contributions from the String class to the set of methods defined for the

 * global object.  escape and unescape used to be defined in the Mocha library,

 * but as ECMA decided to spec them, they've been moved to the core engine

 * and made ECMA-compliant.  (Incomplete escapes are interpreted as literal

 * characters by unescape.)

 */



/*

 * Stuff to emulate the old libmocha escape, which took a second argument

 * giving the type of escape to perform.  Retained for compatibility, and

 * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes.

 */



#define URL_XALPHAS     ((uint8) 1)

#define URL_XPALPHAS    ((uint8) 2)

#define URL_PATH        ((uint8) 4)



static const uint8 urlCharType[256] =

/*      Bit 0           xalpha          -- the alphas

 *      Bit 1           xpalpha         -- as xalpha but

 *                             converts spaces to plus and plus to %20

 *      Bit 2 ...       path            -- as xalphas but doesn't escape '/'

 */

    /*   0 1 2 3 4 5 6 7 8 9 A B C D E F */

    {    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,       /* 0x */

         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,       /* 1x */

         0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4,       /* 2x   !"#$%&'()*+,-./  */

         7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,       /* 3x  0123456789:;<=>?  */

         7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,       /* 4x  @ABCDEFGHIJKLMNO  */

         7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7,       /* 5X  PQRSTUVWXYZ[\]^_  */

         0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,       /* 6x  `abcdefghijklmno  */

         7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,       /* 7X  pqrstuvwxyz{\}~  DEL */

         0, };



/* This matches the ECMA escape set when mask is 7 (default.) */



#define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask))



/* See ECMA-262 15.1.2.4. */

JSBool

str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str;

    size_t i, ni, newlength;

    const jschar *chars;

    jschar *newchars;

    jschar ch;

    jsint mask;

    jsdouble d;

    const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',

                           '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };



    mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;

    if (argc > 1) {

        if (!js_ValueToNumber(cx, argv[1], &d))

            return JS_FALSE;

        if (!JSDOUBLE_IS_FINITE(d) ||

            (mask = (jsint)d) != d ||

            mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))

        {

            char numBuf[12];

            JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask);

            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                 JSMSG_BAD_STRING_MASK, numBuf);

            return JS_FALSE;

        }

    }



    str = js_ValueToString(cx, argv[0]);

    if (!str)

        return JS_FALSE;

    argv[0] = STRING_TO_JSVAL(str);



    chars = str->chars;

    newlength = str->length;

    /* Take a first pass and see how big the result string will need to be. */

    for (i = 0; i < str->length; i++) {

        if ((ch = chars[i]) < 128 && IS_OK(ch, mask))

            continue;

        if (ch < 256) {

            if (mask == URL_XPALPHAS && ch == ' ')

                continue;   /* The character will be encoded as '+' */

            newlength += 2; /* The character will be encoded as %XX */

        } else {

            newlength += 5; /* The character will be encoded as %uXXXX */

        }

    }



    newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar));

    for (i = 0, ni = 0; i < str->length; i++) {

        if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {

            newchars[ni++] = ch;

        } else if (ch < 256) {

            if (mask == URL_XPALPHAS && ch == ' ') {

                newchars[ni++] = '+'; /* convert spaces to pluses */

            } else {

                newchars[ni++] = '%';

                newchars[ni++] = digits[ch >> 4];

                newchars[ni++] = digits[ch & 0xF];

            }

        } else {

            newchars[ni++] = '%';

            newchars[ni++] = 'u';

            newchars[ni++] = digits[ch >> 12];

            newchars[ni++] = digits[(ch & 0xF00) >> 8];

            newchars[ni++] = digits[(ch & 0xF0) >> 4];

            newchars[ni++] = digits[ch & 0xF];

        }

    }

    JS_ASSERT(ni == newlength);

    newchars[newlength] = 0;



    str = js_NewString(cx, newchars, newlength, 0);

    if (!str) {

        JS_free(cx, newchars);

        return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}

#undef IS_OK



/* See ECMA-262 15.1.2.5 */

static JSBool

str_unescape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str;

    size_t i, ni;

    const jschar *chars;

    jschar *newchars;

    jschar ch;



    str = js_ValueToString(cx, argv[0]);

    if (!str)

        return JS_FALSE;

    argv[0] = STRING_TO_JSVAL(str);



    chars = str->chars;

    /* Don't bother allocating less space for the new string. */

    newchars = (jschar *) JS_malloc(cx, (str->length + 1) * sizeof(jschar));

    ni = i = 0;

    while (i < str->length) {

        ch = chars[i++];

        if (ch == '%') {

            if (i + 1 < str->length &&

                JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1]))

            {

                ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]);

                i += 2;

            } else if (i + 4 < str->length && chars[i] == 'u' &&

                       JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) &&

                       JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4]))

            {

                ch = (((((JS7_UNHEX(chars[i + 1]) << 4)

                        + JS7_UNHEX(chars[i + 2])) << 4)

                      + JS7_UNHEX(chars[i + 3])) << 4)

                    + JS7_UNHEX(chars[i + 4]);

                i += 5;

            }

        }

        newchars[ni++] = ch;

    }

    newchars[ni] = 0;



    str = js_NewString(cx, newchars, ni, 0);

    if (!str) {

        JS_free(cx, newchars);

        return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



#if JS_HAS_UNEVAL

static JSBool

str_uneval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str;



    str = js_ValueToSource(cx, argv[0]);

    if (!str)

        return JS_FALSE;

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}

#endif



const char js_escape_str[] = "escape";

const char js_unescape_str[] = "unescape";

#if JS_HAS_UNEVAL

const char js_uneval_str[] = "uneval";

#endif

const char js_decodeURI_str[] = "decodeURI";

const char js_encodeURI_str[] = "encodeURI";

const char js_decodeURIComponent_str[] = "decodeURIComponent";

const char js_encodeURIComponent_str[] = "encodeURIComponent";



static JSFunctionSpec string_functions[] = {

#ifndef MOZILLA_CLIENT

    /* These two are predefined in a backward-compatible way by the DOM. */

    {js_escape_str,             str_escape,                 1,0,0},

    {js_unescape_str,           str_unescape,               1,0,0},

#endif

#if JS_HAS_UNEVAL

    {js_uneval_str,             str_uneval,                 1,0,0},

#endif

    {js_decodeURI_str,          str_decodeURI,              1,0,0},

    {js_encodeURI_str,          str_encodeURI,              1,0,0},

    {js_decodeURIComponent_str, str_decodeURI_Component,    1,0,0},

    {js_encodeURIComponent_str, str_encodeURI_Component,    1,0,0},



    {0,0,0,0,0}

};



jschar      js_empty_ucstr[]  = {0};

JSSubString js_EmptySubString = {0, js_empty_ucstr};



enum string_tinyid {

    STRING_LENGTH = -1

};



static JSPropertySpec string_props[] = {

    {js_length_str,     STRING_LENGTH,

                        JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, 0,0},

    {0,0,0,0,0}

};



static JSBool

str_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    /* Make delete s.length fail even though length is in s.__proto__. */

    if (id == ATOM_KEY(cx->runtime->atomState.lengthAtom))

        *vp = JSVAL_FALSE;

    return JS_TRUE;

}



static JSBool

str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)

{

    JSString *str;



    if (!JSVAL_IS_INT(id))

        return JS_TRUE;

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    if (JSVAL_TO_INT(id) == STRING_LENGTH)

        *vp = INT_TO_JSVAL((jsint)str->length);

    return JS_TRUE;

}



static JSBool

str_resolve1(JSContext *cx, JSObject *obj, JSString *str, jsint slot)

{

    jschar buf[2];

    JSString *str1;



    buf[0] = str->chars[slot];

    buf[1] = 0;

    str1 = js_NewStringCopyN(cx, buf, 1, 0);

    if (!str1)

        return JS_FALSE;

    /* XXX avoid one-char strings -- use a substring weak ref on str? */

    return JS_DefineElement(cx, obj, slot, STRING_TO_JSVAL(str1),

                            JS_PropertyStub, JS_PropertyStub,

                            JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);

}



static JSBool

str_enumerate(JSContext *cx, JSObject *obj)

{

    JSString *str;

    JSBool ok;

    jsint i;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    ok = JS_TRUE;

    js_LockGCThing(cx, str);

    for (i = 0; i < (jsint)str->length; i++) {

        ok = str_resolve1(cx, obj, str, i);

        if (!ok)

            break;

    }

    js_UnlockGCThing(cx, str);

    return ok;

}



static JSBool

str_resolve(JSContext *cx, JSObject *obj, jsval id)

{

    JSString *str;

    jsint slot;



    if (!JSVAL_IS_INT(id))

        return JS_TRUE;

    slot = JSVAL_TO_INT(id);

    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    if ((size_t)slot >= str->length)

        return JS_TRUE;

    return str_resolve1(cx, obj, str, slot);

}



static JSClass string_class = {

    js_String_str,

    JSCLASS_HAS_PRIVATE,

    JS_PropertyStub,  str_delProperty,  str_getProperty,  JS_PropertyStub,

    str_enumerate,    str_resolve,      JS_ConvertStub,   JS_FinalizeStub,

    JSCLASS_NO_OPTIONAL_MEMBERS

};



#if JS_HAS_TOSOURCE



/*

 * String.prototype.quote is generic (as are most string methods), unlike

 * toSource, toString, and valueOf.

 */

static JSBool

str_quote(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    str = js_QuoteString(cx, str, '"');

    if (!str)

        return JS_FALSE;

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsval v;

    JSString *str;

    size_t i, j, k, n;

    char buf[16];

    jschar *s, *t;



    if (!JS_InstanceOf(cx, obj, &string_class, argv))

        return JS_FALSE;

    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);

    if (!JSVAL_IS_STRING(v))

        return js_obj_toSource(cx, obj, argc, argv, rval);

    str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"');

    if (!str)

        return JS_FALSE;

    j = JS_snprintf(buf, sizeof buf, "(new %s(", string_class.name);

    s = str->chars;

    k = str->length;

    n = j + k + 2;

    t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));

    if (!t)

        return JS_FALSE;

    for (i = 0; i < j; i++)

        t[i] = buf[i];

    for (j = 0; j < k; i++, j++)

        t[i] = s[j];

    t[i++] = ')';

    t[i++] = ')';

    t[i] = 0;

    str = js_NewString(cx, t, n, 0);

    if (!str) {

        JS_free(cx, t);

        return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



#endif /* JS_HAS_TOSOURCE */



static JSBool

str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    jsval v;



    if (!JS_InstanceOf(cx, obj, &string_class, argv))

        return JS_FALSE;

    v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);

    if (!JSVAL_IS_STRING(v))

        return js_obj_toString(cx, obj, argc, argv, rval);

    *rval = v;

    return JS_TRUE;

}



static JSBool

str_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    if (!JS_InstanceOf(cx, obj, &string_class, argv))

        return JS_FALSE;

    *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);

    return JS_TRUE;

}



/*

 * Java-like string native methods.

 */

static JSBool

str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

              jsval *rval)

{

    JSString *str;

    jsdouble d;

    jsdouble length, begin, end;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    argv[-1] = STRING_TO_JSVAL(str);



    if (argc != 0) {

        if (!js_ValueToNumber(cx, argv[0], &d))

            return JS_FALSE;

        length = str->length;

        begin = js_DoubleToInteger(d);

        if (begin < 0)

            begin = 0;

        else if (begin > length)

            begin = length;



        if (argc == 1) {

            end = length;

        } else {

            if (!js_ValueToNumber(cx, argv[1], &d))

                return JS_FALSE;

            end = js_DoubleToInteger(d);

            if (end < 0)

                end = 0;

            else if (end > length)

                end = length;

            if (end < begin) {

                if (cx->version != JSVERSION_1_2) {

                    /* XXX emulate old JDK1.0 java.lang.String.substring. */

                    jsdouble tmp = begin;

                    begin = end;

                    end = tmp;

                } else {

                    end = begin;

                }

            }

        }



        str = js_NewStringCopyN(cx, str->chars + (size_t)begin,

                                (size_t)(end - begin), 0);

        if (!str)

            return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

str_toLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                jsval *rval)

{

    JSString *str;

    size_t i, n;

    jschar *s, *news;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    n = str->length;

    news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));

    if (!news)

        return JS_FALSE;

    s = str->chars;

    for (i = 0; i < n; i++)

        news[i] = (jschar)JS_TOLOWER(s[i]);

    news[n] = 0;

    str = js_NewString(cx, news, n, 0);

    if (!str) {

        JS_free(cx, news);

        return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

str_toLocaleLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                jsval *rval)

{

    JSString *str;

/*

 *  Forcibly ignore the first (or any) argument and return toLowerCase(),

 *  ECMA has reserved that argument, presumbaly for defining the locale.

 */

    if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) {

        str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

        if (!str)

            return JS_FALSE;

        return cx->localeCallbacks->localeToLowerCase(cx, str, rval);

    }

    return str_toLowerCase(cx, obj, 0, argv, rval);

}



static JSBool

str_toUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                jsval *rval)

{

    JSString *str;

    size_t i, n;

    jschar *s, *news;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    n = str->length;

    news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));

    if (!news)

        return JS_FALSE;

    s = str->chars;

    for (i = 0; i < n; i++)

        news[i] = (jschar)JS_TOUPPER(s[i]);

    news[n] = 0;

    str = js_NewString(cx, news, n, 0);

    if (!str) {

        JS_free(cx, news);

        return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

str_toLocaleUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                jsval *rval)

{

    JSString *str;

/*

 *  Forcibly ignore the first (or any) argument and return toLowerCase(),

 *  ECMA has reserved that argument, presumbaly for defining the locale.

 */

    if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) {

        str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

        if (!str)

            return JS_FALSE;

        return cx->localeCallbacks->localeToUpperCase(cx, str, rval);

    }

    return str_toUpperCase(cx, obj, 0, argv, rval);

}



static JSBool

str_localeCompare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str, *thatStr;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    argv[-1] = STRING_TO_JSVAL(str);



    if (argc == 0) {

        *rval = JSVAL_ZERO;

    } else {

        thatStr = js_ValueToString(cx, argv[0]);

        if (!thatStr)

            return JS_FALSE;

        if (cx->localeCallbacks && cx->localeCallbacks->localeCompare)

            return cx->localeCallbacks->localeCompare(cx, str, thatStr, rval);

        *rval = INT_TO_JSVAL(js_CompareStrings(str, thatStr));

    }

    return JS_TRUE;

}



static JSBool

str_charAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str;

    jsdouble d;

    size_t index;

    jschar buf[2];



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    argv[-1] = STRING_TO_JSVAL(str);



    if (argc == 0) {

        d = 0.0;

    } else {

        if (!js_ValueToNumber(cx, argv[0], &d))

            return JS_FALSE;

        d = js_DoubleToInteger(d);

    }



    if (d < 0 || str->length <= d) {

        *rval = JS_GetEmptyStringValue(cx);

    } else {

        index = (size_t)d;

        buf[0] = str->chars[index];

        buf[1] = 0;

        str = js_NewStringCopyN(cx, buf, 1, 0);

        if (!str)

            return JS_FALSE;

        *rval = STRING_TO_JSVAL(str);

    }

    return JS_TRUE;

}



static JSBool

str_charCodeAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

               jsval *rval)

{

    JSString *str;

    jsdouble d;

    size_t index;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    argv[-1] = STRING_TO_JSVAL(str);



    if (argc == 0) {

        d = 0.0;

    } else {

        if (!js_ValueToNumber(cx, argv[0], &d))

            return JS_FALSE;

        d = js_DoubleToInteger(d);

    }



    if (d < 0 || str->length <= d) {

        *rval = JS_GetNaNValue(cx);

    } else {

        index = (size_t)d;

        *rval = INT_TO_JSVAL((jsint)str->chars[index]);

    }

    return JS_TRUE;

}



jsint

js_BoyerMooreHorspool(const jschar *text, jsint textlen,

                      const jschar *pat, jsint patlen,

                      jsint start)

{

    jsint i, j, k, m;

    uint8 skip[BMH_CHARSET_SIZE];

    jschar c;



    JS_ASSERT(0 < patlen && patlen <= BMH_PATLEN_MAX);

    for (i = 0; i < BMH_CHARSET_SIZE; i++)

        skip[i] = (uint8)patlen;

    m = patlen - 1;

    for (i = 0; i < m; i++) {

        c = pat[i];

        if (c >= BMH_CHARSET_SIZE)

            return BMH_BAD_PATTERN;

        skip[c] = (uint8)(m - i);

    }

    for (k = start + m;

         k < textlen;

         k += ((c = text[k]) >= BMH_CHARSET_SIZE) ? patlen : skip[c]) {

        for (i = k, j = m; ; i--, j--) {

            if (j < 0)

                return i + 1;

            if (text[i] != pat[j])

                break;

        }

    }

    return -1;

}



static JSBool

str_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str, *str2;

    jsint i, j, index, textlen, patlen;

    const jschar *text, *pat;

    jsdouble d;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    argv[-1] = STRING_TO_JSVAL(str);

    text = str->chars;

    textlen = (jsint)str->length;



    str2 = js_ValueToString(cx, argv[0]);

    if (!str2)

        return JS_FALSE;

    argv[0] = STRING_TO_JSVAL(str2);

    pat = str2->chars;

    patlen = (jsint)str2->length;



    if (argc > 1) {

        if (!js_ValueToNumber(cx, argv[1], &d))

            return JS_FALSE;

        d = js_DoubleToInteger(d);

        if (d < 0)

            i = 0;

        else if (d > textlen)

            i = textlen;

        else

            i = (jsint)d;

    } else {

        i = 0;

    }

    if (patlen == 0) {

        *rval = INT_TO_JSVAL(i);

        return JS_TRUE;

    }



    /* XXX tune the BMH threshold (512) */

    if ((jsuint)(patlen - 2) <= BMH_PATLEN_MAX - 2 && textlen >= 512) {

        index = js_BoyerMooreHorspool(text, textlen, pat, patlen, i);

        if (index != BMH_BAD_PATTERN)

            goto out;

    }



    index = -1;

    j = 0;

    while (i + j < textlen) {

        if (text[i + j] == pat[j]) {

            if (++j == patlen) {

                index = i;

                break;

            }

        } else {

            i++;

            j = 0;

        }

    }



out:

    *rval = INT_TO_JSVAL(index);

    return JS_TRUE;

}



static JSBool

str_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                  jsval *rval)

{

    JSString *str, *str2;

    const jschar *text, *pat;

    jsint i, j, textlen, patlen;

    jsdouble d;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    argv[-1] = STRING_TO_JSVAL(str);

    text = str->chars;

    textlen = (jsint)str->length;



    str2 = js_ValueToString(cx, argv[0]);

    if (!str2)

        return JS_FALSE;

    argv[0] = STRING_TO_JSVAL(str2);

    pat = str2->chars;

    patlen = (jsint)str2->length;



    if (argc > 1) {

        if (!js_ValueToNumber(cx, argv[1], &d))

            return JS_FALSE;

        if (JSDOUBLE_IS_NaN(d)) {

            i = textlen;

        } else {

            d = js_DoubleToInteger(d);

            if (d < 0)

                i = 0;

            else if (d > textlen - patlen)

                i = textlen - patlen;

            else

                i = (jsint)d;

        }

    } else {

        i = textlen;

    }



    if (patlen == 0) {

        *rval = INT_TO_JSVAL(i);

        return JS_TRUE;

    }



    j = 0;

    while (i >= 0) {

        if (text[i + j] == pat[j]) {

            if (++j == patlen)

                break;

        } else {

            i--;

            j = 0;

        }

    }

    *rval = INT_TO_JSVAL(i);

    return JS_TRUE;

}



/*

 * Perl-inspired string functions.

 */

#if !JS_HAS_MORE_PERL_FUN || !JS_HAS_REGEXPS

static JSBool

str_nyi(JSContext *cx, const char *what)

{

    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                         JSMSG_NO_STRING_PROTO, what);

    return JS_FALSE;

}

#endif



#if JS_HAS_REGEXPS

typedef enum GlobMode {

    GLOB_MATCH,

    GLOB_REPLACE,

    GLOB_SEARCH

} GlobMode;



typedef struct GlobData {

    uintN    optarg;    /* input: index of optional flags argument */

    GlobMode mode;      /* input: return index, match object, or void */

    JSBool   global;    /* output: whether regexp was global */

    JSString *str;      /* output: 'this' parameter object as string */

    JSRegExp *regexp;   /* output: regexp parameter object private data */

} GlobData;



static JSBool

match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                 JSBool (*glob)(JSContext *cx, jsint count, GlobData *data),

                 GlobData *data, jsval *rval, JSBool forceFlat)

{

    JSString *str, *src, *opt;

    JSObject *reobj;

    JSRegExp *re;

    size_t index;

    JSBool ok;

    jsint count;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    argv[-1] = STRING_TO_JSVAL(str);

    data->str = str;



    if (JSVAL_IS_REGEXP(cx, argv[0])) {

        reobj = JSVAL_TO_OBJECT(argv[0]);

        re = (JSRegExp *) JS_GetPrivate(cx, reobj);

    } else {

        if (JSVAL_IS_VOID(argv[0])) {

            re = js_NewRegExp(cx, NULL, cx->runtime->emptyString, 0, JS_FALSE);

        } else {

            src = js_ValueToString(cx, argv[0]);

            if (!src)

                return JS_FALSE;

            if (data->optarg < argc) {

                argv[0] = STRING_TO_JSVAL(src);

                opt = js_ValueToString(cx, argv[data->optarg]);

                if (!opt)

                    return JS_FALSE;

            } else {

                opt = NULL;

            }

            re = js_NewRegExpOpt(cx, NULL, src, opt, forceFlat);

        }

        if (!re)

            return JS_FALSE;

        reobj = NULL;

    }

    data->regexp = re;



    if (reobj)

        JS_LOCK_OBJ(cx, reobj);



    data->global = (re->flags & JSREG_GLOB) != 0;

    index = 0;

    if (data->mode == GLOB_SEARCH) {

        ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval);

        if (ok) {

            *rval = (*rval == JSVAL_TRUE)

                    ? INT_TO_JSVAL(cx->regExpStatics.leftContext.length)

                    : INT_TO_JSVAL(-1);

        }

    } else if (data->global) {

        ok = JS_TRUE;

        re->lastIndex = 0;

        for (count = 0; index <= str->length; count++) {

            ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval);

            if (!ok || *rval != JSVAL_TRUE)

                break;

            ok = glob(cx, count, data);

            if (!ok)

                break;

            if (cx->regExpStatics.lastMatch.length == 0) {

                if (index == str->length)

                    break;

                index++;

            }

        }

    } else {

        ok = js_ExecuteRegExp(cx, re, str, &index, data->mode == GLOB_REPLACE,

                              rval);

    }



    if (reobj) {

        JS_UNLOCK_OBJ(cx, reobj);

    } else {

        js_DestroyRegExp(cx, re);

    }

    return ok;

}



typedef struct MatchData {

    GlobData base;

    JSObject *arrayobj;

} MatchData;



static JSBool

match_glob(JSContext *cx, jsint count, GlobData *data)

{

    MatchData *mdata;

    JSObject *arrayobj;

    JSSubString *matchsub;

    JSString *matchstr;

    jsval v;



    mdata = (MatchData *)data;

    arrayobj = mdata->arrayobj;

    if (!arrayobj) {

        arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL);

        if (!arrayobj)

            return JS_FALSE;

        mdata->arrayobj = arrayobj;

    }

    matchsub = &cx->regExpStatics.lastMatch;

    matchstr = js_NewStringCopyN(cx, matchsub->chars, matchsub->length, 0);

    if (!matchstr)

        return JS_FALSE;

    v = STRING_TO_JSVAL(matchstr);

    return js_SetProperty(cx, arrayobj, INT_TO_JSVAL(count), &v);

}

#endif /* JS_HAS_REGEXPS */



static JSBool

str_match(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

#if JS_HAS_REGEXPS

    MatchData mdata;

    JSBool ok;



    mdata.base.optarg = 1;

    mdata.base.mode = GLOB_MATCH;

    mdata.arrayobj = NULL;

    if (!js_AddRoot(cx, &mdata.arrayobj, "mdata.arrayobj"))

        return JS_FALSE;

    ok = match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval,

                          JS_FALSE);

    if (ok && mdata.arrayobj)

        *rval = OBJECT_TO_JSVAL(mdata.arrayobj);

    js_RemoveRoot(cx->runtime, &mdata.arrayobj);

    return ok;

#else

    return str_nyi(cx, "match");

#endif

}



static JSBool

str_search(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

#if JS_HAS_REGEXPS

    MatchData mdata;



    mdata.base.optarg = 1;

    mdata.base.mode = GLOB_SEARCH;

    return match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval,

                            JS_FALSE);

#else

    return str_nyi(cx, "search");

#endif

}



#if JS_HAS_REGEXPS

typedef struct ReplaceData {

    GlobData    base;           /* base struct state */

    JSObject    *lambda;        /* replacement function object or null */

    JSString    *repstr;        /* replacement string */

    jschar      *dollar;        /* null or pointer to first $ in repstr */

    jschar      *chars;         /* result chars, null initially */

    size_t      length;         /* result length, 0 initially */

    jsint       index;          /* index in result of next replacement */

    jsint       leftIndex;      /* left context index in base.str->chars */

} ReplaceData;



static JSSubString dollarStr;



static JSSubString *

interpret_dollar(JSContext *cx, jschar *dp, ReplaceData *rdata, size_t *skip)

{

    JSRegExpStatics *res;

    jschar dc, *cp;

    uintN num, tmp;

    JSString *str;



    JS_ASSERT(*dp == '$');



    /*

     * Allow a real backslash (literal "\\") to escape "$1" etc.

     * Do this for versions less than 1.5 (ECMA 3) only

     */

    if (cx->version != JSVERSION_DEFAULT && cx->version <= JSVERSION_1_4)

        if (dp > rdata->repstr->chars && dp[-1] == '\\')

            return NULL;



    /* Interpret all Perl match-induced dollar variables. */

    res = &cx->regExpStatics;

    dc = dp[1];

    if (JS7_ISDEC(dc)) {

        if (cx->version != JSVERSION_DEFAULT && cx->version <= JSVERSION_1_4) {

            if (dc == '0')

                return NULL;



            /* Check for overflow to avoid gobbling arbitrary decimal digits. */

            num = 0;

            cp = dp;

            while ((dc = *++cp) != 0 && JS7_ISDEC(dc)) {

                tmp = 10 * num + JS7_UNDEC(dc);

                if (tmp < num)

                    break;

                num = tmp;

            }

        } else { /* ECMA 3, 1-9 or 01-99 */

            num = JS7_UNDEC(dc);

            cp = dp + 2;

            dc = dp[2];

            if ((dc != 0) && JS7_ISDEC(dc)) {

                num = 10 * num + JS7_UNDEC(dc);

                cp++;

            }

            if (num == 0)

                return NULL;

        }

        /* Adjust num from 1 $n-origin to 0 array-index-origin. */

        num--;

        *skip = cp - dp;

		  // DREAMWEAVER: replacing REGEXP_PAREN_SUBSTRING with js_RegExpParenSubString

		  // (see comment from dgeorge in jsregexp.h

        return js_RegExpParenSubString(res, num);

    }



    *skip = 2;

    switch (dc) {

      case '$':

        dollarStr.chars = dp;

        dollarStr.length = 1;

        return &dollarStr;

      case '&':

        return &res->lastMatch;

      case '+':

        return &res->lastParen;

      case '`':

        if (cx->version == JSVERSION_1_2) {

            /*

             * JS1.2 imitated the Perl4 bug where left context at each step

             * in an iterative use of a global regexp started from last match,

             * not from the start of the target string.  But Perl4 does start

             * $` at the beginning of the target string when it is used in a

             * substitution, so we emulate that special case here.

             */

            str = rdata->base.str;

            res->leftContext.chars = str->chars;

            res->leftContext.length = res->lastMatch.chars - str->chars;

        }

        return &res->leftContext;

      case '\'':

        return &res->rightContext;

    }

    return NULL;

}



static JSBool

find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep)

{

    JSString *repstr;

    size_t replen, skip;

    jschar *dp;

    JSSubString *sub;

#if JS_HAS_REPLACE_LAMBDA

    JSObject *lambda;



    lambda = rdata->lambda;

    if (lambda) {

        uintN argc, i, j, m, n, p;

        jsval *sp, *oldsp, rval;

        void *mark;

        JSStackFrame *fp;

        JSBool ok;

        /*

         * Save the rightContext from the current regexp, since it

         * gets stuck at the end of the replacement string and may

         * be clobbered by a RegExp usage in the lambda function.

         */

        JSSubString saveRightContext = cx->regExpStatics.rightContext;



        /*

         * In the lambda case, not only do we find the replacement string's

         * length, we compute repstr and return it via rdata for use within

         * do_replace.  The lambda is called with arguments ($&, $1, $2, ...,

         * index, input), i.e., all the properties of a regexp match array.

         * For $&, etc., we must create string jsvals from cx->regExpStatics.

         * We grab up stack space to keep the newborn strings GC-rooted.

         */

        p = rdata->base.regexp->parenCount;

        argc = 1 + p + 2;

        sp = js_AllocStack(cx, 2 + argc, &mark);

        if (!sp)

            return JS_FALSE;



        /* Push lambda and its 'this' parameter. */

        *sp++ = OBJECT_TO_JSVAL(lambda);

        *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda));



#define PUSH_REGEXP_STATIC(sub)                                               \

    JS_BEGIN_MACRO                                                            \

        JSString *str = js_NewStringCopyN(cx,                                 \

                                          cx->regExpStatics.sub.chars,        \

                                          cx->regExpStatics.sub.length,       \

                                          0);                                 \

        if (!str) {                                                           \

            ok = JS_FALSE;                                                    \

            goto lambda_out;                                                  \

        }                                                                     \

        *sp++ = STRING_TO_JSVAL(str);                                         \

    JS_END_MACRO



        /* Push $&, $1, $2, ... */

        PUSH_REGEXP_STATIC(lastMatch);

        i = 0;

        m = cx->regExpStatics.parenCount;

        n = JS_MIN(m, 9);

        for (j = 0; i < n; i++, j++)

            PUSH_REGEXP_STATIC(parens[j]);

        for (j = 0; i < m; i++, j++)

            PUSH_REGEXP_STATIC(moreParens[j]);



#undef PUSH_REGEXP_STATIC



        /* Make sure to push undefined for any unmatched parens. */

        for (; i < p; i++)

            *sp++ = JSVAL_VOID;



        /* Push match index and input string. */

        *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length);

        *sp++ = STRING_TO_JSVAL(rdata->base.str);



        /* Lift current frame to include the args and do the call. */

        fp = cx->fp;

        oldsp = fp->sp;

        fp->sp = sp;

        ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL);

        rval = fp->sp[-1];

        fp->sp = oldsp;



        if (ok) {

            /*

             * NB: we count on the newborn string root to hold any string

             * created by this js_ValueToString that would otherwise be GC-

             * able, until we use rdata->repstr in do_replace.

             */

            repstr = js_ValueToString(cx, rval);

            if (!repstr) {

                ok = JS_FALSE;

            } else {

                rdata->repstr = repstr;

                *sizep = repstr->length;

            }

        }



      lambda_out:

        js_FreeStack(cx, mark);

        cx->regExpStatics.rightContext = saveRightContext;

        return ok;

    }

#endif /* JS_HAS_REPLACE_LAMBDA */



    repstr = rdata->repstr;

    replen = repstr->length;

    for (dp = rdata->dollar; dp; dp = js_strchr(dp, '$')) {

        sub = interpret_dollar(cx, dp, rdata, &skip);

        if (sub) {

            replen += sub->length - skip;

            dp += skip;

        }

        else

            dp++;

    }

    *sizep = replen;

    return JS_TRUE;

}



static void

do_replace(JSContext *cx, ReplaceData *rdata, jschar *chars)

{

    JSString *repstr;

    jschar *cp, *dp;

    size_t len, skip;

    JSSubString *sub;



    repstr = rdata->repstr;

    cp = repstr->chars;

    dp = rdata->dollar;

    while (dp) {

        len = dp - cp;

        js_strncpy(chars, cp, len);

        chars += len;

        cp = dp;

        sub = interpret_dollar(cx, dp, rdata, &skip);

        if (sub) {

            len = sub->length;

            js_strncpy(chars, sub->chars, len);

            chars += len;

            cp += skip;

            dp += skip;

        }

        else

            dp++;

        dp = js_strchr(dp, '$');

    }

    js_strncpy(chars, cp, repstr->length - (cp - repstr->chars));

}



static JSBool

replace_glob(JSContext *cx, jsint count, GlobData *data)

{

    ReplaceData *rdata;

    JSString *str;

    size_t leftoff, leftlen, replen, growth;

    const jschar *left;

    jschar *chars;



    rdata = (ReplaceData *)data;

    str = data->str;

    leftoff = rdata->leftIndex;

    left = str->chars + leftoff;

    leftlen = cx->regExpStatics.lastMatch.chars - left;

    rdata->leftIndex = cx->regExpStatics.lastMatch.chars - str->chars;

    rdata->leftIndex += cx->regExpStatics.lastMatch.length;

    if (!find_replen(cx, rdata, &replen))

        return JS_FALSE;

    growth = leftlen + replen;

    chars = (jschar *)

        (rdata->chars

         ? JS_realloc(cx, rdata->chars, (rdata->length + growth + 1)

                                        * sizeof(jschar))

         : JS_malloc(cx, (growth + 1) * sizeof(jschar)));

    if (!chars) {

        JS_free(cx, rdata->chars);

        rdata->chars = NULL;

        return JS_FALSE;

    }

    rdata->chars = chars;

    rdata->length += growth;

    chars += rdata->index;

    rdata->index += growth;

    js_strncpy(chars, left, leftlen);

    chars += leftlen;

    do_replace(cx, rdata, chars);

    return JS_TRUE;

}

#endif /* JS_HAS_REGEXPS */



static JSBool

str_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

#if JS_HAS_REGEXPS

    JSObject *lambda;

    JSString *repstr, *str;

    ReplaceData rdata;

    jschar *chars;

    size_t leftlen, rightlen, length;



#if JS_HAS_REPLACE_LAMBDA

    if (JS_TypeOfValue(cx, argv[1]) == JSTYPE_FUNCTION) {

        lambda = JSVAL_TO_OBJECT(argv[1]);

        repstr = NULL;

    } else

#endif

    {

        if (!JS_ConvertValue(cx, argv[1], JSTYPE_STRING, &argv[1]))

            return JS_FALSE;

        repstr = JSVAL_TO_STRING(argv[1]);

        lambda = NULL;

    }



    rdata.base.optarg = 2;

    rdata.base.mode = GLOB_REPLACE;

    rdata.lambda = lambda;

    rdata.repstr = repstr;

    rdata.dollar = repstr ? js_strchr(repstr->chars, '$') : NULL;

    rdata.chars = NULL;

    rdata.length = 0;

    rdata.index = 0;

    rdata.leftIndex = 0;



    /*

     * For ECMA 3, the first argument is to be treated as a string

     * (i.e. converted to one if necessary) UNLESS it's a reg.exp object.

     */

    if (!match_or_replace(cx, obj, argc, argv, replace_glob, &rdata.base, rval,

                          (cx->version == JSVERSION_DEFAULT ||

                           cx->version > JSVERSION_1_4))) {

        return JS_FALSE;

    }



    if (!rdata.chars) {

        if (rdata.base.global || *rval != JSVAL_TRUE) {

            /* Didn't match even once. */

            *rval = STRING_TO_JSVAL(rdata.base.str);

            return JS_TRUE;

        }

        leftlen = cx->regExpStatics.leftContext.length;

        if (!find_replen(cx, &rdata, &length))

            return JS_FALSE;

        length += leftlen;

        chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));

        if (!chars)

            return JS_FALSE;

        js_strncpy(chars, cx->regExpStatics.leftContext.chars, leftlen);

        do_replace(cx, &rdata, chars + leftlen);

        rdata.chars = chars;

        rdata.length = length;

    }



    rightlen = cx->regExpStatics.rightContext.length;

    length = rdata.length + rightlen;

    chars = (jschar *)

        JS_realloc(cx, rdata.chars, (length + 1) * sizeof(jschar));

    if (!chars) {

        JS_free(cx, rdata.chars);

        return JS_FALSE;

    }

    js_strncpy(chars + rdata.length, cx->regExpStatics.rightContext.chars,

               rightlen);

    chars[length] = 0;



    str = js_NewString(cx, chars, length, 0);

    if (!str) {

        JS_free(cx, chars);

        return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

#else

    return str_nyi(cx, "replace");

#endif

}



/*

 * Subroutine used by str_split to find the next split point in str, starting

 * at offset *ip and looking either for the separator substring given by sep,

 * or for the next re match.  In the re case, return the matched separator in

 * *sep, and the possibly updated offset in *ip.

 *

 * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next

 * separator occurrence if found, or str->length if no separator is found.

 */

static jsint

find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip,

           JSSubString *sep)

{

    jsint i, j, k;



    /*

     * Stop if past end of string.  If at end of string, we will compare the

     * null char stored there (by js_NewString*) to sep->chars[j] in the while

     * loop at the end of this function, so that

     *

     *  "ab,".split(',') => ["ab", ""]

     *

     * and the resulting array converts back to the string "ab," for symmetry.

     * However, we ape Perl and do this only if there is a sufficiently large

     * limit argument (see str_split).

     */

    i = *ip;

    if ((size_t)i > str->length)

        return -1;



    /*

     * Perl4 special case for str.split(' '), only if the user has selected

     * JavaScript1.2 explicitly.  Split on whitespace, and skip leading w/s.

     * Strange but true, apparently modeled after awk.

     *

     * NB: we set sep->length to the length of the w/s run, so we must test

     * sep->chars[1] == 0 to make sure sep is just one space.

     */

    if (cx->version == JSVERSION_1_2 &&

        !re && *sep->chars == ' ' && sep->chars[1] == 0) {

        /* Skip leading whitespace if at front of str. */

        if (i == 0) {

            while (JS_ISSPACE(str->chars[i]))

                i++;

            *ip = i;

        }



        /* Don't delimit whitespace at end of string. */

        if ((size_t)i == str->length)

            return -1;



        /* Skip over the non-whitespace chars. */

        while ((size_t)i < str->length && !JS_ISSPACE(str->chars[i]))

            i++;



        /* Now skip the next run of whitespace. */

        j = i;

        while ((size_t)j < str->length && JS_ISSPACE(str->chars[j]))

            j++;



        /* Update sep->length to count delimiter chars. */

        sep->length = (size_t)(j - i);

        return i;

    }



#if JS_HAS_REGEXPS

    /*

     * Match a regular expression against the separator at or above index i.

     * Call js_ExecuteRegExp with true for the test argument.  On successful

     * match, get the separator from cx->regExpStatics.lastMatch.

     */

    if (re) {

        size_t index;

        jsval rval;



      again:

        /* JS1.2 deviated from Perl by never matching at end of string. */

        index = (size_t)i;

        if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval))

            return -2;

        if (rval != JSVAL_TRUE) {

            /* Mismatch: ensure our caller advances i past end of string. */

            sep->length = 1;

            return str->length;

        }

        i = (jsint)index;

        *sep = cx->regExpStatics.lastMatch;

        if (sep->length == 0) {

            /*

             * Empty string match: never split on an empty match at the start

             * of a find_split cycle.  Same rule as for an empty global match

             * in match_or_replace.

             */

            if (i == *ip) {

                /*

                 * "Bump-along" to avoid sticking at an empty match, but don't

                 * bump past end of string -- our caller must do that by adding

                 * sep->length to our return value.

                 */

                if ((size_t)i == str->length) {

                    if (cx->version == JSVERSION_1_2) {

                        sep->length = 1;

                        return i;

                    }

                    return -1;

                }

                i++;

                goto again;

            }

        }

        JS_ASSERT((size_t)i >= sep->length);

        return i - sep->length;

    }

#endif



    /*

     * Deviate from ECMA by never splitting an empty string by any separator

     * string into a non-empty array (an array of length 1 that contains the

     * empty string).

     */

    if (!JSVERSION_IS_ECMA(cx->version) && str->length == 0)

        return -1;



    /*

     * Special case: if sep is the empty string, split str into one character

     * substrings.  Let our caller worry about whether to split once at end of

     * string into an empty substring.

     *

     * For 1.2 compatibility, at the end of the string, we return the length as

     * the result, and set the separator length to 1 -- this allows the caller

     * to include an additional null string at the end of the substring list.

     */

    if (sep->length == 0) {

        if (cx->version == JSVERSION_1_2) {

            if ((size_t)i == str->length) {

                sep->length = 1;

                return i;

            }

            return i + 1;

        }

        return ((size_t)i == str->length) ? -1 : i + 1;

    }



    /*

     * Now that we know sep is non-empty, search starting at i in str for an

     * occurrence of all of sep's chars.  If we find them, return the index of

     * the first separator char.  Otherwise, return str->length.

     */

    j = 0;

    while ((size_t)(k = i + j) < str->length) {

        if (str->chars[k] == sep->chars[j]) {

            if ((size_t)++j == sep->length)

                return i;

        } else {

            i++;

            j = 0;

        }

    }

    return k;

}



static JSBool

str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str, *sub;

    JSObject *arrayobj, *reobj;

    jsval v;

    JSBool ok, limited;

    JSRegExp *re;

    JSSubString *sep, tmp;

    jsdouble d;

    jsint i, j, sublen;

    uint32 len, limit;

    const jschar *substr;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    argv[-1] = STRING_TO_JSVAL(str);



    arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL);

    if (!arrayobj)

        return JS_FALSE;

    *rval = OBJECT_TO_JSVAL(arrayobj);



    if (argc == 0) {

        v = STRING_TO_JSVAL(str);

        ok = JS_SetElement(cx, arrayobj, 0, &v);

    } else {

#if JS_HAS_REGEXPS

        if (JSVAL_IS_REGEXP(cx, argv[0])) {

            reobj = JSVAL_TO_OBJECT(argv[0]);

            re = (JSRegExp *) JS_GetPrivate(cx, reobj);

            sep = &tmp;



            /* Set a magic value so we can detect a successful re match. */

            sep->chars = NULL;

        } else

#endif

        {

            JSString *str2 = js_ValueToString(cx, argv[0]);

            if (!str2)

                return JS_FALSE;

            argv[0] = STRING_TO_JSVAL(str2);



            /*

             * Point sep at a local copy of str2's header because find_split

             * will modify sep->length.

             */

            tmp.length = str2->length;

            tmp.chars = str2->chars;

            sep = &tmp;

            reobj = NULL;

            re = NULL;

        }



        /* Use the second argument as the split limit, if given. */

        limited = (argc > 1) && !JSVAL_IS_VOID(argv[1]);

        limit = 0; /* Avoid warning. */

        if (limited) {

            if (!js_ValueToNumber(cx, argv[1], &d))

                return JS_FALSE;



            /* Clamp limit between 0 and 1 + string length. */

            if (!js_DoubleToECMAUint32(cx, d, &limit))

                return JS_FALSE;

            if (limit > str->length)

                limit = 1 + str->length;

        }



        if (reobj) {

            /* Lock to protect re just in case it's shared and global. */

            JS_LOCK_OBJ(cx, reobj);

        }



        len = i = 0;

        while ((j = find_split(cx, str, re, &i, sep)) >= 0) {

            if (limited && len >= limit)

                break;

            sublen = j - i;

            substr = str->chars + i;

            sub = js_NewStringCopyN(cx, substr, (size_t)sublen, 0);

            if (!sub) {

                ok = JS_FALSE;

                goto unlock_reobj;

            }

            v = STRING_TO_JSVAL(sub);

            ok = JS_SetElement(cx, arrayobj, len, &v);

            if (!ok)

                goto unlock_reobj;

            len++;

#if JS_HAS_REGEXPS

            /*

             * Imitate perl's feature of including parenthesized substrings

             * that matched part of the delimiter in the new array, after the

             * split substring that was delimited.

             */

            if (re && sep->chars) {

                uintN num;

                JSSubString *parsub;



                for (num = 0; num < cx->regExpStatics.parenCount; num++) {

                    if (limited && len >= limit)

                        break;

						  // DREAMWEAVER: replacing REGEXP_PAREN_SUBSTRING with js_RegExpParenSubString

						  // (see comment from dgeorge in jsregexp.h

                    parsub = js_RegExpParenSubString(&cx->regExpStatics, num);

                    sub = js_NewStringCopyN(cx, parsub->chars, parsub->length,

                                            0);

                    if (!sub) {

                        ok = JS_FALSE;

                        goto unlock_reobj;

                    }

                    v = STRING_TO_JSVAL(sub);

                    ok = JS_SetElement(cx, arrayobj, len, &v);

                    if (!ok)

                        goto unlock_reobj;

                    len++;

                }

                sep->chars = NULL;

            }

#endif

            i = j + sep->length;

            if (!JSVERSION_IS_ECMA(cx->version)) {

                /*

                 * Deviate from ECMA to imitate Perl, which omits a final

                 * split unless a limit argument is given and big enough.

                 */

                if (!limited && (size_t)i == str->length)

                    break;

            }

        }

        ok = (j != -2);

      unlock_reobj:

        if (reobj)

            JS_UNLOCK_OBJ(cx, reobj);

    }

    return ok;

}



static JSBool

str_substr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

#if JS_HAS_MORE_PERL_FUN

    JSString *str;

    jsdouble d;

    jsdouble length, begin, end;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;



    if (argc != 0) {

        if (!js_ValueToNumber(cx, argv[0], &d))

            return JS_FALSE;

        length = str->length;

        begin = js_DoubleToInteger(d);

        if (begin < 0) {

            begin += length;

            if (begin < 0)

                begin = 0;

        } else if (begin > length) {

            begin = length;

        }



        if (argc == 1) {

            end = length;

        } else {

            if (!js_ValueToNumber(cx, argv[1], &d))

                return JS_FALSE;

            end = js_DoubleToInteger(d);

            if (end < 0)

                end = 0;

            end += begin;

            if (end > length)

                end = length;

        }



        str = js_NewStringCopyN(cx, str->chars + (size_t)begin,

                                (size_t)(end - begin), 0);

        if (!str)

            return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

#else

    return str_nyi(cx, "substr");

#endif

}



#if JS_HAS_SEQUENCE_OPS

/*

 * Python-esque sequence operations.

 */

static JSBool

str_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str, *str2;

    JSBool ok;

    size_t length, length2, newlength;

    jschar *chars, *newchars;

    uintN i;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    argv[-1] = STRING_TO_JSVAL(str);



    length = str->length;

    chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));

    if (!chars)

        return JS_FALSE;

    js_strncpy(chars, str->chars, length);



    ok = JS_TRUE;

    for (i = 0; i < argc; i++) {

        str2 = js_ValueToString(cx, argv[i]);

        if (!str2) {

            ok = JS_FALSE;

            goto out;

        }

        length2 = str2->length;

        newlength = length + length2;

        newchars = (jschar *)

            JS_realloc(cx, chars, (newlength + 1) * sizeof(jschar));

        if (!newchars) {

            ok = JS_FALSE;

            goto out;

        }

        chars = newchars;

        js_strncpy(chars + length, str2->chars, length2);

        length = newlength;

    }



    chars[length] = 0;

    str = js_NewString(cx, chars, length, 0);

    if (!str)

        ok = JS_FALSE;

out:

    if (ok)

        *rval = STRING_TO_JSVAL(str);

    else

        JS_free(cx, chars);

    return ok;

}



static JSBool

str_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str;

    jsdouble d;

    jsdouble length, begin, end;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    argv[-1] = STRING_TO_JSVAL(str);



    if (argc != 0) {

        if (!js_ValueToNumber(cx, argv[0], &d))

            return JS_FALSE;

        length = str->length;

        begin = js_DoubleToInteger(d);

        if (begin < 0) {

            begin += length;

            if (begin < 0)

                begin = 0;

        } else if (begin > length) {

            begin = length;

        }



        if (argc == 1) {

            end = length;

        } else {

            if (!js_ValueToNumber(cx, argv[1], &d))

                return JS_FALSE;

            end = js_DoubleToInteger(d);

            if (end < 0) {

                end += length;

                if (end < 0)

                    end = 0;

            } else if (end > length) {

                end = length;

            }

            if (end < begin)

                end = begin;

        }



        str = js_NewStringCopyN(cx, str->chars + (size_t)begin,

                                (size_t)(end - begin), 0);

        if (!str)

            return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}

#endif /* JS_HAS_SEQUENCE_OPS */



/*

 * HTML composition aids.

 */

static JSBool

tagify(JSContext *cx, JSObject *obj, jsval *argv,

       const char *begin, const jschar *param, const char *end,

       jsval *rval)

{

    JSString *str;

    jschar *tagbuf;

    size_t beglen, endlen, parlen, taglen;

    size_t i, j;



    str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));

    if (!str)

        return JS_FALSE;

    argv[-1] = STRING_TO_JSVAL(str);



    if (!end)

        end = begin;



    beglen = strlen(begin);

    taglen = 1 + beglen + 1;                    /* '<begin' + '>' */

    parlen = 0; /* Avoid warning. */

    if (param) {

        parlen = js_strlen(param);

        taglen += 2 + parlen + 1;               /* '="param"' */

    }

    endlen = strlen(end);

    taglen += str->length + 2 + endlen + 1;     /* 'str</end>' */



    tagbuf = (jschar *) JS_malloc(cx, (taglen + 1) * sizeof(jschar));

    if (!tagbuf)

        return JS_FALSE;



    j = 0;

    tagbuf[j++] = '<';

    for (i = 0; i < beglen; i++)

        tagbuf[j++] = (jschar)begin[i];

    if (param) {

        tagbuf[j++] = '=';

        tagbuf[j++] = '"';

        js_strncpy(&tagbuf[j], param, parlen);

        j += parlen;

        tagbuf[j++] = '"';

    }

    tagbuf[j++] = '>';

    js_strncpy(&tagbuf[j], str->chars, str->length);

    j += str->length;

    tagbuf[j++] = '<';

    tagbuf[j++] = '/';

    for (i = 0; i < endlen; i++)

        tagbuf[j++] = (jschar)end[i];

    tagbuf[j++] = '>';

    JS_ASSERT(j == taglen);

    tagbuf[j] = 0;



    str = js_NewString(cx, tagbuf, taglen, 0);

    if (!str) {

        free((char *)tagbuf);

        return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSBool

tagify_value(JSContext *cx, JSObject *obj, jsval *argv,

             const char *begin, const char *end,

             jsval *rval)

{

    JSString *param;



    param = js_ValueToString(cx, argv[0]);

    if (!param)

        return JS_FALSE;

    argv[0] = STRING_TO_JSVAL(param);

    return tagify(cx, obj, argv, begin, param->chars, end, rval);

}



static JSBool

str_bold(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return tagify(cx, obj, argv, "B", NULL, NULL, rval);

}



static JSBool

str_italics(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return tagify(cx, obj, argv, "I", NULL, NULL, rval);

}



static JSBool

str_fixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return tagify(cx, obj, argv, "TT", NULL, NULL, rval);

}



static JSBool

str_fontsize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return tagify_value(cx, obj, argv, "FONT SIZE", "FONT", rval);

}



static JSBool

str_fontcolor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

              jsval *rval)

{

    return tagify_value(cx, obj, argv, "FONT COLOR", "FONT", rval);

}



static JSBool

str_link(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return tagify_value(cx, obj, argv, "A HREF", "A", rval);

}



static JSBool

str_anchor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return tagify_value(cx, obj, argv, "A NAME", "A", rval);

}



static JSBool

str_strike(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return tagify(cx, obj, argv, "STRIKE", NULL, NULL, rval);

}



static JSBool

str_small(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return tagify(cx, obj, argv, "SMALL", NULL, NULL, rval);

}



static JSBool

str_big(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return tagify(cx, obj, argv, "BIG", NULL, NULL, rval);

}



static JSBool

str_blink(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return tagify(cx, obj, argv, "BLINK", NULL, NULL, rval);

}



static JSBool

str_sup(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return tagify(cx, obj, argv, "SUP", NULL, NULL, rval);

}



static JSBool

str_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    return tagify(cx, obj, argv, "SUB", NULL, NULL, rval);

}



static JSFunctionSpec string_methods[] = {

#if JS_HAS_TOSOURCE

    {"quote",               str_quote,              0,0,0},

    {js_toSource_str,       str_toSource,           0,0,0},

#endif



    /* Java-like methods. */

    {js_toString_str,       str_toString,           0,0,0},

    {js_valueOf_str,        str_valueOf,            0,0,0},

    {"substring",           str_substring,          2,0,0},

    {"toLowerCase",         str_toLowerCase,        0,0,0},

    {"toUpperCase",         str_toUpperCase,        0,0,0},

    {"charAt",              str_charAt,             1,0,0},

    {"charCodeAt",          str_charCodeAt,         1,0,0},

    {"indexOf",             str_indexOf,            2,0,0},

    {"lastIndexOf",         str_lastIndexOf,        2,0,0},

    {"toLocaleLowerCase",   str_toLocaleLowerCase,  0,0,0},

    {"toLocaleUpperCase",   str_toLocaleUpperCase,  0,0,0},

    {"localeCompare",       str_localeCompare,      1,0,0},



    /* Perl-ish methods (search is actually Python-esque). */

    {"match",               str_match,              1,0,0},

    {"search",              str_search,             1,0,0},

    {"replace",             str_replace,            2,0,0},

    {"split",               str_split,              1,0,0},

    {"substr",              str_substr,             2,0,0},



    /* Python-esque sequence methods. */

#if JS_HAS_SEQUENCE_OPS

    {"concat",              str_concat,             0,0,0},

    {"slice",               str_slice,              0,0,0},

#endif



    /* HTML string methods. */

    {"bold",                str_bold,               0,0,0},

    {"italics",             str_italics,            0,0,0},

    {"fixed",               str_fixed,              0,0,0},

    {"fontsize",            str_fontsize,           1,0,0},

    {"fontcolor",           str_fontcolor,          1,0,0},

    {"link",                str_link,               1,0,0},

    {"anchor",              str_anchor,             1,0,0},

    {"strike",              str_strike,             0,0,0},

    {"small",               str_small,              0,0,0},

    {"big",                 str_big,                0,0,0},

    {"blink",               str_blink,              0,0,0},

    {"sup",                 str_sup,                0,0,0},

    {"sub",                 str_sub,                0,0,0},

    {0,0,0,0,0}

};



static JSBool

String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)

{

    JSString *str;



    if (argc > 0) {

        str = js_ValueToString(cx, argv[0]);

        if (!str)

            return JS_FALSE;

    } else {

        str = cx->runtime->emptyString;

    }

    if (!cx->fp->constructing) {

        *rval = STRING_TO_JSVAL(str);

        return JS_TRUE;

    }

    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str));

    return JS_TRUE;

}



static JSBool

str_fromCharCode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                 jsval *rval)

{

    jschar *chars;

    uintN i;

    uint16 code;

    JSString *str;



    chars = (jschar *) JS_malloc(cx, (argc + 1) * sizeof(jschar));

    if (!chars)

        return JS_FALSE;

    for (i = 0; i < argc; i++) {

        if (!js_ValueToUint16(cx, argv[i], &code)) {

            JS_free(cx, chars);

            return JS_FALSE;

        }

        chars[i] = (jschar)code;

    }

    chars[i] = 0;

    str = js_NewString(cx, chars, argc, 0);

    if (!str) {

        JS_free(cx, chars);

        return JS_FALSE;

    }

    *rval = STRING_TO_JSVAL(str);

    return JS_TRUE;

}



static JSFunctionSpec string_static_methods[] = {

    {"fromCharCode",    str_fromCharCode,       1,0,0},

    {0,0,0,0,0}

};



static JSHashTable *deflated_string_cache;

static uint32 deflated_string_cache_bytes;

#ifdef JS_THREADSAFE

static JSLock *deflated_string_cache_lock;

#endif



JSBool

js_InitStringGlobals(void)

{

#ifdef JS_THREADSAFE

    /* Must come through here once in primordial thread to init safely! */

    if (!deflated_string_cache_lock) {

        deflated_string_cache_lock = JS_NEW_LOCK();

        if (!deflated_string_cache_lock)

            return JS_FALSE;

    }

#endif

    return JS_TRUE;

}



void

js_FreeStringGlobals()

{

    if (deflated_string_cache) {

        JS_HashTableDestroy(deflated_string_cache);

        deflated_string_cache = NULL;

    }

#ifdef JS_THREADSAFE

    if (deflated_string_cache_lock) {

        JS_DESTROY_LOCK(deflated_string_cache_lock);

        deflated_string_cache_lock = NULL;

    }

#endif

}



JSBool

js_InitRuntimeStringState(JSContext *cx)

{

    JSRuntime *rt;

    JSString *empty;



    rt = cx->runtime;

    JS_ASSERT(!rt->emptyString);



    /* Make a permanently locked empty string. */

    empty = js_NewStringCopyN(cx, js_empty_ucstr, 0, GCF_LOCK);

    if (!empty)

        return JS_FALSE;

    rt->emptyString = empty;

    return JS_TRUE;

}



void

js_FinishRuntimeStringState(JSContext *cx)

{

    JSRuntime *rt = cx->runtime;



    js_UnlockGCThing(cx, rt->emptyString);

    rt->emptyString = NULL;

}



JSObject *

js_InitStringClass(JSContext *cx, JSObject *obj)

{

    JSObject *proto;



    /* Define the escape, unescape functions in the global object. */

    if (!JS_DefineFunctions(cx, obj, string_functions))

        return NULL;



    proto = JS_InitClass(cx, obj, NULL, &string_class, String, 1,

                         string_props, string_methods,

                         NULL, string_static_methods);

    if (!proto)

        return NULL;

    OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE,

                 STRING_TO_JSVAL(cx->runtime->emptyString));

    return proto;

}



JSString *

js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag)

{

    JSString *str;



    str = (JSString *) js_AllocGCThing(cx, gcflag | GCX_STRING);

    if (!str)

        return NULL;

    str->length = length;

    str->chars = chars;

#ifdef DEBUG

  {

    JSRuntime *rt = cx->runtime;

    JS_RUNTIME_METER(rt, liveStrings);

    JS_RUNTIME_METER(rt, totalStrings);

    JS_LOCK_RUNTIME_VOID(rt,

        (rt->lengthSum += (double)length,

         rt->lengthSquaredSum += (double)length * (double)length));

  }

#endif

    return str;

}



#ifdef DEBUG

#include <math.h>



void printJSStringStats(JSRuntime *rt) {

    double mean = 0., var = 0., sigma = 0.;

    jsrefcount count = rt->totalStrings;

    if (count > 0 && rt->lengthSum >= 0) {

        mean = rt->lengthSum / count;

        var = count * rt->lengthSquaredSum - rt->lengthSum * rt->lengthSum;

        if (var < 0.0 || count <= 1)

            var = 0.0;

        else

            var /= count * (count - 1);



        /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */

        sigma = (var != 0.) ? sqrt(var) : 0.;

    }

    fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n",

            (unsigned long)count, mean, sigma);

}

#endif



JSString *

js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag)

{

    jschar *news;

    JSString *str;



    news = (jschar *)JS_malloc(cx, (n + 1) * sizeof(jschar));

    if (!news)

        return NULL;

    js_strncpy(news, s, n);

    news[n] = 0;

    str = js_NewString(cx, news, n, gcflag);

    if (!str)

        JS_free(cx, news);

    return str;

}



JSString *

js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag)

{

    size_t n, m;

    jschar *news;

    JSString *str;



    n = js_strlen(s);

    m = (n + 1) * sizeof(jschar);

    news = (jschar *) JS_malloc(cx, m);

    if (!news)

        return NULL;

    memcpy(news, s, m);

    str = js_NewString(cx, news, js_strlen(news), gcflag);

    if (!str)

        JS_free(cx, news);

    return str;

}



JS_STATIC_DLL_CALLBACK(JSHashNumber)

js_hash_string_pointer(const void *key)

{

    return (JSHashNumber)key >> JSVAL_TAGBITS;

}



void

js_FinalizeString(JSContext *cx, JSString *str)

{

    js_FinalizeStringRT(cx->runtime, str);

}



void

js_FinalizeStringRT(JSRuntime *rt, JSString *str)

{

    JSHashNumber hash;

    JSHashEntry *he, **hep;



    JS_RUNTIME_UNMETER(rt, liveStrings);

    if (str->chars) {

        free(str->chars);

        str->chars = NULL;

        if (deflated_string_cache) {

            hash = js_hash_string_pointer(str);

            JS_ACQUIRE_LOCK(deflated_string_cache_lock);

            hep = JS_HashTableRawLookup(deflated_string_cache, hash, str);

            he = *hep;

            if (he) {

                free(he->value);

                JS_HashTableRawRemove(deflated_string_cache, hep, he);

                deflated_string_cache_bytes -= str->length;

            }

            JS_RELEASE_LOCK(deflated_string_cache_lock);

        }

    }

    str->length = 0;

}



JSObject *

js_StringToObject(JSContext *cx, JSString *str)

{

    JSObject *obj;



    obj = js_NewObject(cx, &string_class, NULL, NULL);

    if (!obj)

        return NULL;

    OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str));

    return obj;

}



JSString *

js_ValueToString(JSContext *cx, jsval v)

{

    JSObject *obj;

    JSString *str;



    if (JSVAL_IS_OBJECT(v)) {

        obj = JSVAL_TO_OBJECT(v);

        if (!obj)

            return ATOM_TO_STRING(cx->runtime->atomState.nullAtom);

        if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v))

            return NULL;

    }

    if (JSVAL_IS_STRING(v)) {

        str = JSVAL_TO_STRING(v);

    } else if (JSVAL_IS_INT(v)) {

        str = js_NumberToString(cx, JSVAL_TO_INT(v));

    } else if (JSVAL_IS_DOUBLE(v)) {

        str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(v));

    } else if (JSVAL_IS_BOOLEAN(v)) {

        str = js_BooleanToString(cx, JSVAL_TO_BOOLEAN(v));

    } else {

        str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);

    }

    return str;

}



JSString *

js_ValueToSource(JSContext *cx, jsval v)

{

    if (JSVAL_IS_STRING(v))

        return js_QuoteString(cx, JSVAL_TO_STRING(v), '"');

    if (!JSVAL_IS_PRIMITIVE(v)) {

        if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v),

                          cx->runtime->atomState.toSourceAtom,

                          0, NULL, &v)) {

            return NULL;

        }

    }

    return js_ValueToString(cx, v);

}



JSHashNumber

js_HashString(const JSString *str)

{

    JSHashNumber h;

    size_t n, m;

    const jschar *s;



    h = 0;

    n = str->length;

    s = str->chars;

    if (n < 16) {

        /* Hash every char in a short string. */

        for (; n; s++, n--)

            h = (h >> 28) ^ (h << 4) ^ *s;

    } else {

        /* Sample a la java.lang.String.hash(). */

        for (m = n / 8; n >= m; s += m, n -= m)

            h = (h >> 28) ^ (h << 4) ^ *s;

    }

    return h;

}



intN

js_CompareStrings(const JSString *str1, const JSString *str2)

{

    size_t l1, l2, n, i;

    const jschar *s1, *s2;

    intN cmp;



    l1 = str1->length, l2 = str2->length;

    s1 = str1->chars,  s2 = str2->chars;

    n = JS_MIN(l1, l2);

    for (i = 0; i < n; i++) {

        cmp = s1[i] - s2[i];

        if (cmp != 0)

            return cmp;

    }

    return (intN)(l1 - l2);

}



size_t

js_strlen(const jschar *s)

{

    const jschar *t;



    for (t = s; *t != 0; t++)

        ;

    return (size_t)(t - s);

}



jschar *

js_strchr(const jschar *s, jschar c)

{

    while (*s != 0) {

        if (*s == c)

            return (jschar *)s;

        s++;

    }

    return NULL;

}



const jschar *

js_SkipWhiteSpace(const jschar *s)

{

    /* JS_ISSPACE is false on a null. */

    while (JS_ISSPACE(*s))

        s++;

    return s;

}



jschar *

js_strncpy(jschar *t, const jschar *s, size_t n)

{

    size_t i;



    for (i = 0; i < n; i++)

        t[i] = s[i];

    return t;

}



#define INFLATE_STRING_BODY                                                   \

    for (i = 0; i < length; i++)                                              \

        chars[i] = (unsigned char) bytes[i];                                  \

    chars[i] = 0;



void

js_InflateStringToBuffer(jschar *chars, const char *bytes, size_t length)

{

    size_t i;



    INFLATE_STRING_BODY

}



jschar *

js_InflateString(JSContext *cx, const char *bytes, size_t length)

{

    jschar *chars;

    size_t i;



    chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));

    if (!chars)

        return NULL;



    INFLATE_STRING_BODY



    return chars;

}



/*

 * May be called with null cx by js_GetStringBytes, see below.

 */

char *

js_DeflateString(JSContext *cx, const jschar *chars, size_t length)

{

    size_t i, size;

    char *bytes;



    size = (length + 1) * sizeof(char);

    bytes = (char *) (cx ? JS_malloc(cx, size) : malloc(size));

    if (!bytes)

        return NULL;

    for (i = 0; i < length; i++)

        bytes[i] = (char) chars[i];

    bytes[i] = 0;

    return bytes;

}



static JSHashTable *

GetDeflatedStringCache(void)

{

    JSHashTable *cache;



    cache = deflated_string_cache;

    if (!cache) {

        cache = JS_NewHashTable(8, js_hash_string_pointer,

                                JS_CompareValues, JS_CompareValues,

                                NULL, NULL);

        deflated_string_cache = cache;

    }

    return cache;

}



JSBool

js_SetStringBytes(JSString *str, char *bytes, size_t length)

{

    JSHashTable *cache;

    JSBool ok;

    JSHashNumber hash;

    JSHashEntry **hep;



    JS_ACQUIRE_LOCK(deflated_string_cache_lock);



    cache = GetDeflatedStringCache();

    if (!cache) {

        ok = JS_FALSE;

    } else {

        hash = js_hash_string_pointer(str);

        hep = JS_HashTableRawLookup(cache, hash, str);

        JS_ASSERT(*hep == NULL);

        ok = JS_HashTableRawAdd(cache, hep, hash, str, bytes) != NULL;

        if (ok)

            deflated_string_cache_bytes += length;

    }



    JS_RELEASE_LOCK(deflated_string_cache_lock);

    return ok;

}



char *

js_GetStringBytes(JSString *str)

{

    JSHashTable *cache;

    char *bytes;

    JSHashNumber hash;

    JSHashEntry *he, **hep;



    JS_ACQUIRE_LOCK(deflated_string_cache_lock);



    cache = GetDeflatedStringCache();

    if (!cache) {

        bytes = NULL;

    } else {

        hash = js_hash_string_pointer(str);

        hep = JS_HashTableRawLookup(cache, hash, str);

        he = *hep;

        if (he) {

            bytes = (char *) he->value;

        } else {

            bytes = js_DeflateString(NULL, str->chars, str->length);

            if (bytes) {

                if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) {

                    deflated_string_cache_bytes += str->length;

                } else {

                    free(bytes);

                    bytes = NULL;

                }

            }

        }

    }



    JS_RELEASE_LOCK(deflated_string_cache_lock);

    return bytes;

}



/*

 * From java.lang.Character.java:

 *

 * The character properties are currently encoded into 32 bits in the

 * following manner:

 *

 * 10 bits      signed offset used for converting case

 *  1 bit       if 1, adding the signed offset converts the character to

 *              lowercase

 *  1 bit       if 1, subtracting the signed offset converts the character to

 *              uppercase

 *  1 bit       if 1, character has a titlecase equivalent (possibly itself)

 *  3 bits      0  may not be part of an identifier

 *              1  ignorable control; may continue a Unicode identifier or JS

 *                 identifier

 *              2  may continue a JS identifier but not a Unicode identifier

 *                 (unused)

 *              3  may continue a Unicode identifier or JS identifier

 *              4  is a JS whitespace character

 *              5  may start or continue a JS identifier;

 *                 may continue but not start a Unicode identifier (_)

 *              6  may start or continue a JS identifier but not a Unicode

 *                 identifier ($)

 *              7  may start or continue a Unicode identifier or JS identifier

 *              Thus:

 *                 5, 6, 7 may start a JS identifier

 *                 1, 2, 3, 5, 6, 7 may continue a JS identifier

 *                 7 may start a Unicode identifier

 *                 1, 3, 5, 7 may continue a Unicode identifier

 *                 1 is ignorable within an identifier

 *                 4 is JS whitespace

 *  2 bits      0  this character has no numeric property

 *              1  adding the digit offset to the character code and then

 *                 masking with 0x1F will produce the desired numeric value

 *              2  this character has a "strange" numeric value

 *              3  a JS supradecimal digit: adding the digit offset to the

 *                 character code, then masking with 0x1F, then adding 10

 *                 will produce the desired numeric value

 *  5 bits      digit offset

 *  4 bits      reserved for future use

 *  5 bits      character type

 */



/* The X table has 1024 entries for a total of 1024 bytes. */



const uint8 js_X[] = {

  0,   1,   2,   3,   4,   5,   6,   7,  /*  0x0000 */

  8,   9,  10,  11,  12,  13,  14,  15,  /*  0x0200 */

 16,  17,  18,  19,  20,  21,  22,  23,  /*  0x0400 */

 24,  25,  26,  27,  28,  28,  28,  28,  /*  0x0600 */

 28,  28,  28,  28,  29,  30,  31,  32,  /*  0x0800 */

 33,  34,  35,  36,  37,  38,  39,  40,  /*  0x0A00 */

 41,  42,  43,  44,  45,  46,  28,  28,  /*  0x0C00 */

 47,  48,  49,  50,  51,  52,  53,  28,  /*  0x0E00 */

 28,  28,  54,  55,  56,  57,  58,  59,  /*  0x1000 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x1200 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x1400 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x1600 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x1800 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x1A00 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x1C00 */

 60,  60,  61,  62,  63,  64,  65,  66,  /*  0x1E00 */

 67,  68,  69,  70,  71,  72,  73,  74,  /*  0x2000 */

 75,  75,  75,  76,  77,  78,  28,  28,  /*  0x2200 */

 79,  80,  81,  82,  83,  83,  84,  85,  /*  0x2400 */

 86,  85,  28,  28,  87,  88,  89,  28,  /*  0x2600 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x2800 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x2A00 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x2C00 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x2E00 */

 90,  91,  92,  93,  94,  56,  95,  28,  /*  0x3000 */

 96,  97,  98,  99,  83, 100,  83, 101,  /*  0x3200 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x3400 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x3600 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x3800 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x3A00 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x3C00 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x3E00 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4000 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4200 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4400 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4600 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4800 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4A00 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0x4C00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x4E00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5000 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5200 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5400 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5600 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5800 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5A00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5C00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x5E00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6000 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6200 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6400 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6600 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6800 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6A00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6C00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x6E00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7000 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7200 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7400 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7600 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7800 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7A00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7C00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x7E00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8000 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8200 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8400 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8600 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8800 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8A00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8C00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x8E00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9000 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9200 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9400 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9600 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9800 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9A00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0x9C00 */

 56,  56,  56,  56,  56,  56, 102,  28,  /*  0x9E00 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0xA000 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0xA200 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0xA400 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0xA600 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0xA800 */

 28,  28,  28,  28,  28,  28,  28,  28,  /*  0xAA00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xAC00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xAE00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xB000 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xB200 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xB400 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xB600 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xB800 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xBA00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xBC00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xBE00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xC000 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xC200 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xC400 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xC600 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xC800 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xCA00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xCC00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xCE00 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xD000 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xD200 */

 56,  56,  56,  56,  56,  56,  56,  56,  /*  0xD400 */

 56,  56,  56,  56,  56,  56, 103,  28,  /*  0xD600 */

104, 104, 104, 104, 104, 104, 104, 104,  /*  0xD800 */

104, 104, 104, 104, 104, 104, 104, 104,  /*  0xDA00 */

104, 104, 104, 104, 104, 104, 104, 104,  /*  0xDC00 */

104, 104, 104, 104, 104, 104, 104, 104,  /*  0xDE00 */

105, 105, 105, 105, 105, 105, 105, 105,  /*  0xE000 */

105, 105, 105, 105, 105, 105, 105, 105,  /*  0xE200 */

105, 105, 105, 105, 105, 105, 105, 105,  /*  0xE400 */

105, 105, 105, 105, 105, 105, 105, 105,  /*  0xE600 */

105, 105, 105, 105, 105, 105, 105, 105,  /*  0xE800 */

105, 105, 105, 105, 105, 105, 105, 105,  /*  0xEA00 */

105, 105, 105, 105, 105, 105, 105, 105,  /*  0xEC00 */

105, 105, 105, 105, 105, 105, 105, 105,  /*  0xEE00 */

105, 105, 105, 105, 105, 105, 105, 105,  /*  0xF000 */

105, 105, 105, 105, 105, 105, 105, 105,  /*  0xF200 */

105, 105, 105, 105, 105, 105, 105, 105,  /*  0xF400 */

105, 105, 105, 105, 105, 105, 105, 105,  /*  0xF600 */

105, 105, 105, 105,  56,  56,  56,  56,  /*  0xF800 */

106,  28,  28,  28, 107, 108, 109, 110,  /*  0xFA00 */

 56,  56,  56,  56, 111, 112, 113, 114,  /*  0xFC00 */

115, 116,  56, 117, 118, 119, 120, 121   /*  0xFE00 */

};



/* The Y table has 7808 entries for a total of 7808 bytes. */



const uint8 js_Y[] = {

  0,   0,   0,   0,   0,   0,   0,   0,  /*    0 */

  0,   1,   1,   1,   1,   1,   0,   0,  /*    0 */

  0,   0,   0,   0,   0,   0,   0,   0,  /*    0 */

  0,   0,   0,   0,   0,   0,   0,   0,  /*    0 */

  2,   3,   3,   3,   4,   3,   3,   3,  /*    0 */

  5,   6,   3,   7,   3,   8,   3,   3,  /*    0 */

  9,   9,   9,   9,   9,   9,   9,   9,  /*    0 */

  9,   9,   3,   3,   7,   7,   7,   3,  /*    0 */

  3,  10,  10,  10,  10,  10,  10,  10,  /*    1 */

 10,  10,  10,  10,  10,  10,  10,  10,  /*    1 */

 10,  10,  10,  10,  10,  10,  10,  10,  /*    1 */

 10,  10,  10,   5,   3,   6,  11,  12,  /*    1 */

 11,  13,  13,  13,  13,  13,  13,  13,  /*    1 */

 13,  13,  13,  13,  13,  13,  13,  13,  /*    1 */

 13,  13,  13,  13,  13,  13,  13,  13,  /*    1 */

 13,  13,  13,   5,   7,   6,   7,   0,  /*    1 */

  0,   0,   0,   0,   0,   0,   0,   0,  /*    2 */

  0,   0,   0,   0,   0,   0,   0,   0,  /*    2 */

  0,   0,   0,   0,   0,   0,   0,   0,  /*    2 */

  0,   0,   0,   0,   0,   0,   0,   0,  /*    2 */

  2,   3,   4,   4,   4,   4,  15,  15,  /*    2 */

 11,  15,  16,   5,   7,   8,  15,  11,  /*    2 */

 15,   7,  17,  17,  11,  16,  15,   3,  /*    2 */

 11,  18,  16,   6,  19,  19,  19,   3,  /*    2 */

 20,  20,  20,  20,  20,  20,  20,  20,  /*    3 */

 20,  20,  20,  20,  20,  20,  20,  20,  /*    3 */

 20,  20,  20,  20,  20,  20,  20,   7,  /*    3 */

 20,  20,  20,  20,  20,  20,  20,  16,  /*    3 */

 21,  21,  21,  21,  21,  21,  21,  21,  /*    3 */

 21,  21,  21,  21,  21,  21,  21,  21,  /*    3 */

 21,  21,  21,  21,  21,  21,  21,   7,  /*    3 */

 21,  21,  21,  21,  21,  21,  21,  22,  /*    3 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    4 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    4 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    4 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    4 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    4 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    4 */

 25,  26,  23,  24,  23,  24,  23,  24,  /*    4 */

 16,  23,  24,  23,  24,  23,  24,  23,  /*    4 */

 24,  23,  24,  23,  24,  23,  24,  23,  /*    5 */

 24,  16,  23,  24,  23,  24,  23,  24,  /*    5 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    5 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    5 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    5 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    5 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    5 */

 27,  23,  24,  23,  24,  23,  24,  28,  /*    5 */

 16,  29,  23,  24,  23,  24,  30,  23,  /*    6 */

 24,  31,  31,  23,  24,  16,  32,  32,  /*    6 */

 33,  23,  24,  31,  34,  16,  35,  36,  /*    6 */

 23,  24,  16,  16,  35,  37,  16,  38,  /*    6 */

 23,  24,  23,  24,  23,  24,  38,  23,  /*    6 */

 24,  39,  40,  16,  23,  24,  39,  23,  /*    6 */

 24,  41,  41,  23,  24,  23,  24,  42,  /*    6 */

 23,  24,  16,  40,  23,  24,  40,  40,  /*    6 */

 40,  40,  40,  40,  43,  44,  45,  43,  /*    7 */

 44,  45,  43,  44,  45,  23,  24,  23,  /*    7 */

 24,  23,  24,  23,  24,  23,  24,  23,  /*    7 */

 24,  23,  24,  23,  24,  16,  23,  24,  /*    7 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    7 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    7 */

 16,  43,  44,  45,  23,  24,  46,  46,  /*    7 */

 46,  46,  23,  24,  23,  24,  23,  24,  /*    7 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    8 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    8 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*    8 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*    8 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*    8 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*    8 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*    8 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*    8 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*    9 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*    9 */

 16,  16,  16,  47,  48,  16,  49,  49,  /*    9 */

 50,  50,  16,  51,  16,  16,  16,  16,  /*    9 */

 49,  16,  16,  52,  16,  16,  16,  16,  /*    9 */

 53,  54,  16,  16,  16,  16,  16,  54,  /*    9 */

 16,  16,  55,  16,  16,  16,  16,  16,  /*    9 */

 16,  16,  16,  16,  16,  16,  16,  16,  /*    9 */

 16,  16,  16,  56,  16,  16,  16,  16,  /*   10 */

 56,  16,  57,  57,  16,  16,  16,  16,  /*   10 */

 16,  16,  58,  16,  16,  16,  16,  16,  /*   10 */

 16,  16,  16,  16,  16,  16,  16,  16,  /*   10 */

 16,  16,  16,  16,  16,  16,  16,  16,  /*   10 */

 16,  46,  46,  46,  46,  46,  46,  46,  /*   10 */

 59,  59,  59,  59,  59,  59,  59,  59,  /*   10 */

 59,  11,  11,  59,  59,  59,  59,  59,  /*   10 */

 59,  59,  11,  11,  11,  11,  11,  11,  /*   11 */

 11,  11,  11,  11,  11,  11,  11,  11,  /*   11 */

 59,  59,  11,  11,  11,  11,  11,  11,  /*   11 */

 11,  11,  11,  11,  11,  11,  11,  46,  /*   11 */

 59,  59,  59,  59,  59,  11,  11,  11,  /*   11 */

 11,  11,  46,  46,  46,  46,  46,  46,  /*   11 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   11 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   11 */

 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */

 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */

 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */

 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */

 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */

 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */

 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */

 60,  60,  60,  60,  60,  60,  60,  60,  /*   12 */

 60,  60,  60,  60,  60,  60,  46,  46,  /*   13 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   13 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   13 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   13 */

 60,  60,  46,  46,  46,  46,  46,  46,  /*   13 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   13 */

 46,  46,  46,  46,   3,   3,  46,  46,  /*   13 */

 46,  46,  59,  46,  46,  46,   3,  46,  /*   13 */

 46,  46,  46,  46,  11,  11,  61,   3,  /*   14 */

 62,  62,  62,  46,  63,  46,  64,  64,  /*   14 */

 16,  20,  20,  20,  20,  20,  20,  20,  /*   14 */

 20,  20,  20,  20,  20,  20,  20,  20,  /*   14 */

 20,  20,  46,  20,  20,  20,  20,  20,  /*   14 */

 20,  20,  20,  20,  65,  66,  66,  66,  /*   14 */

 16,  21,  21,  21,  21,  21,  21,  21,  /*   14 */

 21,  21,  21,  21,  21,  21,  21,  21,  /*   14 */

 21,  21,  16,  21,  21,  21,  21,  21,  /*   15 */

 21,  21,  21,  21,  67,  68,  68,  46,  /*   15 */

 69,  70,  38,  38,  38,  71,  72,  46,  /*   15 */

 46,  46,  38,  46,  38,  46,  38,  46,  /*   15 */

 38,  46,  23,  24,  23,  24,  23,  24,  /*   15 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   15 */

 73,  74,  16,  40,  46,  46,  46,  46,  /*   15 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   15 */

 46,  75,  75,  75,  75,  75,  75,  75,  /*   16 */

 75,  75,  75,  75,  75,  46,  75,  75,  /*   16 */

 20,  20,  20,  20,  20,  20,  20,  20,  /*   16 */

 20,  20,  20,  20,  20,  20,  20,  20,  /*   16 */

 20,  20,  20,  20,  20,  20,  20,  20,  /*   16 */

 20,  20,  20,  20,  20,  20,  20,  20,  /*   16 */

 21,  21,  21,  21,  21,  21,  21,  21,  /*   16 */

 21,  21,  21,  21,  21,  21,  21,  21,  /*   16 */

 21,  21,  21,  21,  21,  21,  21,  21,  /*   17 */

 21,  21,  21,  21,  21,  21,  21,  21,  /*   17 */

 46,  74,  74,  74,  74,  74,  74,  74,  /*   17 */

 74,  74,  74,  74,  74,  46,  74,  74,  /*   17 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   17 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   17 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   17 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   17 */

 23,  24,  15,  60,  60,  60,  60,  46,  /*   18 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   18 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   18 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   18 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   18 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   18 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   18 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   18 */

 40,  23,  24,  23,  24,  46,  46,  23,  /*   19 */

 24,  46,  46,  23,  24,  46,  46,  46,  /*   19 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   19 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   19 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   19 */

 23,  24,  23,  24,  46,  46,  23,  24,  /*   19 */

 23,  24,  23,  24,  23,  24,  46,  46,  /*   19 */

 23,  24,  46,  46,  46,  46,  46,  46,  /*   19 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   20 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   20 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   20 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   20 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   20 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   20 */

 46,  76,  76,  76,  76,  76,  76,  76,  /*   20 */

 76,  76,  76,  76,  76,  76,  76,  76,  /*   20 */

 76,  76,  76,  76,  76,  76,  76,  76,  /*   21 */

 76,  76,  76,  76,  76,  76,  76,  76,  /*   21 */

 76,  76,  76,  76,  76,  76,  76,  46,  /*   21 */

 46,  59,   3,   3,   3,   3,   3,   3,  /*   21 */

 46,  77,  77,  77,  77,  77,  77,  77,  /*   21 */

 77,  77,  77,  77,  77,  77,  77,  77,  /*   21 */

 77,  77,  77,  77,  77,  77,  77,  77,  /*   21 */

 77,  77,  77,  77,  77,  77,  77,  77,  /*   21 */

 77,  77,  77,  77,  77,  77,  77,  16,  /*   22 */

 46,   3,  46,  46,  46,  46,  46,  46,  /*   22 */

 46,  60,  60,  60,  60,  60,  60,  60,  /*   22 */

 60,  60,  60,  60,  60,  60,  60,  60,  /*   22 */

 60,  60,  46,  60,  60,  60,  60,  60,  /*   22 */

 60,  60,  60,  60,  60,  60,  60,  60,  /*   22 */

 60,  60,  60,  60,  60,  60,  60,  60,  /*   22 */

 60,  60,  46,  60,  60,  60,   3,  60,  /*   22 */

  3,  60,  60,   3,  60,  46,  46,  46,  /*   23 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   23 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   23 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   23 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   23 */

 40,  40,  40,  46,  46,  46,  46,  46,  /*   23 */

 40,  40,  40,   3,   3,  46,  46,  46,  /*   23 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   23 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   24 */

 46,  46,  46,  46,   3,  46,  46,  46,  /*   24 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   24 */

 46,  46,  46,   3,  46,  46,  46,   3,  /*   24 */

 46,  40,  40,  40,  40,  40,  40,  40,  /*   24 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   24 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   24 */

 40,  40,  40,  46,  46,  46,  46,  46,  /*   24 */

 59,  40,  40,  40,  40,  40,  40,  40,  /*   25 */

 40,  40,  40,  60,  60,  60,  60,  60,  /*   25 */

 60,  60,  60,  46,  46,  46,  46,  46,  /*   25 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   25 */

 78,  78,  78,  78,  78,  78,  78,  78,  /*   25 */

 78,  78,   3,   3,   3,   3,  46,  46,  /*   25 */

 60,  40,  40,  40,  40,  40,  40,  40,  /*   25 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   25 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   26 */

 46,  46,  40,  40,  40,  40,  40,  46,  /*   26 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   27 */

 40,  40,  40,  40,  40,  40,  40,  46,  /*   27 */

 40,  40,  40,  40,   3,  40,  60,  60,  /*   27 */

 60,  60,  60,  60,  60,  79,  79,  60,  /*   27 */

 60,  60,  60,  60,  60,  59,  59,  60,  /*   27 */

 60,  15,  60,  60,  60,  60,  46,  46,  /*   27 */

  9,   9,   9,   9,   9,   9,   9,   9,  /*   27 */

  9,   9,  46,  46,  46,  46,  46,  46,  /*   27 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   28 */

 46,  60,  60,  80,  46,  40,  40,  40,  /*   29 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   29 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   29 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   29 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   29 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   29 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   29 */

 40,  40,  46,  46,  60,  40,  80,  80,  /*   29 */

 80,  60,  60,  60,  60,  60,  60,  60,  /*   30 */

 60,  80,  80,  80,  80,  60,  46,  46,  /*   30 */

 15,  60,  60,  60,  60,  46,  46,  46,  /*   30 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   30 */

 40,  40,  60,  60,   3,   3,  81,  81,  /*   30 */

 81,  81,  81,  81,  81,  81,  81,  81,  /*   30 */

  3,  46,  46,  46,  46,  46,  46,  46,  /*   30 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   30 */

 46,  60,  80,  80,  46,  40,  40,  40,  /*   31 */

 40,  40,  40,  40,  40,  46,  46,  40,  /*   31 */

 40,  46,  46,  40,  40,  40,  40,  40,  /*   31 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   31 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   31 */

 40,  46,  40,  40,  40,  40,  40,  40,  /*   31 */

 40,  46,  40,  46,  46,  46,  40,  40,  /*   31 */

 40,  40,  46,  46,  60,  46,  80,  80,  /*   31 */

 80,  60,  60,  60,  60,  46,  46,  80,  /*   32 */

 80,  46,  46,  80,  80,  60,  46,  46,  /*   32 */

 46,  46,  46,  46,  46,  46,  46,  80,  /*   32 */

 46,  46,  46,  46,  40,  40,  46,  40,  /*   32 */

 40,  40,  60,  60,  46,  46,  81,  81,  /*   32 */

 81,  81,  81,  81,  81,  81,  81,  81,  /*   32 */

 40,  40,   4,   4,  82,  82,  82,  82,  /*   32 */

 19,  83,  15,  46,  46,  46,  46,  46,  /*   32 */

 46,  46,  60,  46,  46,  40,  40,  40,  /*   33 */

 40,  40,  40,  46,  46,  46,  46,  40,  /*   33 */

 40,  46,  46,  40,  40,  40,  40,  40,  /*   33 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   33 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   33 */

 40,  46,  40,  40,  40,  40,  40,  40,  /*   33 */

 40,  46,  40,  40,  46,  40,  40,  46,  /*   33 */

 40,  40,  46,  46,  60,  46,  80,  80,  /*   33 */

 80,  60,  60,  46,  46,  46,  46,  60,  /*   34 */

 60,  46,  46,  60,  60,  60,  46,  46,  /*   34 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   34 */

 46,  40,  40,  40,  40,  46,  40,  46,  /*   34 */

 46,  46,  46,  46,  46,  46,  81,  81,  /*   34 */

 81,  81,  81,  81,  81,  81,  81,  81,  /*   34 */

 60,  60,  40,  40,  40,  46,  46,  46,  /*   34 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   34 */

 46,  60,  60,  80,  46,  40,  40,  40,  /*   35 */

 40,  40,  40,  40,  46,  40,  46,  40,  /*   35 */

 40,  40,  46,  40,  40,  40,  40,  40,  /*   35 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   35 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   35 */

 40,  46,  40,  40,  40,  40,  40,  40,  /*   35 */

 40,  46,  40,  40,  46,  40,  40,  40,  /*   35 */

 40,  40,  46,  46,  60,  40,  80,  80,  /*   35 */

 80,  60,  60,  60,  60,  60,  46,  60,  /*   36 */

 60,  80,  46,  80,  80,  60,  46,  46,  /*   36 */

 15,  46,  46,  46,  46,  46,  46,  46,  /*   36 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   36 */

 40,  46,  46,  46,  46,  46,  81,  81,  /*   36 */

 81,  81,  81,  81,  81,  81,  81,  81,  /*   36 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   36 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   36 */

 46,  60,  80,  80,  46,  40,  40,  40,  /*   37 */

 40,  40,  40,  40,  40,  46,  46,  40,  /*   37 */

 40,  46,  46,  40,  40,  40,  40,  40,  /*   37 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   37 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   37 */

 40,  46,  40,  40,  40,  40,  40,  40,  /*   37 */

 40,  46,  40,  40,  46,  46,  40,  40,  /*   37 */

 40,  40,  46,  46,  60,  40,  80,  60,  /*   37 */

 80,  60,  60,  60,  46,  46,  46,  80,  /*   38 */

 80,  46,  46,  80,  80,  60,  46,  46,  /*   38 */

 46,  46,  46,  46,  46,  46,  60,  80,  /*   38 */

 46,  46,  46,  46,  40,  40,  46,  40,  /*   38 */

 40,  40,  46,  46,  46,  46,  81,  81,  /*   38 */

 81,  81,  81,  81,  81,  81,  81,  81,  /*   38 */

 15,  46,  46,  46,  46,  46,  46,  46,  /*   38 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   38 */

 46,  46,  60,  80,  46,  40,  40,  40,  /*   39 */

 40,  40,  40,  46,  46,  46,  40,  40,  /*   39 */

 40,  46,  40,  40,  40,  40,  46,  46,  /*   39 */

 46,  40,  40,  46,  40,  46,  40,  40,  /*   39 */

 46,  46,  46,  40,  40,  46,  46,  46,  /*   39 */

 40,  40,  40,  46,  46,  46,  40,  40,  /*   39 */

 40,  40,  40,  40,  40,  40,  46,  40,  /*   39 */

 40,  40,  46,  46,  46,  46,  80,  80,  /*   39 */

 60,  80,  80,  46,  46,  46,  80,  80,  /*   40 */

 80,  46,  80,  80,  80,  60,  46,  46,  /*   40 */

 46,  46,  46,  46,  46,  46,  46,  80,  /*   40 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   40 */

 46,  46,  46,  46,  46,  46,  46,  81,  /*   40 */

 81,  81,  81,  81,  81,  81,  81,  81,  /*   40 */

 84,  19,  19,  46,  46,  46,  46,  46,  /*   40 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   40 */

 46,  80,  80,  80,  46,  40,  40,  40,  /*   41 */

 40,  40,  40,  40,  40,  46,  40,  40,  /*   41 */

 40,  46,  40,  40,  40,  40,  40,  40,  /*   41 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   41 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   41 */

 40,  46,  40,  40,  40,  40,  40,  40,  /*   41 */

 40,  40,  40,  40,  46,  40,  40,  40,  /*   41 */

 40,  40,  46,  46,  46,  46,  60,  60,  /*   41 */

 60,  80,  80,  80,  80,  46,  60,  60,  /*   42 */

 60,  46,  60,  60,  60,  60,  46,  46,  /*   42 */

 46,  46,  46,  46,  46,  60,  60,  46,  /*   42 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   42 */

 40,  40,  46,  46,  46,  46,  81,  81,  /*   42 */

 81,  81,  81,  81,  81,  81,  81,  81,  /*   42 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   42 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   42 */

 46,  46,  80,  80,  46,  40,  40,  40,  /*   43 */

 40,  40,  40,  40,  40,  46,  40,  40,  /*   43 */

 40,  46,  40,  40,  40,  40,  40,  40,  /*   43 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   43 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   43 */

 40,  46,  40,  40,  40,  40,  40,  40,  /*   43 */

 40,  40,  40,  40,  46,  40,  40,  40,  /*   43 */

 40,  40,  46,  46,  46,  46,  80,  60,  /*   43 */

 80,  80,  80,  80,  80,  46,  60,  80,  /*   44 */

 80,  46,  80,  80,  60,  60,  46,  46,  /*   44 */

 46,  46,  46,  46,  46,  80,  80,  46,  /*   44 */

 46,  46,  46,  46,  46,  46,  40,  46,  /*   44 */

 40,  40,  46,  46,  46,  46,  81,  81,  /*   44 */

 81,  81,  81,  81,  81,  81,  81,  81,  /*   44 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   44 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   44 */

 46,  46,  80,  80,  46,  40,  40,  40,  /*   45 */

 40,  40,  40,  40,  40,  46,  40,  40,  /*   45 */

 40,  46,  40,  40,  40,  40,  40,  40,  /*   45 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   45 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   45 */

 40,  46,  40,  40,  40,  40,  40,  40,  /*   45 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   45 */

 40,  40,  46,  46,  46,  46,  80,  80,  /*   45 */

 80,  60,  60,  60,  46,  46,  80,  80,  /*   46 */

 80,  46,  80,  80,  80,  60,  46,  46,  /*   46 */

 46,  46,  46,  46,  46,  46,  46,  80,  /*   46 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   46 */

 40,  40,  46,  46,  46,  46,  81,  81,  /*   46 */

 81,  81,  81,  81,  81,  81,  81,  81,  /*   46 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   46 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   46 */

 46,  40,  40,  40,  40,  40,  40,  40,  /*   47 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   47 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   47 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   47 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   47 */

 40,  40,  40,  40,  40,  40,  40,   3,  /*   47 */

 40,  60,  40,  40,  60,  60,  60,  60,  /*   47 */

 60,  60,  60,  46,  46,  46,  46,   4,  /*   47 */

 40,  40,  40,  40,  40,  40,  59,  60,  /*   48 */

 60,  60,  60,  60,  60,  60,  60,  15,  /*   48 */

  9,   9,   9,   9,   9,   9,   9,   9,  /*   48 */

  9,   9,   3,   3,  46,  46,  46,  46,  /*   48 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   48 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   48 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   48 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   48 */

 46,  40,  40,  46,  40,  46,  46,  40,  /*   49 */

 40,  46,  40,  46,  46,  40,  46,  46,  /*   49 */

 46,  46,  46,  46,  40,  40,  40,  40,  /*   49 */

 46,  40,  40,  40,  40,  40,  40,  40,  /*   49 */

 46,  40,  40,  40,  46,  40,  46,  40,  /*   49 */

 46,  46,  40,  40,  46,  40,  40,   3,  /*   49 */

 40,  60,  40,  40,  60,  60,  60,  60,  /*   49 */

 60,  60,  46,  60,  60,  40,  46,  46,  /*   49 */

 40,  40,  40,  40,  40,  46,  59,  46,  /*   50 */

 60,  60,  60,  60,  60,  60,  46,  46,  /*   50 */

  9,   9,   9,   9,   9,   9,   9,   9,  /*   50 */

  9,   9,  46,  46,  40,  40,  46,  46,  /*   50 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   50 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   50 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   50 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   50 */

 15,  15,  15,  15,   3,   3,   3,   3,  /*   51 */

  3,   3,   3,   3,   3,   3,   3,   3,  /*   51 */

  3,   3,   3,  15,  15,  15,  15,  15,  /*   51 */

 60,  60,  15,  15,  15,  15,  15,  15,  /*   51 */

 78,  78,  78,  78,  78,  78,  78,  78,  /*   51 */

 78,  78,  85,  85,  85,  85,  85,  85,  /*   51 */

 85,  85,  85,  85,  15,  60,  15,  60,  /*   51 */

 15,  60,   5,   6,   5,   6,  80,  80,  /*   51 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   52 */

 46,  40,  40,  40,  40,  40,  40,  40,  /*   52 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   52 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   52 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   52 */

 40,  40,  46,  46,  46,  46,  46,  46,  /*   52 */

 46,  60,  60,  60,  60,  60,  60,  60,  /*   52 */

 60,  60,  60,  60,  60,  60,  60,  80,  /*   52 */

 60,  60,  60,  60,  60,   3,  60,  60,  /*   53 */

 60,  60,  60,  60,  46,  46,  46,  46,  /*   53 */

 60,  60,  60,  60,  60,  60,  46,  60,  /*   53 */

 46,  60,  60,  60,  60,  60,  60,  60,  /*   53 */

 60,  60,  60,  60,  60,  60,  60,  60,  /*   53 */

 60,  60,  60,  60,  60,  60,  46,  46,  /*   53 */

 46,  60,  60,  60,  60,  60,  60,  60,  /*   53 */

 46,  60,  46,  46,  46,  46,  46,  46,  /*   53 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   54 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   54 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   54 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   54 */

 76,  76,  76,  76,  76,  76,  76,  76,  /*   54 */

 76,  76,  76,  76,  76,  76,  76,  76,  /*   54 */

 76,  76,  76,  76,  76,  76,  76,  76,  /*   54 */

 76,  76,  76,  76,  76,  76,  76,  76,  /*   54 */

 76,  76,  76,  76,  76,  76,  46,  46,  /*   55 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   55 */

 16,  16,  16,  16,  16,  16,  16,  16,  /*   55 */

 16,  16,  16,  16,  16,  16,  16,  16,  /*   55 */

 16,  16,  16,  16,  16,  16,  16,  16,  /*   55 */

 16,  16,  16,  16,  16,  16,  16,  16,  /*   55 */

 16,  16,  16,  16,  16,  16,  16,  46,  /*   55 */

 46,  46,  46,   3,  46,  46,  46,  46,  /*   55 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   56 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */

 40,  40,  46,  46,  46,  46,  46,  40,  /*   57 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   57 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */

 40,  40,  40,  46,  46,  46,  46,  46,  /*   58 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   58 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   59 */

 40,  40,  46,  46,  46,  46,  46,  46,  /*   59 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   60 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   61 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   61 */

 23,  24,  23,  24,  23,  24,  16,  16,  /*   61 */

 16,  16,  16,  16,  46,  46,  46,  46,  /*   61 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   61 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   61 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   61 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   61 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */

 23,  24,  23,  24,  23,  24,  23,  24,  /*   62 */

 23,  24,  46,  46,  46,  46,  46,  46,  /*   62 */

 86,  86,  86,  86,  86,  86,  86,  86,  /*   63 */

 87,  87,  87,  87,  87,  87,  87,  87,  /*   63 */

 86,  86,  86,  86,  86,  86,  46,  46,  /*   63 */

 87,  87,  87,  87,  87,  87,  46,  46,  /*   63 */

 86,  86,  86,  86,  86,  86,  86,  86,  /*   63 */

 87,  87,  87,  87,  87,  87,  87,  87,  /*   63 */

 86,  86,  86,  86,  86,  86,  86,  86,  /*   63 */

 87,  87,  87,  87,  87,  87,  87,  87,  /*   63 */

 86,  86,  86,  86,  86,  86,  46,  46,  /*   64 */

 87,  87,  87,  87,  87,  87,  46,  46,  /*   64 */

 16,  86,  16,  86,  16,  86,  16,  86,  /*   64 */

 46,  87,  46,  87,  46,  87,  46,  87,  /*   64 */

 86,  86,  86,  86,  86,  86,  86,  86,  /*   64 */

 87,  87,  87,  87,  87,  87,  87,  87,  /*   64 */

 88,  88,  89,  89,  89,  89,  90,  90,  /*   64 */

 91,  91,  92,  92,  93,  93,  46,  46,  /*   64 */

 86,  86,  86,  86,  86,  86,  86,  86,  /*   65 */

 87,  87,  87,  87,  87,  87,  87,  87,  /*   65 */

 86,  86,  86,  86,  86,  86,  86,  86,  /*   65 */

 87,  87,  87,  87,  87,  87,  87,  87,  /*   65 */

 86,  86,  86,  86,  86,  86,  86,  86,  /*   65 */

 87,  87,  87,  87,  87,  87,  87,  87,  /*   65 */

 86,  86,  16,  94,  16,  46,  16,  16,  /*   65 */

 87,  87,  95,  95,  96,  11,  38,  11,  /*   65 */

 11,  11,  16,  94,  16,  46,  16,  16,  /*   66 */

 97,  97,  97,  97,  96,  11,  11,  11,  /*   66 */

 86,  86,  16,  16,  46,  46,  16,  16,  /*   66 */

 87,  87,  98,  98,  46,  11,  11,  11,  /*   66 */

 86,  86,  16,  16,  16,  99,  16,  16,  /*   66 */

 87,  87, 100, 100, 101,  11,  11,  11,  /*   66 */

 46,  46,  16,  94,  16,  46,  16,  16,  /*   66 */

102, 102, 103, 103,  96,  11,  11,  46,  /*   66 */

  2,   2,   2,   2,   2,   2,   2,   2,  /*   67 */

  2,   2,   2,   2, 104, 104, 104, 104,  /*   67 */

  8,   8,   8,   8,   8,   8,   3,   3,  /*   67 */

  5,   6,   5,   5,   5,   6,   5,   5,  /*   67 */

  3,   3,   3,   3,   3,   3,   3,   3,  /*   67 */

105, 106, 104, 104, 104, 104, 104,  46,  /*   67 */

  3,   3,   3,   3,   3,   3,   3,   3,  /*   67 */

  3,   5,   6,   3,   3,   3,   3,  12,  /*   67 */

 12,   3,   3,   3,   7,   5,   6,  46,  /*   68 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   68 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   68 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   68 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   68 */

 46,  46, 104, 104, 104, 104, 104, 104,  /*   68 */

 17,  46,  46,  46,  17,  17,  17,  17,  /*   68 */

 17,  17,   7,   7,   7,   5,   6,  16,  /*   68 */

107, 107, 107, 107, 107, 107, 107, 107,  /*   69 */

107, 107,   7,   7,   7,   5,   6,  46,  /*   69 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   69 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   69 */

  4,   4,   4,   4,   4,   4,   4,   4,  /*   69 */

  4,   4,   4,   4,  46,  46,  46,  46,  /*   69 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   69 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   69 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   70 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   70 */

 60,  60,  60,  60,  60,  60,  60,  60,  /*   70 */

 60,  60,  60,  60,  60,  79,  79,  79,  /*   70 */

 79,  60,  46,  46,  46,  46,  46,  46,  /*   70 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   70 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   70 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   70 */

 15,  15,  38,  15,  15,  15,  15,  38,  /*   71 */

 15,  15,  16,  38,  38,  38,  16,  16,  /*   71 */

 38,  38,  38,  16,  15,  38,  15,  15,  /*   71 */

 38,  38,  38,  38,  38,  38,  15,  15,  /*   71 */

 15,  15,  15,  15,  38,  15,  38,  15,  /*   71 */

 38,  15,  38,  38,  38,  38,  16,  16,  /*   71 */

 38,  38,  15,  38,  16,  40,  40,  40,  /*   71 */

 40,  46,  46,  46,  46,  46,  46,  46,  /*   71 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   72 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   72 */

 46,  46,  46,  19,  19,  19,  19,  19,  /*   72 */

 19,  19,  19,  19,  19,  19,  19, 108,  /*   72 */

109, 109, 109, 109, 109, 109, 109, 109,  /*   72 */

109, 109, 109, 109, 110, 110, 110, 110,  /*   72 */

111, 111, 111, 111, 111, 111, 111, 111,  /*   72 */

111, 111, 111, 111, 112, 112, 112, 112,  /*   72 */

113, 113, 113,  46,  46,  46,  46,  46,  /*   73 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   73 */

  7,   7,   7,   7,   7,  15,  15,  15,  /*   73 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   73 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   73 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   73 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   73 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   73 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   74 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   74 */

 15,  15,   7,  15,   7,  15,  15,  15,  /*   74 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   74 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   74 */

 15,  15,  15,  46,  46,  46,  46,  46,  /*   74 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   74 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   74 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   75 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   76 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   76 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   76 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   76 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   76 */

  7,   7,   7,   7,   7,   7,   7,   7,  /*   76 */

  7,   7,  46,  46,  46,  46,  46,  46,  /*   76 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   76 */

 15,  46,  15,  15,  15,  15,  15,  15,  /*   77 */

  7,   7,   7,   7,  15,  15,  15,  15,  /*   77 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   77 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   77 */

  7,   7,  15,  15,  15,  15,  15,  15,  /*   77 */

 15,   5,   6,  15,  15,  15,  15,  15,  /*   77 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   77 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   77 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   78 */

 15,  15,  15,  46,  46,  46,  46,  46,  /*   78 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   79 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   79 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   79 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   79 */

 15,  15,  15,  15,  15,  46,  46,  46,  /*   79 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   79 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   79 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   79 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   80 */

 15,  15,  15,  46,  46,  46,  46,  46,  /*   80 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   80 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   80 */

114, 114, 114, 114, 114, 114, 114, 114,  /*   80 */

114, 114, 114, 114, 114, 114, 114, 114,  /*   80 */

114, 114, 114, 114,  82,  82,  82,  82,  /*   80 */

 82,  82,  82,  82,  82,  82,  82,  82,  /*   80 */

 82,  82,  82,  82,  82,  82,  82,  82,  /*   81 */

115, 115, 115, 115, 115, 115, 115, 115,  /*   81 */

115, 115, 115, 115, 115, 115, 115, 115,  /*   81 */

115, 115, 115, 115,  15,  15,  15,  15,  /*   81 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   81 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   81 */

 15,  15,  15,  15,  15,  15, 116, 116,  /*   81 */

116, 116, 116, 116, 116, 116, 116, 116,  /*   81 */

116, 116, 116, 116, 116, 116, 116, 116,  /*   82 */

116, 116, 116, 116, 116, 116, 116, 116,  /*   82 */

117, 117, 117, 117, 117, 117, 117, 117,  /*   82 */

117, 117, 117, 117, 117, 117, 117, 117,  /*   82 */

117, 117, 117, 117, 117, 117, 117, 117,  /*   82 */

117, 117, 118,  46,  46,  46,  46,  46,  /*   82 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   82 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   82 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   83 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   84 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   84 */

 15,  15,  15,  15,  15,  15,  46,  46,  /*   84 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   84 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   84 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   84 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   84 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   84 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   85 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   85 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   85 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   85 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   85 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   85 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   85 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   85 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   86 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   86 */

 15,  15,  15,  15,  46,  46,  46,  46,  /*   86 */

 46,  46,  15,  15,  15,  15,  15,  15,  /*   86 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   86 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   86 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   86 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   86 */

 46,  15,  15,  15,  15,  46,  15,  15,  /*   87 */

 15,  15,  46,  46,  15,  15,  15,  15,  /*   87 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   87 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   87 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   87 */

 46,  15,  15,  15,  15,  15,  15,  15,  /*   87 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   87 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   87 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   88 */

 15,  15,  15,  15,  46,  15,  46,  15,  /*   88 */

 15,  15,  15,  46,  46,  46,  15,  46,  /*   88 */

 15,  15,  15,  15,  15,  15,  15,  46,  /*   88 */

 46,  15,  15,  15,  15,  15,  15,  15,  /*   88 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   88 */

 46,  46,  46,  46,  46,  46, 119, 119,  /*   88 */

119, 119, 119, 119, 119, 119, 119, 119,  /*   88 */

114, 114, 114, 114, 114, 114, 114, 114,  /*   89 */

114, 114,  83,  83,  83,  83,  83,  83,  /*   89 */

 83,  83,  83,  83,  15,  46,  46,  46,  /*   89 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   89 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   89 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   89 */

 46,  15,  15,  15,  15,  15,  15,  15,  /*   89 */

 15,  15,  15,  15,  15,  15,  15,  46,  /*   89 */

  2,   3,   3,   3,  15,  59,   3, 120,  /*   90 */

  5,   6,   5,   6,   5,   6,   5,   6,  /*   90 */

  5,   6,  15,  15,   5,   6,   5,   6,  /*   90 */

  5,   6,   5,   6,   8,   5,   6,   5,  /*   90 */

 15, 121, 121, 121, 121, 121, 121, 121,  /*   90 */

121, 121,  60,  60,  60,  60,  60,  60,  /*   90 */

  8,  59,  59,  59,  59,  59,  15,  15,  /*   90 */

 46,  46,  46,  46,  46,  46,  46,  15,  /*   90 */

 46,  40,  40,  40,  40,  40,  40,  40,  /*   91 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   91 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   92 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   92 */

 40,  40,  40,  40,  40,  46,  46,  46,  /*   92 */

 46,  60,  60,  59,  59,  59,  59,  46,  /*   92 */

 46,  40,  40,  40,  40,  40,  40,  40,  /*   92 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   92 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   92 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   92 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   93 */

 40,  40,  40,   3,  59,  59,  59,  46,  /*   93 */

 46,  46,  46,  46,  46,  40,  40,  40,  /*   94 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   94 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   94 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   94 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   94 */

 40,  40,  40,  40,  40,  46,  46,  46,  /*   94 */

 46,  40,  40,  40,  40,  40,  40,  40,  /*   94 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   94 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*   95 */

 40,  40,  40,  40,  40,  40,  40,  46,  /*   95 */

 15,  15,  85,  85,  85,  85,  15,  15,  /*   95 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   95 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   95 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   95 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   95 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   95 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   96 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   96 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   96 */

 15,  15,  15,  15,  15,  46,  46,  46,  /*   96 */

 85,  85,  85,  85,  85,  85,  85,  85,  /*   96 */

 85,  85,  15,  15,  15,  15,  15,  15,  /*   96 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   96 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   96 */

 15,  15,  15,  15,  46,  46,  46,  46,  /*   97 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   97 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   97 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   97 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   97 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   97 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   97 */

 15,  15,  15,  15,  46,  46,  46,  15,  /*   97 */

114, 114, 114, 114, 114, 114, 114, 114,  /*   98 */

114, 114,  15,  15,  15,  15,  15,  15,  /*   98 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   98 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   98 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   98 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   98 */

 15,  46,  46,  46,  46,  46,  46,  46,  /*   98 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*   98 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   99 */

 15,  15,  15,  15,  46,  46,  46,  46,  /*   99 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   99 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   99 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   99 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   99 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*   99 */

 15,  15,  15,  15,  15,  15,  15,  46,  /*   99 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*  100 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*  100 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*  100 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*  100 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*  100 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*  100 */

 15,  15,  15,  15,  15,  15,  15,  46,  /*  100 */

 46,  46,  46,  15,  15,  15,  15,  15,  /*  100 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*  101 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*  101 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*  101 */

 15,  15,  15,  15,  15,  15,  46,  46,  /*  101 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*  101 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*  101 */

 15,  15,  15,  15,  15,  15,  15,  15,  /*  101 */

 15,  15,  15,  15,  15,  15,  15,  46,  /*  101 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  102 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  102 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  102 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  102 */

 40,  40,  40,  40,  40,  40,  46,  46,  /*  102 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  102 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  102 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  102 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  103 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  103 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  103 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  103 */

 40,  40,  40,  40,  46,  46,  46,  46,  /*  103 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  103 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  103 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  103 */

122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */

122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */

122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */

122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */

122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */

122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */

122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */

122, 122, 122, 122, 122, 122, 122, 122,  /*  104 */

123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */

123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */

123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */

123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */

123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */

123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */

123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */

123, 123, 123, 123, 123, 123, 123, 123,  /*  105 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  106 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  106 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  106 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  106 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  106 */

 40,  40,  40,  40,  40,  40,  46,  46,  /*  106 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  106 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  106 */

 16,  16,  16,  16,  16,  16,  16,  46,  /*  107 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  107 */

 46,  46,  46,  16,  16,  16,  16,  16,  /*  107 */

 46,  46,  46,  46,  46,  46,  60,  40,  /*  107 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  107 */

 40,   7,  40,  40,  40,  40,  40,  40,  /*  107 */

 40,  40,  40,  40,  40,  40,  40,  46,  /*  107 */

 40,  40,  40,  40,  40,  46,  40,  46,  /*  107 */

 40,  40,  46,  40,  40,  46,  40,  40,  /*  108 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  108 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  109 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  109 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  109 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  109 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  109 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  109 */

 40,  40,  46,  46,  46,  46,  46,  46,  /*  109 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  109 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  110 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  110 */

 46,  46,  46,  40,  40,  40,  40,  40,  /*  110 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  110 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  110 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  110 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  110 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  110 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  111 */

 40,  40,  40,  40,  40,  40,   5,   6,  /*  111 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  112 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  112 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  112 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  112 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  112 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  112 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  112 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  112 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */

 46,  46,  40,  40,  40,  40,  40,  40,  /*  113 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  113 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  114 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  114 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  114 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  114 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  114 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  114 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  114 */

 40,  40,  40,  40,  46,  46,  46,  46,  /*  114 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  115 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  115 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  115 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  115 */

 60,  60,  60,  60,  46,  46,  46,  46,  /*  115 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  115 */

  3,   8,   8,  12,  12,   5,   6,   5,  /*  115 */

  6,   5,   6,   5,   6,   5,   6,   5,  /*  115 */

  6,   5,   6,   5,   6,  46,  46,  46,  /*  116 */

 46,   3,   3,   3,   3,  12,  12,  12,  /*  116 */

  3,   3,   3,  46,   3,   3,   3,   3,  /*  116 */

  8,   5,   6,   5,   6,   5,   6,   3,  /*  116 */

  3,   3,   7,   8,   7,   7,   7,  46,  /*  116 */

  3,   4,   3,   3,  46,  46,  46,  46,  /*  116 */

 40,  40,  40,  46,  40,  46,  40,  40,  /*  116 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  116 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  117 */

 40,  40,  40,  40,  40,  46,  46, 104,  /*  117 */

 46,   3,   3,   3,   4,   3,   3,   3,  /*  118 */

  5,   6,   3,   7,   3,   8,   3,   3,  /*  118 */

  9,   9,   9,   9,   9,   9,   9,   9,  /*  118 */

  9,   9,   3,   3,   7,   7,   7,   3,  /*  118 */

  3,  10,  10,  10,  10,  10,  10,  10,  /*  118 */

 10,  10,  10,  10,  10,  10,  10,  10,  /*  118 */

 10,  10,  10,  10,  10,  10,  10,  10,  /*  118 */

 10,  10,  10,   5,   3,   6,  11,  12,  /*  118 */

 11,  13,  13,  13,  13,  13,  13,  13,  /*  119 */

 13,  13,  13,  13,  13,  13,  13,  13,  /*  119 */

 13,  13,  13,  13,  13,  13,  13,  13,  /*  119 */

 13,  13,  13,   5,   7,   6,   7,  46,  /*  119 */

 46,   3,   5,   6,   3,   3,  40,  40,  /*  119 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  119 */

 59,  40,  40,  40,  40,  40,  40,  40,  /*  119 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  119 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  120 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  120 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  120 */

 40,  40,  40,  40,  40,  40,  59,  59,  /*  120 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  120 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  120 */

 40,  40,  40,  40,  40,  40,  40,  40,  /*  120 */

 40,  40,  40,  40,  40,  40,  40,  46,  /*  120 */

 46,  46,  40,  40,  40,  40,  40,  40,  /*  121 */

 46,  46,  40,  40,  40,  40,  40,  40,  /*  121 */

 46,  46,  40,  40,  40,  40,  40,  40,  /*  121 */

 46,  46,  40,  40,  40,  46,  46,  46,  /*  121 */

  4,   4,   7,  11,  15,   4,   4,  46,  /*  121 */

  7,   7,   7,   7,   7,  15,  15,  46,  /*  121 */

 46,  46,  46,  46,  46,  46,  46,  46,  /*  121 */

 46,  46,  46,  46,  46,  15,  46,  46   /*  121 */

};



/* The A table has 124 entries for a total of 496 bytes. */



const uint32 js_A[] = {

0x0001000F,  /*    0   Cc, ignorable */

0x0004000F,  /*    1   Cc, whitespace */

0x0004000C,  /*    2   Zs, whitespace */

0x00000018,  /*    3   Po */

0x0006001A,  /*    4   Sc, currency */

0x00000015,  /*    5   Ps */

0x00000016,  /*    6   Pe */

0x00000019,  /*    7   Sm */

0x00000014,  /*    8   Pd */

0x00036009,  /*    9   Nd, identifier part, decimal 16 */

0x0827FE01,  /*   10   Lu, hasLower (add 32), identifier start, supradecimal 31 */

0x0000001B,  /*   11   Sk */

0x00050017,  /*   12   Pc, underscore */

0x0817FE02,  /*   13   Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */

0x0000000C,  /*   14   Zs */

0x0000001C,  /*   15   So */

0x00070002,  /*   16   Ll, identifier start */

0x0000600B,  /*   17   No, decimal 16 */

0x0000500B,  /*   18   No, decimal 8 */

0x0000800B,  /*   19   No, strange */

0x08270001,  /*   20   Lu, hasLower (add 32), identifier start */

0x08170002,  /*   21   Ll, hasUpper (subtract 32), identifier start */

0xE1D70002,  /*   22   Ll, hasUpper (subtract -121), identifier start */

0x00670001,  /*   23   Lu, hasLower (add 1), identifier start */

0x00570002,  /*   24   Ll, hasUpper (subtract 1), identifier start */

0xCE670001,  /*   25   Lu, hasLower (add -199), identifier start */

0x3A170002,  /*   26   Ll, hasUpper (subtract 232), identifier start */

0xE1E70001,  /*   27   Lu, hasLower (add -121), identifier start */

0x4B170002,  /*   28   Ll, hasUpper (subtract 300), identifier start */

0x34A70001,  /*   29   Lu, hasLower (add 210), identifier start */

0x33A70001,  /*   30   Lu, hasLower (add 206), identifier start */

0x33670001,  /*   31   Lu, hasLower (add 205), identifier start */

0x32A70001,  /*   32   Lu, hasLower (add 202), identifier start */

0x32E70001,  /*   33   Lu, hasLower (add 203), identifier start */

0x33E70001,  /*   34   Lu, hasLower (add 207), identifier start */

0x34E70001,  /*   35   Lu, hasLower (add 211), identifier start */

0x34670001,  /*   36   Lu, hasLower (add 209), identifier start */

0x35670001,  /*   37   Lu, hasLower (add 213), identifier start */

0x00070001,  /*   38   Lu, identifier start */

0x36A70001,  /*   39   Lu, hasLower (add 218), identifier start */

0x00070005,  /*   40   Lo, identifier start */

0x36670001,  /*   41   Lu, hasLower (add 217), identifier start */

0x36E70001,  /*   42   Lu, hasLower (add 219), identifier start */

0x00AF0001,  /*   43   Lu, hasLower (add 2), hasTitle, identifier start */

0x007F0003,  /*   44   Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */

0x009F0002,  /*   45   Ll, hasUpper (subtract 2), hasTitle, identifier start */

0x00000000,  /*   46   unassigned */

0x34970002,  /*   47   Ll, hasUpper (subtract 210), identifier start */

0x33970002,  /*   48   Ll, hasUpper (subtract 206), identifier start */

0x33570002,  /*   49   Ll, hasUpper (subtract 205), identifier start */

0x32970002,  /*   50   Ll, hasUpper (subtract 202), identifier start */

0x32D70002,  /*   51   Ll, hasUpper (subtract 203), identifier start */

0x33D70002,  /*   52   Ll, hasUpper (subtract 207), identifier start */

0x34570002,  /*   53   Ll, hasUpper (subtract 209), identifier start */

0x34D70002,  /*   54   Ll, hasUpper (subtract 211), identifier start */

0x35570002,  /*   55   Ll, hasUpper (subtract 213), identifier start */

0x36970002,  /*   56   Ll, hasUpper (subtract 218), identifier start */

0x36570002,  /*   57   Ll, hasUpper (subtract 217), identifier start */

0x36D70002,  /*   58   Ll, hasUpper (subtract 219), identifier start */

0x00070004,  /*   59   Lm, identifier start */

0x00030006,  /*   60   Mn, identifier part */

0x09A70001,  /*   61   Lu, hasLower (add 38), identifier start */

0x09670001,  /*   62   Lu, hasLower (add 37), identifier start */

0x10270001,  /*   63   Lu, hasLower (add 64), identifier start */

0x0FE70001,  /*   64   Lu, hasLower (add 63), identifier start */

0x09970002,  /*   65   Ll, hasUpper (subtract 38), identifier start */

0x09570002,  /*   66   Ll, hasUpper (subtract 37), identifier start */

0x10170002,  /*   67   Ll, hasUpper (subtract 64), identifier start */

0x0FD70002,  /*   68   Ll, hasUpper (subtract 63), identifier start */

0x0F970002,  /*   69   Ll, hasUpper (subtract 62), identifier start */

0x0E570002,  /*   70   Ll, hasUpper (subtract 57), identifier start */

0x0BD70002,  /*   71   Ll, hasUpper (subtract 47), identifier start */

0x0D970002,  /*   72   Ll, hasUpper (subtract 54), identifier start */

0x15970002,  /*   73   Ll, hasUpper (subtract 86), identifier start */

0x14170002,  /*   74   Ll, hasUpper (subtract 80), identifier start */

0x14270001,  /*   75   Lu, hasLower (add 80), identifier start */

0x0C270001,  /*   76   Lu, hasLower (add 48), identifier start */

0x0C170002,  /*   77   Ll, hasUpper (subtract 48), identifier start */

0x00034009,  /*   78   Nd, identifier part, decimal 0 */

0x00000007,  /*   79   Me */

0x00030008,  /*   80   Mc, identifier part */

0x00037409,  /*   81   Nd, identifier part, decimal 26 */

0x00005A0B,  /*   82   No, decimal 13 */

0x00006E0B,  /*   83   No, decimal 23 */

0x0000740B,  /*   84   No, decimal 26 */

0x0000000B,  /*   85   No */

0xFE170002,  /*   86   Ll, hasUpper (subtract -8), identifier start */

0xFE270001,  /*   87   Lu, hasLower (add -8), identifier start */

0xED970002,  /*   88   Ll, hasUpper (subtract -74), identifier start */

0xEA970002,  /*   89   Ll, hasUpper (subtract -86), identifier start */

0xE7170002,  /*   90   Ll, hasUpper (subtract -100), identifier start */

0xE0170002,  /*   91   Ll, hasUpper (subtract -128), identifier start */

0xE4170002,  /*   92   Ll, hasUpper (subtract -112), identifier start */

0xE0970002,  /*   93   Ll, hasUpper (subtract -126), identifier start */

0xFDD70002,  /*   94   Ll, hasUpper (subtract -9), identifier start */

0xEDA70001,  /*   95   Lu, hasLower (add -74), identifier start */

0xFDE70001,  /*   96   Lu, hasLower (add -9), identifier start */

0xEAA70001,  /*   97   Lu, hasLower (add -86), identifier start */

0xE7270001,  /*   98   Lu, hasLower (add -100), identifier start */

0xFE570002,  /*   99   Ll, hasUpper (subtract -7), identifier start */

0xE4270001,  /*  100   Lu, hasLower (add -112), identifier start */

0xFE670001,  /*  101   Lu, hasLower (add -7), identifier start */

0xE0270001,  /*  102   Lu, hasLower (add -128), identifier start */

0xE0A70001,  /*  103   Lu, hasLower (add -126), identifier start */

0x00010010,  /*  104   Cf, ignorable */

0x0004000D,  /*  105   Zl, whitespace */

0x0004000E,  /*  106   Zp, whitespace */

0x0000400B,  /*  107   No, decimal 0 */

0x0000440B,  /*  108   No, decimal 2 */

0x0427420A,  /*  109   Nl, hasLower (add 16), identifier start, decimal 1 */

0x0427800A,  /*  110   Nl, hasLower (add 16), identifier start, strange */

0x0417620A,  /*  111   Nl, hasUpper (subtract 16), identifier start, decimal 17 */

0x0417800A,  /*  112   Nl, hasUpper (subtract 16), identifier start, strange */

0x0007800A,  /*  113   Nl, identifier start, strange */

0x0000420B,  /*  114   No, decimal 1 */

0x0000720B,  /*  115   No, decimal 25 */

0x06A0001C,  /*  116   So, hasLower (add 26) */

0x0690001C,  /*  117   So, hasUpper (subtract 26) */

0x00006C0B,  /*  118   No, decimal 22 */

0x0000560B,  /*  119   No, decimal 11 */

0x0007720A,  /*  120   Nl, identifier start, decimal 25 */

0x0007400A,  /*  121   Nl, identifier start, decimal 0 */

0x00000013,  /*  122   Cs */

0x00000012   /*  123   Co */

};



const jschar js_uriReservedPlusPound_ucstr[] =

    {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0};

const jschar js_uriUnescaped_ucstr[] =

    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',

     'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',

     'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',

     'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',

     '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0};



#define URI_CHUNK_LENGTH 64U



/* Concatenate jschars onto an unshared/newborn JSString. */

static JSBool

AddCharsToURI(JSContext *cx, JSString *str, const jschar *chars, size_t length)

{

    size_t total;



    total = str->length + length + 1;

    if (!str->chars ||

        JS_ROUNDUP(total, URI_CHUNK_LENGTH) > JS_ROUNDUP((str->length + 1), URI_CHUNK_LENGTH)) {

        total = JS_ROUNDUP(total, URI_CHUNK_LENGTH);

        str->chars = JS_realloc(cx, str->chars, total * sizeof(jschar));

        if (!str->chars)

            return JS_FALSE;

    }

    js_strncpy(str->chars + str->length, chars, length);

    str->length += length;

    str->chars[str->length] = 0;

    return JS_TRUE;

}



/*

 * ECMA 3, 15.1.3 URI Handling Function Properties

 *

 * The following are implementations of the algorithms

 * given in the ECMA specification for the hidden functions

 * 'Encode' and 'Decode'.

 */

static JSBool

Encode(JSContext *cx, JSString *str, const jschar *unescapedSet,

       const jschar *unescapedSet2, jsval *rval)

{

    size_t length, j, k, L;

    jschar *chars, C, C2;

    uint32 V;

    uint8 utf8buf[6];

    jschar hexBuf[4];

    static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */

    JSString *R;



    R = js_NewString(cx, NULL, 0, 0);

    if (!R)

        return JS_FALSE;



    hexBuf[0] = '%';

    hexBuf[3] = 0;

    chars = str->chars;

    length = str->length;

    for (k = 0; k < length; k++) {

        C = chars[k];

        if (js_strchr(unescapedSet, C) ||

            (unescapedSet2 && js_strchr(unescapedSet2, C))) {

            if (!AddCharsToURI(cx, R, &C, 1))

                return JS_FALSE;

        } else {

            if ((C >= 0xDC00) && (C <= 0xDFFF)) {

                JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                 JSMSG_BAD_URI, NULL);

                return JS_FALSE;

            }

            if (C < 0xD800 || C > 0xDBFF) {

                V = C;

            } else {

                k++;

                if (k == length) {

                    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                     JSMSG_BAD_URI, NULL);

                    return JS_FALSE;

                }

                C2 = chars[k];

                if ((C2 < 0xDC00) || (C2 > 0xDFFF)) {

                    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,

                                     JSMSG_BAD_URI, NULL);

                    return JS_FALSE;

                }

                V = ((C - 0xD800) << 10) + (C2 - 0xDC00) + 0x10000;

            }

            L = OneUcs4ToUtf8Char(utf8buf, V);

            for (j = 0; j < L; j++) {

                hexBuf[1] = HexDigits[utf8buf[j] >> 4];

                hexBuf[2] = HexDigits[utf8buf[j] & 0xf];

                if (!AddCharsToURI(cx, R, hexBuf, 3))

                    return JS_FALSE;

            }

        }

    }



    /* Shrinking reallocs can't fail, can they? */

    (void) JS_realloc(cx, R->chars, (R->length + 1) * sizeof(jschar));

    *rval = STRING_TO_JSVAL(R);

    return JS_TRUE;

}



static JSBool

Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval)

{

    size_t length, start, k;

    jschar *chars, C, H;

    uint32 V;

    jsuint B;

    uint8 octets[6];

    JSString *R;

    intN j, n;



    R = js_NewString(cx, NULL, 0, 0);

    if (!R)

        return JS_FALSE;



    chars = str->chars;

    length = str->length;

    for (k = 0; k < length; k++) {

        C = chars[k];

        if (C == '%') {

            start = k;

            if ((k + 2) >= length)

                goto bad;

            if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))

                goto bad;

            B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);

            k += 2;

            if (!(B & 0x80)) {

                C = (jschar)B;

            } else {

                n = 1;

                while (B & (0x80 >> n))

                    n++;

                if (n == 1 || n > 6)

                    goto bad;

                octets[0] = (uint8)B;

                if (k + 3 * (n - 1) >= length)

                    goto bad;

                for (j = 1; j < n; j++) {

                    k++;

                    if (chars[k] != '%')

                        goto bad;

                    if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2]))

                        goto bad;

                    B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]);

                    if ((B & 0xC0) != 0x80)

                        goto bad;

                    k += 2;

                    octets[j] = (char)B;

                }

                V = Utf8ToOneUcs4Char(octets, n);

                if (V >= 0x10000) {

                    V -= 0x10000;

                    if (V > 0xFFFFF)

                        goto bad;

                    C = (jschar)((V & 0x3FF) + 0xDC00);

                    H = (jschar)((V >> 10) + 0xD800);

                    if (!AddCharsToURI(cx, R, &H, 1))

                        return JS_FALSE;

                } else {

                    C = (jschar)V;

                }

            }

            if (js_strchr(reservedSet, C)) {

                if (!AddCharsToURI(cx, R, &chars[start], (k - start + 1)))

                    return JS_FALSE;

            } else {

                if (!AddCharsToURI(cx, R, &C, 1))

                    return JS_FALSE;

            }

        } else {

            if (!AddCharsToURI(cx, R, &C, 1))

                return JS_FALSE;

        }

    }



    /* Shrinking reallocs can't fail, can they? */

    (void) JS_realloc(cx, R->chars, (R->length + 1) * sizeof(jschar));

    *rval = STRING_TO_JSVAL(R);

    return JS_TRUE;



bad:

    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI);

    return JS_FALSE;

}



static JSBool

str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

              jsval *rval)

{

    JSString *str;



    str = js_ValueToString(cx, argv[0]);

    if (!str)

        return JS_FALSE;

    return Decode(cx, str, js_uriReservedPlusPound_ucstr, rval);

}



static JSBool

str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                        jsval *rval)

{

    JSString *str;



    str = js_ValueToString(cx, argv[0]);

    if (!str)

        return JS_FALSE;

    return Decode(cx, str, js_empty_ucstr, rval);

}



static JSBool

str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

              jsval *rval)

{

    JSString *str;



    str = js_ValueToString(cx, argv[0]);

    if (!str)

        return JS_FALSE;

    return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr,

                  rval);

}



static JSBool

str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,

                        jsval *rval)

{

    JSString *str;



    str = js_ValueToString(cx, argv[0]);

    if (!str)

        return JS_FALSE;

    return Encode(cx, str, js_uriUnescaped_ucstr, NULL, rval);

}



/*

 * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at

 * least 6 bytes long.  Return the number of UTF-8 bytes of data written.

 */

static int

OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char)

{

    int utf8Length = 1;



    JS_ASSERT(ucs4Char <= 0x7FFFFFFF);

    if (ucs4Char < 0x80) {

        *utf8Buffer = (uint8)ucs4Char;

    } else {

        int i;

        uint32 a = ucs4Char >> 11;

        utf8Length = 2;

        while (a) {

            a >>= 5;

            utf8Length++;

        }

        i = utf8Length;

        while (--i) {

            utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80);

            ucs4Char >>= 6;

        }

        *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char);

    }

    return utf8Length;

}



/*

 * Convert a utf8 character sequence into a UCS-4 character and return that

 * character.  It is assumed that the caller already checked that the sequence

 * is valid.

 */

static uint32

Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length)

{

    uint32 ucs4Char;



    JS_ASSERT(utf8Length >= 1 && utf8Length <= 6);

    if (utf8Length == 1) {

        ucs4Char = *utf8Buffer;

        JS_ASSERT(!(ucs4Char & 0x80));

    } else {

        JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) ==

                  (0x100 - (1 << (8-utf8Length))));

        ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1);

        while (--utf8Length) {

            JS_ASSERT((*utf8Buffer & 0xC0) == 0x80);

            ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F);

        }

    }

    return ucs4Char;

}

 

**** End of jsstr.c ****

 

**** Start of jsstr.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsstr_h___

#define jsstr_h___

/*

 * JS string type implementation.

 *

 * A JS string is a counted array of unicode characters.  To support handoff

 * of API client memory, the chars are allocated separately from the length,

 * necessitating a pointer after the count, to form a string header.  String

 * headers are GC'ed while string bytes are allocated from the malloc heap.

 *

 * When a string is treated as an object (by following it with . or []), the

 * runtime wraps it with a JSObject whose valueOf method returns the unwrapped

 * string header.

 */

#include <ctype.h>

#include "jspubtd.h"

#include "jsprvtd.h"

#include "jshash.h"



JS_BEGIN_EXTERN_C



struct JSString {

    size_t          length;

    jschar          *chars;

};



struct JSSubString {

    size_t          length;

    const jschar    *chars;

};



extern jschar      js_empty_ucstr[];

extern JSSubString js_EmptySubString;



/* Unicode character attribute lookup tables. */

extern const uint8 js_X[];

extern const uint8 js_Y[];

extern const uint32 js_A[];



/* Enumerated Unicode general category types. */

typedef enum JSCharType {

    JSCT_UNASSIGNED             = 0,

    JSCT_UPPERCASE_LETTER       = 1,

    JSCT_LOWERCASE_LETTER       = 2,

    JSCT_TITLECASE_LETTER       = 3,

    JSCT_MODIFIER_LETTER        = 4,

    JSCT_OTHER_LETTER           = 5,

    JSCT_NON_SPACING_MARK       = 6,

    JSCT_ENCLOSING_MARK         = 7,

    JSCT_COMBINING_SPACING_MARK = 8,

    JSCT_DECIMAL_DIGIT_NUMBER   = 9,

    JSCT_LETTER_NUMBER          = 10,

    JSCT_OTHER_NUMBER           = 11,

    JSCT_SPACE_SEPARATOR        = 12,

    JSCT_LINE_SEPARATOR         = 13,

    JSCT_PARAGRAPH_SEPARATOR    = 14,

    JSCT_CONTROL                = 15,

    JSCT_FORMAT                 = 16,

    JSCT_PRIVATE_USE            = 18,

    JSCT_SURROGATE              = 19,

    JSCT_DASH_PUNCTUATION       = 20,

    JSCT_START_PUNCTUATION      = 21,

    JSCT_END_PUNCTUATION        = 22,

    JSCT_CONNECTOR_PUNCTUATION  = 23,

    JSCT_OTHER_PUNCTUATION      = 24,

    JSCT_MATH_SYMBOL            = 25,

    JSCT_CURRENCY_SYMBOL        = 26,

    JSCT_MODIFIER_SYMBOL        = 27,

    JSCT_OTHER_SYMBOL           = 28

} JSCharType;



/* Character classifying and mapping macros, based on java.lang.Character. */

#define JS_CCODE(c)     (js_A[js_Y[(js_X[(uint16)(c)>>6]<<6)|((c)&0x3F)]])

#define JS_CTYPE(c)     (JS_CCODE(c) & 0x1F)



#define JS_ISALPHA(c)   ((((1 << JSCT_UPPERCASE_LETTER) |                     \

			   (1 << JSCT_LOWERCASE_LETTER) |                     \

			   (1 << JSCT_TITLECASE_LETTER) |                     \

			   (1 << JSCT_MODIFIER_LETTER) |                      \

			   (1 << JSCT_OTHER_LETTER))                          \

			  >> JS_CTYPE(c)) & 1)



#define JS_ISALNUM(c)   ((((1 << JSCT_UPPERCASE_LETTER) |                     \

			   (1 << JSCT_LOWERCASE_LETTER) |                     \

			   (1 << JSCT_TITLECASE_LETTER) |                     \

			   (1 << JSCT_MODIFIER_LETTER) |                      \

			   (1 << JSCT_OTHER_LETTER) |                         \

			   (1 << JSCT_DECIMAL_DIGIT_NUMBER))                  \

			  >> JS_CTYPE(c)) & 1)



/* A unicode letter, suitable for use in an identifier. */

#define JS_ISUC_LETTER(c)   ((((1 << JSCT_UPPERCASE_LETTER) |                 \

			   (1 << JSCT_LOWERCASE_LETTER) |                     \

			   (1 << JSCT_TITLECASE_LETTER) |                     \

			   (1 << JSCT_MODIFIER_LETTER) |                      \

			   (1 << JSCT_OTHER_LETTER) |                         \

			   (1 << JSCT_LETTER_NUMBER))                         \

			  >> JS_CTYPE(c)) & 1)



/*

* 'IdentifierPart' from ECMA grammar, is Unicode letter or

* combining mark or digit or connector punctuation.

*/

#define JS_ISID_PART(c) ((((1 << JSCT_UPPERCASE_LETTER) |                     \

			   (1 << JSCT_LOWERCASE_LETTER) |                     \

			   (1 << JSCT_TITLECASE_LETTER) |                     \

			   (1 << JSCT_MODIFIER_LETTER) |                      \

			   (1 << JSCT_OTHER_LETTER) |                         \

			   (1 << JSCT_LETTER_NUMBER) |                        \

			   (1 << JSCT_NON_SPACING_MARK) |                     \

			   (1 << JSCT_COMBINING_SPACING_MARK) |               \

			   (1 << JSCT_DECIMAL_DIGIT_NUMBER) |                 \

			   (1 << JSCT_CONNECTOR_PUNCTUATION))                 \

			  >> JS_CTYPE(c)) & 1)



/* Unicode control-format characters, ignored in input */

#define JS_ISFORMAT(c) (((1 << JSCT_FORMAT) >> JS_CTYPE(c)) & 1)



#define JS_ISWORD(c)    (JS_ISALNUM(c) || (c) == '_')



/* XXXbe unify on A/X/Y tbls, avoid ctype.h? */

#define JS_ISIDENT_START(c) (JS_ISUC_LETTER(c) || (c) == '_' || (c) == '$')

#define JS_ISIDENT(c)       (JS_ISID_PART(c) || (c) == '_' || (c) == '$')



#define JS_ISDIGIT(c)   (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER)



/* XXXbe fs, etc. ? */

#define JS_ISSPACE(c)   ((JS_CCODE(c) & 0x00070000) == 0x00040000)

#define JS_ISPRINT(c)   ((c) < 128 && isprint(c))



#define JS_ISUPPER(c)   (JS_CTYPE(c) == JSCT_UPPERCASE_LETTER)

#define JS_ISLOWER(c)   (JS_CTYPE(c) == JSCT_LOWERCASE_LETTER)



#define JS_TOUPPER(c)   ((JS_CCODE(c) & 0x00100000) ? (c) - ((int32)JS_CCODE(c) >> 22) : (c))

#define JS_TOLOWER(c)   ((JS_CCODE(c) & 0x00200000) ? (c) + ((int32)JS_CCODE(c) >> 22) : (c))



#define JS_TOCTRL(c)    ((c) ^ 64)      /* XXX unsafe! requires uppercase c */



/* Shorthands for ASCII (7-bit) decimal and hex conversion. */

#define JS7_ISDEC(c)    ((c) < 128 && isdigit(c))

#define JS7_UNDEC(c)    ((c) - '0')

#define JS7_ISHEX(c)    ((c) < 128 && isxdigit(c))

#define JS7_UNHEX(c)    (uintN)(isdigit(c) ? (c) - '0' : 10 + tolower(c) - 'a')

#define JS7_ISLET(c)    ((c) < 128 && isalpha(c))



/* Initialize truly global state associated with JS strings. */

extern JSBool

js_InitStringGlobals(void);



extern void

js_FreeStringGlobals(void);



/* Initialize per-runtime string state for the first context in the runtime. */

extern JSBool

js_InitRuntimeStringState(JSContext *cx);



extern void

js_FinishRuntimeStringState(JSContext *cx);



/* Initialize the String class, returning its prototype object. */

extern JSObject *

js_InitStringClass(JSContext *cx, JSObject *obj);



extern const char js_escape_str[];

extern const char js_unescape_str[];

extern const char js_uneval_str[];

extern const char js_decodeURI_str[];

extern const char js_encodeURI_str[];

extern const char js_decodeURIComponent_str[];

extern const char js_encodeURIComponent_str[];



/* GC-allocate a string descriptor for the given malloc-allocated chars. */

extern JSString *

js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag);



/* Copy a counted string and GC-allocate a descriptor for it. */

extern JSString *

js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag);



/* Copy a C string and GC-allocate a descriptor for it. */

extern JSString *

js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag);



/* Free the chars held by str when it is finalized by the GC. */

extern void

js_FinalizeString(JSContext *cx, JSString *str);



extern void

js_FinalizeStringRT(JSRuntime *rt, JSString *str);



/* Wrap a string value in a String object. */

extern JSObject *

js_StringToObject(JSContext *cx, JSString *str);



/*

 * Convert a value to a string, returning null after reporting an error,

 * otherwise returning a new string reference.

 */

extern JSString *

js_ValueToString(JSContext *cx, jsval v);



/*

 * Convert a value to its source expression, returning null after reporting

 * an error, otherwise returning a new string reference.

 */

extern JSString *

js_ValueToSource(JSContext *cx, jsval v);



#ifdef HT_ENUMERATE_NEXT	/* XXX don't require jshash.h */

/*

 * Compute a hash function from str.

 */

extern JSHashNumber

js_HashString(const JSString *str);

#endif



/*

 * Return less than, equal to, or greater than zero depending on whether

 * str1 is less than, equal to, or greater than str2.

 */

extern intN

js_CompareStrings(const JSString *str1, const JSString *str2);



/*

 * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen.

 * The patlen argument must be positive and no greater than BMH_PATLEN_MAX.

 * The start argument tells where in text to begin the search.

 *

 * Return the index of pat in text, or -1 if not found.

 */

#define BMH_CHARSET_SIZE 256    /* ISO-Latin-1 */

#define BMH_PATLEN_MAX   255    /* skip table element is uint8 */



#define BMH_BAD_PATTERN  (-2)   /* return value if pat is not ISO-Latin-1 */



extern jsint

js_BoyerMooreHorspool(const jschar *text, jsint textlen,

                      const jschar *pat, jsint patlen,

                      jsint start);



extern size_t

js_strlen(const jschar *s);



extern jschar *

js_strchr(const jschar *s, jschar c);



extern jschar *

js_strncpy(jschar *t, const jschar *s, size_t n);



/*

 * Return s advanced past any Unicode white space characters.

 */

extern const jschar *

js_SkipWhiteSpace(const jschar *s);



/*

 * Inflate bytes to JS chars and vice versa.  Report out of memory via cx

 * and return null on error, otherwise return the jschar or byte vector that

 * was JS_malloc'ed.

 */

extern jschar *

js_InflateString(JSContext *cx, const char *bytes, size_t length);



extern char *

js_DeflateString(JSContext *cx, const jschar *chars, size_t length);



/*

 * Inflate bytes to JS chars into a buffer.

 * 'chars' must be large enough for 'length'+1 jschars.

 */

extern void

js_InflateStringToBuffer(jschar *chars, const char *bytes, size_t length);



/*

 * Associate bytes with str in the deflated string cache, returning true on

 * successful association, false on out of memory.

 */

extern JSBool

js_SetStringBytes(JSString *str, char *bytes, size_t length);



/*

 * Find or create a deflated string cache entry for str that contains its

 * characters chopped from Unicode code points into bytes.

 */

extern char *

js_GetStringBytes(JSString *str);



JSBool

str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);



JS_END_EXTERN_C



#endif /* jsstr_h___ */

 

**** End of jsstr.h ****

 

**** Start of jstypes.h ****

 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

/*

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 *

 * This Original Code has been modified by IBM Corporation.

 * Modifications made by IBM described herein are

 * Copyright (c) International Business Machines

 * Corporation, 2000

 *

 * Modifications to Mozilla code or documentation

 * identified per MPL Section 3.3

 *

 * Date             Modified by     Description of modification

 * 04/20/2000       IBM Corp.      OS/2 VisualAge build.

 */



/*

** File:                jstypes.h

** Description: Definitions of NSPR's basic types

**

** Prototypes and macros used to make up for deficiencies in ANSI environments

** that we have found.

**

** Since we do not wrap <stdlib.h> and all the other standard headers, authors

** of portable code will not know in general that they need these definitions.

** Instead of requiring these authors to find the dependent uses in their code

** and take the following steps only in those C files, we take steps once here

** for all C files.

**/





// DREAMWEAVER snewman 3/16/01: tweaked this file to remove all

// references to "_declspec", to match the changes we had made in the

// JS 1.2 interpreter.  Using _declspec doesn't make sense, because we

// aren't building a separate DLL -- the interpreter is linked directly

// into Dreamweaver.





#ifndef jstypes_h___

#define jstypes_h___



#include <stddef.h>



// DREAMWEAVER

#if WIN32

#define XP_PC 1

#elif __powerc

#define XP_MAC 1

#endif



/***********************************************************************

** MACROS:      JS_EXTERN_API

**              JS_EXPORT_API

** DESCRIPTION:

**      These are only for externally visible routines and globals.  For

**      internal routines, just use "extern" for type checking and that

**      will not export internal cross-file or forward-declared symbols.

**      Define a macro for declaring procedures return types. We use this to

**      deal with windoze specific type hackery for DLL definitions. Use

**      JS_EXTERN_API when the prototype for the method is declared. Use

**      JS_EXPORT_API for the implementation of the method.

**

** Example:

**   in dowhim.h

**     JS_EXTERN_API( void ) DoWhatIMean( void );

**   in dowhim.c

**     JS_EXPORT_API( void ) DoWhatIMean( void ) { return; }

**

**

***********************************************************************/

#ifdef WIN32

#define JS_EXTERN_API(__type) extern _declspec(dllexport) __type

#define JS_EXPORT_API(__type) _declspec(dllexport) __type

#define JS_EXTERN_DATA(__type) extern _declspec(dllexport) __type

#define JS_EXPORT_DATA(__type) _declspec(dllexport) __type



#define JS_DLL_CALLBACK

#define JS_STATIC_DLL_CALLBACK(__x) static __x



#elif defined(WIN16)



#ifdef _WINDLL

#define JS_EXTERN_API(__type) extern __type _cdecl _export _loadds

#define JS_EXPORT_API(__type) __type _cdecl _export _loadds

#define JS_EXTERN_DATA(__type) extern __type _export

#define JS_EXPORT_DATA(__type) __type _export



#define JS_DLL_CALLBACK             __cdecl __loadds

#define JS_STATIC_DLL_CALLBACK(__x) static __x CALLBACK



#else /* this must be .EXE */

#define JS_EXTERN_API(__type) extern __type _cdecl _export

#define JS_EXPORT_API(__type) __type _cdecl _export

#define JS_EXTERN_DATA(__type) extern __type _export

#define JS_EXPORT_DATA(__type) __type _export



#define JS_DLL_CALLBACK             __cdecl __loadds

#define JS_STATIC_DLL_CALLBACK(__x) __x JS_DLL_CALLBACK

#endif /* _WINDLL */



#elif defined(XP_MAC)

#define JS_EXTERN_API(__type) extern __type

#define JS_EXPORT_API(__type) __type

#define JS_EXTERN_DATA(__type) extern __type

#define JS_EXPORT_DATA(__type) __type



#define JS_DLL_CALLBACK

#define JS_STATIC_DLL_CALLBACK(__x) static __x



#elif defined(XP_OS2_VACPP) 

#define JS_EXTERN_API(__type) extern __type

#define JS_EXPORT_API(__type) __type

#define JS_EXTERN_DATA(__type) extern __type

#define JS_EXPORT_DATA(__type) __type

#define JS_DLL_CALLBACK  _Optlink

#define JS_STATIC_DLL_CALLBACK(__x) static __x JS_DLL_CALLBACK



#else /* Unix */



#define JS_EXTERN_API(__type) extern __type

#define JS_EXPORT_API(__type) __type

#define JS_EXTERN_DATA(__type) extern __type

#define JS_EXPORT_DATA(__type) __type



#define JS_DLL_CALLBACK

#define JS_STATIC_DLL_CALLBACK(__x) static __x



#endif



#ifdef _WIN32

#  ifdef __MWERKS__

#    define JS_IMPORT_API(__x)      __x

#  else

#    define JS_IMPORT_API(__x)      __x

#  endif

#else

#    define JS_IMPORT_API(__x)      JS_EXPORT_API (__x)

#endif



#if defined(_WIN32) && !defined(__MWERKS__)

#    define JS_IMPORT_DATA(__x)     __x

#else

#    define JS_IMPORT_DATA(__x)     __x

#endif



/*

 * The linkage of JS API functions differs depending on whether the file is

 * used within the JS library or not.  Any source file within the JS

 * interpreter should define EXPORT_JS_API whereas any client of the library

 * should not.

 */

#ifdef EXPORT_JS_API

#define JS_PUBLIC_API(t)    JS_EXPORT_API(t)

#define JS_PUBLIC_DATA(t)   JS_EXPORT_DATA(t)

#else

#define JS_PUBLIC_API(t)    JS_IMPORT_API(t)

#define JS_PUBLIC_DATA(t)   JS_IMPORT_DATA(t)

#endif



#define JS_FRIEND_API(t)    JS_PUBLIC_API(t)

#define JS_FRIEND_DATA(t)   JS_PUBLIC_DATA(t)



#ifdef _WIN32

#   define JS_INLINE __inline

#elif defined(__GNUC__)

#   define JS_INLINE

#else

#   define JS_INLINE

#endif



/***********************************************************************

** MACROS:      JS_BEGIN_MACRO

**              JS_END_MACRO

** DESCRIPTION:

**      Macro body brackets so that macros with compound statement definitions

**      behave syntactically more like functions when called.

***********************************************************************/

#define JS_BEGIN_MACRO  do {

#define JS_END_MACRO    } while (0)



/***********************************************************************

** MACROS:      JS_BEGIN_EXTERN_C

**              JS_END_EXTERN_C

** DESCRIPTION:

**      Macro shorthands for conditional C++ extern block delimiters.

***********************************************************************/

#ifdef __cplusplus

#define JS_BEGIN_EXTERN_C       extern "C" {

#define JS_END_EXTERN_C         }

#else

#define JS_BEGIN_EXTERN_C

#define JS_END_EXTERN_C

#endif



/***********************************************************************

** MACROS:      JS_BIT

**              JS_BITMASK

** DESCRIPTION:

** Bit masking macros.  XXX n must be <= 31 to be portable

***********************************************************************/

#define JS_BIT(n)       ((JSUint32)1 << (n))

#define JS_BITMASK(n)   (JS_BIT(n) - 1)



/***********************************************************************

** MACROS:      JS_ROUNDUP

**              JS_MIN

**                              JS_MAX

** DESCRIPTION:

**      Commonly used macros for operations on compatible types.

***********************************************************************/

#define JS_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y))

#define JS_MIN(x,y)     ((x)<(y)?(x):(y))

#define JS_MAX(x,y)     ((x)>(y)?(x):(y))



#if (defined(XP_MAC) || (defined(XP_PC) && !defined(XP_OS2))) && !defined(CROSS_COMPILE)

#    include "jscpucfg.h"        /* Use standard Mac or Windows configuration */

#elif defined(XP_UNIX) || defined(XP_BEOS) || defined(XP_OS2) || defined(CROSS_COMPILE)

#    include "jsautocfg.h"       /* Use auto-detected configuration */

#    include "jsosdep.h"         /* ...and platform-specific flags */

#else

#    error "Must define one of XP_PC, XP_MAC or XP_UNIX"

#endif



JS_BEGIN_EXTERN_C



/************************************************************************

** TYPES:       JSUint8

**              JSInt8

** DESCRIPTION:

**  The int8 types are known to be 8 bits each. There is no type that

**      is equivalent to a plain "char". 

************************************************************************/

#if JS_BYTES_PER_BYTE == 1

typedef unsigned char JSUint8;

typedef signed char JSInt8;

#else

#error No suitable type for JSInt8/JSUint8

#endif



/************************************************************************

** TYPES:       JSUint16

**              JSInt16

** DESCRIPTION:

**  The int16 types are known to be 16 bits each. 

************************************************************************/

#if JS_BYTES_PER_SHORT == 2

typedef unsigned short JSUint16;

typedef short JSInt16;

#else

#error No suitable type for JSInt16/JSUint16

#endif



/************************************************************************

** TYPES:       JSUint32

**              JSInt32

** DESCRIPTION:

**  The int32 types are known to be 32 bits each. 

************************************************************************/

#if JS_BYTES_PER_INT == 4

typedef unsigned int JSUint32;

typedef int JSInt32;

#define JS_INT32(x)  x

#define JS_UINT32(x) x ## U

#elif JS_BYTES_PER_LONG == 4

typedef unsigned long JSUint32;

typedef long JSInt32;

#define JS_INT32(x)  x ## L

#define JS_UINT32(x) x ## UL

#else

#error No suitable type for JSInt32/JSUint32

#endif



/************************************************************************

** TYPES:       JSUint64

**              JSInt64

** DESCRIPTION:

**  The int64 types are known to be 64 bits each. Care must be used when

**      declaring variables of type JSUint64 or JSInt64. Different hardware

**      architectures and even different compilers have varying support for

**      64 bit values. The only guaranteed portability requires the use of

**      the JSLL_ macros (see jslong.h).

************************************************************************/

#ifdef JS_HAVE_LONG_LONG

#if JS_BYTES_PER_LONG == 8

typedef long JSInt64;

typedef unsigned long JSUint64;

#elif defined(WIN16)

typedef __int64 JSInt64;

typedef unsigned __int64 JSUint64;

#elif defined(WIN32)

typedef __int64  JSInt64;

typedef unsigned __int64 JSUint64;

#else

typedef long long JSInt64;

typedef unsigned long long JSUint64;

#endif /* JS_BYTES_PER_LONG == 8 */

#else  /* !JS_HAVE_LONG_LONG */

typedef struct {

#ifdef IS_LITTLE_ENDIAN

    JSUint32 lo, hi;

#else

    JSUint32 hi, lo;

#endif

} JSInt64;

typedef JSInt64 JSUint64;

#endif /* !JS_HAVE_LONG_LONG */



/************************************************************************

** TYPES:       JSUintn

**              JSIntn

** DESCRIPTION:

**  The JSIntn types are most appropriate for automatic variables. They are

**      guaranteed to be at least 16 bits, though various architectures may

**      define them to be wider (e.g., 32 or even 64 bits). These types are

**      never valid for fields of a structure. 

************************************************************************/

#if JS_BYTES_PER_INT >= 2

typedef int JSIntn;

typedef unsigned int JSUintn;

#else

#error 'sizeof(int)' not sufficient for platform use

#endif



/************************************************************************

** TYPES:       JSFloat64

** DESCRIPTION:

**  NSPR's floating point type is always 64 bits. 

************************************************************************/

typedef double          JSFloat64;



/************************************************************************

** TYPES:       JSSize

** DESCRIPTION:

**  A type for representing the size of objects. 

************************************************************************/

typedef size_t JSSize;



/************************************************************************

** TYPES:       JSPtrDiff

** DESCRIPTION:

**  A type for pointer difference. Variables of this type are suitable

**      for storing a pointer or pointer sutraction. 

************************************************************************/

typedef ptrdiff_t JSPtrdiff;



/************************************************************************

** TYPES:       JSUptrdiff

** DESCRIPTION:

**  A type for pointer difference. Variables of this type are suitable

**      for storing a pointer or pointer sutraction. 

************************************************************************/

typedef unsigned long JSUptrdiff;



/************************************************************************

** TYPES:       JSBool

** DESCRIPTION:

**  Use JSBool for variables and parameter types. Use JS_FALSE and JS_TRUE

**      for clarity of target type in assignments and actual arguments. Use

**      'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans

**      juast as you would C int-valued conditions. 

************************************************************************/

typedef JSIntn JSBool;

#define JS_TRUE (JSIntn)1

#define JS_FALSE (JSIntn)0



/************************************************************************

** TYPES:       JSPackedBool

** DESCRIPTION:

**  Use PRPackedBOol within structs where bitfields are not desireable

**      but minimum and consistant overhead matters.

************************************************************************/

typedef JSUint8 JSPackedBool;



/*

** A JSWord is an integer that is the same size as a void*

*/

typedef long JSWord;

typedef unsigned long JSUword;



#include "jsotypes.h"



JS_END_EXTERN_C



#endif /* jstypes_h___ */



 

**** End of jstypes.h ****

 

**** Start of jsutil.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 *

 * This Original Code has been modified by IBM Corporation. Modifications made by IBM

 * described herein are Copyright (c) International Business Machines Corporation, 2000.

 * Modifications to Mozilla code or documentation identified per MPL Section 3.3

 *

 * Date        Modified by     Description of modification

 * 04/10/2000  IBM Corp.       Added DebugBreak() definitions for OS/2

 */



/*

 * PR assertion checker.

 */

#include "jsstddef.h"

#include <stdio.h>

#include <stdlib.h>

#include "jstypes.h"

#include "jsutil.h"



#ifdef WIN32

#    include <windows.h>

#endif



#ifdef XP_MAC

#	 include <Carbon/Carbon.h>

#    include <stdarg.h>

#	 include "jsprf.h"

#endif



#if defined(XP_OS2) && defined(DEBUG)

/* Added definitions for DebugBreak() for 2 different OS/2 compilers.  Doing

 * the int3 on purpose for Visual Age so that a developer can step over the

 * instruction if so desired.  Not always possible if trapping due to exception

 * handling IBM-AKR

 */

#if defined(XP_OS2_VACPP)

   #include <builtin.h>

   #define DebugBreak() { _interrupt(3); }

#elif defined(XP_OS2_EMX)

   /* Force a trap */

   #define DebugBreak() { int *pTrap=NULL; *pTrap = 1; }

#else

   #define DebugBreak()

#endif



#elif defined(XP_OS2)

   #define DebugBreak()

#endif /* XP_OS2 && DEBUG */



#ifdef XP_MAC

/*

 * PStrFromCStr converts the source C string to a destination

 * pascal string as it copies. The dest string will

 * be truncated to fit into an Str255 if necessary.

 * If the C String pointer is NULL, the pascal string's length is

 * set to zero.

 */

static void PStrFromCStr(const char* src, Str255 dst)

{

	short 	length  = 0;



	/* handle case of overlapping strings */

	if ( (void*)src == (void*)dst )

	{

		unsigned char*		curdst = &dst[1];

		unsigned char		thisChar;



		thisChar = *(const unsigned char*)src++;

		while ( thisChar != '\0' )

		{

			unsigned char	nextChar;



			/*

                         * Use nextChar so we don't overwrite what we

                         * are about to read

                         */

			nextChar = *(const unsigned char*)src++;

			*curdst++ = thisChar;

			thisChar = nextChar;



			if ( ++length >= 255 )

				break;

		}

	}

	else if ( src != NULL )

	{

		unsigned char*		curdst = &dst[1];

		/* count down so test it loop is faster */

		short 				overflow = 255;

		register char		temp;



		/*

                 * Can't do the K&R C thing of while (*s++ = *t++)

                 * because it will copy trailing zero which might

                 * overrun pascal buffer.  Instead we use a temp variable.

                 */

		while ( (temp = *src++) != 0 )

		{

			*(char*)curdst++ = temp;



			if ( --overflow <= 0 )

				break;

		}

		length = 255 - overflow;

	}

	dst[0] = length;

}



static void jsdebugstr(const char *debuggerMsg)

{

	Str255		pStr;



	PStrFromCStr(debuggerMsg, pStr);

	DebugStr(pStr);

}



static void dprintf(const char *format, ...)

{

    va_list ap;

	char	*buffer;



	va_start(ap, format);

	buffer = (char *)JS_vsmprintf(format, ap);

	va_end(ap);



	jsdebugstr(buffer);

	JS_DELETE(buffer);

}

#endif   /* XP_MAC */



// DREAMWEAVER snewman 3/28/01: wrapped JS_Assert in #ifdef DEBUG

// to avoid compiler warning (no prototype) when DEBUG is turned off.

#ifdef DEBUG



JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln)

{

#if defined(XP_UNIX) || defined(XP_OS2)

    fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln);

#endif

#ifdef XP_MAC

    dprintf("Assertion failure: %s, at %s:%d\n", s, file, ln);

#endif

#if defined(WIN32) || defined(XP_OS2)

    DebugBreak();

#endif

#ifndef XP_MAC

    abort();

#endif

}



#endif 

**** End of jsutil.c ****

 

**** Start of jsutil.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



/*

 * PR assertion checker.

 */



#ifndef jsutil_h___

#define jsutil_h___



JS_BEGIN_EXTERN_C



/***********************************************************************

** FUNCTION:	JS_MALLOC()

** DESCRIPTION:

**   JS_NEW() allocates an untyped item of size _size from the heap.

** INPUTS:  _size: size in bytes of item to be allocated

** OUTPUTS:	untyped pointer to the node allocated

** RETURN:	pointer to node or error returned from malloc().

***********************************************************************/

#define JS_MALLOC(_bytes) (malloc((_bytes)))



/***********************************************************************

** FUNCTION:	JS_DELETE()

** DESCRIPTION:

**   JS_DELETE() unallocates an object previosly allocated via JS_NEW()

**   or JS_NEWZAP() to the heap.

** INPUTS:	pointer to previously allocated object

** OUTPUTS:	the referenced object is returned to the heap

** RETURN:	void

***********************************************************************/

#define JS_DELETE(_ptr) { free(_ptr); (_ptr) = NULL; }



/***********************************************************************

** FUNCTION:	JS_NEW()

** DESCRIPTION:

**   JS_NEW() allocates an item of type _struct from the heap.

** INPUTS:  _struct: a data type

** OUTPUTS:	pointer to _struct

** RETURN:	pointer to _struct or error returns from malloc().

***********************************************************************/

#define JS_NEW(_struct) ((_struct *) JS_MALLOC(sizeof(_struct)))



#ifdef DEBUG



extern JS_PUBLIC_API(void)

JS_Assert(const char *s, const char *file, JSIntn ln);

#define JS_ASSERT(_expr) \

    ((_expr)?((void)0):JS_Assert(# _expr,__FILE__,__LINE__))



#define JS_NOT_REACHED(_reasonStr) \

    JS_Assert(_reasonStr,__FILE__,__LINE__)



#else



#define JS_ASSERT(expr) ((void) 0)

#define JS_NOT_REACHED(reasonStr)



#endif /* defined(DEBUG) */



/*

** Abort the process in a non-graceful manner. This will cause a core file,

** call to the debugger or other moral equivalent as well as causing the

** entire process to stop.

*/

extern JS_PUBLIC_API(void) JS_Abort(void);



JS_END_EXTERN_C



#endif /* jsutil_h___ */

 

**** End of jsutil.h ****

 

**** Start of jsxdrapi.c ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */

#include "jsstddef.h"



#include <string.h>

#include "jstypes.h"

#include "jsutil.h" /* Added by JSIFY */

#include "jsprf.h"

#include "jsapi.h"

#include "jscntxt.h"

#include "jsobj.h"		/* js_XDRObject */

#include "jsstr.h"

#include "jsxdrapi.h"



#ifdef DEBUG

#define DBG(x) x

#else

#define DBG(x) ((void)0)

#endif



typedef struct JSXDRMemState {

    JSXDRState  state;

    uint32      count;

    uint32      limit;

} JSXDRMemState;



#define MEM_BLOCK       8192

#define MEM_PRIV(xdr)   ((JSXDRMemState *)(xdr))



#define MEM_COUNT(xdr)  (MEM_PRIV(xdr)->count)

#define MEM_LIMIT(xdr)  (MEM_PRIV(xdr)->limit)





#define MEM_LEFT(xdr, bytes)                                                  \

    JS_BEGIN_MACRO                                                            \

	if ((xdr)->mode == JSXDR_DECODE &&                                    \

	    MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) {                        \

	    JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL,         \

				 JSMSG_END_OF_DATA);                          \

	    return 0;                                                         \

	}                                                                     \

    JS_END_MACRO



/* XXXbe why does NEED even allow or cope with non-ENCODE mode? */

#define MEM_NEED(xdr, bytes)                                                  \

    JS_BEGIN_MACRO                                                            \

	if ((xdr)->mode == JSXDR_ENCODE) {                                    \

            uint32 _new_limit = JS_ROUNDUP(MEM_COUNT(xdr) + bytes, MEM_BLOCK);\

	    if (MEM_LIMIT(xdr) &&                                             \

		MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) {                    \

		void *_data = JS_realloc((xdr)->cx,                           \

					 (xdr)->data,                         \

					 _new_limit);                         \

		if (!_data)                                                   \

		    return 0;                                                 \

		(xdr)->data = _data;                                          \

		MEM_LIMIT(xdr) = _new_limit;                                  \

	    }                                                                 \

	} else {                                                              \

	    if (MEM_LIMIT(xdr) < MEM_COUNT(xdr) + bytes) {                    \

		JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL,     \

				     JSMSG_END_OF_DATA);                      \

		return 0;                                                     \

	    }                                                                 \

	}                                                                     \

    JS_END_MACRO



#define MEM_DATA(xdr)        ((void *)((char *)(xdr)->data + MEM_COUNT(xdr)))

#define MEM_INCR(xdr,bytes)  (MEM_COUNT(xdr) += (bytes))



static JSBool

mem_get32(JSXDRState *xdr, uint32 *lp)

{

    MEM_LEFT(xdr, 4);

    *lp = *(uint32 *)MEM_DATA(xdr);

    MEM_INCR(xdr, 4);

    return JS_TRUE;

}



static JSBool

mem_set32(JSXDRState *xdr, uint32 *lp)

{

    MEM_NEED(xdr, 4);

    *(uint32 *)MEM_DATA(xdr) = *lp;

    MEM_INCR(xdr, 4);

    return JS_TRUE;

}



static JSBool

mem_getbytes(JSXDRState *xdr, char **bytesp, uint32 len)

{

    MEM_LEFT(xdr, len);

    memcpy(*bytesp, MEM_DATA(xdr), len);

    MEM_INCR(xdr, len);

    return JS_TRUE;

}



static JSBool

mem_setbytes(JSXDRState *xdr, char **bytesp, uint32 len)

{

    MEM_NEED(xdr, len);

    memcpy(MEM_DATA(xdr), *bytesp, len);

    MEM_INCR(xdr, len);

    return JS_TRUE;

}



static void *

mem_raw(JSXDRState *xdr, uint32 len)

{

    void *data;

    if (xdr->mode == JSXDR_ENCODE) {

	MEM_NEED(xdr, len);

    } else if (xdr->mode == JSXDR_DECODE) {

	MEM_LEFT(xdr, len);

    }

    data = MEM_DATA(xdr);

    MEM_INCR(xdr, len);

    return data;

}



static JSBool

mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence)

{

    switch (whence) {

      case JSXDR_SEEK_CUR:

	if ((int32)MEM_COUNT(xdr) + offset < 0) {

	    JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,

				 JSMSG_SEEK_BEYOND_START);

	    return JS_FALSE;

	}

	if (offset > 0)

	    MEM_NEED(xdr, offset);

	MEM_COUNT(xdr) += offset;

	return JS_TRUE;

      case JSXDR_SEEK_SET:

	if (offset < 0) {

	    JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,

				 JSMSG_SEEK_BEYOND_START);

	    return JS_FALSE;

	}

	if (xdr->mode == JSXDR_ENCODE) {

	    if ((uint32)offset > MEM_COUNT(xdr))

		MEM_NEED(xdr, offset - MEM_COUNT(xdr));

	    MEM_COUNT(xdr) = offset;

	} else {

	    if ((uint32)offset > MEM_LIMIT(xdr)) {

		JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,

				     JSMSG_SEEK_BEYOND_END);

		return JS_FALSE;

	    }

	    MEM_COUNT(xdr) = offset;

	}

	return JS_TRUE;

      case JSXDR_SEEK_END:

	if (offset >= 0 ||

	    xdr->mode == JSXDR_ENCODE ||

	    (int32)MEM_LIMIT(xdr) + offset < 0) {

	    JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,

				 JSMSG_END_SEEK);

	    return JS_FALSE;

	}

	MEM_COUNT(xdr) = MEM_LIMIT(xdr) + offset;

	return JS_TRUE;

      default: {

	char numBuf[12];

	JS_snprintf(numBuf, sizeof numBuf, "%d", whence);

	JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,

			     JSMSG_WHITHER_WHENCE, numBuf);

	return JS_FALSE;

      }

    }

}



static uint32

mem_tell(JSXDRState *xdr)

{

    return MEM_COUNT(xdr);

}



static void

mem_finalize(JSXDRState *xdr)

{

    JSContext *cx = xdr->cx;

    JS_free(cx, xdr->data);

}



static JSXDROps xdrmem_ops = {

    mem_get32,      mem_set32,      mem_getbytes,   mem_setbytes,

    mem_raw,        mem_seek,       mem_tell,       mem_finalize

};



JS_PUBLIC_API(void)

JS_XDRNewBase(JSContext *cx, JSXDRState *xdr, JSXDRMode mode)

{

    xdr->cx = cx;

    xdr->mode = mode;

    xdr->registry = NULL;

    xdr->nclasses = 0;

}



JS_PUBLIC_API(JSXDRState *)

JS_XDRNewMem(JSContext *cx, JSXDRMode mode)

{

    JSXDRState *xdr = (JSXDRState *) JS_malloc(cx, sizeof(JSXDRMemState));

    if (!xdr)

	return NULL;

    JS_XDRNewBase(cx, xdr, mode);

    if (mode == JSXDR_ENCODE) {

	if (!(xdr->data = JS_malloc(cx, MEM_BLOCK))) {

	    JS_free(cx, xdr);

	    return NULL;

	}

    } else {

	/* XXXbe ok, so better not deref xdr->data if not ENCODE */

	xdr->data = NULL;

    }

    xdr->ops = &xdrmem_ops;

    MEM_PRIV(xdr)->count = 0;

    MEM_PRIV(xdr)->limit = MEM_BLOCK;

    return xdr;

}



JS_PUBLIC_API(void *)

JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp)

{

    if (xdr->ops != &xdrmem_ops)

	return NULL;

    *lp = MEM_PRIV(xdr)->count;

    return xdr->data;

}



JS_PUBLIC_API(void)

JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len)

{

    if (xdr->ops != &xdrmem_ops)

	return;

    MEM_PRIV(xdr)->limit = len;

    xdr->data = data;

    MEM_PRIV(xdr)->count = 0;

}



JS_PUBLIC_API(JSBool)

JS_XDRUint8(JSXDRState *xdr, uint8 *b)

{

    uint32 l = *b;

    if (!JS_XDRUint32(xdr, &l))

	return JS_FALSE;

    *b = (uint8) l & 0xff;

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_XDRUint16(JSXDRState *xdr, uint16 *s)

{

    uint32 l = *s;

    if (!JS_XDRUint32(xdr, &l))

	return JS_FALSE;

    *s = (uint16) l & 0xffff;

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_XDRUint32(JSXDRState *xdr, uint32 *lp)

{

    JSBool ok = JS_FALSE;

    if (xdr->mode == JSXDR_ENCODE) {

	uint32 xl = JSXDR_SWAB32(*lp);

	ok = xdr->ops->set32(xdr, &xl);

    } else if (xdr->mode == JSXDR_DECODE) {

	ok = xdr->ops->get32(xdr, lp);

	*lp = JSXDR_SWAB32(*lp);

    }

    return ok;

}



JS_PUBLIC_API(JSBool)

JS_XDRBytes(JSXDRState *xdr, char **bytesp, uint32 len)

{

    if (xdr->mode == JSXDR_ENCODE) {

	if (!xdr->ops->setbytes(xdr, bytesp, len))

	    return JS_FALSE;

    } else {

	if (!xdr->ops->getbytes(xdr, bytesp, len))

	    return JS_FALSE;

    }

    len = xdr->ops->tell(xdr);

    if (len % JSXDR_ALIGN) {

	if (!xdr->ops->seek(xdr, JSXDR_ALIGN - (len % JSXDR_ALIGN),

			    JSXDR_SEEK_CUR)) {

	    return JS_FALSE;

	}

    }

    return JS_TRUE;

}



/**

 * Convert between a C string and the XDR representation:

 * leading 32-bit count, then counted vector of chars,

 * then possibly \0 padding to multiple of 4.

 */

JS_PUBLIC_API(JSBool)

JS_XDRCString(JSXDRState *xdr, char **sp)

{

    uint32 len;



    if (xdr->mode == JSXDR_ENCODE)

	len = strlen(*sp);

    JS_XDRUint32(xdr, &len);

    if (xdr->mode == JSXDR_DECODE) {

	if (!(*sp = (char *) JS_malloc(xdr->cx, len + 1)))

	    return JS_FALSE;

    }

    if (!JS_XDRBytes(xdr, sp, len)) {

	if (xdr->mode == JSXDR_DECODE)

	    JS_free(xdr->cx, *sp);

	return JS_FALSE;

    }

    if (xdr->mode == JSXDR_DECODE) {

	(*sp)[len] = '\0';

    } else if (xdr->mode == JSXDR_FREE) {

	JS_free(xdr->cx, *sp);

	*sp = NULL;

    }

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_XDRCStringOrNull(JSXDRState *xdr, char **sp)

{

    uint32 null = (*sp == NULL);

    if (!JS_XDRUint32(xdr, &null))

	return JS_FALSE;

    if (null) {

	*sp = NULL;

	return JS_TRUE;

    }

    return JS_XDRCString(xdr, sp);

}



/*

 * Convert between a JS (Unicode) string and the XDR representation.

 */

JS_PUBLIC_API(JSBool)

JS_XDRString(JSXDRState *xdr, JSString **strp)

{

    uint32 i, len, nbytes;

    jschar *chars = NULL, *raw;



    if (xdr->mode == JSXDR_ENCODE)

	len = (*strp)->length;

    if (!JS_XDRUint32(xdr, &len))

	return JS_FALSE;

    nbytes = len * sizeof(jschar);



    if (xdr->mode == JSXDR_ENCODE) {

	chars = (*strp)->chars;

    } else if (xdr->mode == JSXDR_DECODE) {

	if (!(chars = (jschar *) JS_malloc(xdr->cx, nbytes + sizeof(jschar))))

	    return JS_FALSE;

    }



    if (nbytes % JSXDR_ALIGN)

	nbytes += JSXDR_ALIGN - (nbytes % JSXDR_ALIGN);

    if (!(raw = (jschar *) xdr->ops->raw(xdr, nbytes)))

	goto bad;

    if (xdr->mode == JSXDR_ENCODE) {

	for (i = 0; i < len; i++)

	    raw[i] = JSXDR_SWAB16(chars[i]);

    } else if (xdr->mode == JSXDR_DECODE) {

	for (i = 0; i < len; i++)

	    chars[i] = JSXDR_SWAB16(raw[i]);

        chars[len] = 0;



	if (!(*strp = JS_NewUCString(xdr->cx, chars, len)))

	    goto bad;

    }

    return JS_TRUE;



bad:

    if (xdr->mode == JSXDR_DECODE)

	JS_free(xdr->cx, chars);

    return JS_FALSE;

}



JS_PUBLIC_API(JSBool)

JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp)

{

    uint32 null = (*strp == NULL);

    if (!JS_XDRUint32(xdr, &null))

	return JS_FALSE;

    if (null) {

	*strp = NULL;

	return JS_TRUE;

    }

    return JS_XDRString(xdr, strp);

}



JS_PUBLIC_API(JSBool)

JS_XDRDouble(JSXDRState *xdr, jsdouble **dp)

{

    jsdouble d;

    if (xdr->mode == JSXDR_ENCODE)

	d = **dp;

#if IS_BIG_ENDIAN

    if (!JS_XDRUint32(xdr, (uint32 *)&d + 1) ||

	!JS_XDRUint32(xdr, (uint32 *)&d))

#else /* !IS_BIG_ENDIAN */

    if (!JS_XDRUint32(xdr, (uint32 *)&d) ||

	!JS_XDRUint32(xdr, (uint32 *)&d + 1))

#endif /* IS_BIG_ENDIAN */

	return JS_FALSE;

    if (xdr->mode == JSXDR_DECODE) {

	*dp = JS_NewDouble(xdr->cx, d);

	if (!*dp)

	    return JS_FALSE;

    }

    return JS_TRUE;

}



JS_PUBLIC_API(JSBool)

JS_XDRValue(JSXDRState *xdr, jsval *vp)

{

    uint32 type = JSVAL_TAG(*vp);

    if (!JS_XDRUint32(xdr, &type))

	return JS_FALSE;



    switch (type) {

      case JSVAL_STRING: {

	JSString *str = JSVAL_TO_STRING(*vp);

	if (!JS_XDRString(xdr, &str))

	    return JS_FALSE;

	if (xdr->mode == JSXDR_DECODE)

	    *vp = STRING_TO_JSVAL(str);

	break;

      }

      case JSVAL_DOUBLE: {

	jsdouble *dp;

	if (xdr->mode == JSXDR_ENCODE)

	    dp = JSVAL_TO_DOUBLE(*vp);

	if (!JS_XDRDouble(xdr, &dp))

	    return JS_FALSE;

	if (xdr->mode == JSXDR_DECODE)

	    *vp = DOUBLE_TO_JSVAL(dp);

	break;

      }

      case JSVAL_OBJECT: {

	JSObject *obj;

	if (xdr->mode == JSXDR_ENCODE)

	    obj = JSVAL_TO_OBJECT(*vp);

	if (!js_XDRObject(xdr, &obj))

	    return JS_FALSE;

	if (xdr->mode == JSXDR_DECODE)

	    *vp = OBJECT_TO_JSVAL(obj);

	break;

      }

      case JSVAL_BOOLEAN: {

	uint32 b;

	if (xdr->mode == JSXDR_ENCODE)

	    b = (uint32)JSVAL_TO_BOOLEAN(*vp);

	if (!JS_XDRUint32(xdr, &b))

	    return JS_FALSE;

	if (xdr->mode == JSXDR_DECODE)

	    *vp = BOOLEAN_TO_JSVAL((JSBool)b);

	break;

      }

      case JSVAL_VOID:

	if (!JS_XDRUint32(xdr, (uint32 *)vp))

	    return JS_FALSE;

	break;

      default: {

	char numBuf[12];

	if (type & JSVAL_INT) {

	    uint32 i;

	    if (xdr->mode == JSXDR_ENCODE)

		i = JSVAL_TO_INT(*vp);

	    if (!JS_XDRUint32(xdr, &i))

		return JS_FALSE;

	    if (xdr->mode == JSXDR_DECODE)

		*vp = INT_TO_JSVAL(i);

	    break;

	}

	JS_snprintf(numBuf, sizeof numBuf, "%#lx", type);

	JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,

			     JSMSG_BAD_JVAL_TYPE, type);

	return JS_FALSE;

      }

    }

    return JS_TRUE;

}





JS_PUBLIC_API(void)

JS_XDRDestroy(JSXDRState *xdr)

{

    JSContext *cx = xdr->cx;

    xdr->ops->finalize(xdr);

    if (xdr->registry)

	JS_free(cx, xdr->registry);

    JS_free(cx, xdr);

}



#define REGISTRY_CHUNK 4



JS_PUBLIC_API(JSBool)

JS_RegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *idp)

{

    uintN nclasses;

    JSClass **registry;



    nclasses = xdr->nclasses;

    if (nclasses == 0) {

	registry = (JSClass **)

            JS_malloc(xdr->cx, REGISTRY_CHUNK * sizeof(JSClass *));

    } else if (nclasses % REGISTRY_CHUNK == 0) {

	registry = (JSClass **)

            JS_realloc(xdr->cx,

                       xdr->registry,

                       (nclasses + REGISTRY_CHUNK) * sizeof(JSClass *));

    } else {

        registry = xdr->registry;

    }

    if (!registry)

	return JS_FALSE;

    registry[nclasses++] = clasp;

    xdr->registry = registry;

    xdr->nclasses = nclasses;

    *idp = nclasses;

    return JS_TRUE;

}



JS_PUBLIC_API(uint32)

JS_FindClassIdByName(JSXDRState *xdr, const char *name)

{

    uintN i;



    for (i = 0; i < xdr->nclasses; i++) {

	if (!strcmp(name, xdr->registry[i]->name))

	    return i+1;

    }

    return 0;

}



JS_PUBLIC_API(JSClass *)

JS_FindClassById(JSXDRState *xdr, uint32 id)

{

    if (id > xdr->nclasses)

	return NULL;

    return xdr->registry[id-1];

}

 

**** End of jsxdrapi.c ****

 

**** Start of jsxdrapi.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef jsxdrapi_h___

#define jsxdrapi_h___



/*

 * JS external data representation interface API.

 *

 * The XDR system is comprised of three major parts:

 *

 * - the state serialization/deserialization APIs, which allow consumers

 *   of the API to serialize JS runtime state (script bytecodes, atom maps,

 *   object graphs, etc.) for later restoration.  These portions

 *   are implemented in various appropriate files, such as jsscript.c

 *   for the script portions and jsobj.c for object state.

 * - the callback APIs through which the runtime requests an opaque

 *   representation of a native object, and through which the runtime

 *   constructs a live native object from an opaque representation. These

 *   portions are the responsibility of the native object implementor.

 * - utility functions for en/decoding of primitive types, such as

 *   JSStrings.  This portion is implemented in jsxdrapi.c.

 *

 * Spiritually guided by Sun's XDR, where appropriate.

 */



#include "jspubtd.h"

#include "jsprvtd.h"



JS_BEGIN_EXTERN_C



/* We use little-endian byteorder for all encoded data */



#if defined IS_LITTLE_ENDIAN

#define JSXDR_SWAB32(x) x

#define JSXDR_SWAB16(x) x

#elif defined IS_BIG_ENDIAN

#define JSXDR_SWAB32(x) (((x) >> 24) |                                        \

			 (((x) >> 8) & 0xff00) |                              \

			 (((x) << 8) & 0xff0000) |                            \

			 ((x) << 24))

#define JSXDR_SWAB16(x) (((x) >> 8) | ((x) << 8))

#else

#error "unknown byte order"

#endif



#define JSXDR_ALIGN     4



typedef enum JSXDRMode {

    JSXDR_ENCODE,

    JSXDR_DECODE,

    JSXDR_FREE

} JSXDRMode;



typedef enum JSXDRWhence {

    JSXDR_SEEK_SET,

    JSXDR_SEEK_CUR,

    JSXDR_SEEK_END

} JSXDRWhence;



typedef struct JSXDROps {

    JSBool      (*get32)(JSXDRState *, uint32 *);

    JSBool      (*set32)(JSXDRState *, uint32 *);

    JSBool      (*getbytes)(JSXDRState *, char **, uint32);

    JSBool      (*setbytes)(JSXDRState *, char **, uint32);

    void *      (*raw)(JSXDRState *, uint32);

    JSBool      (*seek)(JSXDRState *, int32, JSXDRWhence);

    uint32      (*tell)(JSXDRState *);

    void        (*finalize)(JSXDRState *);

} JSXDROps;



struct JSXDRState {

    JSXDRMode   mode;

    JSXDROps    *ops;

    JSContext   *cx;

    JSClass     **registry;

    uintN       nclasses;

    void        *data;

};



extern JS_PUBLIC_API(void)

JS_XDRNewBase(JSContext *cx, JSXDRState *xdr, JSXDRMode mode);



extern JS_PUBLIC_API(JSXDRState *)

JS_XDRNewMem(JSContext *cx, JSXDRMode mode);



extern JS_PUBLIC_API(void *)

JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp);



extern JS_PUBLIC_API(void)

JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len);



extern JS_PUBLIC_API(void)

JS_XDRDestroy(JSXDRState *xdr);



extern JS_PUBLIC_API(JSBool)

JS_XDRUint8(JSXDRState *xdr, uint8 *b);



extern JS_PUBLIC_API(JSBool)

JS_XDRUint16(JSXDRState *xdr, uint16 *s);



extern JS_PUBLIC_API(JSBool)

JS_XDRUint32(JSXDRState *xdr, uint32 *lp);



extern JS_PUBLIC_API(JSBool)

JS_XDRBytes(JSXDRState *xdr, char **bytes, uint32 len);



extern JS_PUBLIC_API(JSBool)

JS_XDRCString(JSXDRState *xdr, char **sp);



extern JS_PUBLIC_API(JSBool)

JS_XDRCStringOrNull(JSXDRState *xdr, char **sp);



extern JS_PUBLIC_API(JSBool)

JS_XDRString(JSXDRState *xdr, JSString **strp);



extern JS_PUBLIC_API(JSBool)

JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp);



extern JS_PUBLIC_API(JSBool)

JS_XDRDouble(JSXDRState *xdr, jsdouble **dp);



extern JS_PUBLIC_API(JSBool)

JS_XDRValue(JSXDRState *xdr, jsval *vp);



extern JS_PUBLIC_API(JSBool)

JS_RegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *lp);



extern JS_PUBLIC_API(uint32)

JS_FindClassIdByName(JSXDRState *xdr, const char *name);



extern JS_PUBLIC_API(JSClass *)

JS_FindClassById(JSXDRState *xdr, uint32 id);



/* Magic values */



#define JSXDR_MAGIC_SCRIPT_1        0xdead0001

#define JSXDR_MAGIC_SCRIPT_2        0xdead0002

#define JSXDR_MAGIC_SCRIPT_CURRENT  JSXDR_MAGIC_SCRIPT_2



JS_END_EXTERN_C



#endif /* ! jsxdrapi_h___ */

 

**** End of jsxdrapi.h ****

 

**** Start of prmjtime.c ****

 

/* -*- 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 ***** */



/*

 * PR time code.

 */

#include "jsstddef.h"

#ifdef SOLARIS

#define _REENTRANT 1

#endif

#include <string.h>

#include <time.h>

#include "jstypes.h"

#include "jsutil.h"



#include "jsprf.h"

#include "prmjtime.h"



#define PRMJ_DO_MILLISECONDS 1



#ifdef XP_OS2

#include <sys/timeb.h>

#endif

#ifdef WIN32

#    include <windows.h>

#endif



#if defined(XP_UNIX) || defined(XP_BEOS)



#ifdef _SVID_GETTOD   /* Defined only on Solaris, see Solaris <sys/types.h> */

extern int gettimeofday(struct timeval *tv);

#endif



#include <sys/time.h>



#endif /* XP_UNIX */



#ifdef XP_MAC

static uint64 			 dstLocalBaseMicroseconds;

static unsigned long	 gJanuaryFirst1970Seconds;



static void MacintoshInitializeTime(void)

{

    uint64					upTime;

    unsigned long			currentLocalTimeSeconds,

	   startupTimeSeconds;

    uint64				startupTimeMicroSeconds;

    uint32				upTimeSeconds;

    uint64				oneMillion, upTimeSecondsLong, microSecondsToSeconds;

    DateTimeRec				firstSecondOfUnixTime;



    /*

     * Figure out in local time what time the machine started up. This information can be added to

     * upTime to figure out the current local time as well as GMT.

     */



    Microseconds((UnsignedWide*)&upTime);



    GetDateTime(&currentLocalTimeSeconds);



    JSLL_I2L(microSecondsToSeconds, PRMJ_USEC_PER_SEC);

    JSLL_DIV(upTimeSecondsLong, upTime, microSecondsToSeconds);

    JSLL_L2I(upTimeSeconds, upTimeSecondsLong);



    startupTimeSeconds = currentLocalTimeSeconds - upTimeSeconds;



    /*  Make sure that we normalize the macintosh base seconds to the unix base of January 1, 1970.

     */



    firstSecondOfUnixTime.year = 1970;

    firstSecondOfUnixTime.month = 1;

    firstSecondOfUnixTime.day = 1;

    firstSecondOfUnixTime.hour = 0;

    firstSecondOfUnixTime.minute = 0;

    firstSecondOfUnixTime.second = 0;

    firstSecondOfUnixTime.dayOfWeek = 0;



    DateToSeconds(&firstSecondOfUnixTime, &gJanuaryFirst1970Seconds);



    startupTimeSeconds -= gJanuaryFirst1970Seconds;



    /*  Now convert the startup time into a wide so that we can figure out GMT and DST.

     */



    JSLL_I2L(startupTimeMicroSeconds, startupTimeSeconds);

    JSLL_I2L(oneMillion, PRMJ_USEC_PER_SEC);

    JSLL_MUL(dstLocalBaseMicroseconds, oneMillion, startupTimeMicroSeconds);

}



static SleepQRec  gSleepQEntry = { NULL, sleepQType, NULL, 0 };

static JSBool     gSleepQEntryInstalled = JS_FALSE;



static pascal long MySleepQProc(long message, SleepQRecPtr sleepQ)

{

    /* just woke up from sleeping, so must recompute dstLocalBaseMicroseconds. */

    if (message == kSleepWakeUp)

        MacintoshInitializeTime();

    return 0;

}



/* Because serial port and SLIP conflict with ReadXPram calls,

 * we cache the call here

 */



static void MyReadLocation(MachineLocation * loc)

{

    static MachineLocation storedLoc;	/* InsideMac, OSUtilities, page 4-20 */

    static JSBool didReadLocation = JS_FALSE;

    if (!didReadLocation)

    {

        MacintoshInitializeTime();

        ReadLocation(&storedLoc);

        /* install a sleep queue routine, so that when the machine wakes up, time can be recomputed. */

        if (&SleepQInstall != (void*)kUnresolvedCFragSymbolAddress

#if !TARGET_CARBON

            && NGetTrapAddress(0xA28A, OSTrap) != NGetTrapAddress(_Unimplemented, ToolTrap)

#endif

           ) {

            if ((gSleepQEntry.sleepQProc = NewSleepQUPP(MySleepQProc)) != NULL) {

                SleepQInstall(&gSleepQEntry);

                gSleepQEntryInstalled = JS_TRUE;

            }

        }

        didReadLocation = JS_TRUE;

     }

     *loc = storedLoc;

}





#ifndef XP_MACOSX



/* CFM library init and terminate routines. We'll use the terminate routine

   to clean up the sleep Q entry. On Mach-O, the sleep Q entry gets cleaned

   up for us, so nothing to do there.

*/



extern pascal OSErr __NSInitialize(const CFragInitBlock* initBlock);

extern pascal void __NSTerminate();



pascal OSErr __JSInitialize(const CFragInitBlock* initBlock);

pascal void __JSTerminate(void);



pascal OSErr __JSInitialize(const CFragInitBlock* initBlock)

{

	return __NSInitialize(initBlock);

}



pascal void __JSTerminate()

{

  /* clean up the sleepQ entry */

  if (gSleepQEntryInstalled)

    SleepQRemove(&gSleepQEntry);

  

	__NSTerminate();

}

#endif /* XP_MACOSX */



#endif /* XP_MAC */



#define IS_LEAP(year) \

   (year != 0 && ((((year & 0x3) == 0) &&  \

		   ((year - ((year/100) * 100)) != 0)) || \

		  (year - ((year/400) * 400)) == 0))



#define PRMJ_HOUR_SECONDS  3600L

#define PRMJ_DAY_SECONDS  (24L * PRMJ_HOUR_SECONDS)

#define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * 365L)

#define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */

/* function prototypes */

static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm);

/*

 * get the difference in seconds between this time zone and UTC (GMT)

 */

JSInt32

PRMJ_LocalGMTDifference()

{

#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)

    struct tm ltime;



    /* get the difference between this time zone and GMT */

    memset((char *)&ltime,0,sizeof(ltime));

    ltime.tm_mday = 2;

    ltime.tm_year = 70;

#ifdef SUNOS4

    ltime.tm_zone = 0;

    ltime.tm_gmtoff = 0;

    return timelocal(&ltime) - (24 * 3600);

#else

    return mktime(&ltime) - (24L * 3600L);

#endif

#endif

#if defined(XP_MAC)

    static JSInt32   zone = -1L;

    MachineLocation  machineLocation;

    JSInt32 	     gmtOffsetSeconds;



    /* difference has been set no need to recalculate */

    if (zone != -1)

        return zone;



    /* Get the information about the local machine, including

     * its GMT offset and its daylight savings time info.

     * Convert each into wides that we can add to

     * startupTimeMicroSeconds.

     */



    MyReadLocation(&machineLocation);



    /* Mask off top eight bits of gmtDelta, sign extend lower three. */

    gmtOffsetSeconds = (machineLocation.u.gmtDelta << 8);

    gmtOffsetSeconds >>= 8;



    /* Backout OS adjustment for DST, to give consistent GMT offset. */

    if (machineLocation.u.dlsDelta != 0)

        gmtOffsetSeconds -= PRMJ_HOUR_SECONDS;

    return (zone = -gmtOffsetSeconds);

#endif

}



/* Constants for GMT offset from 1970 */

#define G1970GMTMICROHI        0x00dcdcad /* micro secs to 1970 hi */

#define G1970GMTMICROLOW       0x8b3fa000 /* micro secs to 1970 low */



#define G2037GMTMICROHI        0x00e45fab /* micro secs to 2037 high */

#define G2037GMTMICROLOW       0x7a238000 /* micro secs to 2037 low */



/* Convert from base time to extended time */

static JSInt64

PRMJ_ToExtendedTime(JSInt32 base_time)

{

    JSInt64 exttime;

    JSInt64 g1970GMTMicroSeconds;

    JSInt64 low;

    JSInt32 diff;

    JSInt64  tmp;

    JSInt64  tmp1;



    diff = PRMJ_LocalGMTDifference();

    JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC);

    JSLL_I2L(tmp1,diff);

    JSLL_MUL(tmp,tmp,tmp1);



    JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI);

    JSLL_UI2L(low,G1970GMTMICROLOW);

#ifndef JS_HAVE_LONG_LONG

    JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);

    JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);

#else

    JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,32);

#endif

    JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low);



    JSLL_I2L(exttime,base_time);

    JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds);

    JSLL_SUB(exttime,exttime,tmp);

    return exttime;

}



JSInt64

PRMJ_Now(void)

{

#ifdef XP_OS2

    JSInt64 s, us, ms2us, s2us;

    struct timeb b;

#endif

#ifdef XP_WIN

    JSInt64 s, us,

    win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000),

    ten = JSLL_INIT(0, 10);

    FILETIME time, midnight;

#endif

#if defined(XP_UNIX) || defined(XP_BEOS)

    struct timeval tv;

    JSInt64 s, us, s2us;

#endif /* XP_UNIX */

#ifdef XP_MAC

    JSUint64 upTime;

    JSInt64	 localTime;

    JSInt64       gmtOffset;

    JSInt64    dstOffset;

    JSInt32       gmtDiff;

    JSInt64	 s2us;

#endif /* XP_MAC */



#ifdef XP_OS2

    ftime(&b);

    JSLL_UI2L(ms2us, PRMJ_USEC_PER_MSEC);

    JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);

    JSLL_UI2L(s, b.time);

    JSLL_UI2L(us, b.millitm);

    JSLL_MUL(us, us, ms2us);

    JSLL_MUL(s, s, s2us);

    JSLL_ADD(s, s, us);

    return s;

#endif

#ifdef XP_WIN

    /* The windows epoch is around 1600. The unix epoch is around 1970.

       win2un is the difference (in windows time units which are 10 times

       more precise than the JS time unit) */

    GetSystemTimeAsFileTime(&time);

    /* Win9x gets confused at midnight

       http://support.microsoft.com/default.aspx?scid=KB;en-us;q224423

       So if the low part (precision <8mins) is 0 then we get the time

       again. */

    if (!time.dwLowDateTime) {

        GetSystemTimeAsFileTime(&midnight);

        time.dwHighDateTime = midnight.dwHighDateTime;

    }

    JSLL_UI2L(s, time.dwHighDateTime);

    JSLL_UI2L(us, time.dwLowDateTime);

    JSLL_SHL(s, s, 32);

    JSLL_ADD(s, s, us);

    JSLL_SUB(s, s, win2un);

    JSLL_DIV(s, s, ten);

    return s;

#endif



#if defined(XP_UNIX) || defined(XP_BEOS)

#ifdef _SVID_GETTOD   /* Defined only on Solaris, see Solaris <sys/types.h> */

    gettimeofday(&tv);

#else

    gettimeofday(&tv, 0);

#endif /* _SVID_GETTOD */

    JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);

    JSLL_UI2L(s, tv.tv_sec);

    JSLL_UI2L(us, tv.tv_usec);

    JSLL_MUL(s, s, s2us);

    JSLL_ADD(s, s, us);

    return s;

#endif /* XP_UNIX */

#ifdef XP_MAC

    JSLL_UI2L(localTime,0);

    gmtDiff = PRMJ_LocalGMTDifference();

    JSLL_I2L(gmtOffset,gmtDiff);

    JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);

    JSLL_MUL(gmtOffset,gmtOffset,s2us);



    /* don't adjust for DST since it sets ctime and gmtime off on the MAC */

    Microseconds((UnsignedWide*)&upTime);

    JSLL_ADD(localTime,localTime,gmtOffset);

    JSLL_ADD(localTime,localTime, dstLocalBaseMicroseconds);

    JSLL_ADD(localTime,localTime, upTime);



    dstOffset = PRMJ_DSTOffset(localTime);

    JSLL_SUB(localTime,localTime,dstOffset);



    return *((JSUint64 *)&localTime);

#endif /* XP_MAC */

}



/* Get the DST timezone offset for the time passed in */

JSInt64

PRMJ_DSTOffset(JSInt64 local_time)

{

    JSInt64 us2s;

#ifdef XP_MAC

    /*

     * Convert the local time passed in to Macintosh epoch seconds. Use UTC utilities to convert

     * to UTC time, then compare difference with our GMT offset. If they are the same, then

     * DST must not be in effect for the input date/time.

     */

    UInt32 macLocalSeconds = (local_time / PRMJ_USEC_PER_SEC) + gJanuaryFirst1970Seconds, utcSeconds;

    ConvertLocalTimeToUTC(macLocalSeconds, &utcSeconds);

    if ((utcSeconds - macLocalSeconds) == PRMJ_LocalGMTDifference())

        return 0;

    else {

        JSInt64 dlsOffset;

    	JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC);

    	JSLL_UI2L(dlsOffset, PRMJ_HOUR_SECONDS);

    	JSLL_MUL(dlsOffset, dlsOffset, us2s);

        return dlsOffset;

    }

#else

    time_t local;

    JSInt32 diff;

    JSInt64  maxtimet;

    struct tm tm;

    PRMJTime prtm;

#ifndef HAVE_LOCALTIME_R

    struct tm *ptm;

#endif





    JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC);

    JSLL_DIV(local_time, local_time, us2s);



    /* get the maximum of time_t value */

    JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET);



    if(JSLL_CMP(local_time,>,maxtimet)){

        JSLL_UI2L(local_time,PRMJ_MAX_UNIX_TIMET);

    } else if(!JSLL_GE_ZERO(local_time)){

        /*go ahead a day to make localtime work (does not work with 0) */

        JSLL_UI2L(local_time,PRMJ_DAY_SECONDS);

    }

    JSLL_L2UI(local,local_time);

    PRMJ_basetime(local_time,&prtm);

#ifndef HAVE_LOCALTIME_R

    ptm = localtime(&local);

    if(!ptm){

        return JSLL_ZERO;

    }

    tm = *ptm;

#else

    localtime_r(&local,&tm); /* get dst information */

#endif



    diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) +

	((tm.tm_min - prtm.tm_min) * 60);



    if(diff < 0){

	diff += PRMJ_DAY_SECONDS;

    }



    JSLL_UI2L(local_time,diff);



    JSLL_MUL(local_time,local_time,us2s);



    return(local_time);

#endif

}



/* Format a time value into a buffer. Same semantics as strftime() */

size_t

PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *prtm)

{

#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_MAC) || defined(XP_BEOS)

    struct tm a;



    /* Zero out the tm struct.  Linux, SunOS 4 struct tm has extra members int

     * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets

     * confused and dumps core.  NSPR20 prtime.c attempts to fill these in by

     * calling mktime on the partially filled struct, but this doesn't seem to

     * work as well; the result string has "can't get timezone" for ECMA-valid

     * years.  Might still make sense to use this, but find the range of years

     * for which valid tz information exists, and map (per ECMA hint) from the

     * given year into that range.

     

     * N.B. This hasn't been tested with anything that actually _uses_

     * tm_gmtoff; zero might be the wrong thing to set it to if you really need

     * to format a time.  This fix is for jsdate.c, which only uses

     * JS_FormatTime to get a string representing the time zone.  */

    memset(&a, 0, sizeof(struct tm));



    a.tm_sec = prtm->tm_sec;

    a.tm_min = prtm->tm_min;

    a.tm_hour = prtm->tm_hour;

    a.tm_mday = prtm->tm_mday;

    a.tm_mon = prtm->tm_mon;

    a.tm_wday = prtm->tm_wday;

    a.tm_year = prtm->tm_year - 1900;

    a.tm_yday = prtm->tm_yday;

    a.tm_isdst = prtm->tm_isdst;



    /* Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff

     * are null.  This doesn't quite work, though - the timezone is off by

     * tzoff + dst.  (And mktime seems to return -1 for the exact dst

     * changeover time.)



     */



#if defined(SUNOS4)

    if (mktime(&a) == -1) {

        /* Seems to fail whenever the requested date is outside of the 32-bit

         * UNIX epoch.  We could proceed at this point (setting a.tm_zone to

         * "") but then strftime returns a string with a 2-digit field of

         * garbage for the year.  So we return 0 and hope jsdate.c

         * will fall back on toString.

         */

        return 0;

    }

#endif



    return strftime(buf, buflen, fmt, &a);

#endif

}



/* table for number of days in a month */

static int mtab[] = {

    /* jan, feb,mar,apr,may,jun */

    31,28,31,30,31,30,

    /* july,aug,sep,oct,nov,dec */

    31,31,30,31,30,31

};



/*

 * basic time calculation functionality for localtime and gmtime

 * setups up prtm argument with correct values based upon input number

 * of seconds.

 */

static void

PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm)

{

    /* convert tsecs back to year,month,day,hour,secs */

    JSInt32 year    = 0;

    JSInt32 month   = 0;

    JSInt32 yday    = 0;

    JSInt32 mday    = 0;

    JSInt32 wday    = 6; /* start on a Sunday */

    JSInt32 days    = 0;

    JSInt32 seconds = 0;

    JSInt32 minutes = 0;

    JSInt32 hours   = 0;

    JSInt32 isleap  = 0;

    JSInt64 result;

    JSInt64	result1;

    JSInt64	result2;

    JSInt64 base;



    JSLL_UI2L(result,0);

    JSLL_UI2L(result1,0);

    JSLL_UI2L(result2,0);



    /* get the base time via UTC */

    base = PRMJ_ToExtendedTime(0);

    JSLL_UI2L(result,  PRMJ_USEC_PER_SEC);

    JSLL_DIV(base,base,result);

    JSLL_ADD(tsecs,tsecs,base);



    JSLL_UI2L(result, PRMJ_YEAR_SECONDS);

    JSLL_UI2L(result1,PRMJ_DAY_SECONDS);

    JSLL_ADD(result2,result,result1);



    /* get the year */

    while ((isleap == 0) ? !JSLL_CMP(tsecs,<,result) : !JSLL_CMP(tsecs,<,result2)) {

        /* subtract a year from tsecs */

        JSLL_SUB(tsecs,tsecs,result);

        days += 365;

        /* is it a leap year ? */

        if(IS_LEAP(year)){

            JSLL_SUB(tsecs,tsecs,result1);

            days++;

        }

        year++;

        isleap = IS_LEAP(year);

    }



    JSLL_UI2L(result1,PRMJ_DAY_SECONDS);



    JSLL_DIV(result,tsecs,result1);

    JSLL_L2I(mday,result);



    /* let's find the month */

    while(((month == 1 && isleap) ?

            (mday >= mtab[month] + 1) :

            (mday >= mtab[month]))){

	 yday += mtab[month];

	 days += mtab[month];



	 mday -= mtab[month];



         /* it's a Feb, check if this is a leap year */

	 if(month == 1 && isleap != 0){

	     yday++;

	     days++;

	     mday--;

	 }

	 month++;

    }



    /* now adjust tsecs */

    JSLL_MUL(result,result,result1);

    JSLL_SUB(tsecs,tsecs,result);



    mday++; /* day of month always start with 1 */

    days += mday;

    wday = (days + wday) % 7;



    yday += mday;



    /* get the hours */

    JSLL_UI2L(result1,PRMJ_HOUR_SECONDS);

    JSLL_DIV(result,tsecs,result1);

    JSLL_L2I(hours,result);

    JSLL_MUL(result,result,result1);

    JSLL_SUB(tsecs,tsecs,result);



    /* get minutes */

    JSLL_UI2L(result1,60);

    JSLL_DIV(result,tsecs,result1);

    JSLL_L2I(minutes,result);

    JSLL_MUL(result,result,result1);

    JSLL_SUB(tsecs,tsecs,result);



    JSLL_L2I(seconds,tsecs);



    prtm->tm_usec  = 0L;

    prtm->tm_sec   = (JSInt8)seconds;

    prtm->tm_min   = (JSInt8)minutes;

    prtm->tm_hour  = (JSInt8)hours;

    prtm->tm_mday  = (JSInt8)mday;

    prtm->tm_mon   = (JSInt8)month;

    prtm->tm_wday  = (JSInt8)wday;

    prtm->tm_year  = (JSInt16)year;

    prtm->tm_yday  = (JSInt16)yday;

}

 

**** End of prmjtime.c ****

 

**** Start of prmjtime.h ****

 

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-

 *

 * The contents of this file are subject to the Netscape 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/NPL/

 *

 * Software distributed under the License is distributed on an "AS

 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr

 * 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 Netscape are

 * Copyright (C) 1998 Netscape Communications Corporation. All

 * Rights Reserved.

 *

 * Contributor(s): 

 *

 * Alternatively, the contents of this file may be used under the

 * terms of the GNU Public License (the "GPL"), in which case the

 * provisions of the GPL are applicable instead of those above.

 * If you wish to allow use of your version of this file only

 * under the terms of the GPL and not to allow others to use your

 * version of this file under the NPL, indicate your decision by

 * deleting the provisions above and replace them with the notice

 * and other provisions required by the GPL.  If you do not delete

 * the provisions above, a recipient may use your version of this

 * file under either the NPL or the GPL.

 */



#ifndef prmjtime_h___

#define prmjtime_h___

/*

 * PR date stuff for mocha and java. Placed here temporarily not to break

 * Navigator and localize changes to mocha.

 */

#include <time.h>

#include "jslong.h"

#ifdef MOZILLA_CLIENT

#include "jscompat.h"

#endif



JS_BEGIN_EXTERN_C



typedef struct PRMJTime       PRMJTime;



/*

 * Broken down form of 64 bit time value.

 */

struct PRMJTime {

    JSInt32 tm_usec;		/* microseconds of second (0-999999) */

    JSInt8 tm_sec;		/* seconds of minute (0-59) */

    JSInt8 tm_min;		/* minutes of hour (0-59) */

    JSInt8 tm_hour;		/* hour of day (0-23) */

    JSInt8 tm_mday;		/* day of month (1-31) */

    JSInt8 tm_mon;		/* month of year (0-11) */

    JSInt8 tm_wday;		/* 0=sunday, 1=monday, ... */

    JSInt16 tm_year;		/* absolute year, AD */

    JSInt16 tm_yday;		/* day of year (0 to 365) */

    JSInt8 tm_isdst;		/* non-zero if DST in effect */

};



/* Some handy constants */

#define PRMJ_USEC_PER_SEC	1000000L

#define PRMJ_USEC_PER_MSEC	1000L



/* Return the current local time in micro-seconds */

extern JSInt64

PRMJ_Now(void);



/* get the difference between this time zone and  gmt timezone in seconds */

extern JSInt32

PRMJ_LocalGMTDifference(void);



/* Format a time value into a buffer. Same semantics as strftime() */

extern size_t

PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *tm);



/* Get the DST offset for the local time passed in */

extern JSInt64

PRMJ_DSTOffset(JSInt64 local_time);



JS_END_EXTERN_C



#endif /* prmjtime_h___ */



 

**** End of prmjtime.h ****

 

**** Start of resource.h ****

 

//{{NO_DEPENDENCIES}}

// Microsoft Developer Studio generated include file.

// Used by js3240.rc

//



// Next default values for new objects

// 

#ifdef APSTUDIO_INVOKED

#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NEXT_RESOURCE_VALUE        101

#define _APS_NEXT_COMMAND_VALUE         40001

#define _APS_NEXT_CONTROL_VALUE         1000

#define _APS_NEXT_SYMED_VALUE           101

#endif

#endif

 

**** End of resource.h ****

 

